普通成員變數屬於各個物件,每個物件擁有獨立副本。若需多物件共享單一資料(如計數器、全域設定、資源池),必須使用 static 成員變數。
Student::m_total 或 stu.m_total 效果相同,但推薦前者語義更清晰。class Student {
public:
Student(const char* name, int age, float score);
private:
static int m_total; // 僅宣告
const char* m_name;
int m_age;
float m_score;
};
// 類外定義並初始化(不可加 static,必須指定型別)
int Student::m_total = 0;
關鍵點:若未在類外初始化,連結器將報「未定義的參考」錯誤。private/protected 同樣需在類外初始化,存取權限僅限制存取方式,不影響定義義務。
#include <iostream>
using namespace std;
class Student {
public:
Student(const char* name, int age, float score);
void show() const;
private:
static int m_total; // 靜態成員變數
const char* m_name;
int m_age;
float m_score;
};
int Student::m_total = 0; // 類外初始化
Student::Student(const char* name, int age, float score)
: m_name(name), m_age(age), m_score(score) {
++m_total; // 每建構一個物件,計數器加一
}
void Student::show() const {
cout << m_name << " 年齡 " << m_age
<< " 成績 " << m_score
<< "(當前共有 " << m_total << " 名學生)" << endl;
}
int main() {
(new Student("小明", 15, 90))->show(); // 1
(new Student("李磊", 16, 80))->show(); // 2
(new Student("張華", 16, 99))->show(); // 3
(new Student("王康", 14, 60))->show(); // 4
return 0;
}
一個類別可擁有多個 static 成員變數,所有物件共享這組資料集合。
靜態成員變數在 main() 執行前已存在,main() 結束後才銷毀。即使從未建立任何物件,亦可透過類別名存取:
Student::m_total = 10; // 未建立任何 Student 物件即可操作
若類外初始化不指定值(int Student::m_total;),C++ 保證靜態儲存區變數預設為 0(整數類型),這與區域變數「未初始化值不定」形成鮮明對比。
private static 成員僅能在類別內部或 friend 函數中存取,類外須透過公開靜態成員函數間接操作。
| 錯誤寫法 | 正確寫法 | 說明 |
|---|---|---|
static int m_total = 0; 在類內初始化 | 類內僅宣告 static int m_total;,類外 int Student::m_total = 0; | C++98/03 僅允許 const static 整數類型類內初始化,C++11 起允許更多情況,但類外定義仍是通用安全做法 |
定義時保留 static | 定義移除 static | static 僅用於類內宣告 |
| 以為每個物件有獨立副本 | 理解為類別級共享單一實體 | 記憶體佈局驗證:sizeof(Student) 不包含靜態成員大小 |
靜態成員變數實現「類別級共享狀態」,是計數器、快取、單例、配置參數等場景的標準解法。掌握「類內宣告、類外定義、全域生命週期、共享語義」四大要點,即可靈活運用。