1、通过运行时类型识别,程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型。
通过以下两种操作符提供RTTI:
1)typeid操作符,返回指针或引用所指对象的实际类型。
2)dynamic_cast操作符,将基类类型的引用或指针安全地转换为派生类型的指针或引用。
这些操作符只为带有一个或多个虚函数的类返回动态类型信息,对于其他类型,返回静态(即编译时)类型的信息。对于带虚函数的类,在运行时执行RTTI操作符,但对于其他类型,在编译时计算RTTI操作符。
2、使用动态强制类型转换要小心,只要有可能,定义和使用虚函数比直接接管类型管理好得多。
3、dynamic_cast操作符
1)可以使用 dynamic_cast 操作符将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或指针。与 dynamic_cast 一起使用的指针必须是有效的——它必须为 0 或者指向一个对象。
2)与其他强制类型转换不同,dynamic_cast 涉及运行时类型检查。如果绑定到引用或指针的对象不是目标类型的对象,则 dynamic_cast 失败。如果转换到指针类型的 dynamic_cast 失败,则 dynamic_cast 的结果是 0 值;如果转换到引用类型的 dynamic_cast 失败,则抛出一个 bad_cast 类型的异常。
示例代码
if (Derived *derivedPtr = dynamic_cast<Derived*>(basePtr)) { //use the Derived object to which derivedPtr points } else { //basePtr Points at a Base object //use the Base object to which basePtr Points. }
在条件中执行dynamic_cast保证了转换和结果测试在一个表达式中进行。
3)转换引用类型
dynamic_cast< Type& >(val)
这里,Type 是转换的目标类型,而 val 是基类类型的对象(或引用)。
The dynamic_cast operation converts the operand val to the desired type Type&
only if val actually refers to an object of the type Type or is an object of a type derived
from Type .
Because there is no such thing as a null reference, it is not possible to use the
same checking strategy for references that is used for pointer casts. Instead, when a
cast fails, it throws a std::bad_cast exception. This exception is defined in the typeinfo
library header.
示例代码
void f(const Base &b) { try { const Derived &d = dynamic_cast<const Derived&>(b); //use the Derived object to which b referred } catch(bad_cast) { //handle the fact that the cast failed. } }
4、typeid操作符
typeid(e)
这里 e 是任意表达式或者是类型名。
如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态编译时类型。。如果操作数不是类类型或者是没有虚函数的类,则 typeid 操作符指出操作数的静态类型;如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型。
typeid操作符的结果是名为type_info的标准库类型的对象引用,需要包含库文件:typeinfo。
示例代码
Base *bp; Derived *dp; //compare type at run time of two objects if (typeid(*bp) == typeid(*dp)) { //same type } if (typeid(*bp) == typeid(Derived)) { //bp actually points to a Derived }
注意:typeid操作数是表示对象的表达式,测试*bp,而不bp。
示例代码
//考虑一个类层次,我们希望为它实现相等操作符。如果两个对象的给定数据成员集合的值相同, //它们就相等。因为确定派生类型的相等与确定基类类型的相等所考虑的值不同,所以对层次中 //的每一对类型(潜在地)需要一个不同的相等操作符。而且,希望能够使用给类型作为左操作 //数或右操作数,所以实际上对每一对类型将需要两个操作符,这会带来大量的操作符定义函数。 //如果我们通过定义虚函数实现的话,。虚函数在基类类型和派生类型中必须有相同的形参类型, //这意味着,虚函数操作必须有一个形参是基类的引用,而这样的话,只能比较虚类中的成员。 //所以用虚函数不是很好的方法,我们用RTTI class Base { public: friend bool operator==(const Base&, const Base&); public: //interface protected: virtual bool equal(const Base&) const; //... }; bool operator==(const Base& lhs, const Base& rhs) { //false if not the same type return typeid(lhs) == typeid(rhs) && lhs.equal(rhs); } bool Base::equal(const Base& rhs) const { //do whatever is required to compare to Base objects } class Derived : public Base { // ... }; bool Derived::equal(const Base& rhs) const { if (const Derived *dp = dynamic_cast<const Derived*>(&rhs)) {//这个转换是必须的,以便访问派生类中的成员 //two Derived Objects } else { return false; } }
5、type_info类
t1 == t2 |
如果两个对象 t1 和 t2 类型相同,就返回 true;否则,返回false |
t1 != t2 |
如果两个对象 t1 和 t2 类型不同,就返回 true;否则,返回false |
t.name() |
返回 C 风格字符串,这是类型名字的可显示版本。类型名字用系统相关的方法产生 |
t1.before(t2) |
返回指出 t1 是否出现在 t2 之前的 bool 值。before 强制的次序与编译器有关 |
因为打算作基类使用,type_info 类也提供公用虚析构函数。如果编译器想要提供附加的类型信息,应该在 type_info 的派生类中进行。
默认构造函数和复制构造函数以及赋值操作符都定义为 private,所以不能定义或复制 type_info 类型的对象。程序中创建 type_info 对象的唯一方法是使用 typeid 操作符。