第十三章 拷贝控制

Huan Lee Lv5

几个原则

  • 通常, 我们先考虑一个类是否需要自定义析构函数, 需要自定义析构函数的类通常也需要自定义拷贝和赋值操作(往往涉及指针)
  • 需要拷贝操作的类也需要赋值操作, 反之亦然

Untitled

tips

  • 显式定义默认拷贝or析构, 可以使用=default

  • 显式地阻止拷贝, 可以使用=delete (C++11)

    • 旧标准是通过将拷贝构造函数和拷贝赋值函数声明为private来阻止拷贝的(声明但是不定义)

    • 如果一个类有数据成员不能默认构造, 拷贝,赋值或销毁, 则对应的成员函数是被为删除的

    • 析构函数不能是删除的成员 (语法上没错, 但是会有一系列的问题)

  • 可以在成员函数或运算符的参数列表后(可能前面有一个const)加上&或&&作为引用限定符, 分别表面只能被左值或右值引用

拷贝

拷贝构造函数

  • 如果一个构造函数的第一个参数是自身类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数

    • 必须是引用类型(非引用类型的形参需要拷贝实参),且一般定义为const
  • 不管是否定义了其他拷贝构造函数,编译器总是会合成一个合成拷贝构造函数

    • 合成拷贝构造函数会将其参数的成员逐个拷贝到正在创建的对象中

Untitled

合成拷贝构造函数中成员的拷贝方式

Untitled

拷贝初始化的发生情况

拷贝赋值函数

  • 赋值运算本质就是一个operator=的函数

  • 通常返回一个指向其左侧运算对象的引用(与默认的operator=保持一致)

  • 拷贝赋值通常组合了拷贝构造函数(从右值拷贝数据)和析构函数(左值释放资源)

  • 应该在销毁左侧对象资源前, 先拷贝右值对象(否则自赋值操作时会出现拷贝已释放资源的情况)

    • 建议使用swap简化赋值计算, 这种方式既安全, 又方便, 但是可能增加运算成本(拷贝不需要拷贝的值)

Untitled

销毁(析构函数)

  • 每个类只有一个析构函数~ClassName()

    • 不接受参数,也没有返回值
  • 析构函数先执行函数体, 再按初始化顺序的逆序销毁成员

Untitled

析构函数被调用的情况

对象移动

右值引用C++11

  • 使用&&, 只能绑定一个将要被销毁的对象
  • 不能将一个右值引用绑定到另一个右值引用的变量上
  • 但是可以用std::move获取绑定左值的右值引用

Untitled

移动构造函数

  • 声明方式与拷贝构造函数类似, 但是第一个参数(该类类型的引用)必须是右值引用, 而其他参数必须有默认值
  • (使用标准库容器时)必须声明移动构造函数是noexcept的, 否则标准库在重新分配内存时会使用拷贝构造函数而不是移动构造函数.

Untitled

  • 只有当一个类没有自己版本的拷贝控制成员时, 且类的每个非static成员都可以移动时, 编译器才会为该类合成移动构造函数或移动赋值运算符.
  • 移动右值, 拷贝左值, 但是如果没有移动构造函数或移动赋值运算符, 右值也会被拷贝
  • Title: 第十三章 拷贝控制
  • Author: Huan Lee
  • Created at : 2023-08-20 14:13:30
  • Updated at : 2024-02-26 04:53:15
  • Link: https://www.mirthfullee.com/2023/08/20/notion-第十三章 拷贝控制-66056a82/
  • License: This work is licensed under CC BY-NC-SA 4.0.