天天看點

C++_繼承

1、什麼是繼承?

    繼承機制是面向對象程式設計使代碼可以複用的最重要的手段,它允許程式員在保持原有類特性的基礎上進行擴充,增加功能,這樣産生新的類,稱派生類。繼承呈現了面向對象程式設計的層次結構,展現了由簡單到複雜的認知過程。繼承是類設計層次的複用。

1.1、struct在C語言與C++中的差別?

    C語言中:struct是使用者自定義資料類型,是一種資料結構的是實作體。

    C++中:struct是抽象資料類型,支援成員函數的定義,是一種對象的實作體。

1.2、C++中class和struct的差別?
  1. C++中的struct其實等同于類,隻是class的預設通路權限是private,而struct的預設通路權限是public。(C++要相容C語言)
  2. class預設繼承權限是private,struct預設繼承權限是public。
  3. 在使用模闆的時候

    template <class T>

    /

    template<typename T>

    ,不能使用

    template<struct T>

2、指派相容規則

    public繼承方式:is-a,可以将一個子類對象看成是一個基類對象,在類外所有使用基類對象的位置都可以使用子類對象代替。

    對象模型:對象中各個成員變量在記憶體中的布局方式。

C++_繼承
class Person
{
protected :
	string _name; // 姓名
	string _sex;  // 性别
	int _age; // 年齡
};
class Student : public Person
{
public :
	int _No ; // 學号
};
void Test ()
{
	Student sobj ;
	// 1.子類對象可以指派給父類對象/指針/引用
	Person pobj = sobj ;
	Person* pp = &sobj;
	Person& rp = sobj;
	//2.基類對象不能指派給派生類對象
	sobj = pobj;
	// 3.基類的指針可以通過強制類型轉換指派給派生類的指針
	pp = &sobj
	Student* ps1 = (Student*)pp; // 這種情況轉換時可以的。
	ps1->_No = 10;
	pp = &pobj;
	Student* ps2 = (Student*)pp; // 這種情況轉換時雖然可以,但是會存在越界通路的問題
	ps2->_No = 10; 
}
           
  1. 可以使用子類對象給基類對象來進行指派,反之不行;
  2. 可以使用基類的指針或引用指向子類的對象;
  3. 不能使用子類的指針或引用指向基類的對象。

3、同名隐藏

    在繼承體系中,子類和基類具有相同名稱的成員(成員變量||成員函數),如果使用子類對象通路相同名稱的成員,優先通路的是子類自己的,基類相同名稱的成員無法通路到。

注意:

    >>成員函數:與函數原型是否相同沒有關系;

    >>成員變量:與成員變量的類型是否相同沒有關系。

在繼承體系中子類對象構造過程:

  1. 基類如果沒有定義任何構造函數,則子類可以定義也可以不定義;
  2. 如果基類顯式定義了無參的構造函數||全預設的構造函數,則子類可以根據自己需求選擇給出對應構造函數。即:可以定義也可以不定義,如果子類沒有顯式定義時,則編譯器可以替使用者實作一個預設的構造函數,目的:将子類對象中從基類繼承下來的成員構造完成。
  3. 如果基類顯式定義了構造函數,并且構造函數不是無參||全預設的構造函數,則子類必須要顯式定義自己的構造函數,然後需要在其構造函數初始化清單的位置顯式調用基類構造函數完成基類部分成員的初始化,否則代碼就會編譯失敗
  4. 子類對象構造和析構的過程:

    構造:先調用基類構造函數,然後調用子類構造;

    析構:先調用子類的析構函數,然後再調用基類的析構函數。

    注意:列印結構并不能代表函數的執行次序。

    構造函數:建立哪個類的對象,調用哪個類的構造函數,先要執行初始化清單完成對象中成員的初始化,然後再去執行構造函數中的其它代碼。

    析構函數:銷毀哪個類的對象,調用哪個類的析構函數。編譯器會在子類析構函數最後一條有效語句之後,插入一條彙編語句,call 基類的析構函數。

4、菱形繼承

單繼承:一個子類隻有一個直接父類

C++_繼承

多繼承:一個子類有兩個或者以上直接父類

C++_繼承

菱形繼承:菱形繼承是多繼承的一種特殊情況

C++_繼承

菱形繼承的問題:從下面的對象模型構造,可以看出菱形繼承有資料備援和二義性的問題,在Assistant的對象中Person成員會有兩份。

C++_繼承
4.1、菱形繼承的二義性問題
C++_繼承
  1. 讓通路明确化;d.C1::_b = 1;
  2. 讓最頂層基類中成員在子類中隻存儲一份(菱形虛拟繼承)
4.2、虛拟繼承:

    注意:是在C1和C2繼承中添加virtual關鍵字。

C++_繼承
C++_繼承

虛基表中存放了:1、該子類對象相對于自己的偏移量;2、該子類對象相對于基類部分成員起始位置的偏移量。

  1. 對象中多了4個位元組;
  2. 如果使用者沒有顯式定義構造函數,則編譯器會給子類生成一份預設的構造函數;

    或者如果子類顯式定義了構造函數,則編譯器會對子類構造函數進行修改(向對象前4個位元組中填充資料)

  3. 對象模型布局方式不一樣。

    下圖是菱形虛拟繼承的記憶體對象成員模型:這裡可以分析出D對象中将A對象放到了對象組成的最下面,下面這個A同時屬于B和C,那麼B和C是如何找到公共的A的呢?這裡是通過了B和C的兩個指針,指向的一張表。這兩個指針叫虛基表指針,這兩個表叫做虛基表,虛基表中存的偏移量,通過這個偏移量可以找到下面的A。

C++_繼承

下面是上面的Person關系菱形虛拟繼承的原了解釋:

C++_繼承

5、繼承群組合

  • public繼承是一種 is-a 的關系,也就是說每個派生類對象都是一個基類對象;
  • 組合是一種 has-a 的關系,假設B組合了A,每個B對象中都有一個A對象。
  • 優先使用對象組合,而不是類繼承。
  • 繼承允許你根據基類的實作來定義派生類的實作。這種通過生成派生類的複用通常被稱為白箱複用。術語“白箱”是相對可視性而言:在繼承方式中,基類的内部細節對子類可見。繼承一定程度破壞了基類的封裝,基類的改變,對派生類有很大的影響。派生類和基類間的依賴關系很強,耦合度很高。
  • 對象組合是類繼承之外的另一種複用選擇。新的更複雜的功能可以通過組裝或組合對象來獲得。對象組合要求被組合的對象具有良好定義的接口。這種複用風格被稱為黑箱複用,因為對象的内部細節是不可見的。對象隻以“黑箱”的形式出現。組合類之間沒有很強的依賴關系,耦合度低。優先使用對象組合有助于你保持每個類被封裝。

繼續閱讀