C++标准转换运算符

  C++类型转换在实际编程中会经常使用,其实,本质上对象的类型用来解释(interpret)对象。因为,每个对象都占据一块内存空间,这块内存空间存放了一段二进制数据。通过标记该对象的类型,告诉如何看待这块二进制数据。而对象数据类型转换,就是将源类型解释的内存空间转换为目标类型来解释这块内存空间。

  • 传统转换方式(Traditional Type-Casting)是沿用c的方式,通过强制类型转换,隐式转换,显示转换等方式来标识内存空间的解释模式,这种方式功能强大,约束少,但存在安全隐患。因此,通过 static_cast, dynamic_cast, reinterpret_cast,以一种更安全的方式来完成传统转换方式的替换。
  • static_cast 是在编译时就对类型进行检查和转换,是对传统转换的一种规范的表现形式。它会编译期检查源类型与目标类型的关系,以确定这种转换可否实现。
  • dynamic_cast是在运行时动态对源类型和目标类型进行检查,以确定转换可否实现,并且还会进行源类型对象和目标类型对象的完整性检查。
  • reintepret_cast是一种"野蛮"的强行转换,它不过多进行源对象类型和目标对象类型的检查,只将源对象内存空间按照目标对象类型进行解释即可。

一般在转换过程中可以通过如下几种方式来实现:

转换类型 应用场景和使用
 传统的转换方式(C式或函数式)

使用在基本数据类型和自定义对象类型转换,各种类型的强制转换等场合,使用范围很广,缺点是存在安全隐患,因此提供其他四种类型转换在特定的场合来做替代。

这也是延续C的形式,当然这类转换也是可以用static_cast来替换,但是因为是基本类型,所以  传统转换已经很直观

static_cast转换运算符

用在特定场景 对象转换 之上,static_cast会调用相应的构造函数或者重载的转换运算符[不包括赋值运算符]。对于单参构造函数的存在可能会引发一些隐式的转换,因此用static_cast也可以明确的指出类型的转换过程,避免生成多余的临时对象造成效率下降(Google C++ Style Guide)

[虽然static_cast也可以用在有继承关系的类型指针之间,但是还是将这方面的转换交给dynamic_cast来操作吧]

dynamic_cast转换运算符

将其用在具有继承关系的指针类型之间的转换

无论是从基类到子类的转换,还是子类到基类的转换,都将dynamic_cast套上去,也算是标识它们是一家子

reinterpret_cast转换运算符

一般用在将对象指针类型转换到整数类型或者void * (空指针)。

需要注意的是,若要使用其结果,一定要将类型转换回去后使用,否则会存在隐患。也不要将随意的整数转换成指针类型

const_cast转换运算符

用在需要去除掉const限定的时候。

其实这种情况出现的很少,可能的方法在const_cast一文中已经又举例,不过还是反复强调, 使用const_cast转换后,绝对不可试图修改结果的值

  如果任何一种基于指针或引用的转换,套上四个转换运算符之后都失败,那么所要进行的转换可能就触到了"雷区"了:进行了没意义的转换。比如,对于没有关系的两个类型的指针进行了转换,比如试图转换指向方法的指针了。所以转换运算符对于避免代码出错也很有帮助。

下面我们一一分析上述转换类型


一、传统的转换方式(Traditional Type-Casting)

  C++作为C语言的超集,完全继承了C语言所具有的类型转换方法和能力,因此对于这部分在基础数值类型上的转换是比较容易理解的。但是因为C++是面向对象的语言,有类的概念,因此让又多一层需要理解的内容。

  • 1. 隐式转换 (Implicit Conversion)

       隐式转换不需要任何转换运算符,编译器会自动根据类型兼容性进行不同类型之间的转换。

  • 2. 显示转换 (Explicit Conversion)

    显示转换要表明所要转换的目标对象是什么样的类型,然后编译器会做出转换,它有两种格式:

    • C语言格式(C-like Cast)

       (new_type) expression

    • 函数式(Function-style Cast)

        new_type (expression)


二、static_cast <new_type> (expression)

  • 1.  和传统转换类型一样,static_cast可以对基础类型和对象进行类型转换。而基础类型和对象的转换都是其他三个转换运算符所办不到的。

     这些转换跟传统类型转换比较类似,即传统转换均可加上static_cast。

     static_cast会根据下述顺序寻找合适的方法进行类型转换。

    • <1>. 构造函数(Constructor)
    • <2>. 类型转换运算符(Type –Cast Operator)

    *赋值运算符并不被static_cast作为实际的类型转换使用,因为它自身已经是一种运算符,不能再当做转换运算符来用。

  • 2. static_cast不仅可以用在基本数据类型和对象上,还可以用在指针和引用上。

    在指针和引用方面,只有继承关系是可以被static_cast接受的,其他情况的指针和引用转换都会被static_cast直接扔出编译错误,如下代码。

    这就要通过reinterpret_cast<type>()来完成了。

