即运行阶段类型识别(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;
}
输出结果