const和 static是C++编程语言中的常用关键字,对于初学者来说可能会混淆,毕竟一个代表"常量",一个代表"静态",都是静止类的词汇。
其实这两者并没有本质的联系,其中const是类型限定符,聚焦于修饰变量的 "不可修改性";
而static是存储类说明符,管控变量的存储位置、生命周期与作用域。
由于const和static是性质不同的关键字,下面就分开对两者进行讲解~
const
如果没有const的场景会是怎么样呢?下面通过简单代码举例~
普通变量无const
当普通变量没有const修饰符时:
cpp
#include <iostream>
using namespace std;
// 定义系统最大连接数(核心配置,本应只读)
int MAX_CONN = 100;
void initServer() {
// 开发人员误操作修改了核心常量(编译无报错,运行时才发现问题)
MAX_CONN = 200;
cout << "初始化服务器:最大连接数=" << MAX_CONN << endl;
}
int main() {
initServer();
// 业务逻辑依赖MAX_CONN,误改后导致连接数超限、系统异常
if (150 > MAX_CONN) {
cout << "连接数未超限" << endl;
} else {
cout << "连接数超限,系统崩溃" << endl;
}
return 0;
}
由上可知,若变量没有约束,会导致无意识的篡改,会导致业务逻辑错乱,如果代码量过大,就导致问题难以定位。此时const的作用就显现出来了,加上const的解决方案如下:
cpp
#include <iostream>
using namespace std;
// 加const,强制MAX_CONN只读
const int MAX_CONN = 100;
void initServer() {
// MAX_CONN = 200; // 编译直接报错:只读变量不可赋值,从源头拦截错误
cout << "初始化服务器:最大连接数=" << MAX_CONN << endl;
}
int main() {
initServer();
if (150 > MAX_CONN) {
cout << "连接数未超限" << endl;
} else {
cout << "连接数超限,系统崩溃" << endl;
}
return 0;
}
加上const,变量变常量了,此时MAX_CONN不可修改,如果修改编译器就会报错,就可以从源头拦截问题,这就是const的价值。
函数参数无 const
除了普通变量的篡改引起的错误之外,函数参数无 const也会误修改传入的实参,尤其是要注意指针和引用~
cpp
#include <iostream>
#include <string>
using namespace std;
// 仅用于打印字符串的函数,无const修饰引用参数
void printString(string& s) {
// 开发人员误操作修改了参数(本意只是打印,却篡改了外部实参)
s = "被篡改的字符串";
cout << "打印内容:" << s << endl;
}
int main() {
string original = "原始核心数据";
printString(original);
// 外部实参被函数意外修改,导致后续逻辑出错
cout << "外部原始数据:" << original << endl; // 输出:被篡改的字符串
return 0;
}
函数参数为非 const 引用时,函数内部可直接修改外部实参;即便函数本意是 "只读操作",也无语法约束阻止修改,导致外部数据被无意识篡改。那么,加 const 修饰参数就会解决以上问题。
cpp
#include <iostream>
#include <string>
using namespace std;
// 加const修饰引用参数,明确函数不会修改参数
void printString(const string& s) {
// s = "被篡改的字符串"; // 编译报错:无法修改const引用参数
cout << "打印内容:" << s << endl;
}
int main() {
string original = "原始核心数据";
printString(original);
// 外部实参未被修改,逻辑正常
cout << "外部原始数据:" << original << endl; // 输出:原始核心数据
return 0;
}
对函数参数加上了const限定符,编译期禁止函数内部修改参数,保障外部实参安全。
类成员无const
除了普通变量和函数参数没加const会引起篡改之外,类成员无const也会造成失误修改:
cpp
#include <iostream>
using namespace std;
class Student {
private:
string name;
int score;
public:
Student(string n, int s) : name(n), score(s) {}
// 仅用于读取分数的函数,无const修饰
int getScore() {
// 开发人员误操作修改了成员变量(本意只是返回分数)
score = 0;
return score;
}
};
int main() {
Student s("张三", 90);
// 调用读取函数后,分数被意外修改
cout << "张三的分数:" << s.getScore() << endl; // 输出:0(本该是90)
return 0;
}
类成员函数无 const 修饰时,即便函数本意是 "只读操作",也可随意修改成员变量,违背 "只读接口" 的设计初衷。下面就是该问题的解决方案:
cpp
#include <iostream>
using namespace std;
class Student {
private:
string name;
int score;
public:
Student(string n, int s) : name(n), score(s) {}
// 加const修饰成员函数,承诺不修改成员变量
int getScore() const {
// score = 0; // 编译报错:const成员函数禁止修改成员变量
return score;
}
};
int main() {
Student s("张三", 90);
// 读取函数仅返回值,对象状态未被修改
cout << "张三的分数:" << s.getScore() << endl; // 输出:90
return 0;
}
const 成员函数getScore()被强制约束为 "只读函数",无法修改类的非静态成员变量,保障对象状态的稳定性
上面分别从普通变量、函数参数引用、类成员函数三种情况分析const关键字的防篡改作用,我们可以看出,当我们希望我们的变量、参数引用、成员函数不被错误修改的时候,我们会加上const修饰符,把变量变成常量,此时就无法修改了,修改编译器就会报错。
const 的本质是编译期约束:不会增加运行时开销,仅在编译阶段检查非法修改,既避免拷贝,又保障数据安全,是 "零成本的安全保障"。
static
static是存储类的说明符,在变量前面加上static就成为静态变量,在函数前面加上static就成为静态函数。其核心作用是改变修饰对象的存储位置,将其固定为全局 / 静态区(内存区域有五大分区),在同一作用域内,同名变量会触发重定义错误。同时延长生命周期,但不限制对象的可修改性(和const不同)
static 的典型应用场景分别是修饰局部变量、修饰类成员变量、修饰全局变量 / 函数:
修饰局部变量
存储位置转为全局 / 静态区,生命周期延长至程序全程。因为内存分区分为五大区(栈区、堆区、全局 / 静态区、常量区、代码区),局部变量是存储在栈区,栈区的特点是编译器自动分配和释放内存,具体表现为某个函数的局部变量,当这个函数执行完成后就释放内存,所以生命周期较短,将局部变量的存储位置转为全局 / 静态区,有利于延长生命周期,在程序运行的全过程都可以使用,不仅仅是函数内(栈内)有效。

