Home Item 7 (2/2)- 智能指针(smart pointers)(中文)
Post
Cancel

Item 7 (2/2)- 智能指针(smart pointers)(中文)

std::shared_ptr 循環引用(circular reference)問題

std::shared_ptr 很聰明,我們可以放心的使用它了嗎? No!!! 雖然 std::shared_ptr 真的很不錯,但是不小心使用還是會有問題!

std::shared_ptr 的循環引用(circular reference)問題,是指當兩個或多個 std::shared_ptr 對象相互引用時,會導致它們所管理的資源無法被釋放,從而造成內存泄漏。這是因為每個 std::shared_ptr 都會增加所指向對象的引用計數,並且只有當引用計數為零時,對象才會被銷毀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <memory>

class B; // Forward declaration

class A {
public:
    std::shared_ptr<B> b_ptr;
    A() { std::cout << "A created : " << this << "\n"; }
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::shared_ptr<A> a_ptr;
    B() { std::cout << "B created : " << this << "\n"; }
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    std::cout << "~Enter scope" << std::endl;
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();

        a->b_ptr = b;
        b->a_ptr = a;

        std::cout << "a.use_count : " << a.use_count() << std::endl;
        std::cout << "b.use_count : " << b.use_count() << std::endl;

        // Neither A nor B will be destroyed at the end of this scope due to circular reference
    }
    std::cout << "~Leave scope" << std::endl;

    return 0;
}

執行結果: 可以看到兩個物件有被創立,但離開 scope 的時候,兩個物件都沒有被 smart pointer 自動釋放資源。這樣就造成了 memory leak.

Desktop View

std::weak_ptr

以下是 std::weak_ptr 的一些關鍵點

定義與初始化:

std::weak_ptr 不能單獨創建,它必須從一個 std::shared_ptr 對象初始化。

1
2
std::shared_ptr<int> sp = std::make_shared<int>(3);
std::weak_ptr<int> wp = sp;

不影響引用計數: std::weak_ptr 不增加管理對象的引用計數,因此不會阻止管理對象被銷毀。

使用 weak_ptr 改寫上面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <memory>

class B; // Forward declaration

class A {
public:
    std::shared_ptr<B> b_ptr;
    A() { std::cout << "A created : " << this << "\n"; }
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::weak_ptr<A> a_ptr; // !!!!!!!!!!!!
    B() { std::cout << "B created : " << this << "\n"; }
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    std::cout << "~Enter scope" << std::endl;
    {
        std::shared_ptr<A> a = std::make_shared<A>();
        std::shared_ptr<B> b = std::make_shared<B>();

        a->b_ptr = b;
        b->a_ptr = a;

        std::cout << "a.use_count : " << a.use_count() << std::endl;
        std::cout << "b.use_count : " << b.use_count() << std::endl;

        // Neither A nor B will be destroyed at the end of this scope due to circular reference
    }
    std::cout << "~Leave scope" << std::endl;

    return 0;
}

只有 class B 中的 a_ptr 改成 weak_ptr

執行結果: 可以看到兩個物件都有被正確的釋放。 且 a.use_count 為 1

Desktop View

使用時機

std::weak_ptr 通常用於實現觀察者模式,避免循環引用,或者在需要訪問但不擁有某個資源的情況下使用。

☝ツ☝

This post is licensed under CC BY 4.0 by the author.

👈 ツ 👍

Item 7 (1/2) - Smart pointers(English)

Item 7 (2/2) - Smart pointers(English)