Question

With the following C++ program:

#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;
}

I got the following result:

====

==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


===

Can anyone explain to me what happens with the object at the address "0x28ff08" and the related copy constructing from object at the address "0x28ff04"? I really don't understand why the copy constructor is called here.


I don't known if I get this correct, thus I want to further explain it in great detail. Anyone finds my mistakes please point them out.

First, an image illustrates the details of the execution flow: the execution flow

(1). Create an object a with value 1;

(2). Call function f(). Create an object temp, and the compiler finds that the object will be returned, so it is created directly in the caller's stack;

(3). Assign the returned object of f() (i.e., temp) to object a by calling operator=() of a;

(4). Object a is passed into operator=() as the parameter (rvalue) using the same variable name a.

(5). The method operator=() is called on main::a (lvalue, with the abuse of notation), thus this in the function points to main::a, [!!this is the part that confused me];

(6). operator=() changes the value of main::a to a's value (i.e., from 1 to 2);

(7). The compiler finds that the return type is not a reference, and *this already exists in main(), so it has to copy *this by calling the copy constructor. However, the copy constructor does not initialize the object, so an uninitialized object is created.

(8). [!!not quite sure about this part] The lvalue and the resulting object is the same object, thus no object is really returned due to optimization.

(9). The copied object is destroyed, according to @Mike Seymour, this object is created because the compiler cannot omit it because both the constructor and destructor actually do something (e.g. output the value and address).

(10). When exiting operator=(), object a is destroyed.

(11). When exiting main(), object main::a is finally destroyed.

The above explains the output, however, my current understanding may not be correct. Please help me comprehend this if I am wrong. Thanks a lot.

Was it helpful?

Solution 2

The copy constructor is called because your assignment operator returns a copy of *this. As others have noted, the assignment operator should return a reference rather than a copy; both to avoid unnecessary copying, and to allow the operator to be chained.

Perhaps the question you're trying to ask is, why does returning the value from the assignment operator involve a copy, while returning from f() doesn't?

f() is returning a copy of a local object, which doesn't need to persist after returning from the function. This allows the compiler to perform return value optimisation, where the variable to be returned is stored somewhere accessible by the caller, and becomes the return value without copying or moving it.

operator=() is returning a copy of a persistent object. Since the original object will still exist, and must be separate from the returned value, a copy is necessary here.

Or perhaps you're trying to ask, why doesn't the compiler eliminate the copy since the copied object is never used? That's because you have given the constructor and destructor side effects, and the compiler is not allowed to eliminate those.

OTHER TIPS

It's because your assignment operator returns copy of the object. Here:

 my_class operator=(const my_class & a)

use reference instead:

 my_class& operator=(const my_class & a)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top