【C++ 第十章】继承:在单继承和多继承的情况下,子类中分别含有多少个虚表?

总结放在前面:

继承体系中就是有几个父类就有几张虚表

单继承只有一张虚表,继承下来的父类的虚表被用做子类的虚表了(或者说 继承的父类和子类共用一块虚表空间,父类的虚函数写在前面,子类的在后面)

多继承是有多少个父类就有多少虚表,其中第一个父类的虚表被用做子类的虚表的(原理和上面单继承的一样)

其中,子类新增的虚函数放在第一张虚表后面(即子类的虚表后面)

分四种情况演示:↓

下面我将使用程序打印 子类中虚表的内容:包括虚表本身的地址 与 虚表中各个虚函数的地址

目的是为了观察 子类的虚表中,父类的虚表和子类的虚表相对存储位置

声明:

1、我下面程序实在 32位系统下,指针大小为 4 位,因此直接将对象地址强转成 int* ,使其获取 前 4 个字节的数据(即一个int大小),虚函数表地址一般放在一个对象存储空间的开头,这4个字节直接就获取到 虚表地址了

2、关于为什么要定义函数指针及其使用介绍:因为虚函数表,是一个 函数指针 数组,因此要定义函数指针来取 该数组的元素,又因为该数组名就是该数组的首地址,指针的指针就是 (FunPtr*)

1.单继承无虚函数覆盖:

// 父类
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2" << endl;
	}

};


// 子类
class Derive :public Base
{
public:
	virtual void Func3()
	{
		cout << "Derive::Func3" << endl;
	}
	virtual void Func4()
	{
		cout << "Derive::Func4" << endl;
	}
};




typedef void(*FunPtr)();   // 定义函数 void Fuc() 的函数指针 :函数类型 void ()(....形参列表),函数指针位置在函数名的位置,void (  *p  )(....形参列表)
//打印虚函数表
void PrintVF(FunPtr* VFaddress)
{
	cout << "虚表地址:" << VFaddress << endl;
	int i = 1;
	FunPtr vfunc = VFaddress[0];
	while (vfunc != nullptr)
	{
		cout << "第 " << i << " 个虚函数地址:" << vfunc << endl;
		vfunc();
		cout << '\n';

		VFaddress++;
		vfunc = *VFaddress;
		i++;
	}
	cout << "\n\n";
}

int main()
{
	Base b;
	Derive d;

	cout << "父类虚表:" << "\n\n";
	PrintVF((FunPtr*)*(int*)&b);  
	// *(int*)&b :将对象b的头4个字节的数据取出,这就是对象 b 的虚表地址
	// 将虚表地址强转成函数指针类型 FunPtr*

	cout << "子类虚表:" << endl;
	PrintVF((FunPtr*)*(int*)&d);

	system("pause");
	return 0;
}

在对象的存储中,虚表指针地址在存储空间的开头

单继承时,子类中只有一个虚函数表,父类虚函数在前,子类虚函数在后,按照声明顺序存放。

子类和父类共用同一张虚表,父类指向这张虚表的开头,子类虚表指针也指向这张虚表开头

只是 父子类虚表指针可以访问的范围不同,父子类都被准许访问自己区域的数据


2.单继承有虚函数覆盖

// 父类
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2" << endl;
	}

};


// 子类
class Derive :public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1" << endl;
	}
	virtual void Func2()
	{
		cout << "Derive::Func2" << endl;
	}
};




typedef void(*FunPtr)();   // 定义函数 void Fuc() 的函数指针 :函数类型 void ()(....形参列表),函数指针位置在函数名的位置,void (  *p  )(....形参列表)
//打印虚函数表
void PrintVF(FunPtr* VFaddress)
{
	cout << "虚表地址:" << VFaddress << endl;
	int i = 1;
	FunPtr vfunc = VFaddress[0];
	while (vfunc != nullptr)
	{
		cout << "第 " << i << " 个虚函数地址:" << vfunc << endl;
		vfunc();
		cout << '\n';

		VFaddress++;
		vfunc = *VFaddress;
		i++;
	}
	cout << "\n\n";
}

int main()
{
	Base b;
	Derive d;

	cout << "父类虚表:" << "\n\n";
	PrintVF((FunPtr*)*(int*)&b);  
	// *(int*)&b :将对象b的头4个字节的数据取出,这就是对象 b 的虚表地址
	// 将虚表地址强转成函数指针类型 FunPtr*

	cout << "子类虚表:" << endl;
	PrintVF((FunPtr*)*(int*)&d);

	system("pause");
	return 0;
}



子类中有一个虚函数表,覆盖的虚函数放在虚函数表中继承于父类的位置,新增的虚函数放在继承的父类虚表的后面。(即 重写的虚函数 直接放到继承的父类虚表 后面,共用同块空间)


3.多继承没有虚函数重写:

多继承时,子类中虚表个数等于直接父类的个数,子类中新增的虚函数放在第一个父类虚表末尾,按照声明顺序存放。

class Base1
{
public:
	virtual void Func1()
	{
		cout << "Base1::Func1" << endl;

	}
	virtual void Func2()
	{
		cout << "Base1::Func2" << endl;

	}

};

class Base2
{
public:
	virtual void Func1()
	{
		cout << "Base2::Func1" << endl;

	}
	virtual void Func2()
	{
		cout << "Base2::Func2" << endl;

	}

};

class Derive :public Base1, Base2
{
public:

	virtual void Func3()
	{
		cout << "Derive::Func3" << endl;

	}
	virtual void Func4()
	{
		cout << "Derive::Func4" << endl;

	}

};


typedef void(*FucPtr)();
void PrintVF(FucPtr* VFaddress)
{
	cout << "虚表地址:" << VFaddress << endl;
	int i = 1;
	FucPtr vfunc = VFaddress[0];
	while (vfunc != nullptr)
	{
		cout << "第" << i << "个虚函数地址:" << vfunc << endl;
		vfunc();
		VFaddress++;
		vfunc = *VFaddress;
		i++;
	}
	cout << '\n';
}


int main()
{

	Base1 b1;
	Base2 b2;
	Derive d;

	cout << "父类虚表(Base1):" << endl;
	PrintVF((FucPtr*)*(int*)&b1);
	

	cout << "父类虚表(Base2):" << endl;
	PrintVF((FucPtr*)*(int*)&b2);

	cout << "Derive ::Base1:" << endl;
	PrintVF((FucPtr*)*(int*)&d);

	cout << "Derive ::Base2:" << endl;
	PrintVF((FucPtr*)(*(int*)((char*)&d + sizeof(Base1))));

	system("pause");
	return 0;
}

4.多继承有虚函数重写时

多继承有虚函数重写时,子类中虚表个数等于直接父类的个数,子类中重写的虚函数覆盖继承的父类的虚函数,子类中新增的虚函数放在第一个直接父类的虚表末尾,按照声明顺序存放

参考文章:

c++中的虚函数及虚函数表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值