相比于传统的类型转换,C++中的4种cast作用更加明确,使代码更具有可读性,下面就来依次介绍一下:
1.static_cast
任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast,这几乎就是传统类型转换的替代(为什么是几乎,下文会提到),比如:
double a=10;
int b = static_cast<int>(a);
相比于传统的int b=(int)a,使用static_cast更像是告诉编译器“我知道转换会发生就精度的损失,但是我还是要进行转换”,所以使用static_cast的情况下,编译器也不会发出警告。
但如果想使用他来进行底层const的转换,就会产生问题,比如:
相比之下,传统的类型转换可以进行这样的转换,即使转换后在使用上会有些问题
const int a = 10;
const int *pa = &a;
int *p = (int*)pa;
*p = 11;
cout << *p << endl;
cout << a << endl;
这段代码在visual studio2017下诡异的输出11和10.
另外,如同下面这种类型的转换,用static_cast也是做不到的。
const double a = 10;
const double* pa = &a;
char *p = (char*)(pa);
2.const_cast
const_cast转换可以去掉底层const属性,一旦我们去掉了底层const属性,编译器就不再阻止我们对const对象进行改变了;但尽管如此,我们还需要注意的是,如果我们试图将一个指向常量的指针转换为一个普通指针,再通过这个普通指针去修改原来的常量时,会产生未定义的行为:
#include<iostream>
using namespace std;
int main() {
int a = 10;
const int ac = 10;
const int *pa = ∾
int *p = const_cast<int*>(pa);
*p = 11;
cout << *p << endl;
cout << *pa << endl;
cout << ac << endl;
cout << p << endl;
cout << pa << endl;
cout << &ac << endl;
return 0;
}
测试代码和运行结果如上;
就如上面的代码,我们试图将ac的值改为11,但在visual studio2017的编译环境下(在gcc 7.3.0下也是如此),我们发现,当我们直接访问ac时,ac的值仍然是10,但惊悚的是,当我们通过指针p和指针pa访问ac时,会发现ac的值变成了11,更惊悚的是,指针p、pa指向的地址和ac的地址一样!
另外,const_cast在重载函数中比较有用;
以下例子取自《C++primer 5th》209页:
const string& shorterString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
string& shorterString(string& s1, string& s2)
{
auto &s = shorterString(const_cast<string&>(s1), const_cast<string&>(s2));
return const_cast<string&>(s);
}
第一个版本的shorterString函数可以用来返回两个字符串常量当中长度小的那一个,但当我们需要非常量版本的时候,我们就可以使用const_cast轻易做到这一点,对于上面的那个例子,你可能会说,shorterString函数的逻辑如此简单,我再实现一遍也可以,但是,当我们函数的内部逻辑较为复杂的时候,这就是一种简洁明了的实现重载函数的方式。
3.reinterpret_cast
reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。(《C++primer 5th》145页)
通俗点说,就是你可以让一个char*把他所指向的对象当做char,即使他实际上并不是char,还是原来的类型;
double a = 10.5;
double* pa = &a;
int *p = reinterpret_cast<int*>(pa);
同样的,reinterpret_cast不能用来丢掉const,,要丢掉底层const,请使用const_cast。
另外,它也无法提供高精度向低精度的转换:
要使高精度转换到低精度,可以用传统的类型转换和static_cast。
4.dynamic_cast
C++的运行时类型识别功能由两个运算符实现:dynam_cast和typeid
其中,dynamic_cast的使用形式如下:
dynamic_cast<type*>(e)
dynam_cast<type&>(e)
dynamic_cast<type&&>(e)
这里的type必须为类类型,而且通常情况下有虚函数(毕竟主要用来实现多态功能)
如上所示,在第一种形式中,e必须为类的指针;在第二种形式中,e必须为类的左值引用;在第三种形式中,e必须为类的右值引用;
dynamic_cast用于运行时在父类和子类中向上或者向下转换,如果转换成功,则返回转换后的类型;在转换失败时,如果目标类型是指针,则返回0,目标类型为引用,则返回一个bad_cast异常。
举个使用的例子,假定Derived类是Base类的公有派生类,bp是一个Base类型的指针,而Base类至少有一个虚函数:
if(Derived* dp=dynamic_cast<Derived*>bp)//bp指向一个Derived对象
{
//doSomthing...
}
else//bp指向一个Base对象
{
//doSomthing...
}
使用应用的dynamic_cast可以参照使用指针的dynamic_cast,只是在转换失败时的表达略有不同。
最后说一句,使用任何类型转换都需要谨慎谨慎再谨慎,一定要保证转换前和转换后的结果都在自己的掌握之中,因为如果在类型转换上出了问题,最后都是比较难找的。