找回密码
 FreeOZ用户注册
查看: 1740|回复: 3
打印 上一主题 下一主题

[论坛技术] RTTI, *_cast与C式强制转换

[复制链接]
跳转到指定楼层
1#
发表于 11-5-2009 18:49:36 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?FreeOZ用户注册

x
以C语言中,我们经常写出:
T t = (T)x;

这类代码。其中一个典型的用法就是struct sockaddr的转换。在Java用,不同类型的转换也是用这种看起来有点强横的强制转换方法来做。
而新的C++语法中(到底是哪个版本?C++98?谁能告诉我一下?),引入了各种*_cast操作符,对上面的转换做了各种细化。
如果你的C++语言书没有提及*_cast,可能就需要考虑换一本更新版本的书了。

RTTI:

C++类型转换的第一个重要改变是RTTI。对于Java程序员来说,instance of操作符是很熟悉的。对应地,C++有typeid操作符:

typeid(e)

这里e是任意表达式或类型名。这里要注意,e可以是类型名,而并不必须是“类”名。这就包括了:

0. 常量
1. 内置数据类型,如int, long, char, float和指针,等。
2. 不包含虚函数的类
3. 包含虚函数的类

其中0和1与Java的instance of的适用范围有所不同。而0-2的结果都返回“静态类型”,3则返回“动态类型”。比如:

Base * x = new Derived();

其中 *x 的静态类型为Base,而动态类型则是Derived,至于typeid(*x)返回的是Base还是Derived,就需要看
Base类中有没有虚函数了。(个人认为,不管怎样说,这样的结果是让人头痛、头晕、半身麻痹、动脉收缩,瞳孔放大…………的)

需要注意的是:typeid(e)中的e还可以是类名,比如
if(typeid(*x) == typeid(Derived))

这是合法的。不过,必须知道,这里用的是typeid(*x),而不是typeid(x)。

还有一个容易搞不好的问题是:typeid(*x),到底要不要求指针求解?这就要看Base大爷是哪个门派的了。如果Base大爷
用到了virtual,那就武当派的,如果x没有真的指某个内存对象,就会抛出bad_typeid异常。但如果Base大爷不玩virtual
那就少林派的,编译器不去求*x的值,而是直接返回其静态类型。(无语吧?)

和typeid()息息相关的是type_info类,可惜这个类并没有确定的定义。作为基础,所有的编译器都必须实现 = , !=, .name(), .before()
这四个操作(方法),而.name()和.before()的实现并没有强制性要求。

如果只是想做一个简单的类型测试,看看某个对象 x 是不是 Base 类的对象或其子类的对象,使用typeid(e)似乎做不到,
只能用dynamic_cast<Base>(x)搞一次,不出问题的就是Base类或其子类的对象。如果哪位同学有更好的方法,麻烦告诉我一下。

内容太多,只好另起一篇来写了。请见续集《RTTI, *_cast与C式强制转换[二]》(如果有的话)。

参考:
* C++ Primer 第4版,18.2

评分

参与人数 1威望 +30 收起 理由
coredump + 30 你太有才了!

查看全部评分

回复  

使用道具 举报

2#
 楼主| 发表于 11-5-2009 19:49:49 | 只看该作者

RTTI, *_cast与C式强制转换[二]

* dynamic_cast<Type>(e)

相对于typeid(e)而言,dynamic_cast<Type>(e)才是RTTI的代表人物。

虽然我对C++引入的*_cast的看法有点保留,但dynamic_cast<Type>(e)的正面作用学是很明显的。
此君最重要的目的不是为了转换,而是为了“安全”转换。只可惜dyanic_cast<Type>(e)只能针对基类
带了虚函数的对象引用或指针的转换。因为只有这种对象,才能在基虚函数表中找到上下行的类关系表,
才能确保安全。

所谓安全,就是在转换的同时测试转换进行是否成功。对于指针类型的转换,可以通过传统的NULL返回
实现;而引用转换就必须通过bad_cast exception机制了。

由于C++支持多继承,在进行dynamic_cast<>的时候,可以通过dynamic_cast<>不同类之间的切换,
至于是否成功,就要看返回值或exception。而static_cast<>是不能在全不相关的两个类之间转的。
回复  

使用道具 举报

3#
 楼主| 发表于 11-5-2009 20:06:09 | 只看该作者

RTTI, *_cast与C式强制转换[三]

* static_cast<Type>(e)

static_cast<Type>(e)如其名字所说,就是做静态的转换。所谓静态,就是在编译时就做好的转换,
而dynamic_cast则是在运行时动态的根据对象的多态性进行的转换。也就是说static_cast不是RTTI的
一部分,而是CTTI的一部分——CTTI?这是小弟随口说的词,别太当真了。

static_cast<Type>(e)中e可以是:

1. 类指针/引用
2. void *
3. 数值类型,包括int, long, short, char, float, double

下面的几种类型转换是不成功的:
1. 完全没有关系的类指针转换。如果一个类是通过private继承另一个类,则这两个类实质上还是父子关系,可以通过static_cast进行转换
2. 指针和数值类型的转换
    int ix = 10;
    int px = static_cast<int>(& ix);
    上面的这个转换是不成功的,需要用reinterpret_cast<int>(&ix)来转换

总的来说,static_cast<>不需要多态/虚函数的支持,和dynamic_cast<>相比不安全,但有一定的灵活性,同时又比C中的强制转换多点限制

其他转换就不说了。。。。

以上都只是一些个人经验和观点,请各位同学多多批评指正。


参考:
* http://msdn.microsoft.com/en-us/library/c36yw7x9(VS.80).aspx
回复  

使用道具 举报

4#
发表于 11-5-2009 20:45:29 | 只看该作者
dynamic_cast有一个很大的缺点,就是性能非常糟糕,所以一定要记住千万别在频繁调用的地方使用dynamic_cast,性能具体有多糟糕可以参考下面这个链接:http://stackoverflow.com/questio ... -is-dynamic-casting
oops:不是上面的链接,有一个测试的,忘记是哪个网站了,谁有兴趣可以测试下。
回复  

使用道具 举报

您需要登录后才可以回帖 登录 | FreeOZ用户注册

本版积分规则

小黑屋|手机版|Archiver|FreeOZ论坛

GMT+11, 24-2-2025 14:16 , Processed in 0.034587 second(s), 20 queries , Gzip On, Redis On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表