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()(ie, )temp的返回对象赋值给 objectaoperator=()a

(4)。对象使用相同的变量名作为参数(右值)a传入。operator=()a

(5)。该方法operator=()main::a(左值,滥用符号)上调用,因此this在函数中指向main::a,[!!这是让我感到困惑的部分];

(6)。operator=()改变main::atoa的值(即从 1 到 2);

(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 回答