C++中的RTTI和虚函数
RTTI: Runtime Type Identification,运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。C通过以下的两个操作提供RTTI1typeid运算符该运算符返回其表达式或类型名的实际类型。2dynamic_cast运算符该运算符将基类的指针或引用安全地转换为派生类类型的指针或引用。首先介绍下static_cast、dynamic_cast、const_cast和reinterpret_cast。static_castnew_type (expression) dynamic_castnew_type (expression) const_castnew_type (expression) reinterpret_castnew_type (expression)static_caststatic_cast相当于传统的C语言里的强制转换该运算符把expression转换为new_type类型用来强迫隐式转换如non-const对象转为const对象编译时检查用于非多态的转换可以转换指针及其他但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法①用于类层次结构中基类父类和派生类子类之间指针或引用的转换。进行上行转换把派生类的指针或引用转换成基类表示是安全的进行下行转换把基类指针或引用转换成派生类表示时由于没有动态类型检查所以是不安全的。②用于基本数据类型之间的转换如把int转换成char把int转换成enum。③把空指针转换成目标类型的空指针。④把任何类型的表达式转换成void类型。注意static_cast不能转换掉expression的const、volatile、或者__unaligned属性例char a a; int b static_castchar(a);//正确将char型数据转换成int型数据 double *c new double; void *d static_castvoid*(c);//正确将double指针转换成void指针 int e 10; const int f static_castconst int(e);//正确将int型数据转换成const int型数据 const int g 20; int *h static_castint*(g);//编译错误static_cast不能转换掉g的const属性const_castconst_cast用于修改类型的const或volatile属性。①常量指针被转化成非常量的指针并且仍然指向原来的对象②常量引用被转换成非常量的引用并且仍然指向原来的对象③const_cast一般用于修改底指针。如const char *p形式。例const int g1 20; int *h1 const_castint*(g1);//去掉const常量const属性 const int g2 20; int h2 const_castint (g2);//去掉const引用const属性 const char *g3 hello; char *h3 const_castchar *(g3);//去掉const指针const属性reinterpret_cast:new_type必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数也可以把一个整数转换成一个指针先把一个指针转换成一个整数再把该整数转换成原类型的指针还可以得到原先的指针值。reinterpret_cast意图执行低级转型实际动作及结果可能取决于编辑器这也就表示它不可移植。例:#include iostream using namespace std; int output(int p){ cout p endl; return 0; } typedef int (*test_func)(int );//定义函数指针test_func // Returns a hash code based on an address unsigned short Hash(void *p) { unsigned int val reinterpret_castunsigned long(p); return (unsigned short)( val ^ (val 16)); } class A { public: int m_a; }; class B { public: int m_b; }; class C: public A, public B {}; int main(){ //测试1 cout test1: endl; int p 10; test_func fun1 output; fun1(p);//正确 //test_func fun2 reinterpret_casttest_func(p); //fun2(p);//...处有未经处理的异常: 0xC0000005: Access violation //测试2 cout test2:endl; int a[20]; for (int i 0; i 20; i) cout Hash(a i) endl; //测试3 cout test3endl; //static_cast和reinterpret_cast的区别主要在于多重继承 C c; c.m_a 123; c.m_b 231; B* b1 reinterpret_castB*(c); B* b2 static_cast B*(c); cout c address: ; cout c endl; cout b1 address: ; cout b1 endl; cout b2 address: ; cout b2 endl; cout b1 m_b val: ; cout b1-m_b endl; cout b2 m_b val: ; cout b2-m_b endl; return 0; }结果test1: 10 test2: 25485 25481 25477 25473 25469 25465 25461 25457 25453 25449 25445 25441 25437 25433 25429 25425 25421 25417 25413 25409 test3 c address: 0x7fffdcfdbfc0 b1 address: 0x7fffdcfdbfc0 b2 address: 0x7fffdcfdbfc4 b1 m_b val: 123 b2 m_b val: 231dynamic_castdynamic_cast操作用于检测运行时如下类型转换1.将一个指向父类的指针转换为一个指向子类的指针2.将一个父类的左引用转换为一个子类的左引用3.针对C11将父类的左/右引用转换为子类的右值引用需要注意dynamic_cast在将父类cast到子类时dynamic_cast将一个基类对象指针或引用cast到继承类指针dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。这也是dynamic_cast与其他转换不同的地方dynamic_cast涉及运行时类别检查如果绑定到引用或指针的对象不是目标类型的对象则dynamic_cast失败。父类必须要有虚函数,因为dynamic_cast运行时需要检查RTTI信息,只有带虚函数的类运行时才会检查RTTI。需要注意尽量少使用转型操作尤其是dynamic_cast耗时较高会导致性能的下降尽量使用其他方法替代。RTTI和虚函数普通成员函数一个类如果成员函数不是虚函数的话该函数实现存放在代码区定义多个对象的话都到同样的代码区调用该函数而变量则是每新建一个对象都会新建一块内存来存储该变量。调用普通函数时程序根据指针的类型到该类型类所对应的代码区找到所对应的函数即指针的类型决定了普通函数的调用与指向的对象没关系指针为基类类型就调用基类对应的成员函数指针为派生类类型就调用派生类对应的成员函数。换言之在程序执行之前函数的调用就以这种方式固定下来C 中通常称之为静态解析或者静态绑定也叫做早期绑定。底层关键vtable 与 RTTI 结构拥有虚函数的对象头部存虚表指针 vptr指向虚函数表vtable。 在绝大多数编译器GCC/Clang MSVC实现里GCC每个多态类型对应一个typeinfo对象虚表的第一个 slot 存放指向该typeinfo的指针。MSVC虚表附近关联type_info信息。std::type_info就是 RTTI 的载体里面保存类名字、继承关系、基类列表。RTTI 是运行期数据不是靠解析类名字符串比对而是比较type_info实例以及遍历继承图。当执行dynamic_castDerived*(base_ptr)通过对象的vptr拿到本对象实际运行时类型的typeinfo不是指针声明类型是对象真实类型。将真实对象的type_info和目标转换类型Dest的type_info做继承关系校验是否是目标类型本身是否公有继承自目标类型支持向上、向下、交叉转换如果是多重 / 虚继承还要计算内存偏移调整指针地址。校验不通过 → 返回nullptr指针版本重点判断的是指向对象的真实实际类型不是指针 / 引用的静态声明类型。虚函数若增加虚函数sizeof一个类对象会发现比原来大4个字节。多出来的这4个字节就是实现虚函数的关键----虚函数表指针vptr。这个指针指向一张名为虚函数表vtbl的表而表中的数据则为函数指针存储了虚函数fun_b()具体实现所对应的位置。注意普通函数、虚函数、虚函数表都是同一个类的所有对象公有的只有成员变量和虚函数表指针是每个对象私有的sizeof的值也只包括vptr和var所占内存的大小并且vptr通常会在对象内存的最起始位置。另外当类有多个虚函数时仍然只有一个虚函数表指针vptr指向一个虚函数表而此时的虚函数表vtbl中会有多个函数指针分别指向对应的虚函数实现区域。因此虚函数实现的过程是通过对象内存中的虚函数指针vptr找到虚函数表vtbl再通过vtbl中的函数指针找到对应虚函数的实现区域并进行调用。所以虚函数的调用时由指针所指向内存块的具体类型决定的。把一个函数声明为基类的虚函数就是告诉编译器派生于这个基类的任何类该函数调用都是动态的把类描述为多态意味着其至少包含一个虚函数。需要注意对象调用虚函数总是静态解析只有引用或者指针调用虚函数才会进行动态解析。多态介绍完前面我们可以更明确的理解多态这个概念了多态是一种极为强大的机制我们常常事先不能确定处理哪种类型的对象只能在运行期间确定使用多态可以轻松解决这个问题。例如画图程序基类定义了draw函数但具体画方块或者圆圈取决于派生类那么就可以用基类指针存储用户创建对象的地址调用draw函数绘制相应的图形。例#include iostream #include typeinfo using namespace std; class CBasic { public: virtual void test() { cout CBasic Called endl; } }; class CDerived : public CBasic { public: void test(){ cout CDerived Called endl; } int x; CDerived (int x) { this-x x; } void test1(){ cout CDerived1 Called endl; } }; int main() { CBasic cBasic; CDerived cDerived(10); CBasic *pB1 new CBasic; CBasic *pB2 new CDerived(10); cout The size of the instance is. endl; cout sizeof(pB1) endl; cout sizeof(pB2) endl; CDerived *pD1 dynamic_castCDerived * (pB1); couttypeid(CBasic).name()endl; couttypeid(CDerived).name()endl; couttypeid(*pB1).name()endl; couttypeid(pB1).name()endl; couttypeid(*pB2).name()endl; couttypeid(pB2).name()endl; if(pD1NULL) coutpointer pD1 is NULLendl; else coutpB1 to pD1 cast successed!endl; CDerived *pD2 dynamic_castCDerived * (pB2); if(pD2NULL) coutpointer pD2 is NULLendl; else coutpB2 to pD2 cast successed!endl; CDerived *pD3 new CDerived(10); CBasic *pB dynamic_castCBasic * (pD3); if(pBNULL) coutpointer pB is NULLendl; else coutpD3 to pB cast successed!endl; CDerived *pD4 static_castCDerived * (pB1); if(pD4NULL) cout pointer pD4 is NULLendl; else cout pointer pD4 cast successed!endl; cout pD2-x endl; cout pD4-x endl; pD4-test1(); return 0; }结果The size of the instance is. 8 8 6CBasic 8CDerived 6CBasic P6CBasic 8CDerived P6CBasic pointer pD1 is NULL pB2 to pD2 cast successed! pD3 to pB cast successed! pointer pD4 cast successed! 10 0 CDerived1 Called

