const成员变量只能在构造函数初始化列表中赋值,因声明后不可修改;类内就地初始化不适用于非静态const成员,且初始化顺序严格按成员声明顺序执行,与列表书写顺序无关。

const成员变量只能在构造函数初始化列表中赋值
因为 const 成员变量一旦声明,就不能再被修改,而类内默认初始化(如 int x = 42;)在 C++11 之前不被允许,C++11 起虽支持就地初始化,但对 const 非静态成员仍**不能绕过初始化列表**——否则编译器会报错:error: uninitialized const member。
常见错误是试图在构造函数体内部赋值:
class A {
const int val;
public:
A(int v) {
val = v; // ❌ 编译失败:assignment of read-only member
}
};- 必须写成:
A(int v) : val(v) {} - 若类有多个
const成员,全部得列在同一个初始化列表里,用逗号分隔 - 即使只初始化一个
const成员,也必须写初始化列表,哪怕其他成员不依赖它
初始化列表顺序与成员声明顺序必须一致
初始化列表中写的顺序无关紧要,真正起作用的是类中数据成员的**声明顺序**。如果初始化列表顺序和声明顺序不一致,编译器仍按声明顺序执行初始化,但会发出警告(如 GCC 的 -Wreorder),更严重的是:若后声明的成员依赖先声明成员的值,而你误以为初始化列表顺序决定执行顺序,就可能引发未定义行为。
例如:
立即学习“C++免费学习笔记(深入)”;
class B {
int a;
const int b;
public:
B() : b(a + 1), a(42) {} // ⚠️ 警告:b 在 a 之前声明,却用未初始化的 a 初始化
};-
b按声明顺序先于a初始化,此时a还是垃圾值 - 即使列表里写成
b(a+1), a(42),b仍用未定义的a计算 - 正确写法:
B() : a(42), b(a + 1) {},且确保a声明在b之前
const成员变量不能是默认参数或运行时计算值的直接目标
初始化列表中的表达式必须能在构造函数进入函数体前完成求值,因此不能依赖尚未构造完成的对象状态,也不能调用虚函数、不能访问 this(尽管语法上允许,但语义上不可靠)。
- ✅ 允许:
const double pi = 3.14159;、const std::string s("hello");、const int x(other.x * 2);(other是参数) - ❌ 不允许:
const int y = some_func();(若some_func()依赖this或虚函数调用) - ⚠️ 注意:
std::vector等容器的const成员,初始化列表中必须传入完整构造参数,不能留空再 push_back —— 因为函数体里已无法修改
static const 成员变量是例外,但仍有区别
静态 const 成员不属于对象实例,因此不参与构造函数初始化列表。它们在类外定义(除非是整型/枚举且用常量表达式初始化)。
class C {
public:
static const int MAX = 100; // ✅ OK:整型常量表达式,可内联定义
static const std::string name; // ❌ 必须在 .cpp 中定义:const std::string C::name = "test";
};-
static const整型/枚举类型可用常量表达式在类内初始化,无需初始化列表 - 非整型(如
std::string、double、自定义类型)必须在类外定义,且不能在初始化列表中出现 - 别把它和非静态
const成员混淆——后者永远逃不开初始化列表
实际写代码时最容易忽略的是声明顺序与初始化列表的隐式耦合,尤其多人协作改成员顺序时,很容易漏掉同步调整初始化列表逻辑。建议把初始化列表写在头文件里,并和成员声明上下对齐,减少出错概率。









