zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    .net Dapper 学习系列(1) ---Dapper入门
    ASP.NET WebApi 学习与实践系列(2)---WebApi 路由请求的理解
    ASP.NET WebApi 学习与实践系列(1)---如何创建 WebApi
    Sqlserver 总结(2) 存储过程
    Image.Save出错 GDI 一般错误
    python导入模块的两种方式
    从1KW条数据中筛选出1W条最大的数
    C# EF更新当前实体报错 ObjectManager无法管理具有相同键值的多个对象
    微信小程序 异步请求拿数据+使用外部js库
    微信小程序入门 第一个页面搭建
  • 原文地址:https://www.cnblogs.com/findumars/p/5006170.html
Copyright © 2011-2022 走看看