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

Mah*_*koe 2 c++ stdvector move-semantics

我的C ++代码中有一个具有自己的move构造函数的类。此处显示了简化版本:

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.
    //...
}
Run Code Online (Sandbox Code Playgroud)

在正常情况下,这可以正常工作。但是,最近我需要对这些对象进行向量处理:

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

在经历了段错误并逐步使用gdb之后,我最终发现应该是显而易见的:如果尝试从右值引用构造对象,则可能导致在未初始化的内存上使用move构造函数。

当您有可能将垃圾交换到other类中时,应该如何编写move构造函数?有什么方法可以检测到这一点吗?

Mic*_*zel 6

当您有可能将垃圾交换到另一个类中时,应该如何编写move构造函数?有什么方法可以检测到这一点吗?

未初始化的对象将保留不确定的值,直到为其分配了另一个值[basic.indet] / 1为止。基本上,除了为对象分配适当的值[basic.indet] / 2之外,基本上不允许对它具有不确定的值。由于您甚至不被允许查看对象持有的值,除非对象已被初始化或分配了值,所以不可能仅通过查看对象本身来检测对象是否已初始化(因为您已经甚至不允许看)。因此,严格来说,实际上,您并不仅仅是在“将垃圾值交换到另一个类中”,而是在调用未定义的行为。交换垃圾就是这种未定义行为通常表现出来的方式。

该问题的解决方案很简单:确保始终将指针初始化为有效值,例如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.
    //...
}
Run Code Online (Sandbox Code Playgroud)

与其自己实现move构造函数,不如考虑仅使用type的成员std::unique_ptr并仅依靠隐式定义的move构造函数。例如:

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

    // regular constructor, copy constructor, etc.

    myClass(myClass&&) = default;

    // other member functions, etc.
};
Run Code Online (Sandbox Code Playgroud)