當需要在同一容器中存儲不同派生類對象並統一調用介面時,代理/包裝類 是經典解決方案。本文通過「車輛管理系統」實例,展示 VehicleSurrogate 如何包裝 Vehicle 派生體系實現異構集合。
class Vehicle {
public:
virtual ~Vehicle() {}
virtual void Weight() const = 0;
virtual void Start() const = 0;
virtual Vehicle* Clone() const = 0; // 虛拷貝構造 (原型模式)
};
class SmallCar : public Vehicle {
public:
void Weight() const override { std::cout << "小汽車重 400 斤\n"; }
void Start() const override { std::cout << "小汽車啟動了\n"; }
Vehicle* Clone() const override { return new SmallCar(*this); }
};
class BigBus : public Vehicle {
public:
void Weight() const override { std::cout << "大汽車重 1.5 噸\n"; }
void Start() const override { std::cout << "大汽車啟動了\n"; }
Vehicle* Clone() const override { return new BigBus(*this); }
};
class VehicleSurrogate {
public:
// 默認構造:空代理
VehicleSurrogate() : m_Vehicle(nullptr) {}
// 從基類引用構造:觸發虛拷貝
VehicleSurrogate(const Vehicle& v) : m_Vehicle(v.Clone()) {}
// 拷貝構造:深拷貝被代理對象
VehicleSurrogate(const VehicleSurrogate& other)
: m_Vehicle(other.m_Vehicle ? other.m_Vehicle->Clone() : nullptr) {}
// 複製賦值:複製並交換惯用法
VehicleSurrogate& operator=(VehicleSurrogate other) { // 參數傳值利用拷貝構造
swap(other);
return *this;
}
// 移動構造/賦值 (C++11)
VehicleSurrogate(VehicleSurrogate&& other) noexcept : m_Vehicle(other.m_Vehicle) {
other.m_Vehicle = nullptr;
}
VehicleSurrogate& operator=(VehicleSurrogate&& other) noexcept {
if (this != &other) {
delete m_Vehicle;
m_Vehicle = other.m_Vehicle;
other.m_Vehicle = nullptr;
}
return *this;
}
~VehicleSurrogate() { delete m_Vehicle; }
// 代理轉發
void Weight() const { if (m_Vehicle) m_Vehicle->Weight(); }
void Start() const { if (m_Vehicle) m_Vehicle->Start(); }
// 交換支援
void swap(VehicleSurrogate& other) noexcept {
std::swap(m_Vehicle, other.m_Vehicle);
}
private:
Vehicle* m_Vehicle; // 擁有被代理對象所有權
};
VehicleSurrogate 行為像值類型,可直接存入 std::vectorClone() 創建新對象,避免雙重釋放Clone() 是原型模式應用,解決基類指標無法直接 new 的問題
int main() {
// 異構容器:直接存儲 VehicleSurrogate 值
std::vector garage;
garage.emplace_back(SmallCar()); // 就地構造
garage.emplace_back(BigBus());
std::cout << "=== 遍歷調用 ===\n";
for (const auto& v : garage) {
v.Start();
v.Weight();
}
std::cout << "\n=== 複製容器 ===\n";
std::vector garage2 = garage; // 深拷貝整個車隊
garage2[0].Start(); // 獨立副本
return 0;
}
=== 遍歷調用 === 小汽車啟動了 小汽車重 400 斤 大汽車啟動了 大汽車重 1.5 噸 === 複製容器 === 小汽車啟動了
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 基類指標容器 (vector | 簡單、無額外封裝 | 手動記憶體管理、拷貝容器需深拷貝、異常不安全 | 單一擁有者、生命周期清晰 |
| std::shared_ptr | 共享所有權、自動釋放 | 控制塊開銷、循環引用風險、引用計數原子操作開銷 | 多擁有者、共享所有權 |
| std::unique_ptr | 零開銷、單一擁有權 | 不可拷貝、容器拷貝需轉移 | 獨占所有權、高性能 |
| 代理值類 (VehicleSurrogate) | 值語義、深拷貝自動、標準容器完美支援、異常安全 | 每次拷貝分配記憶體、需實現 Clone() | 值語義集合、需拷貝語義、STL 算法兼容 |
若被代理對象有狀態 (如里程數),代理類天然支援狀態隔離:
class Taxi : public Vehicle {
int mileage_ = 0;
public:
void Drive(int km) { mileage_ += km; }
void Status() const { std::cout << "里程: " << mileage_ << "\n"; }
Vehicle* Clone() const override { return new Taxi(*this); } // 狀態隨對象拷貝
};
// 使用
VehicleSurrogate taxi1(Taxi());
taxi1.Drive(100); // 需在 Surrogate 添加 Drive 轉發
VehicleSurrogate taxi2 = taxi1; // 深拷貝,里程獨立
taxi2.Drive(50);
taxi1.Status(); // 100
taxi2.Status(); // 150
VehicleSurrogate 模式通過值語義包裝多態對象,解決了「異構對象統一存儲 + 深拷貝語義 + STL 容器兼容」三大痛點。核心技術點:
Clone() 實現運行時類型感知拷貝此模式廣泛應用於命令模式 (Command)、策略模式 (Strategy)、插件系統等需要「值語義多態」的場景。