多态,其实就是函数指针的灵活运行。
首先你要知道什么叫继承,继承在内存中就是:class B : public A ==> class B inlucde class A,B的内存中的开始部分是A,当你多继承的时候,也就是第一个项,这就是为什么你强制类型转换后,还能继续访问基类的原因,什么都没有变,只不过使用了类型标志,限定了长度而已。
函数(普通函数,非虚)都是存在在类型上的,可以这样去理解:就是函数在类型的命名空间里,因此你的程序被编译后永远都是安装顺序一步一步走的,所以任何时间都最多只能有一个实例来访问类型的方法,C++中,这个东西就被叫做this。
但虚函数呢,其实也是存储在类型上的,但如果这样的话,多态就出8来了。虚函数将会被委派一个函数指针,也就是通常说的的vtable的指针,这个指针是作为一个数据成员出现的,虽然所有从一个类得到的实例都一样,但还是要有一个指针,毕竟嘛,指针只有四个字节长度那么大。
这样vtable是作为类型的第一个被定义的,只有这样在类型被上塑的时候才不会丢失vtable信息,并且一个类只有一个vtable,这样的话,继承后的类的vtable一般都和父类的vtable不一样,当你调用虚方法的时候,其实就调用了真正内存结构的里的vtable所指的方法。
所以:你的对象上溯的时候类型信息并没有丢失,你简直就可以这样理解,当你使用了虚方法,并且有不同的实现体时,类里面已经定义了一个标志变量,来帮你标志实际的类型。而实际上,C++的效果也正是这样的。
就像楼上说,在C++的编译过程中,类的存储就大体是一个结构体(几去掉成员函数),作为一个完整的封装类型,这其中,指针起到了举足轻重的作用。
比如class b
{
int a;
virtual fun1();
void fun2();
}
在编译后的结构形式大体为
vtable 4字节
int a 4字节
vtable 的作用就是为多态准备的,它是编译后虚表指针所指向的一张表(虚函数的地址表),也就是说有表的项数就为虚函数的个数,每继承一种新的类,就要为其重写了的虚函数重写他的虚表,没有重写的就直接指向其父类的地址,从而达到多态的作用,这些都是在编译的时候由编译器自动完成的。
也许你要问fun2的地址怎么不用存储了,是的,它直接就可以在编译的时候用其函数指针确定;
编译器直接可以翻译成;CALL fun2的地址。