发布于2026-07-12 阅读(0)
扫一扫,手机访问
在复杂订单系统的状态管理中,状态模式确实是C++开发者的老朋友了。核心思路并不复杂:订单对象持有一个指向抽象状态的指针,运行时动态切换行为。但实际落地时,有几个很容易踩的坑,咱们一个一个拆开说。
这一点可以说是状态模式的根基。如果 OrderState 基类没有声明虚析构函数,当通过 OrderState* 删除派生类对象(比如 PaidState)时,结果就是只调用了基类的析构函数。派生类里分配的资源——缓存、句柄、动态内存——全部漏光。
实操上没什么好商量的:所有状态基类必须带上 virtual ~OrderState() = default;。另外,状态类本身要轻量,数据归属在订单主体里,别把大对象往状态里塞。否则每次切换状态都触发深拷贝或重复构造,性能直接崩盘。
还要注意:如果用 std::unique_ptr 管理状态,虚析构是硬性要求,编译期就会卡住。

一个很常见的错误是把所有流转逻辑塞进订单类的某个函数里,写成 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::shared_ptr 则引入不必要的引用计数开销,还可能造成循环引用——比如状态又持有订单的 shared_ptr。
实操要点很清晰:
std::unique_ptr state_; std::unique_ptr,订单用 state_ = next_state; 直接移动赋值Order* 并长期持有——如果订单析构早于状态,指针直接变野指针。改用弱引用或回调函数替代说到底,状态模式真正难的不是写几个类,而是划清责任边界:谁拥有数据、谁触发变更、谁负责清理。一旦状态开始偷偷改订单字段或缓存订单指针,后续加并发、加日志、加回滚,问题就会立刻显现。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8