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
相关推荐
天天摸鱼的java工程师18 分钟前
Java 解析 JSON 文件:八年老开发的实战总结(从业务到代码)
java·后端·面试
白仑色19 分钟前
Spring Boot 全局异常处理
java·spring boot·后端·全局异常处理·统一返回格式
喵手26 分钟前
反射机制:你真的了解它的“能力”吗?
java·后端·java ee
kaika136 分钟前
告别复杂配置!使用 1Panel 运行环境功能轻松搭建 Java 应用
java·1panel·建站·halo
有梦想的攻城狮44 分钟前
Java 11中的Collections类详解
java·windows·python·java11·collections
忒可君1 小时前
C# winform FTP功能
开发语言·windows·c#
六千江山1 小时前
从字符串中提取符合规则的汽车车牌
java
33255_40857_280591 小时前
从韩立结婴看Java进阶:一个10年老码农的修仙式成长指南
java
赵星星5201 小时前
透彻理解Java中的深拷贝与浅拷贝:从误区到最佳实践
java·后端