1、什么是RTTI?
RTTI:Run-Time Type Identification运行时类型识别
程序在运行时能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际的派生类。
在这里要用到两个C++中的关键字:typeid和dynamic_cast
2、typeid
typeid是C++中一个用来获取变量类型的关键字,用法跟使用函数一样 typeid(xxx)
typeid注意事项:
(1)typeid返回的是一个type_info对象的引用,而这个引用是与具体的变量类型有关的,
也就是说不同的变量类型都有一个type_info结构体。type_info结构体如下:
class type_info{ public: const char* name() const; // 打印具体类型的方法 一般这样用 typeid(x1).name() bool operator==(const type_info& rhs) const; // 运算符重载 支持直接 typeid(x1) == typeid(x2) bool operator!=(const type_info& rhs) const; // 运算符重载 int before(const type_info& rhs) const; virtual ~type_info(); // 析构函数 private: ...... };
(2)xxx可以是一个变量名(typeid(a)),也可以是一个具体的变量类型(typeid(int)),通常是用来做比较的时候,如下例子所示:
int i = 0; cout << typeid(i).name() << endl; // int型变量 i cout << typeid(&i).name() << endl; // int *变量 &i if (typeid(i) == typeid(int)) { }
(3)如果我们想通过基类的指针获得派生类的数据类型,基类必须带有虚函数,否则获取的就是基类的类型,并且只能够去获取派生类对象的实际类型,
也就是说我们typeid传参的时候只能是对象类型或者是对象引用,不能是对象指针(注意我这里说的是你想要通过基类获取派生类对象数据类型)
背景:因为我们可以通过基类的指针变量指向一个派生类变量的地址,而typeid关键字提供了使用基类指针去获取派生类对象的数据类型。
1 #include <iostream> 2 using namespace std; 3 4 class Fly1{ 5 public: 6 virtual void fly(void); // Fly1基类中含有虚函数 7 }; 8 9 class Fly2{ 10 public: 11 void fly(void) ; // Fly2基类中不含虚函数 12 }; 13 14 class Plane1 : public Fly1 // Plane1派生类继承于Fly1 15 { 16 public: 17 virtual void fly(void); 18 void transport(void); 19 }; 20 21 class Plane2 : public Fly2 // PLane2派生类继承于Fly2 22 { 23 public: 24 virtual void fly(void); 25 void transport(void); 26 }; 27 28 /****************************************/ 29 void Fly1::fly(void) 30 { 31 cout << "fly---Fly1" << endl; 32 } 33 34 void Fly2::fly(void) 35 { 36 cout << "fly---Fly2" << endl; 37 } 38 39 void Plane1::fly(void) 40 { 41 cout << "fly---Plane1" << endl; 42 } 43 44 void Plane2::fly(void) 45 { 46 cout << "fly---Plane2" << endl; 47 } 48 49 int main(void) 50 { 51 Fly1 *fly1 = new Plane1(); 52 Fly2 *fly2 = new Plane2(); 53 54 cout << typeid(fly1).name() << endl; 55 cout << typeid(*fly1).name() << endl; 56 cout << typeid(fly2).name() << endl; 57 cout << typeid(*fly2).name() << endl; 58 59 return 0; 60 }
运行结果如下:
因为基类Fly1中含有虚函数,并且fly1是一个指针,所以第一行显示就是基类指针变量的类型,而第二行因为传参就是指针指向的对象(也就是对象实际类型),所以能够获取到派生类对象的类型
因为基类Fly2中不含虚函数,所以不管传入的是对象指针还是对象本身,获取的都是基类对应的类型,而不能获取到派生类对象的类型。
所以说要通过基类指针获取派生类对象的类型:第一条件是基类中含有虚函数 第二条件:只能获取派生类对象的类型,不能获取派生类对象指针的类型。
3、dynamic_cast
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
(1)用法:dynamic_cast<type-id>(expression) type-id就是要转换的类型,expression就是待转换的变量,所以dynamic_cast就是将expression变量转化 为type-id类型的变量,type-id必须是类的指针、类的引用或者是void *。
(2)返回值:对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;
对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。
(3)多态类型间转换的3种类型: 子类转为父类的向上转换、父类转为子类的向下转换、平行类间的横向转换(指的是多继承的情况下,各个父类之间的转换)
其中向下转换和横行转换要求被转换的类中必须含有虚函数,否则不能转换成功;而向上转换并没有这个要求。
1 #include <iostream> 2 using namespace std; 3 4 class tao1 5 { 6 public: 7 virtual void a(void) { cout << "a---tao1" << endl; } 8 }; 9 10 class tao2 11 { 12 public: 13 virtual void a(void) { cout << "a---tao2" << endl; } 14 }; 15 16 class tao3 : public tao2, public tao1 17 { 18 public: 19 virtual void a(void) { cout << "a---tao3" << endl; } 20 }; 21 22 23 int main(void) 24 { 25 /*向上转换*/ 26 // tao3 *pTao3 = new tao3(); 27 // tao1 *pTao1 = dynamic_cast<tao1 *>(pTao3); 28 29 /*向下转换*/ 30 // tao1 *pTao1 = new tao3(); 31 // tao3 *pTao3 = dynamic_cast<tao3 *>(pTao1); // 要求被转换的类中函数虚函数 32 33 /*横向转换*/ 34 tao1 *pTao1 = new tao3(); 35 tao2 *pTao2 = dynamic_cast<tao2 *>(pTao1); // 要求被转换的类中含有虚函数 36 37 return 0; 38 }
代码如下:
4、使用typeid和dynamic_cast构建RTTI
背景:在程序中常常需要这样的操作,当我们的一个函数的参数是一个基类指针的时候,而我们调用这个函数传入的参数是一个派生类指针
所以我们需要在函数中通过typeid判断我们的派生类对象的类型,然后通过dynamic_cast将我们的基类对象转换为派生类对象,从而可以
调用派生类中相应的方法进行操作。
代码如下:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class Fly{ 6 public: 7 virtual void fly(void) = 0; 8 }; 9 10 class Plane : public Fly 11 { 12 public: 13 virtual void fly(void); 14 void transport(void); 15 }; 16 17 class Bird : public Fly 18 { 19 public: 20 virtual void fly(void); 21 void eat(void); 22 }; 23 24 /*******************************************/ 25 void Plane::fly(void) 26 { 27 cout << "fly---Plane" << endl; 28 } 29 30 void Plane::transport(void) 31 { 32 cout << "transport---Plane" << endl; 33 } 34 35 /***************************************************/ 36 void Bird::fly(void) 37 { 38 cout << "fly---Bird" << endl; 39 } 40 41 void Bird::eat(void) 42 { 43 cout << "eat---Bird" << endl; 44 } 45 46 /*********************************************/ 47 static void doSomeThing(Fly *obj) 48 { 49 obj->fly(); 50 51 if (typeid(*obj) == typeid(Plane)) // 判断传进来的指针指向的对象的类型 52 { 53 Plane *ptr = dynamic_cast<Plane *>(obj); // 转换类型 54 ptr->transport(); 55 } 56 57 if (typeid(*obj) == typeid(Bird)) 58 { 59 Bird *ptr = dynamic_cast<Bird *>(obj); 60 ptr->eat(); 61 } 62 } 63 64 int main(void) 65 { 66 Plane pla; 67 Bird bir; 68 doSomeThing(&pla); 69 doSomeThing(&bir); 70 return 0; 71 }
运行结果如下: