關于 shared_ptr
和 weak_ptr
的詳細介紹及使用示例:
1. shared_ptr
(共享所有權智能指針)
核心特性
- 引用計數:記錄當前有多少個
shared_ptr
共享同一個對象。 - 自動釋放:當引用計數歸零時,自動釋放對象內存。
- 線程安全:引用計數的增減是原子操作(但對象本身的訪問需自行同步)。
基本用法
#include <memory>
#include <iostream>class MyClass {
public:MyClass() { std::cout << "MyClass 構造\n"; }~MyClass() { std::cout << "MyClass 析構\n"; }void print() { std::cout << "Hello\n"; }
};int main() {// 創建 shared_ptr(推薦使用 make_shared)std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();// 共享所有權(引用計數+1)std::shared_ptr<MyClass> ptr2 = ptr1;// 使用 -> 操作符訪問成員ptr1->print(); // 輸出: Hello// 引用計數查看(調試用)std::cout << "引用計數: " << ptr1.use_count() << std::endl; // 輸出: 2// ptr1 和 ptr2 離開作用域,引用計數歸零,對象自動析構return 0;
}
輸出結果
MyClass 構造
Hello
引用計數: 2
MyClass 析構
2. weak_ptr
(弱引用智能指針)
核心特性
- 不增加引用計數:僅觀察對象,不影響其生命周期。
- 需轉換為
shared_ptr
:通過lock()
獲取臨時shared_ptr
來訪問對象。 - 解決循環引用:打破
shared_ptr
的循環依賴,避免內存泄漏。
3. 循環引用問題與 weak_ptr
解決方案
循環引用示例
#include <memory>
#include <iostream>
class Node {
public:std::shared_ptr<Node> next;Node() { std::cout << "Node 構造\n"; }~Node() { std::cout << "Node 析構\n"; }
};int main() {auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2; // node2 引用計數=2node2->next = node1; // node1 引用計數=2// 退出作用域后,node1/node2 引用計數=1,無法釋放!return 0;
}
輸出結果(內存泄漏)
Node 構造
Node 構造
使用 weak_ptr
解決循環引用
#include <memory>
#include <iostream>
class SafeNode {
public:std::weak_ptr<SafeNode> next; // 使用 weak_ptrSafeNode() { std::cout << "SafeNode 構造\n"; }~SafeNode() { std::cout << "SafeNode 析構\n"; }
};int main() {auto node1 = std::make_shared<SafeNode>();auto node2 = std::make_shared<SafeNode>();node1->next = node2;node2->next = node1;// 退出作用域后,引用計數歸零,正確析構return 0;
}
輸出結果
SafeNode 構造
SafeNode 構造
SafeNode 析構
SafeNode 析構
4. weak_ptr
的典型使用場景
(1) 訪問共享對象前檢查存活狀態
#include <memory>
#include <iostream>class MyClass {
public:MyClass() { std::cout << "MyClass 構造\n"; }~MyClass() { std::cout << "MyClass 析構\n"; }void print() { std::cout << "Hello\n"; }
};void checkObject(std::weak_ptr<MyClass> weak) {if (auto shared = weak.lock()) { // 轉換為 shared_ptrshared->print();} else {std::cout << "對象已被釋放\n";}
}int main() {std::weak_ptr<MyClass> weak;{auto shared = std::make_shared<MyClass>();weak = shared;checkObject(weak); // 輸出: Hello}checkObject(weak); // 輸出: 對象已被釋放return 0;
}
輸出結果
MyClass 構造
Hello
MyClass 析構
對象已被釋放
(2) 觀察者模式(緩存)
class DataCache {std::weak_ptr<MyClass> cachedData;
public:void updateCache(std::shared_ptr<MyClass> data) {cachedData = data;}void useCache() {if (auto data = cachedData.lock()) {data->print();} else {std::cout << "緩存無效\n";}}
};
5. shared_ptr
與 weak_ptr
操作總結
操作 | shared_ptr | weak_ptr |
---|---|---|
所有權 | 擁有對象所有權 | 僅觀察對象 |
引用計數影響 | 增加 | 不影響 |
訪問對象 | 直接通過 -> 或 * | 需調用 lock() 獲取 shared_ptr |
檢查有效性 | if (ptr) | if (weak.expired()) 或 lock() |
6. 最佳實踐
- 優先使用
make_shared
:更高效(單次內存分配對象+控制塊)。 - 避免循環引用:成員指針優先考慮
weak_ptr
。 - 不要用
new
初始化:直接傳遞裸指針可能導致多次釋放。 - 謹慎傳遞
shared_ptr
:僅在需要共享所有權時傳遞,否則傳遞原始引用或指針。