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 框架;)

4

3 回答 3

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>());

这会将 a 包装DrawableEngineComponent在共享指针中并将其存储在列表中。可以通过与原始方法类似的方式访问它:

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

但是这次你有一个可以调用的完全多态的对象。如果对象重载Update()了子类中的方法,那么将调用 this 。如果需要,您还可以使用强制转换来获取指向子类的指针:

for (auto& component: engineComponents)
{
    auto pDrawComponent = dynamic_cast<DrawableEngineComponent*>(component.get());
    if (pDrawComponent)
    {
        // it's drawable
    }
}
于 2013-10-05T21:29:38.630 回答
2

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

您可能想要的是一个指向基类对象的指针列表,即: Astd::list<unique_ptr<EngineComponent>>可能会成功。无论您使用哪种类型的指针,您都需要先将其向下转换为 DrawableEngineComponent,然后才能调用 Draw 方法:

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

我对 C# 了解不多,但我假设当您使用对象时,它实际上是由某种描述的智能指针实现的。

于 2013-10-05T21:36:20.280 回答
1

std::list<EngineComponent>商店一堆EngineComponents。如果您将 a 添加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 的指针列表;但即便如此,建议单独存储可绘制对象(否则您将不得不进行一些转换和检查以确保它可以转换为该类型。

于 2013-10-05T22:01:03.893 回答