[解決済み] ムーブセマンティクスとは何ですか?
質問
ソフトウェア工学のラジオを聴き終えたところです Scott Meyersのポッドキャストインタビュー について C++0x . ほとんどの新機能は私にとって意味があり、1つの例外を除いて、私は今、実際にC++0xに興奮しています。私はまだ 移動セマンティクス ... 具体的にはどのようなものなのでしょうか?
どのように解決するのですか?
ムーブのセマンティクスを理解するには、サンプルコードが最もわかりやすいと思います。ヒープで割り当てられたメモリブロックへのポインタを保持するだけの、非常に単純な文字列クラスから始めましょう。
#include <cstring>
#include <algorithm>
class string
{
char* data;
public:
string(const char* p)
{
size_t size = std::strlen(p) + 1;
data = new char[size];
std::memcpy(data, p, size);
}
メモリを自分で管理することにしたため、以下のように 三の法則 . 代入演算子を書くのを先延ばしにして、とりあえずデストラクタとコピーコンストラクタだけを実装することにします。
~string()
{
delete[] data;
}
string(const string& that)
{
size_t size = std::strlen(that.data) + 1;
data = new char[size];
std::memcpy(data, that.data, size);
}
copyコンストラクタは、文字列オブジェクトをコピーすることの意味を定義しています。パラメータ
const string& that
は文字列型のすべての式に束縛され、次の例でコピーを作成することができます。
string a(x); // Line 1
string b(x + y); // Line 2
string c(some_function_returning_a_string()); // Line 3
さて、ここからは移動のセマンティクスに関する重要な洞察です。最初の行で
x
を検査したいと思うかもしれないので、この深いコピーは本当に必要なのでしょうか?
x
を使用すると、後で非常に驚きます。
x
は、何らかの形で変化していたのです。私が今言ったことに気づきましたか?
x
を3回(この文章を含めると4回)意味し、その間に
全く同じオブジェクト
毎回ですか?私たちは、このような表現を
x
"lvalues"です。
2行目と3行目の引数はlvalueではなくrvalueです。なぜなら、基礎となる文字列オブジェクトには名前がないので、クライアントは後の時点でそれらを再び検査する手段を持たないからです。
rvalueは一時的なオブジェクトを表し、次のセミコロンで(より正確には、語彙的にrvalueを含む完全な式の終わりで)破棄されます。これは重要なことです。
b
と
c
であれば、ソースの文字列で好きなことができますし
クライアントには違いがわからない
!
C++0x では、特に "rvalue reference" と呼ばれる新しいメカニズムが導入されています。 関数のオーバーロードでrvalueの引数を検出することができます。必要なのは、rvalue参照パラメータを持つコンストラクタを書くことだけです。そのコンストラクタの内部では、次のようなことができる。 好きなように のままにしておくと、ソースを いくつか 有効な状態です。
string(string&& that) // string&& is an rvalue reference to a string
{
data = that.data;
that.data = nullptr;
}
ここで何をしたのか?ヒープデータを深くコピーするのではなく、ポインタをコピーして、元のポインタをnullにしました(ソースオブジェクトのデストラクタから「delete[]」が「盗んだばかりのデータ」を解放するのを防ぐためです)。事実上、私たちは元々ソース文字列に属していたデータをquot;stolen"してしまったのです。ここでも重要なのは、どのような状況でもクライアントがソースが変更されたことを検知できないことです。ここでは実際にコピーを行わないので、このコンストラクタを「移動コンストラクタ」と呼びます。このコンストラクタの仕事は、リソースをコピーするのではなく、あるオブジェクトから別のオブジェクトに移動させることです。
おめでとうございます!これで移動のセマンティクスの基本が理解できましたね。続けて、代入演算子を実装してみましょう。もしあなたが コピーとスワップのイディオム 例外安全性に関連した素晴らしいC++のイディオムなので、覚えて帰ってきてください。
string& operator=(string that)
{
std::swap(data, that.data);
return *this;
}
};
という質問に対して、「ここでは必要ない」というのが私の答えです(笑)。
パラメータを渡すことに注意してください。
that
値で
ということで
that
は、他の文字列オブジェクトと同様に初期化する必要があります。まさに、どのように
that
は初期化されるのでしょうか?昔は
C++98
この場合、答えは「コピーコンストラクタ」です。C++0x では、コンパイラは、代入演算子の引数が l 値か r 値かに基づいてコピー コンストラクタと移動コンストラクタのどちらかを選択します。
ということは、もしあなたが
a = b
は、その
コピーコンストラクタ
を初期化します。
that
(なぜなら、式
b
はl値です)、代入演算子はその内容を新しく作成された深いコピーと交換します。コピーを作り、そのコピーと中身を交換し、そしてスコープを出てコピーを取り除く。新しいことは何もない。
しかし、もしあなたが
a = x + y
は、その
移動コンストラクタ
を初期化します。
that
(なぜなら、式
x + y
はr値である)ので、ディープコピーは行われず、効率的な移動のみが行われます。
that
はまだ引数とは独立したオブジェクトですが、その構築は些細なことでした。
ヒープデータをコピーする必要がなく、ただ移動させるだけだからです。コピーする必要がなかったのは
x + y
はrvalueであり、ここでもrvalueで示される文字列オブジェクトからの移動は問題ない。
要約すると、コピーコンストラクタはディープコピーを作成します。 一方、移動コンストラクタはポインタをコピーするだけで、ソースのポインタをヌルに設定できます。この方法では、クライアントがオブジェクトを再び検査する方法がないため、ソース オブジェクトを NULL にしても問題ありません。
この例で、要点が伝わったでしょうか。rvalueの参照と移動のセマンティクスにはもっと多くのことがありますが、シンプルにするために意図的に省いています。もっと詳しく知りたい場合は、以下を参照してください。 補足回答 .
関連
-
[解決済み】C++でユーザー入力を待つ【重複あり
-
[解決済み】エラー。switchステートメントでcaseラベルにジャンプする
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] ルール・オブ・スリーとは?
-
[解決済み] コピーアンドスワップ慣用句とは?
-
[解決済み] スマートポインターとは何ですか?
-
[解決済み] std::move()とは何ですか?また、どのような場合に使用するのですか?
-
[解決済み】C/C++の"-->"演算子とは何ですか?
-
[解決済み】C++11のラムダ式って何?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】coutはstdのメンバではない
-
[解決済み] エラーが発生する。ISO C++は型を持たない宣言を禁じています。
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み] 非常に基本的なC++プログラムの問題 - バイナリ式への無効なオペランド
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】浮動小数点例外エラーが発生する: 8
-
[解決済み】「std::operator」で「operator<<」にマッチするものがない。
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?