C++ const限定符

本文为我学习《C++ Primer 》(第5版)的学习笔记,如有错误,欢迎指出。

1.const的简单理解

当我们想要声明一种变量,它的值不能被改变时,我们就会用到关键字const,例如:

const int ci = 1;

此时ci就是常量,它的值不能被修改,const限定符的作用是将一个对象转换为一个常量。这里有个关键点,const作用的是一个对象
由于const对象的值不能在之后的使用中被修改,所以必须在声明一个const对象时进行初始化,例如:

// 声明时初始化,正确
const int r = 10;
// 没有初始化,错误
const int w;

2.引用和指针的简要区分

引用是一个对象的别名,我们对一个对象进行操作,可以通过对象名称,也可以通过它的引用来完成。如同现实生活中,名字叫张三的学生,学号是1234,老师要让张三回答问题,可以说:名字是张三的同学请回答问题,也可以说:学号是1234的同学请回答问题,效果是一样的。
定义引用时,是把对象与引用绑定一起,而不是拷贝一份对象的值,并且定义引用时必须进行初始化,一旦引用与某个对象绑定一起,则该引用不能再和其他对象绑定,一般而言,引用的类型与要绑定的对象的类型严格匹配,例如:

int val = 1;
/* 
 * 引用的定义形式一般为:数据类型 + &变量名
 * &为标识该变量为引用,而不是取址!
 */
int &rval1 = val;

// 引用必须与实际存在的对象进行绑定,而不能是字面值常量,错误
int &rval2 = 2;

// 没有初始化引用,错误
int &rval3;

// 引用的类型与对象的类型不匹配,错误
double &rval4 = val;

指针是存储一个对象的地址,通俗地讲就是指针指向某个对象。它不需要在定义时初始化(但为了避免野指针的问题,最好是初始化为nullptr),同时在指针的生命周期内,可以给指针赋予其他对象的地址。同样一般而言,指针的类型与指针指向的对象类型要严格匹配,例如:

int valA = 1;
int valB = 2;
/*
 * 指针的定义形式为:数据类型 + *变量名
 */
int *pval1 = &valA;

// pval1 现在指向valB
pval1 = &valB;

// 指向的对象类型与指针类型不匹配,错误
double *pval2 = &valA;
总结:

共同点:
1.两者都是对象的间接访问

不同点:
1.引用不是对象,指针是对象,所以我们有指向指针的引用,指向指针的指针,但是没有引用的引用,指向引用的指针
2.定义时,引用必须初始化,指针可以不初始化
3.引用与对象绑定后,不能再通过赋值与其他对象绑定,而指针在其生命周期里可以通过赋值指向不同的对象

3.const与引用

还记得第1部分里提到的const是作用于对象么?,因此,const与引用的组合只能是const修饰引用绑定的对象,即将引用与const对象进行绑定,称为对常量的引用

要点1:对常量的引用与非常量的引用区别在于引用可参与的操作上,前者不能用来改变引用的对象的值

既然引用的对象是常量,意味着不能通过该引用对常量对象进行修改,由此,我们不难想到一个要点:

要点2:不允许让一个非常量引用与一个常量对象绑定

例子:

const int val = 1;
const int &rval1 = val;

// 不能通过对常量的引用修改常量对象的值,错误
rval1 = 2;

// 不允许让一个非常量引用与一个常量对象绑定,错误
int &rval2 = val;

假设rval2能与常量对象val绑定,则意味着可以通过rval2对val进行修改,这显然是不正确的。

要点3:对常量的引用可以与非常量的对象,字面值,一般表达式进行绑定,只要它们能转换成引用的类型

后两点有点饶,可以从对象的方向进行理解:
1.如果对象是const对象,那么对它的引用只能是对常量的引用,如果对它的引用是非常量引用,则存在通过非常量引用修改const对象的风险
2.如果对象是非const对象,那个对它的引用既可以是常量引用(不能通过这个引用改变对象的值),也可以是非常量引用(可以通过引用改变对象的值)
例子:

int val = 3;
// 将对常量的引用与非常量对象绑定,正确
const int &rval1 = val;

// 不能通过常量引用改变非常量对象的值,不意味着对象的值不能修改
val = 4;

// rval1 * 2的结果是常量,不能与非常量引用绑定, 错误
int &rval2 = rval1 * 2;

4.const与指针

到了最麻烦的组合,先说说跟引用类似的地方吧,就是const修饰的是被指向的对象,此时指针称为指向常量的指针。类似于引用,我们有以下要点:

要点1:指向常量的指针与指向非常量的指针区别在于指针可参与的操作上,前者不能用来改变指向的对象的值,与引用不同的是指向常量的指针可以通过赋值来改变指向的对象
要点2:不允许指向非常量的指针指向一个常量对象
要点3:允许指向常量的指针指向任意对象(常量或非常量)

例子:

const int cval = 1;
const int *pval1 = &cval;

// 不能通过指向常量的指针修改对象的值,错误
*pval1 = 2;

// 不允许指向非常量的指针指向一个常量对象,错误
int *pval2 = &cval;

int val = 3;
// 允许指向常量的指针指向非常量对象,正确
const int *pval3 = &val;

// 非常量的值可以通过其他方式修改
val = 4;

由于指针本身也是一个对象,所以除了指向的对象外,const也可以修饰指针本身,此时指针称为常量指针
看到这里,估计有人就开始迷糊了,指向常量的指针,常量指针,指向常量的指针,常量指针……
先来区分一下两者,要重点记了:

1.指向常量的指针与常量指针是不同的概念,前者表明指向的对象是常量,后者表明指针本身是常量
2.书写方式不同
//指向常量的指针
const int number1 = 1;
const int *ptr1 = &number1;

//常量指针
int number2 = 2;
int *const ptr2 = &number2;

//指向常量对象的常量指针
const int *const ptr3 = &number1;

OK,现在应该清楚两者的区别了吧,那我们继续来讲一下常量指针,由于它是由const修饰的对象,所以根据第1部分,我们可以知道

要点4:常量指针必须初始化,且初始化后,它的值(即存储的地址)不再改变,意味着常量指针一直指向那个对象。

这点看起来就像是引用。
最后是简单的一点:

要点5:常量指针并不意味着不能通过该指针修改其所指向的对象的值,如果指向的对象是非常量对象,则可以通过常量指针去修改对象的值

例子:

int val1 = 1;
int val2 = 2;
int *const pval1 = &val1;

// 常量指针一旦初始化后不能通过赋值改变其指向的对象,错误
pval1 = &val2;

// 常量指针指向的非常量对象的值可以通过常量指针修改,正确
*pval1 = 3

写到这里,大家应该对const与引用,const与指针的组合有个比较清晰的了解,如果不懂,就比对着例子多看几遍,相信能有所体会,接下来就讲一下怎样判断包含着const,引用和指针声明的含义。

5.声明的阅读方式

这部分内容被我扩展成另一篇文章,请看《C++ 理解复杂的声明》

总结

1.const限定符是把一个对象转为常量
2.能做哪些操作,关键是判断哪部分才是常量,const修饰的部分不能被修改,若const修饰的是被指向的对象,则不能通过引用或指针修改对象的值,若修饰的是指针本身,则不能修改指针指向的对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值