一个例子
class Base {
public:
Base() { }
~Base() { cout << "Base 析构" << endl; }
};
class Derived :public Base {
public:
int a;
Derived() { };
~Derived() { cout << "Derived析构" << endl; };
};
Base *p;
p = new Derived;
cout << "*p的大小为" << sizeof(*p) << endl;
cout << "Derived 的大小为" << sizeof(Derived) << endl;
delete p;
输出结果是:
*p的大小为1
Derived的大小为4
Base 析构
class Base {
public:
Base() { }
virtual ~Base() { cout << "Base 析构" << endl; }
};
class Derived :public Base {
public:
int a;
Derived() { };
~Derived() { cout << "Derived析构" << endl; };
};
Base *p;
p = new Derived;
cout << "*p 的大小为" << sizeof(*p) << endl;
cout << "Derived 的大小为" << sizeof(Derived) << endl;
delete p;
输出结果是:
*p的大小为4
Derived的大小为8
Derived析构
Base 析构
所以: 为什么基类的析构函数要声明成虚函数
第一种情况会发生销毁不完全的情况,因为delete p调用的是声明类型(即基类)的析构函数,所以只能销毁基类对象而无法销毁派生类对象。
当基类的析构函数声明为虚函数,那么派生类的析构函数也是虚函数,此时调用delete p时发生动态绑定,运行时会根据实际类型调用该对象的虚函数。
当然,并不是要把所有类的析构函数都写成虚函数。只有当一个类是基类(即希望被继承)的时候才需要声明成虚函数,因为虚函数的作用是实现多态,而多态是建立在继承的基础上。单一类不能把析构函数写成虚函数,因为会产生额外的开销,比如虚表的创建和虚指针的定义。
关于虚函数和虚表的思考
多态:
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
先看一段程序:
typedef void(*Fun)(void);
Base b;
Fun pFun = NULL;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
// Invoke the first virtual function
pFun = (Fun)*((int*)*(int*)(&b));
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 1);// Base::g()
pFun();
pFun = (Fun)*((int*)*(int*)(&b) + 2); // Base::h()
pFun();
运行结果:
虚函数表地址:0018FDCC
虚函数表 — 第一个函数地址:00B38B34
Base::f
Base::g
Base::h
解释:http://blog.csdn.net/haoel/article/details/1948051/
通过这个示例,我们可以看到,我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调用Base::g()和Base::h(),其代码如下: