Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/125.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/matlab/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 移动后右值是否有效?_C++_Move Semantics_Rvalue - Fatal编程技术网

C++ 移动后右值是否有效?

C++ 移动后右值是否有效?,c++,move-semantics,rvalue,C++,Move Semantics,Rvalue,假设我们有以下课程: class Foo { public: Foo() { _bar=new Bar }; Foo(const Foo &right) { _bar=new Bar(right.bar); }; Foo(Foo &&right) { _bar=right._bar; right.bar=new Bar(); }; ~Foo() { delete _bar; } Foo &operator=(const

假设我们有以下课程:

class Foo
{
public:
    Foo() { _bar=new Bar };
    Foo(const Foo &right) { _bar=new Bar(right.bar); };
    Foo(Foo &&right) { _bar=right._bar;  right.bar=new Bar(); };

    ~Foo() { delete _bar; }

    Foo &operator=(const Foo &right) { _bar->opertor=(right.bar); return *this;}
    Foo &operator=(Foo &&right) { std::swap(_bar, right._bar); return *this;}

    void func() { _bar->test=1 };

private:
    Bar *_bar;
};
将其更改为以下内容并期望最终用户知道在执行移动后,rvalue不再有效(在这种情况下,调用除赋值运算符以外的任何内容都可能崩溃),这是否合法

我担心的是func(以及类中的所有其他函数)假设_-bar存在

将其更改为以下内容并期望最终用户知道在执行移动后,右值不再有效是否合法


你当然应该那样做。右值引用不应保持在可用状态。移动构造函数和移动赋值操作符背后的思想是,您正在从对象中移动有用的数据。不过,我不会用“不再有效”来描述它。从C++的观点来看,它仍然是一个有效的对象,就像 NulLPTR> /Cord>是一个有效的指针。

原则上它可能会变得无效,尽管您可能想考虑将它留在可分配状态(您的原始实现,因此编辑,没有做)。这将遵循标准库的政策,即:

除非另有规定,否则从中移动的所有标准库对象都将处于有效但未指定的状态。也就是说,只有没有先决条件的函数(如赋值运算符)才能在对象从中移动后安全地用于对象


我建议重新实现赋值操作符,以便它将“this”对象与新构造的对象交换。这通常是一个很好的方法,可以避免在执行赋值时引入错误行为。

第二个版本更为惯用。将对象移出后,假定不再使用该对象

在调用指针上的
delete
之前,无需检查
nullptr
,因为标准中定义了不执行任何操作


如果用户希望使用“移动自”对象,则其任务是确保该对象处于有效状态(即从有效对象分配)

从对象移动的对象应处于有效但未指定的状态。请注意,这是一项建议,但不是本标准的绝对要求

如果随后对第二个代码执行正常操作(特别是“复制分配”操作符),则第二个代码将中断

如果
\u bar==nullptr
是有效状态,则您的复制分配运算符存在错误;如果它不是一个有效的状态,那么我会说你的移动构造函数被窃听了


注意。在第二个代码中,析构函数中的
if
检查是多余的,因为删除空指针是合法的。

您应该为
Foo
记录每个成员函数的先决条件,例如:

void Foo::func();
要求:
*此
未处于从移动状态

那么,您可以选择需要使用
Foo::func()
的任何客户机,除非

如果您在需要
func()
的其他库中使用
Foo
,并且没有按照“除非处于从状态移动”的行记录某些内容,那么您就倒霉了


例如:假设你的
Foo
有一个
操作符,那么把它改成这个
Foo&operator=(constfoo&other){if(_-bar==nullptr){u-bar=newbar();_-bar->operator=(right.bar);return*this}
Foo&operator=(Foo&other){if(_-bar!=nullptr)删除_-bar;_-bar=right._-bar;right._-bar=nullptr;return*this;}
如果您确实想避免在分配给空
Foo
对象时进行不必要的分配,您可以这样做,但您确认这不是过早的优化吗?如果是这样的话,你可以考虑默认构造函数不做代码>新Bar < /C>,这是唯一一个“多余的”<代码> bar >代码>对象通常会被分配的情况。这取决于这个类的用例。我认为默认构造函数必须有分配,因为没有一个类函数期望_bar是null ptr。没有创建
Foo-Foo的分配用户foo.func()将崩溃。这与代码实现“预期”相比,不是一种优化。假设Bar是20MB,我创建
fooa,b我希望在稍后设置
a=b时使用40MB+我预计只有20MB在使用,因为b现在应该是空的。最上面的例子不能做到这一点,它实际上取决于您希望
Foo
对象具有什么语义,对此没有普遍正确的答案。作为参考,如果构造了默认值,则
std::shared_ptr
无效,如果在该状态下取消引用,则将引发异常。不管是哪种方式,遵循“最小意外原则”,我都会让它在从默认构建时的行为与从默认构建时的行为相同。修复了拼写错误并向运算符添加了空检查=编辑实质上改变了问题。以前,如果在移动后使用
运算符=
,则会取消对空指针的引用;我们以为您在询问移动是否正常,并且调用代码应该知道如何避免这样做。但是现在这个对象在移动之后是有效的,并且不清楚你的问题是什么(在这两种情况下,对象现在都是有效的)。请再次编辑以澄清您的意图。我可能问错了,但对于我来说,问题是相同的,给出了以下代码片段
Foo a,b;a=标准::移动(b);b、 func()。顶层实现不会崩溃,行为可能不受欢迎,但不会崩溃。在新实现下,在移动后调用b.func()将导致崩溃。那么问题是,期望用户知道在移动后在b上调用operator=以外的任何东西都可能崩溃,这是合法的吗?注意:自发布此答案以来,该问题已被编辑-请检查原始编辑
void Foo::func();
bool operator<(const Foo& x, const Foo& y);
std::vector<Foo> v;
// ... fill v
std::sort(v.begin(), v.end());  // oops!