《C++高级编程》读书笔记(五、六:面向对象设计设计可重用代码)

news2024/11/28 11:56:25

1、参考引用

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

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

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

1. 过程化的思考方式

  • 过程语言(例如 C)将代码分割为小块,每个小块(理论上)完成单一的任务。如果在 C 中没有过程,所有代码都会集中在 main() 中,代码将难以阅读
  • 计算机并不关心代码是位于 main() 中还是被分割成具有描述性名称和注释的小块。过程是一种抽象,它的存在是为了帮助程序员和阅读或维护代码的人。这个概念建立在一个与程序相关的基本问题之上:程序的作用是什么?用语言回答这个问题,就是过程化思考

2. 面向对象思想

  • 与基于 “程序做什么” 问题的面向过程方法不同,面向对象方法提出另一个问题:模拟哪些实际对象?OOP(Object Oriented Programming)的基本观念是不应该将程序分割为若于任务,而是将其分为自然对象的模型。乍看上去这有些抽象,但用类组件、属性和行为等术语考虑实际对象时,这一思想就会变得更清晰

2.1 类

  • 类将对象与其定义区分开来
  • 类只是封装了用来定义对象分类的信息
  • 所有对象都属于某个特定的类,对象是类的一个实例

2.2 组件

  • 本质上,组件与类相似,但组件更小、更具体
  • 如果考虑一个复杂的实际对象,可以看到它是由许多小组件组成

2.3 属性

  • 属性将一个对象与其他对象区分开来
  • 类属性由所有的类成员共享,而类的所有对象都有对象属性,但具有不同的值
  • 属性用来描述对象的特征,回答 “为什么这个对象与众不同” 的问题

2.4 行为

  • 行为回答两个问题:“对象做什么” 和 “能对对象做什么”
  • 在面向对象编程中,许多功能性的代码从过程转移到类。通过建立具有某些行为的对象并定义对象的交互方式,OOP 以更丰富的机制将代码和代码操作的数据联系起来
  • 类的行为由类方法实现

3. 对象之间的关系

3.1 “有一个(has a)”关系

  • “有一个” 关系或聚合关系的模式是 A 有一个 B,或者 A 包含一个 B。在此类关系中,可认为某个对象是另一个对象的一部分。前面定义的组件通常代表 “有一个” 关系,因为组件表示组成其他对象的对象

3.2 “是一个(is a)”关系(继承)

  • “是一个” 关系是面向对象编程中非常基本的概念,因此有许多名称,包括派生、子类、扩展和继承。类模拟了现实世界包含具有属性和行为的对象这一事实,继承模拟了这些对象通常以层次方式来组织这一事实。“是一个” 说明了这种层次关系。基本上,继承的模式是:A 是一个 B,或者 A 实际上与 B 非常相似
  • 可定义 Animal 类,用以封装所有动物都有的属性 (大小、生活区域、食物等) 和行为 (走动、进食、睡觉)。特定的动物 (例如猴子) 成为 Animal 的子类,因为猴子包含动物的所有特征,它还有与众不同的其他特征

在这里插入图片描述

  • 当类之间具有 “是一个” 关系时,目标之一就是将通用功能放入基类(base class),其他类可扩展基类。如果所有子类都有相似或完全相同的代码,就应该考虑将一些代码或全部代码放入基类。这样,可在一个地方完成所需的改动,将来的子类可 “免费” 获取这些共享的功能
