4

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?

Matias Chara
  • 921
  • 6
  • 21
  • 2
    `string` may do additional allocation. If you merely `delete buf`, the memory that makes up the `string` goes away. The additional memory allocated by the `string`, Crom only knows what happens to it. The `string` destructor was never called, so most likely it sits there, lost and unloved. Extend this out to generalize it, whatever the placement `new`ed objects destructor needs to do is not done, and that could be OK, bad, or anything in between. – user4581301 Jul 16 '20 at 16:39
  • @user4581301 if you read the original question, and now this comment, it says: "you need to call the destrcutors on each element manually", and thats what I mean. Operate under that assumption as the comment mentioned in my question does . – Matias Chara Jul 16 '20 at 16:41
  • I got you now. That's headed in the opposite direction. Let's make the problem a bit more obvious. If you placement `new`ed an object over top of a `string`, rather than a simple buffer of raw bytes, you'd have to A) manually destroy the `string` before writing over top of it or suffer the consequences and B) If you attempt to delete this new object as a `string`, the program will try to do `string` destruction things to something that isn't a `string` and the results are undefined. Regarding the rest of the question, the `[]`-no `[]` thing, never done or looked it up. – user4581301 Jul 16 '20 at 16:55
  • @user4581301 What char specific "destruction things" does c++ do? dont primitives like char have no destruction routine other than the trivial dealocation done by delete? – Matias Chara Jul 16 '20 at 16:57
  • @user4581301 furthermore, one of the few defined stict aliasing violations includes casting char to other types. – Matias Chara Jul 16 '20 at 17:03
  • That's why I switched to using `std::string` as the initial object in comment 2. C++ doesn't do any [non trivial "destruction things"](https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor) for fundamental datatypes, so there won't be any harm in not formally destroying it (Not sure if this is still formally UB or not) and you are back to the problem raised in my first comment. No casting is involved in placement `new`, but attempting to construct the object in a mis-assigned address is still a bad idea. – user4581301 Jul 16 '20 at 17:29

1 Answers1

2

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • but to be clear, a buffer of void pointer type is better than a char buffer? – Matias Chara Jul 17 '20 at 01:31
  • And also, is there any difference between the two code snippets I compared in the end of my question? – Matias Chara Jul 17 '20 at 01:32
  • 1
    @MatiasChara Yes, using operator new to allocate memory without objects is better than allocating array of chars, when you don't intend to use those chars. About the snippets, I don't know. I've never used array operator new / delete. – eerorika Jul 17 '20 at 01:41