C++使用模板、函数指针、接口和lambda表达式这四种方法做回调函数的区别比较
在C++中,两个类之间存在一种关系,某个类需要另外一个类去完成某一个功能,完成了之后需要告知该类结果,这种最普通最常见的需求,往往使用回调函数来解决。
如题,我总结下来有这么四种方式可以完成这项功能,下面来一一分析:
1、使用模板
1 // CppTest.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <stdlib.h> 6 #include <math.h> 7 8 template<typename T> 9 class MathTemplate 10 { 11 int ops1,ops2; 12 int result; 13 public: 14 void Add(int a,int b,T callback) 15 { 16 ops1 = abs(a); /* 实际上这个函数可能非常复杂,非常耗时,这样回调更突显作用*/ 17 ops2 = abs(b); 18 19 result = ops1+ops2; 20 21 callback.showResult(result); 22 } 23 }; 24 25 class Result 26 { 27 public: 28 void showResult(int res) 29 { 30 printf("result = %d\n",res); 31 } 32 }; 33 34 int _tmain(int argc, _TCHAR* argv[]) 35 { 36 Result reShow; 37 MathTemplate<Result> math; 38 math.Add(1,3,reShow); 39 40 system("pause"); 41 return 0; 42 }
说明:结果类需要知道数学类的处理结果(下面都会使用这个例子),把数学类方法定义为模板函数,回调函数以模板变量的形式传递进去。
优点:两个类耦合度低,数学类不需要知道结果类,结果类因为需要数学类处理,肯定要包括数学类。
缺点:写数学类时,必须要知道结果类有showResult这个方法。
2、使用函数指针
1 // CppTest.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <stdlib.h> 6 #include <math.h> 7 8 class Result; 9 10 typedef void (Result::*CallbackPtr)(int); 11 12 class MathCallBack 13 { 14 int ops1,ops2; 15 int result; 16 public: 17 void Add(int a,int b,Result *caller,CallbackPtr callback) 18 { 19 ops1 = abs(a); /* 实际上这个函数可能非常复杂,非常耗时,这样回调更突显作用*/ 20 ops2 = abs(b); 21 22 result = ops1+ops2; 23 24 (caller->*callback)(result); 25 } 26 }; 27 28 class Result 29 { 30 public: 31 void showResult(int res) 32 { 33 printf("result = %d\n",res); 34 } 35 }; 36 37 int _tmain(int argc, _TCHAR* argv[]) 38 { 39 Result reShow; 40 MathCallBack math; 41 42 math.Add(1,3,&reShow,&Result::showResult); 43 44 system("pause"); 45 46 return 0; 47 }
说明:跟上面一样,结果类需要知道数学类的处理结果,主要注意的是C++函数指针的写法与调用,必须以(对象.*函数指针)(参数)的形式调用。所以,传递回调函数时需要传入调用对象。
缺点:这种方法用起来没有优点,直接说缺点,耦合度高,数学类需要直接知道结果类,数学类不能重用,调用方式写起来也是别扭。
3、使用接口
1 // CppTest.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <stdlib.h> 6 #include <math.h> 7 8 class Result; 9 10 class IProcessResult 11 { 12 public: 13 virtual void ProcessResult(int result)=0; 14 }; 15 16 class MathCallBack 17 { 18 int ops1,ops2; 19 int result; 20 public: 21 void Add(int a,int b,IProcessResult *process) 22 { 23 ops1 = abs(a); /* 实际上这个函数可能非常复杂,非常耗时,这样回调更突显作用*/ 24 ops2 = abs(b); 25 26 result = ops1+ops2; 27 28 process->ProcessResult(result); 29 } 30 }; 31 32 class Result:public IProcessResult 33 { 34 public: 35 void ProcessResult(int res) 36 { 37 printf("result = %d\n",res); 38 } 39 }; 40 41 int _tmain(int argc, _TCHAR* argv[]) 42 { 43 Result reShow; 44 MathCallBack math; 45 46 math.Add(1,3,&reShow); 47 48 system("pause"); 49 50 return 0; 51 }
说明:功能一模一样,一样以回调的方式显示结果。
优点:典型的面向接口编程,即结果类针对结果处理接口编程,不针对具体编程,降低耦合度。
缺点:程序中多了一个接口类,多了一个文件,不要小看多了一个文件,在大型项目工程里,有非常多的类似类之间关系,这样做会多出很多只有一个接口函数的类。
4、使用lambda表达式
1 // CppTest.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include <stdlib.h> 6 #include <math.h> 7 #include <iostream> 8 #include <functional> 9 10 class MathCallBack 11 { 12 int ops1,ops2; 13 int result; 14 15 public: 16 void Add(int a,int b,std::function<void (int)> func) 17 { 18 ops1 = abs(a); /* 实际上这个函数可能非常复杂,非常耗时,这样回调更突显作用*/ 19 ops2 = abs(b); 20 21 result = ops1+ops2; 22 func(result); 23 } 24 }; 25 26 int _tmain(int argc, _TCHAR* argv[]) 27 { 28 MathCallBack math; 29 30 31 math.Add(1,3,[](int result) -> void { 32 printf("result = %d\n",result); 33 }); 34 35 system("pause"); 36 37 return 0; 38 }
说明:功能一模一样,一样以回调的方式显示结果。注意看lambda的回调函数类型哦!
优点:不用多说,整个代码简洁了不知道多少倍,优点无数。
总结:其实写这个博文就是为了学习C++的lambda表达式,在自己的项目中前3中方法都用了,始终感觉耦合度大,代码不简洁。见识过C#中lambda表达式的巨大优势,就知道C++一定能做到。