F
Frederick Gotham
If we have a simple class such as follows:
#include <string>
struct MyStruct {
std::string member;
MyStruct(unsigned const i)
{
member += '0'+i;
}
};
, then using "placement new" to construct an object of the class does the
following:
(1) Calls the constructor of the member object, "member".
(2) Calls the MyStruct constructor for the object itself.
This is conceivibly quite realistically like:
mystruct_obj.member.string();
mystruct_obj.MyStruct();
(i.e. we're invoking the code of two constructors.)
However, take a look at the following life-cycle of a DIY object, paying
particular attention to the way in which we destruct the object:
char *const mem = new char[sizeof(MyStruct)]; /* Allocate the memory */
MyStruct *const p = ::new(mem) MyStruct(5); /* Construct the object */
p->~MyStruct(); /* Destruct the object */
delete [] mem; /* Deallocate the memory */
Notice that when we are destructing the object, it looks as though we're
simply invoking the MyStruct destructor -- but in actuality, we're really
doing:
p->member.~string();
p->~MyStruct();
This is inconsistent with the way in which we construct the object! But not
only that, it's misleading because we are _not_ simply calling the
destructor -- we're doing more than that, we're invoking the constructors
of all base classes and subobjects. For consistency, we should have either
had:
Solution (1) -- Not favourable
char *const mem = new char[sizeof(MyStruct)];
MyStruct &obj = reinterpret_cast<MyStruct&>(*mem);
obj.MyClass(5); /* Construct! */
obj.~MyClass(); /* Destruct! */
delete [] mem;
Solution (2) -- Quite favourable
char *const mem = new char[sizeof(MyStruct)];
MyStruct *const p = ::new(mem) MyClass(5);
::delete(p) MyClass; /* Placement delete! */
The second solution is favourable, as it makes no mention of calling
constructors or destructors, but rather introduces the idea of
"constructing" and "destructing" an object (which may involve the
invocation of one or more constructors/destructors for base classes or
subobjects, etc.). The first solution is downright misleading, because it
resembles exactly the way in which we normally invoke a member function,
and gives no indication that we're also invoking the
constructors/destructors of base classes and subobjects.
My own opinion is that there should be a "placement delete", whose purpose
it is to destruct an object.
The DIY invocation of a destructor via "obj.~Class();" should either:
(1) Be forbidden, as it is for constructors.
(2) Just call _that_ destructor, exactly as if it were a member
function, without invoking any other destructors (such as those of base
objects or subobjects.
Maybe the die is cast... but it's worth a think.
#include <string>
struct MyStruct {
std::string member;
MyStruct(unsigned const i)
{
member += '0'+i;
}
};
, then using "placement new" to construct an object of the class does the
following:
(1) Calls the constructor of the member object, "member".
(2) Calls the MyStruct constructor for the object itself.
This is conceivibly quite realistically like:
mystruct_obj.member.string();
mystruct_obj.MyStruct();
(i.e. we're invoking the code of two constructors.)
However, take a look at the following life-cycle of a DIY object, paying
particular attention to the way in which we destruct the object:
char *const mem = new char[sizeof(MyStruct)]; /* Allocate the memory */
MyStruct *const p = ::new(mem) MyStruct(5); /* Construct the object */
p->~MyStruct(); /* Destruct the object */
delete [] mem; /* Deallocate the memory */
Notice that when we are destructing the object, it looks as though we're
simply invoking the MyStruct destructor -- but in actuality, we're really
doing:
p->member.~string();
p->~MyStruct();
This is inconsistent with the way in which we construct the object! But not
only that, it's misleading because we are _not_ simply calling the
destructor -- we're doing more than that, we're invoking the constructors
of all base classes and subobjects. For consistency, we should have either
had:
Solution (1) -- Not favourable
char *const mem = new char[sizeof(MyStruct)];
MyStruct &obj = reinterpret_cast<MyStruct&>(*mem);
obj.MyClass(5); /* Construct! */
obj.~MyClass(); /* Destruct! */
delete [] mem;
Solution (2) -- Quite favourable
char *const mem = new char[sizeof(MyStruct)];
MyStruct *const p = ::new(mem) MyClass(5);
::delete(p) MyClass; /* Placement delete! */
The second solution is favourable, as it makes no mention of calling
constructors or destructors, but rather introduces the idea of
"constructing" and "destructing" an object (which may involve the
invocation of one or more constructors/destructors for base classes or
subobjects, etc.). The first solution is downright misleading, because it
resembles exactly the way in which we normally invoke a member function,
and gives no indication that we're also invoking the
constructors/destructors of base classes and subobjects.
My own opinion is that there should be a "placement delete", whose purpose
it is to destruct an object.
The DIY invocation of a destructor via "obj.~Class();" should either:
(1) Be forbidden, as it is for constructors.
(2) Just call _that_ destructor, exactly as if it were a member
function, without invoking any other destructors (such as those of base
objects or subobjects.
Maybe the die is cast... but it's worth a think.