在《C/C++回调方式系列之一》中我们总结了函数指针模式回调,这些回调当时比较原始,容易给人一种面向过程的编程的感觉,而且函数指针的格式比较繁琐,可读性相对差一点。本系列二将总结比较推荐的回调使用模式。
一、class接口回调模式
1. 定义接口
C++可以定义virtual纯虚类作为函数接口类,
例如定义 Introducer接口
class Introducer {
public:// 析构函数要virtual,否则在动态绑定时容易引起内存泄漏virtual ~Introducer() {// do nothing.};virtual void selfIntroduce() = 0;  // 接口函数
};
这里有个地方比较特别,就是析构函数定义为vitual,这是为了防止C++动态绑定时出现内存泄漏问题。这里就不展开讲解,具体可以看推荐链接
英文文章: http://www.studytonight.com/cpp/virtual-destructors.php
中文文章:http://www.cnblogs.com/lixiaohui-ambition/archive/2012/07/13/2589716.html
2. 定义调用接口的函数
void introduce(Introducer &person) {person.selfIntroduce();
}
如果没有什么特别的,推荐使用引用&操作符或者指针,可以避免复制构造函数内存拷贝引起的问题,更总要的是有部分变量不能被复制例如iostream输入输出流。
3. 实现接口
class Lucky: public Introducer {
public:Lucky(){};virtual void selfIntroduce() {printf("Hi, I'm lucky.\n");};
};
只要实现对应的虚函数接口就可以了。
4. 完整的代码如下
#include <cstdio>
#include <cstring>class Introducer {
public:virtual ~Introducer() {// do nothing.};virtual void selfIntroduce() = 0;
};class Lucky: public Introducer {
public:Lucky(){};virtual void selfIntroduce() {printf("Hi, I'm lucky.\n");};
};class Jacky: public Introducer {
public:Jacky() {};virtual void selfIntroduce() {printf("Hi, I'm jacky, Nice to meet you.\n");};
};class Penny: public Introducer {
public:Penny() {};virtual void selfIntroduce() {printf("Just call me penny, I like football.\n");};
};void introduce(Introducer &person) {person.selfIntroduce();
}int main(void) {Lucky lucky;introduce(lucky);Jacky jacky;introduce(jacky);Introducer *intr = new Penny();introduce(*intr);delete intr;intr = NULL;return 0;
}

5. 编译和结果输出
lucky@lucky-macbook:cplusplus_callback_function$ g++ class_callback.cpp -o class_callback
lucky@lucky-macbook:cplusplus_callback_function$ ./class_callback
Hi, I'm lucky.
Hi, I'm jacky, Nice to meet you.
Just call me penny, I like football.

二、lambda程式
lambda程式是c++ 2011新标准引入的一种新语法,非常新颖,使用时候简洁,可读性好。
1. lambda
lambda的基本语法如下:
[ capture-list ] ( params ) -> ret { body }
capture-list 这个词我也不知道怎么翻译,功能大概引用变量的列表,与lambda同级的局部变量。
params 函数参数列表
ret 函数返回值
{ body } 函数体内容
可以这么理解,lambda程式其实就是一个函数对象,类似C++ std::function (c++ 2011新标准引入的)。当然比std::function更宽松一点,lambda程式在编译器编译时能够自动转换成函数指针和函数对象。
2. lambda回调的例子
1) 首先定义回调函数
#define TEXT_LEN 128void introduce(void (*pfun)(char *, size_t)) {char szText[TEXT_LEN] = {0};(*pfun)(szText, TEXT_LEN);printf("%s", szText);
}

2) 编写lambda程式实现回调接口并调用

auto fun1 = [] (char *outName, size_t len) -> void {strncpy(outName, "Hi, I'm jacky, Nice to meet you.\n", len);};
introduce(fun1);

3. 完整代码

#include <cstdio>
#include <cstring>
#include <functional>#define TEXT_LEN 128void introduce(void (*pfun)(char *, size_t)) {char szText[TEXT_LEN] = {0};(*pfun)(szText, TEXT_LEN);printf("%s", szText);
}// 定义函数指针
typedef void (*FUN_CALL)(char *, size_t);int main(void) {// 将lambda赋值给函数指针,一般不推荐这么做,推荐使用auto关键词FUN_CALL fun =  [] (char *outName, size_t len) -> void {strncpy(outName, "Hi, I'm lucky.\n", len);};introduce(fun);// 使用auto关键词定义函数变量,auto关键词是C++ 2011使用的。推荐使用auto fun1 = [] (char *outName, size_t len) -> void {strncpy(outName, "Hi, I'm jacky, Nice to meet you.\n", len);};introduce(fun1);// lambda程式直接作为参数传送,推荐使用,显得比较简洁introduce([](char *outName, size_t len) -> void {strncpy(outName, "Just call me penny, I like football.\n", len);});return 0;
}
4. 编译及输出
lucky@lucky-macbook:cplusplus_callback_function$ g++ --std=c++11 lambda_callback.cpp -o lambda_callback
lucky@lucky-macbook:cplusplus_callback_function$ ./lambda_callback
Hi, I'm lucky.
Hi, I'm jacky, Nice to meet you.
Just call me penny, I like football.
三、总结
c / C++ 常用的回调使用方式有三种
1. 函数指针回调
2. 类接口定义回调
3. lambda实现回调
严格上来讲,lambda回调并不是一种新的回调方式,仅仅是C++2011新标准对c++语法的优化,改变c++在指针滥用可读性差的缺点。lambda最终还是通过编译器自动转换为函数指针(作为理解方式,事实需要读取编译器的汇编才能够验证这种说法)。
在实际编程中,比较推荐类接口定义回调,这是目前方便的面相对象模式的回调。如果在使用过程中不可避免的要实现函数指针参数,那么推荐使用lambda实现,尽量避免使用原始的函数指针定义。
说明:函数指针在有些地方确实很难避免使用,例如调用so动态库,这时候就必须使用函数指针来引用动态库的函数了。
目前看过两个开源项目用lambda很出色的,一个是cocos2d-x,另一个是 reactive Cpp。如果有兴趣可以下载去看看。比较推荐cocos2d-x,因为代码量相对少一点,只关注语法还有智能指针模块就可以了,其他图形化的源码不需要看的去专研。

