返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++中的多态问题—理解虚函数表及多态实现原理
  • 620
分享到

C++中的多态问题—理解虚函数表及多态实现原理

C++中的多态C++虚函数表C++多态实现原理 2023-02-05 15:02:10 620人浏览 泡泡鱼
摘要

目录一、多态的概念概念构成条件二、虚函数的重写重写的定义重写的特殊情况override和final关键字区分重写、重载、重定义抽象类的概念三、多态的实现原理父类对象模型补充:生成默认

注:编译环境为VS 2022,指针大小为4字节

一、多态的概念

概念

多态,指完成某个行为,不同的对象去完成时会产生出不同的状态。如:定一个一Animal类,类中包含动物的叫声这种方法,分别定义Dog和Cat类继承自动物类,那么Dog和Cat类中也会包含叫声这种方法,但是他们具体实现是不同的,因为每种动物的声音都不相同,这便是一种多态。

多态的分类

  • 静态多态,也称为静态绑定或者早绑定,是指函数在编译期间就已经确定了函数的行为。函数重载、函数模板等都属于静态多态。
  • 动态多态,即动态绑定或者晚绑定,指程序在运行时才可以确定函数的行为。本文主要分析的是动态多态。

构成条件

  • 在继承体系下,父类中包含虚函数
  • 子类中对父类的虚函数进行重写
  • 通过父类的指针或者引用调用虚函数

多态的体现:不同的类对象调用同一函数,会产生不同的行为。

二、虚函数的重写

重写的定义

虚函数:virtual关键字修饰的函数

子类中有一个跟父类完全相同的虚函数,即返回值类型函数名形参列表都完全相同,则可以说子类重写了父类的虚函数。

Student类中重写了BuyTicket方法:

注意:只要父类中函数用virtual修饰即可,子类可以不加,且虚函数的重写与权限无关。

重写的特殊情况

协变——返回值类型不同

父类的虚函数返回父类对象的指针或者引用,子类虚函数返回子类对象的指针或者引用。

析构函数重写——父类与子类析构函数名字不同

如果父类的析构函数为虚函数,子类的析构函数只要定义了,都能与父类的析构函数构成重写。可以理解为编译器对析构函数的名字做了特殊处理,编译后析构函数的名字统一处理成destructor。

override和final关键字

这两个关键字的主要作用都是帮助用户检测是否构成重写

  • final:修饰虚函数,表示虚函数不可被重写;另外final也可以修饰类,表示该类不能被继承

  • override:修饰虚函数,检查子类虚函数是否重写了父类的虚函数,如果没有构成重写则会报错

区分重写、重载、重定义

抽象类的概念

在虚函数的后面写上=0,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也称接口类),抽象类不能实例化对象。抽象类被集成以后如果没有对虚函数进行重写,则继承的类也是抽象类。

一般情况下,抽象类必须被继承,且必须对虚函数进行重写,否则定义为抽象类则没有实际意义。

Shape类:

class Shape
{
public:
	// 纯虚函数
	virtual double GetArea() = 0;
	virtual double GetCircumference() = 0;
};

三、多态的实现原理

父类对象模型

给出一个Base类,一个Derived类继承Base类

class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Fun1c()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	virtual void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
public:
	int  _a;
};
class Derived:public Base
{
public:
	int _b;
};

父类对象模型:

总结:

  • 类中定义了虚函数以后,定义对象时,编译器会为对象创建一张虚表,并将一个指向这张虚表的指针保存在对象的前四个字节,无论定义几个虚函数,对象都只比多四个字节大小。这个指针称为函数虚表指针。
  • 虚表地址是在构造对象时进行填充的,构造函数如果显式实现,编译器会对用户实现的函数进行修改,增加给对象前四个字节存放虚表地址的语句。
  • 虚表本质就是一个函数指针数组,按照声明顺序依次存放虚函数的地址

补充:生成默认构造方法的场景

学习类与对象时我们知道构造函数是是类的默认成员函数,如果用户没有显式定义,编译器会默认生成,但是实际上并不是在所有情况下编译器都会生成默认的构造函数,编译器只会在需要的时候生成构造函数。

四种生成默认构造方法的场景

B类中包含有A类的对象,B类没有显式定义构造函数,A类定义了无参或者全缺省的构造方法,则编译器会给B类生成默认的构造方法。

分析:因为A类有无参或者全缺省的构造方法,需要在B类中调用A类的构造方法对A类成员进行初始化,所以需要生成B类的构造方法,在其初始化列表中调用A类构造方法。

