五种将c++类的成员函数用作回调函数的方法——史上最全、最简!!!

前言

在写ROS的cpp代码时发现回调函数十分的常用,然后初学的时候没有深入研究过,记录下几种用法,据说在写线程的时候也很常用。方法五应该是最简单实用的了…

方法一

先从最简单的入手,直接使用普通函数用作回调函数

#include "pch.h"
#include <iostream>
using namespace std;
class TestCallback {
public:
	int num = 5;
	void call(void(*callback)(int));
};

void callBack1(int num)
{
	cout << "func1的num = " << num << endl;
}

void TestCallback::call(void(*callback)(int))
{
	cout << "调用回调" << endl;
	callback(this->num);
}

int main()
{
	TestCallback t;
	t.call(callBack1);
}

在这里插入图片描述

方法一总结

这个不是一个好的方法影响了我们cpp的封装特性,也和我们这篇博客的标题不符。但是我们可以看到,我们的调用函数成功调用了回调函数,并且可以访问我们的类成员变量,但是他是通过参数传递来访问我们类的成员变量的。

方法二

使用静态成员函数

#include "pch.h"
#include <iostream>
using namespace std;
class TestCallback {
public:
	int num = 5;
	static int num2;
	void call(void(*callback)(int));
	static void callBack2(int num);
};
int TestCallback::num2 = 6;//静态变量必须初始化

void callBack1(int num)
{
	cout << "------start func1------" << endl;
	cout << "func1的num = " << num << endl;
	cout << "------end func1------" << endl;
}

void TestCallback::call(void(*callback)(int))
{
	cout << "------调用回调------" << endl;
	callback(this->num);
	cout << "------调用结束" << endl;
	cout << endl;
}

void TestCallback::callBack2(int num)
{
	cout << "------start func2------" << endl;
	cout << "func2的num = " << num << endl;
	cout << "func2访问类的静态成员变量num2 = " << TestCallback::num2 << endl;
	cout << "------end func2------" << endl;
}

int main()
{
	TestCallback t;
	t.call(callBack1);
	t.call(TestCallback::callBack2);
}

在这里插入图片描述

方法二总结

我们知道,之所以能用作回调函数,他的前提是该函数的地址是确定的,因为函数指针传递的也就是函数的地址嘛,那么类的成员函数的地址只有在实例化了类的对象后,对象的成员函数的地址才确定了,所以我们不能直接用类的成员函数来做函数指针。
但是静态成员函数就不一样了,静态成员函数相当于是类的全局函数,他的地址是确定的,我们可以通过类名::静态成员函数名这种方式来访问这个函数的地址。
但是!!!使用了静态成员函数就会有别的问题了,静态成员函数无法访问类的非静态成员变量,因此我们例子中的num还是得像方法一那样通过参数传给回调函数,那么我们例子中的num2因为是静态的变量所以就也可以通过类名::静态变量名这种方式来访问。
这里还要注意的一点就是,类的静态成员变量需要在类外面使用前进行初始化,这个是必须的,少了的话编译器会报错找不到这个变量。如代码中这句话:

int TestCallback::num2 = 6;//静态变量必须初始化

方法三

通过友元函数来用作函数指针

#include "pch.h"
#include <iostream>
using namespace std;
class TestCallback {
public:
	int num = 5;
	static int num2;
	void call2(void(*callback)(TestCallback), TestCallback);
	friend void callBack3(TestCallback);
};
int TestCallback::num2 = 6;//静态变量必须初始化

void TestCallback::call2(void(*callback)(TestCallback), TestCallback t)
{
	cout << "------调用回调------" << endl;
	callback(t);
	cout << "------调用结束" << endl;
	cout << endl;
}

void callBack3(TestCallback t)
{
	cout << "------start func3------" << endl;
	cout << "func3的num = " << t.num << endl;
	cout << "func3通过类名访问类的静态成员变量num2 = " << TestCallback::num2 << endl;
	cout << "func3通过对象访问类的静态成员变量num2 = " << t.num2 << endl;
	cout << "------end func3------" << endl;
}

