******************第二次*******************************
虚函数表。就是所有类,包括基类,派生类。都拥有的各自的 函数指针数组。
存放的是对于这个类来说,实际的所有函数指针地址。很明显,派生类数据更多,应为拥有自己独有的函数地址。
多继承,就有多个。为什么?因为派生类用基类指针的时候, 需要这个基类的方法,而每个基类方法是独立的。所有多个表,但是被重写的方法,存储的还是派生类的。 派生类独有的方法,特别的放置在第一个虚函数表中。
Base中虚函数表结构:
Derive中虚函数表结构:
具体看。
#include <stdio.h> #include <iostream> using namespace std; //每个函数都有地址.调用函数时,就压参数,机器状态等,后直接jump到指令. //类也是一样.多压一个this.指针.有一个疑问,如果一个类,无任何数据,无虚方法,无继承,对象的地址有什么用? //但是有虚函数后,重载,有同名函数,编译器起初不知道调用哪个函数.编译器就在类的数据存放前.加一个虚函数表地址. //只要是类继承了其他含有虚方法的类,类的数据前就加入一个指针,指向虚函数表.虚函数表存放了了此类调用的所有方法的实际地址. class Base { public: int a; virtual void f() { cout << "Base::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了. void g() { cout << "Base::g" << endl; }//不在Base的虚函数表中.当然也不再Derive中. virtual void h() { cout << "Base::h" << endl; }//在Derive的虚函数表中.按继承顺序覆盖了后面的基类base的h方法. Base():a(1){} Base(const Base& _p) { a=_p.a; } }; class Base2 { public: int a; virtual void f() { cout << "Base2::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了. virtual void g1() { cout << "Base2::g1" << endl; }//在Base的虚函数表中.在Derive中. virtual void h() { cout << "Base2::h" << endl; }//不在Derive的虚函数表中.按继承顺序覆盖了. Base2():a(2){} }; class Derive : public Base,public Base2{ public: int a; virtual void f() { cout << "Derive::f" << endl; }//在Derive的虚函数表中.覆盖了基类的. virtual void g1() { cout << "Derive::g1" << endl; }//在Derive的虚函数表中.覆盖了基类的. virtual void e(){cout << "mysefl e" << endl;};//在Derive的虚函数表中. Derive():a(3){} }; int main (int argc,char *argv[]) { typedef void(*Fun)(void); Base b; Fun pFun = NULL; printf( "对象b的内存地址,也就是class类base1的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , &b,*(int *)&b ); printf( "对象b的数据a, 地址:%0x, value:%d " , &(b.a),*&(b.a)); printf( "类base1的虚函数表 — 第一个函数地址:%0x " , *(int *)*(int *)(&b) ); pFun = (Fun) *(int *)*(int *)(&b); pFun(); printf( "类base1的虚函数表 — 第二个函数地址:%0x " , *(int *)(*(int *)(&b)+4) ); pFun = (Fun) *(int *)(*(int *)(&b)+4); pFun(); printf( "类base1的虚函数表 — 第三个函数地址:%0x " , *(int *)(*(int *)(&b)+8) ); printf( "类base1的虚函数表 — 第四个函数地址:%0x " , *(int *)(*(int *)(&b)+12) ); printf( "类base1的虚函数表 — 第五个函数地址:%0x " , *(int *)(*(int *)(&b)+16) ); Base2 b2; printf( "对象b的内存地址,也就是class类base2的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , &b2,*(int *)&b2 ); printf( "对象b的数据a, 地址:%0x, value:%d " , &(b2.a),*&(b2.a)); printf( "类base2的虚函数表 — 第一个函数地址:%0x " , *(int *)*(int *)(&b2) ); pFun = (Fun) *(int *)(*(int *)(&b2)+0); pFun(); printf( "类base2的虚函数表 — 第二个函数地址:%0x " , *(int *)(*(int *)(&b2)+4) ); pFun = (Fun) *(int *)(*(int *)(&b2)+4); pFun(); printf( "类base2的虚函数表 — 第三个函数地址:%0x " , *(int *)(*(int *)(&b2)+8) ); pFun = (Fun) *(int *)(*(int *)(&b2)+8); pFun(); printf( "类base2的虚函数表 — 第四个函数地址:%0x " , *(int *)(*(int *)(&b2)+12) ); printf( "类base2的虚函数表 — 第五个函数地址:%0x " , *(int *)(*(int *)(&b2)+16) ); Derive d; printf( "对象d的内存地址,也就是class类Derive的第一张虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , &d,*(int *)&d ); printf( "第一基类的数据地址 :%0x,数据:%0x " , (int *)(&d)+1,*((int *)(&d)+1) ); printf( "对象d的内存地址,也就是class类Derive的第二张虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , (int *)(&d)+2,*((int *)(&d)+2) ); printf( "第一基类的数据地址 :%0x,数据:%0x " , (int *)(&d)+3,*((int *)(&d)+3) ); printf( "对象d的数据a, 地址:%0x, value:%d " , &(d.a),*&(d.a)); printf( "多重继承虚函数表 — 第一张虚拟表base1第一个函数地址:%0x " , *(int *)*(int *)(&d) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)*(int *)(&d); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第二个函数地址:%0x " , *(int *)(*(int *)(&d)+4) );//用了b1的第二函数. pFun = (Fun) *(int *)(*(int *)(&d)+4); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第三个函数地址:%0x " , *(int *)(*(int *)(&d)+8) );//用了b1的第三函数. pFun = (Fun) *(int *)(*(int *)(&d)+8); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x " , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)(*(int *)(&d)+12); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x " , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址. printf( "多重继承虚函数表 — 第一张虚拟表base1第五个函数地址?:%0x " , *(int *)(*(int *)(&d)+16) );//第一个虚拟表,必须没有第5个函数. printf( "多重继承虚函数表 — 第二张虚拟表base1第一个函数地址:%0x " , *(int *)*((int *)(&d)+2) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)*((int *)(&d)+2); pFun(); printf( "多重继承虚函数表 — 第二张虚拟表base1第二个函数地址:%0x " , *(int *)(*((int *)(&d)+2)+4) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)(*((int *)(&d)+2)+4); pFun(); printf( "多重继承虚函数表 — 第二张虚拟表base1第3个函数地址:%0x " , *(int *)(*((int *)(&d)+2)+8) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)(*((int *)(&d)+2)+8); pFun(); printf( "多重继承虚函数表 — 第二张虚拟表base1第4个函数地址:%0x " , *(int *)(*((int *)(&d)+2)+12) );//测试发现,用了自己的地址. printf( "多重继承虚函数表 — 第二张虚拟表base1第5个函数地址?:%0x " , *(int *)(*((int *)(&d)+2)+16) );//测试发现,用了自己的地址. printf("*********************** "); d.f();//属于子类的2个父类的虚拟表地址.都包含 d.e(); d.g(); d.g1(); d.Base2::h(); cout<<"本质上,对象是体现不了多态的.只有指针才能体现多态.Base *pb,调用pd的f方法."<<endl; Base *pb=&d; pb->f();//到这里就会找到d的地址,通过之后找到class derive的虚函数表. Base bb2= (Base)d ;//测试发现,强制转换是调用复制构造函数. cout<<bb2.a<<endl; bb2.f(); Base bb3= static_cast<Base>(d);//也是一样,调用复制构造函数.不过这个书上说会有安全检测.没验证. cout<<bb3.a<<endl; bb3.f(); //Derive dd2= (Derive)bb2;//从基类到派生类,一样,都是需要构造函数,如果有基类为参的派生类构造函数.也行. //Derive dd2= static_cast<Derive>(bb2);//从基类到派生类,一样,都是需要构造函数,如果有基类为参的派生类构造函数.也行. return 0; }
#include <stdio.h> #include <iostream> using namespace std; //每个函数都有地址.调用函数时,就压参数,机器状态等,后直接jump到指令. //类也是一样.多压一个this.指针.有一个疑问,如果一个类,无任何数据,无虚方法,无继承,对象的地址有什么用? //但是有虚函数后,重载,有同名函数,编译器起初不知道调用哪个函数.编译器就在类的数据存放前.加一个虚函数表地址. //只要是类继承了其他含有虚方法的类,类的数据前就加入一个指针,指向虚函数表.虚函数表存放了了此类调用的所有方法的实际地址. class Base { public: int a; virtual void f() { cout << "Base::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了. void g() { cout << "Base::g" << endl; }//不在Base的虚函数表中.当然也不再Derive中. virtual void h() { cout << "Base::h" << endl; }//在Derive的虚函数表中.按继承顺序覆盖了后面的基类base的h方法. Base():a(1){} }; class Base2 { public: int a; virtual void f() { cout << "Base2::f" << endl; }//在Base的虚函数表中.不在Derive中.被继承类覆盖了. virtual void g1() { cout << "Base2::g1" << endl; }//在Base的虚函数表中.在Derive中. virtual void h() { cout << "Base2::h" << endl; }//不在Derive的虚函数表中.按继承顺序覆盖了. Base2():a(2){} }; class Derive : public Base,public Base2{ public: int a; virtual void f() { cout << "Derive::f" << endl; }//在Derive的虚函数表中.覆盖了基类的. virtual void g1() { cout << "Derive::g1" << endl; }//在Derive的虚函数表中.覆盖了基类的. virtual void e(){cout << "mysefl e" << endl;};//在Derive的虚函数表中. Derive():a(3){} }; int main (int argc,char *argv[]) { typedef void(*Fun)(void); Base b; Fun pFun = NULL; printf( "对象b的内存地址,也就是class类base1的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , &b,*(int *)&b ); printf( "对象b的数据a, 地址:%0x, value:%d " , &(b.a),*&(b.a)); printf( "类base1的虚函数表 — 第一个函数地址:%0x " , *(int *)*(int *)(&b) ); pFun = (Fun) *(int *)*(int *)(&b); pFun(); printf( "类base1的虚函数表 — 第二个函数地址:%0x " , *(int *)(*(int *)(&b)+4) ); pFun = (Fun) *(int *)(*(int *)(&b)+4); pFun(); printf( "类base1的虚函数表 — 第三个函数地址:%0x " , *(int *)(*(int *)(&b)+8) ); printf( "类base1的虚函数表 — 第四个函数地址:%0x " , *(int *)(*(int *)(&b)+12) ); printf( "类base1的虚函数表 — 第五个函数地址:%0x " , *(int *)(*(int *)(&b)+16) ); Base2 b2; printf( "对象b的内存地址,也就是class类base2的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , &b2,*(int *)&b2 ); printf( "对象b的数据a, 地址:%0x, value:%d " , &(b2.a),*&(b2.a)); printf( "类base2的虚函数表 — 第一个函数地址:%0x " , *(int *)*(int *)(&b2) ); pFun = (Fun) *(int *)(*(int *)(&b2)+0); pFun(); printf( "类base2的虚函数表 — 第二个函数地址:%0x " , *(int *)(*(int *)(&b2)+4) ); pFun = (Fun) *(int *)(*(int *)(&b2)+4); pFun(); printf( "类base2的虚函数表 — 第三个函数地址:%0x " , *(int *)(*(int *)(&b2)+8) ); pFun = (Fun) *(int *)(*(int *)(&b2)+8); pFun(); printf( "类base2的虚函数表 — 第四个函数地址:%0x " , *(int *)(*(int *)(&b2)+12) ); printf( "类base2的虚函数表 — 第五个函数地址:%0x " , *(int *)(*(int *)(&b2)+16) ); Derive d; printf( "对象d的内存地址,也就是class类Derive的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x " , &d,*(int *)&d ); printf( "对象d的数据a, 地址:%0x, value:%d " , &(d.a),*&(d.a)); printf( "多重继承虚函数表 — 第一张虚拟表base1第一个函数地址:%0x " , *(int *)*(int *)(&d) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)*(int *)(&d); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第二个函数地址:%0x " , *(int *)(*(int *)(&d)+4) );//用了b1的第二函数. pFun = (Fun) *(int *)(*(int *)(&d)+4); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第三个函数地址:%0x " , *(int *)(*(int *)(&d)+8) );//用了b1的第三函数. pFun = (Fun) *(int *)(*(int *)(&d)+8); pFun(); printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x " , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址. pFun = (Fun) *(int *)(*(int *)(&d)+12); pFun(); d.f();//属于子类的2个父类的虚拟表地址.都包含 d.e(); d.g(); d.g1(); d.Base2::h(); cout<<"本质上,对象是体现不了多态的.只有指针才能体现多态.Base *pb,调用pd的f方法."<<endl; Base *pb=&d; pb->f();//到这里就会找到d的地址,通过之后找到class derive的虚函数表. return 0; }
对于派生类的虚表。
1) 对于每个基类都生成一份虚表,
2) 子类的成员函数被放到了第一个基类的虚表中。
3) 内存布局中,其基类虚标依次按声明顺序排列。
4) 每个基类的虚表中的f()函数都被overwrite成了派类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。