虚函数 虚函数表

article/2025/10/4 4:55:45

虚函数是面向对象编程函数的一种特定形态,是C++用于实现多态的一种有效机制。C++的多态可以分为静态多态和动态多态。函数重载和运算符重载实现的多态属于静态多态,而通过虚函数可以实现动态多态。实现函数的动态联编其本质核心则是虚表指针与虚函数表。

虚函数的本质就是通过基类访问派生类定义的函数。每一个含有虚函数的类,其实例对象内部都有一个虚函数表指针,该虚函数表指针被初始化为本类的虚函数表的内存地址。所以在程序中,不管对象类型如何转换,但该对象内部的虚函数表指针是固定的,这样才能实现动态地对对象函数进行调用,这就是C++多态性的原理。

使用虚函数注意事项:

  • 只需在声明函数的类体中使用关键字virtual将函数声明为虚函数,在定义函数时不需要。
  • 将基类中某一成员函数声明为虚函数后,派生类中的同名函数自动成为虚函数。
  • 如果类(基类和派生类)中声明了某成员函数为虚函数,则类中不能再出现与之相同的非虚函数。

当父类有实现名为虚函数fucnA ,而子类没有实现虚函数funcA,那么子类将继承该虚函数,该虚函数将存在子类的虚函数表中。

子类继承父类,并且子类的虚函数覆盖父类的同名虚函数的时候,子类的虚函数表将存放着子类的虚函数及没有被覆盖的同名虚函数。

虚函数工作的原理:

编译器处理虚函数的方法是:给每个对象添加一个隐藏成员,隐藏成员中包含了一个指向函数地址数组的指针。这种数组称为虚函数表。虚函数表中存储了为类对象进行声明的虚函数地址。

基类对象包含一个指针,该指针指向基类中所有函数的地址表。派生类对象将包含一个指向独立指针表的指针,如果派生类提供了函数的新定义,该函数表将保存新的函数的地址,如果派生类没有重新定义函数,则虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,该函数的地址也将被添加到虚函数表中。

调用虚函数时,程序将查看存储在对象中的虚函数表的地址。然后转向相应的函数地址表。使用虚函数时,在内存和执行速度方面有一定的成本,包括:

  • 每个对象都将增大,增大量为存储地址的空间;
  • 对于每个类,编译器都创建一个虚函数地址表( 数组);
  • 对于每个函数调用,都需要执行一项额外的操作,即到表中查找地址。

虚函数,虚函数表里面内存如何分配?

编译时若基类中有虚函数,编译器为该的类创建一个一维数组的虚表,存放是每个虚函数的地址。基类和派生类都包含虚函数时,这两个类都建立一个虚表。构造函数中进行虚表的创建和虚表指针的初始化。在构造子类对象时,要先调用父类的构造函数,初始化父类对象的虚表指针,该虚表指针指向父类的虚表。执行子类的构造函数时,子类对象的虚表指针被初始化,指向自身的虚表。每一个类都有虚表。虚表可以继承,如果子类没有重写虚函数,那么子类虚表中仍然会有该函数的地址,只不过这个地址指向的是基类的虚函数实现。派生类的虚表中虚函数地址的排列顺序和基类的虚表中虚函数地址排列顺序相同。当用一个指针/引用调用一个函数的时候,被调用的函数是取决于这个指针/引用的类型。即如果这个指针/引用是基类对象的指针/引用就调用基类的方法;如果指针/引用是派生类对象的指针/引用就调用派生类的方法,当然如果派生类中没有此方法,就会向上到基类里面去寻找相应的方法。这些调用在编译阶段就确定了。当涉及到多态性的时候,采用了虚函数和动态绑定,此时的调用就不会在编译时候确定而是在运行时确定。不在单独考虑指针/引用的类型而是看指针/引用的对象的类型来判断函数的调用,根据对象中虚指针指向的虚表中的函数的地址来确定调用哪个函数。

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。纯虚函数是虚函数再加上= 0。virtual void fun ()=0。当类中声明有纯虚函数时,则不能创建该类的对象。

虚函数的使用时注意:

1.构造函数不能是虚函数??

虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。

2.析构函数

析构函数应当是虚函数,除非类不用做基类。例如;假设Employee是基类,Singer 是派生类,并添加一个char *成员,该成员指向由new分配的内存。当Singer对象过期时,必须调用~Singer( )析构函数来释放内存。看下面的代码:

Employee *pe=new Singer ;

delete pe;

如果使用默认的静态联编,delete 语句将调用~ Employee( )析构函数。这将释放由Singer对象中的Employee部分指向的内存,但不会释放新的类成员指向的内存。但如果析构函数是虚的,则上述代码将先调用~ Singer析构函数释放由Singer组件指向的内存,然后,调用~Employee( )析构函数来释放由Employee组件指向的内存。
这意味着,即使基类不需要显式析构函数提供服务,也不应依赖于默认构造函数,而应提供虚析构函数,即使它不执行任何操作:
virtual ~BaseClass() 
顺便说一句,给类定义一个虚析构函数并非错误,即使这个类不用做基类;这只是一个效率方面的问题。

3.友元函数能不能是虚函数??

友元不能是虚函数,因为友元不是类的成员,而只有成员才能是虚函数。


 


http://chatgpt.dhexx.cn/article/JfMhfyys.shtml

相关文章

虚函数表和虚表指针

1,虚函数的含义 用virtual声明类的成员函数称之为虚函数 2,作用 用于实现多态 存在继承关系,子类继承父类子类重写了父类的virtual function子类以父类的指针或者引用的身份出现 3,虚函数的实现原理 其中的关键就是两点&#xf…

