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

C++ 智能指針全家族深度解析:從 auto_ptr 到 intrusive_ptr

     編輯:關於C++

前言

C++ 無自動垃圾回收,手動 new/delete 易導致內存洩露、重複釋放、異常安全問題。智能指針通過 RAII(資源獲取即初始化) 機制,在棧對象析構時自動釋放堆內存。本文系統講解 7 種智能指針的特性、適用場景與避坑指南。

智能指針通用特性

  • 本質是棧對象,析構函數釋放管理的堆內存
  • 重載 operator*、operator-> 模擬裸指針行為
  • get() 獲取裸指針;reset() 釋放當前/接管新對象
  • 判空用 if (ptr.get()) 而非 if (ptr)(後者永真)

1. std::auto_ptr(C++98,已棄用 C++11,移除 C++17)

特點:獨占所有權,複製/賦值會轉移所有權(源指針置空)。

std::auto_ptr<int> p1(new int(10));
std::auto_ptr<int> p2 = p1;  // p1 變空,p2 接管
// p1 現在是空指針!使用會崩潰

致命缺陷:

  • 不可放入 STL 容器(複製語義違反容器要求)
  • 不可作為函數參數傳值(所有權意外轉移)
  • release() 僅釋放所有權不釋放內存,易洩露
  • 無法管理數組(delete 而非 delete[])

結論:嚴禁在新代碼中使用,改用 unique_ptr。

2. boost::scoped_ptr / scoped_array(非複製可移動)

特點:獨占所有權,禁止複製/賦值(編譯期報錯),無轉移所有權機制。

boost::scoped_ptr<int> p1(new int(10));
// boost::scoped_ptr<int> p2 = p1;  // 編譯錯誤!
// p1.reset();  // 手動釋放
// 無 release(),析構自動 delete

適用場景:函數局部資源管理、類成員唯一擁有、無需共享/轉移所有權的簡單場景。scoped_array 管理動態數組。

3. boost::shared_ptr / shared_array(引用計數共享所有權)

特點:內部維護引用計數,支持複製、賦值、容器存儲、參數傳遞。計數歸零時自動 delete。

boost::shared_ptr<int> p1(new int(10));
boost::shared_ptr<int> p2 = p1;  // 計數 = 2
cout << p1.use_count();  // 2
p1.reset();  // 計數 = 1,內存未釋放
p2.reset();  // 計數 = 0,自動 delete

多線程注意事項

  • 引用計數增減非原子(舊版),多線程複製/賦值需加鎖
  • 推薦:線程間傳遞 weak_ptr,使用時 lock() 轉為 shared_ptr
  • C++11 std::shared_ptr 計數操作原子化,線程安全

shared_ptr 參數傳遞最佳實踐

方式計數變化適用場景
傳值 func(shared_ptr<T> p)+1/-1被調用方需共享所有權、延長生命週期
傳常量引用 func(const shared_ptr<T>& p)不變被調用方僅觀察/短期使用,調用方保證生命週期
傳裸指針/引用 func(T* p)不變不共享所有權,僅在調用方生命週期內訪問

4. boost::weak_ptr(觀察者,不影響計數)

特點:不增加引用計數,僅觀察 shared_ptr 管理的對象。對象銷毀後自動失效(expired() 返回 true)。

boost::shared_ptr<int> sp(new int(10));
boost::weak_ptr<int> wp = sp;  // 計數仍為 1
cout << wp.use_count();  // 1
if (auto locked = wp.lock()) {  // 安全獲取 shared_ptr
    cout << *locked;
}
sp.reset();  // 對象銷毀
cout << wp.expired();  // true

核心用途

  1. 打破循環引用:A 持有 B 的 shared_ptr,B 持有 A 的 weak_ptr
  2. 緩存/觀察者模式:緩存持有 weak_ptr,不阻礙對象釋放
  3. 多線程安全傳遞:線程間傳 weak_ptr,用時 lock()

5. boost::intrusive_ptr(內嵌引用計數)

特點:不自帶引用計數,要求被管理對象自行實現 intrusive_ptr_add_ref、intrusive_ptr_release。適用於已有引用計數機制的框架(如 COM、某些 GUI 庫)。

class MyObject {
    int ref_ = 0;
    friend void intrusive_ptr_add_ref(MyObject* p) { ++p->ref_; }
    friend void intrusive_ptr_release(MyObject* p) { if (--p->ref_ == 0) delete p; }
};

boost::intrusive_ptr<MyObject> p(new MyObject());  // 調用 add_ref

一般應用層極少直接使用,了解即可。

手寫帶引用計數的智能指針(教學示例)

// 簡易引用計數表
class HeapTable {
    struct Node { void* addr; int count; Node* next; };
    Node* head;
    // ... 增/減/查引用計數 ...
};

template<typename T>
class CSmartPtr {
    T* ptr;
    static HeapTable table;
public:
    CSmartPtr(T* p = nullptr) : ptr(p) { if (ptr) table.AddRef(ptr); }
    CSmartPtr(const CSmartPtr& other) : ptr(other.ptr) { if (ptr) table.AddRef(ptr); }
    CSmartPtr& operator=(const CSmartPtr& other) {
        if (this == &other) return *this;
        release();
        ptr = other.ptr;
        if (ptr) table.AddRef(ptr);
        return *this;
    }
    ~CSmartPtr() { release(); }
    void release() { if (ptr && table.DelRef(ptr) == 0) delete ptr; }
    T& operator*() { return *ptr; }
    T* operator->() { return ptr; }
};

選型決策樹

  1. 能用 C++11+?→ std::unique_ptr / std::shared_ptr / std::weak_ptr
  2. 只能用 Boost?
    • 無需共享、無需轉移 → scoped_ptr/scoped_array
    • 需共享/容器存儲/參數傳遞 → shared_ptr/shared_array
    • 需觀察不持有、打破循環引用 → weak_ptr
    • 對接現有引用計數框架 → intrusive_ptr
  3. 嚴禁 auto_ptr

黃金法則

代碼中不應出現裸 delete/free。所有堆資源由智能指針(或 RAII 容器)管理,構造時綁定,析構時自動釋放,實現異常安全、無洩露的資源管理。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved