运行时类型识别
因为dynamic_cast在运行时使用了虚函数表,所以代价高。You can also
use dynamic_cast with references instead of pointers, but since there is no
such thing as a null reference, you need another way to know if the cast fails.
That “other way” is to catch a bad_cast exception。
bad_cast类在<typeinfo>头文件中定义。
1、我们可以用typeid操作符来获得有关一个对象运行时信息。This operator
returns an object of class type_info, which yields information about the type
of object to which it was applied. If the type is polymorphic, it gives
information about the most derived type that applies (the dynamic type);
otherwise it yields static type information.
#include <iostream>
#include <typeinfo>
using namespace std;
struct PolyBase { virtual ~PolyBase() {} };
struct PolyDer : PolyBase { PolyDer() {} };
struct NonPolyBase {};
struct NonPolyDer : NonPolyBase { NonPolyDer(int) {} };
int main() {
// Test polymorphic Types
const PolyDer pd;
const PolyBase* ppb = &pd;
cout << typeid(ppb).name() << endl;
cout << typeid(*ppb).name() << endl;
cout << boolalpha << (typeid(*ppb) == typeid(pd))
<< endl;
cout << (typeid(PolyDer) == typeid(const PolyDer))
<< endl;
// Test non-polymorphic Types
const NonPolyDer npd(1);
const NonPolyBase* nppb = &npd;
cout << typeid(nppb).name() << endl;
cout << typeid(*nppb).name() << endl;
cout << (typeid(*nppb) == typeid(npd)) << endl;
// Test a built-in type
int i;
cout << typeid(i).name() << endl;
} ///:~
RTTI忽略了顶层的const和volatile限定符。Applying typeid to an
expression that dereferences a null pointer will cause a bad_typeid exception
(also defined in <typeinfo>) to be thrown.也可以用before(type_info&)询问一个type_info对象是否在另一个type_info对象之前。
2、多重继承情况下,派生类对象可以向上类型转换到该继承层次结构的根,选择两个中的一个。也可以从一个根到另一个根进行转换。Casting to intermediate
levels brings up an interesting difference between dynamic_cast and typeid. The
typeid operator always produces a reference to a static type_info object that describes
the dynamic type of the object. Thus, it doesn’t give you intermediate-level
information.
#include <cassert>
#include <typeinfo>
using namespace std;
class B1 {
public:
virtual ~B1() {}
};
class B2 {
public:
virtual ~B2() {}
};
class MI : public B1, public B2 {};
class Mi2 : public MI {};
int main() {
B2* b2 = new Mi2;
Mi2* mi2 = dynamic_cast<Mi2*>(b2);
MI* mi = dynamic_cast<MI*>(b2);
B1* b1 = dynamic_cast<B1*>(b2);
assert(typeid(b2) == typeid(Mi2*));//error
assert(typeid(b2) == typeid(B2*));
delete b2;
} ///:~
可以,b2仍然是B2*类型的指针。
3、不能和void型指针一起工作。RTTI可以和模型一起的工作。
// Order of constructor calls.
#include <iostream>
#include <typeinfo>
using namespace std;
template<int id> class Announce {
public:
Announce() {
cout << typeid(*this).name() << " constructor" << endl;
}
~Announce() {
cout << typeid(*this).name() << " destructor" << endl;
}
};
class X : public Announce<0> {
Announce<1> m1;
Announce<2> m2;
public:
X() { cout << "X::X()" << endl; }
~X() { cout << "X::~X()" << endl; }
};
int main() { X x; } ///:~
4、C++思想就是希望虚函数的多态机制贯穿始终,只有在必要的时候才用RTTI。
However, using virtual functions as they are intended requires
that you have control of the base-class definition, because at some
point in the extension of your program you may discover the base
class doesn’t include the virtual function you need. If the base
class comes from a library or is otherwise controlled by someone
else, one solution to the problem is RTTI; you can derive a new
type and add your extra member function. Else where in the code
you can detect your particular type and call that member function.
This doesn’t destroy the polymorphism and extensibility of the
program, because adding a new type will not require you to hunt
for switch statements. However, when you add new code in the
main body that requires your new feature, you’ll have to detect
your particular type.
实现RTTI的典型做法是:
Typically, RTTI is implemented by placing an additional pointer in a class’s
virtual function table. This pointer points to the type_info structure for that
particular type.
For a dynamic_cast<destination*>(source_pointer), most cases are
quite straight forward: source_pointer’s RTTI information is retrieved,
and RTTI information for the type destination* is fetched. A library
routine then determines whether source_pointer’s type is of type
destination* or a base class of destination*