views:

1449

answers:

11

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?

+9  A: 

It's not a good idea and not something you would do in 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.

Deleting the void* will probably free your memory correctly by chance, but it's wrong because the results are undefined.

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.

Brian R. Bondy
You're right about the destructor not being called, but wrong about the size being unknown. If you give delete a pointer that you got from new, it *does* in fact know the size of the thing being deleted, entirely apart from the type. How it does it isn't specified by the C++ standard, but I've seen implementations where the size is stored immediately before the data that the pointer returned by 'new' points to.
KeyserSoze
Removed the part about the size, although C++ standard says it is undefined. I know that malloc/free would work though for void* pointers.
Brian R. Bondy
Don't suppose you have a weblink to the relevant section of the standard? I know that the few implementations of new/delete I've looked at definitely work correctly without type knowledge, but I admit I haven't looked at what the standard specifies. IIRC C++ originally required an array element count when deleting arrays, but no longer does with the newest versions.
KeyserSoze
Please see @Neil Butterworth answer. His answer should be the accepted one in my opinion.
Brian R. Bondy
@keysersoze: are you sure new returns data preceded by its size? It would be weird for me to avoid using virtuals because of extra space taked for vptr, and then just pay the same price (the size is probably 4 bytes, like the pointer) for thing, that doesn't look very usable.
@keysersoze: Generally I don't agree with your statement. Just because some implementation did store size before allocated memory doesn't mean that it's a rule.
A: 

What I remember from plain old C is that "void" is a funny way to say "int" (which is a funny way to ask for a machine word).

If, and only if, delete can detect a "non object" pointer, it might "decay" to a call to free(), but what's the point?

Is my_alloc() supposed to be an emulation of calloc(), which 0 fills the memory returned? Is my_free supposed to guard for null pointers??? Try this instead:

#define FREE_NULL( p) ( ( p != NULL ? free( p) : 0), p = NULL)
Roboprog
Wrong, although "int" can be used most places "void" is, if you've got a really ancient compiler. Also, "delete" is not a synonym for "free", since it not only deallocates memory but runs destructors.
David Thornley
Some of us have used really ancient compilers. I guess some of us are getting really ancient, not having the spec to your newfangled "C with classes" memorized :-)
Roboprog
Considerring what you put your foot in mouth over, it would have been better to stay silent. What David pointed out is basics from the C++ language, not something that should ever be easily forgotten. I've never seen the standard, and I knew that delete and malloc were 2 completely different things.
Correction, delete and free, sorry.
I'll also point out at this point. Macros are a really really bad idea in C++ when some other language construct is available.
This is a troll.
Johannes Schaub - litb
Woah why so many downvotes?. Roboprog's statement is correct. In early C, void was indeed an int. And delete does "decay" as positied for fundamental types, though it undefined for void. And most importantly, Roboprog points out the futility of this exercise.Any while my I was taught to avoid Macros in school, many popular C++ frameworks depend on them.
Sanjaya R
some people like down votes. I like the "read a file into 1 string in Perl" question. The best (from a slow but Lazy) answer had a negative score. Go figure.
Roboprog
+1  A: 

Now the question is, why does it work? (I was curious, and tried it out)

ryansstack
As I've read here before, working exists in the set of undefined behavior. You just can't rely on it.
overslacked
+4  A: 

Deleting a void pointer is dangerous because destructors will not be called on the value it actually points to. This can result in memory / resource leaks in your application.

JaredPar
+1  A: 

Because char has no special destructor logic. THIS won't work.

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

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

The d'ctor won't get called.

+1  A: 

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). 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. See this section in the C++ FAQ lite. This is a common approach to implementing memory pools in C++.

Paul Morie
+3  A: 

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 above, destructing a void pointer will not call destructors, which can be a problem. In that sense, it is not "safe."

There is no good reason to do what you are doing. 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.

Christopher
+23  A: 

Deleting via a void pointer is undefined by the C++ Standard - see section 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

.

anon
Are you sure you hit the right quote? I think the footnote is referring to this text: "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." :)
Johannes Schaub - litb
You are right - I've updated the answer. I don't think it negates the basic point though?
anon
No, of course not. It still says it's UB. Even more so, now it states it normatively that deleting void* is UB :)
Johannes Schaub - litb
+1  A: 

If you want to use void*, why don't you use just malloc/free? new/delete is more than just memory managing. Basically, new/delete calls a constructor/destructor and there are more things going on. If you just use built-in types (like char*) and delete them through void*, it would work but still it's not recommended. The bottom line is use malloc/free if you want to use void*. 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);
}
young
I didn't write the code in the example - stumbled upon this pattern being used in a couple places, curiously mixing C/C++ memory management, and was wondering what the specific dangers were.
Andrew
Writing C/C++ is a recipe for failure. Whoever wrote that should have written one or the other.
David Thornley
A: 

If just want a buffer, just use malloc/free. If you must use new/delete, consider a trivial wrapper class:

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;
Sanjaya R
+2  A: 

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? (Of course, if you're trying to instrument the new and delete operators, you actually ought to reimplement operator new and operator 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).

bk1e