const的引用
通常情况下:引用的类型都要与之绑定的对象类型严格匹配
int a = 100;
int &val = a;
但是常量的引用不需要。
double i = 14.42;
const int& i0 = i;
cout << i0;
最后的输出是14;
也就是说,常量的引用会对引用的类型不匹配的对象会进行类型的转换。
此处的i0引用了一个int类型的对象,但是i是双精度浮点数。因此为了确保让i0绑定了一个整数,编译器把上述代码变成了如下形式:
const int temp = i; //用变量实现类型转换
const int &i0 = temp; //再用i0对临时量(此时的类型是匹配的)进行绑定
就很奈斯。
另外,此时常量引用,其实严格意义上貌似不是一个常量,虽然但是,我们仍然无法直接对const 对象的引用,进行赋值,或者其他试图更改常量的操作。但是,可以通过其他方式改变常量的值
#include<iostream>
using namespace std;
int main() {
int val = 100;
const int &cval = val;
cout << cval << endl;
int& ival = val;
++ival;
cout << cval;
}
输出结果是
100
101
或者直接对原始对象 val进行赋值,也会改变const对象的引用的值。
补充:通常情况下,引用是不可以用字面值进行赋值的,但是常量引用是可以的
const int &val = 42;
cout << val;
结果是42
也就是说,不能通过常量引用去改变对象,那么绑定的是字面值还是对象都无所谓了,反正都不能做改变也就不存在改变无意义的情况。
指针和const
const double pi = 3.14;//初始化双精度常量pi
const double* cptr = π//只能使用指向常量的指针存放常量对象的地址
double a = 100;
cptr = &a;
cout << *cptr; //输出100;
++ *cptr;//不能给常量赋值
指针的类型必须和他所指的对象的类型一致。但是有两个例外,第一种例外就是允许一个指向常量的指针指向非常量对象。
再看上面的代码,我们发现,指向常量的指针,仅仅要求不能通过该指针改变对象的值,我们甚至可以直接改变指针的指向,指向一个非常量的对象,二者并没有绑定的关系。
这是我自己画的图,我自己的理解,不知道有没有错误,const常量的地址必须存放在const *类型中,但是const *也可以存放非常量的地址。但是无论存放的是谁的地址,都不能通过解引用符“*”,对 对象进行更改。和const引用一样,虽然不能直接通过指针对 对象进行更改,但是其他途径更改对象,无所谓。
这里的const只是在说:这个指针指向的是一个常量,不能通过这个指针乱改这个常量。和接下来要说的const指针大不相同。
书中:所谓指向常量的指针或引用,不过是指针或引用“自以为是”罢了,他们自己觉得自己指向了常量,所以自觉地不去改变所指对象的值。
const指针
其实上面,我们的重点是在类型上。所以我们可以改变指针的指向。下面要说的const指针,是把指针本身定为常量。常量指针和常量一样,必须初始化,并且初始化完成后,存放在指针中的地址就不能再被改变了。
#include<iostream>
using namespace std;
int main() {
int val = 100;
int* const cptr = &val; //把*放在关键字之前说明指针是一个常量
//这样的书写形式隐含着一层意味
//即不变的是指针本身的值(指向的对象的地址)
//而不是指向的对象
//指向的对象是可以通过解引用符修改值的。
const double pi = 3.14;
//这里第一个const保证了不能通过指针修改这个常量
//第二个const保证了,这个指针存放的地址,已经和这个指针绑定,不能更改。
const double* const pip = π
//pip = &val; 不合法,因为pip是一个常量指针,被初始化后,就不能更改值
//*pip = 1111; 不合法,它指向的是一个双精度常量,不能通过该指针修改,他所指向的对象的值。
int* const cptr1 = &val;
*cptr1 = 100;
cout << *cptr1;//输出100,这里的类型int
//表示解引用后得到的是一个int类型的变量,可以更改值
//如果是const int ,则不能修改值
}
我感觉这种定义语句,从右向左看,是一个不错的选择。
顶层const
如前所述,指针本身是一个对象,它又可以指向另外一个对象。因此指针本身是常量,以及指针所指的是不是一个常量是两个相互独立的问题。
底层const:表示指向的对象是一个常量,与指针和引用等复合类型相关。
顶层const:表示指针本身是一个常量,顶层const可以表示任意的对象是常量,对任意数据类型都适用。
比较特殊的是:指针既可以是底层const,又可以是顶层const
int const a = 100;
cout << a;
//我发现不止指针可以在类型后面使用const,其他的数据类型也可以。
用于声明引用的const都是底层const,对常量对象取地址也是const
const int ci =100;
&ci:底层const
int const a = 100; int b = a; const int* c = &a;//顶层const可以给底层const初始化。 //int* const d = c;//底层const不能给顶层const初始化(拷贝),因为因为c本身是底层const,它本身是一个变量 //但是d是顶层const,一个常量。所以变量不可以给一个常量进行拷贝。 //再来一个例子,引用的例子 const int& bval = b;//引用都是底层const const int* const p = &bval; //int* const p2 = &bval; p2是一个顶层const,但是bval是一个底层const,不能执行