在赋值运算符中移动语义-副作用,破坏

move semantics inside assignment operator - side effects, destruction

本文关键字:副作用 破坏 语义 赋值运算符 移动      更新时间:2023-10-16

强制移动语义

所以从某种意义上说,我们已经进入了这里的非确定性破坏:一个变量被分配给,但是该变量以前持有的对象仍然存在某个地方只要能摧毁那个物体就没问题没有任何对外界可见的副作用但是有时析构函数确实有这样的副作用。例如解除析构函数内部的锁。因此应该对有副作用的物体进行销毁显式地在副本赋值的右值引用重载中操作员:

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-value的原始对象通常会在NON赋值的情况下被销毁。

但是,在赋值运算符中,为什么要使用相同的行为

好吧,你必须做你的家务:你负责将旧对象修改为(安全的)可破坏状态(如果你很好,甚至修改为一般有效的对象状态)。一切都有代价(甚至移动语义)。

例如,对于典型的矢量实现:

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()技巧就可以优化)和给自己带来的小不便。你大概避免了一万个签名。值得,不是吗?

此外,如果你在析构函数中使用危险的副作用(你不应该这样做),这将不是你遇到的唯一问题。如果你认为移动作业对你的班级有危险,你也可以禁用它。

在更仔细地重读上面的段落后,我"看到"了答案:当析构函数中有副作用时,您希望能够控制这些副作用何时发生。如果你没有在赋值运算符中显式地破坏原始lhs对象,那么你就无法直接控制何时调用它的析构函数;稍后添加一些代码可能会延长该对象的使用寿命。为什么这很重要?就像链接所说的,如果你的对象只是像Queue<int>这样的数据,那么谁在乎它什么时候被破坏。但是,如果您的对象持有影响其他对象的锁或其他资源,那么您希望所述资源的释放是确定性的。