当前位置:   article > 正文

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

回调函数c++

回调函数目录

1:什么是回调函数

2:为什么需要回调函数

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

4:小结

1:什么是回调函数 

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

2:为什么需要回调函数

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

  1. // test.h
  2. #pragma once
  3. #include<iostream>
  4. using namespace std;
  5. // 声明一个接口,提供给小B,开发
  6. int add(int, int);
  7. int Add(int a, int b) {
  8. cout << add(a, b) << endl;
  9. }
  10. // main.cpp
  11. #include "test.h"
  12. #include<iostream>
  13. #include<string>
  14. using namespace std;
  15. // 小B定义接口。是不是和JAVA里面接口回调很相似。
  16. int add(int a, int b) {
  17. return a + b;
  18. }
  19. void main() {
  20. Add(1, 2);
  21. }

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

  1. // test.h
  2. #pragma once
  3. #include<iostream>
  4. using namespace std;
  5. // 声明一个接口,提供给小B,开发
  6. int add(int, int);
  7. inline void Add(int a, int b) {
  8. cout << add(a, b) << endl;
  9. }
  10. // test.cpp
  11. #include "test.h"
  12. #include<iostream>
  13. #include<string>
  14. using namespace std;
  15. // 小B定义接口。是不是和JAVA里面接口回调很相似。
  16. int add(int a, int b) {
  17. return a + b;
  18. }
  19. // main.cpp
  20. #include "test.h"
  21. #include<iostream>
  22. #include<string>
  23. using namespace std;
  24. void main() {
  25. Add(1, 2);
  26. }

 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:通过函数指针实现回调函数

  1. // test.h
  2. #pragma once
  3. #include<iostream>
  4. using namespace std;
  5. // 定义一个匿名的函数指针
  6. inline void Add(int(*callbackFun)(int, int), int a, int b) {
  7. cout << callbackFun(a, b) << endl;
  8. }
  9. // test.cpp
  10. #include "test.h"
  11. #include<iostream>
  12. #include<string>
  13. using namespace std;
  14. // 小B定义接口。是不是和JAVA里面接口回调很相似。
  15. inline int add(int a, int b) {
  16. return a + b;
  17. }
  18. // main.cpp
  19. #include "test.h"
  20. #include "test.cpp"
  21. #include<iostream>
  22. #include<string>
  23. using namespace std;
  24. void main() {
  25. Add(add,1, 2);
  26. }

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

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

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

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

  1. // test.h
  2. #pragma once
  3. #include<iostream>
  4. using namespace std;
  5. // 定义函数指针类型
  6. typedef int(*callbackFun)(int, int, int);
  7. inline void Add(callbackFun callback, int a, int b,int c = 10) {
  8. cout << callback(a, b,c) << endl;
  9. }
  10. // test.cpp
  11. #include "test.h"
  12. #include<iostream>
  13. #include<string>
  14. using namespace std;
  15. // 小B定义接口。是不是和JAVA里面接口回调很相似。
  16. inline int add(int a, int b,int c) {
  17. return a + b +c;
  18. }
  19. // main.cpp
  20. #include "test.h"
  21. #include "test.cpp"
  22. #include<iostream>
  23. #include<string>
  24. using namespace std;
  25. void main() {
  26. Add(add,1, 2);
  27. }

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

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

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

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

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

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

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class AddClass {
  5. private:
  6. int a, b;
  7. public:
  8. int add(int a, int b);
  9. };
  10. int AddClass::add(int aa, int bb) {
  11. this->a = aa;
  12. this->b = bb;
  13. return a + b;
  14. }
  15. void Add(int(*callbackFun)(int, int), int c =0, int d = 0) {
  16. cout << callbackFun(c, d) << endl;
  17. }
  18. void main() {
  19. //Add(add,1, 2);
  20. Add(AddClass().add, 1, 2); // error : 指向绑定函数的指针只能用于调用函数
  21. // 显然非静态成员函数参数列表有 三个参数
  22. // 而 函数指针:int(*callbackFun)(int ,int) 只有两个参数
  23. }

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

  1. #include<iostream>
  2. #include<string>
  3. using namespace std;
  4. class AddClass {
  5. private:
  6. int a, b;
  7. public:
  8. int add(int a, int b);
  9. };
  10. int AddClass::add(int aa, int bb) {
  11. a = aa;
  12. b = bb;
  13. return a + b;
  14. }
  15. void Add(int(*callbackFun)(int, int), int c =0, int d = 0) {
  16. cout << callbackFun(c, d) << endl;
  17. }
  18. int MyAdd(int a, int b) {
  19. AddClass addClass;
  20. return addClass.add(a, b);
  21. }
  22. void main() {
  23. //Add(add,1, 2);
  24. Add(MyAdd, 1, 2);
  25. }

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

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

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

  1. // test.h
  2. #pragma once
  3. #include<iostream>
  4. using namespace std;
  5. // 定义函数指针类型
  6. typedef int(*callbackFun)(int, int, void*);
  7. inline void Add(callbackFun callback, int a, int b,void* p) {
  8. cout << callback(a, b, p) << endl;
  9. }
  10. // test.cpp
  11. #include "test.h"
  12. #include<iostream>
  13. #include<string>
  14. using namespace std;
  15. class AddClass {
  16. private:
  17. int a, b;
  18. public:
  19. inline static int add(int a, int b,void* temp);
  20. };
  21. int AddClass::add(int aa, int bb,void* p) {
  22. AddClass* temp = (AddClass*)p;
  23. if (temp)
  24. {
  25. temp->a = aa;
  26. temp->b = bb;
  27. }
  28. return temp->a + temp->b;
  29. }
  30. // main.cpp
  31. #include "test.cpp"
  32. #include<iostream>
  33. #include<string>
  34. using namespace std;
  35. void main() {
  36. //Add(add,1, 2);
  37. AddClass* addclass = new AddClass;
  38. Add(AddClass::add, 1, 2, addclass);
  39. delete addclass;
  40. }

4:  小结

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

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

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

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

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

  1. #include <functional>
  2. #include<iostream>
  3. #include<string>
  4. using namespace std;
  5. class A
  6. {
  7. std::function<void()> callback_;
  8. public:
  9. A(const std::function<void()>& f) :callback_(f) {
  10. cout << "构造函数" << endl;
  11. };
  12. void notify(void)
  13. {
  14. cout << "notify" << endl;
  15. callback_();
  16. }
  17. };
  18. class Foo {
  19. public:
  20. void operator()(void)
  21. {
  22. // __FUNCTION__是对应的函数名
  23. std::cout << __FUNCTION__ << std::endl;
  24. }
  25. };
  26. int main(void)
  27. {
  28. Foo foo;
  29. // 仿函数调用
  30. foo();
  31. // 仿函数对象表示:匿名的 function
  32. A aa(foo);
  33. aa.notify();
  34. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/162495
推荐阅读
相关标签
  

闽ICP备14008679号