条款 25:考虑写出一个不抛异常的 swap 函数
核心思想
-
效率提升
- 当标准库的
std::swap
对你的类型效率不高时,应提供一个更高效的自定义swap
函数。
- 当标准库的
-
异常安全
- 确保自定义的
swap
函数不会抛出异常,以避免潜在的不确定性和资源泄漏。
- 确保自定义的
-
成员函数与非成员函数
- 提供一个
member swap
函数用于高效交换对象内部数据,同时提供一个non-member swap
函数调用前者。
- 提供一个
-
std::swap 的特化
- 对于用户定义类型,特化
std::swap
,以便使用更高效的交换逻辑。
- 对于用户定义类型,特化
-
最佳实践
- 使用
using std::swap
声明后,调用无命名空间修饰的swap
函数,确保选择合适的版本。
- 使用
示例代码
#include <algorithm> #include <iostream> class Widget { public: Widget(int data = 0) : data_(new int(data)) {} ~Widget() { delete data_; } Widget(const Widget& rhs) : data_(new int(*rhs.data_)) {} Widget& operator=(Widget rhs) { // 使用 copy-and-swap 技术 swap(rhs); return *this; } void swap(Widget& other) noexcept { // 成员 swap std::swap(data_, other.data_); } int getData() const { return *data_; } private: int* data_; }; // 非成员 swap 函数 void swap(Widget& lhs, Widget& rhs) noexcept { lhs.swap(rhs); // 调用成员 swap } namespace std { // 针对 Widget 特化 std::swap template <> void swap<Widget>(Widget& lhs, Widget& rhs) noexcept { lhs.swap(rhs); // 调用成员 swap } } int main() { Widget w1(10), w2(20); using std::swap; swap(w1, w2); // 调用非成员 swap 或 std::swap std::cout << "w1: " << w1.getData() << ", w2: " << w2.getData() << std::endl; return 0; }
说明
-
效率优势
std::swap
使用拷贝构造和赋值操作符,效率可能较低。通过提供member swap
函数,只需交换指针或其他轻量资源,效率显著提升。
-
异常安全
- 自定义的
swap
函数使用noexcept
关键字,明确保证不抛出异常。
- 自定义的
-
成员与非成员的关系
member swap
是具体的实现逻辑,non-member swap
是接口,增强了函数调用的灵活性。
-
特化
std::swap
- 为
std::swap
提供特化,允许 STL 容器中的对象使用自定义的高效交换逻辑。
- 为
-
using std::swap
的使用- 声明
using std::swap
后调用无命名空间修饰的swap
,让编译器优先选择用户定义的swap
。
- 声明
注意事项
-
不要在
std
命名空间中添加新内容- 仅允许对已有类型进行特化,切勿向
std
中引入新的类型或函数。
- 仅允许对已有类型进行特化,切勿向
-
防止异常传播
- 确保所有与
swap
相关的操作不会抛出异常,否则可能破坏程序的异常安全性。
- 确保所有与
结论
- 提供一个不抛异常的
swap
函数能显著提升性能和安全性。 - 使用
member swap
与non-member swap
配合设计,以增强灵活性和代码重用性。 - 特化
std::swap
以支持更高效的交换行为,同时遵循 C++ 标准的规范。