简体   繁体   English

如何编写移动构造函数来处理未初始化的移动?

[英]How to write move constructor to handle uninitialized move?

I have a class in my C++ code which has its own move constructor. 我的C ++代码中有一个类,它有自己的移动构造函数。 A simplified version is shown here: 这里显示的是简化版本:

class myClass {
    //In this example, myClass must manually manage allocating
    //and freeing a memory buffer.
    char *mem;

    //...
    //Regular constructor, copy constructor, etc
    //...

    myClass(myClass &&other) {
        //Swap our memory pointer with other's memory pointer
        char *tmp = other.mem;
        other.mem = mem;
        mem = tmp;
    } 

    //...
    //Destructor, other member functions, etc.
    //...
}

In normal situations, this works fine. 在正常情况下,这很好。 However, recently I needed to make a vector of these objects: 但是,最近我需要制作这些对象的向量:

vector<myClass> v;
v.reserve(10); //Make space, but do not construct
v.push_back(myClass()); //Problem!

After getting a segfault and stepping through with gdb, I eventually discovered what should have been obvious: if you try to construct an object from an rvalue reference, this can result in using the move constructor on uninitialized memory . 在获得段错误并逐步使用gdb之后,我最终发现了应该是显而易见的:如果您尝试从右值引用构造对象,这可能导致在未初始化的内存上使用移动构造函数。

How are you supposed to write a move constructor when it's possible that you're swapping garbage into the other class? 当你有可能将垃圾交换到other类时,你应该怎么写一个移动构造函数? Is there some way to detect this? 有没有办法检测到这个?

How are you supposed to write a move constructor when it's possible that you're swapping garbage into the other class? 当你有可能将垃圾交换到另一个类时,你应该怎么写一个移动构造函数? Is there some way to detect this? 有没有办法检测到这个?

An object that is not initialized holds an indeterminate value until assigned another value [basic.indet]/1 . 未初始化的对象将保留不确定的值,直到分配另一个值[basic.indet] / 1 You're basically not allowed to do anything with an object holding an indeterminate value except for assigning it a proper value [basic.indet]/2 . 基本上不允许对具有不确定值的对象执行任何操作,除非为其指定适当的值[basic.indet] / 2 Since you're not even allowed to look at the value an object holds unless it has been initialized or assigned a value, there cannot possibly be a way to detect whether an object has been initialized just by looking at the object itself (because you're not allowed to even take a look). 由于您甚至不允许查看对象所持有的值,除非它已被初始化或赋值,因此无法通过查看对象本身来检测对象是否已初始化(因为您'不允许甚至看一看)。 Thus, strictly speaking, you're actually not just "swapping garbage values into the other class", you're invoking undefined behavior. 因此,严格地说,您实际上并不只是“将垃圾值交换到另一个类”,而是调用未定义的行为。 Garbage being swapped is just how that undefined behavior will typically manifest. 被交换的垃圾就是这种未定义的行为通常会表现出来的方式。

The solution to the problem is simple: Make sure that your pointer is always initialized to a valid value, eg, nullptr : 问题的解决方案很简单:确保指针始终初始化为有效值,例如nullptr

class myClass {
    //In this example, myClass must manually manage allocating
    //and freeing a memory buffer.
    char *mem = nullptr;

    //...
    //Regular constructor, copy constructor, etc
    //...

    myClass(myClass &&other) {
        //Swap our memory pointer with other's memory pointer
        char *tmp = other.mem;
        other.mem = mem;
        mem = tmp;
    } 

    //...
    //Destructor, other member functions, etc.
    //...
}

Rather than implement the move constructor yourself, consider, eg, just using a member of type std::unique_ptr and simply relying on the implicitly defined move constructor. 不要自己实现移动构造函数,而是考虑,例如,只使用std::unique_ptr类型的成员,并简单地依赖于隐式定义的移动构造函数。 For example: 例如:

class myClass
{
    std::unique_ptr<char[]> mem;

    // regular constructor, copy constructor, etc.

    myClass(myClass&&) = default;

    // other member functions, etc.
};

Don't swap the pointers in the constructor. 不要交换构造函数中的指针。 That's not how you write move constructors. 这不是你写移动构造函数的方式。 Swapping is for move-assignment, when both objects are live. 当两个对象都是实时交换时,交换是用于移动分配。

Constructors exist to initialize an object. 存在构造函数来初始化对象。 As such, the memory they start with is always in the "uninitialized" state. 因此,他们开始的记忆总是处于“未初始化”状态。 So unless you initialize a member (or it has a default constructor that initializes it for you), the member's value will start uninitialized. 因此,除非您初始化一个成员(或者它有一个为您初始化它的默认构造函数),否则该成员的值将开始未初始化。

The correct way to handle this is just copy the pointer in the member initializer, then null out the other one. 处理这个的正确方法是复制成员初始化程序中的指针,然后将另一个指针归零。

myClass(myClass &&other) : mem(other.mem) {
    other.mem = nullptr;
}

Or, with C++14 (and C++20 with a constexpr version), you can exchange the value : 或者,使用C ++ 14(和带有constexpr版本的C ++ 20),您可以交换值

myClass(myClass &&other)
  : mem(std::exchange(other.mem, nullptr))
{}

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM