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

《C++ 沉思錄》閱讀筆記:句柄類與智能指針演進

     編輯:關於C語言

承接上文:代理類的局限

上一篇介紹的代理類雖解決了異構容器問題,但存在兩大痛點:

  1. 強制深拷貝:每次複製代理類都會 new 一個新對象,大對象或含外部引用的對象開銷巨大,甚至無法正確實現 copy()。
  2. 侵入性修改:要求所有派生類實現 copy(),若在現有代碼庫上擴展往往不被允許。

新思路:共享所有權與引用計數

若不複製對象,如何實現多對象綁定同一實體?C++ 中除指針外,引用也能指向同一對象,但作為返回值極其危險(懸垂引用、左值修改、動態內存無法釋放)。最終選擇回歸指針,並通過封裝類消除其安全隱患——這就是句柄,即現代智能指針的雛形。

句柄類設計演示:以 Point 為例

class Point { 
public: 
    Point() : xval(0), yval(0) {} 
    Point(int x, int y) : xval(x), yval(y) {} 
    int x() const { return xval; } 
    int y() const { return yval; } 
    Point& x(int xv) { xval = xv; return *this; } 
    Point& y(int yv) { yval = yv; return *this; } 
private: 
    int xval, yval; 
};

句柄雛形

class Handle { 
public: 
    Handle(); 
    Handle(int, int); 
    Handle(const Point&); 
    Handle(const Handle&); 
    Handle& operator=(const Handle&); 
    Handle& x(int); // 封裝 Point::x(int) 
    ~Handle(); 
private: 
    Point* p; 
    int* u; // 引用計數指針 
};

關鍵決策:引用計數不能放在 Handle 內(無法同步多實例),也不能放在 Point 內(侵入業務類),故額外分配一塊 int 內存,u 指向它。

核心語義:指針語義 vs 值語義(寫時復制)

當多個句柄綁定同一對象時,修改操作該如何表現?

Handle h(3, 4); 
Handle h2 = h; // 共享同一 Point 
h2.x(5);       // 此時 h.x() 應返回 3 還是 5?
  • 指針語義:所有句柄操作直接作用於共享對象,h.x() 返回 5。實現簡單,但修改影響全局,不可預期。
  • 值語義(寫時復制 COW):讀操作共享,寫操作時若引用計數 > 1,則複製一份新對象修改,原對象不變。h.x() 仍為 3。兼具效率與直觀性,廣泛用於 std::string、OS 頁面管理(fork)等。

引用計數管理通用代碼

Handle::Handle() : u(new int(1)), p(new Point) {} 
Handle::Handle(int x, int y) : u(new int(1)), p(new Point(x, y)) {} 
Handle::Handle(const Point& p0) : u(new int(1)), p(new Point(p0)) {} 
Handle::Handle(const Handle& h) : u(h.u), p(h.p) { ++*u; } 

Handle& Handle::operator=(const Handle& h) { 
    ++*h.u;                 // 先增右邊,防自賦值導致過早刪除 
    if (--*u == 0) { delete u; delete p; } 
    u = h.u; p = h.p; 
    return *this; 
} 

Handle::~Handle() { 
    if (--*u == 0) { delete u; delete p; } 
}

指針語義的 x(int)

Handle& Handle::x(int x0) { p->x(x0); return *this; }

值語義(COW)的 x(int)

Handle& Handle::x(int x0) { 
    if (*u != 1) {          // 共享時需分離 
        --*u; 
        p = new Point(*p);  // 深拷貝 
        u = new int(1);     // 新計數器歸 1 
    } 
    p->x(x0); 
    return *this; 
}

進階:抽離引用計數為獨立類

將計數邏輯封裝為 UseCount,降低耦合:

class UseCount { 
public: 
    UseCount() : p(new int(1)) {} 
    UseCount(const UseCount& u) : p(u.p) { ++*p; } 
    ~UseCount() { if (--*p == 0) delete p; } 
    bool only() { return *p == 1; } 
    bool reattach(const UseCount& u) { // 賦值時重掛計數 
        ++*u.p; 
        if (--*p == 0) { delete p; p = u.p; return true; } 
        p = u.p; return false; 
    } 
    bool makeonly() { // COW 分離 
        if (*p == 1) return false; 
        --*p; p = new int(1); return true; 
    } 
private: 
    int* p; 
};

句柄類精簡為:

class Handle { 
public: 
    Handle() : p(new Point) {} 
    Handle(int x, int y) : p(new Point(x, y)) {} 
    Handle(const Point& p0) : p(new Point(p0)) {} 
    ~Handle() { if (u.only()) delete p; } 
    Handle& operator=(const Handle& h) { 
        if (u.reattach(h.u)) delete p; 
        p = h.p; return *this; 
    } 
    Handle& x(int x0) { 
        if (u.makeonly()) p = new Point(*p); 
        p->x(x0); return *this; 
    } 
private: 
    Point* p; 
    UseCount u; 
};

總結與對比

特性代理類句柄(引用計數)
複製策略每次深拷貝共享+COW 可選
侵入業務類需加 copy()零侵入
內存開銷對象數 = 句柄數對象數 ≤ 句柄數
語義靈活性固定值語義可選指針/值語義

句柄類通過引用計數實現共享所有權,配合 COW 兼顧效率與直觀語義,且不侵入業務代碼,是現代 std::shared_ptr/Copy-on-write 技術的直接源頭。本文出自「菜鳥浮出水」博客:原文鏈接

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