Effective C++ 第三版总复习

串联第三版 55 Items:从对象有效性、RAII、接口与异常安全,到多态、templates、allocation、warnings 和库生态,形成跨条款诊断与工程验收链。

学习目标

  • 能解释 55 Items 如何把对象有效性、ownership、接口、实现、多态和 generic/system contract 串成一条正确性主线
  • 能分析 resource、dispatch、template、build、allocator 与 portability 故障,并定位需要联合使用的 Items
  • 能设计 compile、failure injection、sanitizer、performance 和 review trace 组成的整书验收
Effective C++ · 全书地图55 条条款 · 四大板块 · 从习惯语言到驾驭模板习惯 C++ 与构造析构条款 1-12视 C++ 为联邦const / inline 替代 #define对象初始化构造/析构/赋值资源管理条款 13-17以对象管理资源RAII 拷贝行为访问原始资源智能指针设计与继承条款 18-40接口设计原则成员变量封装is-a 公有继承多态与复合模板与杂项条款 41-55模板隐式接口TMP 与 type traits定制 new/deleteBoost 与 TR1习惯奠基,资源管控,设计封装,模板延伸——55 条准则一条主线
全书四大板块:习惯 C++ 与构造析构(条款 1-12)奠基,资源管理(条款 13-17)管控,设计与继承(条款 18-40)封装,模板与杂项(条款 41-55)延伸。
Effective C++ · 总复习RAII智能指针拷贝行为new/delete 配对封装const 正确性is-a 继承NVI 模式隐式接口typenameTMPtype traitsinline转型控制异常安全Boost资源管理类与继承模板与泛型杂项约定EffectiveC++55 Items四大分支一图收束:资源为基,类与继承为骨,模板为翼,约定为绳
总复习思维导图:中心「Effective C++」辐射四分支——资源管理(RAII、智能指针、拷贝行为、new/delete 配对)、类与继承(封装、const 正确性、is-a、NVI 模式)、模板与泛型(隐式接口、typename、TMP、type traits)、杂项约定(inline、转型控制、异常安全、Boost)。
编码约定:五大维度条款 53-55 杂项 + 贯穿全书的工程习惯接口设计容易被正确使用,不易被误用用类型限制参数,避免误传条款 18inline 策略小函数 inline,大函数不 inlineinline 可能增加代码体积条款 30转型控制尽量少做转型,避免旧式转型static_cast / dynamic_cast条款 27异常安全提供基本/强/不抛异常保证copy-and-swap 模式条款 29编译器警告不要轻忽编译器警告不同编译器警告不同,逐条理解条款 53标准库与 Boost熟悉 TR1 / 标准库 / Boost 组件条款 54-55:不重复造轮子条款 54-55约定 = 把经验固化为习惯——让正确成为默认行为接口防误用、inline 节制、转型最小化、异常安全、善用标准库条款 53-55:编译器警告、TR1 标准库、Boost 生态
编码约定五大维度:接口设计(防误用)、inline 策略(小函数)、转型控制(最小化)、异常安全(基本/强/不抛保证)、编译器警告(不轻忽),外加标准库与 Boost。

跨条款复盘实验

从症状回溯根因链

选择一个事故,先预测第一道边界,再展开能让结论可复核的证据。

关联范围

Items 11 · 13 · 14 · 15 · 29

先预测 ownership 是否闭合,再区分 self-assignment、raw access 与异常回滚。

当前诊断

先预测 ownership 是否闭合,再区分 self-assignment、raw access 与异常回滚。

从“55 条共同保护什么”开始

整书不是 55 个互不相干的 style rules。它们共同把一个程序从“依赖开发者记得做对”改成“对象、类型和工具链默认阻止做错”。

对象语义是所有后续原则的地基

Items 1-12 要求正确初始化、理解 compiler-generated functions、显式控制 copy、virtual destructor、destructor exception、constructor/destructor dispatch 与 assignment。

class Connection {
public:
    explicit Connection(Socket socket) : socket_(std::move(socket)) {}
    Connection(const Connection&) = delete;
    Connection& operator=(const Connection&) = delete;
    Connection(Connection&&) noexcept = default;
private:
    Socket socket_;
};

若对象自身 copy/destruction 不明确,RAII wrapper、container、inheritance 和 exception safety 都无法可靠建立。

RAII 把失败路径纳入正常结构

Items 13-17 与 29 的组合不是“使用 smart pointer”这么窄,而是确保每项资源在所有 control-flow exits 上释放一次,operation 失败后状态满足承诺。

Document load(Path path) {
    File file = File::open(path);
    Buffer data = readAll(file);
    Document candidate = parse(data);
    validate(candidate);
    return candidate;
}

每个 local owner 自动回收;最终状态只在 parse/validate 成功后返回。用第 N 步 failure injection 验证回滚,而不是只测 happy path。

接口把错误挡在 representation 之外

Items 18-25 要求易正确接口、把 class design 当 type design、合理传值、避免返回 local/internal handles、private data、non-member non-friend、对称 conversion 和 swap。

审查 API 时先问:非法 state 能否构造;borrowed handle 能否悬空;implicit conversion 是否无损;错误是否在 compile time 暴露。

实现细节不能泄漏成客户 contract

Items 26-31 处理变量生命周期、casts、internal handles、exception safety、inline 和 compilation dependencies。

