当前位置: 首页 > 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.dtcms.com/a/318036.html

相关文章:

  • linux 使用docker时开放的端口不受防火墙控制的解决方案
  • 医疗AI中GPU部署的“非对等全节点架构“方案分析(上)
  • AI领域的三箭齐发之夜 - genie3,gpt-oss, Opus 4.1
  • hyper-v常见问题解答(此文会持续更新)
  • DNS 服务器
  • 远程连接----ubuntu ,rocky 等Linux系统,WindTerm_2.7.0
  • 当前主流GPU全景讲解:架构、功能与应用方向
  • 一种简单的3dnr去噪算法介绍
  • 北京-4年功能测试2年空窗-报培训班学测开-第六十九天-投简历第一天-从兴奋到害怕
  • Unity工具—Inspector面板增加Rect Transform组件上下左右移动的工具
  • linux IO介绍
  • Android系统性能分析利器:深入解析Tracing框架
  • Dify+Nginx反向代理:80端口冲突的优雅解决方案
  • ICCV 2025 | 视频生成迈入“多段一致”新时代!TokensGen用“压缩Token”玩转长视频生成
  • Mysql如何迁移数据库数据
  • mysql数据库基础操作
  • 每日任务day0806:小小勇者成长记之收获日
  • 在 Visual Studio Code 中免费使用 Gemini 2.5 Pro API
  • 滴滴招java开发
  • 利用DeepSeek改写并增强测试Duckdb和sqlite的不同插入方法性能
  • 虚幻GAS底层原理解剖四 (TAG)
  • Boosting 知识点整理:调参技巧、可解释性工具与实战案例
  • [Oracle] NVL()函数
  • 【概念学习】深度学习有何不同
  • 220降5V,30mA电流,墙壁开关和调光器应用场景WD5201
  • 【秋招笔试】2025.08.02-OPPO秋招第二套-第一题
  • Win10还未停更,对标iPad的教育版Win11也宣布停更了
  • Python爬虫 urllib 模块详细教程:零基础小白的入门指南
  • Pytest项目_day05(requests加入headers)
  • 项目中MySQL遇到的索引失效的问题