1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > C++:函数:回调函数:还不懂回调函数来捶我(二)

C++:函数:回调函数:还不懂回调函数来捶我(二)

时间:2019-11-16 08:51:00

相关推荐

C++:函数:回调函数:还不懂回调函数来捶我(二)

回调函数目录

1:什么是回调函数

2:为什么需要回调函数

3:有哪些函数可以做回调函数

4:小结

1:什么是回调函数

回调函数本质上也是普通函数,只是调用机制有所区别,----首先通过传参的形式将该函数的地址传递给其他函数,然后在其他函数中通过函数指针调用该函数,那么在其他函数中通过函数指针调用该函数的过程就称为:回调。而作为被调用的该函数则被称为回调函数。下面我们一步一步解释,为什么需要回调函数。

2:为什么需要回调函数

1: 这就不得不说联合开发带来的后果,-----接口兼容性问题,举个例子:程序员小A和小B联合开发一个项目,要求小A开发的函数必须为小B开发的函数提供灵活的接口。

// test.h#pragma once#include<iostream>using namespace std;// 声明一个接口,提供给小B,开发int add(int, int);int Add(int a, int b) {cout << add(a, b) << endl;}// main.cpp#include "test.h"#include<iostream>#include<string>using namespace std;// 小B定义接口。是不是和JAVA里面接口回调很相似。int add(int a, int b) {return a + b;}void main() {Add(1, 2);}

2: 现在为了并行开发:开发内容没有变化,但是需要小B单独在另一个文件中开发,来提高工作效率。

// test.h#pragma once#include<iostream>using namespace std;// 声明一个接口,提供给小B,开发int add(int, int);inline void Add(int a, int b) {cout << add(a, b) << endl;}// test.cpp#include "test.h"#include<iostream>#include<string>using namespace std;// 小B定义接口。是不是和JAVA里面接口回调很相似。int add(int a, int b) {return a + b;}// main.cpp#include "test.h"#include<iostream>#include<string>using namespace std;void main() {Add(1, 2);}

3: 但是这-似乎也没什么,不就是在一个函数中调用另一个函数吗?(简单来说就是:小A声明一个接口,小B定义这个接口,然后在合适时机小B定义的这个函数被调用。)

但是你是否意识到为了实现上面功能:小A和小B必须提前商量好接口的名称,返回值,参数列表。对于小A来说,每次调用小B函数,需要先声明,函数多了将会非常麻烦对于小B来说,由于小A定义的函数体对于小B来说是不知道的,小B的函数是如何传入小A也是不那么直观,而且小B定义函数必须和小A声明的函数保持一致。那么为了解决上述问题,回调函数登场了

2.1:回调函数登场

1:小A不再需要每次调用小B定义的函数之前,都要进行声明。

2:小A只需要提供一个函数指针来接收小B传过来的函数地址,而不用在考虑函数名

3:小A只需要用这个指针可以直接调用小B定义的函数。

4:小B可以自己起函数名。

5:通过函数指针实现回调函数

// test.h#pragma once#include<iostream>using namespace std;// 定义一个匿名的函数指针inline void Add(int(*callbackFun)(int, int), int a, int b) {cout << callbackFun(a, b) << endl;}// test.cpp#include "test.h"#include<iostream>#include<string>using namespace std;// 小B定义接口。是不是和JAVA里面接口回调很相似。inline int add(int a, int b) {return a + b;}// main.cpp#include "test.h"#include "test.cpp"#include<iostream>#include<string>using namespace std;void main() {Add(add,1, 2);}

显然从上面的例子可以看出:回调函数必须通过指针进行传递和调用,为了简化代码,一般会将函数指针起各别名,格式为:

typedef 返回值类型 (*指针名) (参数列表)

回调函数规避了在调用函数前声明的弊端,而且能够让用户直观的感受到自动定义的函数被调用,小A需要在声明函数指针时规定参数列表,小B在定义回调函数时需要与小A声明的函数指针保持相同的参数列表。

当然如果小A提前为回调函数形参设置了默认值,那么小B也可以决定不使用。

// test.h#pragma once#include<iostream>using namespace std;// 定义函数指针类型typedef int(*callbackFun)(int, int, int);inline void Add(callbackFun callback, int a, int b,int c = 10) {cout << callback(a, b,c) << endl;}// test.cpp#include "test.h"#include<iostream>#include<string>using namespace std;// 小B定义接口。是不是和JAVA里面接口回调很相似。inline int add(int a, int b,int c) {return a + b +c;}// main.cpp#include "test.h"#include "test.cpp"#include<iostream>#include<string>using namespace std;void main() {Add(add,1, 2);}

