C++类内存分布(运行)

1、怎么看到相关类的内存布局

设置VS,项目属性——命令行——其它选项:

在其他选项这里写上/d1 reportAllClassLayout,它可以看到所有相关类的内存布局

写上/d1 reportSingleClassLayoutXXX(XXX为类名),则只会打出指定类XXX的内存布局

2、运行

(1)普通基类

class Base {
	int a;
	int b;
	static int c;
public:
	void commonFunction();
};

输出内存分布:

1>class Base	size(8):
1>	+---
1> 0	| a
1> 4	| b
1>	+---

可以看到:内存分布依据声明的顺序进行排列(类内偏移为0开始),成员函数不占内存空间,static成员变量分配在静态存储区域

(2)继承 

class DerivedClass : public Base {
	int c;
public:
	void DerivedCommonFunction();
};

输出内存排布: 

1>class DerivedClass	size(12):
1>	+---
1> 0	| +--- (base class Base)
1> 0	| | a
1> 4	| | b
1>	| +---
1> 8	| c
1>	+---

 可以看到同样在内存排布上,先是排布了父类的成员变量,接着排布子类的成员变量

(3)基类加上虚函数

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

class DerivedClass : public Base {
	int c;
public:
	void DerivedCommonFunction();
};

内存排布:

1>class Base	size(16):
1>	+---
1> 0	| {vfptr}
1> 8	| a
1>12	| b
1>	+---
1>
1>Base::$vftable@:
1>	| &Base_meta
1>	|  0
1> 0	| &Base::virtualFunction
1>
1>Base::virtualFunction this adjustor: 0
1>
1>class DerivedClass	size(24):
1>	+---
1> 0	| +--- (base class Base)
1> 0	| | {vfptr}
1> 8	| | a
1>12	| | b
1>	| +---
1>16	| c
1>  	| <alignment member> (size=4)
1>	+---
1>
1>DerivedClass::$vftable@:
1>	| &DerivedClass_meta
1>	|  0
1> 0	| &Base::virtualFunction

对于基类,看到把虚表指针放在了内存的开始处(0地址偏移),然后再是成员变量;下面生成了虚表,紧跟在&Base_meta后面的0表示,这张虚表对应的虚指针在内存中的分布 ;

对于子类,可以看到子类继承了基类的虚表指针,然后先排布了父类的成员变量,接着排布子类的成员变量;下面生成虚表,由于子类没有对虚函数进行重写,如果调用的是virtualFunction,那么会从父类中寻找对应的虚函数;

 

(4)在子类中添加虚函数(覆盖父类的虚方法并写一个新的虚函数)

class DerivedClass : public Base {
	int c;
public:
	void DerivedCommonFunction();
	virtual void virtualFunction();
	virtual void virtualFunction2();
};
1>class DerivedClass	size(24):
1>	+---
1> 0	| +--- (base class Base)
1> 0	| | {vfptr}
1> 8	| | a
1>12	| | b
1>	| +---
1>16	| c
1>  	| <alignment member> (size=4)
1>	+---
1>
1>DerivedClass::$vftable@:
1>	| &DerivedClass_meta
1>	|  0
1> 0	| &DerivedClass::virtualFunction
1> 1	| &DerivedClass::virtualFunction2

 可以看出继承了父类的虚表指针,虚表的0号是子类的virtualFunction,而1号放的是子类的virtualFunction2

 

(5) 多重继承

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

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

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

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

父类排布:

1>class Base	size(16):
1>	+---
1> 0	| {vfptr}
1> 8	| a
1>12	| b
1>	+---
1>
1>Base::$vftable@:
1>	| &Base_meta
1>	|  0
1> 0	| &Base::virtualFunction
1>
1>Base::virtualFunction this adjustor: 0

 Base中有一个虚表指针,地址偏移为0

1>class DerivedClass1	size(24):
1>	+---
1> 0	| +--- (base class Base)
1> 0	| | {vfptr}
1> 8	| | a
1>12	| | b
1>	| +---
1>16	| c
1>  	| <alignment member> (size=4)
1>	+---
1>
1>DerivedClass1::$vftable@:
1>	| &DerivedClass1_meta
1>	|  0
1> 0	| &DerivedClass1::virtualFunction
1>
1>DerivedClass1::virtualFunction this adjustor: 0



