const限定符的理解与运用。
const关键字在变量中的作用
有时候我们希望定义这样一种变量,它的值不能被改变,去用来防止我们有时候可能不小心去改变了这个值。这个时候const关键字就派上用场了。
我们通过在变量类型前加上const关键字,来达到让变量的值不能被改变的目的。
例如:
const int i = 42;
i = 43;// 错误:试图改变一个const对象的值
初始化与const
在某些情况下,我们可以用一个const对象,去初始化一个不是const对象的值。(引用和指针类型的变量除外)
例如:
const int i = 42;
int p = i;
我们把普通变量加上const关键字后称之为常量。
顶层const与底层const的概念
顶层const可以表示任何对象是常量,这一点对任何数据类型都适用,如算数类型,类,指针等。而底层const更多是与指针和引用等复合类型的基本类型有关。我们需要着重注意顶层const与底层const的这一点区别。
我们将const关键字写在变量类型之后,代表它是顶层cosnt。
我们将const关键字写在变量类型之前,则代表它是底层const。
还需记住,顶层const和底层const可以同时出现在同一个变量身上。
还有一条最重要的规则是,我们可以使用非常量去初始化一个底层的const对象,但反过来则不行。
const的引用
我们将普通引用的变量类型前加上了const关键字后,这个普通引用就变成了const的引用。const对象的特点是其值不能改变,所以这个const引用只能指向它引用的对象,不能再去更改所指向的对象了。const引用也称为 常量引用。与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。(ps:常量,不能被改变的变量)
例如:
const int ci = 1024;
const int& r1 = ci;//正确,引用及其所绑定的对象都是常量。
r1 = 1023; //错误:r1是对常量的引用,试图去改变一个常量的值。
int p1 = 5;
const int& p2 = p1;//const应用允许绑定一个不是const对象的变量。
普通引用也不被允许去绑定一个常量引用
const int& i = 42;
int &r1 = i; //错误,r1是一个普通引用
总结:这里用到了非常重要的一条关于初始化的规则:我们可以用一个非常量的值去初始化底层const,但是反过来则不行。
const作用于引用对象上时,唯一的作用就是让这个引用对象不可以再更改它的绑定对象了。其他的都与普通引用没什么差别。
指针和const
这里我认为是理解const对象的一个重难点。
因为指针本身是一个对象,它又可以指向另一个对象。因此,指针本身是不是常量,和指针指向的对象是不是常量就变成了两个问题。
我们为了使这个概念更加的清晰,我们用 顶层const 、与 __底层const__这两个名词来区分这两个问题。
顶层const:代表的是指针本身就是个常量。
底层const:代表的是指针指向的对象是一个常量。
为了更好的理解这个概念,我们接下来举上几个例子来区分顶层const与底层const
例1:
int i = 5;
int* const p1 = &i;
*p1 = 7;正确,因为变量i不是一个常量,所以可以使用指针去修改i这个变量的值
在这个例子中,p1是一个顶层const,它代表的是p1这个指针本身是个常量,所以说,p1不可以更改它所指向的对象,只能指向i。
在这里面,i是常量和不是常量都可以,因为指针本身是个常量与指针指向的对象是不是常量无关。表示顶层const的方法是将const关键字写在变量类型的后面。
例2:
const int ci = 7;
const int* p2 = &ci;
const int c2 = 8;
p2 = &c2;//正确
*p2 = 9;//错误,因为指针指向的值是一个常量,所以不能用指针去修改一个常量的值。
在这个例子中,p1是一个底层const,它代表的是p1这个指针指向的对象是一个常量,而p1本身并不是一个常量,所以p1可以指向任意的int类型的常量对象。表示底层const的方法是将const关键字写在变量类型的前面。
注意⚠️:我们把指向常量的指针叫做常量指针,但是常量指针也可能指向一个非常量的值,但指针以为自己指向的是一个常量,所以指针不能去修改这个常量的值。这一点与常量引用类似。所以下面这个小例子也是对的。
这就是非常重要的一条关于初始化的规则:我们可以用一个非常量的值去初始化底层const,但是反过来则不行。
int ci1 = 9;
const int* p4 = &ci1; //正确,底层const,指针以为自己指向的是一个常量,但其实不是。
ci1 = 10;//我们不可以通过指针去修改变量的值,但我们可以通过更改变量本身的值去修改变量的值,hh
例3:
const int ci = 7;
const int* const bi = &ci;
int i = 5;
bi = &i;//错误,指针本身是一个常量,不可以更改指针所指向的对象。
*bi = 6;//错误,指针指向的对象是一个常量,不可以通过指针去改变一个常量的值
在这个例子之中,我们看到了顶层const和底层const同时出现在了bi这个指针之上,它代表的含义也很简单。意思就是bi这个指针是一个常量,指针所指向的变量也是一个常量。而常量的性质是不可以改变。所以bi不可以指向别的对象,也不可以通过bi去修改其指向变量的值。
总结:对于顶层const与底层const,我们需要记住,顶层const所代表的含义是指针本身是个变量,指针只能指向唯一的变量,此后不能更改,表示顶层const的方法是将const关键字写在变量类型之后,而底层const所代表的含义是指针所指向的对象是一个常量,指针本身可以指向不同的对象,但指向的对象必须是一个常量,我们不可以通过指针去修改一个常量的值,表示底层const的方法是将const关键字写在变量类型之前。
const关键字在函数中的作用
const形参与实参
当形参为const时,我们可以用常量或者非常量的对象去初始化形参。这是因为在实参初始化形参的过程中会忽略掉形参的顶层const。
附录:两条重要的初始化规则。
规则一:我们可以使用一个非常量去初始化一个底层const,但反过来则不行。
规则二:当const作为形参时,会自动忽略顶层const的那一部分,所以,实参可以用常量或者 非常量的方式去初始化这个形参。