关于c ++:删除void指针是否安全?

Is it safe to delete a void pointer?

假设我有以下代码:

1
2
3
4
5
6
7
8
9
void* my_alloc (size_t size)
{
   return new char [size];
}

void my_free (void* ptr)
{
   delete [] ptr;
}

这样安全吗?还是必须在删除之前将ptr强制转换为char*


通过空指针删除是由C++标准定义的——请参阅第5.3.5/3部分:

In the first alternative (delete
object), if the static type of the
operand is different from its dynamic
type, the static type shall be a base
class of the operand’s dynamic type
and the static type shall have a
virtual destructor or the behavior is
undefined. 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.

及其脚注:

This implies that an object cannot be
deleted using a pointer of type void*
because there are no objects of type
void

.


这不是一个好主意,不是你在C++中会做的事情。你没有理由丢失你的类型信息。

对于非基元类型调用析构函数时,不会对数组中要删除的对象调用析构函数。

您应该替代新建/删除。

删除void*可能会碰巧正确地释放内存,但这是错误的,因为结果未定义。

如果出于某种我不知道的原因,你需要将指针存储在一个空的*中,然后释放它,你应该使用malloc和free。


它取决于"安全"。它通常会工作,因为信息与分配本身的指针一起存储,因此释放定位器可以将其返回到正确的位置。从这个意义上说,只要分配器使用内部边界标记,它就是"安全的"。(很多人这样做。)

但是,如上所述,删除一个空指针不会调用析构函数,这可能是一个问题。从这个意义上说,它不是"安全的"。

没有充分的理由去做你正在做的事情。如果要编写自己的释放函数,可以使用函数模板生成具有正确类型的函数。这样做的一个很好的原因是生成池分配器,这对于特定的类型来说非常有效。

正如在其他答案中提到的,这是C++中未定义的行为。一般来说,避免未定义的行为是好的,尽管主题本身很复杂,充满了相互矛盾的意见。


删除一个空指针是危险的,因为不会对它实际指向的值调用析构函数。这可能会导致应用程序内存/资源泄漏。


这个问题毫无意义。你的困惑可能部分是因为人们经常与delete一起使用的粗话:

使用delete销毁动态分配的对象。执行此操作时,将使用指向该对象的指针来形成一个删除表达式。你从不"删除指针"。您真正要做的是"删除由其地址标识的对象"。

现在我们看到为什么这个问题毫无意义:空指针不是"对象的地址"。它只是一个地址,没有任何语义。它可能来自实际对象的地址,但是信息会丢失,因为它是用原始指针的类型编码的。恢复对象指针的唯一方法是将空指针转换回对象指针(这要求作者知道指针的含义)。void本身是一个不完整的类型,因此永远不会是一个对象的类型,并且一个空指针永远不能用来标识一个对象。(对象由其类型和地址共同标识。)


如果你真的必须这样做,为什么不直接剔除中间人(newdelete运营商)并呼叫全球operator newoperator delete?(当然,如果你想使用newdelete操作符,你实际上应该重新实现operator newoperator delete。)

1
2
3
4
5
6
7
8
9
void* my_alloc (size_t size)
{
   return ::operator new(size);
}

void my_free (void* ptr)
{
   ::operator delete(ptr);
}

注意,与malloc()不同,operator new在失败时抛出std::bad_alloc(或者如果注册了new_handler,则调用EDOCX1)。


如果你想使用void*,为什么不使用malloc/free?新建/删除不仅仅是内存管理。基本上,new/delete调用一个构造函数/析构函数,还有更多的事情要做。如果只使用内置类型(如char*)并通过void*将其删除,那么它将起作用,但仍然不推荐使用。如果您想使用void*,底线是use malloc/free。否则,为了方便起见,可以使用模板函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template<typename T>
T* my_alloc (size_t size)
{
   return new T [size];
}

template<typename T>
void my_free (T* ptr)
{
   delete [] ptr;
}

int main(void)
{
    char* pChar = my_alloc<char>(10);
    my_free(pChar);
}


很多人已经评论说不,删除一个空指针是不安全的。我同意这一点,但我还想补充一点,如果您使用空指针来分配连续的数组或类似的东西,那么您可以使用new,这样您就可以安全地使用delete(使用一些额外的工作)。这是通过将一个空指针分配到内存区域(称为"竞技场"),然后将指向竞技场的指针提供给new来完成的。请参阅C++ FAQ中的这一部分。这是在C++中实现内存池的常用方法。


因为char没有特殊的析构函数逻辑。这行不通。

1
2
3
4
5
6
7
8
9
10
class foo
{
   ~foo() { printf("huzza"); }
}

main()
{
   foo * myFoo = new foo();
   delete ((void*)foo);
}

司机不会接到电话的。


对于char的特殊情况。

char是一个没有特殊析构函数的内部类型。所以泄密的论据是没有意义的。

sizeof(char)通常是一个,因此也没有对齐参数。在sizeof(char)不是一个稀有平台的情况下,它们为自己的char分配足够对齐的内存。因此,对齐参数也是一个无意义的参数。

在这种情况下,malloc/free会更快。但是你会失去std::bad_alloc,必须检查malloc的结果。调用全局new和delete操作符可能更好,因为它绕过了中间人。


我在框架中使用了void*,(又称未知类型),因为在代码反射和其他模糊特性中,到目前为止,我没有遇到任何编译器的问题(内存泄漏、访问冲突等)。仅警告,因为操作不标准。

删除一个未知(void*)是完全有意义的。只要确保指针遵循这些准则,否则它可能不再有意义:

1)未知指针不能指向具有普通解构器的类型,因此当转换为未知指针时,不应删除该类型。仅在将未知指针转换回原始类型后删除该指针。

2)实例在堆栈绑定内存或堆绑定内存中被引用为未知指针吗?如果未知指针引用堆栈上的实例,则永远不应删除该实例!

3)您是否100%为正?未知指针是有效的内存区域?不,那就永远不要钻研了!

总之,使用未知(void*)指针类型可以完成的直接工作很少。然而,间接地,空洞*是C++开发者依赖于数据歧义所需的巨大资产。


几乎没有理由这样做。

首先,如果你不知道数据的类型,你只知道它是void*,那么你真的应该把数据当作一个无类型的二进制数据块(unsigned char*),并使用malloc/free来处理它。有时需要这样做,例如波形数据等,在这些数据中,需要将void*指针传递到C API。那很好。

如果您确实知道数据的类型(即它有一个ctor/dtor),但由于某种原因,您最终得到了一个void*指针(无论您有什么原因),那么您真的应该将其转换回您知道的类型,并在其上调用delete


如果您只需要一个缓冲区,请使用malloc/free。如果必须使用new/delete,请考虑一个普通的包装类:

1
2
3
4
5
6
7
8
9
10
template<int size_ > struct size_buffer {
  char data_[ size_];
  operator void*() { return (void*)&data_; }
};

typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer

OpaqueBuffer* ptr = new OpaqueBuffer();

delete ptr;