C++对象模型之内存布局(1)

了解C++类的内存布局以及成员函数是怎么调用,有助于理解C++多态的实现原理。


无多态对象布局

单个类:

定义一个类A

class A
{
public:
	A(int a1 = 0, int a2 = 0);
	~A();
	void A1();

protected:
	int a1;
	int a2;
};

如果类没有虚函数,那么class的布局和struct布局一样,只有成员变量,对象内存布局如下图:

 

继承类:

定义一个类B继承上面的类A

class B : public A
{
public:
	B(int a1 = 0, int a2 = 0, int b1 = 0);
	void B1();

protected:
	int b1;
};

子类的构造函数是先构造父类,再构造子类,所以B对象内存布局只要在A成员之后加上B的成员即可。

多态下对象的内存布局

单个类:

定义一个类,带有虚函数

class A
{
public:
	A(int a1 = 0, int a2 = 0);
	~A();
	virtual void A1();
	virtual void A2();
	virtual void A3();

protected:
	int a1;
	int a2;
};

 因为class A带有虚函数,所以A对象的内存布局要增加一个虚函数表指针,它是一个指针,指向类A的虚函数表。

单继承:

定义如下 

class B : public A
{
public:
	B(int a1 = 0, int a2 = 0, int b1 = 0);
	virtual void B1();
	virtual void B2();
	virtual void A1();

protected:
	int b1;
};

当父类有虚函数时,子类继承父类的虚函数表,而且虚函数的顺序是:先父类的虚函数,再子类的虚函数。当父类的虚函数被子类重写时,则虚函数表中的父类虚函数指针要替换为子类的虚函数。

 

使用VS2017查看对象内存布局如下: 

class A size(12):
        +---
 0      | {vfptr}
 4      | a1
 8      | a2
        +---

A::$vftable@:
        | &A_meta
        |  0
 0      | &A::A1
 1      | &A::A2
 2      | &A::A3

 

class B size(16):
        +---
 0      | +--- (base class A)
 0      | | {vfptr}
 4      | | a1
 8      | | a2
        | +---
12      | b1
        +---

B::$vftable@:
        | &B_meta
        |  0
 0      | &B::A1
 1      | &A::A2
 2      | &A::A3
 3      | &B::B1
 4      | &B::B2

 

验证:

为了验证之前的对象模型是否正确,查看一下下面的程序。 

#include <iostream>
using namespace std;

class A
{
public:
	A(int a1 = 0, int a2 = 0) {};
	~A() {};
	virtual void A1() { cout << "A::A1() " << endl; };
	virtual void A2() { cout << "A::A2() " << endl; };
	virtual void A3() { cout << "A::A3() " << endl; };

protected:
	int a1;
	int a2;
};

class B : public A
{
public:
	B(int a1 = 0, int a2 = 0, int b1 = 0) {};
	~B() {};
	virtual void B1() { cout << "B::B1() " << endl; };
	virtual void B2() { cout << "B::B2() " << endl; };
	virtual void A1() { cout << "B::A1() " << endl; };

protected:
	int b1;
};

typedef void(*pfun)(); //函数指针
int main()
{
	B *bp = new B;
	pfun fun = NULL;

	for (size_t i = 0; i < 5; i++)
	{
		fun = (pfun)*((long *)*(long*)bp + i);
		fun();
	}
	cout << *((long *)*(long*)bp + 5) << endl;
	system("PAUSE");
}

说明一下: 

(long*)bp,将对象的指针类型转换为(long*)类型,用于取出虚函数表的地址.

*(long*)bp,*为取出指针指向的值.此式子即虚函数表的地址,也就是第一个虚函数的地址.

(long*)*(long*)bp,将虚函数表的地址指针转换为(long*),用于后续迭代.

*(long*)*(long*)bp,*求指针值,即为第一个虚函数的地址,最后转换为pfun指针.

运行结果:

B::A1()
A::A2()
A::A3()
B::B1()
B::B2()
0
请按任意键继续. . . 

运行结果与上述一致。 

参考文档:https://mp.weixin.qq.com/s/LMJ4Hsa1hmued2egk9uWMQ

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值