I'm relatively new to C++, and coming from a C# background I'm having trouble with this list iteration:
I have one method which loops through a list of objects and calls an update method for each one, which works great. The list is of type std::list<EngineComponent>
and is called engineComponents
.
void Game::Update()
{
for (EngineComponent component: this->engineComponents)
{
component.Update();
}
}
I also have a subclass of EngineComponent
called DrawableEngineComponent
.
The issue appears when I try to do a similar iteration:
void Game::Draw()
{
for (DrawableEngineComponent component: this->engineComponents)
{
component.Draw();
}
}
This produces the error "No suitable user-defined conversion from 'EngineComponent' to 'DrawableEngineComponent' exists". Given that this implementation was all fine and dandy in C#, I'm not really sure how best to address this issue in C++.
I can think of a few alternative ways that would/should work, but I'm wondering if there's functionality in C++ to do this in a way similar to C# without having to manually define a conversion.
The definitions for the two classes concerned are as follows:
class EngineComponent
{
public:
EngineComponent(void);
~EngineComponent(void);
virtual void Update(void);
};
class DrawableEngineComponent : public EngineComponent
{
public:
DrawableEngineComponent(void);
~DrawableEngineComponent(void);
virtual void Draw(void);
};
And yes, I am copying the XNA framework a bit ;)
The actual reason you get that error is that the way you have defined the range-based for, you are retrieving objects by copy rather than reference:
for (EngineComponent component: this->engineComponents)
{
// component is a copy of the object in the list
}
EngineComponent
is a super-class and so there's no implicit cast to a derived class. If you try to copy a DrawableEngineComponent
out of a list of EngineComponent
the compiler has no way of knowing if the source object is really a derived class.
Standard containers don't really handle polymorphic objects very well. A much better solution is to use std::shared_ptr
to store pointers to the objects.
std::list<std::shared_ptr<EngineComponent>> myList;
myList.push_back(std::make_shared<DrawableEngineComponent>());
This will wrap a DrawableEngineComponent
in a shared pointer and store it in the list. It can be accessed in a similar way to your original method:
for (auto& component: engineComponents)
{
component->Update();
}
But this time you have a fully polymorphic object you can call. If the object overloads the Update()
method in a sub-class then it's this that will be called. You can also use casting to get a pointer to the sub-class if that's what you need:
for (auto& component: engineComponents)
{
auto pDrawComponent = dynamic_cast<DrawableEngineComponent*>(component.get());
if (pDrawComponent)
{
// it's drawable
}
}