所以从某种意义上说,我们已经进入了地狱的世界 这里的非确定性破坏:已经为变量赋值, 但以前由该变量持有的对象仍在那里 某处。 只要破坏该对象就可以了 没有任何外界可见的副作用。但是 有时候析构函数会产生这样的副作用。一个例子是 在析构函数中释放一个锁。因此,任何一部分 应该执行具有副作用的对象的破坏 显式地在副本赋值的rvalue引用重载中 操作者:
X& X::operator=(X&& rhs)
{
// Perform a cleanup that takes care of at least those parts of the
// destructor that have side effects. Be sure to leave the object
// in a destructible and assignable state.
// Move semantics: exchange content between this and rhs
return *this;
}
我认为l值的原始对象通常会被破坏 - 最终 - 在非分配情况下。
但是,在赋值运算符中,为什么要进行相同的行为?
答案 0 :(得分:0)
嗯,你必须做你的家务:你有责任将旧的对象修改为(安全的)可破坏的状态(如果你很好,那么甚至是一个特别有效的对象状态)。一切都有成本(甚至移动语义)。
E.g。对于典型的矢量实现:
V& V::operator=(V&& old){
auto trash = this->data;
this->data = old.data;
//cleanup
delete[] trash;
old.data = nullptr; // expecting delete[] of the destructor of old
}
您支付了三笔额外的作业(可以使用单个swap()
技巧进行优化)并给您自己带来轻微的不便。你避免了大概10000次弃权。值得,不是吗?
此外,如果您在析构函数中使用危险的副作用(您不应该这样做),这不是您遇到的唯一问题。如果你觉得你的班级很危险,你也可以禁用你的移动任务。
答案 1 :(得分:0)
在仔细阅读上一段后,我看到了&#39;答案:当你在析构函数中有副作用时,你希望能够控制这些副作用何时发生。如果你没有在赋值运算符中显式地破坏原始的lhs对象,那么你无法直接控制它的析构函数何时被调用;稍后添加一些代码可能会延长该对象的生命周期。为什么这有关系?就像链接所说的那样,如果你的对象只是像Queue<int>
这样的数据,那么谁会关心它何时被破坏。但是,如果您的对象持有锁或其他影响其他对象的资源,那么您希望释放所述资源是确定性的。