测试环境:WIN8+VS2012
#include <iostream> #include <cstdio> using namespace std; class Cbase{ public: Cbase() : x(10){} virtual void f(){cout << "Cbase::f" << endl;} virtual void g(){cout << "Cbase::g" << endl;} virtual void h(){cout << "Cbase::h" << endl;} private: int x; }; typedef void(*FUN)(); typedef void(Cbase::*PFUN)(); int main(void) { Cbase a; printf("f = 0%X ", &Cbase::f); printf("g = 0%X ", &Cbase::g); printf("h = 0%X ", &Cbase::h); PFUN c1 = &Cbase::f; PFUN c2 = &Cbase::g; PFUN c3 = &Cbase::h; (a.*c1)(); (a.*c2)(); (a.*c3)(); return 0; }输出:
既然能够正常输出,我们有理由相信&Cbase::f,&Cbase::g,&Cbase::h,是虚函数表里的地址,事实是这样吗?
有些书上说是获取到虚函数在虚函数表中的索引。但是实际上VS编译器并不是这么做的,获取到的将会是一个用于虚函数调用的地址,这个地址上的指令会先获取虚函数表,然后再通过虚函数表获取虚函数地址相关的项。而GCC的做法又跟VS不同,通过&获取得的值都是1。总而言之,通过取地址&符号获得的不是函数地址,但是可以通过获得的值成功调用该函数。
但在vs中,成员函数指针不是指向函数的真实地址,而是先指向一个位置,该位置会调用Cbase::`vcall'{0}':之后才跳转到真正地址。不同的函数,Cbase::`vcall'{0}':中的数字不同。
本质上还是编译时确定好每个函数的位置,通过此位置来查询虚函数表。
而在VS中,*(int*)*(int*)(&b)也依旧取不到虚函数表地址
可见经过了一次jmp
为什么会jmp,其实和普通函数一样
http://blog.csdn.net/x_iya/article/details/13161937
====================
#include <map> #include <iostream> using namespace std; class Base { public: virtual void Iam() { cout<<"Base::Iam"<<endl; } virtual void FunBase() { cout<<"Base::FunBase"<<endl; } }; class BaseSec { public: virtual void Iam() { cout<<"BaseSec::Iam"<<endl; } virtual void FunSec() { cout<<"BaseSec::FunSec"<<endl; } }; class Child: public Base, public BaseSec { public: virtual void Iam() { cout<<"Child::Iam"<<endl; } virtual void Fun() { cout<<"Child::Fun"<<endl; } }; int main(void) { typedef void(*Fun)(void); Fun fun; Child child; typedef void (*Fun)(void); int **p = (int**)&child; cout << p[0][0] << endl; fun = (Fun)p[0][0]; fun(); cout << p[0][1] << endl; fun = (Fun)p[0][1]; fun(); cout << p[0][2] << endl; fun = (Fun)p[0][2]; fun(); p = (int**)&child+1; cout << p[0][0] << endl; fun = (Fun)p[0][0]; fun(); cout << p[0][1] << endl; fun = (Fun)p[0][1]; fun(); return 0; }