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();

}

Tessent ATPG进阶:解锁多种Fault Model的工程实践与选型指南 本文深入探讨Tessent ATPG中多种Fault Model的工程实践与选型策略,涵盖Stuck-at、Transition、Bridge等核心模型的应用场景与配置技巧。通过实际案例解析,展示如何组合不同Fault Model提升芯片测试覆盖率,特别针对汽车电子、超低功耗芯片等复杂场景提供专业解决方案。 阅读详情

相关推荐

手把手教你用Sentinel-1和SNAP处理InSAR数据:从下载到生成沉降图(附Python脚本)

本文详细介绍了如何使用Sentinel-1数据和SNAP软件处理InSAR数据,从下载到生成地表沉降图的全流程。通过Python脚本实现自动化处理,适用于城市扩张和地质脆弱带的地表沉降监测,提供高性价比的毫米级形变监测方案。

weixin_26800111的博客 267

虚函数的作用及使用方法

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

20164225的博客 1万+

腾讯会议视频过期了怎么办?亲测可用:用这个免费插件抢救你的会议录制与回放

本文提供了三种亲测有效的腾讯会议视频过期恢复方案,包括浏览器缓存提取、专业工具组合及预防性措施。特别推荐使用免费插件抢救过期会议录制与回放,帮助职场人解决重要视频资料丢失的难题。

weixin_33686714的博客 264

C++中虚函数的作用?

虚函数简明理解!

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

硬件单板类机考复习提纲

6、 功率晶体管低导通损耗时处于的状态(饱和状态、放大状态、截止状态)(放大器的采样频率特性)总结:元器件特性(电阻、电容、二极管、三极管、晶体、晶振、MOS、磁珠等)信号质量问题产生根因(单调性、边沿过缓、毛刺、回沟、抖动等)3、 几种电容容量范围大小(超级电容、钽电容、陶瓷电容、铝电容)66、背板连接器接触设计的接触顺序(先导向、再地针、最后信号针)28、逻辑电平接口的特性(CMOS、TTL、ECL、PECL)76、差分电平(lvpecl、RS-422/485、cml)

2303_79562407的博客 1万+

用户消费行为分析(MapReduce实现,Hive分析)

一、题目要求 数据来自CDNow网站的一份用户购买CD明细,本数据集共有 6 万条 左右数据,数据为 CDNow 网站 1997年1月至1998年6月的用户行为 数据

紫烟的博客 3013

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

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

测试0901-1 1228

【QT开发教程】如何在Qt中使用Protobuf进行高效数据序列化

Protocol Buffers(Protobuf)是Google开发的一种语言中立、平台中立、可扩展的序列化机制,常用于数据交换和存储。Qt作为一个跨平台的C++图形用户界面应用框架,可以与Protobuf结合使用,实现高效的数据序列化和反序列化。本文将介绍如何在Qt中集成和使用Protobuf,包括基本概念、安装步骤、使用示例以及实际应用。

科技改变人类,技术成就未来 1440

C++虚函数的作用

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

weixin_46068274的博客 4800

语音识别:PyAudio、SoundDevice、Vosk、openai-whisper、Argos-Translate、FunASR(Python)

本文介绍了Python中常用的音频处理库PyAudio,详细解析了其核心功能、核心概念和典型工作流程。PyAudio是一个跨平台的音频I/O库,支持音频录制、播放、实时流处理和低延迟操作。核心概念包括PyAudio类(管理音频设备和流)和Stream类(代表音频流)。文章重点讲解了open()方法的关键参数、回调函数的使用方式以及典型的工作流程,包括设备查询、流配置和资源释放。PyAudio适用于需要实时音频处理的场景,如语音识别和音频效果处理。

算法工程师 706

Allegro 中复制一部分PCB到另一个PCB中去

引言:自从学了allegro后,我每天快乐地再拉线,一开心肯定出事情。不料今天一高兴,把本来在5层的铜皮直接在4层画起来了。不过,幸好我知道换层,才省了又要画铜皮的时间。好了,开干,如果要将其他层的铜皮换到想要换到的层上,就需要这个命令。

tianpu2320959696的博客 968

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

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

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

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

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

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

Aaron的博客 1006

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

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

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

chenggongcheng的专栏 1224

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

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

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

758

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、付费专栏及课程。

余额充值