1>class DerivedClass2	size(24):
1>	+---
1> 0	| +--- (base class Base)
1> 0	| | {vfptr}
1> 8	| | a
1>12	| | b
1>	| +---
1>16	| d
1>  	| <alignment member> (size=4)
1>	+---
1>
1>DerivedClass2::$vftable@:
1>	| &DerivedClass2_meta
1>	|  0
1> 0	| &DerivedClass2::virtualFunction
1>
1>DerivedClass2::virtualFunction this adjustor: 0

DerivedClass1和DerivedClass2继承了Base,内存排布是先父类后子类。

1>class DerivedDerivedClass	size(56):
1>	+---
1> 0	| +--- (base class DerivedClass1)
1> 0	| | +--- (base class Base)
1> 0	| | | {vfptr}
1> 8	| | | a
1>12	| | | b
1>	| | +---
1>16	| | c
1>  	| | <alignment member> (size=4)
1>	| +---
1>24	| +--- (base class DerivedClass2)
1>24	| | +--- (base class Base)
1>24	| | | {vfptr}
1>32	| | | a
1>36	| | | b
1>	| | +---
1>40	| | d
1>  	| | <alignment member> (size=4)
1>	| +---
1>48	| e
1>  	| <alignment member> (size=4)
1>	+---
1>
1>DerivedDerivedClass::$vftable@DerivedClass1@:
1>	| &DerivedDerivedClass_meta
1>	|  0
1> 0	| &DerivedDerivedClass::virtualFunction
1>
1>DerivedDerivedClass::$vftable@DerivedClass2@:
1>	| -24
1> 0	| &thunk: this-=24; goto DerivedDerivedClass::virtualFunction
1>
1>DerivedDerivedClass::virtualFunction this adjustor: 0

DerivedDerivedClass,由外向内看,它并列地排布着继承而来的两个父类DerivedClass1与DerivedClass2,还有自身的成员变量e。DerivedClass1包含了它的成员变量c,以及Base,Base有一个0地址偏移的虚表指针,然后是成员变量a和b;DerivedClass2的内存排布类似于DerivedClass1,注意到DerivedClass2里面竟然也有一份Base

这里有两份虚表了,分别针对DerivedClass1与DerivedClass2,在&DerivedDericedClass_meta下方的数字是首地址偏移量,靠下面的虚表的那个-16表示指向这个虚表的虚指针的内存偏移,这正是DerivedClass2中的{vfptr}在DerivedDerivedClass的内存偏移。

 

(6)虚继承

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

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

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

class DerivedDerivedClass : public DerivedClass1, public DerivedClass2 {
	int e;
public:
	void DerivedCommonFunction();
	virtual void virtualFunction();
};
1>class Base	size(16):
1>	+---
1> 0	| {vfptr}
1> 8	| a
1>12	| b
1>	+---
1>
1>Base::$vftable@:
1>	| &Base_meta
1>	|  0
1> 0	| &Base::virtualFunction
1>
1>Base::virtualFunction this adjustor: 0
1>
1>class DerivedClass1	size(32):
1>	+---
1> 0	| {vbptr}
1> 8	| c
1>  	| <alignment member> (size=4)
1>	+---
1>	+--- (virtual base Base)
1>16	| {vfptr}
1>24	| a
1>28	| b
1>	+---
1>
1>DerivedClass1::$vbtable@:
1> 0	| 0
1> 1	| 16 (DerivedClass1d(DerivedClass1+0)Base)
1>
1>DerivedClass1::$vftable@:
1>	| -16
1> 0	| &DerivedClass1::virtualFunction
1>
1>DerivedClass1::virtualFunction this adjustor: 16
1>vbi:	   class  offset o.vbptr  o.vbte fVtorDisp
1>            Base      16       0       4 0
1>
1>class DerivedClass2	size(32):
1>	+---
1> 0	| {vbptr}
1> 8	| d
1>  	| <alignment member> (size=4)
1>	+---
1>	+--- (virtual base Base)
1>16	| {vfptr}
1>24	| a
1>28	| b
1>	+---
1>
1>DerivedClass2::$vbtable@:
1> 0	| 0
1> 1	| 16 (DerivedClass2d(DerivedClass2+0)Base)
1>
1>DerivedClass2::$vftable@:
1>	| -16
1> 0	| &DerivedClass2::virtualFunction
1>
1>DerivedClass2::virtualFunction this adjustor: 16
1>vbi:	   class  offset o.vbptr  o.vbte fVtorDisp
1>            Base      16       0       4 0
1>
1>class DerivedDerivedClass	size(56):
1>	+---
1> 0	| +--- (base class DerivedClass1)
1> 0	| | {vbptr}
1> 8	| | c
1>  	| | <alignment member> (size=4)
1>	| +---
1>16	| +--- (base class DerivedClass2)
1>16	| | {vbptr}
1>24	| | d
1>  	| | <alignment member> (size=4)
1>	| +---
1>32	| e
1>  	| <alignment member> (size=4)
1>	+---
1>	+--- (virtual base Base)
1>40	| {vfptr}
1>48	| a
1>52	| b
1>	+---
1>
1>DerivedDerivedClass::$vbtable@DerivedClass1@:
1> 0	| 0
1> 1	| 40 (DerivedDerivedClassd(DerivedClass1+0)Base)
1>
1>DerivedDerivedClass::$vbtable@DerivedClass2@:
1> 0	| 0
1> 1	| 24 (DerivedDerivedClassd(DerivedClass2+0)Base)
1>
1>DerivedDerivedClass::$vftable@:
1>	| -40
1> 0	| &DerivedDerivedClass::virtualFunction
1>
1>DerivedDerivedClass::virtualFunction this adjustor: 40

