c++ primer c++11 拷贝控制 移动构造 移动赋值 各种运算符重载 实例代码 在也不怕不会用啦 13-14

本文详细介绍了C++11中的拷贝构造函数、移动构造函数、拷贝赋值运算符和移动赋值运算符的使用,包括何时使用=delete和= default,以及析构函数的工作原理。此外,还讨论了右值引用和swap函数在类设计中的重要性,以及各种运算符重载的规则和最佳实践。
摘要由CSDN通过智能技术生成

13

拷贝构造函数
  • 拷贝构造函数第一个参数必须是引用类型 通常是一个const引用

  • 拷贝构造函数在很多情况下会被隐式使用 因此拷贝构造函数通常不应该是explicit的

  • 不管有没有自定义其他构造函数 编译器都会合成一个拷贝构造函数

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

    • 每个成员的类型决定了其如何拷贝 类类型用拷贝构造函数拷贝内置类型直接拷贝

    • 数组虽然不能直接拷贝 但合成拷贝构造函数会逐元素的拷贝一个数组类型的成员

  • 合成拷贝赋值运算符和拷贝构造函数同理

	struct A
	{
   
	    int arr[2];
	};

    A a;
    a.arr[0] = 1;
    a.arr[1] = 2;
    cout << a.arr[0] << a.arr[1] << endl;
    A b = a;
    cout << b.arr[0] << b.arr[1] << endl;

    // 数组不允许直接拷贝和赋值 下面的赋值会编译错误
    // int arr1[2];
    // int arr2[2] = arr1;
析构函数
  • 在析构函数中首先执行函数体 其次按照初始化顺序的逆序销毁成员
    这里需要特别注意 析构函数本身并不直接销毁成员 成员是在函数体执行之后的第二阶段隐式自动的销毁

  • 通常在析构中处理生存期分配的资源的释放

  • 销毁类类型时执行成员自己的析构函数 内置类型成员的销毁什么都不需要处理

  • 隐式的销毁一个内置指针类型成员不会delete其所指向的对象 但是智能指针可以 因为智能治指针时类类型

  • 析构时机

    • 变量离开作用域

    • 对象被销毁时 成员会被销毁

    • 容器(标准库或者数组)被销毁时 其元素也会被销毁

    • 动态分类的对象delete时被销毁

    • 临时对象 当创建他的完整表达式结束时被销毁

  • 合成拷贝构造函数也可以使用=default的方式

  • 定义删除的函数 将拷贝构造函数和拷贝赋值运算符定义为=delete可以阻止拷贝

  • =delete必须出现在函数第一次声明时
    因为=default直到编译器生成代码时才需要 但是=delete编译器需要直到其是delete的以便阻止试图使用它的操作

  • 析构函数不能是删除的成员 因为是删除的就无法销毁了
    如果定义了一个删除析构函数的类型 那么编译器将不允许定义该类型的变量或者创建该类的临时对象
    虽然可以new出来一个动态分配的对象但是却不能delete

  • 合成的拷贝控制成员可能是删除的情况有很多

    • 类的某个成员的析构函数是删除的或者不可访问 则类的合成析构函数定义为删除

    • 类的某个成员的拷贝构造函数是删除的或者不可访问或者类的某个成员的析构函数是删除的或者不可访问
      则类的合成拷贝构造函数定义为删除

    • 类的某个成员的拷贝赋值运算符是删除的或者不可访问或者类有一个const成员或者类有一个引用成员
      则类的合成拷贝赋值运算符定义为删除

    • 类的某个成员的析构函数是删除的或者不可访问
      或者类有一个引用成员且没有类内初始值
      或者类有一个const成员且没有类内初始值且其类型未显示定义默认构造函数
      则类的默认构造函数定义为删除

    • 还有更多情况(比如 移动构造部分 继承部分 union部分)导致定义为删除函数

  • 交换操作swap

    • 对于那些与重排元素顺序的算法一起使用的类 定义swap非常重要
      在这类算法中交换两个元素需要用到swap

    • 如果一个类定义了自己的swap 那么算法将使用类自定义版本 否则算法将使用标准库定义的swap

    • 应该尽量使用swap而不是std::swap 后者直接点名是std中的swap 而不会使用我们的自定义swap

    • 定义swap的操作经常使用swap来定义赋值运算符 这些运算符使用一种拷贝并交换的技术
      这种技术将左侧运算对象与右侧运算对象的一个副本进行交换
      书上说是这种技术自动处理了自身赋值的情况并且也是异常安全的P459
      其实就是将赋值运算符的参数改为非引用 然后参数的传递就会进行拷贝 然后函数体调用swap 大概就这意思

  • 拷贝赋值运算符通常需要执行拷贝构造函数和析构函数的工作 通常公共的工作应该放在private的工具函数中

对象移动与右值引用
  • 这里就比较复杂了这里就比较复杂了

  • 右值引用

    • &&符号

    • 右值引用只能绑定到一个即将销毁的对象 因此可以自由的将一个右值引用的资源移动到另一个对象

    • 常规引用即左值引用(非const的)只能绑定到左值不能绑定到字面值常量或者返回右值的表达式等等
      但是右值引用可以绑定到这些东西 但是不能讲右值引用绑定到一个左值

    • 变量表达式是左值 不能讲右值引用直接绑定到变量上 就算这个变量是右值引用也不行

  • 虽然不能将右值引用直接绑定到左值 但是可以显示的将左值转换为对应的右值引用类型
    通过标准库的move函数获得绑定到左值的右值引用

  • move在头文件utility中

  • 调用std::move(x)就意味着除了对x进行赋值或者销毁 我们不在使用x 调用move之后不能对x的值做任何假设 我们不能使用他

  • 尽量使用std::move 避免名字冲突

  • 移动构造函数 移动赋值运算符

    • 类似拷贝构造函数移动构造函数第一个参数也是该类型的一个引用 只不过是一个右值引用
      除了完成资源移动 还必须保证移后源对象处于一个这样的状态-----销毁他是无害的
      一旦完成资源的移动必须保证源对象不再指向被移动的资源 这些资源已经被新创建的对象接管

    • 移动操作不应该抛出任何异常 要使用noexcept
      noexcept需要出现在参数列表之后 初始化冒号之前 且必须在声明和定义都同时指定noexcept

    • 移动赋值运算符同理也需要noexcept

    • 只有当一个类没有定义任何的拷贝控制成员 且每个非static数据成员都可以移动
      编译器才会合成移动构造函数或者移动赋值运算符

    • 移动操作永远不会隐式定义为删除的函数

    • 一个类如果定义了移动构造函数或者移动赋值运算符 那么该类的合成拷贝构造函数和拷贝赋值运算符定义是删除的
      所以如果定义了移动构造函数 那么基本必须定义拷贝构造函数 不然默认是删除的 gg

    • 对没有定义移动构造函数的类执行std::move其实调用的就是拷贝构造函数啦啦

    • 移动迭代器

      • 移动迭代器解引用生成右值

      • 标准库的 make_move_iterator 可以将普通迭代器转换为移动迭代器

  • 引用限定符 可以是左值引用限定符 也可以是右值引用限定符 可以指定this是左值属性还是右值属性

    • 成员函数可以同时用const和引用限定符限定 但是引用限定符号在cosnt之后

    • 也可以根据引用限定符号来进行重载版本区分

    • 定义两个或者两个以上具有相同名字和参数的成员函数 要么全部都加引用限定 要么都不加

    • 引用限定需要在声明处和定义处都出现

定义行为像值的类 代码的注释很重要 要看要看要看
	class HasPtr
	{
   
	public:
	    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0)
	    {
   
	    }
	    HasPtr(const HasPtr &p) : ps(new std::string(*p.ps)), i(p.i) {
   }
	    HasPtr &operator=(const HasPtr &rhs);
	    ~HasPtr() {
    delete ps; }
	
	private:
	    std::string *ps;
	    int i;
	};
	
	// 正确示范
	// 拷贝赋值运算符 通常需要保证两点
	// 1. 把一个对象赋予自身必须正确工作
	// 2. 通常拷贝赋值运算符需要组合析构函数和拷贝构造函数的工作
	HasPtr &HasPtr::operator=(const HasPtr &rhs)
	{
   
	    auto newp = new string(*rhs.ps);
	    delete ps; 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值