先后看过几次关于C++类型转换的资料,甚至还用过几次,可对其了解还是很模糊,本次特别予以总结。不是随便写的,真的是用心写的,姑且看吧。
C++命名的强制类型转换也称作新式类型转换,也是C语言类型转换的一个演进。
看一个C语言的类型转换:
1 | char *hopeItWorks = ( char *)0x00ff0000; |
《C++必知必会》一书把该种类型转换描述成“龌龊”,因为这种类型转换相对于C++的类型转换来说力度要大,大到有时你并不清楚具体发生了哪些转换,即有可能转换即使超出了你的期望而没有任何提醒,最终导致程序运行失败。个人观点,该书作者(译者)描述太过于夸张了,C语言是个奔放的语言,其设计理念之一就是:程序员清楚自已在做什么。
C++的类型转换其实把C语言类型转换细分了一下,一分为四。(当然,同时兼容老式类型转换)
1、const_cast
该操作符允许添加或移除表达式中类型的const或volatile修饰符:
1 | const Person *getEmployee() |
5 | Person *anEmployee = const_cast <Person *>(getEmployee()); |
6 | Person *anEmployee = (Person *)getEmployee(); |
上面给出两种类型转换,都是去掉函数所返回指针的const属性,表面上看是一样的,但是如果函数发生改变后,就能看出这两种转换的差异:
1 | const Employee *getEmployee() |
5 | Person *anEmployee = const_cast <Person *>(getEmployee()); |
6 | Person *anEmployee = (Person *)getEmployee(); |
这里函数返回类型发生改变,在const_cast试图将const Employee * 转换为Employee *时就已经超出了其能力范围,会在编译阶段给出提示,这往往是很有用的,当然,C++可以做到能力更强的类型转换,且向下看。
2、static_cast
编译器隐式执行的任何类型转换都可以由static_cast显式完成:
2 | char ch = static_cast < char >d; |
对于从一个较大的算式类型到一个较小类型的赋值,编译器通常产生警告,当我们显式地提供强制类型转换时,警告信息就会关闭。此种类型转换告诉编译器:我想要一个静静的(static)转换,你懂的,请闭嘴。
静态转换常用于基类的指针或引用,转型为一个派生类的指针为引用。
需要注意的是这不是一个非常安全的转换,损失的精度需要程序员去考虑。使用此转换的一个考虑是:可跨平台移植。
3、reinterpret_cast
顾名思义,重新解释。它从位(bit)的角度来看待一个对象,从而允许将一个东西看作另一个完全不同的东西:
1 | char *hopeItWorks = reinterpret_cast < char *>(0x00ff0000); |
2 | int *hopeless = reinterpret_cast < int *>(hopeItWorks); |
这是个非常危险的转换,在C++类型转换里面威力也最大,非常容易出错。举个例子,一本横向排版的书,硬是按照竖向排版的方式进行解读,一般是读不出合理的内容。
给出一种几乎是错误的转换:
2 | char *pc = reinterpret_cast < char *>(ip); |
4、dynamic_cast
顾名思义,动态的转换。大家知道,C++是具有多态特性的,该种转换仅用于对多态类型进行向下转型(也就是说,被转型的表达式的类型,必须是一个指向带有虚函数的类类型的指针)。上面讲的static_cast也可以从基类指换到派生类,但是不安全的。dynamic_cast执行运行期检查工作,来判定转型的正常性,当然是要会出代价的,好比,多态性也会带来性能开销一样。
01 | const Shape *getNextShape() |
05 | const Circle *cp = dynamic_cast < const Circle *>(getNextShape()); |
至此,C++四种转换都讲完了。总结一下,C++类型转换其实是对C语言类型转换的细分及扩充(扩充类类型之间的转换)。
《C++Primer》中有一句特别好:强制类型转换关闭或挂起了正常的类型检查。强烈建议程序员避免使用强制类型转换,不依赖强制类型转换也能写出很好的C++程序。