我正在与一些同事争论当在动态分配的类中抛出异常时会发生什么。我知道 malloc 被调用,然后是类的构造函数。构造函数永远不会返回,那么会发生什么 malloc?

考虑以下示例:

class B
{
public:
    B()
    {
        cout << "B::B()" << endl;
        throw "B::exception";
    }

    ~B()
    {
        cout << "B::~B()" << endl;          
    }
};

void main()
{
    B *o = 0;
    try
    {
        o = new B;
    }
    catch(const char *)
    {
        cout << "ouch!" << endl;
    }
}

分配的内存会发生什么 o, ,泄漏吗?CRT 是否捕获构造函数的异常并释放内存?

干杯!
富有的

有帮助吗?

解决方案

致电

new B();

解决两件事:

  • 使用运算符 new() 进行分配(全局运算符或特定于类的运算符,可能是具有以下语法的放置运算符 new (xxx) B())
  • 调用构造函数。

如果构造函数抛出异常,则调用相应的运算符delete。相应的删除是放置删除的情况是调用放置删除运算符而不使用语法 ::operator delete() 的唯一情况。 delete x; 或者 delete[] x; 不要调用布局删除运算符,并且没有与布局新类似的语法来调用它们。

请注意,虽然 B 的析构函数将 不是 被调用时,已构造的子对象(成员或 B 和 B 的基类)将在调用运算符删除之前被破坏。未调用的构造函数是 B 的构造函数。

其他提示

当构造函数抛出异常时,new分配的内存被释放,但不调用B类的析构函数。

在这种情况下,您的对象 o 实际上并未被构造,并且 new 分配的内存被释放。因此,析构函数不会被调用。所以你不需要打电话:

delete o;

一个有趣的设计模式是 RAII——资源获取即初始化。在此模式中,您使用构造函数来封装资源的获取,并在析构函数中释放资源。如果无法获取资源,您将抛出构造函数 - 就像您的示例一样。因此,如果你有一个有效的对象,你就拥有了资源。

如果构造了对象,那么就成功获取了资源。这意味着在对象的生命周期内,您拥有该资源。当对象被删除时,资源被释放。如果该对象从未被构造过,那么您就从未获得过该资源。参见维基百科:

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

来自 C++ 2003 标准 5.3.4/17 - 新:

如果上述对象初始化的任何部分通过抛出异常而终止,并且可以找到合适的释放函数,则调用释放函数来释放构造对象的内存,之后异常继续在上下文中传播新表达的。如果找不到明确匹配的释放函数,则传播异常不会导致对象的内存被释放。[笔记:当被调用的分配函数不分配内存时,这是合适的;否则,很可能会导致内存泄漏。]

因此,可能存在也可能不存在泄漏 - 这取决于是否可以找到合适的释放器(通常是这种情况,除非运算符 new/delete 已被覆盖)。如果存在合适的释放器,则编译器负责如果构造函数抛出异常,则用于连接对其的调用。

请注意,这或多或少与构造函数中获取的资源发生的情况无关,这是我第一次尝试讨论的答案 - 并且是许多常见问题解答、文章和帖子中讨论的问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top