C++学习之路(六)C++ 实现简单的工具箱系统命令行应用 - 示例代码拆分讲解

news2024/11/27 10:36:12

简单的工具箱系统示例介绍:

这个示例展示了一个简单的工具箱框架,它涉及了几个关键概念和知识点:

  1. 面向对象编程 (OOP):使用了类和继承的概念。Tool 是一个纯虚类,CalculatorToolFileReaderTool 是其派生类。

  2. 多态:通过将不同类型的工具对象放入同一个容器中,实现了多态,能够根据用户输入执行不同的工具。

  3. STL 容器 (std::map):使用了 std::map 作为工具箱的存储容器,将工具名称映射到相应的工具对象。

  4. 文件输入输出:在 FileReaderTool 中展示了如何使用文件输入输出流来读取文件内容。

  5. 异常处理:在 CalculatorTool 中检查除数是否为零,避免出现除以零的情况。

这个示例提供了一个基础框架,能够让用户扩展和添加新的工具到工具箱中,并在运行时选择和执行这些工具。它展示了如何使用面向对象的思想创建可扩展的程序结构,并利用 C++ 的特性和标准库来实现这一点。
在这里插入图片描述

功能和概述:

  1. 工具箱功能:用户可以通过添加不同的工具(例如计算器、文件读取器)扩展工具箱。每个工具都执行特定的任务。

  2. 用户交互:程序在运行时会列出所有可用的工具,并要求用户输入要执行的工具名称。用户可以通过输入工具名称来选择要执行的功能。

  3. 多态执行:使用多态性,不同类型的工具对象存储在同一个容器中,通过执行基类 Tool 的虚函数 execute() 来实现不同工具的执行。

  4. 异常处理:在计算器工具中,程序会检查用户输入的除数是否为零,并进行相应的异常处理,避免出现除以零的情况。

  5. 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),可以像函数一样使用。这个头文件提供了很多功能,主要包括以下几个方面:

  1. 函数对象 (Function Objects):包括各种函数对象,如 std::functionstd::bindstd::placeholders 等。这些函数对象允许您以更灵活的方式处理函数,例如将函数作为参数传递给其他函数,或者将函数与特定参数绑定在一起。

  2. 函数适配器 (Function Adapters):提供了一些函数适配器,例如 std::bindstd::mem_fn,它们允许您更改函数的行为或结构。

  3. 一些工具性质的功能:比如 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++ 中定义纯虚函数的标准方式。这样做有两个主要原因:

  1. 为了让编译器理解这是一个纯虚函数,因此任何派生类都需要提供它的实现。
  2. 如果一个类包含了纯虚函数,它就不能被直接实例化,只能被用作其他类的基类,这种情况下它就成为了抽象类。

因此,使用 = 0 是 C++ 中约定的方法,告诉编译器这是一个纯虚函数。


🟥 3. 示例工具类:CalculatorToolFileReaderTool

class CalculatorTool : public Tool {
public:
    void execute() override {
        // ... 实现了简单的计算器功能 ...
    }
};

class FileReaderTool : public Tool {
public:
    void execute() override {
        // ... 实现了文件读取器功能 ...
    }
};

这部分代码展示了两个派生类,分别是 CalculatorToolFileReaderTool。它们继承自 Tool 类,并重写了 execute() 方法,实现了各自的功能。

Tips: 📢 加 public 是什么意思?还有什么其他方式吗?

这段代码定义了一个名为 CalculatorTool 的类,它是 Tool 类的公有派生类。这意味着 CalculatorTool 类继承了 Tool 类的成员和方法,并且可以在其基础上添加自己的成员和方法。

这种继承关系使得 CalculatorTool 类拥有 Tool 类的特性,并且可以通过重写(覆盖)Tool 类中的虚函数来实现自己特定的行为。继承允许在不重复编写相同代码的情况下,扩展已有类的功能,这有助于代码的复用和扩展。

在这里,CalculatorTool 类继承了 Tool 类的一些特性,可能包括一些成员函数、虚函数或其他成员。同时,CalculatorTool 类可以添加自己特有的功能,例如实现 execute() 函数以执行特定的计算器功能。


