[笔记]《Effective C++》第五章 Implementations
条款26:Postpone variabledefinitions as long as possible.
尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率。
- 你不只应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初值实参为止。
条款27:Minimize casting.
- 如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。
- 如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码内。
- 宁可使用C++-style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的职掌。
C++提供四种新式转型:
-
const_cast通常被用来将对象的常量性转除(cast away the constness)。
-
dynamic_cast主要用来执行“安全向下转型”(safe downcasting),也就是用来决定某对象是否归属继承体系中的某个类型。dynamic_cast的许多实现版本执行速度相当慢。
-
reinterpret_cast意图执行低级转型,实际动作(及结果)可能取决于编译器,这也就表示它不可移植。
-
static_cast用来强迫隐式转换(implicit conversions),例如将non-const对象转为const对象(就像条款3所为),或将int转为double等等。
-
转型并不是什么都不做,任何一个类型转换(不论是通过转型操作而进行的显式转换,或通过编译器完成的隐式转换)往往真的令编译器编译出运行期间执行的码。
我们可以避免使用dynamic_cast,需要dynamic_cast通常是因为你想在一个你认定为derived class对象身上执行derived class操作函数,但你的手上却只有一个“指向base”的pointer或reference,你只能靠它们来处理对象。有两种一般性的做法避免这个问题:
- 第一,使用容器并在其中存储直接指向derived class对象的指针(通常是智能指针)。
- 另一种做法可让你通过base class接口处理“所有可能之各种Window派生类”,那就是在base class内提供virtual函数做你想对各个派生类做的事。
条款28:Avoid returning "handles" to object internals.
避免返回handles(包括references、指针、迭代器)指向对象内部。
- 成员变量的封装性最多只等于“返回其reference”的函数的访问级别。
- 如果const成员函数传出一个reference,后者所指数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据。
条款29:Strive for exception-safe code.
- 异常安全函数(Exception-safe functions)即使发生异常也不会泄漏资源或允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。
- “强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义。
- 函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。
带有异常安全性的函数在异常被抛出时,需要做到:
- 不泄漏任何资源。
- 不允许数据败坏。
三种保证:
- 基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。
- 强烈保证:如果异常被抛出,程序状态不改变。
- 不抛掷(nothrow)保证:承诺绝不抛出异常,因为它们总是能够完成它们原先承诺的功能。
copy-and-swap常被用来实现强烈保证,其原则为:
- 为你打算修改的对象(原件)做出一份副本,然后在那副本身上做一切必要修改。若有任何修改动作抛出异常,原对象仍保持未改变状态。待所有改变都成功后,再将修改过的那个副本和原对象在一个不抛出异常的操作中置换(swap)。
条款30:Understand the ins and outs of inlining.
- inline函数背后的整体观念是,将“对此函数的每一个调用”都以函数本体替换之。
- Inlining在大多数C++程序中是编译期行为。
- inline只是对编译器的一个申请,不是强制命令。
- 所有对virtual函数的调用(除非是最平淡无奇的)都会使inlining落空。因为virtual意味“等待,直到运行期才确定调用哪个函数”,而 inline意味“执行前,先将调用动作替换为被调用函数的本体”。
- 构造函数和析构函数往往是inlining 的糟糕候选人。
- 大部分调试器面对inline函数都束手无策。你如何在一个并不存在的函数内设立断点(breakpoint)?
条款31:Minimize compilation dependencies between files.
- 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。
- 程序库头文件应该以“完全且仅有声明式”(full and declaration-only forms)的形式存在。这种做法不论是否涉及templates都适用。
以“声明的依存性”替换“定义的依存性”:
- 如果使用object references或 object pointers 可以完成任务,就不要使用objects。
- 如果能够,尽量以class声明式替换class定义式。
- 为声明式和定义式提供不同的头文件。
分类:
读书笔记
标签:
Note
, Effective C++
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!