关于 swap() 操作的异常安全 - 这有什么问题?
Exception safety regarding swap() operation - what's so wrong with that?
我一直在读swap()
操作,例如:
template<class T>
void swap (T &a, T &b)
{
T temp (a);
a = b;
b = temp;
}
当我们处理异常安全时是有问题的。
它怎么了?此外,我们如何解决它?
在一般实现中,假设T
的任何操作都可以throw
,则无法提供强异常保证,这意味着在异常事件上保持操作前的状态。即使T
上的每个操作都提供了强大的异常保证:
template<class T>
void swap (T &a, T &b)
{
T temp (a); // [1]
a = b; // [2]
b = temp; // [3]
}
如果[1]抛出,则输入保持不变,这很好。如果[2]抛出,并且假设有强大的异常保证,那么这些值仍然没有受到影响,这很好。但是,如果是[3]抛出,则a
已经被修改,这意味着在异常向上传播到堆栈之后,调用者将留下一个既不是原始状态也不是最终状态的状态。
编辑:此外,我们如何解决它
没有通用的解决方案。但在大多数情况下,您可以为您的类型提供异常安全的swap
操作。考虑一个vector<T>
,它通过使用三个指针(begin
、end
、capacity
)在内部管理它的状态。上面的一般swap
可以抛出(分配失败,内部T
的构造函数可能抛出…),但提供一个无抛出的swap
实现是微不足道的:
template <typename T>
class vector {
T *b,*e,*c;
public:
void swap( vector<T>& rhs ) {
using std::swap;
swap( b, rhs.b );
swap( e, rhs.e );
swap( c, rhs.c );
}
//...
};
template <typename T>
void swap( vector<T>& lhs, vector<T>& rhs ) {
lhs.swap(rhs);
}
因为复制指针不能抛出,所以上面的swap
提供了无抛出保证,如果您总是按照上面的模式实现swap
(using std::swap;
后面是对swap
的非限定调用),ADL会认为它比std::swap
匹配得更好。
Daniel当然是对的。
但您不应该忘记,当涉及到异常安全时,交换通常被认为是解决方案的一部分("复制和交换习语"),而不是问题。剩下的问题是,如果你采用这个习惯用法,你必须确保swap可能调用的复制构造函数不会抛出异常,这是Daniel解释的原因。
请参阅:什么是复制和交换习语?
已经解释了异常的来源
但是,您可以通过将参数强制转换为unsigned byte*
并逐字节交换到sizeof(T)
来避免异常。注意,这不会在对象上调用任何构造函数或析构函数,这可能会在发生自引用时违反某些先决条件(当a
在交换后指向b
时,a
将指向a
)
D标准库中的交换就是这样做的(在检查自引用之后)
- 警告处理为错误这里有什么问题
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 当我尝试添加 2 个大字符串时,我无法弄清楚出了什么问题
- 违反const正确性:我应该现实地期待什么问题
- 这个带有模板<类 Vector 的C++代码片段有什么问题>
- 我的逻辑反转字符串中的元音有什么问题?
- 需要以下代码的帮助,下面的代码有什么问题
- 常量公共成员有什么问题?
- 以下代码中的函数模板有什么问题?
- 这个返回元素位置的基于循环的函数有什么问题?
- creat_list2功能有什么问题?
- 格式说明符C++有什么问题
- 任何人都可以告诉我我的 C++ 代码出了什么问题?
- 从 argv[1] 转换为字符 * 字符串后有什么问题?
- 我的堆栈和库存清单程序的结构有什么问题?
- 此工厂功能有什么问题?
- 以下 C++ 代码有什么问题?
- 数组为此合并排序函数提供了正确的输出,但向量给出了不正确的输出.出了什么问题?
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 它解决了什么问题,对于非真空初始化,生命周期在初始化之前就开始了