Reputation: 170489
According to C++03 12.4/12 when a destructor is invoked explicitly
if the object is not of the destructor’s class type and not of a class derived from the destructor’s class type, the program has undefined behavior
So I have this code:
class Base {};
class Derived : public Base {};
char memory[100];
new(memory) Derived();
Base* ptr = (Base*)memory;
ptr->~Base();
Here the object is of type Derived
and "the destructor's class type" is Base
and so it looks like according to the Standard wording there're no grounds for UB.
So does the code above yield UB according to the Standard?
Upvotes: 5
Views: 148
Reputation: 210515
It's not UB, since both classes have trivial destructors, and therefore calling the destructor has the same effect as not calling the destructor (and not calling the destructor is certainly not UB).
Upvotes: 1
Reputation: 5334
please correct me if i'm wrong, i think there isn't undefined behavior but still should be avoided in sence of humanity (or maintainablity). but, consider Derived
is creating some kind of member, e.g. a shared pointer, (which is not untypical even for exceptions :). i tried this code on my machine and also on codepad:
class Base {
public:
boost::shared_ptr<int> x;
};
class Derived : public Base {
public:
boost::shared_ptr<int> y;
};
int main(int argc, char *argv[]) {
boost::shared_ptr<int> xx(new int(0xff));
boost::shared_ptr<int> yy(new int(0xaa));
int memory[100];
for(int i=0; i<100; i++)
memory[i] = 0;
Derived* foo = new(memory) Derived();
foo->x = xx;
foo->y = yy;
(*foo->y)--;
Base* ptr = (Base*)foo;
ptr->~Base();
Derived* bar = new(memory) Derived();
bar->x = xx;
bar->y = yy;
foo->~Derived();
return 0;
}
here is that the shared_ptr yy isn't released, and nobody could guarantee to never forget about Derived shouldn't provide any kind of stuff.
Upvotes: 0
Reputation: 279265
Correct, there's no undefined behavior.
By contrast there is potential UB in this case depending on the types involved:
Base *ptr = new Derived();
delete ptr;
The reason is that for some types an adjustment might have been applied by the implementation to get from Derived*
to Base*
. So without the pointer to the complete object there's no way to free the memory allocation correctly. A virtual destructor ensures that the Base
sub-object provides enough information for the implementation to recover that (the virtual call mechanism must be able to recover the Derived*
pointer in order to pass it as this
).
But in your example the memory isn't freed and so there's no motivation to make it UB. Of course it's still a bad idea, since conceptually the Derived
object is in a broken state. You have no legitimate way to call ~Derived
, even. In your example though both types are trivially destructible, so there's no need to call the destructor of either.
Upvotes: 4