我正在编写游戏引擎,现在我正在考虑如何确保游戏中的状态(无论是实体状态,游戏状态等)只有一个实例。想到单身人士,但这似乎有些过分。我想到的另一件事是无名的课程:
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:解决方案......
我最后去了功能对象。看到状态类只包含一个函数而不包含任何其他内容,因此首先考虑它并不是真的有意义。最后,我想我会选择这样的事情:
typedef void (*EntityStateFunc)(Entity* entity, unsigned long currentTime);
namespace entitystate
{
void walkLeft(Entity* entity, unsigned long currentTime);
void stand(Entity* entity, unsigned long currentTime);
}
这几乎可以解决所有问题:没有单身人士,也没有抱怨它可能会让测试变得更加困难,这很简单......唯一可能的缺点就是,如果状态需要超过功能;任何人都可以想到一个国家需要比这更复杂的事情吗?
答案 0 :(得分:4)
只需创建一个州。
没有理由通过尝试强制执行此操作来使您的代码变得如此复杂。
你是否写了这样的函数:
int foo() {
int x = 5;
x++;
return x;
}
然后去,天啊,我只需要在这个函数中有一个整数变量... 我必须强制执行这个?否。
答案 1 :(得分:3)
您可以避免创建硬约束,而是确保在您创建的内容超出预期时通知您:
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> {
};
显然它不是线程安全的,你可以通过锁定或原子int
操作来实现它。
如果真的只有一个功能,另一个选择是:
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
中有多个虚拟函数,那么这将非常笨拙。
答案 2 :(得分:2)
单身人士在这里可能有些过分。防守是值得称道的 你的编程,并防止误用,但在这种情况下,最简单 解决方案只是定义一个未命名的状态类 源文件中的命名空间。你不会创建多个 在这个文件中的实例,没有其他人甚至可以命名它们 少定义一个实例。 (你也可以不提名,但是 这意味着没有用户定义的析构函数。)
答案 3 :(得分:0)
我需要离开以确定实体的状态。
为什么呢?能够开启状态吗?这正是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
成员函数做正确的事。
答案 4 :(得分:0)
正如我在评论中“暗示”一样,强制执行“只有一个实例可能存在”约束几乎总是错误的做法。实际上,我愿意站出来说, 总是错误的做法。
相反,无法意外创建新实例,但允许程序员(您) 。
在C ++中,有两种常见的方法可以“意外地”创建实例:
EntityState st = otherState;
EntityState st2;
复制构造者可能是排名第一的犯罪者。忘记&
很容易,因此突然创建副本而不是创建对象的引用。因此,通过使类不可复制来防止这种情况。
默认构造函数是一个不太严重的问题,但您可能会创建类型为EntityState
的类成员,并忘记初始化它。而且,默认构造函数为您提供了类的新实例。
所以也要防止这样做。声明一个带有一个或多个参数的构造函数(你不会意外调用),确保不存在默认构造函数,并且复制构造函数是私有的。
然后你必须有意识地考虑它并且想要在它发生之前创建一个实例。因此,只要程序员理智,只要你想要只存在一个实例,你就只有一个实例。