Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should memory be freed after an exception is thrown in C++?

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.

like image 489
Dan Avatar asked Jul 29 '13 21:07

Dan


4 Answers

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();
like image 79
Chris Dodd Avatar answered Nov 05 '22 22:11

Chris Dodd


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.

like image 27
Mats Petersson Avatar answered Nov 06 '22 00:11

Mats Petersson


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");
}
like image 4
Casey Avatar answered Nov 05 '22 23:11

Casey


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).

like image 2
syam Avatar answered Nov 05 '22 22:11

syam