具体代码如下:
cpp
void countCall() {
static int callNum = 0; // 仅初始化一次
callNum++;
std::< "调用次数:< std::endl;
}
以上代码中多次调用countCall(),callNum值持续累加
修饰类成员变量
static修饰的类成员变量属于类本身而非实例,所有对象共享该变量。怎么理解这句话呢?传统的成员变量或者成员函数想要被调用,就需要先对这个类进行实例化对象,然后通过该对象进行调用,如下代码所示:
cpp
class Demo {
public:
int normalValue; // 非静态成员变量
};
int main() {
Demo obj1; // 栈上实例化对象(无new)
obj1.normalValue = 10;
return 0;
}
如果是static修饰的成员变量的话,可以直接通过类来访问,对所有对象共享:
cpp
class Demo {
public:
static int sharedValue; // 类内声明
};
int Demo::sharedValue = 0; // 类外初始化
static修饰成员变量打破 "实例绑定" 的限制,实现类级数据共享,避免冗余存储。
修饰全局变量 / 函数
static修饰全局变量和全局函数可以将链接属性改为内部链接,仅当前编译单元可见,避免多文件重定义冲突,因为在同一作用域内,同名变量会触发重定义错误。
cpp
static int globalStaticVar = 5; // 仅当前.cpp文件可见
static void staticFunc() { /* 函数实现 */ } // 仅当前.cpp文件可调用
const和static的组合
上面分别讲了const和static的作用,其实也可以将const和static组合使用,将二者组合可实现 "只读 + 共享" 的静态常量,存储于只读数据区(常量区),程序全程存在且所有实例共享,是定义系统配置常量的最优解。代码如下所示:
cpp
class Config {
public:
// C++17后可直接类内初始化,无需类外定义
static const int MAX_CONN = 100;
};
// 访问方式:Config::MAX_CONN,无需创建Config实例
定义类级 / 全局级的只读共享常量必须用const和static的组合,如果仅用static,数据可被修改,破坏配置的稳定性;仅用const的话每个实例独立存储一份,冗余且无法脱离实例访问。
补充:const修饰指针
以上分别对const和static关键字的作用和使用场景进行分析,但const除了修饰普通变量之外,在修饰指针上也有一番考究,分别是常量指针 和指针常量:
常量指针
常量指针是针指向的内存内容是常量(不可修改),但指针本身可指向其他地址
cpp
int a=10, b=20;
const int* p = &a; // 常量指针
p = &b; 指针可指向新地址(指针可变)
记忆:常量指针是 "指向常量的指针" ,指向的内存内容是常量,指针本身是变量,const远离指针
使用场景:函数参数传递 、访问只读内存区域
指针常量
指针常量是指针本身的地址值不可修改,但指向的内存内容可修改
cpp
int a=10, b=20;
int* const p = &a; // 指针常量
*p = 30; 可修改指向的内容(内容可变)
记忆:指针常量:指针本身是常量,指向的内容是变量,const靠近指针
使用场景:硬件 / 底层编程 、单例模式
双重 const
语法为const 类型* const 指针名,指针和内容均不可改。
cpp
const int* const p = &a;
// *p = 30; 错误: 内容不可改
// p = &b; 错误:指针不可改
使用场景:访问全局只读配置 、多线程只读共享资源
总结
上述内容主要分为以下四点:
-
const修饰普通变量、函数参数、类成员三种情况,主要是为了防篡改。
-
static修饰局部变量,成员变量,全局变量和全局函数三种情况,主要是为了改变存储位置方便数据共享, 减少数据冗余, 延长生命周期
-
const和static的结合通常定义系统配置常量
-
const修饰指针形成常量指针、指针常量和双重 const三种情况。
以上就是本文的所有内容,如果本文对你有帮助的话欢迎点赞收藏哦~
感兴趣的朋友也欢迎关注哟~