单继承与多继承的虚函数表

我们大部分时候使用的多态虚表实在public继承的基础上实现的
这句话可以怎么说了?
自己简单的思考逻辑认为基类中的成员或者成员函数子类中都会重新拷贝一份,也就是如果派生类中没有定义基类的某虚函数重写但是这个**虚函数的地址已经被写实拷贝写了一份放到派生类·虚表当中了**

  • 好了,来看一下一种情况下的虚表情况吧!
  • 单继承虚表
class Base
{
public:
	virtual void func1()
	{ 
		cout << "Base::func1" << endl;
	}
	virtual void func2()
	{
		cout << "Base::func2" << endl;
	} 
private:  
	int a;
};

class Derive :public Base
{
public:   
	virtual void func1()
	{
		cout << "Derive::func1" << endl;
	}
	virtual void func3() 
	{
		cout << "Derive::func3" << endl;
	}
	virtual void func4()
	{
		cout << "Derive::func4" << endl;
	}
private:  
	int b;
};
int main()
{
	Base b;
	Derive d;
	return 0;
}
  • !!!看一下这里问题就出来了,和理论之中存在差别,基类子类当中的自己虚函数并没有显示出来
  • 这里是编译器的监视窗口故意隐藏了这两个函数, 也可以认为是他的一个小bug。那么我们如何查看d的虚表呢?下面我们使用代码打印出虚表中的函数。
    在这里插入图片描述
    模拟定义一个虚表指针,来把虚表元素打印出来
class Base
{
public:
	virtual void func1()
	{ 
		cout << "Base::func1" << endl;
	}
	virtual void func2()
	{
		cout << "Base::func2" << endl;
	} 
private:  
	int a;
};

class Derive :public Base
{
public:   
	virtual void func1()
	{
		cout << "Derive::func1" << endl;
	}
	virtual void func3() 
	{
		cout << "Derive::func3" << endl;
	}
	virtual void func4()
	{
		cout << "Derive::func4" << endl;
	}
private:  
	int b;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[]) 
{    
	cout << " 虚表地址>" << vTable << endl;   
	for (int i = 0; vTable[i] != nullptr; ++i) 
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);        
		VFPTR f = vTable[i];
		f();   
	}
	cout << endl;
}

int main()
{
	Base b;
	Derive d;
	VFPTR* vTableb = (VFPTR*)(*(int*)&b);   
	PrintVTable(vTableb);

	VFPTR* vTabled = (VFPTR*)(*(int*)&d);  
	PrintVTable(vTabled);

	return 0;
}

在这里插入图片描述

  • 思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数指针的 指针数组,这个数组最后面放了一个nullptr
  • 1.先取b的地址,强转成一个int*的指针
  • 2.再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针
  • 3.再强转成VFPTR*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。
  • 4.虚表指针传递给PrintVTable进行打印虚表
  • 5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最后面没有 放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再编译就好了。
  • 多继承虚表
class Base1 
{
public:  
	virtual void func1() 
	{
		cout << "Base1::func1" << endl; } 
	virtual void func2()
	{
		cout << "Base1::func2" << endl;
	}
private:
	int b1;
};

class Base2
{
public:   
	virtual void func1() 
	{
		cout << "Base2::func1" << endl;
	}
	virtual void func2() 
	{
		cout << "Base2::func2" << endl;
	}
private:
	int b2;
};

class Derive : public Base1, public Base2 
{
public:   
	virtual void func1()
	{
		cout << "Derive::func1" << endl;
	}
	virtual void func3() { 
		cout << "Derive::func3" << endl;
	}
private:    int d1;
};

typedef void(*VFPTR) (); 
void PrintVTable(VFPTR vTable[]) {
	cout << " 虚表地址>" << vTable << endl;    
	for (int i = 0; vTable[i] != nullptr; ++i)    
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);  
		VFPTR f = vTable[i];   
		f(); 
	} 
		
		cout << endl;
}

int main() {
	Derive d;

	VFPTR* vTableb1 = (VFPTR*)(*(int*)&d); 
	PrintVTable(vTableb1);

	VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));
	PrintVTable(vTableb2);

	return 0;
}

在这里插入图片描述
在这里插入图片描述
可以看出:多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值