31

移動コンストラクターが呼び出されるタイミングとコピーコンストラクターが呼び出されるタイミングについて混乱しています。私は次の情報源を読みました:

移動コンストラクターがC++0xで呼び出されない

C++11でセマンティクスと右辺値参照を移動します

msdn

これらのソースはすべて、複雑すぎるか(簡単な例が必要です)、moveコンストラクターの記述方法のみを示しており、呼び出し方法は示していません。より具体的にするために、私は簡単な問題を書きました:

const class noConstruct{}NoConstruct;
class a
{
private:
    int *Array;
public:
    a();
    a(noConstruct);
    a(const a&);
    a& operator=(const a&);
    a(a&&);
    a& operator=(a&&);
    ~a();
};

a::a()
{
    Array=new int[5]{1,2,3,4,5};
}
a::a(noConstruct Parameter)
{
    Array=nullptr;
}
a::a(const a& Old): Array(Old.Array)
{

}
a& a::operator=(const a&Old)
{
    delete[] Array;
    Array=new int[5];
    for (int i=0;i!=5;i++)
    {
        Array[i]=Old.Array[i];
    }
    return *this;
}
a::a(a&&Old)
{
    Array=Old.Array;
    Old.Array=nullptr;
}
a& a::operator=(a&&Old)
{
    Array=Old.Array;
    Old.Array=nullptr;
    return *this;
}
a::~a()
{
    delete[] Array;
}

int main()
{
    a A(NoConstruct),B(NoConstruct),C;
    A=C;
    B=C;
}

現在、A、B、およびCはすべて異なるポインタ値を持っています。Aに新しいポインターを、BにCの古いポインターを、Cにnullポインターを持たせたいと思います。

ややオフトピックですが、これらの新機能について詳細に学ぶことができるドキュメントを提案できれば、私は感謝し、おそらくこれ以上多くの質問をする必要はないでしょう。

4

3 に答える 3

45

移動コンストラクターは次のように呼び出されます。

  • オブジェクト初期化子がstd::move(something)
  • オブジェクト初期化子が左辺値参照型である場合std::forward<T>(something)Tそうでない場合(「完全な転送」のためのテンプレートプログラミングで役立ちます)
  • オブジェクト初期化子が一時的であり、コンパイラーがコピー/移動を完全に排除しない場合
  • 関数ローカルクラスオブジェクトを値で返し、コンパイラがコピー/移動を完全に排除しない場合
  • 関数ローカルクラスオブジェクトをスローし、コンパイラがコピー/移動を完全に排除しない場合

これは完全なリストではありません。パラメータがクラスタイプ(参照ではない)の場合、「オブジェクト初期化子」は関数の引数になる可能性があることに注意してください。

a RetByValue() {
    a obj;
    return obj; // Might call move ctor, or no ctor.
}

void TakeByValue(a);

int main() {
    a a1;
    a a2 = a1; // copy ctor
    a a3 = std::move(a1); // move ctor

    TakeByValue(std::move(a2)); // Might call move ctor, or no ctor.

    a a4 = RetByValue(); // Might call move ctor, or no ctor.

    a1 = RetByValue(); // Calls move assignment, a::operator=(a&&)
}
于 2012-10-29T16:36:14.047 に答える
5

まず第一に、あなたのコピーコンストラクターは壊れています。コピー元とコピー先の両方のオブジェクトが同じものを指し、スコープ外Arrayになると両方がそれを試みdelete[]、未定義の動作になります。これを修正するには、アレイのコピーを作成します。

a::a(const a& Old): Array(new int[5])
{
  for( size_t i = 0; i < 5; ++i ) {
    Array[i] = Old.Array[i];
  }
}

現在、両方の代入ステートメントが右辺値を使用する代わりに左辺値から割り当てているため、ムーブ代入は期待どおりに実行されていません。移動を実行するには、右辺値から移動するか、左辺値を右辺値と見なすことができるコンテキスト(関数のreturnステートメントなど)である必要があります。

目的の効果を得るにはstd::move、右辺値参照を作成するために使用します。

A=C;              // A will now contain a copy of C
B=std::move(C);   // Calls the move assignment operator
于 2012-10-29T16:35:41.720 に答える
0

コピーの省略が発生する可能性があることに注意してください。-fno-elide-constructorsフラグをコンパイラーに渡して無効にすると、コンストラクターが実行される可能性があります。

あなたはここでそれについて読むことができます:https ://www.geeksforgeeks.org/copy-elision-in-c/

于 2019-12-20T08:53:42.017 に答える