简体   繁体   English

删除空指针是否安全?

[英]Is it safe to delete a void pointer?

Suppose I have the following code:假设我有以下代码:

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

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

Is this safe?这安全吗? Or must ptr be cast to char* prior to deletion?还是必须在删除之前将ptrchar*

Deleting via a void pointer is undefined by the C++ Standard - see section 5.3.5/3: C++ 标准未定义通过 void 指针删除 - 请参阅第 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.在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。

And its footnote:以及它的脚注:

This implies that an object cannot be deleted using a pointer of type void* because there are no objects of type void这意味着不能使用 void* 类型的指针删除对象,因为没有 void 类型的对象

. .

It's not a good idea and not something you would do in C++.这不是一个好主意,也不是您在 C++ 中会做的事情。 You are losing your type info for no reason.您无缘无故丢失了类型信息。

Your destructor won't be called on the objects in your array that you are deleting when you call it for non primitive types.当您为非基本类型调用析构函数时,不会在要删除的数组中的对象上调用析构函数。

You should instead override new/delete.您应该改写 new/delete。

Deleting the void* will probably free your memory correctly by chance, but it's wrong because the results are undefined.删除 void* 可能会偶然正确释放您的内存,但这是错误的,因为结果未定义。

If for some reason unknown to me you need to store your pointer in a void* then free it, you should use malloc and free.如果由于某种原因我不知道您需要将指针存储在 void* 中然后释放它,您应该使用 malloc 和 free。

It depends on "safe."这取决于“安全”。 It will usually work because information is stored along with the pointer about the allocation itself, so the deallocator can return it to the right place.它通常会起作用,因为信息与有关分配本身的指针一起存储,因此解除分配器可以将其返回到正确的位置。 In this sense it is "safe" as long as your allocator uses internal boundary tags.从这个意义上说,只要您的分配器使用内部边界标记,它就是“安全的”。 (Many do.) (许多人这样做。)

However, as mentioned in other answers, deleting a void pointer will not call destructors, which can be a problem.但是,正如其他答案中提到的,删除 void 指针不会调用析构函数,这可能是一个问题。 In that sense, it is not "safe."从这个意义上说,它并不“安全”。

There is no good reason to do what you are doing the way you are doing it.没有充分的理由去做你正在做的事情。 If you want to write your own deallocation functions, you can use function templates to generate functions with the correct type.如果要编写自己的解除分配函数,可以使用函数模板生成具有正确类型的函数。 A good reason to do that is to generate pool allocators, which can be extremely efficient for specific types.这样做的一个很好的理由是生成池分配器,这对于特定类型可能非常有效。

As mentioned in other answers, this is undefined behavior in C++.正如其他答案中提到的,这是 C++ 中未定义的行为 In general it is good to avoid undefined behavior, although the topic itself is complex and filled with conflicting opinions.一般来说,避免未定义的行为是好的,尽管主题本身很复杂,而且充满了相互矛盾的意见。

Deleting a void pointer is dangerous because destructors will not be called on the value it actually points to.删除 void 指针是危险的,因为不会在它实际指向的值上调用析构函数。 This can result in memory / resource leaks in your application.这可能会导致应用程序中的内存/资源泄漏。

The question makes no sense.这个问题没有意义。 Your confusion may be partly due to the sloppy language people often use with delete :您的困惑可能部分是由于人们经常与delete使用的草率语言:

You use delete to destroy an object that was dynamically allocated.您可以使用delete来销毁动态分配的对象 Do do so, you form a delete expression with a pointer to that object .这样做,您会形成一个带有指向该对象指针删除表达式 You never "delete a pointer".你永远不会“删除一个指针”。 What you really do is "delete an object which is identified by its address".您真正要做的是“删除由其地址标识的对象”。

Now we see why the question makes no sense: A void pointer isn't the "address of an object".现在我们明白为什么这个问题没有意义了:空指针不是“对象的地址”。 It's just an address, without any semantics.它只是一个地址,没有任何语义。 It may have come from the address of an actual object, but that information is lost, because it was encoded in the type of the original pointer.可能来自实际对象的地址,但该信息丢失了,因为它以原始指针的类型编码。 The only way to restore an object pointer is to cast the void pointer back to an object pointer (which requires the author to know what the pointer means).恢复对象指针的唯一方法是将void指针强制转换回对象指针(这需要作者知道指针的含义)。 void itself is an incomplete type and thus never the type of an object, and a void pointer can never be used to identify an object. void本身是一个不完整的类型,因此永远不会是对象的类型,并且永远不能使用 void 指针来标识对象。 (Objects are identified jointly by their type and their address.) (对象由它们的类型和它们的地址共同标识。)

