Rants on software, computing, and any other topic I feel like.

Friday, May 06, 2011

Using malloc Safely in C++

For many of us C++ programmers, a key advantage of using the language is the compatibility with C. For the most part, this interface works well. However, there are some places where this interface isn't so easy to deal with. Take memory allocation for example. In C, one typically uses malloc (or some equivalent for the platform) to allocate memory and free to release it. In C++, we tend to use the built-in new and delete operators to accomplish the same thing.

When interacting with C libraries, it can sometimes be impossible to avoid using malloc and free. Libraries often assume that any memory that it allocates and hands off to the user will be freed using free. Since you can't tell what sort of memory a particular pointer points to, it's easy to make mistakes when deallocating them. Using mismatched allocators/deallocators might cause memory leaks, crash your program and work just fine depending the platform, compiler, or phases of the moon.

The first thought that any self respecting C++ programmer will think of is RAII (Resource Acquisition Is Initialization). So they might write a class that automatically calls free when the pointer is no longer needed.
template<typename T>
class malloc_handler
{
public:
malloc_handler() : _ptr(malloc(sizeof(T))) {}
~malloc_handler() { free(_ptr); }
T* ptr() { return _ptr; }
private:
T* _ptr;
};

void foo()
{
malloc_handler<int> x();
x.ptr();
}
This has a number of disadvantages including the lack of a copy constructor, the requirement of a new class and the lack of any convenient operator overloads to make it look more like a pointer. There's a much better way and one that doesn't require the programmer to roll their own solution. Use shared_ptr and its customer deleter feature.
shared_ptr<int> x((int*)malloc(sizeof(int)), free);
This feature is probably something that the creators of shared_ptr already envisioned, but I don't generally see it discussed. Now, the above code is a bit ugly, so we can make it a little bit easier by rolling our own code.
template<typename T>
shared_ptr<T> shared_malloc()
{
shared_ptr<T> rval((T*)malloc(sizeof(T)), free);
return rval;
}

shared_ptr<int> x = shared_malloc<int>();
We can create a safe version of strdup:
shared_ptr<char> shared_strdup(const char* str)
{
shared_ptr<char> rval(strdup(str), free);
return rval;
}

shared_ptr<char> s = shared_strdup("Hello World!\n");

Labels: , ,

This page is powered by Blogger. Isn't yours?