很多人对C++中的几个类型转换操作符是有些陌生的,并且代码中类型转换也从来都是用C风格的强制类型转换。而且会有些人认为使用这些操作符麻烦,不方便或者没必要。下面是对网上一些资料的总结,主要分析一下两种方式的用法和各自优点。
C风格的类型转换
显示强制类型转换:用法为 type (expression)或(type)expression
经强制类型转换运算符运算后,返回一个具有type类型的数值,这种强制类型转换操作并不改变操作数本身,运算后操作数本身未改变。如:
- int nVar=0xab65;
-
char cChar=char (nVar);
上述强制类型转换的结果是将整型值0xab65的高端两个字节删掉,将低端两个字节的内容作为char型数值赋值给变量cChar,而经过类型转换后nVar的值并未改变。
隐式强制类型转换:
隐
式类型转换发生在赋值表达式和有返回值的函数调用表达式中。在赋值表达式中,如果赋值符左右两侧的操作数类型不同,则将赋值符右边操作数强制转换为赋值符
左侧的类型数值后,赋值给赋值符左侧的变量。在函数调用时,如果return后面表达式的类型与函数返回值类型不同,则在返回值时将return后面表达
式的数值强制转换为函数返回值类型后,再将值返回。
C++新增的类型转换操作符
static_cast: 用法为 static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
- 用于类层次结构中基类和子类之间指针或引用的转换。
- 进行上行转换(把子类的指针或引用转换成基类表示)是安全的;
- 进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检查,所以是不安全的。
- 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
- 把空指针转换成目标类型的空指针。
- 把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。
主要特性如下:
- static_cast 在编译时使用类型信息执行转换,在转换执行必要的检测(诸如指针越界计算, 类型检查),其操作数相对是安全的。reinterpret_cast 仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换, 如:
- int n=9;
-
double d=static_cast < double > (n);
上面的例子中, 我们将一个变量从 int 转换到 double。 这些类型的二进制表达式是不同的。 要将整数 9 转换到 双精度整数
9,static_cast 需要正确地为双精度整数 d 补足比特位。其结果为 9.0。而reinterpret_cast 不会有这样的操作。
- 在一类东西都可以转, 但是不是一类的就不能转。即, 语义上说不通的, 两个完全不同的数据类型 static_cast 是拒绝工作的。比如你想把一个指针转成浮点数。
dynamic_cast: 用法为 dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。在类层次间进行上行转换时,dynamic_cast和
static_cast的效果是一样的;在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
主要特性如下:
- dynamic_cast用于一颗类继承树上转换时,将利用 RTTI 在运行时检查。一般用于下行转换,即将基类指针转换为派生类指针。比如,
- class A {};
-
class B : public A {};
-
A* a=new B();
这时, 可以用 dynamic_cast 做 类型转换, 把 a 转成 B*。和 static_cast 不同, dynamic_cast 会检查一下 a 到底是不是指向一个B,如果转不了, 将返回一个 NULL。
所以,这种情况下dynamic_cast比static_cast更严格更安全,但执行效率上比static_cast要差。
注意:这里要求派生类要有虚拟函数,否则编译不能够通过。而static_cast则无此要求。
reinterpret_cast 用法为 reinterpret_cast < type-id > (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。
如:它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)。该运算符的用法比较多。
主要特性如下:
- reinterpret_cast 比 static_cast 更接近 C 的强制转换。它可以实现看起来没关系的两种类型的转换,当然它比 static_cast 危险。
const_cast 用法为 const_cast < type-id > (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外,type_id和expression的类型是一样的。
常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
注意:某些情况下const_cast是比reinterpret_cast更加危险的操作,它的使用一定要慎重。
通过以上两种方式的用途的分析会发现:C风格的强制类型转换可以完成C++中四个操作符的所有功能,那么这些操作符就显得很麻烦,很没必要。
但实际上,正是因为C风格的强制类型转换太过粗暴,可以完成几乎所有类型转换的操作,这会使得编译器很难捕捉误用。如:代码中误写的极危险的或者不同于开发者本意的 类型转换操作都能直接编译通过,这会非常影响代码安全性。
同时,C风格类型转换在程序中很难被发现,编辑器里不能方便的查找到类型转换。而因为类型转换操作存在很大的安全隐患,这会造成代码开发维护上极大不便。
总之,C++类型转换操作符的处理方式,能让程序员更清晰的表达他们的真实意图,也使编译器能捕捉到更多的错误,避免类型转换中的误操作。如:
- int a = 7;
- double* p1 = (double*) &a; // ok(但指向的并非 double 类型的对象)
- double* p2 = static_cast<double *>(&a); // 错误
- double* p2 = reinterpret_cast<double *>(&a); // ok:我真的想这么干
-
- const int c = 7;
- int* q1 = &c; // 错误
- int* q2 = (int*)&c; // ok(但 *q2=2; 仍然是不合法的代码,而且有可能失败)
- int* q3 = static_cast<int *>(&c); // 错误:static_cast 不能去除 const 属性
- int* q4 = const_cast<int *>(&c); // 我的确想这么干
总的来说,应该尽量避免使用类型转换,实际工程代码中经常会用到的也就是类层次结构中基类和子类之间的转换,所以static_cast和
dynamic_cast比较常用。最后,尽管类型转换操作符的方式没有非常明显的优势,还是建议在C++工程中,应该尽量避免C风格的类型强转。使用类
型转换操作符的方式进行类型转换,是一种好的良好编码的习惯,他们会使你受益。
阅读(906) | 评论(0) | 转发(0) |