简体   繁体   English

为什么我们需要在移动构造函数中将右值引用设置为 null?

[英]Why do we need to set rvalue reference to null in move constructor?

//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;
};

I can't understand the reason for setting rhd.pds to nullptr .我无法理解将rhd.pds设置为nullptr的原因。

What will happen if we remove this line : rhs.pds = nullptr;如果我们删除这一行会发生什么: rhs.pds = nullptr;

Some details of the class have been removed.该类的一些细节已被删除。 In particular, the constructor dynamically allocates the DataStructure object and the destructor deallocates it.特别是,构造函数动态分配DataStructure对象,析构函数释放它。 If, during a move, you just copied the pointer from one Widget to another, both Widget s would have pointers to the same allocated DataStructure object.如果在移动过程中,您只是将指针从一个Widget复制到另一个Widget ,则两个Widget都将具有指向同一个分配的DataStructure对象的指针。 Then, when those objects are destroyed, they would both attempt to delete it.然后,当这些对象被销毁时,它们都会尝试delete它。 This would give undefined behaviour.这将给出未定义的行为。 To avoid this, the Widget that is being moved from has its internal pointer to set to nullptr .为了避免这种情况,正在移动的Widget将其内部指针设置为nullptr

This a standard pattern when implementing a move constructor.这是实现移动构造函数时的标准模式。 You want to move ownership of some dynamically allocated objects from one object to another, so you need to make sure the original object no longer owns those allocated objects.您希望将一些动态分配的对象的所有权从一个对象转移到另一个对象,因此您需要确保原始对象不再拥有这些分配的对象。

Diagrammatically, you start off with this situation, wanting to move ownership of the DataStructure from one Widget to the other:从图表上看,您从这种情况开始,希望将DataStructure所有权从一个Widget转移到另一个Widget

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

If you just copied the pointer, you'd have:如果你只是复制了指针,你会有:

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

If you then set the original Widget pointer to nullptr , you have:如果您随后将原始Widget指针设置为nullptr ,您将:

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

Ownership has successfully been transferred, and when both Widget s can be destroyed without causing undefined behaviour.所有权已成功转移,并且当两个Widget都可以被销毁而不会导致未定义的行为时。

The DataStructure object is likely "owned" by the Widget , and resetting the pointer prevents it from being accidentally deleted when the Widget is destroyed. DataStructure对象可能由Widget “拥有”,重置指针可以防止它在Widget被销毁时被意外删除。

Alternately, it's conventional to reset objects to an "empty" or "default" state when they are moved-from, and resetting the pointer is a harmless way to follow the convention.或者,在移动对象时将对象重置为“空”或“默认”状态是惯例,并且重置指针是遵循约定的无害方式。

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;
};

I added a destructor in the above class.我在上面的类中添加了一个析构函数。

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

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

To illustrate what would happen if you remove the nullptr assignment, check the above methods.为了说明如果删除 nullptr 分配会发生什么,请检查上述方法。 A widget a would be created in a helper function and assigned to widget b.小部件 a 将在辅助函数中创建并分配给小部件 b。

Since widget a goes out of scope its destructor its called, which deallocates memory, and you are left with widget b which is pointing to invalid memory address.由于小部件 a 超出了其调用的析构函数的作用域,它会释放内存,而小部件 b 则指向无效的内存地址。

If you assign nullptr to rhs, a destructor is also called, but since delete nullptr does nothing all is good :)如果将 nullptr 分配给 rhs,也会调用析构函数,但由于 delete nullptr 什么都不做,一切都很好:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么需要在move构造函数中将右值引用设置为null? - Why need to set rvalue reference to null in move constructor? 为什么移动构造函数需要使用“右值引用”? - Why does a move constructor need to use “rvalue reference”? 为什么移动返回一个rvalue引用参数需要用std :: move()包装它? - Why move return an rvalue reference parameter need to wrap it with std::move()? 为什么我们需要对unique_ptr的vector进行移动构造? - Why do we need a move constructor for vector of unique_ptr? 为什么我们需要引用成员的复制构造函数 - Why do we need a copy constructor for reference members 为什么我必须在右值引用上调用 move ? - Why do I have to call move on an rvalue reference? 为数组类实现move构造函数(右值引用) - Implementing a move constructor(rvalue reference) for an array class 为什么在返回右值时不调用移动构造函数? - Why is the move constructor not invoked when returning an rvalue? 为什么在rvalue引用构造函数的初始化列表中使用std :: forward而不是std :: move作为数据成员? - Why use std::forward rather than std::move for data member in rvalue reference constructor's initialization list? 为什么使用删除的右值引用构造函数? - why use of deleted rvalue reference constructor function?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM