最近在看c++ primer,记录下读书笔记。
const关键字
当我们希望使用一个变量时,同时又不希望被改变时候,就可以用const来定义一个变量。这个变量就变成不可变的变量,约等于一个常量。
const int buf_size = 512;
buf_size = 512;//错误:试图向const对象写值
const对象一经初始化就不能被改变,所以const对象必须初始化。
const int k;//错误,k是一个未经初始化的变量
如果用一个对象去初始化另外一个对象,它们是不是const都无关紧要。
int i = 42;
const int ci = i;
int j = ci;
这种间接的拷贝赋值,只要把对象的值拷贝过去,原来的对象就和新的对象就没什么关系了。
由于const定义只在当前文件内有效,当我们想把它共享给多个文件,同时这个变量又不是一个常量表达式时,我们只需要在定义和声明时候加上关键字 extern就行了。
//file.c,在这个文件中定义并初始化一个常量
extern const int buf_size = fun();
//file.h头文件,这个buf_size和file.c中的是同一个
extern const int buf_size;
const引用和普通的引用不同的是,当被绑定的那个const变量无法通过被绑定引用改变。也就是说const的引用无法改变。
const int ci = 1024;
const int &r1 = ci;
r1 = 42;//错误,r1是一个const引用
int &r2 = ci;//错误,等号两边类型不同
这里很容易看出ci是一个常量,但是r2却是一个普通引用。因为不允许给ci赋值,所以通过别的途径给ci赋值也是非法的。
前面提到,引用的类型必须和其所引用对象的类型一致。但是这里有两个例外,第一种情况就是,在初始化常量引用时候允许用任意表达式作为初始值,只要该表达式能转换成引用的类型即可。
int i = 42;
const int &r1 = i;
const int &r2 = 42;
const int &r3 = r1 * 2;
再来看一种错误的:
double dval = 3.14;
const int &ri = dval;
这里是想用int类型去绑定一个double类型的dval,在编译器上就会转成下面这种形式
const int temp = dval;//生产一个临时的int变量
const int &ri = temp;//让ri绑定这个temp
在编译器上,其实绑定的是一个临时变量,既然我们用了绑定,就是想通过ri去改变dval的值,但是绑定的却是一个临时变量。所以C++把这个视为非法操作。
const绑定的值可以不是const变量,所以我们虽然不能通过绑定去改变值,但是我们可以通过其他途径去改变。
int i = 42;
const int &r1 = i;
r1 = 50;//错误,试图通过r1改变i
i = 50;//正确,i是一个普通变量。
指向常量的指针,是不允许通过指针去改变所指对象的值。要想存放常量对象的地址,就必须使用指向常量的指针。
const int i = 20;
int *p1 = &i;//错误,p1是一个普通指针
const int *p2 = &i;//正确p2是一个const的int指针
*p2 = 30;//错误不能给*p2赋值
指向常量的指针或者引用,只不过是指针或者引用“自以为是”,罢了,他们以为自己指向常量,就不去修改他们的值,但是可以通过其他途径修改。
const指针,若是一个常量指针,那么在定义时候就必须被初始化,一旦初始化完成,那就指针指向的地址就不允许修改。
*放在const之前和之后是不同的
const int i = 50;
const int *p1 = &i;//const放在*之前,表示p1指向的值不能改变
int k = 30;
int *const p2 = &k//const放在*之后表示,p2指向的地址不能改变
还有一种就是在*之前和之后都加const
const int a = 30;
const int *const pa = &a;//表示pa是一个指向常量对象的常量指针
既不能通过pa改变a的值,也不能改变pa指向的地址。
这里还有两个概念就是顶层const(top-level-const)和底层const(low-level-const)。就同上面的一样,在左边的底层const,代表指针指向的值不能改变;在右边的是顶层const,表示指针指向的地址不能改变。
在当执行拷贝操作时候,顶层const还是底层const区别明显,顶层const并比影响什么。因为拷贝时候不会改变对象值的大小,只是简单是拷贝出拷贝入,所以顶层const不影响什么,是否是常量都没什么影响。
int i = 0;
const int ci = 42;
i = ci;//正确,顶层const,对ci的拷贝无影响
const int *p2 = &ci;
const int *const p3 = p2;
p2 = p3;//正确,p2和p3具有相同的数据类型,而且有相同的底层const资格
但是底层const的限制要注意,拷入和拷出对象必须要有相同的底层const资格,两个对象的数据类型要和可以互相转换,非常量可以转换成常量,反之不行。
int *p = p3;//错误,没有相同的底层const资格
p2 = &i;//正确,int * 可以转换成const int *
p3比较特殊及时顶层const也是底层const,所以在拷贝时候无需关系他是顶层const还是底层const,但是千万不要给p3赋值。