《C++高级编程》读书笔记(四:设计专业的C++程序)

news/2024/11/18 16:20:45/

1、参考引用

  • C++高级编程(第4版,C++17标准)马克·葛瑞格尔

2、建议先看《21天学通C++》 这本书入门,笔记链接如下

  • 21天学通C++读书笔记(文章链接汇总)

1. 程序设计概述

  • 在启动新程序(或已有程序的新功能)时,第一步是分析需求
    • 分析阶段的重要输出是 “功能需求” 文档:描述新代码段到底要做什么,但它不解释如何做
    • 需求分析后,也可能得到 “非功能需求” 文档:其中描述最终系统是什么,而非做什么。非功能需求的例子有:系统必须是安全的、可扩展的,还能满足特定性能标准等
  • 第二步启动项目的设计阶段程序设计(或软件设计)是为了满足程序的所有功能和非功能需求而实现的结构规范
  • 大多数设计文档主要包括两个部分
    • (1) 将总的程序分为子系统,包括子系统之间的界面和依赖关系、子系统之间的数据流、每个子系统的输入输出和通用线程模型
    • (2) 每个子系统的详情,包括类的细分、类的层次结构、数据结构、算法、具体的线程模型和错误处理的细节

    设计文档通常包括图和表格,以显示子系统交互关系和类层次关系

2. 程序设计的重要性

  • 在编写代码之前进行规范的设计,有助于判断如何将所有内容组合在一起。程序的设计显示了程序子系统之间的关系和配合方式,以满足软件的需求。如果没有设计规划,可能会漏掉子系统之间的联系、重用或共享信息,以及失去用最简单方法完成任务的可能性。如果没有 “设计宏图”,可能会在某个实现细节上钻牛角尖,以至于忘记整体结构和目标

3. C++ 设计的两个原则

3.1 抽象

  • 关键在于将接口与实现分开
  • 抽象的作用
    • 可使用代码而不必了解底层的实现。在此有一个简单示例,程序可调用在 <cmath> 头文件中声明的 sqrt() 函数,而不需要知道这个函数使用什么算法求平方根。实际上,平方根计算的底层实现可能因库版本而异:但只要接口不变,函数调用就可以照常运行
    • 抽象原则也可以扩展到类:可使用 ostream 类的 cout 对象将数据传输到标准输出,不需要知道 cout 如何将文本输出到屏幕,只需了解公有接口
  • 在设计中使用抽象
    • 应该设计函数和类,使自己和其他程序员可以使用它们,而不需要知道(或依赖)底层的实现

3.2 重用

  • 重用的设计思想适用于自己编写和使用的代码,应该设计程序,以重用类、算法和数据结构
  • 在 C++ 中,模板是一种编写多用途代码的语言技术
    // 可用于任何二维棋盘游戏的 泛型游戏棋盘类
    template <typename PieceType>
    class GameBoard {
    public:void setPieceAt(size_t x, size_t y, PieceType *piece);PieceType *getPieceAt(size_t x, size_t y);bool isEmpty(size_t x, size_t y) const;
    private:...
    }
    

4. 重用代码

4.1 关于术语的说明

  • 有三种可重用的代码

    • 过去编写的代码
    • 同事编写的代码
    • 当前组织或公司以外的第三方编写的代码
  • 所使用的代码可通过以下几种方式来构建

    • 独立的函数或类:当重用自己或同事的代码时,通常会遇到这种类型
    • 库:库是用于完成特定任务 (例如解析 XML) 或者针对特定领域 (如密码系统) 的代码集合。在库中经常可以找到其他许多功能,如线程和同步支持、网络和图像
    • 框架:框架是代码的集合,围绕框架设计程序。例如,微软基础类 (Microsoft FoundationClasses,MFC) 提供了在 Microsoft Windows 中创建图形用户界面应用程序的框架。框架通常指定了程序的结构
  • 应用程序编程接口(API)是另一个经常出现的术语。API 是库或代码为特定目的提供的接口。例如,程序员经常会提到套接字 API,这指的是套接字联网库的公开接口,而不是库本身

  • 如果想要用 C++ 在 Microsot Windows 上编写图形用户界面 (GUI),应该使用 MFC(Microsoft Foundation Class) 或 Qt 等框架。你可能不知道如何在 Windows 上编写创建 GUI 的底层代码,更重要的是不想浪费时间去学习

4.2 重用代码的策略

4.2.1 理解功能和限制因素
  • 对于多线程程序而言,代码安全吗?
  • 库是否要求使用它的代码进行特定的编译器设置?如有必要,项目可以接受吗?
  • 库或框架需要什么样的初始化调用?需要什么样的清理?
  • 库或框架依赖于其他哪些库?
  • 如果从某个类继承,应该调用哪个构造函数?应该重写哪些虚方法?
  • 如果某个调用返回内存指针,调用者还是库负责内存的释放?如果库对此负责,什么时候释放内存?
  • 强烈建议查看是否可使用智能指针来管理由库分配的内存。智能指针在第 1章中讨论过。
  • 库调用检查哪些错误情况?此时做出了什么假定?如何处理错误?如何提醒客户端程序发生了错误?
  • 应该避免使用弹出消息框、将消息传递到stderr/cerr 或stdout/cout 以及终止程序的库。
  • 某个调用的全部返回值(按值或按引用)有哪些?
  • 所有可能抛出的异常有哪些?
