const关键字

const的核心本质只有两个字:约束。它的逻辑体系可以完美地按照"约束的对象是谁"来划分为三个层次:

面向过程:修饰变量与指针

核心逻辑是:保护这份数据不被改写。

  1. const修饰基础变量

把变量变为了常量,const int a = 10;之后a不能被修改。

另外一个冷知识:在全局作用域下,const变量默认自带static属性即内部链接属性。

之前说过普通全局变量是外部链接的,多个文件定义会冲突,必须加static才能限制在文件内。但如果你在.h中写const int MAX_VAL = 100;,并把它#include到多个.cpp中,链接器不会报错。

背后的逻辑:编译器默认给全局const加上了类似static的属性。因为const主要是为了替代宏定义(#define),如果它不是内部链接的,每定义一个常量都需要去写个.cpp来初始化,那程序员会疯掉的。

  1. const修饰指针与引用

常见两种:

const int *pint * const p,很好区分,前者是锁住内容,内容不可变,但指针可以指向别处;后者是锁住自身,指针指向固定,但内容可以改。

函数传参时经常使用前者以及const的引用,保证函数内部不会偷偷修改实参的值。


面向对象:修饰类成员

核心逻辑是:保证对象的状态(成员变量)不被破坏。

  1. const修饰成员变量

逻辑:每个对象都有这个常量。

关键点:必须在初始化列表中初始化,因为一旦构造函数体开始执行,它就被视为"已经存在"了,此时再赋值就晚了。

  1. const修饰成员函数

本质 :其实是把隐式的this指针从T* const升级为了const T* const

权限

  • const对象只能调用const成员函数(因为权限不能放大,如果没有const成员函数就会报错)
  • 普通对象既可以调用普通函数,也可以调用const函数(权限可以缩小,当只存在const时会去调用const函数,都存在时会调用普通成员函数,其实是构成了函数重载)

逃逸逻辑(mutable) :如果某个变量(如缓存、计时器)逻辑上不属于"状态",即使在const函数里也想改,就给它加mutable

arduino 复制代码
class DataProvider {
public:
    // 逻辑:这是一个 Getter,逻辑上它不应该修改对象
    // 所以必须声明为 const,这样 const 对象也能调用它
    int getData() const {
        if (cacheValid) {
            return cachedValue; // 直接返回缓存
        }

        // 逻辑冲突点:
        // getData 是 const 的,但由于我们要更新缓存,必须修改变量。
        // 如果没有 mutable,下面这两行会直接编译报错。
        cachedValue = doExpensiveComputation(); 
        cacheValid = true;

        return cachedValue;
    }

private:
    int doExpensiveComputation() const { return 42; }

    // 关键点:用 mutable 修饰
    mutable int  cachedValue = 0;     // 缓存值
    mutable bool cacheValid  = false; // 缓存是否有效
};

另一个经典场景:多线程锁(mutex)

arduino 复制代码
#include <mutex>

class Counter {
public:
    int getCount() const {
        std::lock_guard<std::mutex> lock(mtx); // 如果 mtx 不是 mutable,这里报错
        return count;
    }

private:
    int count = 0;
    mutable std::mutex mtx; // mutex 几乎总是标记为 mutable
};

类成员中的"黄金搭档":static const

当这两个关键字在类成员里碰头时,它们共同完成了一个逻辑目标:定义一个真正的"类常量"。

  • static的贡献:保证这份数据在内存中只有一份,不随对象重复创建。
  • const的贡献:保证这份数据是只读的,谁也别想改。
arduino 复制代码
class Circle {
    static const double PI = 3.1415926;
};

这是C++中定义"类内常量"的标准方式。正如之前在static部分讨论的,只有static const的整型/枚举类型才允许在类内直接赋初值。

另外,编译器在处理static const这种组合时,会进行非常激进的优化。如果编译器发现一个变量又是静态的(地址固定)又是常量的(值固定),它往往根本不会为这个变量分配物理内存,而是直接把它的值"硬编码"到调用它的指令中(类似于宏替换,但带有类型检查)。

相关推荐
888CC++3 分钟前
C++ 快速学习指南:从入门到进阶的实战路线
开发语言·c++
小小晓.15 分钟前
C++记:函数
开发语言·c++·算法
charlie1145141912 小时前
Cinux —— 给物理内存建账本:bitmap 物理内存管理器
开发语言·c++·操作系统·开源项目
2301_777998345 小时前
Linux线程同步与互斥(三):信号量与环形队列实现生产者消费者模型
linux·c语言·c++
啦啦啦啦啦zzzz5 小时前
设计模式:单例模式和工厂模式
c++·单例模式·设计模式·工厂模式
Henry Zhu1235 小时前
C++ 动态多态详解
c++
此生决int5 小时前
深入理解C++系列(06)——模版初阶与STL
开发语言·c++
c238565 小时前
《枚举算法 “翻译官”:用 enum class 给 “游游的礼盒” 算分》
c语言·c++·算法
noipp5 小时前
推荐题目:洛谷 P5843 [SCOI2012] Blinker 的噩梦
c语言·数据结构·c++·算法·游戏·洛谷·luogu
鱼子星_6 小时前
《算 · 法》题目解析篇(1):最近公共祖先问题,线段树,最长回文子序列
c++·算法·动态规划·递归