类型转换 (type conversion)#

类型转换是指从一个类型的值转换得到另一个类型的值, 这个过程是生成了新的对象或引用, 原来的对象或引用保持不变.

值保留类型转换和窄化类型转换#

我们将 T 类型的值转换为 U 类型的值, 再从 U 类型的值转换回 T 类型的值. 经历这个过程后,

  • 如果新得到的 T 类型值和原来的值相等, 则从 TU 的转换称为值保留 (value-preserving) 类型转换.

  • 如果新得到的 T 类型值和原来的值不等, 则从 TU 的转换称为窄化类型转换.

隐式类型转换#

当类型 T 用于不接受 T 但接受 U 的语境时, 就会 自动发生.

double 隐式类型转换为 int#
1double value = 1.1;
2int value2   = value;  // double 隐式类型转换为 int, 截断为 1
C 风格数组隐式类型转换为指向首元素的指针#
1int array[5] = {};
2sizeof(array)     == sizeof(int) * 5;
3sizeof(array + 0) == sizeof(int*);

如果隐式类型转换是值保留的, 则称为提升 (promotion).

常考的隐式类型转换#

以下内容 (很不幸地) 不是 C++ 所允许的全部隐式类型转换.

整型提升 (integral promotion): 值保留类型转换
  • 同等等级下, 有符号数转为无符号数.

  • int 表示了计算机在进行算术运算时最 "自然" 的大小, 因此 C 语言要求窄于 int 的整型 (char 也是整型!) 在算术运算 (不含比较运算) 时会转换成 intunsigned int, 所以 sizeof(char变量 + 1) 其实是 sizeof(int).

  • bool -> 其他整型

    • true -> 1.

    • false -> 0.

整型转换 (integral conversion): 窄化类型转换
  • 如果目标类型是无符号数 -> 当前的值 % 2^n 所表示的无符号数, 也就是直接截断前面的二进制位.

  • 如果目标类型是有符号数,

    • 目标类型能表示当前的值 -> 当前的值.

    • 不能 -> 当前的值 % 2^n 所表示的, 也就是直接截断前面的二进制位.

  • 如果目标类型是 bool,

    • 非 0 -> true.

    • 0 -> false.

  • char 类型可能采用 signed charunsigned char 的表示方式, 也就是说可能是有符号数或无符号数; 此外, char 是独立的类型, 不是 signed char 类型, 也不是 unsigned char!

-> 浮点数
  • 只要有可能, 转换为足够精确的数值, 例如整数 1 转换为浮点数 1.

警告

数组 -> 指向首元素的指针 -> 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 本身也有类型转换, 但没有对功能进行区分, 因而不自带语义且更加危险.

相关解答#