为什么我的虚拟析构函数执行了这么多次?

Why does my virtual destructor executes these many times?

我一直在练习继承,我发现从指向继承 class 的基 class 指针调用析构函数会导致奇怪的输出,我发现我的析构函数在其中执行比它应该的更多次。恢复我的代码(编辑:要求更多代码):

#include <iostream>

using namespace std;

class B{

public:

    virtual void f(){cout << "f() - B" << endl;}
    virtual void g(){cout << "g() - B" << endl;}
    virtual void h() = 0;
    virtual ~B(){cout << "~B() destructor" << endl;}

protected:

    int b;
};

class D1: virtual public B{

public:

    void f(){cout << "f() - D1" << endl;}
    virtual void g(){cout << "g() - D1" << endl;}
    virtual ~D1(){cout << "~D1() destructor" << endl;}

protected:

    int d1;
};

class D2: virtual public B{

public:

    void f(int i){cout << "f(" << i << ") - D2" << endl;}
    virtual void h(){cout << "h() - D2" << endl;}
    virtual ~D2(){cout << "~D2() destructor" << endl;}

protected:

    int d2;
};

class D3: public D1{

public:
    void g(){cout << "g() - D3" << endl;}
    void h(){cout << "h() - D3" << endl;}

private:

    int d3;
};

class D4: public D1, public D2{

public:

    using D1::f; using D2::f;

    virtual ~D4(){cout << "~D4() destructor" << endl;}

private:

    int d4;
};

void f(B& b){

   cout << "f() out " << endl;
   b.f();
   b.g();
   b.h();
};

int main()
{
    B *pB;
    D2 d2;
    D3 d3;
    D4 d4;
    f(d2);
    f(d3);
    f(d4);
    d4.D1::f();
    d4.f(5);
    d4.f(3.7);
    d4.g();
    d4.h();
    pB = new D4;
    pB -> f();
    dynamic_cast<D4*>(pB)->f(3);
    pB -> g();
    pB -> h();
    delete pB;
}

最终输出为:

//Other tests
.
.
.
f(3) - D2
~D4() destructor
~D2() destructor
~D1() destructor
~B()  destructor
~D4() destructor
~D2() destructor
~D1() destructor
~B()  destructor
~D1() destructor
~B()  destructor
~D2() destructor
~B()  destructor

创建pB指针;指向新的 D4 对象;显式调用 D4 f() 方法和删除调用。我只期待四个 destructor 电话;每个继承 class (D4,D2,D1) 一个,最后一个用于基础 class (B)。

这些结果应该是正常的吗?我的代码有问题吗?

您提供的代码拆分出以下输出:

f(3) - D2
~D4() destructor
~D2() destructor
~D1() destructor
~B() destructor

Live Demo

您预期有 4 次析构函数调用是正确的。 ~D4()->~D2()->~D1()->~B()

但是从你的输出来看,你实际上是在删除两个 D4 个对象,一个 D1 个对象,另一个 D2 个对象。

更新:

析构函数在

时被调用
  1. 您对一个对象调用了 delete。
  2. 超出范围。

现在为了说明我的观点,我将介绍一个自定义范围:

int main()
{
    B *pB;
    { // custom scope
        D2 d2;
        D3 d3;
        D4 d4;
        f(d2);
        f(d3);
        f(d4);
        d4.D1::f();
        d4.f(5);
        d4.f(3.7);
        d4.g();
        d4.h();
        pB = new D4;
        pB->f();
        dynamic_cast<D4*>(pB)->f(3);
        pB->g();
        pB->h();
    }
    delete pB;
}

调用它会打印以下输出:

//function calls
~D4() destructor <-- inside custom scope
~D2() destructor <-- inside custom scope
~D1() destructor <-- inside custom scope
~B() destructor  <-- inside custom scope
~D1() destructor <-- inside custom scope
~B() destructor  <-- inside custom scope
~D2() destructor <-- inside custom scope
~B() destructor  <-- inside custom scope
~D4() destructor <-- outside custom scope
~D2() destructor <-- outside custom scope
~D1() destructor <-- outside custom scope
~B() destructor  <-- outside custom scope

您在堆栈上分配了 D2D3D4 个对象,在堆上分配了一个 D4 对象。您看到的是 D1D2D4 析构函数的输出(您没有在 D3 析构函数中输出任何内容)。

您认为 delete 是调用析构函数的唯一方式吗?它不是,但似乎这就是你的想法。您将获得您创建的所有 4 个对象的析构函数的输出消息。当堆栈上的对象在 main() 结束时超出范围时,它们会自动销毁(按创建的相反顺序)。当您在其上显式调用 delete 时,堆上的对象被破坏:

// when you delete pB...
~D4() destructor
~D2() destructor
~D1() destructor
~B()  destructor

// when d4 goes out of scope...
~D4() destructor
~D2() destructor
~D1() destructor
~B()  destructor

// when d3 goes out of scope...
~D1() destructor
~B()  destructor

// when d2 goes out of scope...
~D2() destructor
~B()  destructor

看到这个 live demo,它具有与您显示的相同的析构函数输出。

如果您将输出添加到 D3 的析构函数,这可能会更加明显:

class D3: public D1{

public:
    void g(){cout << "g() - D3" << endl;}
    void h(){cout << "h() - D3" << endl;}

    virtual ~D3(){cout << "~D3() destructor" << endl;} // <-- add this!

private:

    int d3;
};

// when d3 goes out of scope...
~D3() destructor <-- this message now appears
~D1() destructor
~B()  destructor