static_cast而后RTT1

问题: Static_cast 与 Dynamic_cast的区别

来自书本上的解释:

用 static_cast ( expression )

  1. static_cast(expression) The static_cast<>() is used to cast between the integer types. 'e.g.' char->long, int->short etc.

用来数值之间的转化。

  1. 可以在相关指针之间转换,指针在void * 之间转换,还可以在基类和派生类之间转换。 这些转换是在编译的时候就确定下来转换(无非就是根据继承关系,偏移指针而已),但是这需要自己保证安全。

比如
#include<iostream>

usingnamespacestd;



classBase

{

public:

virtualvoidf() {cout<<"Base::f()"<<endl;}



};



classDerive:publicBase

{

public:

virtualvoidf() {cout<<"Derive::f()"<<endl;}

virtualvoidf2() {cout<<"Derive::f1()"<<endl;}



};





intmain()

{



Base
pbase1=newDerive();

Derive
pderive1=static_cast<Derive>(pbase1);

pderive1
->f();//Derive::f()



Base
pbase2=newBase();

Derive
pderive2=static_cast<Derive>(pbase2);

pderive2
->f();//Base::f()

pderive2->f2();//throw exception "Access violation reading"



delete pbase1;

delete pbase2;



}

虽然 由 pbase2 转化到 pderive2 ,编译器编译正确。 但是当调用pderive2->f(); 应该不是希望的; 调用pderive2->f2() 因为基类本身就没有这个函数,说以运行时出错,抛出异常。

所以说static_cast 是编译时确定下来,需要自己确保转换类型安全,否则运行时会抛出异常.

注意static_cast 不能直接在没有继承关系的对象指针之间进行转换。在Com 里面实现不同接口的同个对象,其也不能再接口之间转换(更何况是动态的),所以COM提供一个query 借口。

用法:dynamic_cast < type-id > ( expression)

是专门用于具有继承关系的类之间转换的,尤其是向下类型转换,是安全的。
#include<iostream>

usingnamespacestd;



classBase

{

public:

virtualvoidf() {cout<<"Base::f()"<<endl;}



};



classDerive:publicBase

{

public:

virtualvoidf() {cout<<"Derive::f()"<<endl;}

virtualvoidf2() {cout<<"Derive::f1()"<<endl;}



};



intmain()

{



Base
pbase1=newDerive();

Derive
pderive1=dynamic_cast<Derive>(pbase1);//down-cast

pderive1->f();//Derive::f()



Base
pbase2=newBase();

Derive
pderive2=dynamic_cast<Derive>(pbase2);//up-cast



if( pderive2)//NULL

{

pderive2
->f();

pderive2
->f2();

}



delete pbase1;

delete pbase2;



}

dynamic_cast 如何保证转换是安全的? 如何知道具体该类的具体类型 以及 继承关系呢?

引入RTTI,其存储着类运行的相关信息, 比如类的名字,以及类的基类。下面就介绍RTTI。

2. RTTI (Run Time Type info)

这个神奇的东西用于存储类的相关信息,用于在运行时识别类对象的信息。C++ 里面只记录的类的名字和类的继承关系链。使得编译成二进制的代码,对象可以知道自己的名字(ASCII),以及在继承链中的位置。

C++ 里面提供 一个关键字 typeid , 一个数据类型 typeinfo,以及对应的头文件 typeinfo.h
1#include<iostream>

2#include<typeinfo>

3usingnamespacestd;

4

5classBase

6{

7public:

8virtualvoidf() {} // it must need the virtual table

9};

10

11

12classDerive:publicBase

13{

14

15};

16

17

18classDerive2:publicBase

19{

20

21};

22

23voidf(Basepbase)

24{

25consttype_info&typeinfo=typeid(pbase);

26cout<<typeinfo.name()<<endl;

27

28

29if(NULL!=dynamic_cast<Derive
>(pbase))

30{

31cout<<"type: Derive"<<endl;

32}

33elseif(NULL!=dynamic_cast<Derive2>(pbase))

34{

35cout<<"type: Derive2"<<endl;

36}

37else

38{

39//ASSERT(0)

40}

41}

42

43

44intmain()

45{

46Base
pbase1=newDerive();

47f(pbase1);

48

49Basepbase=newDerive2();

50f(pbase);

51}
out put:
1classBase


2type: Derive

3classBase*

4type: Derive2
可见 Dynamic 是运行时确定的,是安全的。 那么

  1. RTTI 的信息如何和对象绑定在一起?什么时候绑定的?

  2. 为什么dynam_cast 必须要求转换的类型之间要有虚函数?否则编译通不过。

下面来回答这个问题。

3.RTTI 如何与对象绑定

google,找资料。 下面的图来自于 “Inside C++ Model”, RTTI 的info 是如何和对象之间的关系:
classPoint

{



public:



Point(
floatxval );



virtual~Point();



floatx()const;



staticintPointCount();



protected:



virtualostream&print( ostream&os )const;



float_x;



staticint_point_count;



};

其内存中模型:

