在C++編程中,咱們在編寫一個基類的派生類時,大體能夠分爲四步:ios
• 吸取基類的成員:不管是數據成員仍是函數成員,派生類吸取除基類的構造函數和析構函數以外的所有成員。編程
• 改造基類函數:在派生類中聲明一個或多個與其(某個)基類中的成員函數同名的成員函數,並將它(們)根據新的需求進行重寫函數
• 發展新的成員:在派生類中添加新的成員變量和成員函數,其中新添加的成員要求必須和基類中的成員不一樣名,而且應當保證新添加的成員會使派生類在功能上相比其基類有所發展spa
• 重寫派生類的構造函數和析構函數3d
特別注意:code
在重寫派生類的構造函數時,一般將其基類們的構造函數變成新構造函數的一部分對象
語法:派生類名::派生類名(參數總表):基類名1(參數表1),基類名2(參數表2),......,新增成員名1(參數1),新增成員名2(參數2){}
示例:blog
1 #include<iostream> 2 using namespace std; 3 class Father{ //基類 4 public: 5 Father()=default; //默認構造函數 6 Father(int value):father_value(value){} //帶參數的構造函數 7 ~Father(){} //析構函數 8 void show(){ 9 cout<<"這是基類Father"<<endl; 10 } 11 void father_func(){ 12 cout<<"這是基類Father的方法father_func:"<<endl; 13 cout<<"\t成員father_value的值爲:"<<father_value<<endl; 14 } 15 private: 16 int father_value; 17 }; 18 19 class Son:public Father{ //Father類的派生類Son 20 public: 21 /*步驟一:吸取Father類中除構造函數和析構函數以外的全部成員 22 void show(){ 23 cout<<"這是基類Father"<<endl; 24 } 25 void father_func(){ 26 cout<<"這是基類Father的方法father_func:"<<endl; 27 cout<<"\t成員father_value的值爲:"<<father_value<<endl; 28 } 29 */ 30 //步驟二:改造基類Father中的成員 31 void show(){ 32 cout<<"這是派生類Son"<<endl; 33 } 34 //步驟三:發展新的成員 35 void son_func(){ 36 cout<<"這是派生類Son的方法son_func: "<<endl; 37 cout<<"\t成員son_value的值爲:"<<son_value<<endl; 38 } 39 //步驟四:重寫構造函數和析構函數 40 Son()=default; 41 Son(int value):Father(value),son_value(value){} 42 ~Son(){} 43 private: 44 /*步驟一:吸取Father類中除構造函數和析構函數以外的全部成員 45 int father_value; 46 */ 47 //步驟三:發展新的成員 48 int son_value; 49 }; 50 int main(){ 51 Father father(10); 52 father.show(); 53 father.father_func(); 54 cout<<"------------分界線--------------------"<<endl; 55 Son son(20); 56 son.show(); 57 son.father_func(); 58 son.son_func(); 59 return 0; 60 }
咱們先來看一個示例:繼承
1 #include<iostream> 2 using namespace std; 3 class Father1{ //基類1 4 public: 5 Father1(){ 6 cout<<"這是Father1類的構造函數"<<endl; 7 } 8 }; 9 10 class Father2{ //基類2 11 public: 12 Father2(){ 13 cout<<"這是Father2類的構造函數"<<endl; 14 } 15 }; 16 17 class Father3{ //基類3 18 public: 19 Father3(){ 20 cout<<"這是Father3類的構造函數"<<endl; 21 } 22 }; 23 24 class Son:public Father1,public Father2,public Father3{ //派生類 25 public: 26 Son(){ 27 cout<<"這是Son類的構造函數"<<endl; 28 } 29 }; 30 31 int main(){ 32 Son s; 33 return 0; 34 }
由上面的例子能夠看出,派生類在建立對象時會先調用其基類們的構造函數,而後纔會調用本身的構造函數。下面是類Son的對象s在內存中的存放形式:內存
那麼派生類調用基類的構造函數的順序又是如何肯定的呢?咱們在來看一個例子:
1 #include<iostream> 2 using namespace std; 3 class Father1{ //基類1 4 public: 5 Father1(){ 6 cout<<"這是Father1類的構造函數"<<endl; 7 } 8 }; 9 10 class Father2{ //基類2 11 public: 12 Father2(){ 13 cout<<"這是Father2類的構造函數"<<endl; 14 } 15 }; 16 17 class Father3{ //基類3 18 public: 19 Father3(){ 20 cout<<"這是Father3類的構造函數"<<endl; 21 } 22 }; 23 24 class Son:public Father3,public Father1,public Father2{ //派生類 25 public: 26 Son(){ 27 cout<<"這是Son類的構造函數"<<endl; 28 } 29 private: 30 Father1 father1; 31 Father2 father2; 32 Father3 father3; 33 }; 34 35 int main(){ 36 Son s; 37 return 0; 38 }
因而可知,派生類在建立對象時其調用構造函數的順序是:
• 先按照派生類對基類的繼承順序調用基類的構造函數。上例中由語句「class Son:public Father3,public Father1,public Father2」可知類Son先繼承基類Father3,而後繼承基類Father1,最後繼承基類Father2,所以其調用基類的構造函數的順序也是先Fathe3,再Father1,最後Father2。
•( 若派生類的成員變量中存在其基類的對象 )接着按照基類的對象在派生類定義中聲明的前後順序調用基類的構造函數。上例中按照成員對象的定義順序依次調用Father一、Father2和Father3的構造函數。
• 最後調用派生類本身的構造函數
特別注意:
1.若是基類中沒有定義默認構造函數或帶有缺省值的構造函數而只有帶參數的構造函數時,派生類的構造函數中必須顯式的給出基類名和參數表,不然編譯器將報錯
1 #include<iostream> 2 using namespace std; 3 class Father1{ //基類1 4 public: 5 Father1(int v):value1(v){ 6 cout<<"這是Father1類的構造函數"<<endl; 7 } 8 private: 9 int value1; 10 }; 11 12 class Father2{ //基類2 13 public: 14 Father2(int v):value2(v){ 15 cout<<"這是Father2類的構造函數"<<endl; 16 } 17 private: 18 int value2; 19 }; 20 21 class Father3{ //基類3 22 public: 23 Father3(int v):value3(v){ 24 cout<<"這是Father3類的構造函數"<<endl; 25 } 26 private: 27 int value3; 28 }; 29 30 class Son:public Father1,public Father2,public Father3{ //派生類 31 public: 32 Son(int v):Father1(v),Father2(v),Father3(v),value4(v){ //派生類的構造函數中必須顯式的給出基類名和參數表 33 cout<<"這是Son類的構造函數"<<endl; 34 } 35 private: 36 int value4; 37 }; 38 39 int main(){ 40 Son s(10); 41 return 0; 42 }
2.若是基類中沒有定義構造函數,這派生類也能夠不定義構造函數,系統會自動在類中添加默認的構造函數的
3.若是基類中定義了帶有參數表的構造函數時,派生類就應當定義相應的構造函數
QUESTION:基類在派生類的構造函數的初始化列表中的順序是否會影響派生類的構造函數調用順序?
ANSWER: 咱們先來看一個示例:
1 #include<iostream> 2 using namespace std; 3 class Father1{ //基類1 4 public: 5 Father1(int v):value1(v){ 6 cout<<"這是Father1類的構造函數"<<endl; 7 } 8 private: 9 int value1; 10 }; 11 12 class Father2{ //基類2 13 public: 14 Father2(int v):value2(v){ 15 cout<<"這是Father2類的構造函數"<<endl; 16 } 17 private: 18 int value2; 19 }; 20 21 class Father3{ //基類3 22 public: 23 Father3(int v):value3(v){ 24 cout<<"這是Father3類的構造函數"<<endl; 25 } 26 private: 27 int value3; 28 }; 29 30 class Son:public Father3,public Father1,public Father2{ //派生類 31 public: 32 Son(int v):Father1(v),Father2(v),Father3(v),value4(v){ 33 cout<<"這是Son類的構造函數"<<endl; 34 } 35 private: 36 int value4; 37 }; 38 39 int main(){ 40 Son s(10); 41 return 0; 42 }
上面的例子中派生類Son是以順序Father三、Father一、Father2來繼承基類的,但在其構造函數中基類的順序確實Father一、Father二、Father3。最終的結果代表類Son仍以其繼承基類的順序來調用基類的構造函數,而非基類在派生類構造函數中的順序。所以可見基類在派生類的構造函數的初始化列表中的順序不會影響派生類的構造函數調用順序
• 在派生類中,其析構函數只須要關心新增的通常成員的「善後工做」。而對於新增的成員對象和基類的「善後工做」,系統會本身調用成員對象和基類的析構函數來完成,而不須要用戶來關心。
• 在派生類中,析構函數各部分的執行順序與其構造函數的調用順序恰好相反,即派生類的析構函數先對其新增的通常成員進行析構,而後對新增的成員對象進行析構,最後按照與其繼承基類的相反順序來調用基類的析構函數。
1 #include<iostream> 2 using namespace std; 3 class Father1{ //基類1 4 public: 5 Father1(){ 6 cout<<"這是Father1類的構造函數"<<endl; 7 } 8 ~Father1(){ 9 cout<<"這是Father1類的析構函數"<<endl; 10 } 11 }; 12 13 class Father2{ //基類2 14 public: 15 Father2(){ 16 cout<<"這是Father2類的構造函數"<<endl; 17 } 18 ~Father2(){ 19 cout<<"這是Father2類的析構函數"<<endl; 20 } 21 }; 22 23 class Father3{ //基類3 24 public: 25 Father3(){ 26 cout<<"這是Father3類的構造函數"<<endl; 27 } 28 ~Father3(){ 29 cout<<"這是Father3類的析構函數"<<endl; 30 } 31 }; 32 33 class Son:public Father3,public Father1,public Father2{ //派生類 34 public: 35 Son(){ 36 cout<<"這是Son類的構造函數"<<endl; 37 } 38 ~Son(){ 39 cout<<"這是Son類的析構函數"<<endl; 40 } 41 private: 42 Father1 father1; 43 Father2 father2; 44 Father3 father3; 45 }; 46 47 int main(){ 48 Son s; 49 return 0; 50 }