《More Effective C++》 笔记

news2024/12/23 10:28:08

基础议题

pointers(指针)、references(引用)、casts(类型转换)、arrays(数组)、constructors(构造函数)。

1:仔细区别 pointers和 references

  • 没有所谓的 null reference。一个 reference 必须总代表某个对象。所以如果你有一个变量,其目的是用来指向(代表)另一个对象,但是也有可能它不指向(代表)任何对象,那么你应该使用 pointer,因为你可以将 pointer设为 null。

  • 当你需要考虑“不指向任何对象”的可能性时,或是考虑“在不同时间指向不同对象”的能力时,你就应该采用 pointer。

  • 当你知道你需要指向某个东西,而且绝不会改变指向其他东西,或是当你实现一个操作符而其语法需求无法由pointers 达成,你就应该选择 references。任何其他时候,请采用pointers。

2:最好使用 C++转型操作符

C++导入 4个新的转型操作符(castoperators):static_cast,const_cast,dynamic_cast 和reinterpret_cast。

  • static_cast 基本上拥有与 C 旧式转型相同的威力与意义,以及相同的限制。
    例如,你不能够利用 static_cast 将一个 struct 转型为 int,或将一个 double转型为 pointer;这些都是 C 旧式转型动作原本就不可以完成的任务。

  • const_cast 用来改变表达式中的常量性(constness)或变易性(volatileness)。
    使用 const_cast,便是对人类(以及编译器)强调,通过这个转型操作符,你唯一打算改变的是某物的常量性或变易性。这项意愿将由编译器贯彻执行。如果你将 const_cast 应用于上述以外的用途,那么转型动作会被拒绝。

  • dynamic_cast,用来执行继承体系中“安全的向下转型或跨系转型动作”。
    也就是说你可以利用dynamic_cast,将“指向 base class objects的 pointers或references”转型为“指向 derived(或 sibling base)class objects的pointers 或 references”,并得知转型是否成功。
    如果转型失败,会以一个 null指针(当转型对象是指针)或一个 exception(当转型对象是 reference)表现出来。
    dynamic_cast 只能用来协助你巡航于继承体系之中。它无法应用在缺乏虚函数(请看条款 24)的类型身上,也不能改变类型的常量性(constness)。

  • reinterpret_cast 的最常用用途是转换“函数指针”类型。

class Widget {
public:
    // 必须要添加,dynamic_cast需要baseClass是多态的。
    // 否则error: 'Widget' is not polymorphic
    virtual ~Widget() = default;
};

class SpecialWidget : public Widget {
public:
    ~SpecialWidget() override = default;
};

void update(SpecialWidget *psw) {};
void update2(SpecialWidget &psw) {};

void testMore02() {
    SpecialWidget sw;
    const SpecialWidget &csw = sw;

    // update(&csw); // 错误,函数参数非const
    update(const_cast<SpecialWidget *>(&csw)); // 使用const_cast改变表达式中的常量性
    update((SpecialWidget *) &csw); // 旧式语法直接转换
    update2(const_cast<SpecialWidget &>(csw));
    update2((SpecialWidget &) csw); // 旧式语法直接转换

    Widget *pw = new SpecialWidget;
    // update(pw); // 错误,函数需要SpecialWidget
    update(dynamic_cast<SpecialWidget *>(pw));
    update2(dynamic_cast<SpecialWidget &>(*pw));
}

3:绝对不要以多态(polymorphically)方式处理数组

C++语言规范中说,通过 base class 指针删除一个由derived classes objects构成的数组,其结果未定义。我们知道所谓“未定义”的意思就是:执行之后会产生苦恼。

  • 多态(polymorphism)和指针算术不能混用。数组对象几乎总是会涉及指针的算术运算,所以数组和多态不要混用。

4:非必要不提供 default constructor

所谓 default constructor(也就是说不给任何自变量就可调用者)是C++一种“无中生有”的方式。Constructors 用来将对象初始化,所以default constructors 的意思是在没有任何外来信息的情况将对象初始化。
为一个不需要 defaultconstructor 的 class 画蛇添足地加上一个 default constructor”而获得提升。同事添加无意义的 default constructors,也会影响 classes 的效率。

但无default constructor有以下缺点,需要在开发过程中注意:

  1. 缺乏 default constructor,其运行可能在 3种情况下出现问题。第一个情况是在产生数组的时候。一般而言没有任何方法可以为数组中的对象指定 constructor 自变量,所以几乎不可能产生一个由 EquipmentPiece objects 构成的数组:
  2. Classes 如果缺乏 default constructors,带来的第二个缺点是:它们将不适用于许多 template-based container classes。

