DEV Community

Tomislav Kraljic
Tomislav Kraljic

Posted on • Edited on

C++ Copy Constructors

Objective

In this article, I will explain what a copy constructor is and the differences between a deep copy and a shallow copy. I will show you how you can implement both copy constructor's through code snippets and I will explain what we are doing line by line. By the end of this article, you will be able to implement your own copy constructors confidently.

What Are Copy Constructors?

A Copy Constructor is a special constructor that initializes an object by copying from another object.

Example One: Shallow Copy

Alt Text

Let's break this down:

Here, we created a simple class called Student. It has a private variable called name and age. We also have two functions that are public called set_dimensions and show_data.


class Student {
private:
    int age;
    std::string name;
public:
    Student (std::string name, int age) {
        this->name = name;
        this->age = age;
   }
    void show_data() {
        std::cout << "Name:  " << name << "\n";
        std::cout << "Age: " << age << "\n";
    }

Enter fullscreen mode Exit fullscreen mode

In the Student constructor, it takes in a parameter of name and age. That parameter is pointing to the variable of name and age in the Student class.

Student(std::string name, int age) {
    this->name = name;
    this->age = age;
};

Enter fullscreen mode Exit fullscreen mode

In show_data, we are printing a statement to the console and passing name and age in.

void show_data() {
    std::cout << "Name:  " << name << "\n";
    std::cout << "Age: " << age << "\n";
}
Enter fullscreen mode Exit fullscreen mode

Now, let's move on to the main function. Here, we are instantiating an object called s1

int main() {
    Student* s1 = new Student("Tommy", 23);
    ...
}

Enter fullscreen mode Exit fullscreen mode

Then, we call the show_data function to see the output on the screen.

int main() {
    Student* s1 = new Student("Tommy", 23);
    s1.show_date();
    ...    
}

Enter fullscreen mode Exit fullscreen mode

So, after creating our first object, we want to use a copy constructor to create another one. In C++, the compiler has an implicit copy constructor.

Student* s2(s1);

Enter fullscreen mode Exit fullscreen mode

What do you think the memory address is of both objects?

 Student* s1 = new Student("Tommy", 23);
    s1->show_data();
    std::cout << s1 << "\n";

    Student* s2(s1);
    s2->show_data();
    std::cout << s2 << "\n";

Enter fullscreen mode Exit fullscreen mode

If you said that they have the same memory address, you are correct!

Here, we used C++'s compiler-generated copy constructor.

When we run our program, we see that both objects share the same memory address after using the compiler-generated copy constructor. This is known as a shallow copy

.

Example 2: Deep Copy

carbon(14).png

Let's break this down:

In the Student class, we changed int age to a pointer variable int* age

Student class {
private:
    std::string name;
    int* age;
....
Enter fullscreen mode Exit fullscreen mode

Next, in our constructor, we create a new int with our age variable. This is because our age variable is a pointer in our Student class.

Student(){
    age = new int;
...
Enter fullscreen mode Exit fullscreen mode

After this, we also add the pointer to this->age = age in our set_dimensions function because our age variable is not a pointer in our class.

void set_dimensions(std::string name, int age) {
    this->name = name;
    *this->age = age;
Enter fullscreen mode Exit fullscreen mode

After that, then we move on to creating our own custom copy constructor

Student(Student& other) {
    name = other.name;
    age = new int;
    *age = *(other.age);

}
Enter fullscreen mode Exit fullscreen mode

Here, we are creating a new int variable of age and creating a pointer for it. We are setting that pointer to the age of the new object that will be created. This creates a new memory address for us when we call this copy constructor. This is known as a deep copy

Lastly, we need to create our own destructor as well. Whenever we allocate memory, we must also deallocate that memory as well. If we don't, it will be holding up memory which is negatively impacting our performance.

~Student(){
    delete age;
    std::cout << std::endl;
    std::cout << "Destructor Called...." << std::endl;
}

Enter fullscreen mode Exit fullscreen mode

Shallow Copy vs Deep Copy

When you let the compiler implicitly create a copy constructor, under the hood, it is a shallow copy. It takes all the data from one object and copies it to another referencing the same memory address.

This may be problematic because if you delete one copied object, it will also delete the other object because they both share the same memory address.

So, when we create our own custom copy constructor, we are doing a deep copy. This gives the new copied object a unique memory address. It is no longer referencing the same memory address. Therefore, If you delete this object, it will not impact any others that you copied it from.

Top comments (2)

Collapse
 
mohammadmahdijavid profile image
MohammadMahdi Javid • Edited

Hello Tomislav,
Thanks for sharing great information.
I tried what you said about the default copy constructor, but the memory address of those objects are not the same I've commented those addresses in front of them. so it seems that the default copy ctor uses deep copy.
can you help me to figure out what is wrong?
thanks

Link to my code:
dev-to-uploads.s3.amazonaws.com/up...

Collapse
 
tomislavkraljic profile image
Tomislav Kraljic • Edited
    Student* s1 = new Student("Tommy", 23);
    std::cout << s1 << std::endl;

    Student* s2(s1);
    std::cout << s2 << std::endl;
Enter fullscreen mode Exit fullscreen mode

You want to create a new instance of Student with a pointer. The pointer points to a memory address when you create the first object. When you shallow copy it, it also copies the memory address of the first student.