了解移动构造函数、std::move 和析构函数

Understanding move constructor, std::move, and destructor

本文关键字:move 析构函数 std 移动 构造函数 了解      更新时间:2023-10-16

我是 c++11 的新手,编写了以下代码来了解std::move的工作原理:

#include <queue>
#include <stdio.h>
class X {
 public:
  X(int x) : x_(x) {}
  ~X() {
    printf("X(%d) has be released.n", x_);
  }
  X(X&&) = default;
  X& operator = (X&&) = default;
  X(const X&) = delete;
  X& operator = (const X&) = delete;
 private:
  int x_;
};

int main() {
  std::queue<X> xqueue;
  for (int x = 0; x < 5; ++x) {
    xqueue.push(std::move(X(x)));
  }
  return 0;
}

但是,它会生成以下输出,指示每个X(n)的析构函数已被调用两次:

X(0) has be released.
X(1) has be released.
X(2) has be released.
X(3) has be released.
X(4) has be released.
X(0) has be released.
X(1) has be released.
X(2) has be released.
X(3) has be released.
X(4) has be released.

我可以想象第二轮输出发生在函数main()末尾,当这些中间X超出范围时,第一轮可能发生在循环中。

但是,我认为这种中间X的所有权将完美地转移到队列中,并且在所有权转移期间不应调用它们的析构函数。

所以我的问题是:

  1. 当我看到一个实例被释放两次时,这是否意味着它执行复制而不是移动?
  2. 如果上面的答案是肯定的,那么我怎么能真正避免复制呢?

谢谢

如果从一个对象移动到另一个对象,总共仍然有两个对象。它们都需要被摧毁。也许move是一个略带误导性的术语,但从一个地方移动到另一个地方的不是对象本身(对象实际上从未移动过) - 而是对象的内容。

  1. 不。如上所述,移动不会从其中一个对象中消失。复制和从一个对象移动到另一个对象都将涉及两个对象。区别在于它们对对象的影响。副本当然会将一个对象的成员复制到另一个对象。另一方面,移动会将成员从一个对象移动到另一个对象 - 通常是更快的操作。
  2. 不适用

移动构造函数窃取对象的属性/成员并将其提供给新对象。因此,在您的示例中,当移动构造函数的作用域结束并且现在为空的对象被销毁时,将发生第一组 delete 语句。

第二组删除语句发生在队列内新创建的对象被销毁时

另外值得注意的是,如果已删除对象的属性不是 POD 类型,那么在 delete 方法中访问它们不是一个好主意,否则会导致 seg 错误