C++ dynamic_cast实现原理

dynamic_cast是一个操作符,其用法不再赘述。查看汇编码可以发现实际调用的是这个函数__RTDynamicCast,其内部实现如下:

rtti.h:

 

[cpp] view plaincopy
 
  1. #pragma once  
  2.   
  3. extern "C" {  
  4. #include <windows.h>  
  5. };  
  6.   
  7. typedef const type_info TypeDescriptor;  
  8.   
  9. struct PMD  
  10. {  
  11.     ptrdiff_t mdisp; //vftable offset  
  12.     ptrdiff_t pdisp; //vftable offset  
  13.     ptrdiff_t vdisp; //vftable offset(for virtual base class)  
  14. };  
  15.   
  16. typedef const struct _s_RTTIBaseClassDescriptor    
  17. {  
  18.     TypeDescriptor                  *pTypeDescriptor;  
  19.     DWORD                           numContainedBases;  
  20.     PMD                             where;  
  21.     DWORD                           attributes;  
  22. } _RTTIBaseClassDescriptor;  
  23.   
  24. typedef const struct  _s_RTTIBaseClassArray     
  25. {  
  26.     _RTTIBaseClassDescriptor* arrayOfBaseClassDescriptors[3];  
  27. }_RTTIBaseClassArray;  
  28.   
  29. typedef const struct _s_RTTIClassHierarchyDescriptor   
  30. {  
  31.     DWORD                           signature;  
  32.     DWORD                           attributes;  
  33.     DWORD                           numBaseClasses;  
  34.     _RTTIBaseClassArray             *pBaseClassArray;  
  35. }_RTTIClassHierarchyDescriptor;  
  36.   
  37. typedef const struct _s_RTTICompleteObjectLocator      
  38. {  
  39.     DWORD                           signature;  
  40.     DWORD                           offset;          //vftbl相对this的偏移  
  41.     DWORD                           cdOffset;        //constructor displacement   
  42.     TypeDescriptor                  *pTypeDescriptor;  
  43.     _RTTIClassHierarchyDescriptor   *pClassDescriptor;  
  44. }_RTTICompleteObjectLocator;  
  45.   
  46. #define BCD_NOTVISIBLE              0x00000001  
  47. #define BCD_AMBIGUOUS               0x00000002  
  48. #define BCD_PRIVORPROTINCOMPOBJ     0x00000004  
  49. #define BCD_PRIVORPROTBASE          0x00000008  
  50. #define BCD_VBOFCONTOBJ             0x00000010  
  51. #define BCD_NONPOLYMORPHIC          0x00000020  
  52.   
  53. #define BCD_PTD(bcd)                ((bcd).pTypeDescriptor)  
  54. #define BCD_NUMCONTBASES(bcd)       ((bcd).numContainedBases)  
  55. #define BCD_WHERE(bcd)              ((bcd).where)  
  56. #define BCD_ATTRIBUTES(bcd)         ((bcd).attributes)  
  57.   
  58. #define CHD_MULTINH                 0x00000001 //多重继承  
  59. #define CHD_VIRTINH                 0x00000002 //虚拟继承  
  60. #define CHD_AMBIGUOUS               0x00000004 //有重复基类的多重继承  
  61.   
  62. #define CHD_SIGNATURE(chd)          ((chd).signature)  
  63. #define CHD_ATTRIBUTES(chd)         ((chd).attributes)  
  64. #define CHD_NUMBASES(chd)           ((chd).numBaseClasses)  
  65. #define CHD_PBCA(chd)               ((chd).pBaseClassArray)  
  66.   
  67. #define COL_SIGNATURE(col)          ((col).signature)  
  68. #define COL_OFFSET(col)             ((col).offset)  
  69. #define COL_CDOFFSET(col)           ((col).cdOffset)  
  70. #define COL_PTD(col)                ((col).pTypeDescriptor)  
  71. #define COL_PCHD(col)               ((col).pClassDescriptor)  
  72.   
  73. extern "C" PVOID __cdecl __RTDynamicCast (PVOID, LONG, PVOID, PVOID, BOOL);  
  74.   
  75. extern "C" PVOID __cdecl __RTtypeid (PVOID);     // ptr to vfptr  
  76.   
  77. #define TYPEIDS_EQ(pID1, pID2)  ((pID1 == pID2) || !strcmp(pID1->name(), pID2->name()))  

 

 

rtti.cpp:

 

