虚函数表

******************第二次*******************************

虚函数表。就是所有类,包括基类,派生类。都拥有的各自的 函数指针数组。

存放的是对于这个类来说,实际的所有函数指针地址。很明显,派生类数据更多,应为拥有自己独有的函数地址。

多继承,就有多个。为什么?因为派生类用基类指针的时候, 需要这个基类的方法,而每个基类方法是独立的。所有多个表,但是被重写的方法,存储的还是派生类的。 派生类独有的方法,特别的放置在第一个虚函数表中。

 

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\n" , &b,*(int *)&b );
printf( "对象b的数据a, 地址:%0x, value:%d\n" , &(b.a),*&(b.a));
printf( "类base1的虚函数表 — 第一个函数地址:%0x\n" , *(int *)*(int *)(&b) );
pFun = (Fun) *(int *)*(int *)(&b);
pFun();
printf( "类base1的虚函数表 — 第二个函数地址:%0x\n" , *(int *)(*(int *)(&b)+4) );
pFun = (Fun) *(int *)(*(int *)(&b)+4);
pFun();
printf( "类base1的虚函数表 — 第三个函数地址:%0x\n" , *(int *)(*(int *)(&b)+8) );

printf( "类base1的虚函数表 — 第四个函数地址:%0x\n" , *(int *)(*(int *)(&b)+12) );
printf( "类base1的虚函数表 — 第五个函数地址:%0x\n\n" , *(int *)(*(int *)(&b)+16) );




Base2 b2;


printf( "对象b的内存地址,也就是class类base2的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x\n" , &b2,*(int *)&b2 );
printf( "对象b的数据a, 地址:%0x, value:%d\n" , &(b2.a),*&(b2.a));

printf( "类base2的虚函数表 — 第一个函数地址:%0x\n" , *(int *)*(int *)(&b2) );
pFun = (Fun) *(int *)(*(int *)(&b2)+0);
pFun();
printf( "类base2的虚函数表 — 第二个函数地址:%0x\n" , *(int *)(*(int *)(&b2)+4) );
pFun = (Fun) *(int *)(*(int *)(&b2)+4);
pFun();
printf( "类base2的虚函数表 — 第三个函数地址:%0x\n" , *(int *)(*(int *)(&b2)+8) );
pFun = (Fun) *(int *)(*(int *)(&b2)+8);
pFun();
printf( "类base2的虚函数表 — 第四个函数地址:%0x\n\n" , *(int *)(*(int *)(&b2)+12) );


printf( "类base2的虚函数表 — 第五个函数地址:%0x\n\n" , *(int *)(*(int *)(&b2)+16) );


Derive d;



printf( "对象d的内存地址,也就是class类Derive的第一张虚拟表的地址的地址 :%0x,虚拟表的地址:%0x\n" , &d,*(int *)&d );
printf( "第一基类的数据地址 :%0x,数据:%0x\n" , (int *)(&d)+1,*((int *)(&d)+1) );
printf( "对象d的内存地址,也就是class类Derive的第二张虚拟表的地址的地址 :%0x,虚拟表的地址:%0x\n" , (int *)(&d)+2,*((int *)(&d)+2) );
printf( "第一基类的数据地址 :%0x,数据:%0x\n" , (int *)(&d)+3,*((int *)(&d)+3) );
printf( "对象d的数据a, 地址:%0x, value:%d\n" , &(d.a),*&(d.a));


printf( "多重继承虚函数表 — 第一张虚拟表base1第一个函数地址:%0x\n" , *(int *)*(int *)(&d) );//测试发现,用了自己的地址.
pFun = (Fun) *(int *)*(int *)(&d);
pFun();

printf( "多重继承虚函数表 — 第一张虚拟表base1第二个函数地址:%0x\n" , *(int *)(*(int *)(&d)+4) );//用了b1的第二函数.
pFun = (Fun) *(int *)(*(int *)(&d)+4);
pFun();
printf( "多重继承虚函数表 — 第一张虚拟表base1第三个函数地址:%0x\n" , *(int *)(*(int *)(&d)+8) );//用了b1的第三函数.
pFun = (Fun) *(int *)(*(int *)(&d)+8);
pFun();
printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x\n" , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址.
pFun = (Fun) *(int *)(*(int *)(&d)+12);
pFun();
printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x\n" , *(int *)(*(int *)(&d)+12) );//测试发现,用了自己的地址.

printf( "多重继承虚函数表 — 第一张虚拟表base1第五个函数地址?:%0x\n" , *(int *)(*(int *)(&d)+16) );//第一个虚拟表,必须没有第5个函数.