在 C++ 中,public 是一种访问控制修饰符,它用于指定类成员的访问级别。在这里,class CalculatorTool : public Tool 中的 public 关键字表示派生类 CalculatorTool 继承自基类 Tool 并且继承的是基类中的公有成员。

访问控制修饰符包括三种:publicprotectedprivate。它们决定了派生类对基类的继承成员的访问权限:

  • public 继承意味着基类中的公有成员在派生类中仍然是公有的,基类的保护和私有成员在派生类中不可直接访问。
  • protected 继承意味着基类中的公有和保护成员在派生类中都变为保护成员,基类的私有成员在派生类中不可直接访问。
  • private 继承意味着基类中的所有成员都变为派生类的私有成员,不可在派生类之外访问。

除了 public 继承之外,还有 protectedprivate 继承。这些继承方式决定了派生类对基类成员的访问权限。

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::stringstd::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♪(・ω・)ノ 🍇。

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

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

相关文章

JavaScript WebApi(二) 详解

监听事件 介绍 事件监听是一种用于在特定条件下执行代码的编程技术。在Web开发中&#xff0c;事件监听器可以用于捕获和响应用户与页面交互的各种操作&#xff0c;如点击、滚动、输入等。 事件监听的基本原理是&#xff0c;通过在特定元素上注册事件监听器&#xff0c;当事件…

Elastic Search的RestFul API入门:初识mapping

本节课旨在探讨Elasticsearch中Mapping的使用。在Elasticsearch中&#xff0c;Mapping是定义索引中字段&#xff08;Field&#xff09;的数据类型和属性的过程。它为Elasticsearch提供了一种途径&#xff0c;以解析和处理文档中的各个字段&#xff0c;以便在搜索、排序和聚合等…

【开源】基于Vue和SpringBoot的个人健康管理系统

项目编号&#xff1a; S 040 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S040&#xff0c;文末获取源码。} 项目编号&#xff1a;S040&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 健康档案模块2.2 体检档案模块2.3 健…

前端入门(三)Vue生命周期、组件原理、脚手架、插槽插件、存储、组件事件、动画、跨域与代理

文章目录 Vue生命周期Vue 组件化编程 - .vue文件非单文件组件组件的注意点组件嵌套Vue实例对象和VueComponent实例对象Js对象原型与原型链Vue与VueComponent的重要内置关系 应用单文件组件构建 Vue脚手架 - vue.cli项目文件结构组件相关高级属性引用名 - ref数据接入 - props混…

Linux常用命令——bc命令

在线Linux命令查询工具 bc 算术操作精密运算工具 补充说明 bc命令是一种支持任意精度的交互执行的计算器语言。bash内置了对整数四则运算的支持&#xff0c;但是并不支持浮点运算&#xff0c;而bc命令可以很方便的进行浮点运算&#xff0c;当然整数运算也不再话下。 语法 …

【Kotlin】内联函数

文章目录 内联函数noinline: 避免参数被内联非局部返回使用标签实现Lambda非局部返回为什么要设计noinline crossinline具体化参数类型 Kotlin中的内联函数之所以被设计出来&#xff0c;主要是为了优化Kotlin支持Lambda表达式之后所带来的开销。然而&#xff0c;在Java中我们似…

NBIot 平台篇③OneNET物联网平台

NBIot 平台篇③OneNET物联网平台 1、平台介绍:2、平台注册与登录:3、创建产品:4、添加设备:5、设备调试:1、平台介绍: (1)、OneNET物联网开放平台是中国移动打造的面向产业互联和智慧生活应用的物联网PaaS平台,OneNET支持适配各种网络环境和协议类型,可实现各种传感…

【腾讯云云上实验室】用向量数据库在金融信用数据库分析中的实战运用

一、前言 这篇文章将带领读者探索数据库的多样化解决方案及其演进历程&#xff0c;特别关注向量数据库的重要性和在实际项目中的应用。 通过深入剖析腾讯云向量数据库及其在金融信用数据库分析中的实战运用&#xff0c;为读者提供全面而实用的指南&#xff0c;帮助他们理解、…

GitHub 信息动态统计:让你的 Readme 更具吸引力 | 开源日报 No.87

