面试题答案
一键面试C++中异常安全的三种保证级别
- 基本保证(Basic Guarantee):
- 解释:当异常抛出时,程序的状态仍然保持有效,没有资源泄漏,所有对象都处于一个合理的可析构状态。但是,操作可能部分完成,对象的状态可能已改变,但仍然是有效的。例如,一个容器类在插入元素时抛出异常,容器仍然可用,只是插入操作未完全成功。
- 强保证(Strong Guarantee):
- 解释:如果一个操作抛出异常,程序状态不会改变,就像该操作从未发生过一样。所有对象都保持操作前的状态,没有资源泄漏。对于复杂的操作,如涉及多个步骤的事务性操作,要么全部成功,要么全部回滚。
- 不抛出保证(No - Throw Guarantee):
- 解释:函数承诺永远不会抛出异常。这通常适用于一些关键的底层操作,如内存释放、析构函数等,这些操作必须保证在任何情况下都能顺利完成,否则可能导致严重的程序错误。
确保类满足强异常安全保证的示例
#include <memory>
#include <vector>
#include <iostream>
class MyComplexClass {
private:
std::unique_ptr<int[]> data;
int size;
public:
MyComplexClass(int n) : size(n) {
data = std::make_unique<int[]>(n);
// 初始化数据,这里简单设为0
for (int i = 0; i < n; ++i) {
data[i] = 0;
}
}
// 赋值运算符重载,实现强异常安全保证
MyComplexClass& operator=(const MyComplexClass& other) {
if (this == &other) {
return *this;
}
// 使用临时对象来存储新数据,这样如果构造临时对象抛出异常,原对象不受影响
std::unique_ptr<int[]> tempData = std::make_unique<int[]>(other.size);
for (int i = 0; i < other.size; ++i) {
tempData[i] = other.data[i];
}
// 构造成功后,交换数据
data.swap(tempData);
size = other.size;
return *this;
}
// 析构函数,保证不抛出异常
~MyComplexClass() noexcept {
// 自动释放unique_ptr管理的内存
}
};
解释:
- 构造函数:使用
std::make_unique<int[]>(n)
分配内存,若分配失败会抛出std::bad_alloc
异常。但如果分配成功,后续初始化数据的循环不会抛出异常(这里简单初始化,实际情况可能不同),所以整体构造函数满足强异常安全保证,若构造失败,对象不会处于无效状态。 - 赋值运算符重载:
- 首先检查自赋值,避免不必要操作。
- 使用临时
std::unique_ptr<int[]>
来存储新数据,这样在构造临时对象过程中若抛出异常(如内存分配失败),原对象data
和size
不受影响,满足强异常安全保证。 - 构造临时对象成功后,通过
swap
操作交换数据,swap
通常是异常安全的(std::unique_ptr
的swap
是不抛出异常的)。
- 析构函数:声明为
noexcept
,保证在析构时不会抛出异常,避免异常从析构函数中抛出导致程序终止。std::unique_ptr
会自动释放其管理的内存,不会有资源泄漏问题。