【问题标题】:Providing correct move semantics提供正确的移动语义
【发布时间】:2012-01-26 22:03:41
【问题描述】:

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

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

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()ing 指针时做一些事情来使析构函数安全。据我所知,这里最安全的做法是将其设置为移动对象中的0,因为这会在以后将delete 变为无操作。到目前为止,这个推理是否正确?或者std::move() 是否真的足够聪明,可以为我清空指针,使其使用安全?到目前为止,我在实际的测试套件中没有看到任何崩溃,但我不确定实际调用了 move-constructor。

【问题讨论】:

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


    【解决方案1】:

    “移动”指针与复制指针没有什么不同,并且 not 是否将移动的值设置为 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 对象。

    【讨论】:

    • @ildjarn:不幸的是,直接持有 big_and_complicated 是不可能的,因为我以后需要将此指针存储在其他地方。其他地方取决于结构(想想:迭代器),所以这没关系。如果有人将迭代器保留的时间比实际结构长,那是他们的错。
    • @LiKao :听起来可能是std::shared_ptr&lt;&gt;std::weak_ptr&lt;&gt; 的候选人。 :-]
    • ildjarn:这就是我之前的内容,但后来我优化了。
    • 问题已经从答案中删除了吗?
    • @Candy :如果您指的是霍华德指出的那个,是的,那是在之后立即修复的。 :-]
    猜你喜欢
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    • 2016-03-10
    • 1970-01-01
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    相关资源
    最近更新 更多