zoukankan      html  css  js  c++  java
  • C++ Primer学习笔记

    4.11 类型转换

    相互转换 conversion,如果两种类型可以相互转换,那么它们是关联的。
    例如,下面的表达式,ival会初始化为6

    int ival = 3.541 + 3;
    

    编译器会自动将double类型3.541转换为int类型3,然后让3+3得到6,再赋值给ival。这个过程的类型转换是自动执行的,无须程序员介入,称为隐式转换(implicit conversion)

    何时发生隐式类型转换?
    以下情形,编译器会自动转换运算对象的类型:

    • 大多数表达式中,比int类型小(指的是存储的位数少)的整型值,会提升为较大的整数类型。比如char 数据 + int 数据,会将char数据转换成int类型。
    • 在条件中,非布尔转换为布尔类型。如if (((int)a)){} ,这里a会自动转换为bool类型。
    • 初始化过程中,初值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧对象类型。如int a = 3.14,double 3.14会转换成int型;double b = 3.14; int a = b; ,b会转换成int类型。
    • 如果算术运算或关系运算的运算对象有多种类型,需要转换成同种类型。如double a = 1 + 2.0 + 3.1f; , 等式右侧会转换成double类型进行运算。
    • 函数调用也会发生类型转换(实参 -> 形参)。

    4.11.1 算术转换

    原则:不丢失数据信息

    整型提升
    负责把小整数转换成较大的整数类型。小整数优先转换成int,其次是unsigned int。
    bool, char, signed char, unsigned char, short, unsiged short等类型,只要所有值都能存在int内,就会转换成int。否则,提升为unsigned int。

    无符号类型的运算对象

    1. 如果一个运算对象是无符号类型,另外一个运算对象是带符号类型,其中的无符号类型不小于带符号类型(指的是存储位宽),那么带符号类型运算对象转换成无符号的。
      例如,假设两个类型分别为unsigned int和int,则int类型运算对象转换成unsinged int。

    2. 如果带符号类型大于无符号类型,此时转换结果依赖于机器。如果无符号类型所有值都能存在该带符号类型中,则无符号类型的运算对象转换成带符号类型。如果不能,那么带符号类型运算对象转换成无符号类型。
      例如,如果两个类型分别是long和unsigned int,且int和long大小相同(都是4byte),则long数转换成unsigned int。如果long类型占用空间比int多,则unsigned int转换成long。

    理解算术转换
    下面的例子,用typeid和sizeof来判断转换后的类型和占用字节。其中,typeid运算符获取类型信息,sizeof打印占用字节数。

    bool flag;  char cval;
    short sval; unsigned short usval;
    int ival;   unsigned int uival;
    long lval;  unsigned long ulval;
    float fval; double  dval;
    
    cout << "===basic type info==="<< endl;
    cout << "bool flag: " << typeid(flag).name() << ", " << sizeof(flag) << endl;
    cout << "char cval: " << typeid(cval).name() << ", " << sizeof(cval) << endl;
    cout << "short sval: " << typeid(sval).name() << ", " << sizeof(sval) << endl;
    cout << "unsigned short usval: " << typeid(usval).name() << ", " << sizeof(usval) << endl;
    cout << "int ival: " << typeid(ival).name() << ", " << sizeof(ival) << endl;
    cout << "unsinged int uival: " << typeid(uival).name() << ", " << sizeof(uival) << endl;
    cout << "long lval: " << typeid(lval).name() << ", " << sizeof(lval) << endl;
    cout << "unsigned long ulval: " << typeid(ulval).name() << ", " << sizeof(ulval) << endl;
    cout << "float uival: " << typeid(uival).name() << ", " << sizeof(uival) << endl;
    cout << "double ival: " << typeid(ival).name() << ", " << sizeof(ival) << endl;
    
    cout << "
    ===conversion type exmpale info==="<< endl;
    cout << "3.14159L + 'a': " << typeid(3.14159L + 'a').name() << ", " << sizeof(3.14159L + 'a') << endl; // 'a' 提升为int, 然后转换为long double
    cout << "dval + ival: " << typeid(dval + ival).name() << ", " << sizeof(dval + ival) << endl; // ival转换成double
    cout << "dval + fval: " << typeid(dval + fval).name() << ", " << sizeof(dval + fval) << endl; // fval转换成double
    cout << "ival = dval: " << typeid(ival = dval).name() << ", " << sizeof(ival = dval) << endl; // dval转换成int
    cout << "flag = dval: " << typeid(flag = dval).name() << ", " << sizeof(flag = dval) << endl; // dval转换成bool
    cout << "cval + fval: " << typeid(cval + fval).name() << ", " << sizeof(cval + fval) << endl; // cval提升为int, 然后转化成float
    cout << "sval + cval: " << typeid(sval + cval).name() << ", " << sizeof(sval + cval) << endl; // sval和cval都提升为int
    cout << "cval + lval: " << typeid(cval + lval).name() << ", " << sizeof(cval + lval) << endl; // cval转换为long
    cout << "ival + ulval: " << typeid(ival + ulval).name() << ", " << sizeof(ival + ulval) << endl; // ival转换为unsigned long
    cout << "usval + ival: " << typeid(usval + ival).name() << ", " << sizeof(usval + ival) << endl; // 根据unsigned short和int所占空间大小进行提升
    cout << "uival + ival: " << typeid(uival + ival).name() << ", " << sizeof(uival + ival) << endl; // 根据unsigned int和long所占空间大小进行提升
    

    运行结果

    ===basic type info===
    bool flag: b, 1
    char cval: c, 1
    short sval: s, 2
    unsigned short usval: t, 2
    int ival: i, 4
    unsinged int uival: j, 4
    long lval: l, 4
    unsigned long ulval: m, 4
    float uival: j, 4
    double ival: i, 4
    
    ===conversion type exmpale info===
    3.14159L + 'a': e, 12
    dval + ival: d, 8
    dval + fval: d, 8
    ival = dval: i, 4
    flag = dval: b, 1
    cval + fval: f, 4
    sval + cval: i, 4
    cval + lval: l, 4
    ival + ulval: m, 4
    usval + ival: i, 4
    uival + ival: j, 4
    

    4.11.2 其他隐式转换

    除算术转换为的几种隐式类型转换:
    1. 数组转换成指针
    大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针:

    int ia[10]; // ia是含有10个元素的数组
    int* ip = ia; // ia转换成指向数组首元素的指针
    

    当数组被用作decltype关键字参数时,或者作为取地址符(&),sizeof及typeid等运算符运算对象时,转换不会发生。而表达式使用函数类型时,会发生类似指针转换。

    2. 指针的转换
    常量整数值0或字面值nullptr能转换成任意指针类型;
    指向任意非常量的指针能转换成void *;
    指向任意对象的指针能转换成const void *;
    在有继承关系的类型间还有另外一种指针转换的方式;

    3. 转换成布尔类型
    指针/算术类型 = 0 => flase;
    指针/算术类型 = 1 => true;

    char *cp = get_string();
    if (cp) /*...*/
    while (*cp) /*...*/
    

    4. 转换成常量

    int i;
    const int &j = i; // 非常量转换成const int的引用
    const int *p = &i; // 非常量的地址转换成const int的地址
    int &r = j, *q = p; // 错误:不允许const转换成非常量
    

    5. 类类型定义的转换
    编译器自动执行,每次只能执行一种类型转换。如果同时提出多个转换请求,这些请求将被拒绝。

    string s, t = "a value"; // 字符串字面量转换成string
    while(cin >> s) {...} // istream类型cin转换成bool
    

    4.11.3 显式转换

    命名的强制类型转换
    1. static_cast
    任何具有明确定义的类型转换,只要不包含底层const, 都可以使用static_cast。 static_cast不能转换掉const性质。
    另外,在确保基类向派生类转换是安全的情况下,可以使用staci_cast强制覆盖编译器的检查工作。
    例,通过将一个int数转换成double进行浮点数运算

    double slope = static_cast<double>(j) / i; // i, j 都是int类型
    

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

    void *p = &d;
    double *dp = static_cast<double *>(p); // 将void *转换回初始化的指针类型。需要确保转换后的类型就是指针所指的类型,否则会产生未定义后果
    

    2. dynamic_cast
    一般情况下,不允许将基类指针或引用转换成派生类指针或引用。dynamic_cast让类型转换的安全检查在运行时执行,从而安全地将基类指针或引用,转换为派生类指针或引用。
    参考C++中深入理解dynamic_cast

    class Base {
    public:
      Base();
      ~Base();
      void print();
    };
    
    class Inherit : public Base {
    public:
      Inherit();
      ~Inherit();
      
      void show();
    }
    
    int main() {
      Base* pB = new Base();
      Inherit *pI = dynamic_cast<Inherit *>pB;
      pI->show();
      return 0;
    }
    

    3. const_cast
    去掉底层const。const_cast只能改变常量属性

    const char *pc;
    char *p = const_cast<char *>(pc); // 正确:但通过p写值是未定义行为。能否通过p写值,取决于对象本身是否为一个常量
    

    4. reinterpret_cast
    通常为运算对象的位模式提供较低层次上的重新解释。
    慎用,可能导致运行错误。

    int *ip;
    char *pc = reinterpret_cast<char *>(ip);
    
    string str(pc); // 错误,可能导致运行时异常
    

    需要牢记pc实际上是指向int的指针,如果将pc指向char的指针,可能导致异常。

    何为顶层const和底层const?
    顶层const:表示指针对象本身是一个常量,或者const修饰的任意对象本身是常量;
    底层const:表示指针所指对象是一个常量;
    简便记法:const距离谁近(指针,还是。如果是,要求const在*的前面),就表示对于对象不可变。

    int i = 0;
    int *const p1 = &i; // const距离p1近,表示指针p1是常量,也就是说p1是顶层const
    const int ci = 42;  // const修饰的ci是常量,这是顶层const
    const int *p2 = &ci; // p2能改变,但p2指向的对象不能改变,这是底层const
    
  • 相关阅读:
    OpenJDK源码研究笔记(十二):JDBC中的元数据,数据库元数据(DatabaseMetaData),参数元数据(ParameterMetaData),结果集元数据(ResultSetMetaDa
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 257 二叉树的所有路径
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 242 有效的字母异位词
    Java实现 LeetCode 241 为运算表达式设计优先级
    Java实现 LeetCode 241 为运算表达式设计优先级
    Java实现 LeetCode 241 为运算表达式设计优先级
  • 原文地址:https://www.cnblogs.com/fortunely/p/14453815.html
Copyright © 2011-2022 走看看