首先创建一个类对象的话,这个对象内存空间里面只有类的非静态成员和虚指针vptr(指向虚函数表)。
        然后说下编译器怎么调用非虚函数。非虚函数不在对象的内存空间中,所以编译器只能通过指向类的成员函数的指针(&A::*ptr,ptr是指向函数的指针,这个表达式应该是这么写的,如果我没记错的话。)来访问,而不是通过this指针(this指针是对象的地址)。
        再说下编译器怎么调虚函数,虚函数地址存在虚函数表vtbl里,类的对象里又有个指向虚函数表的指针vptr。所以调用方式应该是先通过this指针找到虚表指针vptr,然后虚表指针通过下标访问到对应的虚函数地址。编译的时候编译器只知道你这个函数是虚函数,地址在虚函数表中是第几个位置,但是它不知道是子类的虚函数,还是派生类的,还是其他类的,要运行时才能确认。
        你这里的A类的f1不是虚函数,所以在编译期就已经静态绑定了,编译器知道你调用的是a类的非虚函数。编译的时候编译器知道你创建了一个A类的指针,所以在编译时期你这个类的静态类型就是A类,然后会给你绑定f1函数。况且你不需要通过this指针来调非虚函数,所以这里调的是A::f1。
        这里程序运行时期,A类指针指向了一个B类地址。调用虚函数的时候是(this->vptr)[虚函数在虚表中的位置索引],得到虚函数地址。刚说了虚函数怎么调的,编译的时候获得了索引1,因为f2是A类第一个虚函数,虚函数表第0位是(RTTI用于运行时类型确认)。然后你现在运行时,你调用的又是B的虚表,因为vptr改变指向了,原来指向A的虚表,现在指向B的了,但是B的第一个虚函数又是f1,所以它访问到的自然也就是B::f1。
我暂时只能这么理解。。。