状态接口必须用虚析构函数,否则 delete 父类指针会内存泄漏;状态应轻量且不存大对象;状态转换须由状态自身决定,避免订单类硬编码流转逻辑;状态类不得直接修改订单字段,应通过受控接口通信;推荐用 std::unique_ptr 管理状态以确保安全切换。

状态接口必须用虚析构函数,否则 delete 父类指针会内存泄漏
状态模式的核心是让订单对象持有一个指向抽象状态的指针,运行时动态切换。如果 OrderState 接口没声明虚析构函数,当通过 OrderState* 删除派生类对象(比如 PaidState)时,只会调用基类析构,派生类中分配的资源(如缓存、句柄)不会释放。
实操建议:
- 所有状态基类必须带
virtual ~OrderState() = default; - 避免在状态类里存大对象——状态应轻量,数据归属订单主体;否则每次切换状态都触发深拷贝或重复构造
- 若用
std::unique_ptr管理,虚析构是强制要求,否则编译不过
状态转换不能靠 if-else 链硬编码,要用状态自身决定下一个状态
常见错误是把所有流转逻辑塞进订单类的某个函数里,比如 if (state == "paid") { state = new ShippedState(); }。这会导致订单类和所有状态强耦合,新增状态要改订单代码,违反开闭原则。
正确做法是每个状态实现自己的响应函数,返回下一个状态指针:
class PaidState : public OrderState {
public:
std::unique_ptr<orderstate> handlePay(Order& o) override { return nullptr; } // 重复支付不变更
std::unique_ptr<orderstate> handleShip(Order& o) override {
o.log("shipping...");
return std::make_unique<shippedstate>();
}
};</shippedstate></orderstate></orderstate>
订单类只需调用 current_state->handleShip(*this) 并接管返回值即可。这样新增一个 RefundedState,只改状态类,订单类完全不动。
避免在状态类中直接修改订单字段,用受控接口通信
状态类不是订单的“内部管家”,它不该直接写 order.status = "shipped" 或 order.items.clear()。一来破坏封装,二来难以审计状态副作用。
推荐方式是定义明确的回调接口:
- 订单类暴露
markAsShipped()、lockItems()等语义清晰的成员函数 - 状态类只调用这些函数,不碰字段
- 必要时让订单传
this给状态构造,但仅限读取只读字段(如order.id())
例如:ShippedState 构造时接收 const Order&,仅用于日志记录或校验,不存引用也不修改。
std::unique_ptr + 移动语义是状态切换最安全的组合
用裸指针管理状态容易悬空;用 std::shared_ptr 引入不必要的引用计数开销,且可能造成循环引用(比如状态又持有订单的 shared_ptr)。
实操要点:
- 订单类成员声明为
std::unique_ptrstate_; - 状态响应函数返回
std::unique_ptr,订单用state_ = next_state;直接移动赋值 - 禁止在状态类中保存
Order*并长期持有——若订单析构早于状态,指针变野指针;改用弱引用或回调函数替代
状态模式真正难的不是写几个类,而是划清责任边界:谁拥有数据、谁触发变更、谁负责清理。一旦状态开始偷偷改订单字段或缓存订单指针,后续加并发、加日志、加回滚就会立刻出问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











