抽象クラスとインターフェース 解答ページ | Programming Place Plus C++編【言語解説】 第29章

トップページ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++編を作成中です。

問題①

問題① 「飛ぶ (Fly)」という動作に注目して、「鳥 (Bird)」のクラスを定義してください。


#include <iostream>
#include <vector>

// 鳥の抽象クラス
class Bird {
public:
    virtual ~Bird() {}

    virtual void Fly() = 0;
};

// 鷹
class Hawk : public Bird {
public:
    virtual void Fly()
    {
        std::cout << "flying hawk" << std::endl;
    }
};

// ツバメ
class Swallow : public Bird {
public:
    virtual void Fly()
    {
        std::cout << "flying swallow" << std::endl;
    }
};

// ペンギン
class Penguin : public Bird {
public:
    virtual void Fly()
    {
        std::cout << "penguins can't fly" << std::endl;
    }
};


int main()
{
    typedef std::vector<Bird*> BirdContainer;

    BirdContainer birds;
    birds.push_back(new Hawk());
    birds.push_back(new Swallow());
    birds.push_back(new Penguin());

    const BirdContainer::iterator itEnd = birds.end();
    for (BirdContainer::iterator it = birds.begin(); it != itEnd; ++it) {
        (*it)->Fly();
    }

    for (BirdContainer::iterator it = birds.begin(); it != itEnd; ++it) {
        delete *it;
    }
}

実行結果:

flying hawk
flying swallow
penguins can't fly

「鳥」という言葉には、具体的な鳥の種類が想定されていませんから、抽象クラスとして設計するのは、適切な考え方です。具体的な鳥の種類を想定するのは、派生クラスの役割です。

ちなみに、飛べない鳥であるペンギンを、鳥のクラスから派生して定義することは、場合によっては問題のある設計になるかもしれません。Flyメンバ関数を呼びだしたら、対象のオブジェクトが「飛ぶ」ことを期待しているのなら、Penguin::Flyメンバ関数は、何らかの手段でプログラムを停止させるなり、エラーを出すなりすべきでしょう。

問題②

問題② 「飛ぶ (Fly)」という動作は、鳥だけでなく、飛行機などにも適用できます。そのような観点で、インターフェースを設計してください。


#include <iostream>
#include <vector>

// 「飛べるもの」を表すインターフェース
class IFly {
public:
    virtual ~IFly() {}

    virtual void Fly() = 0;
};

// 鷹
class Hawk : public IFly {
public:
    virtual void Fly()
    {
        std::cout << "flying hawk" << std::endl;
    }
};

// ツバメ
class Swallow : public IFly {
public:
    virtual void Fly()
    {
        std::cout << "flying swallow" << std::endl;
    }
};

// 飛行機
class Airplane : public IFly {
public:
    virtual void Fly()
    {
        std::cout << "flying airplane" << std::endl;
    }
};



int main()
{
    typedef std::vector<IFly*> FlyObjectContainer;

    FlyObjectContainer flyObjects;
    flyObjects.push_back(new Hawk());
    flyObjects.push_back(new Swallow());
    flyObjects.push_back(new Airplane());

    const FlyObjectContainer::iterator itEnd = flyObjects.end();
    for (FlyObjectContainer::iterator it = flyObjects.begin(); it != itEnd; ++it) {
        (*it)->Fly();
    }

    for (FlyObjectContainer::iterator it = flyObjects.begin(); it != itEnd; ++it) {
        delete *it;
    }
}

実行結果:

flying hawk
flying swallow
flying airplane

基本的な形は、抽象クラスの場合と変わりませんが、派生クラスが、「鳥」と is-a関係で結ばれる必要はなくなったので、「飛ぶことができる」のであれば、何でも派生してよくなりました。そのため、「飛行機」を派生できますが、一方で、飛ぶことができない「ペンギン」を派生することは、明確に誤っていることになります。


参考リンク

更新履歴

’2016/4/9 新規作成。



第29章のメインページへ

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

Programming Place Plus のトップページへ



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