C++实现状态模式处理复杂的订单逻辑 _ 状态接口与类转换【源码】
状态模式用于C++订单系统状态管理,需确保基类虚析构避免内存泄漏;状态转换由各状态类自行决定,订单类仅调用并接管返回值;状态类通过订单类回调接口通信,不直接修改字段;使用std::unique_ptr管理状态,避免悬空指针和引用计数开销。
在复杂订单系统的状态管理中,状态模式确实是C++开发者的老朋友了。核心思路并不复杂:订单对象持有一个指向抽象状态的指针,运行时动态切换行为。但实际落地时,有几个很容易踩的坑,咱们一个一个拆开说。
虚析构函数:绕不开的底线
这一点可以说是状态模式的根基。如果 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 handlePay(Order& o) override { return nullptr; }
std::unique_ptr handleShip(Order& o) override {
o.log("shipping...");
return std::make_unique();
}
};
订单类只需要调用 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*并长期持有——如果订单析构早于状态,指针直接变野指针。改用弱引用或回调函数替代
说到底,状态模式真正难的不是写几个类,而是划清责任边界:谁拥有数据、谁触发变更、谁负责清理。一旦状态开始偷偷改订单字段或缓存订单指针,后续加并发、加日志、加回滚,问题就会立刻显现。

































