C++ Rtti

C++ RTTI详解
本文详细介绍了C++中的RTTI(运行时刻类型识别)概念,包括dynamic_cast操作符的使用场景及其运行时刻与编译时刻的行为区别,以及typeid操作符如何帮助获取对象的实际类型。

C++ Rtti

文章来源网络 属于 c/c++ 分类 电脑编程网整理 200918
简 介:这是c++ rtti(run-time type indentifiation,运行时刻类型识别)的详细页面,介绍了和c/c++,,c++,rtti(run- time,type,indentifiation,运行时刻类型识别)有关的知识,谢谢大家的观看!要查看更多有关信息,请点击此处

c++ rtti(run-time type indentifiation,运行时刻类型识别)

 

rtti 运行时刻类型识别允许用指向基类的指针或引用来操纵对象的程序能够获
取到这些指针或引用所指对象的实际派生类型在c++中为了支持rtti 提供了两个
操作符dynamic_cast和typeid

 

1 dynamic_cast 操作符可以用来把一个类类型对象的指针转换成同一类层次结构中的其他
类的指针同时也可以用它把一个类类型对象的左值转换成同一类层次结构中其他类的引用.

 

 

 

1.1 当dynamic_cast操作符的操作数是带有虚拟函数的类类型指针和左值时
dynamic_cast 是在运行时刻执行的.
并且如果指针或左值操作数不能被转换成目标类型,
dynamic_cast在运行时将失败.
 
针对指针类型的dynamic_cast失败,dynamic_cast 的结果是0
针对引用类型的dynamic_cast失败,dynamic_cast 的结果是抛出一个异常.

和 "c++ rtti(run-time type indentifiation,运行时刻类型识别)" 有关的c#、asp.net、c++ 编程小帖士:

strong>DateValue(datetime)
取得符合国别设定样式的日期值,并包含时间。

void company::payroll( employee *pe )
{
  programmer *pm = dynamic_cast< programmer* >( pe );
  if ( pm ) { 
    // dynamic_cast 成功使用 programmer 的成员函数
  } else {
    // dynamic_cast 失败使用 employee 的成员函数
  }
}

 


void company::payroll( employee &re )
{
  try {
    programmer &rm = dynamic_cast< programmer & >( re );
    // 用 rm 调用 programmer::bonus()
  } catch ( std::bad_cast ) {
    // 使用 employee 的成员函数
  }
}

 

 

 

1.2 当dynamic_cast操作符的操作数不是是带有虚拟函数的类类型指针和左值时
dynamic_cast 是在编译时刻执行的.
并且如果指针或左值操作数不能被转换成目标类型,编译时就将失败.
例如
class employee {                          
public:
  employee() {};
  ~employee() {};
//成员函数都是非虚的
};

 

class programmer : public employee{                          
public:
  programmer() {};
  ~programmer() {};
//成员函数都是非虚的,但有可能存在重载
};

 

employee eobj;

 

void company::payroll( employee *pe )
{
  programmer *pm = dynamic_cast< programmer* >( pe );
  if ( pm ) { 
    // dynamic_cast 成功使用 programmer 的成员函数
  } else {
    // dynamic_cast 失败使用 employee 的成员函数
  }
}

 

payroll(&eobj); //编译时刻执行dynamic_cast,编译出错,

 

programmer pobj;
payroll(&pobj); //编译时刻执行dynamic_cast,编译ok.

 

 

 


2 typeid 操作符指出指针或引用指向的对象的实际派生类型
typeid 操作符必须与表达式或类型名一起使用

 

 

 

2.1 当typeid操作符的操作数是类类型并带有虚拟函数的类类型时
typeid 操作符会指出操作数的底层真正对象的类型
#include <type_info>
programmer pobj;
employee eobj;
employee* pe = &pobj;
employee &re = pobj;

 

cout << typeid(*pe).name() << endl;   // 操作符指出指针指向的对象的实际派生类型 name() 返回 programmer.name()
cout << typeid(re).name() << endl;   // 操作符指出引用指向的对象的实际派生类型 name() 返回 programmer.name()

 

 

 

2.2 当typeid操作符的操作数不是类类型时typeid 操作符会指出操作数的类型
例如内置类型的表达式和常量可以被用作typeid 的操作数
int iobj;
cout << typeid( iobj ).name() << endl; // 打印: int
cout << typeid( 8.16 ).name() << endl; // 打印: double

 

 

 

