目录

重学C++:类型系统基础


常见的坑

  1. int, short, long, long long都是带符号的,在前面添加unsigned就能得到无符号类型。

  2. 字符型被分为3种:char, signed char, unsigned char,前两种并不等价。 虽然有三种类型,但是实际上只有两种表现形式:有符号的和无符号的。

  3. 有符号类型在与无符号类型运算时会隐式转换为无符号类型。

  4. 虽然变量初始化时候使用了=号,但是初始化和变量赋值并不相同。

  5. 变量默认初始化:

    变量类型 位置在函数内部 位置在函数外部
    内置类型 undefined 0
    自定义类型 由类决定 由类决定
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    
    #include <iostream>
    
    int default_initialize(int a) {
        // 输出必定是0
        std::cout << a << std::endl;
        int b;
        return b;
    }
    
    int main() {
        int a;
        // 输出是随机值
        std::cout << default_initialize(a) << std::endl; 
    }
    
  6. 如果在函数体内部试图初始化一个extern标记的变量会引发错误。

  7. 在嵌套作用域中,内层作用域中的定义可以覆盖外层作用域中声明的变量。

    可以显式使用域操作符::来指明使用哪层的变量。

必须要理解的点

  1. 字面量的意思就是从这个表示形式就能推断其对应类型的量,不同表示形式的字面量和不同类型是多对一的关系。

  2. 变量的组成部分:类型和值。说白了就是一个定性一个定量。

    类型决定变量在内存里面的存储方式,包括大小和布局方式,以及能参与的运算。

    值在实际代码运行过程中则被各种函数使用参与运算。

  3. 变量声明和定义:

    声明的意思就是:我要用这个变量。

    定义的意思就是:我要对这个操作的变量做出定义,规定其具体的细节。

    声明 定义
    规定变量的类型和名字
    申请空间
    初始化
    执行多次

    extern标记未初始化的变量来表明只对变量作声明:

    1
    2
    3
    
    extern int i;      //只声明不定义
    int i;             //声明并且定义
    extern int i = 10; //声明并且定义
    

    Q:为什么会有声明和定义这两个概念?

    A:因为C++支持分离式编译机制,这允许程序被分割成若干个文件,每个文件可以被独立编译。如果要在多个文件中使用同一个变量,就必须要将声明和定义分离。变量的定义必须且只能出现在一个文件中,其他用到这个变量的文件必须对其进行声明,且绝对不能进行重复定义。

  4. 名字的作用域:

    同一个名字在不同的作用域中可以指向不同的实体。

    名字的有效区域始于声明语句,以声明语句所在的作用域末端结束。

建议

  1. 明确数值不可能为负时使用unsigned类型。

  2. 使用int执行整数运算,范围不够时使用long long

  3. 使用double执行浮点数运算。

  4. 算术表达式中不要使用boolchar

  5. 避免写出依赖实现环境的代码,否则代码不可移植。

  6. 避免有符号类型和无符号类型之间的隐式类型转换。

  7. C++11中引入了列表初始化,例如:

    1
    2
    3
    4
    5
    6
    
    // 传统的初始化方式
    int units_sold = 0;
    int units_sold(0);
    // 现代的初始化方式
    int units_sold{0};
    int units_sold = {0};
    

    列表初始化在用于内置类型变量时,如果初始值存在丢失信息的风险,编译器会报错。

    1
    2
    3
    
    long double pi = 3.1415926536;
    int a{pi}, b = {pi};   // 错误:没有执行类型转换,因为可能丢失信息
    int a(pi), b = pi;     // 正确:执行了隐式类型转化,丢失了信息
    
  8. 对每个内置类型的变量都执行显式默认初始化以防止undefined行为。

  9. 在变量第一次使用的地方进行定义操作。