const限定符

const

  • const限定符用于定义常量:
    • 常量:一旦创建后其值就不可改变
    • const对象必须初始化
    1
    const int var = 42;    //定义一个名为var的int常量

作用域

  • 默认情况下,const对象被设定为仅在文件内生效
  • 若需要const对象能被其他文件访问,须在声明或定义时添加extern关键字
    1
    2
    3
    4
    //A.cc
    extern const int var = 42; //在A.cc中定义var
    //B.cc
    extern const int var; //在B.cc中声明var

const与引用

  • 对const的引用(C++程序员常通俗称作常量引用):
    • 常量引用引用的对象可以是非常量对象,其不可用作修改所引用的对象,引用非常量对象时可通过其他途径修改所指对象的值
    • 引用常量对象只能使用常量引用
      1
      2
      const int var = 42;
      const int &rf = var;
    • 常量引用初始化时可以绑定任意表达式,只要其结果可以转换为引用类型,这会为此常量引用绑定临时量
      1
      2
      3
      4
      5
      6
      7
      // Your code
      double var = 1.61;
      const int &rf = var;
      // 概念上,编译器会这样解释
      double var = 1.61;
      const int temp = var; //将var转换为临时int常量
      const int &rf = temp; //让rf绑定临时量

常量引用实际并不存在,引用并非一个对象,无法使其本身保持不变,这仅是C++程序员约定俗成的称呼


const与指针

  • 指向常量的指针
    • 指向常量的指针可以指向非常量对象,其不可用于修改所指向的对象,指向非常量对象时可通过其他途径修改所指对象的值
    • 指向常量对象只能使用指向常量的指针
      1
      2
      const int var = 42;
      const int *ptr = &var;
  • 常量指针
    • 指针作为一个对象,可以将其本身定义为一个常量,这会使其存放的地址无法改变,而非指向的值,常量指针必须初始化
      1
      2
      int var = 42;
      int *const ptr = &var;

理解声明符:从右向左读,以int *const ptr = &var;为例:
将var的地址赋予名为ptr的对象(ptr = &var) –> ptr是常量对象(const ptr) –> 常量对象ptr是一个指针(*const ptr) –> 常量指针ptr指向int对象(int *const ptr) –> ptr是一个指向int对象var的常量指针


const层级

  • 顶层const表示对象是常量,底层const与复合类型的基本类型部分有关,表示绑定的对象是常量:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int var = 0;
    int *const ptr = &var; //顶层const,ptr的值不可修改
    const int cvar = 42; //顶层const,var的值不可修改

    const int *ptr1 = &var; //底层const,ptr的值可以修改
    const int &r = cvar; //底层const,r引用的对象可变更

    const int *const ptr2 = ptr1;
    /* 靠右的const是顶层const,它使ptr2的值不可改变,另一个是底层const
    */
  • const层级与拷贝赋值:
    • 拷贝操作不会修改对象的值,因此顶层const无明显影响:
    1
    2
    var = cvar;    //Valid
    ptr1 = ptr2; //Valid
    • 但底层const限制必须拷入和拷出的对象具有相同层级的const,或者可以转换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	int *p = ptr2;    //Invalid,ptr3有底层const,p没有
    ptr1 = ptr2; //Valid,都有底层const
    ptr1 = &var; //Valid,var的地址int*可以转换为const int*
    int &r = cvar; //Invalid,cvar有底层const,r没有
    const int &r2 = var //Valid,const int&可以绑定int,满足转换条件
    * * *
    ## 常量表达式
    - **常量表达式**是指值不会改变且在**编译时**就可以得到值的表达式,由它的数据类型和初始值决定
    ```C++
    const int A = 42; //A为常量表达式
    const int B = A + 1; //B为常量表达式
    int C = 9; //C不是常量,不是常量表达式
    const int size = get_size(); // 除非get_size()是constexpr函数,否则这不是常量表达式
  • constexpr类型
    • 将变量声明为constexpr类型以告诉编译器验证变量是否为常量表达式,声明的变量一定是常量,且必须使用常量表达式初始化;普通函数不可以作为constexpr变量的初始值,必须为constexpr函数(在编译时就能获得其值)
    • constexpr隐含顶层const
    • constexpr定义时用到的类型必须为字面值类型——简单、值显而易见的类型(算数类型、指针、引用等)
    1
    2
    3
    constexpr int A = 20;    //Valid
    constexpr int B = A + 1; //Valid
    constexpr int size = getSize(); //只有getSize是constexpr函数式此语句才正确

    当你笃定一个变量是常量表达式时,就应该把它声明为constexpr类型

    • constexpr与指针:
      • 在constexpr声明中定义一个指针,constexpr只对指针生效,与其所指向的对象无关;它也可以指向非常量
      • constexpr指针初始化时必须绑定具有固定地址的对象或为空指针

易错

  • 指向常量的指针对const的引用“自以为”它们绑定了一个常量(即使是非常量),因此无法通过它们改变所绑定对象的值,但可以通过其他方式改变
  • 指向常量的指针VS常量指针
    指向常量的指针 常量指针 特性
    不可以 可以 是否可以通过它修改所指向的非常量的值
    可以 不可以 是否可以修改其所指向的地址
    可以 不可以 是否可以指向常量
    底层const 顶层const const层级
    • 两者结合既不可以通过它修改所指向的非常量的值,又不可以修改其所指向的地址,但可以指向常量(右到左读法理解=>指向常量的常量指针)
    • constexpr声明中定义的指针为常量指针