C++顶层const和底层const

《C++primer》中写到:顶层const表示指针本身是个常量,底层const表示指针所指的对象是一个常量。

顶层const可以表示任意的对象是常量,这一点对任何数据类型都适用。底层const则与指针和引用等复合类型的基本类型部分有关,比较特殊的是,指针类型既可以是顶层const也可以是底层const

cpp 复制代码
int i = 0;
int *const p1 = &i; // 顶层const
const int ci = 42; // 顶层const
const int *p2 = &ci; // 底层const
const int *const p3 = p2; // 靠右的const是顶层const,靠左的const是底层const
const int &r = ci;        // 用于声明引用的const都是底层const

感觉还是好难理解,那么,如何分辨顶层const和底层const呢?

顶层const是施加在类型本身上的const。

cpp 复制代码
const int a;  // const施加在类型本身int上,所以是顶层const
const int *p;  // const施加在int上,而没有施加在类型本身int *上,所以不是顶层const
int *const p;  // const施加在类型本身int *上,所以是顶层const
const int &ref;  // const施加在int上,而没有施加在类型本身int &上(当然也不可能),所以不是顶层const
const int arr[5];  // const施加在数组的元素类型int上,而没有施加在数组类型int[5]本身,所以不是顶层const
// 这里补充一句,如果对数组类型使用cv修饰符,则cv修饰符修饰的是数组的元素类型(C++20标准9.3.4.5-5)

"以*****分界,把一个声明从右向左 读"

注意语法,* 读作 pointer to (指向...的指针),const (常量) 是形容词,char (变量类型) 和 p (变量名) ,只要这样读,哪怕是初中生也分得清了

  1. const char * p 读作:p is a pointer to a const char,译:p 是一个指针(变量),它指向一个常量字符(const char)
  2. char * const p 读作:p is a const pointer to a char,译:p 是一个常量指针(const p),它指向一个字符(变量)。
cpp 复制代码
const int v2 = 0; // 顶层const
const int *p2 = &v2, *const p3 = &i, &r2 = v2;
// 分解一下
const int *p2 = &v2; // p2指向的内容不可变,顶层const
const int * const p3 = &i; // p3指向的内容和p3本身都不可变,既是顶层const也是底层const
const int &r2 = v2; // r2是一个常引用,它的值不可变,是一个顶层const
// int v1 = v2;
// int *p1 = &v1;
// const int *p2 = &v2;
p1 = p2;  // 非法
          // p2是一个底层const,它指向的内容不可变,强约束
          // p1是个普通整型指针,属松散约束
          // 从强约束到宽约束的转换是不允许的
// 编译器报错
// error C2440: '=': cannot convert from 'const int *' to 'int *'
p1 = p3; // 用p1接收p2都已经非法了,何况p3既是底层const也是顶层const
p2 = p1; // 合法,p2是底层const,指针p2本身是可以变化的
p2 = p3; // 合法
         // p3是双const约束,内容和指针本身都不可以变
         // p2是底层const,它控制的是指向的内容的只读性
         // 用p2接收p3, 各司其职,非常丝滑

为什么要区分顶层const和底层const呢,有两个作用:

  1. 执行对象拷贝时有限制:常量的底层const不能赋值给非常量的底层const。
    也就是说,区分顶层const和底层const可以避免赋值错误
cpp 复制代码
int num_c = 3;
const int *p_c = &num_c; // p_c为底层const的指针
// int *p_d = p_c;  // 错误,不能将底层const指针赋值给非顶层const指针
const int *p_d = p_c; // 正确,可以将底层const指针复制给底层const指针
  1. 使用命名的强制类型转换函数const_cast时,需要能够分辨底层const和顶层const,因为const_cast只能改变运算对象的底层cosnt
cpp 复制代码
int num_e = 4;
const int *p_e = &num_e;
// *p_e = 5;  // 错误,不能改变底层const指针指向的内容
int *p_f = const_cast<int *>(p_e);  // 正确,const_cast可以改变运算对象的底层const。但是使用时一定要知道num_e不是const的类型。
*p_f = 5;  // 正确,非顶层const指针可以改变指向的内容
cout << num_e;  // 输出5
相关推荐
小冉在学习9 分钟前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论
y5236489 分钟前
Javascript监控元素样式变化
开发语言·javascript·ecmascript
IT技术分享社区40 分钟前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
极客代码43 分钟前
【Python TensorFlow】入门到精通
开发语言·人工智能·python·深度学习·tensorflow
疯一样的码农1 小时前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
代码之光_19801 小时前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi1 小时前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
&岁月不待人&1 小时前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove1 小时前
G1垃圾回收器日志详解
java·开发语言
对许1 小时前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j