类成员函数作为回调函数的方法及注意点

编程中遇到一个错误,提示为error C2597: illegal reference to non-static member

即因为一个类的静态成员函数调用了类的非静态成员变量,而报错。

下面具体介绍一些相关知识点,以防下次再出错。

类成员函数当回调函数的方法

参考自:https://blog.youkuaiyun.com/this_capslock/article/details/17001003

方法一:回调函数为普通的全局函数,但在函数体内执行类的成员函数

在创建线程调用回调函数时,传入类对象的指针(比如this指针)作为参数,并在回调函数中把void*强制转换为类的指针(MyClass*),就能使用该指针调用类的成员函数。

这样做的原理是把当前对象的指针当作参数先交给一个外部函数,再由外部函数调用类成员函数。以外部函数作为回调函数,但执行的是成员函数的功能,这样相当于在中间作了一层转换。

缺点:回调函数在类外,影响了封装性。

方法二:回调函数为类内静态成员函数,在其内部调用类的非静态成员函数

此时需要一个指向类本身的、类的静态成员变量指针(static MyClass* CurMy),用来存储当前回调函数调用的对象,相当于法1中给回调函数传入的指针参数。在回调函数中通过CurMy指针调用类的成员函数。

优点:1、解决了法1的封装性问题,

           2、没有占用callback的参数,可以从外界传递参数进来

缺点:每个对象启动子线程前一定要注意先让CurMy正确的指向自身,否则将为其它对象开启线程。

方法三:对成员函数进行强制转换,使其作为回调函数

这个方法是原理是,MyClass::func最终会转化成 void func(MyClass *this);即在原第一个参数前插入指向对象本身的this指针。可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数。

typedef void* (*FUNC)(void*);
FUNC callback = (FUNC)&MyClass::func;

对编译器而言,void (MyClass::*FUNC1)()和void* (*FUNC)(void*)这两种函数指针虽然看上去很不一样,但他们的最终形式是相同的,因此就可以把成员函数指针强制转换成普通函数的指针来当作回调函数。在建立线程时要把当前对象的指针this当作参数传给回调函数(成员函数func),这样才能知道线程是针对哪个对象建立的。

注意:此方法中FUNC函数的参数一定要是void*,这样才能在编译后把this指针转变为MyClass *this。

优点:法3的封装性比法2更好,因为不涉及多个对象共用一个静态成员的问题,每个对象可以独立地启动自己的线程而不影响其它对象。

为什么回调函数必须为静态函数?

普通的C++成员函数都隐含了一个“this”指针参数,当在类的非静态成员函数中访问类的非静态成员时,C++编译器通过传递一个指向对象本身的指针给其成员函数,从而能够访问类的数据成员。也就是说,即使你没有写上this指针,编译器在编译的时候自动加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。

正是由于this指针的作用,使得将一个CALLBACK型的成员函数作为回调函数时就会因为隐含的this指针使得函数参数个数不匹配,从而导致回调函数匹配失败。所以为了实现回调,类中的成员函数必须舍弃掉隐藏的this指针参数。因此,类中的回调函数必须为静态函数,加上static关键字。

类的静态成员函数如何访问非静态成员?

静态成员不属于某个具体的对象,而是被所有对象所共享。即静态成员属于整个类,不属于具体某个对象;非静态成员属于具体某个对象。因而静态成员函数只能访问类的静态成员,不能访问类中非静态成员。

那么,如何让静态函数访问类的非静态成员?

方法是:对于静态成员函数,我们显示的为其传递一个对象的首地址(该类的指针)。一般在这个静态成员函数的形参列表中加入一个  void*  类型的参数,来保存对象的首地址。并在该函数内部对该参数进行类型转换,通过类型转换后的参数来调用非静态成员。

或者用一个类的全局指针数组,保存每一个创建出来的类的this指针,用全局指针去调用。

 

如qsort 等函数需要函数指针才能回调 用此函数库可以将成员函数指针转为普通函数指针 测试代码如下 #include <stdio.h> #include <algorithm> #include <vector> #include <string> #include <iostream> #include <math.h> using cmpfunc = int(__cdecl*)(const void*, const void*); using DebugArrayFunc = void(__stdcall *)(std::string &out;); #include "thunk.h" class MySort { public: int Rule; MySort(int a):Rule(a){} // 回调函数 template<typename T> int __cdecl sort(const void* a, const void* b); }; class Test { public: std::vector<int> mm; void Sort(int (*comp)(const void *,const void *)) { return qsort(mm._Myfirst,mm.size(),sizeof(int),comp); } void Entry(DebugArrayFunc func) { std::string string; cmpfunc comp; TemplateThunk athunk; // 正序 comp = (cmpfunc)athunk.GetCall(&MySort;::sort<int>, &MySort;(0)); Sort(comp); func(string); std::cout << string << std::endl; // 逆序 comp = (cmpfunc)athunk.GetCall(&MySort;::sort<int>, &MySort;(1)); Sort(comp); func(string); std::cout << string << std::endl; } }; class CallBack { public: std::vector<int> *pthis; CallBack(std::vector<int> *ff):pthis(ff){} void __stdcall DebugArray(std::string &out;) { char buff[100]; char *aa = buff; for each (auto a in *pthis) { aa += sprintf(aa, "%d ", a); } out.assign(buff); } }; void main() { TemplateThunk athunk; Test tt; tt.mm = { 1, 3, 7, 8, 5, 6, 4, 2, 3, 10 }; tt.Entry(athunk.GetCall(&CallBack;::DebugArray,&CallBack;(&tt;.mm))); } template <typename T> int __cdecl MySort::sort(const void* a, const void* b) { return Rule ? *static_cast<const T*>(a)-*static_cast<const T*>(b) : *static_cast<const T*>(b)-*static_cast<const T*>(a); }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值