1. 问题描述:
最近,在通过使用抽象类作为接口时出现了内存泄漏的问题。通过阅读相关文章,发现是因为在构建时基类析构函数没有设置为虚函数。
2. 相关概念:
(1)虚函数: 某基类中声明为virtual并在一个或多个派生类中重新定义的成员函数叫做虚函数。
virtual void fun();
(2)纯虚函数: 将其声明为如下形式的成员函数为纯虚函数,没有实现。
virtual void fun() = 0;
(3)抽象类: 有一个成员函数是纯虚函数的类——抽象类不能实例化。
class Father0
{
public:
virtual ~Father0(){}
virtual void fun() = 0;
};
3. 虚函数实现多态
在定义了虚函数后,可实现在派生类中对虚函数进行重写,从而实现统一的接口和不同的执行过程。在函数运行阶段动态的选择合适的成员函数。
相关的实现代码如下:
#include <iostream>
using namespace std;
class Father0
{
public:
virtual ~Father0(){}
virtual void fun() = 0;
};
class Father: public Father0
{
public:
Father()
{
cout << "class Father init" << endl;
}
virtual ~Father()
{
cout << "class Father destroyed" << endl;
}
void fun0()
{
cout << "this is a base function" << endl;
}
virtual void fun()
{
cout << "this is a virtual function" << endl;
}
};
class Son1 : public Father
{
public:
Son1()
{
cout << "class Son1 init" << endl;
}
~Son1()
{
cout << "class Son1 destroyed" << endl;
}
void fun()
{
cout << "this is a virtual function of Son1" << endl;
}
};
class Son2 : public Son1
{
public:
Son2()
{
cout << "class Son2 init" << endl;
}
~Son2()
{
cout << "class Son2 destroyed" << endl;
}
void fun11()
{
cout << "this is a virtual function of Son2" << endl;
}
};
int main()
{
Father0* p;
p= new Son2;
p->fun();
delete p;
system("pause");
return 0;
}
构建抽象类Father0,通过Father0的指针指向子类,当指向不同的子类时可以实现不同的功能(多态性)。
当使用抽象类指针指向子类Son2时,构造函数从父类开始,析构函数相反。另外,在多重继承时,如果所指向的子类没有要调用的函数,则往上一层进行查找并调用。如下所示为运行的结果:
/*
class Father init
class Son1 init
class Son2 init
this is a virtual function of Son1
class Son2 destroyed
class Son1 destroyed
class Father destroyed
*/
当不将抽象类的析构函数设为虚函数时,析构时只有指针类会释放。在以上代码中,若将Father0类的析构函数的virtual取消,则产生如下运行结果:
/*
class Father init
class Son1 init
class Son2 init
this is a virtual function of Son1
*/
另外,若将Father0类的析构函数的virtual取消,Father类的析构函数设为虚函数,将指针更改为Father则产生如下运行结果:
/*
class Father init
class Son1 init
class Son2 init
this is a virtual function of Son1
class Father destroyed
*/
若仅仅将指针更改为Father,则产生如下运行结果:
/*
class Father init
class Son1 init
class Son2 init
this is a virtual function of Son1
class Son2 destroyed
class Son1 destroyed
class Father destroyed
*/
4. 结论
在通过构建虚函数实现多态时,一定要记得将基类的析构函数设为虚函数。另外,若不使用基类指针,则不需要将基类的析构函数设为虚函数。
如将
Father0* p;
p= new Son2;
改为
Son2* p;
p= new Son2;
则运行结果为:
/*
class Father init
class Son1 init
class Son2 init
this is a virtual function of Son1
class Son2 destroyed
class Son1 destroyed
class Father destroyed
*/
最后,对析构函数感兴趣的朋友可以查看下面的参考文献。
参考文献:
[1] 为什么析构函数必须是虚函数
[2] 虚函数与多态
[3] c++虚函数详解