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
相关推荐
Kisorge21 分钟前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
路在脚下@1 小时前
spring boot的配置文件属性注入到类的静态属性
java·spring boot·sql
森屿Serien1 小时前
Spring Boot常用注解
java·spring boot·后端
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
苹果醋32 小时前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
晓纪同学2 小时前
QT-简单视觉框架代码
开发语言·qt
威桑2 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
Hello.Reader2 小时前
深入解析 Apache APISIX
java·apache
飞飞-躺着更舒服2 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb