11

メソッドからオブジェクトを値で返すときにコピーコンストラクターが呼び出される理由。コントロールを返すときにメソッドからオブジェクトを返しているという点で、以下のコードを参照してください。コピーコンストラクターにヒットしてから戻ります。私は次のことを理解していません:
1)コピーコンストラクターを呼び出す理由。
2) どのオブジェクトがコピー コンストラクターに暗黙的に渡されているか、
3) どのオブジェクト コピー コンストラクターがコンテンツをコピーするか、
4) 返されるときにオブジェクトのコンテンツをコピーする必要性は何か。助けてください。

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;

class ClassA
{
   int a, b;
     public:
   ClassA()
   {
     a = 10;
     b = 20;
   }
   ClassA(ClassA &obj)
   {
    cout << "copy constructor called" << endl;
   }
 };

 ClassA function (ClassA &str)
 {
  return str;
 }

 int main ()
 {
   ClassA str;
   function(str);
   //function(str);
   return 0;
 }
4

4 に答える 4

19

一度にすべての質問に答えようとします。

観察している動作は、C++ で値によってオブジェクトを返す方法によるものです。まず、関数によって返された値から一時オブジェクトがコピー構築 (C++11 ではムーブ構築) されます。次に、この戻り値を使用して別のオブジェクトを初期化すると、次のようになります。

Class c = fxn();

オブジェクトcは、そのテンポラリからコピー構築 (C++11 ではムーブ構築) されます。

これは、C++ 11 標準のパラグラフ 12/ 8.31に従って、コピーまたは移動コンストラクターへのこれらの呼び出しのいずれか (具体的なケース) または両方を省略して、これらのコンストラクターに副作用があり、関数の戻り値を直接c:

特定の基準が満たされると、コピー/移動操作用に選択されたコンストラクターおよび/またはオブジェクトのデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。このような場合、実装は、省略されたコピー/移動操作のソースとターゲットを、同じオブジェクトを参照する 2 つの異なる方法として扱い、そのオブジェクトの破棄は、2 つのオブジェクトが削除された時点のいずれか遅い方の時点で発生します。122 コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます (複数のコピーを排除するために組み合わせることができます) 。

— クラスの戻り値の型を持つ関数のステートメントで、式が関数の戻り値の型と同じ cv-unqualified 型を持つreturn不揮発性自動オブジェクト (関数または catch-clause パラメーター以外) の名前である場合、自動オブジェクトを関数の戻り値に直接構築することにより、コピー/移動操作を省略できます

— [...]

— 参照 (12.2) にバインドされていない一時クラス オブジェクトが同じ cv 非修飾型のクラス オブジェクトにコピー/移動される場合、一時オブジェクトを省略したコピー/移動の対象

— [...]

あなたの特定のケースで、コピーまたは移動コンストラクターへの呼び出しの1つだけを省略できると書いた理由は、上記の標準引用の最初の箇条書きにある太字の文です。

[...] (関数または catch-clause パラメーター以外) [...]

関数のパラメーターを返す場合、コピー省略は禁止されます。

また、コンストラクターの署名は次のようにする必要があることに注意してください。

Class(Class const& c)
//          ^^^^^

constコピー元のオブジェクトを変更するつもりはないため、コピー コンストラクターでnon- への左辺値参照を受け入れる理由はありません。

さらに悪いことに、上記は右辺値 (一時値など) からのコピーの構築を妨げるため、次のコードはコンパイルされません。

Class foo() { return Class(); }

実装がコピーを省略できるとしても、実行可能でアクセス可能なコピー コンストラクター (ムーブの場合はムーブ コンストラクター) がまだ存在する必要があります。

于 2013-05-24T09:36:14.903 に答える
3

関数はオブジェクトを返します。したがって、そのオブジェクトが存在する必要があります。したがって、そのオブジェクトはどこかから作成する必要があります。明らかに、これはそのコンストラクターの 1 つが使用されることを意味します。問題は、どちらですか?

を選択したのでreturn str;、これを作成するために使用する命令です。コピー コンストラクターを使用せずに、return 命令を使用してオブジェクトを作成して返すことができるでしょうか。戻り値を初期化するために を使用する必要があることは明らかなstrので、他のオプション (パラメーターなしのコンストラクター) は使用しません。

于 2013-05-24T09:37:01.163 に答える
2
ClassA function (ClassA &str)
{
  return str;
}

objectは、さらに使用するためstrに type を持つ一時オブジェクトにコピー構築されます。ClassAただし、コンパイラは最適化のためにそれを省略することができます。

于 2013-05-24T09:36:22.963 に答える
2

参照ではなく値で呼び出すため、コピー コンストラクターが呼び出されます。したがって、オブジェクトのすべてのメンバーは返されたインスタンスで同じ値を持つ必要があるため、現在のオブジェクトから新しいオブジェクトをインスタンス化する必要があります。そうしないと、参照によって返されるオブジェクト自体を返すことになるためです。この場合、参照オブジェクトを変更すると、元のオブジェクトも変更されます。これは、通常、値によって返されるときに必要な動作ではありません。

于 2013-05-24T09:38:53.810 に答える