2013-10-05 74 views
2

我是比较新的C++,和我有这个名单迭代麻烦C#背景的:迭代子类错误列表

我有过的对象和通话清单循环的一种方法每一个更新方法,这很好。该列表的类型为std::list<EngineComponent>,称为engineComponents

void Game::Update() 
{ 
    for (EngineComponent component: this->engineComponents) 
    { 
     component.Update(); 
    } 
} 

我也有EngineComponent子类,称为DrawableEngineComponent

void Game::Draw() 
{ 
    for (DrawableEngineComponent component: this->engineComponents) 
    { 
     component.Draw(); 
    } 
} 

这将产生错误“没有合适的用户定义的转换,从‘EngineComponent’到‘DrawableEngineComponent’存在”:

的问题,当我试图做类似的反复出现。考虑到这个实现在C#中都很好,我不确定在C++中如何最好地解决这个问题。

我能想到的一些替代方法,将/应该工作,但我不知道是否有在C++的功能要做到这一点类似于C#的方式,而不必手动定义转换。是

对有关这两个类的定义如下:

class EngineComponent 
{ 
public: 
    EngineComponent(void); 
    ~EngineComponent(void); 

    virtual void Update(void); 
}; 


class DrawableEngineComponent : public EngineComponent 
{ 
public: 
    DrawableEngineComponent(void); 
    ~DrawableEngineComponent(void); 

    virtual void Draw(void); 
}; 

是的,我复制XNA框架位;)

+1

这种投射尝试尖叫出潜在的切片问题,你可能会做得很好[阅读这个问题和答案](http://stackoverflow.com/questions/274626/what-is-the-slicing-problem-in-c/274634#274634)了解更多关于它 – WhozCraig

+1

你说这个在C#中有效,但我不确定这是真的:-)在C#中,如果列表中的某个组件是* not * Drawable,会发生什么情况。它会崩溃吗?或者跳过列表中的项目?请澄清你在这方面的期望/愿望。 –

+0

@AaronMcDaid你是对的,它没有。当我写这个时,我有点困惑。在C#中通过检查每个对象在循环中出现时(使用一行代码)很容易实现,这是我应该写的。好地方:) –

回答

2

你得到这个错误的实际原因是该方法您所定义的范围为基础的,你是通过检索复制对象,而不是参考:

for (EngineComponent component: this->engineComponents) 
{ 
    // component is a copy of the object in the list 
} 

EngineComponent是超一流的,因此也没有我mplicit强制转换为派生类。如果你尝试将DrawableEngineComponent复制出来的EngineComponent列表的编译器无法知道源对象确实是一个派生类的方式。

标准集装箱真的不处理多态对象非常好。更好的解决方案是使用std::shared_ptr来存储指向对象的指针。

std::list<std::shared_ptr<EngineComponent>> myList; 
myList.push_back(std::make_shared<DrawableEngineComponent>()); 

这将在共享指针紧裹DrawableEngineComponent并将其存储在列表中。它可以以类似的方式访问您的原始方法:

for (auto& component: engineComponents) 
{ 
    component->Update(); 
} 

但是,这次您可以调用一个完全多态的对象。如果该对象在子类中重载Update()方法,则会调用该方法。您也可以使用铸造获得一个指向子类,如果这就是你所需要的:

for (auto& component: engineComponents) 
{ 
    auto pDrawComponent = dynamic_cast<DrawableEngineComponent*>(component.get()); 
    if (pDrawComponent) 
    { 
     // it's drawable 
    } 
} 
+0

+1(我等着别针和针你终于取消删除这个东西,所以我可以对它投票= P)。 – WhozCraig

+0

这正是我所追求的!非常感谢!虽然,我没有实现'std :: shared_ptr',这一切似乎都工作得很好。我不确定这样做的优缺点,我将不得不阅读它:) –

2

std::list<EngineComponent>就是这样;引擎组件对象列表。当你推入列表时,你正在制作你正在推送的对象的副本。除非您定义了子类和其基类之间的转换,否则将会失败。同样,如果您正在从基础转换到子类,也会失败。

你可能想要的是指针的基类对象的列表,即:A std::list<unique_ptr<EngineComponent>>可能会做的伎俩。无论使用何种类型的指针,你需要将其向下转换到DrawableEngineComponent后,才能调用Draw方法:

 
for (unique_ptr<EngineComponent> & engCompPtr: engineComponents) 
{ 
    DrawableEngineComponent & drawableEngComp = dynamic_cast<DrawableEngineComponent &>(*engCompPtr); 
    drawableEngComp.Draw(); 
} 

我不知道C#很大,但我相信,当你使用对象它实际上是通过一些描述的智能指针来实现的。

1

std::list<EngineComponent>存储一堆EngineComponents。如果添加DrawableEngineComponent到列表中,你是切片“绘制”部分关闭它:

std::list<EngineComponent> engineComponents; 
EngineComponent comp1; 
engineComponents.push_back(comp1); // no problem, just copies it into the list 
DrawableEngineComponent comp2; 
EngineComponent newComp2 = *(EngineComponent*)(&comp2); // the drawable part is now sliced out! 
engineComponents.push_back(newComp2); // this adds the sliced version to the list 

极有可能,你是想存储指针EngineComponents列表更多;但即使如此,最好单独存放可绘制的元素(否则你将不得不进行一些铸造和检查以确保它可以铸造成该类型)。