每日一个C++知识点|const和static的区别

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; 错误:指针不可改

使用场景:访问全局只读配置多线程只读共享资源

总结

上述内容主要分为以下四点:

  1. const修饰普通变量、函数参数、类成员三种情况,主要是为了防篡改。

  2. static修饰局部变量,成员变量,全局变量和全局函数三种情况,主要是为了改变存储位置方便数据共享, 减少数据冗余, 延长生命周期

  3. const和static的结合通常定义系统配置常量

  4. const修饰指针形成常量指针、指针常量和双重 const三种情况。

以上就是本文的所有内容,如果本文对你有帮助的话欢迎点赞收藏哦~

感兴趣的朋友也欢迎关注哟~

相关推荐
唐·柯里昂7982 小时前
[rk3566AI模型部署]泰山派buildroot部署yolov5 使用rknn_model_zoo
c语言·c++·笔记·yolo·rk3566·瑞芯微·泰山派
郝学胜-神的一滴2 小时前
Linux 多线程编程:深入理解 `pthread_join` 函数
linux·开发语言·jvm·数据结构·c++·程序人生·算法
Trouvaille ~2 小时前
【C++篇】C++11新特性详解(二):右值引用与移动语义
c++·stl·基础语法·右值引用·默认成员函数·完美转发·移动语义
罗湖老棍子2 小时前
瑞瑞的木板(洛谷P1334 )
c++·算法·优先队列·贪心·哈夫曼树
embrace992 小时前
【数据结构学习】数据结构和算法
c语言·数据结构·c++·学习·算法·链表·哈希算法
milan-xiao-tiejiang2 小时前
ROS2面试准备
c++·面试·自动驾驶
杨恒982 小时前
GESPC++三级编程题 知识点
数据结构·c++·算法
week_泽2 小时前
题目 3330: 蓝桥杯2025年第十六届省赛真题-01 串
c++·贪心算法·蓝桥杯