对于C ++析构函数中的异常,所有属性都会自动销毁

时间:2013-05-17 16:16:04

标签: c++ exception

我多年来一直在使用C ++,但在使用可能被认为是老式的环境中。具体来说,我们没有使用auto_ptr,并且不允许在构造函数中分配内存。

接下来,我以为自己已经掌握了一切,直到......看到下面的代码:

    #include <memory>
    #include <iostream>

    using namespace std;

    class Foo
        {
        public:
        int x;
        ~Foo() { cout << "~Foo" << endl; }
        };

    class Bar
        {
        public:
        Bar() { throw 0; }
        ~Bar() { cout << "~Bar" << endl; }
        int y;
        };

    class HumBug
        {
        public:
            HumBug();
            virtual ~HumBug();

        auto_ptr<Foo> foo;
        auto_ptr<Bar> bar;
        };

    HumBug::HumBug()
        {
        cout << "before foo" << endl;
        foo = auto_ptr<Foo>(new Foo);
        cout << "after foo" << endl;
        bar = auto_ptr<Bar>(new Bar);
        cout << "after bar" << endl;
        }

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

    int main()
        {
        try 
            {
            HumBug humbug;
            }
        catch (...)
            {
            }
        return 0;
        }

我理解不会调用HumBug的析构函数,因为在调用HumBug构造函数期间会发生异常。我原以为创建的Foo属性会泄漏。然而valgrind说没关系:

==4985== Memcheck, a memory error detector
==4985== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==4985== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==4985== Command: ./a.out
==4985== 
before foo
after foo
~Foo
==4985== 
==4985== HEAP SUMMARY:
==4985==     in use at exit: 0 bytes in 0 blocks
==4985==   total heap usage: 3 allocs, 3 frees, 108 bytes allocated
==4985== 
==4985== All heap blocks were freed -- no leaks are possible
==4985== 
==4985== For counts of detected and suppressed errors, rerun with: -v
==4985== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

那么,虽然没有调用析构函数,但属性仍然可靠地被破坏了吗?我认为为了可靠地清理它,我必须重写HumBug的构造函数:

    HumBug::HumBug()
        {
        auto_ptr<Foo> new_foo (new Foo);
        auto_ptr<Bar> new_bar (new Bar);
        // no exceptions after this point
        foo = new_foo;
        bar = new_bar;
        }

但似乎情况并非如此。

我能依靠吗?

3 个答案:

答案 0 :(得分:4)

[except.ctor] / 2:

  

任何存储持续时间的对象,其初始化或销毁由异常终止,将为所有完全构造的子对象执行析构函数(不包括类似联合的类的变体成员),< strong>即,对于主要构造函数(12.6.2)已完成执行且析构函数尚未开始执行的子对象。同样,如果是一个对象的非委托构造函数   已完成执行并且该对象的委托构造函数以异常退出,将调用该对象的析构函数。如果对象是在new-expression中分配的,则调用匹配的释放函数(3.7.4.2,5.3.4,12.5)(如果有的话)以释放对象占用的存储空间。

(强调我的)

子对象的ctors将在mem-initializer-list中调用(你的是空的,因此会发生默认构造)。当进入主体时,所有成员都已经成功建造,因此,他们的主人将被召唤。

答案 1 :(得分:4)

是的,你可以指望这一点。

在构造期间抛出异常时,所有已完全构造的子对象/成员对象也将被销毁(大多数与构造的顺序相反)。

那就是说,你应该知道:

  1. auto_ptr已被弃用 - 我建议不要在新代码中使用它。
  2. 无论如何,你对auto_ptr的使用似乎是无意义的。
  3. 您目前对HumBug的定义似乎没有什么作用,只有foobar作为普通成员(而不是auto_ptr s)也不会那样做:

    class HumBug { 
        Foo foo;
        Bar bar;
    public:
         ~HumBug() { std::cout << "~HumBug"; }
    };
    

    成员对象按照它们在类定义中的声明顺序构造,因此这将构造Foo对象,然后尝试构造Bar对象。当Bar抛出时,Foo将被销毁。

    顺便说一下,using namespace std;往往是不赞成的(虽然你可能只是为了同样的演示,它可能很好 - 虽然在实际代码中不推荐)。

答案 2 :(得分:1)

即使HumBeg的析构函数不能运行,因为HumBug没有完全构造,嵌入对象的析构函数将完全构造。 foo和bar都是构造的,如果有对象,它们将释放它们。