C++ 底层const&顶层const与引用

先从const在C与C++的区别说起,
区别1:
C的const定义了一个const常量, 该变量只具备读的功能, 而不具备写的功能。
C++的const定义了一个常量。

1
2
3
const int a = 5;
int array[a];//在C语言中是错误的,因为在C语言中是定义了一个只读变量
int array[a];//在c++中是正确的,因为在C++中定义了一个常量

区别2:
C不能定义const函数。
C++可以定义const函数, const成员函数不能修改类数据成员变量的值
(via: http://blog.csdn.net/u010889616/article/details/47265109 )

0x00 底层const&顶层const的概念:

这两个词汇大多是针对指针而进行区别的, 因为指针具有其他一般变量不同的特点:
一般局部变量:在栈内开辟一块空间, 并在此位置上有一个值;
声明为局部的, 某一变量类型的指针:同样在栈内开辟一块空间, 并在此位置上有一个值, 同时这个值初始化后为某一变量的地址。

从语法上来说, 指针比其他变量类型多了一个使用手法, 那就是可以解引用来访问地址上的值。
所以, 用const来限定指针的话, 在不明确说明的情况下, 我们不知道这个const限定的到底是指针本身的值, 还是值解引用后的值, 这才有了底层和顶层之分。

底层const:
无法改变指针解引用后的值

1
2
3
4
5
int num_a = 3;
int num_b = 4;
int const * ptr_num = &num_a; // 底层const, const在*左边
// *ptr_num = 7 // Error, 无法改变指针解引用后的值
ptr_num = &num_b; // 允许, 改变的是指针自身的值

顶层const:
无法改变指针自身的值

1
2
3
4
5
int num_a = 3;
int num_b = 4;
int * const ptr_num = &num_a; // 顶层const, const在*右边
// ptr_num = &num_b; // Error, 无法改变指针自身的值
*ptr_num = 7; // 允许, 改变的是指针解引用后的值

我们可以发现, 指针的顶层const和其他一般变量的const限定效果是一样的, 都是无法改变自身的值, 同时必须需要初始值。

0x01 底层const&顶层const的操作差异

0x01. 00
底层指针只能可以初始化或赋值给底层指针, 顶层指针可以初始化任何指针, 但不能赋值给顶层指针。
eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int num_a = 3;
int num_b = 4;
int const * ptr_num1 = &num_a;
int * const ptr_num2 = &num_b;
//ptr_num3为无const限定指针
// int * ptr_num3 = ptr_num1; // Error, ptr_num3非底层
int * ptr_num3 = ptr_num2;
// ptr_num4为底层const指针
int const * ptr_num4 = ptr_num1; // 允许, ptr_num4为底层
ptr_num4 = ptr_num2;
// ptr_num5为顶层const指针
// int *const ptr_num5 = ptr_num1; // Error, ptr_num5非底层
int *const ptr_num5 = ptr_num2;

为什么会这样? 假设, 我们可以把底层指针初始化顶层指针, 那么顶层指针拥有底层指针的值, 顶层指针的解引用不受限制, 也就可以改变解引用后的值, 这便破坏了底层指针的const限制。

0x02 底层const&顶层const与引用

不少文章都提到, 只有指针才有底层const, 我认为这是不太准确的。
引用是c++新加入的变量类型, 引用类型必须要初始化, 并且初始化就绑定, 无法再改变。
不难发现, 这和顶层const指针是非常相似的。

1
2
3
int num_a = 3;
int &ref_num = num_a; // 引用的初始化
int *const ptr_num = &num_a; // 顶层指针的初始化

引用的赋值

1
ref_num = 4; // 可以改变值, 但会发现此时num_a被赋值为4, 说明ref_num没有与num_a解绑

顶层指针的解引用赋值

1
*ptr_num = 4; // 可以改变解引用后的值, 此时num_a被赋值为4

由此, 我们推断引用是被包装过的顶层指针。

那么const引用其实就是底层+顶层指针
在引用层面, 它无法被赋值, 也因为引用自身的类型导致无法解绑;
在指针层面, 它无法被解引用后赋值, 也无法改变自身的值。