C/C++回调方式系列之二class接口回调和lambda程式
2017-05-30 20:41
961 查看
在《C/C++回调方式系列之一》中我们总结了函数指针模式回调,这些回调当时比较原始,容易给人一种面向过程的编程的感觉,而且函数指针的格式比较繁琐,可读性相对差一点。本系列二将总结比较推荐的回调使用模式。
一、class接口回调模式
1. 定义接口
C++可以定义virtual纯虚类作为函数接口类,
例如定义 Introducer接口
这里有个地方比较特别,就是析构函数定义为vitual,这是为了防止C++动态绑定时出现内存泄漏问题。这里就不展开讲解,具体可以看推荐链接
英文文章: http://www.studytonight.com/cpp/virtual-destructors.php 中文文章:http://www.cnblogs.com/lixiaohui-ambition/archive/2012/07/13/2589716.html
2. 定义调用接口的函数
如果没有什么特别的,推荐使用引用&操作符或者指针,可以避免复制构造函数内存拷贝引起的问题,更总要的是有部分变量不能被复制例如iostream输入输出流。
3. 实现接口
只要实现对应的虚函数接口就可以了。
4. 完整的代码如下
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) 首先定义回调函数
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,因为代码量相对少一点,只关注语法还有智能指针模块就可以了,其他图形化的源码不需要看的去专研。
一、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>5. 编译和结果输出
#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;
}
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 128 void 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 128 void 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++回调方式系列之一 函数指针和函数回调模式
- C/C++回调方式系列之一 函数指针和函数回调模式
- 最简单的方式理解“接口回调”的设计
- 实战c++中的智能指针unique_ptr系列-- unique_ptr与lambda的错误结合(尤其是捕获lambda中的unique_ptr)
- c++ 11 多线线程系列-----------使用c++11 lambda创建线程
- C++中模块(Dll)对外暴露接口的方式
- C++中实现回调机制的几种方式
- 广播方式注册的接口回调
- C++再学习系列:使用合理的引用参数实现接口的自说明
- Android中Service与Activity的通信---回调接口方式
- c++之回调实现方式
- C++中的struct与class继承方式
- C++中实现回调机制的几种方式
- 实战c++中的智能指针unique_ptr系列-- unique_ptr与lambda的错误结合(尤其是捕获lambda中的unique_ptr)
- Fragment学习之使用接口回调的方式实现Fragment与Activity通信
- Android中的接口回调方式
- mybatis系列六:使用getMapper方式实现面向接口的编程
- C++中模块(Dll)对外暴露接口的几种方式
- C++不同物件的生存方式及C++程式的生与死(建模式与解模式) [大三TJB_708]
- C++程序员学Java系列之十九:接口