Today we are going to talk about two special member functions in object-oriented systems: copy constructor and destructor. In C++, these two functions are parts of what's called the Rule of Three. This rule is a rule of thumb in C++ for building exception-safe code and for successful memory management.
The Rule of Three states that if a class defines one of the following methods, it should probably define all three:
- Copy constructor
- Assignment operator
The rule is based on the fact that automatically generated, implicit versions of these methods exist. If one of them needs to be defined by the programmer, that means that the implicit version did not fit the program's needs; therefore, the same will probably also be true for the other two methods.
We will be discussing the copy constructor and the destructor. The assignment operator will be covered in one of the next articles when we introduce operator overloading, but for now, it's just important to know about it, and to understand that all three methods need to be defined for very similar reasons.
These reasons will become clear when we explain the concept of shallow copy and deep copy.
One method of copying an object is the shallow copy. For example, let's say we want to make a copy of object A. A new object B is created, and the fields' values of A are copied over to B - a bit-wise copy is performed. However, what happens if our class definition contains complex data structures or has external references such as pointers? For example, if the field value is a reference to an object (i.e. it holds a memory address), the shallow copy only copies the reference, hence referring to the same object as A. The referenced objects are thus shared, so if one of these objects is modified (from A or B), the change is visible in the other. This can cause real problems, like accidentally deleting objects you were referencing. This is why deep copying is performed.
Deep copies copy not only references of the original object, but also copy the objects that are referenced. This way, there are no shared objects and the possibility of error is smaller. However, deep copies are also more complicated to write.
Now, let's return to what we started talking about: copy constructors are actually what enables the programmer to make deep copies of objects. This is necessary when a shallow copy just isn't good enough, and that is, as we mentioned, when our class contains complex data structures or references/pointers.
Let's see how you would write a copy constructor in C++:
MyClass (const MyClass &obj)
//copy constructor body
The above was a common form of the copy constructor in C++. Here,
is a reference to an object being used to initialize another object. This reference is also usually declared
, which will cause the variable to be passed without copying but also stop it from then being altered. Full meaning and usage of the
keyword would exceed the limits of this article; in the context of parameter passing it is also a subject of debate in the C++ community. For now, we will just consider the example above as the proper form of a copy constructor.
On the other hand, a destructor functions are the "inverse" of constructors. They are called when objects are destroyed (deallocated). The destructor is commonly used to "clean up" when an object is no longer necessary. However, the default destructor may sometimes not be enough - for instance, in the same situations that we mentioned before (when there is any dynamic memory involved). The typical declaration of a class destructor in C++ would be:
In the next article, we will continue with explaining dynamic memory management in C++. We talked about this in the C tutorial, and now we are going to introduce C++'s operators
delete and see how they are used. This will also enable us to give meaningful and helpful examples of the copy constructor and destructor, so stay tuned!