【C++】const 的用法

1. const对象的链接性

1.1 默认状况

  • 默认情况下,const对象被设定为内部链接即仅在文件内有效
  • 当多个文件出现同名的const变量时,等同于在不同文件中分别定义了独立的变量

1.2 实现外部链接的方法

对于const变量,**不管是声明还是定义* 都添加extern关键字。即可实现外部链接性。

1
2
3
4
//file1.cpp定义并初始化了一个常量,该常量能被其他文件访问
extern const int SIZE = 0;
//file2.h文件下声明
extern const int SIZE;//与之前定义的变量为同一个

2 对const的引用

const的引用即常量引用。
对且仅对引用可参与的操作进行了限定。对于引用的对象本身是否是一个常量未作限定

2.1 const引用的初始化

一般而言,引用的对象必须与引用类型严格匹配。

1
2
3
4
5
int i = 0;
double d = 3.14;
int &r = i;//正确
int &ra = d;//错误
int &rb = 3.14;//错误

但是,const引用是个例外,它允许使用任意表达式作为初始值只要该表达式的结果能转换成引用的类型即可

1
2
3
4
5
6
int i = 0;
double d = 3.14;
const int &ra = i;
const int &rb = 42;
const int &rc = i * 2;
const int &rd = d;//注意

2.2 内部机制

对于

1
2
double d = 3.14;
const int &rd = d;

之所以可以行得通,是因为编译器把上述代码变为了一下的形式:

1
2
const int temp = d;
const int &rd = temp;

可以看出const引用可以绑定临时变量,这是它的一个重要的不同点。而普通的引用是不可以这么做的,因此:

1
2
const int &ra = 3 * (2 + 4);//生成临时变量,可以
const int &rb = fun();//fun()为返回一个int临时变量的函数,可以

3. const与指针

3.1 指向常量的指针

基本概念

不能通过该指针改变指向的量的值(不管它是不是常量)。

1
2
double PI = 3.14;
const double *p = Π//不一定非要指向常量

基本原则

一般而言,指针的类型一定要与指向的对象严格匹配,除了两种例外。第一个就是允许令一个指向常量的指针指向一个非常量对象。
指向常量的指针并不要求其指向的对象一定为常量,但普通指针一定不能指向常量。
可以这么想:所谓“指向常量的指针(或引用)”只不过是自以为是罢了,它们觉得自己指向了一个常量,因而自觉地不去改变对象的之
——《C++ Primer》

3.2 const指针

基本概念

指针本身是常量,不能变更自己指向的对象。且必须初始化

1
2
int n = 0;
int * const p = &i;//p不可改变,必须一直指向n

3.3 区分

技巧:

  • *符号为界,看const在左还是在右。
  • 只需从右向左阅读r的定义,离变量名最近的符号对变量的类型有最直接的影响, 并以此来分析。
1
2
3
4
5
6
const int *p = &n;//指向常量的指针
int const *p = &n;//指向常量的指针

int *const p = &n;//常指针

const int * const p = &n;//指向常量的常指针

4. 顶层const与底层const

4.1 概念

大多数情况下,这对概念是针对指针而言的

  • 顶层const 若指针本身为常量(即常指针),则称为顶层const
    更一般的,顶层const可以表示任意的对象其本身是常量。
1
2
3
int i = 0;
int *const p1 = &i;//不能改变p1的值,这是一个顶层const
const int ci =42;//不能改变ci的值,这是一个顶层const
  • 底层const 若指针所指的对象为一个常量,则成为底层const

注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。如 const引用为底层const

1
2
const int *p2 = &ci;//允许改变p2的值,这是一个底层const
const int &r = ci;//用于声明引用的const为底层const

4.2 记忆技巧

const考虑成向右结合
如果const右结合修饰的为类型或者*,那这个const就是一个底层const,即指向常量的指针。(底层比较隐晦,从你看到的对象开始,要再转一道才可以)
如果const右结合修饰的为标识符,那这个const就是一个顶层const,即常指针或普通常量。(顶层比较显眼,你看到对象就是)

4.3 作用

为什么要区分顶层和底层呢?这是因为在拷贝赋值时,const是顶层还是底层对拷贝是否合法起决定性作用。

有以下规则:

  1. 顶层const对拷贝赋值不起影响。
  2. 左值若无底层const, 则右值有无底层const都无所谓。
  3. 左值若有底层const, 则右值一定不可有底层const

简而言之一句话:变量指针或引用不可指向常量
举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
int i = 0;
const int ci = 5;//顶层const
i = ci;//正确,右值n有一个顶层const,对拷贝不造成影响

int *const p1 = &i;//p1有顶层const
const int *p2 = &ci;//p2有底层const
const int *const p3 = p2;
p2 = p3;//正确,p3的顶层const不影响, 二者都有底层const

p2 = p3;//正确,都有底层const
int *p = p3;//错误,p无底层const而p3有
int *const px = &ci;//错误,&ci 将const int 转换为 const int *,有底层const,而左值没有
int &r = ci;//错误,变量引用不可绑定到常量上(int类型数据无底层const的说法)