当前位置: 首页 > backend >正文

Effective C++ 条款25:考虑写出一个不抛异常的swap函数

Effective C++ 条款25:考虑写出一个不抛异常的swap函数


核心思想为自定义类型提供高效的、不抛异常的swap特化实现,优先通过成员swap实现,再通过非成员函数调用,最后全特化std::swap,确保类型在泛型代码中高效交换。

⚠️ 1. 默认swap的性能陷阱

深拷贝性能问题

class ResourceIntensive {
public:ResourceIntensive(const ResourceIntensive& other): data_(new int[other.size_]), size_(other.size_) {std::copy(other.data_, other.data_ + size_, data_);}// 默认swap通过拷贝构造实现 → 深拷贝
private:int* data_;size_t size_;
};// 交换成本:O(n)拷贝
std::swap(obj1, obj2);  // 调用3次拷贝构造/赋值

异常安全问题

class Connection {
public:~Connection() { release(); }Connection(const Connection&) = delete;  // 不可拷贝// 默认swap无法工作
private:void release();  // 释放资源Handle handle_;  // 系统资源句柄
};

🚨 2. 解决方案:定制swap实现

三步定制法

// 步骤1:类内提供高效swap成员函数
class Widget {
public:void swap(Widget& other) noexcept {using std::swap;               // 为内置类型准备swap(dataPtr, other.dataPtr);  // 仅交换指针swap(size, other.size);        // 交换辅助数据}private:Data* dataPtr;  // 指向大型数据size_t size;
};// 步骤2:非成员swap函数调用成员swap
void swap(Widget& a, Widget& b) noexcept {a.swap(b);  // 调用高效成员函数
}// 步骤3:全特化std::swap
namespace std {template<> void swap<Widget>(Widget& a, Widget& b) noexcept {a.swap(b);}
}

现代C++改进

// C++11后推荐仅提供非成员swap(避免特化std)
namespace WidgetStuff {class Widget { /* ... 成员swap ... */ };void swap(Widget& a, Widget& b) noexcept {a.swap(b);}
}// 通用调用方式
template<typename T>
void process(T& a, T& b) {using std::swap;  // 引入std::swapswap(a, b);       // 通过ADL查找最佳swap
}

⚖️ 3. 关键原则与实现策略
实现方式适用场景优点缺点
成员swap+非成员swap自定义命名空间类型标准兼容,ADL支持需额外命名空间
全特化std::swap标准库类型扩展被std算法直接使用禁止添加新模板到std
偏特化std::swap类模板(C++98/03)支持泛型C++11起非法
移动语义swapC++11支持移动的类型自动高效(默认已优化)需类型支持移动操作

Pimpl惯用法优化

// 前置声明
class WidgetImpl;class Widget {
public:void swap(Widget& other) noexcept {std::swap(pImpl, other.pImpl);  // 仅交换指针}private:WidgetImpl* pImpl;  // 指向实现类
};// 非成员swap
void swap(Widget& a, Widget& b) noexcept { a.swap(b); }

类模板swap实现

// 类模板需在自身命名空间提供swap
namespace Custom {template<typename T>class Array { public:void swap(Array& other) noexcept {std::swap(data_, other.data_);std::swap(size_, other.size_);}private:T* data_;size_t size_;};// 非成员swap模板template<typename T>void swap(Array<T>& a, Array<T>& b) noexcept {a.swap(b);}
}// 使用示例
Custom::Array<int> arr1, arr2;
using std::swap;
swap(arr1, arr2);  // 调用Custom::swap

💡 关键原则总结

  1. 高效交换原则
    • 资源管理类必须提供高效swap
    • 仅交换指针/句柄,而非完整数据
  2. 异常安全保证
    • swap函数必须声明noexcept
    • 禁止在swap中抛出异常
  3. 定制实现三步曲
    • 成员swap:执行实际交换操作
    • 非成员swap:调用成员函数
    • (可选) 全特化std::swap
  4. 泛型编程兼容
    • 使用using std::swap; + 无限定swap调用
    • 依赖ADL查找最佳swap实现

危险使用重现

class Bitmap { /* 大数据 */ };
class Widget {
private:Bitmap* pb;  // 指向大型数据
};// 使用默认swap
Widget w1, w2;
std::swap(w1, w2);  // 深拷贝三次Bitmap数据 → 性能灾难

安全优化方案

class Widget {
public:void swap(Widget& other) noexcept {std::swap(pb, other.pb);  // 仅交换指针}
private:Bitmap* pb;
};// 非成员swap(同一命名空间)
void swap(Widget& a, Widget& b) noexcept {a.swap(b);
}// 使用示例
Widget w1, w2;// 方式1:直接调用(明确类型)
swap(w1, w2);  // 方式2:泛型代码调用
template<typename T>
void processWidgets(T& a, T& b) {using std::swap;  // 必要声明swap(a, b);       // 自动选择最佳swap
}// 方式3:STL算法使用
std::vector<Widget> widgets;
std::swap(widgets[0], widgets[1]);  // 调用高效swap
http://www.xdnf.cn/news/17098.html

相关文章:

  • 每日任务day0806:小小勇者成长记之收获日
  • NAT转化
  • Knife4j:实时接口文档的利器
  • PyTorch生成式人工智能(26)——使用PyTorch构建GPT模型
  • 学习 Android (十六) 学习 OpenCV (一)
  • 基于PHP的论坛社交网站系统开发与设计
  • Spring Boot 参数校验全指南
  • [滑动窗口]904. 水果成篮
  • 基于PHP的快递管理系统的设计与实现
  • 【动态规划 | 01背包】动态规划经典:01背包问题详解
  • C++线程中 detach() 和 join() 的区别
  • FPGA学习笔记——VGA彩条显示
  • AVDTP Media Packet 传输全流程解析:从 SDP 到连接终止
  • 从 0 到 1 创建 InfluxDB 3 表:标签、字段、命名规范一篇讲透
  • X86-ubuntu22.04远程桌面只有1/4无法正常操作
  • C++实现线程池(5)计划线程池
  • python学智能算法(三十四)|SVM-KKT条件回顾
  • KGF75N65KDF-U/H KEC 集成电路IC 工业电机驱动
  • 加密视频流程教程分享
  • 移动商城平台适配:ZKmall开源商城鸿蒙 / 小程序端开发要点
  • Mark两个Redis for windows
  • 【概念学习】深度学习有何不同
  • 当前主流且经过市场验证的开源 BI 系统推荐
  • 【多模态微调】【从0开始】Qwen2-VL + llamafactory
  • C语言高级编程技巧与最佳实践
  • 面向流程和产品的安全档案论证方法
  • Jenkinsfile各指令详解
  • 脑洞大开——AI流程图如何改变思维?
  • C++ - 仿 RabbitMQ 实现消息队列--服务器模块实现
  • 【计算机网络 | 第3篇】物理媒介