访问者设计模式通过双分派解耦数据结构与操作,核心是元素类提供accept方法调用访问者visit函数;新增操作只需添加访问者子类,符合开闭原则,但新增元素需修改所有访问者。

访问者设计模式在C++中主要用于分离数据结构与作用于其上的操作,特别适合处理具有稳定层次结构但操作频繁变化的复杂对象(比如AST、XML节点树、图形场景对象等)。核心思路是让“操作”从“数据”中解耦,通过双分派(double dispatch)实现运行时动态选择合适的行为。
定义访问者接口和元素基类
先声明抽象访问者和可被访问的元素基类。元素必须提供 accept 方法,接收访问者指针并反向调用访问者的 visit 方法——这是实现双分派的关键一步:
- 元素基类(Element)定义纯虚函数 accept(Visitor&)
- 访问者基类(Visitor)为每种具体元素类型声明对应的 visit 重载函数
- 所有具体元素(如 ConcreteElementA、B)实现 accept,内部调用 v.visit(*this)
利用C++重载+虚函数实现双分派
C++原生不支持多分派,但通过“虚函数 + 函数重载”组合可模拟双分派:第一次分派靠元素的虚函数 accept(确定元素类型),第二次靠访问者参数类型匹配(确定访问者行为)。例如:
- element->accept(v) 调用的是 ConcreteElementA::accept(第一次分派)
- 该函数内执行 v.visit(*this),由于 *this 类型是 ConcreteElementA&,编译器自动绑定到 Visitor::visit(ConcreteElementA&)(第二次分派)
支持新增操作无需修改元素类
这是访问者模式的核心价值。当要增加新功能(如导出为JSON、计算内存占用、做语义检查),只需添加新访问者子类,实现对应 visit 方法即可。原有元素类(ConcreteElementA/B/C)完全不用动,符合开闭原则。注意:如果新增元素类型,则所有已有访问者都要补 visit 方法——这是该模式的典型权衡。
立即学习“C++免费学习笔记(深入)”;
实用技巧与常见变体
真实项目中常做几点优化:
- 用 std::variant + std::visit 替代经典双分派(C++17起),更简洁安全,尤其适合扁平结构
- 访问者方法返回值可设为 std::any 或自定义结果类型,支持带返回值的操作(如求值、转换)
- 对容器类(如 ObjectStructure),提供 acceptAll(Visitor&) 批量遍历子元素并调用 accept
- 避免循环引用:访问者通常不持有元素指针,只做临时操作;若需缓存状态,用成员变量 + clear/reset 接口
基本上就这些。访问者不是万能钥匙,但它在编译期类型明确、结构稳定、行为多变的场景下非常扎实。写的时候注意虚析构、const 正确性、以及别让 visit 方法变得过于臃肿——拆分成小函数或辅以策略类更易维护。










