ikautak.log

C/C++, Python, CUDA, Android, Linux kernel, Network, etc.

More Effective C++ 項目31 複数のオブジェクトに関して関数を仮想的にする

新訂版 More Effective C++ (AddisonーWesley professional co)

新訂版 More Effective C++ (AddisonーWesley professional co)

  • 作者: スコット・メイヤーズ,安村通晃,伊賀聡一郎,飯田朱美,永田周一
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2007/06/29
  • メディア: 単行本(ソフトカバー)
  • 購入: 8人 クリック: 129回
  • この商品を含むブログ (43件) を見る

項目31 複数のオブジェクトに関して関数を仮想的にする
object1->foo();

↑では、object1の1つに関して関数を仮想的にできる。

foo(object1, object2);

みたいなとき、同じ基底クラスから派生した別クラスのオブジェクト2つに関してそれぞれ関数を仮想的にする方法。

仮想関数と実行時型識別を利用する
class Base {
public:
    virtual void foo(Base& object) = 0;
    ...
};

class Child1 : public Base {
public:
    virtual void foo(Base& object);
    ...
};

class Child2 : public Base {
public:
    virtual void foo(Base& object);
    ...
};

...

void Child1::foo(Base& object) {
    const type_info& objectType = typeid(object);

    if (objectType == typeid(Child1)) {
        // Child1とChild1の処理
    } else if (objectType == typeid(Child2)) {
        // Child1とChild2の処理
    }
    ...
}

foo()の中でクラスごとにtypeidするif-elseを作る。これだと兄弟クラスを知る必要がある。

仮想関数のみを用いる
class Child1;
class Child2;
...
class Base {
public:
  virtual void foo(Base& object) = 0;
  virtual void foo(Child1& object) = 0;
  virtual void foo(Child2& object) = 0;
  ...
};

class Child1 : public Base {
public:
  virtual void foo(Base& object);
  virtual void foo(Child1& object);
  virtual void foo(Child2& object);
  ...
};

...

void Child1::foo(Base& object) {
  // 引数の型により他のfoo()へ
  object.foo(*this);
}

void Child1::foo(Child1& object) {
  // Child1とChild1の処理
}

void Child1::foo(Child2& object) {
  // Child1とChild2の処理
}

この方法も兄弟を知る必要がある。クラスが追加されると、foo()メソッドも追加する必要がある。

仮想関数テーブルのエミュレート

各派生クラス毎に別名メンバ関数fooChild1()などを作り、それをmapに格納しておく。 foo()の実装で、typeid(object).name()で取れるクラス名から適切な関数をmapから取り出して実行する。

メンバーでない関数を利用する

クラスの外に、namespaceでくくって、foo()にあたる関数を作る。クライアントに提供するクラス定義が最小限で済む。

エミュレートされた仮想関数テーブルの初期化

派生クラスが増えるたびに関数を追加して全て再コンパイルが必要になるので、動的に関数を追加・削除できるようにする。