本文へスキップ
BecomeCoder

C++コース · 第12章 演算子オーバーロードとコピー/ムーブ · レッスン51

ムーブセマンティクス ― コピーせず移す

ブラウザで完結

導入

大きなデータを「コピー」するのは無駄なことがあります。もう使わない一時オブジェクトなら、中身を「移す(ムーブ)」だけで十分です。C++11 で入った「ムーブセマンティクス」は、モダン C++ の性能を支える重要な仕組みです。

説明

カギは「rvalue 参照 型&&」です。名前のない一時オブジェクト(右辺値)を受け取り、その中身を奪って(ポインタを付け替えて)自分のものにします。コピーのように複製しないので高速です。

flowchart LR
  a["コピー:中身を丸ごと複製<br/>元も残る(遅い)"]
  b["ムーブ:ポインタを付け替えるだけ<br/>元は空になる(速い)"]
  a -->|"一時オブジェクトなら"| b
#include <iostream>
#include <utility>   // std::move
using namespace std;

class Buffer {
    int* data;
    int size;
public:
    Buffer(int n) : data(new int[n]()), size(n) {}

    // ムーブコンストラクタ:other から資源を奪う
    Buffer(Buffer&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;   // 元は空に(デストラクタで二重解放しないため)
        other.size = 0;
        cout << "ムーブされた" << endl;
    }
    ~Buffer() { delete[] data; }
    int getSize() const { return size; }
};

int main() {
    Buffer a(1000);
    Buffer b = std::move(a);   // a をムーブ(コピーしない)
    cout << b.getSize() << endl;   // 1000
    cout << a.getSize() << endl;   // 0(中身は b に移った)
    return 0;
}

std::move は「この値はもう使わないので、ムーブしてよい」という目印です(実際に動かすわけではなく、rvalue 参照に変換するだけ)。vector に大きな要素を入れるときなどに、内部で自動的にムーブが使われて高速化されています。

まとめ

ムーブは「中身を複製せず移す」高速な受け渡しです。型&&(rvalue 参照)で受け取り、std::move で明示的にムーブできます。移した後の元オブジェクトは「空だが破棄はできる」状態にしておくのが約束です。

実際に動かしてみよう

下のエディタにC++を書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のC++コンパイラ(clang + libc++)がその場でコンパイルして実行し、出力が表示されます。std::string・vector・クラス・テンプレート・STL などモダンC++がそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(cin)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。