RTTI、虚函数和虚基类的实现方式、开销分析及使用指导

告别SendKeys!用DD驱动级模拟在Windows 10/11上实现真·后台键鼠操作(Python实战) 本文详细介绍了如何使用DD驱动级模拟技术在Windows 10/11上实现真后台键鼠操作,解决传统方法如pyautogui和SendKeys的局限性。通过Python实战案例,展示了驱动级模拟在游戏自动化、RPA等场景中的高效应用,包括环境配置、核心API解析及高级优化技巧。 阅读详情

“在正确的场合使用恰当的特性” 对称职的C++程序员来说是一个基本标准。想要做到这点,首先要了解语言中每个特性的实现方式及其开销。本文主要讨论相对于传统 C 而言,对效率有影响的几个C++新特性:

  • 编译时开销
  • 运行时开销
  • 相关文档:
    • C++编码规范与指导
    • C++异常机制的实现方式和开销分析
    • 多处理器环境和线程同步的高级话题
    • C++0x(C++11)新特性点评

相对于传统的 C 语言,C++ 引入的额外开销体现在以下两个方面:

编译时开销

模板、类层次结构、强类型检查等新特性,以及大量使用了这些新特性的 STL 标准库都增加了编译器负担。但是应当看到,这些新机能在不降低,甚至(由于模板的内联能力)提升了程序执行效率的前提下,明显减轻了广大 C++ 程序员的工作量。

用几秒钟的CPU时间换取几人日的辛勤劳动,附带节省了日后调试和维护代码的时间,这点开销当算超值。

当然,在使用这些特性的时候,也有不少优化技巧。比如:编译一个 广泛依赖模板库的大型软件时,几条显式实例化指令就可能使编译速度提高几十倍;恰当地组合使用部分专门化和完全专门化,不但可以最优化程序的执行效率,还可以让同时使用多种不同参数实例化一套模板的程序体积显著减小……

 

运行时开销

运行时开销恐怕是程序员最关心的问题之一了。相对与传统C程序而言,C++中有可能引入额外运行时开销的新特性包括:
  1. 虚基类
  2. 虚函数
  3. RTTI(dynamic_cast和typeid)
  4. 异常
  5. 对象的构造和析构

关于其中第四点:异常,对于大多数现代编译器来说,在正常情况(未抛出异常)下,try块中的代码执行效率和普通代码一样高,而且由于不再需要使用传统上通过返回值或函数调用来判断错误的方式,代码的实际执行效率还可能进一步提高。抛出和捕捉异常的效率也只是在某些情况下才会稍低于函数正常返回的效率,何况对于一个编写良好的程序,抛出和捕捉异常的机会应该不多。关于异常使用的详细讨论,参见:C++编码规范正文中的相关部分和C++异常机制的实现方式和开销分析一节。

而第五点,对象的构造和析构开销也不总是存在。对于不需要初始化/销毁的类型,并没有构造和析构的开销,相反对于那些需要初始化/销毁的类型来说,即使用传统的C方式实现,也至少需要与之相当的开销。这里要注意的一点是尽量不要让构造和析构函数过于臃肿,特别是在一个类层次结构中更要注意。时刻保持你的构造、析构函数中只有最必要的初始化和销毁操作,把那些并不是每个(子)对象都需要执行的操作留给其他方法和派生类去解决。

其实对一个优秀的编译器而言,C++的各种特性本身就是使用C/汇编加以千锤百炼而最优化实现的。可以说,想用C甚至汇编比编译器更高效地实现某个C++特性几乎是不可能的。要是真能做到这一点的话,大侠就应该去写个编译器造福广大程序员才对~

C++之所以 被广泛认为比C“低效”,其根本原因在于:由于程序员对某些特性的实现方式及其产生的开销不够了解,致使他们在错误的场合使用了错误的特性。而这些错误基本都集中在:

  • 把异常当作另一种流控机制,而不是仅将其用于错误处理中
  • 一个类和/或其基类的构造、析构函数过于臃肿,包含了很多非初始化/销毁范畴的代码
  • 滥用或不正确地使用RTTI、虚函数和虚基类机制

其中前两点上文已经讲过,下面讨论第三点。

为了说明RTTI、虚函数和虚基类的实现方式,这里首先给出一个经典的菱形继承实例,及其具体实现(为了便于理解,这里故意忽略了一些无关紧要的优化):


图中虚箭头代表偏移,实箭头代表指针

由上图得到每种特性的运行时开销如下:
 
