类型转换
如果两种类型可以相互转换,那么它们就是关联的。
隐式类型转换
- 在大多数表达式中,比
int
类型小的整型值首先会被提升为较大的整数类型。 - 在条件中,非布尔值转换成布尔值。
- 在初始化过程中,初始值转换成变量的类型。
- 在赋值语句中,右侧对象转换成左侧对象的类型。
- 如果算术运算或关系运算的对象有多种类型,需要转换成同一种类型。
算术转换
算术转换的含义是把一种算术类型转换成另外一种算术类型。
整型提升
整型提升是把小整数类型转换成较大的整数类型。
- 对于
bool、char、signed char、unsigned char、short、unsigned short
等类型只要它们的值都是在int
里,都会被提升为int
类型,否则提升为unsigned int
类型。 - 对于较大的
char
类型(wchar_t、char16_t、char32_t
) 提升为int、unsigned int、long、unsigned long、long long、unsigned long long
中最小的类型。
无符号类型的运算对象
如果一个运算对象是无符号类型,另外一个运算对象是带符号类型的:
- 无符号类型不小于带符号类型,那么带符号运算对象转换成无符号的。例如:两个类型分别是
unsigned int
和int
,则int
类型的运算对象转换成unsigned int
。 - 带符号类型大于无符号类型,此时转换的结果依赖于机器:如果无符号类型的所有值都能存在该带符号类型中,则无符号类型的运算对象转换成带符号类型;如果不能,那么带符号类型的运算对象转换成无符号类型。例如:如果两个运算对象的类型分别是
long
和unsigned int
,并且int
和long
的大小相同,则long
类型转换成unsigned int
;如果long 类型占用的空间比 int更大, 则unsigned int
类型转换成long
类型。
boo flag;
char cval;
short sval;
unsigned short usval;
int ival;
unsigned int uival;
long lval;
unsigned long ulval;
float fval;
double dval;
3.1415: + 'a'; //'a'被提升为int,然后该int再转换成long double
dval + ival; //ival转换成double
dval + fval; //fval转换成double
flag = dval; //dval是0,则flag是false,否则flag是true
cval + fval; //cval提升为int,然后该int被提升为float
sval + cval; //savl、cval都被提升为int
cval + lval; //cval 转换成long
ival + ulval; //ival转换成unsigned long
usval + ival; //根据unsigned short和int所占空间大小进行提升
uival + lval; //根据unsigned int和long所占空间大小进行转换
其他隐式类型转换
数组转换成指针:在大多数用到数组的表达式中,数组会自动转换成指向数组首元素的指针。
指针的转换:
- 常量整数值0或者字面值
nullptr
能够转换成任意的指针类型。 - 指向任意非常量的指针都能转换成
void*
。 - 指向任意对象的指针都能转换成
const void *
。
转换成布尔类型:存在一种从算术类型或指针类型自动向布尔类型自动转换的机制。如果指针或算术类型的值为0,转换的结果是 false
,否则转换结果是 true
。
转换成常量:允许将指向非常量类型的类型转换成指向相应的常量类型的指针,对于引用也是这样。也就是说,如果T是一种类型,我们就能将指向T的指针或引用转换成指向 const T
的指针或引用。
int i;
const int &j = i; //非常量转换成const int 的引用
const int *p = &i; //非常量的地址转换成const的地址
int &r = j,*q = p; //错误,不允许const转换成非常量
类类型定义的转换:类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型的转换。
例如:
- 使用标准库
string
类型的地方使用C风格字符串。 while(cin>>s)
,while
把条件部分cin
转换成布尔值。
显示转换
虽然有时候不得不使用强制类型转换,但这种方法本质上是十分危险的。
命名的强制类型转换
命名的强制类型转换形式为:
cast_name<type> (expression);
其中,type
是转换的目标类型而 expression
是要转换的值,如果 type
是引用类型,则结果是左值。
cast_name
是 static_cast、dynamic_cast、const_cast、reinterpret_cast
中的一种。
static_cast
任何具有明确定义的类型转换,只要不包含底层 const
,都可以使用 static_cast
。
例如:
double slope = static_cast<double>(j) / i; //浮点数除法
当需要把一个较大的算术类型转换成较小的类型,static_cast
非常有用,此时强制类型转换会提醒编译器忽略潜在的精度损失,否则编译器会报警告信息。
const_cast
const_cast
只能改变运算对象的底层 const
。
const char *pc;
char *p = const_cast<char*>(pc); //正确,但是通过p写值是未定义的行为
一旦去掉某个对象的const性质,编译器就不再阻止对该对象的写操作,如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用 const_cast
执行写操作就会产生未定义的结果。
reinterpret_cast
reinterpret_cast
通常为运算对象的位模式提供较低层次的重新解释。
int *ip;
char *pc = reinterpret_cast<char*>(ip);
使用 reinterpret_cast
是十分危险的,在这里必须始终牢记, pc
所指向的真实对象是 int
而不是 char
,如果把 pc
当成普通的字符指针使用就可能发生运行时错误,例如:
string str(pc);
reinterpret_cast
本质上依赖于机器,向安全的使用 reinterpret_cast
必须对涉及的类型和编译器实现转换的过程十分了解。
旧式的强制类型转换
早期版本的C++中,显示的强制类型转换有两种形式:
type (expr); //函数形式的强制类型转换
(type) expr; //C语言风格的强制类型转换