第5章:构造、析构与复制语义

对齐原书第5章 Semantics of Construction, Destruction, and Copy:纯虚析构与 virtual/const 契约、继承构造、vptr、复制、效率和逆序析构。

学习目标

  • 能解释 pure virtual destructor、virtual specification 与 const within a virtual specification 对 class interface 和销毁安全的约束
  • 能推导 object construction without/under inheritance、virtual inheritance 与 vptr initialization 的完整阶段顺序
  • 能分析 object copy semantics、object efficiency 和 semantics of destruction,并定位 raw copy、构造期虚调度与非虚析构错误

机制总览

第5章:构造、析构与复制语义:机制路径

  1. 1

    从“接口声明决定对象能否安全活完一生”开始

    constructor/destructor 不是独立函数;它们维护 object 从“不存在”到“各 subobject 有效”再回到“不存在”的状态机。先预测一个带 pure virtual destructor 的 abstract base 是否可以不给 destructor body:de…

  2. 2

    重新审视 Class Declaration

    polymorphic base 若允许通过 base pointer 销毁 dynamic derived object,destructor 必须 virtual。它也可以 pure virtual 以表达 interface 不应直接实例化,但仍要定义,因为 destruction chain 总会进入 base subobject teardown。

  3. 3

    1 Object Construction without…

    对于没有 inheritance 的 abstract data type,construction 仍分为 member initialization 与 body。initializer list 直接选择 member constructors;body 运行时 members 已经有效。若 …

先按顺序建立机制,再进入实验切换阶段并检查失效证据。

章级决策实验

第5章:构造、析构与复制语义:机制与证据

切换《第5章:构造、析构与复制语义》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。

选择推理阶段

当前阶段 · 从“接口声明决定对象能否安全活完一生”开始

constructor/destructor 不是独立函数;它们维护 object 从“不存在”到“各 subobject 有效”再回到“不存在”的状态机。先预测一个带 pure virtual destructor 的 abstract base 是否可以不给 destructor body:de…

可核验证据

用对象大小、成员地址、反汇编或构造析构轨迹核对「从“接口声明决定对象能否安全活完一生”开始」,并区分标准语义与当前 ABI 实现。

学完《第5章:构造、析构与复制语义》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。

失效—证据矩阵

第5章:构造、析构与复制语义:失效与核验

从“接口声明决定对象能否安全活完一生”开始

典型失效

若只从源码表面理解「从“接口声明决定对象能否安全活完一生”开始」,忽略编译器生成布局、调用约定和生命周期代码,调试时就会把实现机制误当成语言承诺。

核验证据

用对象大小、成员地址、反汇编或构造析构轨迹核对「从“接口声明决定对象能否安全活完一生”开始」,并区分标准语义与当前 ABI 实现。

重新审视 Class Declaration

典型失效

若只从源码表面理解「重新审视 Class Declaration」,忽略编译器生成布局、调用约定和生命周期代码,调试时就会把实现机制误当成语言承诺。

核验证据

用对象大小、成员地址、反汇编或构造析构轨迹核对「重新审视 Class Declaration」,并区分标准语义与当前 ABI 实现。

1 Object Construction without…

典型失效

若只从源码表面理解「1 Object Construction without…」,忽略编译器生成布局、调用约定和生命周期代码,调试时就会把实现机制误当成语言承诺。

核验证据

用对象大小、成员地址、反汇编或构造析构轨迹核对「1 Object Construction without…」,并区分标准语义与当前 ABI 实现。

每个判断都必须能落到观测、测试或产物,不能只凭代码表面推测。

从“接口声明决定对象能否安全活完一生”开始

constructor/destructor 不是独立函数;它们维护 object 从“不存在”到“各 subobject 有效”再回到“不存在”的状态机。先预测一个带 pure virtual destructor 的 abstract base 是否可以不给 destructor body:derived teardown 最终仍要调用 base destructor,所以“纯虚”禁止直接实例化,却不取消 base cleanup definition 的责任。

重新审视 Class Declaration

Presence of a Pure Virtual Destructor

polymorphic base 若允许通过 base pointer 销毁 dynamic derived object,destructor 必须 virtual。它也可以 pure virtual 以表达 interface 不应直接实例化,但仍要定义,因为 destruction chain 总会进入 base subobject teardown。

struct Shape {
    virtual double area() const = 0;
    virtual ~Shape() = 0;
};
 
Shape::~Shape() = default;
 
struct Circle final : Shape {
    double area() const override { return 1.0; }
};

Presence of a Virtual Specification

不是所有 operation 都应 virtual。virtual 适合 base contract 允许 dynamic replacement 的点;nonvirtual operation 可封装 invariant 并调用 protected virtual hook。无差别 virtual 会扩大 override surface、引入 polymorphic representation,并让 construction/destruction 期间的行为更难推导。

Presence of const within a Virtual Specification

double area() constdouble area() 是不同 member-function types。derived 必须写匹配的 const 才 override;override 关键字把错误变成 compile-time diagnostic。const member 接收 const object view,但不自动保证 thread safety,也不代表 physical bytes 永远不变。

“reconsidered class declaration” 的目标是让 destructor policy、override points、const contract 与 data ownership 一起可审计,而不是先写 hierarchy,再让 compiler-generated lifetime work 猜测设计意图。

5.1 Object Construction without Inheritance

Abstract Data Type

对于没有 inheritance 的 abstract data type,construction 仍分为 member initialization 与 body。initializer list 直接选择 member constructors;body 运行时 members 已经有效。若 class 有 virtual functions,常见 ABI 还要在适当位置建立 vptr,使 body 中的 virtual call 表现为当前 class。

class Buffer {
public:
    explicit Buffer(std::size_t size)
        : data_(size), checksum_(compute(data_)) {}
 
private:
    std::vector<std::byte> data_;
    std::uint64_t checksum_;
};

declaration order 是 data_checksum_,所以后者可以读取已构造 member。若反过来声明,调整 initializer list 文本顺序也不能修复。constructor body 适合执行依赖所有 members 已有效的工作,不适合用 assignment 代替必须的 direct initialization。

Preparing for Inheritance

准备继承时要问:是否会经 base pointer 删除?哪些 operation 真正需要 dynamic replacement?copy 是保留 dynamic type、clone,还是禁止 slicing?base constructor 是否调用可覆写行为?data 应 private 还是通过 protected invariant-preserving hooks 暴露?这些决定会直接改变 generated construction/destruction code。

5.2 Object Construction under Inheritance

derived constructor 不是只执行花括号内语句。概念展开包含:most-derived constructor 负责所有 virtual bases;direct bases 按 base-list order;implementation 建立当前 class 的 virtual state;members 按 declaration order;最后进入 body。每个 base constructor 又在自己的 subobject phase 执行同类规则。

Virtual Inheritance

中间 base 单独作为 complete object 时要负责其 virtual base;作为更大 derived 的 subobject 时,most-derived constructor 接管。旧实现可能通过 hidden flag/parameters 区分,现代 ABI 也有 construction vtables 等机制。需要掌握的是语义 ownership,不把某个 ABI 参数形式当标准。

The Semantics of the vptr Initialization

base constructor 运行时,derived invariant 尚未建立,因此 virtual call 只表现为当前 base phase;进入 derived phase 后,vptr representation 再对应 derived overrides。complete construction 后,dynamic dispatch 才稳定指向 most-derived final overrider。destruction 则反向退回各 base phase。

qualified call 是静态选择,constructor 内的普通 virtual call 受 construction dispatch rules 限制。不要依赖从 base constructor 调到 derived override 完成初始化;那会读取尚未构造 members,语言机制正是为了阻止这种错误。

5.3 Object Copy Semantics

copy construction 建立新 lifetime:virtual/direct bases 与 members 按目标对象 construction 顺序复制,target vptr 由 target construction phase 建立。copy assignment 修改已有 object:调用 base/member assignment,target dynamic identity 不因 source bytes 改变。

struct Base {
    virtual ~Base() = default;
    int id = 0;
};
 
struct Derived : Base {
    std::string name;
};
 
Derived first;
Derived second = first; // 构造新对象
second = first;         // 赋值给已有对象

对 polymorphic object 执行 memcpy 会绕过 string/resource copy,并可能把 source vptr、self-pointer 或 padding 状态覆盖到 target。按 Base value copy Derived 还会 slicing。需要保留动态类型时设计 virtual clone 或 value wrapper;不允许复制时显式 delete copy operations。

5.4 Object Efficiency

object-based value class 可拥有 direct calls、contiguous data 与 trivial operations;object-oriented hierarchy 提供 runtime substitution,常付 vptr、indirect branch、adjustment 和 nontrivial lifetime work。成本不是“class vs struct”,而是具体 semantics 的选择。

测试应分开:对象 size/alignment;批量 construction/destruction;copy/move count;direct/virtual call;single/multiple/virtual inheritance;cache locality。若 optimizer 去虚化或消除对象,说明该 workload 没保留想测的动态语义,需要通过 external target set、separate translation unit 或 profile-aware setup修正,而不是关闭所有优化制造不真实结果。

5.5 Semantics of Destruction

destruction 是 construction 的逆向状态退场,但 destructor body 运行时当前 class members 尚存在;body 结束后 members 逆序销毁,再到 direct bases,shared virtual bases 最后。每退到一个 base phase,virtual dispatch 也受当前 active class 限制。

通过 base pointer delete derived object 要求 base destructor virtual,否则 behavior undefined。destructor 默认应 noexcept;若 stack unwinding 中再次抛出未处理 exception,会终止程序。资源释放交给 member RAII owner,可让 compiler-generated destructor 正确组合顺序。

若某个 member constructor 抛出,complete object 从未构造成功,因此其 destructor body 不运行;但此前已成功的 bases/members 会逆序销毁。raw acquire 放在 body 早期却未进入 owner,可能在后续 throw 时泄漏。initializer + RAII 是正确的 failure path 表达。

第5章验证清单

  1. polymorphic base 明确 virtual destructor;pure virtual destructor 仍提供 definition。
  2. 每个 virtual operation 检查 const/ref qualifier 与 override 是否精确匹配。
  3. 按 virtual bases、direct bases、vptr phase、members、body 推导 construction。
  4. 构造期 virtual call 只使用 active class,不读取未建立 derived invariant。
  5. copy construction 与 assignment 分开,vptr 不当业务 bytes 复制。
  6. 需要动态复制时定义 clone/value wrapper,避免 slicing。
  7. object efficiency 分别测 lifetime、dispatch、adjustment、size 和 locality。
  8. destructor body、members、bases、virtual bases 逆序撤场;failure 只 unwind 已构造部分。

小结

  • pure virtual destructor 让 base abstract,但仍必须提供 teardown definition
  • virtual specification 与 const qualifier 定义 override contract 和对象表示需求
  • 无继承 construction 也要先建立 members/virtual state,再运行 body
  • 有继承 construction 由 most-derived virtual bases 开始,逐层建立 direct bases、vptr、members
  • vptr initialization 随 active construction/destruction phase 变化,阻止调用未就绪 derived state
  • copy construction 建新 lifetime,assignment 修改现有 state,二者都不能 raw-copy polymorphic identity
  • object efficiency 来自所选语义,不来自 class/struct 标签
  • destruction 逆序撤销已建立 subobjects,partial construction 只 unwind 成功部分

名词解释

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

pure virtual destructor

使 base abstract 但仍有定义的虚析构函数。

presence of a virtual specification

以 virtual 声明动态覆写契约。

const within a virtual specification

作为 override signature 一部分的 const。

object construction without inheritance

建立单一 class 的 members 与 representation。

preparing for inheritance

为 base role 重审 lifetime、copy 与 extension contract。

object construction under inheritance

从 virtual/direct bases 到 derived body 的构造序列。

construction under virtual inheritance

most-derived 路径构造一次 shared virtual base。

semantics of the vptr initialization

按 active subobject phase 建立 virtual state。

object copy semantics

构造/赋值时复制 subobjects 而保留 target identity。

object efficiency

可测的对象表示、lifetime 与 dispatch 成本。

semantics of destruction

body 后按逆序撤销 members 与 bases。

partial construction unwinding

只销毁异常前成功构造的 subobjects。

练习

  1. 问题 1:修正 abstract base 声明。 给一个缺少 virtual destructor、const override 不匹配、constructor 调用 virtual hook 的 hierarchy,逐项说明错误与修复。
  1. 问题 2:展开 diamond object 的构造与析构。 两个中间 bases 虚继承 Root,Leaf 同时继承它们;列出 virtual base、direct bases、members、vptr phases、bodies 与 reverse teardown。
  1. 问题 3:审计 polymorphic copy 与性能。 找出 memcpy、Base 按值参数、非虚析构和频繁 virtual-base access 四类问题,并给 correctness 与 measurement 方案。

讨论

评论区加载中…