使用以下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)。创建值为1的对象a
;
(2)。调用函数f()
。创建一个对象temp
,编译器发现该对象将被返回,因此它直接在调用者的堆栈中创建;
(3)。通过调用f()
的{{1}},将返回的temp
对象(即a
)分配给对象operator=()
;
(4)。对象a
使用相同的变量名a
作为参数(rvalue)传递到operator=()
。
(5)。方法a
在operator=()
上调用(左值,滥用符号),因此函数中的main::a
指向this
,[!!这是部分困惑我];
(6)。 main::a
将operator=()
的值更改为main::a
的值(即从1更改为2);
(7)。编译器发现返回类型不是引用,a
中已存在*this
,因此必须通过调用复制构造函数来复制main()
。但是,复制构造函数不会初始化对象,因此会创建未初始化的对象。
(8)。 [!!对此部分不太确定]左值和结果对象是同一个对象,因此优化不会返回任何对象。
(9)。根据@Mike Seymour,复制的对象被销毁,因为编译器不能省略它,因为构造函数和析构函数实际上都做了某些事情(例如输出值和地址),因此创建了这个对象。
(10)。退出*this
时,对象operator=()
将被销毁。
(11)。退出a
时,对象main()
最终会被销毁。
以上解释了输出,但是,我目前的理解可能不正确。如果我错了,请帮助我理解这一点。非常感谢。
答案 0 :(得分:4)
这是因为你的赋值运算符返回了对象的副本。这里:
my_class operator=(const my_class & a)
改为使用引用:
my_class& operator=(const my_class & a)
答案 1 :(得分:2)
调用复制构造函数是因为赋值运算符返回*this
的副本。正如其他人所指出的那样,赋值运算符应返回引用而不是副本;两者都是为了避免不必要的复制,并允许操作员被链接。
也许您要问的问题是,为什么从赋值运算符返回值涉及副本,而从f()
返回则不然?
f()
返回一个本地对象的副本,从函数返回后不需要保留。这允许编译器执行返回值优化,其中要返回的变量存储在调用者可访问的某个位置,并且成为返回值而不复制或移动它。
operator=()
正在返回持久对象的副本。由于原始对象仍然存在,并且必须与返回的值分开,因此这里需要一个副本。
或许您正在试图问,为什么编译器不会删除副本,因为从未使用过复制的对象?那是因为你已经给出了构造函数和析构函数的副作用,并且不允许编译器消除那些。