深度探索C++对象模型(20)——函数语义学(4)——多继承第二基类对虚函数支持的影响、虚继承下的虚函数

1.多继承第二基类对虚函数支持的影响

子类继承了几个父类,子类就有几个虚函数表

this指针调整的目的就是让对象指针正确的指向对象首地址,从而能正确的调用对象的成员函数或者说正确确定数据成员的存储位置。

多重继承下,有几种情况,第二个或者后续的基类会对虚函数的支持产生影响,即需要调整this指针

a)通过指向第二个基类的指针调用继承类的虚函数;

b)一个指向派生类的指针,调用第二个基类中的虚函数

c)允许虚函数的返回值类型有所变化

代码:

#include <iostream>
#include <stdio.h>

using namespace std;

class Base
{
public:
	virtual void f() { cout << "Base::f()" << endl; }
	virtual void g() { cout << "Base::g()" << endl; }
	virtual void h() { cout << "Base::h()" << endl; }
	virtual ~Base() {
		
	}

	virtual Base *clone() const
	{
		return new Base();
	}
};

class Base2
{
public:
	virtual void hBase2() {

		cout << "Base2::hBase2()" << endl;
	}

	virtual ~Base2() {

	}

	virtual Base2 *clone() const
	{
		return new Base2();
	}
};

class Derive :public Base, public Base2 {
public:
	virtual void i() { cout << "Derive::i()" << endl; }
	virtual void g() { cout << "Derive::g()" << endl; }
	void myselffunc() {} //只属于Derive的函数
	virtual ~Derive() {
		
	}

	virtual Derive *clone() const
	{
		return new Derive();
	}
};

int main()
{
	//a)通过指向第二个基类的指针调用继承类的虚函数;
	Base2 *pb1 = new Derive();
	delete pb1; //调用子类的虚析构函数,需要进行this指针调整

	//b)一个指向派生类的指针,调用第二个基类中的虚函数
	Derive *pb2 = new Derive();
	pb2->hBase2();   //调用第二或后续基类的虚函数,需要this指针调整到该类子对象首地址,然后通过虚函数表进行正确的调用

	//c)允许虚函数的返回值类型有所变化
	Base2 *pb3 = new Derive(); //pb3指向的是Base2子对象的首地址
	Base2 *pb4 = pb3->clone(); //Derive::clone();
	         //执行clone()时,pb3首先会调整回指向Derivce对象的首地址,这样调用的是Derive版本的clone()
			//执行Derive版本的clone(),返回的是Derive*,又进行this指针调整指向Base2
}

2.虚继承下的虚函数

代码:

#include <iostream>
#include <stdio.h>

using namespace std;

class Base
{
public:
	virtual void f() {}
	virtual ~Base() {}
	int m_basei;
};

class Derive :public virtual Base
{
public:
	virtual ~Derive() {}
	int m_derivei;
};

int main()
{
	cout << sizeof(Derive) << endl;  //16
	Derive dobj;
	dobj.m_basei = 2;
	dobj.m_derivei = 5;

	Derive *pdobj = new Derive();
	pdobj->f();

	Derive *pderive = new Derive();
	Base *pbase2 = (Base *)pderive;
	pbase2->m_basei = 7;

	cout << "pderive = " << pderive << endl;
	cout << "pbase2 = " << pbase2 << endl;
}

运行结果:

内存详情查看:

对象dobj内存中0x00cc9b4c和0x00cc9b44,一个是虚基类表指针,一个是虚函数表指针

调用f()的时候call eax,即是在调用f(),eax是虚函数f()的首地址,此时eax为:

查看地址0x00cc9b44内容:

前四个字节正好是eax的值,即虚函数f()的首地址,所以0x00cc9b44是虚函数表指针,则0x00cc9b4c是虚基类表指针

综合这里的内存详情和运行结果,可以得出以下

内存布局:

如果没有虚继承,在单重继承中,pderive和pbase2应该指向相同的地址,但是有了虚继承之后内存布局顺序就被打乱了

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值