虚函数+带基类指针的STL容器

时间:2013-02-03 12:19:42

标签: c++ inheritance stl override virtual

我有一个名为 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:我刚刚意识到的一件事是,在通过仿函数创建Derived实例后,我没有调用dynamic_cast(或类似的东西)(但我不会这样做)因为它是通用/动态的,所以无论如何都知道要把它投射到什么地方。它只是一个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 *而不是哪个精确的衍生物。想到的一个想法可能是存储用于在类的第一个位置创建 Derived 实例的函数,以便稍后重用它。所以我的克隆看起来像这样:

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

有人看到更好的方法吗?

1 个答案:

答案 0 :(得分: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>{};