之前的兩篇文章,我們已經了解了c++中繼承的一些基本知識,也探索了初始化派生類的次序。本文主要是更深入的了解建構函式所扮演的角色(初始化乙個派生類時)。我們繼續沿用上篇文章的例子。
class base};
class derived: public base
};
初始化非派生類時,建構函式只需要關心它自己的成員。例如,
int main()
當cbase被例項化時,發生如下事件:
1.為cbase預留記憶體
2.合適的base建構函式被呼叫
3.初始化列表對變數進行初始化
4.執行建構函式的函式體
5.從呼叫出返回
這個過程非常的直觀。但是換成初始化乙個派生類時,事情變得稍微複雜點:
int main()
當cderived被例項化時,發生了如下事情:
1.為cderived預留足夠的記憶體(包括base部分以及derived部分)
2.合適的派生類建構函式被呼叫
3.base物件被首先構造通過合適的base建構函式
4.初始化列表對變數進行初始化
5.執行建構函式的函式體
6.從呼叫處返回
兩者的唯一區別是例項化派生類時,基類的建構函式被先呼叫。base建構函式建立物件的基類部分,然後返回到派生類的建構函式,接著派生類建構函式被允許執行它的工作。
初始化基類成員
當建立乙個派生類物件時,我們該如何同時初始化m_dvalue(派生類derived的成員變數)和m_nvalue(基類base的成員變數)?
新程式設計師可能打算通過下列方法才解決問題:
class derived: public base
};
這是個好的設想,點子是對的。我們要明確的給建構函式加上乙個引數,否則c++將無法知道該給m_nvalue初始化為何值。
然而,c++不允許在建構函式的初始化列表中對基類成員進行初始化。換句話說,建構函式的初始化列表只能初始化屬於自己的成員變數。
那c++為什麼要這樣做呢?答案和const以及reference變數有關。設想下,如果m_nvalue是乙個const變數,將發生什麼。因為const變數必須被初始化當它被建立的時候,基類建構函式必須給它賦值。然而,當基類建構函式執行完後,派生類的建構函式初始化列表會被執行。如果這樣的話,每乙個派生類的都有可能初始化這個變數並可能改變這個值。
所以上述的例子無法工作,因為m_nvalue來自base基類,而只有非繼承的變數才能在初始化列表中被改變。
有些新程式設計師可能會使用如下方法來初始化基類成員變數:
class derived: public base
};
當m_nvalue是非const變數時,上面的辦法可行,但當m_nvalue為const變數事,則行不通。因為m_nvalue被賦值了2次:1次實在base基類的成員初始化列表中,1次是在派生類的建構函式體內。
慶幸的是,c++賦予我們選擇執行哪個基類建構函式的的能力,通過在派生類初始化成員列表中呼叫指定基類建構函式來實現:
class derived: public base
};
測試**如下:
int main()
基類建構函式base(int)將被用來初始化m_nvalue為5,派生類建構函式將m_dvalue初始化為1.3.
更詳細的,發生了如下事情:
1.分配記憶體給cderived
2.derived(double, int) 建構函式被呼叫,dvalue=1.3, nvalue=5
3.編譯器會檢查我們是否要求執行指定基類建構函式。我們指定了!所以將呼叫 base(int)引數值為5
4.基類建構函式初始化列表給m_nvalue賦值5
5.執行基類建構函式體
6.基類建構函式返回
7.派生類建構函式初始化列表給m_dvalue賦值1.3
8.執行派生類建構函式體
9.派生類建構函式返回
上面列出了9條,看起來挺複雜的,其實很簡單。發生的所有事情就是派生類構造函式呼叫了指定的基類建構函式來初始化派生類物件的基類部分。因為m_nvalue存在於物件的基類部分,基類建構函式是唯一能對其進行初始化的。
來看看另外乙個例項:
#include class person
int getage()
bool ismale()
person(std::string strname = "", int nage = 0, bool bismale = false)
: m_strname(strname), m_nage(nage), m_bismale(bismale)
}; // baseballplayer publicly inheriting person
class baseballplayer : public person
};
baseballplayer類只初始化了自己的成員,並沒有呼叫指定的person建構函式,這意味著每個baseballplayer將使用預設person建構函式,它初始化name為空,age為0。這並不符合我們的意圖,修改後的baseballplayer類如下:
// baseballplayer publicly inheriting person
class baseballplayer : public person
};
測試**如下:
int main()
更詳細的測試**如下:
int main()
結果:
pedro cerrano
3242
繼承鏈
例項:
#include using namespace std;
class a};
class b: public a};
class c: public b};
int main()
結果:
a: 5
b: 4.3
c: r
析構函式
當乙個派生類被銷毀時,析構函式的呼叫次序與建構函式完全相反。在上面的例子中,如果cclass被銷毀,c的析構函式被最先呼叫,然後是b的,最後是a的。你可以自己寫測試程式來驗證。
(35 1)派生類的建構函式
建構函式和類的名字是一致的 派生類名 形式引數列表 基類名 基類建構函式實參列表 派生類初始化列表 class point 建構函式 class rect public point 派生類建構函式 類名 形式引數列表 子物件名 子物件建構函式實參列表 類初始化列表 classa a類沒有建構函式,系...
19 派生類的建構函式
class bug class flyclass public bug bug bug int legs,int color flybug flybug int legs,int color,int wings 正確的flybug建構函式 flybug flybug int legs,int col...
1 4 3 7 派生類的構造方法
根據改寫。加入自己見解使其更易懂 如下 功能 講述上圖派生類建構函式的執行順序 包含各層次類都靜態構造 例項建構函式 public class mybaseclass public mybaseclass 5.第乙個類的例項建構函式執行 public class mydrivedclass myba...