copy-and-swap语义

std::swap 是 C++ 标准库中的一个函数模板,位于 <algorithm> 头文件中(C++11 之前在 <utility> 中)。它的主要作用是交换两个对象的值。下面为你详细介绍它的用法、实现原理和示例。

copy-and-Swap 可以解决拷贝赋值函数中自赋值以及申请新内存可能导致异常的问题:

  1. 利用拷贝构造函数:
    1. 通过值传递参数(比如String s),调用拷贝构造函数创建一个临时对象。
    2. 如果拷贝构造函数抛出异常,不会影响当前对象的状态。
  1. 交换资源:
    1. 使用 swap 函数将临时对象的资源与当前对象的资源交换。
    2. swap 操作是 noexcept 的,不会抛出异常。
  1. 自动释放旧资源:
    1. 临时对象的析构函数会自动释放旧资源。

那什么是copy-and-swap?直接看代码:

arduino 复制代码
class String {
char *str;
public:
String &operator=(String s) // the pass-by-value parameter servrs as a temporary
{
    s.swap(*this); // Non-throwing swap
    return *this;
}
void swap(String &s) noexcept {
    std::swap(this->str, s.str);
}
};

当然也可以:

arduino 复制代码
class String {
char *str;
public:
String &operator=(const String &s) {
    if (this != &s)
    {
        String(s).swap(*this); // Copy-constructor and non-throeing swap
    }

    // Old resources are released with the destruction of the temporrary abore
    return *this;
}
void swap(String *s) noexcept
{
    std::swap(this->str, s.str);
}
};

更优雅的写法是:

arduino 复制代码
String &operator=(String s) {
    s.swap(*this);
    return *this;
}
void swap(String *s) noexcept
{
    std::swap(this->str, s.str);
}

这种方式不仅方便,而且也做了进一步优化:

  • 如果参数原来是个左值,会直接做拷贝,而其实这次拷贝无论在哪都无法避免
  • 如果参数原来是右值或者临时对象,就节省了一次拷贝和析构,这也叫copy elision,这种operator也就统一赋值运算符

In C++11, such an assignment operator is known as a unifying assignment operator because it eliminates the need to write two different assignment operators: copy-assignment and move-assignment. As long as a class has a move-constructor, a C++11 compiler will always use it to optimize creation of a copy from another temporary (rvalue). Copy-elision is a comparable optimization in non-C++11 compilers to achieve the same effect.

优点

  • 异常安全:如果拷贝构造函数抛出异常,当前对象的状态不会被破坏。
  • 代码简洁:不需要手动检查自赋值,也不需要显式释放资源。
  • 支持移动语义(C++11 及以上):如果传递的是右值(临时对象),编译器会自动优化,调用移动构造函数而不是拷贝构造函数。
  • 统一赋值运算符:一个赋值运算符同时支持拷贝赋值和移动赋值,减少了代码重复。

总结

  1. copy-and-Swap 的核心:通过值传递参数调用拷贝构造函数,利用 swap 函数交换资源,确保异常安全。
  2. 适用场景:适用于需要动态管理资源的类(如字符串、容器等)。
  3. 优点:异常安全、代码简洁、支持移动语义。
  4. 注意事项 :确保 swap 函数是 noexcept 的,也要确保拷贝构造函数和析构函数正确实现。
相关推荐
David猪大卫13 小时前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
HPtiger13 小时前
C++ 入门笔记:从 C 到 C++ 的核心过渡(命名空间、输入输出、缺省参数、重载、引用、内联)
c语言·c++·学习
努力努力再努力wz16 小时前
【Lua 进阶系列】从 C++ 对象到 Lua 变量:一文理解 userdata、Lua 栈与元表绑定机制
c++·junit·lua
慧都产品智能化16 小时前
Qt 表格升级攻略:复用已有编辑器,减少重复开发
c++·qt·编辑器·qt6.3
ShineWinsu17 小时前
对于Qt:QTableWidget常见属性以及QTableWidgetItem类的解析
开发语言·c++·qt·gui·客户端·表格·qtablewidget
ShineWinsu18 小时前
对于Qt:QListWidget常见属性以及QListWidgetItem类的解析
开发语言·c++·qt·客户端·列表·qlistwidget·qlistwidgetitem
坤坤子吖18 小时前
Linux网络编程:自定义应用层协议与 JSON 序列化
linux·服务器·网络·c++·笔记·json
沫璃染墨19 小时前
从零入门计算机网络系列(十八):TCP连接管理详解——从三次握手与四次挥手到TIME_WAIT机制
运维·服务器·网络·c++·网络协议·tcp/ip·tcp
自信1504130575921 小时前
重生之从0开始学习c++之——vector-中篇
c++·学习·算法
ShineWinsu1 天前
对于Qt:QDial常见属性以及信号的解析
开发语言·c++·qt·gui·客户端·旋钮·qdial