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

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.lryc.cn/news/612402.html

相关文章:

  • 【前端开发】五. ES5和ES6对比
  • imx6ull-驱动开发篇11——gpio子系统
  • 常用排序方法
  • 排序算法归并排序
  • 单变量单步时序预测:CNN-BiGRU卷积神经网络结合双向门控循环单元
  • 202506 电子学会青少年等级考试机器人六级实际操作真题
  • 基于RPR模型的机械臂手写器simulink建模与仿真
  • Qt Frameless Widget跨平台无边框窗口
  • 机器学习-KNN​​
  • 云平台托管集群:EKS、GKE、AKS 深度解析与选型指南-第四章
  • iT 运维: WindoWs CMD 命令表
  • Flutter开发 Image组件使用示例
  • <form> + <iframe> 方式下载大文件的机制
  • 基于Github Pages搭建个人博客站点:hexo环境搭建、本地预览与发布
  • 当前就业形势下,软件测试工程师职业发展与自我提升的必要性
  • AI 软件工程开发 AI 算法 架构与业务
  • [FBCTF2019]RCEService
  • Kafka-exporter采集参数调整方案
  • android NDK 报错日志解读和还原报错方法名
  • 数语科技登陆华为云商店,助力企业释放数据潜能
  • FPGA学习笔记——VGA彩条显示
  • 【软考系统架构设计师备考笔记4】 - 英语语法一篇通
  • 机器人定位装配的精度革命:迁移科技如何重塑工业生产价值
  • Spring Boot 参数校验全指南
  • 应急响应linux
  • vue3+element-plus,el-popover实现筛选弹窗的方法
  • ACL 2025 Oral|Evaluation Agent:面向视觉生成模型的高效可提示的评估框架
  • 【关于Java的对象】
  • C++、STL面试题总结(二)
  • Android14的QS面板的加载解析