问题
in this answer: https://stackoverflow.com/a/222578/4416169
with this code:
char *buf = new char[sizeof(string)]; // pre-allocated buffer
string *p = new (buf) string("hi"); // placement new
string *q = new string("hi"); // ordinary heap allocation
there is a comment that says:
keep in mind that the strings are destrcuted manually before deleting the buffer, thats what the comment below already assumes.
Strictly, it's undefined behaviour to call delete[] on the original char buffer. Using placement new has ended the lifetime of the original char objects by re-using their storage. If you now call delete[] buf the dynamic type of the object(s) pointed to no longer matches their static type so you have undefined behaviour. It is more consistent to use operator new/operator delete to allocate raw memory inteded for use by placement new.
Is this comment correct on what it claims? Should we instead create a buffer of void* pointers with operator new to create it and operator delete to delete it as in the following code?:
void *raw_memory = operator new(sizeof(int));
int *dynamicInt = new(raw_memory) int;
operator delete(raw_memory);
is this code^^ strictly equivalent to the following code?:
void *raw_memory = operator new[](sizeof(int));//notice the [] after new
int *dynamicInt = new(raw_memory) int;
operator delete[](raw_memory);//notice the [] after delete
or is it ok to simply use a char* buffer and do the usual array delete and new?
回答1:
Calling delete on original buffer of char type after casting .c++
Note that there is no casting involved in the examples.
Using placement new has ended the lifetime of the original char objects by re-using their storage.
This is certainly correct. Standard quote:
[basic.life] ... The lifetime of an object o ... ends when: ... the storage which the object occupies is ... reused by an object that is not nested within o
If you now call delete[] buf the dynamic type of the object(s) pointed to no longer matches their static type so you have undefined behaviour.
The offending rule presumably is this:
[expr.delete] In a single-object delete expression, if the static type of the object to be deleted is different from its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In an array delete expression, if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
Let us take a look at what "dynamic type" means:
[defns.dynamic.type] type of the most derived object ([intro.object]) to which the glvalue refers
[intro.object] If a complete object, a data member, or an array element is of class type, its type is considered the most derived class, to distinguish it from the class type of any base class subobject; an object of a most derived class type or of a non-class type is called a most derived object.
So, the issue here is that there is no object, and therefore there is no most derived object whose type could match with the static type.
To me, this seems like bit of a technicality because we are considering a trivial non-class type. This technicality is not a problem with a bare operator new.
Should we instead create a buffer of
void*
pointers with operator new
Even better, we probably should use std::allocator<std::string>::allocate
which internally calls ::operator new
. This makes our program easier to accommodate custom allocators and obviates the need to calculate the sizes of arrays based on the size of the element.
来源:https://stackoverflow.com/questions/62939015/calling-delete-on-original-buffer-of-char-type-after-casting-c