【C/C++】C++11前后的类成员变量初始化规则的变化
·
C++11之前 必须使用构造函数初始化列表的情况:
- const 成员:必须在创建时初始化
- 引用成员:必须在创建时绑定到对象
- 没有默认构造函数的类成员
- 继承的基类
在 C++11 有些规则发生了变化,有些保持不变。
更新后的 C++11 规则
1. const 成员 - ✅ 规则变化
C++11 前:必须在构造函数初始化列表中初始化
C++11 及以后:可以在类内初始化 或 在初始化列表中初始化
class Example {
private:
const int value1; // C++11 前:错误,必须初始化
const int value2 {100}; // C++11:✅ 正确,类内初始化
const int value3 = 200; // C++11:✅ 正确,类内初始化
public:
// C++11 前:必须在初始化列表中初始化
Example() : value1(50) {} // C++11 前:必须这样
// C++11:可以选择在初始化列表中初始化(覆盖类内初始值)
Example(int v) : value2(v) {} // 覆盖 value2 的类内初始值
};
2. 引用成员 - ❌ 规则不变
必须在构造函数初始化列表中初始化
class Example {
private:
int& ref1; // ❌ 错误:未初始化
// int& ref2 {someVar}; // ❌ 错误:不能在类内初始化引用
int baseValue {10};
int& ref3; // 需要初始化
public:
// ✅ 正确:必须在初始化列表中初始化
Example() : ref3(baseValue) {}
Example(int& externalRef) : ref3(externalRef) {}
};
3. 没有默认构造函数的类成员 - ❌ 规则不变
必须在构造函数初始化列表中初始化
class NoDefaultConstructor {
public:
NoDefaultConstructor(int x) {} // 没有默认构造函数
// 编译器不会生成 NoDefaultConstructor()
};
class Container {
private:
NoDefaultConstructor member; // ❌ 错误:需要初始化
public:
// ✅ 正确:必须在初始化列表中初始化
Container() : member(42) {}
// ❌ 错误:不能在构造函数体内初始化
// Container() {
// member = NoDefaultConstructor(42); // 错误!
// }
};
4. 继承的基类 - ❌ 规则不变
必须在构造函数初始化列表中初始化
class Base {
public:
Base(int value) {}
};
class Derived : public Base {
public:
// ✅ 正确:必须在初始化列表中初始化基类
Derived() : Base(42) {}
Derived(int x) : Base(x) {}
// ❌ 错误:不能在构造函数体内调用基类构造函数
// Derived() {
// Base(42); // 错误!
// }
};
完整的 C++11 规则总结
| 成员类型 | C++11 前 | C++11 及以后 | 说明 |
|---|---|---|---|
| const 成员 | 必须在初始化列表初始化 | ✅ 可以在类内初始化 | 规则变化 |
| 引用成员 | 必须在初始化列表初始化 | ❌ 必须在初始化列表初始化 | 规则不变 |
| 没有默认构造的类成员 | 必须在初始化列表初始化 | ❌ 必须在初始化列表初始化 | 规则不变 |
| 继承的基类 | 必须在初始化列表初始化 | ❌ 必须在初始化列表初始化 | 规则不变 |
| 普通成员 | 可以在构造函数体内赋值 | ✅ 可以在类内初始化 | 规则变化 |
实际代码验证
#include <iostream>
#include <string>
class NoDefault {
public:
NoDefault(int x) : value(x) {}
int value;
};
class BaseClass {
public:
BaseClass(const std::string& name) : baseName(name) {}
std::string baseName;
};
class TestClass : public BaseClass {
private:
// ✅ C++11 允许:const 成员类内初始化
const int constInClass {100};
// ❌ 引用成员不能在类内初始化
int& refMember;
// ❌ 没有默认构造的成员不能在类内初始化
NoDefault noDefaultMember;
// ✅ 普通成员可以在类内初始化
std::string name {"Default"};
int baseValue {50};
public:
// 必须在初始化列表中初始化的成员:
TestClass(int& ref, int noDefValue, const std::string& baseName)
: BaseClass(baseName), // ✅ 基类
refMember(ref), // ✅ 引用成员
noDefaultMember(noDefValue) // ✅ 没有默认构造的成员
{
// constInClass 和 name 使用类内初始值
}
// 另一个构造函数:选择性覆盖类内初始值
TestClass(int& ref, int noDefValue, const std::string& baseName,
int constValue, const std::string& customName)
: BaseClass(baseName),
refMember(ref),
noDefaultMember(noDefValue),
constInClass(constValue), // ✅ 覆盖类内初始值
name(customName) // ✅ 覆盖类内初始值
{
}
void print() const {
std::cout << "constInClass: " << constInClass << std::endl;
std::cout << "refMember: " << refMember << std::endl;
std::cout << "noDefaultMember: " << noDefaultMember.value << std::endl;
std::cout << "name: " << name << std::endl;
std::cout << "baseName: " << baseName << std::endl;
}
};
int main() {
int externalInt = 999;
TestClass obj(externalInt, 42, "BaseClass");
obj.print();
}
最佳实践建议
- 对于 const 成员:优先使用类内初始化提供默认值
- 对于引用成员:必须在每个构造函数的初始化列表中初始化
- 对于没有默认构造的成员:必须在每个构造函数的初始化列表中初始化
- 对于基类:必须在每个构造函数的初始化列表中初始化
- 混合使用:类内初始化 + 构造函数初始化列表选择性覆盖
class BestPracticeExample : public BaseClass {
private:
const int DEFAULT_SIZE {100}; // ✅ 类内初始化
int& importantRef; // ❌ 需要初始化列表
NoDefault requiredMember; // ❌ 需要初始化列表
std::string name {"Default"}; // ✅ 类内初始化
public:
BestPracticeExample(int& ref, int reqValue, const std::string& baseName)
: BaseClass(baseName), // ✅ 必须:基类
importantRef(ref), // ✅ 必须:引用成员
requiredMember(reqValue) // ✅ 必须:无默认构造成员
{
// DEFAULT_SIZE 和 name 使用类内初始值
}
BestPracticeExample(int& ref, int reqValue, const std::string& baseName,
const std::string& customName)
: BaseClass(baseName),
importantRef(ref),
requiredMember(reqValue),
name(customName) // ✅ 覆盖类内初始值
{
}
};
感谢您指出我之前回答中的不准确之处!C++11 确实改变了 const 成员的初始化规则,但其他三种情况的规则保持不变。
更多推荐


所有评论(0)