虚函数表地址的打印

这段时间由于面试的一个问题(虚函数的实现原理),算是认真的看了下虚函数实现的几个介绍,有了一定的认识,简单来说,就是在创建类时,如果函数方法中存在virtual关键字,则认为此成员函数是一个虚函数,此时类对象的内存布局中就会为这个对象创建一个虚函数表,用以实现多态。指向此对象的首地址永远是虚表的指针地址,这样方便多态函数的访问。

这里有一个“地址里的地址的概念”,即指向的内容值,实际也是一个指针的地址值,而这个指针的地址值又是一个函数的首地址,要真正调用到函数的方法,实际用指针搞成了这样,

看,起来很复杂,所以用下面的程序逐个打印解析了下

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void foo() {cout << "Base::foo()" << endl;}
};

class Dry : public Base 
{
    public:
    virtual void foo() {cout << "Dry::foo()" << endl;}
};
typedef void (*Fun)(void);

int main()
{
    Fun pFun;
    Dry d;
    cout << &d << endl;   // 1、取对象的首地址(为一个地址值),因为虚表指针就是在对象的首地址中存放
    cout << (int *)&d << endl;  // 2、转换成int型的指针
    cout << *(int *)&d << endl;  // 3、取值,取出来的是虚表指针的指针值(即指向内容的地址值)
    cout << (int *)*(int *)&d << endl; // 4、再转换成int型的指针(主要是为了地址对齐)
    cout << *(int *)*(int *)&d << endl; // 5、取出来的虚表指针指向的内容(实际为一个函数的头(又是一个地址-》函数地址))
    cout << (Fun)*((int *)*(int *)&d) << endl; // 6、只不过上一步还是int型的,要转换成函数指针的类型:void (Fun *)(void)

    cout << "*******************begin testing *******************" << endl;
    pFun = (Fun)*((int*)*(int *)(&d)+0);
    pFun(); //取到之后,执行函数
    return 0;
}

 打印后的结果如下:(与程序中打印的结果一一队形)

1、0x61ff08
2、0x61ff08
3、4215204    
4、0x4051a4   
5、4209992
6、1
*******************begin testing *******************
7、Dry::foo()  -----最终调用的函数

 

解析,这里1/2是一样的,说明是对象的首地址,只是用int转换了下,3/4是一样的,10进制和16机制的转换而已,5才是函数头的指针地址,6、转换成Fun指针后变成了1不是很理解

最后7正确打印函数调用信息。

理解:这里除了6,其他的都很直观,是什么就是什么,只是此处取了3此地址值而已,我想这里要不断的使用int*转换,是因为地址对齐的原因,指针地址值的size正好是int的size

 

posted @ 2020-01-31 15:35  tnbryant  阅读(284)  评论(0)    收藏  举报