类型转换 (type conversion)#
类型转换是指从一个类型的值转换得到另一个类型的值, 这个过程是生成了新的对象或引用, 原来的对象或引用保持不变.
值保留类型转换和窄化类型转换#
我们将 T
类型的值转换为 U
类型的值, 再从 U
类型的值转换回 T
类型的值. 经历这个过程后,
如果新得到的
T
类型值和原来的值相等, 则从T
到U
的转换称为值保留 (value-preserving) 类型转换.如果新得到的
T
类型值和原来的值不等, 则从T
到U
的转换称为窄化类型转换.
隐式类型转换#
当类型 T
用于不接受 T
但接受 U
的语境时, 就会 自动发生.
double
隐式类型转换为 int
#1double value = 1.1;
2int value2 = value; // double 隐式类型转换为 int, 截断为 1
1int array[5] = {};
2sizeof(array) == sizeof(int) * 5;
3sizeof(array + 0) == sizeof(int*);
如果隐式类型转换是值保留的, 则称为提升 (promotion).
常考的隐式类型转换#
以下内容 (很不幸地) 不是 C++ 所允许的全部隐式类型转换.
- 整型提升 (integral promotion): 值保留类型转换
同等等级下, 有符号数转为无符号数.
int
表示了计算机在进行算术运算时最 "自然" 的大小, 因此 C 语言要求窄于int
的整型 (char
也是整型!) 在算术运算 (不含比较运算) 时会转换成int
或unsigned int
, 所以sizeof(char变量 + 1)
其实是sizeof(int)
.bool
-> 其他整型true
-> 1.false
-> 0.
- 整型转换 (integral conversion): 窄化类型转换
如果目标类型是无符号数 -> 当前的值 % 2^n 所表示的无符号数, 也就是直接截断前面的二进制位.
如果目标类型是有符号数,
目标类型能表示当前的值 -> 当前的值.
不能 -> 当前的值 % 2^n 所表示的, 也就是直接截断前面的二进制位.
如果目标类型是
bool
,非 0 ->
true
.0 ->
false
.
char
类型可能采用signed char
或unsigned char
的表示方式, 也就是说可能是有符号数或无符号数; 此外,char
是独立的类型, 不是signed char
类型, 也不是unsigned char
!
- -> 浮点数
只要有可能, 转换为足够精确的数值, 例如整数
1
转换为浮点数1
.
- -> 整型
截断小数部分.
- ->
bool
有指向的对象 (无论该对象是否实际存在) ->
true
.空指针 ->
false
.
- -> 指向数组首元素的指针
比如拷贝时 (按值传参也可以当做拷贝, 所以
int array[]
或int array[5]
作为参数其实是int* array
).比如运算时 (
array + 0
,+array
等等).
- -> 指向该函数的指针
1void print(); 2 3void (*pointer)() = &print; // 显式取地址, 得到指向该函数的指针 4void (*pointer)() = print; // 隐式类型转换为指向该函数的指针
警告
- 数组 -> 指向首元素的指针 ->
bool
必然有指向的对象故必然
true
.
- 字符串字面值是字符数组 -> 指向首元素的指针 ->
bool
必然有指向的对象故必然
true
.
最佳实践#
使用
{}
进行初始化, 它更通用, 且不允许double
转换为int
这样的缩窄转换.1double value{1.1}; 2int value2{value}; // 错误: 发生缩窄转换
移除不需要的隐式类型转换, 将必需的转换用显式类型转换替代.
1double value{1.1}; 2int value2{static_cast<int>(value)};
更好地, 参考 C++ 环境配置 尽量开启静态检查来查找出隐式类型转换.
显式类型转换#
除了标准规定的隐式类型转换, 我们也可以显式地进行类型转换, 这让未来的读者知道, 这个转换是你明确需要的, 而不是一时疏忽.
以下是 C++ 引入的类型转换符, 分别进行不同功能的类型转换: (仅介绍极小的部分)
static_cast<To>(value)
表示这是我明确要求的类型转换, 也许会有精度的损失, 但这就是我想要的.
reinterpret_cast<To&>(value)
表示我要 "打破" C++ 的类型系统, 强制进行类型转换, 并为它的后果负责. 如果不了解其中细节, 很容易出现未定义行为.
写出这一个只是因为会考的 二进制输入输出 需要用到.
dynamic_cast<To>(base)
通常用于将类层次的基类有检查地转换为派生类.
dynamic_cast<Derived*>(&base)
: 如果转换失败, 返回空指针.dynamic_cast<Derived&>(base)
: 如果转换失败, 抛出异常.
危险
C 本身也有类型转换, 但没有对功能进行区分, 因而不自带语义且更加危险.
别看: C 类型转换与 C++ 类型转换的关系
