Consider the following program:

```
#include <iostream>
class Fraction
{
private:
int m_numerator{ 0 };
int m_denominator{ 1 };
public:
// Default constructor
Fraction(int numerator=0, int denominator=1)
: m_numerator{numerator}, m_denominator{denominator}
{
}
void print() const
{
std::cout << "Fraction(" << m_numerator << ", " << m_denominator << ")\n";
}
};
int main()
{
Fraction f { 5, 3 }; // Calls Fraction(int, int) constructor
Fraction fCopy { f }; // What constructor is used here?
f.print();
fCopy.print();
return 0;
}
```

You might be surprised to find that this program compiles just fine, and produces the result:

Fraction(5, 3) Fraction(5, 3)

Let’s take a closer look at how this program works.

The initialization of variable `f`

is just a standard brace initialization that calls the `Fraction(int, int)`

constructor.

But what about the next line? The initialization of variable `fCopy`

is also clearly an initialization, and you know that constructor functions are used to initialize classes. So what constructor is this line calling?

The answer is: the copy constructor.

The copy constructor

A **copy constructor** is a constructor that is used to initialize an object with an existing object of the same type. After the copy constructor executes, the newly created object should be a copy of the object passed in as the initializer.

An implicit copy constructor

If you do not provide a copy constructor for your classes, C++ will create a public **implicit copy constructor** for you. In the above example, the statement `Fraction fCopy { f };`

is invoking the implicit copy constructor to initialize `fCopy`

with `f`

.

By default, the implicit copy constructor will do memberwise initialization. This means each member will be initialized using the corresponding member of the class passed in as the initializer. In the example above, `fCopy.m_numerator`

is initialized using `f.m_numerator`

(which has value `5`

), and `fCopy.m_denominator`

is initialized using `f.m_denominator`

(which has value `3`

).

After the copy constructor has executed, the members of `f`

and `fCopy`

have the same values, so `fCopy`

is a copy of `f`

. Thus calling `print()`

on either has the same result.

Defining your own copy constructor

We can also explicitly define our own copy constructor. In this lesson, we’ll make our copy constructor print a message, so we can show you that it is indeed executing when copies are made.

The copy constructor looks just like you’d expect it to:

```
#include <iostream>
class Fraction
{
private:
int m_numerator{ 0 };
int m_denominator{ 1 };
public:
// Default constructor
Fraction(int numerator=0, int denominator=1)
: m_numerator{numerator}, m_denominator{denominator}
{
}
// Copy constructor
Fraction(const Fraction& fraction)
// Initialize our members using the corresponding member of the parameter
: m_numerator{ fraction.m_numerator }
, m_denominator{ fraction.m_denominator }
{
std::cout << "Copy constructor called\n"; // just to prove it works
}
void print() const
{
std::cout << "Fraction(" << m_numerator << ", " << m_denominator << ")\n";
}
};
int main()
{
Fraction f { 5, 3 }; // Calls Fraction(int, int) constructor
Fraction fCopy { f }; // Calls Fraction(const Fraction&) copy constructor
f.print();
fCopy.print();
return 0;
}
```

When this program is run, you get:

Copy constructor called Fraction(5, 3) Fraction(5, 3)

The copy constructor we defined above is functionally equivalent to the one we’d get by default, except we’ve added an output statement to prove the copy constructor is actually being called. This copy constructor is invoked when `fCopy`

is initialized with `f`

.

A reminder

Access controls work on a per-class basis (not a per-object basis). This means the member functions of a class can access the private members of any class object of the same type (not just the implicit object).

We use that to our advantage in the `Fraction`

copy constructor above in order to directly access the private members of the `fraction`

parameter. Otherwise, we would have no way to access those members directly (without adding access functions, which we might not want to do).

A copy constructor should not do anything other than copy an object. This is because the compiler may optimize the copy constructor out in certain cases. If you are relying on the copy constructor for some behavior other than just copying, that behavior may or may not occur. We discuss this further in lesson 14.15 -- Class initialization and copy elision.

Best practice

Copy constructors should have no side effects beyond copying.

Prefer the implicit copy constructor

Unlike the implicit default constructor, which does nothing (and thus is rarely what we want), the memberwise initialization performed by the implicit copy constructor is usually exactly what we want. Therefore, in most cases, using the implicit copy constructor is perfectly fine.

Best practice

Prefer the implicit copy constructor, unless you have a specific reason to create your own.

We’ll see cases where the copy constructor needs to be overwritten when we discuss dynamic memory allocation (21.13 -- Shallow vs. deep copying).

The copy constructor’s parameter must be a reference

It is a requirement that the parameter of a copy constructor be an lvalue reference or const lvalue reference. Because the copy constructor should not be modifying the parameter, using a const lvalue reference is preferred.

Best practice

If you write your own copy constructor, the parameter should be a const lvalue reference.

Pass by value and the copy constructor

When an object is passed by value, the argument is copied into the parameter. When the argument and parameter are the same class type, the copy is made by implicitly invoking the copy constructor.

This is illustrated in the following example:

