C++ 拷贝控制 · 交互实验
从资源语义到异常安全:把“怎么复制”设计清楚
先决定副本关系,再观察 copy-and-swap 的事务式提交,最后让 vector 用类型能力选择迁移策略。每一步都对应可观察的程序承诺。
副本应该与原对象是什么关系?
请选择最接近的公开承诺。关键不在于“成员里有没有指针”,而在于用户复制后应该观察到什么。
即时挑战:设备会话应该怎样复制?
一个对象独占连接句柄;出现两个负责人会导致重复关闭。最合理的策略是:
copy-and-swap:准备成功后才提交
左值 target = [7],右值 source = [1,2,3]。点击单步;也可以在准备阶段注入分配异常。
target(左值)
replacement(临时对象)
1 · 准备
构造临时副本
申请独立数组并复制 source。唯一可能失败的工作先发生。
2 · 提交
swap
用不抛异常的指针和长度交换,一次性提交新状态。
3 · 清理
临时对象析构
replacement 接走旧资源,离开作用域时负责释放。
等待操作。左值仍拥有 old@A → [7]。
即时挑战:异常发生后,哪项保证成立?
noexcept 如何改变 vector 的迁移选择
实验先读取实际旧容量 C,再请求 reserve(C + 1)。新请求严格大于旧容量,因此确定触发重新分配;这里不假定首次容量是 1。
vector<Item> 的旧存储
size = 3,old_capacity = 4
ABC
const auto old_capacity = values.capacity();
values.reserve(old_capacity + 1);
当前预测:移动可能抛、且类型可复制,因此容器可复制旧元素来维护强异常保证。
复制 0
移动 0
请求容量 5
即时挑战:不可复制 + 可能抛的移动
这时容器为何仍可能尝试移动?
错误的所有权关系应通过不变式、诊断或 AddressSanitizer 来定位;不要依赖重复释放时偶然出现的输出。