• 右值引用和强制移动语义


    假设X是一个类型,那么X&&称作X的右值引用类型,于是为了区分,我们称X&为X的左值引用类型。

    右值引用和左值引用很相似,当然也有一些细微的不同点:当我们重载函数解析的时候,参考下面的例子:

    void foo(X& x); 
    void foo(X&& x); 
    
    X x;
    X foobar();
    
    foo(x); // 参数是左值,所以调用foo(X&)
    foo(foobar()); // 参数是右值,所以调用foo(X&&)

    //这些都是在编译时刻确定的。

    在任何时候,你都可以去实现这两种引用类型的重载,但是在大多数情况下,我会建议你在拷贝和赋值构造的时候使用这种重载比较好。

    X& X::operator=(X const & rhs); 
    X& X::operator=(X&& rhs)
    {
      // 移动语义:交换this和rhs
    return *this; }

    拷贝构造同理。

    警告:在上面的实现中,交换this和rhs是不够好的,我们会在之后讨论这点。

    注意:

    如果你实现了

    void foo(X&);
    

    但是没实现

    void foo(X&&);
    

    那么很明显foo将会调用参数为左值的表达式,参数为右值的情况将不会通过编译。

    如果你实现了

    void foo(X const &);
    

    但是没有实现

    void foo(X&&);
    

    那么无论作用在左值或者右值上,foo都会成功。但是他不会区分左值和右值。

    但是如果你实现了

    void foo(X&&);

    但是没有实现其他两个重载,对于右值来说当然是ok的,但是对左值是行不通的。

    之前我们说到的move语义,是对右值进行操作的,但是很可怕的是,你同样可以对左值进行move操作。

    最明显的例子就是std中的swap操作。

    template<class T>
    void swap(T& a, T& b) 
    { 
      T tmp(a);
      a = b; 
      b = tmp; 
    } 
    
    X a, b;
    swap(a, b);

    上面操作的表达式均不是右值表达式,而且也没有利用move constructor,但是我们很明确的知道使用移动语义的好处,所以我们当然希望在这里也使用到移动语义。

    于是有人来拯救我们了,c++11中有一个函数叫std::move,他的作用就是把他的参数转成右值然后返回。

    所以在c++11中的swap是这样子的:

    template<class T> 
    void swap(T& a, T& b) 
    { 
      T tmp(std::move(a));
      a = std::move(b); 
      b = std::move(tmp);
    } 

    如果T没有实现相应的构造函数(右值引用),那么swap操作将会像之前的行为一样(调用拷贝构造)。

    使用move有很多好处:

    1.对于那些实现了move语义的操作,你会得到一些性能上的提升。

    2.STL容器通常要求元素支持拷贝操作,当然如果我们支持move操作,这也会满足这些容器的要求。

     但是:

    我们看下面这个操作

    a=std::move(b);

    如果move的内部实现是一个swap操作,那么a和b的值最终会交换。

    所 以这个操作完成后,a所携带的资源并不会被释放,其实a的资源会不会被释放的关键取决于b的生存周期,或者说b所占的资源此时代表了当初那个应该被释放的 资源,然而这个资源什么时候最终会被释放呢,我们无法确定。当然如果说这个释放的时间是没有副作用的,那就皆大欢喜了,但是如果,万一这个资源必须在拷贝 的时候被释放掉,那我们非做不可,而不是做一下简单的swap。

    X& X::operator=(X&& rhs)
    {
    
      // 提前释放那些有可能产生副作用的资源
      // 确保对象目前的状态是可析构的和可拷贝的 
    // swap操作
      
      return *this;
    }
  • 相关阅读:
    springboot+mybatis集成多数据源MySQL/Oracle/SqlServer
    向Spring容器中注册组件的方法汇总小结
    使用spring initialization创建SpringBoot项目
    mybatis-generator 的坑我都走了一遍
    初识Jsp,JavaBean,Servlet以及一个简单mvc模式的登录界面
    【Linux】用户只显示$问题
    【Linux】Ubuntu创建用户、删除用户、设置用户密码,root和普通用户切换
    【linux】ubuntu安装ssh
    【整理】【JS】map的基本操作
    【整理】【JS】数组定义、添加、删除、替换、遍历基本操作
  • 原文地址:https://www.cnblogs.com/houhoujun/p/4405010.html
Copyright © 2020-2023  润新知