奇怪的复制构造函数

时间:2012-12-21 12:28:30

标签: c++ copy-constructor copy-assignment

使用以下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”处的对象构造的相关副本?我真的不明白为什么在这里调用复制构造函数。


我不知道我是否认为这是正确的,因此我想进一步详细解释它。任何人发现我的错误,请指出它们。

首先,图像说明了执行流程的细节: the execution flow

(1)。创建值为1的对象a;

(2)。调用函数f()。创建一个对象temp,编译器发现该对象将被返回,因此它直接在调用者的堆栈中创建;

(3)。通过调用f()的{​​{1}},将返回的temp对象(即a)分配给对象operator=();

(4)。对象a使用相同的变量名a作为参数(rvalue)传递到operator=()

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

(6)。 main::aoperator=()的值更改为main::a的值(即从1更改为2);

(7)。编译器发现返回类型不是引用,a中已存在*this,因此必须通过调用复制构造函数来复制main()。但是,复制构造函数不会初始化对象,因此会创建未初始化的对象。

(8)。 [!!对此部分不太确定]左值和结果对象是同一个对象,因此优化不会返回任何对象。

(9)。根据@Mike Seymour,复制的对象被销毁,因为编译器不能省略它,因为构造函数和析构函数实际上都做了某些事情(例如输出值和地址),因此创建了这个对象。

(10)。退出*this时,对象operator=()将被销毁。

(11)。退出a时,对象main()最终会被销毁。

以上解释了输出,但是,我目前的理解可能不正确。如果我错了,请帮助我理解这一点。非常感谢。

2 个答案:

答案 0 :(得分:4)

这是因为你的赋值运算符返回了对象的副本。这里:

 my_class operator=(const my_class & a)

改为使用引用:

 my_class& operator=(const my_class & a)

答案 1 :(得分:2)

调用复制构造函数是因为赋值运算符返回*this的副本。正如其他人所指出的那样,赋值运算符应返回引用而不是副本;两者都是为了避免不必要的复制,并允许操作员被链接。

也许您要问的问题是,为什么从赋值运算符返回值涉及副本,而从f()返回则不然?

f()返回一个本地对象的副本,从函数返回后不需要保留。这允许编译器执行返回值优化,其中要返回的变量存储在调用者可访问的某个位置,并且成为返回值而不复制或移动它。

operator=()正在返回持久对象的副本。由于原始对象仍然存在,并且必须与返回的值分开,因此这里需要一个副本。

或许您正在试图问,为什么编译器不会删除副本,因为从未使用过复制的对象?那是因为你已经给出了构造函数和析构函数的副作用,并且不允许编译器消除那些。