2014-02-13 70 views
1

我新的C++ 11和写了下面的代码来了解std::move是如何工作的:了解移动构造函数,的std ::移动和析构函数

#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. 如果上面的答案是肯定的,那么我怎样才能真正避免复制?

谢谢

回答

4

如果从一个物体转移到另一个物体,你还有总共两个对象。它们都需要被销毁。也许move是一个稍有误导的术语,但它不是从一个地方移动到另一个地方的对象本身(对象从不实际移动) - 它是对象的内容。

  1. 不可以。如上所述,移动并不会消失其中一个对象。复制和从一个对象移动到另一个对象都会涉及两个对象。不同之处在于它们对物体有什么影响。当然,副本会将一个对象的成员复制到另一个对象的成员。另一方面,移动会将成员从一个对象移到另一个对象 - 通常是一个更快的操作。
  2. N/A
+0

因此,如果我的对象有成员指针,并且它的析构函数将释放这些指针。这是否意味着我无法真正移动这些对象? – keelar

+1

@keelar你可以 - 当你从它移动时,只需设置指向'nullptr'的指针。在空指针上执行delete操作将不起作用。在实现移动构造函数/赋值运算符时,应该使对象保持有效(但不确定)状态。 –

+0

非常感谢!这听起来很完美。我能否进一步知道'= default'移动构造函数/赋值是否将'nullptr'赋值给源成员指针?或者我应该开发自己的移动构造函数/任务? – keelar

1

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

和第二组DELETE语句的时候出现在队列中新创建的对象被销毁

另外值得注意的是,如果被删除的对象的属性不是POD类型,那么它是不是一个好想法访问他们在删除方法否则会导致段故障