本文部分内容转载自:http://www.cppblog.com/smagle/archive/2010/05/14/115286.aspx
在揭开typeid神秘面纱之前,我们先来了解一下RTTI(Run-Time Type Identification,运行时类型识别),它使程序能够获取由基指针或引用所指向的对象的实际派生类型,即允许“用指向基类的指针或引用来操作对象”的程序能够获取到“这些指针或引用所指对象”的实际派生类型。在C++中,为了支持RTTI提供了两个操作符:dynamic_cast和typeid。
dynamic_cast允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转化类型,与之相对应的还有一个非安全的转换操作符static_cast,因为这不是本文的讨论重点,所以这里不再详述,感兴趣的可以自行查阅资料。下面就开始今天我们的话题:typeid。
typeid操作符的返回结果是名为type_info的标准库类型的对象的引用。
如果表达式的类型是类类型且至少包含有一个虚函数,则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。
type_info的name成员函数返回C-style的字符串,用来表示相应的类型名,但务必注意这个返回的类型名与程序中使用的相应类型名并不一定一致(往往如此,见后面的程序),这具体由编译器的实现所决定的,标准只要求实现为每个类型返回唯一的字符串。
#include<iostream> #include<string> using namespace std; class Base {}; class Derived: public Base {}; int main() { Base b, *pb; pb = NULL; Derived d; cout << typeid(int).name() << endl << typeid(unsigned).name() << endl << typeid(long).name() << endl << typeid(unsigned long).name() << endl << typeid(char).name() << endl << typeid(unsigned char).name() << endl << typeid(float).name() << endl << typeid(double).name() << endl << typeid(string).name() << endl << typeid(Base).name() << endl << typeid(b).name()<<endl << typeid(pb).name()<<endl << typeid(Derived).name() << endl << typeid(d).name()<<endl << typeid(type_info).name() << endl; return 0; }
vc输出结果如下
int unsigned int long unsigned long char unsigned char float double class std::basic_string<char,struct std::char_traits<char>,class std::allocator< char> > class Base class Base class Base * class Derived class Derived class type_info Press any key to continue
下面对上面的代码稍微添加一点内容,如下:
Base *pb2 = dynamic_cast<Base *>(new Derived); Base &b2 = d; Base *pb3 = &d; cout << typeid(pb2).name() <<endl//输出Base * << typeid(b2).name()<<endl //输出Base << typeid(pb3).name()<<endl//输出Base * << typeid(*pb3).name()<<endl;//输出Base
因为Base不包含虚函数,所以typeid的结果指出,表达式的类型是Base或Base *型,尽管他们的底层对象是Derived。即:当typeid操作符的操作数是不带有虚函数的类类型时,typeid操作符会指出操作数的类型,而不是底层对象的类型。
下面在对Base函数做一个小小调整,为其加上一个虚函数,再看输出结果。
class Base {virtual void f(){}; }; /*...*/ cout << typeid(pb2).name() <<endl//输出Base * << typeid(b2).name()<<endl //输出Derived << typeid(pb3).name()<<endl//输出Base * << typeid(*pb3).name()<<endl;//输出Derived
这次Base含有虚函数,注意看结果,指针仍然是Base*的,尽管他们指向的是底层对象Derived,而这些Base对象的类型却是Derived的。
因为指针pb3不是类类型,所以typeid就返回该指针pb3的指针类型Base *。而*pb3是一个类类型的表达式,而且该类带有虚函数,所以指出该pb3指向的底层对象的类型Derived。
如果typeid操作符的操作数是至少包含一个虚拟函数的类类型时,并且该表达式是一个基类的引用,则typeid操作符指出底层对象的派生类类型。
再看一个例子:
#include <iostream> #include <typeinfo> using namespace std; int main() { class Base1 { }; class Derive1 : public Base1 { }; Derive1 d1; Base1& b1 = d1; cout << typeid(b1).name() << endl; // 输出"class Base1",因为Derive1和Base1之间没有多态性 //下面的sample 编译时需要加参数 /GR class Base2 { virtual void fun( void ) {} }; class Derive2 : public Base2 { }; Derive2 d2; Base2& b2 = d2; cout << typeid(b2).name() << endl; // 输出"class Derive2",因为Derive1和Base1之间有了多态性 // sample class Derive22 : public Base2 { }; // 指针强制转化失败后可以比较指针是否为零,而引用却没办法,所以引用制转化失败后抛出异常 Derive2* pb1 = dynamic_cast<Derive2*>(&b2); cout << boolalpha << (0!=pb1) << endl; // 输出"true",因为b2本身确实是指向Derive2 Derive22* pb2 = dynamic_cast<Derive22*>(&b2); cout << boolalpha << (0!=pb2) << endl; // 输出"false",因为b2本身不是指向Derive2 try { Derive2& rb1 = dynamic_cast<Derive2&>(b2); cout << "true" << endl; } catch(bad_cast) { cout << "false" << endl; } try { Derive22& rb2 = dynamic_cast<Derive22&>(b2); cout << "true" << endl; } catch(bad_cast) // 应该是 bad_cast,但不知道为什么在VC++6.0中却不行?在g++里是可以的,要想在VC中运行的话,改为catch(...) { cout << "false" << endl; } return 0; }