C++中的循環(huán)引用
雖然C++11引入了智能指針的,但是開發(fā)人員在與內(nèi)存的斗爭問題上并沒有解放,如果我門實用不當仍然有內(nèi)存泄漏問題,其中智能指針的循環(huán)引用缺陷是最大的問題。
// // main.cpp // test // // Created by 杜國超 on 17/9/9. // Copyright © 2017年 杜國超. All rights reserved. // #include <iostream> #include <memory> #include <vector> using namespace std; class CObjB; class CObjA { public: CObjA() { cout << "CObjA Constructor..." << endl; } ~CObjA() { cout << "CObjA Destructor..." << endl; } shared_ptr<CObjB> m_pb; // 在A中引用B }; class CObjB { public: CObjB() { cout << "CObjB Constructor..." << endl; } ~CObjB() { cout << "CObjB Destructor..." << endl; } shared_ptr<CObjA> m_pa; // 在B中引用A }; int main() { shared_ptr<CObjA> tmpPa = make_shared<CObjA>(); shared_ptr<CObjB> tmpPb = make_shared<CObjB>(); tmpPa->m_pb = tmpPb; tmpPb->m_pa = tmpPa; std::cout << "CObjA referencr num:" << tmpPa.use_count() << endl; std::cout << "CObjB referencr num:" << tmpPb.use_count() << endl; // tmpPa->m_pb.reset(); // tmpPb->m_pa.reset(); // std::cout << "CObjA referencr num:" << tmpPa.use_count() << endl; // std::cout << "CObjB referencr num:" << tmpPb.use_count() << endl; }
我們可以看到在出main函數(shù)作用域之前兩個指針指向的內(nèi)存并沒有釋放(指針指向的對象沒有調(diào)用析構(gòu)函數(shù)),我門把當前的引用數(shù)打印出來為2這個沒有問題,為什么在函數(shù)結(jié)束時沒有調(diào)用對象的析構(gòu)函數(shù)呢?這就好像多線程之間的死鎖一樣,對象a想要析構(gòu)但是發(fā)現(xiàn)對象b引用了自己所以就等待對象b析構(gòu)不再引用自己,而b想要析構(gòu)卻發(fā)現(xiàn)對象a引用了自又等待a析構(gòu)如此就導致兩個指針指向的對象沒有析構(gòu)釋放內(nèi)存,這就是循環(huán)引用導致的內(nèi)存問題。
如何證明這個結(jié)論呢,我們手動釋放掉兩個對象對對方的引用,就可以解除循環(huán)引用關(guān)系,正確析構(gòu)對象了(把注釋部分代碼打開)。運行結(jié)果:
我門可以看到,調(diào)用reset函數(shù)釋放引用關(guān)系后,指針的引用計數(shù)變?yōu)橐?,等到函?shù)運行結(jié)束,兩個shared指針生命周期結(jié)束調(diào)用析構(gòu)函數(shù),對象的引用計數(shù)減為0,對象內(nèi)存釋放。
但是如果每次都要手動解除引用來解決循環(huán)引用,那么智能指針似乎變成了傻子指針了,這時候還有一個東西能解決,那就是weak_ptr,它不會改變所共享的shared_ptr的引用計數(shù),即使我門可以通過該指針訪問它所指向的對象。
// // main.cpp // test // // Created by 杜國超 on 17/9/9. // Copyright © 2017年 杜國超. All rights reserved. // #include <iostream> #include <memory> #include <vector> using namespace std; class CObjB; class CObjA { public: CObjA() { cout << "CObjA Constructor..." << endl; } ~CObjA() { cout << "CObjA Destructor..." << endl;} void Say() {cout << "CObjA Say..." << endl; } shared_ptr<CObjB> GetPb() { return m_pb.lock(); } public: weak_ptr<CObjB> m_pb; // 在A中引用B }; class CObjB { public: CObjB() { cout << "CObjB Constructor..." << endl; } ~CObjB() { cout << "CObjB Destructor..." << endl; } void Say() {cout << "CObjB Say..." << endl; } shared_ptr<CObjA> GetPa() { return m_pa.lock(); } public: weak_ptr<CObjA> m_pa; // 在B中引用A }; int main() { shared_ptr<CObjA> tmpPa = make_shared<CObjA>(); shared_ptr<CObjB> tmpPb = make_shared<CObjB>(); tmpPa->m_pb = tmpPb; tmpPb->m_pa = tmpPa; std::cout << "CObjA referencr num:" << tmpPa.use_count() << endl; std::cout << "CObjB referencr num:" << tmpPb.use_count() << endl; if (tmpPa->GetPb()!= NULL){ tmpPa->GetPb()->Say(); } if (tmpPb->GetPa()!= NULL){ tmpPb->GetPa()->Say(); } }
這樣就得到我門想要的結(jié)果了。最后說一句雖然智能指針帶來了很多方便,但是也要小心使用它仍然有很多坑等著我門,以后再作詳述。
總結(jié)
以上所述是小編給大家介紹的C++中的循環(huán)引用,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
數(shù)據(jù)結(jié)構(gòu)順序表操作示例
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)順序表操作示例,其中有在第I個元素前插入數(shù)據(jù)x,元素從0開始計數(shù)、刪除第i個元素,元素從0開始計數(shù)的方法,需要的朋友可以參考下2014-03-03C語言中的時間函數(shù)clock()和time()你都了解嗎
這篇文章主要為大家詳細介紹了C語言中的時間函數(shù)clock()和time(),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-02-02C語言使用openSSL庫DES模塊實現(xiàn)加密功能詳解
這篇文章主要介紹了C語言使用openSSL庫DES模塊實現(xiàn)加密功能,簡單講解了DES加密的相關(guān)概念,并結(jié)合實例形式分析了DES加密的具體實現(xiàn)技巧,需要的朋友可以參考下2017-05-05C++?system()函數(shù)的常用用法(全網(wǎng)最新)
system()用于從C?/C++程序調(diào)用操作系統(tǒng)命令,這里給大家講解下C++?system()函數(shù)的常用用法,感興趣的朋友跟隨小編一起看看吧2023-01-01關(guān)于C++靜態(tài)成員函數(shù)訪問非靜態(tài)成員變量的問題
靜態(tài)成員函數(shù)不能訪問非靜態(tài)成員,這是因為靜態(tài)函數(shù)屬于類而不是屬于整個對象,靜態(tài)函數(shù)中的 member可能都沒有分配內(nèi)存。靜態(tài)成員函數(shù)沒有隱含的this自變量。所以,它就無法訪問自己類的非靜態(tài)成員2013-10-10C/C++ 中memset() 函數(shù)詳解及其作用介紹
這篇文章主要介紹了C/C++ 中memset() 函數(shù)詳解及其作用介紹,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-07-07