3.2.1 继承技术
  • 添加功能
    • 派生类可在基类的基础上添加功能。例如,猴子是一种可挂在树上的动物。除了具有动物的所有行为以外,猴子还具有在树间移动的行为,即 Monkey 类有 swingFromTrees() 方法,这个行为只存在于 Monkey 类中
  • 替换功能
    • 派生类可完全替换或重写父类的行为。例如,大多数动物都步行,因此 Animal 类可能拥有模拟步行的 move 行为。但袋鼠是一种通过跳跃而不是步行移动的动物,Animal 基类的其他属性和行为仍然适用,Kangaroo 派生类只需要改变 move 行为的运行方式

    当然,如果对基类的所有功能都进行替换,就可能意味着采用继承的方式根本就不正确,除非基类是一个抽象基类:抽象基类会强制每个子类实现未在抽象基类中实现的所有方法,无法为抽象基类创建实例

  • 添加属性
    • 除了从基类继承属性外,派生类还可添加新属性。企鹅具有动物的所有属性,此外还有 beak size (鸟喙大小) 属性
  • 替换属性
    • 与重写方法类似,C++ 提供了重写属性的方法。然而,这么做通常是不合适的,因为这会隐藏基类的属性,例如,基类可为具有特定名称的属性指定一个值,而派生类可给该属性指定另一个值
3.2.2 多态性与代码重用
  • 多态性指具有标准属性和方法的对象可互换使用
  • 在模拟动物园时,可通过编程遍历动物园中的所有动物,让每个动物都移动一次。由于所有动物都是 Animal 类的成员,因此它们都知道如何移动。某些动物重写了移动行为,但这正是亮点所在:代码只是告诉每个动物移动,而不知道也不关心是哪种动物,所有动物都按自己的方式移动
  • 除多态性外,使用继承还有一个原因,通常这只是为了利用现有的代码。例如,如果需要一个具有回声效果的音乐播放类,而同事已经编写了一个播放音乐的类,但没有其他任何效果,此时可扩展这个已有的类,添加回声新功能

3.3 not-a 关系

  • 当考虑类之间的关系时,应该考虑类之间是否真的存在关系。当实际事物之间存在明显关系,而代码中没有实际关系时,问题就出现了。OO (面向对象) 层次结构需要模拟功能关系,而不是人为制造关系。下图显示的关系作为概念集或层次结构是有意义的,但在代码中并不能代表有意义的关系
    在这里插入图片描述

避免不必要继承的最好方法是首先给出大概的设计。为每个类和派生类写出计划设置的属性和行为。如果发现某个类没有自己特定的属性或方法,或者某个类的所有属性和方法都被派生类重写,只要这个类不是前面提到的抽象基类,就应该重新考虑设计

3.4 层次结构

  • 正如类 A 可以是类 B 的基类一样,B 也可以是 C 的基类,面向对象层次结构可模拟类似的多层关系。当编写每个派生类的代码时,许多代码可能是相似的。此时应该考虑让它们拥有共同的父类

在这里插入图片描述

  • 优秀的面向对象层次结构能做到以下几点
    • 使类之间存在有意义的功能关系
    • 将共同的功能放入基类,从而支持代码重用
    • 避免子类过多地重写父类的功能,除非父类是一个抽象类

3.5 多重继承

  • 在多重继承中,一个类可以有多个基类(通常应避免使用多重继承)

在这里插入图片描述

4. 抽象

4.1 接口与实现

  • 抽象的关键在于有效分离接口与实现
    • 实现是用来完成任务的代码,接口是其他用户使用代码的方式
    • 在 C 中,描述库函数的头文件是接口,在面向对象编程中,类的接口是公有属性和方法的集合
    • 优秀的接口只包含公有行为,类的属性/变量绝不应该公有,但可通过公有方法公开,这些方法称为获取器和设置器

4.2 决定公开的接口

  • 当设计类时,其他程序员如何与你的对象交互是一个问题。在 C++ 中,类的属性和方法可以是公有的 (public)、受保护的 (protected) 和私有的 (private)
    • public 意味着其他代码可以访问它们
    • protected 意味着其他代码不能访问这个属性或行为,但子类可以访问
    • private 意味着不仅其他代码不能访问这个属性或行为,子类也不能访问
  • 设计公开的接口就是选择哪些应该成为 public
    • 考虑用户:个人、团队中的其他成员、用户等
    • 考虑目的:API、工具类或库、子系统接口、组件接口
    • 考虑将来:如果将来的用途不明,就不要设计包含一切的日志类,因为这样做会不必要地将设计、实现和公有接口复杂化

