C++中的循環(huán)引用
雖然C++11引入了智能指針的,但是開發(fā)人員在與內(nèi)存的斗爭問題上并沒有解放,如果我門實用不當(dāng)仍然有內(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ù)),我門把當(dāng)前的引用數(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)如此就導(dǎo)致兩個指針指向的對象沒有析構(gòu)釋放內(nèi)存,這就是循環(huán)引用導(dǎo)致的內(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)文章
通過c語言調(diào)用系統(tǒng)curl動態(tài)庫的示例詳解
這篇文章中我們將通過一個簡單的示例來講解如何在Ubuntu系統(tǒng)中通過C語言調(diào)用動態(tài)庫(共享庫)的方法,我們將使用libcurl庫,這是一個基于客戶端的URL傳輸庫,廣泛用于各種程序和應(yīng)用中以訪問網(wǎng)頁和服務(wù)器數(shù)據(jù),需要的朋友可以參考下2024-03-03
深入探討Linux靜態(tài)庫與動態(tài)庫的詳解(一看就懂)
本篇文章是對Linux靜態(tài)庫與動態(tài)庫進行了詳細的分析介紹,需要的朋友參考下2013-05-05
基于OpenCV實現(xiàn)車道線檢測(自動駕駛 機器視覺)
無人駕駛技術(shù)是機器學(xué)習(xí)為主的一門前沿領(lǐng)域,在無人駕駛領(lǐng)域中機器學(xué)習(xí)的各種算法隨處可見,本文將為大家介紹無人駕駛技術(shù)中的車道線檢測,感興趣的小伙伴可以了解一下2021-11-11
C語言學(xué)生學(xué)籍管理系統(tǒng)課程設(shè)計
這篇文章主要介紹了C語言學(xué)生學(xué)籍管理系統(tǒng)課程設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-01-01

