分享

C dynamic

 贝嘉安 2016-12-15

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

rtti.h:

#pragma onceextern 'C' {#include };typedef const type_info TypeDescriptor;struct PMD{ ptrdiff_t mdisp; //vftable offset ptrdiff_t pdisp; //vftable offset ptrdiff_t vdisp; //vftable offset(for virtual base class)};typedef const struct _s_RTTIBaseClassDescriptor { TypeDescriptor *pTypeDescriptor; DWORD numContainedBases; PMD where; DWORD attributes;} _RTTIBaseClassDescriptor;typedef const struct _s_RTTIBaseClassArray { _RTTIBaseClassDescriptor* arrayOfBaseClassDescriptors[3];}_RTTIBaseClassArray;typedef const struct _s_RTTIClassHierarchyDescriptor { DWORD signature; DWORD attributes; DWORD numBaseClasses; _RTTIBaseClassArray *pBaseClassArray;}_RTTIClassHierarchyDescriptor;typedef const struct _s_RTTICompleteObjectLocator { DWORD signature; DWORD offset; //vftbl相对this的偏移 DWORD cdOffset; //constructor displacement TypeDescriptor *pTypeDescriptor; _RTTIClassHierarchyDescriptor *pClassDescriptor;}_RTTICompleteObjectLocator;#define BCD_NOTVISIBLE 0x00000001#define BCD_AMBIGUOUS 0x00000002#define BCD_PRIVORPROTINCOMPOBJ 0x00000004#define BCD_PRIVORPROTBASE 0x00000008#define BCD_VBOFCONTOBJ 0x00000010#define BCD_NONPOLYMORPHIC 0x00000020#define BCD_PTD(bcd) ((bcd).pTypeDescriptor)#define BCD_NUMCONTBASES(bcd) ((bcd).numContainedBases)#define BCD_WHERE(bcd) ((bcd).where)#define BCD_ATTRIBUTES(bcd) ((bcd).attributes)#define CHD_MULTINH 0x00000001 //多重继承#define CHD_VIRTINH 0x00000002 //虚拟继承#define CHD_AMBIGUOUS 0x00000004 //有重复基类的多重继承#define CHD_SIGNATURE(chd) ((chd).signature)#define CHD_ATTRIBUTES(chd) ((chd).attributes)#define CHD_NUMBASES(chd) ((chd).numBaseClasses)#define CHD_PBCA(chd) ((chd).pBaseClassArray)#define COL_SIGNATURE(col) ((col).signature)#define COL_OFFSET(col) ((col).offset)#define COL_CDOFFSET(col) ((col).cdOffset)#define COL_PTD(col) ((col).pTypeDescriptor)#define COL_PCHD(col) ((col).pClassDescriptor)extern 'C' PVOID __cdecl __RTDynamicCast (PVOID, LONG, PVOID, PVOID, BOOL);extern 'C' PVOID __cdecl __RTtypeid (PVOID); // ptr to vfptr#define TYPEIDS_EQ(pID1, pID2) ((pID1 == pID2) || !strcmp(pID1->name(), pID2->name()))


rtti.cpp:

