C++ 智能指針詳解
智能指針是 C++11 引入的內存管理工具,位于 <memory>
頭文件中,用于自動管理動態分配的內存,防止內存泄漏。主要類型如下:
1. std::unique_ptr
(獨占所有權)
- 特點:唯一擁有所指對象,不可復制(可移動)
- 適用場景:獨占資源所有權
- 內存開銷:幾乎為零(僅包裝原始指針)
#include <memory>void unique_ptr_example() {// 創建 unique_ptr (C++14 推薦 make_unique)auto ptr = std::make_unique<int>(42);// 訪問對象*ptr = 100;std::cout << *ptr; // 輸出 100// 移動所有權auto ptr2 = std::move(ptr); // ptr 變為 nullptr// 自定義刪除器(處理特殊資源)auto file_deleter = [](FILE* f) { if(f) fclose(f); };std::unique_ptr<FILE, decltype(file_deleter)> file_ptr(fopen("test.txt", "r"), file_deleter);// 離開作用域自動釋放內存
}
2. std::shared_ptr
(共享所有權)
- 特點:多個指針共享對象,使用引用計數
- 適用場景:需要共享所有權的資源
- 內存開銷:控制塊(引用計數 + 弱計數)
void shared_ptr_example() {// 創建 shared_ptr (推薦 make_shared)auto ptr1 = std::make_shared<std::string>("Hello");// 共享所有權auto ptr2 = ptr1; // 引用計數 +1// 查看引用計數std::cout << ptr1.use_count(); // 輸出 2// 自定義刪除器auto arr_deleter = [](int* p) { delete[] p; };std::shared_ptr<int> arr_ptr(new int[10], arr_deleter);// 離開作用域時自動減少引用計數// 引用計數為0時釋放內存
}
3. std::weak_ptr
(弱引用)
- 特點:不增加引用計數,解決循環引用問題
- 適用場景:打破 shared_ptr 循環引用
- 用法:需轉換為 shared_ptr 訪問對象
struct Node {std::shared_ptr<Node> next;std::weak_ptr<Node> prev; // 使用 weak_ptr 避免循環引用
};void weak_ptr_example() {auto node1 = std::make_shared<Node>();auto node2 = std::make_shared<Node>();node1->next = node2;node2->prev = node1; // 不會增加引用計數// 訪問 weak_ptr 指向的對象if(auto locked = node2->prev.lock()) {// 成功轉換為 shared_ptr} else {// 對象已被釋放}
}
4. 智能指針對比表
特性 | unique_ptr | shared_ptr | weak_ptr |
---|---|---|---|
所有權 | 獨占 | 共享 | 無(觀察者) |
是否影響引用計數 | 否 | 是 | 否 |
復制語義 | 禁用(僅移動) | 允許 | 允許 |
內存開銷 | 幾乎為零 | 控制塊(約16-32字節) | 控制塊指針 |
典型應用場景 | 工廠模式返回值 | 共享數據 | 打破循環引用 |
線程安全 | 對象訪問需同步 | 引用計數原子操作 | 同 shared_ptr |
5. 最佳實踐與注意事項
-
優先使用
make_unique/make_shared
:// 更安全高效(單次內存分配) auto ptr = std::make_shared<MyClass>(arg1, arg2);
-
避免循環引用:
- 父對象用 shared_ptr 持有子對象
- 子對象用 weak_ptr 引用父對象
-
不要混用原始指針:
// 危險操作! MyClass* raw = new MyClass(); std::shared_ptr<MyClass> p1(raw); std::shared_ptr<MyClass> p2(raw); // 會導致雙重釋放
-
接口設計原則:
- 函數接收原始指針:不獲取所有權
- 函數接收 unique_ptr:獲取所有權
- 函數返回 unique_ptr:轉移所有權
-
特殊資源管理:
// 管理數組 (C++17+) auto arr = std::make_unique<int[]>(10);// 管理自定義資源 auto custom_deleter = [](Resource* r) { cleanup(r); }; std::unique_ptr<Resource, decltype(custom_deleter)> res(new Resource, custom_deleter);
6. 性能考慮
unique_ptr
:幾乎無開銷,等同于原始指針shared_ptr
:- 控制塊分配開銷(使用 make_shared 可優化)
- 原子操作引用計數(約比非原子操作慢10倍)
- 高頻訪問場景:考慮 unique_ptr + 移動語義
7. C++17/20 增強
-
數組支持:
// C++17 特有 auto arr = std::make_unique<int[]>(5); arr[0] = 10;
-
std::allocate_shared
:// 自定義分配器 CustomAllocator alloc; auto ptr = std::allocate_shared<MyClass>(alloc, args...);
-
std::weak_from_this
:class MyClass : public std::enable_shared_from_this<MyClass> { public:std::weak_ptr<MyClass> weak_ref() {return weak_from_this(); // C++17} };
8. 常見錯誤
// 錯誤1:返回局部對象的智能指針
std::shared_ptr<int> create() {int value = 42;return std::make_shared<int>(value); // 正確// return &value; // 災難!
}// 錯誤2:循環引用
struct A {std::shared_ptr<B> b; // 應使用 weak_ptr
};
struct B {std::shared_ptr<A> a; // 應使用 weak_ptr
};// 錯誤3:誤用 get() 管理生命周期
void process(int* raw) { /*...*/ }auto ptr = std::make_unique<int>(10);
process(ptr.get()); // 安全
delete ptr.get(); // 災難!
智能指針是現代 C++ 內存管理的核心工具,正確使用可消除 90% 以上的內存管理問題。