顶层 const 和底层 const

简单说,顶层const是指"指针本身是常量",而底层const是指"指针指向的对象是常量"。

底层是常量的指针,本身不可改变。(常量指针) 顶层是指针的常量,指向不可改变。(指针常量)

从 const 指针开始说起。const int* pInt; 和 int *const pInt = &someInt;,前者是 *pInt 不能改变,而后者是 pInt 不能改变。因此指针本身是不是常量和指针所指向的对象是不是常量就是两个互相独立的问题。用顶层表示指针本身是个常量,底层表示指针所指向的对象是个常量。

更一般的,顶层 const 可以表示任意的对象是常量,这一点对任何数据类型都适用;底层 const 则与指针和引用等复合类型有关,比较特殊的是,指针类型既可以是顶层 const 也可以是底层 const 或者二者兼备

const char * pchar * const p 两种声明的意思分别应该是:

  • p 是一个指向常量字符的指针,不变的是 char 的值,即该字符的值在定义时初始化后就不能再改变。
  • p 是一个指向字符的常量指针,不变的是 p 的值,即该指针不能再指向别的。

一个比较好的记忆方法: "以*分界,把一个声明从右向左读"

注意语法,* 读作 pointer to (指向…的指针),const (常量) 是形容词,char (变量类型) 和 p (变量名) ,

  • const char * p 读作:p is a pointer to a const char
    • 译:p 是一个指针(变量),它指向一个常量字符(const char)。
  • char * const p 读作:p is a const pointer to a char
    • 译:p 是一个常量指针(const p),它指向一个字符(变量)。
  • 另外const位置多变, const char * pchar const * p,首先以 * 分界,虽然 const 的位置改变了,但它都是在修饰 char,常量字符。
int i = 0;
int *const p1 = &i; //p1为顶层const,其值不能改变
const int ci = 42;  //ci为顶层const,其值不能改变
const int *p2 = &ci; //允许改变p2的值,这是一个底层const
const int *const p3 = p2; //靠右的为顶层const,靠左的为底层const
const int &r = ci; //用于声明引用的都为底层const
i = ci; //正确,ci顶层const不受影响
p2 = p3;//正确,p2和p3指向的对象都相同,且p3又是一个顶层const其本身也为常量
int *p = p3; //错误,p3包含底层const定义但是p没有
p2 = &i;//正确,int i 可以转换为常量
int &ri = ci;//错误,普通的int &不能绑定到int常量上
const int &r2 = i;//正确,const int& 可以绑定到一个普通的int上

vector<int>::iterator /*可以理解为*/ int* //代表非常量
vector<int>::const_iterator /*可以理解为*/ const int* //代表底层const
const vector<int>::iterator /*可以理解为*/ int* const //代表顶层const
const vector<int>::iterator /*可以理解为*/ const int* const //左侧的const为底层const,右侧的顶层const

const修饰规则:

const int *p1可看作是const修饰的类型是int,修饰的内容是*p1,即*p1不允许改变。 int const *p2 可以看作const修饰的类型是int,修饰的内容是*p2,即*p2不允许改变。 int *const p3 可以看作const修饰的类型是int *,修饰的内容是p3,即p3不允许改变

简单来说就是在 *号左边还是右边

参考资料