C++之对象内存模型

对象内存大小

首先确定类的构成:
1) 数据成员:内置类型,类类型
2) 函数成员:虚函数、非虚函数

1)数据成员

内置类型大小,字节为单位

1
2
3
4
5
6
char 1 
short 2
int 4
long 4
float 4
double 8

主要有3条规则:

  1. 结构体内的成员的首地址相对于结构体首地址的偏移量是其类型大小的整数倍,比如说double型成员相对于结构体的首地址的地址偏移量应该是8的倍数。
  2. 结构体(或类对象)的大小等于结构体(或类对象)内最大成员大小的整数倍。
  3. 为了满足规则1和2编译器会在结构体(或类对象)成员之后进行字节填充。

2)函数成员

函数成员是不占用内存中类的对象的字节。C++中为了兼容C语言,也允许struct 作为类的声明。在C语言中struct是用来声明结构体类型的,只不过C中的结构体没有函数成员。 同样 C++中允许的函数成员,只不过是类给函数提供了一个作用域。
一个对象调用函数的时候,可以等价为普通函数的调用。例如:

1
2
3
4
5
6
class A
{
cout<<"Hello";
};
A a;
a.f();

a.f();等价于调用f(&a);类中的成员函数(static 成员函数除外)形参中都有个隐含的this 指针,它指向类对象本身。 当对象 a 调用f()的时候,它会把a 的地址传给this 指针,所以f()就等价执行

1
2
3
4
f(const A* this)
{
cout<<"Hello";
}

对象内存大小计算举例

例1:

1
2
3
4
5
6
7
8
9
10
11
class A
{
char c;
int i;
};
A a;
// 对象a的内存大小sizeof(a) = 8 bytes
// c 放在起始位置0,占1 个字节。
// i 是int 要4 字节对齐,所以前面要空3 字节
// 最后类要按照他最长的数据成员对齐,就是i, 是4 字节对齐.因为已经占用了8 个字节,
// 8 是对齐4 的,所以不用额外增加字节数了

例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class B
{
doube d;
char c;
A a; // eg.1 中的类类型A
};
B b;
// 对象b 的内存大小sizeof(b)=24
/*
d 放在起始位置0到7,占8个字节。
c 是char 要1字节对齐,所以放在位置8,占1个字节。
b 是类类型,在eg.1中知道它是8字节对齐的,所以前面要空7个字节,它从位置16
开始存储,一直到23,占8个字节。
最后类要按照他最长的数据成员对齐,也就是8字节对齐,因为已经占用了24个字
节,24是对齐8 的,所以不用额外增加字节数了。最后sizeof(a)=24。
*/

对象内存结构

1 基类

  • 有虚函数:先存放虚函数表指针,然后存放自己的数据成员
  • 没有虚函数:直接存放数据成员

2 单一继承派生类

  • 无虚函数:先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据
  • 有虚函数: 同基类有虚函数情况,且本类虚函数表中,先存放父类的虚函数,再存放子类的虚函数
  • 重载了父类的某些虚函数: 新的虚函数将虚函数表中父类的这些虚函数覆盖

单一继承派生类,派生类无虚函数
单一继承派生类,派生类无虚函数

单一继承派生类,派生类有虚函数
单一继承派生类,派生类有虚函数

单一继承派生类,派生类有虚函数,且存在虚函数重载
单一继承派生类,派生类有虚函数,且存在虚函数重载

3 多重继承派生类

  • 无虚函数: 先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,依次类推,最后存放自己的数据成员。 其中每一个父类拷贝都包含一个虚函数表指针
  • 有虚函数:本类的虚函数表,存储于第一个父类的虚函数表后边部分

多重继承,子类无虚函数
多重继承,子类无虚函数

多重继承,子类有虚函数
多重继承,子类有虚函数

上图Child5的int child5数据上面一个int base3应改为int base4,这里写错误了。

------ 本文结束------
赞赏此文?求鼓励,求支持!
0%