`const` 是 C++ 型別系統的核心,遠不止「定常量」。正確使用 `const` 能讓編譯器捕獲邏輯錯誤、優化代碼、表達設計意圖、支援多執行緒安全。本文系統整理 8 大基本規則、指針/引用/成員函數/模板中的進階用法、常見誤區與最佳實踐。
| # | 規則 | 正確示例 | 錯誤示例 |
|---|---|---|---|
| 1 | 修飾變量為唯讀,必須初始化 | `const int MAX = 100;` | `const int x; // 錯誤` |
| 2 | 取代 `#define`,擁有型別與作用域 | `const double PI = 3.14159;` | `#define PI 3.14159` |
| 3 | 跨文件共享需 `extern` 聲明,定義處初始化 | h: `extern const int N;` cpp: `const int N = 10;` | h: `extern const int N = 10;` |
| 4 | 編譯器執行型別檢查,避免隱式縮窄 | `const long L = 10; char c = L; // 警告` | `#define L 10; char c = L; // 無警告` |
| 5 | 字串常量僅一份拷貝,節省記憶體 | `const char* s = "hello";` | `#define S "hello"` 每處展開一份 |
| 6 | 可用函數返回值初始化(運行期常量) | `const int sz = computeSize();` | 無 |
| 7 | 可通過強制轉型修改(未定義行為,勿用) | `const int i=0; *(int*)&i = 1; // UB` | 視為只讀優化依據 |
| 8 | 區分「頂層 const」與「底層 const」 | 見下節 | 混淆導致賦值錯誤 |
int i = 0;
const int ci = 10;
const int *p1 = &ci; // 底層 const:指向常量的指針,可改指向,不可改值
int *const p2 = &i; // 頂層 const:常量指針,不可改指向,可改值
const int *const p3 = &ci; // 雙重 const
// 判斷口訣:從右向左讀
// const int *p → "p is a pointer to const int"
// int *const p → "p is a const pointer to int"
賦值相容性:`const int*` 可接收 `int*`(加強限制),`int*const` 不可接收 `int*`(頂層 const 不影響指向對象權限)。
class Buffer {
char *data;
size_t len;
public:
// 承諾不修改對象可見狀態
size_t size() const { return len; }
char operator[](size_t i) const { return data[i]; } // 唯讀版本
char& operator[](size_t i) { return data[i]; } // 讀寫版本
// mutable 成員可在 const 函數中修改(緩存、鎖、統計計數)
mutable int accessCount;
char get(size_t i) const { ++accessCount; return data[i]; }
};
規則:`const` 對象只能調用 `const` 成員函數;`const` 成員函數不可修改非 `mutable` 成員。
template
void func(const T& param); // 推導 T 時保留 const 屬性
// C++17 std::as_const:將左值轉為 const 右值引用
#include <utility>
void legacyAPI(Widget&); // 非 const 參數
legacyAPI(std::as_const(widget)); // 編譯錯誤,防止誤調
| 特性 | const | constexpr |
|---|---|---|
| 求值時機 | 運行期或編譯期 | 強制編譯期 |
| 函數 | 不可 | 可(C++14 放寬限制) |
| 適用對象 | 任何變量 | 字面類型 |
| 用作模板參數/陣列大小 | 不可 | 可 |
**原則**:能 `constexpr` 就 `constexpr`,將計算前移編譯期。