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

C++ 智能指針手寫實現:引用計數管理動態記憶體完整教程

     編輯:關於C語言

C++ 智能指針手寫實現:引用計數管理動態記憶體完整教程

前言

C++ 原始指針易導致記憶體洩漏、重複釋放、懸空指針。智能指針通過 RAII + 引用計數自動管理對象生命周期。本文從零手寫一個引用計數智能指針 SmartPoint,深度解析核心機制。

一、設計架構

SmartPoint (用戶層)          UPoint (控制塊,內部)
+-------------------+        +-------------------+
| UPoint* m_UPoint  | ───→   | Point m_Point     |  ← 被管理對象 (值語義)
|                   |        | int m_iUse        |  ← 引用計數
+-------------------+        +-------------------+

關鍵設計決策

  • 控制塊分離:UPoint 獨立分配,存儲對象副本 + 計數器
  • 淺拷貝共享:所有 SmartPoint 複製共享同一 UPoint*
  • 計數歸零刪除:最後一個 SmartPoint 析構時 delete m_UPoint

二、被管理類:Point


class Point {
public:
    Point() : m_x(0), m_y(0) {}
    Point(int x, int y) : m_x(x), m_y(y) {}
    ~Point() { std::cout << "Point destructed\n"; }
    
    void SetX(int x) { m_x = x; }
    void SetY(int y) { m_y = y; }
    int GetX() const { return m_x; }
    int GetY() const { return m_y; }
    void SetName(const std::string& n) { m_name = n; }
    void ShowInfo() const { 
        std::cout << "Point[" << m_name << "] = (" << m_x << ", " << m_y << ")\n"; 
    }
private:
    int m_x, m_y;
    std::string m_name;
};

三、控制塊:UPoint (私有,僅 SmartPoint 可訪問)


class UPoint {
    friend class SmartPoint;  // 關鍵:授予 SmartPoint 完全訪問權
private:
    UPoint() : m_iUse(1) {}
    UPoint(int x, int y) : m_Point(x, y), m_iUse(1) {}
    UPoint(const Point& p) : m_Point(p), m_iUse(1) {}
    
    Point m_Point;  // 值語義存儲被管理對象
    int m_iUse;     // 引用計數
};

四、智能指針核心實現:SmartPoint


class SmartPoint {
public:
    // 構造函數族
    SmartPoint() : m_UPoint(new UPoint()) {}
    SmartPoint(int x, int y) : m_UPoint(new UPoint(x, y)) {}
    SmartPoint(const Point& p) : m_UPoint(new UPoint(p)) {}
    
    // 拷貝構造:淺拷貝指標,計數+1
    SmartPoint(const SmartPoint& other) : m_UPoint(other.m_UPoint) {
        ++m_UPoint->m_iUse;
        std::cout << "Copy ctor, use=" << m_UPoint->m_iUse << "\n";
    }
    
    // 複製賦值:處理自賦值、舊計數-1、新計數+1
    SmartPoint& operator=(const SmartPoint& other) {
        std::cout << "Assignment operator\n";
        if (this != &other) {  // 自賦值檢查
            ++other.m_UPoint->m_iUse;  // 先增新,防異常
            
            // 釋放舊資源
            if (--m_UPoint->m_iUse == 0) {
                std::cout << "Deleting UPoint in assignment\n";
                delete m_UPoint;
            }
            
            m_UPoint = other.m_UPoint;  // 指向新控制塊
        }
        return *this;
    }
    
    // 析構:計數-1,歸零刪除
    ~SmartPoint() {
        std::cout << "Destructor, use=" << m_UPoint->m_iUse - 1 << "\n";
        if (--m_UPoint->m_iUse == 0) {
            std::cout << "Deleting UPoint\n";
            delete m_UPoint;
        }
    }
    
