问题描述
在C ++中,需要move构造函数来重置已移动的对象,在我看来,这在大多数情况下是析构函数所做的重复。
定义重置方法并同时在析构函数和移动构造函数中使用它是最佳方法是否正确?还是有更好的方法?
解决方法
移动构造函数通常会“窃取”参数所拥有的资源(例如,指向动态分配的对象的指针,文件描述符,TCP套接字,I / O流,运行线程等),而不是复制它们并留下参数处于某种有效但不确定的状态。对于某些类型,例如std::unique_ptr
,完全指定了移出状态。
不应释放“被盗”资源,因为这通常会导致错误。例如,“窃取”指针的move构造函数必须确保从对象移出的析构函数不会delete
指针。否则,将有双免。一种常见的实现方法是将移动的指针重置为nullptr
。
这里是一个例子:
struct Pointer {
int *ptr;
// obtain a ptr resource which we will manage
Pointer(int* ptr) : ptr{ptr} {}
// steal another object's ptr resource,assign it to nullptr
Pointer(Pointer &&moveOf) : ptr{moveOf.ptr} {
moveOf.ptr = nullptr;
}
// make sure that we don't delete a stolen ptr
~Pointer() {
if (ptr != nullptr) {
delete ptr;
}
}
};
定义重置方法并同时在析构函数和移动构造函数中使用它是最佳方法是否正确?还是有更好的方法?
这取决于所管理的资源,但是通常析构函数和移动构造函数会执行不同的操作。 move构造函数将窃取资源,而析构函数将释放资源(如果尚未被窃取)。
在C ++中,需要move构造函数来重置已移动的对象,在我看来,这在大多数情况下是析构函数所做的重复。
您是对的,经常会有重复的工作。这是因为C ++没有破坏性的移动语义,因此即使从某个对象移出了析构函数,它也仍将单独调用。在我显示的示例中,即使移动后,~Pointer()
仍需要被调用。这带有检查是否ptr == nullptr
的运行时成本。具有破坏性移动语义would be Rust的语言示例。
相关帖子:
- Why does C++ move semantics leave the source constructed?
- How does Rust provide move semantics?
- How to define a move constructor?
需要重置移动的对象,在我看来,这在大多数情况下是析构函数的重复。
在我看来,您在大多数情况下误解了析构函数的作用。
移动中“重置”(如您所称)的目的是设置对象的状态,以使其满足析构函数的内部前提条件(更一般地,任何类不变式)。如果构造函数不这样做,则该对象将无法销毁,这将违反约定和良好实践,并可能导致错误。
在许多情况下,析构函数可能无法执行此相同的“重置”。例如,无法将无效指针与有效指针区分开。这就是为什么智能指针的move构造函数将指针重置为null的原因。
定义重置方法并在析构函数和move-constructor中使用它是最佳方法吗?
目前尚不清楚何时可以使用。似乎并不典型。