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

相关推荐
叼烟扛炮8 分钟前
C++第三讲:类和对象(中)
开发语言·c++·类和对象
KuaCpp12 分钟前
C++新特性学习
c++·学习
墨染千千秋40 分钟前
C/C++ Keywords
c语言·c++
ximu_polaris41 分钟前
设计模式(C++)-行为型模式-中介者模式
c++·设计模式·中介者模式
CSCN新手听安2 小时前
【Qt】Qt窗口(八)QFontDialog字体对话框,QInputDialog输入对话框的使用,小结
开发语言·c++·qt
tumu_C3 小时前
用std::function减缓C++模板代码膨胀和编译压力的一个场景
开发语言·c++
Hical614 小时前
C++17 实战心得:那些真正改变我写代码方式的特性
c++
Hical614 小时前
实测:C++20 协程 vs Go Gin vs Rust Actix,谁的 Web 性能更强?
c++
草莓熊Lotso5 小时前
《告别 “会用不会讲”:C++ string 底层原理拆解 + 手撕实现,面试 / 开发都适用》
开发语言·c++·面试
会编程的土豆5 小时前
【数据结构与算法】空间复杂度从入门到面试:不仅会算,还要会解释
数据结构·c++·算法·面试·职场和发展