C++ RTTI 机制

本文介绍了C++中的RTTI机制,重点讨论了dynamic_cast的实现原理、应用场景,以及typeid的功能和使用注意事项。dynamic_cast用于多态类型的安全转换,依赖于类的虚函数表中的type_info信息。typeid则提供对象的类信息,适用于非强制转换的值,运行时开销较小。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RTTI(Run Time Type information): 是编译器在 编译期间 生成的特殊类型信息(type_info),包括对象继承关系,对象本身的描述,RTTI 是为多态而生成的信息,所以 只有具有虚函数的对象才会生成 RTTI。

在 C++ 中,有以下两个函数用于 运行时类型检测


一、dynamic_cast


语法:

dynamic_cast < new_type > ( expression )

返回类型为 new_type 的值 或 抛出一个异常。

  • new_type

    new_type 必须是指向完整类类型或 void * 的指针或引用。必须是类的指针或引用,或 void*。因为没有对 void 的引用。

  • expression

    expression 必须是指向完整类类型的指针或引用。而且,expression 所代表的类必须包含虚函数。

如果转换成功,dynamic_cast 将返回一个 new_type 的值。 如果转换失败,而且 new_type 是指针,那么将返回NULL; 而如果 new_type 是引用,那么将抛出异常 std::bad_cast。

1.1 实现原理

C++ 对象模型 中 , 我们知道,在包含虚函数的类中,会产生一张虚函数表,而虚函数表的前面,还会有一部分额外的信息,用于记录与类相关的信息 —— type_info,比如说,对象继承关系,对象本身的描述等等。


这里写图片描述

dynamic_cast 能否转换成功,所依据的就是这一部分 type_info 信息。具体可参考 C++ dynamic_cast实现原理

dynamic_cast 的时间和空间代价是相对较高的,在设计时应避免使用。可参考 How expensive is RTTI?

如果整个工程都不需要 dynamic_cast,可以禁用运行时类型信息(vs2008默认是启用的)。

禁用方法如下:

依次选择【工程属性】、【配置属性】、【C/C++】、【语言】。将【启用运行时类型信息】改为”否“。

1.2 应用场景

dynamic_cast 常用于有“父子”关系的两个类对象指针或引用之间进行转换。而且,只能在有虚函数的类层次之间使用 dynamic_cast 。

如下一段代码:

class Interface
{
public:
        virtual void GenericOp() = 0;
};

class SpecificClass : public Interface
{
public:
        virtual void GenericOp();
        virtual void SpecificOp();
};

我们定义一个Interface 类型的指针:

Interface * ptr = new SpecificClass; 
// 可以用基类指针指向派生类对象,因为这是安全的。

// 而不能用派生类指针指向基类对象,因为可能调用基类不存在的方法,是不安全的。

ptr 指针指向的实际类型是 SpecificClass 对象,如果我们希望通过 ptr 指针调用 SpecificClass::SpecificOp 方法,为了保证基类指针顺利安全的调用派生类方法。需要使用 dynamic_cast 进行动态类型转换:

SpecificClass* ptr_spec = dynamic_cast < SpecificClass * >(ptr);
if( ptr_spec )
{
    ptr_spec->SpecificOp();
}
else
{
    ptr->GenericOp();
};

dynamic_cast 是如何保证安全的? dynamic_cast 会对待检测指针进行类型检测,只有 ptr 指向的对象确实是 SpecificClass 类型(或者包含) ,才能成功转换,否则返回 NULL 或者 抛出 std::bad_cast 异常(当转换对象为NULL的引用)。

dynamic_cast 小结:

  • 没有虚函数,不能动态转换。

  • 转换的依据是,type_info 信息。

  • 如果 expression 所指对象的实际类型就是 new_type,则转换成功。 如: 基类指针(假如指向派生类对象),转换为派生类对象时,转换成功。

  • 如果 expression 所指对象的实际类型“包含” new_type,则转换成功。 如:A 派生了 B, B 派生了 C, A * ap = new C; B * bp = dynamic<B*>(ap) ;

  • 如果 expression 和 new_type 之间毫无关系,则转换失败。

  • expression 指向的类一定要包含虚函数,否则编译出错。new_type 只要保证是类的指针或者引用,就不会编译出错。

可以用 typeid 在语义上实现 dynamic_cast :

template<typename T, typename T1>
T1 *my_dynamic_cast(T *t){
    if(typeid(t) == typeid(T1)){
        return (T1*)t;
    } else{
        return NULL;
    }
}


二、typeid


函数原型:

type_info & typeid( object );

typeid 的主要作用是获得object变量的类型;它将返回一个type_info 对象的引用,type_info用于描述对象。如果object是对一个NULL指针的解引用,那么将抛出std::bad_typeid异常。

当需要获得一个对象的类信息时,总是选择typeid 而不是 dynamic_cast因为typeid检测一个类型或者非强制转换的值时,只耗费常数时间。而 dynamic_cast 需要在运行时,对对象进行参数推导。

typeid 只对多态类型(该类至少包含一个virtual成员函数)有效,而且参数 object 是指针的解引用(typeid(*ptr) )或者对象引用( typeid(ref) )。因为只有包含虚函数的类指针(或者引用)才需要运行时检测,而其他的类型在编译时便能确定。

#include <iostream>
#include <typeinfo>  //for 'typeid' to work

class Person {
public:
   // ... Person members ...
   virtual ~Person() {}
};

class Employee : public Person {
   // ... Employee members ...
};

int main () {
   Person person;
   Employee employee;
   Person *ptr = &employee;

   std::cout << typeid(person).name() << std::endl;   // Person (statically known at compile-time)
   std::cout << typeid(employee).name() << std::endl; // Employee (statically known at compile-time)
   std::cout << typeid(ptr).name() << std::endl;      // Person * (statically known at compile-time)
   std::cout << typeid(*ptr).name() << std::endl;     // Employee (looked up dynamically at run-time
                                                      //           because it is the dereference of a
                                                      //           pointer to a polymorphic class)
}

结果如下:

Person
Employee
Person*
Employee
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值