【问题标题】:What is the common idiom(s) for resetting the moved object?重置移动对象的常用习语是什么?
【发布时间】:2020-12-19 00:18:55
【问题描述】:

在 C++ 中,移动构造函数需要重置移动的对象,在我看来,在大多数情况下,这似乎是析构函数所做的重复。

定义一个重置方法并在析构函数和移动构造函数中使用它是最好的方法是否正确?或者也许有更好的方法?

【问题讨论】:

  • "...析构函数做了什么..." 不,因为移动到对象窃取了从对象移动的内容。您所要做的就是让移动的对象处于有效状态(特定于您的对象)并避免双重所有权等。
  • “需要重置被移动的对象” 移动构造函数或移动赋值运算符对被移动实例的唯一责任是让它都可破坏和可分配的。通常,移动构造函数和移动赋值运算符不会释放被移动实例的资源,它们(顾名思义)将它们移动到另一个实例。如果你的移动构造函数和析构函数做同样的事情,你就犯了一个严重的错误。
  • “在 C++ 中需要移动构造函数来重置移动的对象” — 不,不是。这只是常见的做法,很大程度上取决于resetcontents 的语义。并非所有对象都拥有资源。

标签: c++ c++11 move move-semantics


【解决方案1】:

Move 构造函数通常“窃取”参数持有的资源(例如,指向动态分配对象、文件描述符、TCP 套接字、I/O 流、正在运行的线程等的指针)而不是复制它们,然后离开论证处于某种有效但在其他方面不确定的状态。对于某些类型,例如std::unique_ptr,完全指定了移出状态。

不应释放“被盗”资源,因为这通常会导致错误。例如,“窃取”指针的移动构造函数必须确保被移动对象的析构函数不会delete 指针。否则,将出现双免。实现这一点的常用方法是将移动的指针重置为nullptr

这是一个例子:

struct Pointer {
    int *ptr;

    // obtain a ptr resource which we will manage
    Pointer(int* ptr) : ptr{ptr} {}

    // steal another object's ptr resource, assign it to nullptr
    Pointer(Pointer &&moveOf) : ptr{moveOf.ptr} {
        moveOf.ptr = nullptr;
    }

    // make sure that we don't delete a stolen ptr
    ~Pointer() {
        if (ptr != nullptr) {
            delete ptr;
        }
    }
};

定义一个重置方法并在析构函数和移动构造函数中使用它是最好的方法是否正确?或者也许有更好的方法?

这取决于管理的资源,但通常析构函数和移动构造函数做不同的事情。移动构造函数窃取资源,析构函数释放未被窃取的资源。

在 C++ 中,移动构造函数需要重置移动的对象,在我看来,在大多数情况下,这似乎是析构函数所做的重复。

你说得对,经常有重复工作。这是因为 C++ 没有破坏性的移动语义,所以析构函数仍然会被单独调用,即使对象已经被移动。在我展示的示例中,~Pointer() 仍然需要被调用,即使在移动之后也是如此。这伴随着检查ptr == nullptr 是否存在的运行时成本。具有破坏性移动语义的语言示例would be Rust


相关帖子:

【讨论】:

【解决方案2】:

需要重置移动的对象,在我看来,这似乎是析构函数在大多数情况下所做的重复。

在我看来,您误解了析构函数在大多数情况下的用途。

move 中“重置”(如您所说)的目的是设置对象的状态,使其满足析构函数的内部先决条件(更一般地说,任何类不变量)。如果构造函数不这样做,则无法销毁对象,这将违反约定和良好做法,并可能导致错误。

在许多情况下,析构函数不可能执行相同的“重置”。例如,没有办法区分无效指针和有效指针。这就是智能指针的移动构造函数将指针重置为 null 的原因。

定义一个重置方法并在析构函数和移动构造函数中使用它是最好的方法是否正确?

目前尚不清楚这何时有用。看起来并不典型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-29
    相关资源
    最近更新 更多