通过不同类型的指针删除缓冲区?

10

假设我有以下的C++代码:

char *p = new char[cb];
SOME_STRUCT *pSS = (SOME_STRUCT *) p;
delete pSS;

这是否符合C++标准?我需要把它强制转换回char*,然后使用delete[]吗?我知道它会在大多数C++编译器中工作,因为它是普通数据,没有析构函数。但它保证是安全的吗?

10个回答

9

6
不,这是未定义的行为 - 编译器可能会执行不同的操作,并且根据C++ FAQ条目,链接到的thudbangoperator delete[]可能会被重载以执行与operator delete不同的操作。有时你可以得逞,但在那些情况下,将delete[]与new[]匹配起来是一个好的习惯。

4

我非常怀疑这一点。

有很多可疑的释放内存的方法,例如你可以在你的char数组上使用delete(而不是delete[]),它可能会正常工作。我在博客中详细介绍了这个问题(抱歉自己链接,但这比重写所有内容更容易)。

编译器并不是问题,平台才是问题。大多数库将使用底层操作系统的分配方法,这意味着相同的代码在Mac、Windows和Linux上可能会表现出不同的行为。我见过这样的例子,每一个都是可疑的代码。

最安全的方法是始终使用相同的数据类型来分配和释放内存。如果你正在分配char并将其返回给其他代码,你可能最好提供特定的分配/释放方法:

SOME_STRUCT* Allocate()
{
    size_t cb; // Initialised to something
    return (SOME_STRUCT*)(new char[cb]);
}

 

void Free(SOME_STRUCT* obj)
{
    delete[] (char*)obj;
}

(重载newdelete操作符也是一种选择,但我从来不喜欢这样做。)

2

这是一个非常类似的问题,我在这里回答了一个类似的问题:链接文本

简而言之,根据C++标准,不,它不安全。如果出于某种原因,您需要在内存大小与size_of(SOME_STRUCT)不同(最好更大!)的区域中分配SOME_STRUCT对象,则最好使用类似全局operator new的原始分配函数来执行分配,然后使用放置new在原始内存中创建对象实例。如果对象类型没有构造函数,则放置new将非常便宜。

void* p = ::operator new( cb );
SOME_STRUCT* pSS = new (p) SOME_STRUCT;

// ...

delete pSS;

大多数时候这个方法可以工作。如果SOME_STRUCT是一个 POD-struct,它应该总是有效的。如果SOME_STRUCT的构造函数不抛出异常并且SOME_STRUCT没有自定义 operator delete,它也适用于其他情况。此技术还消除了任何类型转换的需要。

::operator new::operator delete是 C++ 中最接近 malloc free 的等效物。在适当的情况下,它们会被newdelete表达式调用(如果没有类覆盖)。它们可以组合使用(请小心!)。


2

C++标准[5.3.5.2]声明:

如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并在本节的其余部分中使用转换后的操作数替代原始操作数。无论采取哪种替代方案,delete的操作数的值都可以是空指针值。如果不是空指针值,在第一种替代方案(删除对象)中,delete的操作数的值应为指向非数组对象的指针或表示此类对象的基类(第10条)的子对象的指针。否则,行为未定义。在第二种替代方案(删除数组)中,delete的操作数的值应为先前数组new表达式产生的指针值。如果不是,则行为未定义。[注意:这意味着delete表达式的语法必须与由new分配的对象的类型匹配,而不是new表达式的语法。-结束注释] [注意:指向const类型的指针可以是delete表达式的操作数;在将指针表达式用作delete表达式的操作数之前,不需要强制转换指针表达式的常量性(5.2.11)。-结束注释]


0
虽然这个方法应该可以工作,但我不认为你能保证它是安全的,因为SOME_STRUCT不是一个char*(除非它只是一个typedef)。
此外,由于你正在使用不同类型的引用,如果你继续使用*p访问,并且内存已被删除,你将会得到一个运行时错误。

0

如果指向的内存和指针本身都是POD类型,那么这个方法就可以正常工作。在这种情况下,没有析构函数被调用,内存分配器也不知道或者关心内存中存储的数据类型。

对于非POD类型,唯一能够使用这个方法的情况是当指针所指向的对象是指针类型的子类时(例如你用一个Vehicle*指向一个Car),并且指针的析构函数已经声明为虚函数。


0

这样做是不安全的,到目前为止,所有的回答都没有强调这种做法的疯狂之处。如果你认为自己是一个真正的程序员,或者想要在团队中作为专业程序员工作,那么就根本不要这样做。你只能说你的结构体目前不包含析构函数,但是你正在为未来设置一个可能的编译器和系统特定的陷阱。此外,你的代码很可能无法按预期工作。你最好的希望是它不会崩溃。然而,我怀疑你会慢慢地出现内存泄漏,因为通过 new 分配的数组分配通常会在返回指针之前分配额外的字节。你释放的不是你认为的内存。一个好的内存分配例程应该能够检测到这种不匹配,像 Lint 等工具也可以。

根本不要这样做,并且从你的思维过程中清除任何导致你考虑这种荒谬的想法的思路。


0

我已经修改了代码,使用malloc/free。虽然我知道MSVC如何为普通数据(在这种情况下是Win32结构,因此是简单的C)实现new/delete,但我只是想知道它是否是一种可移植的技术。

它不是,所以我会使用一些可移植的东西。


0

如果您使用malloc/free而不是new/delete,则malloc和free不会关心类型。

因此,如果您正在使用类似C的POD(普通旧数据),例如内置类型或结构体,则可以分配某种类型的内存,再释放另一个类型的内存。请注意,即使它能工作,这也是不好的编程风格。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接