Effective C++快速复习
创始人
2025-05-30 18:49:24

Effective C++快速复习

习惯 C++

01 视 C++ 为一个语言联邦:C、Object-Oriented C++、Template C++、STL

02 尽量以 const, enum, inline 替换 #define:其实是尽量以编译器替换预处理器比较好,因为 #define 只是简单的字符串匹配替换,编译器无法看到,不利于 debug,也容易出问题

03 尽可能使用 const:Rust 语言设计就是 const 作为默认语义,可以帮助编译器侦测出错误用法

04 确定对象被使用前已经被初始化:内置类型对象进行手动初始化,C++不保证初始化他们;尽量使用初始化列表来初始化

构造,析构,赋值

05 了解 C++ 默认编写了哪些函数:默认构造函数、默认拷贝构造函数、默认拷贝赋值操作符、默认移动赋值操作符、析构函数

06 可以明确拒绝编译器自动生成的函数:例如将相应函数声明为 private 且不予实现,例如 Boost 里边有个 uncopyable 类

07 为多态基类编写 virtual 函数:带有多态性质的基类应该声明 virtual 析构函数,或者说一个类只要带有 virtual 函数,就应该有 virtual 析构函数;

08 别让异常逃离析构函数:如果析构函数调用的函数可能抛出异常,那么析构函数就应该捕捉任何一场,不传播或者直接 abort ;如果客户需要对某个操作函数运行期间抛出的异常作出反应,那么类应当提供一个普通函数,instead of 析构函数,执行该操作

09 绝不在构造和析构过程中调用 virtual 函数:你以为你调用的是子类的 virtual 函数,实际上可能是父类的 virtual 函数。在基类构造和析构期间调用的 virtual 函数不可下降至派生类。

*10 令 operator= 返回一个 this 引用:为了实现连续赋值

11 在 operator= 中处理自我赋值:比较来源对象和目标对象的地址是否相同;包括如果操作一个大的对象里有若干个对象,也要分别考虑是否有自我赋值。

12 复制对象时勿忘其每一个成分:拷贝赋值操作符要覆盖所有成员变量以及基类的成分(例如直接调用基类的拷贝赋值操作符);但是不要在拷贝赋值操作符里调用拷贝构造函数,而是要把二者的共同点提取出来作为一个新的函数

资源管理

13 以对象管理资源:使用 RAII 和智能指针的技术防止资源泄露

14 资源管理类中小心 coping 行为:常见的 RAII class copying 行为是:抑制复制、使用引用计数法、一同复制底部资源、转移底部资源的所有权

15 在资源管理类中提供对原始资源的访问:因为 APIs 往往要求访问原始资源(raw resources);可以采用显式转换和隐式转换的方式,前者比较安全,后者方便用户使用

16 成对使用 new 和 delete 时要采取相同形式:就是在有没有带数组 [] 符号上,new 和 delete 必须要统一

17 以独立语句将 new 得到的对象放入智能指针:意思是智能指针的以一个原始指针作为参数的构造函数是 explicit 的,所以要主动将 new 得到的对象指针放入智能指针里。

设计与声明

18 让接口容易被正确使用、不易被误用:接口一致、与内置类型行为兼容;消除客户的资源管理责任

19 设计 class 犹如设计 type:考虑新的类型应当如何被创建和销毁、初始化和赋值有着怎样的差别、值传递和引用传递有什么差别、是否需要配合某个继承体系、是否需要类型转换、如何使用、是否真的需要一个新的 class

20 宁以 pass-by-reference-to-cont 替换 pass-by-value:前者更高效,并且在继承关系存在时还可以防止子类部分被切割掉;但对于内置类型、STL迭代器和函数对象,后者更加合适。

21 必须返回对象时,不要返回其 reference:local stack 对象在函数结束时就会被销毁,如果是 heap-allocated 对象也应该返回 pointer 而不是 reference

22 将成员变量声明为 private:protedted 并不必 public 更具有封装性;将成员变量全部声明为 private,可赋予客户访问数据的一致性、进行更细粒度的访问控制、允许约束条件得到保证,给类的编写者更加充分的实现弹性

23 宁以 non-member、non-friend 替换 member 函数:这样可以提高封装性、包裹弹性和机能扩充性,可以将这类 non- 函数与类放在同一个命名空间下。(可以这样理解:增加这些 non- 函数只会在原有的封装性下做事情,而增加 member 函数却有可能会暴露出新的访问权限)

24 若所有参数皆需类型转化,请为此采用 non-member 函数:令 class 支持隐式类型转换往往是个坏主意;例如设计一个新的数值类型,它的算数运算想要兼容原有的内置类型,就需要将这些 operator+ 等函数写成 non-member;