#include #include #include 'rtti.h'#pragma warning(disable:4297)static PVOID __cdecl FindCompleteObject(PVOID *);static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance(PVOID,_RTTICompleteObjectLocator *,TypeDescriptor *,int,TypeDescriptor *);static ptrdiff_t __cdecl PMDtoOffset(PVOID pThis, const PMD& pmd);extern 'C' PVOID __cdecl __RTtypeid (PVOID inptr) { if (!inptr) { throw std::bad_typeid ('Attempted a typeid of NULL pointer!'); return NULL; } __try { // Ptr to CompleteObjectLocator should be stored at vfptr[-1] _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]); return (PVOID) pCompleteLocator->pTypeDescriptor; } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH) { throw std::__non_rtti_object ('Access violation - no RTTI data!'); }}extern 'C' PVOID __cdecl __RTDynamicCast ( PVOID inptr, // Pointer to polymorphic object LONG VfDelta, // Offset of vfptr in object PVOID SrcType, // Static type of object pointed to by inptr PVOID TargetType, // Desired result of cast BOOL isReference) // TRUE if input is reference, FALSE if input is ptr{ PVOID pResult; _RTTIBaseClassDescriptor *pBaseClass; if (inptr == NULL) return NULL; __try { PVOID pCompleteObject = FindCompleteObject((PVOID *)inptr); _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]); // Adjust by vfptr displacement, if any inptr = (PVOID *) ((char *)inptr - VfDelta); // Calculate offset of source object in complete object int inptr_delta = (char *)inptr - (char *)pCompleteObject; if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_MULTINH)) { // if not multiple inheritance pBaseClass = FindSITargetTypeInstance(pCompleteObject, pCompleteLocator, (TypeDescriptor *) SrcType, inptr_delta, (TypeDescriptor *) TargetType); } else if (!(CHD_ATTRIBUTES(*COL_PCHD(*pCompleteLocator)) & CHD_VIRTINH)) { // if multiple, but not virtual, inheritance pBaseClass = FindMITargetTypeInstance(pCompleteObject, pCompleteLocator, (TypeDescriptor *) SrcType, inptr_delta, (TypeDescriptor *) TargetType); } else { // if virtual inheritance pBaseClass = FindVITargetTypeInstance(pCompleteObject, pCompleteLocator, (TypeDescriptor *) SrcType, inptr_delta, (TypeDescriptor *) TargetType); } if (pBaseClass != NULL) { // Calculate ptr to result base class from pBaseClass->where pResult = ((char *) pCompleteObject) + PMDtoOffset(pCompleteObject, pBaseClass->where); }else { pResult = NULL; if (isReference) { throw std::bad_cast('Bad dynamic_cast!'); } } } __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER: EXCEPTION_CONTINUE_SEARCH) { pResult = NULL; throw std::__non_rtti_object ('Access violation - no RTTI data!'); } return pResult; }///////////////////////////////////////////////////////////////////////////////// FindCompleteObject - Calculate member offset from PMD & this//// Output: pointer to the complete object containing class *inptr//// Side-effects: NONE.//static PVOID __cdecl FindCompleteObject (PVOID *inptr) // Pointer to polymorphic object{ // Ptr to CompleteObjectLocator should be stored at vfptr[-1] _RTTICompleteObjectLocator *pCompleteLocator = (_RTTICompleteObjectLocator *) ((*((void***)inptr))[-1]); char *pCompleteObject = (char *)inptr - pCompleteLocator->offset; // Adjust by construction displacement, if any if (pCompleteLocator->cdOffset) pCompleteObject += *(ptrdiff_t *)((char *)inptr - pCompleteLocator->cdOffset); return (PVOID) pCompleteObject;}static _RTTIBaseClassDescriptor * __cdecl FindSITargetTypeInstance ( PVOID pCompleteObject, // pointer to complete object _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object TypeDescriptor *pSrcTypeID, // pointer to TypeDescriptor of source object int SrcOffset, // offset of source object in complete object TypeDescriptor *pTargetTypeID) // pointer to TypeDescriptor of result of cast{ _RTTIBaseClassDescriptor *pBase; _RTTIBaseClassDescriptor * const *pBasePtr; DWORD i; for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors; i < pcolocator-="">pClassDescriptor->numBaseClasses; i++, pBasePtr++) { // Test type of selected base class pBase = *pBasePtr; if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) && !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE)) { return pBase; } } return NULL;}static _RTTIBaseClassDescriptor * __cdecl FindMITargetTypeInstance ( PVOID pCompleteObject, // pointer to complete object _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object TypeDescriptor *pSrcTypeID, // pointer to TypeDescriptor of source object int SrcOffset, // offset of source object in complete object TypeDescriptor *pTargetTypeID) // pointer to TypeDescriptor of result of cast{ _RTTIBaseClassDescriptor *pBase, *pSubBase; _RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr; DWORD i, j; // First, try down-casts for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors; i < pcolocator-="">pClassDescriptor->numBaseClasses; i++, pBasePtr++) { pBase = *pBasePtr; // Test type of selected base class if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) { // If base class is proper type, see if it contains our instance of source class for (j = 0, pSubBasePtr = pBasePtr+1; j < pbase-="">numContainedBases; j++, pSubBasePtr++) { pSubBase = *pSubBasePtr; if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) && (PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) { // Yes, this is the proper instance of source class return pBase; } } } } // Down-cast failed, try cross-cast for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors; i < pcolocator-="">pClassDescriptor->numBaseClasses; i++, pBasePtr++) { pBase = *pBasePtr; // Check if base class has proper type, is accessible & is unambiguous if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) && !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) && !(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) { return pBase; } } return NULL;}static _RTTIBaseClassDescriptor * __cdecl FindVITargetTypeInstance ( PVOID pCompleteObject, // pointer to complete object _RTTICompleteObjectLocator *pCOLocator, // pointer to Locator of complete object TypeDescriptor *pSrcTypeID, // pointer to TypeDescriptor of source object int SrcOffset, // offset of source object in complete object TypeDescriptor *pTargetTypeID) // pointer to TypeDescriptor of result of cast{ _RTTIBaseClassDescriptor *pBase, *pSubBase; _RTTIBaseClassDescriptor * const *pBasePtr, * const *pSubBasePtr; _RTTIBaseClassDescriptor *pResult = NULL; DWORD i, j; // First, try down-casts for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors; i < pcolocator-="">pClassDescriptor->numBaseClasses; i++, pBasePtr++) { pBase = *pBasePtr; // Test type of selected base class if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID)) { // If base class is proper type, see if it contains our instance of source class for (j = 0, pSubBasePtr = pBasePtr+1; j < pbase-="">numContainedBases; j++, pSubBasePtr++) { pSubBase = *pSubBasePtr; if (TYPEIDS_EQ(pSubBase->pTypeDescriptor, pSrcTypeID) && (PMDtoOffset(pCompleteObject, pSubBase->where) == SrcOffset)) { // Yes, this is the proper instance of source class - make sure it is unambiguous // Ambiguity now determined by inequality of offsets of source class within complete object, not pointer inequality if ((pResult != NULL) && (PMDtoOffset(pCompleteObject, pResult->where) != PMDtoOffset(pCompleteObject, pBase->where))) { // We already found an earlier instance, hence ambiguity return NULL; } else { // Unambiguous pResult = pBase; } } } } } if (pResult != NULL) return pResult; // Down-cast failed, try cross-cast for (i = 0, pBasePtr = pCOLocator->pClassDescriptor->pBaseClassArray->arrayOfBaseClassDescriptors; i < pcolocator-="">pClassDescriptor->numBaseClasses; i++, pBasePtr++) { pBase = *pBasePtr; // Check if base class has proper type, is accessible & is unambiguous if (TYPEIDS_EQ(pBase->pTypeDescriptor, pTargetTypeID) && !(BCD_ATTRIBUTES(*pBase) & BCD_NOTVISIBLE) && !(BCD_ATTRIBUTES(*pBase) & BCD_AMBIGUOUS)) { return pBase; } } return NULL;}static ptrdiff_t __cdecl PMDtoOffset( PVOID pThis, // ptr to complete object const PMD& pmd) // pointer-to-member-data structure{ ptrdiff_t RetOff = 0; if (pmd.pdisp >= 0) { // if base is in the virtual part of class RetOff = pmd.pdisp; RetOff += *(ptrdiff_t*)((char*)*(ptrdiff_t*)((char*)pThis + RetOff) + pmd.vdisp); } RetOff += pmd.mdisp; return RetOff;}

测试代码:

// WinDemo.cpp : 定义控制台应用程序的入口点。//#include 'stdafx.h'#include #include 'rtti.h'using namespace std;class A{public: virtual void func() { cout < 'a::func()'="">< endl;="" }};class="" b="" :="" public="" a{public:="" virtual="" void="" func()="" {="" cout="">< 'b::func()'="">< endl;="" }};class="" c="" :="" public="" a{public:="" virtual="" void="" func()="" {="" cout="">< 'c::func()'="">< endl;="" }private:="" int="" _val;};int="" main(int="" argc,="" char*="" argv[]){="" a*="" pa="new" c;="" typedescriptor*="" ptypea="&typeid(A);" typedescriptor*="" ptypec="&typeid(C);" c*="" pc="(C*)__RTDynamicCast(pa," 0,="" (lpvoid)ptypea,="" (lpvoid)ptypec,="" false);="" cout="">< pc="">< endl;="" return="">

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


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

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

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

禁用方法如下:

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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约