Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++多态实现原理

C++多态实现原理

作者头像
gaigai
发布于 2021-03-06 14:28:51
发布于 2021-03-06 14:28:51
57800
代码可运行
举报
文章被收录于专栏:Windows开发Windows开发
运行总次数:0
代码可运行

系统学习Windows客户端开发

多态是C++的一个重要特性,本文将深入介绍C++编译器是如何实现C++多态特性。

先看一段涉及多态的代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
using namespace std;

class BaseClass
{
public:
    virtual ~BaseClass() {}
    virtual void VirtualFunction1() { 
        cout << "I am base virtual function 1" << endl; 
    }
    virtual void VirtualFunction2() { 
        cout << "I am base virtual function 2" << endl; 
    }
    void NonVirtualFunction() { 
        cout << "I am base non virtual function" << endl; 
    }
private:
    int m_nBaseField = 0;
};

class SubClass : public BaseClass 
{
public:
    virtual ~SubClass() {}
    virtual void VirtualFunction1() override { 
        cout << "I am subclass function 1, override base class" << endl; 
    }
    void NonVirtualFunction() { 
        cout << "I am subclass non virtual function" << endl; 
    }
private:
    int m_nSubClassField = 0;
};

int main()
{
    BaseClass baseClass;
    SubClass subClass;
    BaseClass* pBaseClass = &baseClass;
    pBaseClass->VirtualFunction1();
    pBaseClass = &subClass;
    pBaseClass->VirtualFunction1();
    pBaseClass->NonVirtualFunction();
    return 0;
}

如上示例代码,定义基类BaseClass,BaseClass定义了虚析构函数、虚函数VirtualFunction1、虚函数VirtualFunction2、非虚函数NonVirtualFunction。

然后定义子类SubClass继承于BaseClass,覆盖重写BaseClass的虚析构函数、虚函数VirtualFunction1、函数NonVirtualFunction,未覆盖重写BaseClass的虚函数VirtualFunction2。

Main函数定义了两个对象baseClass和subClass,定义了BaseClass指针pBaseClass,先让pBaseClass指向baseClass对象,调用VirtualFunction1,然后让pBaseClass指向subClass,调用VirtualFunction1和NonVirtualFunction。

下面是输出结果:

从输出结果知:

第一,pBaseClass指向baseClass对象,调用VirtualFunction1时,是调用BaseClass::VirtualFunction1。当pBaseClass指向subClass对象,调用VirtualFunction1时,是调用SubClass::VirtualFunction1。也就是说,pBaseClass同样调用VirtualFunction1,但是会因为pBaseClass指向的对象不同,而产生不同的行为(调用不同的方法),这就是多态。

第二,pBaseClass指向subClass对象,调用NonVirtualFunction时,调用的是BaseClass::NonVirtualFunction,而不是SubClass::NonVirtualFunction。

接下来将重点解释下这两个结论,首先让我们看下baseClass和subClass这两个对象的内存布局。

从上图可以发现,baseClass有一个数据成员__vfptr,__vfptr是为了实现多态,编译器在最前面隐式添加的一个指针,叫虚函数表指针,其指向虚函数表。baseClass虚函数表有三个元素,对应BaseClass定义的三个虚函数(虚构函数、VirtualFunction1、VirtualFunction2),这三个虚函数的地址是编译阶段编译器计算生成。

subClass继承于baseClass,内存布局上首先是baseClass的__vfptr和数据成员m_nBaseField,然后是SubClass定义的数据成员m_nSubClassField。subClass的__vfptr的值与baseClass的__vfptr的值不同,说明它们指向的虚函数表是不一样的。由于subClass没有新增虚函数,所以虚函数表也是三个元素与BaseClass的虚函数个数相同。但是,subClass覆盖重写了baseClass的虚析构函数和VirtualFunction1,所以第一和第二个元素的值不同,指向的是SubClass对应的虚函数。由于VirtualFunction2未被覆盖重写,所以第三个元素的值与BaseClass的虚函数表的第三个元素的值相同,都指向BaseClass::VirtualFunction2。

