多重継承にともなう あいまいさの問題

実装が異なる同名のメソッドを継承するパターン

クラスDC1C2を多重継承したことにより, 同じ名前のメソッド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)という語もよく使われている.

備考


はたいたかし
2001-07-21
トップ > 開発ツール > C++