C++ Tutorials
Copy Constructors
This post is part of a series of tutorials on C++
In C++ classes, it is often taken for granted that we can treat them like primitive types. For example, the seemingly innocent bit of code hopefully does what we want it to:
A standard interpretation of the last line of code is that animal
will store {'c', 'a', 't'}
, and indeed it will. But, the way in which this happened is quite curious. Running the above code will show output that indicates the overloaded constructor is called twice. Nevertheless, we have presumably created three instances of ThreeLetterWord
. So, what constructor was called for animal
?
It turns out that when you create a class in C++, a variety of default functionality is created for you so that you can do the above. The line ThreeLetterWord animal = cat;
takes advantage of this, in particular, by using an implicitly defined copy constructor. Copy constructors always have the form:
These constructors take a const
reference to a class of the same type, with the express purposes of reading its values and transferring them to the current instance of the class being created. When a line like ThreeLetterWord animal = cat;
executes, C++ implicitly calls the copy constructor. One could have just as easily executed ThreeLetterWord animal(cat);
and achieved the same effect.
For most simple class types, using the implicitly-defined, default implementation of the copy constructor is often fine. Nevertheless, it’s also a good practice to get in the habit of coding them yourself. For our class, this would look like this:
It’s important to understand what the C++ default does for these. Typically, a C++ copy constructor does a shallow copy. In particular, this means that pointer and array types are copied with a direct assignment. As a result, their values are not copied, but rather pointers are set to point to the same place. This means that in our first version of ThreeLetterWord
, where we did not code our own copy constructor, we could fall prey to the following type of bug:
The above code has serious problems. In addition to allowing one member to access the memory of the other, you can’t properly de-allocated it! When you execute this code, you may get a segfault or a command terminated by signal 11
because the destructor ~ThreeLetterWord()
I coded up will end up calling delete[]
on the same address twice.
You’ll note that I changed the private data member letters
to be heap-allocated char*
instead of a statically allocated array of length 3. It turns out that when the member was char letters[3]
C++ knows how to do the right deep copy of all the array entries. Any class that has to do memory management within it should implement a copy constructor, and in general, it’s useful to just get in the habit of coding this. Some people also use access rights like private
to specifically disable certain types of construction.