1. 背景
RTTI的英文全称是"Runtime Type Identification",中文称为"运行时类型识别",它指的是程序在运行的时候才确定需要用到的对象是什么类型的。用于在运行时(而不是编译时)获取有关对象的信息。
在C++中,由于存在多态行为,基类指针或者引用指向一个派生类,而其指向的真正类型,在编译阶段是无法知道的:
Base *b = new Derived;
Base &b1 = *b;
在上述代码中,如果想知道b的具体类型,只能通过其他方式,而RTTI
正是为了解决此问题而诞生,也就是说在运行时,RTTI可以通过特有的方式来告诉调用方其所调用的对象具体信息,一般有如下几种:
-
typeid
操作符; -
type_info
类; -
dynamic_cast
操作符;
2. typeid 和 type_info
typeid
是C++的关键字之一,等同于sizeof
这类的操作符。用来获取类型、变量、表达式的类型信息,适用于C++基础类型、内置类、用户自定义类、模板类等。有如下两种形式:
-
typeid(type)
-
typeid(expr)
#include <cassert>
#include <iostream>
#include <typeinfo>
class Base {
public:
virtual float f() {
return 1.0;
}
virtual ~Base() {}
};
class Derived : public Base {
};
int main() {
Base* p = new Derived;
Base& r = *p;
assert(typeid(p) == typeid(Base*));
assert(typeid(p) != typeid(Derived*));
assert(typeid(r.f()) == typeid(float));
const char *name = typeid(p).name();
std::cout << name << std::endl;
return 0;
}
typeid()
会返回一个const std::type_info&
对象,其中存储这对象的基本信息,那么如果其类型对象为多态和非多态时候,其又有什么区别呢?
如果类型对象至少包含一个虚函数,那么typeid
操作符的类型是运行时的事情,也就是说在运行时才能获取到其真正的类型信息;否则,在编译期就能获取其具体类型,甚至在某些情况下,可以对typeid()
的结果直接进行替换。
我们知道经常用于运行时,也就是说在运行时刻才会知道其指针或者引用指向的具体类型,如果要对一个包含虚函数的对象获取其类型信息(typeid),那么也是在运行时才能具体知道。
-
对于存在虚函数的类来说,其对象的typeinfo信息存储在该类的虚函数表中。在运行时刻,根据指针的实际指向,获取其
typeinfo()
信息,从而进行相关操作。 -
在编译期,编译器已经知道了对象的具体信息,进而可以在某些情况下,直接由编译器进行替换(比如
typeinf().name()
操作等)。
3. dynamic_cast
对于dynamic_cast
,如果操作失败了会有什么行为?
- 对于指针类型转换,如果失败,则返回NULL,而对于引用,转换失败就抛出bad_cast。
作为C++开发人员,基本都知道dynamic_cast
是C++中几个常用的类型转换符之一,其通过类型信息(typeinfo)进行相对安全的类型转换,在转换时,会检查转换的src对象是否真的可以转换成dst类型。dynamic_cast
转换符只能用于含有虚函数的类,因此其常常用于运行期,对于不包括虚函数的类,完全可以使用其它几个转换符在编译期进行转换。通常来说,其类型转换分为向上转换和向下转换两种。
3.1 dynamic_cast 向上转换
dynamic_cast
操作中,向上转换是静态操作,在编译阶段完成。在汇编代码中,没有外部函数调用(__dynamic_cast
),而仅仅是一些常用的跳转和比较指令。
3.2 dynamic_cast 向下转换
向下转换则需要调用__dynamic_cast
函数:
__dynamic_cast (const void *src_ptr, // src_ptr代表需要转换的指针
const __class_type_info *src_type, // src_type原始类型
const __class_type_info *dst_type, // dst_type目标类型
ptrdiff_t src2dst) // src2dst表示从dst到src的偏移量
这个函数先通过src_ptr来初始化部分局部变量:
-
vtable 通过对src_ptr解引用(deref)获取
-
vtable_prefix 子对象虚函数表地址,通过vtable的类型信息和offset_to_top来获取
-
whole_ptr src_ptr最底层的派生类地址,一般为src_ptr的值加上offset_to_top
-
whole_type src_ptr最底层的派生类的虚函数表中的类型信息(type info)
-
whole_vtable whole对象的虚函数表地址
然后调用whole_type->__do_dyncast,而这也是该函数的核心模块。然后根据返回值的内容来判断结果,并进行相应的操作。
dynamic_cast的过程就是遍历图结构确定路径关系的过程,采用的是深度优先搜索。
标签:typeid,src,dynamic,C++,cast,Base,RTTI,type From: https://www.cnblogs.com/love-9/p/18086992