C++ 虚函数的内部实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/DdogYuan/article/details/83411102

单继承的情况下

若类有虚函数,则在构造函数的时候编译器会自动为类的实例(对象)在其内存的首部(0地址偏移处)增添一个虚函数表指针vfptr,指向该类的虚函数表。虚函数表中会存放该类所有的虚函数地址,普通函数则不会被放入其中。如果是子类重写了父类的虚函数,那么在建立虚函数表的时候被重写的虚函数的地址被替换成了子类的虚函数。
而使用父类指针BaseClass* base指向一个子类对象DerivedClass时,当调用虚函数virtualFunc的时候,其实际执行过程是base->vfptr->virtualFunc , 这样就实现了父类指针调用实际子类的成员函数。

多继承的情况下

普通的多继承

class Base
{
    int a;
    int b;
public:
    void virtual VirtualFunction();
};


class DerivedClass1: public Base
{
    int c;
public:
    void virtual VirtualFunction();
};

class DerivedClass2 : public Base
{
    int d;
public:
    void virtual VirtualFunction();
};

class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
    int e;
public:
    void virtual VirtualFunction();
};

DerivedDerivedClass的内存分布中,会有两个vfptr分别指向两个虚函数表(DerivedClass1,DerivedClass2)
同时会存在两份Base类的成员a,b:
在这里插入图片描述

虚继承

若改动如下:

class DerivedClass1: virtual public Base
...

class DerivedClass2 : virtual public Base
...

则DerivedClass1和DerivedClass2的内存分布就已经有变化了。
指向virtual base Base的vfptr以及虚基类Base的成员变量被放在了内存的最后部分:
在这里插入图片描述
即额外增加了一个虚函数表指针vbptr指向派生类的虚函数表。

并且最终DerivedDerivedClass的内存分布如下:
在这里插入图片描述
可见,虚继承是通过增加额外的虚函数表指针来达到保证基类的数据只有一份的特性。

虚函数表什么时候初始化?在内存哪一块?

虚函数表指针vptr是每个对象实例都有一份的,其指向的虚函数表是属于类的,每个类都有自己单独的唯一一份虚函数表,放置在只读数据段.rodata。虚函数表是由一个个的虚函数指针组成的。.rodata只读数据段里面存放的还有由const修饰的只读数据。
在这里插入图片描述

虚函数表中的偏移量有何作用?

在这里插入图片描述
在这里插入图片描述
可以看到虚函数表中,DerivedClass1中的20表明它的虚指针{vbptr}离虚基表指针{vfptr}的距离。同理DerivedClass2的12。最后一张表是虚基表,-20指明了它对应的虚指针{vfptr}在内存中的偏移。
这些偏移量的作用暂时还不清楚其具体的应用过程,希望有同学可以互相交流和补充。

参考:
探索C++虚函数在g++中的实现
https://www.cnblogs.com/senior-engineer/p/7832915.html

C++类内存分布
http://www.cnblogs.com/jerry19880126/p/3616999.html

猜你喜欢

转载自blog.csdn.net/DdogYuan/article/details/83411102
今日推荐