C++设计模式之行为型模式:解释器模式(Interpreter) - 详解

news/2025/10/4 20:06:08/文章来源:https://www.cnblogs.com/slgkaifa/p/19125875

C++设计模式之行为型模式:解释器模式(Interpreter) - 详解

解释器模式(Interpreter)是行为型设计模式的一种,它用于定义语言的语法规则并构建解释器来解释该语言的句子。这种模式特别适合处理简单的语法解析场景,如表达式计算、配置文件解析、轻松脚本解释等。

一、核心思想与角色

解释器模式的核心是“将语法规则抽象为类层次结构,依据递归调用解释句子”。其核心角色如下:

角色名称核心职责
抽象表达式(AbstractExpression)定义所有表达式的通用接口,声明解释方法(如interpret())。
终结符表达式(TerminalExpression)实现语法中终结符的解释(如表达式中的数字、变量),是递归的叶子节点。
非终结符表达式(NonterminalExpression)实现语法中非终结符的解释(如运算符+-),包含其他表达式作为子节点,通过递归解释子表达式完成计算。
上下文(Context)存储解释器的全局信息(如变量映射表),提供给表达式解释时使用。
客户端(Client)根据语法规则构建抽象语法树(由终结符和非终结符表达式组成),并调用解释方法。

核心思想:将语法规则分解为一系列表达式类,每个类对应一条语法规则;通过组合这些表达式类构建抽象语法树(AST),最终通过递归调用interpret()方法解释整个句子。

二、建立示例(简单算术表达式解析)

假设我们需要解析并计算包含+-运算符和整数的简单算术表达式(如1 + 2 - 3)。使用解释器模式可将表达式拆解为语法树并计算结果:

#include <iostream>#include <string>#include <vector>#include <memory>#include <cctype>#include <map>// 3. 上下文:存储表达式中的变量和全局信息(此处简化为无变量)class Context {public:// 示例:若有变量,可通过此方法设置(如x=5)void setVariable(const std::string& name, int value) {variables[name] = value;}// 获取变量值int getVariable(const std::string& name) const {auto it = variables.find(name);if (it != variables.end()) {return it->second;}return 0; // 变量未定义时返回0}private:std::map<std::string, int> variables; // 变量映射表};// 1. 抽象表达式class AbstractExpression {public:virtual int interpret(const Context& context) const = 0;virtual ~AbstractExpression() = default;};// 2. 终结符表达式:数字(如"123")class NumberExpression : public AbstractExpression {private:int number; // 存储解析后的数字public:NumberExpression(int num) : number(num) {}// 解释:直接返回数字值int interpret(const Context& context) const override {return number;}};// 2. 非终结符表达式:加法(如"a + b")class AddExpression : public AbstractExpression {private:AbstractExpression* left;  // 左操作数AbstractExpression* right; // 右操作数public:AddExpression(AbstractExpression* l, AbstractExpression* r): left(l), right(r) {}// 解释:递归计算左右表达式并相加int interpret(const Context& context) const override {return left->interpret(context) + right->interpret(context);}// 析构:释放子表达式~AddExpression() override {delete left;delete right;}};// 2. 非终结符表达式:减法(如"a - b")class SubtractExpression : public AbstractExpression {private:AbstractExpression* left;AbstractExpression* right;public:SubtractExpression(AbstractExpression* l, AbstractExpression* r): left(l), right(r) {}int interpret(const Context& context) const override {return left->interpret(context) - right->interpret(context);}~SubtractExpression() override {delete left;delete right;}};// 辅助工具:表达式解析器(构建抽象语法树)class ExpressionParser {private:// 解析数字(从字符串中提取连续数字字符)int parseNumber(const std::string& expr, int& index) {int num = 0;while (index < expr.size() && isdigit(expr[index])) {num = num * 10 + (expr[index] - '0');index++;}return num;}// 递归解析表达式(简单处理:先加后减,无优先级)AbstractExpression* parse(const std::string& expr, int& index) {// 解析第一个数字作为左操作数AbstractExpression* left = new NumberExpression(parseNumber(expr, index));// 循环解析后续的运算符和右操作数while (index < expr.size()) {// 跳过空格while (index < expr.size() && isspace(expr[index])) {index++;}if (index >= expr.size()) break;// 解析运算符char op = expr[index];index++;// 跳过空格while (index < expr.size() && isspace(expr[index])) {index++;}// 解析右操作数AbstractExpression* right = new NumberExpression(parseNumber(expr, index));// 根据运算符创建非终结符表达式if (op == '+') {left = new AddExpression(left, right);} else if (op == '-') {left = new SubtractExpression(left, right);} else {// 未知运算符,释放内存并退出delete left;delete right;return nullptr;}}return left;}public:// 解析入口AbstractExpression* parse(const std::string& expr) {int index = 0;// 跳过开头空格while (index < expr.size() && isspace(expr[index])) {index++;}return parse(expr, index);}};// 客户端代码:解析并计算表达式int main() {// 待解析的表达式std::string expr1 = "1 + 2 - 3";std::string expr2 = "10 + 20 - 5 + 8";// 创建解析器和上下文ExpressionParser parser;Context context;// 解析并计算第一个表达式AbstractExpression* tree1 = parser.parse(expr1);if (tree1) {std::cout << "表达式: " << expr1 << " = " << tree1->interpret(context) << std::endl;delete tree1;}// 解析并计算第二个表达式AbstractExpression* tree2 = parser.parse(expr2);if (tree2) {std::cout << "表达式: " << expr2 << " = " << tree2->interpret(context) << std::endl;delete tree2;}return 0;}

