C++ Primer第五版筆記——多重繼承與虛繼承

多重繼承

在派生類的派生列表中可以包含多個基類,以逗號分隔:

class Panda : public Bear,public Endangered

和單繼承一樣,多重繼承的派生列表也只能包含已經被定義過的類,而且這些類不能是final(類被final修飾,不能被繼承)的。

派生類構造函數初始化所有基類
  構造一個派生類對象的同時構造並初始化它的所有基類子對象,還是以逗號來分隔:

//顯示的初始化所有基類
Panda::Panda(string s1,string s2)
			:Bear(s1),Endangered(s2){}

派生類的構造函數初始化列表將實參分別傳遞給每個直接基類,其中基類的構造順序與派生類列表中基類的出現順序一致,而與派生類構造函數初始值列表中的基類的順序無關

繼承的構造函數與多重繼承
  在C++11新標準中,允許派生類從它的一個或幾個基類中繼承構造函數,但是如果從多個基類中繼承了相同的構造函數,程序將產生錯誤:

struct Base1{
	Base1() = default;
	Base1(const string&);
	Base1(shared_ptr<int>);
};

struct Base2{
	Base2() = default;
	Base2(const string&);
	Base2(int);
};
//錯誤,D1試圖從兩個基類中都繼承D1::D1(const string&)
struct D1 : public Base1,public Base2{
	using Base1::Base1;
	using Base2::Base2;
};
//如果一個類從它的多個基類中都繼承了相同的構造函數,那它必須定義自己的版本
struct D2 : public Base1,public Base2{
	using Base1::Base1;
	using Base2::Base2;
	D2(const string& s):Base1(s),Base2(s){}
};

類型轉換與多個基類

在只有一個基類的情況下,派生類的指針和引用可以自動轉換成一個可以訪問基類的指針或引用。多個基類的情況與之類似,可以令某個訪問基類的指針或引用直接指向一個派生類對象。
  但是編譯器不會在派生類向基類的幾種轉換中進行比較和選擇,在它看來轉換到任一基類都是一樣好的,因此如果兩個基類中存在同名的重載形式時,可能會有二義性錯誤,例如:

//Bear和Endangered類都是Panda的基類
void print(Bear&);
void print(Endangered&);

Panda p("tt");
print(p);				//二義性錯誤

與一個基類的繼承一樣,對象、指針或引用的靜態類型決定了能夠使用的成員,例如通過基類1的指針訪問派生類的對象時,只能使用屬於基類1的接口與屬性,派生類的特有部分和其他基類的部分都是不可見的。


多重繼承下的類作用域

在只有一個基類的情況下,派生類的作用域嵌套在直接基類和間接基類的作用域中,查找過程沿着繼承體系自底向上進行,知道找到所需名字,派生類的名字將隱藏基類的同名成員。
  在多重繼承下,相同的查找過程在所有直接基類中同時進行。如果名字在多個基類中都被找到,則不加前綴限定符直接使用該名字將引發二義性。


虛繼承

虛繼承的目的是令某個類做出聲明,承諾願意共享它的基類。其中共享的基類子對象被稱爲虛基類,不論虛基類在繼承體系下被繼承多少次,在派生類中都只包含唯一一個共享的虛基類子對象(菱形繼承)。

使用虛基類
  指定虛基類的方式是在派生列表中添加關鍵字virtual:

//public和virtual的順序隨意
class Raccon : public virtual ZooAnimal{};
class Bear : virtual public ZooAnimal{};

class Panda : public Raccon,public Bear{};

因爲Raccon和Bear繼承ZooAnimal的方式都是虛繼承,因此在Panda中只有一個ZooAnimal基類部分。

虛基類成員的可見性
  因爲在每個共享的虛基類中只有唯一一個共享的子對象,所以該基類的成員可以被直接訪問,並且不會產生二義性。
  例如:類B有一個成員名爲x,類D1和D2是通過虛繼承的方式繼承自B,類D通過多重繼承繼承D1和D2,如果通過D來訪問x,則可能的情況有:
  1.在D1和D2中都沒有對x的定義,則x將被解析爲B的成員,此時不存在二義性;
  2.在D1或D2中有對x的定義,則x將被解析成D1或D2的成員,其優先級高於虛基類,同樣沒有二義性;
  3.在D1和D2中都有對x的定義,直接訪問x將產生二義性。


構造函數與虛繼承

在虛派生中,虛基類是由最低層的派生類初始化的
  爲理解這一規則,不妨假設以普通規則來處理初始化任務時會出現什麼情況:虛基類將在多條路徑下被重複初始化。
  當然繼承體系中的每個類都可能在某個時刻成爲“最低層的派生類”,只要能創建虛基類的派生類對象,該派生類的構造函數就必須初始化它的虛基類。例如:B是基類,D1和D2虛繼承自B,D繼承自D1和D2,當創建D1的對象時,有D1的構造函數直接初始化其基類B的部分;當創建D的對象時,D來負責初始化B基類部分,即使B不是D的直接基類。

虛繼承的對象的構造方式
  含有虛基類的對象的構造順序與一般的順序稍有區別:首先使用提供給最底層派生類構造函數的初始值初始化該對象的虛基類子部分,接下來按照基類在派生列表中出現的順序依次對其進行初始化。
  如果派生類沒有顯示的初始化虛基類,則虛基類的默認構造函數將被調用,如果其沒有默認構造函數,則代碼將發生錯誤。
  如果有多個虛基類,那麼這些虛的子對象按照他們在派生列表中出現的順序來進行構造,例如:

class B{};
class D1:virtual public B{};
class D2{};
class D3{};

class D : public D2,public D1,virtual public D3{}

以上進行構造的順序應該是先構造B,再構造D3,再是D2,最後是D1。
析構函數的順序相反。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章