A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

今天同事问了一个关于拷贝构造函数的问题,类中包含指针的情况,今天就来说说c++的拷贝构造函数。
c++的拷贝构造函数是构造函数的一种,是对类对象的初始化,拷贝构造函数只有一个参数就是本类的引用。
注意,默认构造函数(即无参构造函数)不一定存在,但是拷贝构造函数总是会存在。
下面是一个拷贝构造函数的例子。
[url=][/url]
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 }[url=][/url]

输出结果为:
1010
如果编写了拷贝构造函数,则默认拷贝构造函数就不存在了。下面是一个非默认拷贝构造函数的例子。
[url=][/url]
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 }[url=][/url]

输出结果为:
10call copy construct10
拷贝构造函数被调用的三种情况
拷贝构造函数在以下三种情况下会被调用。
1) 当用一个对象去初始化同类的另一个对象时,会引发拷贝构造函数被调用。例如,下面的两条语句都会引发拷贝构造函数的调用,用以初始化 test_b。
1 A test_b(test_a);2 A test_b = test_a;
这两条语句是等价的。

注意,第二条语句是初始化语句,不是赋值语句。赋值语句的等号左边是一个早已有定义的变量,赋值语句不会引发拷贝构造函数的调用。例如:
1 A test_a,test_b;2 test_b = test_a;
这条语句不会引发拷贝构造函数的调用,因为  test_b  早已生成,已经初始化过了。
2) 如果函数 F 的参数是类 A 的对象,那么当 F 被调用时,类 A 的拷贝构造函数将被调用。换句话说,作为形参的对象,是用复制构造函数初始化的,而且调用拷贝构造函数时的参数,就是调用函数时所给的实参。
3) 如果函数的返冋值是类 A 的对象,则函数返冋时,类 A 的拷贝构造函数被调用。换言之,作为函数返回值的对象是用拷贝构造函数初始化 的,而调用拷贝构造函数时的实参,就是 return 语句所返回的对象。例如下面的程序:
[url=][/url]
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 }[url=][/url]

输出结果:
call copy construct4
针对于第三条,有些编译器可能会有以下的结果:
4
这是因为编译器编译的时候进行了优化,函数返回值对象就不用拷贝构造函数初始化了,这其实并不符合 C++的标准。
浅拷贝和深拷贝
重头戏来了,内含指针的拷贝构造函数,C++是如何实现的呢,来看个例子:
[url=][/url]
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 }[url=][/url]

输出结果如下:
[url=][/url]
100xf1901020100xf1901020*** glibc detected *** ./a.out: double free or corruption (fasttop): 0x0000000000f19010 ***...[url=][/url]

可以看到对于class A 的对象 test_a 和 test_b 指针p 指向了同一块内存,在对象析构的时候被析构了两次导致了crash,这就是我们常说的浅拷贝。
因此,在我们日常编写代码的时候特别需要注意这一点,对于指针我们需要相应的开辟一块新的内存,将指向的值拷贝过来,也就是所谓的深拷贝,下面是正确的写法:
[url=][/url]
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 }[url=][/url]

输出结果如下:
100xd4d01020100xd4d03020

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马