您的位置:首页 > 编程语言 > C语言/C++

C++ dynamic_cast实现原理

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

rtti.h:

[cpp] view plaincopy

#pragma once

extern "C" {

#include <windows.h>

};

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:

[cpp] view plaincopy

#include <stdio.h>

#include <typeinfo>

#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;

}

测试代码:

[cpp] view plaincopy

// WinDemo.cpp : 定义控制台应用程序的入口点。

//

#include "stdafx.h"

#include <iostream>

#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 0;

}

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



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

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

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

禁用方法如下:

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

/article/2725429.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: