标准::交换中不需要的析构函数调用

Unwanted destructor call in std::swap

本文关键字:析构 函数调用 交换 标准 不需要      更新时间:2023-10-16

我遇到了std::swap的问题。 我必须交换一个对象。 该对象在其析构函数中释放内存。 我编写了一个移动构造函数和一个移动赋值运算符,用于将指针复制到该内存。 默认构造函数将该指针设置为 NULL。

当然,我

有一个常规的复制构造函数和赋值运算符,但它们分配和复制内存,这显然不是我想要的交换操作。

当我调用 std::swap 时,它会使用我的移动构造函数从_Left创建一个临时对象。 然后,它使用我的移动赋值运算符将_Right移动到_Left,最后,它将临时对象移动到_Right。

当你到达std::swap的底部时,这一切看起来都很好。 但是,当您走出它的底部时,临时对象的析构函数将运行,从而释放_Right对象预期具有的内存。

通常接受的方法是什么? 我希望避免编写交换函数,因为这是移动构造函数/移动赋值运算符的重点。 我必须使用自己的 swap() 来避免这种情况吗?

移动操作应使要移动的对象处于可破坏状态,该状态可能与移动状态不同。

如果我理解正确,听起来您的对象的 move-ctor 需要将要移动的对象中的指针设置为它们附带的值以外的其他内容。这些现在移动的对象上的后续 dtor 应该单独保留它们曾经引用的内存。

好的,经过更多的研究,我明白了我的基本问题和解决方案。

简短版本:
在移动构造函数中,从源对象复制值,然后将它们设置为默认构造函数运行时的值。

长版本:
创建移动构造函数或移动赋值运算符时,必须将要从中分配的对象保留为可销毁的默认状态。 交换无法实现此目的。 相反,您需要首先从源对象窃取资源,然后将源对象的成员设置为构造函数运行时它们所处的状态。

交换让你陷入热水的一个例子是你正在处理指针的地方,就像我一样。 您无法复制或交换它。 如果复制指针,则指针值在被销毁时仍将位于源对象中。 如果交换指针,则它将是一个未初始化的值,这可能会使析构函数崩溃。相反,应复制指针值,然后将源对象中的指针设置为 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;
}