C++异常安全类设计:拷贝赋值与继承
立即解锁
发布时间: 2025-08-21 01:43:10 阅读量: 4 订阅数: 8 


C++编程深度解析与实践
# C++ 异常安全类设计:拷贝赋值与继承
## 1. 异常安全的三个常见级别
异常安全有三个常见级别,它们各自有着不同的特点和重要性,如下表所示:
| 级别 | 解释 | 重要性 |
| --- | --- | --- |
| 基本保证 | 如果抛出异常,不会泄漏资源,对象保持可销毁和可用状态,但状态不一定可预测。 | 适用于调用代码能够处理已对对象状态产生更改的失败操作的情况,是最弱的可用异常安全级别。 |
| 强保证 | 如果抛出异常,程序状态保持不变。此级别始终意味着提交或回滚语义,包括操作失败时容器的引用或迭代器不会失效。 | 提供了更严格的异常安全保证,确保操作的原子性。 |
| 无抛出保证 | 函数在任何情况下都不会抛出异常。 | 某些函数必须提供此保证,否则可能无法实现强保证甚至基本保证,例如析构函数和内存释放函数。 |
## 2. 强异常安全拷贝赋值的规范形式
强异常安全拷贝赋值的规范形式包含两个步骤:
1. 提供一个不抛出异常的 `Swap()` 函数,用于交换两个对象的内部状态:
```cpp
void T::Swap( T& other ) /* throw() */
{
// ...swap the guts of *this and other...
}
```
2. 使用“创建临时对象并交换”的惯用法实现 `operator=()`:
```cpp
T& T::operator=( const T& other )
{
T temp( other ); // do all the work off to the side
Swap( temp ); // then "commit" the work using
return *this; // nonthrowing operations only
}
```
## 3. Cargill Widget 示例分析
考虑以下 `Widget` 类:
```cpp
class Widget
{
public:
Widget& operator=( const Widget& ); // ???
// ...
private:
T1 t1_;
T2 t2_;
};
```
假设任何 `T1` 或 `T2` 操作都可能抛出异常。在不改变类结构的情况下,无法编写一个强异常安全的 `Widget::operator=( const Widget& )`。原因如下:
- 若尝试更改 `t1_` 时抛出异常,`t1_` 必须保持不变。这意味着 `Widget::operator=()` 的异常安全依赖于 `T1` 提供的异常安全保证,即 `T1::operator=()` 要么成功,要么不改变目标对象,这接近要求 `T1::operator=` 提供强保证,对 `T2::operator=` 同理。
- 若尝试更改 `t1_` 成功,但尝试更改 `t2_` 时抛出异常,就进入了“半途”状态,通常无法回滚对 `t1_` 已做的更改。例如,若尝试重新分配 `t1_` 的原始值或其他合理值也失败,`Widget` 对象甚至无法保证恢复到保持其不变量的一致状态。
## 4. 通用技术:使用 Pimpl 惯用法
即使不改变 `Widget` 类的结构就无法使其 `operator=` 强异常安全,但可以使用以下简单转换来实现几乎强异常安全的赋值:通过指针而不是值来持有成员对象,最好使用 Pimpl 转换将所有成员对象放在一个指针后面。
```cpp
// Example 22-2: The general solution to
// Cargill's Widget Example
//
class Widget
{
public:
Widget(); // initializes pimpl_ with new WidgetImpl
~Widget(); // must be provided, because the implicit
// version causes usage problems
// (see Items 30 and 31)
Widget& operator=( const Widget& );
// ...
private:
class WidgetImpl;
auto_ptr<WidgetImpl> pimpl_;
// ... provide copy construction that
// works correctly, or suppress it ...
};
// Then, typically in a separate
// implementation file:
//
class Widget::WidgetImpl
{
public:
// ...
T1 t1_;
T2 t2_;
};
```
接下来可以轻松实现不抛出异常的 `Swap()` 函数,从而实现接近强保证的异常安全拷贝赋值:
```cpp
void Widget::Swap( Widget& other ) /* throw() */
{
auto_ptr<WidgetImpl> temp( pimpl_ );
pimpl_ = other.pimpl_;
other.pimpl_ = temp;
}
Widget& Widget::operator=( const Widget& other )
{
Widget temp( other ); // do all the work
```
0
0
复制全文
相关推荐









