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

时间:2017-06-27 00:21:06

标签: c++ inheritance multiple-inheritance

我一直在练习继承,我发现从基类指针调用析构函数到一个继承的类会产生一个奇怪的输出,在那里我发现我的析构函数执行的次数比它应该的多。恢复我的代码(编辑:要求更多代码):

#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次电话;一个用于每个继承的类( D4,D2,D1 ),最后一个用于基类( B )。

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

2 个答案:

答案 0 :(得分:3)

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

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

Live Demo

你想要4个析构函数调用是正确的。 ~D4()->~D2()->~D1()->~B()

但从输出结果来看,实际上是删除了两个D4个对象,一个D1个对象和另一个D2个对象。

<强>更新

时调用析构函数
  1. 您在对象上调用delete。
  2. 超出范围。
  3. 现在说明我的观点我将介绍一个自定义范围:

    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
    

答案 1 :(得分:1)

您在堆栈上分配了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