在我們學習編寫程序當中 回調
是我們最常見的詞語之一蟹略,回調
事件大大的增強了我們程序的邏輯性和可讀性登失,是編程中不可缺少的魁寶。當然挖炬,我們怎么說也是學過 Object-C
的人揽浙,再怎么說我們也知道在 OC 當中回調事件有代理,block 這兩種慣用的模式意敛,但是馅巷,在 C/C++ 中,我們又如何使用回調呢草姻?使用回調又如何確保安全(當然指線程安全和防止內存泄露呢)钓猬?今天博主就帶大家好好認識一番。
博主先拋磚引玉撩独,寫出兩種 C/C++ 常用的回調手段敞曹,函數(shù)指針
和 lambda 表達式
。
1综膀、使用函數(shù)指針
首先我們先理解一下什么函數(shù)指針:函數(shù)指針澳迫,看名字也就知道嘛,就是指向函數(shù)的指針咯剧劝,貌似好像是廢話橄登,額,我們就實操講解一下吧。
void function_a() {
printf("大家好拢锹,我就是一個函數(shù)");
};
// 調用
void (*f_p)() = NULL; // 一個函數(shù)指針
f_p = &function_a;
f_p();
運行上面代碼谣妻,得到的 logger 為 :大家好,我就是一個函數(shù)
分析:
上面代碼中面褐,我們首先定義了一個函數(shù) void function_a()
拌禾,之后我們就能看到所謂的函數(shù)指針定義了 void (*f_p)()
,很明顯,它指向了函數(shù) function_a
的地址展哭,之后我們便能通過函數(shù)指針來直接使用函數(shù)了湃窍。
實戰(zhàn):
那么我們該如何使用函數(shù)指針實現(xiàn)我們的回調呢?我們模仿工程當中使用匪傍,來為大家解決疑惑
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
的代理變量,然后直接通過函數(shù)指針調用其方法手蝎,具體怎么設計這里就不做過多的描述了榕莺,留給小伙伴們一個實踐的命題吧。
2棵介、使用 lambda 表達式
很顯然钉鸯, lambda 表達式
是 C++ 11 出來的產(chǎn)物,一部分年老的程序員還是對其抱著觀望的態(tài)度邮辽,不敢貿然使用唠雕,但是,你覺我們像是年老的程序員嘛 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,相信大家都異常的歡喜了士聪,但是我們在實戰(zhàn)當中如何使用 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
使用自動引用計數(shù)器,將內存交給系統(tǒng)管理藐鹤,這段運行并不會崩潰瓤檐,也不會報錯,唯一的致命點在于內存泄露娱节,用慣了 Object-C ARC
的我們看得出挠蛉,這泥馬存在這循環(huán)引用呀。
我們先看看為什么會發(fā)生循環(huán)引用
沒錯括堤,就是這么坑爹碌秸,未使用過 C++11 特性的舊程序員們,又怎么料到這種事情呢悄窃,然后內存過多地方如此的寫讥电,程序崩潰0 0。
為了解決上面的循環(huán)引用轧抗,我們當然要將對象實現(xiàn)弱引用恩敌,讓不讓 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
的時候沒有發(fā)生循環(huán)引用呢纠炮?大哥,我們已經(jīng)手動 delete 了 cb_灯蝴。
至此恢口,博主拋磚引用的實現(xiàn)了兩種 C/C++ 中的回調方法,接下來小伙伴們發(fā)揮自己的想象力穷躁,實現(xiàn)更的方式吧耕肩。
大哥,既然來了,就點個喜歡吧猿诸。