设置一个回调函数,它是一个类的非静态成员函数

use*_*379 5 c++

typedef void (*CALLBACK)();
class Filter
{
public:
     void callback()
    {
        cout << "callback" << endl;
    }
};

void SetCallback(CALLBACK pCallBack )
{
    pCallBack();
}



int main()
{
    Filter f;
    SetCallback(f.callback);
}
Run Code Online (Sandbox Code Playgroud)

主要是SetCallback(f.callback); 声明给出了错误.任何人都可以帮我解决这个问题

Seb*_*edl 7

问题是成员函数不是没有参数的普通函数,因为它总是有隐含this参数.

如果您遇到需要普通回调函数但没有用户上下文参数的遗留C接口(void*函数只是传递给回调),则会出现问题.

如果你有用户上下文,那很容易.将对象指针作为上下文传递,并使用包装函数作为实际回调:

typedef void (*CALLBACK)(void*);
class Filter
{
public:
    static void CallbackWrapper(void* context) {
        static_cast<Filter*>(context)->callback();
    }

private:
    void callback();
};

int main() {
    Filter f;
    SetCallback(&Filter::CallbackWrapper, &f);
}
Run Code Online (Sandbox Code Playgroud)

如果您没有上下文,可以选择以下选项:

  • 将对象存储在全局变量中并从包装器访问它.这具有使用全局变量的明显缺点,并且不允许以这种方式允许多个回调.对于长时间运行的回调,这非常糟糕.
  • 对上述内容的一个小改进是使用线程局部全局变量.对于严格范围的回调,这很有趣,例如,您调用的函数会立即多次使用您的回调然后返回.想一想qsort().至少这种方式,您不会遇到线程安全问题.仍然不是长时间回调的选择.
  • 最后,该选项适用于大多数平台,但需要做很多工作,您可以在运行时生成存根函数,嵌入对象指针.这基本上意味着分配一块内存,如果平台使用它,则禁用该内存上的执行保护,并将机器代码放在那里加载对象指针并在其上调用函数.

最后一个选项仍然有很多缺点:它是非常特定于平台的,甚至可能根本不用于某些(你不能在iOS,AFAIK中禁用执行保护),它是特定于CPU的(因为你需要生成正确的代码)对于每个),存在管理存根的内存的问题.另一方面,有时它是唯一可行的.Delphi有时会为它的窗口和钩子程序做这种事情,而ATL也是如此.


Ste*_*eve 5

这是我用来实现对成员函数指针的回调的方法。

它可能需要 C++11。

#include <iostream>
#include <string>
#include <functional>

using namespace std;

struct MessageSource
{
       function<void(const string& msg)> _callback;

       template<typename A, typename B>
       void connect(A func_ptr, B obj_ptr)
       {
              _callback = bind(func_ptr, obj_ptr, placeholders::_1);
       }

       void send_msg(const string& msg)
       {
              if (_callback)
                     _callback(msg);
       }

       void disconnect()
       {
              _callback = nullptr;
       }
};

struct Printer
{
       void print(const string& msg) { std::cout << msg << std::endl; };
};

int main()
{
       {
              Printer p;
              MessageSource s;
              s.connect(&Printer::print, &p);
              s.send_msg("test");
              s.disconnect();
              s.send_msg("test again");
       }

       system("pause");
       return 0;
}
Run Code Online (Sandbox Code Playgroud)


Ser*_*sta 0

您应该考虑回调到底是什么以及如何调用成员函数。

当您提供回调函数时,您只需提供稍后将使用您通常无法控制的参数进行调用的函数的地址。

当调用成员函数时,它的第一个参数是指针this,即对调用该方法的对象的引用。

这就是为什么不能使用成员方法作为回调的原因。您只能使用不需要特殊参数(对于程序员来说是隐式的,但从编译器角度来看是真实的)的真正函数或静态成员函数this