运行时类型识别

来源:互联网 发布:不安全的网络怎么连接 编辑:程序博客网 时间:2024/06/05 18:12

前言:当仅有一个指针或引用指向基类型时,利用运行时类型识别(RTTI)可以找到一个对象的动态类型。由于继承的层次结构的典型描述是基类在派生类之上,所以这种类型转换也称为向下类型转换.

 

 

    

  1. #ifndef __SECURITY_H
  2. #define __SECURITY_H
  3. #include <iostream>
  4. using namespace std;
  5. class Security
  6. {
  7.     public:
  8.         virtual ~Security()
  9.         {}
  10. };
  11. class Stock:public Security
  12. {};
  13. class Bond:public Security
  14. {};
  15. class Investment:public Security
  16. {
  17.     public:
  18.         void Special()
  19.         {
  20.             cout << "Special Invenstment function" << endl;
  21.         }
  22. };
  23. class Metal:public Investment
  24. {};
  25. #endif
  26. //.cpp
  27. #include "Security.h"
  28. #include <vector>
  29. int main()
  30. {
  31.     vector<Security *> vecPort;
  32.     vecPort.push_back(new Metal);
  33.     vecPort.push_back(new Investment);
  34.     vecPort.push_back(new Bond);
  35.     vecPort.push_back(new Stock);
  36.     for(vector<Security *>::iterator iter=vecPort.begin(); iter!=vecPort.end(); ++iter)
  37.     {
  38.         Investment *pInvest=dynamic_cast<Investment *>(*iter);
  39.         if( pInvest )
  40.             pInvest->Special();
  41.         else
  42.             cout << "not an Investment" << endl;
  43.     }
  44.     cout << "cast from intermediate pointer:" << endl;
  45.     Security *s=new Metal;
  46.     Investment *p=dynamic_cast<Investment *>(s);
  47.     if( p )
  48.     {
  49.         cout << " it'an Investment" << endl;
  50.         Metal *pMetal=dynamic_cast<Metal *>(p);
  51.         if( pMetal )
  52.             cout << " it's a Metal too!" << endl;
  53.     }
  54.     
  55.     for(vector<Security *>::iterator iter=vecPort.begin(); iter!=vecPort.end(); ++iter)
  56.     {
  57.         delete *iter;
  58.         *iter=NULL;
  59.     }
  60.     
  61.     delete s;
  62.     system("pause");
  63.     return 0;
  64. }

输出结果为:

Special Invenstment function
Special Invenstment function
not an Investment
not an Investment
cast from intermediate pointer:
 it'an Investment
 it's a Metal too!
Press any key to continue . . .

 

从中可以看出,如果想要安全的进行向下类型转换,dynamic_cast要求使用的目标对象的类型是多态(polymorphic)的。这就要求该类必须有一个虚函数。

    如果不用指针而用引用,由于没有诸如没有空引用这样的情况,这就需要用异常捕获来解决这个问题。bad_cast异常就这样诞生了。

  1. int main()
  2. {
  3.     Metal m;
  4.     //Stock m;
  5.     Security &s=m;
  6.     try
  7.     {
  8.         Investment &c=dynamic_cast<Investment &>(s);
  9.         cout << "It's an Investment" << endl;
  10.     }
  11.     catch(bad_cast &e)
  12.     {
  13.         cout << "s is not an Investment: " << e.what() << endl; 
  14.     }
  15.     //cout << "It's not a bond type" << endl;
  16.     system("pause");
  17.     return 0;
  18. }
  19. 输出结果如下:
  20. s is not an Investment: Bad dynamic
    Press any key to continue . . .

    如果需要获得一个关于运行时信息,就需要用typeid操作符来完成,这种操作符返回一个type_info类的对象,该对象给出了相关对象类型的信息,在MS中type_info声明如下:

  1. class type_info {
  2. public:
  3.     virtual ~type_info();
  4.     _CRTIMP_PURE bool __CLR_OR_THIS_CALL operator==(const type_info& rhs) const;
  5.     _CRTIMP_PURE bool __CLR_OR_THIS_CALL operator!=(const type_info& rhs) const;
  6.     _CRTIMP_PURE int __CLR_OR_THIS_CALL before(const type_info& rhs) const;
  7.     _CRTIMP_PURE const char* __CLR_OR_THIS_CALL name(__type_info_node* __ptype_info_node = &__type_info_root_node) const;
  8.     _CRTIMP_PURE const char* __CLR_OR_THIS_CALL raw_name() const;
  9. private:
  10.     void *_m_data;
  11.     char _m_d_name[1];
  12.     __CLR_OR_THIS_CALL type_info(const type_info& rhs);
  13.     type_info& __CLR_OR_THIS_CALL operator=(const type_info& rhs);
  14.     _CRTIMP_PURE static const char *__CLRCALL_OR_CDECL _Name_base(const type_info *,__type_info_node* __ptype_info_node);
  15.     _CRTIMP_PURE static void __CLRCALL_OR_CDECL _Type_info_dtor(type_info *);
  16. };

