【C++】vectorに複数の派生クラスオブジェクトを格納する方法【ポインター・キャスト演算子】

タイトル C++
スポンサーリンク
スポンサーリンク

やりたいこと

今、下の図のような継承関係のクラスがあるとします。ここで、Baseは基底クラスであり、2つの派生クラスDerivedAとDerivedBが存在することを考えます。

継承関係

今回やりたいことは、このような継承関係がある時、それぞれの派生クラスのオブジェクトをひとつのコンテナに格納することです。

複数のクラス型を格納するコンテナ

しかし、vectorのようなコンテナを扱う場合、vectorオブジェクトの型をvector<int>のように宣言します。したがって、vectorに複数の型の変数(オブジェクト)を格納することができません。vector<int>と宣言してしまったら、string型の変数を格納することはできません。そのため、各派生クラスオブジェクトを、「基底クラスのポインター」として格納するコンテナを用意する必要があります。

スポンサーリンク

対処法:基底クラスのポインターを格納する

基底クラスのポインターを格納するコンテナは、次のように宣言します。

vector<Base*>

コード例

では、実際に基底クラスと派生クラスを定義してみます。メンバー関数として、基底クラスには仮想関数を持たせます。そして、派生クラスでは、基底クラスの持つ仮想関数をオーバーライドさせます。

#include <iostream>
#include <vector>
using namespace std;

class Base
{
public:
    virtual void show()
    {
        cout << "Base" << endl;
    }
};

class DerivedA : public Base
{
public:
    virtual void show() override
    {
        cout << "DerivedA" << endl;
    }
};

class DerivedB : public Base
{
public:
    virtual void show() override
    {
        cout << "DerivedB" << endl;
    }
};

int main()
{
    Base base;
    DerivedA derivedA;
    DerivedB derivedB;
    vector<Base*> vec = {&base, &derivedA, &derivedB}; // 各クラスのポインターを格納

    for (auto element : vec) // 範囲for文では型推論をよく使う
    {
        element->show(); // ポインター経由なのでアロ―演算子で呼び出し
    }
}

実行結果

Base
DerivedA
DerivedB

基底クラス、2つの派生クラスのメソッドを呼び分けることに成功しました!

スポンサーリンク

派生クラス特有のメソッドを呼び出す

では、派生クラスが独自に持つメソッドを呼び出す場合はどうでしょうか?以下のような呼び出しで上手く動作するでしょうか?

vec[1]->uniqueA();
// error : クラス "Base" にメンバー "uniqueA" がありません

これは、上手くいきません。vector<Base*>には、あくまで「基底クラスのポインター」として、派生クラスオブジェクトを格納しているためです。

では、派生クラスオブジェクト特有のメソッドは、どうしたら呼び出せるでしょうか?

基底クラスのポインターから呼び出せないなら、「派生クラスのポインター」に変換(キャスト)すればいいのです。これを「ダウンキャスト」と言います。

dynamic_cast<Derived*>(base_pointer) // 基底クラスのポインターを派生クラスのポインターにキャスト

実行結果

#include <iostream>
#include <vector>
using namespace std;

class Base
{
public:
    virtual void show()
    {
        cout << "Base" << endl;
    }
};

class DerivedA : public Base
{
public:
    virtual void show() override
    {
        cout << "DerivedA" << endl;
    }
    void uniqueA()
    {
        cout << "only DerivedA" << endl;
    }
};

class DerivedB : public Base
{
public:
    virtual void show() override
    {
        cout << "DerivedB" << endl;
    }
    void uniqueB()
    {
        cout << "only DerivedB" << endl;
    }
};

int main()
{
    Base base;
    DerivedA derivedA;
    DerivedB derivedB;
    vector<Base*> vec = {&base, &derivedA, &derivedB}; 

    // error : vec[1]->uniqueA();
    dynamic_cast<DerivedA*>(vec[1])->uniqueA();

    // error : vec[2]->uniqueB();
    dynamic_cast<DerivedB*>(vec[2])->uniqueB();  
}

実行結果

only DerivedA
only DerivedB

2つの派生クラスが独自に持っているメソッドを呼ぶことに成功しました!

まとめ

  • コンテナに複数の派生クラスを格納するには、基底クラスのポインターとして格納する必要がある。
  • 基底クラスのポインターとして格納した要素は、ポインター経由でオーバーライドされた仮想関数を呼び出すことができる。
  • 派生クラス特有のメンバーを呼び出すためには、基底クラスのポインターとして格納された要素をダウンキャストする必要がある。

コメント