C++学习之路(六)C++ 实现简单的工具箱系统命令行应用 - 示例代码拆分讲解
简单的工具箱系统示例介绍:
这个示例展示了一个简单的工具箱框架,它涉及了几个关键概念和知识点:
-
面向对象编程 (OOP):使用了类和继承的概念。
Tool是一个纯虚类,CalculatorTool和FileReaderTool是其派生类。 -
多态:通过将不同类型的工具对象放入同一个容器中,实现了多态,能够根据用户输入执行不同的工具。
-
STL 容器 (std::map):使用了
std::map作为工具箱的存储容器,将工具名称映射到相应的工具对象。 -
文件输入输出:在
FileReaderTool中展示了如何使用文件输入输出流来读取文件内容。 -
异常处理:在
CalculatorTool中检查除数是否为零,避免出现除以零的情况。
这个示例提供了一个基础框架,能够让用户扩展和添加新的工具到工具箱中,并在运行时选择和执行这些工具。它展示了如何使用面向对象的思想创建可扩展的程序结构,并利用 C++ 的特性和标准库来实现这一点。

功能和概述:
-
工具箱功能:用户可以通过添加不同的工具(例如计算器、文件读取器)扩展工具箱。每个工具都执行特定的任务。
-
用户交互:程序在运行时会列出所有可用的工具,并要求用户输入要执行的工具名称。用户可以通过输入工具名称来选择要执行的功能。
-
多态执行:使用多态性,不同类型的工具对象存储在同一个容器中,通过执行基类
Tool的虚函数execute()来实现不同工具的执行。 -
异常处理:在计算器工具中,程序会检查用户输入的除数是否为零,并进行相应的异常处理,避免出现除以零的情况。
-
C++ 特性应用:使用了面向对象编程的概念,包括类、继承和虚函数,以及标准库中的
std::map和输入输出流。
这个框架提供了一个基础结构,可用作添加更多功能的起点,用户可以根据需求扩展和修改工具箱,并通过添加新的工具类来实现更多的功能。
示例在Clion中运行步骤:
1. 新建项目