[cpp] view plaincopy
 
  1. #include <stdio.h>  
  2. #include <typeinfo>  
  3. #include "rtti.h"  
  4.   
  5. #pragma warning(disable:4297)  
  6.   
  7. static PVOID __cdecl FindCompleteObject(PVOID *);  
  8. static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);  
  9. static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);  
  10. static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);  
  11. static ptrdiff_t __cdecl PMDtoOffset(PVOID pThis, const PMD& pmd);  
  12.   
  13. extern "C" PVOID __cdecl __RTtypeid (PVOID inptr)           
  14. {  
  15.     if (!inptr) {  
  16.         throw std::bad_typeid ("Attempted a typeid of NULL pointer!");   
  17.         return NULL;  
  18.     }  
  19.   
  20.     __try {  
  21.         // Ptr to CompleteObjectLocator should be stored at vfptr[-1]  
  22.         _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);  
  23.         return (PVOID) pCompleteLocator->pTypeDescriptor;  
  24.     }  
  25.     __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER:             EXCEPTION_CONTINUE_SEARCH)   
  26.     {  
  27.         throw std::__non_rtti_object ("Access violation - no RTTI data!");  
  28.     }  
  29. }  
  30.   
  31.   
  32. extern "C" PVOID __cdecl __RTDynamicCast (  
  33.                                         PVOID inptr,         // Pointer to polymorphic object  
  34.                                         LONG VfDelta,       // Offset of vfptr in object  
  35.                                         PVOID SrcType,      // Static type of object pointed to by inptr  
  36.                                         PVOID TargetType,   // Desired result of cast  
  37.                                         BOOL isReference)   // TRUE if input is reference, FALSE if input is ptr  
  38. {  
  39.     PVOID pResult;  
  40.     _RTTIBaseClassDescriptor *pBaseClass;  
  41.   
  42.     if (inptr == NULL)  
  43.         return NULL;  
  44.   
  45.     __try {  
  46.         PVOID pCompleteObject = FindCompleteObject((PVOID *)inptr);  
  47.         _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);  
  48.   
  49.         // Adjust by vfptr displacement, if any  
  50.         inptr = (PVOID *) ((char *)inptr - VfDelta);  
  51.         // Calculate offset of source object in complete object  
  52.         int inptr_delta = (char *)inptr - (char *)pCompleteObject;  
  53.   
  54.         if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_MULTINH)) {             // if not multiple inheritance  
  55.             pBaseClass = FindSITargetTypeInstance(pCompleteObject,  
  56.                                                   pCompleteLocator,  
  57.                                                   (TypeDescriptor *) SrcType,  
  58.                                                   inptr_delta,  
  59.                                                   (TypeDescriptor *) TargetType);  
  60.         } else if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_VIRTINH)) { // if multiple, but not virtual, inheritance  
  61.             pBaseClass = FindMITargetTypeInstance(pCompleteObject,  
  62.                                                   pCompleteLocator,  
  63.                                                   (TypeDescriptor *) SrcType,  
  64.                                                   inptr_delta,  
  65.                                                   (TypeDescriptor *) TargetType);  
  66.         } else {                                                                   // if virtual inheritance  
  67.             pBaseClass = FindVITargetTypeInstance(pCompleteObject,  
  68.                                                   pCompleteLocator,  
  69.                                                   (TypeDescriptor *) SrcType,  
  70.                                                   inptr_delta,  
  71.                                                   (TypeDescriptor *) TargetType);  
  72.         }  
  73.   
  74.         if (pBaseClass != NULL) {  
  75.             // Calculate ptr to result base class from pBaseClass->where  
  76.             pResult = ((char *) pCompleteObject) + PMDtoOffset(pCompleteObject, pBaseClass->where);  
  77.         }else {  
  78.             pResult = NULL;  
  79.             if (isReference) {  
  80.                 throw std::bad_cast("Bad dynamic_cast!");  
  81.             }  
  82.         }  
  83.   
  84.     }  
  85.     __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH) {  
  86.         pResult = NULL;  
  87.         throw std::__non_rtti_object ("Access violation - no RTTI data!");  
  88.     }  
  89.   
  90.     return pResult;  
  91.           
  92. }  
  93.   
  94. /////////////////////////////////////////////////////////////////////////////  
  95. //  
  96. // FindCompleteObject - Calculate member offset from PMD & this  
  97. //  
  98. // Output: pointer to the complete object containing class *inptr  
  99. //  
  100. // Side-effects: NONE.  
  101. //  
  102. static PVOID __cdecl FindCompleteObject (PVOID *inptr)          // Pointer to polymorphic object  
  103. {  
  104.     // Ptr to CompleteObjectLocator should be stored at vfptr[-1]  
  105.     _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]);  
  106.     char *pCompleteObject = (char *)inptr - pCompleteLocator->offset;  
  107.     // Adjust by construction displacement, if any  
  108.     if (pCompleteLocator->cdOffset)  
  109.         pCompleteObject += *(ptrdiff_t *)((char *)inptr - pCompleteLocator->cdOffset);  
  110.     return (PVOID) pCompleteObject;  
  111. }  
  112.   
  113. static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance (  
  114.                                 PVOID pCompleteObject,                          // pointer to complete object  
  115.                                 _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object  
  116.                                 TypeDescriptor *pSrcTypeID,        // pointer to TypeDescriptor of source object  
  117.                                 int SrcOffset,                                          // offset of source object in complete object  
  118.                                 TypeDescriptor *pTargetTypeID)     // pointer to TypeDescriptor of result of cast  
  119. {  
  120.     _RTTIBaseClassDescriptor *pBase;  
  121.     _RTTIBaseClassDescriptor * const *pBasePtr;  
  122.     DWORD i;  
  123.   
  124.     for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;  
  125.          i < pCOLocator->pClassDescriptor->numBaseClasses;  
  126.          i++, pBasePtr++) {  
  127.   
  128.         // Test type of selected base class  
  129.         pBase = *pBasePtr;  
  130.         if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&  
  131.             !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE)) {  
  132.             return pBase;  
  133.         }  
  134.     }  
  135.     return NULL;  
  136. }  
  137.   
  138. static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance (  
  139.                                 PVOID pCompleteObject,                          // pointer to complete object  
  140.                                 _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object  
  141.                                 TypeDescriptor *pSrcTypeID,        // pointer to TypeDescriptor of source object  
  142.                                 int SrcOffset,                                          // offset of source object in complete object  
  143.                                 TypeDescriptor *pTargetTypeID)     // pointer to TypeDescriptor of result of cast  
  144. {  
  145.     _RTTIBaseClassDescriptor *pBase, *pSubBase;  
  146.     _RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr;  
  147.     DWORD i, j;  
  148.   
  149.                                 // First, try down-casts  
  150.     for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;  
  151.          i < pCOLocator->pClassDescriptor->numBaseClasses;  
  152.          i++, pBasePtr++) {  
  153.           
  154.         pBase = *pBasePtr;  
  155.                                 // Test type of selected base class  
  156.         if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) {  
  157.                                 // If base class is proper type, see if it contains our instance of source class  
  158.             for (j = 0, pSubBasePtr = pBasePtr+1;  
  159.                  j < pBase->numContainedBases;  
  160.                  j++, pSubBasePtr++) {  
  161.   
  162.                 pSubBase = *pSubBasePtr;  
  163.                 if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) &&  
  164.                     (PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) {  
  165.                                 // Yes, this is the proper instance of source class  
  166.                     return pBase;  
  167.                 }  
  168.             }  
  169.         }  
  170.     }  
  171.   
  172.                                 // Down-cast failed, try cross-cast  
  173.     for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;  
  174.          i < pCOLocator->pClassDescriptor->numBaseClasses;  
  175.          i++, pBasePtr++) {  
  176.   
  177.         pBase = *pBasePtr;  
  178.                                 // Check if base class has proper type, is accessible & is unambiguous  
  179.         if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&  
  180.             !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) &&  
  181.             !(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) {  
  182.             return pBase;  
  183.         }  
  184.     }  
  185.   
  186.     return NULL;  
  187. }  
  188.   
  189. static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance (  
  190.                                 PVOID pCompleteObject,                          // pointer to complete object  
  191.                                 _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object  
  192.                                 TypeDescriptor *pSrcTypeID,        // pointer to TypeDescriptor of source object  
  193.                                 int SrcOffset,                                          // offset of source object in complete object  
  194.                                 TypeDescriptor *pTargetTypeID)     // pointer to TypeDescriptor of result of cast  
  195. {  
  196.     _RTTIBaseClassDescriptor *pBase, *pSubBase;  
  197.     _RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr;  
  198.     _RTTIBaseClassDescriptor *pResult = NULL;  
  199.     DWORD i, j;  
  200.   
  201.                                 // First, try down-casts  
  202.     for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;  
  203.          i < pCOLocator->pClassDescriptor->numBaseClasses;  
  204.          i++, pBasePtr++) {  
  205.           
  206.         pBase = *pBasePtr;  
  207.                                 // Test type of selected base class  
  208.         if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) {  
  209.                                 // If base class is proper type, see if it contains our instance of source class  
  210.             for (j = 0, pSubBasePtr = pBasePtr+1;  
  211.                  j < pBase->numContainedBases;  
  212.                  j++, pSubBasePtr++) {  
  213.   
  214.                 pSubBase = *pSubBasePtr;  
  215.                 if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) &&  
  216.                     (PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) {  
  217.                                 // Yes, this is the proper instance of source class - make sure it is unambiguous  
  218.                                 // Ambiguity now determined by inequality of offsets of source class within complete object, not pointer inequality  
  219.                     if ((pResult != NULL) && (PMDtoOffset(pCompleteObject, pResult->where) != PMDtoOffset(pCompleteObject, pBase->where))) {  
  220.                                 // We already found an earlier instance, hence ambiguity  
  221.                         return NULL;  
  222.                     }  
  223.                     else {  
  224.                                 // Unambiguous  
  225.                         pResult = pBase;  
  226.                     }  
  227.                 }  
  228.             }  
  229.         }  
  230.     }  
  231.   
  232.     if (pResult != NULL)  
  233.         return pResult;  
  234.   
  235.                                 // Down-cast failed, try cross-cast  
  236.     for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors;  
  237.          i < pCOLocator->pClassDescriptor->numBaseClasses;  
  238.          i++, pBasePtr++) {  
  239.   
  240.         pBase = *pBasePtr;  
  241.                                 // Check if base class has proper type, is accessible & is unambiguous  
  242.         if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) &&  
  243.             !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) &&  
  244.             !(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) {  
  245.             return pBase;  
  246.   
  247.         }  
  248.     }  
  249.   
  250.     return NULL;  
  251. }  
  252.   
  253. static ptrdiff_t __cdecl PMDtoOffset(  
  254.                                 PVOID pThis,                    // ptr to complete object  
  255.                                 const PMD& pmd)                 // pointer-to-member-data structure  
  256. {  
  257.     ptrdiff_t RetOff = 0;  
  258.   
  259.     if (pmd.pdisp >= 0) {                       // if base is in the virtual part of class  
  260.         RetOff = pmd.pdisp;  
  261.         RetOff += *(ptrdiff_t*)((char*)*(ptrdiff_t*)((char*)pThis + RetOff) + pmd.vdisp);  
  262.     }  
  263.   
  264.     RetOff += pmd.mdisp;  
  265.   
  266.     return RetOff;  
  267. }  


