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++的观点来看,它仍然是一个有效的对象,就像
原则上它可能会变得无效,尽管您可能想考虑将它留在可分配状态(您的原始实现,因此编辑,没有做)。这将遵循标准库的政策,即:
除非另有规定,否则从中移动的所有标准库对象都将处于有效但未指定的状态。也就是说,只有没有先决条件的函数(如赋值运算符)才能在对象从中移动后安全地用于对象我建议重新实现赋值操作符,以便它将“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!