真正不可变的对象需满足三点:定义时加const、所有非静态成员必须通过构造函数初始化列表赋值、不使用mutable。否则对象仍可能被修改或绕过限制。

用 const 修饰对象本身,不是只修饰成员函数
很多人以为加个 const 成员函数就够了,其实那只是保证函数不改成员变量;真正让对象不可变,得从定义时就锁死。比如:
class Point {
public:
int x, y;
Point(int x, int y) : x(x), y(y) {}
int get_x() const { return x; }
};这个类本身是可变的——你可以写 Point p(1,2); p.x = 3;。要让它不可变,得这样定义:const Point p(1, 2);这时连
p.x = 3 都会编译失败。成员变量必须在构造函数初始化列表中赋值
const 对象的所有非静态成员都必须是 const 或引用类型,且不能在构造函数体里赋值——否则编译器不知道该用什么初值。常见错误:
class Config {
const std::string name;
public:
Config(std::string n) {
name = n; // ❌ 错误:const 成员不能在构造函数体内赋值
}
};正确写法:Config(std::string n) : name(n) {} // ✅ 必须用初始化列表如果成员是 const 内置类型(如 const int)、const 类型对象、或引用,全都得走初始化列表。避免隐式拷贝破坏不可变性
定义了 const Point p,但如果 Point 没有禁用拷贝或没写移动语义,别人仍可能通过拷贝绕过限制:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
Point q = p; // 如果 Point 的拷贝构造函数非 const,q 就是可变的 q.x = 99; // 合法,但违背了“不可变”的本意所以真要严格不可变,建议:
- 把拷贝构造和拷贝赋值设为
delete - 或者确保所有成员本身也是
const且无副作用(比如不包含裸指针) - 若需传递,统一用
const Point&引用,而非值传递
注意 mutable 是后门,慎用
即使对象声明为 const,只要成员标了 mutable,它就能被 const 成员函数修改。比如缓存计数器:
class Cache {
mutable int hit_count = 0;
public:
int get(int key) const {
++hit_count; // ✅ 合法:mutable 允许在 const 函数里改
return key * 2;
}
};这不算 bug,但如果你真想要“完全不可变”,就别加 mutable——它本质是为“逻辑 const”服务的,不是为数据冻结设计的。一旦用了,那个对象就不再是字面意义上的不可变。真正不可变的对象,核心就三点:定义时加 const、成员全靠初始化列表塞值、不给 mutable 留缝。其余技巧都是在绕开这些约束,反而容易出错。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