2. 粘贴代码
#include <iostream>
#include <map>
#include <functional>
#include <fstream>// 工具基类
class Tool {
public:virtual void execute() = 0;virtual ~Tool() = default;
};// 示例工具类:计算器
class CalculatorTool : public Tool {
public:void execute() override {std::cout << "Executing Calculator Tool..." << std::endl;double num1, num2;char op;std::cout << "Enter first number: ";std::cin >> num1;std::cout << "Enter operator (+, -, *, /): ";std::cin >> op;std::cout << "Enter second number: ";std::cin >> num2;double result;switch (op) {case '+':result = num1 + num2;break;case '-':result = num1 - num2;break;case '*':result = num1 * num2;break;case '/':if (num2 != 0) {result = num1 / num2;} else {std::cout << "Error! Division by zero is not allowed." << std::endl;return;}break;default:std::cout << "Invalid operator." << std::endl;return;}std::cout << "Result: " << result << std::endl;}
};// 示例工具类:文件读取器
class FileReaderTool : public Tool {
public:void execute() override {std::cout << "Executing File Reader Tool..." << std::endl;std::string filename;std::cout << "Enter file name to read: ";std::cin >> filename;std::ifstream file(filename);if (file.is_open()) {std::string line;while (std::getline(file, line)) {std::cout << line << std::endl;}file.close();} else {std::cout << "Failed to open file." << std::endl;}}
};// 工具箱
class Toolbox {
private:std::map<std::string, std::unique_ptr<Tool>> tools;public:// 添加工具到工具箱void addTool(const std::string &name, std::unique_ptr<Tool> tool) {tools[name] = std::move(tool);}// 列出所有可用工具void listTools() {std::cout << "Available tools: ";for (const auto &tool : tools) {std::cout << tool.first << ", ";}std::cout << std::endl;}// 执行特定工具void executeTool(const std::string &name) {auto it = tools.find(name);if (it != tools.end()) {it->second->execute();} else {std::cout << "Tool not found." << std::endl;}}
};int main() {Toolbox toolbox;// 创建工具对象并添加到工具箱中toolbox.addTool("Calculator", std::make_unique<CalculatorTool>());toolbox.addTool("FileReader", std::make_unique<FileReaderTool>());// 列出所有可用工具toolbox.listTools();// 执行特定工具std::string selectedTool;std::cout << "Enter tool name to execute: ";std::cin >> selectedTool;toolbox.executeTool(selectedTool);return 0;
}
3. 编译运行
代码拆解,知识点总结
让我们逐步分解和解释这个示例:
🟥 1. 引入头文件和命名空间
#include <iostream>
#include <map>
#include <functional>
#include <fstream>
这部分代码引入了所需的标准库头文件,包括用于输入输出的 iostream,用于映射的 map,用于函数对象的 functional 和文件流的 fstream。同时,使用了 std 命名空间。
Tips: 📢 什么是 functional ?
<functional> 是 C++ 标准库中的头文件,它提供了一组函数对象,以及用于操作函数的工具。函数对象是可调用对象(callable object),可以像函数一样使用。这个头文件提供了很多功能,主要包括以下几个方面:
-
函数对象 (Function Objects):包括各种函数对象,如
std::function、std::bind、std::placeholders等。这些函数对象允许您以更灵活的方式处理函数,例如将函数作为参数传递给其他函数,或者将函数与特定参数绑定在一起。 -
函数适配器 (Function Adapters):提供了一些函数适配器,例如
std::bind和std::mem_fn,它们允许您更改函数的行为或结构。 -
一些工具性质的功能:比如
std::reference_wrapper,它允许将引用封装为可复制的对象,方便函数接受引用参数。
使用 <functional> 头文件,您可以更方便地处理函数式编程、函数组合和函数对象。例如,std::function 允许您将函数作为参数传递给其他函数,std::bind 允许您绑定参数到函数,创建新的函数对象等。
这个头文件提供了许多功能,使得 C++ 中的函数操作更加灵活和便捷。
🟥 2. Tool 类
class Tool {
public:virtual void execute() = 0;virtual ~Tool() = default;
};
这里定义了一个抽象基类 Tool,它包含一个纯虚函数 execute(),这个函数没有实现,需要在派生类中具体实现。另外,声明了虚析构函数,使得这个类可以作为多态基类。
Tips: 📢 virtual void 和 virtual ~Tool() = default 是什么意思?
这两个内容涉及到 C++ 中的虚函数和虚析构函数的概念。
virtual void execute() = 0;
这行代码定义了一个纯虚函数 execute()。虚函数是在基类中声明为虚函数的函数,在派生类中可以进行重写。纯虚函数没有具体的实现,它的目的是让派生类强制实现该函数。类中包含纯虚函数的类称为抽象类,抽象类不能被实例化,只能被用作其他类的基类。在这个例子中,Tool 类定义了一个纯虚函数 execute(),所有继承自 Tool 的类都必须提供自己的 execute() 函数的实现。
virtual ~Tool() = default;
这行代码定义了一个虚析构函数。虚析构函数是用来释放内存的,通常当一个基类指针指向一个派生类对象时,如果基类的析构函数不是虚函数,那么当通过基类指针删除对象时,只会调用基类的析构函数而不会调用派生类的析构函数,可能会导致内存泄漏。所以将基类的析构函数声明为虚函数能够确保通过基类指针删除派生类对象时正确地调用派生类的析构函数。virtual ~Tool() = default; 表示使用默认实现的虚析构函数,即使用编译器生成的默认析构函数。
为什么要写成 virtual void execute() = 0 ?
在 C++ 中,将一个函数声明为纯虚函数(Pure Virtual Function)可以通过在函数声明末尾加上 = 0 来实现。这种写法告诉编译器,这个函数在基类中没有实际的实现,派生类必须提供自己的实现才能创建对象。
纯虚函数实际上是一个占位符,它为派生类提供了一个契约:如果你要成为这个基类的子类,你必须提供这个函数的实际实现。这种机制使得基类能够定义一组接口,但不提供具体的实现,而是将实现的责任交给派生类。
写成 = 0 的语法是 C++ 中定义纯虚函数的标准方式。这样做有两个主要原因:
- 为了让编译器理解这是一个纯虚函数,因此任何派生类都需要提供它的实现。
- 如果一个类包含了纯虚函数,它就不能被直接实例化,只能被用作其他类的基类,这种情况下它就成为了抽象类。
因此,使用 = 0 是 C++ 中约定的方法,告诉编译器这是一个纯虚函数。
🟥 3. 示例工具类:CalculatorTool 和 FileReaderTool
class CalculatorTool : public Tool {
public:void execute() override {// ... 实现了简单的计算器功能 ...}
};class FileReaderTool : public Tool {
public:void execute() override {// ... 实现了文件读取器功能 ...}
};
这部分代码展示了两个派生类,分别是 CalculatorTool 和 FileReaderTool。它们继承自 Tool 类,并重写了 execute() 方法,实现了各自的功能。
Tips: 📢 加 public 是什么意思?还有什么其他方式吗?
这段代码定义了一个名为 CalculatorTool 的类,它是 Tool 类的公有派生类。这意味着 CalculatorTool 类继承了 Tool 类的成员和方法,并且可以在其基础上添加自己的成员和方法。
这种继承关系使得 CalculatorTool 类拥有 Tool 类的特性,并且可以通过重写(覆盖)Tool 类中的虚函数来实现自己特定的行为。继承允许在不重复编写相同代码的情况下,扩展已有类的功能,这有助于代码的复用和扩展。
在这里,CalculatorTool 类继承了 Tool 类的一些特性,可能包括一些成员函数、虚函数或其他成员。同时,CalculatorTool 类可以添加自己特有的功能,例如实现 execute() 函数以执行特定的计算器功能。
在 C++ 中,public 是一种访问控制修饰符,它用于指定类成员的访问级别。在这里,class CalculatorTool : public Tool 中的 public 关键字表示派生类 CalculatorTool 继承自基类 Tool 并且继承的是基类中的公有成员。
访问控制修饰符包括三种:public、protected 和 private。它们决定了派生类对基类的继承成员的访问权限:
public继承意味着基类中的公有成员在派生类中仍然是公有的,基类的保护和私有成员在派生类中不可直接访问。protected继承意味着基类中的公有和保护成员在派生类中都变为保护成员,基类的私有成员在派生类中不可直接访问。private继承意味着基类中的所有成员都变为派生类的私有成员,不可在派生类之外访问。
除了 public 继承之外,还有 protected 和 private 继承。这些继承方式决定了派生类对基类成员的访问权限。
class Derived : protected Base {// 在这里,Derived 是 protected 继承自 Base// Base 中的公有成员在 Derived 中变成了 protected
};class Derived : private Base {// 在这里,Derived 是 private 继承自 Base// Base 中的所有成员在 Derived 中都变成了 private
};
但一般情况下,使用 public 继承是最常见和推荐的方式,因为它保持了基类的接口,并且能够更加清晰地表达派生类和基类之间的关系。
🟥 4. Toolbox 类
class Toolbox {
private:std::map<std::string, std::unique_ptr<Tool>> tools;public:void addTool(const std::string &name, std::unique_ptr<Tool> tool) {// ... 将工具名称和对象关联存储到map中 ...}void listTools() {// ... 列出所有可用工具 ...}void executeTool(const std::string &name) {// ... 执行特定工具 ...}
};
这个类用来管理工具。它包含一个私有成员 tools,这是一个 map,将工具名称与对应的工具对象关联起来。addTool() 方法用于向工具箱中添加工具,listTools() 方法用于列出所有可用的工具,executeTool() 方法根据用户输入的名称执行相应的工具。
Tips: 📢 有关 std::map 知识点讲解
std::map<std::string, std::unique_ptr<Tool>> tools; 这段代码定义了一个名为 tools 的成员变量,它是一个 std::map 对象。让我来解释一下:
std::map是 C++ 标准库中的关联容器,它提供了键值对(key-value)的存储机制,允许使用一个键来快速检索相关联的值。- 在这个例子中,
std::map使用了两个模板参数:std::string和std::unique_ptr<Tool>。它将字符串(std::string)作为键,将指向Tool类对象的独占指针(std::unique_ptr<Tool>)作为值。 - 意思是
tools这个 map 变量能够通过字符串键来快速存储和检索不同的工具对象。
例如,可以用这个 tools map 来存储不同的工具,每个工具都有一个与之对应的字符串名称。这样,通过工具的名字就能够方便地获取对应的工具对象。
例如:
std::map<std::string, std::unique_ptr<Tool>> tools;// 添加一个名为 "calculator" 的计算器工具
tools["calculator"] = std::make_unique<CalculatorTool>();// 添加一个名为 "fileReader" 的文件读取器工具
tools["fileReader"] = std::make_unique<FileReaderTool>();// 通过名称获取特定工具
std::string selectedToolName = "calculator";
auto selectedTool = tools[selectedToolName]; // 获取名为 "calculator" 的工具
selectedTool->execute(); // 执行对应的工具操作
这段代码展示了如何向 tools map 中添加不同的工具,并通过名称检索和执行特定的工具操作。
🟥 5. main() 函数
int main() {Toolbox toolbox;// ... 添加工具到工具箱中 ...toolbox.listTools();std::string selectedTool;std::cout << "Enter tool name to execute: ";std::cin >> selectedTool;toolbox.executeTool(selectedTool);return 0;
}
在 main() 函数中,创建了一个 Toolbox 对象,并添加了具体的工具到工具箱中。然后列出了所有可用的工具,等待用户输入工具名称,最后执行用户选择的工具。
这个拆解旨在展示这个程序的基本结构和主要组成部分。每个部分都有其特定的功能,通过彼此协作来实现整体的功能。
Tips: 📢 别忘了跑起来,检查检查有没有BUG ~ 😁
本文就到这里了,感谢您的阅读,明天还有更多的实例学习文章等着你 🎆。别忘了点赞、收藏~ Thanks♪(・ω・)ノ 🍇。
相关文章:
C++学习之路(六)C++ 实现简单的工具箱系统命令行应用 - 示例代码拆分讲解
简单的工具箱系统示例介绍: 这个示例展示了一个简单的工具箱框架,它涉及了几个关键概念和知识点: 面向对象编程 (OOP):使用了类和继承的概念。Tool 是一个纯虚类,CalculatorTool 和 FileReaderTool 是其派生类。 多态࿱…...
redis运维(十四) hash缓存案例
一 缓存案例 ① 需求 ② 个人理解 策略:不更新缓存,而是删除缓存大部分观点认为:1、做缓存不应该是去更新缓存,而是应该删除缓存2、然后由下个请求去缓存,发现不存在后再读取数据库,写入redis缓存 高并发场景下,到底先更新缓存还是先更…...
Rust UI开发(三):iced如何打开图片(对话框)并在窗口显示图片?
注:此文适合于对rust有一些了解的朋友 iced是一个跨平台的GUI库,用于为rust语言程序构建UI界面。 这是一个系列博文,本文是第三篇,前两篇的链接: 1、Rust UI开发(一):使用iced构建…...
网络爬虫(Python:Requests、Beautiful Soup笔记)
网络爬虫(Python:Requests、Beautiful Soup笔记) 网络协议简要介绍一。OSI参考模型二、TCP/IP参考模型对应关系TCP/IP各层实现的协议应用层传输层网络层 HTTP协议HTTP请求HTTP响应HTTP状态码 Requests(Python)Requests…...
【Kotlin】内联函数
文章目录 内联函数noinline: 避免参数被内联非局部返回使用标签实现Lambda非局部返回为什么要设计noinline crossinline具体化参数类型 Kotlin中的内联函数之所以被设计出来,主要是为了优化Kotlin支持Lambda表达式之后所带来的开销。然而,在Java中我们似…...
Unity技美35——再URP管线环境下,配置post后期效果插件(post processing)
前两年在我的unity文章第10篇写过,后效滤镜的使用,那时候大部分项目用的还是unity的基础管线,stander管线。 但是现在随着unity的发展,大部分项目都用了URO管线,甚至很多PC端用的都是高效果的HDRP管线,这就…...
Redis:持久化RDB和AOF
目录 概述RDB持久化流程指定备份文件的名称指定备份文件存放的目录触发RDB备份redis.conf 其他一些配置rdb的备份和恢复优缺点停止RDB AOF持久化流程AOF启动/修复/恢复AOF同步频率设置rewrite压缩原理触发机制重写流程no-appendfsync-on-rewrite 优缺点 如何选择 概述 Redis是…...
基于python协同过滤推荐算法的音乐推荐与管理系统
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 基于Python的协同过滤推荐算法的音乐推荐与管理系统是一个集成了音乐推荐和管理的系统,它使用协同过滤算…...
【极客技术】真假GPT-4?微调 Llama 2 以替代 GPT-3.5/4 已然可行!
近日小编在使用最新版GPT-4-Turbo模型(主要特点是支持128k输入和知识库截止日期是2023年4月)时,发现不同商家提供的模型回复出现不一致的情况,尤其是模型均承认自己知识库达到2023年4月,但当我们细问时,Fak…...
STK Components 二次开发-创建地面站
1.地面站只需要知道地面站的经纬高。 // Define the location of the facility using cartographic coordinates.var location new Cartographic(Trig.DegreesToRadians(-75.596766667), Trig.DegreesToRadians(40.0388333333), 0.0); 2.创建地面站 创建方式和卫星一样生成对…...
数据结构与算法(三)贪心算法(Java)
目录 一、简介1.1 定义1.2 基本步骤1.3 优缺点 二、经典示例2.1 选择排序2.2 背包问题 三、经典反例:找零钱3.1 题目3.2 解答3.3 记忆化搜索实现3.4 动态规划实现 一、简介 1.1 定义 贪心算法(Greedy Algorithm),又名贪婪法&…...
057-第三代软件开发-文件监视器
第三代软件开发-文件监视器 文章目录 第三代软件开发-文件监视器项目介绍文件监视器实现原理关于 QFileSystemWatcher实现代码 关键字: Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML&…...
二十七、微服务案例
目录 一、实现输入搜索功能 1、下载代码,在idea上打开 2、新建RequestParams类,用于接收解析请求 3、在启动类中加入客户端地址Bean,以便实现服务 4、编写搜索方法 5、新建返回分页结果类 6、实现搜索方法 7、编写控制类,…...
(C++)string类的模拟实现
愿所有美好如期而遇 前言 我们模拟实现string类不是为了去实现他,而是为了了解他内部成员函数的一些运行原理和时间复杂度,在将来我们使用时能够合理地去使用他们。 为了避免我们模拟实现的string类与全局上的string类冲突(string类也在std命名空间中)&…...
处理数据中的缺失值--删除缺少值的行
两个最主要的处理缺失值的方法是: ❏ 删除缺少值的行; ❏ 填充缺失值; 我们首先将serum_insulin的中的字段值0替换为None,可以看到缺失值的数量为374个; print(pima[serum_insulin].isnull().sum()) pima[serum_insu…...
Kotlin学习——kt里的集合,Map的各种方法之String篇
Kotlin 是一门现代但已成熟的编程语言,旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作,并提供了多种方式在多个平台间复用代码,以实现高效编程。 https://play.kotlinlang.org/byExample/01_introduction/02_Functio…...
MIT 6.824 -- MapReduce Lab
MIT 6.824 -- MapReduce Lab 环境准备实验背景实验要求测试说明流程说明 实验实现GoLand 配置代码实现对象介绍协调器启动工作线程启动Map阶段分配任务执行任务 Reduce 阶段分配任务执行任务 终止阶段 崩溃恢复 注意事项并发安全文件转换golang 知识点 测试 环境准备 从官方gi…...
创新研报|顺应全球数字化,能源企业以“双碳”为目标的转型迫在眉睫
能源行业现状及痛点分析 挑战一:数字感知能力较弱 挑战二:与业务的融合度低 挑战三:决策响应速度滞后 挑战四:价值创造有待提升 挑战五:安全风险如影随形 能源数字化转型定义及架构 能源行业数字化转型体系大体…...
Blender 连续 5 天遭受大规模 DDoS 攻击
Blender 发布公告指出,在2023年11月18日至23日期间,blender.org 网站遭受了持续的分布式拒绝服务(DDoS)攻击,攻击者通过不断发送请求导致服务器超载,使网站运营严重中断。此次攻击涉及数百个 IP 地址的僵尸…...
Python 获取本地和广域网 IP
Python 获取本地IP ,使用第三方库,比如 netifaces import netifaces as nidef get_ip_address():try:# 获取默认网络接口(通常是 eth0 或 en0)default_interface ni.gateways()[default][ni.AF_INET][1]# 获取指定网络接口的IP地…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 -----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

