先引用一段话
为什么析构函数总是虚函数?如果这是必要的,那么为什么C++不把虚析构函数直接作为默认值?为什么纯虚析构函数可以通过编译,但是不能通过连接?
回答:
编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。
所以建议的方式是将析构函数声明为虚函数。如果你使用MFC,并且以CObject或其派生类为基类,那么MFC已经为你做了这件事情;CObject的析构函数是虚函数。一个函数一旦声明为虚函数,那么不管你是否加上virtual 修饰符,它在所有派生类中都成为虚函数。但是由于理解明确起见,建议的方式还是加上virtual 修饰符。
C++不把虚析构函数直接作为默认值的原因是虚函数表的开销以及和C语言的类型的兼容性。有虚函数的对象总是在开始的位置包含一个隐含的虚函数表指针成员。如果是对于MFC类CPoint和CSize这样的小型类,增加一个指针就增加了很多内存占用,而且使得其内存表示和基类POINT和SIZE不一致。如果两个类的内存表示一致,那么这样你可以安全地把一个类的指针或数组当作另一个类的指针或数组使用。
//panda.h
#ifndef __hji__panda__
#define __hji__panda__
#include <iostream>
#include "bear.h"
class panda : public bear
{
public:
panda();
~panda();
};
#endif /* defined(__hji__panda__) */
//panda.cpp
#include "panda.h"
#include "bear.h"
panda::panda()
{
std::cout<<"panda init..."<<std::endl;
}
panda::~panda()
{
std::cout<<"panda delete..."<<std::endl;
}
//bear.h
#ifndef __hji__bear__
#define __hji__bear__
#include <iostream>
#include "animal.h"
class bear : public animal
{
public:
bear();
~bear();
};
#endif /* defined(__hji__bear__) */
//bear.cpp
#include "bear.h"
bear::bear()
{
std::cout<<"bear init..."<<std::endl;
}
bear::~bear()
{
std::cout<<"bear delete..."<<std::endl;
}
//animal.h
#include <iostream>
class animal
{
public:
animal();
virtual ~animal();
};
#endif /* defined(__hji__animal__) */
//animal.cpp
#include "animal.h"
animal::animal()
{
std::cout<<"animal init..."<<std::endl;
}
animal::~animal()
{
std::cout<<"animal delete..."<<std::endl;
}
int main()
{
// panda *p = new panda;
// bear *b = new bear;
// animal *a = new animal;
//
// delete p;
// delete b;
// delete a;
animal *a = new panda;
delete a;
}
//运行结果
animal init...
bear init...
panda init...
panda delete...
bear delete...
animal delete...
如果把基类animal的virtual去掉,则不会构成动态绑定(基类指针调用虚成员函数),然后就只输出 然后就只输出animal的析构,反之,加上的话,就是动态绑定了,调用panda的析构,然后再一层层往上析构。
另外,最顶端基类animal设为virtual之后,他的派生类的析构函数也会默认为virtual,一虚到底!所以也就不用再为bear制定virtual类型了。