所以我目前正在为C++游戏开发输入管理器。我正在考虑将其作为map<char, list<Listener::method>>
来做,这样当我注册按下的按钮时,我会调用为特定键注册的所有方法。
问题是,这样做我必须指定要从中调用该方法的实例。有没有办法在变量中不仅保存方法,而且保存调用方实例?
像这样:
class Foo
{
public:
Foo ();
~Foo ();
void DoSomething();
};
Foo fooObject;
void(Foo::*fPtr)(void) = &fooObject::DoSomething;
(*fPtr)();
这就叫fooObject
DoSomething()
可能吗?
如果没有,有人可以指出我的事件侦听器模式吗?不必过于复杂或线程安全。只是基本结构。
成员函数指针的问题在于你需要一个类的实例来调用它们。 仅使用指针无法实现此目的,因为指针仅指向函数,而不是需要传递给函数的其他对象(请记住,所有成员函数都有一个采用类类型的隐式第一个参数)。
可以使用std::function
来拥有一个函数对象,该对象保存要调用的对象和要调用的函数。 如果你有
std::function<void()> func = [&](){ return fooObject.DoSomething(); }; // Bind with a reference. Use this if the object will live as long as the function to save on a copy
std::function<void()> func = [=](){ return fooObject.DoSomething(); }; // Bind with a copy. Use this if the function will outlive the object
然后,当您使用 func()
调用 func
时,它将运行 Lambda 的主体,该 lambda 在 fooObject
上调用DoSomething
。
您还可以使用 std::bind
将成员函数指针绑定到对象以调用它,但 lambda 通常是首选,因为语法易于使用。
有没有办法在变量中不仅保存方法,而且保存调用方实例?
是的,您可以将方法和对象绑定在一起。
自 C++11 以来,您拥有bind
实用程序。
您的概念中的最小示例:
struct Foo {
void foo();
};
void bar() {
// your map
std::map<char, std::function<void()>> actions;
// Bind method and object together inside fn
Foo foo;
auto fn = std::bind(&Foo::foo, &foo);
// Insert fn into map
actions.emplace('f', fn);
// call fn from map! Be careful about object lifetime
actions['f']();
}
戈博尔特代码
请注意,因为"绑定对象"(在我的示例中fn
)将只存储对对象的引用foo
。因此,如果在对象foo
被销毁后调用fn
,您将获得未定义的行为。
如果您想关心对象生存期,可以使用 lambda 函数复制它。例如:
auto fn = [foo]() { foo.foo(); }
或者使用自定义结构并将复制的对象存储在其中。
没有"来自特定实例的方法"这样的东西。
- 可以按住指向类的成员函数的指针。此类指针不绑定到任何特定对象。
- 可以在类的任何对象上使用该函数指针。
鉴于
class Foo
{
public:
Foo ();
~Foo ();
void DoSomething();
};
任何类都可以保存指向 DoSomething
成员函数的指针(您将其称为方法)。
void(Foo::*fPtr)(void) = &Foo::DoSomething;
您需要一个对象才能调用成员函数。调用语法有点迟钝,但就在这里。
Foo foo = <some function call>;
(foo.*fPtr)();
您可以使用 bind
-function 来获取函数对象,该函数参数已绑定到参数。这样,您可以将对象引用(隐式是任何非静态成员函数中的第一个参数)绑定到您希望为其进行回调的相应对象:
class Foo
{
public:
int x;
Foo (int _x) : x(_x) {} ;
void doSomething() { cout << x << endl; }
};
int main() {
Foo fooObject1(1);
Foo fooObject2(2);
std::function<void ()> fx1 = std::bind(&Foo::doSomething,&fooObject1);
std::function<void ()> fx2 = std::bind(&Foo::doSomething,&fooObject2);
std::vector<std::function<void ()>> callbacks;
callbacks.push_back(fx1);
callbacks.push_back(fx2);
for (auto f : callbacks) {
f();
}
}
输出:
1
2