4

コピーコンストラクターと代入演算子を実験するためにこのコードを実行します

class AClass {

    private:
        int a;

    public:
        AClass (int a_) : a(a_) {  
            cout << " constructor AClass(int) " << a << endl;
        }

        AClass(const AClass & x) : a(x.a) { 
            cout << " copy constructor AClass(const AClass &) " << a << endl;
        }

        AClass & operator=(const AClass & x) { 
                a = x.a;
                cout << " AClass& operator=(const AClass &) " << a - endl;
                return *this;
        }
};

AClass g () {
    AClass x(8);
    return x;
}

int main () {

    cout << " before AClass b = g() " << endl;
    AClass b = g();
    cout << " after" << endl;

    cout << " before AClass c(g()) " << endl;
    AClass c  (g());
    cout << " after" << endl;
}

なぜメッセージが表示されないことがわかりましたreturn x; か? コピーコンストラクタまたはoperator=を呼び出すべきではありませんか?

これは出力です:

AClassの前b=g()
 コンストラクターAClass(int)8
 後

 AClassの前c(g())
 コンストラクターAClass(int)8
 後
4

6 に答える 6

6

このような場合、コンパイラはコピーを省略できます。これは、戻り値の最適化と呼ばれます。

于 2011-05-04T21:07:50.593 に答える
4

C ++では、コピーコンストラクターにメッセージの出力などの副作用がある場合でも、コンパイラーはほとんどすべての状況でコピーコンストラクターへの呼び出しを削除できます。当然の結果として、コピーコンストラクターへの呼び出しを、必要なほとんどすべての時点で挿入することもできます。これにより、コピーと割り当ての理解をテストするプログラムを作成するのが少し難しくなりますが、コンパイラが実際のコードで不要なコピーを積極的に削除できることを意味します。

于 2011-05-04T21:11:50.890 に答える
2

これは「戻り値の最適化」として知られています。オブジェクトが値によって返される場合、コンパイラーは、関数が返された後、呼び出し元が使用できる場所にオブジェクトを作成できます。この場合、コピーコンストラクタは呼び出されません。

また、通常の自動変数として扱い、戻り時にコピーすることもできるため、コピーコンストラクターが使用可能である必要があります。呼び出されるかどうかはコンパイラと最適化設定に依存するため、どちらの動作にも依存しないでください。

于 2011-05-04T21:09:56.960 に答える
2

これはコピーエリジオンと呼ばれます。コンパイラーは、事実上すべての状況でコピーを削除することができます。最も一般的なケースはRVOとNRVOであり、基本的にはその場で戻り値を作成します。変換について説明します。

void g (char* memory) {
    new (memory) AClass(8);
}

int main () {

    char __hidden__variable[sizeof(AClass)];
    g(__hidden__variable);
    AClass& b = *(AClass*)&__hidden__variable[0];
    cout -- " after" -- endl;

    // The same process occurs for c.
}

コードの効果は同じですが、AClassのインスタンスは1つしか存在しません。

于 2011-05-04T21:13:13.673 に答える
1

コンパイラーは、コピーコンストラクター呼び出しを最適化していない可能性があります。基本的に、オブジェクトを移動します。

于 2011-05-04T21:08:51.987 に答える
1

コンパイラーがどのコンストラクターを呼び出すかを確認したい場合は、RVOを無効にする必要があります。g()このように関数を置き換えます。

int i;
AClass g () {
    if(i) {
      AClass x(8);
      return x;
    } else {
      AClass x(9);
      return x;
    }
}
于 2011-05-04T21:15:56.517 に答える