0

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.

George Stocker
  • 57,289
  • 29
  • 176
  • 237
RainSia
  • 113
  • 1
  • 7
  • 1
    Your output doesn't match the logging in your code. – Useless Dec 21 '12 at 12:31
  • 1
    This code has undefined behaviour because you're using `value` without initialising it when an object is created via the copy constructor. – Seth Carnegie Dec 21 '12 at 12:32
  • The object created by the copy constructor is never used. As you can see, the object is destroyed as soon as the program leaves the copy-assignment. By the way, I don't provide a default constructor, why the object can be created while without being initialized. – RainSia Dec 21 '12 at 12:39
  • "Your output doesn't match the logging in your code."-- Yes, I updated the program but forgot to rerun the program. I have updated the result. – RainSia Dec 21 '12 at 12:41
  • Copy constructor doesn't call default constructor. There is no chaining here, and this kind of chaining is allowed only in C++11, see delegating constructors. – hate-engine Dec 21 '12 at 12:54

2 Answers2

4

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)
hate-engine
  • 2,300
  • 18
  • 26
  • 1
    Yes, I know in real project I should use my_class &. But I'm just curious about the behavior of this program. Since I didn't provide a default constructor, why an object with the uninitialized value could be created? I just can't imagine what happened when the program ran. – RainSia Dec 21 '12 at 12:46
  • You did provide copy constructor: `my_class(const my_class & a)`, however it (your code) doesn't copy value into newly constructed object. – hate-engine Dec 21 '12 at 12:51
  • I got your point. But do you think that this call to the copy constructor is totally unnecessary? The object created by the default constructor was destroy right after it was created. I don't know how to express this, but my point is that since the created object is like a temporary object, why the compiler didn't eliminate it for optimization purposes. – RainSia Dec 21 '12 at 13:18
  • 1
    @RainSia: Creating and destroying the object has side effects, since you perform output in the constuctor and destructor. The compiler isn't allowed to eliminate those side effects. – Mike Seymour Dec 21 '12 at 13:19
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.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Yes. That's what I was trying to figure out. Now, I think I understand the behavior of this program. Thank you. – RainSia Dec 21 '12 at 13:24