特性时间开销空间开销
RTTI几次整形比较和一次取址操作(可能还会有1、2次整形加法)每类型一个type_info对象(包括类型ID和类名称),典型情况下小于32字节

 

虚函数一次整形加法和一次指针间接引用每类型一个虚表,典型情况下小于128字节

每对象若干个(大部分情况下是一个)虚表指针,典型情况下小于8字节

 

虚基类从虚继承的子类中访问虚基类的数据成员或其虚函数时,将增加两次指针间接引用和一次整形加法(部分情况下可以优化为一次指针间接引用)。每类型一个虚基类表,典型情况下小于32字节

每对象若干虚基类表指针,典型情况下小于8字节

在同时使用了虚函数的时候,虚基类表可以合并到虚表(virtual table)中,每对象的虚基类表指针(vbptr)也可以省略(只需vptr即可)。实际上,很多实现都是这么做的。 这样做的缺点是需要为一些中间类型(如:B1、B2 等)准备多个虚表。

如果指定类型在其类层次结构中只有一个虚基类(大部分使用了虚基类的情况下都是如此,如:上例中就只有 BB 一个虚基类),则可将 vbptr 直接替换为虚基类的偏移地址,这样做将可节省一次指针间接引用,从而提高效率。很多编译器都会自动开启这类优化措施。

此外,由于在很多原本需要访问虚表内 offset 字段的场合中(例如:调用某些虚函数时),该值都是编译时已知的。此时只需一个整形立即数加法即可完成从基类对象到派生类 this 指针的转换。因此,在不怎么影响时间效率的前提下,可以仅保留一个 vbptr 指针(意即:上例中 B2 内的 vbptr 可以被省略)。这种优化方式常常与前文提到的,在单虚基类的场合中将 vbptr 直接替换为虚基类偏址的做法一同使用,以期在时间效率和空间效率间取得较好的平衡,例如:VC 就经常使用这样的优化方式。

 

 * 其中“每类型”或“每对象”是指用到该特性的类型/对象。对于未用到这些功能的类型及其对象,则不会增加上述开销

可见,关于老天“饿时掉馅饼、睡时掉老婆”等美好传说纯属谣言。但凡人工制品必不完美,总有设计上的取舍,有其适应的场合也有其不适用的地方。

C++中的每个特性,都是从程序员平时的生产生活中逐渐精化而来的。在不正确的场合使用它们必然会引起逻辑、行为和性能上的问题。对于上述特性,应该只在必要、合理的前提下才使用。

"dynamic_cast" 用于在类层次结构中漫游,对指针或引用进行自由的向上、向下或交叉强制。"typeid" 则用于获取一个对象或引用的确切类型,与 "dynamic_cast" 不同,将 "typeid" 作用于指针通常是一个错误,要得到一个指针指向之对象的type_info,应当先将其解引用(例如:"typeid(*p);")。

一般地讲,能用虚函数解决的问题就不要用 "dynamic_cast",能够用 "dynamic_cast" 解决的就不要用 "typeid"。比如:



void
rotate(
IN const CShape& iS)
{
    
if (typeid(iS) == typeid(CCircle))
    {
        
// ...
    }
    
else if (typeid(iS) == typeid(CTriangle))
    {
        
// ...
    }
    
else if (typeid(iS) == typeid(CSqucre))
    {
        
// ...
    }

    
// ...
}

以上代码用 "dynamic_cast" 写会稍好一点,当然最好的方式还是在CShape里定义名为 "rotate" 的虚函数。

虚函数是C++众多运行时多态特性中开销最小,也最常用的机制。虚函数的好处和作用这里不再多说,应当注意在对性能有苛刻要求的场合,或者需要频繁调用,对性能影响较大的地方(比如每秒钟要调用成千上万次,而自身内容又很简单的事件处理函数)要慎用虚函数。

需要特别说明的一点是:虚函数的调用开销与通过函数指针的间接函数调用(例如:经典C程序中常见的,通过指向结构中的一个函数指针成员调用;以及调用DLL/SO中的函数等常见情况)是相当的。比起函数调用本身的开销(保存现场->传递参数->传递返回值->恢复现场)来说,一次指针间接引用是微不足道的。这就使得在绝大部分可以使用函数的场合中都能够负担得起虚方法的些微额外开销。

