クラスD
にC1
とC2
を多重継承したことにより,
同じ名前のメソッドid()
(C1::id()
とC2::id()
の2つ)が取り込まれてしまっている.
class C1 { public: int id() { return 1; }; }; class C2 { public: int id() { return 2; }; }; class D : public C1, public C2 { };
これにより,次のメソッド呼び出しがあいまいになり,コンパイル時エラーとなる.
D obj; cout << obj.id() << endl;
このような場合には,次のように修飾することでC1::id()
とC2::id()
のいずれを呼び出せばよいかを明示する必要がある.
D obj; cout << obj.C1::id() << endl; cout << obj.C2::id() << endl;
class Base { public: virtual int id() { return 1; } }; class C1 : public Base { }; class C2 : public Base { }; class D : public C1, public C2 { };
前のパターンとの違いは,
まったく同じメソッドid()
がC1
経由とC2
経由の2系統でD
に継承されていること.
同じものなんだからイイじゃんと思えてくるんだけども,ダメらしくて,次のコードはコンパイル時エラーになってしまう.
D obj; cout << obj.id() << endl;
この場合もどちら経由で継承したメソッドを使うかを明示する方法
(obj.C1::id()
,obj.C2::id()
またはobj.Base::id()
と明示する方法)が使えるけど,
このメソッドの実装は共通の祖先Base
にあって同じものなのだから違和感がある.
そこでC++では仮想基本クラス(virtual base class)という考え方が導入されている.
class Base { public: virtual int id() { return 1; } }; class C1 : public virtual Base { }; class C2 : public virtual Base { }; class D : public C1, public C2 { };
このようにvirtual
をつけて継承しておけば,Base
は仮想基本クラスと解釈され,obj.id()
と呼び出してよくなる.
D obj; cout << obj.id() << endl;
クラス自体を仮想基本クラスと定義するのではなく(Base
の定義でvirtual class Base { ... };
とするのではなく),
継承するときに“仮想基本クラスとして継承する”ことをコンパイラに指示するというモデルになっていることに注意.
仮想基本クラスというモノがあるわけではなく,あくまで継承の一形態であるということを理解しておかないと混乱する.
そのため,“仮想基本クラス”(virtual base class)という用語のかわりに
“仮想継承”(virtual inheritance)という語もよく使われている.
virtual
はC1
とC2
両方のクラス定義についていないとダメ.obj.Base::id()
とかobj.C1::id()
のように明示的に修飾して呼び出してもよい.
ただ,このように明示する方法は
それが単にあいまいさを回避するためのものだったのか,
あるいは他の実装ではなくまさにその指定した実装を使う必要があったのかがわからなくなって,
基本クラス側の構成がかわったときなどに困る原因になりそうだ.
特に意味がないのであれば仮想基本クラスとして継承することで あいまいさを回避して,呼び出しはシンプルにobj.id()
としたい.