C++【对象模型】| 虚函数表 & 多态如何调用虚函数

C++ | 【03】类的【继承、多态、复合、委托】

一、虚函数表

当改类中存在虚函数或其基类含有虚函数,编译器将会添加一个虚函数表vptr

1、简介

只要在类中定义了virtual函数当创建对象时,编译器会自动给对象添加一个vptr指针,指向虚函数表;

  • 大小为4字节;
 #include <iostream>
 
 using namespace std;
 
 class A {
 	A() {}
 	~A(){}
 	
 	void func();	
 	int m_a;	
 }; 
 
 
 class B {
 	B() {}
 	~B(){}
 	
 	virtual void func(){};
 	int m_a;
 }; 
 
 int main() {
 	
 	cout << "class sizeof(A) no virtual func:" << sizeof(A) << endl;
 	cout << "class sizeof(B) with virtual func:" << sizeof(B) << endl;
 	return 0;
 }

在这里插入图片描述

2、虚函数

一个函数在类中声明时,在头部加上virtual即被声明为虚函数;

在这里插入图片描述

2.1类中虚函数与vpbl的关系

在这里插入图片描述

2.2 编译器是vtbl如何生成以及给定
1、继承开发环境:
	为每个可能需要vtbl的对象文件生成一个vtbl拷贝,连接程序后去除拷贝,最后在exe文件或程序库种为每一个vtbl保留一个
	实例;
2、启发式算法:
	要在一个对象文件生成一个类的vtbl,要求该文件包含该类的第一个非内联、非纯虚拟函数定义;
	由于非内联的限制,启发式算法会失败,故尽量避免把虚函数声明为内联函数;
2.3 虚函数与内联的关系
- 虚函数一般不能是内联;
- 由于内联式在编译期间用被调用的函数体本身来代替函数调用的指令;
- 而虚函数式直到运行时才知道要调用哪一个函数;
2.4 运行时类型识别 RTTI
- 能够让我们在运行时找到对象和类的有关信息【typeid】;
- 是被设计为在类的vtbl基础上实现的;
- 是在每个类的vtbl中的占用的额外单元在加上type_info对象的空间;

3、使用多态时如何通过指针找到对应虚函数

当虚继承时,一般说来,派生类地址和其虚基类地址之间的偏移量是不固定的,因为如果这个派生类又被进一步继承的话,最终派生类会把共享的虚基类实例数据放到一个与上一层派生类不同的偏移量处;

 #include <iostream>
 
 using namespace std;
 
class Animal{
public:
	virtual void do_sm(){
		
	};
	
	virtual void cry(){
		
	};
	virtual void fly(){
		
	};
	
private:
	string m_name;
}; 

class woodpecker : public Animal {
public:
	virtual void do_sm() override {
		cout << "woodpecker do ...." << endl;
	}
	virtual void cry() override {
		cout << "woodpecker cry ...." << endl;
	}
	virtual void fly() override {
		cout << "woodpecker fly ...." << endl;
	}
	void peck_wood() {
		cout << "woodpecker peck_wood ...." << endl;
	}
	
private:
	int m_weight;	
};

class  chicken : woodpecker {
public:
	virtual void do_sm() override {
		cout << "chicken do ...." << endl;
	}
	virtual void cry() override {
		cout << "chicken cry ...." << endl;
	}
	void lay_egg(){
		cout << "chicken lay_egg ...." << endl;
	}

private:
	string m_name;
	int m_canLayEggs;	
}; 
 
 int main() {
 	typedef void(*Func)(void);
 	
	woodpecker w;
	 
	int* obj = (int*)&w; 	// 转换指向对象的指针类型 
	int* vp = (int*)*obj;	// 获取虚表指针 

	Func f1 = (Func)vp[0]; // (*obj->vp[n])(p)
	Func f2 = (Func)vp[1];
	Func f3 = (Func)vp[2];
	f1();	// do_sm	
	f2();	// cry
	f3();	// fly
	
 	return 0;
 }

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jxiepc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值