int main()
{
	TestCallback t;
	t.call2(callBack3,t);
}

在这里插入图片描述

方法三总结

友元函数在定义的时候是不需要加上类名的,实质上感觉和普通函数是一个道理。但是它如果想要访问类的非静态变量也是不可以直接访问的,需要在调用函数call2的参数上加上一个类的对象,这样作为参数一直传递下去来访问,访问静态变量则和方法二相同,这种方法感觉不是很实用,一般情况我们只要设计回调函数而不会去修改调用函数。当然了,友元函数我们不把这个类的对象一直作为参数传递下去自然也是可以的,就和普通函数没什么区别。

方法四

联合类转换类成员方法指针到普通函数指针

#include "pch.h"
#include <iostream>
using namespace std;
class TestCallback {
public:
	int num = 5;
	static int num2;
	void call(void(*callback)(void));
	void callBack4();
};
int TestCallback::num2 = 6;//静态变量必须初始化


void TestCallback::call(void(*callback)(void))
{
	cout << "------调用回调------" << endl;
	callback();
	cout << "------调用结束" << endl;
	cout << endl;
}


void TestCallback::callBack4()
{
	cout << "------start func4------" << endl;
	cout << "func4的num2 = " << TestCallback::num2 << endl;
	cout << "------end func4------" << endl;
}

int main()
{
	TestCallback t;
	union {
		void (*callBack4)();
		void (TestCallback::*callBack4_temp)();
	} Union;
	Union.callBack4_temp = &TestCallback::callBack4;
	t.call(Union.callBack4);
}

在这里插入图片描述

方法四总结

这是网上看的花里胡哨转换方法,联合体成员共享一片内存大小,所以他这里用来做函数类型转换。这样的方法还是一样的毛病,在callBack4中没法访问类的非静态成员变量。

方法五

直接使用类成员函数当作回调函数

#include "pch.h"
#include <iostream>
using namespace std;
class TestCallback {
public:
	int num = 5;
	static int num2;
	void call(void *p,void (TestCallback::*callback)(void));
	void callBack5();
};
int TestCallback::num2 = 6;//静态变量必须初始化


void TestCallback::call(void *p,void (TestCallback::*callback)(void))
{
	cout << "------调用回调------" << endl;
	TestCallback *temp = (TestCallback *)p;
	(temp->*callback)();
	cout << "------调用结束" << endl;
	cout << endl;
}


void TestCallback::callBack5()
{
	cout << "------start func5------" << endl;
	cout << "func5的num = " << this->num << endl;
	cout << "func5的num2 = " << this->num2 << endl;
	cout << "------end func5------" << endl;
}

int main()
{
	TestCallback t;
	t.call(&t,&TestCallback::callBack5);
}

在这里插入图片描述

方法五总结

这个方法应该和ROS底层写调用回调函数的原理相同,我们这里的原理就是在调用函数中,我们知道回调函数的类的地址,又知道这个类的回调函数相对于类的地址,这样我们相当于明确了函数的具体地址,就可以直接用成员函数当作回调函数。。。好像说不明白好像又说明白了。。。不知道对不对。这种方法在使用的时候应该是最为方便的了,不需要定义静态函数就可以访问类的成员函数和变量

总结

在网上搜了下还有boost库的function和bind函数可以用来解决这个问题,这里就先记录这五种方法,第五种方法也算是让我搞懂了ROS的回调函数为啥可以使用成员函数了,他在传参数的时候显式的传递了this指针,应该是底层将this指针的类型转换成了对应的类的类型,然后调用回调函数。

发布了81 篇原创文章 · 获赞 21 · 访问量 8811

猜你喜欢

转载自blog.csdn.net/qq_37668436/article/details/105163037