作为一种支持多继承的面向对象语言,虚基类有时是保证类层次结构正确一致的一种必不可少的手段。但在需要频繁使用基类提供的服务,又对性能要求较高的场合,应该尽量避免使用它。在基类中没有数据成员的场合,也可以解除使用虚基类。例如,在上图中,如果类 "BB" 中不存在数据成员,那么 "BB" 就可以作为一个普通基类分别被 "B1" 和 "B2" 继承。这样的优化在达到相同效果的前提下,解除了虚基类引起的开销。不过这种优化也会带来一些问题:从 "DD" 向上强制到 "BB" 时会引起歧义,破坏了类层次结构的逻辑关系。

上述特性的空间开销一般都是可以接受的,当然也存在一些特例,比如:在存储布局需要和传统C结构兼容的场合、在考虑对齐的场合、在需要为一个本来尺寸很小的类同时实例化许多对象的场合等等。

Simpack实战:5分钟搞定轨道车辆车轮不圆的3种定义方法(附详细参数设置) 本文详细介绍了在Simpack中定义轨道车辆车轮不圆的3种高效方法:Radius deviations、Fourier coefficients和Harmonic function,并提供了详细的参数设置指南。通过实战案例和参数优化技巧,帮助工程师快速掌握车轮不圆度的定义与仿真,提升轨道车辆动力学分析的准确性和效率。 阅读详情

相关推荐

Notion卡顿救星:3分钟搞定DNS优化,手机电脑全平台加速指南

本文针对Notion访问慢和卡顿问题,提供了全平台DNS优化与Hosts文件修改的详细指南。通过更换优质DNS服务器或建立静态IP映射,可有效解决因网络延迟和域名解析导致的加载缓慢,实现手机与电脑端的快速加速,提升Notion使用流畅度。

v6b7n8m9q0的博客 1306

RTTI、虚函数和虚基类的开销分析及使用指导

“在正确的场合使用恰当的特性” 对称职的C++程序员来说是一个基本标准。想要做到这点,首先要了解语言中每个特性的实现方式及其开销。本文主要讨论相对于传统 C 而言,对效率有影响的几个C++新特性。相对于传统的 C 语言,C++ 引入的额外开销体现在以下两个方面:编译时开销模板、类层次结构、强类型检查等新特性,以及大量使用了这些新特性的 STL 标准库都增加了编译器负担。

gogoda的专栏 909

手把手教你用C语言实现GNSS卫星方位角计算(附RTKLIB风格代码解析)

本文详细介绍了如何使用C语言实现GNSS卫星方位角计算,包括从地固坐标系到站心坐标系的转换链条。通过RTKLIB风格的代码解析,展示了XYZ到ENU坐标转换的核心原理和C语言实现框架,帮助开发者掌握卫星方位角和高度角的计算技巧,适用于GNSS观测数据质量分析和精密定位算法开发。

weixin_30699915的博客 365

虚基类

虚基类的作用: 当一个基类被声明为虚基类后,即使它成为了多继承链路上的公共基类,最后的派生类中也只有它的一个备份。 虚基类的特点: 虚基类构造函数的参数必须由最新派生出来的类负责初始化(即使不是直接继承); 虚基类的构造函数先于非虚基类的构造函数执行。 实例分析: 一、无虚函数的虚基类 class Base{ public:     int a;     Base(inta

liminlimin_limin的专栏 1537

Hermes+MemOS记忆系统:用SQLite构建可编辑AI个人认知资产

AI记忆不是简单延长上下文或叠加RAG,而是构建具备分层存储、混合检索与自然语言反馈能力的个人认知资产。其核心原理在于将用户交互痕迹(L1)、偏好策略(L2)、结构化世界模型(L3)和结晶化技能统一管理,实现记忆的可追溯、可修正、可进化。技术价值体现在降低token消耗、提升长程一致性、支持离线演进,并突破传统‘静态快照’式知识库局限。典型应用场景包括个人知识助手、客服工单分析Agent、研发文档理解系统等。本文聚焦Hermes Agent与MemOS深度集成方案,详解SQLite作为ACID底座支撑L1/

weixin_33623304的博客 434

【创作赢红包】Gurobi学术版证书更新与Python环境配置全攻略

本文详细解析了Gurobi学术版证书过期后的完整更新流程与Python环境配置方法。从彻底清理旧版残留、手把手注册获取新许可证,到软件安装、证书激活,最后重点讲解如何将gurobipy接口正确配置到Python的site-packages目录,并提供了常见问题排查方案,帮助用户高效解决证书与配置难题。

efc1234567的博客 612

《C++虚函数调用开销大揭秘:性能与灵活性的权衡》

虚函数表是一个存储函数指针的数组,每个包含虚函数的类都有一个对应的虚函数表。需要注意的是,这个测试结果只是一个示例,实际的虚函数调用开销会受到很多因素的影响,如硬件平台、编译器优化、虚函数的实现复杂度等。虽然虚函数的调用开销在大多数情况下是可以接受的,但是在性能关键的代码路径中,我们需要考虑虚函数的调用开销,并采取一些措施来减少开销。例如,如果我们有一个函数需要根据对象的类型来执行不同的操作,但是在某些情况下我们可以在编译时确定对象的类型,那么我们可以使用模板或者函数重载来代替虚函数。

xy520521的博客 1430

「模型部署系列」ubuntu 使用vllm部署Qwen3-8B模型

解决这个问题的方法正如错误信息中所提示的,需要在命令行中显式地设置。下载单个文件到指定本地文件夹(以下载README.md到当前路径下“dir”目录为例)解决:这表明你正在使用的 Tesla T4 GPU 的计算能力为 7.5,而。,用于获取更详细的 NCCL 调试信息,当所有其他问题排除,可尝试增加。1、下载vllm v0.8.5(此处已经下好了,去仓库拉资源)在下载前,请先通过如下命令安装ModelScope。,这一错误最终致使引擎进程启动失败。更多更丰富的命令行下载选项,可参见。

汪依帆的博客 5044

《RTTI、虚函数和虚基类的实现方式、开销分析及使用指导》

=================================================================转载链接:    http://www.baiy.cn/doc/cpp/inside_rtti.htm    “在正确的场合使用恰当的特性” 对称职的C++程序员来说是一个基本标准。想要做到这点,首先要了解语言中每个特性的实现方式及其开销。本文主要讨论相对于传统 C 而...

zhipingxi的博客 661

EAST算法(Efficient and Accuracy Scene Text))

1.EAST模型简介 本文介绍的文本检测模型EAST,便简化了中间的过程步骤,直接实现端到端文本检测,优雅简洁,检测的准确性和速度都有了进一步的提升。如下图: 其中,(a)、(b)、(c)、(d)是几种常见的文本检测过程,典型的检测过程包括候选框提取、候选框过滤、bouding box回归、候选框合并等阶段,中间过程比较冗长。而(e)即是本文介绍的EAST模型检测过程,从上图可看出,其过程简化为只有FCN阶段(全卷积网络)、NMS阶段(非极大抑制),中间过程大大缩减,而且输出结果支持文本行、单词的多个角度

博客 9579

Openclaw错误排查及解决方案之:Something went wrong while processing your request. Please try again.

Openclaw错误排查及解决方案之:Something went wrong while processing your request. Please try again.

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 3787

RTTI(Run-Time Type Identification)、虚函数和虚基类的实现方式、开销分析及使用指导

RTTI(Run-Time Type Identification)、虚函数和虚基类的实现方式、开销分析及使用指导 为了说明RTTI、虚函数和虚基类的实现方式,这里首先给出一个经典的菱形继承实例,及其具体实现(为了便于理解,这里故意忽略了一些无关紧要的优化): 图中虚箭头代表偏移,实箭头代表指针 由上图得到每种特性的运行时开销如下:   特性 时间开销

Enjoy Coding 672

自助结账商品检测(Scanned Item Detection)——基于YOLOv8的RPC数据集实战

RPC数据集是由中国科学院自动化研究所发布的零售商品结账检测公开数据集,广泛用于自助结账和零售场景下的目标检测与识别。YOLOv8是YOLO系列最新版本,由Ultralytics推出,继承了YOLO家族的高效快速和高准确性,适用于实时检测场景。本文系统介绍了基于YOLOv8的RPC数据集自助结账商品检测方案,涵盖数据准备、训练、推理及UI界面设计。未来,可结合多模态感知、RFID融合技术,打造更加智能和稳定的自助结账系统。

YOLO 250

C++ read

C++编码规范与指导:http://www.cnblogs.com/94cool/archive/2012/10/31/2747399.html RTTI、虚函数和虚基类的实现方式、开销分析及使用指导:http://baiy.cn/doc/cpp/inside_rtti.htm 转载于:https://www.cnblogs.com/johnnyflute/p/3675955.html...

weixin_30528371的博客 420

温度PID控制PLC程序

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

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

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

上一篇: STL基础4:STL7个常用容器的比较
下一篇: C++异常机制的实现方式和开销分析
雪岢奇
博客等级 码龄17年 310粉丝 60原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值