0

コンパイラgcc4.5.3(cygwin)

コピーコンストラクターが引数に対して呼び出される条件を特定しようとしています。コピーコンストラクターを呼び出す必要のない引数を渡す方法を見つけたいと思います。この問題を調査するために、次のテストコードを作成しました。

次のコードでは、コピーコンストラクターがfnc1()に対して2回呼び出されます。複数回呼び出す必要がある理由は何ですか?

コピーコンストラクターを呼び出さない方法はありますか?

# include <iostream>

using namespace std;

class able {
public:
   long x;
   able(): x(1) {}
   able(const able&) {cout << " const "; }
   ~able() { cout << " ~able" << endl; }
};

able fnc1(able x)         { cout << "fnc1(able x)"         ; return x; }
able fnc2(able& x)        { cout << "fnc2(able& x)"        ; return x; }
able fnc3(const able&  x) { cout << "fnc3(const able&  x)" ; return x; }
able fnc4(able const & x) { cout << "fnc4(able const & x)" ; return x; }
able fnc5(able* x)        { cout << "fnc4(able* x)"        ; return *x; }

int main(int argc, char** argv) {

   able* x = new able();
   fnc1(*x);
   fnc2(*x);
   fnc3(*x);
   fnc4(*x);
   fnc5(x);
   cout << "test fini" << endl;
   return 0;
}

output
 const fnc1(able x) const  ~able
  |                 |      |
  |                 |      o first destrucor
  |                 |      
  |                 o second call
  o first call
 ~able
 |
 o second destructor
fnc2(able& x) const  ~able
fnc3(const able&  x) const  ~able
fnc4(able const & x) const  ~able
fnc4(able* x) const  ~able
test fini
4

4 に答える 4

6

オブジェクトを値で関数に渡し、able値で返します。これらはそれぞれコピーを含み、コピーコンストラクターを使用します。まず、。を使用して関数にコピーされますfnc1(*x);。次に、そのコピーが。を使用して関数からコピーされますreturn x;

出力の順序に関して、あなたが目撃しているのは次のとおりです。

  1. const

    オブジェクトがコピーされます-これは、引数として関数に渡されるオブジェクトです。

  2. fnc1(able x)

    の実行fnc1

  3. const

    オブジェクトが再度コピーされます-これは関数から返されるオブジェクトです。

  4. ~able

    デストラクタが呼び出されます。これは、関数スコープの最後に到達したために破棄される引数を渡すときに作成されるコピーです。

  5. ~able

    fnc1(*x);デストラクタが呼び出されます。これは、行の完了時に破棄される関数から返された一時オブジェクトです。

によって引き起こされた2番目のコピーはreturn x;、コンパイラーによって省略される可能性があります(いくつかの副作用がある場合でも)。

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

于 2013-02-24T22:58:22.663 に答える
2

この関数では:

able fnc1(able x) { ... } 

あなたは:

  1. 入力引数を値で取得します。これは、初期化のためにそのコピーが作成されることを意味しますx。これが、コピー コンストラクターの最初の呼び出しの理由です。

  2. 型のオブジェクトをable で返す: これは、返すオブジェクトのコピーである一時的なオブジェクトが構築されることを意味しますが、コピー コンストラクター (そしてもちろんデストラクタへの呼び出し) へのこの最後の呼び出しは、コンパイラによって省略される可能性があります。 (名前付き) 戻り値の最適化、または (N)RVO の下。これが、コピー コンストラクターの2 回目の呼び出しの理由です。

したがって、表示されているコピー コンストラクターへの2 つの呼び出し。

また、プログラムでメモリ リークが発生していることにも注意してください。を使用してオブジェクトを割り当ててnewいますが、対応する への呼び出しによって割り当てを解除していませんdelete

于 2013-02-24T23:03:03.977 に答える
0

この関数:

able fnc1(able x) { ... } 

オブジェクトのコピーを作成する必要があります-参照なしでクラスを使用する場合、これはコントラクトの一部です。これにより、fnc1()はオブジェクトを「混乱」させ、渡された元のオブジェクトは同じままになります。これは、場合によっては正確に必要なものです。

于 2013-02-24T22:58:42.340 に答える
0

「できる」オブジェクトの責任を呼び出し元に移したいようです。その場合、 のようなプロトタイプがあり、貴重なものを台無しにしたくない場合void fn1(able &x)は、呼び出し元がオブジェクトのプライベート コピーを作成する必要があり ます。x

他の人が言ったように、パラメーター用に 1 つのコピー、戻り値用に 1 つのコピー。

関数から新しいオブジェクトを返す限り、そのオブジェクトを構築するためのコンストラクターが必要です。その時点で新しいオブジェクトが必要ない場合は、関数はその他のオブジェクトへのポインターまたは参照を返す必要があります。でも、

  1. このスタック フレームでローカルに構築したもの (つまり、ローカル変数または関数の引数) への参照を返してはなりません
  2. imvho、呼び出し元が参照するコンテンツを更新するだけの場合、そのオブジェクトへの参照を返すことは意味がありませんvoid。また、戻り値の型として「セッターのような」API を使用することも同様に簡単です。

#2に従わない唯一の理由は、関数を連鎖させたい、つまりobj->fn1()->fn2()->fn3()他のプログラミングパターンよりも価値があるということです。その場合、オブジェクトへのポインターを受け取って返し、メンバーにアクセスするのではなく使用するableという事実を受け入れることをお勧めします。fnx()obj->mobj.m

于 2013-02-24T23:02:33.550 に答える