C++ Copy Constructors

Ref:http://www.cplusplus.com/articles/y8hv0pDG/

Once you start building objects that connect to other objects, creating a copy of an object becomes much more complicated. If we think of the object as a simple container like this:

Making a copy of this is easy. We just create a new chunk of memory, and copy into that container the values currently in the original object box. This can be done by just copying in all the bits. That is exactly what is done automatically by C++ using the default “copy constructor”.

However, if the object contains pointers to other objects, then making a simple copy gives us something we do not expect:

This is not what we want. the object we are thinking of here is a “list”, and we want a copy of the entire list, not just one part of it!

This picture shows what is going on when we do a copy of a complex data structure:

../../_images/copy-constructor.png

If we want the whole list to be copied, we want a “deep copy”, meaning everything in the data structure is copied, not just the first management object.

Obviously, we want the “deep copy” for most purposes.

Copy Constructors

If you do not write one of your own, the compiler will build a “shallow copy” constructor for you. This default one does the simple “shallow copy” operation, which is fine for many simple classes.

We need to create our own copy constructor to ge a “deep copy” of the object. This is mor eomcplex, and depends on you, since you put together this structire!

Creating an exact copy of our linked list is much easier if we add one additional pointer to the class. This one will be used to track the end of the list. Here is the basic idea:

LinkedList::LinkedList(const LinkedList & src); {
    Node *node = src.head;
    while (node != nullptr) {
        addTail(node->Item);
        node = node->next;
    }
}

Here, we have captured all the logic needed to build a new node in the addTail method.

Class Destructors

If we build such a “deep copy”, we probably want to set up a way to destroy the copy as well.

Again, if you do nothing C++ sets up a default destructor, which simply deletes the memory used by one object. If your one object is a management object, maintaining pointers to your data structure, thi is definitly not what you want.

You write a destructor by putting a setting up code that looks like constructor code, except you add a “squiggle” in front of the class name. For our linked list, this is a proper destructor:

LinkedList::~LinkedList() {
    while (Head != nullptr) {
        Node *temp = head;
        remove(head);
        delete temp;
    }
}

This code uses the remove method to delete nodes from the list.

Destructors are called automatically whenever an object goes “out of scope”, meaning when you exit a function where you set up an object as a local variable (object). Failing to properly dispose of a complex data structure leads to something called a “memory leak” Those linked nodes are still around, taking up memory, but onthing can reach then, since the management node was erased!

Summarizing

Once you start building objects, and passing them around as parameters to ofhter parts of oyur code, you really need to be thinking about what gets passed. Unless you set up parameters as reference parameters, when you hand an object to a function, thta is done by creating a copy of that object and passing that into the function. That may work, but you might start seeing really weird results, because you got a shallow copy instead of a deep one!

If you create your own copy constructor, things are safer, but you need to watch for th eperformance hit you take if you copy a huge data structure. It is far smarter to pass a reference to that structure instead. Less memory is used, and les time is taken creating things yoy will throw away later!