【问题标题】:Unwanted destructor call in std::swapstd::swap 中不需要的析构函数调用
【发布时间】:2014-11-15 01:07:15
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 展示你的实现。
  • 向我们展示您的 move 特殊成员函数实现比写几段关于该问题的段落要容易得多。无论如何,听起来你没有将源对象中的指针设置为nullptr

标签: c++ c++11 stl move


【解决方案1】:

移动操作应该使被移动的对象处于可破坏状态,这可能与它进入移动时的状态不同。

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

【讨论】:

  • 不是unspecified状态而不是destructible吗?
  • @remyabel:要以任何方式可用,状态必须是“未指定但有效”,其中“有效”至少意味着“可破坏”。所有标准库类型都提供此保证;用户定义的类型,通常不能安全地移出。
【解决方案2】:

好的,经过大量研究,我了解了我的基本问题和解决方案。

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

加长版:
当您创建移动构造函数或移动赋值运算符时,您必须使被赋值的对象处于可以被破坏的默认状态。交换不能做到这一点。相反,您需要先从源对象中窃取资源,然后将源对象的成员设置为它们在构造函数运行时所处的状态。

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

【讨论】:

  • "释放内存时必须在析构函数中检查NULL", no delete nullptr;很好
  • 你是对的,你可以在空指针上调用 delete/free。但是,如果您正在做更多的事情来释放资源,那么进行检查是有意义的。对我来说,感觉更好。
猜你喜欢
  • 2018-08-12
  • 1970-01-01
  • 2018-06-19
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 2012-12-22
  • 1970-01-01
相关资源
最近更新 更多