首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

C++ 智慧指標深度解析:參考計數、輔助類與控制塊兩大實作策略

     編輯:更多程式設計知識

前言

智慧指標以 RAII 管理動態記憶體,核心機制為參考計數。本文剖析兩種經典實作策略:輔助類 (類 shared_ptr 早期實作) 與控制塊 (現代標準庫實作),附完整代碼與記憶體圖解。

一、 為什麼需要智慧指標?

  • C++ 無 GC,手動 new/delete 易洩漏/雙重釋放/懸空指標。
  • 含指標成員的類別需正確實作「三大法則/五大法則」,負擔重。
  • 智慧指標封裝擁有權語意:unique_ptr (獨佔)、shared_ptr (共享)、weak_ptr (觀察)。

二、 策略一:輔助類

單獨類別 RefPtr 封裝原始指標 + 計數器,智慧指標持有 RefPtr*。

代碼實作


class Point {  // 被管理物件
    int x, y;
public:
    Point(int xv=0, int yv=0):x(xv),y(yv){}
    int getX() const { return x; }
    int getY() const { return y; }
};

class RefPtr {  // 輔助類:私有,僅 SmartPtr 可存取
    friend class SmartPtr;
    RefPtr(Point* p): ptr(p), count(1) {}
    ~RefPtr() { delete ptr; }
    int count;
    Point* ptr;
};

class SmartPtr {
    RefPtr* rp;
public:
    SmartPtr(Point* p = nullptr) : rp(p ? new RefPtr(p) : nullptr) {}
    SmartPtr(const SmartPtr& sp) : rp(sp.rp) { if (rp) ++rp->count; }
    SmartPtr& operator=(const SmartPtr& rhs) {
        if (rhs.rp) ++rhs.rp->count;  // 先增右邊,防自賦值
        if (rp && --rp->count == 0) delete rp;
        rp = rhs.rp;
        return *this;
    }
    ~SmartPtr() { if (rp && --rp->count == 0) delete rp; }
    Point* operator->() const { return rp->ptr; }
    Point& operator*() const { return *rp->ptr; }
};

記憶體結構

SmartPtr (棧上)          RefPtr (堆上)           Point (堆上)
+----------------+       +----------------+      +----------------+
| rp  ───────────+----→  | count: 2       |      | x, y           |
+----------------+       | ptr  ──────────+-----→+----------------+
                         +----------------+
SmartPtr2 (棧上)         ↑
+----------------+       |
| rp  ────────────+------+
+----------------+

優缺點

  • ✅ 概念清晰,計數與物件綁定
  • ❌ 額外配置 RefPtr (兩次 new)
  • ❌ 不支援陣列、自訂刪除器、型別擦除
  • ❌ RefPtr 需為 friend,封裝性弱

三、 策略二:控制塊

智慧指標直接持有 T* 與 int* count (控制塊分離),支援多型、自訂刪除器、弱引用。

代碼實作


class Point {
    int x, y;
public:
    Point(int xv=0, int yv=0):x(xv),y(yv){}
    virtual Point* clone() const { return new Point(*this); }  // 多型複製
    virtual ~Point() = default;
    int getX() const { return x; }
    int getY() const { return y; }
};

class D3Point : public Point {
    int z;
public:
    D3Point(int xv, int yv, int zv):Point(xv,yv),z(zv){}
    D3Point* clone() const override { return new D3Point(*this); }
    int getZ() const { return z; }
};

class SmartPtr {
    Point* ptr;
    int* count;  // 控制塊:單獨配置的計數器
public:
    SmartPtr(Point* p = nullptr) : ptr(p), count(p ? new int(1) : nullptr) {}
    SmartPtr(Point& obj) : ptr(obj.clone()), count(new int(1)) {}  // 多型建構

    SmartPtr(const SmartPtr& sp) : ptr(sp.ptr), count(sp.count) {
        if (count) ++*count;
    }
    SmartPtr& operator=(const SmartPtr& sp) {
        if (sp.count) ++*sp.count;  // 先增
        release();                  // 再釋放舊資源
        ptr = sp.ptr; count = sp.count;
        return *this;
    }
    ~SmartPtr() { release(); }

    void release() {
        if (count && --*count == 0) {
            delete ptr;
            delete count;
        }
    }
    Point* operator->() const { return ptr; }
    Point& operator*() const { return *ptr; }
};

記憶體結構

SmartPtr (棧上)          控制塊 (堆上)           Point (堆上)
+----------------+       +----------+          +----------------+
| ptr  ──────────+----→  | count: 2 |          | vptr, x, y     |
| count  ────────+----→  +----------+          +----------------+
+----------------+
SmartPtr2 (棧上)         ↑
+----------------+       |
| ptr  ──────────+------+
| count  ────────+------+
+----------------+

關鍵進階特性

  • 多型支援:建構式接受 Point&,透過 virtual clone() 複製實際衍生類別 (D3Point)。
  • 控制塊分離:計數器獨立配置,允許同一物件被不同型別智慧指標管理 (需型別擦除刪除器)。
  • 弱引用基礎:weak_ptr 共享控制塊但不增加強引用計數。

四、 現代標準庫實作差異 (C++11 std::shared_ptr)

特性教學版控制塊std::shared_ptr
控制塊內容僅計數器強/弱計數、刪除器、配置器、型別擦除存儲
配置優化兩次配置 (物件+控制塊)make_shared 單次配置 (物件+控制塊相鄰)
執行緒安全無原子操作計數器
自訂刪除器不支援支援 (型別擦除)
陣列支援不支援shared_ptr<T[]> (C++17)

五、 使用建議

  1. 優先 std::make_shared:單次配置、快取友好、例外安全。
  2. 避免循環引用:父子關係用 weak_ptr 打破環。
  3. 傳遞語意:函式參數用 const shared_ptr<T>& (不增計數) 或 T*/T& (觀察者)。
  4. 勿混用原始指標:shared_ptr 建構後,物件生命週期完全交給它管理。

結語

從輔助類到控制塊,智慧指標實作演進反映了 C++ 資源管理的核心哲學:擁有權語意明確化、控制資訊集中化、效能最佳化。理解底層機制能幫助開發者正確選擇 unique/shared/weak、避開循環引用、撰寫高效能現代 C++ 程式碼。

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