13.1 概述
检查错误: callee
/ | 无法处理 throw
/ | 希望处理 catch
1 分离 | 传递
\ |
\ |/
处理错误: caller
2 抛出异常
栈展开
异常 从 `抛出点` 开始, `向上` 传递到 处理程序
空 异常说明 throw() 作用等价于 noexcept, 即 若抛出异常, 则 程序终止
void f(int) throw();
13.2 vector 的实现
异常安全代码的 范例
1 简单 vector
(1) data
1 个 分配器 alloc
3个指针: elem / space / last 分别指向
分配空间 start
可扩展空间 start == 元素序列 end
分配空间 end
(2) ctor
template<class T, class A = allocator<T> >
...
vector(size_type n, const T& val = T(), const A& a = A() );
[1] 分配内存 elem = alloc.allocate(n)
[2] 另 2 个指针 赋值
[3] 逐元素 `构造 新值的 copy`
for(T* p)
alloc.construct(p, val);
2处可能引发 异常
[1] allocate() 抛出异常
[2] T `copy ctor 无法 copy 新值`
construct(p, val) ...
(3) 带 异常处理 的 ctor
try{}
catch(...)
{
逐元素销毁 // for (Iter q = beg, ...)
// (&*q)->~T();
重新抛出 // throw;
}
}
经验: try
书写 异常安全代码
的 难度 远远超出人们的预期
(1) vector 实现 没用 try 块 ( 除了 隐藏在 原始_copy() 中那个 之外 )
|
| 替换为 更简单有效的方法
|/
(2) RAII
+ 小心 设计操作顺序
以 确保, 抛出异常
时,
vector 不被改变
( push_back() ) 或 至少处于 (我们认为的) 有效状态
( copy assignent )
2. 引入 辅助 struct (vector_base)
以 directly 处理 T 类型的 memory
好处: 简化代码 + 降低 忘记释放内存的 可能性
(1) ctor
alloc.allocate(size) + 3Ptrs 指向相应位置
dtor
alloc.deallocate(elem, last - elem)
(2) copy delete
那 vector copy 如何实现 ?
vector copy ctor/assignement
(3) move
[1] move ctor
lhs
ptrs/alloc 初始化(copy) 为 rhs's ptrs/alloc
rhs
ptrs 置 nullptr
[2] move assignment
lhs 已存在 => swap(*this, rhs);
直接 move vector_base
, 即 move 任意已分配 memory 的 ownership
: 优化效率/性能
3 vector 实现
template <class T, class A = alloctor<T> >
class vector
{
private:
// 只有1个 成员数据
vector_base<T, A> vb;
// dtor 的 辅助(销毁元素的)函数
destory_elements();
public:
// (1) 6 大函数
explicit vector(size_type n, const T& val = T(), const A& a = A() );
~vector() { destory_elements(); }
// (2) 尺寸/容量函数
size_type size() const { return vb.space - vb.elem; }
size_type capacity() const { return vb.last - vb.elem; };
// (3) 改变尺寸 的 4 大函数
// 10. 第 10 大函数
void clear() { resize(0); }
};
(1) dtor 辅助函数
逐成员销毁: 显式调 T 的 dtor
note
没有哪种方法 能 切实有效地 防止 dtor 抛出异常
(2) ctor
调 成员 vb 的 ctor: vb{a, n}
原始_fill();
(3) copy ctor
调 成员 vb 的 ctor: vb{v.alloc, v.size() }
uninitialized_copy() from rhsVecMemory
(4) 移动 ctor
对 `成员 vb` 用 `std::move` 转移 所有权
(5) 移动 赋值
销毁 所有元素
对 `vector` 用 `swap` 转移 所有权
(6) copy assignement
version1 `强保障` 缺点: 难维护
midVecBase
uninitialized_copy from rhsVecMemory : uninitialized_copy(rhs.begin(), rhs.end(), midVecBase.elem)
move memory's ownership 到 dstVec's vecBase : swap(vb, midVecBase)
scope 结束时, midVecBase 的 dtor 隐式 `释放 (被转移给的) 旧空间`
|
|
|/
version2 `强保障` 易维护, 与 version1 性能接近
midVec : 从 rhs copy ctor
move ownership : std::swap(*this, midVec)
|
| 效率高于
| 元素 (copy) 赋值 —— —— ——> 析构 (1 个元素) + 构造 (1个元素 )
|/
version3 `基本保障` 复杂度高
if dstVecCapacity < rhsVecSize
version2
else // >=
if dstVecSize >= rhsVecSize
copy() rhsVecSize
多余 elem: 逐个销毁
p->~T();
else // <
copy() dstVecSize
额外 elem:
uninitialized_copy() from rhsVec
基本保障: 抛出异常时, `正被 copy 的 elem 可能 既非旧值 也非新值` -> 视 vector 为 有效状态
————————————————————————————————————————————————————————————————————————————————
copy 赋值 vs. 移动 赋值
————————————————————————————————————————————————————————————————————————————————
copy 赋值 | const 引用形参 | copy 后 右对象(左值 / 右值) 可能 不会被 立即销毁
移动 赋值 | 右值 引用形参 | 移动 后 右对象(右值) 会被 立即销毁
————————————————————————————————————————————————————————————————————————————————
(7) reserve()
if newCapacity 更小
doNothing, return
else
对 vector 扩展 capacity = 分配 newSpace + 元素 移入
template<class T, class A>
void vector<T, A>::reserve(size_type newCapacity);
1) version1
[1] 建 newVector
[2] copy() 旧 elem 到 newSpace
[3] move ownership
[4] scope 结束: newVector dtor 隐式 `销毁 旧值 + 释放 旧空间`
2) version2 : `基本保障: 要 避免 move 操作 抛出异常`
[1] 建 newVecBase
[2] 自定义 uninitialized_move() 算法, 在 各 newElemPos 用 oldElemValue `移动 构造` newElem
[3] move memory's ownership: swap(newVecBase, vb)
[4] scope 结束: newVecBase 的 dtor 隐式 仅 `释放 旧空间`
template<typename In, typename Out>
Out uninitialized_move(In b, In e, Out oo)
{
for(; b != e; ++b, ++oo)
{
new (static_cast<void*>(&*oo) ) T { std::move(*b) };
b->~T(); // `销毁/析构 旧空间中 元素`
}
}
reserve 一旦移动构造了元素, vector 迭代器 可能 失效
(8) resize()
template<class T, class A>
void vector<T, A>::resize(size_type newSize, const T& x);
[1] 先调 reverse(newsize)
[2]
if newSize 更大
uninitialized_fill(): 构造 额外 elem
else
destory(): 销毁 多余 elem
[3] 更新 space&last 指针: 均指向 memory newsize 尾
template<typename In>
void destory(In b, In e)
{
for(; b != e; ++b)
b->~T();
}
(9) push_back()
从 异常安全 角度看, push_back() `与 copy assignment 相似`
若 copy 构造 抛出异常, 仍保证了 vector 内容没变
原因:
copy 构造 后的 更新 spacePtrPos 没被执行
vector 因 reserve() 可能 扩容而 换了 space, 但 data/content 却不变
template<class T, class A>
void vector<T, A>::push_back(const T& x);
[1]
if(capacity() == size() ) // 若 满载
reserve(sz ? 2*sz : 8); // 扩capacity 1 倍 或 分配为 8 (若为 初始分配)
[2] 在 pushedPos 用指定值 `copy 构造` 新值
vb.alloc.construct(&vb.elem[size()], x)
[3] 更新 spacePtrPos
++vb.space;
325.jpg
326.jpg
327.jpg
328.jpg
329.jpg
330.jpg
331.jpg
332.jpg
333.jpg
334.jpg
网友评论