C++ の Move コンストラクタ

胡金庫 2023年10月12日
C++ の Move コンストラクタ

この記事では、C++ で move コンストラクターを使用する方法を紹介します。

移動コンストラクターを使用して、C++ のクラスに効率的なコピー制御を提供する

クラスのコピー制御は、クラスオブジェクトがコピー、移動、割り当て、または破棄されたときに何が起こるかを指定するために必要なコア関数を定義します。これらの関数には、copy-constructor 関数や move-constructor 関数など、特別な C++ の命名法があり、オブジェクトを同じタイプの別のオブジェクトで初期化する方法を定義します。コピー代入関数とムーブ代入関数は、オブジェクトを同じタイプのオブジェクトに割り当てる方法を定義します。デストラクタは、オブジェクトがスコープ外になったときに実行されるルーチンを処理します。これらの関数の一部はユーザーが定義する可能性が最も高いですが、そうでない場合は、コンパイラー自体がデフォルトのプロトタイプを作成します。

クラスオブジェクトが動的メモリを管理し、データが非常に大きい場合、コピー操作はかなり計算集約的になる可能性があります。それらは、パフォーマンスに影響を与える重要なリソースを使い果たす可能性があります。したがって、多くの場合、移動コンストラクターを使用して、動的データを新しいメモリ位置にコピーせずに再割り当てします。これは、古いオブジェクトのポインタを、新しく初期化または割り当てられたオブジェクトの対応するメンバーに割り当てることによって実現されます。次の例には move コンストラクターが含まれておらず、copy コンストラクターが複数回呼び出され、デフォルトのコンストラクターに委任されていることに注意してください。

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

class MyClass {
 private:
  int* data;

 public:
  explicit MyClass(int d) {
    data = new int;
    *data = d;
    cout << "Constructor 1 is called" << endl;
  };

  MyClass(const MyClass& source) : MyClass(*source.data) {
    cout << "Copy Constructor is called " << endl;
  }

  int getData() const { return *data; }

  ~MyClass() {
    delete data;
    cout << "Destructor is called" << endl;
  }
};

void printVec(const vector<MyClass>& vec) {
  for (const auto& i : vec) {
    cout << i.getData() << " ";
  }
  cout << endl;
}

int main() {
  vector<MyClass> vec;

  vec.push_back(MyClass(10));
  vec.push_back(MyClass(11));
  printVec(vec);
  cout << "------------------" << endl;

  return EXIT_SUCCESS;
}

出力:

Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Constructor 1 is called
Constructor 1 is called
Copy Constructor is called
Constructor 1 is called
Copy Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called

通常、最初の引数として r 値参照(&&表記で示される)を取る必要がある移動コンストラクターを定義すると、タイプ MyClass の新しい要素が追加されるため、ベクトルの初期化がより効率的になります。移動コンストラクターは新しいメモリを割り当てず、渡されたオブジェクトが保持する場所を引き継ぐため、前のオブジェクトのメンバーに nullptr を割り当てる必要があります。それ以外の場合、デストラクタは同じメモリ位置を 2 回解放しようとし、実行時エラーをスローします。

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

class MyClass {
 private:
  int* data;

 public:
  explicit MyClass(int d) {
    data = new int;
    *data = d;
    cout << "Constructor 1 is called" << endl;
  };

  MyClass(const MyClass& source) : MyClass(*source.data) {
    cout << "Copy Constructor is called " << endl;
  }

  MyClass(MyClass&& source) noexcept : data(source.data) {
    source.data = nullptr;
    cout << "Move Constructor is called" << endl;
  }

  int getData() const { return *data; }

  ~MyClass() {
    delete data;
    cout << "Destructor is called" << endl;
  }
};

void printVec(const vector<MyClass>& vec) {
  for (const auto& i : vec) {
    cout << i.getData() << " ";
  }
  cout << endl;
}

int main() {
  vector<MyClass> vec;

  vec.push_back(MyClass(10));
  vec.push_back(MyClass(11));
  printVec(vec);
  cout << "------------------" << endl;

  return EXIT_SUCCESS;
}

出力:

Constructor 1 is called
Move Constructor is called
Destructor is called
Constructor 1 is called
Move Constructor is called
Move Constructor is called
Destructor is called
Destructor is called
10 11
------------------
Destructor is called
Destructor is called
著者: 胡金庫
胡金庫 avatar 胡金庫 avatar

DelftStack.comの創設者です。Jinku はロボティクスと自動車産業で8年以上働いています。自動テスト、リモートサーバーからのデータ収集、耐久テストからのレポート作成が必要となったとき、彼はコーディングスキルを磨きました。彼は電気/電子工学のバックグラウンドを持っていますが、組み込みエレクトロニクス、組み込みプログラミング、フロントエンド/バックエンドプログラミングへの関心を広げています。

LinkedIn Facebook

関連記事 - C++ Class