2

我已经阅读了 Michael Burr 关于引发异常的构造函数的精彩总结,这里是:以下代码会导致 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 的析构函数吗?如果您能指出一个涵盖此案例的适当且可靠的资源,我将不胜感激。

4

1 回答 1

3

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

分配的内存new B将由堆栈展开过程自动清理。但是,pint两者都psomeclass 被泄露,因为您没有为这些成员使用 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销毁
  • 如果psomeclass并且pint是智能指针,这将释放它们动态分配的内存。他们不是,所以这被泄露了。

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

作为参考,也许从这篇(非常古老的)文章开始:GOTW 66

于 2012-12-12T11:44:39.307 回答