-1

私は 5 のルールを突き止めようとしてきましたが、オンラインの情報のほとんどは非常に複雑であり、サンプル コードは異なります。

私の教科書でさえ、このトピックをうまくカバーしていません。

移動中のセマンティクス:

私が理解しているように、テンプレート、右辺値、および左辺値は別として、移動セマンティクスは次のとおりです。

int other     = 0;           //Initial value
int number    = 3;           //Some data

int *pointer1 = &number;     //Source pointer
int *pointer2 = &other;      //Destination pointer

*pointer2     = *pointer1;   //Both pointers now point to same data 
 pointer1     =  nullptr;    //Pointer2 now points to nothing

//The reference to 'data' has been 'moved' from pointer1 to pointer2

コピーとは対照的に、これは次のようなものと同等です。

pointer1      = &number;     //Reset pointer1

int newnumber = 0;           //New address for the data

newnumber     = *pointer1;   //Address is assigned value
pointer2      =  &newnumber; //Assign pointer to new address

//The data from pointer1 has been 'copied' to pointer2, at the address 'newnumber'

右辺値、左辺値、またはテンプレートの説明は不要です。これらのトピックは無関係であると言えます。

最初の例が 2 番目の例よりも高速であるという事実は、当然のことです。また、C++ 11 より前の効率的なコードはすべてこれを行うことも指摘しておきます。

私の理解では、アイデアは、このすべての動作を標準ライブラリのきちんとした小さな演算子 move() にバンドルすることでした。

コピー コンストラクターとコピー代入演算子を記述するときは、単純に次のようにします。

Text::Text(const Text& copyfrom) {
    data  = nullptr;  //The object is empty
    *this = copyfrom;

}


const Text& Text::operator=(const Text& copyfrom) {
    if (this != &copyfrom) {
        filename = copyfrom.filename;
        entries  = copyfrom.entries;

        if (copyfrom.data != nullptr) {  //If the object is not empty
            delete[] data;
        }

        data = new std::string[entries];

        for (int i = 0; i < entries; i++) {
            data[i] = copyfrom.data[i];
            //std::cout << data[i];
        }
        std::cout << "Data is assigned" << std::endl;

    }

    return *this;
}

同等のものは、次のようになります。

Text::Text(Text&& movefrom){
    *this = movefrom;
}

Text&& Text::operator=(Text&& movefrom) {
    if (&movefrom != this) {
        filename = movefrom.filename;
        entries  = movefrom.entries;
        data     = movefrom.data;

        if (data != nullptr) {
            delete[] data;
        }

        movefrom.data    = nullptr;
        movefrom.entries = 0;
    }
    return std::move(*this);
}

これがうまくいかないことは確かなので、私の質問は次のとおりです。移動セマンティクスを使用して、このタイプのコンストラクター機能をどのように実現しますか?

4

1 に答える 1

0

あなたのコード例で何が証明されるべきか、またはこの質問の焦点が何であるかは、私には完全には明らかではありません。

C ++で「ムーブセマンティクス」というフレーズが意味する概念は何ですか?

それは「ムーブ クターとムーブ代入演算子をどのように記述すればよいか」です。?

これが私の概念を紹介する試みです。コード例を見たい場合は、コメントにリンクされている他の SO の質問を見てください。


直観的に、C および C++ では、オブジェクトはメモリ内に存在するデータの一部を表すと想定されています。さまざまな理由から、通常、そのデータを別の場所に送信する必要があります。

多くの場合、オブジェクトへのポインター/参照をデータが必要な場所に渡すだけの直接的なアプローチを取ることができます。その後、ポインターを使用して読み取ることができます。ポインターを取得してポインターを移動するのは非常に安価であるため、多くの場合、これは非常に効率的です。主な欠点は、オブジェクトが必要なだけ存続することを保証する必要があることです。そうしないと、ダングリング ポインター/参照とクラッシュが発生します。それを確認するのは簡単な場合もあれば、そうでない場合もあります。

そうでない場合、明らかな代替手段の 1 つは、参照渡しではなく、コピーを作成してそれを渡す (値渡し) ことです。データが必要な場所にデータの独自の個人用コピーがある場合、そのコピーが必要な限り存在することを保証できます。ここでの主な欠点は、コピーを作成する必要があることです。これは、オブジェクトが大きい場合、コストがかかる可能性があります。

