删除void指针是否安全?

An̲*_*rew 88 c++ memory-management casting void-pointers

假设我有以下代码:

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

void my_free (void* ptr)
{
   delete [] ptr;
}
Run Code Online (Sandbox Code Playgroud)

这样安全吗?或者必须在删除前ptr进行投射char*

小智 138

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

在第一个替代(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或行为未定义.在第二个备选(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义.

其脚注:

这意味着无法使用void*类型的指针删除对象,因为没有void类型的对象

.

  • 你确定你找到了合适的报价吗?我认为脚注是指这个文本:"在第一个替代(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应该是操作数的动态类型的基类和静态type应该有一个虚析构函数或者行为是未定义的.在第二种方法(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为是未定义的.:) (5认同)
  • 你是对的 - 我已经更新了答案。我不认为它否定了基本观点? (3认同)

Bri*_*ndy 24

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

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

您应该改为覆盖新/删除.

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

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

  • 对于没有被调用的析构函数你是对的,但是对于未知的大小是错误的.如果你给删除一个你从new获得的指针,它*确实*知道被删除的东西的大小,完全除了类型.它是如何做的不是由C++标准指定的,但是我已经看到了大小存储在'new'返回的指针指向的数据之前的实现. (5认同)
  • @keysersoze:一般我不同意你的陈述.仅仅因为某些实现在分配内存之前存储了大小并不意味着它是一个规则. (2认同)

Chr*_*her 22

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

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

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

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

  • 这是一个公认的答案?"删除无效指针"没有任何意义 - 安全是一个没有实际意义的点. (8认同)
  • @rxantos提供一个反例,在C++中做一个问题的作者想做的事情是个好主意. (8认同)
  • "没有充分的理由去做你正在做的事情." 这是你的意见,而不是事实. (5认同)

Jar*_*Par 13

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

  • char 没有构造函数/析构函数。 (2认同)

Ker*_* SB 7

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

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

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

  • Downvoter,关心解释异议? (3认同)

小智 5

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

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

main()
{
   foo * myFoo = new foo();
   delete ((void*)foo);
}
Run Code Online (Sandbox Code Playgroud)

d ctor不会被叫到.


Pau*_*rie 5

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


you*_*ung 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);
}
Run Code Online (Sandbox Code Playgroud)

  • @David 那是 C++,而不是 C/C++。C 没有模板,也没有使用 new 和 delete。 (2认同)

bk1*_*k1e 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);
}
Run Code Online (Sandbox Code Playgroud)

请注意,不同的是malloc(),operator new抛出std::bad_alloc失败(或调用new_handlerif如果已注册).