我目前正在使用SFML创建游戏。简单地说,我有一个Object
类,它具有所有对象的所有共同特征。 Player
和Enemy
类继承自Object
。还有一个ObjectManager
,其中包含所有Object
的列表并更新并绘制它们等。
// using SFML shapes
class Player : public Object
{
public:
sf::CircleShape playerShape;
};
class Enemy : public Object
{
public:
sf::Sprite enemyShape;
};
class Object
{
public:
void move(...) { // move the shape, but the shapes are only found in the derived }
void draw(...) { // same problem as above, etc... }
};
class ObjectManager
{
private:
std::map<int, std::shared_ptr<Object>> gameObjectMap; // id, object
public:
void updateAll(...) {
// loop over all objects and update them
for (auto itr = gameObjectMap.begin(); itr != gameObjectMap.end(); ++itr) {
itr->second->move(...);
itr->second->draw(...);
}
}
};
上面你可以看到Object::move()
无法使用,因为Object
不知道派生类中的SFML形状。因此,您可以使函数Object::move()
纯虚拟,但这意味着您需要在派生类中为您需要使用的任何特定于形状的函数编写实现,这还包括有关形状的任何属性,例如它的位置sf::Shape::getPosition()
等等。所以这种方法不能很好地扩展。
我想到的另一种方法是将Object
类作为类模板
template<typename T>
class Object
{
protected:
T object;
public:
void move(...) { object.move(...); }
T getObject() { return object; }
};
class Player : public Object<sf::CircleShape>
{ ... };
但是,这意味着ObjectManager
现在必须在地图中包含类模板Object
,而我不确定这是如何工作的?
处理这种情况的最佳方法是什么?
答案 0 :(得分:0)
怎么样:
class Object
{
virtual auto move() -> void = 0;
};
template <class Shape>
class Shape_object : Object
{
Shape shape;
auto move() -> void override
{
// implementation
}
};
// you can have specializations for each shape type
// if you can't use the generic one
class Player : public Shape_object<sf::CircleShape>
{
//
};
class Enemy : public Shape_object<sf::Sprite>
{
//
};
答案 1 :(得分:0)
template<typename T> class Object { // ... };
如果你真的考虑使用模板类,你应该使用CRTP(又名静态多态性):
template<typename Derived>
class Object {
public:
/* virtual */ auto move() -> void {
// ^^^^^^^^^^^^^ Hooray! No more vtable \o/ (but wait ...)
static_cast<Derived*>(this)->doMove(); // Fails to compile, if
// Derived isn't inheriting from
// Object<Derived> or Derived
// doesn't implement doMove().
};
};
该模式的缺点是必须在编译时解决所有问题。接口实现的运行时注入(例如通过插件)不能很好地使用该模式。
你可以留下一个薄层来进行虚拟破坏并正常工作:
struct ObjectBase {
virtual ~ObjectBase() {} // << that's enough
};
template<typename Derived>
class Object : public ObjectBase {
// ...
}