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 组成的整书验收

从“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 仍未机制化。

小结

  • 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。 copy assignment 在资源获取失败时发生事故。
  1. 问题 2:derived 在本地测试正确,经 Base pointer 行为错误。 设计诊断。
  1. 问题 3:制定一个 subsystem 的整书验收。 它包含 templates、custom allocator 和第三方 library。

讨论

评论区加载中…