• 基类中的虚析构函数


    转自:http://glatue.com/category/basic-knowledge/

    场景

    如果一个类会被作为基类,那么基类的析构函数最好声明为虚函数。

    原因是为了避免下面这样的操作,造成派生类的析构函数不能被调用。

    Base *d = new Drived();
    delete d; //如果Base类中的析构函数是非虚的,那么此delete操作只会调用Base的析构函数,而不会调用Drived的析构函数。

    把Base的析构函数定义成虚函数,则能够在上面的delete操作中调用到Drived的析构函数

    底层原理

    首先明白以下几点:
    1. 如果Base的析构函数声明为虚函数,则Drived的析构也会是虚函数(即使不定义为virtual),并且编译器会把Drived的析构函数当做是Base的析构函数的一个实现。
    2. 如果Drived的析构函数被调用,则Base的析构函数会在之后也会被调用到。

    那么我们来看看当Base的析构函数被定义为虚函数以后,会发生什么?

    1. 当 new Drived() 时

    Base被创建,并且创建自身的虚函数表,表中指针指向Base的析构函数

    Drived基于Base被创建,并在Base的虚函数表上进一步的修改,将表中指向Base的析构函数指针指向自身的析构函数位置。

    那么此时,Drived中的虚函数表中的析构函数指针,指向自身的析构函数。

    2. 当 Base *d = new Drived() 时

    编译器会把new Drived() 产生的对应的内存位置当做Base来处理(此时Drived虚函数表中的指针并没有改变)

    3. 当 delete d 时

    此时会去调用析构函数。
    此时d的类型是Base,而Base的析构函数是虚函数,所以需要去虚函数表里找,而此时,虚函数表里析构函数的指针指向的是Drived的析构函数,所以调用析构函数时实际上调用了Drived的析构函数。
    而Base是Drived的基类,所以调用完Drived的析构函数后,会自动调用Base的析构函数。

    4. 那么这样一来

    无论delete谁,都会调用到最“子”的对象的析构函数,进而调用到所有父类的析构函数。就避免了子类析构不被调用的问题。

    验证的代码

    class Base
    {
    public:
        Base(){cout << "Base" << endl;};
        virtual ~Base(){cout << "Base -" << endl;};
        
    };
    
    class Drived : public Base
    {
    public:
        Drived(){cout << "Drived" << endl;};
        ~Drived(){cout << "Drived -" << endl;};
    
        
    };
    
    int main()
    {
        Base *d = new Drived();
        delete d;
        return 0;
    }
    

    输出

    Base
    Drived 
    Drived -
    Base -
    
  • 相关阅读:
    liunx知识点滴积累(1)
    Regsvr32命令的使用
    QTP知识点滴积累
    LoadRunner的Apache的监控
    CMM和过程改进的“妙语” 集锦
    Linux 性能调优的几种方法
    数据库学习笔录(转载)
    Windows性能管理解析
    使用NUnit在.Net编程中进行单元测试
    Google 工程师文化 互助篇
  • 原文地址:https://www.cnblogs.com/demian/p/6538210.html
Copyright © 2020-2023  润新知