Я делаю движок для игры и не могу решить следующую проблему.
Итак, у меня есть базовый класс компонентов, из которого получены все различные компоненты. GameObject — это в основном контейнер для разных компонентов. Компоненты хранятся в векторе, содержащем указатели на базовый класс компонентов. Теперь мне нужно, чтобы класс GameObject имел шаблон функции-члена getComponent, который будет возвращать компонент с запрошенным типом из вектора.
Чтобы быть более понятным:
class Component
{
/..../
};
class RigidBody : Component
{
/..../
};
class Animation : Component
{
/..../
};
class GameObject
{
public:
template <class T>
T* getComponent();
void addComponent(Component*);
private:
std::vector<Component*> m_components;
};
/...../
GameObject test;
test.AddComponent(new RigidBody());
test.AddComponent(new Animation());
Animation * animation = test.getComponent<Animation>();
Или что-то в этом роде.
Для простоты скажем, что вектор гарантированно будет иметь компонент, который мы ищем, и что нет компонентов того же типа.
Так как указатели в векторе имеют базовый тип компонента, как я могу проверить, были ли они изначально запрошенного типа? Заранее спасибо!
При условии, что Component
имеет по крайней мере одну виртуальную функцию (иначе какой смысл наследовать от нее, верно?), вы должны иметь возможность делать то, что вам нужно, используя информацию о типе времени выполнения (RTTI) и dynamic_cast
, как это:
template <class T> T* getFirstComponent() {
for (int i = 0 ; i != m_components.size() ; i++) {
T *candidate = dynamic_cast<T*>(m_components[i]);
if (candidate) {
return candidate;
}
}
return nullptr;
}
Напомним, что dynamic_cast<T*>
вернет ненулевое значение только тогда, когда приведение выполнено успешно. Код выше проходит через все указатели и выбирает первый, для которого dynamic_cast<T*>
преуспевает.
Важная заметка: Хотя это и должно заставить вашу программу делать то, что вы хотите, рассмотрите возможность изменения дизайна: вместо того, чтобы извлекать объекты по типу, предоставьте им виртуальные функции, которые позволят вам использовать их все единообразно. Бессмысленно класть объекты разных классов в один контейнер, только чтобы потом их раздвигать. RTTI следует использовать как последнее средство, а не как основной инструмент, потому что это делает вашу программу более трудной для понимания.
Другой допустимый подход — хранить отдельные компоненты отдельно, а не в одном векторе, и получать вектор только тогда, когда вам нужно обрабатывать объекты равномерно.
Менее важное примечание: если nullptr
не компилируется в вашей системе, замените на return 0
,