我对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框架;)
答案 0 :(得分:2)
你得到那个错误的实际原因是你定义基于范围的方式,你是通过复制而不是引用来检索对象:
for (EngineComponent component: this->engineComponents)
{
// component is a copy of the object in the list
}
EngineComponent
是一个超类,因此没有对派生类的隐式强制转换。如果您尝试从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
}
}
答案 1 :(得分:2)
std::list<EngineComponent>
就是这样;引擎组件对象列表。当您按下列表时,您正在复制正在推送的对象。除非您在子类及其基础之间定义了转换,否则它将失败。同样,尝试从基础转换为子类也会失败。
你可能想要的是一个指向基类对象的指针列表,即:std::list<unique_ptr<EngineComponent>>
可能会有所帮助。无论使用何种类型的指针,在调用Draw方法之前,都需要将其向下转换为DrawableEngineComponent:
for (unique_ptr<EngineComponent> & engCompPtr: engineComponents) { DrawableEngineComponent & drawableEngComp = dynamic_cast<DrawableEngineComponent &>(*engCompPtr); drawableEngComp.Draw(); }
我不太了解C#,但我假设你在处理对象时,它实际上是通过一些描述的智能指针实现的。
答案 2 :(得分: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的指针列表;但即便如此,建议单独存储可绘制的那些(否则你将需要做一些铸造和检查,以确保它可以铸造到那种类型。