I apologize if this question is a duplicate - I searched for a while, but it's possible that my Google-fu just isn't up to snuff.
I am modifying a C++ program that calls into a C library. The C library allocates a bunch of memory (using malloc()
), and the C++ program uses it and then frees it. The catch is that the C++ program can throw an exception midway through execution, causing the allocated memory to never be freed.
As a (rather contrived) example:
/* old_library.c */
char *allocate_lots() {
char *mem = (char *)malloc(1024);
return mem;
}
/* my_prog.cpp */
void my_class::my_func () {
char *mem = allocate_lots();
bool problem = use(mem);
if (problem)
throw my_exception("Oh noes! This will be caught higher up");
free(mem); // Never gets called if problem is true
}
My question is: how ought I to deal with this? My first idea was to wrap the whole thing in a try/catch block, and in the catch just check and free the memory and re-throw the exception, but this seems graceless and clunky to me (and wouldn't work well if I want to actually catch an exception). Is there a better way to do it?
EDIT: I probably should have mentioned that we're using g++ 4.2.2, from back in 2007 before std::unique_ptr was introduced. Chalk it up to corporate inertia.
Use std::unique_ptr
with a custom deleter that calls free:
class free_mem {
public:
void operator()(char *mem) { free(mem); }
};
void my_class::my_func() {
std::unique_ptr<char, free_mem> mem = allocate_lots();
You should make sure that you don't throw until after you have freed the memory - or that you use a suitable smart pointer structure to store the mem
, such that when the throw
happens, and the stack unwinds, the mem
gets freed.
Wrap that rascal:
struct malloc_deleter {
template <typename T>
void operator () (T* p) const {
free(p);
}
};
void my_class::my_func () {
std::unique_ptr<char[],malloc_deleter> mem{allocate_lots()};
bool problem = use(mem.get());
if (problem)
throw my_exception("Oh noes! This will be caught higher up");
}
Since you're using an old compiler version that doesn't have unique_ptr
, you can write your RAII wrapper yourself:
class ResourceWrapper {
public:
ResourceWrapper(char* ptr) : m_ptr(ptr) {}
~ResourceWrapper() { free(m_ptr); }
// whatever getters suit you, at the very least:
char* get() const { return m_ptr; }
private:
char* const m_ptr;
};
void my_class::my_func () {
ResourceWrapper mem(allocate_lots());
bool problem = use(mem.get());
if (problem)
throw my_exception("Oh noes! This will be caught higher up");
}
Just make sure not to allow copy/assignment even implicitly (which is why I made m_ptr
const) or you'd risk ending up with double-freeing your memory ("move" semantics à la auto_ptr
are best avoided unless you absolutely need it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With