詳談c++的構造和析構
c++的構造
概念:對象被創建時,編譯系統對象分配內存空間,並自動調用該構造函數->由構造函數完成成員的初始化工作
例:
1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 public: 6 A():a(1),b(0){cout<<"A"<<endl;} 7 private: 8 int a; 9 const int b; 10 }; 11 12 int main() 13 { 14 A a; 15 A*b(&a);16 A &c(a); 17 return 0; 18 }
A *(&a) 和 A &c(a)都沒有產生新的對象,所以都沒有在調用構造函數,輸出結果為:A
可通過構造函數對成員變量進行初始化
子父類的構造與析構
1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 public: 6 A(){cout<<"A"<<endl;} 7 virtual ~A(){cout << "~A" <<endl;}8 }; 9 10 class B{ 11 public: 12 B(){cout << "B" <<endl;} 13 virtual ~B(){cout << "~B" << endl;} 14 }; 15 16 class C : public A{ 17 public: 18 C(){cout << "C" << endl;} 19 virtual ~C(){cout << "~C" <<endl;} 20 private: 21 B b; 22 };23 24 int main() 25 { 26 C c; 27 return 0; 28 }
輸出結果為:
1 A 2 B 3 C 4 ~C 5 ~B 6 ~A
父類的構造函數:
(1)如果某個類具有基類,執行基類的默認構造函數。除非在ctor-initializer中調用了基類構造函數,此時調用這個構造函數,而不是默認構造函數
(2)類的非靜態數據成員按照聲明的順序創建
(3)執行該類的構造函數
父類的析勾函數:
(1)調用類的析構函數
(2)銷毀類的數據成員,與創建順序相反
(3)如果有父類,調用父類的析構函數
註意所有的析構函數都是virtual。如果前面的析構函數沒有聲明為virtual,代碼也可以繼續運行。然而,如果代碼使用delete刪除一個實際指向派生類的基類指針,析構函數調用鏈將被破壞。如下:
1 #include <iostream> 2 using namespace std; 3 4 class A{ 5 public: 6 A(){cout<<"A"<<endl;} 7 ~A(){cout << "~A" <<endl;} 8 }; 9 10 11 class C : public A{ 12 public: 13 C(){cout << "C" << endl;} 14 ~C(){cout << "~C" <<endl;} 15 }; 16 17 int main() 18 { 19 A *c = new C();z 20 delete c; 21 return 0; 22 }
輸出結構為:
A C ~A
註:當基類代碼中顯示了沒有默認構造函數,相關版本的派生類必須顯示地告訴編譯器如何調用Super的構造函數,否則代碼將無法編譯。
1 class Super 2 { 3 public: 4 Super(int i); 5 }; 6 class Sub : public Super 7 { 8 public: 9 Sub(int i); 10 }; 11 12 Sub::Sub(int i) : Super(i) 13 { 14 15 }View Code
從派生類向基類傳遞構造函數的參數很正常,毫無問題,但是無法傳遞數據成員。因為在調用基類的構造之後才會初始化數據成員。如果將數據成員作為參數傳遞給父類構造函數,數據成員不會初始化。
父類的析構函數
析構函數的調用順序剛好與構造函數相反:
(1)調用類的析構函數
(2)銷毀類的數據成員,與創建的順序相反
(3)如果有父類,調用父類的析構函數
註意:所有的析構函數都是virtual。根據經驗,所有析構函數都應該聲明為virtual。如果前面的析構函數沒有聲明為virtual,代碼也可以繼續運行。然而,如果代碼使用delete刪除一個實際指向派生類的基類指針,析構函數調用鏈將被破壞。
從技術角度看,將基類的析構函數聲明為virtual,可以糾正上面的問題。派生類將自動“虛化”。然而,建議顯示地將所有析構函數聲明為virtual
詳談c++的構造和析構