5、继承与派生4-派生类的构造和析构函数
派生類的構造函數只負責對派生類新增的成員進行初始化,對所有從基類繼承下來成員,其初始化工作還是由基類的構造函數完成。同樣,對派生類對象的掃尾、清理工作也需要加入新的析構函數。
1、構造函數
構造派生類的對象時,就要對基類數據成員、新增數據成員和成員對象的數據成員進行初始化。派生類的構造函數需要以合適的初值作為參數,其中一些參數要用于對派生類新增的成員進行初始化,另一些參數要分別傳遞給基類的構造函數和對象成員的構造函數。用于初始化相應的成員。
在構造派生類的對象時,會首先隱含調用基類和內嵌對象成員的構造函數,來初始化他們各自的數據成員,然后才執行派生類構造函數的函數體。
派生類構造函數的一般語法形式為:
派生類名::派生類名(參數總表):基類名1(參數表1),...,基類名n(參數表n),
內嵌對象名1(內嵌對象參數表1),...,內嵌對象名m(內嵌對象參數表m)
{
派生類新增成員的初始化語句;
}
這里的派生類的構造函數名與類名相同。在生成派生類對象時,系統首先會使用這里列出的參數,調用基類和內嵌對象成員的構造函數。
下面來討論什么時候需要聲明派生類的構造函數。如果基類聲明了帶有形參表的構造函數時,派生類就應當聲明構造函數,提供一個將參數傳遞給基類構造函數的途徑,保證在基類進行初始化時能夠獲得必要的數據。
派生類構造函數執行的一般次序如下:
1)調用基類構造函數,調用順序按照他們被繼承時聲明的順序(從左向右)
2)調用內嵌成員對象的構造函數,調用順序按照他們在類中聲明的順序
3)派生類的構造函數體中的內容。
其中,如果派生類中新增成員中有內嵌對象,第二步的調用才會執行。
eg:派生類構造函數舉例(多繼承,含有內嵌對象)
這是一個具有一般性特征的例子,有三個基類B1、B2和B3,其中B3只有一個默認的構造函數,其余兩個基類的成員只有一個帶有參數的構造函數。類c由這三個基類經過公有派生而來。派生類新增加了三個私有對象成員,分別是B1、B2和B3類的對象,如下
#include<iostream>
using namespace std;
class B1
{
public:
B1(int i){cout<<"constructing B1"<<i<<endl;}
};
class B2
{
public:
B2(int j){cout<<"constructing B2"<<j<<endl;}
};
class B3
{
public:
B3(){cout<<"constructing B3 *"<<endl;}
};
class C:public B2,public B1,public B3 ? //派生新類C,注意基類名的順序
{
public:
C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){}
//注意基類名的個數與順序;成員對象名的個數與順序
private:
B1 memberB1;
B2 memberB2;
B3 memeberB3;
};
int main()
{
C obj(1,2,3,4);
}
因為基類及內嵌對象成員都具有非默認形式的構造函數,所以派生類中需要聲明一個非默認形式(即帶參數)的構造函數。這個派生類構造函數的主要功能就是初始化基類及內嵌對象成員。需要注意的是:首先,這里并沒有列出全部基類和成員對象,由于B3類只有默認構造函數,不需要給它傳遞參數,因此基類B3以及B3類成員對象memberB3就不必列出,但是系統還是會自動調用該類的默認構造函數。其次,基類名和成員對象名的順序是隨意的。這個派生類的構造函數的函數體為空,可見實際上只是起到了傳遞參數和調用基類及內嵌對象的構造函數的作用。
2、拷貝構造函數
若建立派生類對象時調用默認拷貝構造函數,則編譯器將自動調用基類的拷貝構造函數。
如果要為派生類編寫拷貝構造函數,則需要為基類相應的拷貝構造函數傳遞參數。如:C類是B類的派生類,C類的拷貝構造函數形式如下:
C::C(C &c1):B(c1)
{...}
這里B的拷貝構造函數的引用使用c1,這是由于類型兼容規則的原因,可以用派生類的引用去初始化基類的引用。
3、析構函數
派生類的析構函數的功能是在該類對象消亡之前進行一些必要的清理工作。它沒有類型也沒有參數。
派生類析構函數的聲明方法與沒有繼承關系的類中析構函數的聲明方法完全相同,只要在函數體中負責把派生類新增的非對象成員的清理工作做好就夠了,系統會自己調用基類及對象成員的析構函數來對基類及對象成員進行清理。但他的執行次序和構造函數正好嚴格相反,首先對派生類新增普通成員進行清理,然后對派生類新增的對象成員進行清理;最后對所有從基類繼承來的成員進行清理。這些清理工作分別是執行派生類析構函數體,調用派生類對象成員所在類的析構函數和調用基類析構函數。
eg:派生類析構函數舉例(多繼承、含有嵌入對象)
#include<iostream>
using namespace std;
class B1
{
public:
B1(int i){cout<<"constructing B1"<<i<<endl;}//B1的構造函數
~B1(){cout<<"destructing B1"<<endl;}//B1的析構函數
};
class B2
{
public:
B2(int j){cout<<"constructing B2"<<j<<endl;}//B2的構造函數
~B2(){cout<<"destructing B2"<<endl;}//B2的析構函數
};
class B3
{
public:
B3(){cout<<"constructing B3 *"<<endl;}//B3的構造函數
~B3(){cout<<"destructing B3"<<endl;}//B3的析構函數
};
class C:public B2,public B1,public B3 ? //派生類C定義
{
public:
C(int a,int b,int c,int d):B1(a),memberB2(d),memberB1(c),B2(b){}//派生類的構造函數定義
private:
B1 memeberB1;
B2 memberB2;
B3 memberB3;
};
int main()
{
C obj(1,2,3,4);
}
結果如下
程序中,我們給三個基類分別加入了析構函數,派生類沒有做任何改動,仍然使用的是由系統提供的默認析構函數。主函數也保持原樣。程序在執行時,首先執行派生類的構造函數,然后執行派生類的析構函數。派生類默認的析構函數有分別調用了成員對象及基類的析構函數。
?
轉載于:https://www.cnblogs.com/gary-guo/p/6233652.html
總結
以上是生活随笔為你收集整理的5、继承与派生4-派生类的构造和析构函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vue.js几行实现的简单的todo l
- 下一篇: bind绑定参数