当前位置: 首页 > news >正文

跟我学C++中级篇——虚函数的性能

一、虚函数性能

一般来说,面向对象的设计中,继承和多态是其中两个非常重要的特征。从使用的过程来看,一般应用到继承的,使用多态的可能性就非常大。而多态的实现有很多种,
但开发者通常认为的多态(动多态)一般是指通过虚函数来实现的多态。
虚函数的不同于普通函数,它会通过一个虚表来控制函数的二次跳转或者叫做重定向。在普通的认知里,虚函数这个特征一般是无法进行诸如内联等进行优化的。所以一谈到虚函数都会认为其性能堪忧。
但在前面的分析中也知道了,什么东西都有特殊情况。但无论如何说明,在常识里,虚函数就是要比普通函数的性能要低。那么到底虚函数性能为什么会低?是不是所有情况下都低?下面进行一下分析。

二、用例子看问题

先看对比的例子:

#include <iostream>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <chrono>
class Parent
{
public:virtual double exeReadDataV(double a, int b){return std::sqrt(a) * std::sin(b);}double exeReadData(double a, int b){return  std::sqrt(a) * std::sin(b);}
};int main()
{Parent* p = new Parent();double a = 3.14f;int b = 3;double sum = 0.f;auto t1 = std::chrono::steady_clock::now();for (int num = 0; num < 100000000; num++){//使用一个定值,一个动态值sum += p->exeReadDataV(a,num);//全是定值//sum += p->exeReadDataV(a, b);}auto t2 = std::chrono::steady_clock::now();auto escape1 = t2 - t1;std::cerr << "escape1 is:" << escape1.count() << std::endl;auto t3 = std::chrono::steady_clock::now();for (int num = 0; num < 100000000; num++){sum += p->exeReadData(a,num);//sum += p->exeReadData(a, b);}auto t4 = std::chrono::steady_clock::now();auto escape2 = t4 - t3;std::cerr << "escape2 is:" << escape2.count() << std::endl;return 0;
}

执行结果:

escape1 is:1982749665
escape2 is:1850111712

从上面的执行可以看到,两者的执行基本没区别。这可能打破了不少人的感官认知。无论哪种函数,决定性能的有两个重要环节:一个是调用的开销;另外一个是确定性调用。前者比较好理解,后者则不容易弄明白。其实可以这样理解,一个写代码要尽量降低调用的开销,一个是写出的代码编译器能更准确的知晓上下文,然后进行优化。在前者确定的情况下,后者就非常重要了。而虚函数被大多数人认为性能低的主要原因就在于后者。
虚函数需要一个虚表进行跳转,在内存中这种开销与函数的功能开销相对来说可以忽略。但这种跳转本身意味着大量的未知,而未知就意味着编译无法掌控更多的确定性,而对某些很简单的优化可能都无法进行。正如早期的编译器,在for循环中,直接给一个变量和一个表达式,效率差不少就是这个原因。而后的编译器则对此进行了优化,将其直接转成一个常量值。把上面的例子中注释部分打开并注释当前的执行(即两个参数都为定值的情况):

执行结果:

escape1 is:867604532
escape2 is:100431290

在执行的函数调用中,两个参数中一个为定值,一个为变量时,是否调用虚函数或者普通函数,基本运行是差不多的。但是一旦调用的都为定值时,此时普通函数可以直接将两个计算函数std::sqrt(a) 和 std::sin(b)均优化为固定值并只计算一次。此时再看,计算结果可就差了将一个量级了。
而在前面的文章中(“内联补遗”)分析过的虚函数可以内联,恰恰是那种可以明确确定的虚拟函数,可以内联。即编译器知道虚函数不具有多态性的情况下,它可普通函数没有什么区别。把原来的例子拿上来:

class A{
public:inline virtual void Test(){...}
};
class B:public A
{
public:inline  virtual void Test(){...}
};
inline void Get(A& a){a.Test();
}
int main(){A a;B b;b.Test();//可以内联//下面不确定Get(b);Get(a);
return 0;
}

那么可以从内联的角度来分析,虚函数为什么会给大家一个性能低的印象?首先为什么内联函数快,主要就是固定地址,编译器优化两大方面。而上面的虚拟函数可以内联,仿佛是与此结论相反,但恰恰提到了能够内联的虚拟函数的情形。互相印证,应该就明白为什么虚函数在多态的情况下性能低的原因了。
总之,虚函数本身不是性能低的代表,但是虚函数多态的调用会影响优化才是性能降低的根源。这些优化包括计算优化、分支跳转优化以及调用优化等等。而这些优化无法被编译器使用,自然也就会使得编译出来的代码有着很多的多余的运行指令。

三、设计上对虚函数数的替代

如果对性能的敏感性不强,那么如何使用虚函数不是一个多大的问题。可是如果实际情况对此要求比较严格时,可以考虑用如下的方式来解决虚函数的使用问题:
1、模板的方法如CRTP(奇异递归)
2、使用宏(不推荐)
3、通过设计模式等设计方法实现(如访问者模式等 )
4、使用一些技术或方法绕开多态,比如就直接写多个类然后直接控制
到底如何使用或不使用虚函数,是根据实际情况来决定的。还是那句话,没有一个技术是包打天下的。

四、总结

学习知识不是简单的为了会用,而是能够灵活的运用。要想灵活的运用,则必须掌握技术本质的内涵。只有把其内在体系掌握,才能在具体的场景上发挥其优势。这也是总说的从必然世界到自由世界的一个哲学问题。

相关文章:

跟我学C++中级篇——虚函数的性能

一、虚函数性能 一般来说&#xff0c;面向对象的设计中&#xff0c;继承和多态是其中两个非常重要的特征。从使用的过程来看&#xff0c;一般应用到继承的&#xff0c;使用多态的可能性就非常大。而多态的实现有很多种&#xff0c; 但开发者通常认为的多态&#xff08;动多态&…...

trl - 微调、对齐大模型的全栈工具

文章目录 一、关于 TRL亮点 二、安装1、Python包2、从源码安装3、存储库 三、命令行界面&#xff08;CLI&#xff09;四、如何使用1、SFTTrainer2、RewardTrainer3、PPOTrainer4、DPOTrainer 五、其它开发 & 贡献参考文献最近策略优化 PPO直接偏好优化 DPO 一、关于 TRL T…...

GuLi商城-商品服务-API-品牌管理-品牌分类关联与级联更新

先配置mybatis分页&#xff1a; 品牌管理增加模糊查询&#xff1a; 品牌管理关联分类&#xff1a; 一个品牌可以有多个分类 一个分类也可以有多个品牌 多对多的关系&#xff0c;用中间表 涉及的类&#xff1a; 方法都比较简单&#xff0c;就不贴代码了...

【linux】服务器ubuntu安装cuda11.0、cuDNN教程,简单易懂,包教包会

【linux】服务器ubuntu安装cuda11.0、cuDNN教程&#xff0c;简单易懂&#xff0c;包教包会 【创作不易&#xff0c;求点赞关注收藏】 文章目录 【linux】服务器ubuntu安装cuda11.0、cuDNN教程&#xff0c;简单易懂&#xff0c;包教包会一、版本情况介绍二、安装cuda1、到官网…...

在 Apifox 中如何高效批量添加接口请求 Body 参数?

在使用 Apifox 进行 API 设计时&#xff0c;你可能会遇到需要添加大量请求参数的情况。想象一下&#xff0c;如果一个接口需要几十甚至上百个参数&#xff0c;若要在接口的「修改文档」里一个个手动添加这些参数&#xff0c;那未免也太麻烦了&#xff0c;耗时且易出错。这时候&…...

专业PDF编辑工具:Acrobat Pro DC 2024.002.20933绿色版,提升你的工作效率!

软件介绍 Adobe Acrobat Pro DC 2024绿色便携版是一款功能强大的PDF编辑和转换软件&#xff0c;由Adobe公司推出。它是Acrobat XI系列的后续产品&#xff0c;提供了全新的用户界面和增强功能。用户可以借助这款软件将纸质文件转换为可编辑的电子文件&#xff0c;便于传输、签署…...

车载音视频App框架设计

简介 统一播放器提供媒体播放一致性的交互和视觉体验&#xff0c;减少各个媒体应用和场景独自开发的重复工作量&#xff0c;实现媒体播放链路的一致性&#xff0c;减少碎片化的Bug。本文面向应用开发者介绍如何快速接入媒体播放器。 主要功能&#xff1a; 新设计的统一播放U…...

StarRocks on AWS Graviton3,实现 50% 以上性价比提升

在数据时代&#xff0c;企业拥有前所未有的大量数据资产&#xff0c;但如何从海量数据中发掘价值成为挑战。数据分析凭借强大的分析能力&#xff0c;可从不同维度挖掘数据中蕴含的见解和规律&#xff0c;为企业战略决策提供依据。数据分析在营销、风险管控、产品优化等领域发挥…...

VUE中setup()

在Vue中&#xff0c;setup() 函数是Vue 3.0及更高版本引入的一个重要特性&#xff0c;它是Composition API的入口点。setup() 函数用于初始化组件的状态和逻辑&#xff0c;包括定义响应式数据、方法和生命周期钩子。以下是关于setup() 函数的详细解释&#xff1a; 1. 作用与特…...

【单元测试】SpringBoot

【单元测试】SpringBoot 1. 为什么单元测试很重要&#xff1f;‼️ 从前&#xff0c;有一个名叫小明的程序员&#xff0c;他非常聪明&#xff0c;但有一个致命的缺点&#xff1a;懒惰。小明的代码写得又快又好&#xff0c;但他总觉得单元测试是一件麻烦事&#xff0c;觉得代码…...

分布式搜索引擎ES-elasticsearch入门

1.分布式搜索引擎&#xff1a;luceneVS Solr VS Elasticsearch 什么是分布式搜索引擎 搜索引擎&#xff1a;数据源&#xff1a;数据库或者爬虫资源 分布式存储与搜索&#xff1a;多个节点组成的服务&#xff0c;提高扩展性(扩展成集群) 使用搜索引擎为搜索提供服务。可以从海量…...

TCP三次握手与四次挥手详解

1.什么是TCP TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的通信协议&#xff0c;属于互联网协议族&#xff08;TCP/IP&#xff09;的一部分。TCP 提供可靠的、顺序的、无差错的数据传输服务&…...

【Windows】操作系统之任务管理器(第一篇)

一、操作系统简介 Windows操作系统是由微软公司&#xff08;Microsoft&#xff09;开发的一款图形操作系统&#xff0c;它以其强大的功能和广泛的用户基础&#xff0c;成为了目前世界上用户使用最多、兼容性最强的操作系统之一。以下是关于Windows操作系统的详细介绍&#xff…...

图同构的必要条件

来源&#xff1a;离散数学...

Django获取request请求中的参数

支持 post put json_str request.body # 属性获取最原始的请求体数据 json_dict json.loads(json_str)# 将原始数据转成字典格式 json_dict.get("key", "默认值") # 获取数据参考 https://blog.csdn.net/user_san/article/details/109654028...

kotlin compose 实现应用内多语言切换(不重新打开App)

1. 示例图 2.具体实现 如何实现上述示例,且不需要重新打开App ①自定义 MainApplication 实现 Application ,定义两个变量: class MainApplication : Application() { object GlobalDpData { var language: String = "" var defaultLanguage: Strin…...

记录些MySQL题集(16)

MySQL 存储过程与触发器 一、初识MySQL的存储过程 Stored Procedure存储过程是数据库系统中一个十分重要的功能&#xff0c;使用存储过程可以大幅度缩短大SQL的响应时间&#xff0c;同时也可以提高数据库编程的灵活性。 存储过程是一组为了完成特定功能的SQL语句集合&#x…...

【算法基础】Dijkstra 算法

定义&#xff1a; g [ i ] [ j ] g[i][j] g[i][j] 表示 v i v_i vi​ 到 $v_j $的边权重&#xff0c;如果没有连接&#xff0c;则 g [ i ] [ j ] ∞ g[i][j] \infty g[i][j]∞ d i s [ i ] dis[i] dis[i] 表示 v k v_k vk​ 到节点 v i v_i vi​ 的最短长度&#xff0c; …...

使用 Flask 3 搭建问答平台(三):注册页面模板渲染

前言 前端文件下载 链接https://pan.baidu.com/s/1Ju5hhhhy5pcUMM7VS3S5YA?pwd6666%C2%A0 知识点 1. 在路由中渲染前端页面 2. 使用 JinJa 2 模板实现前端代码复用 一、auth.py from flask import render_templatebp.route(/register, methods[GET]) def register():re…...

pycharm如何debug for循环里面的错误值

一般debug时&#xff0c;在for循环里面的话&#xff0c;需要自己一步一步点。如果循环几百次那种就比较麻烦。此时可以采用try except的方式来解决 例子如下 #ptyhon debug for循环的代码 num[1,2,3,s,4] ans0 for i in num:try:ansiexcept:print(错误) print(ans) 结果如下&a…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个&#xff1f;3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制&#xff08;过半机制&#xff0…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...