C++中的虚函数的作用

C++中虚函数的原理和作用 本文档详细介绍了C++中虚函数的原理以及在何场景下要用到虚函数。 立即下载
       纯虚函数主要是你不知道这个函数如何定义,即你无法为其写出定义部分,这部分只有在子类中才能够正确表达,比如,我们要定义一个动物类,那它的呼吸方法怎么定义呢,是像鱼那样,还是像人那样,这个没法确定,只能在子类中去定义。
       而虚函数则是肯定知道这个函数的定义,并且大部分子类对这方法的定义应该是差不多的,比如我们要定义一个树类,树都可以进行光合作用,大部分树其进行光合作用的方法都差不多吧,我们就可以将这个方法定义成虚函数,所在大部分的子类都不用再定义这个方法了,而只有部分特别的子类需要定义一下这个方法。
       关于虚函数的使用方法,我在这里不做过多的阐述。大家可以看看相关的C++的书籍。在这篇文章中,我只想从虚函数的实现机制上面为大家一个清晰的剖析。
当然,相同的文章在网上也出现过一些了,但我总感觉这些文章不是很容易阅读,大段大段的代码,没有图片,没有详细的说明,没有比较,没有举一返三。不利于学习和阅读,所以这是我想写下这篇文章的原因。也希望大家多给我提意见。


 言归正传,让我们一起进入虚函数的世界。

虚函数表
       对C++了解的人都应该知道虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。简称为V-Table。在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承、重载的问题,保证其容真实反应实际的函数。这样,在有虚函数的类的实例中这个表被分配在了这个实例的内存中,所以,当我们用父类的指针来操作一个子类的时候,这张虚函数表就显得由为重要了,它就像一个地图一样,指明了实际所应该调用的函数。
       这里我们着重看一下这张虚函数表。在C++的标准规格说明书中说到,编译器必需要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
       听我扯了那么多,我可以感觉出来你现在可能比以前更加晕头转向了。 没关系,下面就是实际的例子,相信聪明的你一看就明白了。

假设我们有这样的一个类:

class Base
{
public:virtual void f() 
	   { cout << "Base::f" << endl;}
	   virtual void g() 
	   { cout << "Base::g" << endl;}
	   virtual void h() 
	   { cout << "Base::h" << endl;}
};

按照上面的说法,我们可以通过Base的实例来得到虚函数表。下面是实际例程:

	typedef void(*Fun)(void);
	Base b;
	Fun pFun = NULL;
	cout << "虚函数表地址:" << (int*)(&b) << endl;
	cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;// Invoke the first virtual function 
	pFun = (Fun)*((int*)*(int*)(&b));
	pFun();

实际运行经果如下:(Windows XP+VS2003, Linux 2.6.22 + GCC 4.1.3)

虚函数表地址:0012FED4

虚函数表 — 第一个函数地址:0044F148

Base::f


       通过这个示例,我们可以看到,我们可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f(),这在上面的程序中得到了验证(把int* 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调用Base::g()和Base::h(),其代码如下:

(Fun)*((int*)*(int*)(&b)+0); // Base::f()

(Fun)*((int*)*(int*)(&b)+1); // Base::g()

(Fun)*((int*)*(int*)(&b)+2); // Base::h()


       这个时候你应该懂了吧!什么?还是有点晕。也是,这样的代码看着太乱了。

       注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值是如果1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

       下面,我将分别说明“无重载”和“有重载”时的虚函数表的样子。没有重载父类的虚函数是毫无意义的。我之所以要讲述没有重载的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。
一般继承(无虚函数重载)

我们可以看到下面几点:

1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。

我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。

一般继承(有虚函数重载)

我们从表中可以看到下面几点,

1)重载的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被重载的函数依旧。

这样,我们就可以看到对于下面这样的程序,

Base *b = new Derive();

b->f();



由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

多重继承(无虚函数重载)

我们可以看到:

1)每个父类都有自己的虚表。
2)子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)

这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

多重继承(有虚函数重载)

