多重継承
これまでは、継承について説明する際に一つの基本クラスから一つの派生クラスを派生させていました。
しかし、一つの基本クラスから複数の派生クラス、複数の基本クラスから一つの派生クラスを定義することも出来ます。
一つの基本クラスから複数の派生クラスは問題ないでしょう。
複数のクラスを定義する際に全て同じ基本クラスを設定するだけです。
複数の基本クラスから一つの派生クラスを定義することを多重継承と言います。
例えば、クラスB1、B2、B3の全てのメンバを継承したクラスDを生成することになります。
やり方は、クラスを宣言する際、基本クラスを「,」で区切って複数並べます。
多重継承したクラスのインスタンスを生成すると、全ての基本クラスのコンストラクタと派生クラスのコンストラクタが呼び出されます。
多重継承を行うとクラスの継承関係が複雑になります。
クラス間の継承関係を表すために継承図と言うものがあります。
矢印は派生クラスから基本クラスに向いています。
この例ではクラスAの派生クラスがクラスB、クラスBの派生クラスがクラスCとなります。
単純な派生を例にしていますが、複雑な場合にも使うようにしましょう。
名前の衝突
多重継承した時に、複数の基本クラスに同じ名前のメンバがある可能性があります。
基本クラス同士は継承関係が無いのでオーバーライドにはなりません。
この場合どちらを呼び出せば良いのかコンパイラが判断できないのでそのまま呼び出すとエラーになります。
そこで、メンバの前に「【クラス名】::」を付けて判別します。
具体例を挙げましょう。
クラスAとクラスBから派生したクラスCがあるとします。
クラスAとクラスBはどちらもf()というメンバ関数を持っています。
この関数をそれぞれクラスCのインスタンスから呼び出しましょう。
multi_inheritance_sample1
C c;
c.A::f();
c.B::f();
二行目がクラスAのf()、三行目がクラスBのf()を呼び出しています。
これならどちらを呼び出すかわかりますね。
仮想基本クラス
次の継承図のような継承を考えましょう。
クラスBとCはクラスAの派生クラスでクラスDはBとCを多重継承した派生クラスです。
このような継承関係を図の様子から菱形継承と呼びます。
このとき、クラスDのインスタンスを生成したらどうなるでしょうか。
また、B、Cでそれぞれオーバーライドした関数を呼び出す場合はどうなるでしょうか。
それぞれ考えていきましょう。
まずクラスDのインスタンスを生成した場合です。
派生クラスのインスタンスを生成したとき、基本クラスのコンストラクタが呼び出されますね。
例えばクラスBのインスタンスを生成するとAとBのコンストラクタが呼び出されます。
Dの場合はどうなるでしょうか。
BとCのコンストラクタが呼び出されるのは当然としてAのはどうなるでしょう。
BとCはそれぞれ派生クラスなのでAが2回呼び出されるかもしれません。
続いてB、Cでそれぞれオーバーライドした関数を呼び出す場合です。
ただ名前が同じであれば先ほどのクラス名を記述する方法を使えばよいですが、この場合はただの同名関数ではなくそれぞれオーバーライドした関数です。
こちらもどちらの関数を呼び出すのかわかりませんね。
補足ですが、クラスDでもオーバライドした場合はDのメンバが呼び出されます。
このように、菱形継承の場合はアクセス先に曖昧さが残ります。
これを解決するには派生の際に仮想基本クラスというものを設定します。
継承するときの基本クラスの指定の前にvirtualを付けることで設定できます。
multi_inheritance_sample2
class B : virtual public A
{
}
このような設定をクラスB、Cを定義するときに設定します。
クラスDの定義には必要ありません。
これで菱形継承でDを使うことができます。
インスタンスを生成した場合、クラスA、B、C、Dのコンストラクタが全て1度ずつ呼び出されます。