第52章 多重継承事始2

 さて、今回も多重継承をやってみましょう。2つ以上の「同じ」クラスを継承するとどうなるのか、そういうお話です。


 では、今回の要点です。


 では、いってみましょう。


 前回の CJohnin クラスは、次のような継承関係を持っていました。

CFirstClass CNinja
CJohnin

 このように、2つ以上のクラスを継承することを多重継承と呼ぶのでした。

 ここで、CJohnin に一度に2つ以上の CFirstClass を継承させることはできません。つまり、

CFirstClass CFirstClass CNinja
CJohnin

 ということはできないわけです。まぁ、こういうことをするよりはコンポジションした方が便利なので、これは問題ない話です。

 しかし、次のようにすると、2つ継承することができます。

CFirstClass CFirstClass
CMaster CNinja
CJohnin

 つまり、間に何かを挟めば同じクラスを2つ以上継承できるわけです。


 しかし、ここで3つの疑問が生まれます。

  1. CNinja と CMaster で GetString をオーバーライドしたとき、CJohnin ではどちらの GetString が呼ばれるのか
  2. CFirstClass にアップキャストするとどちらの CFirstClass になるのか
  3. CJohnin で GetString をオーバーライドするとどうなるのか

です。

 先ずは1つ目の疑問を見てみましょう。CJohnin は違う実装のされた2つの GetString を持っていることになります。ということは、普通に johnin.GetString() と使ってもどちらを呼んでいいのか分からないはずです。ということは、どちらの GetString を呼ぶのかを指定する必要がありそうです。どうやれば指定できるでしょうか?

 これには2通りの方法が考えられます。1つはアップキャストであり、もう1つはアクセス解決です。では、両方やってみましょう。

プログラム1
// MulInh2.cpp
#include <iostream.h>

class CFirstClass
{
public:
    virtual const char* GetString() const = 0;
};

class CNinja  : public CFirstClass
{
    public: virtual ~CNinja();
    virtual const char* GetString() const;
};

class CMaster : public CFirstClass
{
    public: virtual ~CMaster();
    virtual const char* GetString() const;
};

class CJohnin : public CNinja, public CMaster
{
public:
    virtual ~CJohnin();
};

CNinja::~CNinja(){ }
CMaster::~CMaster(){ }
CJohnin::~CJohnin(){ }

const char* CNinja::GetString() const
{
    return "忍者";
}

const char* CMaster::GetString() const
{
    return "マスター";
}

int main()
{
    CJohnin johnin;
    // アップキャスト
    cout << dynamic_cast<CNinja&>(johnin).GetString() << endl;
    // アクセス解決
    cout << johnin.CMaster::GetString() << endl;
    return 0;
}
実行結果
忍者
マスター

 どちらも成功しました。試しに johnin.GetString() でやってみるのもいいでしょう。その場合、やはり「どちらを呼べばいいのか分かりません」というようなエラーが吐かれます。

 このように、呼びたい方の仮想関数を持っているクラスを指定してやれば自由に呼び分けられるというわけです。

 第2の疑問も、これで大体解決できたと思います。どちらの CFirstClass なのかをアップキャストで指定してやればいいわけですね(この場合はアクセス解決は使えませんね)。


 では、いよいよ第3の疑問です。「どちらの GetString をオーバーライドするか」という指定ができない以上、CJohnin で GetString をオーバーライドすると両方の GetString がオーバーライドされると考えられます。

 これが本当なのか、実際にやって確かめてみましょう。

プログラム2
// MulInh3.cpp
#include <iostream.h>

class CFirstClass
{
public:
    virtual const char* GetString() const = 0;
};

void DispInfo(const CFirstClass& obj)
{
    cout << "そのクラスの情報は“"
         << obj.GetString() << "”です。" << endl;
}

class CNinja  : public CFirstClass
{
    public: virtual ~CNinja();
    virtual const char* GetString() const;
};

class CMaster : public CFirstClass
{
    public: virtual ~CMaster();
    virtual const char* GetString() const;
};

class CJohnin : public CNinja, public CMaster
{
public:
    virtual ~CJohnin();
    virtual const char* GetString() const;
};

CNinja::~CNinja(){ }
CMaster::~CMaster(){ }
CJohnin::~CJohnin(){ }

const char* CNinja::GetString() const
{
    return "忍者";
}

const char* CMaster::GetString() const
{
    return "マスター";
}

const char* CJohnin::GetString() const
{
    return "上忍";
}

int main()
{
    CJohnin johnin;
    DispInfo(dynamic_cast<CNinja&>(johnin));
    DispInfo(dynamic_cast<CMaster&>(johnin));
    return 0;
}
実行結果
そのクラスの情報は“上忍”です。
そのクラスの情報は“上忍”です。

 このように、両方の GetString がオーバーライドされることが分かります。

 また、このことから分かるように、両方ともオーバーライドされてはまずいような処理は別々の関数にする必要があるわけです。


 では、今回の要点です。


 それでは、次回まで。


第51章 多重継承事始 | 第53章 融合

Last update was done on 2001.5.12

この講座の著作権はロベールが保有しています