操作符(Operators)操作符

5:对定制的“类型转换函数”保持警觉

6:区别 increment/decrement 操作符的前置(prefix)和后置(postfix)形式

7:千万不要重载&&,||和,操作符

8:了解各种不同意义的 new和 delete

  • new operator、delete operator
    这个操作符是由语言内建的,就像sizeof 那样,不能被改变意义,总是做相同的事情。它的动作分为两方面。
    第一,它分配足够的内存,用来放置某类型的对象。以上例而言,它分配足够放置一个string 对象的内存。
    第二,它调用一个 constructor,为刚才分配的内存中的那个对象设定初值。newoperator总是做这两件事,无论如何你不能够改变其行为。

  • 函数
    void* operator new(size_t size)
    void operator delete(void* memoryToBeDeallocated)

异常(Exceptions)异常

9:利用 destructors避免泄漏资源

在对象析构函数中释放资源;
使用智能指针。

10:在 constructors内阻止资源泄漏(resource leak)

结论是:如果你以 auto_ptr 对象来取代 pointer class members,你便对你的constructors 做了强化工事,免除了“exceptions 出现时发生资源泄漏”的危机,不再需要在 destructors 内亲自动手释放资源,并允许 const member pointers 得以和non-const member pointers有着一样优雅的处理方式。

11:禁止异常(exceptions)流出 destructors之外

在析构函数中使用try-cach,如果有异常发生的话。这么做有如下好处:

  1. 它可以避免 terminate函数在 exception传播过程的栈 展开(stack-unwinding)机制中被调用;
  2. 它可以协助确保destructors 完成其应该完成的所有事情

12:了解“抛出一个 exception”与“传递一个参数”或“调用一个虚函数”之间的差异

13:以 by reference方式捕捉 exceptions

它不像 catch-by-pointer,不会发生对象删除问题,因此也就不难捕捉标准的 exception。
它和 catch-by-value也不同,所以没有切割(slicing)问题,而且exception objects 只会被复制一次。

void fun() {
    try {
    } catch(exception& ex) {
        // 使用引用捕获异常
    }
}

14:明智运用 exception specifications

15:了解异常处理(exception handling)的成本

  • 如果使用 try 语句块,代码大约整体膨胀 5%~10%,执行速度亦大约下降这个数。这是在假设没有任何 exceptions被抛出的情况下。

  • 和正常的函数返回动作比较,由于抛出exception 而导致的函数返回,其速度可能比正常情况下慢 3个数量级。

效率(Efficiency)效率

16:谨记 80-20 法则

  • 软件的整体性能几乎总是由其构成要素(代码)的一小部分决定。
    一个程序 80%的资源用于 20%的代码身上。是的,80%的执行时间花在大约 20%的代码身上,80%的内存被大约 20%的代码使用,80%的磁盘访问动作由 20%的代码执行,80%的维护力气花在 20%的代码上面。

  • 假设你的程序太慢,你需要一个分析器告诉你程序的不同区段各花费多少时间,于是你便可以专注在特别耗时的地方加以改善,这不但可以巨幅提升局部效率,对整体效率也会有极大帮助。

17:考虑使用 lazy evaluation(缓式评估)

18:分期摊还预期的计算成本

19:了解临时对象的来源

20:协助完成“返回值优化(RVO)”

此特殊的优化行为——利用函数的 return 点消除一个局部临时对象(并可能用函数调用端的某对象取代)——不但广为人知而且很普遍地被实现出来。它甚至有个专属名称:return value optimization。

21:利用重载技术(overload)避免隐式类型转换(implicit type conversions)

22:考虑以操作符复合形式(op=)取代其独身形式(op)

23:考虑使用其他程序库

24:了解 virtual functions、multiple inheritance、virtual base classes、runtime type identification的成本

  • virtualtables和 virtual table pointers——此二者常被简写为 vtbls 和vptrs。
    vtbl 通常是一个由“函数指针”架构而成的数组。

  • 你必须为每个拥有虚函数的 class耗费一个vtbl 空间,其大小视虚函数的个数(包括继承而来的)而定。
    每个class 应该只有一个 vtbl,所以 vtbls 的总空间通常并不是很大,但如果你有大量这类 classes,或是你在每一个 class 中拥有大量虚函数,你可能会发现,vtbls 占用不少内存。

  • 凡声明有虚函数的 class,其对象都含有一个隐藏的 data member,用来指向该class 的 vtbl。这个隐藏的 data member——所谓的 vptr——被编译器加入对象内某个唯编译器才知道的位置。

  • 运行时期类型辨识(runtime typeidentification,RTTI)的成本。
    RTTI 让我们得以在运行时期获得 objects 和 classes 的相关信息,所以一定得有某些地方用来存放那些信息才行——是的,它们被存放在类型为 type_info 的对象内。

  • RTTI 的设计理念是:根据 class 的 vtbl 来实现。

