假设我们有以下课程:
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不再有效(在调用除了赋值运算符之外的任何其他内容可能会崩溃)?
class Foo
{
public:
Foo() { _bar=new Bar };
Foo(const Foo &right) { _bar=new Bar(right.bar); };
Foo(Foo &&right) { _bar=right._bar; right.bar=nullptr; };
~Foo() { if(_bar != nullptr) delete _bar; }
Foo &operator=(const Foo &right)
{
if(_bar == nullptr)
_bar=new Bar();
_bar->opertor=(right.bar);
return *this;
}
Foo &operator=(Foo &&right)
{
if(_bar != nullptr)
delete _bar;
_bar=right._bar;
right._bar=nullptr;
return *this;
}
void func() { _bar->test=1 };
private:
Bar *_bar;
};
我担心的是func(以及该类中的所有其他函数)假设_bar存在。
答案 0 :(得分:0)
将其更改为以下内容是否合法,并期望最终用户知道在执行移动后rvalue不再有效?
你当然应该这样做。不应将右值引用保持在可用状态。移动构造函数和移动赋值运算符背后的想法是,您正在移动来自对象的有用数据。我不会使用不再有效的来描述它。从C ++的角度来看,它仍然是一个有效的对象,就像nullptr
是一个有效的指针一样。
答案 1 :(得分:0)
您应该记录每个成员函数的Foo
前提条件,例如:
void Foo::func();
要求:
*this
未处于移出状态。
然后,您可以选择需要使用Foo::func()
的任何客户,除非......
如果您将Foo
与需要func()
的其他人的图书馆一起使用,并且没有记录“除非处于移动状态”之外的内容,那么您运气不好
例如:假设您的Foo
有operator<
这样:
bool operator<(const Foo& x, const Foo& y);
Requries:
x
和y
都不属于移出状态。
现在,如果您执行以下操作:
std::vector<Foo> v;
// ... fill v
std::sort(v.begin(), v.end()); // oops!
上面的最后一行要求Foo
为LessThanComparable
,无论Foo
是否处于移出状态。这对于std :: lib中的通用代码的 all 都是如此。 std代码的requires子句适用于用户提供的代码,不会为移动的对象设置例外。
但是,如果你的operator<
在任一参数处于移动状态时有效,那么对std::sort
的调用就可以了。即使func()
仍然要求Foo
不处于移出状态,也是如此。这是因为std::sort
根本不需要func()
。
总而言之,这完全取决于您的Foo
与之互动的其他代码。也许没关系,也许不是。记录Foo
的作用,并了解您使用Foo
的代码的要求。