C++中类对象的内存布局以及虚函数表生成

类对象内存布局计算

l 空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);

l 一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;

l 因此一个对象的大小≥所有非静态成员大小的总和;

l 当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;

l 虚承继的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,这两者所占的空间大小为:8(或8乘以多继承时父类的个数);

l 在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;

l 类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。

虚函数表

C++多态的实现和Java类似,都是利用虚函数表(Java叫方法表),不同的是,Java中,不用virtual修饰,函数自带virtual属性(指的是那些非静态函数)。之前的文章《从JVM角度看Java多态》中,也简单分析了Java多态的实现机制。C++和Java是类似的。下面我们直接分析吧。

1)对于单继承

这里写图片描述

Child类重写了Parent的f(),GrandChild类重写了Child类的f()和g_child();

GrandChild对象的内存布局为:

这里写图片描述

可以看到:

l 虚函数表在最前面的位置。

l 成员变量根据其继承和声明顺序依次放在后面。

l 在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。

2)对于多继承

这里写图片描述

Derive对象的内存布局

这里写图片描述

可以看到:

l 每个父类都有自己的虚表。

l 子类的成员函数被放到了第一个父类的表中。

l 内存布局中,其父类布局依次按声明顺序排列。

l 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

3)对于菱形继承问题

这里写图片描述

D对象的内存布局

这里写图片描述

可以看到

我们可以看见,最顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,容易产生二义性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值