如果A类没有显式定义构造函数,则不会生成B类构造方法,默认赋随机值;如果A类定义的构造方法不是无参或者全缺省的,则需要在初始化列表中对A类对象初始化: 

继承中,B继承A,A中定义了无参或者全缺省的构造方法,B未显式定义,则编译器会给B类生成默认的构造方法。将B中继承自A的部分初始化。

虚拟继承中,B类虚拟继承子A类,B类未显式定义构造方法,编译器会给B类生成默认的构造方法,目的是:给B类对象的前4个字节填充虚基表地址

类中包含虚函数,未显示定义构造方法,则编译器会自动生成构造方法,为对象的前4个字节填充虚表地址

子类对象模型

子类虚表构建规则

1.将父类虚表内容拷贝一份放到子类虚表中,注意父类和子类用的不是同一张虚表,仍以上面的Base和Derived类为例

可以看出,两个虚表指针的地址不同,但虚表中保存的虚函数的地址都相同

2.如果子类中将父类的虚函数进行了重写,则用子类的虚函数地址替换虚函数表中相同偏移量的虚函数的地址。

3.子类中增加的虚函数按照其在类当中的声明次序放在虚表的最后

子类中增加了两个虚函数:

但是由于VS监视窗口中无法显式新增加的子类,而内存窗口只能显式虚函数的地址,无法确认是哪个函数,所以这里通过打印的方式进行验证。

通过上图中程序的方式打印出了子类对象中虚函数的分布情况,在这里VFP是一个函数指针类型,前面加typedef表示为函数指针类型,如果不加,则是函数指针变量。

所以是用VFP*接收指向第一个虚函数指针的指针,p与*p的类型:

所以最终的结论是:子类新增的虚函数按照其在类中的声明次序放在虚函数表的最后。

子类对象的构造过程

构造子类对象时,在初始化列表中先调用父类的构造函数,此时对象的前4个字节保存的虚表指针指向父类的虚表,之后构造子类自己的虚表,虚表再指针指向子类的虚表。

总结

  • 虚表的本质是函数指针数组,在编译时生成
  • 虚函数的重写也叫覆盖,指的是虚表中虚函数的覆盖,重写是语法层的叫法,覆盖是原理层的叫法
  • 对象中保存的是虚表指针,虚表中保存的是虚函数指针,虚函数和普通函数一样保存在代码段,在VS中虚表也保存在代码区
  • 同一个类的对象共用同一张虚表,父类和子类各自拥有各自的虚表。

多态的调用原理

父类对象,函数调用时的汇编代码:

普通函数调用时直接传递函数的地址,这个地址在编译期间就确定了,虚函数则要经过虚表指针寻址等步骤。从上面的汇编代码也可以看出动态多态的晚绑定的特点,在编译期间普通函数的调用已经确定了要调用的具体函数,虚函数则无法确定,只有等程序运行起来,形参b是具体哪个对象确定了以后,才能确定要调用的函数的地址

上面是传递父类对象时的调用情况,子类对象调用时的汇编代码与父类对象相同,区别就是子类对象有自己的虚表,最终调用的是子类需表中的函数。

总结多态的原理:

创建对象时,编译器会给包含虚函数的类对象创建一张虚表,并将虚表地址填充在对象的前4个字节,子类对象会拷贝父类对象的虚表,然后再对自己重写的虚函数进行替换,并在虚表中添加子类新增的虚函数;函数调用时,编译器会先从对象的前4个字节获取该对象虚表的地址,然后在虚表中获取虚函数地址进行函数调用;由于每个类对象都有属于该类的一张虚表,且虚函数一般都进行了重写,即函数名与父类相同,但函数执行的内容不同,最终产生的结果就是,不同类的对象调用同一函数产生不同的结果,由此形成了多态。

多继承的虚函数表

给出两个父类Base1和Base2,Derived子类继承自两个父类

通多监视窗口查看子类对象的模型:

多态中多继承的子类对象模型与多继承的模型原理相同,但是VS的监视窗口无法查看子类新增的虚函数在需表中的位置,按照之前但继承中打印虚表中函数的原理进行打印:

最终得到的结果:

可以看出,子类中增加的虚函数保存在上面的虚表中。

多继承子类对象模型及对象虚表:

四、继承与多态中的常见问题

1.析构函数可以设置为虚函数吗?

可以,在继承体系中,最好将父类的析构函数设置为虚函数;如果子类中涉及到资源管理,则必须将父类的析构函数设置为虚函数,这样父类和子类中的析构函数便会构成重写(重写的特殊情况),形成多态,通过父类指针指向子类对象时,delete父类对象的指针也会调用子类的析构函数