5. 如何设计可重用代码

  • 可重用代码有两个主要目标:代码必须通用、代码必须易用

5.1 使用抽象

  • 使用抽象对于自己和使用代码的客户都有好处
    • 客户会获得好处,因为他们不需要担心实现细节:他们可利用提供的功能,而不必理解代码的实际运行方式
    • 你会获得好处,是因为可修改底层的代码,而不需要改变接口。这样就可升级和修订代码,而不需要客户改变他们的用法(如果使用动态链接库,客户甚至不需要重新生成可执行程序)
  • 有时为将某个接口返回的信息传递给其他接口,库要求客户代码保存这些信息。这一信息有时叫作句柄 (handle),经常用来跟踪某些特定的实例,这些实例调用时的状态需要被记住
    • 如果库的设计需要句柄,不要公开句柄的内部情况。可将句柄放入某个不透明类,程序员不能直接访问这个类的内部数据成员,也不能通过公有的获取器或设置器来访问
    • 不要要求客户代码改变句柄内部的变量。一个不良设计的示例是,一个库为了启用错误日志,要求设置某个结构的特定成员,而这个结构所在的句柄本来应该是不透明的

5.2 构建理想的重用代码

5.2.1 避免组合不相干的概念或者逻辑上独立的概念
  • 当设计组件时,应该关注单个任务或一组任务,即 “高聚合”,也称为 SRP(Single Responsibility Principle,单一责任原则)。不要将无关概念组合在一起,例如随机数生成器和 XML解析器

    • 这个编程策略模拟了现实中可互换的独立部分的设计原则。例如,可编写一个 Car 类,在其中放入引擎的所有属性和行为。但引擎是独立组件,未与小汽车的其他部分绑定。可将引擎从一辆小汽车卸下,安装在另一辆小汽车中。合理的设计是添加一个 Engine 类,其中包含与引擎相关的所有功能。此后,Car 实例将包含 Engine 实例
  • 将程序分为逻辑子系统

    • 将子系统设计为可单独重用的分立组件,即“低耦合”。例如,如果设计一款网络游戏,应该将网络和图形用户界面放在独立的子系统中,这样就可以重用一个组件,而不会涉及另一个组件。假定现在要编写一款单机游戏,就可以重用图形界面子系统,但是不需要网络功能。与此类似,可以设计一个对等文件共享程序,在此情况下可重用网络子系统,但是不需要图形用户界面功能
  • 用类层次结构分离逻辑概念

    • 除了将程序分为逻辑子系统以外,在类级别上应该避免将无关概念组合在一起。例如,假定要为自驾车编写类。你决定首先编写小汽车的基本类,然后在其中直接加入所有自驾逻辑。但是,如果程序中只需要非自驾车,该怎么办?此时,与自驾相关的所有逻辑都会失效,而程序必须与本可避开的库 (如 vision 库和 LIDAR 库) 链接,解决方案是创建一个类层次结构,将自驾车作为普通汽车的一个派生类

在这里插入图片描述

  • 用聚合分离逻辑概念
    • 当不适合使用继承方法时,可以使用聚合分离没有关系的功能或者有关系但独立的功能。例如,假定要编写一个 Family 类来存储家庭成员。显然,树数据结构是存储这些信息的理想结构。不应该把树数据结构的代码整合到 Family 类中,而是应该编写一个单独的 Tree 类,然后 Family 类可以包含并使用 Tree 实例
