派生類的建構函式和解構函式(C++學習筆記 32)
引入繼承的目的:①派生類繼承了基類的成員,實現了原有程式碼的重用。②實現程式碼的擴充,只有在派生類中通過新增新的成員,加入新的功能,類的派生才有實際意義。
基類的建構函式和解構函式不能被繼承,在派生類中,如果對派生類新增的成員進行初始化,就需要加入派生類的建構函式,同時,對所有從基類繼承下來的成員的初始化工作,還是由基類的建構函式完成
1、派生類建構函式和解構函式的執行順序
通常,當建立派生類物件時,首先執行基類的建構函式,隨後執行派生類的建構函式;當撤銷派生類物件時,則先執行派生類的解構函式,隨後再執行基類的解構函式。解構函式的呼叫順序與建構函式的呼叫順序正好相反。
2、派生類建構函式和解構函式的構造規則
(1)簡單的派生類的建構函式
在C++中,派生類建構函式的一般格式為:
派生類名(引數總表):基類名(引數表){
派生類新增資料成員的初始化語句
}
- 其中,基類建構函式的引數,通常來源於派生類建構函式的引數總表,也可以用常數值,因為這裡是呼叫基類建構函式,所以這些引數是實參不是形參,它們可以是派生類建構函式總引數表中的引數,也可以是常量和全域性變數。
- 當基類的建構函式沒有引數,或沒有顯式定義建構函式時,派生類可以不向基類傳遞引數,甚至可以不定義建構函式。
- 派生類不能繼承基類中的建構函式和解構函式。當基類含有帶引數的建構函式時,派生類必須定義建構函式,以提供把引數傳遞給基類建構函式的途徑。
例 1:當基類含有帶引數的建構函式,派生類建構函式的構造方法。
#include<iostream>
#include<string>
using namespace std;
class Student{ //宣告類 Student
protected:
int number; //學號
string name; //姓名
float score; //成績
public:
Student(int num1,string name1,float score1){ // 基類建構函式
number=num1;
name=name1;
score= score1;
}
void print(){
cout<<"number:"<<number<<endl;
cout<<"name:"<<name<<endl;
cout<<"score:"<<score<<endl;
}
};
class Ustudent:public Student{ //宣告公有派生類 Student
private:
string major;
public:
Ustudent(int num1,string name1,float score1,string major1):Student(num1,name1,score1){
major=major1;
}
void print1(){
print();
cout<<"major:"<<major<<endl;
}
};
int main(){
Ustudent stu(12345,"叮叮",100,"數學");
stu.print1();
return 0;
}
說明:
① 在類的外部定義派生類的建構函式,在類體內只寫建構函式的宣告。
如,例 1 可以寫成:
Ustudent(int num1,string name1,float score1,string major1);
而在類的外部定義派生類的建構函式:
Ustudent(int num1,string name1,float score1,string major1):Student(num1,name1,score1){
major=major1;
}
② 若基類使用預設建構函式或不帶引數的建構函式,則在派生類中定義建構函式時可略去“:基類建構函式名(引數表)”,此時若派生類不需要建構函式,則可不定義派生類建構函式。
③ 當基類建構函式不帶引數時,派生類不一定需要定義建構函式,而當基類的建構函式哪怕只帶有一個引數,它所有的派生類都必須定義建構函式,甚至所定義的派生類建構函式的函式體可能為空,僅僅起引數的傳遞作用。
(2) 派生類的解構函式
在派生類中可以根據需要定義自己的解構函式,用來對派生類中的所增加的成員進行清理工作,基類的清理工作仍然由基類的解構函式負責。
在執行派生類的解構函式時,系統會自動呼叫基類的解構函式,對基類的物件進行清理。解構函式的呼叫順序與建構函式正好相反,先執行派生類的解構函式,再執行基類的解構函式。
(3)含有物件成員(子物件)的派生類的建構函式
當派生類中含有內嵌的物件成員(也稱子物件時),其建構函式的一般形式為:
派生類名(引數總表):基類名(引數表 0), 物件成員名 1(引數表 1), ···, 物件成員名 n(引數表 n)
{
派生類新增成員的初始化語句
}
可參考類的組合:https://blog.csdn.net/aaqian1/article/details/84494415
在定義派生類物件時,建構函式的執行順序如下:
① 呼叫基類的建構函式,對基類資料成員初始化;
② 呼叫內嵌物件成員的建構函式,對內嵌物件成員的資料成員初始化;
③ 執行派生類的建構函式體,對派生類資料成員初始化。
- 撤銷物件時,解構函式的呼叫順序與建構函式的呼叫順序正好相反。
- 當在派生類中含有多個內嵌物件成員時,呼叫內嵌物件成員的建構函式順序由它們在類中宣告的順序確定。
例 2:含有物件成員的派生類建構函式和解構函式的執行順序
#include<iostream>
using namespace std;
class Base{ //宣告基類 Base
private:
int x;
public:
Base(int i){ //基類的建構函式
x=i;
cout<<"Constructing base class"<<endl;
}
~Base(){ //基類的解構函式
cout<<"Destructing base class"<<endl;
}
void show(){
cout<<"x="<<x<<endl;
}
};
class Derived:public Base{ //宣告公有派生類 Derived
private:
Base d; //d 為基類物件,作為派生類的內嵌物件成員
public:
Derived(int i):Base(i),d(i){ //派生類的建構函式,綴上要呼叫的基類建構函式和物件成員建構函式
cout<<"Constructing derived class"<<endl;
}
~Derived(){
cout<<"Destructing derived class"<<endl;
}
};
int main(){
Derived obj(5);
obj.show();
return 0;
}
執行結果:
例 3:含有多個物件成員的派生類建構函式的執行順序
#include<iostream>
#include<string>
using namespace std;
class Student{
protected:
int number; //學號
string name; //姓名
float score; //成績
public:
Student(int num1,string name1,float score1){ // 基類建構函式
number=num1;
name=name1;
score=score1;
}
void print(){
cout<<"number:"<<number<<endl;
cout<<"name:"<<name<<endl;
cout<<"score:"<<score<<endl;
}
};
class Ustudent:public Student{ //宣告公有派生類 Ustudent
private:
string major; //專業
Student auditor1; //定義物件成員 1(旁聽生 )
Student auditor2; //定義物件成員 2(旁聽生 )
public:
Ustudent(int num1,string name1,float score1,int num2,string name2,
float score2,int num3,string name3,float score3,string major1):Student(num1,name1,score1),
auditor2(num2,name2,score2),auditor1(num3,name3,score3){
major=major1;
}
void print(){
cout<<"正式生是:"<<endl;
Student::print();
cout<<"major:"<<major<<endl<<endl;
}
void print_auditor1(){
cout<<"旁聽生是:"<<endl;
auditor1.print();
}
void print_auditor2(){
auditor2.print();
}
};
int main(){
Ustudent stu(1,"小明",92,2,"小紅",99,3,"小蘭",63,"英語");
stu.print();
stu.print_auditor1();
stu.print_auditor2();
return 0;
}
執行結果:
呼叫內嵌物件成員建構函式的順序由它們在類中宣告的順序確定。 在本例中有兩個內嵌物件成員,雖然在派生類建構函式首部,內嵌物件成員 auditor2 的建構函式寫在 auditor1 的前面,但是呼叫順序還是先執行 auditor1 的建構函式。
如果派生類的基類也是一個派生類,每個派生類只需負責其直接基類資料成員的初始化,依次上溯。