class A{
public:
int m_a;
} int main(){
A* pa = new A();
int ia = static_cast<long>(pa);
return ;
}

  error: invalid static_cast from type ‘A*’ to type ‘long int’
  int ia = static_cast<long>(pa);
                   ^

  继承关系类型间转换可以通过static_cast,但会存在安全隐患,实际上这层关系上的转换又几乎都可以被dynamic_cast所代替。

  因此,static_cast<type>()用在特定场景对象转换之上,static_cast会调用相应的构造函数或者重载的转换运算符[不包括赋值运算符]。对于单参构造函数的存在可能会引发一些隐式的转换,因此用static_cast也可以明确的指出类型的转换过程,避免生成多余的临时对象造成效率下降.[虽然static_cast也可以用在有继承关系的类型指针之间,但是还是将这方面的转换交给dynamic_cast来操作吧]


三、dynamic_cast <new_type> (expression)

  dynamic_cast是动态类型转换,及对象类型的转换过程发生在运行时,如果转换不成功就返回null,转换成功返回目标对象。dynamic在转换的过载中会对源对象和目标对象类型做完整性检查。也就是目标对象的内存模型必须 >= 源目标对象的内存模型

  • 1. 子类 -->基类 ,static_cast和dynamic_cast都是成功并且正确的(所谓成功是说转换没有编译错误或者运行异常;所谓正确是指方法的调用和数据的访问输出是期望的结果),这是面向对象多态性的完美体现。     
  • 2. 基类 -->子类 ,  static_cast和dynamic_cast都是成功的,但是正确性方面,我对两者的结果都先进行了是否非空的判别:
    • dynamic_cast的结果显示是空指针,
    • 而static_cast则是非空指针。

    但很显然,static_cast的结果应该算是错误的,子类指针实际所指的是基类的对象,而基类对象并不具有子类的Study()方法。

  • 3. 对于没有关系的两个类之间的转换,输出结果表明,
    • dynamic_cast依然是返回一个空指针以表示转换是不成立的;
    • static_cast直接在编译期就拒绝了这种转换。
    • reinterpret_cast成功进行了转换,而且返回的值并不是空指针,但是结果显然是错误的。

  最后,程序里还用dynamic_cast希望把用其他转换运算符转换过去的指针转换回来。对于使用static_cast转换后指向了子类对象的基类指针,dynamic_cast判定转换是合理有效的,因此转换成功获得一个非空的指针并且正确输出了结果;而对于reinterpret_cast转换的类型,的确如它的功能一样——重新解析,变成新的类型,所以才得到dynamic_cast判定该类型已经不是原来的类型结果,转换得到了一个空指针。


四、reinterpret_cast <new_type> (expression)

  reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expressoin)有完全相同的内存空间。

  也就是说,reinterpret_cast会将源对象的内存空间按照目标对象的类型格式进行解释,以将源类型对象转换目标类型对象。reinterpret_cast的字面意思:重新解释(类型的内存空间)。转换后的对象是否合法和正确、可用,reinterpret并不做保证。


五、const_cast <new_type> (expression)

  用const_cast来去除const限定,使用const_cast去除const限定的目的绝对不是为了修改它的内容。


六、总结

  以上分别介绍了各种类型转换,及其可能使用的场景。

  总得说来,static_cast和reinterpret_cast运算符要么直接被编译器拒绝进行转换,要么就一定会得到相应的目标类型的值。而dynamic_cast却会进行判别,确定源指针所指的内容,是否真的合适被目标指针接受。如果是否定的,那么dynamic_cast则会返回null。这是通过检查"运行期类型信息"(Runtime type information,RTTI)来判定的,它还受到编译器的影响,有些编译器需要设置开启才能让程序正确运行(导师的PPT详细介绍了Visual Studio的情况),因此dynamic_cast也就不能用传统的转换方式来实现了。

参考 :

  [1]. Type conversions.   http://www.cplusplus.com/doc/tutorial/typecasting/

  [2]. C++标准转换运算符.  http://www.cnblogs.com/ider/archive/2011/07/22/cpp_cast_operator_part2.html

   [3]. C++用户自定义转换(User-Defined Conversion). http://www.cnblogs.com/ider/archive/2011/07/03/cpp_cast_operator_part1.html

[4]. C++标准转换运算符dynamic_cast.  http://www.cnblogs.com/ider/archive/2011/08/01/cpp_cast_operator_part5.html

上一篇:ACM ICPC 2015 Moscow Subregional Russia, Moscow, Dolgoprudny, October, 18, 2015 A. Anagrams


下一篇:hdu 5444 Elven Postman(二叉树)——2015 ACM/ICPC Asia Regional Changchun Online