Pimpl/interface 不是为了隐藏代码而隐藏,而是稳定客户看到的 contract;inline/cast 也需要 profiling 和语义证据。

继承审查从 substitutability 开始

Items 32-40 的核心问题不是“virtual 怎么写”,而是每条 inheritance edge 表达什么关系。

class Shape {
public:
    void draw(Color color = Color::red) const { doDraw(color); }
private:
    virtual void doDraw(Color color) const = 0;
};

NVI 统一 default、检查和流程,virtual hook 只选择变化算法。composition 表达 has-a/implemented-in-terms-of;private/multiple inheritance 需 protected/virtual/EBO 或多身份证据。

template 错误要按阶段诊断

Items 41-48 展示 implicit interface、typename、dependent base lookup、code bloat factoring、member templates、hidden friend、traits/tag dispatch 与 TMP。

template<class Iter>
void advanceFast(Iter& iter, std::ptrdiff_t n) {
    using Category = typename std::iterator_traits<Iter>::iterator_category;
    doAdvance(iter, n, Category{});
}

traits 提供类型事实,tag/concept 选择实现;constraints 应只表达算法真实使用,避免 overconstraint。重模板还要测 compile time、symbols 和 binary size。

allocation contract 是成对的系统

Items 49-52 把 handler、替换动机、常规和 placement failure 串起来。

allocator 测试必须覆盖 zero bytes、OOM、handler retry、Derived size、null delete、aligned/array/sized forms 和 constructor exception。

工具链和库是 contract 的最后一层

Items 53-55 要求把 warnings 当语义信号,熟悉标准库/TR1/Boost,并用不同 compiler/library 验证 portability。

warning-free 不是正确性证明,库进入 std 也不保证所有实现性能相同;仍需 contract test 和 benchmark。

从症状映射到跨 Item 根因

真实事故往往跨章:double free 可能同时涉及 Item 11 self-assignment、Item 13 RAII、Item 14 copying behavior 和 Item 29 exception safety。不要找到一条相关 Item 就停止。

一次完整代码审查

按以下顺序审查一个 subsystem:

  1. 每个 type 的 invariant、special members 和 destruction 是否明确。
  2. 每项 resource 的 owner/borrow/release graph 是否闭合。
  3. public API 是否暴露 representation、危险 conversion 或悬空 handle。
  4. failure 是否有 explicit guarantee 和 rollback evidence。
  5. inheritance 是否满足 substitutability,static/dynamic binding 是否一致。
  6. templates/allocation/toolchain 是否有 constraints、cost 和 portability gates。

整书验收门禁

验收必须包含 positive/negative compile tests、failure injection、sanitizers、performance budgets 和 multi-toolchain builds。一次运行通过只证明一个路径。

先预测一个故障会在哪一层被拦截,再执行门禁;若只能在 production 发现,说明 contract 仍未机制化。

把每次复盘写成“症状 → 候选 Items → 最小复现 → 失败证据 → 修复后的正反测试”链条;不能只引用最熟悉的一条规则。

小结

  • 55 Items 共同把正确性从人的记忆迁入对象、ownership、接口、类型和工具链
  • 对象语义是 RAII、异常安全、继承和泛型的前置
  • 真实故障要建立跨 Item 根因链,不能用单条口号解释完毕
  • 整书掌握以代码审查迁移和六层 evidence gate 验收,而不是背诵条款

资料与写作方式声明

本章以Effective C++, Third Edition, Items 1-55权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

名词解释

本章出现的专业名词,用大白话再讲一遍。

对象有效性
对象生命周期内持续满足不变量。
机制化正确性
由语言和类型自动承载正确性。
对象语义合同

特殊成员共同定义值与生命周期。

默认生成偏差

生成行为与真实 ownership 不一致。

RAII ownership

资源立即进入自动释放 owner。

失败后状态保证
异常后资源和状态层级承诺。
接口不变量边界
只有合法操作可修改对象。
封装破坏面

可访问 representation 的代码集合。

实现句柄泄漏

内部地址或 iterator 逃逸给客户。

依赖传播成本

header 变化造成重编译与 ABI 耦合。

substitutability

derived 保持 base contract。

混合绑定

default 静态、virtual body 动态选择。

generic expression contract

模板必须成立的表达式集合。

模板阶段诊断

按查找推导替换转换实例化定位。

allocation contract surface

分配失败对齐和配对完整边界。

构造失败配对

placement new 对应失败 delete。

编译诊断门禁
warning 与多工具链构建政策。
库选型边界
标准优先和第三方隔离原则。
跨 Item 根因链

同时追踪多条原则的事故分析。

六层代码审查
对象到系统层的固定审查顺序。
条款追踪矩阵

原则到代码、测试和 owner 的映射。

练习

  1. 问题 1:分析 shared resource 的 double free(self-assignment、RAII、raw access、exception guarantee)。 copy assignment 在资源获取失败时发生事故。
  1. 问题 2:derived 在本地测试正确,经 Base pointer 行为错误(virtual function、default argument、override)。 设计诊断。
  1. 问题 3:制定一个 subsystem 的整书验收(templates、placement delete、compiler warnings、Boost)。 它包含 templates、custom allocator 和第三方 library。

讨论

评论区加载中…