仮想継承

仮想継承（かそうけいしょう、）とは、C++プログラミング言語における継承の一種で、多重継承によって生じる問題（菱形継承問題）を解決するもの。どの上位クラスのメンバーを使うか曖昧さが生じる部分で、明確化する。仮想継承は、部分の合成という意味での継承よりも、継承が上位クラスへの制限を表している場合に使われる。多重継承の基底クラスは、codice_1 というキーワードで仮想継承であることを指定される。
問題.
次のようなクラス階層を考える。
class Animal 
 public:
 virtual void eat();
class Mammal : public Animal 
 public:
 virtual Color getHairColor();
class WingedAnimal : public Animal 
 public:
 virtual void flap();
// Bat（こうもり）は翼のある哺乳類（winged mammal）である
class Bat : public Mammal, public WingedAnimal {};
Bat bat;
codice_2 の codice_3 メソッドを呼び出した場合、どうなるだろうか? 上の宣言では codice_4 の呼び出しは曖昧である。codice_5 なのか codice_6 なのか判らない。問題は、いわゆる多重継承の意味論が現実をモデル化したものではない点にある。感覚的に言えば、codice_7 が codice_7 でしかないように、codice_9 は codice_10 であると同時に codice_11 である。しかしcodice_9（こうもり）のcodice_10（哺乳類）性におけるcodice_7（動物）性は、codice_11（翼のある動物）性におけるcodice_7（動物）性と同じである。
このような状況を菱形継承と呼び、これを解決するのが仮想継承である。
クラス表現.
仮想継承を解説する前に、C++での継承におけるクラス表現を簡単に説明しておく。継承は、メモリ上では親クラスの後に子クラスを置いたリストで表される。上記の Bat の場合、(Animal, Mammal, Animal, WingedAnimal, Bat) となり、Animal が二度出現しているため、曖昧さが生じる。
解決策.
クラス宣言を次のように修正する。
// 2つのクラスは Animal を仮想継承する
class Mammal : public virtual Animal 
 public:
 virtual Color getHairColor();
class WingedAnimal : public virtual Animal 
 public:
 virtual void flap();
// こうもりは依然として翼のある哺乳類である
class Bat : public Mammal, public WingedAnimal {};
これで、codice_17 の codice_7 部分は、codice_19 で使われている codice_7 と同一になり、つまり1つの codice_9 インスタンスには1つの codice_7 だけが対応することになって、codice_23 呼び出しが曖昧でなくなる。
これは、codice_10 と codice_11 にvtableポインタを伴うような実装になる。つまり、codice_10 の先頭と codice_7 部分の先頭とのメモリオフセットは実行時まで不明となる。従って、Bat は (vtable*, Mammal, vtable*, WingedAnimal, Bat, Animal) となる。1つのオブジェクトに2つの vtable ポインタがあるため、そのぶんだけオブジェクトのサイズが大きくなるが、Animal は1つだけになるので曖昧さがなくなる。2つの vtable ポインタは Mammal と WingedAnimal それぞれの Animal の仮想継承に対応して存在する。Bat 型の全オブジェクトの vtable * の値は同じだが、Bat 型オブジェクトはそれぞれユニークな Animal オブジェクトを内包する。別のクラス、例えば Squirrel（リス）が Mammal を継承する場合、Squirrel における Mammal オブジェクトの vtable* は Bat の場合とは異なる。ただし、オブジェクトにおける Bat 部分と Squirrel 部分が同じサイズであれば、vtable* は同じになる。仮想関数テーブルは実際には同一ということはないが、基本的に距離（オフセット）を格納しているという点で同じである。