C++11の右値参照と移動の意味

7190 ワード

右の値の参照は、メモリの重複申請、コピー、解放を減らすことで、C++プログラムのパフォーマンスを効果的に向上させることができるC++11に追加された新しい参照タイプです.右値参照とは何かを理解するには、まずC++11のlvalue、rvalue、xvalueを理解する必要があります.詳細は、C++11の左値、右値、およびアポトーシス値を参照してください.
右値参照と通常の非常量参照の唯一の違いは、非常量参照(non-const reference)は通常非一時変数をバインドし、右値参照は通常一時変数をバインドすることである.
定数参照(const reference)は、一時変数をバインドすることも、非一時変数をバインドすることもできます.定数参照(const reference)のこの特性のため、クラスの宣言にコンストラクション関数が移動していない場合、すなわちA(A&&rhs){}は、一時変数を伝達し、A(const A&rsh){}コンストラクション関数を呼び出して構築することができる.
深いコピーを避けるための右の参照
深いコピーの例
class A
{
public:
    A() : m_ptr(new int(0))
    {
        cout << "construct" << endl;
    }
    A(const A& rhs) : m_ptr(new int(*rhs.m_ptr)) //       
    {
        cout << "copy construct" << endl;
    }
    ~A()
    {
        cout << "destruct" << endl;
        delete m_ptr;
    }
private:
    int* m_ptr;
};

A Get(bool flag)
{
    A a, b;
    if (flag)
    {
        return a;
    }
    return b;
}

int main()
{
    A a = Get(false);//        
    return 0;
}

実行結果:construct construct copy construct destruct destruct destruct destruct
質問:上記の例でGet関数は一時変数を返し、この一時変数をコピーして新しいオブジェクトaを構築します.一時変数はコピー構造が完了した後に破棄されます.スタックメモリが大きいと、このコピー構造は時間がかかり、性能の損失をもたらします.一時オブジェクトのコピー構造を避ける方法はありますか.答え:右の引用
深いコピーを改善する例
class Aに次の移動コンストラクタを追加します.
A(A&& rhs) : m_ptr(rhs.m_ptr) //      
{
  rhs.m_ptr = nullptr;
  cout << "move construct" << endl;
}

実行結果:construct construct move construct destruct destruct destruct destruct
  • が改良された後、コピーコンストラクション関数を呼び出すのではなく、モバイルコンストラクション関数(move construct)を呼び出す.モバイルコンストラクション関数の実装から、そのパラメータは右値参照タイプのパラメータT&&であり、ここでは深いコピーはなく、浅いコピーしかなく、一時オブジェクトへの深いコピー、リソースの重複申請と解放を回避し、性能を向上させることが明らかになった.
  • Get(false)関数は右の値を返すため、パラメータタイプA&&の移動構造関数が呼び出される.
  • モバイルコンストラクション関数は、一時的なオブジェクトのリソースを浅いコピーするだけで、深いコピーのオーバーヘッドを回避し、パフォーマンスを向上させます.
  • これはいわゆる移動意味(move semantic)であり、右値参照の重要な目的は移動意味をサポートすることである.

  • 右の値は主に次の2つの問題を解決するために参照されます.
  • 移動語義(move semantic)
  • パーフェクト転送(perfect forwarding)
  • 移動の意味(move semantic)
    質問:移動の意味は右の値の参照によって一時的な値に一致しますが、通常の左の値も移動の意味によってパフォーマンスを最適化できますか?-C++11上記の問題を解決するためにstd::moveメソッドは左値を右値に変換し、移動意味の適用を容易にする.-moveは実際には何も移動できません.彼の唯一の機能は、左の値を強制的に右の値の参照に変換することで、右の値の参照でこの値を使用することができます.-It’s confusing, because “move” is a verb; it sounds like it should do something. But in fact it’s just a cast, just like const_cast or reinterpret_cast. Like those casts, std::move just lets us use an object in a different way; on its own, it doesn’t do anything.
    シミュレーションstringにおける移動構造関数の例
      class string {
     public:
      string(const string& other);  // Copy constructor, exists pre C++11
    
      string(string&& other) {      // Move constructor, new in C++11
        length = other.length;
        capacity = other.capacity;
        data = other.data;
        other.data = nullptr;
      }
    
     private:
      size_t length;
      size_t capacity;
      const char* data;
    };
  • モバイルコンストラクタの処理フロー:①一時変数otherのポインタを取得し、他の非ポインタメンバーをコピーし、②一時オブジェクトのポインタを空に設定し、other.data = nullptr;.他の人をdataが空に設定されています.dataは2回解放され、プログラム異常を引き起こします.次にstringを使用する例を示します.一時変数構造を使用すると、移動構造関数が呼び出されます.
  • string a(get_string());  // move constructor
    string b(a);             // copy constructor
    string c(std::move(b));  // move constructor

    注意:stringに移動構造関数string(string&&other)がない場合、string c(std::move(b));のパラメータはconst referenceであるため、上記のstring(const string& other)はコピー構造関数を呼び出して構造されます.
    シミュレーションstringの割り当てオペレータ
    上記の分析から、stringの付与オペレータにもこの書き方が適用できることがわかります.コードは次のとおりです.
    class string {
     public:
      string& operator=(const string& other); // Copy assn operator, pre C++11
      string& operator=(string&& other) {     // Move assn operator, new in C++11
        length = other.length;
        capacity = other.capacity;
        delete data;  // OK even if data is null
        data = other.data;
        other.data = nullptr;
        return *this;
      }
    };
    
    string a, b;
    a = get_string();  // Move assignment
    a = b;             // Copy assignment
    a = std::move(b);  // Move assignment

    C++11では、コンパイラがクラスに移動コンストラクタと移動付与オペレータを自動的に追加するため、C++11ではクラスを宣言し、コンパイラはデフォルトでコンストラクタ、コピーコンストラクタ、解析関数、移動コンストラクタ、移動付与操作を追加し、クラスの「5馬車」と呼ばれ、C++11以前の「3馬車」ではありません.
    ユーザーがコピーコンストラクタをカスタマイズすると、コンパイラは自動的にモバイルコンストラクタと移動付与操作を追加しません.これらの関数の後に=deleteを追加すると、コンパイラが自動的に対応する関数を追加することを阻止します.
    パーフェクト転送(perfect forwarding)
    次のC++11テンプレートの導出、参照の折りたたみ、完璧な転送を回します.
    リファレンス
  • http://thbecker.net/articles/rvalue_references/section_01.html
  • A Practical Introduction to C++11 Rvalue References