c + + copy constructors (focus on shallow and deep copies with pointers)

Keywords: C++ glibc

Today, my colleague asked a question about the copy constructor. The class contains pointers. Let's talk about the copy constructor of c + +.

The copy constructor of c + + is a kind of constructor, which is the initialization of class objects. Only one parameter of the copy constructor is the reference of this class.

Note that the default constructor (that is, the parameterless constructor) does not necessarily exist, but the copy constructor always does.

Here is an example of a copy constructor.

 1 #include<iostream>
 2 using namespace std;
 3 class A{
 4 public:
 5     int a;
 6     A(int value){
 7         a = value;
 8     }
 9     void show(){
10         cout<<a<<endl;
11     }
12 }; 
13 int main(){
14     A test_a(10);
15     test_a.show();
16     
17     A test_b(test_a);
18     test_b.show();
19     
20     return 0;
21 }

The output result is:

10
10

If you write a copy constructor, the default copy constructor does not exist. Here is an example of a non default copy constructor.

 1 #include<iostream>
 2 using namespace std;
 3 class A{
 4 public:
 5     int a;
 6     A(int value){
 7         a = value;
 8     }
 9     A(A& tmp){
10         a = tmp.a;
11         cout<<"call copy construct"<<endl;
12     }
13     void show(){
14         cout<<a<<endl;
15     }
16 }; 
17 int main(){
18     A test_a(10);
19     test_a.show();
20     
21     A test_b(test_a);
22     test_b.show();
23     
24     return 0;
25 }

The output result is:

10
call copy construct
10

Three cases of copy constructor being called

The copy constructor is called in three cases.

1) when one object is used to initialize another object of the same kind, the copy constructor will be called. For example, both of the following statements raise a call to the copy constructor to initialize test? B.

1 A test_b(test_a);
2 A test_b = test_a;

The two statements are equivalent.

Note that the second statement is an initialization statement, not an assignment statement. To the left of the equal sign of the assignment statement is a variable that has already been defined. The assignment statement will not cause the call of the copy constructor. For example:

1 A test_a,test_b;
2 test_b = test_a;

This statement will not raise a call to the copy constructor, because test ﹣ B ﹣ has already been generated and initialized.

2) if the parameter of function F is an object of class A, the copy constructor of class a will be called when F is called. In other words, the object as a parameter is initialized with the copy constructor, and the parameter when the copy constructor is called is the argument given when the function is called.

3) if the return value of a function is an object of class A, the copy constructor of class a will be called when the function returns. In other words, the object as the function return value is initialized with the copy constructor, and the actual parameter when calling the copy constructor is the object returned by the return statement. For example, the following procedure:

 1 #include<iostream>
 2 using namespace std;
 3 class A{
 4 public:
 5     int a;
 6     A(int value){
 7         a = value;
 8     }
 9     A(A& tmp){
10         a = tmp.a;
11         cout<<"call copy construct"<<endl;
12     }
13     void show(){
14         cout<<a<<endl;
15     }
16 }; 
17 A Func() {
18     A test_a(4);
19     return test_a;
20 }
21 int main(){
22     Func().show(); 
23     
24     return 0;
25 }

Output results:

call copy construct
4

For the third article, some compilers may have the following results:

4

This is because the compiler is optimized when compiling, and function return value objects do not need to copy constructor initialization, which does not conform to the standard of C + +.

Shallow copy and deep copy

Here's the play. How does C + + implement the copy constructor with pointer? Here's an example:

 1 #include<iostream>
 2 using namespace std;
 3 class A{
 4 public:
 5         int a;
 6         int *p;
 7         A(int value1, int value2){
 8                 a = value1;
 9                 p = new int(value2);
10         }
11         ~A(){
12                 delete p;
13         }
14 
15         void show(){
16                 cout<<a<<endl;
17                 cout<<p<<endl;
18                 cout<<*p<<endl;
19         }
20 };
21 
22 int main(){
23         A test_a(10,20);
24         test_a.show();
25 
26         A test_b(test_a);
27         test_b.show();
28 
29         return 0;
30 }

The output results are as follows:

10
0xf19010
20
10
0xf19010
20
*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000000f19010 ***
...

It can be seen that the test UU A and the test UU B pointer p of class A point to the same block of memory. When the object is destructed twice, it causes crash, which is often referred to as shallow copy.

Therefore, we need to pay special attention to this point in our daily code writing. For pointers, we need to create a new memory and copy the values pointed to, that is, the so-called deep copy. The following is the correct writing method:

 1 #include<iostream>
 2 using namespace std;
 3 class A{
 4 public:
 5     int a;
 6     int *p;
 7     A(int value1, int value2){
 8         a = value;
 9         p = new int(value2);
10     }
11     A(A& tmp){
12         a = tmp.a;
13         p = new int(* tmp.p);
14     }
15     ~A(){
16         delete p;
17     }
18 
19     void show(){
20         cout<<a<<endl;
21         cout<<p<<endl;
22         cout<<*p<<endl;
23     }
24 }; 
25 
26 int main(){
27     A test_a(10,20); 
28     test_a.show();
29     
30     A test_b(test_a);
31     test_b.show();
32     
33     return 0;
34 }

The output results are as follows:

10
0xd4d010
20
10
0xd4d030
20

Posted by AbraCadaver on Wed, 23 Oct 2019 03:41:00 -0700