讲师中心 微信公众号
AI工具推荐 视频效率加速

C++如何实现状态模式?(行为随状态改变)

陌静大大_8567

陌静大大_8567

发布时间:2026-03-03 04:39:09

|

291人浏览过

|

来源于php中文网

原创

状态类必须继承共同接口且不可直接 new,核心是行为委托给当前状态对象;应通过工厂或静态实例管理状态生命周期,Context 持有 std::unique_ptr<State> 并提供 setState(),切换时需先调用 onExit() 再替换指针。

c++如何实现状态模式?(行为随状态改变)

状态类必须继承共同接口,且不能直接 new 具体状态

状态模式的核心是把行为委托给当前状态对象,而不是用 if-else 或 switch 硬编码判断。所以得先定义一个抽象状态接口,比如 State,所有具体状态(IdleState、RunningState)都继承它,并实现统一的 handle() 或 onEvent() 方法。

常见错误是让上下文类(比如 Context)在内部直接 new RunningState() —— 这会导致状态对象生命周期失控、无法共享或复用。正确做法是让状态对象由外部管理,或通过工厂/静态实例提供,确保状态切换时旧状态能被安全替换。

  • 状态类应是无状态的(不保存上下文数据),所有依赖的数据从 Context* 参数传入
  • 避免在状态构造函数里访问 Context 的未初始化成员
  • C++11 起推荐用 std::unique_ptr<state></state> 管理状态生命周期,防止裸指针悬挂

Context 类需持有状态指针并提供 setState() 接口

Context 是用户直接交互的对象,它不自己实现行为逻辑,而是把调用转发给当前 m_state。关键点在于:状态变更必须显式调用 setState(),而不是在某个事件处理中悄悄 new 一个新状态塞进去。

示例中容易漏掉的是:状态切换后,原状态对象可能还持有对 Context 的引用(比如回调注册),若没清理,会引发野指针或重复触发。所以 setState() 应先调用旧状态的 onExit()(如有),再替换指针。

立即学习“C++免费学习笔记(深入)”;

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载
  • setState() 接收 std::unique_ptr<state>&&</state>,避免拷贝和裸指针传递
  • 不要在 State 子类里直接修改 Context::m_state,破坏封装
  • 如果状态需要异步触发切换(如定时器回调),确保 Context 生命周期长于回调执行期

避免循环依赖:State 头文件不能 #include Context 实现细节

编译时最常卡住的地方是头文件依赖。比如 RunningState.h 为了调用 context->doSomething(),就 #include "Context.h";而 Context.h 又要声明 std::unique_ptr<state></state>,于是又 #include "State.h" —— 如果 State.h 里又用了 Context 的具体类型,就成环了。

解法是前向声明 + 指针/引用 + 实现文件延迟包含。在 State.h 中只写 class Context;,函数参数用 Context* 或 Context&;具体调用逻辑全挪到 State.cpp 里,那里才 #include "Context.h"。

  • 违反这点会导致编译失败,错误信息常是 invalid use of incomplete type 'class Context'
  • 别为了省事把所有东西塞进一个 .cpp 文件——状态逻辑分散才利于维护
  • 如果 Context 接口很大,可考虑抽一个轻量 ContextView 供状态使用,进一步解耦

std::shared_ptr 不适合做状态管理,除非明确需要共享所有权

有人图方便用 std::shared_ptr<state></state>,觉得自动管理省心。但状态模式里,“当前状态”是排他的:一次只能有一个活跃状态,且切换时旧状态应立即失效。用 shared_ptr 容易掩盖生命周期问题——比如某个异步任务还持着旧状态的引用,导致它继续响应事件,行为错乱。

真正需要共享的不是状态本身,而是状态所依赖的只读数据(如配置、资源句柄)。这些可以单独拎出来,用 shared_ptr 管理,状态对象仍用 unique_ptr 持有它们的弱引用(weak_ptr)或原始指针。

  • unique_ptr 强制表达“独占当前状态”的语义,编译器也能帮查空指针解引用
  • 若真要用 shared_ptr,务必重载 operator== 并检查是否指向同一对象,避免误判状态相等
  • 调试时打印 m_state.get() 地址比打印内容更可靠,因为状态对象通常没重载 operator<<
状态对象的析构时机、跨线程访问、以及事件队列中滞留的老状态指针——这三处最容易出问题,而且一出就是偶发 crash 或逻辑跳变,调试成本远高于前期多写两行生命周期控制。

热门AI工具

更多
DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

相关专题

更多
switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

1051

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

1417

2024.03.13

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

359

2026.04.23

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3148

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

4889

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3789

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

446

2026.01.19

class在c语言中的意思
class在c语言中的意思

在C语言中,"class" 是一个关键字,用于定义一个类。想了解更多class的相关内容,可以阅读本专题下面的文章。

3896

2024.01.03

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

60

2026.10.16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn