[英]Is it safe to delete a void pointer?
假设我有以下代码:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
这安全吗? 还是必须在删除之前将ptr
为char*
?
C++ 标准未定义通过 void 指针删除 - 请参阅第 5.3.5/3 节:
在第一种选择(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数动态类型的基类,且静态类型应具有虚拟析构函数或行为未定义. 在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。
以及它的脚注:
这意味着不能使用 void* 类型的指针删除对象,因为没有 void 类型的对象
.
这不是一个好主意,也不是您在 C++ 中会做的事情。 您无缘无故丢失了类型信息。
当您为非基本类型调用析构函数时,不会在要删除的数组中的对象上调用析构函数。
您应该改写 new/delete。
删除 void* 可能会偶然正确释放您的内存,但这是错误的,因为结果未定义。
如果由于某种原因我不知道您需要将指针存储在 void* 中然后释放它,您应该使用 malloc 和 free。
这取决于“安全”。 它通常会起作用,因为信息与有关分配本身的指针一起存储,因此解除分配器可以将其返回到正确的位置。 从这个意义上说,只要您的分配器使用内部边界标记,它就是“安全的”。 (许多人这样做。)
但是,正如其他答案中提到的,删除 void 指针不会调用析构函数,这可能是一个问题。 从这个意义上说,它并不“安全”。
没有充分的理由去做你正在做的事情。 如果要编写自己的解除分配函数,可以使用函数模板生成具有正确类型的函数。 这样做的一个很好的理由是生成池分配器,这对于特定类型可能非常有效。
正如其他答案中提到的,这是 C++ 中未定义的行为。 一般来说,避免未定义的行为是好的,尽管主题本身很复杂,而且充满了相互矛盾的意见。
删除 void 指针是危险的,因为不会在它实际指向的值上调用析构函数。 这可能会导致应用程序中的内存/资源泄漏。
这个问题没有意义。 您的困惑可能部分是由于人们经常与delete
使用的草率语言:
您可以使用delete
来销毁动态分配的对象。 这样做,您会形成一个带有指向该对象的指针的删除表达式。 你永远不会“删除一个指针”。 您真正要做的是“删除由其地址标识的对象”。
现在我们明白为什么这个问题没有意义了:空指针不是“对象的地址”。 它只是一个地址,没有任何语义。 它可能来自实际对象的地址,但该信息丢失了,因为它以原始指针的类型编码。 恢复对象指针的唯一方法是将void指针强制转换回对象指针(这需要作者知道指针的含义)。 void
本身是一个不完整的类型,因此永远不会是对象的类型,并且永远不能使用 void 指针来标识对象。 (对象由它们的类型和它们的地址共同标识。)
如果你真的必须这样做,为什么不去掉中间人( new
和delete
操作符)并直接调用全局operator new
和operator delete
呢? (当然,如果您尝试检测new
和delete
运算符,您实际上应该重新实现operator new
和operator 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
)。
因为 char 没有特殊的析构函数逻辑。 这行不通。
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
d'ctor 不会被叫到。
很多人已经评论说不,删除空指针是不安全的。 我同意这一点,但我还想补充一点,如果您使用 void 指针来分配连续数组或类似的东西,您可以使用new
来执行此操作,以便您可以安全地使用delete
(使用,嗯,有点额外的工作)。 这是通过为内存区域(称为“arena”)分配一个空指针,然后将指向该arena 的指针提供给new 来完成的。 请参阅C++ FAQ中的这一部分。 这是在 C++ 中实现内存池的常用方法。
如果您想使用 void*,为什么不只使用 malloc/free? 新建/删除不仅仅是内存管理。 基本上,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);
}
几乎没有理由这样做。
首先,如果您不知道数据的类型,并且您只知道它是void*
,那么您真的应该将该数据视为二进制数据的无类型blob ( unsigned char*
),并使用malloc
/ free
来处理吧。 有时对于波形数据等需要这样做,您需要将void*
指针传递给 C api。 没关系。
如果你知道数据的类型(即它有一个构造函数/析构函数),但由于某种原因你结束了一个void*
指针(不管是什么原因,你有),那么你真的应该将它转换回你知道类型它是,并调用delete
它。
我在我的框架中使用了 void*,(又名未知类型),用于代码反射和其他歧义的壮举,到目前为止,我没有遇到任何编译器的问题(内存泄漏、访问冲突等)。 仅因操作不规范而发出警告。
删除未知 (void*) 是完全有意义的。 只需确保指针遵循这些准则,否则它可能会失去意义:
1) 未知指针不能指向具有平凡解构函数的类型,因此当它被转换为未知指针时,它永远不能被删除。 仅在将其转换回原始类型后删除未知指针。
2) 实例是否被引用为堆栈绑定或堆绑定内存中的未知指针? 如果未知指针引用堆栈上的一个实例,则永远不应删除它!
3) 您是否 100% 肯定未知指针是有效的内存区域? 不,那么它永远不应该被删除!
总之,使用未知 (void*) 指针类型可以完成的直接工作非常少。 然而,间接地,当需要数据歧义时,void* 是 C++ 开发人员依赖的重要资产。
对于 char 的特殊情况。
char 是一种没有特殊析构函数的内在类型。 因此,泄漏论点是没有实际意义的。
sizeof(char) 通常是 1,所以也没有对齐参数。 在 sizeof(char) 不是 1 的罕见平台的情况下,它们分配足够对齐的内存以供其 char 使用。 所以对齐论点也是一个没有实际意义的论点。
malloc/free 在这种情况下会更快。 但是你没收 std::bad_alloc 并且必须检查 malloc 的结果。 调用全局 new 和 delete 运算符可能会更好,因为它绕过了中间人。
如果您只想要一个缓冲区,请使用 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;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.