类型转换
前言
C++语言中,某些类型之间有关联。如果两种类型有关联,那么程序需要其中一种类型的运算对象时,可以用另一种关联类型的对象或值来替代。换句话说,如果两种类型可以相互转换,那么它们就是关联的。
一、隐式转换
类型转换是自动执行的,无须程序员的介入,甚至不需要程序员了解的,它们被称作隐式转换。
int ival = 3.541 + 3; //编译器会自动转换类型,但是会损失精度,输出:6
1.何时发生隐式类型转换
在下面这些情况下,编译器会自动转换运算符对象的类型:
1.在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。
2.在条件中,非布尔值转换成布尔类型。
3.初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算符对象转换成左值运算符对象的类型。
4.如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。
5.函数调用时也会发生类型转换。
二、算术转换
算术转换的含义是把一种算术类型转换成另外一种算术类型,算术转换的规则定义了一套类型转换的层次,其中运算符的运算对象将转换成最宽的类型。
比如,如果一个运算对象的类型是long double,那么不论另外一个运算对象的类型是什么都会转换成long double。
三、整型提升
整型提升负责把小整数类型转换成较大的整数类型。对于bool、char、signed、char、unsigned char 、short和unsigned short等类型来说,只要它们所有可能的值都存在int里,它们就会提升成int类型;否则,提升成unsigned int类型。比如布尔值false提升成0,true提升成1。
四、其他隐式类型转换
1.数组转换成指针
在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:
int ia[10]; //含有10个整数的数组
int *ip = ia; //ia转换成指向数组首元素的指针
当数组被用作decltype关键字的参数,或者作为取地址符(&)、sizeof及typeid等运算符对象时,上述转换不会发生。同样的,如果用一个引用来初始化数组,上述转换也不会发生。
2.指针的转换
C++还规定来几种其他的指针转换方式,包括常量整数值0或者字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*。
3.转换成布尔类型
存在一种算术类型或指针类型向布尔类型自动转换的机制。如果指针或算术类型的值为0,转换结果为false,否则转换结果为true:
char *cp =get_string();
if(cp) /*...*/ //如果指针cp不是0,条件为真
while(*cp) /*...*/ //如果*cp不是空字符,条件为真
4.转换成常量
允许将指向非常量类型的指针转换成指向相应常量类型的指针,对于引用也是这样。也就是说,如果T是一种类型,我们就能将向T的指针或引用分别转换成指向const T的指针或引用:
int i;
const int &j = i; //非常量转换成const int的引用
const int *p = &i; //非常量的地址转换成const的地址
int &r =j, *q = p; //错误:不允许const转换成非常量
相反的转换并不存在,因它试删除底层const。
5.类类型定义的转换
类类型定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型的转换。
string s,t = "a value"; //字符串字面值转换成string类型
while(cin >> s) //while的条件部分把cin转换成布尔值
二、显示转换
有时候我们希望显示地将对象强制转换成另外一种类型。例如,如果想在下面的代码中执行浮点数除法:
int i,j;
double slope = i/j;
就要使用某种方法将i或j显示的转换成double,这种方法称作强制类型转换。
1.命名的强制类型转换
一个命名的强制类型转换具有如下形式:
cast-name<type>(expression)
其中,type是转换的目标类型而expression是要转换的值。如果type是引用类型,则结果是左值。cast-name是static_cast、dynamic_cast、const_cast和reinterpret_cast中的一种。
一.static_cast
任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。例如,通过将一个运算对象强制转换成double类型就能使表达式执行浮点数除法:
//进行强制类型转换
double slope =static_cast<double>(j)/i;
当需要把一个较大的算术类型赋值给较小的类型时,static_cast非常有用。
static_cast对于编译器无法自动执行的类型转换也非常有用。例如,我们可以使用static_cast找回存在于void*指针中的值:
void *p = &d; //正确:任何非常量对象的地址都能存入void*
double *pd =static_cast<double*>(p); //正确:将void*转换回初始的指针类型
强制转换的结果要与原始的地址值相等,所以我们必须确保转换后所得的类型就是指针所指的类型。
二.const_cast
const_cast只能改变运算对象的底层const:
const char *pc;
char *p =const_cast<char*>(pc); //正确:但是通过p写值是未定义的行为
对于将常量对象转换非常量对象的行为,一般称其为“去掉const性质”。一旦失去const性质,编译器不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,在使用const_cast执行写操作就会产生回定义的后果。
只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改表达式的常量属性都会引发错误。也不能用const_cast改变表达式的类型:
const char *pc;
char *q =static_cast<char*>(pc); //错误:static_cast不能转换掉const性质
static_cast<string>(cp); //正确:字符串字面值转换成string类型
const_cast<string>(cp); //错误:const_cast值改变常量属性
2.旧式的强制类型转换
C++语言中,显示地进行强制类型转换包含两种形式:
type(expr); //函数形式的强制类型转换
(type)expr; //c语言风格的强制类型转换
旧式的强制类型转换分别具有与const_cast、static_cast相似的行为。当在执行旧式的强制类型转换时,如果换成const_cast和static_cast也合法,则其行为与对应的命名转换一致。如果替换后不合法,则旧式的强制类型转换执行为reinterpret_cast类似的功能:
char *pc =(char*)ip; //ip是指向整数的指针
总结