相关新闻

C++11介绍之移动构造函数、move函数和右值引用

C++11介绍之移动构造函数、move函数和右值引用

移动构造函数和右值引用是C11新增的特性,可以减少资源开销,提升效率。右值引用首先给一个右值引用声明的例子:​ std::string&& r5("Hello World!!"); 这里右值引用r5绑定了一个临时变量,临时变量的内容为"…

2026/10/27 14:35:14 阅读更多 →
2026年自己做一个小程序商城怎么做?工具选择、搭建步骤与运营

2026年自己做一个小程序商城怎么做?工具选择、搭建步骤与运营

一、引言:从“自己拖拽页面”到“自己持续经营”,自助搭建要看完整能力自己做一个小程序商城,并不意味着所有环节都要从零写代码。对多数中小商家来说,更现实的方式是使用标准化后台配置商品、订单、支付、会员和页面,…

2026/10/27 5:21:01 阅读更多 →
如何通过zotero-style插件让文献管理变得生动有趣:5个视觉化创新功能

如何通过zotero-style插件让文献管理变得生动有趣:5个视觉化创新功能

如何通过zotero-style插件让文献管理变得生动有趣:5个视觉化创新功能 【免费下载链接】zotero-style Ethereal Style for Zotero 项目地址: https://gitcode.com/GitHub_Trending/zo/zotero-style 还在为枯燥的文献管理界面感到疲惫吗?zotero-sty…

2026/10/25 17:53:07 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/27 17:33:32 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/28 7:28:00 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/28 9:02:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/27 18:20:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/28 9:00:54 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/28 7:29:02 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 13:55:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 9:01:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 19:19:37 阅读更多 →