三、代码解析

  1. 上下文(Context)
    存储表达式中的变量信息(如x=5),提供setVariable()getVariable()方法管理变量,供表达式解释时使用(示例中未使用变量,仅展示结构)。

  2. 抽象表达式(AbstractExpression)
    定义了interpret()纯虚方法,所有具体表达式都需实现该方法,完成自身的解释逻辑。

  3. 终结符表达式(NumberExpression)
    对应语法中的“数字”,存储解析后的整数,interpret()直接返回该数字(无需依赖其他表达式)。

  4. 非终结符表达式

    • AddExpressionSubtractExpression分别对应“加法”和“减法”,包含leftright两个子表达式(左、右操作数)。
    • interpret()方法通过递归调用子表达式的interpret(),再将结果相加或相减,完成非终结符的解释。
  5. 解析器(ExpressionParser)
    负责将输入的字符串表达式(如"1 + 2 - 3")转换为抽象语法树(AST):

    • parseNumber()从字符串中提取数字。
    • 递归parse()方法构建语法树,先解析左操作数,再循环解析运算符和右操作数,通过组合AddExpressionSubtractExpression形成完整的树结构。
  6. 客户端使用
    客户端创建解析器,调用parse()生成语法树,再通过interpret()计算表达式结果,无需关心解析和计算的细节。

四、核心优势与适用场景

优势
  1. 易于扩展语法:新增语法规则只需添加对应的表达式类(如新增*运算符只需添加MultiplyExpression),符合开闭原则。
  2. 语法清晰:将语法规则分散到多个表达式类中,每个类对应一条规则,代码结构清晰,便于维护。
  3. 可定制性:可通过修改或组合表达式类,灵活定制语法解析逻辑。
适用场景
  1. 简单语法解析:如部署文件格式解析、简单的数学表达式计算、小型脚本语言解释器。
  2. 重复出现的语法:当某类问题频繁出现且可被抽象为容易语法规则时(如正则表达式引擎的部分实现)。
  3. 教学或演示:用于展示语法解析的基本原理(如编译原理中的语法分析阶段)。

五、局限性与与其他模式的区别

