C++ 深度复制和移动支持

示例

如果类型希望具有值语义,并且它需要存储动态分配的对象,那么在复制操作中,该类型将需要分配这些对象的新副本。它还必须执行此操作以进行副本分配。

这种复制称为“深层复制”。它有效地吸收了原本应该是引用语义的内容,并将其转换为值语义:

struct Inner {int i;};

const int NUM_INNER = 5;
class Value
{
private:
  Inner *array_; //通常具有参考语义。

public:
  Value() : array_(new Inner[NUM_INNER]){}

  ~Value() {delete[] array_;}

  Value(const Value &val) : array_(new Inner[NUM_INNER])
  {
    for(int i = 0; i < NUM_INNER; ++i)
      array_[i] = val.array_[i];
  }

  Value &operator=(const Value &val)
  {
    for(int i = 0; i < NUM_INNER; ++i)
      array_[i] = val.array_[i];
    return *this;
  }
};

C ++ 11

移动语义允许一种类型,Value以避免真正复制其引用数据。如果用户以引起移动的方式使用该值,则可以将“复制”自对象的数据保留为空:

struct Inner {int i;};

constexpr auto NUM_INNER = 5;
class Value
{
private:
  Inner *array_; //通常具有参考语义。

public:
  Value() : array_(new Inner[NUM_INNER]){}

  //单击确定以删除,即使nullptr
  ~Value() {delete[] array_;}

  Value(const Value &val) : array_(new Inner[NUM_INNER])
  {
    for(int i = 0; i < NUM_INNER; ++i)
      array_[i] = val.array_[i];
  }

  Value &operator=(const Value &val)
  {
    for(int i = 0; i < NUM_INNER; ++i)
      array_[i] = val.array_[i];
    return *this;
  }

  //移动意味着没有内存分配。
  //无法引发异常。
  Value(Value &&val) noexcept : array_(val.array_)
  {
    //我们窃取了旧的价值。
   val.array_= nullptr;
  }

  //无法引发异常。
  Value &operator=(Value &&val) noexcept
  {
    //聪明的把戏。由于无论如何val将很快被销毁,
    //我们与我们交换他的数据。他的破坏者将破坏我们的数据。
    std::swap(array_, val.array_);
  }
};

的确,如果我们想禁止深层复制同时仍允许对象移动,那么我们甚至可以使这种类型不可复制。

struct Inner {int i;};

constexpr auto NUM_INNER = 5;
class Value
{
private:
  Inner *array_; //通常具有参考语义。

public:
  Value() : array_(new Inner[NUM_INNER]){}

  //单击确定以删除,即使nullptr
  ~Value() {delete[] array_;}

  Value(const Value &val) = delete;
  Value &operator=(const Value &val) = delete;

  //移动意味着没有内存分配。
  //无法引发异常。
  Value(Value &&val) noexcept : array_(val.array_)
  {
    //我们窃取了旧的价值。
   val.array_= nullptr;
  }

  //无法引发异常。
  Value &operator=(Value &&val) noexcept
  {
    //聪明的把戏。由于无论如何val将很快被销毁,
    //我们与我们交换他的数据。他的破坏者将破坏我们的数据。
    std::swap(array_, val.array_);
  }
};

我们甚至可以通过使用来应用零规则unique_ptr:

struct Inner {int i;};

constexpr auto NUM_INNER = 5;
class Value
{
private:
  unique_ptr<Inner []>array_; //仅移动类型。

public:
  Value() : array_(new Inner[NUM_INNER]){}

  //无需显式删除。甚至声明。
  ~Value() = default; {delete[] array_;}

  //无需显式删除。甚至声明。
  Value(const Value &val) = default;
  Value &operator=(const Value &val) = default;

  //将执行逐元素移动。
  Value(Value &&val) noexcept = default;

  //将执行逐元素移动。
  Value &operator=(Value &&val) noexcept = default;
};