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; } |
这样安全吗?还是必须在删除之前将
通过空指针删除是由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++中未定义的行为。一般来说,避免未定义的行为是好的,尽管主题本身很复杂,充满了相互矛盾的意见。
删除一个空指针是危险的,因为不会对它实际指向的值调用析构函数。这可能会导致应用程序内存/资源泄漏。
这个问题毫无意义。你的困惑可能部分是因为人们经常与
使用
现在我们看到为什么这个问题毫无意义:空指针不是"对象的地址"。它只是一个地址,没有任何语义。它可能来自实际对象的地址,但是信息会丢失,因为它是用原始指针的类型编码的。恢复对象指针的唯一方法是将空指针转换回对象指针(这要求作者知道指针的含义)。
如果你真的必须这样做,为什么不直接剔除中间人(
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); } |
注意,与
如果你想使用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); } |
很多人已经评论说不,删除一个空指针是不安全的。我同意这一点,但我还想补充一点,如果您使用空指针来分配连续的数组或类似的东西,那么您可以使用
因为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++开发者依赖于数据歧义所需的巨大资产。
几乎没有理由这样做。
首先,如果你不知道数据的类型,你只知道它是
如果您确实知道数据的类型(即它有一个ctor/dtor),但由于某种原因,您最终得到了一个
如果您只需要一个缓冲区,请使用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; |