0

次の C++ プログラムを使用します。

#include <memory>
#include <iostream>

using namespace std;

struct my_class{
    int value;

    my_class(int id): value(id){
        cout<<"constructing "<<id<<endl;
        cout<<"address is "<<static_cast<const void *>(this)<<endl;
    }

    my_class(const my_class & a){
        cout<<"construct copying "<<a.value<<endl;
        cout<<static_cast<const void *>(this)<<"<-"<<static_cast<const void *>(&a)<<endl;
    }

    my_class operator=(const my_class & a){
        cout<<"assignment copying "<<a.value<<endl;
        this->value = a.value;
        cout<<static_cast<const void *>(this)<<"<-"<<static_cast<const void *>(&a)<<endl;
        return *this;
    }

    ~my_class(){
        cout<<"deleting "<<this->value<<endl;
        cout<<"address is "<<static_cast<const void *>(this)<<endl;
    }
};

my_class f(){
    cout<<"==in f=="<<endl;
    my_class temp(2);
    cout<<"==out f=="<<endl;
    return temp;
}

int main(){
    cout<<"==in main=="<<endl;
    my_class a(1);

    a = f();

    a.value++;
    cout<<"==out main=="<<endl;
    return 0;
}

次の結果が得られました。

====

==in main==

constructing 1

address is 0x28ff04

==in f==

constructing 2

address is 0x28ff0c

==out f==

assignment copying 2

0x28ff04<-0x28ff0c

construct copying 2

0x28ff08<-0x28ff04

deleting 2686868

address is 0x28ff08

deleting 2

address is 0x28ff0c

==out main==

deleting 3

address is 0x28ff04


===

アドレス「0x28ff08」のオブジェクトと、アドレス「0x28ff04」のオブジェクトから作成された関連コピーで何が起こるかを誰か説明できますか? ここでコピーコンストラクターが呼び出される理由が本当にわかりません。


これが正しいかどうかわからないので、さらに詳細に説明したいと思います。誰かが私の間違いを見つけたら、指摘してください。

まず、画像は実行フローの詳細を示しています。 実行フロー

(1)。a値 1のオブジェクトを作成します。

(2)。関数を呼び出しますf()。オブジェクトを作成するtempと、コンパイラはオブジェクトが返されることを検出するため、呼び出し元のスタックに直接作成されます。

(3)。の返されたオブジェクトf()(つまり、temp) を objectaoperator=()割り当てaます。

(4)。オブジェクトは、同じ変数名を使用してパラメーター (右辺値) としてa渡されます。operator=()a

(5)。メソッドoperator=()main::a(左辺値、表記の乱用) で呼び出されるため、関数内で [!!これは私を混乱させた部分]; をthis指します。main::a

(6)。の値をtoのoperator=()値に変更します (つまり、1 から 2 に)。main::aa

(7)。コンパイラは、戻り値の型が参照ではなく、*this既に に存在するmain()ことを検出したため*this、コピー コンストラクターを呼び出してコピーする必要があります。ただし、コピー コンストラクターはオブジェクトを初期化しないため、初期化されていないオブジェクトが作成されます。

(8). [!!この部分についてはよくわかりません] 左辺値と結果のオブジェクトは同じオブジェクトであるため、最適化のために実際にはオブジェクトは返されません。

(9)。@Mike Seymourによると、コピーされたオブジェクトは破棄されます。このオブジェクトは、コンストラクターとデストラクターの両方が実際に何かを行うため(値とアドレスを出力するなど)、コンパイラーがそれを省略できないため作成されます。

(10)。を終了するoperator=()と、オブジェクトaは破棄されます。

(11)。を終了するmain()と、オブジェクトmain::aは最終的に破棄されます。

上記は出力を説明していますが、私の現在の理解は正しくない可能性があります。私が間違っている場合は、これを理解するのを手伝ってください。どうもありがとう。

4

2 に答える 2

4

これは、代入演算子がオブジェクトのコピーを返すためです。ここ:

 my_class operator=(const my_class & a)

代わりに参照を使用してください:

 my_class& operator=(const my_class & a)
于 2012-12-21T12:34:30.980 に答える
2

代入演算子が のコピーを返すため、コピー コンストラクターが呼び出されます*this。他の人が指摘したように、代入演算子はコピーではなく参照を返す必要があります。不必要なコピーを回避し、オペレーターを連鎖できるようにするためです。

おそらく、あなたが尋ねようとしている質問は、なぜ代入演算子から値を返すのにコピーが含まれるのに、コピーから返されf()ないのですか?

f()関数から戻った後に保持する必要のないローカルオブジェクトのコピーを返しています。これにより、コンパイラは戻り値の最適化を実行できます。この場合、返される変数は呼び出し元がアクセスできる場所に格納され、コピーまたは移動せずに戻り値になります。

operator=()永続オブジェクトのコピーを返しています。元のオブジェクトは引き続き存在し、戻り値とは別でなければならないため、ここではコピーが必要です。

あるいは、コピーされたオブジェクトは決して使用されないのに、なぜコンパイラはコピーを削除しないのか? これは、コンストラクタとデストラクタに副作用があり、コンパイラがそれらを排除することを許可されていないためです。

于 2012-12-21T13:18:06.923 に答える