C++智能指針循環引用問題分析

C++11中引入了三種智能指針,分別是shared_ptr、weak_ptr和unique_ptr

智能指針的作用
智能指針可以幫助我們管理動態分配的堆內存,減少內存泄漏的可能性
手動管理堆內存有引起內存泄漏的可能,比如這段代碼

try {
    int* p = new int;
    // Do something
    delete p;
} catch(...) {
    // Catch exception
}


如果在執行Do something的時候發生了異常,那麼程序就會直接跳到catch語句捕獲異常,delete p這句代碼不會被執行,發生了內存泄漏
我們把上面的程序改成

try {
    shared_ptr<int> p(new int);
    // Do something
} catch(...) {
    // Catch exception
}


當執行Do something的時候發生了異常,那麼try塊中的棧對象都會被析構。因此代碼中p的析構函數會被調用,引用計數從1變成0,通過new分配的堆內存被釋放,這樣就避免了內存泄漏的問題

循環引用問題
雖然智能指針會減少內存泄漏的可能性,但是如果使用智能指針的方式不對,一樣會造成內存泄漏。比較典型的情況是循環引用問題,比如這段代碼

class B; // 前置聲明
class A {
public:
    shared_ptr<B> ptr;
};

class B {
public:
    shared_ptr<A> ptr;
};

int main()
{
    while(true) {
        shared_ptr<A> pa(new A());
        shared_ptr<B> pb(new B());
        pa -> ptr = pb;
        pb -> ptr = pa;
    }
    return 0;
}


這個程序中智能指針的引用情況如下圖

在這裏插入圖片描述

上圖中,class A和class B的對象各自被兩個智能指針管理,也就是A object和B object引用計數都爲2,爲什麼是2?

分析class A對象的引用情況,該對象被main函數中的pa和class B對象中的ptr管理,因此A object引用計數是2,B object同理

在這種情況下,在main函數中一個while循環結束的時候,pa和pb的析構函數被調用,但是class A對象和class B對象仍然被一個智能指針管理,A object和B object引用計數變成1,於是這兩個對象的內存無法被釋放,造成內存泄漏,如下圖所示

在這裏插入圖片描述

解決方法
解決方法很簡單,把class A或者class B中的shared_ptr改成weak_ptr即可,由於weak_ptr不會增加shared_ptr的引用計數,所以A object和B object中有一個的引用計數爲1,在pa和pb析構時,會正確地釋放掉內存
————————————————
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章