为什么需要在移动构造函数中将rvalue引用设置为null?

时间:2015-11-20 19:49:45

标签: c++ move rvalue

我已阅读帖子:Why do we need to set rvalue reference to null in move constructor?

它说在移动过程中,你只是将指针从一个对象复制到另一个对象,所以两个指针指向临时对象。当临时对象超出范围时,它的析构函数将运行。然后由2个指针指向的对象将是deallocates。因此,移动构造函数构造的对象的指针字段将指向不可用的位置。

但是,请查看以下代码:

int *p = new int(3);
int *q = p;
delete p;
std::cout << *q << std::endl;

删除指针p后,指针q仍然可以访问int对象。

那么,这只是因为当rvalue引用访问对象时会产生差异吗?

5 个答案:

答案 0 :(得分:2)

不,你不能。它似乎有效,但它完全是未定义的行为。内存将释放到运行时。

当您移动某些内容时,您有效地说原始对象不再有效 - 您只能从移动到的对象访问其原始内容。此外,移动后设置为NULL可防止双重删除 - 当原始对象超出范围并尝试释放该内存时,它将立即失败(如果新对象已经空闲&#39; d)或当新对象本身试图释放该内存时,它将失败。

答案 1 :(得分:1)

Accessing deleted memory of the free store is undefined behavior.

在移动构造中,构造对象的指针设置为右值的指针,然后将其设置为nullptr
rvalue 引用的对象在此之后终止其生命周期,毕竟它是临时。因此,设置为nullptr的指针预计不会再使用。当它被破坏时,执行delete nullptr;,即无操作 OTOH,当构造对象的生命周期结束时,实际资源为deleted - 指向它的指针在移动构造函数中指定。

答案 2 :(得分:0)

不,删除指针p后,指针q 无法仍然访问int对象。如果你不这么认为,因为你运行它并在输出中得到3 - 这是因为你造成了未定义的行为而UB可以&#34;工作&#34;作为众多选择之一。

一般情况下,内存地址尚未被重用,仍然属于程序(因此它有权访问且值尚未被删除),所以你会看到任何内容值已在该内存中删除。所有这一切都意味着,如果代码完全没问题就会显得好像。在这种情况下你也可以使用* p进行打印,它也没有什么不同。 p和q变量都包含相同的地址......由删除释放。

其中一个有趣的方面是高安全性应用程序或GPG等加密应用程序用垃圾手动填充已删除的内存,这样它就不会被嗅出 - 让私钥可以访问世界。否则,密钥仍然只能写入RAM,直到有人在那里写了其他东西!

答案 3 :(得分:0)

您链接的问题和您的问题都是在取消引用q时试图避免未定义的行为。

问题是,(在链接的问题中)您必须知道,通过将其设置为q,您不能再取消引用nullptr(您没有管理它)。但是,在q之后,不要取消引用delete是您的事。在q之前删除std::cout << *q << std::endl;是非常可预测的。你取消引用它是你的错!

尽管如此,将q设置为nullptr有助于调试,delete q不再是UB,nullptr表示指向不存在的对象的指针。

答案 4 :(得分:0)

这是指针的问题。它们不传达所有权语义。

您应该使用std::unique_ptr之类的内容。当您将指针从一个对象移动到另一个对象时,旧对象会识别它不再拥有该对象,因此您可以访问该数据。

简单的解决方案。就像其他C ++社区一样,停止使用这样的指针。

std::unqieu_ptr<int> p(new int(3));
std::unique_ptr<int> q = p;              // Fails to compile


std::unqieu_ptr<int> p(new int(3));
std::unique_ptr<int> q = std::move(p);   // explicitly transfer ownership.


// delete p;  // No longer need this as when the onbject goes out of scope
              // it is deleted.