下面我们再来看看,如果发生虚函数重载的情况。 

我们可以看见,三个父类虚函数表中的f()的位置被替换成了子类的函数指针。这样,我们就可以任一静态类型的父类来指向子类,并调用子类的f()了。如:

Derive d;

Base1 *b1 = &d;

Base2 *b2 = &d;

Base3 *b3 = &d;

b1->f(); //Derive::f()

b2->f(); //Derive::f()

b3->f(); //Derive::f() 

b1->g(); //Base1::g()

b2->g(); //Base2::g()

b3->g(); //Base3::g()


安全性

每次写C++的文章,总免不了要批判一下C++。这篇文章也不例外。通过上面的讲述,相信我们对虚函数表有一个比较细致的了解了。水可载舟,亦可覆舟。下面,让我们来看看我们可以用虚函数表来干点什么坏事吧。

1、通过父类型的指针访问子类自己的虚函数

       我们知道,子类没有重载父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重载的。虽然在上面的图中我们可以看到Base1的虚表中有Derive的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:

Base1 *b1 = new Derive();b1->f1(); //编译出错

       任何妄图使用父类指针想调用子类中的未重载父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。(关于这方面的尝试,通过阅读后面附录的代码,相信你可以做到这一点)


2、访问non-public的虚函数

另外,如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数,这是很容易做到的。

如:

class Base {

private:virtual void f() { cout << "Base::f" << endl; }

};

class Derive : public Base{};

typedef void(*Fun)(void);

void main() 

{

Derive d;

Fun pFun = (Fun)*((int*)*(int*)(&d)+0);

pFun();

}

RaiDrive+AList保姆级教程:5分钟搞定OneDrive/百度网盘挂载到本地(附WebDAV配置) 本文提供RaiDrive+AList的保姆级教程,5分钟内实现将OneDrive/百度网盘挂载到本地的详细步骤,包括WebDAV配置。通过AList统一转换云存储协议,RaiDrive虚拟磁盘挂载,显著提升多网盘管理效率,适合需要跨平台操作文件的用户。 阅读详情

相关推荐

医学影像弹性形变处理

这段代码的主要流程是:读取 DICOM 医学影像,尝试使用 MONAI 进行弹性形变(根据版本适配),如果失败则使用 OpenCV 实现,最后可视化并保存处理结果。这样可以在不同环境下灵活处理医学影像的弹性形变需求。后面会写一篇解释弹性变形方法的短文。

wh_xia_jun的专栏 779

虚函数的作用及使用方法

转载自:http://c.biancheng.net/cpp/biancheng/view/244.html 我们知道,在同一类中是不能定义两个名字相同、参数个数和类型都相同的函数的,否则就是“重复定义”。但是在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。 打个比方,假设在Circle类中定义了 area函数,在Circle类的派生类Cylinde...

20164225的博客 1万+

windows10下 iperf3测试带宽

客户端输入:"iperf3.exe -u -c 服务器IP地址 -b 5g" //客户端给服务器发送udp数据包,最大带宽为5g。客户端输入:“iperf3.exe -c 服务器IP地址 -P 10“//表示客户端10路并行给服务器发送TCP包;上图可知:带宽是15Gbps(这里不知道为什么下面测试的UDP会比这里TCP慢 按道理UDP不可能比TCP慢的5.)3. 验证是否下载成功,在打开的窗口中输入“iperf3.exe -v”,会出现安装的版本,表示安装成功了。5. iperf3参数。

xiaoqixiaoguai的博客 5890

C++中虚函数的作用?

虚函数简明理解!

不积跬步,无以至千里! 2571

学Simulink——基于软开关与高频变换的整流器系统场景实例:移相全桥ZVS整流器在轻载条件下的效率优化