If you really must do this, why not cut out the middle man (the new and delete operators) and call the global operator new and operator delete directly?如果你真的必须这样做,为什么不去掉中间人( newdelete操作符)并直接调用全局operator newoperator delete呢? (Of course, if you're trying to instrument the new and delete operators, you actually ought to reimplement operator new and operator delete .) (当然,如果您尝试检测newdelete运算符,您实际上应该重新实现operator newoperator delete 。)

void* my_alloc (size_t size)
{
   return ::operator new(size);
}

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

Note that unlike malloc() , operator new throws std::bad_alloc on failure (or calls the new_handler if one is registered).请注意,与malloc()不同, operator new在失败时抛出std::bad_alloc (如果注册了则调用new_handler )。

Because char has no special destructor logic.因为 char 没有特殊的析构函数逻辑。 THIS won't work.这行不通。

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

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

The d'ctor won't get called. d'ctor 不会被叫到。

A lot of people have already commented saying that no, it's not safe to delete a void pointer.很多人已经评论说不,删除空指针是不安全的。 I agree with that, but I also wanted to add that if you're working with void pointers in order to allocate contiguous arrays or something similar, that you can do this with new so that you'll be able to use delete safely (with, ahem, a little of extra work).我同意这一点,但我还想补充一点,如果您使用 void 指针来分配连续数组或类似的东西,您可以使用new来执行此操作,以便您可以安全地使用delete (使用,嗯,有点额外的工作)。 This is done by allocating a void pointer to the memory region (called an 'arena') and then supplying the pointer to the arena to new.这是通过为内存区域(称为“arena”)分配一个空指针,然后将指向该arena 的指针提供给new 来完成的。 See this section in the C++ FAQ .请参阅C++ FAQ中的这一部分。 This is a common approach to implementing memory pools in C++.这是在 C++ 中实现内存池的常用方法。

If you want to use void*, why don't you use just malloc/free?如果您想使用 void*,为什么不只使用 malloc/free? new/delete is more than just memory managing.新建/删除不仅仅是内存管理。 Basically, new/delete calls a constructor/destructor and there are more things going on.基本上,new/delete 调用构造函数/析构函数,并且还有更多事情要做。 If you just use built-in types (like char*) and delete them through void*, it would work but still it's not recommended.如果您只是使用内置类型(如 char*)并通过 void* 删除它们,它会起作用,但仍然不推荐。 The bottom line is use malloc/free if you want to use void*.如果你想使用 void*,底线是使用 malloc/free。 Otherwise, you can use template functions for your convenience.否则,您可以为方便起见使用模板函数。

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

There is hardly a reason to do this.几乎没有理由这样做。

First of all, if you don't know the type of the data, and all you know is that it's void* , then you really should just be treating that data as a typeless blob of binary data ( unsigned char* ), and use malloc / free to deal with it.首先,如果您不知道数据的类型,并且您只知道它是void* ,那么您真的应该将该数据视为二进制数据的无类型blobunsigned char* ),并使用malloc / free来处理吧。 This is required sometimes for things like waveform data and the like, where you need to pass around void* pointers to C apis.有时对于波形数据等需要这样做,您需要将void*指针传递给 C api。 That's fine.没关系。

If you do know the type of the data (ie it has a ctor/dtor), but for some reason you ended up with a void* pointer (for whatever reason you have) then you really should cast it back to the type you know it to be , and call delete on it.如果知道数据的类型(即它有一个构造函数/析构函数),但由于某种原因你结束了一个void*指针(不管是什么原因,你有),那么你真的应该将它转换回你知道类型它是,并调用delete它。

I have used void*, (aka unknown types) in my framework for while in code reflection and other feats of ambiguity, and so far, I have had no troubles (memory leak, access violations, etc.) from any compilers.我在我的框架中使用了 void*,(又名未知类型),用于代码反射和其他歧义的壮举,到目前为止,我没有遇到任何编译器的问题(内存泄漏、访问冲突等)。 Only warnings due to the operation being non-standard.仅因操作不规范而发出警告。

It perfectly makes sense to delete an unknown (void*).删除未知 (void*) 是完全有意义的。 Just make sure the pointer follows these guidelines, or it may stop making sense:只需确保指针遵循这些准则,否则它可能会失去意义:

1) The unknown pointer must not point to a type that has a trivial deconstructor, and so when casted as an unknown pointer it should NEVER BE DELETED. 1) 未知指针不能指向具有平凡解构函数的类型,因此当它被转换为未知指针时,它永远不能被删除。 Only delete the unknown pointer AFTER casting it back into the ORIGINAL type.仅在将其转换回原始类型后删除未知指针。

2) Is the instance being referenced as an unknown pointer in stack bound or heap bound memory? 2) 实例是否被引用为堆栈绑定或堆绑定内存中的未知指针? If the unknown pointer references an instance on the stack, then it should NEVER BE DELETED!如果未知指针引用堆栈上的一个实例,则永远不应删除它!

3) Are you 100% positive the unknown pointer is a valid memory region? 3) 您是否 100% 肯定未知指针是有效的内存区域? No, then it should NEVER BE DELTED!不,那么它永远不应该被删除!

In all, there is very little direct work that can be done using an unknown (void*) pointer type.总之,使用未知 (void*) 指针类型可以完成的直接工作非常少。 However, indirectly, the void* is a great asset for C++ developers to rely on when data ambiguity is required.然而,间接地,当需要数据歧义时,void* 是 C++ 开发人员依赖的重要资产。

For the particular case of char.对于 char 的特殊情况。

char is an intrinsic type that does not have a special destructor. char 是一种没有特殊析构函数的内在类型。 So the leaks arguments is a moot one.因此,泄漏论点是没有实际意义的。

sizeof(char) is usually one so there is no alignment argument either. sizeof(char) 通常是 1,所以也没有对齐参数。 In the case of rare platform where the sizeof(char) is not one, they allocate memory aligned enough for their char.在 sizeof(char) 不是 1 的罕见平台的情况下,它们分配足够对齐的内存以供其 char 使用。 So the alignment argument is also a moot one.所以对齐论点也是一个没有实际意义的论点。

malloc/free would be faster on this case. malloc/free 在这种情况下会更快。 But you forfeit std::bad_alloc and have to check the result of malloc.但是你没收 std::bad_alloc 并且必须检查 malloc 的结果。 Calling the global new and delete operators might be better as it bypass the middle man.调用全局 new 和 delete 运算符可能会更好,因为它绕过了中间人。

If you just want a buffer, use malloc/free.如果您只想要一个缓冲区,请使用 malloc/free。 If you must use new/delete, consider a trivial wrapper class:如果您必须使用 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM