6

如果这个问题是重复的,我深表歉意 - 我搜索了一段时间,但我的 Google-fu 可能无法满足要求。

我正在修改一个调用 C 库的 C++ 程序。C 库分配一堆内存(使用malloc()),C++ 程序使用它然后释放它。问题是 C++ 程序可以在执行过程中抛出异常,导致分配的内存永远不会被释放。

作为一个(相当人为的)示例:

/* 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
}

我的问题是:我应该如何处理这个问题?我的第一个想法是将整个东西包装在一个 try/catch 块中,并在 catch 中检查并释放内存并重新抛出异常,但这对我来说似乎很不优雅和笨重(如果我不能很好地工作想要实际捕获异常)。有更好的方法吗?

编辑:我可能应该提到我们正在使用 g++ 4.2.2,早在 2007 年引入 std::unique_ptr 之前。将其归结为企业惯性。

4

7 回答 7

8

std::unique_ptr与免费调用的自定义删除器一起使用:

class free_mem {
public:
    void operator()(char *mem) { free(mem); }
};

void my_class::my_func() {
    std::unique_ptr<char, free_mem> mem = allocate_lots();
于 2013-07-29T21:23:13.303 回答
4

您应该确保在释放内存之前不要抛出 - 或者使用合适的智能指针结构来存储mem,这样当throw发生时,堆栈展开,mem就会被释放。

于 2013-07-29T21:21:09.960 回答
4

把那个流氓包起来:

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");
}
于 2013-07-29T21:22:58.323 回答
2

由于您不能使用std::unique_ptr,您可以创建自己的删除器类,以 RAII 方式控制指针的生命周期。为了简单起见,这个例子没有包装实际的指针,而是存在于它旁边;更安全的方法是创建一个真正的智能指针类。

class AutoFree
{
public:
    AutoFree(void* p) : m_p(p)
    {
    }
    ~AutoFree()
    {
        free(m_p);
    }
private:
    void* m_p;
};

void my_class::my_func () {
    char *mem = allocate_lots();
    AutoFree mem_free(mem);
    bool problem = use(mem);
    if (problem)
        throw my_exception("Oh noes! This will be caught higher up");
}
于 2013-07-29T22:03:25.870 回答
2

由于您使用的是没有 的旧编译器版本,因此unique_ptr您可以自己编写 RAII 包装器:

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");
}

只要确保即使是隐式地不允许复制/分配(这就是我制作const 的原因),否则您可能会最终导致双重释放内存(除非您绝对需要,否则最好避免m_ptr使用“移动”语义)。auto_ptr

于 2013-07-29T21:49:04.107 回答
1

有什么理由不简单地释放 if 子句中的内存吗?

if (problem) {
    free (mem);
    throw my_exception ("Drat!");
}
于 2013-07-29T21:22:12.803 回答
1

使用 unique_ptr:http ://coliru.stacked-crooked.com/view?id=cd3f0fc64d99cc07a2350e2ff9686500-542192d2d8aca3c820c7acc656fa0c68

#include <stdexcept>
#include <iostream>

#include <memory>

/* old_library.c */
char *allocate_lots()
{
    return static_cast<char*>(malloc(1024));
}

struct my_exception : virtual std::exception {
    const char* const msg;
    my_exception(const char* const msg) : msg(msg) {}
    const char* what() const noexcept { return msg; }
};

struct my_class
{
    struct Free { void operator() (char* p) const { free(p); } };
    /* my_prog.cpp */
    void my_func()
    {
        std::unique_ptr<char, Free> mem;

        mem.reset(allocate_lots());
        bool problem = use(mem.get());

        if(problem)
        {
            throw my_exception("Oh noes! This will be caught higher up");
        }
    }

    static bool use(char*) { return true; }
};

int main()
{
    my_class prog;
    prog.my_func();
}
于 2013-07-29T21:32:26.883 回答