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

C++ 智能指針手寫實現:引用計數與 Handle-Body 機制深度解析

     編輯:關於C語言

設計思路:Handle-Body 分離

將引用計數與被管理對象指針解耦,分別封裝為 UseCount 計數類與 Point 業務類,智能指針 SmartPoint 組合兩者,實現共享所有權語義。

三大核心類實現

1. 業務對象 Point

class Point {
public:
    Point() : m_x(0), m_y(0) {}
    Point(int& x, int& y) : m_x(x), m_y(y) {}
    Point(const Point& other) : m_x(other.m_x), m_y(other.m_y) {}
    ~Point() { cout << "Point 析構" << endl; }
    int GetX() const { return m_x; }
    int GetY() const { return m_y; }
    void SetX(int x) { m_x = x; }
    void SetY(int y) { m_y = y; }
private: int m_x, m_y;
};

2. 引用計數器 UseCount

關鍵點:

  • 構造/拷構/賦值/析構中正確維護 *m_iUse
  • 提供 ReAttach 判斷「舊計數歸零後重新接管新計數」場景(賦值操作核心)
class UseCount {
public:
    UseCount() : m_iUse(new int(1)) {}
    UseCount(const UseCount& other) : m_iUse(other.m_iUse) { ++*m_iUse; }
    UseCount& operator=(UseCount& other) {
        if (this == &other) return *this;
        ++*other.m_iUse;           // 右側先增
        if (--*m_iUse == 0) delete m_iUse; // 左側後減,歸零釋放
        m_iUse = other.m_iUse;
        return *this;
    }
    ~UseCount() { if (--*m_iUse == 0) delete m_iUse; }
    bool ReAttach(UseCount& other) { // 用於 SmartPoint 賦值
        ++*other.m_iUse;
        if (--*m_iUse == 0) { delete m_iUse; m_iUse = other.m_iUse; return true; }
        m_iUse = other.m_iUse; return false;
    }
    int GetUseNum() const { return *m_iUse; }
private: int* m_iUse;
};

3. 智能指針 SmartPoint

class SmartPoint {
public:
    SmartPoint() : m_Point(new Point()) {}
    SmartPoint(int x, int y) : m_Point(new Point(x, y)) {}
    SmartPoint(const Point& p) : m_Point(new Point(p)) {}
    SmartPoint(SmartPoint& other) : m_Point(other.m_Point), m_UseCount(other.m_UseCount) {}
    SmartPoint& operator=(SmartPoint& other) {
        if (this == &other) return *this;
        if (m_UseCount.ReAttach(other.m_UseCount)) delete m_Point; // 舊對象無人引用則刪除
        m_Point = other.m_Point;
        return *this;
    }
    void SetX(int x) { m_Point->SetX(x); }
    void SetY(int y) { m_Point->SetY(y); }
    int GetX() const { return m_Point->GetX(); }
    int GetY() const { return m_Point->GetY(); }
    void GetUseNum() const { cout << "use number is " << m_UseCount.GetUseNum() << endl; }
    ~SmartPoint() { if (m_UseCount.GetUseNum() == 1) delete m_Point; }
private:
    Point* m_Point;
    UseCount m_UseCount; // 值語義,隨 SmartPoint 生命週期
};

測試驗證

int main() {
    SmartPoint* PA = new SmartPoint();
    SmartPoint* PB = new SmartPoint(*PA); // 拷構,計數=2
    SmartPoint* PC = new SmartPoint();
    *PC = *PA; // 賦值,PA/PC 共享,PB 仍共享原對象 → 計數=3
    PC->GetUseNum(); // 3
    PA->GetUseNum(); // 3
    delete PA;       // 計數遞減為 2,對象存活
    PC->GetUseNum(); // 2
    delete PB;       // 計數遞減為 1
    PC->GetUseNum(); // 1
    delete PC;       // 計數歸零,觸發 Point 析構
}

輸出結果分析

use number is 3
use number is 3
use number is 2
use number is 1
Point 析構函數調用了

完美演示了共享所有權:最後一個 SmartPoint 析構時釋放 Point。

現代 C++ 對比與建議

  • 生產代碼請直接使用 std::shared_ptr(C++11)、std::make_shared,內部採用控制塊優化內存佈局,並實現了線程安全引用計數(原子操作)。
  • 本手寫版為教學演示,非線程安全,且缺乏弱指針、自定義刪除器、數組支持等完整特性。
  • 理解 Handle-Body 分離、引用計數增減時機、自賦值檢查、析構順序等核心邏輯,有助於排查 shared_ptr 循環引用、雙重釋放等高級問題。

本文轉載自 “風清揚song” 博客,原文地址:https://2309998.blog.51cto.com/2299998/1304175

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