I have this code:
class Test
{
public:
Test(int num)
{
this->num = num;
std::cout << "Test constructed. Num = " << num << std::endl;
}
Test(const Test& rhs)
{
num = rhs.num;
std::cout << "Test copy constructed. Num = " << num << std::endl;
}
~Test()
{
std::cout << "Test destructed. Num = " << num << std::endl;
}
int num;
};
int main()
{
{
Test t = Test(1);
t = Test(2);
}
}
The code outputs:
Test constructed. Num = 1
Test constructed. Num = 2
Test destructed. Num = 2 <---- I hoped to have here Num = 1
Test destructed. Num = 2
Why is it in the third line of the output is like destructor invoked for the object with value 2 in the 'num' field ('2-num-object' for brevity)? Shouldn't it be invoked for '1-num-object'? We destroy '1-num-object' and instead of it we'll have '2-num-object' in the 't' variable. No?
Trying to comprehend it I even considered that '2-num-object' is on top of the stack, so it should be deleted first, before the '1-num-object', ok. But apart of this internals, after all, it's '1-num-object' destructed, so shouldn't the destructor with the value 1 in the 'num' field be invoked?
Thank you in advance!
Class
Testdoes not declare a copy-assignment operator. The rules are complicated, but in this case, the compiler will supply a default copy-assignment operator. That's because you do declare a destructor and copy-constructor. (See slide 30 of Howard Hinnant's 2014 ACCU presentation.)The compiler-supplied, default copy-assignment operator does not call the destructor of class
Test. Rather, it performs assignment on each member of classTestin turn. That means assigning membernum, which is a simple integer assignment. No destructors are called in the process.The destructor for variable
tis not called until execution arrives at the closing brace of the block wheretis defined.The destructor of the temporary object created by
Test(2)is called before that. It is called at the end of the expression statement where it appears, at the semi-colon (;) that ends that statement. Thus, the lifetime of the temporary ends at the semi-colon.You can verify this by adding a user-defined copy-assignment operator to class
Test. Inside it, check the address of thethispointer, to see which object had itsoperator=function called. You can also check the address of variablerhs. Similar checks of thethispointer should be made in the constructor and destructor functions.With those changes, function
maindocuments the following sequence of events.The first two lines show that variable
tis allocated asobject 0.The next two lines show the allocation of
object 1, which is the temporary object created byTest(2).object 1is deallocated prior to the message "Assignment complete."object 1, therefore, was destructed at the semi-colon which terminates the assignment.object 0is not destructed until later, after the assignment is complete, and the block is being exited.So, object 1, which is the temporary, is deallocated before object 0.
Details of the program
Looking at pointer values makes my eyes blur, so I created vector
object_idto save them. Every time one of the constructors runs, I push the value of thethispointer onto the back of the vector. The subscript where a pointer is stored becomes itsid.Given a pointer, function
get_idlooks it up in the vector, and, if found, returns the subscript where it was found. If not found, functionget_idreturns-1.Inside each of the critical functions, I added
get_id(this)to thestd::coutstatement.That gave me the following program.