问题描述
当非静态类成员函数作为回调函数时,如果不做任何处理会报错。如下面的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> #include <functional> #include <vector>
class Test{ public: Test(){}
void Print(int i) { std::cout << i << std::endl; }
void SetValue(int i) { v = i; }
void PrintValue() { std::cout << v << std::endl; } private: int v = 0; };
void callback(void(*f)(int)) { f(1); }
int main() { Test test; test.PrintValue(); callback(&Test::Print); return 0; }
|
编译时会报错:
1
| error: cannot convert 'void (Test::*)(int)' to 'void (*)(int)'
|
如果这个函数不使用其他类成员变量和非静态成员函数,那么可以把它声明为静态。这样调用是没问题的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> #include <functional> #include <vector>
class Test{ public: Test(){}
static void Print(int i) { std::cout << i << std::endl; }
void SetValue(int i) { v = i; }
void PrintValue() { std::cout << v << std::endl; } private: int v = 0; };
void callback(void(*f)(int)) { f(1); }
int main() { Test test; test.PrintValue(); callback(&Test::Print); return 0; }
|
但很多时候我们都需要使用类成员变量,例如类中的另一个SetValue
函数,必须保留为非静态函数。
此时问题根源在于非静态成员函数调用时有一个隐藏参数this,
表示它与一个类实例相对应。我们在调用时必须把类实例的地址也传给callback函数。
解决方法一:将其硬设成静态函数
设成静态函数后我们无法直接使用类内非静态成员变量,但如果我们把类地址传给这个静态函数,就可以读取,改变这个类的成员变量。因此我们需要给setValue
函数加上Test *
类型的变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream> #include <functional> #include <vector>
class Test{ public: Test(){}
static void Print(int i) { std::cout << i << std::endl; }
static void SetValue(Test *test, int i) { test->v = i; }
void PrintValue() { std::cout << v << std::endl; } private: int v = 0; };
void callback(void(*f)(Test *, int), Test *test) { f(test, 1); }
int main() { Test test; test.PrintValue(); callback(&Test::SetValue, &test); test.PrintValue(); return 0; }
|
这样我们实现了用类内静态函数调用类内非静态成员变量的功能。
解决方法二:bind
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <iostream> #include <functional> #include <vector>
class Test{ public: Test(){}
static void Print(int i) { std::cout << i << std::endl; }
void SetValue(int i) { v = i; }
void PrintValue() { std::cout << v << std::endl; } private: int v = 0; };
int main() { Test test; test.PrintValue(); auto _callback = std::bind(&Test::SetValue, &test, std::placeholders::_1); _callback(1); test.PrintValue(); return 0; }
|
thread可以直接用(&A::f,
&a, params)的形式
如下面的例子:当thread线程的回调函数是非静态类成员函数时,由于print函数有隐藏参数this,而thread可以直接传入各个参数,这样就能够正确调用实例a的函数print
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include<iostream> #include<vector> #include<algorithm> #include<thread>
using namespace std;
class A{ public: A(int num){num_ = num};
void print(int a) { cout << num_ + a << endl; }
private: int num_ = 0; };
int main() { A a(1); thread t(&A::print, &a, 1); t.join(); return 0; }
|
当这个函数调用发生在类内时,只需要用this取代&a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include<iostream> #include<vector> #include<algorithm> #include<thread>
using namespace std;
class A{ public: A(int num){num_ = num};
void callback() { thread t(&A::print, this, 1); t.join(); }
void print(int a) { cout << num_ + a << endl; }
private: int num_ = 0; };
int main() { A a(1); a.callback(); return 0; }
|
解决方法三:设置中转函数
如果需要回调函数的接口不方便自由修改,只能传入一个函数指针,比如是某个编写好的库,那我们可以加一个中转函数,此时需要test实例为全局变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <iostream> #include <functional> #include <vector>
class Test{ public: Test(){}
static void Print(int i) { std::cout << i << std::endl; }
void SetValue(int i) { v = i; }
void PrintValue() { std::cout << v << std::endl; } private: int v = 0; };
void callback(void(*f)(int)) { f(1); }
Test test; static void newSetValue(int a) { test.SetValue(a); }
int main() { test.PrintValue(); callback(newSetValue); test.PrintValue(); return 0; }
|