C++关键字const
const是为了使程序员在变和不变之间画出了一条界限,这条界限的两边分别是变量和常量。想要区别他们并非易事,所以,本文稍微的总结了一下const在什么时候、为什么、和怎么样使用关键字const。
- const限定符
- 值替代
- C/C++之辨析
- 常量表达式
- 引用与const
- 左值引用
- 右值引用
- 指针与const
- 函数参数和返回值
- 顶层const与底层const
- 类
- constexpr
- constexpr变量
- 指针与constexpr
值替代
const产生最初的动机是取代预处理器#define来进行值替代。令人感到吃惊的是,这样的一个关键字竟然被广泛的用于指针、函数变量、返回类型、类对象、类数据成员、类成员函数。当然掌握这些所有的用法,需要时间慢慢来消化、斟酌、反思。
当我们使用C语言进行程序设计的时候,预处理器可以不受限制的建立宏并用它来替代值。但是预处理器只能做文本的替换,它没有类型检查功能。于是,C/C++可以通过使用const值来避免一些问题。
预处理的用法:
#define BUFSIZE 100
BUFSIZE很奇怪,它仅仅在预处理的阶段存在,因此不占据存储空间同时能放入一个头文件中,目的是为使用它的编译单元提供一个值。进而,当程序进行决定更改一个值的时候,必须手工编辑。最重要的是不能跟踪以保证没有漏掉其中的一个。
C++中使用const来消除这些问题。
const int bufsize = 100;
这样就可以在编译时编译器需要知道这个值的任何的地方使用bufsize,同时编译器还可以进行“常量折叠”,可以简略的理解为通过必要的计算把一个复杂的“常量表达式(下有说明)”通过缩减简单化。例如在数组中:
char buf[bufsize];
因此,我们应该完全使用const 取代#define的值替代。
在头文件中,使用const也必须放入头文件中。这样,可以通过包含头文件,可以把const定义单独放在一个地方并把它分配在一个编译单元中。但是C ++中的const默认为内部链接,意思为:const仅仅在被定义过的文件里才是可见的,而在连接不能被其他编译单元看到。当定义一个const,必须赋值给它,除非使用extern做出了清楚的说明:
extern const int bufsize; //强制分配空间
通常C++编译器并不为const创建存储空间,相反把这个定义保存在他的符号表里,但是extern强制分配了空间。由于编译器不能完全的避免为const分配内存,所以const默认为内部连接!
const在运行器件产生的值初始化一个变量,而且知道在变量的生命期间是不变的,则用const限定该变量是程序设计中一个很好的做法,如果偶然试图改变它,编译器会给出一个错误的信息。
const int i = 100;
const int j = i+10;
long address = (long)&j;
char buf[j+10];
分析上面的代码:i是一个编译期间的const,但是j是从i中计算出来的,然而由于i是一个const,j的计算值来自一个常数表达式,而它自身也是一个编译期间的const,下面的一行需要j的地址,所以迫使编译器给j分配存储空间。
从上面我们可以看出const意味着“不能改变的一块存储空间”。然而不能在编译器件使用它的值,因为编译器在编译器件不需要知道存储器的内容。所以下面的代码是有错误的:
const int i[] = {
1,2,3,4,};
float f[i[3]]; //error!
C/C++之辨析
常量的引进是在C++的早期标准中订制的,当时标准C规范正在制定,C委员会在C中使用const的意思是“一个不能被改变的普通变量”,const常量总是占用存储而且它的名字是全局符,因此,C编译器不能将const 看成一个编译期间的常量。