赞
踩
1:什么是回调函数
2:为什么需要回调函数
3:有哪些函数可以做回调函数
4:小结
回调函数本质上也是普通函数,只是调用机制有所区别,----首先通过传参的形式将该函数的地址传递给其他函数,然后在其他函数中通过函数指针调用该函数,那么在其他函数中通过函数指针调用该函数的过程就称为:回调。而作为被调用的该函数则被称为回调函数。下面我们一步一步解释,为什么需要回调函数。
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定义的这个函数被调用。)
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);
- }
可以做回调函数的函数在C++中目前有两种情况,第一种C语言风格函数,第二种静态成员函数。第一种就是上面的格式,这个我们已经做 了介绍,下面详细说说静态成员函数。
众所周知:类的非静态成员函数的参数列表中隐含了一个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;
- }
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();
- // 仿函数对象表示:匿名的 function
- A aa(foo);
- aa.notify();
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。