删除void指针是否安全?

时间:2009-06-02 20:47:47

标签: c++ memory-management casting void-pointers

假设我有以下代码:

void* my_alloc (size_t size)
{
   return new char [size];
}

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

这样安全吗?或者在删除之前必须ptr转换为char*

13 个答案:

答案 0 :(得分:135)

C ++标准未定义通过void指针删除 - 请参阅第5.3.5 / 3节:

  

在第一种选择中(删除   对象),如果是静态类型的   操作数与其动态不同   类型,静态类型应为基础   操作数的动态类型的类   而静态类型应该有一个   虚拟析构函数或行为是   未定义。在第二种选择   (删除数组)如果动态类型   要删除的对象不同于   它的静态类型,行为是   未定义。

及其脚注:

  

这意味着一个对象不可能   使用void *类型的指针删除   因为没有类型的对象   空隙

答案 1 :(得分:24)

这不是一个好主意,也不是你在C ++中会做的事情。您无缘无故地丢失了类型信息。

当您为非基本类型调用它时,不会在您正在删除的数组中的对象上调用析构函数。

您应该覆盖新/删除。

删除void *可能会错误地释放你的记忆,但这是错误的,因为结果是未定义的。

如果由于某些原因我不知道你需要将指针存储在void *中然后释放它,你应该使用malloc和free。

答案 2 :(得分:20)

这取决于“安全”。它通常会起作用,因为信息与指针一起存储有关分配本身,因此解除分配器可以将它返回到正确的位置。从这个意义上说,只要你的分配器使用内部边界标签,它就是“安全的”。 (很多人都这么做。)

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

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

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

答案 3 :(得分:13)

删除void指针很危险,因为不会在它实际指向的值上调用析构函数。这可能导致应用程序中的内存/资源泄漏。

答案 4 :(得分:7)

这个问题毫无意义。您的困惑可能部分归因于人们经常使用delete

时使用的草率语言

您使用delete销毁动态分配的对象。这样做,您使用指向该对象的指针形成删除表达式。你永远不会“删除一个指针”。你真正做的是“删除一个由其地址标识的对象”。

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

答案 5 :(得分:5)

因为char没有特殊的析构函数逻辑。这不起作用。

class foo
{
   ~foo() { printf("huzza"); }
}

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

d ctor不会被召唤。

答案 6 :(得分:5)

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

答案 7 :(得分:5)

如果你想使用void *,为什么不使用malloc / free? new / delete不仅仅是内存管理。基本上,new / delete调用构造函数/析构函数,还有更多的事情要发生。如果您只是使用内置类型(如char *)并通过void *删除它们,它会工作,但仍然不建议。如果你想使用void *,底线是使用malloc / free。否则,您可以使用模板功能以方便使用。

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);
}

答案 8 :(得分:5)

如果你真的必须这样做,为什么不切断中间人(newdelete运营商)并直接呼叫全球operator newoperator delete? (当然,如果您要对newdelete运算符进行检测,实际上您应该重新实现operator newoperator delete。)

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,则会调用{{1}}。

答案 9 :(得分:0)

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

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;

答案 10 :(得分:0)

没有理由这样做。

首先,如果你不知道数据的类型,而你所知道的只是void*,那么你真的应该把这些数据视为一个二进制数据的无类型 blob unsigned char*),并使用malloc / free来处理它。对于波形数据之类的东西,有时需要这样做,你需要将void*指针传递给C apis。没关系。

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

答案 11 :(得分:0)

我在我的框架中使用了void *,(也就是未知的类型),而在代码反射和其他歧义的功能中,到目前为止,我没有任何编译器的麻烦(内存泄漏,访问冲突等) 。仅由于操作是非标准的警告。

删除未知(void *)是完全有意义的。只要确保指针遵循这些指导原则,或者它可能会停止有意义:

1)未知指针不能指向具有普通解构函数的类型,因此当作为未知指针转换时,它应该永远不会被删除。仅在将未知指针强制转换为ORIGINAL类型后删除它。

2)实例是否被引用为堆栈绑定或堆绑定内存中的未知指针?如果未知指针引用堆栈上的实例,那么它永远不会被删除!

3)你是100%肯定未知指针是有效的内存区域吗?不,那么它永远不应该被删除!

总之,使用未知(void *)指针类型可以完成很少的直接工作。但是,间接地,void *是C ++开发人员在需要数据模糊时依赖的重要资产。

答案 12 :(得分:0)

对于char的特定情况。

char是一种没有特殊析构函数的内在类型。所以泄漏论证是没有实际意义的。

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

在这种情况下,

malloc / free会更快。但是你放弃了std :: bad_alloc并且必须检查malloc的结果。调用全局new和delete运算符可能会更好,因为它绕过了中间人。

相关问题