其中,

  1. /* Define _CRTIMP */ 
  2. #ifndef _CRTIMP
  3. #ifdef  _DLL
  4. #define _CRTIMP __declspec(dllimport)
  5. #else   /* ndef _DLL */
  6. #define _CRTIMP
  7. #endif  /* _DLL */
  8. #endif  /* _CRTIMP */
  9. #ifndef _CRTIMP_PURE
  10.  #if defined(_M_CEE_PURE) || defined(_STATIC_CPPLIB)
  11.   #define _CRTIMP_PURE
  12.  #else
  13.   #define _CRTIMP_PURE _CRTIMP
  14.  #endif
  15. #endif
  16. #define __CLR_OR_THIS_CALL

现在就可以通过typeid操作符来获取一个对象的动态类型的名称了,见如下:

  1. #include <iostream>
  2. #include <typeinfo>
  3. using namespace std;
  4. struct PolyBase
  5. {
  6.     virtual ~PolyBase()
  7.     {}
  8. };
  9. struct PolyBer:PolyBase
  10. {
  11. };
  12. struct NonPolyBase
  13. {
  14. };
  15. struct NonpolyBer:NonPolyBase
  16. {
  17. };
  18. int main()
  19. {
  20.     const PolyBase pd;
  21.     const PolyBase *ppd=&pd;
  22.     cout << typeid(&pd).name() << endl;
  23.     cout << typeid(ppd).name() << endl;
  24.     cout << typeid(*ppd).name() << endl;
  25.     //cout << typeid(PolyBer).before(pd) << endl;
  26.     cout << typeid(ppd).raw_name() << endl;
  27.     cout << typeid(*ppd).raw_name() << endl;
  28.     cout << boolalpha << (typeid(*ppd)==typeid(pd)) << endl;
  29.     cout << boolalpha << (typeid(ppd)==typeid(pd)) << endl;
  30.     cout << (typeid(PolyBer)==typeid(const PolyBer)) << endl;
  31.     system("pause");
  32.     return 0;
  33. }

 

输出如下:

struct PolyBase const *
struct PolyBase const *
struct PolyBase
.PBUPolyBase@@
.?AUPolyBase@@
true
false
true
Press any key to continue . . .

需要注意的是,RTTI忽略了顶层的const和volatile限定符,借助非多态类型,可以获得静态类型(该指针本身的类型)。RTTI仅仅能对完整的类型而工作,这就不能使用void型指针,一个void *是“无类型信息”。

 

    当然,RTTI也可以和多重继承,模板一起工作,在这里就没有深入了。

 

总结:

    实现RTTI的方法是,通过在类的虚函数表中放置一个附加的指针,这个指针指向那个特别类型的type_info结构,typeid()表达式的结果非常简单:虚函数表指针取得type_info指针,产生一个对type_info结构的引用。因为dynamic_cast使用的类库的程序必须从头至尾对一个基类表进行检查,dynamic_cast的开销就很有可能比typeid大,找到一个基类比一个派生类花销的时间就会更多。另外,dynamic_cast将任何一个类型于任何其他类型相比较,在同一层次中可以不受限制的进行类型比较,这就增加了程序的额外开销。

原创粉丝点击