提供正确的移动语义

时间:2012-01-26 22:03:42

标签: c++ c++11 rvalue-reference move-semantics

我目前正试图弄清楚如何使用包含指向已分配内存的指针的对象正确移动语义。我有一个大的数据结构,其中包含一个指向实际存储的内部原始指针(出于效率原因)。现在我添加了一个移动构造函数并移动operator=()。在这些方法中,我std::move()指向新结构的指针。但是我不知道如何处理来自其他结构的指针。

以下是我正在做的一个简单示例:

class big_and_complicated {
   // lots of complicated code
};

class structure {
public:
   structure() :
      m_data( new big_and_complicated() )
   {}

   structure( structure && rhs ) :
      m_data( std::move( rhs.m_data ) )
   {
      // Maybe do something to rhs here?
   }

   ~structure()
   {
      delete m_data;
   }

private:
   big_and_complicated * m_data;
}

int main() {
  structure s1;
  structure s2( std::move( s1 ) );
  return 0;
}

根据我的理解,在std::move( s1 )s2之后,s1上唯一安全的事情就是调用它的构造函数。但是据我所知,这将导致删除析构函数中s1中包含的指针,使s2无效。所以我猜我必须做一些事情来在std::move()指针时使析构函数安全。据我所知,这里最安全的做法是将它设置为移动对象中的0,因为这会在以后将delete转换为无操作。到目前为止,这种推理是否正确?或者std::move()是否足够聪明,可以为我指出指针,使其使用安全?到目前为止,我在实际的测试套件中看不到崩溃,但我还没确定实际调用了move-constructor。

1 个答案:

答案 0 :(得分:13)

“移动”指针与复制指针没什么不同,将move-from值设置为null('moving'在引号中,因为std::move不移动任何东西实际上,它只是更改了参数的值类别。只需复制rhs'指针,然后将其设置为nullptr

struct structure
{
    structure()
      : m_data{new big_and_complicated{}}
    { }

    structure(structure&& rhs)
      : m_data{rhs.m_data}
    {
        rhs.m_data = nullptr;
    }

    structure& operator =(structure&& rhs)
    {
        if (this != &rhs)
        {
            delete m_data;
            m_data = rhs.m_data;
            rhs.m_data = nullptr;
        }
        return *this;
    }

    ~structure()
    {
        delete m_data;
    }

private:
    big_and_complicated* m_data;

    structure(structure const&) = delete;             // for exposition only
    structure& operator =(structure const&) = delete; // for exposition only
}

更好的是,使用std::unique_ptr<big_and_complicated>代替big_and_complicated*,您无需自己定义任何内容:

#include <memory>

struct structure
{
    structure()
      : m_data{new big_and_complicated{}}
    { }

private:
    std::unique_ptr<big_and_complicated> m_data;
}

最后,除非您确实希望structure保持不可复制,否则最好只在big_and_complicated内部实现正确的移动语义并让structure持有big_and_complicated直接对象。