RValue 引用在分配给另一个指针后是否删除它们的绑定(非指针)对象?
Do RValue references delete their bound (non-pointer) object after it is assigned to another pointer?
简短的问题:
我知道在移动构造函数/重载赋值中,您必须设置引用指向 nullptr
(最后)的指针的 RValue 引用,以避免重新分配该资源。
出于同样的原因,我假设 RValue 引用在分配给另一个指针后删除它们的绑定(非指针)对象,这意味着指针的行为在此之后是未定义的。 (我不确定这是不是真的)
我的问题是与以下类似的代码片段是否会定义行为(如果我使用 pNum
)? :
//Foo.h
class Foo {
public:
Foo(int &&num);
private:
int *pNum;
};
//Foo.cpp
Foo::Foo(int &&num)
:pNum(&num)//Would using/dereferencing pNum in the future cause defined behaviour?
{}
如果这是通过使用 RValue 引用作为参数来优化函数的错误方法,那么正确的方法是什么?例如像 emplace 或 make_shared 这样的标准函数使用什么方法?
I am aware that in a move constructor / overloaded assignment you must set RValue references that refer to pointers to nullptr (at the end) in order to avoid deallocation of that resource.
你不知道。这是一种常见的模式,但它不是移动语义如何工作的基础。您需要做的是进行设置,以使移出对象的破坏不会弄乱移入对象。对于析构函数要释放的指针,将它们设置为 nullptr
是显而易见的选择,但是将单独的 bool m_hasBeenMovedFrom
成员设置为 true 也可以,将指针设置为 point到新分配的对象。
不过,重要的是右值引用 自身 不会做那样的事情。右值引用只是引用的一种变体,与左值引用的绑定语义略有不同。它们不会像左值引用那样删除任何东西。
在您发布的代码中,构造函数正在接收对对象的右值引用,并存储指向该对象的指针。这很危险,因为你可以做类似 Foo f(3)
的事情(或者,因为它是一个非 explicit
单参数构造函数,甚至只是将 3
传递给期望 Foo
的函数), 这将存储指向过期值的指针。另一方面,如果保证传入对象的寿命比 Foo
长,一切都会好起来的,但是如果您需要它,为什么要使用右值引用?
总的来说,虽然您在那里显示的代码 本身 没有未定义的行为,但绝对 乞求 UB 发生当使用 class 时。右值引用的含义是"this object will be gone soon, so feel free to steal its belongings"。一个 int
没有任何物品,所以这没有用......并且因为它即将消失,所以你不想持有指向它的指针。
顺便说一下,您会经常看到 template<typename T> someFunc(T && arg)
形式的标准库函数(和其他函数!)。那是 不是 右值引用,而是 "forwarding reference",又名 "universal reference",大量页面会告诉您(但 this 是我的最喜欢的一个)。这里要记住的主要事情是 T &&
只有在给定右值的情况下才会像右值引用一样工作,并且函数本身需要注意不要无条件地将其视为右值引用。
简短的问题:
我知道在移动构造函数/重载赋值中,您必须设置引用指向 nullptr
(最后)的指针的 RValue 引用,以避免重新分配该资源。
出于同样的原因,我假设 RValue 引用在分配给另一个指针后删除它们的绑定(非指针)对象,这意味着指针的行为在此之后是未定义的。 (我不确定这是不是真的)
我的问题是与以下类似的代码片段是否会定义行为(如果我使用 pNum
)? :
//Foo.h
class Foo {
public:
Foo(int &&num);
private:
int *pNum;
};
//Foo.cpp
Foo::Foo(int &&num)
:pNum(&num)//Would using/dereferencing pNum in the future cause defined behaviour?
{}
如果这是通过使用 RValue 引用作为参数来优化函数的错误方法,那么正确的方法是什么?例如像 emplace 或 make_shared 这样的标准函数使用什么方法?
I am aware that in a move constructor / overloaded assignment you must set RValue references that refer to pointers to nullptr (at the end) in order to avoid deallocation of that resource.
你不知道。这是一种常见的模式,但它不是移动语义如何工作的基础。您需要做的是进行设置,以使移出对象的破坏不会弄乱移入对象。对于析构函数要释放的指针,将它们设置为 nullptr
是显而易见的选择,但是将单独的 bool m_hasBeenMovedFrom
成员设置为 true 也可以,将指针设置为 point到新分配的对象。
不过,重要的是右值引用 自身 不会做那样的事情。右值引用只是引用的一种变体,与左值引用的绑定语义略有不同。它们不会像左值引用那样删除任何东西。
在您发布的代码中,构造函数正在接收对对象的右值引用,并存储指向该对象的指针。这很危险,因为你可以做类似 Foo f(3)
的事情(或者,因为它是一个非 explicit
单参数构造函数,甚至只是将 3
传递给期望 Foo
的函数), 这将存储指向过期值的指针。另一方面,如果保证传入对象的寿命比 Foo
长,一切都会好起来的,但是如果您需要它,为什么要使用右值引用?
总的来说,虽然您在那里显示的代码 本身 没有未定义的行为,但绝对 乞求 UB 发生当使用 class 时。右值引用的含义是"this object will be gone soon, so feel free to steal its belongings"。一个 int
没有任何物品,所以这没有用......并且因为它即将消失,所以你不想持有指向它的指针。
顺便说一下,您会经常看到 template<typename T> someFunc(T && arg)
形式的标准库函数(和其他函数!)。那是 不是 右值引用,而是 "forwarding reference",又名 "universal reference",大量页面会告诉您(但 this 是我的最喜欢的一个)。这里要记住的主要事情是 T &&
只有在给定右值的情况下才会像右值引用一样工作,并且函数本身需要注意不要无条件地将其视为右值引用。