14
//code from https://skillsmatter.com/skillscasts/2188-move-semanticsperfect-forwarding-and-rvalue-references
class Widget {
public:
    Widget(Widget&& rhs)
        : pds(rhs.pds) // take source’s value
    { 
        rhs.pds = nullptr;  // why??
    }

private:
    struct DataStructure;
    DataStructure *pds;
};

我无法理解设置rhd.pds为的原因nullptr

如果我们删除这一行会发生什么:rhs.pds = nullptr;

4

3 回答 3

15

课程的一些细节已被删除。特别是,构造函数动态分配DataStructure对象,而析构函数释放它。如果在移动过程中,您只是将指针从一个复制Widget到另一个,则两个Widgets 都将具有指向同一个分配DataStructure对象的指针。然后,当这些对象被销毁时,它们都会尝试这样delete做。这将给出未定义的行为。为避免这种情况,Widget正在移动的 将其内部指针设置为nullptr

这是实现移动构造函数时的标准模式。您希望将一些动态分配的对象的所有权从一个对象转移到另一个对象,因此您需要确保原始对象不再拥有这些分配的对象。

从图表上看,您从这种情况开始,希望将所有权DataStructure从一个Widget转移到另一个:

    ┌────────┐        ┌────────┐
    │ Widget │        │ Widget │
    └───╂────┘        └────────┘
        ┃
        ▼
 ┌───────────────┐
 │ DataStructure │
 └───────────────┘

如果您只是复制指针,您将拥有:

    ┌────────┐        ┌────────┐
    │ Widget │        │ Widget │
    └───╂────┘        └───╂────┘
        ┗━━━━━━━━┳━━━━━━━┛
                  ▼
         ┌───────────────┐
         │ DataStructure │
         └───────────────┘

如果您随后将原始Widget指针设置为nullptr,则您有:

    ┌────────┐         ┌────────┐
    │ Widget │         │ Widget │
    └────────┘         └───╂────┘
                           ┃
                           ▼
                  ┌───────────────┐
                  │ DataStructure │
                  └───────────────┘

所有权已成功转移,并且两个Widgets 都可以被销毁而不会导致未定义的行为。

于 2014-03-01T11:58:24.823 回答
2

DataStructure对象可能为 所“拥有” Widget,并且重置指针可防止它在Widget被销毁时被意外删除。

或者,当对象被移出时,将对象重置为“空”或“默认”状态是惯例,并且重置指针是遵循惯例的无害方式。

于 2014-03-01T11:57:40.647 回答
1
class Widget {
  public:
    Widget(Widget&& rhs)
       : pds(rhs.pds) // take source’s value
    { 
        rhs.pds = nullptr;  // why??
    }
    ~Widget() {delete pds}; // <== added this line

private:
    struct DataStructure;
    DataStructure *pds;
};

我在上面的类中添加了一个析构函数。

Widget make_widget() {
    Widget a;
    // Do some stuff with it
    return std::move(a);
}

int main {
    Widget b = make_widget;
    return 0;
}

为了说明如果删除 nullptr 分配会发生什么,请检查上述方法。小部件 a 将在辅助函数中创建并分配给小部件 b。

由于小部件 a 超出了范围,它调用了它的析构函数,这会释放内存,并且您留下的小部件 b 指向无效的内存地址。

如果将 nullptr 分配给 rhs,也会调用一个析构函数,但由于 delete nullptr 什么都不做,一切都很好:)

于 2014-03-01T12:11:27.457 回答