4.2.2 大 O 表示法
  • 通常用大 O 表示法记录算法和库的性能
  • 大 O 表示法表示相对性能而不是绝对性能。例如,大 O 表示法不会指出某个算法运行需要的时间,而是指出当输入量增加时算法如何执行。大 O 表示法仅适用于速度依赖于输入的算法,不适用于没有输入或者运行时间随机的算法
  • 大 O 表示法用 O(n) 表示这个排序算法的性能。意味着使用大 O 表示法,n 表示输入量。O(n) 表示排序算法的速度是输入量的直接线性函数,并非所有算法的性能与输入量的关系都是线性的,下表按照性能好坏排序

在这里插入图片描述

4.2.3 理解性能的几点提示
  • 当数据量加倍时,算法所需要的时间也加倍,这根本就没有说需要多长时间!如果某个糟糕的算法具有较大的规模,这绝不符合需求。例如,如果算法进行了不必要的磁盘访问,可能不会影响大 O 表示法,但性能非常糟糕
  • 按照这一思路,很难比较两个具有相同大 O 运行时间的算法。例如,两个不同的排序算法都声称 O ( n l o g n ) O(nlogn) O(nlogn),如果不进行测试,很难说哪个算法实际上更快些
  • 大 O 表示法描述了算法的渐进时间复杂度,因为输入量会无限增大。对于小规模输入,大 O 时间很容易引起误解:当输入量规模不大时, O ( n 2 ) O(n^2) O(n2) 算法的实际执行性能可能要优于 O ( l o g n ) O(logn) O(logn) 算法
4.2.4 理解平台限制
  • 在开始使用库代码之前,一定要理解运行库的平台。这看上去是显而易见的,但即使是那些号称跨平台的库,在不同的平台上也会有微妙差别。此外,平台不仅包括不同的操作系统,还包括同一操作系统的不同版本
4.2.5 理解许可证和支持
  • 使用第三方的库常会带来复杂的许可证问题。为使用第三方供应商提供的库,有时必须支付许可证费用。还可能有其他的许可限制,包括出口限制。此外,开放源代码库有时会要求与其有关的任何代码都公开源代码
  • 使用第三方库还带来了支持问题。在使用某个库之前,一定要理解提交 bug 的过程,并了解修正 bug 所需的时间。如果可能,判断这个库会被支持多长时间,这样就可以相应地制定计划
4.2.6 原型
  • 当首次使用某个新库或框架时,最好编写一个快速原型。测试代码是熟悉库功能的最好方法。应该考虑在程序设计之前测试库,这样就可以熟悉库的功能和限制。这种实际检验还可判断库的性能特征
  • 即使原型应用程序与最终应用程序没有任何相似之处,花费在原型上的时间也不会浪费。不要觉得编写实际应用程序的原型很难,可编写一个虚拟程序来测试想使用的库功能,这样做是为了让自己熟悉库

5. 设计一个国际象棋程序

5.1 需求

  • 在开始设计前,应该弄清楚对于程序功能和性能的需求。理想情况下,这些需求应该是以需求规范 (requirements specification) 形式给出的文档。国际象棋程序的需求应该包含下列类型的规范,当然实际的需求规范应该比下面的内容更详细,条目更多
    • 程序支持标准的国际象棋规则
    • 程序支持两个玩家。程序不提供具有人工智能的计算机玩家
    • 程序提供基于文本的界面
      • 程序以纯文本形式提供棋盘和棋子
      • 玩家通过输入代表位置的数字在棋盘上移动棋子

5.2 设计步骤

在需要时设计应该包含图示和表格,制作图示的行业标准称为 UML(统一建模语言),UML 定义了一组标准图示,可用于说明软件设计 (如类图、序列图等)。建议使用 UML,至少也要尽量使用类似 UML 的图示。但不一定要严格遵循 UML 语法,因为图示清晰、易于理解,要比语法正确更重要

5.2.1 将程序分割为子系统
  • 设计的第一步是将程序分割为通用功能子系统,并指明子系统之间的接口和交互关系。此时不需要考虑特定的数据结构和算法,甚至不需要考虑类,只是试着感受程序不同部分和它们之间的交互关系
  • 建议使用模型-视图-控制(MVC)模式将数据存储和数据显示明确分离
    • MVC 模式建立了如下理念:许多应用程序经常要处理一组数据,处理这些数据上的一个或多个视图,并操作这些数据。在 MVC 中,这组数据称为模型,视图是模型的一个特定界面,控制器修改模型,以响应某个事件的代码。MVC 的 3 个组件在反馈循环中交互操作,动作由控制器处理,控制器会调整模型,把修改返回到视图中
    • 在 MVC 设计中,下标中的 ChessBoard 和 ChessPiece 子系统是模型部分,ChessBoardView 和ChessPieceView 是视图部分,Player 是控制器部分