3 番目の方法は、オブジェクトをコピーするのではなく移動することです。オブジェクトを移動しても複製されず、新しいサイトでのみ使用できるようになり、古いサイトでは使用できなくなります。明らかに、古いサイトでもう必要ない場合にのみこれを行うことができますが、その場合、これによりコピーが保存され、大きな節約になる可能性があります.

オブジェクトが単純な場合、これらの概念はすべて、実際に実装して正しく理解するのはかなり簡単です。たとえば、trivialオブジェクト、つまり簡単な構築/破棄を行うオブジェクトがある場合、C プログラミング言語で行うのとまったく同じように、memcpy. memcpyバイトのブロックのバイトごとのコピーを生成します。自明なオブジェクトが適切に初期化された場合、その作成には副作用がなく、その後の破棄にも副作用がないため、memcpyコピーも適切に初期化され、有効なオブジェクトになります。

ただし、最新の C++ では、オブジェクトの多くは自明ではありません。オブジェクトはヒープ メモリへの参照を「所有」し、RAII を使用してこのメ​​モリを管理します。これにより、オブジェクトの有効期間がリソースの使用に結び付けられます。たとえばstd::string、関数内にローカル変数として a がある場合、文字列は完全に「連続した」オブジェクトではなく、メモリ内の 2 つの異なる場所に接続されています。スタックには小さな固定サイズ (sizeof(std::string)実際には ) のブロックがあり、これにはポインターとその他の情報が含まれており、ヒープ上の動的にサイズ変更されたバッファーを指しています。形式的には小さな「制御」の部分だけがstd::stringオブジェクトですが、直感的にプログラマーの立場からすれば、バッファも文字列の「部分」であり、普段考えている部分です。あなたはできる'std::stringmemcpy--アドレスからバイトstd::string sをコピーして 2 番目の文字列を取得しようとするとどうなるかを考えてみてください。2 つの異なる文字列オブジェクトの代わりに、それぞれが同じバッファーを指す 2 つの制御ブロックができあがります。そして、最初のバッファが破棄されるとそのバッファが削除されるため、2 番目のバッファを使用するとセグメンテーション違反が発生するか、2 番目のバッファが破棄されると二重削除が発生します。sizeof(std::string)&s

一般に、重要な C++ オブジェクトを でコピーすることmemcpyは違法であり、未定義の動作を引き起こします。これは、オブジェクトの作成と破棄が ctors と dtors を使用してプログラマーによって定義された重要な結果をもたらす可能性があるという C++ の中心的な考え方の 1 つと競合するためです。オブジェクトの有効期間は、プログラムについて推論するために使用する不変条件を作成および強制するために使用できます。memcpyいくつかのバイトをコピーするだけの「愚かな」低レベルの方法です。プログラムを機能させる不変条件を強制するメカニズムをバイパスする可能性があります。これが、誤って使用すると未定義の動作を引き起こす可能性がある理由です。

代わりに、C++ には、重要なオブジェクトのコピーを安全に作成するために使用できるコピー コンストラクターがあります。オブジェクトに必要な不変条件を保持する方法でこれらを記述する必要があります。3 のルールは、実際にそれを行う方法に関するガイドラインです。

C++11 の「ムーブ セマンティクス」のアイデアは、C++98 の従来のコピー構築メカニズムを拡張および改良するために追加された新しいコア言語機能のコレクションです。具体的には、すでに移動できる単純なオブジェクトだけでなく、潜在的に複雑な RAII オブジェクトをどのように移動するかについてです。言語がコピー コンストラクターの場合と同様に、可能な場合にムーブ コンストラクターなどを自動的に生成するにはどうすればよいでしょうか。古いコードにバグを発生させたり、言語のコアとなる前提を壊したりせずに、時間を節約できる場合に移動オプションを使用するにはどうすればよいでしょうか。(これが、 と を使用したコード例が C++11 の移動セマンティクスとはほとんど関係がないとint私が言う理由です。)int *

したがって、5 のルールは 3 のルールの対応する拡張であり、特定のクラスに対しても move ctor / move 代入演算子を実装する必要があり、言語のデフォルトの動作に依存しない場合の条件を説明します。

于 2016-02-10T05:27:56.070 に答える