• 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
    
  • 相关阅读:
    python学习笔记(4)装饰器
    python学习笔记(3)函数
    python学习笔记(2)集合
    python学习笔记(1)字典
    nginx.conf文件内容详解
    关于斐波拉契数列引出的迭代器生成器的一点讨论
    MAC电脑运行python并发编程遇到的问题
    docker 11 :私有仓库搭建
    docker 10 :docker单机网络模式
    【转】C#环形队列
  • 原文地址:https://www.cnblogs.com/fortunely/p/14453815.html
Copyright © 2020-2023  润新知