在这里插入图片描述

  • 由于表格无法形象地表示子系统之间的关系,通常会使用图示来表明程序的子系统,在此箭头表示一个子系统对另一子系统的调用。下图用 UML 用例图显示了国际象棋游戏的各个子系统

在这里插入图片描述

5.2.2 选择线程模型
  • 在设计阶段,可以选择程序中高级线程的数目并指定线程的交互方式
    • 高级线程的示例有 UI 线程、音频播放线程、网络通信线程等
    • 在多线程设计中,应该尽可能避免共享数据,这样可使程序更简单、更安全
    • 如果无法避免共享数据,应该指定加锁需求。如果不熟悉或平台不支持多线程,那么程序应该是单线程
    • 然而,如果程序有多个不同的任务,每个任务都并行运行,多线程是个不错的选择。例如:图形用户界面程序经常让一个线程执行主程序,其他线程等待用户按下按钮或者选择菜单项
    • 国际象棋程序只需要一个线程来控制游戏流程
5.2.3 指定每个子系统的类层次结构
  • 国际象棋程序需要一个类层次结构来代表棋子,这个类层次结构如下图所示。在这个类层次结构中,ChessPiece 泛型类作为抽象基类,ChessPieceView 类也有类似的类层次结构

在这里插入图片描述

  • 另一个类层次结构用于 ChessBoardView 类,以实现游戏的文本界面或用户图形界面。下图给出了这个类层次结构,可以在控制台以文本方式显示棋盘,也可以用 2D 或 3D 图形显示棋盘,Player 控制器和 ChessPieceView 类层次结构的各个类也需要类似的类层次结构

在这里插入图片描述

5.2.4 指定每个子系统的类、数据结构、算法和模式

在这里插入图片描述

在这里插入图片描述

5.2.5 为每个子系统指定错误处理

在这里插入图片描述


http://www.ppmy.cn/news/258147.html

相关文章

SpringBoot整合邮箱验证码实现用户注册

唠嗑部分 今天我们来分享一下在系统开发过程中&#xff0c;如何使用验证码来验证用户并完成用户注册 首先来看一下成品界面展示 说一下以上注册功能的设计&#xff1a; 用户手动输入用户名(全数据库唯一)、密码、确认密码、邮箱地址(单个邮箱最多可注册3个用户)、正确的邮箱…

触屏笔哪个牌子好用?高性价比的电容笔推荐

事实上&#xff0c;苹果原本的电容笔和普通的电容笔最大的区别就是重力压感&#xff0c;市面上还没有一款能像apple pencil这样拥有着独特重力压感的电容笔。但是因为苹果的pencil太贵&#xff0c;很多人都把注意力都放在平替电容笔一边&#xff0c;平替电容笔的性能也在不断提…

哪个牌子的电容笔好,电容笔牌子排行

电容笔作为现在当下热销的数码配件&#xff0c;由于原装的价格太高了&#xff0c;所以市面上出现许多品牌的平替&#xff0c;在无数品牌商加入其中&#xff0c;导致许多小伙伴购选时难度加大&#xff0c;而大部分的品牌都是大同小异&#xff0c;看得眼花缭乱&#xff0c;更不知…

哪个牌子集成灶好用,这款登上《消费主张》栏目的集成灶品牌火了

如今&#xff0c;随着集成灶行业的发展&#xff0c;集成灶品牌众多&#xff0c;产品也比较多样化。因此市场上也出现了十大集成灶排名等供广大消费者参考的权威榜单。那么在这些知名品牌中&#xff0c;哪个牌子集成灶好用&#xff1f;集成灶有哪些功能是值得重点关注的呢&#…

Python time 模块

time 是python的内置模式,使用的时候需要import time 引入 time 的几个主要函数如下 import time# 当前时间戳 print("time:", time.time()) # 打印结果time: 1686107990.798039 # 返回可读形式的时间 print("ctime:", time.ctime()) # 打印结果ctime:…

PWA:Service Worker实现离线访问、域名失效启用备用域名......

介绍下Service Worker service worker 运行在独立的线程&#xff0c;可以拦截和处理网络请求&#xff0c;以实现离线缓存、推送通知和后台同步等功能。具体可以看MDN的介绍 怎么使用Service Worker 1、注册脚本 // useServiceWorker.ts navigator.serviceWorker?.register…

美敦力PB560呼吸机设计图纸 源代码分享

美敦力PB560呼吸机设计图纸 源代码分享 全套资料下载&#xff1a;一牛网论坛

PyQt5实时曲线实现(肺功能仪,呼吸机)

PyQt5——实时曲线 摘自大佬&#xff1a;https://www.it610.com/article/1282184782742044672.htm 稍微修改了一下&#xff0c;仅做笔记&#xff0c;还会添加功能 import sys import random from PyQt5.QtChart import QDateTimeAxis, QValueAxis, QSplineSeries, QChart, QCh…