【C++】顶层const和底层const

C++顶层const和底层const

按照c++ primer里面的描述习惯,认为对象为“具有某种数据类型的内存空间”

一、const与引用:对常量的引用(reference to const)

const int ci = 0;//常量int对象
int &a = ci;//报错

将 “int &” 类型的引用绑定到 “const int” 类型的初始值设定项时,限定符被丢弃,这是因为引用的类型必须与其所引用对象的类型一致。

int i = 0;//非常量int对象
const int ci = 0;//常量int对象

const int &a = i;//指向常量的引用(一般称为常量引用),绑定到非常量
const int &b = ci;//指向常量的引用,绑定到常量

引用的类型必须与其所引用对象的类型一致,但也有两种例外情况(本文将讲第一种例外情况):只要等号右边的表达式的结果,能转换为引用的类型。尤其,允许为一个常量引用绑定非常量的对象、字面值,甚至一般表达式。

  1. 如上两个引用都是常量引用(reference to const,对常量的引用)。
  2. 如上代码const int &a = i,可以把int类型转换为const int &类型,即可以把常量引用绑定到非常量对象上,但基本类型得一样。
  3. 如上代码const int &b = ci,把常量引用绑定到常量对象,这是正常操作,类型一样其中没有转换过程。
  4. 如上四个量,只有i可以进行赋值操作,因为只有它是非常量,赋值后,它的引用a的值也随着改变。换句话说,不能通过常量引用a来赋值,但由于a绑定的是非常量i,所以i可以进行赋值操作。

二、const与指针:指向常量的指针

const int ci = 0;//常量int对象
int *a = &ci;//报错

与引用一样,非常量指针不能指向常量

int i = 1;//非常量int对象
const int ci = 5;//常量int对象

const int *a = &i;//指向常量的指针,指向了非常量
const int *b = &ci;//指向常量的指针,指向了常量
i = 10;

指针的类别必须与其所指对象的类型一致,但有两种例外情况(本文将讲第一种):指向常量的指针,指向了非常量对象。

  1. 如上两个指针都是指向常量的指针(pointer to const)。
  2. 如上代码const int *a = &i,可以把指向常量的指针,指向非常量对象。
  3. 如上代码const int *b = &ci,可以把指向常量的指针,指向常量对象,这是正常操作。
  4. 如上四个量,只有i可以进行赋值操作,因为只有它是非常量。换句话说,不能通过指向常量的指针a来赋值,但由于a指向的是非常量i,所以i可以进行赋值操作。

常量引用,和指向常量的指针一样,虽然字面上都是说的指向常量,但没有规定其所指对象必须是一个常量。只是因为它们认为自己指向的是常量,所以不能通过常量引用或指向常量的指针,来改变指向的对象的值。但如果指向的对象是非常量,那么这个非常量本身进行赋值操作就是正常操作。

三、const与指针:常量指针(const point)

指针是对象,因此可以把指针本身定为常量。常量指针必须初始化,初始化完成后,它的值(指针存的地址)就不能改变了。

int a = 0;//非常量
int *const ai = &a;//常量指针,指向了非常量

const double pi = 3.14;//常量
const double *const api = π//常量指针,指向了常量

int * const * aii = &ai;//常量指针的二级指针
const double * const * aapi = &api;//常量指针的二级指针

如果只是对常量指针解引用,那么解引用后得到指针指向的对象,根据指向对象为常量或者非常量,来决定常量指针解引用后可不可以赋值操作:

*ai = 1;//可运行
*api = 3.0;//报错,表达式必须是可修改的左值

对常量指针赋值,直接报错:

ai = 0;//报错
api = 0;//报错

读法是从右往走读,const修饰const左边那个星号,若const左边没有星号,那么就是指的最底层的对象。

在这里插入图片描述

四、顶层const和底层const

对于一般对象来说,其实只有顶层const。而对于指针这种,本身是一个对象,又指向一个对象。所以,指针本身是不是常量,和指针指向对象是不是常量,是两个独立的问题。
用顶层top-level const表示指针本身是一个常量,用底层low-level const表示指针指向对象是一个常量。

1、引用

用于声明引用的const都是底层const。因为引用本身不是对象,所以不可能有顶层const。

int i = 0;//非常量int对象
const int ci = 0;//常量int对象

const int &a = i;//指向常量的引用(一般称为常量引用),绑定到非常量
const int &b = ci;//指向常量的引用,绑定到常量

2、指针

*const代表的是顶层const,指针存的地址不能改变。
const int代表的是底层const,指针指向一个常量,常量自然不能改变

int i = 0;
int *const p1 = &i;
//不能改变p1指针存的地址,顶层const
const int ci = 42;
//常量不能改变,也算是顶层const
const int *p2 = &ci;
//p2存的地址可以改变,但p2解引用后得到const int,不能改变,底层const
const int *const p3 = p2;
//分析p3类型,*const说明是顶层const,const int说明是底层const
p2 = p3;

在执行对象的拷贝操作时,顶层const不会受影响:

  1. const int *const p3 = p2中,p2没有顶层const,p3有顶层const。
  2. p2 = p3中,执行前,p2没有顶层const,p3有顶层const,执行后,也是一样。

在执行对象的拷贝操作时,两个对象必须具有相同的底层const资格,或者能够转换(一般来说,非常量可以转换成常量,反之不行):

  1. int *p = p2; int *p = p3;这两句都会报错,不管p2 p3是不是有顶层const,在拷贝时,都会只看成const int *,但是由于从“const int *”转换到“int *”是不可以的(常量不可以转换为非常量),所以报错。
  2. p2 = p3可运行,p2 p3都是底层const,所以符合“两个对象必须具有相同的底层const资格”。虽然p3还是个常量指针。
  3. p2 = &i可运行,&i后得到一个普通指针int *,可以从“int *”转换到“const int *”,符合“非常量可以转换成常量”。

3、constexpr

constexpr即为const expression常量表达式。用于声明constexpr的类型一般为字面值类型literal type。指针、引用也属于字面值类型,但指针和引用定义成constexpr时,它们的初始值必须受到限制。
一个constexpr指针的初始值必须是一个存储在固定地址的对象,或者是nullptr或0。
一般来说,函数体内定义的变量不是存放在固定地址中的。但函数也允许定义一类有效范围超过函数自身的变量,这类变量也有固定地址。一个constexpr指针只能指向这些变量。

constexpr设置变量本身为顶层const:相当于在星号后面加个const。

const int *p = nullptr;
constexpr int *q = nullptr;
constexpr const int *k = nullptr;

在这里插入图片描述
原文链接:https://blog.csdn.net/anlian523/article/details/82949615

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值