即运行阶段类型识别(Runtime Type Identification)
C++有三个支持RTTI的元素
1.dynamic_cast,使用一个基类的指针生成一个指向派生类的指针,失败返回0,即空指针。
尖括号表示括号中的类型能否安全的转为尖括号中的类型
本质上就是个更加规范的类型转换
2.typeid, 返回一个指出对象类型的值(可接受类名和结果为对象的表达式)
3.type_info结构体,存储了有关特定类型的信息。
dynamic_cast
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; class Grand { private: int hold; public: explicit Grand(int h = 0) : hold(h) {} virtual void Speak() const { cout << "I am a Grand class\n"; } virtual int Value() const { return hold; } }; class Superb : public Grand { public: explicit Superb(int h) : Grand(h) {} void Speak() const override { cout << "I am a Superb class\n"; } virtual void Say() const { cout << "I hold the superb value of " << Value() << endl; } }; class Magnificent : public Superb { private: char ch; public: explicit Magnificent(int h=0, char c = 'A') : Superb(h), ch(c) {} void Speak() const override { cout << "I am a Magnificent class\n"; } void Say() const override { cout << "I hold the char" << ch << "and the int " << Value() << endl; } }; Grand * GetOne(); int main() { srand(time(NULL)); //基类指针 Grand * pg; //派生类指针 Superb * ps; for(int i=0;i<5;i++){ //基类指针随机指向一个对象 pg = GetOne(); pg->Speak(); //RTTI判断能否安全转换 //使用一个基类指针(pg)生成一个派生类指针(ps) if(ps = dynamic_cast<Superb*>(pg)){ ps->Say(); }else cout << "不安全的转换" << endl; } return 0; } Grand * GetOne() { Grand * p; switch (rand() % 3) { case 0 : p = new Grand(rand()%100); break; case 1 : p = new Superb(rand()%100); break; case 2 : p = new Magnificent(rand()%100, 'A'+rand()%26); break; } return p; }
运行结果
因为派生类指针不能指向基类,所以在这里
不会调用Say()函数。这里的功劳就要归功于dynamic_cast<Superb*>(pg)他了
关于这里的语法可能有一些奇怪,赋值表达式的值是他的左值,如果转换成功,则ps的值为非零(true)。如果类型转换失败,则返回一个空指针(0,即false)。
特别的,当用引用作为参数时,应该用try,catch语句捕获错误。
也可用typeid函数,不用dynamic_cast,改为分别的以下判断并且使用强制转换。
//注意括号里的是对象,不是对象指针 typeid(Superb) == typeid(*pg);
但其实这样写不仅代码变长,也存在一些缺陷,比如需要靠大量的else if维护(或者switch)。
typeid 运算符 和 type_info 结构体
前者能够确定两个对象是否为同种类型,并返回一个后者对象的引用(<typeinfo>中定义)。
type_info类重载了 == 和 != 。
如果有空指针比较(*pg(nullptr)),引发bad_typeid异常(异常类型从exception派生而来)。
其中type_info的定义
class type_info { private: type_info(const type_info&); type_info& operator=(const type_info&);//type_info类的复制构造函数和赋值运算符是私有的。 public: virtual ~type_info();//析构函数 bool operator==(const type_info&)const;//在type_info类中重载了==运算符,该运算符可以比较两个对象的类型是否相等。 bool operator!=(const type_info&)const;//重载的!=运算符,以比较两个对象的类型是否不相等 const char* name()const;//使用得较多的成员函数name,该函数返回对象的类型的名字。前面使用的typeid(a).name()就调用了该成员函数 bool before(const type_info&); };
对上面的程序进行小小的修改
if(ps = dynamic_cast<Superb*>(pg)){ ps->Say(); }else cout << "不安全的转换" << endl; if(typeid(*pg)==typeid(Magnificent)){ cout << "生成了一个Magnificent类,好耶!"; //type_inf定义的类方法,返回const char *(即类名) cout << "use .name : " << typeid(*pg).name() << endl; }
输出结果