int i, j;
double k = i / j;
但我们都知道,这样的操作得来的结果是不正确的,根据隐式类型转换规则,它得到的结果将是舍弃小数点后的部分再转换为double类型的值。由此,难道我们的代码中需要做相除操作的地方只能统一定义为浮点类型么?答案是否定的,我们还可以通过显式类型转换得到预期的结果!比如:
int i, j;
double k = (double)i / j;
对i进行显式类型转换之后,若要进行计算,则需要i,j类型一致,j就将被隐式转换为double类型,如此,计算就得到正确结果。
或许,很多人都习惯了这种强制转换的用法,但其实,这种老式的强制转换并非安全的。显式强制转换就会干扰编译器的类型检查,也就是说,若我们使用了这种老实的强制转换,就将会下面的这种情况:
int value = 0;
int *pValue = &value;
char *p = (char *)pValue;
char *类型的操作在一定程度上都与int *类型有所不同,如果上述操作不被干预,那结果就将是未知的,未知的结果就是可怕的。所幸,我们有了更具体、更好用的显式类型转换方式——命名强制类型转换。
命名强制类型转换形式如下:
cast_name <type> (expression);
其中type为转换的目标类型,expression则为需要转换的表达式,cast_name则是命名的强制转换方式,分为四种:static_cast, dynamic_cast, const_cast及reinterpret_cast。
任意具有明确定义的表达式,只要不具有底层const(low-level const),都可以使用static_cast。static_cast常用的方式大致分两种:1.较大数据类型转换为较小数据类型时,编译器将给予警告,若使用了static_cast,将不会再有警告;2.对编译器无法自动执行的类型转换进行转换。对于第二种,值得一提,比如我们可以使用static_cast从void *类型转换原类型:
double value;
void *p = &value;
double *pd = static_cast<double *>(p);
对于上述操作,若不使用static_cast,编译器将会报错,提示不能从void *转到double *。static_cast虽然比老式强制转换更好用,但对于void *的处理方式,也与老式强制转换相差无几,所以,我们即便使用static_cast我们也必须明确确定目标类型是否是正确的!
const_cast的使用范围相较于static_cast无疑小了很多,它只能用于去除底层const(low-level const)的限制,也就是说,我们可以做以下操作:
const char str[20] = "Hello World!";
const char *pStr = str;
char *p = const_cast<char *>(pStr);
cin >> p;
cout << str << endl;
并且str是将被改变的。不过const_cast的用途不多,一般只用于具有函数重载的上下文。
reinterpret_const则是对运算对象的位模式提供较低层次上的重新解释,这就可以用来解决刚开始提出的那个int *转char *的问题,重新解释之后,或许将有可能变为合法。不过它并不常用,要想安全地使用它,必须清楚地了解类型及编译器对于类型转换所做的工作,所以尽量少用较好。
dynamic_cast则是可以在运行时确定类型,更多的不再赘述。后续将会有更清晰介绍dynamic_cast的文章,敬请期待。
对于显式强制转换,我们还是应该尽量避免,毕竟强制转换都是有一定风险的。而对于新式与老式之分,我们还是应该尽量使用新式,虽然它书写麻烦,但毕竟有时候它有更明确的目的性,我们也更容易去追踪程序中出现的错误。对于命名强制转换的内容暂时到此,后续将有另外的主题介绍,敬请期待。