C++中的组合类型主要指类和结构体,默认成员初始化指的是在创建组合类型对象时,若没有显式给成员赋值,成员会自动获得预设的初始值。不同的初始化方法有不同的生效逻辑,下面逐一介绍常见的实现方式。

类内初始值初始化
从C++11标准开始,允许在组合类型的成员声明时直接赋予初始值,这就是类内初始值。当创建对象时没有通过其他方式给该成员赋值,就会使用这个预设的初始值。
#include <iostream>
#include <string>
struct Student {
// 类内初始值,默认初始化为18
int age = 18;
// 类内初始值,默认初始化为空字符串
std::string name = "";
};
int main() {
Student s1;
std::cout << "默认年龄: " << s1.age << std::endl;
std::cout << "默认姓名: " << s1.name << std::endl;
return 0;
}
这种方式的好处是初始化逻辑和成员声明放在一起,可读性更强,也避免了忘记初始化成员的问题。
构造函数初始化列表初始化
组合类型的构造函数可以通过初始化列表为成员设置默认值,当创建对象时调用该构造函数且没有传入对应参数,就会使用初始化列表中的值。
#include <iostream>
class Person {
private:
int id;
float salary;
public:
// 构造函数初始化列表,设置默认id为1001,默认salary为3000.0
Person() : id(1001), salary(3000.0) {}
// 带参数的构造函数,会覆盖默认值
Person(int inputId, float inputSalary) : id(inputId), salary(inputSalary) {}
void printInfo() {
std::cout << "ID: " << id << ", Salary: " << salary << std::endl;
}
};
int main() {
Person p1;
p1.printInfo(); // 输出默认初始化的id和salary
Person p2(1002, 4500.0);
p2.printInfo(); // 输出传入的参数值
return 0;
}
需要注意的是,初始化列表的初始化优先级高于类内初始值,如果同时存在,会优先使用初始化列表的值。
默认构造函数与值初始化
如果用户没有定义任何构造函数,编译器会自动生成默认构造函数,此时如果创建对象时使用组合类型名 对象名{}的形式进行值初始化,内置类型成员会被初始化为0,类类型成员会调用其自身的默认构造函数。
#include <iostream>
struct Data {
int num;
double score;
};
int main() {
Data d1{}; // 值初始化,num为0,score为0.0
std::cout << "num: " << d1.num << ", score: " << d1.score << std::endl;
return 0;
}
如果创建对象时没有加花括号,比如Data d1;,对于内置类型成员,是否初始化取决于变量是全局还是局部,局部的内置类型成员不会被默认初始化,值是未定义的,这也是容易出现bug的地方。
不同方法的优先级说明
当多种默认初始化方式同时存在时,生效的优先级顺序如下:
- 构造函数初始化列表的优先级最高,会直接覆盖其他默认值
- 其次是类内初始值,当没有初始化列表赋值时生效
- 最后是值初始化的默认规则,当既没有初始化列表也没有类内初始值时,使用值初始化的规则
注意事项
首先,类内初始值不能使用圆括号,比如int age(18);是不合法的,只能用等号或者花括号,比如int age = 18;或者int age{18};。
其次,对于常量成员和引用类型成员,必须在构造函数初始化列表中完成初始化,不能使用类内初始值之外的其他方式,否则会编译报错。
最后,如果组合类型包含数组成员,类内初始值不能直接初始化数组,需要通过构造函数初始化列表或者默认构造函数的逻辑来完成数组的默认初始化。