3

コピー コンストラクターは、伝統的に C++ プログラムではどこにでもありました。ただし、C++11以降、これに正当な理由があるかどうかは疑問です。

プログラム ロジックがオブジェクトのコピーを必要としない場合でも、オブジェクトの再割り当てのみを目的としてコピー コンストラクター(通常はデフォルト)が含まれることがよくありました。コピー コンストラクターがないと、オブジェクトを に格納したり、関数からオブジェクトを返すことさえできませんでした。std::vector

ただし、C++11 以降では、ムーブ コンストラクターがオブジェクトの再割り当てを担当しています。

コピー コンストラクターのもう 1 つの使用例は、単純にオブジェクトのクローンを作成することでした。.copy()ただし、コピー コンストラクターよりもor.clone()メソッドの方がその役割に適していると確信しています。

  1. オブジェクトのコピーはあまり一般的ではありません。確かに、オブジェクトのインターフェースに「自分自身の複製を作成する」メソッドを含める必要がある場合もありますが、それは場合によってのみです。その場合、明示的は暗黙的よりも優れています。

  2. 場合によっては、オブジェクトがいくつかの異なる の.copy()ようなメソッドを公開することがあります。異なるコンテキストでは、コピーを異なる方法で作成する必要がある場合があるためです (たとえば、より浅いまたはより深い)。

  3. コンテキストによっては、プログラム ロジックに関連する重要な処理をメソッドに実行させたい場合があり.copy()ます (カウンターをインクリメントするか、コピーの新しい一意の名前を生成するなど)。コピー コンストラクターに明白でないロジックを含むコードは受け入れません。

  4. 最後になりましたが、必要に応じてメソッドを仮想にすることができ、スライス.copy()の問題を解決できます。


実際にコピー コンストラクターを使用したい唯一のケースは次のとおりです。

  • コピー可能なリソースの RAII ハンドル (明らかに)
  • 数学ベクトルや行列など、組み込み型のように使用することを意図した構造体 - 単​​純に
    頻繁にコピーされ、vec3 b = a.copy()冗長すぎるため。

補足: CAS にはコピー コンストラクターが必要であるという事実を考慮しましたが、まったく同じ理由に基づいて冗長であると考えるCASが必要です。本当にこれが必要な場合は、+を使用することをお勧めします。)operator=(const T&)
.copy()operator=(T&&) = default

T(const T&) = delete私にとって、それはデフォルトでどこでも使用し.copy()、必要に応じてメソッドを提供するのに十分なインセンティブです。(おそらく定型文なしprivate T(const T&) = defaultで書けるようになるためだけcopy()のものでもあります。)virtual copy()

Q: 上記の推論は正しいですか、それとも、ロジック オブジェクトが実際にコピー コンストラクターを必要とするか、何らかの形でコピー コンストラクターから利益を得る理由を見逃しているのでしょうか?

具体的には、移動コンストラクターが C++11 でオブジェクトの再割り当ての責任を完全に引き継いだという点で正しいですか? オブジェクトの状態を変更せずに、オブジェクトをメモリ内の別の場所に移動する必要があるすべての状況で、非公式に「再割り当て」を使用しています。

4

4 に答える 4

5

問題は、「オブジェクト」という言葉が何を指しているのかです。

オブジェクトが変数が参照するリソースである場合(従来の OOP パラダイムを使用してポインターを介して Java や C++ で行われるように)、すべての「変数間のコピー」は「共有」であり、単一の所有権が課せられる場合、「共有」は「移動」になります。 .

オブジェクトが変数自体である場合、各変数には独自の履歴が必要であるため、別の値を優先して値の破壊を強制できない/したくない場合は、「移動」できません。

たとえば、コサイダーstd::strings

   std::string a="Aa";
   std::string b=a;
   ...
   b = "Bb";

の値がa変わると予想しますか、それともそのコードはコンパイルされませんか? そうでない場合は、コピーが必要です。

これを考慮してください:

   std::string a="Aa";
   std::string b=std::move(a);
   ...
   b = "Bb";

a の値 (より適切には、それを含む動的メモリ) が に「移動」されたため、 a は空のままbです。の値がb変更され、古いものは"Aa"破棄されます。

本質的に、move は、明示的に呼び出された場合、または右の引数が「一時的」である場合にのみ機能します。

  a = b+c;

の戻りによって保持されているリソースはoperator+、割り当て後には明らかに必要ないため、別の保持されている場所にコピーして削除するaよりも、 に移動する方が効果的です。a

移動とコピーは別物です。移動は「コピーの代わり」ではありません。オブジェクトがそれ自体のクローンを生成する必要がない場合、すべてのケースでコピーのみを回避するためのより効率的な方法です。

于 2013-05-08T19:20:43.163 に答える
1

つまりね。移動は新しいデフォルトであり、新しい最小要件です。しかし、コピーは依然として有用で便利な操作であることがよくあります。

コピー コンストラクターを提供するために後ろ向きになる必要はもうありません。しかし、単純に提供できるのであれば、ユーザーにとってコピー可能性は依然として有用です。

すぐにコピー コンストラクターを捨てるつもりはありませんが、私自身の型については、すぐにではなく、必要であることが明らかになったときにのみ追加することを認めます。これまでのところ、これは非常に少数のタイプです。

于 2013-05-09T09:03:38.900 に答える