5.2.2 对泛型数据结构和算法使用模板
  • C++ 模板的概念允许以类型或类的形式创建泛型结构。例如,假定为整型数组编写了代码。如果以后要使用 double 数组,就需要重写并复制所有代码。模板的概念将类型变成一个需要指定的参数,这样就可以创建个适用于任何类型的代码体。模板允许编写适用于任何类型的数据结构和算法

    • 最简单的示例是 std::vector 类,这个类是 C++ 标准库的一部分。为创建整型的vector,可编写 std::vector<int>;为创建 double 类型的 vector,可编写 std::vector<double>
  • 模板的问题

    • 模板并不是完美的。首先,其语法令人迷惑,对于没有用过模板的人而言更是如此。其次,模板要求相同类型的数据结构,在一个结构中只能存储相同类型的对象
  • 模板与继承

    • 如果打算为不同的类型提供相同的功能,则使用模板。例如,如果要编写一个适用于任何类型的泛型排序算法,应该使用模板
    • 如果要创建一个可以存储任何类型的容器,应该使用模板,关键的概念在于模板化的结构或算法会以相同方式处理所有类型。但是,如有必要,可给特定的类型特殊化模板,以区别对待这些类型
    • 当需要提供相关类型的不同行为时,应该使用继承。例如,如果要提供两个不同但类似的容器,例如队列和优先队列,应该使用继承。
    • 现在可以把二者结合起来,可以编写一个模板基类,此后从中派生一个模板化的类
  • 扩展性

    • 设计的类应当具有扩展性,可通过从这些类派生其他类来扩展它们。不过,设计好的类应当不再修改,也就是说,其行为应当是可扩展的,而不必修改其实现。这称为开放/关闭原则 (Open/ClosedPrinciple,OCP)

5.3 设计有用的接口

5.3.1 设计容易使用的接口
  • 采用熟悉的处理方式

    • 开发易于使用的接口的最佳策略是遵循标准的、熟悉的做事方法。当人们遇到的接口与他们过去用过的接口类似时,就能更好地理解这个接口,更容易采用这个接口,也不大可能用错。创新当然很重要,但应该在底层实现中创新,而不是在接口上
    • 回到 C++,这个策略表明,开发的接口应该遵循 C++ 程序员熟悉的标准
    • C++ 提供了一种叫作运算符重载的语言特性,帮助为对象开发易于使用的接口
  • 不要省略必须的功能

    • 首选,接口应该包括用户可能用到的所有行为
    • 其次,在实现中包含尽可能多的功能
  • 提供整洁的接口

    • 不要在接口中提供多余的功能,保持接口的简洁
  • 提供文档和注释

    • 提供接口文档有两种方法:接口自身内部的注释和外部的文档。应该尽量提供这两种文档。大多数公开的 API 只提供外部文档:许多标准 UNIX 和 Windows 头文件中都缺少注释。在 UNIX 中,文档形式通常是名为 manpages 的在线手册。在 Windows 中,集成开发环境通常附带文档
5.3.2 设计通用接口
  • 提供执行相同功能的多种方法

    • 为让所有“顾客”都满意,有时可提供执行相同功能的多种方法。然而,应该慎用这种方法,因为过多的应用很容易让接口变得混乱不堪
  • 提供定制

    • 为增强接口的灵活性,可提供定制。定制可以很简单,如允许用户打开或关闭错误日志。定制的基本前提是向每个客户提供相同的基本功能,但给予用户稍加调整的能力
    • 通过函数指针和模板参数,可提供更强的定制
5.3.3 协调通用性和使用性
  • 提供多个接口

    • 为在提供足够功能的同时降低复杂性,可提供两个独立接口。这称为接口隔离原则 (Interface SegregationPrinciple,ISP)。例如,编写的通用网络库可以具有两个独立的方向:一个为游戏提供网络接口,另一个为超文本传输协议 (HTTP,一种网络浏览协议)提供网络接口
  • 让常用功能易于使用

    • 当提供通用接口时,有些功能的使用频率会高于其他功能。应该让常用功能易于使用,同时仍提供高级功能选项

5.4 SOLID 原则

在这里插入图片描述

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

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

相关文章

玄奘文旅集团主办学习强企玄奘之路戈壁挑战赛在敦煌圆满完赛!