技术(Techniques,Idioms,Patterns)技术

25:将 constructor和 non-member functions虚化

26:限制某个 class所能产生的对象数量

27:要求(或禁止)对象产生于 heap之中

28:Smart Pointers(智能指针)

29:Reference counting(引用计数)

30:Proxy classes(替身类、代理类)

31:让函数根据一个以上的对象类型来决定如何虚化

杂项讨论

32:在未来时态下发展程序

33:将非尾端类(non~leaf classes)设计为抽象类(abstract classes)

34:如何在同一个程序中结合 C++和 C

将双方都使用的函数声明为 extern "C"。

35:让自己习惯于标准 C++语言


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

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

相关文章

XRP价格跌破2.20美元 1.94美元是否下一波牛市的关键支撑?

原文转自&#xff1a;XRP价格跌破2.20美元 1.94美元是否下一波牛市的关键支撑&#xff1f; - 币热网 - 区块链数字货币新闻消息资讯 XRP价格经历剧烈波动后强势反弹&#xff0c;$1.94或成新牛市关键支撑 在过去24小时内&#xff0c;XRP价格经历了一场过山车式的剧烈波动。价…

centos-stream9系统安装docker

如果之前安装过docker需要删除之前的。 sudo dnf -y remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 安装yum-utils工具&#xff1a; dnf -y install yum-utils dnf-plugin…

H264编解码标准码流分析:SPS语法

H264编解码标准 SPS 语法解析 解释:H264编解码标准中的SPS(Sequence Parameter Set,序列参数集)是一组编码视频序列的全局参数,包含了视频编码序列的基本属性和配置信息。分析工具:elecard streamEye、elecard StreamAnalyzer、h264Visa 等elecard StreamAnalyzer 展示形…

使用 AI 辅助开发一个开源 IP 信息查询工具:一

本文将分享如何借助当下流行的 AI 工具,一步步完成一个开源项目的开发。 写在前面 在写代码时&#xff0c;总是会遇到一些有趣的机缘巧合。前几天&#xff0c;我在翻看自己之前的开源项目时&#xff0c;又看到了 DDNS 相关的讨论。虽然在 2021 年我写过两篇相对详细的教程&am…

门控循环单元(GRU):深度学习中的序列数据处理利器

目录 ​编辑 引言 GRU的诞生背景 GRU的核心机制 GRU的计算过程 GRU的数学公式 GRU的应用领域 代码示例&#xff1a;PyTorch中的GRU GRU与LSTM的比较 参数比较 GRU的技术发展 BiGRU&#xff08;双向GRU&#xff09; BiGRU的实现示例 GRU与CNN的结合 GRU的应用案例…

Sui 基金会任命 Christian Thompson 为新任负责人

Sui 基金会是专注于推动 Sui 蓬勃发展的生态增长与采用的机构。近日&#xff0c;基金会宣布任命 Christian Thompson 为新任负责人。在 Sui 主网发布的开创性一年里&#xff0c;Sui 凭借其无与伦比的速度、可扩展性和效率&#xff0c;迅速崛起为领先的 Layer 1 区块链之一&…

Vue2五、商品分类:My-Tag表头组件,My-Table整个组件

准备&#xff1a; 安包 npm less less-loader。拆分&#xff1a;一共分成两个组件部分&#xff1a; 1&#xff1a;My-Tag 标签一个组件。2&#xff1a;My-Table 整体一个组件&#xff08;表头不固定&#xff0c;内容不固定&#xff08;插槽&#xff09;&#xff09; 一&…

mysql运维篇笔记——日志,主从复制,分库分表,读写分离

目录 日志 错误日志 二进制日志 查询日志 慢查询日志 主从复制 概念&#xff1a; 优点&#xff1a; 原理&#xff1a; 搭建&#xff1a; 1&#xff0c;服务器准备 2&#xff0c;主库配置 3&#xff0c;从库配置 4&#xff0c;测试 分库分表&#xff1a; 介绍 问题分析 中心思想…

【JavaEE初阶】线程 和 thread

