在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++一定能做到。