Post

C++: const 限定符

《C++ Primer》读书笔记。

基本作用

const 对象创建后,值不能再改变。

  • 初始化:
    1
    2
    3
    
    int i = 42;
    const int ci = i; // 正确:i 的值被拷贝给了 ci
    int j = ci; // 正确: ci 的值被拷贝给了 j
    
  • 默认情况下,const 对象仅在文件内有效。
  • 需要文件间共享时,使用 extern 关键字。
    1
    2
    3
    4
    
    // file_1.cc 定义并初始化另外一个常量,该常量能被其他文件访问
    extern const int bufSize = fcn();
    // file_1.h 头文件
    extern const int bufSize; // 与 file_1.cc 中定义的 bufSize 是同一个
    

对常量的引用 (reference to const)

与普通引用不同,对常量的引用不能被用于修改其所绑定的对象。

1
2
3
4
const int ci = 1024
const int &r1 = ci; // 正确:引用及其对应的对象都是常量
r1 = 42; // 错误:r1 是对常量的引用
int &r2 = ci; // 错误:试图让一个非常量引用指向一个常量对象

临时量对象:

1
2
3
4
5
double dval = 3.14;
const int &ri = dval;
/* ======= 等价于 ======= */
const int temp = dval;
const int &ri = temp;

常量引用的对象本身可以不是一个常量。

1
2
3
4
5
int i = 42;
int &r1 = i;
const int &r2 = i; 
r1 = 0; // 正确:可以通过 r1 修改 i 的值
r2 = 0; // 错误:不允许通过 r2 修改 i 的值

指针和 const

  • 指向常量的指针 (pointer to const):不能改变其所指的对象。想存放常量对象的地址,只能使用指向常量的指针。
    1
    2
    3
    4
    
      const double pi = 3.14;
      double *ptr = π // 错误:ptr 是一个普通指针
      const double *cptr = π // 正确: cptr 可以指向一个双精度常量
      *cptr = 42; // 错误:不能给 *cptr 赋值
    

    指向常量的指针所指的对象可以是一个非常量。

    1
    2
    
      double dval = 3.14;
      cptr = &dval; // 正确:注意不能通过 cptr 改变 dval 的值
    
  • 常量指针 (const pointer):指针本身为常量。初始化完成后,它的值不能再改变。书写方式为把 * 放在 const 关键字前。
    1
    2
    3
    4
    
      int errNumb = 0;
      int *const curErr = &errNumb; // curErr 将一直指向 errNumb
      const double pi = 3.14159;
      const double *const pip = π // pip 是一个指向常量对象的常量指针
    

顶层 const

  • 顶层 const (top-level const):指针本身是常量。更一般地,表示任意对象是常量,适用于任何数据类型。
  • 底层 const (low-level const):指针所指的对象是一个常量。更一般地,通常和指针、引用等复合类型的基本类型部分有关。

一些例子:

1
2
3
4
5
6
int i = 0;
int *const p1 = &i; // 顶层 const
const int ci = 42;  // 顶层 const
const int *p2 = &ci // 底层 const
const int *const p3 = p2 // 靠右的 const 是顶层 const,靠左的是底层 const
const int &r = ci; // 用于声明引用的 const 都是底层 const
  • 执行对象的拷贝操作时,顶层 const 不受影响。但底层 const 受限制:拷入和拷出的对象必须具有相同的底层 const 资格,或者两个对象的数据类型必须能够转换。一般来说,非常量可以转换成常量,反之则不行。
    1
    2
    3
    4
    5
    6
    7
    8
    
      i = ci // 正确:ci 为顶层 const,对此操作无影响
      p2 = p3 // 正确:p2 和 p3 所指类型相同,p3 顶层 const 部分不受影响
    
      int *p = p3; // 错误:p3 包含底层 const 的定义,而 p 不包含
      p2 = p3; // 正确:p2 和 p3 都是底层 const
      p2 = &i; // 正确:int* 能转换成 const int*
      int &r = ci; // 错误:普通的 int& 不能绑定到 int 常量上
      const int &r2 = i; // 正确:const int& 可以绑定到一个普通 int 上
    

const 形参和实参

  • 用实参初始化形参时会忽略顶层 const,也就是形参的顶层 const 被忽略了。
    1
    2
    3
    
      // 以下两个函数定义重复
      void fcn(const int i) {}
      void fcn(int i) {}
    
  • 指针或引用形参与 const
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
      void reset(int *ip) {
          *ip = 0;
      }
    
      void reset(int &i) {
          i = 0;
      }
    
      int i = 0;
      const int ci = i;
      string::size_type ctr = 0;
    
      reset(&i); // 正确:调用形参类型是 int* 的 reset 函数
      reset(&ci); // 错误:不能用指向 const int 对象的指针初始化 int*
    
      reset(i); // 正确:调用形参是 int& 的 reset 函数
      reset(ci); // 错误:不能把普通引用绑定到 const 对象 ci 上
      reset(42); // 错误:不能把普通引用绑定到字面值上
      reset(ctr); // 错误:类型不匹配,ctr 是无符号类型
    
This post is licensed under CC BY 4.0 by the author.