C++之虚析构函数的必要性

构造函数不能是虚函数,主要有两个原因:

1.派生类不能继承基类的构造函数,因此把基类的构造函数声明为虚函数没有意义,无法实现多态;

2.C++中的构造函数用来的在创建对象的时候进行初始化工作,在执行构造函数的时候,对象尚未创建完成,虚函数表这个时候还不存在,也没有指向虚函数表的指针,所以此时还无法查询虚函数表。也就不知道调用哪一个构造函数。

析构函数用来在销毁对象的时候进行清理工作,可以声明为虚函数,有时必须声明为虚函数。

#include<iostream>
using namespace std;

class Base{
public:
        Base();
        ~Base();
protected:
        char *str;
};
Base::Base(){
        str=new char[100];
        cout<<"Base constractor"<<endl;
}
Base::~Base(){
        delete[] str;
        cout<<"Base deconstractor"<<endl;
}

class Derived:public Base{
public:
        Derived();
        ~Derived();
private:
        char *name;
};
Derived::Derived(){
        name=new char[100];
        cout<<"Dervied constractor"<<endl;
}
Derived::~Derived(){
        delete[] name;
        cout<<"Derived deconstractor"<<endl;
}

int main()
{
        Base *pb = new Derived();
        delete pb;
        cout<<"-----------------------"<<endl;
        Derived *pd = new Derived();
        delete pd;
}

执行结果:

Base constractor
Dervied constractor
Base deconstractor
-----------------------
Base constractor
Dervied constractor
Derived deconstractor
Base deconstractor

从运行结果可以看出,语句delete pb;只调用了基类的析构函数,没有调用派生类的析构函数;而语句delete pd;同时调用了派生类和基类的析构函数。

在本例中,不调用派生类的析构函数会导致 name 指向的 100 个 char 类型的内存空间得不到释放;除非程序运行结束由操作系统回收,否则就再也没有机会释放这些内存。这是典型的内存泄露。

为什么delete pb不会调用派生类的析构函数呢?

因为这里的析构函数是非虚函数,通过指针访问非虚函数时候,编译器会根据指针是类型来调用析构函数。也就是说,指针是什么类型,就调用哪个类的析构函数。如这里pb是基类指针,就调用基类的析构函数。

为什么delete pd会同时调用基类和派生类的析构函数?

pd 是派生类的指针,编译器会根据它的类型匹配到派生类的析构函数,在执行派生类的析构函数的过程中,又会调用基类的析构函数。派生类析构函数始终会调用基类的析构函数。

更改上面的代码,把析构函数更改虚函数:

#include<iostream>
using namespace std;

class Base{
public:
        Base();
        virtual ~Base();
protected:
        char *str;
};
Base::Base(){
        str=new char[100];
        cout<<"Base constractor"<<endl;
}
Base::~Base(){
        delete[] str;
        cout<<"Base deconstractor"<<endl;
}

class Derived:public Base{
public:
        Derived();
        ~Derived();
private:
        char *name;
};
Derived::Derived(){
        name=new char[100];
        cout<<"Dervied constractor"<<endl;
}
Derived::~Derived(){
        delete[] name;
        cout<<"Derived deconstractor"<<endl;
}

int main()
{
        Base *pb = new Derived();
        delete pb;
        cout<<"-----------------------"<<endl;
        Derived *pd = new Derived();
        delete pd;
}

执行结果:

Base constractor
Dervied constractor
Derived deconstractor
Base deconstractor
-----------------------
Base constractor
Dervied constractor
Derived deconstractor
Base deconstractor

将基类的析构函数声明为虚函数后,派生类也会自动称为虚函数,这个时候,编译器会忽略指针类型,而是根据指针的指向来调用函数,也就是说,指针指向那个对象就调用那个对象的函数。

pb、pd 都指向了派生类的对象,所以会调用派生类的析构函数,继而再调用基类的析构函数。如此一来也就解决了内存泄露的问题。

 实际开发中,一旦我们自己定义了析构函数,就是希望在对象销毁时用它来进行清理工作,比如释放内存、关闭文件等,如果这个类又是一个基类,那么我们就必须将该析构函数声明为虚函数,否则就有内存泄露的风险。也就是说,大部分情况下都应该将基类的析构函数声明为虚函数。

注意,这里强调的是基类,如果一个类是最终的类,那就没必要再声明为虚函数了。

原文链接: https://www.cnblogs.com/njit-sam/p/13230850.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++之虚析构函数的必要性

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/361769

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 下午2:00
下一篇 2023年3月2日 下午2:00

相关推荐