• 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
    ",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 }
    View Code

      说明:结果类需要知道数学类的处理结果(下面都会使用这个例子),把数学类方法定义为模板函数,回调函数以模板变量的形式传递进去。

      优点:两个类耦合度低,数学类不需要知道结果类,结果类因为需要数学类处理,肯定要包括数学类。

      缺点:写数学类时,必须要知道结果类有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
    ",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 }
    View Code

      说明:跟上面一样,结果类需要知道数学类的处理结果,主要注意的是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
    ",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 }
    View Code

      说明:功能一模一样,一样以回调的方式显示结果。

      优点:典型的面向接口编程,即结果类针对结果处理接口编程,不针对具体编程,降低耦合度。

      缺点:程序中多了一个接口类,多了一个文件,不要小看多了一个文件,在大型项目工程里,有非常多的类似类之间关系,这样做会多出很多只有一个接口函数的类。

      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
    ",result); 
    33         });
    34 
    35     system("pause");
    36 
    37     return 0;
    38 }
    View Code

      说明:功能一模一样,一样以回调的方式显示结果。注意看lambda的回调函数类型哦!

      优点:不用多说,整个代码简洁了不知道多少倍,优点无数。

      总结:其实写这个博文就是为了学习C++的lambda表达式,在自己的项目中前3中方法都用了,始终感觉耦合度大,代码不简洁。见识过C#中lambda表达式的巨大优势,就知道C++一定能做到。

  • 相关阅读:
    Cesium加载Geowebcache切片
    Vue开发--脚手架的搭建
    OpenLayers动态测量距离和面积,并可自定义测量的线样式
    OpenLayers要素拖拽
    改造SuperMap的DrawHandler接口,自定义绘制的图形样式
    Cesium动态绘制实体(点、标注、面、线、圆、矩形)
    ArcMap制图遇到的小问题
    GeoServer 2.15.2版本跨域问题解决方法
    MySQL 8.0 主从同步
    Service__cmd--MySQL安装并连接SQLyog
  • 原文地址:https://www.cnblogs.com/kanite/p/8299147.html
Copyright © 2020-2023  润新知