Can I new[], then cast the pointer, then delete[]

2019-03-12 21:48发布

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?

7条回答
贼婆χ
2楼-- · 2019-03-12 22:03

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.

查看更多
狗以群分
3楼-- · 2019-03-12 22:05

Since wchar_t and char 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):

A pointer to an object can be explicitly converted to a pointer to an object of different type. Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

From a practical POV I can't imagine an implementation where a wchar_t* value is not a valid char* value, so your code should be OK on all the platforms. Just not standard-compliant...

查看更多
地球回转人心会变
4楼-- · 2019-03-12 22:05

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..

查看更多
做个烂人
5楼-- · 2019-03-12 22:07

My initial thought was that it is undefined behavior.

5.3.5/3: "In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.73).

Footnote 73 reads, "This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void".

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:

5.2.10/3: "[reinterpret_cast] might, or might not, produce a representation different from the original value"

5.3.5/2: "The value of the operand of delete shall be the pointer value which resulted from a previous array new-expression".

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[].

5.2.10/7: "Except that casting [between certain pointer types] and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified".

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.

查看更多
ら.Afraid
6楼-- · 2019-03-12 22:12

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:

In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

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 of new[]. In this case, what you're doing is clearly defined, and also gives the reader a clear indication of intent.

查看更多
Bombasti
7楼-- · 2019-03-12 22:13

It is undefined behaviour because delete[] invokes the wrong destructor. However, wchar_t and char are PODs, so they have no dedicated destructor and all delete[] 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.

查看更多
登录 后发表回答