const 是 C++ 中最重要、用途最廣的關鍵字之一。它不僅用於定義常量,更是實現「只讀語義」、提升代碼安全性、啟用編譯器優化的關鍵工具。本文系統梳理 const 的七大核心用法場景。
const double PI = 3.1415926; // 類型安全、有作用域、可調試
相比 #define PI 3.1415926,const 變量:
class Test {
private:
const int m_i; // 必須在初始化列表中初始化
public:
Test(int ii) : m_i(ii) {} // 只能在這裡初始化
void demo() const { cout << m_i * m_i << endl; }
void error() { m_i = 0; } // 編譯錯誤:不能修改 const 成員
};
關鍵點: 非靜態 const 成員**只能**在構造函數的初始化列表中初始化,不能在類內賦值,也不能在構造函數體內賦值。
class Test {
private:
static const int m_i = 100; // 類內初始化(僅限整型/枚舉型)
public:
void demo() const { cout << m_i << endl; }
};
注意: C++11 前,非整型靜態 const 成員需在類外定義;C++11 起支持類內初始化更多類型。靜態 const 成員屬於類,不佔用對象內存。
核心口訣:「看 const 在 * 的哪一邊」
| 聲明 | 含義 | 可修改指針值 | 可修改指向值 |
|---|---|---|---|
const int* p / int const* p | 指向常量的指針 | 是 | 否 |
int* const p | 常量指針(指針本身不可變) | 否 | 是 |
const int* const p | 指向常量的常量指針 | 否 | 否 |
int a = 123, b = 789;
// 1. 指向常量的指針
const int* p1 = &a; // *p1 = 456; // 錯誤
p1 = &b; // 合法
// 2. 常量指針
int* const p2 = &a; // p2 = &b; // 錯誤
*p2 = 456; // 合法,a 變為 456
// 3. 兩者結合
const int* const p3 = &a; // *p3 = 456; 錯誤;p3 = &b; 錯誤
引用本質是別名,天然適合配合 const 使用:
int a = 123;
const int& ref = a; // 只讀引用
// ref = 456; // 錯誤:不能通過 ref 修改
a = 456; // 合法:通過原變量可修改
cout << ref; // 輸出 456
const T&?
void print(const string& s) { // 高效且安全
cout << s << endl;
}
void f(const int& i) { // 保證不修改 i
// i = 12; // 編譯錯誤
cout << i * i << endl;
}
int& f(int& i) { i = 12; return i; } // 返回引用
f(a) = 33; // 合法但危險:f(a) 作為左值修改了 a
const int& f(int& i) { i = 12; return i; } // 返回 const 引用
f(a) = 33; // 編譯錯誤:禁止修改返回值
最佳實踐: 若返回引用/指針且不希望調用者修改,返回類型應加 const。
class Test {
private:
int m_i;
public:
Test(int ii) : m_i(ii) {}
void setVal(int i) { m_i = i; } // 非 const 成員函數
void echoVal() const { cout << m_i << endl; } // const 成員函數
};
const Test obj(123);
obj.echoVal(); // 合法
// obj.setVal(456); // 編譯錯誤:const 對象不能調用非 const 成員函數
在成員函數後加 const,承諾:
編譯器會強制檢查此承諾。mutable 關鍵字可豁免特定成員(如緩存、鎖)。
constconst T&constconstconst 替代 #define 定義常量掌握 const,是寫出健壯、高效、可維護 C++ 代碼的關鍵一步。