确保每个州只有一个

时间:2011-06-13 09:21:24

标签: c++

我正在编写游戏引擎,现在我正在考虑如何确保游戏中的状态(无论是实体状态,游戏状态等)只有一个实例。想到单身人士,但这似乎有些过分。我想到的另一件事是无名的课程:

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);
}

这几乎可以解决所有问题:没有单身人士,也没有抱怨它可能会让测试变得更加困难,这很简单......唯一可能的缺点就是,如果状态需要超过功能;任何人都可以想到一个国家需要比这更复杂的事情吗?

5 个答案:

答案 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的类成员,并忘记初始化它。而且,默认构造函数为您提供了类的新实例。

所以也要防止这样做。声明一个带有一个或多个参数的构造函数(你不会意外调用),确保不存在默认构造函数,并且复制构造函数是私有的。

然后你必须有意识地考虑它并且想要在它发生之前创建一个实例。因此,只要程序员理智,只要你想要只存在一个实例,你就只有一个实例。