在我的代码中,我实际上有以下内容:
wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );
有问题的类型都是内置的,因此它们具有微不足道的析构函数。在 VC++ 中,上面的代码可以正常工作 -new[]
只是分配内存,然后delete[]
释放它。
在 C++ 中可以接受吗?它是未定义的行为吗?
在我的代码中,我实际上有以下内容:
wchar_t* buffer = new wchar_t[size];
// bonus irrelevant code here
delete[] reinterpret_cast<char*>( buffer );
有问题的类型都是内置的,因此它们具有微不足道的析构函数。在 VC++ 中,上面的代码可以正常工作 -new[]
只是分配内存,然后delete[]
释放它。
在 C++ 中可以接受吗?它是未定义的行为吗?
我最初的想法是这是未定义的行为。
5.3.5/3:“在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。73)。
脚注 73 写道:“这意味着不能使用类型指针删除void*
对象,因为没有类型对象void
”。
可以说,您示例中的对象没有动态类型,因为 1.3.3 中“动态类型”的定义提到了“最衍生对象”,而 1.8/4 中“最衍生对象”的定义是在谈论对象类类型。所以我一直在寻找:
5.2.10/3:“[reinterpret_cast] 可能会或可能不会产生与原始值不同的表示”
5.3.5/2:“的操作数的值
delete
应是指针值,该指针值是由先前的数组 new-expression产生的”。
我不确定 reinterpret_cast 是否会产生与输入相同的指针值。可能它已被我尚未找到的其他一些标准所清除。我不会在没有找到明确声明的情况下将此代码称为“OK”,即如果您重新解释一个指针,则结果与之前的“指针值”相同,因此通过将其传递给 delete[],您将传递“指针值"来自新[]。
5.2.10/7:“除了转换 [在某些指针类型之间] 并返回到其原始类型会产生原始指针值之外,这种指针转换的结果是未指定的”。
这对我来说似乎是个坏消息——它显然没有说演员产生相同的价值,只是说这对演员来回产生相同的价值。这向我表明,允许单个演员产生不同的值,但这只是暗示性的,而不是明确的。这是“如果标准未说明行为,则行为未定义”的规则的常见问题。仅仅因为它没有在我可以使用索引找到的任何段落中说明它,并不意味着它没有在其他地方说明它......
我们知道,在实践中,我们可以将事物强制转换为 unsigned char* 以检查它们的字节,或者 void* 使用 memcpy 复制 POD,因此必须保证某些强制转换会创建别名。您可能会认为,如果您的实现确实创建了具有某些强制转换的别名,那么您将传入从 new[] 获得的“相同值”。但我仍然不确定删除 [] 是否足够好。我想我错过了一些重要的东西。
这是未定义的行为,因为delete[]
调用了错误的析构函数。但是,wchar_t
andchar
是 POD,因此它们没有专用的析构函数,并且delete[]
所做的只是调用堆实现来释放指针。因此,它最有可能工作,没有字节丢失。但严格来说,它仍然是未定义的。
至少正如我所读的那样,您有一个静态类型(指针的类型),它不同于动态类型(它指向的对象的真实类型)。在这种情况下,§5.3.5/3 的第二句适用:
在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。
编辑:由于您显然想要的是分配“原始”内存缓冲区而不是对象数组,因此我建议使用::operator new
而不是new[]
. 在这种情况下,您正在做的事情是明确定义的,并且也给读者一个明确的意图指示。
iso14882 第 5.2.10.3 节:
The mapping performed by reinterpret_cast is is implementation defined
iso14882 第 5.3.5.2 节:
The value of the operand of delete[] shall be the pointer value which resulted from a previous array new-expression
换句话说,它的实现定义了 delete[] 是否调用未定义的行为。避开。
由于wchar_t
和char
都是内置类型,因此将调用正确的释放函数 ( void operator delete(void* ptr)
),并且没有要调用的析构函数。
然而,C++ 03 标准说结果reinterpret_cast<T1*>(T2*)
是未定义的(第 5.2.10.7 节):
指向对象的指针可以显式转换为指向不同类型对象的指针。除了将“指向 T1 的指针”类型的右值转换为“指向 T2 的指针”类型(其中 T1 和 T2 是对象类型,并且 T2 的对齐要求不比 T1 的对齐要求更严格)并返回其原始类型会产生原始指针值,这种指针转换的结果是未指定的。
从实际的 POV 来看,我无法想象一个wchar_t*
值不是有效值的实现char*
,因此您的代码在所有平台上都应该没问题。只是不符合标准...
delete[] 运算符在内部使用某种形式的循环来破坏数组的元素。如果元素是不同的对象,则将使用不同的析构函数——这可能会导致未定义的行为。由于 是 wchar 和 char - 原始类型 - 它可能不会导致任何不良行为。
警告:如果您继续阅读,后果自负!对未来未定义行为的粗略描述。这仅用于教育目的。
示例 1:
如果您有两个大小相同的对象,并且它们的析构函数所做的所有事情都是将内存清零,那么它可能不会导致不良行为。
示例 2:
但是,如果您有两个对象,其中一种类型封装了一个资源的单个 4 字节句柄,而另一种类型有两个这样的元素,并且您将后者的数组转换为单数情况 - 那么您将泄漏一半的句柄大批。情况如下:
..2:[1|2][1|2]免费..
其中 '2:' 表示数组的大小。在向下转换之后,编译器将生成一个删除,将数据视为:
..2:[1][1]免费...
因此在免费之后的东西看起来像这样:
..免费[1|2]免费..
为什么要使用 reinterpret_cast<..>?如果你用纯 C++ 编写东西,那么你不需要重新解释演员表。在您的情况下,您没有为对象分配内存。您正在为 wchar_t 分配内存。为什么不使用字符串而不是 wchar_t 数组?