static_cast而后RTT1

明显RTTI info 存在于虚表的第一项。第二个问题就可以回答,因为RTTI 依赖于虚表,所以用dynamic_cast 对应的类一定要有虚函数。

下面在VC中验证一下,

在VC中,我们知道虚指针指向虚表,对应的虚表第一项就是第一个虚函数。如果我们认为虚函数构成虚表,那么就可以认为RTTI info 就走虚表的紧邻上面。

下面验证:

  1. 在VC 中查看RTTI中类名字

static_cast而后RTT1

从上面图表可见,RTTI 对应的内容是空的。那么VC的实现和 书中的模型不一致吗?难道RTTI不在虚表的上面吗 ?接着有了下面的验证:

  1. 把虚表上面指向RTTI info 的地址,给设置为0, 那么typeid 还可以工作吗? Dynamic_cast 还可以工作吗?如果还可以工作,则说明这个地址指向的数据无关。

static_cast而后RTT1

如果将虚表上的RTTI的指针置空,dynamic_cast 就不能运行,抛出异常“std:: __non_rtti_object” . 那说明这个地址,还是与RTTI有关。 那问题出在哪里?

尝试在google 里面搜索,但是未果。 那么Dynamic_cast 的依赖于 RTTI的信息,那么Dynamic_cast的实现着手看看. 查看一下 他的汇编代码。 于是有了下面的实验。

  1. RTTI 在VC里面如何实现的。

将上面的代码以汇编形式输出,查看。
24: Derive * pderive = dynamic_cast(pbase);



push0

pushOFFSET ??_R0?AVDerive@@@8

pushOFFSET ??_R0?AVBase@@@8

push0

moveax, DWORD PTR _pbase$[ebp]

pusheax

call___RTDynamicCast

addesp,20;00000014H

movDWORD PTR _pderive$[ebp], eax
发现 dynamic_cast的实现依赖于 对象本身,以及??_R0?AVDerive@@@8 和??_R0?AVBase@@@8 . 于是继续查看代码

<擅自略去汇编,有兴趣者看原文>
原来虚表上面指向是一个 Derive::`RTTI Complete Object Locator 。 用google 搜索下面的该关键字,有了下面的文章http://www.openrce.org/articles/full_view/23和该图:

static_cast而后RTT1


谜底揭晓: 原来虚表上面的地址是指向一个结构 Derive::`RTTI Complete Object Locator , 这个结构指向该类的名字,和其对象继承链。

这就回答了第一个问题,RTTI info 如何和对象绑定的? 在对象创建的时候,调用构造函时候,创建虚表以及RTTI info,这样dynamic cast 就可以去访问RTTI,从而保证安全。

同样有个一问题,那就是RTTI 效率底下,试下如果一个类其继承多层,而且有多继承,那么查找链就相当遍历一个链表。

4. 实现一个代码用来从RTTI中读取类名字

1#include"iostream"

2#include"string"

3#include<typeinfo>

4usingnamespacestd;

5

6

7classBase

8{

9public:

10virtualvoidf() { }

11};

12

13classDerive :publicBase

14{

15};

16

17typedef unsignedlongDWORD;

18

19structTypeDescriptor

20{

21DWORD ptrToVTable;

22DWORD spare;

23charname[ ];

24};

25structRTTICompleteObjectLocator

26

27{

28

29DWORD signature;//always zero ?

30

31DWORD offset;//offset of this vtable in the complete class

32

33DWORD cdOffset;//constructor displacement offset

34

35structTypeDescriptorpTypeDescriptor;//TypeDescriptor of the complete class

36

37int
ptr;

38//struct RTTIClassHierarchyDescriptor pClassDescriptor;//describes inheritance hierarchy

39

40};

41

42

43intmain()

44{

45

46Base
pderive=newDerive();

47

48intptr=(int)(&pderive);

49

50intptable=(int)((int)(ptr));

51

52int
rtti=ptable-1;

53

54RTTICompleteObjectLocatorRIIT_locator=(RTTICompleteObjectLocator)((int)rtti);

55

56cout<<RIIT_locator->pTypeDescriptor->name<<endl;

57

58}
Out put:

.?AVDerive@@

当然可以根据RTTI的信息,可以遍历其继承关系图。留作一个练习,可以尝试一下。

总结:

static_cast 用于数值类型之间的转换,也可以用于指针之间的转换,编译的已经确定好,效率高,但须要自己保证其安全性。

dynamic_cast 用于有继承关系的类之间转换,是基于RTTI数据信息的,运行时检测,安全,但是效率低。

Refernce:

RTTI intoduction:

  1. http://www.rcs.hu/Articles/RTTI_Part1.htm [介绍RTTI 的应用,需要的借口,以及一个实现]

  2. http://www.codeproject.com/KB/cpp/dynamic_cpp.aspx
    原文链接: https://www.cnblogs.com/zhanglanyun/archive/2012/05/05/2484994.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/49494

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午1:09
下一篇 2023年2月9日 上午1:11

相关推荐