1. 程式人生 > >[C++11] 迴圈引用

[C++11] 迴圈引用

前言

雖然C++11中的智慧指標,一定程度上簡化了C++當中的記憶體管理;但是,shared_ptr<>的使用同時也引出了另一個問題:迴圈引用

例子

讓我們先來看一段示例程式碼。

#include <iostream>
#include <vector>
#include <memory>

using namespace std;

class parent;
class children;

class parent {
public:
    ~parent() { std::cout << "~parent()" << std::endl; }

public:
    std::shared_ptr<children> child;
};

class children {
public:
    ~children() { std::cout << "~children()" << std::endl; }

public:
    std::shared_ptr<parent> parent;
};

void Verify() {
    std::shared_ptr<parent> p(new parent);
    std::shared_ptr<children> c(new children);

    p->child = c;
    c->parent = p;
}

int main() {
    std::cout << "Begin" << std::endl;

    Verify();

    std::cout << "Done" << std::endl;
}

執行之後,我們可以發現兩個物件都沒有被正常析構。 在這裡插入圖片描述

分析

當我們想要parent物件釋放時,children物件中仍保留了該parent物件的shared_ptr,導致其無法被正常釋放。既然是因為children物件保留了引用,那麼就先釋放children物件唄?很好,parent物件中保留了該children物件的shared_ptr。這樣,我們就陷入了一個死迴圈:迴圈引用。

解決辦法

1. 手動打破"迴圈引用"

void Verify() {
    std::shared_ptr<parent> p(new parent);
    std::shared_ptr<children> c(new children);

    p->child = c;
    c->parent = p;

    p->child.reset();	// let it go,手動打破“迴圈引用”這種尷尬的局面;
}

2. 使用weak_ptr

weak_ptr僅保持對物件的引用,而不負責具體的資源管理; 但是,相比裸指標而言,weak_ptr提供了expired()介面,方便檢測引用的物件是否已經釋放,這點是裸指標所不具備的。

class parent {
public:
    ~parent() { std::cout << "~parent()" << std::endl; }

public:
    std::shared_ptr<children> child;
};

class children {
public:
    ~children() { std::cout << "~children()" << std::endl; }

public:
    std::weak_ptr<parent> parent;	// 替換成“弱引用”
};