析构函数(当然也包括构造函数)与其他成员函数之间的区别在于,如果常规成员函数在派生类中具有正文,则只会执行Derived类中的版本。在析构函数的情况下,派生以及基类版本都会被执行吗?
很高兴知道在析构函数(可能是虚拟的)和&的情况下究竟发生了什么。构造函数,即使删除了最派生的类对象,也会为它们的所有基类调用它们。
提前致谢!
答案 0 :(得分:16)
标准说
执行析构函数体并破坏体内分配的任何自动对象后, X类的析构函数调用X的直接非变体成员的析构函数, X的直接析构函数 基类和,如果X是派生类最多的类(12.6.2),它的析构函数调用析构函数 X的虚拟基类。所有析构函数都被调用,好像它们被引用了一个合格的名称,即 忽略更多派生类中任何可能的虚拟覆盖析构函数。 基地和成员被摧毁 按照完成构造函数的相反顺序(见12.6.2)。 a中的返回声明(6.6.3) 析构函数可能不会直接返回调用者;在将控制转移给调用者之前,析构函数 为会员和基地打电话。数组元素的析构函数以相反的顺序调用 他们的建筑(见12.6)。
同样,根据RAII资源需要与合适对象的生命周期联系起来,并且必须调用各个类的析构函数来释放资源。
例如,以下代码泄漏了内存。
struct Base
{
int *p;
Base():p(new int){}
~Base(){ delete p; } //has to be virtual
};
struct Derived :Base
{
int *d;
Derived():Base(),d(new int){}
~Derived(){delete d;}
};
int main()
{
Base *base=new Derived();
//do something
delete base; //Oops!! ~Base() gets called(=>Memory Leak).
}
答案 1 :(得分:13)
构造函数和析构函数与其他常规方法不同。
构造强>
struct A {};
struct B : A { B() : A() {} };
// but this works as well because compiler inserts call to A():
struct B : A { B() {} };
// however this does not compile:
struct A { A(int x) {} };
struct B : A { B() {} };
// you need:
struct B : A { B() : A(4) {} };
<强>析构强>:
struct C
{
virtual ~C() { cout << __FUNCTION__ << endl; }
};
struct D : C
{
virtual ~D() { cout << __FUNCTION__ << endl; }
};
struct E : D
{
virtual ~E() { cout << __FUNCTION__ << endl; }
};
int main()
{
C * o = new E();
delete o;
}
输出:
~E
~D
~C
如果基类中的方法标记为virtual
,则所有继承的方法都是虚拟的,即使您没有将D
和E
中的析构函数标记为{{ 1}}它们仍然是virtual
,它们仍然会以相同的顺序被调用。
答案 2 :(得分:11)
这是设计的。必须调用基类上的析构函数才能释放其资源。经验法则是派生类应该只清理自己的资源并让基类自行清理。
来自C++ spec:
执行后的身体 析构者并摧毁任何人 在。中分配的自动对象 body,类X调用的析构函数 X的直接析构函数 成员,X的析构者 直接基类,如果X是 派生类最多的类型 (12.6.2),它的析构函数调用了 X的虚拟基础的析构函数 类。所有析构函数都称为 如果他们被引用了 限定名称,即忽略任何名称 可能的虚拟覆盖 更多派生类中的析构函数。 基地和成员被摧毁 完成的逆序 他们的构造函数(见 12.6.2)。
另外,因为只有一个析构函数,所以类必须调用哪个析构函数没有歧义。构造函数不是这种情况,如果没有可访问的默认构造函数,程序员必须选择应该调用哪个基类构造函数。
答案 3 :(得分:2)
因为这就是dtor的工作方式。当你创建一个对象时,从基数开始调用ctors,并一直到最大的派生。当您(正确地)销毁对象时,会发生相反的情况。使dtor虚拟产生差异的时间是,是否/当你通过指针(或引用,虽然这是相当不寻常的)对基类型销毁对象时。在这种情况下,替代方案并不是真正只有派生的dtor被调用 - 相反,替代方案只是未定义的行为。这种情况恰好采用仅调用派生的dtor的形式,但它也可能采用完全不同的形式。
答案 4 :(得分:2)
正如Igor所说,必须为基类调用构造函数。考虑如果不会被调用会发生什么:
struct A {
std::string s;
virtual ~A() {}
};
struct B : A {};
如果在删除A
实例时未调用B
的析构函数,则永远不会清除A
。
答案 5 :(得分:2)
基类析构函数可能负责清理由基类构造函数分配的资源。
如果你的基类有一个默认的构造函数(一个不带参数或者它的所有参数都有默认值),那么在构造派生实例时会自动调用构造函数。
如果您的基类有一个需要参数的构造函数,则必须在派生类构造函数的初始化列表中手动调用它。
由于析构函数不接受参数,因此在删除派生实例时将始终自动调用基类析构函数。
如果您正在使用多态并且您的派生实例由基类指针指向,则只有在基本析构函数为虚拟时才会调用派生的类析构函数。
答案 6 :(得分:0)
当任何对象被销毁时,析构函数将针对所有子对象运行。这包括通过包含重用和通过继承重用。