    // 代理轉發接口
    void SetX(int x) { m_UPoint->m_Point.SetX(x); }
    void SetY(int y) { m_UPoint->m_Point.SetY(y); }
    int GetX() const { return m_UPoint->m_Point.GetX(); }
    int GetY() const { return m_UPoint->m_Point.GetY(); }
    void SetName(const std::string& n) { m_UPoint->m_Point.SetName(n); }
    void ShowInfo() const { m_UPoint->m_Point.ShowInfo(); }
    
private:
    UPoint* m_UPoint;  // 指向控制塊
};

五、測試驗證


int main() {
    std::cout << "=== 創建 A ===\n";
    SmartPoint* A = new SmartPoint();
    A->SetName("Class A");
    A->ShowInfo();  // Point[Class A] = (0, 0)
    
    std::cout << "\n=== 拷貝構造 B = A ===\n";
    SmartPoint* B = new SmartPoint(*A);
    B->ShowInfo();  // 共享同一對象
    
    std::cout << "\n=== 創建 C 並賦值 C = B ===\n";
    SmartPoint* C = new SmartPoint();
    *C = *B;  // 複製賦值
    A->ShowInfo();
    
    std::cout << "\n=== 刪除 A ===\n";
    delete A;  // 計數 3→2,不刪除 UPoint
    B->ShowInfo();
    
    std::cout << "\n=== 刪除 B ===\n";
    delete B;  // 計數 2→1,不刪除
    C->ShowInfo();
    
    std::cout << "\n=== 刪除 C ===\n";
    delete C;  // 計數 1→0,刪除 UPoint → Point 析構
    
    std::cin.get();
    return 0;
}

六、運行輸出分析

=== 創建 A ===
Point[Class A] = (0, 0)

=== 拷貝構造 B = A ===
Copy ctor, use=2
Point[Class A] = (0, 0)

=== 創建 C 並賦值 C = B ===
Assignment operator
Point[Class A] = (0, 0)

=== 刪除 A ===
Destructor, use=2
Point[Class A] = (0, 0)

=== 刪除 B ===
Destructor, use=1
Point[Class A] = (0, 0)

=== 刪除 C ===
Destructor, use=0
Deleting UPoint
Point destructed

七、核心邏輯圖解

引用計數變化流程:
創建 A     : UPoint(use=1)
拷貝 B=A   : UPoint(use=2)  ← 共享
賦值 C=B   : UPoint(use=3)  ← 共享
delete A   : UPoint(use=2)  ← 仍有 B,C
delete B   : UPoint(use=1)  ← 仍有 C
delete C   : UPoint(use=0)  ← 刪除 UPoint + Point

八、與標準庫對比

特性手寫 SmartPointstd::shared_ptr
控制塊分配單獨 new UPoint通常與對象同塊分配 (make_shared 優化)
線程安全無 (計數非原子)控制塊計數原子操作
弱引用不支援std::weak_ptr 打破循環引用
刪除器固定 delete可自定義 Deleter
數組支援無shared_ptr 專用偏特化

九、避坑指南

  1. 循環引用:A 持有 B 的 SmartPoint,B 持有 A 的 SmartPoint → 永不釋放。解決:引入 weak_ptr 或人工斷環
  2. 多線程:引用計數 ++/-- 非原子,並發拷貝/銷毀會導致計數錯誤。解決:用 std::shared_ptr (原子計數) 或加鎖
  3. this 指針陷阱:類內部創建 shared_ptr(this) 會產生雙重控制塊。解決:繼承 std::enable_shared_from_this
  4. 性能:控制塊額外分配 + 原子操作開銷。高性能場景考慮 unique_ptr + 移動語義

十、總結

智能指針核心三件套:RAII 封裝 + 引用計數共享 + 歸零自動刪除。手寫實現能深度理解:控制塊分離設計、拷貝/賦值/析構的計數邏輯、自賦值檢查、異常安全 (先增新後減舊)。實際項目請直接使用 std::shared_ptr/std::unique_ptr,配合 std::make_shared 獲得最佳性能與安全性。

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