问题描述
我正在编写一个游戏引擎,现在我正在考虑如何确保游戏中的每个状态(例如实体状态,游戏状态等)都只有一个实例。单身人士浮现在脑海中,但这似乎有些过分。我想到的另一件事是无名类:
class EntityState
{
public:
virtual void foo() = 0;
};
class : public EntityState
{
public:
void foo() {}
} walkLeftState;
// Because it inherits from EntityState,a named class,I can also
// pass it as a parameter:
void Entity::changeState(EntityState* state) {}
但是问题是我想添加Python脚本,而且我不认为Python具有无名的类。还有什么其他选择?
编辑:为什么我只需要每个状态之一。
我需要确定一个实体的状态。我可以使用魔术值(即ID字符串)来做到这一点,但这是一个非常丑陋的解决方案,imo。我宁愿通过比较指针来做到这一点,我保证指针始终是唯一的。但是除非有每个状态的单个实例,否则我无法通过指针识别...
编辑2:解决方案...
最后我去了功能对象。看到State类只包含一个函数而没有别的东西了,现在我考虑一下,将类放在首位实际上并没有任何意义。最后,我想我会去做这样的事情:
typedef void (*EntityStateFunc)(Entity* entity,unsigned long currentTime);
namespace entitystate
{
void walkLeft(Entity* entity,unsigned long currentTime);
void stand(Entity* entity,unsigned long currentTime);
}
这几乎可以解决所有问题:没有单例,没有抱怨可能会使测试变得更加困难,这很简单……唯一可能的缺点是,如果一个州需要更多的东西,我几乎会束手无策比功能有人能想到一个状态需要比这更复杂的情况吗?
解决方法
只需创建每个状态之一。
没有理由通过强制实施来使您的代码变得如此复杂。
您是否编写像这样的函数:
int foo() {
int x = 5;
x++;
return x;
}
然后走吧,天哪,我在此函数内只需要一个整数变量...我必须强制执行此操作吗?没有。
, 您可以避免创建硬约束,而可以确保是否以及何时创建超出预期范围的通知:
template <typename T>
struct expected_unique {
static int &getcount() {
static int count = 0;
return count;
}
static void object_created() {
int &lcount = getcount();
++lcount;
if (lcount > 1) {
std::err << \"More than one \" << typeid(T).name() << \" \" << lcount << \"\\n\";
}
}
expected_unique() {
object_created();
}
expected_unique(const expected_unique&) {
object_created();
}
// optionally,if you only want to check no more than one at a time
// rather than no more than one ever.
~expected_unique() {
--getcount();
}
};
class WalkLeftState : public EntityState,private expected_unique<WalkLeftState> {
};
显然,它不是线程安全的,可以使用锁或原子atomic4ѭ操作来实现。
如果确实只有一个功能,则另一种选择是:
class EntityState
{
void (*foo_func)();
public:
EntityState(void(*f)()) : foo_func(f) {}
bool operator==(const EntityState &rhs) {
return foo_func == rhs.foo_func;
}
bool operator!=(const EntityState &rhs) {
return !(*this == rhs);
}
void foo() { foo_func(); }
};
void walk_left_foo() {
}
EntityState walkLeftState(walk_left_foo);
现在,是否有多个使用相同函数的EntityState
实例无关紧要,因为比较是根据所涉及的两个状态是否执行同一例程来进行的。因此,只需将现有的指针比较切换为对象比较即可。
但是,如果现实生活中的EntityState
中有多个虚拟函数,那么这将非常笨拙。
, 在这里,单身人士可能会过分杀人。在防守方面值得称赞
您的编程,并防止滥用,但在这种情况下,最简单
解决方案只是在未命名的状态中定义每个状态类
源文件中的名称空间。您不会创建多个
一个文件中的实例,没有其他人甚至可以命名它们
少定义一个的实例。 (您也可以不命名,但
这意味着没有用户定义的析构函数。)
,
我需要确定一个实体的状态。
为什么?能够开启状态?这正是State模式应首先阻止您执行的操作。它用多态性代替了“打开状态”。也就是说,代替此:
switch (state.getState())
{
case WALK_LEFT:
--x;
break;
case WALK_RIGHT:
++x;
break;
case WALK_UP:
--y;
break;
case WALK_DOWN:
++y;
break;
}
你只是说:
state.step();
并让具体的step
成员函数做正确的事情。
,正如我在注释中的“提示”所示,强制执行“仅可能存在一个实例”约束几乎总是错误的做法。实际上,我愿意弯腰说这总是错误的事情。
取而代之的是,不可能意外地创建新实例,而要让程序员(您)在真正想要的时候这样做。
在C ++中,有两种常见的方式可以“意外地”创建实例:
EntityState st = otherState;
EntityState st2;
复制构造函数可能是第一犯人。很容易忘记ѭ12,因此您突然创建了一个副本,而不是创建对该对象的引用。因此,通过使该类不可复制来防止这种情况。
默认构造函数是一个不太严重的问题,但是,例如,您可能创建类型为“ 6”的类成员,而忘记初始化它。而且,默认构造函数为您提供了该类的新实例。
因此也要防止这种情况。声明一个带有一个或多个参数的构造函数(您不会意外调用),确保不存在默认构造函数,并且副本构造函数是私有的。
然后,您必须有意识地考虑它,并想在它发生之前创建一个实例。因此,只要程序员是理智的,只要您只想存在一个实例,您就只会有一个实例。