設計類時逐項自查,能消除 80% 潛在缺陷:
const T&。*this。==/!=/</>/... 是否語義一致?delete[] 配對 new[]。const T&。const,const 對象只能調用 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)優化修改。
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()
};
將計數器提取為獨立 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 同前
};
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 設計精髓,在無法使用標準庫的環境(嵌入式、舊標準)下實現同等能力。