右值引用-移动构造详解

先理解右值引用,要理解右值引用先要回顾左值引用,要理解左值引用先回顾下右值和左值

定义:左值与右值的定义在于一个赋值等号,赋值等号左边成为左值,等号右边成为右值

左值(L-value):表示存储在计算机内存的对象,可寻址,相当于地址值

右值(R-value):代表的为真实值,可读,即数据值

左值引用,是一种数据类型,他是对一个左值的引用,同理右值引用,也是一种数据类型,他是对优质的引用

左值引用常用的场景是函数参数的传递,相当于扩展了左值的作用域到函数中

那么在来说说右值引用,普通的右值引用好理解,下面说下将亡值,C++11中即将消失的变量,如函数的返回值为将亡值,注意将亡值是即将被回收的值,是不能用左值引用的,但是可以进行右值引用,有什么用呢,原来的时候,函数返回,返回值会被拷贝到一个左值中,然后返回值被析构,使用右值引用时候,返回值将不会被析构,直接被右值引用接管,如果返回是是复杂数据类型,就能避免不必要的拷贝构造,提高效率

void fun(int &tmp)
{
    std::cout << tmp << std::endl;
}

int fun1()
{
    int a = 1;
    return a;
}

int main(int argc, char *argv[])
{
    int b = 1; // 左值
    int &rb = b; // 左值引用

    int &&c = 2;  // 右值引用

    int &d = fun1(); // 错误,不能左值引用将亡值
    int &&e = fun1(); // 正确,右值引用将亡值,接管了将亡值,详单与扩展将亡值的作用域


    fun(b);  // 将b的范围扩展到fun函数中
    return 0;
}

下面来说说移动构造,移动构造可以理解为文件的剪切操作,或者理解为二手交易,比如一个人要挂掉了,临走时,他要将自己的东西交付给其他人,之前的做法是通过拷贝构造进行拷贝,然后自己析构挂掉,但移动构造是直接将自己的东西转交给下个人,自己挂掉

原来类有四大默认构造函数、析构函数、拷贝构造函数、赋值函数(operator=),C++11又新增了两个移动构造、移动赋值。

#include <iostream>
#include <algorithm>

class MemoryBlock
{
public:

   // 构造函数
   explicit MemoryBlock(size_t length)
      : _length(length)
      , _data(new int[length])
   {
      std::cout << "gouzao. length = "
                << _length << "." << std::endl;
   }

   // 析构函数.
   ~MemoryBlock()
   {
      std::cout << "xigou. length = "
                << _length << ".";

      if (_data != nullptr)
      {
         std::cout << " Deleting resource.";
         // Delete the resource.
         delete[] _data;
      }

      std::cout << std::endl;
   }

   // 拷贝构造
   MemoryBlock(const MemoryBlock& other)
      : _length(other._length)
      , _data(new int[other._length])
   {
      std::cout << "kaobeigouzao. length = "
                << other._length << ". Copying resource." << std::endl;

      std::copy(other._data, other._data + _length, _data);
   }

   // 拷贝赋值函数.
   MemoryBlock& operator=(const MemoryBlock& other)
   {
      std::cout << "keobeifuzhi. length = "
                << other._length << ". Copying resource." << std::endl;

      if (this != &other)
      {
         // Free the existing resource.
         delete[] _data;

         _length = other._length;
         _data = new int[_length];
         std::copy(other._data, other._data + _length, _data);
      }
      return *this;
   }

   // 移动构造
   MemoryBlock(MemoryBlock&& other) noexcept
      : _data(nullptr)
      , _length(0)
   {
      std::cout << "yidonggouzao. length = "
                << other._length << ". Moving resource." << std::endl;

      // Copy the data pointer and its length from the
      // source object.
      _data = other._data;
      _length = other._length;

      // Release the data pointer from the source object so that
      // the destructor does not free the memory multiple times.
      other._data = nullptr;
      other._length = 0;
   }

   // 移动赋值.
   MemoryBlock& operator=(MemoryBlock&& other) noexcept
   {
      std::cout << "yidongfuzhi. length = "
                << other._length << "." << std::endl;

      if (this != &other)
      {
         // Free the existing resource.
         delete[] _data;

         // Copy the data pointer and its length from the
         // source object.
         _data = other._data;
         _length = other._length;

         // Release the data pointer from the source object so that
         // the destructor does not free the memory multiple times.
         other._data = nullptr;
         other._length = 0;
      }
      return *this;
   }

   // Retrieves the length of the data resource.
   size_t Length() const
   {
      return _length;
   }

private:
   size_t _length; // The length of the resource.
   int* _data; // The resource.
};

MemoryBlock getMemoryBlock()
{
    return MemoryBlock(5);
}

// 注意:     该例子需要需要关闭编译其优化选项QMAKE_CXXFLAGS += -fno-elide-constructors

int main(int argc, char *argv[])
{
    MemoryBlock m(2);    // 调用构造
    MemoryBlock n(m); // 调用拷贝构造
    MemoryBlock n1 = m; // 调用拷贝构造
    MemoryBlock n2(3);
    n2 = m; // 调用拷贝赋值

    //MemoryBlock &tmp = getMemoryBlock();  // 错误,不能左值引用将亡值
    MemoryBlock &&tmp = getMemoryBlock();  // 调用移动构造
    std::cout << std::endl;
    std::cout << std::endl;
    std::cout << std::endl;
    MemoryBlock &&tmp1(getMemoryBlock()); // 调用移动构造

    MemoryBlock tmp3(10);
    // 调用移动赋值
    tmp3 = std::move(m);  // std::move相当于强制类型转换,将左值转为右值,否则不能编译通过
    int mLentth = m.Length();  // mLentth为0 因为其内容已经移动到tmp3
    std::cout << "m length: " << mLentth;

    std::move(n2);
    mLentth = n2.Length();  // mLentth 不为0,因为虽然强转了一次,但并没有执行移动操作

    return 0;
}

右值引用和移动构造是两个相互独立的概念,只不过不是移动构造中需要用到右值引用,因为要通过右值引用来进行函数重载,如拷贝构造对应移动构造,拷贝赋值对应移动赋值

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值