In my code I have effectively the following:
wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );
Types in question are all built-in and so they have trivial destructors. In VC++ the code above works allright - new[]
just allocates memory, then delete[]
just frees it.
Is it acceptable in C++? Is it undefined behaviour?
iso14882 section 5.2.10.3:
The mapping performed by reinterpret_cast is is implementation defined
iso14882 section 5.3.5.2:
The value of the operand of delete[] shall be the pointer value which resulted from a previous array new-expression
In other words, it's implementation defined whether or not the delete[] invokes undefined behaviour. Steer clear.
Since
wchar_t
andchar
are both built-in types, the correct deallocation function (void operator delete(void* ptr)
) would be called, and there is no destructor to call.However the C++ 03 standard says the result of
reinterpret_cast<T1*>(T2*)
is undefined (section 5.2.10.7):From a practical POV I can't imagine an implementation where a
wchar_t*
value is not a validchar*
value, so your code should be OK on all the platforms. Just not standard-compliant...The delete[] operator internally uses a loop of some form to destruct the elements of your array. If the elements are different objects a different destructor will be used -- which can potentially cause undefined behaviour. Since the is a wchar and a char -- primitive types -- it probably won't cause any undesireable behaviour.
WARNING: IF YOU CONTINUE READING DO SO AT YOUR OWN PERIL !! GROSS DESCRIPTIONS OF UNDEFINED BEHAVIOUR AHEAD. THIS IS FOR EDUCATIONAL PURPOSES ONLY.
Example 1:
If you had two objects which were the same size and all their destructor did was zero out the memory then again it would probably not cause undersireable behaviour.
Example 2:
However if you had two objects where one type encapsulated a single 4 byte handle to a resource and the other had two such elements and you casted an array of the later into the singular case -- well then you would leak half of the handles of your array. The situation would look as follows:
..2:[1|2][1|2]FREE..
where the '2:' represents the size of the array. After a downcast the compiler will generate a delete that perceived the data as so :
..2:[1][1]FREE...
therefore after the free things would look like so:
..FREE[1|2]FREE..
My initial thought was that it is undefined behavior.
Footnote 73 reads, "This implies that an object cannot be deleted using a pointer of type
void*
because there are no objects of typevoid
".Arguably the object in your example doesn't have a dynamic type, since the definition of "dynamic type" at 1.3.3 mentions "most derived object", and the definition of "most derived object" at 1.8/4 is talking about objects of class type. So I kept looking:
I'm not sure whether a reinterpret_cast results in the same pointer value as was input, or not. Possibly it's cleared up by some other bit of the standard which I haven't found yet. I would not call this code "OK" without finding something to definitively state that if you reinterpret_cast a pointer, the result is the same "pointer value" as before, so that by passing it to delete[] you are passing "the pointer value" from new[].
This looks like bad news to me - it conspicuously doesn't say that the cast yields the same value, only that the pair of casts over and back, yields the same value. This suggests to me that the single cast is allowed to yield a different value, but it is only suggestive, not explicit. This is the usual problem with the rule that "if the standard doesn't state the behavior, then the behavior is undefined". Just because it doesn't state it in any of the paragraphs I can find using the index, doesn't mean it doesn't state it somewhere else...
We know that in practice we can cast things to unsigned char* in order to inspect their bytes, or void* to copy PODs using memcpy, so there must be some casts guaranteed to create aliases. You might think that if your implementation does create aliases with certain casts, then you're passing in the "same value" you got from new[]. But I'm still not sure that's good enough for delete[]. I think I'm missing something important.
At least as I'd read it, you have a static type (the type of the pointer) that differs from the dynamic type (the real type of the object it points at). That being the case, the second sentence of §5.3.5/3 applies:
Edit: Since what you apparently want is to allocate a buffer of "raw" memory instead of an array of objects, I'd advise using
::operator new
instead ofnew[]
. In this case, what you're doing is clearly defined, and also gives the reader a clear indication of intent.It is undefined behaviour because
delete[]
invokes the wrong destructor. However,wchar_t
andchar
are PODs, so they have no dedicated destructor and alldelete[]
does is calling the heap implementation to free up the pointer. Therefore, it is most likely to work, no byte is lost. But strictly speaking it is still undefined.