局限性
  1. 复杂语法效率低:对于复杂语法(如C++语言),解释器模式会导致表达式类数量爆炸,且递归解释效率低,此时应使用专业的解析工具(如ANTLR)。
  2. 维护成本高:语法规则过多时,表达式类的数量会急剧增加,增加维护难度。
与其他模式的区别
模式核心差异点
解释器模式专注于语法解析,将语法规则抽象为类层次结构,通过递归解释句子。
组合模式构建树形结构表示“部分-整体”关系,统一单个对象和组合对象的使用,不涉及语法解析。
策略模式封装算法家族,使算法可动态替换,不涉及语法规则和递归解释。

六、实践建议

  1. 限制运用场景:仅用于方便语法解析,复杂语法优先利用成熟的解析工具(如YACC、ANTLR)。
  2. 结合备忘录模式:对于需要回溯的解析过程(如语法错误恢复),可使用备忘录模式保存解析状态。
  3. 缓存解释结果:对于重复出现的表达式,可缓存解释结果,避免重复计算(如频繁使用的公式)。
  4. 简化语法设计:设计语法时尽量简洁,避免过多的规则和优先级,降低表达式类的复杂度。

解释器模式的核心价值在于“将语法规则具象化为可扩展的类结构”,它经过递归组合表达式类,实现了对自定义语法的解析。纵然在复杂场景中实用性有限,但在简单语法解析和教学演示中,是一种直观且灵活的设计方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/927496.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

免费的小程序模板网站做网站服务公司

一、 搜索算法 深度优先搜索和广度优先搜索是最暴力的图的搜索算法。算法的目标是&#xff0c;给定一张图&#xff0c;一对初始和终止节点&#xff0c;找到两节点之间的节点路径。&#xff08;代码均是找到两个节点之间的路径&#xff09; 广度优先搜索是一层一层搜索&#xf…

中国最大网站建设商网站生成器怎么做

python绑定项目 官方未提供python的封装绑定&#xff0c;直接调用执行文件 https://github.com/stlukey/whispercpp.py提供了源码和Cpython结合的绑定 https://github.com/zhujun1980/whispercpp_py提供了ctype方式的绑定&#xff0c;需要先make libwhisper.so Pybind11 bi…

dede视频网站宣城市住房和城乡建设局网站首页

关注公号【逆向通信猿】更精彩!!! 1. 构造编码器对象 采用MATLAB内置的comm.LDPCEncoder构造编码器对象,其中使用默认的校验矩阵,信息位长32400比特,码长64800比特,该校验矩阵中除第一行中1的个数为6个外,其余行中1的个数均为7;前12960列中1的个数为8,后32400列构成…

如何备份一个网站广西住建局

1. C/C++ IDE 安装Clion Nova和VsCode 2. 编译器 2.1 g++ 当程序比较小时,安装g++编译器进行编译,常用命令如下: // 安装g++ sudo apt-get install g++ // 默认生成a.out的可执行文件 g++ main.cpp // 生成其他名字的可执行文件 g++ main.cpp -o exe_name2.2 cmake 当…

网站开发一次性费用网站建设公司发展方向及趋势

2020年9月10日&#xff0c;亚马逊通技术服务&#xff08;北京&#xff09;有限公司&#xff08;AWS中国&#xff09;与毕马威企业咨询&#xff08;中国&#xff09;有限公司&#xff08;毕马威中国&#xff09;宣布已签署战略合作协议。 双方将充分利用各自的全球品牌影响力和资…

深入解析:PostgreSQL向量检索:pgvector入门指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

网贷之家网站建设你访问的网站正在建设

以上问题在编译器中出现可以在编译器中最上面加入&#xff1a; #define_CRT_SECURE_NO_WARNINGS 或者将scanf修改为scanf_s 一定要在最上端&#xff01;&#xff01;&#xff01;最上端&#xff01;&#xff01;&#xff01;最上端加入&#xff01;&#xff01;&#xff01; 虽…

