Object-C++关于回调的那些事

作者: Abson在简书 | 来源:发表于2016-08-25 11:53 被阅读1018次

在我们学习编写程序当中 回调 是我们最常见的词语之一,回调 事件大大的增强了我们程序的逻辑性和可读性,是编程中不可缺少的魁宝。当然,我们怎么说也是学过 Object-C的人,再怎么说我们也知道在 OC 当中回调事件有代理,block 这两种惯用的模式,但是,在 C/C++ 中,我们又如何使用回调呢?使用回调又如何确保安全(当然指线程安全和防止内存泄露呢)?今天博主就带大家好好认识一番。

博主先抛砖引玉,写出两种 C/C++ 常用的回调手段,函数指针lambda 表达式

1、使用函数指针

首先我们先理解一下什么函数指针:函数指针,看名字也就知道嘛,就是指向函数的指针咯,貌似好像是废话,额,我们就实操讲解一下吧。

void function_a() {
printf("大家好,我就是一个函数");
};

// 调用
void (*f_p)() = NULL; // 一个函数指针
f_p = &function_a;
f_p();

运行上面代码,得到的 logger 为 :大家好,我就是一个函数

分析
上面代码中,我们首先定义了一个函数 void function_a() ,之后我们就能看到所谓的函数指针定义了 void (*f_p)(),很明显,它指向了函数 function_a 的地址,之后我们便能通过函数指针来直接使用函数了。

实战
那么我们该如何使用函数指针实现我们的回调呢?我们模仿工程当中使用,来为大家解决疑惑

Class CBClass {
public:
std:string m_str; 
void (*m_callback)();
}

void fun_callback(CBClass *cb){
cb->m_str = "we reset m_str";
}

// 调用
CBClass *cb_ = new CBClass();
cb->m_callback = &fun_callback;
cb->m_callback(cb);
std::cout << cb->m_str << std::endl;

运行上面程序,得到的 loggler 为: we reset m_str

这就是我们在用 C++ 面向对象的时候使用的回调,个人感觉,还是跟代理有点想得,因为你还可以设置一个 auto 的代理变量,然后直接通过函数指针调用其方法,具体怎么设计这里就不做过多的描述了,留给小伙伴们一个实践的命题吧。

2、使用 lambda 表达式

很显然, lambda 表达式 是 C++ 11 出来的产物,一部分年老的程序员还是对其抱着观望的态度,不敢贸然使用,但是,你觉我们像是年老的程序员嘛 0 0,不,我们是代码的搬运工。好吧,我们来理解什么是 lambda 表达式

auto lambda_ = [=]() {
cout << "大家好我是一个 lambda 表达式"
};

auto lambda_ = [&]() {
cout << "大家好我又是一个 lambda 表达式"
};

上面代码中使用了两种 lambda 表达式,学过 Swift 的小伙伴们都知道啥是 值类型引用类型,我们同时可以理解为 [=] 相当于 值类型 , 而 [&] 相当于引用类型。当然,也有 Object-C 的理解方法,[=] 相对与在你使用 block 中单单只是使用外部变量的值,并不关心使用变量的改变,具有使用如下

int a = 10;
void (^Callback)() = ^() {
NSLog(@"%d", a);
};

但是,如果你想要不管是 lambda内部或外部修改引用变量的值时,就要使用到 [&] ,相当于 Object-C中的 __block使用了,如下

__block int a = 10;
void (^Callback)() = ^() {
NSLog(@"%d", a);
};
a = 20;

好了,lambda表达式这么像我们的 Object-C的 block,相信大家都异常的欢喜了,但是我们在实战当中如何使用 lambda 呢?

Class CBClass {

using Callback = function<void()>;

public:
std:string m_str; 
Callback m_callback;
}

// 调用
CBClass *cb_ = new CBClass();
cb_->m_callback = [=](){
cb_->m_str = "we reset m_str";
};
cb_->m_callback();
cout << cb_->m_str << endl;
delete cb_;

