C++ primer 摘要《多繼承、虛擬繼承》20090203

===18.2 多繼承===
基類構造函數被調用的順序以類派生表中聲明的順序爲準
private繼承的話,原來父類的public ,protected成員繼承到子類中,併成爲private類型成員;
protected繼承的話,原來父類的public ,protected成員繼承到子類中,併成爲protected類型成員;
public繼承的話,原來父類的public ,protected成員繼承到子類中,類型不變

===18.3.2 免除exempting 個別成員的私有繼承影響===
class PeekbackStack : private IntArray {
public:
    // 維持公有訪問級別
    using IntArray::size;
    // ...
};
派生類只能將繼承得到的成員恢復到原來的訪問級別,該訪問級別不能比基類中原來指定的級別更嚴格或更不嚴格

class Engine {
 public:
   Engine(int numCylinders){}
   void start();                 // Starts this Engine
 };
void Engine::start()
{
 cout<<"start!"<<endl;
}

class Car : private Engine {    // Car has-a Engine
 public:
   Car() : Engine(8) { }
   void run()
   {
    start(); //ok
   }
 };

Car car;
car.start();//錯誤start私有
car.run();


class Car : private Engine {    // Car has-a Engine
 public:
   Car() : Engine(8) { }
   using Engine::start;          //將start設爲原有的公有訪問級別
 };
Car car;
car.start();//ok

===18.4 繼承下的類域===
1、在繼承下,派生類的域被嵌套在直接基類的域中,如果一個名字在派生類域中沒有被解析出來,則編譯器在外圍基類域中查找該名字的定義
2、名字解析的實際過程:
局部域->派生類->基類->全局域
編譯器總是先解析一個類成員然後再判斷該訪問是否合法
class ZooAnimal {
private:
    double dval;
};
class Bear : public ZooAnimal {
};

int dval;
int Bear::mumble( int ival )
{
    // 錯誤: 解析爲 ZooAnimal::dval 的私有成員函數
    return ival + dval;
}

3、解析過程中,找到了名字則停止查找
int ival;
int Bear::mumble( int ival )
{
    return ival + // 參數實例
    ::ival + // 全局實例
    ZooAnimal::ival +
    Bear::ival;
}
對ival 未限定修飾的引用被解析爲形式參數實例,如果mumble()中沒有定義ival 則訪問
Bear 的ival 成員實例,如果在類Bear 中也沒有定義ival 則訪問ZooAnimal 的ival 成員實例,
如果在ZooAnimal 類中也沒有定義ival 則訪問全局的ival 實例

===18.4.1 多繼承下的類域===
1、二義性問題:
class Endangered {
public:
    ostream&print( ostream&) const;
    void highlight();
    // ...
};
class ZooAnimal {
public:
    bool onExhibit() const;
    // ...
private:
    bool highlight( int zoo_location );
    // ...
};
class Bear : public ZooAnimal {
public:
    ostream&print( ostream&) const;
    void dance( dance_type ) const;
    // ...
};
當用多繼承派生Panda 類時
class Panda : public Bear, public Endangered {
public:
    void cuddle() const;
    // ...
};
雖然從Bear 和Endangered 基類中繼承print()和highlight()都存在潛在的二義性
但是在真正引用這些函數之前編譯器並不會報告二義錯誤
print()成員的二義性是顯然的,但是在兩個highlight()之間的衝突有些令人喫驚,畢竟兩個實例有不同的訪問級別和不同的函數原型
在多繼承下,查找過程對每個基類的繼承子樹***同時***進行檢查

2、解決辦法:
a、在程序層次上,解決成員二義性的方案是用類域操作符顯式地限定修飾期望被調用的實例==>這種方法不推薦
b、在提供預期行爲的派生類中定義一個同名實例(即在派生類中重新定義好有二義性的函數)

===18.5 虛擬繼承===
在虛擬繼承下只有一個共享的基類子對象被繼承,如iostream對ios的繼承,
而無論該基類在派生層次中出現多少次。共享的基類子對象被稱爲虛擬基類virtual base class


===18.5.1 虛擬基類聲明===
下列聲明使得ZooAnimal 成爲Bear 和Raccoon 的虛擬基類
// 關鍵字 public 和 virtual的順序不重要
class Bear : public virtual ZooAnimal { ... };
class Raccoon : virtual public ZooAnimal { ... };
===18.5.2 特殊的初始化語義===
在非虛擬派生中派生類只能顯式初始化其***直接基類***
然而在虛擬派生中,只有Panda 可以直接調用其ZooAnimal 虛擬基類的構造函數,繼承關係見圖


虛擬基類的初始化變成了最終派生類most derived class 的責任,這個最終派生類是由每個特定類對象的聲明來決定的
Bear winnie( "pooh" );//Bear爲最終派生類,ZooAnimal構造由它負責
Raccoon meeko( "meeko" );//Raccoon爲最終派生類,ZooAnimal構造由它負責
Panda yolo( "yolo" );//Panda爲最終派生類,ZooAnimal構造由它負責,中間類Bear、Raccoon對虛擬基類構造函數的調用被自動抑制
如果Panda 的構造函數沒有顯式地爲ZooAnimal 構造函數指定實參,則發生下面兩個動
作之一,調用ZooAnimal 的缺省構造函數,或者如果沒有缺省構造函數,則編譯器在編譯
Panda 構造函數的定義時會給出一個錯誤消息
===18.5.3 構造函數與析構函數順序===
無論虛擬基類出現在繼承層次中的哪個位置上,它們都是在非虛擬基類之前被構造
class Character { ... };
class BookCharacter : public Character { ... };
class Bear : public virtual ZooAnimal { ... };
class ToyAnimal { ... };
class TeddyBear : public BookCharacter, public Bear, public virtual ToyAnimal
{ ... };

已知聲明
TeddyBear Paddington;

基類構造函數的調用順序如下:
ZooAnimal(); // Bear 的虛擬基類
ToyAnimal(); // 直接虛擬基類
Character(); // BookCharacter 的非虛擬基類
BookCharacter(); // 直接非虛擬基類
Bear(); // 直接非虛擬基類
TeddyBear(); // 最終派生類

===18.5.4 虛擬基類成員的可視性===
在虛擬派生下,對於虛擬基類成員的繼承,比該成員後來重新定義的實例的權值小
// ok: 在虛擬繼承下沒有二義
// 調用 Bear::onExhibit(),不會調用虛擬基類的ZooAnimal::onExhibit()
yolo.onExhibit();

 

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