本文以Simulink为平台,详细介绍了移相全桥ZVS整流器在轻载条件下的建模与优化方法。通过六个步骤系统讲解了从项目创建到仿真运行的全过程,重点分析了移相全桥拓扑结构、ZVS检测逻辑设计及轻载特性。结果显示,传统移相全桥在轻载时ZVS失效导致效率下降(<88%)。针对此问题,提出了动态频率调整、不对称半桥模式等优化策略。该案例为理解软开关技术、提升电源效率提供了实用建模方法和优化思路。

xiaoheshang_123的博客 117

【异常】OpenClaw LLM请求超时故障全链路排查与解决方案 LLM request timed out. 网络连接超时了

摘要: OpenClaw LLM请求超时故障是高频网络交互问题,表现为客户端在预设时间内未收到服务端响应。根因分为四类:客户端配置缺陷(如超时阈值不合理)、网络链路异常(防火墙拦截等)、服务端问题(限流/宕机)及硬件瓶颈(算力不足)。解决方案分三步: 通用排查:检查网络连通性、关闭防火墙/代理校验,调整超时阈值至60-300秒; 分场景修复:云端API需核对密钥/限流规则,本地部署需验证服务状态与硬件资源; 兜底方案:重启程序、更新版本或更换服务端点,结合日志精准定位。修复后不影响OpenClaw数据完整性

奶爸的编程之路,也就一周冷个三天 2058

C++中虚函数的作用是什么?它应该怎么用呢?

C++中虚函数的作用: 1、简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。 2、实现多态性,多态性是将接口与实现进行分离。 3、当基类指针指向一个子类对象,通过这个指针调用子类和基类同名成员函数的时候,基类声明为虚函数就会调子类的这个函数,不声明就会调用基类的。 C+...

测试0901-1 1228

Skills入门学习路线

简单来说,Skills就像是给Claude准备的”插件包"或”工具箱”。它是一个集合,里面装了特定的操作指令(Instructions)、可执行脚本(Scripts)和相关素材(Resources)。2025 年 10 月中旬,Anthropic 正式发布 Claude Skills。两个月后,Agent Skills 作为开放标准被进一步发布,意在引导一个新的 AI Agent 开发生态。OpenAI、Github、VS Code、Cursor 均已跟进。为了更好的理解,你可以把。

十二测试录 1098

C++虚函数的作用

借鉴了很多博主的文章,我也通过整理资料和博主们做对比,用通俗的语言分析给大家

weixin_46068274的博客 4800

Android 14.0 Launcher3定制化之最近任务的全部清除由左边移到下边显示

在最近14.0的系统rom产品开发中,在Launcher3的定制化开发中,在最近任务列表中,发现点击recents最近任务键后 显示的全部清除按键在左边 由于是横屏的产品显示在左边不太合理 所以要求显示在下边比较合理,所以要从Launcher3的显示流程来解决这个问题

安卓兼职framework和app工程师的博客 764

深入解析雪球网股票行情数据爬取:基于WebSocket与API的实时数据获取技术实践

本文详细介绍了如何使用Python爬虫技术获取雪球网的股票行情数据,涵盖了WebSocket实时数据连接和RESTful API接口调用两种主要技术方案。完整的WebSocket客户端,支持实时行情订阅和数据处理功能丰富的API客户端,支持多种数据类型的获取数据存储和管理系统,使用SQLite进行数据持久化实时监控和预警系统,支持自定义预警规则技术指标计算,提供基本的数据分析功能。

2201_76125261的博客 1255

浅谈C++中的虚函数及其作用

浅谈C++中的虚函数 虚函数的作用 在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而 功能不同的函数。编译系统按照同名覆盖的原则决定调用的对象。 C++中的虚函数就是用来解决这个问题的。虚函数的作用是允许在派生类中重新定义与 基类同名的函数,

关注互联网安全的小虾米一枚 4122

Android 13.0 开机启动优化之PMS扫描apk耗时相关功能优化

在android13.0的系统rom定制化开发中,在系统中开发产品时,会根据客户要求内置第三方app,这时如果内置app过多,或者安装的app过多,在系统开机的过程中 在pms扫描安装app的时候,就会比较耗时,这时候就需要优化下pms扫描安装app这块的功能代码,用多线程来实现pms扫描安装app,来加快开机时间,接下来 来实现这个功能

