我知道这是可能的,但我对 C++ 的缺乏经验限制了我。我想要做的是有一些系统,我可以update()
向某些类添加方法,并让程序中的所有更新方法在每一帧都被调用。
Unity3D 用户可能对此很熟悉。Update()
但是,当未确定的类中有未确定数量的方法时,程序如何知道调用每个方法。
我想这是一个在整个程序中动态调用函数的系统。
没有魔杖可以为您做到这一点。您需要手动维护您创建的具有此Update
方法的所有对象的列表,并在需要时迭代列表,依次调用每个对象的方法。
此外,您必须先满足编译器的要求,然后它才会允许您执行此操作。这意味着如果列表实际上是一个标准容器,例如std::vector
,它需要是一个指向某个基类的容器,该基类由它内部的所有实例共享(这是标准方法)。有一些方法可以让你绕过这个限制,但在诉诸此类之前,你应该非常清楚自己在做什么。
您正在寻找的内容与观察者模式非常相似。您需要注册您的对象,以便在发生某些操作时调用它们的“通知”(或更新)方法。该动作可以是计时器或每个循环,这取决于您。
http://en.wikipedia.org/wiki/Observer_pattern将让您对这种设计模式有一个很好的了解。
我把它放在这里是为了证明概念。如果你继承自Updateable
: 你可以UpdateModule::Update(delta_time);
从你的主循环调用来更新任何构造的类。
它使用循环链表来跟踪要更新的项目,从而避免了动态内存分配的需要。UpdateModule 使用了一个技巧来确保即使是静态创建的对象也能正常工作:
class Updatable
{
friend class UpdateModule;
public:
virtual void Update(float delta_time) = 0;
protected:
Updatable();
virtual ~Updatable();
private:
Updatable* m_next;
Updatable* m_prev;
};
class UpdateModule
{
friend class Updatable;
public:
static void Update(float delta_time)
{
Updatable* head = *GetHead();
Updatable* current = head;
if (current)
{
do
{
current->Update(delta_time);
current = current->m_next;
} while( current != head);
}
}
private:
static void AddUpdater(Updatable* updatable)
{
Updatable** s_head = GetHead();
if (!*s_head)
{
updatable->m_next = updatable;
updatable->m_prev = updatable;
}
else
{
(*s_head)->m_prev->m_next = updatable;
updatable->m_prev = (*s_head)->m_prev;
(*s_head)->m_prev = updatable;
updatable->m_next = (*s_head);
}
*s_head = updatable;
}
static void RemoveUpdater(Updatable* updatable)
{
Updatable** s_head = GetHead();
*s_head = updatable->m_next;
if (*s_head != updatable)
{
updatable->m_prev->m_next = updatable->m_next;
updatable->m_next->m_prev = updatable->m_prev;
}
else
{
*s_head = NULL;
}
updatable->m_next = NULL;
updatable->m_prev = NULL;
}
private:
static Updatable** GetHead()
{
static Updatable* head = NULL;
return &head;
}
};
Updatable::Updatable() : m_next(NULL), m_prev(NULL)
{
UpdateModule::AddUpdater(this);
}
Updatable::~Updatable()
{
UpdateModule::RemoveUpdater(this);
}
示例用法:
class SomeClass : private Updatable
{
public:
virtual void Update(float)
{
printf("update for SomeClass was called\n");
}
};
class AnotherClass : private Updatable
{
public:
virtual void Update(float)
{
printf("update for AnotherClass was called\n");
}
};
int main()
{
SomeClass a, b;
AnotherClass c, d;
UpdateModule::Update(0);
}
我能想到的解决这个问题的唯一解决方案是使用类似AspectC++
. 当你想做这样的事情时,面向方面的编程非常有用。
从抽象类派生具有此 Update 方法的所有类,然后将指针放入您希望在某种向量中调用此方法的所有变量。
这是一个基本的例子:
class Updatable
{
public:
virtual void update() = 0;
};
class SomeClass : public Updatable
{
public:
virtual void update()
{
printf("update for SomeClass was called\n");
}
};
class AnotherClass : public Updatable
{
public:
virtual void update()
{
printf("update for AnotherClass was called\n");
}
};
int main()
{
Updatable* v[10];
int n = 0;
SomeClass a, b;
AnotherClass c, d;
v[n++] = &a;
v[n++] = &b;
v[n++] = &c;
v[n++] = &d;
for (int i = 0; i < n; i++)
v[i]->update();
}
使用 CRTP(Curiously Recurring Template Pattern)的一种有趣方式:
template <class T> struct updatable
{
static std::set<updatable*> objects;
updatable()
{
objects.insert(this);
}
~updatable()
{
objects.erase(this);
}
void update_all()
{
for (auto& o : objects)
static_cast<T* const>(o)->update();
}
};
template <class T>
std::set<updatable<T>*> updatable<T>::objects = {};
struct Some_object : updatable<Some_object>
{
void update()
{
// logic here.
}
};
现在您可以使用 .update() 方法更新任何一个对象。但是您也可以通过调用从父级继承的 .update_all() 方法来更新某种类型的所有对象。注意:我还没有测试过这段代码的错误,但它的要点就在那里。