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地…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在 GPU 上对图像执行 均值漂移滤波(Mean Shift Filtering),用于图像分割或平滑处理。 该函数将输入图像中的…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...