27

MSDN の記事How to: Write a Move Constuctorには、次の推奨事項があります。

クラスにムーブ コンストラクターとムーブ代入演算子の両方を提供する場合、ムーブ コンストラクターを記述してムーブ代入演算子を呼び出すことにより、冗長なコードを削除できます。次の例は、move 代入演算子を呼び出す move コンストラクターの改訂版を示しています。

// Move constructor.
MemoryBlock(MemoryBlock&& other)
   : _data(NULL)
   , _length(0)
{
   *this = std::move(other);
}

このコードはMemoryBlockの値を二重に初期化することで非効率的ですか? それとも、コンパイラは余分な初期化を最適化して取り除くことができますか? 移動代入演算子を呼び出すことによって、常に移動コンストラクターを作成する必要がありますか?

4

6 に答える 6

4

MemoryBlockクラスの私の C++11 バージョン。

#include <algorithm>
#include <vector>
// #include <stdio.h>

class MemoryBlock
{
 public:
  explicit MemoryBlock(size_t length)
    : length_(length),
      data_(new int[length])
  {
    // printf("allocating %zd\n", length);
  }

  ~MemoryBlock() noexcept
  {
    delete[] data_;
  }

  // copy constructor
  MemoryBlock(const MemoryBlock& rhs)
    : MemoryBlock(rhs.length_) // delegating to another ctor
  {
    std::copy(rhs.data_, rhs.data_ + length_, data_);
  }

  // move constructor
  MemoryBlock(MemoryBlock&& rhs) noexcept
    : length_(rhs.length_),
      data_(rhs.data_)
  {
    rhs.length_ = 0;
    rhs.data_ = nullptr;
  }

  // unifying assignment operator.
  // move assignment is not needed.
  MemoryBlock& operator=(MemoryBlock rhs) // yes, pass-by-value
  {
    swap(rhs);
    return *this;
  }

  size_t Length() const
  {
    return length_;
  }

  void swap(MemoryBlock& rhs)
  {
    std::swap(length_, rhs.length_);
    std::swap(data_, rhs.data_);
  }

 private:
  size_t length_;  // note, the prefix underscore is reserved.
  int*   data_;
};

int main()
{
   std::vector<MemoryBlock> v;
   // v.reserve(10);
   v.push_back(MemoryBlock(25));
   v.push_back(MemoryBlock(75));

   v.insert(v.begin() + 1, MemoryBlock(50));
}

正しい C++11 コンパイラでMemoryBlock::MemoryBlock(size_t)は、テスト プログラムで 3 回だけ呼び出す必要があります。

于 2013-10-10T21:43:04.793 に答える
1

大きなパフォーマンスの違いに気付くことはないと思います。move コンストラクターから move 代入演算子を使用することをお勧めします。

ただし、より論理的であるため、 std::move の代わりに std::forward を使用したいと思います。

*this = std::forward<MemoryBlock>(other);
于 2013-06-14T22:42:44.127 に答える
0

移動代入演算子が何をするかによって異なります。リンク先の記事を参照すると、次の部分が表示されます。

  // Free the existing resource.
  delete[] _data;

したがって、このコンテキストでは、最初に初期化せず_dataにムーブ コンストラクターからムーブ代入演算子を呼び出すと、初期化されていないポインターを削除しようとすることになります。したがって、この例では、非効率であろうとなかろうと、値を初期化することが実際に重要です。

于 2013-06-15T07:25:09.313 に答える