



Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
These lecture notes cover various topics in C++ programming, including copy constructors, destructors, operator=, templates, and recursion. The notes discuss the default copy constructor, destructor, and operator= provided by the compiler, and when it is necessary to define them manually. The notes also cover the use of templates to write functions that can apply to many different classes of objects, and provide examples of recursive functions, including the factorial function and the Towers of Hanoi problem.
Typology: Lecture notes
1 / 6
This page cannot be seen from the preview
Don't miss anything!




The compiler will provide a default copy constructor, destructor, and operator= if you don’t define them. This is fine
as long as there are no pointer variables in the private data that use dynamic memory allocation. The image library
that I have made available uses dynamic memory allocation. So, when you are writing your new image class, you
will need to implement these operations. The copy constructor and destructor should be straightforward. (You can
use the functions in ImageLib!) The only tricky part is in operator=, it is important that you check for self-
assignment. Consider this example for an arbitrary class:
const someClass & someClass::operator=(const someClass &input) {
if (this != &input) { // necessary since deallocation will
// destroy input if they are the same!
// deallocate memory for “this” here
// then copy input into “this”
return *this;
Note that the copy constructor must also take a const & parameter, since otherwise the copy constructor would be
needed to pass the parameter by value! (The copy constructor is used for three cases, call by value, return by value,
and declaration with initialization, such as Object a = b.)
Usually, these three methods should be written first (if you are using dynamic memory), since they are called
automatically in some cases. Otherwise, this may cause your code to crash. The signature for the copy constructor
and destructor looks like this:
someClass::someClass(const someClass &input) {
// copy from input to “this” here
someClass::~someClass() {
// deallocate memory allocated to object here
In many cases, we would like to write a function so that it would apply to many different classes of objects.
Examples include min(a, b) , swap(a, b) or sort(array, n). Here is a simple example of min:
template
Comparable min(Comparable a, Comparable b) {
if (a < b) return a;
else return b;
or
template
const Comparable &min(const Comparable &a, const Comparable &b) {
if (a < b) return a;
else return b;
See the Complex number class on the course website for a template class example:
Complex.h
Complex.cpp
ComplexDriver.cpp
Note that templates are difficult to handle for compilers, since they are compiled on demand. The declaration and
definition need to be compiled at the same time. This does not mean that they need to be in the same file. Using a
#include to include the implementation file at the end of the header file is one option. None of your programming
projects this quarter will require you to write a template class (this would be tricky, in fact).
You should have been introduced to recursion in your previous courses. Here is a quick recap:
Recursion is a fundamental technique for writing programs. The basic idea is break the problem into easier
problems of the same general type. Note that there are two distinct parts to recursive functions:
breaking the problem down into a simpler problem. In this case, it handles the case where there are no
numbers to add.
part makes (at least) one recursive call to itself. In this case, it adds all of the numbers, except the last one.
The factorial function is a common function that can illustrate recursion. The factorial function is:
F(n) = n * (n – 1) * (n – 2) * … * 2 * 1 (for any integer n > 0)
We can write a simple (non-recursive) function to compute factorial:
// Computes the factorial of n, if n > 0. Otherwise, returns 1.
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++)
result = result * i;
return result;
Note that we can also write: F(n) = n * F(n-1) (for n > 1). Since this defines the function in terms of a simpler
version of the problem, we can use recursion to compute factorial:
// Computes the factorial of n, if n > 0. Otherwise, returns 1.
int factorial(int n) {
if (n <= 1) return 1;
else return n * factorial(n – 1);
Let’s compare the two versions:
The iterative version has two local variables; the recursive version has none.
The iterative version requires more lines of code than the iterative version.
The iterative version is more difficult to understand (arguably).
The recursive version is simpler, because the computer is doing more of the work. The recursive version may
require more memory (if the compiler is not smart enough to optimize the code) and it may take slightly longer
(owing to function calling overhead), but these are not overriding factors. If you write your code using an efficient
algorithm and, after it is working properly, find that the speed is not acceptable, then you can worry about
optimization.
To solve the problem, we will assume that we already have classes corresponding to towers and disks with the
following interface:
disk:
No methods needed.
tower:
disk removeTopDisk()
bool addDiskToTop(disk diskAdded) // Returns false if new disk is too big to be added
// Recursive algorithm that solves the towers of Hanoi problem.
// Pre-conditions: Source has at least n disks on it.
// Destination and spare are either empty or they hold only
// disks that are larger than the top n disks on source.
// Post-conditions: The top n disks on source have been moved to destination.
// Disks that start on destination or spare are not moved.
void hanoi(int n, tower &source, tower &destination, tower &spare)
if (n > 0)
hanoi(n – 1, source, spare, destination);
disk diskToMove = source.removeTopDisk();
destination.addDiskToTop(diskToMove);
hanoi(n – 1, spare, destination, source);
Believe it or not, that solves the entire problem!
A java applet that demonstrates the solution can be found at:
http://www.cut-the-knot.org/recurrence/hanoi.shtml
Here is a trace the algorithm for n = 4:
hanoi(4, A, B, C);
hanoi(3, A, C, B);
hanoi(2, A, B, C)
hanoi(1, A, C, B)
Move disk from a to c
Move disk from a to b
hanoi(1, C, B, A)
Move disk from c to b
Move disk from a to c
hanoi(2, B, C, A)
hanoi(1, B, A, C)
Move disk from b to a
Move disk from b to c
hanoi(1, A, C, B)
Move disk from a to c
Move disk from a to b
hanoi(3, C, B, A);
hanoi(2, C, A, B)
hanoi(1, C, B, A)
Move disk from c to b
Move disk from c to a
hanoi(1, B, A, C)
Move disk from b to a
Move disk from c to b
hanoi(2, A, B, C)
hanoi(1, A, C, B)
Move disk from a to c
Move disk from a to b
hanoi(1, C, B, A)
Move disk from c to b
How do we know that the pre-conditions are never violated? We need induction to prove this, so you’ll have to trust
me for now.
An interesting question is: given n, how many moves does it take to solve the towers of Hanoi problem using the
above algorithm. The number of moves is given by the following recurrence relation:
moves(n) = moves(n – 1) + 1 + moves(n – 1) = 2 * moves(n – 1) + 1
moves(1) = 1
For example: moves(3) = 2 * moves(2) + 1 = 2 * (2 * moves(1) + 1) + 1 = 2 * (2 * 1 + 1) + 1 = 7
It can be observed (and proven using induction) that moves(n) = 2
n
One season on an episode of Survivor, the immunity challenge involved an intelligence test where the teams played
a game against each other called 21 flags. At the start of the game, there are 21 flags in a designated location. The
two teams take turns removing 1, 2, or 3 flags from the game. The winning team is the team to remove the last flag.
Interestingly, if the first team knows the optimal strategy, they can guarantee that they win the game. The solution
can be seen using recursive (and logical) reasoning.
First, let’s think of the base case. At what position can you guarantee that you can win the game? If there are 1, 2, or
3 flags left, you win. Now, how can you put the other team in a position such that they must leave you with 1, 2, or 3
flags. The only case that guarantees this is if they have 4 flags during their turn. If you can leave them with exactly 4
flags, then you win. From here, we need to devise a way to leave the opponent with exactly four flags. The key is to
see that this is exactly like trying to leave the opponent with 0 flags (winning the game). If they have exactly 8 flags
on their turn, then you can leave them with exactly 4 flags on their next turn, and then win. This same situation
applies at each step – you want to leave you opponent with a number of flags that is a multiple of 4 every time. If
you go first, you remove 1 flag, leaving the opponent with 20. No matter how many flags your opponents takes, you
leave them with 16, then 8, then 4, then 0, and you win no matter what they do.
There is a famous sequence of numbers called the Fibonacci sequence. The textbook describes it in terms of
breeding rabbits using the following assumptions:
Rabbits never die.
A rabbit is able to reproduce when it is two months old.
Rabbits are always born in male-female pairs.
Each month, every pair of rabbits that is able to reproduce gives birth to one male-female pair.
Starting with a single pair of newborn rabbits, how many rabbits are there after 3 months? 6 months? n months?
Month 1: 1 pair
Month 2: 1 pair (not old enough to reproduce)
Month 3: 2 pairs (the first pair reproduced)
Month 4: 3 pairs (the first pair reproduced, but the new pair was not old enough)
Month 5: 5 pairs
Month 6: 8 pairs
Month n: number at month n – 1 plus number at month n – 2
The Fibonacci sequence is usually defined as F(n) = F(n – 1) + F(n – 2), where F(0) = 0 and F(1) = 1. (Definitions
sometimes vary in whether the first term is 0 or 1, but this only changes the indexes by one.)
We can compute the number of rabbits at any month using a recursive function: