目录
- 🚀前言
- 💻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 成员的“类外初始化”和“共享特性”)。