“学习改变命运&#xff0c;强企复兴中国”&#xff0c;2023年4月16日-20日&#xff0c;由玄奘文旅集团主办的“第23届26届学习强企玄奘之路戈壁挑战赛&#xff08;联赛&#xff09;”在千年古城敦煌圆满落幕&#xff01; 这是商界各行业的一次跨界融合&#xff0c;也是疫情过…

感性了解一下互斥和信号量

一、互斥的四个概念 我们把大家都能看到的资源叫做&#xff1a;公共资源 a、互斥&#xff1a;任何一个时刻&#xff0c;都只允许一个执行流在进行共享资源的访问——加锁 b、我们把任何一个时刻&#xff0c;都只允许一个执行流进行访问的共享资源叫做临界资源 c、临界资源需…

【TA100】图形 2.4 传统经验光照模型详解

一、光照模型 ● 一种模拟自然光照过程的计算机模型 ● 本次课程可以这样理解&#xff1a;光线与物体表面的作用 ● 分类 ○ 基于物理的光照模型&#xff08;PBR&#xff09;&#xff08;有可依据的公式&#xff09; ○ 经验模型&#xff08;进行了一些近似、模拟&#xff0c;…

【小呆的概率论学习笔记】正态分布的代数运算

文章目录 1. 正态分布简介1. 正态分布的数字特征2. 正态分布的代数运算a. 单随机变量的代数运算b. 两个正态分布随机变量的和c. 多个正态分布随机变量的线性组合 1. 正态分布简介 正态分布应该是概率论和数理统计中最重要的一类概率分布&#xff0c;最早的完整论述是由数学王子…

自用-华东师范大学硕士学位论文LATEX模板

文章目录 前言部分页面效果预览使用说明&#xff1a;毕业论文LaTeX模板1. 安装LaTeX发行版和TeX Studio&#xff1a;2. 下载毕业论文模板&#xff1a;3. 编辑论文内容&#xff1a;**标题和作者信息&#xff1a;****摘要和关键词&#xff1a;****章节内容&#xff1a;****图片插…

【Linux】-关于调试器gdb的介绍和使用

作者&#xff1a;小树苗渴望变成参天大树 作者宣言&#xff1a;认真写好每一篇博客 作者gitee:gitee 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录 前言一、Linux中的debug和release二、gdb的使用**1.进入调试****2.显示代码*…

配置SSH免密登陆

配置SSH免密登陆 1.生成公私密钥对1&#xff09;检查是否已存在公私钥对2&#xff09;生成公私钥对 2.上传公钥到服务器3.ssh-add&#xff08;mac的坑点&#xff09;4.使用配置文件连接 博主是MAC用户&#xff0c;文章参考知乎 1.生成公私密钥对 1&#xff09;检查是否已存在…

Redis高性能设计之epoll和IO多路复用深度解析

一 多路复用 并发多客户端连接&#xff0c; 在多路复用之前最简单和典型的方案&#xff1a;同步阻塞网络IO模型 这种模型的特点就是用一个进程来处理一个网络连接&#xff08;一个用户请求&#xff09;&#xff0c;比如一段典型的示例代码如下&#xff1a; //直接调用recv函数…

【Java】JAVASE面向对象知识点总结

面向对象 概念 现实生活&#xff1a; ​ 类&#xff1a;抽象的概念&#xff0c;把具有相同特征和操作的事物归为一类 ​ 先有实体&#xff0c;再有类的概念 代码世界&#xff1a; ​ 类&#xff1a;抽象的概念&#xff0c;把具有相同属性和方法的对象归为一类 ​ 编写顺序&…

epoll的LT与ET模式以及阻塞和非阻塞

文章目录 1、基本概念2、关于在socket和EPOLL中的阻塞与非阻塞3、几种IO模型的触发方式4、代码验证5、总结 1、基本概念 Level_triggered(水平触发)&#xff1a;当被监控的文件描述符上有可读写事件发生时&#xff0c;epoll_wait()会通知处理程序去读写。如果这次没有把数据一次…

