C++学习笔记二十一-运行时类型识别
一、通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型。
通过下面两个操作符提供 RTTI:
1.typeid 操作符,返回指针或引用所指对象的实际类型。
2.dynamic_cast 操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
这些操作符只为带有一个或多个虚函数的类返回动态类型信息,对于其他类型,返回静态(即编译时)类型的信息,对于带虚函数的类,在运行时执行 RTTI 操作符,但对于其他类型,在编译时计算 RTTI 操作符。
3.当具有基类的引用或指针,但需要执行不是基类组成部分的派生类操作的时候,需要动态的强制类型转换。通常,从基类指针获得派生类行为最好的方法是通过虚函数。当使用虚函数的时候,编译器自动根据对象的实际类型选择正确的函数。
4.但是,在某些情况下,不可能使用虚函数。在这些情况下,RTTI 提供了可选的机制。然而,这种机制比使用虚函数更容易出错:程序员必须知道应该将对象强制转换为哪种类型,并且必须检查转换是否成功执行了。
5.使用动态强制类型转换要小心。只要有可能,定义和使用虚函数比直接接管类型管理好得多
二、dynamic_cast 操作符:
1.dynamic_cast 操作符将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或指针。与 dynamic_cast 一起使用的指针必须是有效的——它必须为 0 或者指向一个对象。
2.与其他强制类型转换不同,dynamic_cast 涉及运行时类型检查。如果绑定到引用或指针的对象不是目标类型的对象,则 dynamic_cast 失败。如果转换到指针类型的 dynamic_cast 失败,则 dynamic_cast 的结果是 0 值;如果转换到引用类型的 dynamic_cast 失败,则抛出一个 bad_cast 类型的异常。
3.因此,dynamic_cast 操作符一次执行两个操作。它首先验证被请求的转换是否有效,只有转换有效,操作符才实际进行转换。一般而言,引用或指针所绑定的对象的类型在编译时是未知的,基类的指针可以赋值为指向派生类对象,同样,基类的引用也可以用派生类对象初始化,因此,dynamic_cast 操作符执行的验证必须在运行时进行。
4.可以对值为 0 的指针应用 dynamic_cast,这样做的结果是 0。
5.在条件中执行 dynamic_cast 保证了转换和其结果测试在一个表达式中进行。
三、使用 dynamic_cast 和引用类型:
1.使用 dynamic_cast 将基类引用转换为派生类引用,这种 dynamic_cast 操作的形式如下:
dynamic_cast< Type& >(val)
只有当 val 实际引用一个 Type 类型对象,或者 val 是一个 Type 派生类型的对象的时候,dynamic_cast 操作才将操作数 val 转换为想要的 Type& 类型。
2.因为不存在空引用,所以不可能对引用使用用于指针强制类型转换的检查策略,相反,当转换失败的时候,它抛出一个 std::bad_cast 异常,该异常在库头文件 typeinfo 中定义。
四、typeid 操作符
1.typeid 表达式形如:
typeid(e)
这里 e 是任意表达式或者是类型名。
2.如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态编译时类型。例如,如果表达式对基类指针解引用,则该表达式的静态编译时类型是基类类型;但是,如果指针实际指向派生类对象,则 typeid 操作符将说表达式的类型是派生类型。
3.typeid 操作符可以与任何类型的表达式一起使用。内置类型的表达式以及常量都可以用作 typeid 操作符的操作数。如果操作数不是类类型或者是没有虚函数的类,则 typeid 操作符指出操作数的静态类型;如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型。
4.只有当 typeid 的操作数是带虚函数的类类型的对象的时候,才返回动态类型信息。测试指针(相对于指针指向的对象)返回指针的静态的、编译时类型。
5.如果指针 p 的值是 0,那么,如果 p 的类型是带虚函数的类型,则 typeid(*p) 抛出一个 bad_typeid 异常;如果 p 的类型没有定义任何虚函数,则结果与 p 的值是不相关的。正像计算表达式 sizeof一样,编译器不计算 *p,它使用 p 的静态类型,这并不要求 p 本身是有效指针。
五、RTTI 的使用:
1.type_info 类:type_info 类的确切定义随编译器而变化
2.因为打算作基类使用,type_info 类也提供公用虚析构函数。如果编译器想要提供附加的类型信息,应该在 type_info 的派生类中进行。
3.默认构造函数和复制构造函数以及赋值操作符都定义为 private,所以不能定义或复制 type_info 类型的对象。程序中创建 type_info 对象的唯一方法是使用 typeid 操作符。
4.name 函数为 type_info 对象所表示的类型的名字返回 C 风格字符串。给定类型所用的值取决于编译器,具体来说,无须与程序中使用的类型名字匹配。对 name 返回值的唯一保证是,它为每个类型返回唯一的字符串。
5.name 返回的格式和值随编译器而变化。