Deep Copy VS Shallow Copy in C++

  1. Shallow Copy is Used by Default Copy Constructor in C++
  2. Use Custom Copy Constructor to Implement Deep Copy Behavior in C++

This article will demonstrate multiple methods about how to use deep copy VS shallow copy in C++.

Shallow Copy is Used by Default Copy Constructor in C++

C++ classes are generally defined with several operations, collectively referred to as copy control, specified explicitly by the user or implicitly by the compiler. These member functions are denoted as: copy constructor, copy-assignment operator, move constructor, move-assignment operator, and destructor. Copy constructor and move constructor implement operations that happen when the object is initialized from another object of the same type. Although, when these functions are synthesized by the compiler implicitly, some class types may behave incorrectly. E.g., the classes that manage dynamic memory will share data members that need to be allocated manually. Thus the programmer is responsible for implementing the above member functions explicitly.

In this case, we demonstrate the case of a copy constructor in a class named Person with two std::string data members, one of which is allocated using the new operator. The following example code shows what happens when the copy constructor is not defined explicitly, and we initialize a Person object with another Person object. Notice that P1 has stored strings - Buddy/Rich after initialization and P2 has the same values after copy constructor is called in the statement - Person P2 = P1;. After the renamePerson function is executed on the P1 object, the surname data member of the P2 object is also modified.

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout; using std::endl;
using std::vector; using std::string;

class Person {
public:
    Person() = default;
    Person(string n, string s) {
        name = std::move(n);
        surname = new string(std::move(s));
    }

    ~Person() {
        delete surname;
    }

    void renamePerson(const string &n, const string &s) {
        name.assign(n);
        surname->assign(s);
    };

    string& getName() { return name; };
    string& getSurname() { return *surname; };

    void printPerson() {
        cout << name << " " << *surname;
    }

private:
    string name;
    string *surname{};
};

int main()
{
    Person P1("Buddy", "Rich");
    Person P2 = P1;

    P1.printPerson();
    cout << endl;
    P2.printPerson();
    cout << endl;

    P1.renamePerson("Heinz", "Lulu");

    P1.printPerson();
    cout << endl;
    P2.printPerson();
    cout << endl;

    exit(EXIT_SUCCESS);
}

Output:

Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Lulu

Use Custom Copy Constructor to Implement Deep Copy Behavior in C++

On the other hand, when we implement a custom copy constructor for the Person class, it behaves correctly and does not modify the P2 object after the P1.renamePerson("Heinz", "Lulu") statement. In the previous code snippet, surname member of P2 object pointed to the same string as the P1 object, and renamePerson modified both objects. This time, P2 has its own surname member allocated on dynamic memory and does not share it with the P1 object.

#include <iostream>
#include <string>
#include <utility>
#include <vector>

using std::cout; using std::endl;
using std::vector; using std::string;

class Person {
public:
    Person() = default;
    Person(string n, string s) {
        name = std::move(n);
        surname = new string(std::move(s));
    }
    Person(Person &p) {
        name = p.name;
        surname = new string(*p.surname);
    }

    ~Person() {
        delete surname;
    }

    void renamePerson(const string &n, const string &s) {
        name.assign(n);
        surname->assign(s);
    };

    string& getName() { return name; };
    string& getSurname() { return *surname; };

    void printPerson() {
        cout << name << " " << *surname;
    }

private:
    string name;
    string *surname{};
};

int main()
{
    Person P1("Buddy", "Rich");
    Person P2 = P1;

    P1.printPerson();
    cout << endl;
    P2.printPerson();
    cout << endl;

    P1.renamePerson("Heinz", "Lulu");

    P1.printPerson();
    cout << endl;
    P2.printPerson();
    cout << endl;

    exit(EXIT_SUCCESS);
}

Output:

Buddy Rich
Buddy Rich
Heinz Lulu
Buddy Rich
Contribute
DelftStack is a collective effort contributed by software geeks like you. If you like the article and would like to contribute to DelftStack by writing paid articles, you can check the write for us page.

Related Article - C++ Class

  • Use Private vs Protected Class Members in C++
  • Implement Assignment Operator Overloading in C++