0%

C++中的顶层const和底层const

​ const对象一旦创建后其值就不能再改变。而对于指针这种本身是一个对象,它又可以指向另一个对象的情况时,指针本身是不是常量以及指针所指的是不是一个常量就是两个相互独立的问题了,用名词顶层const(top-level const)表示指针本身是个常量,而用底层const(low-level const)表示指针所指的对象是一个常量。

top-level const 的一些演示

1
2
3
4
5
6
7
//初始化两个非常量
int i1 = 20;
int i2 = 30;
//初始化一个顶层const指针,这个操作意味着指针本身的值是一个常量,其本身是不可以改变的,而指针所指的值是一个非常量是可以改变的
int *const p = &i1;
p = &i2; //错误。指针p本身是一个常量,是不可以改变的
*p = i2; //正确。指针所指的值并不是一个常量,是可以改变的

low-level const 的一些演示

1
2
3
4
5
6
7
//初始化两个非常量
int i1 = 20;
int i2 = 30;
//初始化一个底层const指针,这个操作意味着这个指针指向一个常量,不可以通过该指针去改变指针所指的对象,但指针本身是可以更改的
const int *p = &i1;
*p = i2; //错误。指针p所指的是一个常量,不可以通过指针去修改其指向的对象
p = &i2; //正确。指针本身不是常量,可以修改指针本身

​ 上面的展示应该很清楚地展示了顶层const和底层const的区别所在,当然我们也可以将一个指针声明为既是底层cosnt亦是顶层const,这样我们就不可以通过指针去修改指针所指的对象,也不可以修改指针本身,其声明方式如下。

1
2
3
4
5
6
7
//初始化两个非常量
int i1 = 20;
int i2 = 30;
//初始化一个既是底层const也是顶层const的指针
const int *const p = &i1;
*p = i2; //错误。指针p是一个底层const,其所指的对象是一个常量。
p = &i2; //错误。指针p是一个顶层const,其本身是一个常量。

所谓的指向常量的指针(pointer to const)只是指针自己以为自己指向的是常量

​ 为什么上面的例子都是初始化两个非常量呢?因为其实对于指向常量的指针来说(意思是底层const),只是指针自以为自己指向的是一个常量,所以指针自觉地不去改变所指的对象。但其实指向常量的指针没有规定其所指的对象必须是一个常量。

1
2
3
4
5
6
7
8
//初始化两个非常量
int i1 = 20;
int i2 = 30;
//初始化一个底层const指针,即指向常量的指针
const int *p = &i1;
*p = i2; //错误。指针认为自己指向的是常量,不能通过自己去改变所指的对象
i1 = i2; //正确。指向常量的指针没有规定所指的对象是一个常量,所以可以直接修改非常量对象本身
std::cout << *p; //将会打印"30",而不是"20"

指针和constexpr

​ C++11中,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。但必须明确一点,在constexpr声明中如果定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。意思就是通过constexpr定义的指针是一个顶层const。

1
2
3
4
constexpr int i = 20;	//定义一个常量
int j = 100; //定义一个非常量
constexpr int *p = &i; //错误。p本身是一个常量,但p所指的不是一个常量,所以p不能指向一个常量整数
constexpr int *p = &j; //正确。p本身是一个常量指针,指向一个整数