C++11新しいプロパティ:移動の意味と右の参照

5242 ワード

右参照
従来のC++参照(左参照)は、識別子を左値に関連付ける.左の値は、変数名や参照を解除するポインタなどのデータを表す式で、プログラムはそのアドレスを取得することができます.C++11に右参照が追加されました.右の値は、名前の通り、右の値に関連付けることができます.すなわち、付与式の右側に表示されますが、アドレス演算子の値は適用されません.右の値には、文字列定数(Cスタイル文字列を除き、アドレスを表す)が含まれます.x+yなどの式および戻り値の関数(この関数が参照ではないことを条件とする).右の値の参照は記号&&&で表されます.次のようになります.
int x = 10, y = 23;
int & r = x + y;//  ,     
int && r2 = x + y;//  
double && r3 = std::sqrt(2.0);

右値を右値参照に関連付けると、右値が特定の位置に格納され、その位置のアドレスが取得されます.すなわち、演算子&を12または(x+y)に用いることはできないが、r 1に用いることができる.データを特定のアドレスに関連付けることで、右の値参照でデータにアクセスできます.
しかし、これはC++11が右値参照を導入する主な原因ではありません.
意味の移動
一部の関数では、式の実行によって一時オブジェクトが作成され、一時オブジェクトが破棄される直前に文字やクラスオブジェクトなどの変数を元の場所に残し、削除せずに所有権を変更して元のアドレスに保存されるようにしますが、一時オブジェクトのアイデンティティから抜け出す方法を移動意味と呼びます.
たとえば、関数が一時変数を返します.コンパイラは、一時オブジェクトのコピーを作成し、一時オブジェクトを破棄してから、一時オブジェクトのコピーを通常オブジェクトに割り当て、コピーを破棄します.意味を移動する方法は、一時オブジェクトの所有権を通常オブジェクトに直接渡すことです.
実は移動の意味は実際に元のデータを移動することを避けて、ただ記録を修正しただけです.
移動の意味を右の値で参照
移動の意味を実現するには、コンパイラにいつコピーする必要があり、いつ不要なのかを知らせる方法が必要です.これが右の値の引用が機能する場所です.
クラスでは、移動構造関数を定義できます.通常のレプリケーションコンストラクタ(左参照を使用し、一般的にconstとして宣言される)とは異なり、移動コンストラクタは右値参照をパラメータとして使用し、この参照は右値実パラメータに関連付けられます.また、複製コンストラクション関数とは異なり、移動コンストラクション関数はレコードを調整するだけです.所有権を新しいオブジェクトに移行する過程で、構造関数を移動して実パラメータを変更することがあります.これは、右の値参照パラメータがconstではないことを意味します.
次に、モバイルコンストラクション関数の使用方法と、レプリケーションコンストラクション関数との違いを示します.
//    ,                         
#include "iostream"
using namespace std;

class example{
 private:
    int n;      //number of elements
    char * pc;  //pointer to data
 public:
    example();
    explicit example(int k);
    example(int k, char ch);
    //copy constructor
    example(const example & f): n(f.n) {
        pc = new char[n];
        for (int i = 0; i < n; ++i)
            pc[i] = f.pc[i];
    }
    //move constructor
    example(example && f) : n(f.n) {
        pc = f.pc;
        f.pc = nullptr; //c++11
        f.n = 0;
    }
    ~example();
};

次の文は、深いレプリケーションを実行するレプリケーションコンストラクタを使用します.
example two = one;  //  f      one

次の文は、移動コンストラクション関数を使用します.
example three (one + two);

モバイルコンストラクション関数の定義では、pcを既存のデータに指向させ、これらのデータの所有権を取得します.この場合、pcとf.pcが同じ詩句を指す場合、プログラムが同じアドレスに対してdelete[]を2回呼び出すことができないため、構造関数を呼び出すと面倒になる.したがって、コンストラクション関数は、元のポインタを空のポインタに設定します.これもconstと宣言しない理由です.このような所有権を奪う方法は窃盗になる.
簡単に言えば、移動の意味を実装する2つのステップ:右の参照を使用して、コンパイラが移動の意味をいつ使用できるかを教え、移動構造関数を記述して必要な動作を提供する.
したがって、左参照を使用するレプリケーションコンストラクタと右参照を使用するコンストラクタを指定することで、2つのコンストラクタは初期化を2つのグループに分けます.左オブジェクトを使用してオブジェクトを初期化すると、複製コンストラクタが使用され、右オブジェクトを使用してオブジェクトを初期化すると、移動コンストラクタが使用されます.プログラマは、必要に応じてこれらの構造関数に異なる動作を与えることができる.
もちろん,構造関数に適用される移動意味の考慮は,付与演算子にも適用される.のように
example & example::operator=(example && f) {
    if (this == &f) return *this;
    delete []pc;
    n = f.n;
    pc = f.pc;
    f.n = 0;
    f.pc = nullptr; //C++11
    return *this;
}

移動賦課演算子ターゲット・オブジェクトの元のデータを削除し、ソース・オブジェクトの所有権をターゲットに譲渡します.
強制移動:
onetwoが2つのクラスオブジェクトであると仮定すると、次の文では付与演算子が呼び出されます.
one = two;

しかし、レプリケーション演算子を呼び出すのではなく、移動の意味を直接使用したい場合は?
c++11は、ヘッダファイルutilityのmove関数を提供して変換します.
#include
one = std::move(two);

移動付与演算子が定義されていることを前提としています.