• 表达式——类型转换


    一、隐式转换

    1、对象的类型定义了对象能包含的数据和能参与的运算,其中一种运算被大多数类型支持,就是将对象从一种给定的类型转换为另一种相关类型。

    2、当在程序的某处我们使用了一种类型而其实对象应该取另一种类型时,程序会自动进行类型转换,因此它们被称作隐式转换

    3、发生隐式转换的情况:

    • 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。
    • 在条件中,非布尔类型转换成布尔类型。
    • 初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。
    • 如果算数运算或关系运算的运算对象有多种类型,需要转换成同一种类型。
    • 函数调用时也会发生类型转换。

    4、当给某种类型的对象强行赋了另一种类型的值时,类型所能表示的值的范围决定了转换的过程

    • 当我们把一个非布尔类型的算术值赋给布尔类型时,初始值为0则结果为false,否则结果为true。
    • 当我们把一个布尔值赋给非布尔类型时,初始值为false则结果为0,初始值为true则结果为1。
    • 当我们把一个浮点数赋给整数类型时,进行了近似处理。结果值将仅保留浮点数中小数之前的部分。
    • 当我们把一个整数值赋给浮点类型时,小数部分记为0。如果该整数所占的空间超过了浮点类型的容量,精度可能有损失。
    • 当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。
    • 当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的。程序可能继续工作、可能崩溃,也可能生成垃圾数据。

      当在程序的某处使用了一种算术类型的值而其实所需的是另一种类型的值时,编译器同样会执行上述的类型转换。

    5、含有无符号类型的表达式

      负数转换成无符号数:把负数转换成无符号数类似于直接给无符号数赋一个负值,结果等于这个负数加上无符号数的模。

     1 #include <iostream>
     2 #include <memory>
     3 #include <string>
     4 #include <vector>
     5 
     6 int main()
     7 {
     8     unsigned int x = -1;
     9     std::cout << x << std::endl;
    10     return 0;
    11 }
    View Code

      切勿混用带符号类型和无符号类型:如果表达式既有带符号类型右有无符号类型,当带符号类型取值为负时会出现异常结果,这是因为带符号数会自动地转换成无符号数。

    二、算术转换规则

      算术转换的含义是把一种算数类型转换成另外一种算术类型。算术转换的规则定义了一套类型转换的层次,其中运算对象将转换成表达式中最宽的类型。例如,当表达式中既有浮点数也有整数类型时,整数值将转换成相应的浮点类型。

    1、整型提升

      整型提升负责把小整数类型转换成较大的整数类型。对于bool、signed char、unsigned char、short和unsigned char等类型来说,只要它们所有可能的值都能存在int里,它们就会提升成int类型;否则,提升成unsigned int类型。

      较大的char类型(wchar_t、char16_t、char32_t)提升成int、unsigned int、long、unsigned long、long long 和unsigned long long中最小的一种类型,前提是转换后的类型要能容纳原类型所有可能的值。

    2、无符号类型的运算对象

      如果某个运算符的运算对象类型不一致,这些运算对象将转换成同一种类型,但是如果某个运算对象的类型是无符号类型,那么转换的结果就要依赖于机器中各个整数类型的相对大小了。

      首先进行整型提升。如果结果的类型匹配,无须进行进一步的转换。如果两个提升后的运算对象的类型要么是带符号的、要么是不带符号的,则小类型的运算对象转换成较大的类型。

      如果一个运算对象是无符号类型、另外一个运算对象是带符号类型。情况1:如果其中的无符号类型不小于带符号类型,那么带符号的类型转换成无符号的。  情况2:如果带符号类型大于无符号类型,此时的转换结果依赖于机器。如果无符号类型的所有值都能包含在该带符号类型中,则无符号类型转换成带符号类型;如果不能,那么带符号类型的运算对象转换成无符号类型。

    三、其他隐式类型转换

    1、数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针。当数组被用作decltype关键字的参数,或者取地址符(&)、sizeof及typeid等运算符的运算对象时,用一个引用来初始化数组,该转换不会发生。

    2、指针的 转换:常量数值0或者字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*。

    3、转换成布尔类型:如果指针或算术类型的值为0,转换结果是false,否则为true。

    4、类类型定义的转换:类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一种类类型的转换。

    四、显示转换

      一个命名的强制类型转换具有如下形式:

      cast_name<type>(expression);

    其中,cast_name是static_cast、dynamic_cast(支持运行时类型识别)、const_cast、reinterpret_cast中的一种,指定了执行的是哪种转换。type是转换的目标类型,如果type是引用类型,则结果是左值。expression是要转换的值。

    1、static_cast

      任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。

      当需要把一个较大的算术类型赋值给较小的类型时,static_cast非常有用。此时强制类型转换告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失。一般来说,如果编译器发现一个较大的算术类型试图赋值给较小的类型,就会发出警告信息;但是当我们执行了显示的类型转换后,警告信息就会被关闭了。

      static_cast对于编译器无法自动执行的类型转换也非常有用。例如我们可以使用static_cast找回存在于void*指针中的值:

    1 #include <iostream>
    2 int main()
    3 {
    4     double x = 6.6;
    5     void *p = &x;
    6     double *p2 = static_cast<double*> (p);
    7     std::cout << (*p2) << std::endl;
    8     return 0;
    9 }

    当我们把指针存放在void*中,并且使用static_cast将其强制转换回原来的类型时,应该确保指针的值保持不变。也就是说,强制转换的结果将与原始的地址值相等,因此我们必须确保转换后所得的类型就是指针所指的类型。类型一旦不符,将产生未定义的后果。

    2、const_cast

      const_cast只能改变运算对象的底层const。

    1 #include <iostream>
    2 int main()
    3 {
    4     const char *pc;
    5     char *p = const_cast<char*>(pc);// 正确:但是通过p写值是未定义的行为
    6     return 0;
    7 }

    对于将常量对象转换成非常量的行为,我们一般称其为“去掉const性质”。一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的行为。

    3、reinterpret_cast

      reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。

    1 #include <iostream>
    2 int main()
    3 {
    4     int *ip = 0;
    5     char  *pc = reinterpret_cast<char*>(ip);
    6     return 0;
    7 }

     必须牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。

    五、旧式的强制类型转换

      在早期的C++语言中,显示的进行强制类型转换包含两种形式:

      type(expr); // 函数形式的类型转换

      (type)expr; // C语言风格的强制类型转换

      根据所涉及的类型不同,旧式的强制类型转换分别具有与static_cast、const_cast、reinterpret_cast相似的行为。当我们在某处执行旧式的强制类型转换时,如果换成const_cast、static_cast也合法,则其行为与对应的命名转换一致。如果替换后不合法,则旧式强制类型转换执行与reinterpret_cast类似的功能。

    1 #include <iostream>
    2 int main()
    3 {
    4     int *ip = 0;
    5     char  *pc = (char*)(ip); // 效果与使用reinterpret_cast一样
    6     return 0;
    7 }
  • 相关阅读:
    在线程中更新UI
    Panel容器
    ImageList组件
    PrograssBar控件
    PictureBox控件
    GroupBox控件
    Timer控件Forms.Timer\System.Timers.Timer\System.Threading.Timer
    DataGridView控件1——手动添加数据,遍历数据
    SplitContainer控件
    Git理论知识
  • 原文地址:https://www.cnblogs.com/ACGame/p/10134627.html
Copyright © 2020-2023  润新知