【问题标题】:Uncaught exception at constructor after allocating memory分配内存后构造函数中未捕获的异常
【发布时间】:2012-11-30 02:21:54
【问题描述】:

我已经阅读了 Michael Burr 关于引发异常的构造函数的精彩总结,这里:Will the below code cause memory leak in c++

我的问题是:从构造函数调用的函数引发异常时的行为是否相似?即,如果异常没有在抛出的地方被捕获,因此它会爬到构造函数并进一步爬到调用构造函数的函数。

我特别想知道该对象的类中包含的成员对象是什么,它们的析构函数会被调用吗?更具体地说,在我的例子中,它是关于 boost::shared_ptr 类型的成员。

考虑以下示例:

class A {
    A() { throw some_exception }
};

class B {
    B() { A a = new A(); }
};

class C {
    boost::shared_ptr<B> p2b;
    int i;
    int *pint;
    someclass objsomeclass;
    someclass* psomeclass;

public:
    C() {
        objsomeclass = someclass();
        psomeclass = new someclass();
        pint = new int(); 
        p2b(new B);
    }
};

void foo()
{
    C c();
}

main()
{
    foo();
}

p2a 的析构函数会被调用吗? 如果您能指出一个涵盖此案例的适当且可靠的资源,我将不胜感激。

【问题讨论】:

  • 格式化:不要使用制表符。关于代码:B 应该从 A 派生吗?否则 p2a 初始化似乎没有意义。
  • 如果你的意思是它没有意义,因为 B 除了初始化 A 什么都不做,那只是为了举例。
  • p2a(new B) 只有在 B* 可转换为 A* 时才能编译 ...但在您的代码中,它不是。此外,B::B 无论如何都会泄漏A 的一个实例。
  • @Useless,B::B 不会泄漏 A 的实例,因为 A 为其构造函数抛出了异常,因此从未创建 A a。请参阅我帖子上的链接。我错了吗?
  • 没错,A 只有在没有抛出异常的情况下才会被泄露……但这意味着 B 的构造函数(如所写)只有在它从未成功的情况下才能正确。

标签: c++ exception constructor shared-ptr


【解决方案1】:

假设您更改代码使其编译,p2a(现在为p2b)的析构函数将被调用,因为它已成功默认构造。但是,它仍将保持 NULL,因为您尝试在 C::C 的正文中重置它失败。

new B 分配的内存将被堆栈展开过程自动清理。但是,pintpsomeclass 都会泄露,因为您没有为这些成员使用 RAII。

为了澄清,让我们单步执行代码:

C::C() {
    objsomeclass = someclass();

    psomeclass = new someclass();
    pint = new int();

    p2b.reset(new B);
    /* in your code as posted, the call sequence is:
       new B (allocate sizeof B)
         -> B::B
           -> new A (allocate sizeof A)
             -> A::A which throws
           <- new A failed, so memory is freed
         <- B::B failed (initialized subobjects would be
            destroyed here, but there are none)
       new B failed, so memory is freed
    */
}

请注意:

  • 所有成员都已经默认初始化(因为您没有使用初始化列表),所以当C::C 的主体展开时,它们都会被销毁
  • 如果psomeclasspint 是智能指针,这将释放它们动态分配的内存。他们不是,所以这被泄露了。

一般来说,使用初始化列表和 RAII 会更好。

作为参考,不妨从这篇(很老的)文章开始:GOTW 66

【讨论】:

  • Assuming you change the code so it compiles... 如果他这样做,您将无法确定代码实际上是做什么的,对吗? ;-)
  • 你回答了我的问题,谢谢。我仍然对 AlexDan 提出的案例感到好奇:如果构造函数成员初始化列表中的调用抛出异常。请在下面查看我的评论。
  • +1 供参考。但它并没有明确解决具体情况。但它让我找到了that article,它讨论了这个问题。顺便说一句,根据它的说法,如果在成员初始化列表阶段引发异常,则不会调用 p2b 的析构函数。如果我错了,请纠正我。谢谢。
  • 为什么? “new B”语句不是已经分配内存了吗?即使它的构造函数没有被调用。
  • new B 失败,因此内存被自动清理。我已经在答案中澄清了这一点,但链接的文章也讨论了它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-07
  • 1970-01-01
  • 2014-04-03
  • 1970-01-01
  • 2019-08-12
  • 2013-05-02
相关资源
最近更新 更多