Allow or block media autoplay in Firefox

https://support.mozilla.org/en-US/kb/block-autoplay

寺庙网站开发文案wordpress免费主题插件下载

前文&#xff1a;最近跟着DataWhale组队学习这一期“Sora原理与技术实战”&#xff0c;本篇博客主要是基于DataWhale成员、厦门大学平潭研究院杨知铮研究员分享的Sora技术原理详解课件内容以及参考网上一些博客资料整理而来&#xff08;详见文末参考文献&#xff09;&#xff0…

[WC2018] 即时战略

[WC2018] 即时战略分享一下全局平衡二叉树的做法。 先讲下部分分。 \(n\le 100,T\le 10000\) 从 \(1\) 开始 DFS,对于当前 \(u\),枚举点 \(v\),如果 \(\text{explore}(u,v)\) 不为 \(fa_u\),则 \(v\) 为 \(u\) 子结…

商城网站源码大全微网站建站

怎么解决java导出excel时文件名乱码发布时间&#xff1a;2020-06-19 16:59:00来源&#xff1a;亿速云阅读&#xff1a;137作者&#xff1a;元一java解决导出Excel时文件名乱码的方法示例&#xff1a;String agent request.getHeader("USER-AGENT").toLowerCase();re…

实用指南:Unity学习之C#的反射机制

实用指南:Unity学习之C#的反射机制pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco…

电子政务网站建设参考文献wdcp 网站打不开

一金融机构在近期发生了一起数据泄露事件。 经过调查&#xff0c;发现是由于一名员工将包含客户敏感信息的文件通过电子邮件发送给了未经授权的第三方。 这一事件导致客户数据泄露&#xff0c;给该机构带来了严重的声誉损失和信任危机。 这一案例凸显了数据防泄漏系统的重要性…

网站开发 数据库深圳制作网站

1、背景介绍 重装win10系统&#xff0c;重装Python。在坑出现之前&#xff0c;已经完成了Python的安装&#xff08;D盘&#xff09;&#xff0c;并且在系统中添加了环境变量。由于平时需要用到Python2.7和Python3.6&#xff0c;这里将对应的解释器分别改名为Python2和Python3。…

HDF5文件 ——之三

H5G、H5L、H5O 是 HDF5 C API 的分组接口,在 HDF.PInvoke.NETStandard(C# 的 HDF5 封装包)里,它们分别代表 HDF5 底层 对象层(Object layer)、组层(Group layer) 和 链接层(Link layer) 的操作模块。🧱 一…

MySQL库的操作(ubuntu) - 教程

MySQL库的操作(ubuntu) - 教程2025-10-04 19:34 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importan…

创业做社交网站有哪些做一个网站 多少钱

1&#xff09;熟悉之前的SysUser登录流程 过滤器链验证配置 这里security过滤器链增加了前置过滤器链jwtFilter 该过滤器为我们自定义的&#xff0c;每次请求都会经过jwt验证 ok我们按ctrl alt B跳转过去来看下 首先会获取登录用户LoginUser 内部通过header键&#xff0c;获…

代码随想录算法训练营|Day 25

Day 25 第七章 回溯算法 part04 491.递增子序列 本题和大家刚做过的 90.子集II 非常像,但又很不一样,很容易掉坑里。 https://programmercarl.com/0491.递增子序列.html 视频讲解:https://www.bilibili.com/video/B…

深入解析:SAE J3072-2024插电式电动汽车(PEV)中的车载逆变器系统安全标准介绍

深入解析:SAE J3072-2024插电式电动汽车(PEV)中的车载逆变器系统安全标准介绍pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

冷僻模板整理

min25筛 可以低于线性的解决1到N中的质数的k次幂的求和的问题,并且在处理了N之后对于1到N中数论分块所需的点x(l,r)都可以通过val=g[ID(x)]以O(1)的代价获取到 如果不需要多次查询,建议把命名空间外的定义放到m…