25 考虑写一个不抛出异常的 swap 函数:自己提供的 swap 成员函数效率更高;定义完一个 member swap,也应当提供一个 non-member swap(即特化 std::swap()),来方便使用;为用户定义的类型进行 std templates 全特化是好的,但是不要往 std 命名空间里加入全新的东西。

实现

26 尽可能延后变量定义出现的时间:要用了再来定义,而不要像一些老的代码一样在开头定义好所有需要用到的变量;这样程序更加清晰,而且效率也可能得到提升。

27 尽量少做 casting 动作:即使要类型转换,也用 C++ 的新式类型转换(static_cast、dynamic_cast、const_cast、reinterprete_cast),这样在代码中容易被识别,也方便编译器诊断出错误;注重效率的代码中尽量不要用 dynamic_cast;注意 reinterprete_cast 不可移植;

28 避免返回 handles 指向对象内部成分:handles 指 references、指针、迭代器等,这样可以增加封装性

29 注意异常安全:注意不泄露任何资源、不允许数据破坏

30 透彻了解 inlining 的里里外外:inline 更适合小型的、被频繁调用的函数上,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。

31 将文件键的编译依存关系降至最低:能使用 object reference 或者 pointer 弯沉任务的,就不要使用 object;尽量用 class 声明式替换定义式;为声明和定义提供不同的源文件,头文件应当完全仅有声明式。

继承与面型对象设计

32 public 继承应当塑模出 is-a 关系:is-a 关系是说,子类是一种基类,比如苹果是一种水果

33 避免遮掩继承而来的名称:可以用 using 声明或者 forwarding functions 的技巧来使用父类中被遮掩的名称

34 区分接口继承和实现继承:成员函数的接口总是会被继承;

  • 声明 pure virtual 函数是为了让派生类只继承函数接口、而必须要自己实现这个函数;
  • 声明 impure virtual 函数的目的,是让派生类继承该函数接口,同时提供一个默认实现版本
  • 声明 non-virtual 函数是为了让派生类直接继承函数接口,并且提供强制性实现

35 考虑 virtual 函数以外的其他选择:例如可以用 non-vritual interface 实现 Template Method 模式、Function Pointer 或 std::function 实现 Stategy 模式、

36 绝不重新定义继承而来的 non-virtual 函数:正如 34 中所讲,这是为了提供一份强制性的实现。

37 绝不重新定义继承而来的缺省参数值:缺省参数值是静态绑定,如果派生类继承了一个带有缺省参数值的函数,那么一定不要改缺省参数值(可以直接不写)

38 通过复合塑模出 has-a 或 is-implemented-by:复合是指 composition

39 审慎地使用 private 继承:private 继承也是 is-implemented-by 的意思,比复合的级别更低,可以实现 EBO;

40 审慎地使用多重继承:多重继承可能会带来歧义,而且 virtual 继承会带来 overhead,当 virtual base class 不带任何数据,将是最具实用价值的情况。当然多重继承也有正当用途,例如 public 继承某个 interface class 的同时 private 继承某个协助实现的 class

模板与泛型编程

41 了解隐式接口和编译器多态:对于 template 而言,接口是隐式的,基于有效表达式,多态则是通过template 具现化和函数重载解析发生于编译期。

42 了解 typename 的双重意义:既可以用在模板参数声明时,和 class 一样的用法,又可以用在表示由模板参数得到的派生类型上,标识嵌套从属类型名称,告诉编译器这是个类型。

43 学习处理模板化基类内的名称:在 derived class template 内通过 this-> 来引用 base class template 内的成员名称

44 将与参数无关的代码抽离 templates:因非类型模板参数而造成的代码膨胀,往往可以通过以函数参数或者 class 成员变量替换 template 参数来消除;

45 运用成员函数模板接受所有兼容类型:如果声明 member templates 用于泛化拷贝构造动作或泛化赋值操作,还是需要声明正常的拷贝构造函数和赋值操作符

46 需要类型转换时请为模板定义非成员函数:当我们编写一个 class template,而它所提供的与此template相关的函数支持所有参数的隐式类型转换时,请将哪些函数定义为 class template 内部的 friend 函数(例如前面提到的定义一个新的数值类型)

47 使用 traits classes 表现类型信息:通过萃取其可以是类型相关信息在编译器可用,参考 STL 的 iterator 实现

48 认识模板元编程:是一种编程范式,可将工作从运行期移往编译器,更早实现错误侦测,具有更高的执行效率

定制 new 和 delete

49 了解 new-handler 的行为

  • 需要让更多内存可以被使用

  • 当前 new-handler 无法获取更多内存时,可以安装别的 new-handler 替换自己

  • 卸除 new-handler

  • 抛出 (派生自)bad_alloc 的异常

  • 不返回:通常调用 abort 或 exit

