定义为 virtual的 函数是基类期待派生类(derived class)重定义的, 基类期待 派生类继承的函数不能定义为虚函数。
动态绑定:程序能 使用继承层次中任意类型的对象,无需关心对象的具体类型。 使用这些类的程序无须区分函数在基类还是在继承类中定义的。
尽管不是必须,派生类一般会重定义基类中的虚函数。如果没有重定义,则继承基类中定义的版本。派生类中虚函数的声明原型必须与基类中的定义方式完全匹配,但有一个例外:基类中返回值是对基类性的引用(或指针)的虚函数,在派生类中虚函数可以返回 基类和派生类 的引用(或指针)。
C++默认不适用动态绑定,要触发动态绑定,得满足2条件:1,只有指定为虚函数的成员函数才能进行动态绑定,2,必须通过基类类型的引用(或指针)才能进行函数调用。
可以使用基类类型的引用(或指针)来引用基类类型或派生类类型对象。基类类型引用和指针的关键点在于:静态类型(编译时可知的引用类型或指针类型)与动态类型(运行时才可知的,指针或引用绑定的对象类型)可能不同。
即动态绑定是由实参决定的。
1 class Cshape 2 { 3 public: 4 void SetColor(intcolor){m_nColor=color;} 5 virtual void Display(void){cout<<"Cshape"<<endl;} 6 private: 7 int m_nColor; 8 }; 9 10 class Crectangle:public Cshape{ 11 public: 12 virtual void Display(void) 13 { 14 cout<<"Crectangle"<<endl; 15 } 16 }; 17 18 class Ctriangle:public Cshape{ 19 virtual void Display(void) 20 {cout<<"Ctriangle"<<endl;} 21 }; 22 23 class Cellipse:public Cshape{ 24 public: 25 virtual void Display(void) 26 {cout<<"Cellipse"<<endl;} 27 }; 28 29 voidmain() 30 { 31 Cshape obShape; 32 Cellipse obEllipse; 33 Ctriangle obTriangle; 34 Crectangle obRectangle; 35 Cshape*pShape[4]={&obShape,&obEllipse,&obTriangle,&obRectangle}; 36 for(int i=0;i<4;i++) 37 pShape[i]->Display();//动态绑定 38 }
本程序运行结果:
Cshape
Cellipse
Ctriangle
Crectangle
如果把Cshape类里面virtual void Display(void) 中的virtual去掉的话
运行结果就不一样了:
Cshape
Cshape
Cshape
Cshape
继承层次的根类 一般要定义虚析构函数。
找工作时,面试官经常会问虚函数是如何实现的,我就懵了,以为知道以上理论虚函数就过关了,书上也没说过虚函数是如何实现的,现在在此补一补:
C++中的虚函数的实现一般是通过虚函数表(C++规范并没有规定具体用哪种方法,但大部分的编译器厂商都选择此方法)。
类的虚函数表是一块连续的内存,每个内存单元中记录一个JMP指令的地址。
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数。这样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
虚基类与虚函数没有关系。用于多重继承,防止二义性产生。