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
- Class rect
- {
- PRIVATE:
- Int width;
- Int height;
- };
- Rect rect1;
- 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
- Void fun1 (rect)
- {
- ...
- }
- Int main ()
- {
- Rect rect1;
- Fun1 (rect1); // the object will be copied at this time
- Return 0;
- }
(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
- Rect fun2 ()
- {
- Rect;
- Return rect;
- }
- Int main ()
- {
- Rect rect1;
- Rect1 = fun2 ();
- // When fun2 returns an object, an object is copied and a temporary object is copied,
- // Then assign the temporary object "value" to rect1
- Return 0;
- }
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
- Rect: rect (const rect & R)
- {
- Width = R. width;
- Height = R. height;
- }
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
- Class rect
- {
- Public:
- Rect () // constructor, counter plus 1
- {
- Count ++;
- }
- ~ Rect () // destructor, counter minus 1
- {
- Count --;
- }
- Static int getcount () // returns the counter value
- {
- Return count;
- }
- PRIVATE:
- Int width;
- Int height;
- Static int count; // a static member acts as a counter
- };
- Int rect: Count = 0; // initialize the counter
- Int main ()
- {
- Rect rect1;
- Cout <"The Count of rect:" <rect: getcount () <Endl;
- Rect rect2 (rect1); // use rect1 to copy rect2. At this time, there should be two objects
- Cout <"The Count of rect:" <rect: getcount () <Endl;
- Return 0;
- }
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
- Class rect
- {
- Public:
- Rect () // constructor, counter plus 1
- {
- Count ++;
- }
- Rect (const rect & R) // copy constructor
- {
- Width = R. width;
- Height = R. height;
- Count ++; // Add 1 to the counter
- }
- ~ Rect () // destructor, counter minus 1
- {
- Count --;
- }
- Static int getcount () // returns the counter value
- {
- Return count;
- }
- PRIVATE:
- Int width;
- Int height;
- Static int count; // a static member acts as a counter
- };
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
- Class rect
- {
- Public:
- Rect () // constructor. P points to a space allocated in the heap.
- {
- P = new int (100 );
- }
- ~ Rect () // destructor to release the dynamically allocated space
- {
- If (P! = NULL)
- {
- Delete P;
- }
- }
- PRIVATE:
- Int width;
- Int height;
- Int * P; // a pointer member
- };
- Int main ()
- {
- Rect rect1;
- Rect rect2 (rect1); // copy an object
- Return 0;
- }
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
- Class rect
- {
- Public:
- Rect () // constructor. P points to a space allocated in the heap.
- {
- P = new int (100 );
- }
- Rect (const rect & R)
- {
- Width = R. width;
- Height = R. height;
- P = new int; // dynamically allocate space for the new object
- * P = * (R. P );
- }
- ~ Rect () // destructor to release the dynamically allocated space
- {
- If (P! = NULL)
- {
- Delete P;
- }
- }
- PRIVATE:
- Int width;
- Int height;
- Int * P; // a pointer member
- };
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