安卓兼职framework和app工程师的博客 1333

C++中的多态是什么?如何实现多态?解释一下C++中的虚函数和纯虚函数,它们的作用是什么?

虚函数的主要作用是允许派生类覆盖(Override)基类的函数,这样当使用基类指针或引用来调用该函数时,会根据对象的实际类型(运行时绑定)来调用相应的派生类版本。多态指的是允许一个接口(或一个父类引用)在多种数据类型上被实现,或者一个接口被多个不同的类以不同的方式实现。需要注意的是,为了使多态能够工作,基类中的函数必须是虚函数(在函数声明前加上 virtual 关键字),并且在派生类中需要重写这个函数(使用 override 关键字是一个好习惯,虽然不是强制性的,但可以提高代码的可读性和避免潜在的错误)。

Aaron的博客 1006

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

C++中的虚函数的作用主要是实现了多态的机制

http://blog.youkuaiyun.com/wuchuanpingstone/article/details/6742465chenggongcheng0我的:收件箱资源博客空间设置|帮助|退出   目录(?) [+] C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指

chenggongcheng的专栏 1224

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

C++中的纯虚函数和虚函数的作用

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class)。 2.虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(b...

758

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

QT C++ 中的虚函数作用介绍

看此文章可以了解到,虚函数的存在意义,以及作用

LZL19980119的博客 898

C++中的虚函数和析构函数的定义和作用

虚函数 定义: C++中的虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。 虚函数的使用方法是: 在基类用virtual声明成员函数为虚函数。这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。在类外定义虚函数时,不必再加virtual。在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类...

mulinsen77的博客 730

虚函数C++中的虚函数的作用主要是实现了多态的机制

C++中的虚函数的作用主要是实现了多态的机制

无穷山色 1361

解释一下 C++ 中的虚函数是什么,以及它们的作用。

虚函数

qq_43729554的博客 596

C++中的虚函数

多态 多态是面向对象的三大特征之一,其它两大特征分别是 封装 和 继承 所谓 多态,简单来说,就是当发出一条命令时,不同的对象 接收到同样的命令后所做出的动作是不同的 而书本上的定义则是: 其实就是在说两个概念:静态多态和动态多态 静态多态 静态多态,也叫 早绑定 看如下实例: 定义一个矩形类:Rect,其中有两个同名成员函数:calcArea(),显然 二者互为重载(名字相同,参数可辨) 在使用时: 当实例化一个 Rect 的对象后,就可以通过对象分别调用这两个函数, 计算机在编译...

wesigj的博客 1248

C++中的虚函数、虚函数的作用和使用方法

人们提出这样的设想,能否用同一个调用形式,既能调用派生类又能调用基类的同名函数。在程序中不是通过不同的对象名去调用不同派生层次中的同名函数,而是通过指针调用它们。例如,用同一个语句“pt->display( );”可以调用不同派生层次中的display函数,只需在调用前给指针变量 pt 赋以不同的值(使之指向不同的类对象)即可。 虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以

算法改变人生 652

C++中的虚函数的作用和使用方法

转载自http://c.biancheng.net/cpp/biancheng/view/244.html 我们知道,在同一类中是不能定义两个名字相同、参数个数和类型都相同的函数的,否则就是“重复定义”。但是在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。例如在例12.1(具体代码请查看:C++多态性的一个典型例子)程序中,在Circle类中定义了

wubangbang的博客 387

c语言虚函数的作用,C++中的虚函数(virtual function)_c语言

1.简介虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。假设我们有下面的类层次:class A{public:virtual void foo() { cout << "A::foo() is called" << endl;}};class B: public A{public:virtual void foo()...

weixin_30118919的博客 1131
上一篇: 【随机数应用之】在半径为R的圆内找随机n个点
下一篇: 小数精确相乘
otuhacker
博客等级 码龄18年 21粉丝 84原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值