Re: Contents of an object after destruction
{ reformatted by mod to fix quoting and long lines. please consider
doing it yourself next time. -mod }
On Thursday, July 31, 2014 12:10:02 AM UTC+3, Tobias M?ller wrote:
<avi@cloudius-systems.com> wrote:
The generated code for
#include <cassert>
struct X {
int i;
void clear() { i = 0; }
~X() { assert(i == 0); }
};
void f(X* x) {
x->clear();
x->~X();
}
contains a write to x->i. I would have expected that after an object
is destroyed, the compiler may assume no further access (it becomes
a pile of bytes) and eliminate the store. Is this a missed
optimization opportunity, or is the compiler indeed required to
retain the store?
TL;DR: Never call the destructor explicitly like that. (Except when you
have use 'placement new' but then you should already know what you're
doing).
That is in the case, in fact.
Actually, there is no 'store' in your code. You never create a variable of
type X.
There is a store in X::clear(), which if called from f(). If you compile
the code and disassemble it, you will see it in the generated machine code
as well.
But even if there was such a variable, how could your function f know, how
the variable was allocated?
It is immaterial how the variable was allocated for my question. I am
asking why the compiler does not eliminate the store.
x could be allocated with 'new' (the easy case), on the stack or be part
of
an array. The pointer can't tell you the difference.
Besides that construction/destruction are not tied to
allocation/deallocation in C++. They are related and usually happen next
to
each other but they don't have to. The exception is 'placement new'.
'Placement new' means, that you call 'new' on an already existing memory
block, the constructor is called but no memory is allocated.
Since 'placement new' uses memory that it didn't allocate itself, you
cannot just 'delete' the object, because that would also free that memory
(that you don't own).
Instead you explicitly call the destructor, such that the memory will
_not_
be freed.
void* mem = malloc(sizeof X);
// ...
X* x = new (mem) X; // placement new, no allocation
// ...
x->~T(); // explicit destructor, no deallocation
Indeed that is how the code is intended to be used. The point is that
after x->~T(), there is no valid data in *x, so I expected the compiler
to eliminate the store.
// ...
free(mem);
This pattern is primarily used to efficiently implement container classes.
That is my use case.
Tobi
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]