50 了解替换编译器的 new 和 delete 的合理时机:往往是为了获得非传统的行为

  • 用来检测运用上的错误:例如 new 成功而 delete 失败造成的内存泄漏
  • 强化效能:编译器提供的 new 和 delete 不适用于长时间执行的程序
  • 收集统计数据:用于调试、收集 heap 使用信息

51 编写 new 和 delete 时需固守常规

  • operator new 内应当含有一个无穷循环,并且不断尝试分配内存;如果无法满足内存需求,就应当调用 new-handler;能够处理 0B 的申请;
  • operator delete 在接受 null 指针时不要做任何事情;

52 写了 palcement new 也要写 placement delete

杂项讨论

53 不要轻忽编译器的警告:0 error 不是最终目标

54/55 熟悉 TR1、Boost 等标准程序库 :虽然很多东西现在 C++11 以后都有了

相关内容

热门资讯

数字操作方法 系列文章目录 前端系列文章——传送门 JavaScript系列文章——传送门 文章目录系列文章目录...
Cartesi 2023 年 ... 查看 Cartesi Machine、Cartesi Rollups 和 Noether 的更新正在...
JavaWeb——jsp概述入... JSP定义:  在如下一个jsp文件里面有如下的代码  <%@ page content...
一切喜怒哀乐都来自于你的认知 01 有个学子,准备出国,父母请来清华的教授宁向东。请问教授࿱...
JAVA并发编程——synch... 引言         Java语言为了解决并发编程中存在的原子性、可见性和有序性问题,...
git学习----3.21 未... 文章目录前言Git :一个分布式版本控制工具目标一、概述1.1 开发中的实际场景1.2...
Qt优秀开源项目之十七:QtP... QtPromise是Promises/A+规范的Qt/C++实现。该规范的译...
【前端八股文】JavaScri... 文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和...
海康硬盘录像机接入RTSP/o... EasyNVR安防视频云服务平台可支持设备通过RTSP/Onvif协议接入平台,能提供...
在混合劳动力时代如何避免网络安... 在混合劳动力时代如何避免安全网络风险 三年多来,混合工作一直是工作生活中不可或缺的一...
2023还不懂Jmeter接口... 这里介绍的Jmeter接口测试的的实战,如果文章内容没遇看懂的话,我这边...
基于4G/5G弱网聚合的多链路... 基于4G/5G多卡聚合(弱网聚合)的智能融合通信设备技术亮点 增强带宽提供可靠连接 通过将多个有线和...
如何使用Synplify综合v... 文章目录使用Synplify综合的好处synplify的教程方法1(无效)...
2023年全国最新高校辅导员精... 百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等ÿ...
2022年18个值得期待的Le... 有数百个独特的LearnDash附加组件,您可能很难选择您的LearnDash LMS...
【java基础】Stream流... 文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法fl...
javaweb高校行政办公自动... 本课题基于我国高校管理信息化建设现状,结合在实际工作中所遇到的问题和收获,...
一款专门为自动化测试打造的集成... 你好,我是不二。 随着行业内卷越来越严重,自动化测试已成为测试工程师的...
【go-zero】golang... 一、casbin 概览 1、casbin基本了解 casbin的GitHub:https://git...
现在开发低代码平台算晚吗? 现在开发低代码平台算晚吗?作为低代码的亲戚——零代码厂商,这篇就以“厂商...
【JavaWeb】书城项目(2... 222.书城项目-第三阶段:修改所有html页面为jsp页面 改成jsp页面之后&#x...
基于jeecgboot的大屏设...      通过前面设计好数据源后,就要进行数据集的设计了。      一、还是在onl...
Linux命令小技巧:显示文件... 工作中会有很多千奇百怪的需求,比如:如何在 Linux 命令行中快速找到...
【找工作】-- 大数据工程师找... 目录 1.前言 2.找工作的理论知识 2.1 分析个人特征 2.1.1 你自身优势是什么?
C++基础算法④——排序算法(... 排序算法 1.插入排序 2.桶排序 1.插入排序 基本思想:将初始数据分为有序部分和...
nginx快速入门.跟学B站n... nginx快速入门.跟学B站nginx一小时精讲课程笔记nginx简介及环境准备nginx简介环境准...
ORACLE存过互相调用之间事... 今天在问答区看到一个问题是 假如有procedureA、procedureB和procedureC&...
基于java中Springbo... 基于java中Springboot框影视影院订票选座管理系统 开发语言:Java 框...
CVE-2018-18086 最近闲来无事,看到青少年CTF平台,感觉对新手还是比较友好的࿰...
【深度学习】基于Hough变化... 💥💥💞💞欢迎来到本博客❤️❤️&#x...