const 引入的歷史動機:替代 #define 的缺陷
| #define 缺陷 | const 解決方案 |
| 單純文本替換,無類型檢查 | 編譯器類型檢查,消除隱患 |
| 無作用域,全局污染 | 遵循作用域規則 (塊/類/命名空間) |
| 調試器不可見 (符號表無條目) | 進入符號表,調試可見 |
| 無法用於複雜類型 (類/結構體) | 支援任意類型 const 對象 |
**核心共同優勢**:避免魔術數字、便於參數調整、**編譯期常量無存儲開銷** (關鍵)。
編譯器處理 const 的三階段策略
- 編譯期常量傳播:普通 `const int x = 10;` → **不分配存儲**,值直接嵌入指令流 (立即數)、符號表保留類型/值信息,**等同 #define 效率**
- 強制分配存儲:以下情況編譯器**必須分配內存**:
- 取地址操作:`const int* p = &x;`
- 外部連接:`extern const int x;` (跨編譯單元)
- 引用綁定:`const int& r = x;` (某些 ABI 要求)
- 類靜態常量成員 (C++17 前需定義):`static const int N = 10;` + `.cpp` 中 `const int A::N;`
- 大型對象/非字面類型
- 只讀數據段保護:分配存儲時,放入 `.rdata` (Windows) / `.rodata` (ELF) **只讀段**,硬件頁保護防寫入,**運行時違規觸發 SIGSEGV/Access Violation**
const 與指針/引用的組合語義
| 聲明 | 含義 | 頂層/底層 const |
| `const int* p` / `int const* p` | 指向常量的指針 (底層 const) | 底層:指向的對象不可修改 |
| `int* const p` | 常量指針 (頂層 const) | 頂層:指針本身不可修改 |
| `const int* const p` | 指向常量的常量指針 | 頂層 + 底層 |
| `const int& r` | 常量引用 (綁定臨時對象、延長生命週期) | 引用本身無頂層 const 概念 |
函數返回值 const 陷阱
- `const int foo()` → **無意義**,返回值為右值,賦值給 `int` 即拷貝,const 被忽略
- `const MyClass foo()` → **有意義**,防止 `foo().mutating_method()` 或 `foo() = other` (若賦值運算子非 const 引用)
- **現代建議**:返回值**不加 const**,移動語義 (C++11) 已消除拷貝開銷,const 反而阻礙移動
constexpr (C++11) vs const
| 特性 | const | constexpr |
| 編譯期求值保證 | 否 (視編譯器優化) | 是 (強制) |
| 可用於模板參數/數組大小 | 否 (C++11 前) | 是 |
| 函數/構造函數 | 不可 | 可 (字面類型、單一 return) |
| 運行期對象 | 可 | 不可 (必須編譯期確定) |
**原則**:能用 `constexpr` 盡量用,表達意圖更明確,啟用更強優化。
工程最佳實踐
- 常量定義優先 constexpr:`constexpr int MAX_CONN = 1024;`
2. 函數參數用 const 引用:`void func(const BigObj& o)` 避免拷貝、表達不修改意圖
- 成員函數標註 const:`int get() const` 允許常量對象調用、助編譯器優化
- 避免 const_cast:除非對接無 const 正確性的舊 API,且確知對象非常量
- 編譯器優化信任**:現代編譯器 (GCC/Clang/MSVC) 對 const 傳播、死代碼消除、只讀段保護極其激進,**無需手工微優化**
結語
const 從「替代 #define」演進為 **C++ 類型系統核心基石**,編譯器通過 **編譯期傳播、按需分配存儲、只讀段硬件保護** 三重機制,實現 **零開銷抽象 + 靜態安全 + 運行時保護**。現代 C++ 以 `constexpr` 強化編譯期計算,以 `const` 正確性貫穿接口設計,**信任編譯器優化,專注表達意圖**,是駕馭 const 的最高境界。