```
#include <iostream>
class Fraction
{
private:
int m_numerator{ 0 };
int m_denominator{ 1 };
public:
// Default constructor
Fraction(int numerator = 0, int denominator = 1)
: m_numerator{ numerator }, m_denominator{ denominator }
{
}
// Copy constructor
Fraction(const Fraction& fraction)
: m_numerator{ fraction.m_numerator }
, m_denominator{ fraction.m_denominator }
{
std::cout << "Copy constructor called\n";
}
void print() const
{
std::cout << "Fraction(" << m_numerator << ", " << m_denominator << ")\n";
}
};
void printFraction(Fraction f) // f is pass by value
{
f.print();
}
int main()
{
Fraction f{ 5, 3 };
printFraction(f); // f is copied into the function parameter using copy constructor
return 0;
}
```

On the author’s machine, this example prints:

Copy constructor called Fraction(5, 3)

In the above example, the call to `printFraction(f)`

is passing `f`

by value. The copy constructor is invoked to copy `f`

from `main`

into the `f`

parameter of function `printFraction()`

.

Return by value and the copy constructor

In lesson 2.5 -- Introduction to local scope, we noted that return by value creates a temporary object (holding a copy of the return value) that is passed back to the caller. When the return type and the return value are the same class type, the temporary object is initialized by implicitly invoking the copy constructor.

For example:

```
#include <iostream>
class Fraction
{
private:
int m_numerator{ 0 };
int m_denominator{ 1 };
public:
// Default constructor
Fraction(int numerator = 0, int denominator = 1)
: m_numerator{ numerator }, m_denominator{ denominator }
{
}
// Copy constructor
Fraction(const Fraction& fraction)
: m_numerator{ fraction.m_numerator }
, m_denominator{ fraction.m_denominator }
{
std::cout << "Copy constructor called\n";
}
void print() const
{
std::cout << "Fraction(" << m_numerator << ", " << m_denominator << ")\n";
}
};
void printFraction(Fraction f) // f is pass by value
{
f.print();
}
Fraction generateFraction(int n, int d)
{
Fraction f{ n, d };
return f;
}
int main()
{
Fraction f2 { generateFraction(1, 2) }; // Fraction is returned using copy constructor
printFraction(f2); // f2 is copied into the function parameter using copy constructor
return 0;
}
```

When `generateFraction`

returns a `Fraction`

back to `main`

, a temporary `Fraction`

object is created and initialized using the copy constructor.

Because this temporary is used to initialize `Fraction f2`

, this invokes the copy constructor again to copy the temporary into `f2`

.

And when `f2`

is passed to `printFraction()`

, the copy constructor is called a third time.

Thus, on the author’s machine, this example prints:

Copy constructor called Copy constructor called Copy constructor called Fraction(1, 2)

If you compile and execute the above example, you may find that only two calls to the copy constructor occur. This is a compiler optimization known as *copy elision*. We discuss copy elision further in lesson 14.15 -- Class initialization and copy elision.

Using `= default`

to generate a default copy constructor

If a class has no copy constructor, the compiler will implicitly generate one for us. If we prefer, we can explicitly request the compiler create a default copy constructor for us using the `= default`

syntax:

```
#include <iostream>
class Fraction
{
private:
int m_numerator{ 0 };
int m_denominator{ 1 };
public:
// Default constructor
Fraction(int numerator=0, int denominator=1)
: m_numerator{numerator}, m_denominator{denominator}
{
}
// Explicitly request default copy constructor
Fraction(const Fraction& fraction) = default;
void print() const
{
std::cout << "Fraction(" << m_numerator << ", " << m_denominator << ")\n";
}
};
int main()
{
Fraction f { 5, 3 };
Fraction fCopy { f };
f.print();
fCopy.print();
return 0;
}
```

Using `= delete`

to prevent copies

Occasionally we run into cases where we do not want objects of a certain class to be copyable. We can prevent this by marking the copy constructor function as deleted, using the `= delete`

syntax:

```
#include <iostream>
class Fraction
{
private:
int m_numerator{ 0 };
int m_denominator{ 1 };
public:
// Default constructor
Fraction(int numerator=0, int denominator=1)
: m_numerator{numerator}, m_denominator{denominator}
{
}
// Delete the copy constructor so no copies can be made
Fraction(const Fraction& fraction) = delete;
void print() const
{
std::cout << "Fraction(" << m_numerator << ", " << m_denominator << ")\n";
}
};
int main()
{
Fraction f { 5, 3 };
Fraction fCopy { f }; // compile error: copy constructor has been deleted
return 0;
}
```

In the example, when the compiler goes to find a constructor to initialize `fCopy`

with `f`

, it will see that the copy constructor has been deleted. This will cause it to emit a compile error.

As an aside…

You can also prevent the public from making copies of class object by making the copy constructor private (as private functions can’t be called by the public). However, a private copy constructor *can* still be called from other members of the class, so this solution is not advised unless that is desired.

For advanced readers

The **rule of three** is a well known C++ principle that states that if a class requires a user-defined copy constructor, destructor, or copy assignment operator, then it probably requires all three. In C++11, this was expanded to the **rule of five**, which adds the move constructor and move assignment operator to the list.

Not following the rule of three/rule of five is likely to lead to malfunctioning code. We’ll revisit the rule of three and rule of five when we cover dynamic memory allocation.

We discuss destructors in lesson 15.4 -- Introduction to destructors and 19.3 -- Destructors, and copy assignment in lesson 21.12 -- Overloading the assignment operator.

Quiz time

Question #1

In the lesson above, we noted that the parameter for a copy constructor must be a (const) reference. Why aren’t we allowed to use pass by value?