NonVirtualFunction不是虚函数,所以没有在虚函数表中。

接下来,我将结合虚函数表和汇编代码对上面的结论进行解释说明。

指针pBaseClass指向subClass对象,为什么调用NonVirtualFunction时不是调用subClass的NonVirtualFunction方法?这是因为pBaseClass是BaseClass*类型,NonVirtualFunction方法不是虚函数,所以它的调用地址在编译阶段,编译器就已经决定调用CBaseClass::NonVirtualFunction方法,我们从如下汇编代码知道call指令的地址固定为0A61519h。

指针pBaseClass指向subClass对象,为什么调用VirtualFunction1方法时,是调用subClass::VirtualFunction1呢?让我们先看下汇编代码。

首先,将指针pBaseClass的值(subClass对象的地址)放入寄存器eax。

其次,读取eax所指向的内存的值放入寄存器edx,也就是pBaseClass->__vfptr的值(虚函数表指针的值)。

再次,读取edx+4所指向的内存的值放入eax,也就是pBaseClass->__vfptr[1],也就是VirtualFunction1函数的地址。

最后,call eax,也就是call VirtualFunction1。

可见,调用虚函数和调用非虚函数有很大区别,调用虚函数时是在运行时期决定的,而调用非虚函数是在编译时期就决定了。调用虚函数时会查询虚函数表对应函数的地址,如果子类覆盖重写基类的虚函数,子类的虚函数表对应函数的地址就会被编译器更新为子类的函数。子类对象的地址可以赋值给基类(BaseClass* pBaseClass = &subClass),由于__vfptr数据成员是从基类继承,所以pBaseClass虽然指向SubClass对象,但是可以访问到__vfptr,而此时__vfptr指向SubClass的虚函数表,自然就调用SubClass的方法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Windows开发 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++进阶-多态
C++进阶-多态 零、前言 一、多态的概念和定义 二、虚函数 1、概念和定义 2、虚函数重写的特例 3、C++11 override 和 final 4、重载/重写/重定义对比 三、抽象类 四、多态的原理 1、虚函数表 2、多态的原理 3、动态绑定与静态绑定 4、多继承虚函数表 五、继承和多态常见的面试问题 零、前言 C++有五大特性:对象,封装,继承,抽象和多态。而本章则将学习讲解C++中的多态 一、多态的概念和定义 概念: 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会
用户9645905
2022/11/30
6320
C++进阶-多态
【C++】多态
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
zxctscl
2024/04/25
1160
【C++】多态
【C++】多态详细讲解
多态通俗来说就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态)。
羚羊角
2025/02/06
1070
【C++】多态详细讲解
C++从入门到精通(第九篇) :多态
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情
雪芙花
2022/10/31
4890
C++从入门到精通(第九篇) :多态
【C++修炼之路】16.C++多态
多态的概念: 通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
每天都要进步呀
2023/03/28
5310
【C++修炼之路】16.C++多态
【C++】多态
多态(polymorphism)的概念:通俗来说,就是多种形态。多态分为编译时多态(静态多态)和运⾏时多态(动态多态),这⾥我们重点讲运⾏时多态,编译时多态(静态多态)和运⾏时多态(动态多态)。编译时多态(静态多态)主要就是我们前⾯讲的函数重载和函数模板,他们传不同类型的参数就可以调⽤不同的 函数,通过参数不同达到多种形态,之所以叫编译时多态,是因为他们实参传给形参的参数匹配是在编译时完成的,我们把编译时⼀般归为静态,运⾏时归为动态。
用户11375356
2024/11/22
770
【C++】多态
多态与虚(函数)表
续接上回(继承),我们了解了继承是如何通过虚基表,来解决派生类和父类有相同的成员变量的情况,但是类和对象中可不只有成员变量,如果成员函数也有同名,更或者如果我们想在访问不同情况(类)但是相同函数名时,根据不同类满足不同需求。
比特大冒险
2023/04/16
6010
多态与虚(函数)表
【C++】多态
1. 多态是在继承的基础之上实现的,我们说继承是类设计层次的代码复用的一种手段,而多态则是在此基础上实现的多种形态,完成某一件事,可以由于对象的不同产生不同的完成结果,我们称这种现象为多态。
举杯邀明月
2023/04/12
5790
【C++】多态
C++ —— 关于多态
迷迭所归处
2024/11/19
830
C++ —— 关于多态
【c++】全面理解C++多态:虚函数表深度剖析与实践应用
多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价
用户11029103
2024/05/24
3770
【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“
同样有关运算符重载的知识这里也不再过多讲解,在类和对象中已经讲的很详细了,忘了就快去复习一下吧!! 👉 C++ 类和对象 进阶篇
换一颗红豆
2025/04/01
660
【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“
C++之多态
本文主要介绍了C++中面向对象三大特性之一的多态的相关概念。主要介绍了多态的原理,如何实现多态以及虚函数等相关概念。
摘星
2023/04/28
3760
C++之多态
【C++】多态(下)
被static修饰的变量a存放在静态区,局部变量b存储在栈区,指针p1指向在堆上开辟出的对象,常量字符串的指针存放在代码段
s-little-monster
2024/10/04
930
【C++】多态(下)
C++:多态
编译时多态主要就是函数重载和函数模板,传递不同类型的参数调用不同的函数,通过参数不同达到多种形态。至于叫做编译时多态的原因,是它们实参传给形参的参数匹配是在编译时完成的。
HZzzzzLu
2024/11/26
1210
C++:多态
【C++进阶】多态,这样学才够清楚
本篇文章带你深入学习面向对象设计思想的重要体现之一——多态。 多态在面向对象编程(OOP)中具有深远的意义,它不仅是OOP的三大特性之一,还是实现代码复用、提高程序灵活性和可扩展性的重要手段。
_小羊_
2024/10/16
1010
【C++进阶】多态,这样学才够清楚
【C++】模板/继承/多态
基类对象 < -派生类对象 类型从下到上的转换(可以) 派生类对象 <- 基类对象 类型从上到下的转换(不可以) 基类指针(引用)<- 派生类对象 类型从下到上的转换(可以) 派生类指针(引用)<-基类对象 类型从上到下的转换(不可以)
洁洁
2024/09/05
1620
C++多态
多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。
南桥
2024/08/07
1200
C++多态
今天你学C++了吗?——C++中的多态
具体来说,运行时多态允许我们在完成某个行为(函数)时,传入不同的对象就会完成不同的行为,从而达到多种形态。假设我们正在开发一个图形绘制应用程序,该程序需要支持多种图形对象(如圆形、矩形和三角形)的绘制。我们可以设计一个基类Shape,并在其中定义一个虚函数draw()用于绘制图形。不同的图形对象(圆形、矩形和三角形)将作为Shape的派生类,并在这些派生类中重写draw()函数以实现各自的绘制逻辑。同样地,在动物叫声的模拟中,传入猫对象时发出“喵”的叫声,传入狗对象时发出“汪汪”的叫声,这也是通过多态性来实现的。
用户11352420
2025/03/16
1540
今天你学C++了吗?——C++中的多态
【C++】看不懂多态?这篇文章带你吃透它!
​ 该内容中的代码以及解释都是在 vs2022 下的 x86 环境中,涉及的指针都是 4 个字节,如果要在其他的平台下运行,部分代码需要改动。
利刃大大
2025/02/03
790
【C++】看不懂多态?这篇文章带你吃透它!
C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)
注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承后在派生类依旧保持虚函数属性)但是该种写法不规范,大家还是少用为好。
是Nero哦
2024/03/17
6420
C++进阶:详解多态(多态、虚函数、抽象类以及虚函数原理详解)
相关推荐
C++进阶-多态
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验