1

仮想関数を定義するBaseという基本クラスがあります。Derivedクラスはそれを継承し、その仮想関数を実装/上書きします。次のコードは問題なく機能します。

Base* pB = new Derived();
pB->virtual_function(); // function of class Derived gets called -> good

私の問題は、すべての派生インスタンスをSTLコンテナに格納することstd::map<ID, Base*>です。後でそのコンテナを繰り返し処理し、各Base *で仮想関数を呼び出そうとすると、ランタイムはポインタをBase *型としてのみ認識し、 Derivedクラスのオーバーライドされた実装を呼び出さないため、これは問題を引き起こすようです。

それを意図したとおりに機能させる方法はありますか、それともここで重要なポイントを見逃していますか?

編集1:いくつかの追加コードが要求されたので、ここに行きます:

std::map<ComponentType, Base*> m_Components;
// The factory instantiates a Derived* (via functors) and returns it as Base*
Base* pB = m_pComponentFactory->createComponent(this, type);
// Lazy insert (since there is no map entry with key 'type' at that stage) 
m_Components[type] = pB;

[...]

Base* pB;
for(ComponentMap::const_iterator it = m_Components.begin(); it != m_Components.end( ); ++it) 
{
    pB = it->second;
    pB->virtual_function(); // goes to Base instead of Derived
}

編集2:私が今気づいたことの1つは dynamic_cast、ファンクターを介してDerivedインスタンスを作成した後、呼び出し(または同様のもの)しないことです(ただし、すべて汎用/動的であるため、とにかく何にキャストするかわかりません)。return creator()クリエーターがファンクターであるだけです。それが問題ですか?

クリエータータイプ(ファンクトンタイプ)の定義:

typedef Base*(*ComponentCreator)([some params]);

編集3: 実際のファンクターは、たとえば次のように定義されます(RenderableとLocationはBaseから派生したクラスです)。

&Renderable::Create<Renderable> // or
&Location::Create<Location>

Create()メソッドは、クラスBaseのテンプレート関数です。

template<typename T> 
static Component* Create([some params]) 
{ 
    return new T([some params]); 
}

編集4: 問題は私のclone()+CopyConstructorの処理にあるようです。私のクローンは現在次のようになっています:

Base* Base::clone() const
{
    return new Base(*this);
}

Base *のみを作成しているため、後で仮想解像度を作成することはできません。しかし、私が今残している問題は、クローンを変更する方法がわからないことです。EDIT 1に示されているようにm_Components、Base*ポインターを使用したマップがあります。今、それらのクローンを作成する必要がありますが、それらがBase *のものであり、正確な派生物ではないことだけを知っています。頭に浮かぶアイデアの1つは、Derivedインスタンスを作成するために使用したファンクターをクラスの最初の場所に格納し、後で再利用することです。したがって、私のクローンは次のようになります。

Base* Component::clone() const
{
    return m_pCreationFunctor([some params]);
}

より良いアプローチを見ている人はいますか?

4

1 に答える 1

5

あなたはスライスの犠牲者です。Baseをコピーして作成すると、オブジェクトの派生部分が失われます。詳細については、 http://en.wikipedia.org/wiki/Object_slicingを参照してください。基本クラスがインスタンス化されない場合は、将来この間違いを防ぐために、基本クラスを抽象化することを検討してください。

この場合の修正は、おそらく仮想Base * clone()メソッドを持ち、派生クラスでそれをオーバーライドすることです。

すなわち

class Base{
...
virtual Base * clone() const = 0;
...
};

class Derived : public Base {
...
Base * clone() const override { return new Derived(*this); }
...
};

クローンメソッドの書き換えを本当に避けたい場合は、中間のCRTPクラスを使用できます。

struct Base{
    virtual Base * clone() = 0;
};

template <typename D>
struct B : public Base {
    virtual Base * clone() { return new D(*static_cast<D*>(this)); }
};

struct D : public B<D>{};
于 2013-02-03T13:10:27.670 に答える