【问题标题】:When I use dynamic_cast and delete the object delete , how the destructor works?当我使用 dynamic_cast 并删除对象 delete 时,析构函数是如何工作的?
【发布时间】:2018-01-12 13:16:10
【问题描述】:

我为它创建了一个派生类对象和一个基类点, 然后我使用 dynamic_cast 将基类点转换为派生类点。 我删除了派生类点,但程序调用了派生析构函数和基析构函数。为什么程序调用基本析构函数?毕竟,基本析构函数不是虚函数......

#include<iostream>
#include<stdlib.h>
using namespace std;


class con {
private:
    double num;
public:
    con() {
        num = 0;
        cout << "default..." << endl;

    }
    void getnum() {
        cout << num << endl;
    }
};




class base {
public:
    virtual void A() {
        cout << "it is base A" << endl;
    }

    void B() {
        cout << "it is base B" << endl;
    }

    ~base() {
        cout << "it is base decon" << endl;
    }
};


class child : public base {
public:
    void A() {
        cout << "it is child A" << endl;
    }

    void B() {
        cout << "it is child B" << endl;
    }

    ~child() {
        cout << "it is child decon" << endl;
    }

};


int main(int argc, char** argv) {

    base* b = new child();
    child* c = dynamic_cast<child*>(b);
    delete c; //the program print "it is child decon"   "it is base decon"
    getchar();
    return 0;
}

【问题讨论】:

  • 你真的应该把析构函数变成虚拟的!
  • 如果你总是删除具体类型,你就不需要虚拟析构函数。但正如@Someprogrammerdude 所说:不要这样做。
  • 我不知道为什么程序调用派生析构函数然后调用基类析构函数,即使我没有将基类析构函数设为虚拟......我原以为它应该只调用派生的析构函数。谢谢...:-)@manni66
  • 啊。现在我明白了。派生类总是调用派生析构函数和基类析构函数。谢谢@manni66

标签: c++ c++11


【解决方案1】:

案例1:如果删除一个指针派生类,它总是先调用派生类的析构函数,然后是基类的析构函数。

案例 2:如果使用 non-virtual 基类析构函数删除指向派生类基类的指针,则它不知道派生类类,所以它只是删除基类。

案例 3:如果使用 virtual 基类析构函数删除指向派生类基类的指针,它使用虚拟调度到派生的析构函数类,然后调用基类的析构函数。

案例 2 不好。它可能导致资源泄漏等。应通过始终在打算作为基类的类中声明析构函数来避免这种情况。

您的示例说明了案例 1。

如果您想查看案例 2 的实际效果,请尝试在您的示例中使用 delete b 而不是 delete c

【讨论】:

  • 非常感谢......像我的例子中的case2不好,因为它会导致资源泄漏......还有,我想问另一个相关的问题,以避免我们的资源泄漏使用虚拟基类析构函数,当我使用基类指向派生类时,是否有任何其他方法可以避免资源泄漏期望虚拟基类析构函数方法......(这对我来说是一个面试问题......)。谢谢你。 @戴尔威尔逊
  • 如果由于某种原因您不能将基类析构函数声明为虚拟的,您可以将其设置为受保护的,因此delete b 将是一个语法错误。如果您根本无法更改基类,则可以使用私有继承,或者您可以将基类作为成员嵌入而不是从它继承,但是您可能必须将派生类中的方法转发到基类。作为最后的手段,您可以制定一个强制执行的编码标准,规定永远不要使用delete b,否则我们将切断您的免费咖啡供应。
【解决方案2】:

C++ 中的析构函数不会“重载”;他们连锁。当你说

{
    child c;
}

然后在右大括号处,编译器将插入对~child() 的调用,而后者又会调用~base()(以及其他基类的析构函数,如果你有的话)。这对于避免内存泄漏至关重要——否则base 的成员将如何被销毁?请注意,这并不要求 base 析构函数为 virtual

当您使用 delete 手动销毁某些东西时,这同样适用:

child* c = new child{};

delete c; // runs ~child(), which calls ~base()

同样,这并不要求析构函数是virtual

虚拟析构函数的作用 是允许您使用指向base 的指针来销毁动态类型为child 的变量,即

 child* c = new child{}
 base* b = c;
 delete b; // Will call ~child() if destructor is virtual, otherwise ~base()

【讨论】:

    猜你喜欢
    • 2011-01-12
    • 2012-04-30
    • 2019-11-24
    • 1970-01-01
    • 2013-09-30
    • 2013-05-21
    • 2012-02-05
    • 2013-12-30
    • 2013-03-26
    相关资源
    最近更新 更多