我遇到了std :: swap的问题。我必须交换一个对象。该对象在其析构函数中释放内存。我编写了一个移动构造函数和一个移动赋值运算符,它将指针复制到该内存。默认构造函数将该指针设置为NULL。
当然,我有一个常规的复制构造函数和赋值运算符,但是它们分配和复制内存,这显然不是我想要的交换操作。
当我调用std :: swap时,它使用我的移动构造函数从_Left创建一个临时对象。然后,它使用我的移动赋值运算符将_Right移动到_Left,最后,它将临时对象移动到_Right。
当你到达std :: swap的底部时,这一切看起来都很好。但是,当你走出它的底部时,temp对象的析构函数会运行,从而释放_Right对象所期望的内存。
正常接受的方式是什么?我希望避免编写交换函数,因为这是移动构造函数/移动赋值运算符的重点。我是否必须使用自己的swap()来避免这种情况?
答案 0 :(得分:4)
移动操作应该使对象移动到可破坏状态,这可能与移动时的状态不同。
如果我正确理解问题,那么听起来你的对象的移动需要将被移动对象中的指针设置为除了它们所带来的值之外的其他东西。对那些现在移动的对象的后续dtor应该留下他们曾经单独提到的内存。
答案 1 :(得分:0)
好的,经过更多的研究,我了解我的根本问题和解决方案。
简短版本:
在移动构造函数中,复制源对象中的值,然后将它们设置为默认构造函数运行时的值。
长版本:
创建移动构造函数或移动赋值运算符时,必须将所分配的对象保留为可以销毁的默认状态。交换不能实现这一点。相反,您需要先从源对象中窃取资源,然后将源对象的成员设置为构造函数运行时的状态。
在热水中交换你的一个例子就是你像我一样处理指针。您无法复制或交换它。如果复制指针,则指针值在被破坏时仍将在源对象中。如果你交换指针,那么它将是一个未初始化的值,这可能会破坏你的析构函数。相反,您应该复制指针值,然后将源对象中的指针设置为NULL。当然,释放内存时必须在析构函数中检查NULL。
如果您正在窃取的其中一个成员是您控制的类实例,那么您应该使用std :: move来复制它,因为这将调用其移动赋值运算符,同时使其保持可破坏性。
<强>加成强>:
不要重新发明轮子。只需从移动构造函数中调用移动赋值运算符。
void CObject::CObject(CObject&& other)
{
*this = std::move(other);
}
CObject& CObject::operator = (CObject&& other)
{
// m_pResource is a pointer.
// Copy the value.
m_pResource = other.m_pResource;
// Set other to default state so the destructor doesn't free it.
other.m_pResource = NULL;
// m_iCount is an int who's value does not matter in the destructor.
m_iCount = other.m_iCount;
// m_Obj is a class instance.
// Invoking move semantics will use its move assignment operator if it has one.
m_Obj = std::move(other.m_Obj);
return *this;
}