2.3 当typeid操作符的操作数是类类型但不是带有虚拟函数的类类型时
typeid 操作符会指出操作数的类型而不是底层对象的类型
class base { /* 没有虚拟函数 */ };
class derived : public base { /* 没有虚拟函数 */ };
derived dobj;
base *pb = &dobj;
cout << typeid( *pb ).name() << endl; // 打印: base

 


2.4 可以对typeid 的结果进行比较.

 

#include <type_info>
programmer pobj;
employee eobj;
employee* pe = &pobj;

 

if ( typeid( pe ) == typeid( employee* ) ) // true
if ( typeid( pe ) == typeid( programmer* ) ) // false

 

if ( typeid( *pe ) == typeid( employee ) ) // false
if ( typeid( *pe ) == typeid( programmer ) ) // true

 

看出true和false不同结果了吗?
前者是typeid操作符对不是类类型(指针类型)的操作.
后者是typeid操作符对类类型的操作.

 

 

 

3. 另外需要指出的是对于带有虚拟函数的类而言rtti 操作符是
运行时刻的事件(如上所述)而对于其他类而言它只是编译时刻的事件
因此1.1和2.1的情况是运行时刻的转换事件
而1.1和2.2,2.3的情况是运行时刻的转换事件

08-02
<think>我们正在讨论C++ RTTI(运行时类型识别)。根据引用内容,RTTI是一种在运行时获取对象类型信息的机制。它主要包含`dynamic_cast`、`typeid`和`type_info`等关键字和类。 首先,用户询问C++ RTTI是什么以及如何使用。我们需要从基本概念出发,然后介绍使用方法。 ### 步骤1:解释RTTI是什么 RTTI(Runtime Type Information)允许程序在运行时获取对象的类型信息。这在处理多态时特别有用,因为有时我们需要知道一个基类指针或引用实际指向的是哪个派生类的对象。 ### 步骤2:介绍RTTI的组成部分 根据引用[1]和[3]: - `dynamic_cast`:用于在继承层次结构中安全地进行类型转换。如果转换失败(比如试图将基类指针转换为不相关的派生类指针),则返回空指针(对于指针类型)或抛出异常(对于引用类型)。 - `typeid`:返回一个`type_info`对象的引用,该对象包含类型信息。可以用于比较两个对象的类型是否相同。 - `type_info`:存储类型信息的类,通常由编译器生成。 ### 步骤3:使用示例 我们需要给出一些简单的代码示例来展示如何使用这些特性。 #### 示例1:使用dynamic_cast 假设有一个基类`Base`和派生类`Derived`: ```cpp class Base { public: virtual ~Base() {} // 基类必须有虚函数才能使用dynamic_cast }; class Derived : public Base { public: void specificFunction() { // 派生类特有的函数 } }; int main() { Base* basePtr = new Derived(); // 尝试将基类指针转换为派生类指针 Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); if (derivedPtr) { // 转换成功,可以调用派生类特有的函数 derivedPtr->specificFunction(); } delete basePtr; return 0; } ``` #### 示例2:使用typeid ```cpp #include <typeinfo> #include <iostream> class Base { public: virtual ~Base() {} }; class Derived : public Base {}; int main() { Base* basePtr = new Derived(); // 获取指针指向对象的实际类型信息 const std::type_info& ti = typeid(*basePtr); std::cout << "Type: " << ti.name() << std::endl; // 输出类型名(由编译器决定,可能不易读) // 比较两个对象的类型 Derived derived; if (typeid(*basePtr) == typeid(derived)) { std::cout << "Same type" << std::endl; } delete basePtr; return 0; } ``` 注意:使用`typeid`时,如果基类没有虚函数,则`typeid(*basePtr)`返回的是基类的类型信息(静态类型),而不是实际派生类的类型。因此,为了获得动态类型,基类必须至少有一个虚函数(通常将析构函数设为虚函数是好的做法)。 ### 步骤4:注意事项 - RTTI会带来一些性能开销(引用[1]),因为需要存储和访问类型信息。 - 在某些嵌入式系统或对性能要求极高的场景中,可能会禁用RTTI(通过编译器选项)。此时,使用`dynamic_cast`和`typeid`会导致编译错误。 - 引用[5]建议:尽量使用虚函数和`dynamic_cast`来替代显式的类型判断(如使用`typeid`逐一比较),因为这样更符合面向对象的设计原则,代码更简洁且扩展性更好。 ### 步骤5:生成相关问题 根据用户的问题,我们可以生成一些相关问题,帮助用户深入理解。 ### 最终回答组织 1. 解释C++ RTTI是什么。 2. 介绍RTTI的主要组成部分:`dynamic_cast`、`typeid`和`type_info`。 3. 给出使用示例。 4. 提及注意事项和性能影响。 5. 生成相关问题。</think>### C++ RTTI 详解 #### 1. **RTTI 是什么?** C++ RTTI(Runtime Type Information,运行时类型识别)是一种在程序执行期间获取对象类型信息的机制。它通过以下组件实现: - **`dynamic_cast`**:安全地在继承层次中进行向下转型(downcasting),失败时返回空指针(指针类型)或抛出异常(引用类型)[^1][^3]。 - **`typeid`**:返回对象的类型信息(`type_info`对象),包含类型名称和比较功能[^3][^4]。 - **`type_info`**:存储类型信息的类,由编译器生成[^3]。 RTTI 主要用于处理多态场景,当需要基于对象实际类型执行特定操作时(如调用非虚函数或调试)[^4]。 --- #### 2. **RTTI 的工作原理** - **虚函数表(vtable)支持**: 启用 RTTI 后,编译器会为每个含虚函数的类生成虚函数表,其中包含指向 `type_info` 对象的指针。当使用 `typeid` 或 `dynamic_cast` 时,通过对象的虚函数表查询其实际类型[^3][^4]。 例如: ```cpp class Base { virtual ~Base() {} }; // 必须有虚函数 class Derived : public Base {}; Base* obj = new Derived; const type_info& ti = typeid(*obj); // 通过虚函数表获取Derived的类型信息 ``` - **`dynamic_cast` 的工作流程**: 1. 检查源类型到目标类型的继承关系(通过编译时信息)。 2. 运行时通过虚函数表验证对象实际类型是否匹配目标类型。 3. 若匹配则返回转换后的指针,否则返回 `nullptr`[^3][^5]。 --- #### 3. **如何使用 RTTI** ##### (1) 安全向下转型:`dynamic_cast` ```cpp class Base { public: virtual ~Base() {} }; class Derived : public Base { public: void log() { /* 派生类特有方法 */ } }; Base* basePtr = new Derived; // 尝试转换为Derived类型 Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); if (derivedPtr) { // 检查转换是否成功 derivedPtr->log(); // 安全调用派生类方法 } ``` ##### (2) 类型识别:`typeid` ```cpp #include <typeinfo> #include <iostream> void checkType(Base* obj) { if (typeid(*obj) == typeid(Derived)) { // 比较实际类型 std::cout << "Object is Derived\n"; } // 输出类型名称(编译器相关,如"class Derived") std::cout << "Type: " << typeid(*obj).name() << "\n"; } ``` ##### (3) 类型信息操作:`type_info` ```cpp const std::type_info& ti1 = typeid(Derived); const std::type_info& ti2 = typeid(Base); if (ti1 != ti2) { std::cout << "Different types\n"; } ``` --- #### 4. **使用注意事项** - **必须启用虚函数**: 基类至少需有一个虚函数(如虚析构函数),否则 `typeid` 返回静态类型,`dynamic_cast` 无法使用[^3][^4]。 - **性能开销**: RTTI 会增加内存(存储类型信息)和运行时开销(类型检查),在性能敏感场景需谨慎使用[^1][^3]。 - **替代方案**: 优先使用虚函数实现多态,避免过度依赖 RTTI。例如: ```cpp class Base { public: virtual void log() {} // 用虚函数替代类型检查 }; ``` - **编译器支持**: 默认情况下,多数编译器(如 GCC、MSVC)启用 RTTI,可通过编译选项禁用(如 GCC 的 `-fno-rtti`)[^2]。 --- #### 5. **典型应用场景** 1. **调试与日志**:记录对象的实际类型信息[^4]。 2. **序列化/反序列化**:根据类型动态创建对象。 3. **框架设计**:如 GUI 库中处理事件分发(如 wxWidgets 的模板类)[^2]。 --- ### 相关问题 1. **`dynamic_cast` 与 `static_cast` 有何本质区别?** `dynamic_cast` 依赖 RTTI 进行运行时类型检查,而 `static_cast` 在编译时完成转换且无安全检查。 2. **禁用 RTTI 后如何实现安全的向下转型?** 可通过在基类添加枚举类型标识或自定义类型检查虚函数模拟 RTTI 功能。 3. **RTTI 在多重继承场景下如何处理类型转换?** 编译器会检查继承图中的所有路径,确保转换符合菱形继承等复杂结构的规则[^3]。 4. **为何某些嵌入式项目禁用 RTTI?** 主要为避免内存开销和运行时性能损失,尤其在资源受限的系统中[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值