Learn C++ Quickly: A Complete Beginner’s Guide to Learning C++, Even If You’re New to Programming (Crash Course With Hands-On Project Book 3) by Quickly Code

Learn C++ Quickly: A Complete Beginner’s Guide to Learning C++, Even If You’re New to Programming (Crash Course With Hands-On Project Book 3) by Quickly Code

Author:Quickly, Code [Quickly, Code]
Language: eng
Format: epub
Published: 2020-07-28T16:00:00+00:00


15.4 - Copy constructors—shallow and deep copy

When we copy an object in C++, we create a new object from an existing one. As you might recall that we have already explained that the default in C++ is always to copy values. There are several reasons for an object to be copied, for example, as you learned in an earlier section, when we pass an object by value, or when we return an object from a function by value. In C++, the compiler must be able to define a copy if, by peradventure, you don't provide one. For doing so, we use what is called a copy constructor . If you do provide a copy constructor, there is a proper way for doing so, which we will demonstrate and explain.

Basically, using the default way (by the compiler) to copy objects by value can work well, but if you need to copy raw pointers, you may have a problem. The reason is that the pointers will be copied, but NOT what they are pointing to, and this is what is called " shallow copy " (which will be discussed further in this section). What you need to remember is that when dealing with pointers, it is always good practice to provide a copy constructor. It is also best said that using raw pointers as data members is not recommended, as it opens a door for many errors.

When using a copy constructor, we use the const reference parameter, as we are copying the source, and we don’t want to modify it. As we demonstrated before, the name of the constructor will still remain the same as the name of the class. Here is the syntax used with our Tests class:

Tests :: Tests (const Tests &source);

When we want to implement the copy constructor, we can do it using the initialization list:

Tests :: Tests (const Tests &source)

: name {source .name},

age {source.age},

score {source.score}

{

}

You can see that it is simple to initialize the newly created attributes using the source object attributes.

By this point, you probably understand very well that constructors allocate storage dynamically, while destructors free up memory when called. When copying, we can use a shallow copy or a deep copy . As mentioned earlier, a shallow copy is the default copy method, and the problem with it starts when copying raw pointers, as the copy copies the pointer itself, and not what it is pointing to. When we release the storage of one of the objects by using the destructor, what do you think will happen?

Well, if you think that the second object, which was copied still points to the same storage, while this storage is no longer valid, then you are correct. The way to avoid this is to use a deep copy, which doesn't only copy the pointer, but also the data the pointer is pointing to while it allocates storage on the heap.

Let’s look at an example. Here is a class that uses shallow copy :

class Salary

{

private:

int *sum; // this is our



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.