跟我一起学习C++虚函数--第二篇

上一篇,我们讨论了带有虚函数的对象的内存布局情况。这一篇,主要讨论带有虚函数的类在单一继承情况下的内存布局情况。

     还是从例子入手:

01 #include <iostream>
02  
03 using namespace std;
04  
05 class Point
06 {
07 public:
08     int x();
09     virtual void y(){cout << "Point y" << endl;};
10     virtual void print1(){cout << "Point print" << endl;};
11     int _x;
12 };
13  
14 class Point2:public Point
15 {
16 public:
17     virtual void y(){ cout << "Point2 y" << endl;};
18     virtual void print2(){cout << "Point2 print" << endl;};
19     int _y;
20 };
21  
22 int main()
23 {
24     Point *p = new Point();
25     Point2 *p2 = new Point2();
26  
27     /*first part*/
28     cout << p2 << "\t" << &p2->_x << "\t" << &p2->_y << endl;
29  
30     /*second part*/
31     typedef void (* Func)(void);
32     Func pFunc = (Func)*((int *)*(int *)(p2));
33     pFunc();//输出:Point2 y
34     pFunc = (Func)*((int *)*(int *)(p2)+1);
35     pFunc();//输出:Point print
36     pFunc = (Func)*((int *)*(int *)(p2)+2);
37     pFunc();//输出:Point2 print
38  
39     return 0;
40 }

 对与这个例子,我们从两部分讨论。

第一部分:继承情况下,对象本身(除虚函数表外)的内存布局。

         从代码中first part的输出情况,我们可以看出对象内存布局情况如下图所示:

            

        虚指针始终在对象的起始地址处,接下来是父类的成员变量,最后是子类自己的成员变量。

第二部分:继承情况下,虚函数表的内存布局。

        从代码second part的输出情况,我们可以看出虚函数表的内存布局如下图所示:

        

        编译器之所以这样安排父类和子类中虚函数的位置,说白了就是为了实现对父子类虚函数的动态绑定,因为对虚函数是通过虚表的槽位来进行调用的。举例说明:

        比如调用void y();

        编译器可能把代码:

1 Point *p = new Point();
2 p->y();
3 p = new Point2();
4 p->y();

        把所有对y()函数的调用都变成  p->vptr->vptbl[0] ;而Point和Point2对象的槽位0中分别是它们自己的void y()函数,因此就实现了动态绑定。类似的,对子类没有覆盖的虚函数,也可以通过同样的槽位进行调用;对子类有而父类没有的虚函数,子类对象可以用自己独有的槽位来进行调用。

 

参考文献:

        1.《深度探索C++对象模型》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值