子类中涉及资源管理,调用父类析构函数析构子类对象,则会有内存泄漏,如图:

2.构造函数可以设置为虚函数吗?

不能,虚函数是放在虚表中的,虚表指针是在构造方法的初始化列表中进行填充的,通过虚表指针才能找到虚函数,但是不调用构造方法就没有虚表指针,二者矛盾。即如果构造方法是虚函数,那么调用构造方法就要通过虚表指针,但是虚表指针是要通过调用构造方法才能填充的?。拷贝构造与构造函数原理相同。

3.赋值运算符重载函数可以设置为虚函数吗?

可以,但是没有意义,因为赋值运算符重载函数参数和返回值都是本类类型对象的引用,设置程序函数无法进行重写,无法构成多态。

4.静态函数可以设置为虚函数吗 ?

虚函数必须在创建对象后,通过对象的前4个字节的虚表指针调用。而静态成员函数可以通过 类名::成员函数 的方式进行调用,不用通过象,这样就无法找到虚表,也无法访问虚函数。

5.内联函数可以是虚函数吗?

可以设置,但是没有意义,因为虚函数关键字virtual和inline是矛盾的,inline属性会被忽略,函数不会展开,而是放到虚函数表中。

6.友元函数可以是虚函数吗?

不可以,因为virtual只能修饰类的成员函数。

7.为什么多态必须通过指针或者引用实现?

因为当我们用一个父类指针或者引用指向子类对象时,会发生内存切割,用子类中属于父类的部分给父类赋值:

	Animal& animal1 = dog;
 	Animal* animal2 = new Dog();

而下面的语句则不会产生内存切割:

ainmal animal3 = dog;

为什么会这样呢?

“一个pointer或一个reference之所以支持多态,是因为它们并不引发内存任何“与类型有关的内存委托操作; 会受到改变的。只有它们所指向内存的大小和解释方式 而已”。 ——《深度探索c++对象模型》

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: C++中的多态问题—理解虚函数表及多态实现原理

