假設有一個問題,類似于鴨子這樣的動物有很多種,如企鵝和魷魚,它們也可能會有一些共同的特性。例如,我們可以有一個叫做 AquaticBird (涉禽,水鳥的一類)的類,它又繼承自 Animal 和 SwimmingAnimal,就像這樣:
class AquaticBird : public Animal, public SwimmingAnimal { // AquaticBird類既是Animal也是SwimmingAnimal
};
如果我們同時繼承了這兩個類,像下圖所示,將發生菱形繼承:
Animal / \
FlyingAnimal AquaticBird \ / Duck
在這個結構中,Duck 既繼承了 FlyingAnimal,也間接繼承了 Animal 通過 AquaticBird 的方式。問題在于,如果我們試圖調用 Animal 類的方法,例如 eat(),Duck 會造成二義性:它不清楚是通過哪個路徑來調用 Animal 類的實例。
那么如何解決菱形繼承問題呢
解決菱形繼承問題
通過使用?虛繼承,我們可以確保在最終的?Duck?類中,只有一個?Animal?的實例。
class Animal {
public: void eat() { std::cout << "Eating food." << std::endl; }
};
class FlyingAnimal : virtual public Animal { // 使用virtual
public: void fly() { std::cout << "Flying." << std::endl; }
};
class SwimmingAnimal : virtual public Animal { // 使用virtual
public: void swim() { std::cout << "Swimming." << std::endl; }
};
class Duck : public FlyingAnimal, public SwimmingAnimal { // Duck類繼承自FlyingAnimal和SwimmingAnimal
};
現在,當?Duck?類調用?eat()?方法時,編譯器知道只有一份?Animal?的實例,從而避免了二義性。
那么虛繼承的底層實現原理是什么呢?
g++ -fdump -class-hlerarchy *.cpp gcc8.0之前
g++ fdump -lang - class *.cpp gcc8.0及以后
通過虛表指針偏移來實現虛繼承
父類的vptr都有到共同基類的偏移量,從而讓子類多繼承是指向同一個“父類的父類”
只有C++有菱形繼承
一般不使用菱形繼承,但在庫里面使用了
如下所示,下面的箭頭都是繼承,我們就會發現中間出現了菱形繼承,iostream繼承了istream和ostream。