在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(,,reShow); system("pause");
return ;
}

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

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

  缺点:写数学类时,必须要知道结果类有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(,,&reShow,&Result::showResult); system("pause"); return ;
}

  说明:跟上面一样,结果类需要知道数学类的处理结果,主要注意的是C++函数指针的写法与调用,必须以(对象.*函数指针)(参数)的形式调用。所以,传递回调函数时需要传入调用对象。

  缺点:这种方法用起来没有优点,直接说缺点,耦合度高,数学类需要直接知道结果类,数学类不能重用,调用方式写起来也是别扭。

  3、使用接口

 // CppTest.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include <stdlib.h>
#include <math.h> class Result; class IProcessResult
{
public:
virtual void ProcessResult(int result)=;
}; 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(,,&reShow); system("pause"); return ;
}

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

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

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

  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(,,[](int result) -> void {
printf("result = %d\n",result);
}); system("pause"); return ;
}

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

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

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

C++使用模板、函数指针、接口和lambda表达式这四种方法做回调函数的区别比较的更多相关文章

  1. java四种引用与回调函数

    JAVA四种引用 java对象的引用包括: 强引用 软引用 弱引用 虚引用 Java中提供这四种引用类型主要有两个目的: 第一是可以让程序员通过代码的方式决定某些对象的生命周期: 第二是有利于JVM进 ...

  2. Lambda01 编程范式、lambda表达式与匿名内部类、函数式接口、lambda表达式的写法

    1 编程范式 主要的编程范式有三种:命令式编程,声明式编程和函数式编程. 1.1 命令式编程 关注计算机执行的步骤,就是告诉计算机先做什么后做什么 1.2 声明式编程 表达程序的执行逻辑,就是告诉计算 ...

  3. 使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测

    概述 单测是提升软件质量的有力手段.然而,由于编程语言上的支持不力,以及一些不好的编程习惯,导致编写单测很困难. 最容易理解最容易编写的单测,莫过于独立函数的单测.所谓独立函数,就是只依赖于传入的参数 ...

  4. C#函数式程序设计之函数、委托和Lambda表达式

    C#函数式程序设计之函数.委托和Lambda表达式 C#函数式程序设计之函数.委托和Lambda表达式   相信很多人都听说过函数式编程,提到函数式程序设计,脑海里涌现出来更多的是Lisp.Haske ...

  5. 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

  6. Java核心技术-接口、lambda表达式与内部类

    本章将主要介绍: 接口技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口. lambda表达式:这是一种表示可以在将来的某个时间点执行的代码块的简洁方法. 内 ...

  7. 优雅实现INotifyPropertyChanged接口——利用Lambda表达式

    原文:优雅实现INotifyPropertyChanged接口--利用Lambda表达式 参考文章 在14年的时候,曾经读过上面的参考文章,不过当时并没有怎么理解,慢慢地也就将这篇文章忘诸脑后了. 直 ...

  8. Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。

    本文首发于 blog.zhaochunqi.com 转载请注明 blog.zhaochunqi.com 根据JSR 335, Java 终于在 Java 8 中引入了 Lambda 表达式.也称之为闭 ...

  9. 匿名函数、委托和Lambda表达式

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

随机推荐

  1. noip2017爆炸记——题解&总结&反省(普及组+提高组)

    相关链接: noip2018总结 noip2017是我见过的有史以来最坑爹的一场考试了. 今年北京市考点有一个是我们学校,我还恰好被分到了自己学校(还是自己天天上课的那个教室),于是我同时报了普及提高 ...

  2. 多线程-java并发编程实战笔记

    线程安全性 编写线程安全的代码实质上就是管理对状态的访问,而且通常都是共享的,可变的状态. 一个对象的状态就是他的数据,存储在状态变量中,比如实例域或静态域.所谓共享是指一个对象可以被多个线程访问:所 ...

  3. SHoj A序列

    A序列 发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 1000ms   内存限制: 128M 描述 如果一个序列有奇数个正整数组成,不妨令 ...

  4. localStorag的一点见解

    dot方法对localStorag方法进行键值操作 设值 localStorage.hello = 'world'; localStorage.zhangsan = 'lisi'; 取值: var v ...

  5. Java面试题集(三)

    Jdk与jre的区别? Java运行是环境(jre)是将要执行java程序的java虚拟机. Java开发工具包(jdk)是完整的java软件开发包,包含jre,编译器和其他工具如javaDoc,ja ...

  6. Ansible进阶

    YAML YAML简介 YAML是一个可读性高,并用来表达资料序列的格式.YAML参考了其它多种语言,包括:XML.C语言.Python.Perl以及电子邮件格式RFC2822等 它是一种直观的能够被 ...

  7. centos7 搭建hadoop

    参考文档:http://blog.csdn.net/xiaoxiangzi222/article/details/52757168 https://waylau.com/centos-7-instal ...

  8. VScode开发Vue初尝试(一)

    由于公司近期有新的H5项目开发,而前端的同事也离职了,所以就临时顶缸,研究学习一下Vue框架开发. 本人也是初学,在学习过程中,把一些学习所得分享出来,可能会有很多问题和疏漏,希望大家能够多多指正,共 ...

  9. Java的常用对象

    PO:持久对象 (persistent object),po(persistent object)就是在Object/Relation Mapping框架中的Entity,po的每个属性基本上都对应数 ...

  10. Java_AOP原理

    AOP : 面向切面编程 在程序设计中,我们需要满足高耦合低内聚,所以编程需满足六大原则,一个法则. AOP面向切面编程正是为了满足这些原则的一种编程思想. 一.装饰者模式: 当我们需要给对象增加功能 ...