首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 智能指針完全指南:unique_ptr、shared_ptr、weak_ptr 核心原理與最佳實踐

     編輯:關於C++

智能指針演進

  • auto_ptr (C++98):**已棄用 (C++11)**,移動語義缺陷導致所有權轉移隱患
  • unique_ptr (C++11):**獨占所有權**,零開銷,支持數組
  • shared_ptr (C++11):**共享所有權**,引用計數,線程安全控制塊
  • weak_ptr (C++11):**非擁有觀察者**,打破循環引用

unique_ptr:獨占語義

std::unique_ptr<int> p1(new int(42));
std::unique_ptr<int> p2 = std::move(p1); // 显式移動,p1 置空
// p2 = p1; // 編譯錯誤:禁止拷貝

// 數組專用版本
std::unique_ptr<int[]> arr(new int[10]);
arr[5] = 100; // 支持下標訪問

// 自定義刪除器
auto deleter = [](FILE* f){ fclose(f); };
std::unique_ptr<FILE, decltype(deleter)> fp(fopen("a.txt","r"), deleter);

關鍵特性:移動構造/賦值轉移所有權;臨時對象賦值合法(右值引用);無額外控制塊開銷,大小等同裸指針。

shared_ptr:共享所有權

控制塊結構

堆上分配控制塊,包含:

  • 強引用計數(shared_ptr 數量)
  • 弱引用計數(weak_ptr 數量)
  • 可選:自定義刪除器、分配器
std::shared_ptr<int> sp1 = std::make_shared<int>(42); // 推薦:單次分配對象+控制塊
std::shared_ptr<int> sp2 = sp1; // 引用計數 +1
sp1.use_count(); // 2
sp1.reset();     // 計數 -1,sp2 仍持有
// 計數歸零時調用刪除器釋放對象

核心 API

shared_ptr() noexcept;
template<class Y> explicit shared_ptr(Y* p);
template<class Y, class D> shared_ptr(Y* p, D d);
shared_ptr(const shared_ptr& r) noexcept;
shared_ptr& operator=(const shared_ptr& r) noexcept;
void reset();
T& operator*() const;
T* operator->() const;
T* get() const;
bool unique() const; // use_count() == 1
long use_count() const;
explicit operator bool() const; // 可用於 if(sp)
void swap(shared_ptr& other);

make_shared 優勢

  • 單次內存分配(對象+控制塊相鄰),提升緩存局部性
  • 異常安全:構造拋異常不會洩漏
  • 不可用於自定義刪除器場景

weak_ptr:打破循環引用

不增加強引用計數**,僅增加弱引用計數。對象銷毀時弱引用計數不為 0,控制塊保留直到最後 weak_ptr 銷毀。

std::shared_ptr<Node> parent = std::make_shared<Node>();
std::shared_ptr<Node> child = std::make_shared<Node>();
parent->child = child;
child->parent = std::weak_ptr<Node>(parent); // 父指針用 weak_ptr

// 使用時鎖定
if (auto p = child->parent.lock()) {
    // p 是 shared_ptr,安全訪問
}

lock() 原子檢查強計數>0 並構造 shared_ptr,失敗返回空。

選擇指南

場景推薦理由
單一所有者、作用域內管理unique_ptr零開銷、編譯期強制所有權轉移
多所有者、生命周期不確定shared_ptr引用計數自動管理
觀察者/緩存/父子雙向引用weak_ptr不延長生命周期、打破循環
STL 容器存儲指針shared_ptr支持拷貝/賦值,算法兼容
工廠函數返回unique_ptr明確所有權轉移給調用者

常見陷阱

  • 循環引用:A→shared_ptr→B→shared_ptr→A → 永不釋放 → 用 weak_ptr 打破
  • 裸指針混用:`shared_ptr p(new T); shared_ptr q(p.get());` → 雙重釋放 → 始終用 `make_shared` 或同一控制塊
  • 跨 DLL 邊界:控制塊分配/釋放需同一堆,否則崩潰 → 統一分配器或避免跨邊界共享
  • 線程安全:控制塊引用計數原子操作,**但對象本身訪問需外部同步**

現代 C++ 最佳實踐

  1. 優先 make_unique (C++14) / make_shared
  2. 函數參數:
     唯一所有權傳遞 → `unique_ptr` 由值傳遞
     共享觀察 → `const shared_ptr&` 或 `shared_ptr` 由值傳遞(需延長壽命)
     非擁有訪問 → `T*` / `T&` / `weak_ptr`
  3. 避免 `get()` 暴露裸指針,必要時文檔註明所有權不轉移
  4. 自定義刪除器統一用 lambda,捕獲上下文
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved