Is it possible to use placement-new to change the type of a polymorphic object? if not, what exactly in the standard prohibits it?
Consider this code:
#include <new>
struct Animal {
Animal();
virtual ~Animal();
virtual void breathe();
void kill();
void *data;
};
struct Dead: Animal {
void breathe() override;
};
void Animal::kill() {
this->~Animal();
new(this) Dead;
}
Is calling "kill" ever legal?
Update: Early comments do not address the (il)legality according to the standard of the programming technique shown here of changing the type of an object by explicit call to destructor and applying placement-new for a new object compatible.
Because there is interest in why would anyone would like to do this, I can mention the use case that led me to the question, although it is not relevant to the question I am asking.
Imagine you have a polymorphic type hierarchy with several virtual methods. During the lifetime of the object, things happen that can be modeled in the code as the object changing its type. There are many perfectly legal ways to program this, for example, keeping the object as a pointer, smart or not, and swapping in a copy of the other type. But this may be expensive: One has to clone or move the original object into another one of a different type just to swap the new in.
In GCC, Clang, and others, changing the type of an object can be as cheap as to simply changing the virtual table pointer, but in portable C++ this is not possible except by constructing in-place an object of a new type.
In my original use case, the object could not be held as a pointer either.
I would like to know what the standard says on the subject of reusing memory.
Placement new is a variation new operator in C++. Normal new operator does two things : (1) Allocates memory (2) Constructs an object in allocated memory. Placement new allows us to separate above two things. In placement new, we can pass a preallocated memory and construct an object in the passed memory.
Placement new allows you to construct an object in memory that's already allocated. You may want to do this for optimization when you need to construct multiple instances of an object, and it is faster not to re-allocate memory each time you need a new instance.
Placement new This is used to construct objects in allocated storage: // within any block scope... { // Statically allocate the storage with automatic storage duration // which is large enough for any object of type `T`.
[basic.life]/8 If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
...
(8.4) — the original object was a most derived object (1.8) of type
T
and the new object is a most derived object of typeT
(that is, they are not base class subobjects).
In your example, a call to kill()
itself may be valid (if it so happens that sizeof(Animal)==sizeof(Dead)
, which I don't think is guaranteed), but most attempts to use Animal*
pointer or Animal&
lvalue through which that call was made would trigger undefined behavior by way of accessing the object whose lifetime has ended. Even assuming that the stars align and Animal
subobject of Dead
perfectly overlays the original location of the original stand-alone Animal
object, such a pointer or lvalue is not considered to refer to the former, but to the now-expired latter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With