Reputation: 4612
Today I was wondering about c++ destructors so I wrote a small test program. That answered my original question but raised a new one which is:
The following program:
#include "stdafx.h"
#include <vector>
#include <iostream>
using namespace std;
class test
{
public:
int id;
vector<test> collection;
test(){}
test(int id_in){id = id_in;}
~test(){cout << "dying: " << id << "\n";}
};
int _tmain(int argc, _TCHAR* argv[])
{
{
test obj(1);
obj.collection.push_back(test(2));
obj.collection.push_back(test(3));
cout << "before overwrite\n";
obj = test(4);
cout << "before scope exit\n";
}
int x;
cin >> x;
}
produces the following output:
dying: 2
dying: 2
dying: 3
before overwrite
dying: 2
dying: 3
dying: 4
before scope exit
dying: 4
Why don't I see a destructor for my test object with id 1? If its destructor isn't called when it is overwritten, than what calls the destructors of the instances in its vector?
Upvotes: 7
Views: 1406
Reputation: 145239
Regarding the in-practice behavior, the statement
obj = test(4);
changes the value of the id
member to 4
. Consequently, when that object is destroyed, it reports that the object with id 4
is destroyed. The assignment performs a memberwise assignment because you haven't defined the copy assignment operator.
Regarding the formally guaranteed behavior, you only have that (for a hosted implementation) if the non-standard "stdafx.h"
header in your code defines the macros _tmain
and _TCHAR
such that preprocessing yields a standard main
function as required by the standard:
C++11 §3.6.1/1:
“A program shall contain a global function calledmain
, which is the designated start of the program. It is implementation-defined whether a program in a freestanding environment is required to define a main function.”
Although unlikely, this means that if the header does not define those macros appropriately, then you can, in principle, get the output you see regardless of the rest of the code.
One way to ensure that that undefined behavior does not happen, is to simply use a standard main
.
After all, as of 2012 there is absolutely no advantage in using those Microsoft macros that are designed to support Windows 9x, especially considering that Microsoft, with the Layer for Unicode, made those macros obsolete already in 2001.
I.e., continuing to use them over 10 years after, is simply meaningless obfuscation and added work, including that you can't formally say that your program must produce any particular result.
Upvotes: 1
Reputation: 28050
You violate the Rule of Three by creating a destructor, but no assignment operator.
From reading that, you can interpret your code as follows:
When the line
obj = test(4);
is compiled, a temporary instance of test
is created with id 4.
Then, the assignment operator is called. Since you did not provide one, the compiler generated one for you that looks like this:
test& operator=(const test& other)
{
id = other.id;
collection = other.collection;
return *this;
}
The id 1 is simply overwritten with the 4 from the temporary, and for the collection assignment, the assignment operator of std::vector
is called.
std::vector
's assignment operator deletes all previously contained elements, which is why you see
dying: 2
dying: 3
in your output. Finally, the temporarily created obj instance with id 4 is deleted, causing
dying: 4
to appear for the first time. When obj
goes out of scope, you see the
dying: 4
output once more.
Upvotes: 6
Reputation: 12865
You don't see 1 because you destruct obj at the very end. And before you have rewritten it by test(4). Therefore 1 is rewritten by 4.
Upvotes: 0
Reputation: 10487
obj
is not destructed when you do this:
obj = test(4);
All that's happening is that a test(4)
is being created and assigned onto the existing object so the id
of 1 will be overwritten with 4, which is why you see the last one as:
dying: 4
Upvotes: 3
Reputation: 170489
That's because you don't have assignment operator implemented and so a memberwise assignment is done instead. So this line:
obj = test(4);
causes id
in the very first object (test obj(1)
) to be overwritten to 4
. The last line dying: 4
is from destroying that very object.
Upvotes: 6