本文链接: https://lsjlt.com/news/194253.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C++中的多态问题—理解虚函数表及多态实现原理
    目录一、多态的概念概念构成条件二、虚函数的重写重写的定义重写的特殊情况override和final关键字区分重写、重载、重定义抽象类的概念三、多态的实现原理父类对象模型补充:生成默认...
    99+
    2023-02-05
    C++中的多态 C++虚函数表 C++多态实现原理
  • 详解C++虚函数中多态性的实现原理
    目录什么是虚函数多态性的实现多态的底层原理多态性的好处总结在面向对象的编程中,多态性是一个非常重要的概念。多态性意味着在不同的上下文中使用同一对象时,可以产生不同的行为。C++是一种...
    99+
    2023-05-19
    C++虚函数的多态性原理 C++虚函数 多态性 C++虚函数
  • 关于虚函数实现多态的原理及分析
    目录1、C++中如何实现多态2、虚函数实现多态的原理2.1 单类继承2.2 多类继承示例总结1、C++中如何实现多态 基类中先声明一个虚函数至少有一个继承该基类的子类 2、虚函数实现...
    99+
    2023-02-05
    虚函数 虚函数实现多态 多态的原理
  • C++ 多态虚函数的底层原理深入理解
    目录1 多态的基本概念1.1 什么是多态1.2 怎么实现多态2 虚函数的底层原理1 多态的基本概念 1.1 什么是多态 多态是在不同继承关系的类对象,去调用同一函...
    99+
    2022-11-13
    C++ 多态虚函数底层原理 C++ 多态虚函数
  • 【C++】多态的实现及其底层原理
    个人主页:🍝在肯德基吃麻辣烫 我的gitee:gitee仓库 分享一句喜欢的话:热烈的火焰,冰封在最沉默的火山深处。 文章目录 前言一、什么是多态?二、多态的构成条件2.1什么是虚函数?2.2虚函数的重...
    99+
    2023-08-17
    c++ 开发语言 多态
  • 深入解析C++中多态性和虚函数使用原理
    目录1.为什么需要virtual2.纯虚函数和抽象类3.构造函数与虚函数4.虚析构函数与纯虚析构函数5.纯虚析构函数的应用1.为什么需要virtual 按照Java的思维方式,在有了...
    99+
    2023-05-20
    C++ 多态性虚函数 C++ 多态性 C++ 虚函数
  • 详解C++中多态的底层原理
    目录前言1.虚函数表(1)虚函数表指针(2)虚函数表2.虚函数表的继承–重写(覆盖)的原理3.观察虚表的方法(1)内存观察(2)打印虚表(3)虚表的位置4.多态的底层过程...
    99+
    2024-04-02
  • C++学习之虚函数表与多态详解
    目录概述C++虚函数表指针和虚函数表C++ 虚函数表和多态概述 C++的多态在不同环境下实现方式可能不一样,虚函数表是C++实现多态的一种方式。 问题: 什么情况下C++会使用虚指针...
    99+
    2023-05-14
    C++虚函数表 多态 C++虚函数表 C++ 多态
  • C++中多态性实现问题及解决方法的讨论
    C++中多态性实现问题及解决方法的讨论多态性是C++语言中一项非常重要的特性,它使得一个类的对象可以根据其具体类型表现出不同的行为。然而,在实际的应用中,我们有时会遇到一些问题,特别是在多继承和虚析构函数的使用场景下。一、多态性的实现在C+...
    99+
    2023-10-22
    多态性 (Polymorphism) C++编程 (C++ programming) 实现问题 (Implementat
  • 深入解析C++中的虚函数与多态
    1.C++中的虚函数C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类...
    99+
    2022-11-15
    虚函数 多态
  • C++虚函数表与多态实例代码分析
    这篇文章主要介绍“C++虚函数表与多态实例代码分析”,在日常操作中,相信很多人在C++虚函数表与多态实例代码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++虚函数表与多态实例代码分析”的疑惑有所帮助!...
    99+
    2023-07-05
  • java中多态概念、实现原理详解
    一.什么是多态?1.多态的定义指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用)2.多态的作用消除类型之间的耦合关系3.多态的说明近代网络小说泛滥,我们可以用它来举一个例子...
    99+
    2023-05-31
    java 多态 ava
  • C++ 虚拟函数与继承:理解多态继承中的奥秘
    虚拟函数和继承在面向对象编程中实现多态性:声明虚拟函数允许派生类重写基类方法,并根据对象的运行时类型调用。继承建立类层次,派生类可访问和扩展基类数据和方法。多态继承允许派生类从多个基类继...
    99+
    2024-04-28
    c++ 多态
  • C++多态性的实现及常见问题分析
    C++多态性的实现及常见问题分析引言:多态性是面向对象编程语言的一个重要特性,在C++中也得到了广泛应用。多态性允许不同类型的对象以相同的方式进行处理,提高了代码的灵活性和可维护性。本文将介绍C++中多态性的实现方式,并分析常见的多态性问题...
    99+
    2023-10-22
    实现 C++关键词:多态性 常见问题分析
  • C++多态性的实现及常见问题解决方法
    C++多态性的实现及常见问题解决方法引言:在C++编程中,多态性是一种重要的概念和特性。它允许我们使用基类的指针或引用来操作派生类的对象,从而实现了程序的灵活性和复用性。本文将介绍C++中多态性的实现方式,并探讨一些常见的多态性问题及其解决...
    99+
    2023-10-22
    多态性 关键词:C++ 问题解决方法 实现:C++多态性问题
  • C++多态的实现与原理及抽象类实例分析
    这篇文章主要讲解了“C++多态的实现与原理及抽象类实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++多态的实现与原理及抽象类实例分析”吧!多态的概念多态: 从字面意思来看,就是事物...
    99+
    2023-06-29
  • C++的多态和虚函数实例分析
    这篇文章主要介绍了C++的多态和虚函数实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C++的多态和虚函数实例分析文章都会有所收获,下面我们一起来看看吧。一、C++的面试常考点阿里虽然是国内Java的第一...
    99+
    2023-06-29
  • C++的多态与虚函数你了解吗
    目录多态性虚函数总结多态性 多态性是面向对象程序设计的关键技术之一,若程序设计语言不支持多态性,不能称为面向对象的语言,利用多态性技术,可以调用同一个函数名的函数,实现完全不同的功能...
    99+
    2024-04-02
  • 深入了解C++的多态与虚函数
    目录1.多态的机制与虚函数的机制1.1 多态的机制1.2 虚函数的机制1.3虚函数表的结构图1.4 动态多态实现的三个前提件(很重要)2.多态实例应用3.多态的巨大问题与虚析构3.1...
    99+
    2024-04-02
  • C++中多态的定义及实现详解
    目录1.多态概念1.1概念2.多态的定义及实现2.1多态的构成条件2.2虚函数2.3虚函数的重写2.4代码示例2.4.1没构成重写2.4.2构成重写2.5虚函数重写的两个例外&nbs...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作