3: 有哪些函数可以做回调函数

可以做回调函数的函数在C++中目前有两种情况,第一种C语言风格函数,第二种静态成员函数。第一种就是上面的格式,这个我们已经做 了介绍,下面详细说说静态成员函数。

3.1: 静态成员函数做回调函数

众所周知:类的非静态成员函数的参数列表中隐含了一个this指针,当用类的对象访问类的非静态成员函数时,编译器慧将this指针指向该对象,从而保证了函数体中操纵的成员变量是该对象的成员变量,即使你没有写this指针,编译器在编译的时候,还是会自动添加this指针。

那么这就会造成一个问题:非静态成员函数的参数列表和函数指针参数列表个数无法匹配。

如下所示:函数指针callbackFun有两个参数,非静态成员函数隐含this指针,从而有三个参数,显然不匹配。

#include<iostream>#include<string>using namespace std;class AddClass {private:int a, b;public:int add(int a, int b);};int AddClass::add(int aa, int bb) {this->a = aa;this->b = bb;return a + b;}void Add(int(*callbackFun)(int, int), int c =0, int d = 0) {cout << callbackFun(c, d) << endl;}void main() {//Add(add,1, 2);Add(AddClass().add, 1, 2); // error : 指向绑定函数的指针只能用于调用函数// 显然非静态成员函数参数列表有 三个参数// 而 函数指针:int(*callbackFun)(int ,int) 只有两个参数}

但是你如果执着,使用非静态成员函数,就只能通过全局函数中转,如下例所示:

#include<iostream>#include<string>using namespace std;class AddClass {private:int a, b;public:int add(int a, int b);};int AddClass::add(int aa, int bb) {a = aa;b = bb;return a + b;}void Add(int(*callbackFun)(int, int), int c =0, int d = 0) {cout << callbackFun(c, d) << endl;}int MyAdd(int a, int b) {AddClass addClass;return addClass.add(a, b);}void main() {//Add(add,1, 2);Add(MyAdd, 1, 2);}

由于:类的静态成员函数属于类,为所有对象共享,它没有this指针,因此这里我们采用静态成员函数作为回调函数,但是这样我们遇到另一一个问题:静态成员函数无法方位类的非静态成员。

因此为了解决上述这个问题,我们需要在静态成员函数参数列表中做个修改:

在形参列表中加入 万能指针 *void 作为形参,然后在函数体中做类型强转。

// test.h#pragma once#include<iostream>using namespace std;// 定义函数指针类型typedef int(*callbackFun)(int, int, void*);inline void Add(callbackFun callback, int a, int b,void* p) {cout << callback(a, b, p) << endl;}// test.cpp#include "test.h"#include<iostream>#include<string>using namespace std;class AddClass {private:int a, b;public:inline static int add(int a, int b,void* temp);};int AddClass::add(int aa, int bb,void* p) {AddClass* temp = (AddClass*)p;if (temp){temp->a = aa;temp->b = bb;}return temp->a + temp->b;}// main.cpp#include "test.cpp"#include<iostream>#include<string>using namespace std;void main() {//Add(add,1, 2);AddClass* addclass = new AddClass;Add(AddClass::add, 1, 2, addclass);delete addclass;}

4: 小结

1:回调函数本质其实是对函数指针的一种应用,上面的例子都比较简单,还没有完成体现回调函数的威力。

2:回调函数是一种设计系统的思想,能够解决系统架构中的部分问题,但是系统中不宜过多使用回调函数,因为回调函数会改变整个系统运行轨迹和执行顺序,耗费资源,而且会使代码变得臃肿

3:C++ STL中大量使用了回调函数的例子,比如遍历函数 for_each()中的 lambda表达式就是一个回调函数。

#include<iostream>#include<vector>#include<algorithm>using namespace std;int main(){vector<int> v{1,2,3,4,5};for_each (v.begin(), v.end(), [=](int val){cout<< val<< " "<< endl;});}

五:回调函数使代码延迟执行

#include <functional>#include<iostream>#include<string>using namespace std;class A{std::function<void()> callback_;public:A(const std::function<void()>& f) :callback_(f) {cout << "构造函数" << endl;};void notify(void){cout << "notify" << endl;callback_();}};class Foo {public:void operator()(void){// __FUNCTION__是对应的函数名std::cout << __FUNCTION__ << std::endl;}};int main(void){Foo foo;// 仿函数调用foo();// 仿函数对象表示:匿名的 functionA aa(foo);aa.notify();}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。