C++ const理解
const 的作用
使用类型修饰符 const 说明的类型成为常类型,常类型的变量或对象的值是不能被更新的。所以在编程过程中存在保持不变的值时应当用 const 限定。
const 的基本使用
注意:因为 const 对象一旦创建后其值就不能再改变,所以 const 对象必须要初始化。
初始化 bufsize 的值:
1 | const int bufSize = 512; |
默认状态下,const 对象仅在文件内有效
注意:如果想在多个文件之间共享 const 对象,必须在变量的定义之前添加 extern 关键字。
解决 const 对象仅在文件内有效的方法:对于 const 变量不管是声明还是定义都添加 extern 关键字,这样只需定义一次就行。
1 | // file1.cpp |
const 的引用
“对 const 的引用”简称为“常量引用”。
把引用绑定到 const 对象上,就像绑定到其他对象上一样,称为对常量的引用。与普通引用不同,对常量的引用不能被用作修改它所绑定的对象:
1 | const int ci = 1024; |
注意:引用的类型必须与其所引用的对象类型一致。但是存在两种例外。
在初始化常量引用时允许用任意表达式作为初始量,只要该表达式的结果能转换成引用的类型即可。尤其,允许为一个常量引用绑定非常量的对象、字面量、甚至是个一般表达式:
1 | int i = 42; |
常量引用(对 const 的引用)可能引用一个并非 const 的对象
常量引用仅对引用可参与的操作做出了限定,对引用的对象本身是不做限定的。因为对象也可能是个非常量,所以允许通过其他途径改变它的值:
1 | int i = 42; |
为什么常量引用可以绑定一个与其类型不一样的对象呢?
1 | double i = 42.5; |
因为要保证 r1 能够绑定一个整型,会先把 i 赋值给一个整型的临时量,再让 r1 绑定这个临时量。由于 r1 绑定的是临时量而非 i,这就让引用失去了它原本的含义,所以 C++ 也把这种行为归为非法。如果是常量引用,这个引用无法被修改,自然就不会破坏引用的意义。
拓展:
- 左值是可寻址的变量;
- 右值一般是不可寻址的常量;
- 左值一般可以被修改,而右值不能。
指针和 const
与 const 相关的指针:
const int * p
或者int const * p
:指向 const 对象的指针或者说是指向常量的指针。int * const p
:常量指针、const 指针。const int * const p
:指向常量对象的常量指针。
从上面我们可以看出,当 *
比 const 离变量名更近时说明它先是一个指针,称为指向常量的指针;当 const 比 *
离变量名更近时说明它先是一个常量,称为常量指针。
指向常量的指针
指向常量的指针不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针。
1 | const double PI = 3.14; // PI 是一个常量,它的值不能改变 |
和引用一样,指针的类型必须与其所引用的对象类型一致,但是允许一个指向常量的指针指向一个非常量对象:
1 | const double PI = 3.14; // PI 是一个常量,它的值不能改变 |
注意:虽然不能给 *cptr
赋值,但是我们可以让 cptr
重新指向新的双精度常量。
和常量引用一样,所谓指向常量的指针仅仅只是不允许通过该指针去修改对象的值,并没有限制用其他途径去修改对象的值。
const 指针
指针是对象而引用不是,因此允许把指针本身定义为常量。常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。把 *
放在 const 关键字之前说明指针是一个常量,即不变的是指针本身,而非指针指向的值。
1 | int errNum = 0; |
注意:常量指针并不意味着不能用指针修改所指向对象的值。
如果我们用常量指针去指向一个常量,也会出现错误,因为常量指针是可以修改指向对象的值的:
1 | const int num = 0; |
所以我们可以总结出:
- 如果想要用指针指向常量,我们应该用指向常量的指针。
- 如果只想无法通过指针去修改指向对象的值,我们应该用常量指针,前提是指向的对象是一个非常量。
顶层 const 和底层 const
top-level 顶层 const: 指针本身是个常量(常量指针)。
low-level 底层 const: 指针所指向的对象是个常量(指向常量的指针)。
引用就是一个常量指针,不能更改绑定的对象地址;若是常量引用(即 const&
),能绑定常量,并且不能用常量引用修改对象的值,但是如果对象不是常量,则可以用其他方式可以修改。
1 | int i = 0; |
在拷贝对象时,顶层 const 和底层 const 的限制有所不同:
- 顶层 const 对拷贝对象时,无影响;因为拷贝对象时,并不会修改被拷贝对象的值,所以无论是拷入还是拷出的对象是否为常量都没有什么影响。
- 底层 const 对拷贝对象时,有影响;拷入和拷出必须具有相同的底层 const 资格,或者两个对象的数据类型必须能转换。一般来说,非常量可以转换成常量,反之不行。
1 | int i = 0; |
总之就是要考虑在拷贝后会不会影响原来对象的原有限制。
总结
- 在编程过程中存在保持不变的值时应当用 const 限定。
- 因为 const 对象一旦创建后其值就不能再改变,所以 const 对象必须要初始化。
- 如果想在多个文件之间共享 const 对象,必须在变量的定义之前添加 extern 关键字。
- 用常量引用绑定对象,无法通过常量引用修改对象的值;但如果对象是个非常量的话,可以通过其他途径修改对象的值。
- 在初始化常量引用时允许用任意表达式作为初始量,只要该表达式的结果能转换成引用的类型即可。
- const 和指针:
const char * p
或者char const * p
:指向常量的指针,不允许通过该指针去修改对象的值。char * const p
:常量指针,初始化后存放在指针中的那个地址不能再改变。- 要想存放常量对象的地址,只能使用指向常量的指针,允许一个指向常量的指针指向一个非常量对象。
- 虽然不能给指向常量的指针赋值,但是我们可以让指向常量的指针重新指向新的双精度常量。
- 常量指针必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。
- 常量指针并不意味着不能用指针修改所指向对象的值。
- 如果想要用指针指向常量,我们应该用指向常量的指针。
- 如果只想无法通过指针去修改指向对象的值,我们应该用常量指针,前提是指向的对象是一个非常量。
- top-level 顶层 const:指针本身是个常量(常量指针)。
- low-level 底层 const: 指针所指向的对象是个常量(指向常量的指针)。
- 在拷贝对象时,顶层 const 无影响,而底层 const 有影响;拷入和拷出必须具有相同的底层 const 资格,或者两个对象的数据类型必须能转换。一般来说,非常量可以转换成常量,反之不行。
📌最后:希望本文能够给您提供帮助,文章中有不懂或不正确的地方,请在下方评论区💬留言!
🔗参考文献:
📖 C++ Primer中文版 第5版 [(美)李普曼,(美)拉乔伊,(美)默著][电子工业出版社][2013.08][838页]