This is a short post about using std::unique_ptr with malloc() and free(). Although, it can be used with other resource management functions too (files, sockets, etc.).
Often, when working with legacy C and C++ code, we see usages of malloc()
and free()
. Just like new and delete, explicit memory management should be hidden in the guts of libraries whenever possible and never be exposed to the casual programmer.
It would be great if the legacy code could be easily changed to use std::make_unique()
to make it nice clean and safe at the same time. Unfortunately, std::make_unique()
uses new and delete operators internally. So, if our legacy code uses some custom functions to allocate and deallocate memory, then we may be forced to do more refactoring than we might have time for (e.g. to use new expressions instead of custom or malloc based allocators).
Luckily, we can still get the benefit of RAII by using std::unique_ptr
by trading off some cleanliness.
But std::unique_ptr
takes a deleter type. No problem, we have decltype()
to the rescue!
#include <memory>
int main()
{
auto Data =
std::unique_ptr<double, decltype(free)*>{
reinterpret_cast<double*>(malloc(sizeof(double)*50)),
free };
return 0;
}
The decltype(free)
gives back a function type of “void (void*)
” but we need a pointer to a function. So, we say “decltype(free)*
” which gives us “void (*)(void*)
”. Excellent!
A bit awkward, but it is still nice, since it does RAII (automatic free) and both the allocator (malloc()
) and the deallocator (free()
) is clearly visible to the reader.
With decltype()
, we don’t have to write our own deleter functor like in this example:
#include <memory>
struct MyDeleter
{
void operator()(double *p) { free(p); }
};
int main()
{
auto Data =
std::unique_ptr<double, MyDeleter>{
reinterpret_cast<double*>(malloc(sizeof(double) * 50)) };
return 0;
}
Also, with decltype()
, we don’t have to spell out the type of the deleter like in this example:
#include <memory>
int main()
{
auto Data =
std::unique_ptr<double, void(*)(void*)>{
reinterpret_cast<double*>(malloc(sizeof(double) * 50)),
free };
return 0;
}
So, with std::unique_ptr
, you can quickly hack RAII into legacy code. However, as a general guideline, prefer refactoring legacy code using modern C++.
CodeProject