如果类型希望具有值语义,并且它需要存储动态分配的对象,那么在复制操作中,该类型将需要分配这些对象的新副本。它还必须执行此操作以进行副本分配。
这种复制称为“深层复制”。它有效地吸收了原本应该是引用语义的内容,并将其转换为值语义:
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; } };
移动语义允许一种类型,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; };