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

《C++ 沉思錄》精讀:類設計核查表、代理類與句柄類實戰

     編輯:關於C++

一、 類設計核查表:12 條鐵律

設計類時逐項自查,能消除 80% 潛在缺陷:

  1. 構造函數:是否需要?所有成員是否初始化?
  2. 數據成員私有性:是否合理封裝?
  3. 默認構造函數:數組/容器存儲時必須提供。
  4. 析構函數:管理資源(內存/句柄/鎖)時必須定義。
  5. 虛析構函數:基類若可能被刪除派生對象指針,必須虛析構。
  6. 拷貝構造函數:深拷貝需求?參數 const T&。
  7. 拷貝賦值運算符:自賦值檢查、釋放舊資源、分配新資源、返回 *this。
  8. 關係運算符:==/!=/</>/... 是否語義一致?
  9. 數組刪除:delete[] 配對 new[]。
  10. 引用參數 const 性:輸入參數應為 const T&。
  11. 成員函數 const 正確性:不修改狀態的函數標記 const,const 對象只能調用 const 成員函數。
  12. 拷貝/賦值參數 const:T(const T&)、T& operator=(const T&)。

二、 代理類:在容器中存儲多態對象

問題場景

class Vehicle { virtual double weight()=0; virtual void start()=0; };
class Aircraft : public Vehicle { ... };
class Helicopter : public Aircraft { ... };

// Vehicle parking_lot[100]; // 錯誤:Vehicle 抽象類不可實例化
// Vehicle* parking_lot[100]; // 可行但需手動管理指針生命周期

直接存對象會切片丟失派生特性;存裸指針引入手動內存管理負擔。

代理類方案

核心思想:代理類持有基類指針,轉發虛函數調用,自動管理所指對象生命周期(深拷貝/銷毀)。

class Vehicle {
public:
    virtual ~Vehicle() = 0;               // 虛析構
    virtual Vehicle* clone() const = 0;   // 虛拷貝(原型模式)
    virtual double weight() const = 0;
    virtual void start() = 0;
};

class VehicleSurrogate {
    Vehicle* vp; // 持有指針
public:
    VehicleSurrogate() : vp(nullptr) {}
    VehicleSurrogate(const Vehicle& v) : vp(v.clone()) {} // 從實體構造
    ~VehicleSurrogate() { delete vp; }

    // 拷貝構造:深拷貝
    VehicleSurrogate(const VehicleSurrogate& s) : vp(s.vp ? s.vp->clone() : nullptr) {}
    // 拷貝賦值:複製並交換
    VehicleSurrogate& operator=(const VehicleSurrogate& s) {
        if (this != &s) {
            delete vp;
            vp = s.vp ? s.vp->clone() : nullptr;
        }
        return *this;
    }

    // 轉發
    double weight() const { if (!vp) throw "empty"; return vp->weight(); }
    void start() { if (!vp) throw "empty"; vp->start(); }
};

// 使用
VehicleSurrogate lot[1000];
Truck myTruck;
lot[0] = VehicleSurrogate(myTruck); // 自動深拷貝

優勢:值語義、自動資源管理、容器可直接存儲 VehicleSurrogate 對象(無切片、無裸指針)。

三、 句柄類:共享實體的引用計數

代理類每次拷貝都深拷貝實體,對大對象/不可拷貝資源(文件、socket)代價高。句柄類引入引用計數,多個句柄共享同一實體,寫時復制(COW)優化修改。

演進 1:內嵌引用計數

class UPoint { // 實體+計數
    Point p; int u;
    UPoint() : u(1) {}
    UPoint(int x,int y) : p(x,y), u(1) {}
    UPoint(const Point& p0) : p(p0), u(1) {}
    friend class Handle;
};

class Handle {
    UPoint* up;
public:
    Handle() : up(new UPoint) {}
    Handle(int x,int y) : up(new UPoint(x,y)) {}
    Handle(const Point& p) : up(new UPoint(p)) {}
    Handle(const Handle& h) : up(h.up) { ++up->u; }
    Handle& operator=(const Handle& h) {
        ++h.up->u;
        if (--up->u == 0) delete up;
        up = h.up;
        return *this;
    }
    ~Handle() { if (--up->u == 0) delete up; }

    // 寫時復制
    Handle& x(int x0) {
        if (up->u != 1) { --up->u; up = new UPoint(up->p); }
        up->p.x(x0); return *this;
    }
    // ... 同理 y()、getX()、getY()
};

演進 2:分離引用計數(成員指針)

將計數器提取為獨立 int* u,實體指針 Point* p,支持繼承體系(句柄可綁定派生類對象)。

class Handle {
    Point* p; int* u;
public:
    Handle() : p(new Point), u(new int(1)) {}
    Handle(const Handle& h) : p(h.p), u(h.u) { ++*u; }
    Handle& operator=(const Handle& h) {
        ++*h.u;
        if (--*u == 0) { delete u; delete p; }
        p = h.p; u = h.u;
        return *this;
    }
    ~Handle() { if (--*u == 0) { delete u; delete p; } }
    // COW 同前
};

演進 3:抽象引用計數類 UseCount

將計數邏輯封裝為可復用組件,句柄類組合 UseCount,實現關注點分離。

class UseCount {
    int* p;
public:
    UseCount() : p(new int(1)) {}
    UseCount(const UseCount& u) : p(u.p) { ++*p; }
    ~UseCount() { if (--*p == 0) delete p; }
    bool only() const { 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;
    }
};

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

總結對比

模式拷貝語義適用場景複雜度
值類 (直接存對象)深拷貝小型、可拷貝、無多態低
代理類深拷貝實體多態對象、需值語義、實體可拷貝中
句柄類 (引用計數)共享實體 + COW大對象、不可拷貝資源、頻繁拷貝/傳遞高
智能指針 (現代 C++)shared_ptr/unique_ptr通用資源管理首選 (C++11+)標準庫

理解代理/句柄機制,能深刻體會 shared_ptr 設計精髓,在無法使用標準庫的環境(嵌入式、舊標準)下實現同等能力。

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