c++对象模型02:单继承内存布局

单继承

派生类的定义

class Derive : public Base
{
public:
	Derive(int d) :Base(1000), DeriveI(d) {};
	//overwrite父类虚函数
	virtual void print(void) { cout << "Drive::print_overwrite"; }
	// Derive声明的新的虚函数
	virtual void Drive_print() { cout << "Drive::Drive_print_newwrite"; }
	virtual ~Derive() {}
private:
	int DeriveI;
};

继承类图

在这里插入图片描述

简单对象模型

在这里插入图片描述

简单对象模型的缺点就是因间接性导致的空间存取时间上的额外负担,优点则是类的大小是固定的,基类的改动不会影响子类对象的大小。

表格驱动对象模型

在表格驱动对象模型中,我们可以为子类对象增加第三个指针:基类指针(bptr),基类指针指向指向一个基类表(base class table),同样的,由于间接性导致了空间和存取时间上的额外负担,优点则是无须改变子类对象本身就可以更改基类。

c++对象模型

在C++对象模型中,对于一般继承(这个一般是相对于虚拟继承而言),若派生类重写(overwrite)了基类的虚函数,则派生类虚函数将覆盖虚表中对应的基类虚函数;若子类并没有重写overwrite基类虚函数,而是声明了自己新的虚函数,则该虚函数地址将扩充到虚函数表最后(在vs中无法通过监视看到扩充的结果,不过我们通过取地址的方法可以做到,派生类新的虚函数确实在基类的虚函数表末端)。虚函数表以0x0000000结束,类似字符串以 ’ \0 ’ 结束。

在这里插入图片描述

图中虚表位置的析构函数和重写后的print函数位置颠倒了

代码演示
typedef void(*Fun)(void);

int main()
{
	Base b(1000);
	//[0]
	cout << "[0]Base::vptr";
	cout << "\t地址:" << (int*)(&b) << endl;
	Fun funb0 = (Fun) * ((int*)*((int*)(&b)));
	//funb0();
	//析构函数无法通过地址调用,故手动输出
	cout << "  [0]" << "Base::~Base";
	cout << "\t地址:\t" << *((int*)*((int*)(&b))) << endl;
	cout << "  [1]";
	Fun funb1 = (Fun) * ((int*)*((int*)(&b)) + 1);
	funb1();
	cout << "\t地址:\t" << *((int*)*((int*)(&b)) + 1) << endl;

	cout << "[1]Base::baseI=" << *(int*)((int*)(&b) + 1);
	cout << "\t地址:" << (int*)(&b) + 1;
	cout << endl;
	cout << "---------------------------" << endl;

	Derive d(2000);
	//[0]
	cout << "[0]Base::vptr";
	cout << "\t地址:" << (int*)(&d) << endl;
	//vprt[0]

	Fun fun0 = (Fun) * ((int*)*((int*)(&d)));
	//fun0();
	//vprt[1]析构函数无法通过地址调用,故手动输出
	cout << "  [0]" << "Derive::~Derive";
	cout << "\t地址:\t" << *((int*)*((int*)(&d))) << endl;
	cout << "  [1]";
	Fun fun1 = (Fun) * ((int*)*((int*)(&d)) + 1);
	fun1();
	cout << "\t地址:\t" << *((int*)*((int*)(&d)) + 1) << endl;

	//vprt[2]
	cout << "  [2]";
	Fun fun2 = (Fun) * ((int*)*((int*)(&d)) + 2);
	fun2();
	cout << "\t地址:\t" << *((int*)*((int*)(&d)) + 2) << endl;


	//[1]
	cout << "[1]Base::baseI=" << *(int*)((int*)(&d) + 1);
	cout << "\t地址:" << (int*)(&d) + 1;
	cout << endl;
	//[2]
	cout << "[2]Derive::DeriveI=" << *(int*)((int*)(&d) + 2);
	cout << "\t地址:" << (int*)(&d) + 2;
	cout << endl;
	getchar();
}

在vs中,此处派生类对象的虚表中并没有新的虚函数,但是可以通过地址访问到新写的虚函数。

在这里插入图片描述

运行结果与对象模型中的描述相符合。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值