【问题标题】:Freeing local buffers when throwing exceptions in C++在 C++ 中抛出异常时释放本地缓冲区
【发布时间】:2011-12-06 19:59:54
【问题描述】:

假设我在 C++ 类中有以下构造函数:

MyClass::MyClass()
{
    char* buffer = malloc(100);
    if (0 != someD3DXCallThatCanFail(..., buffer, ...))
    {
        free(buffer);
        throw MyException(L"some message");
    }
    char* buffer2 = malloc(200);
    if (0 != anotherD3DCallThatCanFail(..., buffer2, ...))
    {
        free(buffer);
        free(buffer2);
        throw MyException(L"another message");
    }
    .. more code here where buffer and buffer2 are still used

    free(buffer);
    free(buffer2);
}

编辑:我讨厌 malloc/free 和 new/delete,但不幸的是,我需要使用缓冲区来加载纹理,然后将这些纹理传递给 ID3D10ShaderResourceView、ID3D10Buffer、顶点缓冲区等。所有这些都需要指向缓冲区的指针。

我正在尝试使用异常而不是返回错误代码。 我还希望在需要它们的地方创建缓冲区,并在不再需要它们后立即释放它们。

然而,看起来很难看的是,如果出现错误,无论我返回错误代码还是抛出异常,我仍然应该记得清理到那时创建的任何缓冲区。如果我有 10 个缓冲区和 10 个可能的错误点,我将不得不调用 free() 100 次(在每个错误情况下记得释放每个缓冲区)。

现在假设我或更糟糕的是,我的同事想要更改一些逻辑,例如,在中间某处添加另一个缓冲区。现在,他必须仔细检查其余方法中可能发生的所有错误,并在每个这样的地方为该缓冲区添加 free()。如果他赶时间,他很容易忽略一些这样的地方,然后你就会出现内存泄漏。

这也极大地膨胀了代码。

finally 关键字可以解决 Java 或 C# 中的问题。无论异常发生在代码中的哪个位置,我仍然会在“finally”中清理所有这些缓冲区(顺便说一句,垃圾收集不需要这些缓冲区)。据我了解,在 C++ 中,我可能必须为任何此类缓冲区创建一个成员变量,并在析构函数中确保清理缓冲区。对我来说看起来也很丑陋,因为名称为“pBuffer”的成员变量,即使是私有变量,也只是垃圾,因为它仅用于一种方法(在本例中为构造函数),其余部分始终为 NULL时间。

一定是一个常见问题,但是我没有设法使用搜索找到答案。谢谢!

【问题讨论】:

  • 除非绝对必要,否则您应该改掉在 C++ 中使用malloc/free 的习惯。更喜欢使用new/delete
  • 是的,但也不喜欢使用new/delete :) 使用容器、智能指针等来避免手动管理内存。它们使保证异常安全变得更加容易。

标签: c++ exception constructor buffer free


【解决方案1】:

停止手动管理内存,您将不会遇到此类问题。使用 std::vector<char> 之类的东西。

或者,您可以使用 Boost 的 shared_array 之类的东西,但这对于您在这里尝试做的事情来说太过分了:

http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_array.htm

这里要说明的更一般的一点是,您应该使用 RAII 习惯用法 - 也就是说,当您获取资源时,您将它们存储在一个类的实例中,该实例的析构函数再次释放它们。然后,无论该资源拥有类的实例超出范围,资源都保证被释放。

看这里:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

【讨论】:

    【解决方案2】:

    规范的答案是“资源获取即初始化”(RAII)和智能指针的原则。您在堆栈上创建一个类实例,它将释放其析构函数中的内存,例如boost::scoped_ptr.

    【讨论】:

    • 谢谢,我现在从您和其他人的回答中理解了 RAII 的概念))
    【解决方案3】:

    改为使用raii:

    MyClass::MyClass()
    {
        std::vector<char> buffer(100);
        if (0 != someD3DXCallThatCanFail(...))
        {
            throw MyException(L"some message");
        }
        std::vector<char> buffer2c(200);
        if (0 != anotherD3DCallThatCanFail(...))
        {
            throw MyException(L"another message");
        }
        .. more code here 
    }
    

    【讨论】:

      【解决方案4】:

      为此使用惯用的 C++ 方法:RAII。这个维基百科页面有 C++ 示例。

      【讨论】:

        【解决方案5】:

        再次搜索“C++ 智能指针”。您需要在内存上使用异常安全的包装器,而不是原始的malloc,正如您所指出的那样,这会带来很多麻烦,顺便说一句,现在您正在编写 C++ 而不是 C 代码,最好用 operator new 代替。

        上一个答案here 涵盖了这个区域。

        【讨论】:

          【解决方案6】:

          对此的规范答案将是 C++11 中的 unique_ptr。在那之前,可能scoped_ptr (http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/scoped_ptr.htm) 和scoped_array (http://www.boost.org/doc /libs/1_47_0/libs/smart_ptr/scoped_array.htm) 用于数组,均来自 Boost。或者您可以自己编写等效代码。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-04-01
            • 1970-01-01
            • 1970-01-01
            • 2020-10-24
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多