C++用virtual关键字支持多态性。virtual修饰的函数称为虚函数。虚函数的调用是基于运行时对象的真实类型决定的,而非虚函数在编译时就已经根据对象定义的类型决定了。
#include <cstdio> class Base { public: int a; Base() { printf("Base constructor\n"); } void fun1() { printf("Base fun1\n"); } virtual void fun2() { printf("Base fun2\n"); } virtual ~Base() { printf("~Base\n"); } }; class Sub: public Base { public: Sub() { printf("Sub constructor\n"); } void fun1() { printf("Sub fun1\n"); } void fun2() { printf("Sub fun2\n"); } virtual ~Sub() { printf("~Sub\n"); } }; int main () { Base *p = new Sub(); p->fun1(); p->fun2(); delete p; return 0; }
代码的输出如下:
Base constructor
Sub constructor
Base fun1
Sub fun2
~Sub
~Base
fun2的调用的是基类的函数,因为p指针是Base类型,在编译时就已经决定了它将运行基类的fun2。而fun1则是动态绑定的,由于程序中生成的是Sub类对象,p指向的真实类型是Sub,那么fun1就会运行Sub的fun1。
在释放对象的时候,析构函数由于是虚函数,所以基类的析构函数也会调用,如果基类析构函数不用virtual修饰,那么将不会调用基类的析构函数。所以如果你的基类有些东西要做析构的时候处理,那么最好将析构函数写成virtual。
在c++ primer plus中提到过,如果一个类被实现了拷贝构造函数的话,那么就应该再重写赋值运算符和析构函数。因为自己实现拷贝构造函数的原因大多是因为类内的成员需要管理他们的拷贝问题,比如浅拷贝深拷贝。那么为了保持类生成的对象一致,赋值运算符必须与拷贝构造函数一致,那么析构的时候,就要处理到底该不该释放内存或着其他资源,此时的析构函数必须被调用,否则就会有资源被浪费。因此析构函数一旦实现,最好是virtual修饰的。