仮想継承(カソウケイショウ)とは | 意味や読み方など丁寧でわかりやすい用語解説

仮想継承(カソウケイショウ)の意味や読み方など、初心者にもわかりやすいように丁寧に解説しています。

作成日: 更新日:

読み方

日本語表記

仮想継承 (カソーケイショウ)

英語表記

virtual inheritance (バーチャル インヘリタンス)

用語解説

仮想継承とは、オブジェクト指向プログラミングにおける多重継承の際に発生する「ダイヤモンド継承問題」を解決するために、C++言語が提供する特別な継承メカニズムである。これは、複数の継承パスを通じて同じ基底クラスが派生クラスに複数回含まれることを防ぎ、基底クラスのサブオブジェクトが一つだけになるように保証する機能だ。主にメモリの冗長性を排除し、メンバーアクセス時の曖昧性を解消することを目的とする。

多重継承とは、一つのクラスが複数の基底クラスから同時に機能や属性を継承する仕組みを指す。これにより、コードの再利用性が高まり、複雑なシステムをより柔軟に設計できるというメリットがある。例えば、ある「自動車」クラスが「乗り物」クラスと「陸上を走る」クラスから継承するといった応用が考えられる。しかし、この多重継承には「ダイヤモンド継承問題」と呼ばれる特有の複雑さが伴うことがある。

ダイヤモンド継承問題は、次のような継承構造で発生する。まず、共通の基底クラスAが存在する。次に、クラスBとクラスCがそれぞれクラスAから継承する。そして最後に、クラスDがクラスBとクラスCの両方から多重継承する。この構造を図にすると、菱形(ダイヤモンド)に見えるため、この名が付けられた。このとき、クラスDはクラスBを経由してクラスAのサブオブジェクトを持ち、同時にクラスCを経由して別のクラスAのサブオブジェクトを持つことになる。結果として、クラスDのインスタンス内にはクラスAのサブオブジェクトが論理的に二つ存在してしまう状況が生まれる。

この二重のクラスAサブオブジェクトは、いくつかの問題を引き起こす。第一に、メモリの無駄だ。同じ基底クラスのデータメンバーが二重に格納されるため、不要なメモリ消費が発生する。第二に、曖昧性の問題だ。クラスDからクラスAのメンバー(例えば、特定のメソッドや変数)にアクセスしようとした場合、コンパイラはクラスB経由でアクセスすべきか、クラスC経由でアクセスすべきかを判断できず、エラーとなる。これは、パスが複数存在するために、どのパスを選択すべきか決定できないためだ。この曖昧性を解消するためには、プログラマが明示的にスコープ解決演算子(::)を用いてパスを指定する必要があり、コードの記述が煩雑になる。

仮想継承はこの問題を解決するために導入された。仮想継承を利用するには、中間クラス(上記の例ではBとC)が共通の基底クラス(A)を継承する際に、継承指定子にvirtualキーワードを付与する。具体的には、class B : virtual public Aclass C : virtual public A のように記述する。このvirtualキーワードを指定することで、コンパイラはクラスDがクラスBとクラスCから多重継承された際に、共通の基底クラスAのサブオブジェクトが一つだけになるように特別に処理する。これにより、クラスDのインスタンス内にはクラスAのサブオブジェクトが物理的に一つだけ存在することになり、メモリの冗長性と曖昧性の問題が解消される。

仮想継承された基底クラスのコンストラクタの呼び出し方には、通常の継承とは異なるルールがある。通常の多重継承では、派生クラスのコンストラクタが直接の基底クラスのコンストラクタを呼び出し、その基底クラスがさらに自身の基底クラスのコンストラクタを呼び出す、という連鎖的な呼び出しが行われる。しかし、仮想継承の場合、最も派生したクラス(上記の例ではクラスD)が、仮想基底クラス(クラスA)のコンストラクタを直接呼び出す責任を持つ。これは、仮想基底クラスのコンストラクタが、すべての継承パスを通じて一度だけ呼び出されることを保証するためだ。中間クラス(BやC)のコンストラクタが仮想基底クラスAのコンストラクタを呼び出しても、その呼び出しは最も派生したクラスDによって無視されるか、実行されない。もし最も派生したクラスDが仮想基底クラスAのコンストラクタを明示的に呼び出さない場合、Aのデフォルトコンストラクタが暗黙的に呼び出される。この特殊なコンストラクタ呼び出しルールは、仮想継承の動作を理解する上で非常に重要である。

仮想継承は非常に強力な機能だが、わずかながら実行時のオーバーヘッドを伴う可能性がある。仮想基底クラスのサブオブジェクトへのアクセスは、通常の継承に比べて間接的なポインタ参照を介して行われる場合があるため、パフォーマンスに微小な影響を与える可能性がある。しかし、現代のコンパイラの最適化技術と、ダイヤモンド継承問題の根本的な解決というメリットを考慮すると、そのオーバーヘッドは通常、許容範囲内であることがほとんどだ。

仮想継承はC++特有の概念であり、JavaやC#のように多重継承を直接サポートせず、インターフェースによる多重実装で代用する言語ではこの問題自体が発生しないため、仮想継承という仕組みも存在しない。C++が多重継承の柔軟性を提供しつつ、その複雑さに対処するために設計された機能の一つとして理解することが重要である。これにより、複雑なクラス階層を持つシステムでも、整合性を保ちながら効率的に設計・実装することが可能となる。

仮想継承(カソウケイショウ)とは | 意味や読み方など丁寧でわかりやすい用語解説 | いっしー@Webエンジニア