C++:const关键字


💡 本篇的内容来自C++ Primer中的2.4节

基本内容

定义与初始化

We can make a variable unchangeable by defining the variable’s type as const.

  • 因为const变量,不能改变,因此必须初始化。
const int i = get_size();  // 正确,runtime初始化 
const int j = 42;          // 正确,compile time初始化
const int k;               // 错误,没有初始化

const变量可以接受那些无法改变变量本身数据的操作,例如赋值和copy等。

const int ci = i; 
int j = ci;       // 正确,j初始化后,与ci无关。

特性:const Objects Are Local to a File

  • const variables are defined as local to the file.
  • When we define a const with the same name in multiple files, it is as if we had written definitions for separate variables in each file.

对于const变量,规定其只在本文件中起作用。

为什么这样规定?这要提到compiler。对于const int bufSize = 512这种使用编译时常量初始化const变量时, 在编译时,编译器会将所有提到的变量进行替换,为了完成替换,编译器必须知道初始化值,当程序分成多个文件时,为了支持编译器的这种操作,需要在所有使用该变量的多个文件中定义同名的const变量,这是我们不愿见到的,因此定义const变量只在本文件生效。

当在一个文件中定义了const变量,但是想在另一个文件中使用,需要使用extern关键字。

// file_1.cc defines and initializes a const that is accessible to other files 
extern const int bufSize = fcn();

// file_1.h 
extern const int bufSize; // same bufSize as defined in file_1.cc

这样可以实现const变量的共享。

References to const

我想,有时候学技术,还是要看英文原版的说明,用词更准确和直接。

澄清概念

这里首先明确 references to const 指的是:

  • bind a reference to an object of a const type.
  • the object is a reference that refers to a const type;

但是,这种对于const的引用,不能通过引用改变其值。

const int ci = 1024; // ok: both reference and underlying object are const 
const int &r1 = ci; 
r1 = 42;        // error: r1 is a reference to const
int &r2 = ci;   // error: nonconst reference to a const object

我们在之前讲引用时,提到引用必须初始化,且在整个生命周期中,不能改变其绑定的变量。因此,这里需要澄清一下references to const 中易于混淆的概念:

  • 有人认为:references to const即为 const references,这会造成误解,其实本质不同;
  • 由于引用本身不能更换引用的对象,因此 in some sense all references are const,或者说,技术上,不存在const references;
  • 我们一直提的const,不能改变,其实指的是reference to const中,不能通过该引用改变其绑定的值;

初始化

在引用中,引用本身的类型必须与绑定的变量的类型匹配,但是加入了const后,就出现了例外。

const对reference初始化的影响

reference必须初始化,且终生不能改变绑定对象;而且其引用的对象类型必须与自己相匹配;且初始化时使用的表达式只能是objects, 不能是 literal 或者运算表达式。

但是const的使用改变了这些规则。

we can initialize a reference to const from any expression that can be converted to the type of the reference.

const int i = 42; 
const int &r1 = i;
const int &r2 = 42;     // ok: r1 is a reference to const 
const int &r3 = r1 * 2; // ok: r3 is a reference to const
int &r4 = r * 2;        //error: r4 is a plain, nonconst reference

对于类型不匹配的也是可以的,其中存在隐式类型转换。

double dval = 3.14;
const int &i = dval;
cout << i << endl;   // 输出3

这里可以这样理解,编译器会生成一个临时变量 const int temp = dval, 然后通过i 引用该temp,所以i 并没有直接引用dval。

references to const可以绑定非常量对象

我们提到references to const不能改变绑定对象的值,但是并不意味着绑定对象只能是const变量,只要保证无法通过references to const改变其值即可,因此绑定对象可以为非常量。

int i = 42; 
int &r1 = i;       // ok 
const int &r2 = i; // ok 
r1 = 0;            // ok, i可以改变
r2 = 0;            // error, i不能通过r2改变

所谓,功成不必在我。

Pointers and const

pointer to const

使用指针指向一个const变量,是可行的,但是前提是该pointer是一个pointer to const。

const double pi = 3.14;   
double *ptr = &pi;        // error: ptr is a plain pointer
const double *cptr = &pi; // ok, pointer to const
*cptr = 42;               // error: cannot assign to *cptr

注意事项:

  • pointer to const: 指向的对象不一定是const,这个有一点离奇,但是就是这么回事,其核心要强调的点是:通过该pointer to const不能改变指向对象的值,但是该对象的值可以通过别的方式改变;
    double dval = 3.14; 
    cptr = &dval;
    *cptr = 1.23; // error
    ptr = &dval;
    *ptr = 1.23; // ok
    

const pointer

  • 与reference不同,pointer是一个object, 因此其本身也可以是const的。
  • const pointer指的是该pointer本身不能改变,即不能指向其他的对象;
  • 同一般的reference一样,也必须进行初始化;
  • 最容易的理解方式:还是从右往左读;
    int errNumb = 0; 
    int *const curErr = &errNumb; // const pointer
    const int *ptr = &errNUmb; // pointer to const 
    const double pi = 3.14159; 
    const double *const pip = &pi // const pointer and pointer to const
    
  • 对于const pointer,与pointer to const一样,对于所指向的对象是否为const,没有限制;

Top-level const

这里只是一个名词的解释:

  • top-level const: indicate that the pointer itself is a const;
  • low-level const: indicate that a pointer can point to a const object;

这两个名词,从英文来看很好理解,有什么用呢?语法规定:

  • Top-level const can appear in any object type;
  • Low-level const appears in the base type of compound types such as pointers or references; 因为只有指针和引用可以指向别的对象。

什么意思?

  • 在const对象和非const之间可以随便赋值,拷贝对象,不限制一定是const;
  • 但是,在pointer to const对象中,如果进行赋值和拷贝,两者必须都是pointer to const. 这样保证,即便发生了复制,也不能通过新的pointer改变指向的对象的值。
int i = 10;
const int *p1 = &i;
int *p2 = p1;       // error
const int *p3 = p1; // ok

Constant Expressions

A constant expression is an expression whose value cannot change and that can be evaluated at compile time.

const

const修饰的一定是constant expression吗?不一定,因为这些值不一定是compile time时确定的, 即:const并不会区分runtime 和compile time变量。

const int sz = get_size(); // sz is not a constant expression

constexpr

编译时检查

由于const与constant expression, 即不是充分条件,也不是必要条件,但是会让人混淆。在C++11中,constexpr被提出,让compiler确认,通过其修饰的一定是compile time常量,即constant expression, 因此其一定必须初始化。

constexpr int mf = 20; // 20 is a constant expression constexpr 
int limit = mf + 1; // mf + 1 is a constant expression constexpr 
int sz = size(); // ok only if size is a constexpr function

因此,最好使用constexpr代替const,使得能够在compile time时进行检查。

数组的维度,也是在compile time检查的,可以结合进行验证。

literal types

由于constexpr变量在编译时验证,因此其应用的类型也受到限制,只能是literal types:

  • arithmetic, reference, and pointer types are literal types.
  • the library IO and string types are not literal types.
constexpr string i = "hello, world"; // error, c++20可以

pointers

when we define a pointer in a constexpr declaration, the constexpr specifier applies to the pointer, not the type to which the pointer points.

constexpr产生的是 top-level const on the objects it defines.

const int *p = nullptr;     // p is a pointer to a const int
constexpr int *q = nullptr; // q is a const pointer to int
constexpr const int *p = &i; // p is a constant pointer to the const int i

这语法,C++ NB。


文章作者: alex Li
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 alex Li !
  目录