static:一個關鍵字,三重身份
1. 控制區域變數儲存期:靜態儲存期
void Counter() {
static int cnt = 0; // 僅初始化一次,儲存在 .bss/.data 段
std::cout << ++cnt << '\n';
}
int main() { Counter(); Counter(); Counter(); } // 輸出 1 2 3
- **生命週期** = 程式全程,**作用域** = 函數內部
- 解決「函數內部狀態保持」需求,避免全域變數汙染名稱空間
- 執行緒不安全(C++11 起初始化執行緒安全,但讀寫需自行同步)
2. 控制全域變數/函數連結性:內部連結
// File1.cpp
static int g_moduleState = 0; // 僅本編譯單元可見
static void Helper() { ... } // 僅本檔案可呼叫
// File2.cpp
// extern int g_moduleState; // 連結錯誤:找不到符號
- 對應關鍵字
extern(外部連結,預設)
- 現代 C++ 偏好 **匿名命名空間** 替代:
namespace { int x; }
3. 類別靜態成員:屬於類別,不屬於物件
class Widget {
public:
static int s_count; // 宣告
static void Reset() { s_count = 0; }
private:
static const int MAX = 100; // 整數常數可類內初始化
};
int Widget::s_count = 0; // 定義(在 .cpp 中,分配儲存空間)
- 靜態資料成員:單一實體,全程存在,
Widget::s_count 存取
- 靜態成員函數:無
this 指標,僅能存取靜態成員,Widget::Reset() 呼叫
- 初始化順序:跨編譯單元靜態物件初始化順序**未定義** → 使用「構造時初始化」單例模式避免
static 總結表
| 對象 | 儲存期 | 連結性 | 作用域 |
| 區域 static 變數 | 靜態(程式全程) | 無連結 | 區塊內 |
| 全域 static 變數/函數 | 靜態 | 內部連結 | 檔案內 |
| 類別 static 成員 | 靜態 | 外部連結(需定義) | 類別域 |
const:不只是「常數」
1. 基本語義:唯讀語義
const int MAX = 100; // 編譯期常數,內部連結(C++)
const double PI = 3.14159; // 取址時才分配儲存空間
2. 指標與 const 的四種組合
const int* p1; // 指向 const int:資料不可改,指標可改
int* const p2; // const 指向 int:指標不可改,資料可改
const int* const p3; // 指向 const int 的 const 指標:雙重不可改
int const* p4; // 同 p1(const 在 * 左側修飾資料)
**口訣**:* 左邊有 const → 資料唯讀;* 右邊有 const → 指標唯讀。
3. 參數傳遞:const 參考
void Process(const BigObject& obj); // 避免複製,承諾不修改
void Process(BigObject&& obj); // C++11 右值參考,移動語義
4. 成員函數 const:語義契約
class Date {
int y, m, d;
public:
int Year() const { return y; } // 承諾不修改 *this
void SetYear(int v) { y = v; } // 非 const
// 錯誤:int Year() { return y++; } // 修改了物件
};
const 物件**只能呼叫 const 成員函數**
mutable 成員可在 const 函數中修改(快取、鎖、日誌)
5. const 與 static 交互
static const int CLASS_CONST = 42; // 類內靜態常數,C++17 起可 inline 定義
constexpr int COMPILE_TIME = 3*4; // 編譯期常數,隱含 const、static、內部連結
經典陷阱大全
| 代碼 | 問題 | 解釋 |
const int* p = &x; *p = 5; | 編譯錯誤 | 資料唯讀 |
int* const p = &x; p = &y; | 編譯錯誤 | 指標唯讀 |
char* p = "literal"; | 未定義行為 (C++11 起非法) | 字面常數區唯讀,應 const char* |
static int x = f(); | 跨檔初始化順序未定義 | 改用函數靜態區域變數單例 |
const_cast(p)->Modify(); | 若原物件真為 const → UB | 僅用於舊 API 互操作,極度謹慎 |
總結
- static:決定「活多久」(靜態儲存期)與「誰看得到」(內部連結)
- const:決定「能不能改」(唯讀語義)與「介面契約」(const 正確性)
- 現代 C++:
constexpr 取代宏常數,inline static 簡化靜態成員定義,const 參考/指標貫穿介面設計