跟我一块儿学习C++虚函数--第一篇

    咱们知道,虚函数做为C++实现多态的方式,具备强大的RTTI(RunTime Type Identification)功能。虚函数使用起来比较简单,可是也很容易出错。本系列将带着你一步一步了解虚函数的内部实现机制,在掌握原理后,我相信你会对虚函数以及C++自己会有进一步的认识和理解。

    注:本系列全部的关于C++虚函数的探索都是在GCC平台上进行的。

1、带有虚函数的对象内存布局ios

    让咱们先看一段代码:
#include <iostream>
using namespace std;

class Test
{
public:
    virtual void f(){cout << "Func:f()" << endl;};

public:
    int a;
};

int main()
{
    cout << sizeof(Test) << endl; //输出:8
    Test test;
    cout << &test << "\t" << &test.a << endl;//输出:0xbf91e508      0xbf91e50c
    return 0;
}

    在上面的例子中,咱们定义了一个简单的包含一个虚函数的类。从输出咱们能够看出两点:
    1)编译器在包含虚函数的类中插入了虚指针vptr,大小为4。
    2)对包含虚函数的对象,虚指针被安放在对象的起始位置。以下图所示:
               
    这个例子咱们只是简单地介绍了包含虚函数对象的成员内存布局,下面咱们慢慢深刻看看虚表的结构。仍是从例子入手:
#include <iostream>
using namespace std;

class Test
{
public:
    virtual void f(){cout << "Func:f()" << endl;};
    virtual void g(){cout << "Func:g()" << endl;};
    virtual void h(){cout << "Func:h()" << endl;};
private:
    int a;
};

int main()
{
    Test test;
    typedef void (*Func)(void);//定义个函数指针宏
    cout << "vptbl address:" << (int *)(&test) << endl;//输出:vptbl address:0xbfc85494
    cout << "func1 address:" << (int *)*(int *)(&test) << endl;//输出:func1 address:0x8048988

    Func pFunc = (Func)*((int *)*(int *)(&test));
    pFunc();//输出:Func:f()

    pFunc = (Func)*((int *)*(int *)(&test)+1);
    pFunc();//输出:Func:g()

    pFunc = (Func)*((int *)*(int *)(&test)+2);
    pFunc();//输出:Func:h()

    return 0;
}

    从输出结果咱们能够看出,咱们能够直接获取到虚函数表以及表中每个函数的地址。以下图所示:
    
    
    这里对上面的操做进行简单解释,主要是指针方面的:
    1)取得虚函数表地址:(int *)(&test)。
       在前面咱们已经讨论过,对象的起始地址为虚指针地址。对对象地址进行int *强制转换实际上就是获得虚指针,而虚指针的内容就是虚函数表地址。
    
    2)取得虚函数表中第一个虚函数地址:(int *)*(int *)(&test)。
       取得虚表地址后,再次进行取址便获得了第一个虚函数地址。
    
    3)调用虚函数表中的函数(Func)*((int *)*(int *)(&test))。
       取得第一个虚函数地址后,进行强制类型转换,便获得了函数地址。

    好,到这里相信你们应该对带有虚函数的对象的内存布局有一个较为清楚的了解了吧。下一章咱们讨论在继承状况下的内存布局状况。


参考文献:
      1.《深度探索C++对象模型》
相关文章
相关标签/搜索