c++++标准库的容器和算法提供了不同程度的异常安全保证,具体取决于使用方式。1. stl容器提供三种异常安全等级:强保证(如vector的push_back)、基本保证(如插入失败保持原有数据)和无异常保证(如swap、front)。2. 标准库算法本身不抛出异常,但若传入的函数对象可能抛出,则异常会直接传播,且部分完成的操作可能导致状态不一致。3. 异常安全编程建议包括:优先使用raii资源管理类、避免在容器操作中混杂复杂逻辑、使用swap技术进行安全替换。4. 判断操作是否安全需查阅文档或使用noexcept查询,c++11后很多操作已明确声明为noexcept。总之,编写异常安全代码需从设计阶段开始考虑“谁可能抛?谁负责恢复?”这两个核心问题。
C++标准库的算法和容器在异常处理方面有一套明确的规则,理解这些规则对写出健壮、可靠的代码非常重要。简单来说:标准库组件会提供不同程度的异常安全保证,但具体行为取决于你如何使用它们。
下面从几个实际关心的角度来展开:
1. STL 容器的异常安全等级
STL 容器(如
vector
,
,
list
等)提供的异常安全保证大致可以分为三种等级:
立即学习“C++免费学习笔记(深入)”;
- 强保证(Strong guarantee):操作要么完全成功,要么不改变状态。例如,
vector
的
push_back
在内存足够时通常提供强保证。
- 基本保证(Basic guarantee):操作失败后,对象仍处于有效但不确定的状态。比如,在插入元素导致重新分配内存失败时,原有数据保持完整。
- 无异常保证(Nothrow guarantee):操作不会抛出异常。例如,某些交换操作(如
swap
)或访问已有元素的操作(如
front()
、
back()
)通常是 nothrow。
需要注意的是,这些保证依赖于你使用的类型是否也满足相应条件。如果你放入容器的对象构造函数可能抛出异常,那么整个操作的行为也会受到影响。
2. 标准库算法与异常传播
大多数标准库算法(如
std::sort
,
std::copy
,
std::transform
)本身不会抛出异常,除非你传入的函数对象(如比较器、谓词等)可能会抛出。
举个例子:
std::sort(v.begin(), v.end(), may_throw_comparator);
如果
may_throw_comparator
抛出了异常,那这个
std::sort
就会直接把异常“扔出来”,算法不会做任何异常捕获处理。
所以:
- 如果你希望算法调用是异常安全的,要确保你传入的所有回调函数也是异常安全的。
- 某些算法在部分完成工作后如果抛出异常,会导致中间状态无法回滚,这可能会破坏程序逻辑。
3. 异常安全编程技巧
为了写出更安全的代码,你可以遵循一些实用建议:
- 优先使用 RaiI 资源管理类:比如
unique_ptr
、
lock_guard
,这样即使抛出异常,资源也能自动释放。
- 避免在容器操作中混杂复杂逻辑:比如在
vector::resize()
或
map::insert()
中触发了自定义类型的构造/析构异常,容易让状态变得难以控制。
- 使用 swap 技术进行安全替换:先在一个临时对象上操作,再通过
swap
替换原对象,这样即使前面步骤失败也不会影响原数据。
例如:
std::vector<int> temp = get_data(); // 可能失败 data.swap(temp); // swap 是 nothrow 的
4. 如何判断某个操作是否安全?
这个问题其实没有统一答案,因为 C++ 标准并没有对所有情况都强制规定。你需要查阅文档或者标准说明。
常见的做法包括:
另外,C++11 后很多容器操作都提供了
noexcept
版本声明,比如:
bool empty() const noexcept;
这类函数可以放心在异常敏感区域使用。
基本上就这些。异常安全不是一蹴而就的事情,它需要你在设计阶段就有意识地考虑每一步操作的后果。虽然看起来有点复杂,但只要抓住“谁可能抛?谁负责恢复?”这两个核心问题,就能做出合理判断。