本节⽬标 认识多线程 掌握多线程程序的编写 掌握多线程的状态 一. 认识线程&#xff08;Thread&#xff09; 1概念 1) 线程是什么 ⼀个线程就是⼀个 "执⾏流". 每个线程之间都可以按照顺序执⾏⾃⼰的代码. 多个线程之间 "同时" 执⾏着多份代码. 还…

设计模式期末复习

一、设计模式的概念以及分类 二、设计模式的主题和意图 设计模式的主题是关于软件设计中反复出现的问题以及相应的解决方案。这些主题是基于长期实践经验的总结&#xff0c;旨在提供一套可复用的设计思路和框架&#xff0c;以应对软件开发中的复杂性和变化性。 三、面向对象程…

【小白51单片机专用教程】protues仿真AT89C51入门

课程特点 无需开发板0基础教学软件硬件双修辅助入门 本课程面对纯小白&#xff0c;因此会对各个新出现的知识点在实例基础上进行详细讲解&#xff0c;有相关知识的可以直接跳过。课程涉及protues基本操作、原理图设计、数电模电、kell使用、C语言基本内容&#xff0c;所有涉及…

MFC用List Control 和Picture控件实现界面切换效果

添加List Control 和Picture控件 添加 3个子窗体 把子窗体边框设置为None, 样式设为Child 声明 CListCtrl m_listPageForm;void ShowForm(int nIndex);void CreatFormList();void CMFCApplication3Dlg::DoDataExchange(CDataExchange* pDX) {CDialogEx::DoDataExchange(pDX);DD…

Linux高并发服务器开发 第五天(压缩解压缩/vim编辑器)

目录 1.压缩和解压缩 1.1压缩 1.2解压缩 2.vim编辑器 2.1vim的3种工作模式 2.2切换编辑模式 2.3保存和退出 2.4光标移动 1.压缩和解压缩 - Linux 操作系统&#xff0c;默认支持的 压缩格式&#xff1a;gzip、bzip2。 默认&#xff0c;这两种压缩格式&#xff0c;只能…

接口测试Day-02-安装postman项目推送Gitee仓库

postman安装 下载 Postman&#xff08;已提供安装包&#xff0c;此步可以跳过&#xff09; https://www.postman.com/downloads/安装 Postman 安装Postman插件newman 要想给 postman 安装 newman 插件&#xff0c;必须 先 安装 node.js。 这是前提&#xff01; 安装node.js 可能…

虚拟地址空间 -- 虚拟地址,虚拟内存管理

1. C/C语言的内存空间分布 用下列代码来观察各种区域的地址&#xff1a; #include <stdio.h> #include <unistd.h> #include <stdlib.h>int g_unval; int g_val 100;int main(int argc, char *argv[], char *env[]) {const char *str "helloworld&qu…

【数字化】华为数字化转型架构蓝图-2

目录 1、客户联结的架构思路 1.1 ROADS体验设计 1.2 具体应用场景 1.3 统一的数据底座 1.4 案例与成效 2、一线作战平台的架构思路 2.1 核心要素 2.2 关键功能 2.3 实施路径 2.4 案例与成效 3、能力数字化的架构思路 3.1 能力数字化的核心目标 3.2 能力数字化的实…

【优选算法】—移动零(双指针算法)

云边有个稻草人-CSDN博客 想当一名牛的程序员怎么能少的了练习算法呢&#xff1f;&#xff01; 今天就立即开启一个新专栏&#xff0c;专干算法&#xff0c;提高算法能力&#xff08;废柴的我也在准备蓝桥杯哈哈&#xff09;—— 目录 1.【 283. 移动零 - 力扣&#xff08;Lee…

AI的进阶之路:从机器学习到深度学习的演变(三)

&#xff08;承接上集&#xff1a;AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;二&#xff09;&#xff09; 四、深度学习&#xff08;DL&#xff09;&#xff1a;机器学习的革命性突破 深度学习&#xff08;DL&#xff09;作为机器学习的一个重要分支&am…

Python自动化测试:线上流量回放

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 在自动化测试中&#xff0c;线上流量回放是一项关键技术&#xff0c;可以模拟真实用户的请求并重现线上场景&#xff0c;验证系统的性能和稳定性。本文将介绍Pytho…

初始C语言3

目录 9. 操作符 9.1 算术操作符 9.2 移位操作符 9.3 位操作符 9.4 赋值操作符 9.5 单目操作符 9.6 关系操作符 9.7 逻辑操作符 9.8 条件操作符 9.9 逗号表达式 下标引用、函数调用和结构成员 10. 常见关键字 10.1 typedef 10.2 static 10.2.1 修饰局部变量 10.…