即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

C++虚表撞上虚继承后的内存布局

编程语言 PROGRAM_anywhere 8℃ 0评论

先看一个有虚表的内存布局:

class  K
{
    double a;
    virtual void fun();
};
class L : public K
{
    int b;
    virtual void fun();
};//24
/*
        +---
        | +--- (base class K)
 0      | | {vfptr}
 8      | | a
        | +---
16      | b
        | member> (size=4)
        +---
*/

这个没啥说的应该都会,注意字节对齐就OK

再来看看加上虚继承呢?

class  A
{
    virtual void fun();
};
class B :virtual public A
{
    int b;
    virtual void fun();
};//12
/*
        +---
 0      | {vbptr}
 4      | b
        +---
        +--- (virtual base A)
 8      | {vfptr}
        +---
*/

在这里就纠正一下有些人的默认思维,总以为有了虚表,vfptr总是放在这个类开始地址处,从上面我就能看见并不是这样。


你可能会说:vfptr 的优先级是不是没有 vbptr高啊?


其实并不是这样 vfptr的优先级是比vbptr高的,只是有了虚继承,自己类的东西在内存上会放在最上面,而父类的东西就放在下面了,这样做的好处是啥?就是为了不重复公共父类的成员变量。

再来看一个菱形继承内存布局:

class  O
{
    int a;
};
class P : virtual public O
{
    int a;
};
class R : virtual public O
{
    int a;
};
class S : public P, public R
{
    int a;
};//24
/*
        +---
        | +--- (base class P)
 0      | | {vbptr}
 4      | | a
        | +---
        | +--- (base class R)
 8      | | {vbptr}
12      | | a
        | +---
16      | a
        +---
        +--- (virtual base O)
20      | a     这就是公共父类,在子类中只有一份,节约了内存
        +---
*/

然后验证我说的vfptr优先级比vbptr高:

class  G
{
    int a;
    virtual void fun();
};
class H : virtual public G
{
    int b;
    virtual void fun22();
};
/*
        +---
 0      | {vfptr}                   //自己的虚表
 4      | {vbptr}
 8      | b
        +---
        +--- (virtual base G)
12      | {vfptr}                   //父类的虚表
16      | a
        +---
*/

是不是vfptr优先级比vbptr高啊?在这种情况下虚表指针还是处于类的前四个字节。


为什么会出现这样的情况呢?虚继承需要有自己的虚表,当自己类中出现和父类不一样的虚函数就需要创建一个属于自己的虚表,并且还要继承父类的虚表。这方面也能说明一个类的虚表可以有多个。

有人会问为啥要两个虚表?一个不行么?


再看两个例子

class  C
{
    int a;
};
class D :public C
{
    int b;
};
class E :public D
{
    int c;
};
/*
        +---
        | +--- (base class D)
        | | +--- (base class C)
 0      | | | {vfptr}
 4      | | | a
        | | +---
 8      | | b
        | +---
12      | c
        +---
*/

这个是普通继承子类是包含于父类的,当然父类的vfptr我们可以利用,没必要两个vfptr

but:

class  C
{
public:
    int a;
};
class D :virtual public C
{
public:
    int b;
};
class E :virtual public D
{
public:
    int c;
};
/*
        +---
 0      | {vbptr}
 4      | c
        +---
        +--- (virtual base C)
 8      | {vfptr}
12      | a
        +---
        +--- (virtual base D)
16      | {vfptr}
20      | {vbptr}
24      | b
        +---
*/

如果是虚继承,子类虚继承的父类和自己是并列的关系,并不是包含。所以vfptr可能有多个。


为啥不是嵌套类型?就要说到虚继承是如何节约内存的啦。就是为了公共父类不被重复创建,所以才每个类占一个块,这样就不可能在子类中产生两个一样的父类了

大东辛苦创作O(∩_∩)O哈哈~

转载请注明:CodingBlog » C++虚表撞上虚继承后的内存布局

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情