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 都指向了派⽣类的对象,所以会调⽤派⽣类的析构函数,继⽽再调⽤基类的析构函数。如此⼀来也就解决了内存泄露的问题。
实际开发中,⼀旦我们⾃⼰定义了析构函数,就是希望在对象销毁时⽤它来进⾏清理⼯作,⽐如释放内存、关闭⽂件等,如果这个类⼜是⼀个基类,那么我们就必须将该析构函数声明为虚函数,否则就有内存泄露的风险。也就是说,⼤部分情况下都应该将基类的析构函数声明为虚函数。
注意,这⾥强调的是基类,如果⼀个类是最终的类,那就没必要再声明为虚函数了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。