媒介
在学习C++时,const关键字的知识点分散在书的各个章节。当我们实验在编程时利用const时,总会感觉有一些细节被遗忘,因而不能得心应手地利用const关键字。因此,本篇文章实验着对const关键字的做一些总结。参考书籍《C++ Primer Plus》
const总结
这里是我做的关于const关键字的一些总结,之后的各章便是对书中知识点的明白。
- const限定符创建的常量不可再次修改。
- 创建常量时记得初始化。
- const创建的常量可以用来声明数组长度。
- const int * p;,p指向常量,p可修改,*p不可修改。
- int * const p;, p指向变量,p不可修改,*p可修改。
- const指针可以继承const数据和非const数据。
- 非const指针仅可以接收非const数据。
- 不允许将非const指针的地址赋值给const指针。
- const引用创建临时变量的两种情况。
- const全局变量是内部链接性,如static。可用extern更改链接性。可在头文件中利用。
- cv-限定符。
- const成员函数,void show() const;,表示函数不会修改调用对象(类成员)。
const限定符
const关键字是C++中较为常用的一个关键字。当我们想创建一个符号常量时,按照C语言的习惯,我们一般会利用#define这种预处理器方法,例如#define ZERO 0。但在C++中,提供了一种更好的处理符号常量的方法,那就是const关键字。
创建常量
创建一个常量的通用格式:const type name = value;
例如:- const int zero = 0; // 一个普通的常量·
复制代码 这个例子中,被const修饰过的变量zero会变为常量。常量zero被初始化后,其值就被固定了,C++编译器不允许再次修改常量的值。
常量初始化
这里有一点需要注意:在用const声明一个变量时,需要举行初始化。例如下面的代码是错误的:- const int zero;//声明常量时需要进行初始化,否则zero的值未知。
- zero = 0;//因为C++编译器不允许再次修改常量的值。所以此处错误。
复制代码 另外,常量可以用来作为声明名数组时的元素数目。例如:- const int ten = 10;//创建了一个常量。
- int array[ten];//用常量创建数组。
复制代码 一级指针与const
在用const修饰指针时则会出现一些很玄妙的地方。在C++中,可以用两种差别的方式将const关键字用于指针。第一种方法是让指针指向一个常量,第二种方法是让指针本身就是常量。此中,第一种方法可以防止利用该指针来修改所指向的值。而第二种方法可以防止改变指针所指的位置。例如:- int value = 0;
- const int * p1 = &value; //第一种用法,防止利用p1修改value的值。
- int * const p2 = &value; //第二种用法,p2本身不能再修改了。
复制代码 这里有一个特别情况令人在意。如果将一个指向变量的指针指向一个常量会发生什么?代码如下:- const int value = 0;
- int * p2 = &value; //这是错误的,C++禁止这样的行为。
复制代码 我们发现,value是常量。但p2是指向value的,那么我们可以通过* p2对value的值举行修改。可是做这样的话const的作用就失效了。
事实上,C++禁止这样的用法。也就是说,C++禁止将const常量的地址赋值给非const指针。因此,上面的代码是错误的。在逻辑上也很好解释。
我们可以这样明白:为保证常量不可再次修改的属性,我们不能通过指针修改常量,因此,非const指针仅可以接收非const数据。与之雷同,因为我们声明了const指针目的是不会通过当前指针修改其指向的数据,因此其指向的数据一直都是安全的,天然const数据和非const数据都可以。因此,const指针可以继承const数据和非const数据。
二级指针利用const的限制
关于二级指针与常量的关系有些复杂,我们来看下面的代码:- const int ** pp;//这是一个二级const指针
- int *p;
- const int value = 0;//这是一个常量
- pp = &p; //这里是错误的,虽然高亮没有提示错误。
- //错误C2440:初始化:无法从int **转换为const int **。
- *pp = &value;//两个都是常量,赋值没有问题
- *p = 10; //通过p修改了value的值!
复制代码 如果pp = &p允许的话,那么我们可以通过二级指针绕开const的限制,如上诉代码一样。C++规定,仅当且只有一层间接关系(如指针指向根本数据范例)时,才能将非const地址赋值给const指针。也就是说,C++不允许将非const指针的地址赋值给const指针。
最后,关于const与指针的关系,下面还有几个例子,请看:- const int value =0;//这是常量
- const int *p1;//p1可变,*p1不可变
- int * const p2 = &value;//p2不可变,*p2可变
- const int ** pp3;//pp3可变,*pp3可变,**pp3不可变
- int * const * pp4;//pp4可变,*pp4不可变,**pp4可变
- int ** const pp5;//pp5不可变,*pp5不可变,**pp5可变
- const int * const *const p6 = &p1;//pp6不可变,*pp6不可变,**pp6不可变
复制代码 const引用
我们在利用函数的时候,一般会利用引用形参。原因就是因为速率快,无需走复制的流程。当我们利用引用的时候,如果实参与引用参数不匹配,那么C++将产生临时变,关于const引用却有需要了解的知识点。
如果引用形参是const,则C++编译器将在下面两种情况下生成临时变量:
- 实参的范例准确,但不是左值。
- 实参的范例不准确,但可以转换为准确的范例。
左值:在C语言中,左值最初指的是可出现在赋值语句左边的实体,但现在,通例变量和const变量都视为左值,因为可以通过地址访问它们。
左值例子:变量,数组元素,布局成员,引用,解除引用的指针等。
非左值例子:字面常量(用引号括起的字符串除外,因为它们是地址),包含多项的表达式等。
代码例子如下:- double refcube (const double &ra){return ra * ra * ra;}
- double side = 3.0;
- long edge = 5L;
- double x1 = refcube(edge);//实参类型不正确,但可以转换为正确的类型
- double x2 = refcube(7.0);//实参类型正确,但不是左值(字面常量)
- double x3 = refcube(side+10.0);//实参类型正确,但不是左值(表达式)
复制代码 const全局变量
在C++中,const限定符对默认存储范例稍有影响。默认情况下,C++全局变量的链接性是外部的,但const全局变量的链接性为内部的。也就是说,在c++看来,全局const定义就像利用了static说明符一样。
C++这样做有着许多的利益,这意味着每个文件都有自己的一组常量,而不是全部文件共享一组常量。因此我们可以将常量定义到头文件中,这样只要在两个源代码文件中包括同一个头文件,则它们将获得同一组常量。固然,如果我们希望某个常量的链接性为外部的,那么我们可以利用extern关键字来覆盖默认的内部连接线。extern const int states = 50;
cv-限定符
- const
- volatile
const限定符表明,内存被初始化后,步伐便不会再对它举行修改。
volatile限定符表明,纵然步伐没有对内存单位举行修改,但此中的值也可能发生变化。可能由于硬件的原因,也可能由其他步伐修改,如共享数据。这个关键字的作用重要是为了改善编译器的优化能力。
防止编译器将该值用缓存的方式举行优化。
mutable限定符也是需要了解的。当我们声明一个数据布局体为常量,而此中某个成员却需要修改时。我们可以利用mutable限定符对需要修改的成员加以修饰。例子如下:
- struct Data{
- int x;
- mutable int y;//声明此成员是可被再次修改的。
- };
- const Data data = {1,2};//data实例是常量
- data.y=3;//但data的y成员可以被再次修改!
复制代码 上述代码中,data的const禁止步伐修改data的成员,但由于y成员的mutable限定符说明了data的y成员不受这种限制,仍然可以被再次修改。
const成员函数
请看如下的代码片段:
[code]class Data{int x;public:void show(){std::cout |