测试代码:

 

 

[cpp] view plaincopy
 
  1. // WinDemo.cpp : 定义控制台应用程序的入口点。  
  2. //  
  3. #include "stdafx.h"  
  4. #include <iostream>  
  5. #include "rtti.h"  
  6. using namespace std;  
  7.   
  8. class A  
  9. {  
  10. public:  
  11.     virtual void func()  
  12.     {  
  13.         cout << "A::func()" << endl;  
  14.     }  
  15. };  
  16.   
  17. class B : public A  
  18. {  
  19. public:  
  20.     virtual void func()  
  21.     {  
  22.         cout << "B::func()" << endl;  
  23.     }  
  24. };  
  25.   
  26. class C : public A  
  27. {  
  28. public:  
  29.     virtual void func()  
  30.     {  
  31.         cout << "C::func()" << endl;  
  32.     }  
  33. private:  
  34.     int _val;  
  35. };  
  36.   
  37. int main(int argc, char* argv[])  
  38. {  
  39.     A* pa = new C;  
  40.     TypeDescriptor* ptypeA = &typeid(A);  
  41.     TypeDescriptor* ptypeC = &typeid(C);  
  42.     C* pc = (C*)__RTDynamicCast(pa, 0, (LPVOID)ptypeA, (LPVOID)ptypeC, FALSE);  
  43.     cout << pc << endl;  
  44.   
  45.     return 0;  
  46. }  

 

从以上代码可以看出:只能在有虚函数的类层次之间使用dynamic_cast。要实现dynamic_cast,编译器会在每个含有虚函数的类的虚函数表的前四个字节存放一个指向_RTTICompleteObjectLocator结构的指针,当然还要额外空间存放_RTTICompleteObjectLocator及其相关结构的数据。以上面代码的类C来说:

这个_RTTICompleteObjectLocator就是实现dynamic_cast的关键结构。里面存放了vfptr相对this指针的偏移,构造函数偏移(针对虚拟继承),type_info指针,以及类层次结构中其它类的相关信息。如果是多重继承,这些信息更加复杂。

所以,dynamic_cast的时间和空间代价是相对较高的,在设计时应避免使用。

如果整个工程都不需要dynamic_cast,可以禁用运行时类型信息(vs2008默认是启用的),这样编译器就不会产生_RTTICompleteObjectLocator及相关数据。

禁用方法如下:

依次选择【工程属性】、【配置属性】、【C/C++】、【语言】。将【启用运行时类型信息】改为”否“。

http://blog.csdn.net/passion_wu128/article/details/38511957

posted @ 2015-11-30 05:45  findumars  Views(3515)  Comments(0Edit  收藏  举报