虚函数是C++中实现多态的一种方法,父类A的一个函数声明为虚函数,在子类B中覆盖定义之后,当在调用的时候使用A*a=new B(),此时调用对应的那个虚函数的名字,则会执行B中的函数。当父类中没有定义虚函数的实体时候,virtual void foo()=0;这个函数就是一个纯虚函数,对应的父类就是抽象类,则这个抽象类不能被实例化,只能由子类派生实例化。
每个含有虚函数的对象都有一个虚指针,这个虚指针和这个对象的基地址是一样的,即一个对象的第一块内存单元存储的一定这个类对象的虚指针。
普通继承中,B对象中只有一个虚指针,这个指针指向A、B中的所有虚函数存在的虚表;对于虚拟继承来讲,在B的对象中会有两个虚指针,一个是仅仅只有A对象的虚表,另一个指针是A、B所有的虚函数虚表指针。当然,包含A、B所共有的虚函数的虚表指针是和B在同一个基地址上的,即B对象的第一个单元的指针即是虚表指针。另外还需要注意的是当B覆盖了A中定义的虚函数之后,B对象中的仅仅只有A的虚函数的那个虚指针所在的虚表项目会做调整,里面的函数指针指向的是覆盖后的函数(B中的函数)。
还有一个地方需要注意,就是虚函数和重载函数的问题。虚函数重写的时候是允许返回值有差异的,但是对参数的要求很严格。遵循以下规则:
a) 参数必须类型一样,即使有继承关系也不行,Base* Base::copy(Base*); Base* Derived::copy(Derived*),这样是不能够达到虚函数的效果的;
b) 参数类型一样,而且也允许在父类中有默认值,则子类继承这个默认值,即ostream& Base::print(int,ostream&=cout); ostream& Derived::print(int,ostream&);这样的写法是允许的,而且这个默认值写在子类和父类都是可以的,若写在父类中,使用子类调用的时候默认值必须写上;
c) 对于函数const属性修饰符,若一个加一个没有加,则看成是重载函数而不是重写;
d) 返回类型若是基本类型,则必须保持一样,否则会报错;返回类型若是类对象,则可以允许返回值之间有继承关系(直接/间接继承都可以)即可(菱形继承除外)