printf( "多重继承虚函数表 — 第二张虚拟表base1第一个函数地址:%0x\n" , *(int *)*((int *)(&d)+2) );//测试发现,用了自己的地址.
pFun = (Fun) *(int *)*((int *)(&d)+2);
pFun();

printf( "多重继承虚函数表 — 第二张虚拟表base1第二个函数地址:%0x\n" , *(int *)(*((int *)(&d)+2)+4)   );//测试发现,用了自己的地址.
pFun = (Fun) *(int *)(*((int *)(&d)+2)+4);
pFun();


printf( "多重继承虚函数表 — 第二张虚拟表base1第3个函数地址:%0x\n" , *(int *)(*((int *)(&d)+2)+8)   );//测试发现,用了自己的地址.
pFun = (Fun) *(int *)(*((int *)(&d)+2)+8);
pFun();

printf( "多重继承虚函数表 — 第二张虚拟表base1第4个函数地址:%0x\n" , *(int *)(*((int *)(&d)+2)+12)   );//测试发现,用了自己的地址.


printf( "多重继承虚函数表 — 第二张虚拟表base1第5个函数地址?:%0x\n" , *(int *)(*((int *)(&d)+2)+16)   );//测试发现,用了自己的地址.



printf("***********************\n");

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\n" , &b,*(int *)&b );
printf( "对象b的数据a, 地址:%0x, value:%d\n" , &(b.a),*&(b.a));
printf( "类base1的虚函数表 — 第一个函数地址:%0x\n" , *(int *)*(int *)(&b) );
pFun = (Fun) *(int *)*(int *)(&b);
pFun();
printf( "类base1的虚函数表 — 第二个函数地址:%0x\n" , *(int *)(*(int *)(&b)+4) );
pFun = (Fun) *(int *)(*(int *)(&b)+4);
pFun();
printf( "类base1的虚函数表 — 第三个函数地址:%0x\n" , *(int *)(*(int *)(&b)+8) );

printf( "类base1的虚函数表 — 第四个函数地址:%0x\n" , *(int *)(*(int *)(&b)+12) );
printf( "类base1的虚函数表 — 第五个函数地址:%0x\n\n" , *(int *)(*(int *)(&b)+16) );




Base2 b2;


printf( "对象b的内存地址,也就是class类base2的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x\n" , &b2,*(int *)&b2 );
printf( "对象b的数据a, 地址:%0x, value:%d\n" , &(b2.a),*&(b2.a));

printf( "类base2的虚函数表 — 第一个函数地址:%0x\n" , *(int *)*(int *)(&b2) );
pFun = (Fun) *(int *)(*(int *)(&b2)+0);
pFun();
printf( "类base2的虚函数表 — 第二个函数地址:%0x\n" , *(int *)(*(int *)(&b2)+4) );
pFun = (Fun) *(int *)(*(int *)(&b2)+4);
pFun();
printf( "类base2的虚函数表 — 第三个函数地址:%0x\n" , *(int *)(*(int *)(&b2)+8) );
pFun = (Fun) *(int *)(*(int *)(&b2)+8);
pFun();
printf( "类base2的虚函数表 — 第四个函数地址:%0x\n\n" , *(int *)(*(int *)(&b2)+12) );


printf( "类base2的虚函数表 — 第五个函数地址:%0x\n\n" , *(int *)(*(int *)(&b2)+16) );


Derive d;



printf( "对象d的内存地址,也就是class类Derive的虚拟表的地址的地址 :%0x,虚拟表的地址:%0x\n" , &d,*(int *)&d );
printf( "对象d的数据a, 地址:%0x, value:%d\n" , &(d.a),*&(d.a));


printf( "多重继承虚函数表 — 第一张虚拟表base1第一个函数地址:%0x\n" , *(int *)*(int *)(&d) );//测试发现,用了自己的地址.
pFun = (Fun) *(int *)*(int *)(&d);
pFun();

printf( "多重继承虚函数表 — 第一张虚拟表base1第二个函数地址:%0x\n" , *(int *)(*(int *)(&d)+4) );//用了b1的第二函数.
pFun = (Fun) *(int *)(*(int *)(&d)+4);
pFun();
printf( "多重继承虚函数表 — 第一张虚拟表base1第三个函数地址:%0x\n" , *(int *)(*(int *)(&d)+8) );//用了b1的第三函数.
pFun = (Fun) *(int *)(*(int *)(&d)+8);
pFun();
printf( "多重继承虚函数表 — 第一张虚拟表base1第四个函数地址:%0x\n" , *(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()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

 

posted @ 2016-06-11 15:56  琴鸟  阅读(344)  评论(0编辑  收藏  举报