Flask-蓝图

1、使用步骤&#xff1a; 创建蓝图 blue Blueprint("myblue01", __name__) 使用蓝图装饰视图函数 blue.route(/) def index():return index 将蓝图注册到app中 from appdemo_blueprint import blue app.register_blueprint(blue) 2、以包的形式使用蓝图 <…

Java007——Java注释学习

围绕以下3点介绍&#xff1a; 1、什么是Java注释&#xff1f; 2、Java注释的作用&#xff1f; 3、Java注释长什么样&#xff0c;以及怎么使用Java注释&#xff1f; 一、什么是Java注释&#xff1f; Java注释是在Java程序中用来描述代码的特殊语句。 注释被忽略并且不被编译器…

PyTorch 深度学习 || 专题六:PyTorch 数据的准备

PyTorch 数据的准备 1. 生成数据的准备工作 import torch import torch.utils.data as Data#准备建模数据 x torch.unsqueeze(torch.linspace(-1, 1, 500), dim1) # 生成列向量 y x.pow(3) # yx^3#设置超参数 batch_size 15 # 分块大小 torch.manual_seed(10) # 设置种子点…

javascript基础三十:JavaScript如何实现上拉加载,下拉刷新?

一、前言 下拉刷新和上拉加载这两种交互方式通常出现在移动端中 本质上等同于PC网页中的分页&#xff0c;只是交互形式不同 开源社区也有很多优秀的解决方案&#xff0c;如iscroll、better-scroll、pulltorefresh.js库等等 这些第三方库使用起来非常便捷 我们通过原生的方…

哈工大 大数据 数据库实验(3) 物理数据库设计--索引结构

指导书: HIT邹老师数据库实验三资源-CSDN文库 导入数据库 操作 两个元组的分布规律 1.查询元组的数据分布 SELECT COUNT(*) FROM Foo; SELECT COUNT(*) FROM Foo WHERE id 0; SELECT COUNT(*) FROM Foo WHERE a 0; SELECT COUNT(*) FROM Foo WHERE b 0; SELECT COUNT(*…

c++ 解析html与htmlcxx库

目录 1&#xff0c;htmlcxx Github 版本源码下载 2&#xff0c;htmlcxx Linux 版本源码下载 3&#xff0c;htmlcxx 解析例子 1&#xff0c;htmlcxx Github 版本源码下载 正如在前一篇文章 c CFile 类 里提到的&#xff0c;我想要从指定的 html 文件里提取…

【裸机开发】镜像文件内部信息构成

由于当前使用的是裸板&#xff0c;没有OS&#xff0c;DDR的初始化、文件保存地址都尚未确定&#xff0c;所以我们生成二进制文件以后&#xff0c;是无法直接放到开发板上运行的。 因此&#xff0c;我们一般会在二进制文件之前加一些头部信息&#xff0c;这些头部信息会告诉开发…

xsync集群分发脚本

脚本&#xff1a; #!/bin/bash #1. 判断参数个数 if [ $# -lt 1 ] thenecho Not Enough Arguement!exit; fi #2. 遍历集群所有机器 for host in hadoop02 hadoop03 doecho $host #3. 遍历所有目录&#xff0c;挨个发送for file in $do#4. 判断文件是否存在if [ -e $file ]th…

文本三剑客awk:命令讲解

awk-命令讲解&#xff1a; 一、awk&#xff1a; 1.定义&#xff1a; &#xff08;1&#xff09;在 Linux/UNIX 系统中&#xff0c;awk 是一个功能强大的编辑工具&#xff0c;逐行读取输入文本&#xff0c;默认以空格或tab键作为分隔符作为分隔。并按模式或者条件执行编辑命令…

006-从零搭建微服务-注册中心(二)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff1a;https://gitee.com/csps/mingyue 文档地址&#xff1a;https://gitee.com/csps/mingyue/wikis 核心依赖 需要注册配置中心的服务引入下面 …