一. 概述
通過幾個簡單的實驗,回顧下派生類中拷貝構造的相關知識。
環境: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++基礎與提高》 王桂林