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 { /* ... */ };
現在需要一個容器存放各種交通工具。乍看簡單,實則暗藏玄機。
Vehicle parking_lot[1000];
失敗原因:Vehicle 含純虛函數,屬抽象類,無法實例化。
語法合法,但賦值時會發生對象切片:
Helicopter x = /* ... */; parking_lot[num_vehicles++] = x; // x 被切片為 Vehicle,失去 Helicopter 特有屬性
這就像將 double 強轉為 int 丟失小數部分一樣不可接受。
Vehicle* parking_lot[1000]; Helicopter x; parking_lot[num_vehicles++] = &x;
隱患:若 x 為局部變量,離開作用域後指針懸垂,導致未定義行為。
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