可以看到Base没有变化;

DerivedClass1有两个虚表指针了,一个是vbptr,另一个是vfptr。vbptr是这个DerivedClass1对应的虚表指针,它指向DerivedClass1的虚表vbtable,另一个vfptr是虚基类表对应的虚指针,它指向vftable。

DerivedClass2的内存分布类似于DerivedClass1,同样会有两个虚指针,分别指向两张虚表(第二张是虚基类表)。

 

DerivedDerivedClass的内存分布,这里面有三个虚指针了,但base却只有一份。

 

虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(更多的虚表指针)。

下面总结一下(当基类有虚函数时):

1. 每个类都有虚指针和虚表;

2. 如果不是虚继承,那么子类将父类的虚指针继承下来,并指向自身的虚表(发生在对象构造时)。有多少个虚函数,虚表里面的项就会有多少。多重继承时,可能存在多个的基类虚表与虚指针;

3. 如果是虚继承,那么子类会有两份虚指针,一份指向自己的虚表,另一份指向虚基表,多重继承时虚基表与虚基表指针有且只有一份。

 参考:https://www.cnblogs.com/jerry19880126/p/3616999.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++ 中,结构体(struct)是一种复合数据型,它可以用来存储不同型的数据。结构体中的数据成员在内存中的分布方式与其定义的顺序有关,通常可以通过 sizeof 运算符来获取结构体的大小,以及使用地址运算符(&)来获取结构体中每个数据成员的地址。 下面以一个简单的结构体为例,说明结构体中数据成员在内存中的分布方式。 ```c++ #include <iostream> struct MyStruct { char c; short s; int i; }; int main() { std::cout << "sizeof(MyStruct) = " << sizeof(MyStruct) << std::endl; MyStruct s; std::cout << "&s.c = " << (void*)&s.c << std::endl; std::cout << "&s.s = " << (void*)&s.s << std::endl; std::cout << "&s.i = " << (void*)&s.i << std::endl; return 0; } ``` 输出结果为: ``` sizeof(MyStruct) = 8 &s.c = 0x7ffce85d3f70 &s.s = 0x7ffce85d3f72 &s.i = 0x7ffce85d3f74 ``` 可以看到,该结构体中的数据成员按照定义的顺序依次分布内存中。由于 char 型的大小为 1,short 型的大小为 2,int 型的大小为 4,因此结构体的大小为 8(1 + 2 + 4)。 在内存中,结构体中的数据成员按照定义的顺序依次存放,但是具体的存储方式可能会受到编译器、操作系统等因素的影响。例如,对于不同的编译器和操作系统,结构体的对齐方式可能会不同。可以使用 `#pragma pack` 指令来控制结构体的对齐方式。 总体来说,结构体中的数据成员在内存中的分布方式是与结构体的定义顺序相关的,但是具体的实现方式可能会受到编译器和操作系统等因素的影响。因此,在编写程序时,需要注意结构体的对齐方式和内存布局,以确保程序在不同的平台上都能正确运行

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值