多重継承 解答ページ | Programming Place Plus C++編【言語解説】 第30章

トップページC++編

C++編で扱っている C++ は 2003年に登場した C++03 という、とても古いバージョンのものです。C++ はその後、C++11 -> C++14 -> C++17 -> C++20 と更新されており、今後も 3年ごとに更新されます。
なかでも C++11 での更新は非常に大きなものであり、これから C++ の学習を始めるのなら、C++11 よりも古いバージョンを対象にするべきではありません。特に事情がないなら、新しい C++ を学んでください。 当サイトでは、C++14 をベースにした新C++編を作成中です。

問題①

問題① 「飛べるもの」を表す抽象クラス FlyObject と、「乗り物」を表す抽象クラス Vehicle があるとき、「飛行機」のクラスを多重継承を使って定義してください。FlyObject と Vehicle はそれぞれ、速度を表すメンバ変数 mSpeed と、それを返す GetSpeedメンバ関数を持つものとします。


#include <iostream>

class FlyObject {
public:
    explicit FlyObject(double speed) :
        mSpeed(speed)
    {}

    virtual void Fly() = 0;

    inline double GetSpeed() const
    {
        return mSpeed;
    }

private:
    double  mSpeed;
};

class Vehicle {
public:
    explicit Vehicle(double speed) :
        mSpeed(speed)
    {}

    virtual void Run() = 0;

    inline double GetSpeed() const
    {
        return mSpeed;
    }

private:
    double  mSpeed;
};

class Airplane : public FlyObject, public Vehicle {
public:
    Airplane() :
        FlyObject(1000.0),
        Vehicle(1000.0)
    {}

    virtual void Fly()
    {
        std::cout << "Fly (speed == " << FlyObject::GetSpeed() << ")" << std::endl;
    }

    virtual void Run()
    {
        std::cout << "Run (speed == " << Vehicle::GetSpeed() << ")" << std::endl;
    }
};


int main()
{
    Airplane* airplane = new Airplane();

    airplane->Fly();
    airplane->Run();

    delete airplane;
}

実行結果:

Fly (speed == 1000)
Run (speed == 1000)

2つの基底クラスのそれぞれが、mSpeed、GetSpeed という共通の名前のメンバを持っています。そのため、派生クラス側からこれらを使う場合に曖昧さが生じます。

この解答例では、「飛べるもの」の速度も、「乗り物」の速度も、「飛行機」という具体的なクラスとしては、同一の概念と考えています。そのように考えるのであれば、2カ所に同じ意味のメンバが存在することになり、あまり良い形とは言えません。そこで、問題②へつながります。

問題②

問題② 問題①において、FlyObjectクラスを廃止して、代わりに「飛べるもの」を表すインターフェース IFly を導入したら、派生クラスはどう変化するでしょうか。IFlyインターフェースは、「飛ぶ」という動作にしか興味がないので、Fly という純粋仮想関数と仮想デストラクタのみを持ちます。


#include <iostream>

class IFly {
public:
    virtual ~IFly() {}

    virtual void Fly() = 0;
};

class Vehicle {
public:
    explicit Vehicle(double speed) :
        mSpeed(speed)
    {}

    virtual void Run() = 0;

    inline double GetSpeed() const
    {
        return mSpeed;
    }

private:
    double  mSpeed;
};

class Airplane : public IFly, public Vehicle {
public:
    Airplane() :
        Vehicle(1000.0)
    {}

    virtual void Fly()
    {
        std::cout << "Fly (speed == " << GetSpeed() << ")" << std::endl;
    }

    virtual void Run()
    {
        std::cout << "Run (speed == " << GetSpeed() << ")" << std::endl;
    }
};


int main()
{
    Airplane* airplane = new Airplane();

    airplane->Fly();
    airplane->Run();

    delete airplane;
}

実行結果:

Fly (speed == 1000)
Run (speed == 1000)

インターフェースは、できるだけその目的の動作に関するメンバだけを持つようにするべきです。IFly から速度に関するメンバを廃止したのはそのためです。これで、速度に関するメンバは、Vehicleクラスの側だけでまかなわれるようになったので、曖昧さが解消しました。

多重継承を使うにしても、メンバ変数を持ったクラスばかりを使うと、曖昧さの解決に苦心することが多くなります。そんなときには、インターフェースを利用することを考えてみると、奇麗に納められることがあります。


参考リンク

更新履歴

’2016/5/2 新規作成。



第30章のメインページへ

C++編のトップページへ

Programming Place Plus のトップページへ



はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る