摘要訊息 : 分清 const
標識符的作用和定位.
0. 前言
由於 C++ 支援多重指標, 它和 const 標識結合之後會讓人感到迷惑. 例如 const int *const *const **const i {nullptr};
這個陳述式中, 各個 const 分別標識哪一個部分?
《C++ Primer》這本書的第五版中引入了兩個概念 : 頂層 const
和底層 const
. 書中的介紹非常簡單, 頂層 const
用來標識變數, 底層 const
用來標識記憶體. 但是有了這個解釋之後, 又有多少人能夠正確回答上面這個問題呢?
為了解決這個問題, 我們重新對 const
標識符進行學習.
更新紀錄 :
- 2022 年 3 月 20 日進行第一次更新和修正.
1. 改寫宣告
拿到一個宣告陳述式 const T a
, 我們可以將其改寫為 T const a
. 這兩個宣告最終的效果是一樣的, 只是 const
和型別 T
的出現順序不一樣. 但是改寫之後, 我們能夠更佳輕鬆地識別各個 const
的作用.
例題 1. 改寫 const int *const *const **const i
.
解 :我們只需要交換第一個
const
和int
的位置, 便可以得到int const *const *const **const i
.\blacksquare
2. 從右至左
我們仍然以 const int *const *const **const i
為例, 例題 1 表明, 其改寫後為 int const *const *const **const i
. 現在我們要學習從右到左審視這個宣告. 為了方便閱讀, 我們給出以下記號 :
int const1 *const2 *const3 **const4 i
.
如果把 const
全部去掉, 那麼 i
的型別應該是 int ****
. 從右邊開始 :
const4
標識的是i
, 而i
是一個指標, 因此const4
表明了i
是不可變的. 也就是指標本身的值不能發生改變;const3
標識的是**i
. 此處, 我們將*
視為解參考運算子, 那麼**i
是記憶體中的值. 也就是記憶體中的值不能發生改變;
和const1
const2
分別標識***i
和****i
. 將*
視為解參考運算子的話,***i
和****i
都是記憶體中的值, 它們不可以發生改變.
現在我們就可以分辨這些 const
的屬性了. const4
標識的是 i
, i
是一個變數, 因此 const4
屬於頂層 const. 剩下的 const
都是標識記憶體中的值, 那麼這些 const
都是底層 const
.
如果使用 auto
來推導 i
的型別 : auto i2 {i};
, 那麼 i2
的型別應該是 const int *const *const **
. 因為在 auto
推導的過程中, 會主動忽略掉頂層 const
:
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
const int *const *const **const i {nullptr};
auto i2 {i};
cout << is_same_v<decltype(i2), const int *const *const **> << endl; // 輸出結果 : 1
}
3. 總結
現在, 我們做一個總結 : 在改寫宣告之後, 當一個 const
後面緊跟著的就是變數名稱的話, 這個 const
是用於標識這個變數的, 而且這個 const
是頂層 const
; 當 const
後面存在 *
的時候, 說明這個 const
是標識記憶體中某個值的, 屬於底層 const
.
還有一個標識符 volatile
, 它和 const
具有同等地位, 但是用到的次數卻比 const
少很多. 對於 const *volatile const *volatile **const volatile *const i
這種宣告, 也可以使用類似的方法對 volatile
進行分類.
另外, 值得一提的是, 頂層 const
的全稱應該是 top-level cv-qualifiers, 它在 C++ 20 國際標準中首次出現於 6.7.3 節中. 而底層 const
(可能叫 underlying-level cv-qualifiers 或者 low-level cv-qualifiers) 是在 C++ 20 國際標準中沒有出現的, 這個概念應該是由《C++ Primer》這本書創造的.
自創文章, 原著 : Jonny. 如若閣下需要轉發, 在已經授權的情況下請註明本文出處 :