C ++ copy constructor (including default copy constructor and deep copy and shallow copy)

Source: Internet
Author: User

Copying objects in C ++ is like "Cloning". Using an existing object, you can quickly copy multiple identical objects. Generally, object replication is used in the following three cases:

(1) create a new object and initialize the new object with another existing object of the same type. For example:

[CPP]
View plaincopy
  1. Class rect
  2. {
  3. PRIVATE:
  4. Int width;
  5. Int height;
  6. };
  7. Rect rect1;
  8. Rect rect2 (rect1); // use rect1 to initialize rect2. At this time, the object will be copied.

 

(2) When the function parameter is a class object, when this function is called, value transmission is used, and object replication is also generated. For example:

[CPP]
View plaincopy
  1. Void fun1 (rect)
  2. {
  3. ...
  4. }
  5. Int main ()
  6. {
  7. Rect rect1;
  8. Fun1 (rect1); // the object will be copied at this time
  9. Return 0;
  10. }

(3) When the return value of a function is a class object, at the end of the function call, you need to copy the object in the function to a temporary object and pass it to the call location of the function, for example:

[CPP]
View plaincopy
  1. Rect fun2 ()
  2. {
  3. Rect;
  4. Return rect;
  5. }
  6. Int main ()
  7. {
  8. Rect rect1;
  9. Rect1 = fun2 ();
  10. // When fun2 returns an object, an object is copied and a temporary object is copied,
  11. // Then assign the temporary object "value" to rect1
  12. Return 0;
  13. }

Object replication is completed through a special constructor, which is also called a copy constructor ). The copy constructor is very simple in most cases, and can play a very good role even if we don't know its existence. However, in some special cases, especially when the object has dynamic members, we need to be very careful with copying constructors. Next, let's take a look at the use of the copy constructor.

1. Default copy constructor

Most of the time, if we do not know how to copy constructors, it is good to pass objects to function parameters or return objects, this is because the compiler will automatically generate a copy constructor, which is the "Default copy constructor". This constructor is very simple, only use the value of the data member of the "old object" to assign values to the data member of the "new object". It generally takes the following form:

 

[CPP]
View plaincopy
  1. Rect: rect (const rect & R)
  2. {
  3. Width = R. width;
  4. Height = R. height;
  5. }

 

 

Of course, the above Code does not need to be written, and the compiler will automatically generate for us. However, if we think this will solve the object replication problem, it will be wrong. Let's consider the following code:

[CPP]
View plaincopy
  1. Class rect
  2. {
  3. Public:
  4. Rect () // constructor, counter plus 1
  5. {
  6. Count ++;
  7. }
  8. ~ Rect () // destructor, counter minus 1
  9. {
  10. Count --;
  11. }
  12. Static int getcount () // returns the counter value
  13. {
  14. Return count;
  15. }
  16. PRIVATE:
  17. Int width;
  18. Int height;
  19. Static int count; // a static member acts as a counter
  20. };
  21. Int rect: Count = 0; // initialize the counter
  22. Int main ()
  23. {
  24. Rect rect1;
  25. Cout <"The Count of rect:" <rect: getcount () <Endl;
  26. Rect rect2 (rect1); // use rect1 to copy rect2. At this time, there should be two objects
  27. Cout <"The Count of rect:" <rect: getcount () <Endl;
  28. Return 0;
  29. }

This Code makes a slight modification to the previous class and adds a static member to count and count the number of created objects. When each object is created, the constructor increments. when an object is destroyed, the constructor increments. In the main function, first create the object rect1, output the number of objects at this time, then use rect1 to copy the object rect2, and then output the number of objects at this time, according to understanding, at this time, two objects should exist, but when the actual program runs, the output is 1, reflecting that only one object exists. In addition, when an object is destroyed, because two objects are called and destroyed, the class destructor call twice, and the counter changes to a negative number. The most fundamental cause of these problems is that when copying objects, the counter does not increase progressively. The solution is to re-compile the copy constructor and add the counter processing to the copy constructor, the resulting copy constructor is as follows:

[CPP]
View plaincopy
  1. Class rect
  2. {
  3. Public:
  4. Rect () // constructor, counter plus 1
  5. {
  6. Count ++;
  7. }
  8. Rect (const rect & R) // copy constructor
  9. {
  10. Width = R. width;
  11. Height = R. height;
  12. Count ++; // Add 1 to the counter
  13. }
  14. ~ Rect () // destructor, counter minus 1
  15. {
  16. Count --;
  17. }
  18. Static int getcount () // returns the counter value
  19. {
  20. Return count;
  21. }
  22. PRIVATE:
  23. Int width;
  24. Int height;
  25. Static int count; // a static member acts as a counter
  26. };

Writing a copy constructor by yourself can be divided into two situations: shallow copy and deep copy.

 

Ii. Shallow copy

The so-called shallow copy refers to simply assigning values to the data members in the object during object replication. The above examples are all in the case of a shallow copy,By default, the copy constructor executes the shortest copy function.. In most cases, "Shallow copy" can work well, but once the object has dynamic members, there will be problems with the shallow copy. Let's consider the following code:

[CPP]
View plaincopy
  1. Class rect
  2. {
  3. Public:
  4. Rect () // constructor. P points to a space allocated in the heap.
  5. {
  6. P = new int (100 );
  7. }
  8. ~ Rect () // destructor to release the dynamically allocated space
  9. {
  10. If (P! = NULL)
  11. {
  12. Delete P;
  13. }
  14. }
  15. PRIVATE:
  16. Int width;
  17. Int height;
  18. Int * P; // a pointer member
  19. };
  20. Int main ()
  21. {
  22. Rect rect1;
  23. Rect rect2 (rect1); // copy an object
  24. Return 0;
  25. }

A running error occurs before the code is executed. The reason is that during object replication, the dynamically allocated content is not correctly operated. Let's analyze:

After running the definition rect1 object, since there is a dynamically allocated statement in the constructor, the memory after execution is roughly as follows:

 

When rect1 is used to copy rect2, The rect1.p and rect2.p have the same value as the rect2, because the rect1 is used to copy the rect2, only the value of the member is assigned, that is, the two pointers point to the same space in the heap, as shown in:

 

Of course, this is not the expected result. When the object is destroyed, the destructor of the two objects will release the same memory twice, which is the cause of the error. What we need is not that two P have the same value, but two PPointThe space has the same value. The solution is to use "Deep copy ".

 

Iii. Deep copy

In the case of "Deep copy", for dynamic members of an object, it is not just a simple assignment, but a dynamic allocation of space should be performed, the above example should be processed as follows:

[CPP]
View plaincopy
  1. Class rect
  2. {
  3. Public:
  4. Rect () // constructor. P points to a space allocated in the heap.
  5. {
  6. P = new int (100 );
  7. }
  8. Rect (const rect & R)
  9. {
  10. Width = R. width;
  11. Height = R. height;
  12. P = new int; // dynamically allocate space for the new object
  13. * P = * (R. P );
  14. }
  15. ~ Rect () // destructor to release the dynamically allocated space
  16. {
  17. If (P! = NULL)
  18. {
  19. Delete P;
  20. }
  21. }
  22. PRIVATE:
  23. Int width;
  24. Int height;
  25. Int * P; // a pointer member
  26. };

After copying an object, the memory usage is roughly as follows:

 

 

In this case, the p of rect1 and the P of rect2 point to a memory space, but they point to the same content, which is called "Deep copy ".

In addition, the same problem also occurs when "Object assignment" is similar to "Object copying. This issue is further discussed in the "Object assignment" text.

Through the analysis of object replication, we found that most object replication occurs when "value transfer". Here is a small trick to prevent passing by value -- declaring a private copy constructor. You don't even need to define this copy constructor. Because the copy constructor is private, if you try to pass by value or the function returns this class object, a compilation error is returned, this prevents passing or returning objects by value.

 

 

 

 

References:

C ++ Programming

Bruce Eckel, C ++ programming ideology 1st

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.