目錄
- 🚀前言
- 💻const:“只讀”的守護者
- 💯修飾普通變量
- 💯修飾指針
- 💯修飾函數
- 💯修飾類成員
- 💯修飾對象
- 🌟static:“靜態存儲”與“作用域控制”
- 💯修飾全局變量
- 💯修飾局部變量
- 💯修飾類成員
- 🎯static 成員變量
- 🎯static成員函數
- 💧const 與 static 結合應用
- 💯關鍵用法:類內靜態常量的初始化
- 🎋避坑指南
- ??結尾總結
🚀前言
大家好!我是 EnigmaCoder。
- 本文將分別介紹const與static的用法,最后介紹兩者的結合應用和常見坑點。
💻const:“只讀”的守護者
const
的核心作用是限制變量/對象的“可修改性”,強制編譯器檢查“意外修改”,本質是給代碼加“安全鎖”。它的用法圍繞“修飾誰”展開,不同修飾對象的含義完全不同,也是初學者最易踩坑的點。
💯修飾普通變量
- 作用:變量初始化后不可修改,編譯期會檢查賦值操作。
- 注意:必須在定義時初始化(局部/全局
const
均需)。 - 示例:
const int max_len = 10; // 正確:定義時初始化 max_len = 20; // 錯誤:編譯器直接報錯,禁止修改
💯修飾指針
const
修飾指針關鍵看 const
和 *
的位置:
const int* p
(const
在*
左邊):限制“指針指向的內容”不可改,指針本身可以換指向。int a = 5, b = 10; const int* p = &a; *p = 6; // 錯誤:指向的內容(a的值)不能改 p = &b; // 正確:指針可以指向新地址(b)
int* const p
(const
在*
右邊):限制“指針本身”不可改,指向的內容可以改。int a = 5, b = 10; int* const p = &a; *p = 6; // 正確:指向的內容(a的值)可以改 p = &b; // 錯誤:指針不能換指向
💯修飾函數
- 修飾函數參數:保證函數內部不修改參數(尤其針對引用/指針參數,避免意外篡改外部變量)。
// 傳入字符串,僅讀取不修改,用 const 保護 void printStr(const string& s) {s += "test"; // 錯誤:禁止修改 const 參數cout << s << endl; // 正確:僅讀取 }
- 修飾函數返回值:限制返回值不可被修改(常見于返回指針/引用的場景,避免外部篡改內部數據)。
// 返回數組首地址,禁止外部修改數組內容 const int* getArr() {static int arr[3] = {1,2,3};return arr; } int main() {const int* p = getArr();*p = 4; // 錯誤:返回值是 const,禁止修改 }
💯修飾類成員
const
成員變量:屬于對象的“常量”,必須在構造函數初始化列表中初始化(不能在定義時直接賦值)。class Person { private:const int age; // const 成員變量 public:// 正確:在初始化列表中初始化Person(int a) : age(a) {}// 錯誤:不能在構造函數體內賦值// Person(int a) { age = a; } };
const
成員函數:保證函數內部不修改任何非mutable
成員變量,函數聲明和定義都要加const
。class Person { private:int age; public:// 聲明時加 constvoid showAge() const; //語法:返回值類型 函數名() const; }; // 定義時也要加 const(位置不能錯) void Person::showAge() const {age = 20; // 錯誤:const 函數禁止修改成員變量cout << age << endl; // 正確:僅讀取 }
💯修飾對象
const
修飾對象,本質是把對象變成 “只讀對象”—— 一旦定義,這個對象的成員變量就不能被修改,能有效保證數據安全(比如防止誤改關鍵數據)。
- 格式很簡單:在對象定義前加
const
,和定義const
變量(比如const int a=10
)的邏輯一致。
#include <iostream>
#include <string>
using namespace std;// 定義一個學生類
class Student {
public:string name; // 成員變量:姓名int score; // 成員變量:成績// 構造函數(初始化姓名和成績)Student(string n, int s) : name(n), score(s) {}// 普通成員函數:修改成績(可能改成員變量)void setScore(int s) {score = s;}// const 成員函數:只打印信息(不修改成員變量)void showInfo() const {cout << "姓名:" << name << ",成績:" << score << endl;}
};
int main() {// 1. 普通對象(可以修改成員,調用任意函數)Student s1("張三", 90); s1.score = 85; // 允許:普通對象能改成員變量s1.setScore(88); // 允許:普通對象能調用非const函數s1.showInfo(); // 允許:普通對象也能調用const函數// 2. const對象(只讀,核心限制:不能改成員,只能調const函數)const Student s2("李四", 85); // s2.score = 90; // 錯誤!const對象不能直接修改成員變量// s2.setScore(92); // 錯誤!const對象不能調用非const函數s2.showInfo(); // 允許!const對象只能調用const成員函數return 0;
}
遵循兩個規則:
const
對象的成員變量絕對不能修改。const
對象只能調用const
成員函數。
🌟static:“靜態存儲”與“作用域控制”
static
的核心是改變變量/函數的“存儲周期”或“作用域”,本質是管理“數據的生命周期”和“訪問范圍”,常見于“共享數據”“全局唯一”場景。
💯修飾全局變量
- 作用:全局
static
變量的作用域僅限當前.cpp
文件,避免不同文件中“同名全局變量”的命名沖突。 - 對比普通全局變量:普通全局變量默認“跨文件可見”(用
extern
可引用),static
全局變量則“文件私有”。 - 示例:
// a.cpp static int global_val = 5; // 僅 a.cpp 可見// b.cpp extern int global_val; // 錯誤:無法引用 a.cpp 的 static 全局變量
💯修飾局部變量
- 作用:局部
static
變量的“作用域”仍在函數內,但“存儲周期”是整個程序(僅初始化一次,函數調用結束后不銷毀)。 - 典型場景:函數內的計數器、全局唯一的臨時數據。
- 示例:
void countCall() {static int cnt = 0; // 僅初始化一次(第一次調用時)cnt++;cout << "調用次數:" << cnt << endl; } int main() {countCall(); // 輸出 1countCall(); // 輸出 2(cnt 未被銷毀) }
💯修飾類成員
static
類成員是“所有對象共享的數據/方法”,不依賴具體對象存在。
🎯static 成員變量
- 特點:屬于整個類,所有對象共用同一內存,必須在類外單獨初始化(不能在構造函數中初始化)。由于static成員變量存儲在類的外部,計算類的大小時不包含在內。
- 示例:統計類的對象創建個數
class Student { private:static int total; // 聲明:屬于 Student 類 public:Student() { total++; } // 創建對象時計數+1~Student() { total--; } // 銷毀對象時計數-1// 必須用 static 函數訪問 static 變量(見下文)static int getTotal() { return total; } };// 類外初始化:不加 static,需指定類名 int Student::total = 0;int main() {Student s1, s2;cout << Student::getTotal(); // 輸出 2(兩個對象) }
🎯static成員函數
- 特點:沒有
this
指針(因為不依賴對象),只能訪問static
成員變量/函數,不能訪問非static
成員。 - 調用方式:直接用“類名::函數名”調用(無需創建對象)。
- 示例:上文
Student::getTotal()
,直接通過類名調用。
💧const 與 static 結合應用
兩者結合的核心場景是“類的靜態常量”,即 static const
(或 const static
,兩者在類中等價),用于定義“類級別的常量”(所有對象共用,且不可修改),既實現了數據共享又達到了數據不可被改變的目的。
💯關鍵用法:類內靜態常量的初始化
-
C++11 及以后:
static const
成員變量可在類內直接初始化(僅限字面量類型,如 int、char 等)。 -
示例:
class Config { public:// C++11+ 支持:類內直接初始化靜態常量static const int MAX_NUM = 100;static const string DEFAULT_NAME; // 非字面量類型,需類外初始化 };// 非字面量類型的 static const 成員,仍需類外初始化 const string Config::DEFAULT_NAME = "unknown";
-
注意:C++11 前,
static const
成員變量必須在類外初始化,即使是字面量類型。
🎋避坑指南
-
坑 1:const 變量能“強制修改”?
用指針強制轉換可以修改const
變量,但這是“未定義行為”(編譯器不報錯,但運行結果不可控,可能觸發崩潰),絕對禁止!const int a = 5; int* p = (int*)&a; // 強制轉換(危險!) *p = 10; // 運行時可能修改成功,但屬于未定義行為
-
坑 2:static 局部變量的線程安全?
- C++11 及以后:
static
局部變量的初始化是“線程安全”的(編譯器會加鎖,避免多線程同時初始化)。 - C++11 前:非線程安全,多線程同時調用可能導致重復初始化。
- C++11 及以后:
-
坑 3:類 static 成員忘記類外初始化?
僅在類內聲明static
成員變量,未在類外初始化,會導致鏈接錯誤(編譯器找不到變量的定義)。記住:static
類成員“聲明在類內,定義在類外”。
??結尾總結
const
和 static
雖基礎,但用法靈活,核心記住兩點:
- const:圍繞“只讀”,為代碼加安全鎖,避免意外修改(重點分清修飾指針的兩種場景、類 const 成員的初始化)。
- static:圍繞“靜態存儲/作用域”,管理數據生命周期(重點掌握類 static 成員的“類外初始化”和“共享特性”)。