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.
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
使用時機
std::weak_ptr 通常用於實現觀察者模式,避免循環引用,或者在需要訪問但不擁有某個資源的情況下使用。