anuraghazra/github-readme-stats Stars: 59.4k License: MIT GitHub Readme Stats 是一个开源项目&#xff0c;它可以在 README 文件中动态生成 GitHub 统计数据。该项目的主要功能包括&#xff1a; GitHub 统计卡片&#xff1a;显示 stars、commits 和 pull requests 等公共…

2016年五一杯数学建模C题二孩政策问题解题全过程文档及程序

2016年五一杯数学建模 C题 二孩政策问题 原题再现 多年来实施的严、紧计划生育政策对控制人口增长起到关键作用。在优生优育政策的指引下&#xff0c;我国人口质量显著提高&#xff0c;但也带来了不利影响&#xff0c;生育率偏低、男女比例失衡、人口老龄化情况严重等问题。2…

数组与链表

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 除了HashMap&#xff0…

MATLAB中corrcoef函数用法

目录 语法 说明 示例 矩阵的随机列 两个随机变量 矩阵的 P 值 相关性边界 NaN 值 corrcoef函数的功能是返回数据的相关系数。 语法 R corrcoef(A) R corrcoef(A,B) [R,P] corrcoef(___) [R,P,RL,RU] corrcoef(___) ___ corrcoef(___,Name,Value) 说明 R corrc…

geemap学习笔记014:加载本地的tif文件

前言 Colab中似乎没法直接加载云盘中的数据&#xff0c;但是可以先上传到GEE中的assets中&#xff0c;再加载本地的数据。下面是以这个数据为例进行展示。 1 上传数据 首先将本地的tif数据上传到Asset中&#xff0c;得到独一的Image ID。 2 加载数据 使用ee.Image加载数据 …

【Web】PHP反序列化刷题记录

目录 ①[NISACTF 2022]babyserialize ②[NISACTF 2022]popchains ③[SWPUCTF 2022 新生赛]ez_ez_unserialize ④[GDOUCTF 2023]反方向的钟 再巩固下基础 ①[NISACTF 2022]babyserialize <?php include "waf.php"; class NISA{public $fun"show_me_fla…

python爬取招聘网站信息

废话不多说&#xff0c;直接上代码&#xff0c;开箱即用。该文件抓取的是智联招聘网站的招聘信息&#xff0c;可以根据需要设置输入搜索关键词和查找页数&#xff0c;就会得到结果&#xff0c;可以搜索到每个岗位的岗位名称、公司名称、学历要求、公司规模、福利待遇、行业、薪…

STL: 容器适配器stack 与 queue

目录 1.容器适配器 1.1 STL标准库中stack和queue的底层结构 1.2 deque的简单介绍(了解) 1.2.1 deque的原理介绍 1.2.2 deque的缺陷 1.2.3 为什么选择deque作为stack和queue的底层默认容器 2. stack的介绍和使用 2.1 stack的介绍 2.2 stack的使用 2.3 利用deque模拟实现…

一起学docker系列之十一使用 Docker 安装 Redis 并配置持久化存储

目录 前言1 基本安装步骤安装Redis镜像&#xff1a;查看已下载的Redis镜像&#xff1a;运行Redis容器&#xff1a;进入Redis容器&#xff1a;使用Redis CLI进行基本操作&#xff1a; 2 配置文件同步准备配置文件&#xff1a;修改Redis配置文件 /app/redis/redis.conf&#xff1…

【Java+SQL Server】前后端连接小白教程

目录 &#x1f4cb; 流程总览 ⛳️【SQL Server】数据库操作 1. 新建数据库text 2. 新建表 3. 编辑表 ⛳️【IntelliJ IDEA】操作 1. 导入jar包 2. 运行显示错误 &#x1f4cb; 流程总览 ⛳️【SQL Server】数据库操作 打开SQL Server数据库-->sa登录-->新建数据库…

如何处理枚举类型(下)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 上一篇我们通过编写MyB…

百面深度学习-自然语言处理

自然语言处理 神经机器翻译模型经历了哪些主要的结构变化&#xff1f;分别解决了哪些问题&#xff1f; 神经机器翻译&#xff08;Neural Machine Translation, NMT&#xff09;是一种使用深度学习技术来实现自动翻译的方法。自从提出以来&#xff0c;NMT模型经历了几个重要的…