我的C ++代码中有一个类,它具有自己的move构造函数。简化的版本如下所示:
$ ./bin/paticle particles.txt
frame: 0 particles: 5
1.23 2.33 4.56
1.23 2.33 5.56
1.23 2.33 6.56
1.23 2.33 7.56
1.23 2.33 8.56
frame: 1 particles: 5
2.23 2.33 4.56
2.23 3.33 5.56
2.23 4.33 6.56
2.23 5.33 7.56
2.23 6.33 8.56
在正常情况下,这可以正常工作。但是,最近我需要对这些对象进行向量处理:
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.
//...
}
在遇到段错误并逐步使用gdb之后,我最终发现应该是显而易见的:如果尝试从右值引用构造对象,这可能导致在未初始化的内存上使用move构造函数 。
当您可能将垃圾交换到vector<myClass> v;
v.reserve(10); //Make space, but do not construct
v.push_back(myClass()); //Problem!
类中时,应该如何编写move构造函数?有什么方法可以检测到这一点吗?
答案 0 :(得分: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.
//...
}
考虑而不是自己实现move构造函数,例如,考虑仅使用类型为std::unique_ptr
的成员,并仅依靠隐式定义的move构造函数。例如:
class myClass
{
std::unique_ptr<char[]> mem;
// regular constructor, copy constructor, etc.
myClass(myClass&&) = default;
// other member functions, etc.
};
答案 1 :(得分:2)
不要在构造函数中交换指针。那不是您编写move构造函数的方式。当两个对象都处于活动状态时,交换用于移动分配。
存在构造函数以初始化对象。这样,它们开始时的内存始终处于“未初始化”状态。因此,除非您初始化成员(或者它具有为您初始化它的默认构造函数),否则该成员的值将开始未初始化。
正确的处理方法是将指针复制到成员初始化器中,然后将另一个初始化为空。
myClass(myClass &&other) : mem(other.mem) {
other.mem = nullptr;
}
或者,对于C ++ 14(和具有constexpr
版本的C ++ 20),您可以exchange the value:
myClass(myClass &&other)
: mem(std::exchange(other.mem, nullptr))
{}