我多年来一直在使用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;
}
但似乎情况并非如此。
我能依靠吗?
答案 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)
是的,你可以指望这一点。
在构造期间抛出异常时,所有已完全构造的子对象/成员对象也将被销毁(大多数与构造的顺序相反)。
那就是说,你应该知道:
auto_ptr
已被弃用 - 我建议不要在新代码中使用它。auto_ptr
的使用似乎是无意义的。您目前对HumBug的定义似乎没有什么作用,只有foo
和bar
作为普通成员(而不是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都是构造的,如果有对象,它们将释放它们。