C/C++回调方式系列之二class接口回调和lambda程式相关推荐

  1. 【玩转SQLite系列】(二)SQLite创建和打开数据库的三种方式

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53234396 本文出自[DylanAndroid的博客] [玩转SQLite系 ...

  2. [Axis2与Eclipse整合开发Web Service系列之二] Top-Down方式,通过WSDL逆向生成服务端(续)

    前言 本篇是承接上一篇: [Axis2与Eclipse整合开发Web Service系列之二] Top-Down方式,通过WSDL逆向生成服务端 在上一篇粗略地介绍了如何使用Top-Down的方式创建 ...

  3. Camera开发系列之二 相机数据回调处理

    章节 Camera开发系列之一-显示摄像头实时画面 Camera开发系列之二-相机预览数据回调 Camera开发系列之三-相机数据硬编码为h264 Camera开发系列之四-使用MediaMuxer封 ...

  4. 《 Python List列表全实例详解系列(二)》__创建列表(5种方式)

    < Python List列表全实例详解系列(二)> __创建列表(5种方式) 上一篇:< Python List 列表全实例详解系列(一)>__系列总目录.列表概念 本章目录 ...

  5. RxJS 系列之二 - Observable 详解

    查看新版教程,请访问前端修仙之路 RxJS 系列目录 RxJS 系列之一 - Functional Programming 简介 RxJS 系列之二 - Observable 详解 (本文) RxJS ...

  6. Linux设备模型剖析系列之二(uevent、sysfs)

    CSDN链接: Linux设备模型剖析系列一(基本概念.kobject.kset.kobj_type) Linux设备模型剖析系列之二(uevent.sysfs) Linux设备模型剖析系列之三(de ...

  7. 你想要的系列:网络请求框架OkHttp3全解系列 - (二)OkHttp的工作流程分析

    Okhttp系列文章: 你想要的系列:网络请求框架OkHttp3全解系列 - (一)OkHttp的基本使用 你想要的系列:网络请求框架OkHttp3全解系列 - (二)OkHttp的工作流程分析 你想 ...

  8. AspNetCore-MVC实战系列(二)之通过绑定邮箱找回密码

    AspNetCore - MVC实战系列目录 . 爱留图网站诞生 . git源码:https://github.com/shenniubuxing3/LovePicture.Web . AspNetC ...

  9. Skype for business混合部署系列之二自定义拓扑信息

    Skype for business混合部署系列之二自定义拓扑信息 此次部署前端服务器共3台,后端数据库2台采用always on方式,2台SQL Server服务器已经安装完成,在这里不做文档,本章 ...

  10. 黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级)

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(二) Cryptography Application Block (高级) 本章介绍的是企业库加密应用程序模块 ...

最新文章

  1. 十八、前端必学Bootstrap美化(上篇)
  2. Android媒体解码MediaCodec,MediaExtractor
  3. Silverlight中全屏处理
  4. java keygenerator_Java密码学KeyGenerator类
  5. 我的内核学习笔记11:linux leds-gpio驱动应用实例
  6. 智能指针变量做函数参数的一个值得注意的地方
  7. Linux命令解释之df
  8. 宿舍小助手之个人分析NABCD
  9. js简单屏蔽鼠标右键实现方式
  10. baum welch java_Baum Welch估计HMM参数实例
  11. ITIL4实用指南 | ITSM的未来属于敏捷
  12. 手机点餐系统概述_餐馆点菜系统概要设计说明书.doc
  13. c# 多线程 源码5
  14. 在日冕病毒时代的爱情-远程工作的技巧,窍门和最佳实践
  15. 求最小公倍数c语言最简公式,C语言求最小公倍数和最大公约数三种算法(经典)...
  16. 8086汇编(10、int9键盘中断)
  17. STM32基础-外部中断与优先级
  18. Mysql常用命令行大全
  19. 汉寿计算机职业中专,汉寿县职业中等专业学校2021年招生简章
  20. Unrecognised tag: snapshotPolicy (position: START_TAG seen

热门文章

  1. 2017_9_20 生活记录
  2. 【POJ 3666】Making the Grade【线性DP】
  3. 边缘保留滤波matlab,【DIP】各种边缘保留滤波器一览
  4. git提交过滤target文件 idea_详解如何在IntelliJ IDEA中使用.ignore插件忽略不必要提交的文件...
  5. Dijkstra及其堆优化
  6. 第三部分数据结构[专业课考试3]
  7. Chrome firefox ie等浏览器空格nbsp;宽度不一样
  8. CSS3+HTML5特效5 - 震动的文字
  9. 服务器上多个php,php – 在多个服务器上扩展cronjobs
  10. 【转】cron表达式详解