C++派生类的拷贝构造
一. 概述
通过几个简单的实验,回顾下派生类中拷贝构造的相关知识。
环境:Centos7 64位, g++ 4.8.5
在继承中,构造器与析构器均没有被继承下来。拷贝构造,也是一种构造,也没有被继承下来。
父类中,一部分成员需要拷贝构造来完成,子类,也有一部分成员需要拷贝构造来完成。子类中的内嵌子对象中的成员也需要拷贝构造来完成。
二. 实验过程
1.无自实现(系统默认)
派生类中,不自实现拷贝构造函数,看下系统默认的拷贝构造情况。
基类A,派生类C继承了基类A,派生类C中有一个内嵌子对象B bb。
通过下面的运行情况,对象c2拷贝了c1,派生类中调用了父类中默认的拷贝构造函数,内嵌子对象也是一样。
1 #include <iostream> 2 3 using namespace std; 4 5 class A 6 { 7 public: 8 A(int x = 10) 9 :a(x) 10 { 11 cout<<"constructor A()"<<endl; 12 } 13 14 int a; 15 }; 16 17 class B 18 { 19 public: 20 B(int y = 20) 21 :b(y) 22 { 23 cout<<"constructor B()"<<endl; 24 } 25 26 int b; 27 }; 28 29 class C: public A 30 { 31 public: 32 C(int x, int y, int z = 30) 33 :A(x), bb(y) 34 { 35 c = z; 36 cout<<"cosntructor C()"<<endl; 37 } 38 39 int c; 40 B bb; 41 }; 42 43 int main() 44 { 45 C c1(42, 21, 14); 46 cout<<"c1: "<<c1.a<<" "<<c1.bb.b<<" "<<c1.c<<endl; 47 48 cout<<"----------"<<endl; 49 C c2(c1); 50 cout<<"c2: "<<c2.a<<" "<<c2.bb.b<<" "<<c2.c<<endl; 51 52 return 0; 53 }
运行结果如下:
达到了预期结果。
c2成功地拷贝了c1。根据打印结果,可知,c1对象在生成时,先调用了基类A的构造函数,然后是内嵌子对象bb的构造函数,最后是C自己的构造函数。
2.派生类中自实现拷贝构造函数,不显示调用父类、内嵌子对象中的拷贝构造函数
派生类C中,自实现拷贝构造函数,第11行-第14行,如下。
通过打印结果发现,派生类调用了基类的构造函数,而不是默认的拷贝构造函数。内嵌子对象也是一样。
对象c1和c2中的两个成员a, b结果均不一致,也就是说父类和内嵌子对象中的成员都没有被拷贝过来,c2中的a、b的值是父类、内嵌子对象中调用构造函数进行初始化而来的。此时,拷贝构造也没有什么意义了。无法达到拷贝的效果。
1 class C: public A 2 { 3 public: 4 C(int x, int y, int z = 30) 5 :A(x), bb(y) 6 { 7 c = z; 8 cout<<"cosntructor C()"<<endl; 9 } 10 11 C(const C &another) 12 { 13 c = another.c; 14 } 15 16 int c; 17 B bb; 18 };
运行结果如下:
3.派生类中自实现拷贝构造,显示调用父类、内嵌子对象中的拷贝构造函数
派生类C中添加显示调用,第12行代码。
注:A(another),将派生类对象赋值给父类的引用,用到了赋值兼容。
此时,派生类中的拷贝构造函数调用了基类中默认的拷贝构造函数。此时,浅拷贝也可以满足需求(关于浅拷贝与深拷贝)。
1 class C: public A 2 { 3 public: 4 C(int x, int y, int z = 30) 5 :A(x), bb(y) 6 { 7 c = z; 8 cout<<"cosntructor C()"<<endl; 9 } 10 11 C(const C &another) 12 :A(another), bb(another.bb) 13 { 14 c = another.c; 15 } 16 17 int c; 18 B bb; 19 };
运行结果如下:
运行结果符合预期,实现了拷贝的目的。
4.在3的基础上,如果需要实现深拷贝的目的,则父类中也需要自实现拷贝构造
类A,增加第10行-第14行代码
1 class A 2 { 3 public: 4 A(int x = 10) 5 :a(x) 6 { 7 cout<<"constructor A()"<<endl; 8 } 9 10 A(const A &another) 11 { 12 a = another.a; 13 cout<<"A(const A &another)"<<endl; 14 } 15 16 int a; 17 };
类B,增加第10行-第14行代码
1 class B 2 { 3 public: 4 B(int y = 20) 5 :b(y) 6 { 7 cout<<"constructor B()"<<endl; 8 } 9 10 B(const B &another) 11 { 12 b = another.b; 13 cout<<"B(const B &another)"<<endl; 14 } 15 16 int b; 17 };
运行结果如下:
根据打印结果可知, 对象c2在拷贝c1时,调用了基类和内嵌子对象的拷贝构造函数。
四. 总结
当派生类中不自实现拷贝构造时,默认调用父类的拷贝构造函数;
当派生类中自实现拷贝构造时,不做特殊处理(显示地调用父类的拷贝构造函数,包括系统默认和自实现拷贝构造函数),此时,只会调用父类的构造函数。此时,也失去了拷贝的意义,无法实现拷贝;
当派生类自实现拷贝构造,进行特殊处理(显示地调用父类的拷贝构造函数,包括系统默认和自实现的拷贝构造函数),此时,会调用父类的拷贝构造函数。
内嵌子对象与上面类似。
参考材料:
《C++基础与提高》 王桂林