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

《C++ 沉思錄》閱讀筆記:代理類模式深度解析

     編輯:關於C語言

前言

Andrew Koenig 與 Barbara Moo 這對 C++ 界的「神仙眷侶」著作《C++ 沉思錄》條理清晰,深入淺出。本文記錄書中關於「代理類」一章的核心心得,探討如何優雅地解決容器存儲不同派生類對象時的內存管理與類型綁定問題。

問題背景:異構容器的困境

假設我們設計一個交通工具類別體系:

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

class RoadVehicle : public Vehicle { /* ... */ };
class AutoVehicle : public RoadVehicle { /* ... */ };
class Aircraft : public Vehicle { /* ... */ };
class Helicopter : public Aircraft { /* ... */ };

現在需要一個容器存放各種交通工具。乍看簡單,實則暗藏玄機。

嘗試 1:直接存放對象

Vehicle parking_lot[1000];

失敗原因:Vehicle 含純虛函數,屬抽象類,無法實例化。

嘗試 2:移除純虛函數後存放對象

語法合法,但賦值時會發生對象切片:

Helicopter x = /* ... */; 
parking_lot[num_vehicles++] = x; // x 被切片為 Vehicle,失去 Helicopter 特有屬性

這就像將 double 強轉為 int 丟失小數部分一樣不可接受。

嘗試 3:存放基類指針

Vehicle* parking_lot[1000]; 
Helicopter x; 
parking_lot[num_vehicles++] = &x;

隱患:若 x 為局部變量,離開作用域後指針懸垂,導致未定義行為。

嘗試 4:動態分配副本

parking_lot[num_vehicles++] = new Helicopter(x);

雖解決懸垂指針,但引入新問題:

  • 需手動管理內存(delete 時機難把控)
  • 編譯期類型未知時無法複製:例如欲將 parking_lot[q] 的對象複製到 parking_lot[p],卻不知其具體類型,無法調用對應構造函數;若共享指針則會導致雙重釋放。

破局關鍵:虛擬複製函數與代理類

步驟一:引入虛擬 copy()

在基類定義純虛函數 virtual Vehicle* copy() const = 0;,各派生類實現自我複製:

Vehicle* Helicopter::copy() const { return new Helicopter(*this); }

調用端無需知道具體類型:

parking_lot[num_vehicles++] = x.copy(); 
parking_lot[p] = parking_lot[q]->copy();

解決了「編譯期類型未知」的複製問題,但每次複製仍產生新對象,內存開銷大,且需修改所有派生類(侵入性強)。

步驟二:代理類——「用類表示概念」

書中提出深刻觀點:類不僅可表示具體事物,也可表示抽象概念。代理類如同明星的經紀人,管理真實對象(明星)的生命周期與接口轉發。

設計 VehicleProxy:

class VehicleProxy { 
public: 
    VehicleProxy();                           // 默認構造,產生「空代理」
    VehicleProxy(const Vehicle&);           // 綁定任意 Vehicle 派生對象
    ~VehicleProxy();                          // 析構時釋放內部指針
    VehicleProxy(const VehicleProxy&);      // 複製構造:深拷貝內部對象
    VehicleProxy& operator=(const VehicleProxy&); // 賦值:刪舊拷新
private: 
    Vehicle* p; 
};

核心實現:

VehicleProxy::VehicleProxy() : p(0) {} 
VehicleProxy::VehicleProxy(const Vehicle& v) : p(v.copy()) {} 
VehicleProxy::~VehicleProxy() { delete p; } 
VehicleProxy::VehicleProxy(const VehicleProxy& v) 
    : p(v.p ? v.p->copy() : 0) {} 
VehicleProxy& VehicleProxy::operator=(const VehicleProxy& v) { 
    if (this != &v) { 
        delete p; 
        p = v.p ? v.p->copy() : 0; 
    } 
    return *this; 
}

完美容器誕生

VehicleProxy parking_lot[1000]; 
Helicopter x; 
parking_lot[num_vehicles++] = x; // 隱式轉換調用 VehicleProxy(const Vehicle&)

總結

  • 核心難點:異構容器的內存管理 + 編譯期類型綁定。
  • 代理類模式:將複雜繼承體系「壓縮」為單一可實例化類型,內部持有基類指針,利用虛擬 copy() 實現深拷貝與多態綁定。
  • 設計哲理:「用類表示概念」——代理類本身非具體業務對象,而是管理協議的封裝者,極大簡化了客戶端代碼。

本文出自「菜鳥浮出水」博客,原文鏈接:https://rangercyh.blog.51cto.com/1444712/1291958

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