1. 程式人生 > >派生類的建構函式和解構函式(C++學習筆記 32)

派生類的建構函式和解構函式(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 的建構函式。
  如果派生類的基類也是一個派生類,每個派生類只需負責其直接基類資料成員的初始化,依次上溯。