C++ 虚函数和虚函数表

一、虚函数 1.虚函数的概念 1.虚函数就是在基类中被关键字 virtual 说明,并在派生类中重新定义的函数。 2.虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。 3.虚函数的定义是在基类…

C++虚函数表剖析

关键词:虚函数,虚表,虚表指针,动态绑定,多态 一、概述 为了实现 C 的多态,C 使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态…

C++ 虚函数表解析

C 虚函数表解析 陈皓 http://blog.csdn.net/haoel 前言 C中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”&#x…

虚函数表详解

关键词:虚函数,虚表,虚表指针,动态绑定,多态 一、概述 为了实现C的多态,C使用了一种动态绑定的技术。这个技术的核心是虚函数表(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定…

虚函数及虚函数表

虚函数及虚函数表 各个类对象共享类的虚函数表,每个类对象有个虚函数指针vptr,虚函数指针vptr指向虚函数表(对于只有一个虚函数表的情况)。 虚函数 简单的说,每一个含有虚函数(无论是其本身的&#xff0…

简述虚函数表

前段时间我在博客中简单地说了下C的虚函数,所谓虚函数,就是C实现多态性的方法。那么编译器是如何识别虚函数的呢?据百度百科描述,C并未规定用何种方法实现虚函数,但是大部分编译器厂商都选择使用虚函数表这种方法&…

虚函数表的问题

虚函数表: 多态是由虚函数实现的,而虚函数主要是通过虚函数表(V-Table)来实现的。 如果一个类中包含虚函数(virtual修饰的函数),那么这个类就会包含一张虚函数表,虚函数表存储的每…

虚函数表详解及其应用场景

目录 概述1. 虚函数表概述2. 虚函数表的实现原理2.1. 虚函数的声明和定义2.2. 虚函数表的创建和初始化2.3. 虚函数调用的过程 3. 虚函数表的应用场景3.1. 多态性3.2. 基类指针和引用的使用3.3. 动态绑定3.4. 接口定义 结论 概述 在面向对象编程中,虚函数表&#xf…

C++ 面试必问:深入理解虚函数表

点击蓝字 关注我们 深入理解C 虚函数表 C中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。 Derive d; Base1 *b1 &d; Base2 *b2 &d; Base3 *b3 …

C++ 虚函数表

C类在内存中的存储方式 C 内存分为 5 个区域: 堆 heap :由 new 分配的内存块,其释放编译器不去管,由程序员自己控制。如果程序员没有释放掉,在程序结束时系统会自动回收。涉及的问题:“缓冲区溢出”、“内…

ThinkPHP3.2.2获取数据列getField()优化

getField()是一个常用方法,我习惯用来获取带key的数组,方便数据整合。 使用第1个参数,传入一个字段名,获取某一个数据值,返回满足条件的数据表中的该字段的第一行的值: $id M("User")->getFi…

java class getfield_java.lang.Class.getField()方法实例

全屏 java.lang.Class.getField() 返回一个Field对象,它反映此Class对象所表示的类或接口的指定公共成员字段。 name参数是一个字符串,指定所需字段的简单名称。 声明 以下是java.lang.Class.getField()方法的声明public Field getField(String name) th…

java getfield_Java 反射:通过 getField() 设置公共全局变量

Java 通过 getField() 操作公共全局变量 以前写 JavaWeb 项目启动初始化系统配置全局变量的代码,都是 variable Properties.getProperty(name) 这样一行一行代码的设置,变量少还好说,变量一多真的很磨叽。所以一直想通过 循环 简化代码&…

getField和getDeclaredField的区别

这两个方法都是用于获取字段 1.getField 只能获取public的,包括从父类继承来的字段。 2.getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,但并不…

vscode插件开发总结

一、关于vscode插件 相信大家对vscode应该都不陌生,VSCode是微软出的一款轻量级代码编辑器,免费而且功能强大,以功能强大、提示友好、不错的性能和颜值俘获了大量开发者的青睐,对JavaScript和NodeJS的支持非常好,自带…

2021-前端-VsCode插件

此乃吾习前端,VsCode之插件,个人所装,喜着自拿,不足之处还望海涵,多加批评。 1.Auto Close Tag——自动闭合尾部的标签 2.Atuo Rename Tag——修改 html 标签 自动帮你完成头部和尾部闭合标签的同步修改 3.Bracket…

关于VSCode插件的安装位置

VSCode的插件地址修改_上善若泪-CSDN博客_vscode插件位置文章目录1 data文件夹2 使用--extensions-dir命令3 使用mklink命令vscode编辑器强大的地方是可以使用各种各样的插件,但是插件默认的地方是在:C盘,让一些强迫症可能会受不了,非要迁移到…

vscode 插件-常用插件

VSCode常用插件(安装步骤同汉化) 1、*Auto Close Tag (自动闭合HTML/XML标签) 2、*Auto Rename Tag (自动帮你完成尾部闭合标签的同步修改,不过有些bug) 3、*Prettier(Prettier 是目前 Web 开发中最受欢迎的代码格式化程序) 安装Prettier -Code formatter这个插件…

Vscode 插件包下载并离线安装

打开VSCode插件官网 官网链接是https://marketplace.visualstudio.com/vscode 搜索Go 在输入框中输入go,搜索,结果如下: 点击Download Extension下载 注意:有时候找不到Download Extension,可能是网速加载慢&…