假设我有以下 C++:
char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;
根据 C++ 标准,这是否安全?我需要转换回 achar*
然后使用delete[]
吗?我知道它适用于大多数 C++ 编译器,因为它是普通数据,没有析构函数。能保证安全吗?
假设我有以下 C++:
char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;
根据 C++ 标准,这是否安全?我需要转换回 achar*
然后使用delete[]
吗?我知道它适用于大多数 C++ 编译器,因为它是普通数据,没有析构函数。能保证安全吗?
不能保证安全。这是 C++ FAQ lite 中的相关链接:
[16.13][]
删除某些内置类型( , 等)的数组时可以删除char
吗int
?
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.13
不,这是未定义的行为 - 编译器可能会做一些不同的事情,并且正如thudbang 链接到的 C++ FAQ 条目所说,operator delete[]
可能会被重载以执行与operator delete
. 有时您可以侥幸逃脱,但在您无法做到的情况下,养成将 delete[] 与 new[] 匹配的习惯也是一种好习惯。
我对此表示高度怀疑。
有很多有问题的释放内存的方法,例如你可以delete
在你的char
数组上使用(而不是delete[]
),它可能会正常工作。我在博客上详细介绍了这一点(为自我链接道歉,但它比重写它更容易)。
编译器与其说是平台问题,不如说是问题。大多数库将使用底层操作系统的分配方法,这意味着相同的代码在 Mac、Windows 和 Linux 上的行为可能不同。我见过这样的例子,每一个都是有问题的代码。
最安全的方法是始终使用相同的数据类型分配和释放内存。如果您正在分配char
s 并将它们返回给其他代码,则最好提供特定的分配/解除分配方法:
SOME_STRUCT* Allocate()
{
size_t cb; // Initialised to something
return (SOME_STRUCT*)(new char[cb]);
}
void Free(SOME_STRUCT* obj)
{
delete[] (char*)obj;
}
(重载new
anddelete
运算符也可能是一种选择,但我从不喜欢这样做。)
C++ 标准 [5.3.5.2] 声明:
如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并在本节的其余部分中使用转换后的操作数代替原始操作数。在任一替代方案中,delete 的操作数的值都可以是空指针值。如果它不是空指针值,在第一种选择(删除对象)中,删除操作数的值应是指向非数组对象的指针或指向表示此类基类的子对象(1.8)的指针一个对象(第 10 条)。如果不是,则行为未定义。在第二种选择(删除数组)中,delete 的操作数的值应是从前一个数组 new-expression 产生的指针值。77) 如果不是,则行为未定义。[ 注意:这意味着 delete-expression 的语法必须与 new 分配的对象的类型匹配,而不是 new-expression 的语法。——尾注][ 注意:指向 const 类型的指针可以是删除表达式的操作数;在将指针表达式用作删除表达式的操作数之前,没有必要抛弃它的 constness (5.2.11)。——尾注]
这是一个与我在这里回答的问题非常相似的问题:link text
简而言之,不,根据 C++ 标准,它是不安全的。如果由于某种原因,您需要一个 SOME_STRUCT 对象分配在与大小不同的内存区域中size_of(SOME_STRUCT)
(并且最好更大!),那么您最好使用像 global 这样的原始分配函数operator new
来执行分配和然后使用放置在原始内存中创建对象实例new
。如果对象类型没有构造函数,放置new
将非常便宜。
void* p = ::operator new( cb );
SOME_STRUCT* pSS = new (p) SOME_STRUCT;
// ...
delete pSS;
这将在大多数情况下起作用。如果SOME_STRUCT
是 POD 结构,它应该始终有效。SOME_STRUCT
如果的构造函数没有抛出并且SOME_STRUCT
没有自定义运算符 delete ,它也可以在其他情况下工作。这种技术也消除了对任何演员的需要。
::operator new
and ::operator delete
are C++'s closest equivalent to malloc
and free
and as these (in the absence of class overrides) are called as appropriate by new
and delete
expressions they can (with care!) be used in combination.
虽然这应该可行,但我认为您不能保证它是安全的,因为 SOME_STRUCT 不是 char* (除非它只是 typedef)。
此外,由于您使用不同类型的引用,如果您继续使用 *p 访问,并且内存已被删除,您将收到运行时错误。
如果指向的内存和您指向的指针都是 POD,这将正常工作。在这种情况下,无论如何都不会调用析构函数,并且内存分配器不知道也不关心内存中存储的类型。
对于非 POD 类型,唯一可以这样做的情况是,指针对象是指针的子类型(例如,您使用 Vehicle* 指向汽车)并且指针的析构函数已被声明为虚拟的。
这是不安全的,到目前为止,没有一个回应足够强调这样做的疯狂。如果您认为自己是真正的程序员,或者曾经想在团队中作为专业程序员工作,请不要这样做。您现在只能说您的结构包含非析构函数,但是您正在为将来设置一个令人讨厌的编译器和系统特定的陷阱。此外,您的代码不太可能按预期工作。您可以期望的最好的结果是它不会崩溃。但是我怀疑你会慢慢地得到内存泄漏,因为通过 new 分配的数组通常会在返回指针之前的字节中分配额外的内存。你不会释放你认为的记忆。一个好的内存分配例程应该可以解决这种不匹配问题,就像 Lint 等工具一样。
根本不要那样做,从你的脑海中清除任何导致你甚至考虑这些废话的思考过程。
我已将代码更改为使用 malloc/free。虽然我知道 MSVC 如何为普通旧数据实现新/删除(在这种情况下 SOME_STRUCT 是 Win32 结构,所以很简单的 C),但我只是想知道它是否是一种可移植技术。
它不是,所以我会使用它的东西。
如果您使用 malloc/free 而不是 new/delete,则 malloc 和 free 不会关心类型。
因此,如果您使用类似 C 的 POD(普通旧数据,如内置类型或结构),您可以 malloc 一些类型,并释放另一种类型。请注意,即使它有效,这也是一种糟糕的风格。