静态多态(早绑定)
class Rect //矩形类
{
public:
int calcArea(int width);
int calcArea(int width,int height);
};
即函数重载,函数名相同,但参数个数或类型不同。
动态多态(晚绑定)
利用虚函数实现了运行时的多态,也就是说在系统编译的时候并不知道程序将要调用哪一个函数,只有在运行到这里的时候才能确定接下来会跳转到哪一个函数的栈帧。
虚函数 virtual
在基类中声明该函数是虚拟的(在函数之前加virtual关键字),然后在子类中正式的定义(子类中的该函数的函数名,返回值,函数参数个数,参数类型,全都与基类的所声明的虚函数相同,此时才能称为重写,才符合虚函数,否则就是函数的重载)
class A{//虚函数示例代码
public:
virtual void fun(){cout<<1<<endl;}
virtual void fun2(){cout<<2<<endl;}
};
class B : public A{
public:
void fun(){cout<<3<<endl;}
void fun2(){cout<<4<<endl;}
};
由于这两个类中有虚函数存在,所以编译器就会为他们两个分别插入一段你不知道的数据,并为他们分别创建一个表。那段数据叫做vptr指针,指向那个表。那个表叫做vtbl,每个类都有自己的vtbl,vtbl的作用就是保存自己类中虚函数的地址。
A *p=new A;
p->fun();
调用了A::fun(),但是A::fun()是如何被调用的呢?首先是取出vptr的值,这个值就是vtbl的地址,再根据这个值来到vtbl这里,由于调用的函数A::fun()是第一个虚函数,所以取出vtbl中第一个Slot的值即为第一个虚函数的地址(在不同的编译器下第一个vtbl布局不完全相同,在VS2008中第一个Slot的值为指向第一个虚函数的指针,而其他编译器中也可能出现第一个Slot中值为type_info对象的指针),这个值就是A::fun()的地址了,最后调用这个函数。
virtual在函数中的使用限制
- 普通函数不能是虚函数,也就是说这个函数必须是某一个类的成员函数,不可以是一个全局函数,否则会导致编译错误。
- 静态成员函数不能是虚函数 static成员函数是和类同生共处的,他不属于任何对象,使用virtual也将导致错误。
- 内联函数不能是虚函数 如果修饰内联函数 如果内联函数被virtual修饰,计算机会忽略inline使它变成存粹的虚函数。
- 构造函数不能是虚函数,否则会出现编译错误。