3

我多年来一直在使用 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;
        }

但似乎情况并非如此。

我可以依靠这个吗?

4

3 回答 3

4

[除了.ctor]/2:

任何存储持续时间的对象,如果其初始化或销毁被异常终止,将为其所有完全构造的子对象(不包括类联合类的变体成员)执行析构函数,也就是说,对于主构造函数 ( 12.6.2) 已完成执行,而析构函数尚未开始执行。类似地,如果对象的非委托构造函数已完成执行,并且该对象的委托构造函数因异常退出,则将调用对象的析构函数。如果对象是在 new 表达式中分配的,则调用匹配的释放函数(3.7.4.2、5.3.4、12.5)(如果有)以释放对象占用的存储空间。

(强调我的)

子对象的 ctor 将在 mem-initializer-list 中调用(你的为空,因此会发生默认构造)。当进入ctor body时,所有成员都已经构建成功,因此,它们的dtors将被调用。

于 2013-05-17T16:25:14.167 回答
4

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

当在构造过程中抛出异常时,所有已经完全构造的子对象/成员对象也将被销毁(多以构造相反的顺序)。

也就是说,您应该意识到:

  1. auto_ptr已弃用——我建议不要在新代码中使用它。
  2. 无论如何,您的使用auto_ptr似乎毫无意义。

您当前对 HumBug 的定义似乎并没有起到什么作用,仅仅拥有fooandbar作为普通成员(而不是auto_ptrs)不会做得那么好:

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

成员对象是按照它们在类定义中的声明顺序构造的,所以 this 将构造Foo对象,然后尝试构造Bar对象。Bar抛出时,将Foo被破坏。

顺便说一句,using namespace std;往往大多数人都不赞成(尽管您可能只是为了相同的演示而这样做,这可能很好——但在实际代码中不推荐)。

于 2013-05-17T16:25:50.060 回答
1

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

于 2013-05-17T16:28:31.037 回答