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