运行以上代码,得到的 logger 为 : we reset m_str

大家看出,lambda 的调用方式真的是跟 block像得不能再像了。

但是是什么让老一辈的 C++ 对这种新语法往而却步呢? 难道 lambda 会坑得他们变成宝字辈?
没错,lambda 也有坑。已上面 CBClass 为列子观看以下代码。

shared_ptr<CBClass> bc_ = make_shared<CBClass>();
cb_->m_callback = [=](){
cb_->m_str = "we reset m_str";
};
cb_->m_callback();
cout << cb_->m_str << endl;

上面代码中,我们使用了共享指针 shared_ptr 来让 CBClass使用自动引用计数器,将内存交给系统管理,这段运行并不会崩溃,也不会报错,唯一的致命点在于内存泄露,用惯了 Object-C ARC的我们看得出,这泥马存在这循环引用呀。
我们先看看为什么会发生循环引用

发生循环引用

没错,就是这么坑爹,未使用过 C++11 特性的旧程序员们,又怎么料到这种事情呢,然后内存过多地方如此的写,程序崩溃0 0。
为了解决上面的循环引用,我们当然要将对象实现弱引用,让不让 lambda 来为我们的 cb_对象管理内存咯。

shared_ptr<CBClass> bc_ = make_shared<CBClass>();
auto weak_cb = cb_->get();
cb_->m_callback = [=](){
weak_cb->m_str = "we reset m_str";
};
cb_->m_callback();
cout << cb_->m_str << endl;

上面代码,相当于如下效果

弱引用

但是为什么上面没有使用 shared_ptr 的时候没有发生循环引用呢?大哥,我们已经手动 delete 了 cb_。


至此,博主抛砖引用的实现了两种 C/C++ 中的回调方法,接下来小伙伴们发挥自己的想象力,实现更的方式吧。

大哥,既然来了,就点个喜欢吧。

心如止水,奋力前行

相关文章

  • Object-C++关于回调的那些事

    在我们学习编写程序当中 回调 是我们最常见的词语之一,回调 事件大大的增强了我们程序的逻辑性和可读性,是编程中不可...

  • 关于回调

    能量回调的时候,可能觉得自己好像掉下来了,并执着着曾经的高点;这恰恰是考验的时候,也是夯实的时候。 放下曾经高过、...

  • 关于回调

    关于回调 几种场景 A函数在执行过程中调用了B函数,而B函数再流程中需要外部A的配合(扩展性),调用B时传入回调接...

  • 关于接口回调

    今天来写一下最基本的接口回调,有什么不对的望指正。 class TestInterface implements ...

  • 关于回调函数

    个人觉得,回调函数的好处是方便扩展,多人开发的时候,能够解耦 举一个例子:我去饭馆吃饭,这个饭馆是专门做油炸的,但...

  • 曾文珂:11.20黄金强势不改持续攀升,回调做多稳拿20点

    曾文珂:11.20黄金强势不改持续攀升,回调做多稳拿20点 过去的事,不后悔;将来的事,不害怕。对于那些应经发生的...

  • 回调函数的一点点补充

    之前自己有总结过一些关于js中回调函数的知识了,此次主要是补充一些关于回调函数中的参数的来源的东西 在使用回调函数...

  • Promise

    回调 把一个函数A传给另一个函数B调用,那么A就是回调函数。 回调地狱 回调套回调套回调套回调套回调套回调套回调....

  • 曾文珂:11.20黄金回调蓄力一触即发,短线五单完美翻仓

    曾文珂:11.20黄金回调蓄力一触即发,短线五单完美翻仓 过去的事,不后悔;将来的事,不害怕。对于那些应经发生的事...

  • 调志愿的那些事

    高考今天出来,其实成绩还没出来的时候,老爸老妈已经开始念叨了,因为有个哥哥是读军校的,所以他们似乎对于军校,恋爱有...

网友评论

    本文标题:Object-C++关于回调的那些事

    本文链接:https://www.haomeiwen.com/subject/kmxasttx.html