概要#
普通の継承は、基底クラスを構築してから派生クラスを構築すると言えますが、仮想継承は基底クラスを派生クラスに一度だけ埋め込み、仮想基底クラスが一度だけ構築されるようにします。
基底クラスのコンストラクタに "constructor" という出力文が含まれている場合、仮想継承と普通の継承では出力結果が異なります。
以下のサンプルコードを考えてみましょう:
#include <iostream>
class Base {
public:
Base() {
std::cout << "constructor" << std::endl;
}
};
class Derived : public virtual Base {
public:
Derived() : Base() {
}
};
int main() {
Derived d;
return 0;
}
上記のコードでは、Derived
クラスが仮想継承を使用してBase
クラスを継承し、コンストラクタで基底クラスのコンストラクタを呼び出しています。このコードを実行すると、"constructor" が一度だけ出力されることがわかります。これは、仮想継承によって仮想基底クラス(ここではBase
クラス)が一度だけ構築されることを保証しているためです。多重継承の場合でも同様です。したがって、派生クラスDerived
のコンストラクタでは、基底クラスBase
のコンストラクタが一度だけ呼び出されます。
次に、普通の継承を使用する場合を考えてみましょう:
#include <iostream>
class Base {
public:
Base() {
std::cout << "constructor" << std::endl;
}
};
class Derived : public Base {
public:
Derived() : Base() {
}
};
int main() {
Derived d;
return 0;
}
この例では、Derived
クラスが普通の継承を使用してBase
クラスを継承し、コンストラクタで基底クラスのコンストラクタを呼び出しています。このコードを実行すると、"constructor" が 2 回出力されます。1 回目は基底クラスのコンストラクタ呼び出しによるものであり、2 回目は派生クラスのコンストラクタ呼び出しによるものです。
したがって、仮想継承は仮想基底クラスが一度だけ構築されることを保証し、普通の継承では各継承階層で基底クラスのコンストラクタが呼び出されます。これは、仮想継承と普通の継承のコンストラクタ呼び出しの違いです。
仮想継承の場合、一度の "constructor" 出力は最終派生クラスのコンストラクタが基底クラスのコンストラクタを呼び出すことによるものです。
以下のサンプルコードを考えてみましょう:
#include <iostream>
class Base {
public:
Base() {
std::cout << "constructor" << std::endl;
}
};
class Intermediate : public virtual Base {
public:
Intermediate() : Base() {
}
};
class Derived : public Intermediate {
public:
Derived() : Intermediate() {
}
};
int main() {
Derived d;
return 0;
}
この例では、Intermediate
という中間クラスが存在し、仮想継承を使用してBase
クラスを継承しています。派生クラスDerived
はIntermediate
クラスを継承し、間接的にBase
クラスを仮想継承しています。
このコードを実行すると、"constructor" が一度だけ出力されます。この出力は、最終派生クラスDerived
のコンストラクタがIntermediate
クラスのコンストラクタを呼び出し、Intermediate
クラスのコンストラクタが基底クラスBase
のコンストラクタを呼び出すためです。
したがって、仮想継承では一度の "constructor" 出力は最終派生クラスのコンストラクタが仮想基底クラスのコンストラクタを呼び出すことによるものです。これにより、仮想基底クラスが一度だけ構築されることが保証されます。
普通の継承では、派生クラスのオブジェクトを構築する際にはまず基底クラスの部分が構築され、それに基づいて派生クラスの部分が構築されます。つまり、基底クラスのコンストラクタは派生クラスのコンストラクタの前に呼び出されます。したがって、普通の継承では基底クラスが先に構築され、その後に派生クラスが構築されます。
一方、仮想継承では基底クラスのコンストラクタは一度だけ呼び出され、継承関係の深さに関係なく同じです。仮想継承では、仮想基底クラスを派生クラスに埋め込むための追加のポインタやメカニズムが導入され、仮想基底クラスが一度だけ構築されるようにします。派生クラスのコンストラクタ呼び出しチェーンでは、最終派生クラスが仮想基底クラスのコンストラクタを呼び出す責任があります。すべての派生クラスが基底クラスのコンストラクタを呼び出すわけではありません。
したがって、普通の継承は基底クラスを先に構築し、その後に派生クラスを構築すると言えますが、仮想継承は基底クラスを派生クラスに一度だけ埋め込み、仮想基底クラスが一度だけ構築されるようにします。
この違いは、ダイヤモンド継承などの多重継承の問題を解決するために導入されました。仮想継承の導入により、多重継承の場合でも基底クラスのメンバーをより柔軟に管理および初期化することができるようになりました。