使用多态时,必须要让父类base class 的析构函数 成为virtual的析构函数
(否则,就会出现在释放指向子类对象的父类指针时(这是使用多态必须写的语句),会因为无法释放子类部分的内存而导致内存的泄露问题)
请看以下代码:
#include<iostream>
using namespace std;
class TimeKeeper
{
public:
TimeKeeper() { cout << "调用TimeKeeper的构造函数" << endl; }
~TimeKeeper() { cout << "调用TimeKeeper的析构函数" << endl; }
};
class AtomicClock : public TimeKeeper
{
public:
AtomicClock() { cout << "调用AtomicClock的构造函数" << endl; }
~AtomicClock() { cout << "调用AtomicClock的析构函数" << endl; }
};
void test()
{
TimeKeeper* timeKeeper = new AtomicClock;//父类指针指向子类对象
if (timeKeeper != NULL) {
delete timeKeeper;
}
timeKeeper = NULL;
}
int main()
{
test();
system("pause");
return 0;
}
运行结果为:
可见,此时无法调用子类的析构函数。因此未解决这样一个问题,我们需要在用多态时必须给其析构函数加一个virtual关键字,使其变成虚函数,因此子类的析构函数可以重写父类的析构函数了,也即子类的函数也可以给调用了!
下面把父类和子类的析构函数都改为虚函数,也即改为:
//父类的析构函数改为:
virtual ~TimeKeeper() { cout << "调用TimeKeeper的析构函数" << endl; }
//子类的析构函数改为:
virtual ~AtomicClock() { cout << "调用AtomicClock的析构函数" << endl; }
再次运行结果为:
可见,问题已然解决!
当然,这里再记录一下某大神的解释:
构造函数不能是虚函数的原因:
执行虚函数需要借助虚指针vptr(virtual table pointer),虚指针指向虚函数表vtbl(virtual table),而虚指针在构造函数中初始化,虚指针若还没有被初始化,就不能执行虚函数,因此,虚构造函数和虚指针是矛盾的。
析构函数有时必须是构造函数的原因:
一个类作为基类被继承时(若是用于多态的话,这是重要的前提!),则其基类的析构函数必须是虚函数。如果基类的析构函数不是虚函数,当使用基类指针指向子类的对象时,delete 基类指针,只有基类的析构函数被调用,基类指针中的派生类的部分的内存没有释放。
(若只是用于Uncopyable的话,我们只想不让编译器自动生成一些函数,那么你就可以用这个类去继承这个Uncopyable类,详情可见我的另一篇博客)
在Effective C++的条款6中已经指出,这些类和标准程序库中的类(比如string类)还有STL中的容器类(比如vector,list,map等类)一样,它们并非被设计用来进行多态的(也即让基类的指针指向子类对象),因此它们就不需要virtual析构函数了。
参考:
1:Effective C++之条款7