博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
static_cast而后RTT1
阅读量:5312 次
发布时间:2019-06-14

本文共 6167 字,大约阅读时间需要 20 分钟。

 

问题: Static_cast 与 Dynamic_cast的区别

来自书本上的解释:

  用 static_cast<type-id > ( expression )  

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

   用来数值之间的转化。

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

   比如

#include
<
iostream
>
using
namespace
std;
class
Base
{
public
:
virtual
void
f() {cout
<<
"
Base::f()
"
<<
endl;}
};
class
Derive:
public
Base
{
public
:
virtual
void
f() {cout
<<
"
Derive::f()
"
<<
endl;}
virtual
void
f2() {cout
<<
"
Derive::f1()
"
<<
endl;}
};
int
main()
{
Base
*
pbase1
=
new
Derive();
Derive
*
pderive1
=
static_cast
<
Derive
*>
(pbase1);
pderive1
->
f();
//
Derive::f()
Base
*
pbase2
=
new
Base();
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
>
using
namespace
std;
class
Base
{
public
:
virtual
void
f() {cout
<<
"
Base::f()
"
<<
endl;}
};
class
Derive:
public
Base
{
public
:
virtual
void
f() {cout
<<
"
Derive::f()
"
<<
endl;}
virtual
void
f2() {cout
<<
"
Derive::f1()
"
<<
endl;}
};
int
main()
{
Base
*
pbase1
=
new
Derive();
Derive
*
pderive1
=
dynamic_cast
<
Derive
*>
(pbase1);
//
down-cast
pderive1
->
f();
//
Derive::f()
Base
*
pbase2
=
new
Base();
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
>
3
 
using
namespace
std;
4
5
 
class
Base
6
{
7
 
public
:
8
virtual
void
f() {} // it must need the virtual table
9
};
10
11
12
 
class
Derive:
public
Base
13
{
14
15
};
16
17
18
 
class
Derive2:
public
Base
19
{
20
21
};
22
23
 
void
f(Base
*
pbase)
24
{
25
const
type_info
&
typeinfo
=
typeid(pbase);
26
cout
<<
typeinfo.name()
<<
endl;
27
28
29
if
(NULL
!=
dynamic_cast
<
Derive
*>
(pbase))
30
{
31
cout
<<
"
type: Derive
"
<<
endl;
32
}
33
else
if
(NULL
!=
dynamic_cast
<
Derive2
*>
(pbase))
34
{
35
cout
<<
"
type: Derive2
"
<<
endl;
36
}
37
else
38
{
39
//
ASSERT(0)
40
 
}
41
}
42
43
44
 
int
main()
45
{
46
Base
*
pbase1
=
new
Derive();
47
f(pbase1);
48
49
Base
*
pbase
=
new
Derive2();
50
f(pbase);
51
}

out put:

1
class
Base
*
2
type: Derive
3
 
class
Base
*
4
type: Derive2

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

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

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

下面来回答这个问题。

3.RTTI 如何与对象绑定

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

class
Point
{
public
:
Point(
float
xval );
virtual
~
Point();
float
x()
const
;
static
int
PointCount();
protected
:
virtual
ostream
&
print( ostream
&
os )
const
;
float
_x;
static
int
_point_count;
};

其内存中模型:

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

下面在VC中验证一下,

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

下面验证:

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

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

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

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

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

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

  将上面的代码以汇编形式输出,查看。

24
: Derive * pderive = dynamic_cast<Derive*>(pbase)
;
push
0
push
OFFSET ??_R0?AVDerive@@@8
push
OFFSET ??_R0?AVBase@@@8
push
0
mov
eax, DWORD PTR _pbase$[ebp]
push
eax
call
___RTDynamicCast
add
esp,
20
;
00000014H
mov
DWORD 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 和该图:
 

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

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

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

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

1
#include
"
iostream
"
2
#include
"
string
"
3
#include
<
typeinfo
>
4
using
namespace
std;
5
6
7
class
Base
8
{
9
public
:
10
virtual
void
f() { }
11
};
12
13
class
Derive :
public
Base
14
{
15
};
16
17
typedef unsigned
long
DWORD;
18
19
struct
TypeDescriptor
20
{
21
DWORD ptrToVTable;
22
DWORD spare;
23
char
name[ ];
24
};
25
struct
RTTICompleteObjectLocator
26
27
{
28
29
DWORD signature;
//
always zero ?
30
31
DWORD offset;
//
offset of this vtable in the complete class
32
33
DWORD cdOffset;
//
constructor displacement offset
34
35
struct
TypeDescriptor
*
pTypeDescriptor;
//
TypeDescriptor of the complete class
36
37
int
*
ptr;
38
//
struct RTTIClassHierarchyDescriptor* pClassDescriptor;
//
describes inheritance hierarchy
39
40
};
41
42
43
int
main()
44
{
45
46
Base
*
pderive
=
new
Derive();
47
48
int
**
ptr
=
(
int
**
)(
&
pderive);
49
50
int
*
ptable
=
(
int
*
)(
*
(
int
*
)(
*
ptr));
51
52
int
*
rtti
=
ptable
-
1
;
53
54
RTTICompleteObjectLocator
*
RIIT_locator
=
(RTTICompleteObjectLocator
*
)(
*
(
int
*
)rtti);
55
56
cout
<<
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

你可能感兴趣的文章
简单【用户输入验证】
查看>>
python tkinter GUI绘制,以及点击更新显示图片
查看>>
20130330java基础学习笔记-语句_for循环嵌套练习2
查看>>
Spring面试题
查看>>
窥视SP2010--第一章节--SP2010开发者路线图
查看>>
C语言栈的实现
查看>>
代码为什么需要重构
查看>>
TC SRM 593 DIV1 250
查看>>
SRM 628 DIV2
查看>>
2018-2019-2 20165314『网络对抗技术』Exp5:MSF基础应用
查看>>
Python-S9-Day127-Scrapy爬虫框架2
查看>>
SecureCRT的使用方法和技巧(详细使用教程)
查看>>
右侧导航栏(动态添加数据到list)
查看>>
81、iOS本地推送与远程推送详解
查看>>
虚拟DOM
查看>>
自建数据源(RSO2)、及数据源增强
查看>>
关于View控件中的Context选择
查看>>
2018icpc徐州OnlineA Hard to prepare
查看>>
Spark的启动进程详解
查看>>
使用命令创建数据库和表
查看>>