详解C++11中的右值引用与移动语义

2020-01-06 16:47:32于丽


#include <iostream> 
#include <algorithm> 

class MemoryBlock 
{ 
public: 
  // 构造函数
  explicit MemoryBlock(size_t length) : _length(length) , _data(new int[length]) {} 

  // 析构函数 
  ~MemoryBlock() 
  {
    if (_data != nullptr)  delete[] _data;
  } 

  // 拷贝赋值运算符 
  MemoryBlock& operator=(const MemoryBlock& other) 
  { 
    if (this != &other) 
    { 
      delete[] _data; 
      _length = other._length; 
      _data = new int[_length]; 
      std::copy(other._data, other._data + _length, _data); 
    } 
    return *this; 
  } 

  // 拷贝构造函数 
  MemoryBlock(const MemoryBlock& other) 
    : _length(0) 
    , _data(nullptr) 
  { 
    *this = other; 
  } 

  // 移动赋值运算符,通知标准库该构造函数不抛出任何异常(如果抛出异常会怎么样?)
  MemoryBlock& operator=(MemoryBlock&& other) noexcept
  {
    if (this != &other) 
    {  
      delete[] _data; 
      // 移动资源
      _data = other._data; 
      _length = other._length; 
      // 使移后源对象处于可销毁状态
      other._data = nullptr; 
      other._length = 0; 
    } 
    return *this; 
  }

  // 移动构造函数
  MemoryBlock(MemoryBlock&& other) noexcept
    _data(nullptr) 
    , _length(0) 
  { 
    *this = std::move(other); 
  } 

  size_t Length() const 
  { 
    return _length; 
  } 

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

只有当一个类没有定义任何自己版本的拷贝控制成员,且类的每个非static数据成员都可移动时,编译器才会为它合成移动构造函数会移动赋值运算符。编译器可以移动内置类型;如果一个类类型有对应的移动操作,编译器也能移动这个类型的成员。此外,定义了一个移动构造函数或移动赋值运算符的类必须也定义自己的拷贝操作;否则,这些成员默认地定义为删除的。而移动操作则不同,它永远不会隐式定义为删除的。但如果我们显式地要求编译器生成=defualt的移动操作,且编译器不能移动所有成员,则编译器会将移动操作定义为删除的函数。

如果一个类既有移动构造函数又有拷贝构造函数,编译会使用普通的函数匹配规则来确定使用哪个构造函数。但如果只定义了拷贝操作而未定义移动操作,编译器不会合成移动构造函数,此时即使调用move来移动它们,也是调用的拷贝操作。


class Foo{
public:
  Foo() = default;
  Foo(const Foo&);
  // 为定义移动构造函数
};
Foo x;
Foo y(x);         //调用拷贝构造函数
Foo z(std::move(x));   //调用拷贝构造函数,因为未定义移动构造函数

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持ASPKU。


注:相关教程知识阅读请移步到C++教程频道。