我可以使用 C++ 中的内置类型安全地 new[],然后强制转换指针,然后安全地删除[] 吗?
在我的代码中,我实际上具有以下内容:
wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );
有问题的类型都是内置的,因此它们具有简单的析构函数。在 VC++ 中,上面的代码工作正常 - new[]
只是分配内存,然后 delete[]
只是释放它。
在C++中可以接受吗?这是未定义的行为吗?
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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我最初的想法是这是未定义的行为。
脚注 73 写道:“这意味着不能使用
void*
类型的指针删除对象,因为不存在void
类型的对象”。可以说,您的示例中的对象没有动态类型,因为 1.3.3 中“动态类型”的定义提到了“最派生的对象”,而“最派生的对象”的定义在1.8/4正在谈论类类型的对象。所以我一直在寻找:
我不确定reinterpret_cast是否会产生与输入相同的指针值。可能它被我还没有找到的标准的其他一些位清除了然而,如果没有找到一些明确的内容来明确说明如果您reinterpret_cast一个指针,结果与之前的“指针值”相同,那么我不会称这段代码为“OK”,因此通过将其传递给delete[],您将传递“the”。 new[] 中的指针值”。
这对我来说似乎是个坏消息 - 它显然没有说强制转换产生相同的值,只是说来回的一对强制转换产生相同的值。这对我来说表明允许单个强制转换产生不同的值,但这只是暗示性的,而不是明确的。这是“如果标准没有说明行为,则行为是未定义的”规则的常见问题。在我可以找到的任何段落中使用索引,并不意味着它没有在其他地方声明它...
我们知道,在实践中我们可以将内容转换为 unsigned char* 以便检查它们的字节或 void*使用 memcpy 复制 POD,因此必须有一些强制转换来保证创建别名。您可能会认为,如果您的实现确实使用某些强制转换创建了别名,那么您将传递从 new[] 获得的“相同值”。我仍然不确定这是否足以删除[],我认为我错过了一些重要的东西。
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.
这是未定义的行为,因为
delete[]
调用了错误的析构函数。但是,wchar_t
和char
是 POD,因此它们没有专用的析构函数,而delete[]
所做的只是调用堆实现来释放指针。因此,它最有可能工作,不会丢失任何字节。但严格来说,它仍然是未定义的。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.至少正如我读到的那样,您有一个与动态类型(它指向的对象的真实类型)不同的静态类型(指针的类型)。既然如此,则适用§5.3.5/3第二句:
编辑:由于您显然想要的是分配“原始”内存缓冲区而不是对象数组,因此我建议使用
::operator new
而不是new[].在这种情况下,您正在做的事情被明确定义,并且也向读者明确表明了意图。
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.iso14882 第 5.2.10.3 节:
reinterpret_cast 执行的映射是由实现定义的
iso14882 第 5.3.5.2 节:
delete[] 操作数的值应为从前一个操作产生的指针值array new-expression
换句话说,无论delete[] 是否调用未定义的行为,它都是由实现定义的。避开。
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.
由于
wchar_t
和char
都是内置类型,因此将调用正确的释放函数(voidoperatordelete(void* ptr)
),并且没有析构函数可以调用。然而,C++ 03 标准表示
reinterpret_cast(T2*)
的结果未定义(第 5.2.10.7 节):从实际的 POV 来看,我无法想象
wchar_t*
值不是有效char*
值的实现,因此您的代码应该所有平台都OK。只是不符合标准...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...delete[] 运算符在内部使用某种形式的循环来破坏数组的元素。如果元素是不同的对象,将使用不同的析构函数——这可能会导致未定义的行为。由于是 wchar 和 char(原始类型),因此它可能不会导致任何不良行为。
警告:如果您继续阅读,后果自负!未来未定义行为的总体描述。这仅用于教育目的。
示例 1:
如果您有两个大小相同的对象,并且它们的析构函数所做的所有操作都是将内存归零,那么它可能不会导致不良行为。
示例 2:
但是,如果您有两个对象,其中一种类型封装了资源的单个 4 字节句柄,而另一种则有两个这样的元素,并且您将后者的数组转换为单一情况 - 好吧那么你就会泄漏数组的一半句柄。情况如下所示:
..2:[1|2][1|2]FREE..
其中“2:”表示数组的大小。在向下转型之后,编译器将生成一个删除,将数据感知为:
..2:[1][1]FREE...
因此在免费之后,事情看起来像这样:
..FREE[1|2]FREE。 。
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..
你为什么使用reinterpret_cast<..>?如果您用纯 C++ 编写某些内容,则不需要重新解释强制转换。在您的情况下,您没有为对象分配内存。您正在为 wchar_t 分配内存。为什么不使用字符串而不是 wchar_t 数组?
Why are you using reinterpret_cast<..>? If you are writing something in pure C++, then you don't need reinterpret cast. In your case you are not allocating the memory for a object. You are allocating the memory for wchar_t. Why don't use to string instead of array of wchar_t?