We are going to talk about two special C++ methods, the copy constructor and the copy assignment operator. You will often hear about these in the context of the rule of three or the rule of five. We’re not going to cover either of these rules. Instead we’re going to focus on these two methods themselves. What are the copy constructor and assignment operator for? Why are there two different methods that seem to do the same thing? When do they get used?
The copy constructor is defined just like a normal constructor, except it takes a single argument, a constant reference to an object of that class. The copy assignment operator is defined as an overload of the =
operator. Let’s define a trivial class called Bond, that has a single field, a constructor, a copy constructor and a copy assignment operator.
#include <iostream>
class Bond
{
private:
double Rate;
public:
Bond(double rate) : Rate(rate)
{
std::cout << "Constructing" << std::endl;
}
// copy assignment
Bond& operator=(const Bond& other)
{
std::cout << "Copy assigning" << std::endl;
Rate = other.Rate;
return *this;
}
// copy constructor
Bond(const Bond& other) : Rate(other.Rate)
{
std::cout << "Copy constructing" << std::endl;
}
};
Suppose we run the following code:
Bond A(1.6);
Bond B(A);
You will see, “Constructing” printed to the terminal, followed by “Copy constructing”. Nothing surprising here, when we create Bond
A, we use the normal constructor. However, when we create Bond
B, we use the copy constructor.
Similarly, if we run the following code from a main method:
Bond A(1.6);
Bond B(1.2);
B = A;
This time we will see our “Constructing” message appear twice followed by “Copy Assigning”. That is exactly what you would expect, as we are clearly using the assignment operator to copy from the Bond A to the Bond B. Now you might think that the copy assignment method is used every time we use the =
operator to copy Bond objects. However it is not! Let’s say we ran the following code from a main method:
Bond A(1.6);
Bond B = A;
You won’t see the “Copy Assigning” output, you will see the “Copy constructing” message instead! That’s because we are not copy assigning here. Copy assignment happens whenever we use the =
operator to assign to a Bond
object that has already been initialised. Whereas, the copy constructor is called when we copy a Bond
object into a new Bond that has not been initialised yet.
In our second example, the Bond
B was already initialised with the normal constructor, before we assigned to it with =
, so the copy assignment method was called. however, in our third example, the Bond
B did not exist yet when we used the =
operator, so the copy constructor was called. This difference is easy to remember, because constructors always initialise new objects!
When you see an example like this, you might ask yourself a couple of questions. Why are we splitting hairs over these two different cases? Why do we even need two different methods, couldn’t C++ just use a single copy method here?
Well, with these kind of trivial examples, it is a little pointless to have a separate copy constructor and copy assignment operator. So let’s imagine a slightly more complex example. Suppose we have a class, Holder
that has a pointer to some object on the heap. Now when copy construct we know that our Holder
object is uninitialised, so we can just copy the from source to the target. Whether we do a deep or shallow copy is irrelevant here. When we do a copy assignment, the target object will have a pointer to an object on the heap. So, before we can copy from the source object, we must free this memory with a delete
. Otherwise we would end up with a serious memory leak.
To consider an even more complicated example. imagine a client class that talks to a server. When we copy a client object, we may want different behaviour depending on whether the target object has already started talking to a server or not.
So, in more complicated examples, the difference between copy assignment and copy construction can be quite important. The thing that you need to remember is that copy assignment happens when the target is already initialised, copy construction when it has not been initialised yet.