【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定

这篇具有很好参考价值的文章主要介绍了【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

梳理虚函数表、多态原理、动静态绑定的知识

目录

一、虚函数表

二、多态的原理

三、动态绑定和静态绑定


一、虚函数表

在学习多态原理之前,我们需要了解一下虚函数表的概念 

我们先一起来看下下面这段代码

// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};

通过测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr指针放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表

虚函数表指针和虚表的关系我们在后文中会详细讲解。

【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定

 

我们先来学习一下虚表的知识,这个派生类中的表(虚表)究竟是什么呢?它里面又放了些什么呢?我们接着往下分析

// 针对上面的代码我们做出以下改造
// 1.我们增加一个派生类Derive去继承Base
// 2.Derive中重写Func1
// 3.Base再增加一个虚函数Func2和一个普通函数Func3
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};

class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};

int main()
{
	Base b;
	Derive d;
	return 0;
}

通过观察和测试,我们发现了以下几点问题:

1. 派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,其中包括虚表指针和父类的成员变量,另一部分是自己的成员。

2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。

3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函数,所以不会放进虚表。

4. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。

5. 总结一下派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中;b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数;c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。

6. 这里还有几个很容易混淆的问题:虚函数存在哪的?虚表存在哪的?虚函数是不是存在虚表中?虚表是不是存在对象里? 答:注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数是一样的,都是存在代码段的,只是他的指针又存到了虚表中另外对象中存的不是虚表,存的是虚表指针那么虚表存在哪的呢?实际我们去验证一下会发现,虚表在vs下是存在于代码段的。

【总结】对象->虚表指针->虚表(虚函数指针的指针数组)->虚函数指针->虚函数。

【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定

 
 


二、多态的原理

通过上面的学习,我们对虚表有了大概的了解。那么多态的原理到底是什么?和虚表又有什么样的关系呢?接下来让我们一起来看下面的图例,这里Func函数传Person调用的Person::BuyTicket,传Student调用的是Student::BuyTicket

【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定

代码如下

class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
};

void Func(Person& p)
{
	p.BuyTicket();
}

int main()
{
	Person Mike;
	Func(Mike);
	Student Johnson;
	Func(Johnson);
	return 0;
}

运行上面的代码并打开监视窗口,我们可以得到

【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定

1. 观察下图的红色箭头我们看到,p是指向mike对象时,p->BuyTicket在mike的虚表中找到虚函数是Person::BuyTicket。

2. 观察下图的蓝色箭头我们看到,p是指向johnson对象时,p->BuyTicket在johson的虚表中找到虚函数是Student::BuyTicket。

3. 这样就实现出了不同对象去完成同一行为时,展现出不同的形态。

4. 反过来思考我们要达到多态,有两个条件,一个是虚函数覆盖,一个是要对象(基类)的指针或引用调用虚函数。有兴趣的小伙伴可以思考一下为什么?答案就是虚函数覆盖才能输出不同的结果,通过基类的指针或引用调用虚函数,才能准确完成下面的流程:对象->虚表指针->虚表->虚函数指针->虚函数。

5. 再通过下面的汇编代码分析,我们可以得出,满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的。不满足多态的函数调用时编译时确认好的。

//多态调用
void Func(Person* p)
{
	p->BuyTicket();
}
int main()
{
	Person mike;
	Func(&mike);
	mike.BuyTicket();

	return 0;
}

下面展示的是多态调用的反汇编 

// 以下汇编代码中与问题不相关的都被去掉了
void Func(Person* p)
{
	...
	p->BuyTicket();
	// p中存的是mike对象的指针,将p移动到eax中

	001940DE  mov         eax, dword ptr[p]
	// [eax]就是取eax值指向的内容,这里相当于把mike对象头4个字节(虚表指针)移动到了edx

	001940E1  mov         edx, dword ptr[eax]
	// [edx]就是取edx值指向的内容,这里相当于把虚表中的头4字节存的虚函数指针移动到了eax

	00B823EE  mov         eax, dword ptr[edx]

    001940EA  call        eax
    // call eax中存虚函数的指针。这里可以看出满足多态的调用,不是在编译时确定的,是运行起来
	//以后到对象的中取找的。
	
    001940EC  cmp         esi, esp
}

下面展示的是普通函数调用的反汇编 

//普通函数调用
int main()
{
	...
	// 首先BuyTicket虽然是虚函数,但是mike是对象,不满足多态的条件,所以这里是普通函数
	// 普通函数的调用转换成地址时,是在编译时已经从符号表确认了函数的地址,直接call 地址
	mike.BuyTicket();
	00195182  lea         ecx, [mike]
	00195185  call        Person::BuyTicket(01914F6h)
	...
}

三、动态绑定和静态绑定

1. 静态绑定又称为前期绑定(早绑定),在程序编译期间确定了程序的行为,也称为静态多态,比如:函数重载。

2. 动态绑定又称后期绑定(晚绑定),是在程序运行期间,根据具体拿到的类型确定程序的具体行为,调用具体的函数,也称为动态多态。【我们接触到的多态多为动态多态】。

