来自C ++ FAQ:
[11.4]我可以为我的班级重载析构函数吗? 否。
我意识到这意味着你不能改变返回类型,参数的类型和参数的数量。我可能会分析单词的语法,但是可以覆盖 Parent的析构函数吗?
class Child : public Parent {
public:
virtual Parent::~Parent() {
// New definition
}
};
就此而言,这是递归吗?
class Grandchild : public Child {
public:
Child::Parent::~Parent() {
// An even newer definition
}
};
我读过this和related post这让我觉得因为析构函数不是继承的,它们不能被覆盖,但我从未见过明确说过。
编辑:我改变了这一点,以反映我想覆盖Parent的析构函数,注意Child和Grandchild重写~Parent()。
我这样做的主要原因是维护Parent的界面,同时改变它的销毁方式(子类的完整原因)。我将有其他一些管理所有Parent的内容,并将在我选择的后期明确调用它们的析构函数。
答案 0 :(得分:8)
我可能会分析单词
的语法
不,你绝对不是 - 这是两件截然不同的事。
但是可以覆盖析构函数吗?
是的,实际上你必须在许多情况下这样做。为了使其适用于多态对象,您需要将基类析构函数声明为virtual
,但是:
Parent const& p = Child();
在范围结束时正确调用p.~Child()
,因为Parent::~Parent
是虚拟的。
答案 1 :(得分:6)
是的,可以覆盖类的析构函数。实际上,当您定义使用多态的类层次结构时,必须在基类中声明虚拟析构函数。
析构函数的覆盖与普通成员函数的覆盖完全相同,当你通过指向基类的delete
对象销毁对象时,派生类的析构函数被正确调用。这就是为什么必须在基类中为多态层次结构设置虚拟析构函数。
但是,虚拟析构函数和虚拟成员方法之间存在差异,这与析构函数的virtual
性质无关。也就是说,在执行这样的代码时:
class A
{
public:
virtual void Foo() {}
virtual ~A() {};
};
class B : public A
{
public:
void Foo() {};
~B() {}
};
int main()
{
A* a = new B;
a->Foo(); // B::Foo() is called
delete a; // B is destroyed via B::~B()
}
...当您致电a->Foo()
时,会调用Foo()
中的方法B
。由于B::Foo()
未明确调用A::Foo()
,因此不会调用A::Foo()
。
但是,当通过delete a;
销毁对象时,首先调用析构函数B::~B()
,然后在完成之后但在控制返回到程序之前,基类析构函数A::~A()
也被调用。
当然,当你考虑它时,这是显而易见的,而且这与析构函数的virtual
性质无关,但它的行为与普通的virtual
方法调用不同,所以我以为我会指出它。
义务标准报价:
执行析构函数体并破坏任何析构函数 在body中分配的自动对象,类X的析构函数 调用X的直接成员的析构函数,X的析构函数 直接基类,如果X是派生类最多的类型 (12.6.2),它的析构函数调用X的虚拟基础的析构函数 类。所有析构函数都被调用,就好像它们是使用限定名称引用的那样,即忽略任何可能的虚拟名称 覆盖更多派生类中的析构函数。基地和成员是 以完成他们的相反顺序销毁 构造函数(见12.6.2)。析构函数中的return语句(6.6.3) 可能不会直接返回来电者;转移控制之前 对于调用者,调用成员和基础的析构函数。 数组元素的析构函数以相反的顺序调用 他们的建筑(见12.6)。
答案 2 :(得分:3)
是:您可以拥有virtual
个析构函数,唯一的原因是在派生类中覆盖它们。
看起来像这样:
class Parent {
public:
virtual ~Parent();
};
class Child : public Parent {
public:
virtual ~Child();
};
class Grandchild : public Child {
public:
~Grandchild(); // virtual is inherited here
};
请注意,析构函数不会像普通函数一样被 name 覆盖,因为该名称始终是您正在销毁其实例的类的名称。
另请注意,父类的析构函数也总是被调用,因此您不需要复制它们的清理代码:读取成员对象和基类子对象构造以及详细信息的销毁顺序。
virtual
函数。您根本无法更改签名(使用协变返回类型除外)。因此, override 始终与继承的虚函数具有相同的签名。 答案 3 :(得分:0)
是;只要你有一个可以使用对基类的引用销毁的子类,你就可以而且应该使析构函数成为虚拟的。如果您不提供虚拟析构函数,静态代码分析工具甚至会抱怨。
考虑以下示例:
class A
{
public:
A() { a = new int; }
virtual ~A() { delete a; }
private:
int *a;
};
class B : public A
{
public:
B() { b = new int; }
virtual ~B() { delete b; }
private:
int *b;
};
int main()
{
A *a = new B();
delete a;
}
如果析构函数不虚拟,那么delete a
只会调用A的析构函数,最终会导致内存泄漏。但是因为它是虚拟的,所以将按~B()
- >的顺序调用两个析构函数。 ~A()
。