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
相关推荐
咖啡教室2 小时前
java日常开发笔记和开发问题记录
java
咖啡教室2 小时前
java练习项目记录笔记
java
鱼樱前端3 小时前
maven的基础安装和使用--mac/window版本
java·后端
RainbowSea3 小时前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq
RainbowSea3 小时前
5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
java·消息队列·rabbitmq
我不会编程5555 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
李少兄5 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
此木|西贝5 小时前
【设计模式】原型模式
java·设计模式·原型模式
可乐加.糖5 小时前
一篇关于Netty相关的梳理总结
java·后端·网络协议·netty·信息与通信
懒羊羊大王&5 小时前
模版进阶(沉淀中)
c++