覆盖析构函数C ++

时间:2013-06-13 17:33:47

标签: c++ inheritance override destructor multiple-inheritance

来自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
    }
};

我读过thisrelated post这让我觉得因为析构函数不是继承的,它们不能被覆盖,但我从未见过明确说过。

编辑:我改变了这一点,以反映我想覆盖Parent的析构函数,注意Child和Grandchild重写~Parent()。

我这样做的主要原因是维护Parent的界面,同时改变它的销毁方式(子类的完整原因)。我将有其他一些管理所有Parent的内容,并将在我选择的后期明确调​​用它们的析构函数。

4 个答案:

答案 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方法调用不同,所以我以为我会指出它。

义务标准报价:

[C ++ 03] 12.4 / 6:析构函数

  

执行析构函数体并破坏任何析构函数   在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()