views:

88

answers:

3

Hello there, I have the following code

#include <iostream>
using namespace std;

class Object {

public:
   Object(int id){
     cout << "Construct(" << id << ")" << endl; 
     m_id = id;       
   }

   Object(const Object& obj){
      cout << "Copy-construct(" << obj.m_id << ")" << endl;  
      m_id = obj.m_id;
   }

   Object& operator=(const Object& obj){
      cout << m_id << " = " << obj.m_id << endl; 
      m_id = obj.m_id;
      return *this; 
   }

   ~Object(){
       cout << "Destruct(" << m_id << ")" << endl; 
   }
private:
   int m_id;

};

Object func(Object var) { return var; }

int main(){
   Object v1(1);
   cout << "( a )" << endl;
   Object v2(2);
   v2 = v1;
   cout << "( b )" << endl;
   Object v4 = v1;
   Object *pv5;
   pv5 = &v1;
   pv5 = new Object(5);
   cout << "( c )" << endl;
   func(v1);
   cout << "( d )" << endl;
   delete pv5;
}

which outputs

Construct(1)
( a )
Construct(2)
2 = 1
( b )
Copy-construct(1)
Construct(5)
( c )
Copy-construct(1)
Copy-construct(1)
Destruct(1)
Destruct(1)
( d )
Destruct(5)
Destruct(1)
Destruct(1)
Destruct(1)

I have some issues with this, firstly why does Object v4 = v1; call the copy constructor and produce Copy-construct(1) after the printing of ( b ).

Also after the printing of ( c ) the copy-constructor is again called twice?, Im not certain of how this function works to produce that Object func(Object var) { return var; }

and just after that Destruct(1) gets called twice before ( d ) is printed.

sorry for the long question, I'm confused with the above.

+1  A: 

As for the first question, Object v4 = v1; is syntactic sugar for Object v4(v1);, which more apparently calls the copy constructor.

The second one is a bit more complicated. When passing variables by value to functions, they must be copied - thus a call to the copy constructor. A copy of the object must also be placed on the stack in a location where the caller can access it because the copy passed to the function ceases to exist when the function returns. After these two copies are made, the parameter is destructed as it is popped off the stack, and the return value is destructed because it's value is not being used. They have the same ID because they are copies of v1.

Cristián Romo
ahk thanks a lot. also one more question, the last 3 outputs of `Destruct(1)` are due to `Object v1(1);,v2 = v1,Object v4 = v1;` correct? and `Destruct(5)` came just before as it was deleted manually?
sil3nt
+1  A: 

I have some issues with this, firstly why does Object v4 = v1; call the copy constructor and produce Copy-construct(1) after the printing of ( b ).

Despite the = sign, you are calling the copy constructor here. Remember, you have no default constructor. You are constructing a new Object and initialising it with the value of v1. Were you to do:

cout << "( b )" << endl;
Object v4(0);
v4 = v1;

...you would see...

( b )
Construct(0)
0 = 1

...which I think you were expecting.

Also after the printing of ( c ) the copy-constructor is again called twice?, Im not certain of how this function works to produce that Object func(Object var) { return var; }

Here you are passing var by value (rather than by reference [&]), meaning that a copy of the object is created (one call to the copy constructor). Then you return another object (again, rather than a reference) so another copy must be made (a second call to the copy constructor).

and just after that Destruct(1) gets called twice before ( d ) is printed.

Those objects that you just created with the copy constructors? They just went out of scope and their destructors were called.

When you delete v5 its destructor is called.

Then you reach the end of your main function and the three Object instances you created on the stack (v1, v2, v4) reach the end of their lifetimes and are destroyed as the stack is unwound.

You have probably already noticed that you have exactly as many destructor calls as you have constructor calls!

Johnsyweb
could you explain the last 4 destructor calls also?.
sil3nt
I have updated my answer to explain these.
Johnsyweb
+1  A: 
Object v1(1);
// Construct(1)

Regular constructor call for an automatic stack variable (destroyed at the end of the function).

cout << "( a )" << endl;
// ( a )

Object v2(2);
// Construct(2)

Another constructor call.

v2 = v1;
// 2 = 1

The assignment operator is called because v2 was already created (we called the constructor for it) and now we're assigning one existing object to another.

cout << "( b )" << endl;
// ( b )

Object v4 = v1;
// Copy-construct(1)

The copy constructor is called here because Object v4 is still not created, so we create it as a copy of v1. The assignment is taken here to mean the same as if you did Object v4(v1)

Object *pv5;
pv5 = &v1;
pv5 = new Object(5);
// Construct(5)

Call the constructor for a heap object (destroyed explicitly with delete).

cout << "( c )" << endl;
// ( c )

func(v1);
// Copy-construct(1) <br />
// Copy-construct(1) <br />
// Destruct(1) <br />
// Destruct(1) <br />

The copy constructor is first called to copy v1 to the parameter var. It is called again to create a copy of var as return value to the caller. var is destroyed as it's popped off the stack when exiting the function. The return value is destroyed after at the expression func(v1).

cout << "( d )" << endl;
// ( d )

delete pv5;
// Destruct(5)

The object pointed at by pv5 is manually destroyed.

} // end of main
// Destruct(1) <br />
// Destruct(1) <br />
// Destruct(1) <br />

The automatic variables v1, v2, v4 (all having copied the id of v1 from either assignment or copy construction) are popped off the stack and the destructor is called for each.

Firas Assaad