const关键字总结
是什么
以const
(即单词constant)创建的变量的值不能改变,因为值不能修改,可以理解为常量,但是不存在真正的常量,只是在const
的作用域中,该变量才不能被修改,超过了这个const
的作用域,有可能是可以修改。
注意:const
仅表示不能改变,不表示具体的变量类型,按照从右向左的方式来理解变量的命名,首先要说明这是一个什么类型的变量,其次才是变不变,因此const
的位置应该在变量类型名
左边。如下所示:
//从右向左读变量,首先a是一个int型变量,在这基础上,a才是一个值不能改变的int型变量
const int a = 0;
初始化
因为const
的对象创建以后就不能修改,因此在创建时必须初始化
怎么用
核心思想
const
创建的对象只能完成不改变其本身值的操作,也就是说,**不管操作多复杂,只要不改变const
对象的值,const
对象都支持。**这个核心思想包含了两方面,1)阻止显式更新const
对象;2)阻止隐式更新const
对象,代码如下。
const int a = 0;
1)阻止显式更新a
{ //不合法
a = 42;
}
2)阻止隐式更新a
{ //不合法
int &a1 = a;
}
- 阻止显式更新:很明显,不能直接改变
const
对象的值,这也是最直观的一种 - 阻止隐式更新:定义一个
非const
类型的引用指向const
对象。虽然并没有直接改变const
对象a
,但如果这一步合法了,那么通过非const
类型的引用a1
岂不是可以间接改变const
对象a
了么
const引用
有一种特殊情况:const
对象(包括引用)在初始化时允许用任意表达式作为初始值,也就是说,可以将一个变量作为初始值赋给一个const
对象。也就是说下面的情况是合法的:
int a0 = 0;
//(1)合法
const int a = a0;
//(2)合法
const int &aa = a0;
为什么这样是合法的呢,按照核心思想中的理解,就算现在把int
变量a0
作为初始值赋给const
对象a
,在初始化阶段给const
赋值是合法的;但是将一个const
引用aa
指向一个int
类型变量a0
,不属于隐式更新的可能么,直接修改int
变量a0
不就是等于修改了它的引用aa
么?
实际上像上面代码中第(2)种情况,将一个常量引用绑定到其他类型上时发生了一个转折,代码如下:
double a_double = 1.111;
const int &aa = a_double;
//上面两行代码等效于
double a_double = 1.111;
const int temp = a_double; //临时常量进行强制类型转换
const int &aa = temp;
对于常量的引用之所以能够用任意表达式进行初始化,原因就是创建了一个临时的const
常量进行了强制类型转换。
总结一下:对于常量的引用(const 类型 &引用名)所绑定的对象,是不是const
对象都不要紧。关于const引用
绑定一个非const对象
的操作,经常发生在传引用进入一个函数内部,但不希望在函数内部改变该引用所绑定的对象的值的情况中。
指向const对象的指针&const指针
const与指针结合就不像const引用
这么粗暴了,因为引用是别名,引用名与引用绑定的对象都是同一个,但是指针作为一种对象,与const对象结合一下就有了变化:
指向const对象的指针
:当一个指向const对象的指针被初始化以后,表示不能通过该指针修改所指对象的值。指针本身就是一个const对象
,是真正的const指针
:const指针
必须初始化,指针的值不能改变,就是const指针内存储的内存地址不能改变。可以通过const指针
修改指向对象的值,除非指向的是一个常量对象那就不能修改。
//指向const对象的指针,也叫底层const
const int a = 0;
const int *a_p = &a;
//const指针,指针本身就是一个const对象,称为顶层const
int b = 0;
int *const b_p = &a;
同样是从右向左读,a_p
的理解顺序为{指针,int类变量,常量}:a_p
是一个指针,指向一个int类型的常量;b_p
的理解顺序为{常量,指针,int类型}:b_p
是一个const指针,指向一个int类型的变量。
与const引用
一样,指向const对象的指针
一样要阻止显式更新(直接改变指针所指向对象的值)与隐式更新(使用普通指针指向常量对象):
const int a = 0;
const int *a_p = &a;
//1)阻止显式更新
*a = 1; //不合法
//2)阻止隐式更新
int *a_ppp = &a;
但是,指向const对象的指针
同样具有一种例外,对于常量指针的初始化,可以用任意表达式来赋值,是不是常量对象都没关系。
const指针
只是要求指针内存储的内存地址不能被修改,没有禁止这块内存上存储的变量的值不能修改:
int b = 0;
int *const b_p = &b;
*b_p = 1; //合法,因为b_p内始终存储b的地址
int c = 2;
*b_p = &c; //不合法,试图改变b_p指向的内存地址
多文件
若当前编写的文件中包含多个其他文件,并且在不同文件中都用到了const
创建对象,甚至有可能const
对象的名字都一样,这种情况会经常发生那。在默认情况下,一个文件中的const
对象的作用域被设置为仅在当前文件内。
如果确实是想要在多个文件中共享一个const
对象,也就是说只在一个文件中定义const
对象并初始化,在其他文件中声明并使用该对象,办法就是将该const
对象的定义和声明都添加extern
关键字。
//初次在一个文件中定义并初始化一个const对象
extern const int a = 0;
//在其他文件中声明该const对象后就能使用
extern const int a;
对于extern
关键字的理解同样要遵循从右到左的顺序:变量a
首先是一个int
型变量,其次是值不能修改的int
对象,最后才是可以在多个文件中共享的、值不能修改的int
对象。
顶层const
概念
顶层const与底层const是针对指针来说的,因为指针也可以作为对象,所以指针是不是常量和指针指向的对象是不是常量就是两个独立的问题。
顶层const
:指针本身就是常量,为const指针
底层const
:指针指向的对象是一个常量对象,即指向const对象的指针
除了指针和引用这种复合数据类型,其他所有普通类型(int、double等)的const对象基本都是顶层const。
一个指针既可以是顶层const
又可以是底层const
,此时表示一个const指针指向了一个const对象。
int a = 0;
const int *a_p = &a; //底层const
int b = 0
int *const b_p = &b; //顶层const
int c = 0;
const int *const c_p = &c; //既是底层const又是顶层const