3. 第二节中展示的反汇编代码汇编代码很好的解释了什么是静态(编译器)绑定和动态(运行时)绑定。

满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的,因此它为动态绑定。而一般的函数调用,在程序编译期间确定了程序的行为,是静态绑定。


以上就是本篇文章的所有内容,如果对您有帮助,不妨点赞、收藏、关注,感谢您的阅读。文章来源地址https://www.toymoban.com/news/detail-436176.html

到了这里,关于【C++】虚函数表 & 多态的原理 & 动态绑定和静态绑定的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • <c++>虚函数与多态 | 虚函数与纯虚函数 | 多态的实现原理 | 虚析构函数

    🚀 个人简介:CSDN「 博客新星 」TOP 10 , C/C++ 领域新星创作者 💟 作    者: 锡兰_CC ❣️ 📝 专    栏: 从零开始的 c++ 之旅 🌈 若有帮助,还请 关注➕点赞➕收藏 ,不行的话我再努努力💪💪💪 在上一篇文章中,我们介绍了 c++ 中类与对象的继承,继承可以根据一个或

    2023年04月18日
    浏览(38)
  • 【C++】多态的底层原理(虚函数表)

    🌏博客主页: 主页 🔖系列专栏: C++ ❤️感谢大家点赞👍收藏⭐评论✍️ 😍期待与大家一起进步! 通过观察测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函

    2024年02月13日
    浏览(37)
  • C++:多态的底层实现原理 -- 虚函数表

    目录 一. 多态的原理 1.1 虚函数表 1.2 多态的实现原理 1.3 动态绑定与静态绑定 二. 多继承中的虚函数表 2.1 虚函数表的打印 2.2 多继承中虚函数表中的内容存储情况 对于一个含有虚函数的的类,在实例化出来对象以后,对象所存储的内容包含两部分: 类的成员变量。 一

    2023年04月21日
    浏览(37)
  • C++类和对象-多态->多态的基本语法、多态的原理剖析、纯虚函数和抽象类、虚析构和纯虚析构

    #includeiostream using namespace std; //多态 //动物类 class Animal { public:     //Speak函数就是虚函数     //函数前面加上virtual,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。     virtual void speak()     {         cout \\\"动物在说话\\\" endl;     } }; //猫类 class Cat

    2024年02月20日
    浏览(37)
  • 【C++庖丁解牛】面向对象的三大特性之一多态 | 抽象类 | 多态的原理 | 单继承和多继承关系中的虚函数表

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 需要声明的,本节课件中的代码及解释都是在vs2013下的x86程序中,涉及的指针都是4bytes。如果要其他平台

    2024年04月10日
    浏览(55)
  • 【C++】早绑定、析构与多态 | 一道关于多态的选择题记录

    今天在和群友聊天的时候看到了一道很坑的题目,分享给大家 先来看看题目 请问这个的输出是什么? 答案是E,编译出错! 先来说说这道题目里面涉及到了什么知识点 多态调用; 多态重写函数需要满足什么条件; 类内函数后加 const 的作用; 类内函数后加 override 的作用;

    2024年02月11日
    浏览(35)
  • 思科交换机端口动态、静态安全绑定案例

    1、该端口不是聚合端口 2、该端口是access模式 3、配置前要关闭该端口(shutdown) 违规操作:(没有加入端口安全的PC机做一下操作) 1、给没有加入到安全端口的PC进行IP地址配置(增加1次) 2、进行ping操作第一次增加5次,下一次增加4次 查看当前访问过得IP地址信息 Switch(

    2023年04月08日
    浏览(47)
  • 华为(huawei)三层交换的ip绑定mac地址配置示例(静态及动态dhcp绑定)

    在某些需要较高网络安全性的单位中,为了网络安全的考虑有时候会采取IP绑定mac地址的这类安全措施。一方面防止内部人员擅自改动接入IP(可能导致IP冲突或者联不上网络),另一方面也可以防止ARP欺骗。 拓扑图 拓扑说明: HX_SW为核心交换机,SW1和SW2分别为业务网段和服

    2024年02月05日
    浏览(42)
  • 【C++】多态及原理

    1.多态的概念 多态,顾名思义就是多种状态, 具体点就是去完成某种行为,但是通过不同的对象去完成某种行为都会产生不同的状态,这就是多态 比如买票这个行为。当 普通人 买票时,是全价买票; 学生 买票时,是半价买票; 军人 买票时是优 先买票。 这就是不同的对象

    2024年02月16日
    浏览(33)
  • C++中多态的原理

    上篇讲解了多态的原理,这篇文章来详细讲解一下多态的原理。 这里有一道常考笔试题:sizeof(Base)是多少? 为什么不是8? 可以调试带大家看一下。 仔细看,对象的头部多了一个指针。 这个指针叫做虚函数表指针。 上面不重要,重要的是下面的东西,多态的原理。 这个指

    2024年02月04日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包