C++进阶-类对象的内存模型

类对象的内存模型

成员变量和成员函数的内存

类是对属性和行为的封装,在类的对象中也就应该有属性和行为的体现,那就是对象的成员变量和成员函数。而对象最终是保存在某个内存位置,反映到类对象的内存模型中,也就是内存中应该有对象的成员变量和成员函数。
例如,有这样一头“牛”:

class Base
{
	// 行为
	public:
	void foo1(void){};
	void foo2(void){};
	// 属性
	public:
	double m_fMember1;
	int m_nMember2;
};

其中, Base 类有两个成员变量 m_fMember1 和 m_nMember2,以及两个成员函数 foo1()和foo2()。如果我们创建 Base 类的一个对象,这个对象也同样由这四个成员共同构成。
这些成员在内存中到底是如何分布的?

// 定义类成员函数指针类型
// 用于得到类的成员函数指针
typedef void (Base::*CLASS_FUNC)(void);
int main()
{
	// 创建一个 Base 类的对象
	Base base;
	// 输出类对象所在的地址
	cout<<"类对象的地址是: "<<&base<<endl;
	// 输出类对象中成员变量的地址
	cout<<"类对象中成员变量 m_fMember1 的地址是: "<<
	&(base.m_fMember1)<<endl;
	cout<<"类对象中成员变量 m_fMember1 占用的内存字节数是: "<<
	sizeof(base.m_fMember1)<<endl;
	cout<<"类对象中成员变量 m_nMember2 的地址是: "<<
	&(base.m_nMember2)<<endl;
	// 输出类中成员函数的地址
	// 第一个函数
	CLASS_FUNC pFunc = &Base::foo1;
	unsigned int* tmp = (unsigned int*)&pFunc;
	cout<<"Base 类第一个成员函数的地址是: "<<hex<<*tmp<<endl;
	// 第二个函数
	pFunc = &Base::foo2;
	tmp = (unsigned int*)&pFunc;
	cout<<"Base 类第二个成员函数的地址是: "<<hex<<*tmp<<endl;
	return 0;
}

编译运行这个程序,我们就可以得到一份关于 base 对象的检测报告:

类对象的地址是: 001CF820
类对象中成员变量 m_fMember1 的地址是: 001CF820
类对象中成员变量 m_fMember1 占用的内存字节数是: 8
类对象中成员变量 m_nMember2 的地址是: 001CF828
Base 类第一个成员函数的地址是: 41217
Base 类第二个成员函数的地址是: 410e6

仔细分析这份类对象的检测报告就会发现,对象的第一个成员变量的地址跟整个对象的地址相同,这表明在对象的内存模型中排在第一位的就是第一个成员变量。第二个成员变量的地址就是第一个成员变量的地址加上它所占用的内存空间,换句话说,第二个成员变量紧排在第一个成员变量之后。由此可以看出,对象中的成员变量是按照类声明中的顺序从对象的开始位置依次排列的

跟每个对象都有一份独立的成员变量不同,并不是每个对象都有一份独立的成员函数。因为同一个类的所有对象的成员函数实际上都是相同的,没有必要为每个对象都配备一份成员函数。在 C++类对象模型中,类的所有成员函数都被放在一个特殊的位置,所有这个类的对象都共用这份成员函数。换句话说就是,两个不同的类对象,拥有两份不同的成员变量,但却共用同一份成员函数

虚函数的内存

如果类中有虚函数存在,则情况会发生一点点变化。为了让对象能够在运行时刻找到并调用它自己重写的虚函数,在每个对象最开始的内存位置添加了一个指向其虚函数表的指针 vfptr,这个指针会占用 4 个字节的内存位置,其后才是对象的成员变量内存数据

虚函数表(Virtual Table)

虚函数表(Virtual Table),是类当中一块连续的内存区域, 它有多个内存单元, 每个内存单元中记录一个虚函数的跳转地址。每个拥有虚函数的类都有一个虚函数表,而这个虚函数表被它的多个对象所共享。运行时,如果使用父类指针调用虚函数,它会在指针实际所指对象的虚函数表中动态地查找这个虚函数的地址。如果这个对象是父类对象,其虚函数表中记录的自然是父类的虚函数地址,因此调用父类的虚函数。对应地,如果这个对象是子类对象,且子类重写了父类的虚函数,其虚函数表中的记录就变成了子类的虚函数地址,因此会调用子类的虚函数。 这样就做到了根据指针实际所指向的真实对象来调用函数, 实现了C++的多态机制。

派生类的内存

如果某个类是个派生类,那么它的对象内存中最开始的地方实际上是基类对象的一个拷贝,紧接着就是派生类自己的成员变量数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值