C++中运行时类型识别(RTTI)
1、dynamic_cast操作符
将基类类型的指针或引用安全的转换为派生类类型的指针或引用。
注意:基类至少带有一个虚函数,这样源类型才可能是多态的,才会在运行时确定类型。否则将导致编译错误。
如果转换成功,则返回一个指向转换后类型的指针或引用;如果转换失败,指针类型的转换结果为0,引用类型则抛出bad_cast类型的异常。
如果是指针类型,操作数可以是0。但是没有空类型的引用。
测试代码:
#include <iostream>
#include <typeinfo>
using namespace std;
class B
{
public:
virtual void f()
{
cout<<"B::f"<<endl;
}
void ff()
{
cout<<"B::ff"<<endl;
}
};
class D:public B
{
public:
void f()
{
cout<<"D::f"<<endl;
}
void g()
{
cout<<"D::g"<<endl;
}
};
void ta()
{///pointer
B *b=0;
D *d=0;
b = new D();
b->f();///D::f
d = dynamic_cast<D*>(b);
if(d)
{ d->g(); }///D::g
else
{ b->ff(); }
b = new B();
d = dynamic_cast<D*>(b);
if(d)
{ d->g(); }
else
{ b->ff(); }///B::ff
}
void tb()
{
D d;
B c;
B &b=d;
try
{
D & dt = dynamic_cast<D&>(b);
dt.g();
}
catch(bad_cast)
{
cout<<"dynamic cast failed"<<endl;
}
B &br = c;///如果这里写b=c;将发生奇怪的现象。
try
{
D & dt = dynamic_cast<D&>(br);
dt.g();
}
catch(bad_cast)
{
cout<<"dynamic cast failed"<<endl;
}
}
int main()
{
ta();
cout<<"-------"<<endl;
tb();
return 0;
}
运行结果:
D::f
D::g
B::ff
-------
D::g
dynamic cast failed
2、typeid操作符
typeid的表达式:
typeid(e)
其中e可以是任意表达式或者类型名。
如果表达式的类型是类类型且该类包含至少1个虚函数,则表达式的动态类型(实际类型)可能不同于它的静态类型。
如果操作数的不是类类型,或者是没有虚函数的类,则typeid操作符指出操作数的静态类型。如果操作数定义了至少一个虚函数,则在运行时计算类型。
ISO C++标准并没有确切定义type_info,它的确切定义是编译器相关的,但是标准却规定了其实现必需提供如下四种操作:
t1 == t2 | 如果两个对象t1和t2类型相同,则返回true;否则返回false |
t1 != t2 | 如果两个对象t1和t2类型不同,则返回true;否则返回false |
t.name() | 返回类型的C-style字符串,类型名字用系统相关的方法产生 |
t1.before(t2) | 返回指出t1是否出现在t2之前的bool值 |
type_info类提供了public虚 析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象。程序中创建type_info对象的唯一方法是使用typeid操作符(由此可见,如果把typeid看作函数的话,其应该是type_info的 友元)。type_info的name成员函数返回C-style的字符串,用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名并不一定一致(往往如此,见后面的程序),这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符串。
测试代码:
#include <iostream>
#include <typeinfo>
using namespace std;
class B
{
public:
virtual void f()
{
cout<<"B::f"<<endl;
}
void ff()
{
cout<<"B::ff"<<endl;
}
};
class D:public B
{
public:
void f()
{
cout<<"D::f"<<endl;
}
void g()
{
cout<<"D::g"<<endl;
}
};
void tc()
{
D d;
B b;
int k=0;
cout<<typeid(k).name()<<","<<typeid(int).name()<<endl;
cout<<typeid(float).name()<<endl;
cout<<typeid(b).name()<<endl;
cout<<typeid(d).name()<<endl;
B *bp=new D();
cout<<"prt:"<<typeid(bp).name()<<endl;//直接判断指针,而不是指针所指向的对象的类型
cout<<typeid(*bp).name()<<endl;
bp = new B();
cout<<typeid(*bp).name()<<endl;
cout<<typeid(*bp).before(typeid(char))<<endl;
cout<<typeid(char).before(typeid(*bp))<<endl;
cout<<typeid(B).before(typeid(D))<<endl;
}
int main()
{
tc();
return 0;
}
结果:
i,i
f
1B
1D
prt:P1B
1D
1B
1
0
1
这里使用的是G++,vs可能与此不同。