一、聚合类(Aggregate Class)概念
- 聚合(Aggregate) 是 C++ 中一类特殊的类类型,无用户自定义构造函数 、无私有或受保护非静态数据成员 、无虚函数 、无基类(C++11 起基类必须也是聚合且无私有/受保护成员)等。
- 对聚合类型,可使用 聚合初始化 (aggregate initialization):通过花括号
{}
直接按声明顺序对成员进行初始化,无需编写构造函数。
二、C++ 标准中聚合定义演变
标准 | 聚合定义要素 |
---|---|
C++98 | 无用户构造函数、无私有/受保护成员、无虚函数、无基类 |
C++11 | 同 C++98;允许成员带默认初始值 |
C++17 | 同 C++11;继承自 struct 基类仍可是聚合 |
C++20 | 放宽:允许有基类、允许有 = default 构造(若符合条件) |
三、聚合初始化语法
cpp
struct Point { double x, y; };
Point p1{1.0, 2.0}; // x=1.0, y=2.0
Point p2 = {3.0, 4.0}; // 等价
Point arr[] = { {0,0}, {1,1}, {2,2} };
-
按成员声明顺序依次匹配初始值列表。
-
对于带默认成员初始化的聚合,可只初始化前几个成员,剩余成员用默认值:
cppstruct S { int a{1}; int b{2}; int c{3}; }; S s1{10}; // a=10, b=2, c=3 S s2{10,20}; // a=10, b=20, c=3 S s3{}; // a=1, b=2, c=3
四、聚合条件详解
要成为聚合,类必须满足(C++20):
- 无用户提供的构造函数 (包括
= delete
或= default
) - 所有非静态数据成员均为公有
- 无虚基类(可以有非虚基类,只要该基类自身也是聚合)
- 无虚函数
- 无成员初始值设定以外的"专用"初始化
- 若有基类,则该基类也必须是聚合,且不含私有/受保护成员
注意 :C++20 放宽了对
= default
构造的限制,但如果显式声明了任何构造(即使= default
),则该类不再是聚合。
五、使用聚合的优势
-
简洁:无需编写繁琐构造函数,即可完成对象初始化。
-
直观:初始化列表按成员顺序一目了然。
-
易于与传统 C 风格交互 :如
memset
、数组初始化等场景。 -
支持结构化绑定(C++17 起):
cppstruct P { int x; int y; }; P p{5,6}; auto [a,b] = p; // a=5, b=6
六、常见注意事项
-
初始化次序
- 聚合初始化严格按成员声明顺序,即使列表书写顺序乱也按声明顺序匹配。
-
禁止多余初始化器
-
提供的初始值个数不得超过成员总数,否则编译报错:
cppPoint p{1,2,3}; // ❌ 多余
-
-
非聚合类型无法聚合初始化
- 如含私有成员、用户构造或虚函数,就必须使用显式构造或工厂函数。
-
默认成员初始化配合
- 若聚合成员已在类内提供默认值,聚合初始化可选择性覆盖。
-
带有引用成员
-
引用成员需在初始化列表中提供值,否则报错。
cppstruct R { int& r; }; int x; R rr{x}; // OK R rr2{}; // ❌ 引用未初始化
-
-
数组成员初始化
-
支持对数组成员进行聚合初始化:
cppstruct A { int a[3]; }; A a{{1,2,3}};
-
-
嵌套聚合
-
嵌套聚合成员也按顺序初始化:
cppstruct Inner { int i; }; struct Outer { Inner in; double d; }; Outer o{{42}, 3.14};
-
七、综合示例
cpp
#include <iostream>
#include <string>
struct Address {
std::string city;
int zip;
};
struct Person {
std::string name;
int age{30}; // 默认成员初始化
Address address;
double scores[3];
};
int main() {
// 全部成员初始化
Person p1{"Alice", 25, {"NYC", 10001}, {90.0, 85.5, 92.0}};
std::cout << p1.name << ", " << p1.age
<< ", " << p1.address.city
<< ", scores[1]=" << p1.scores[1] << "\n";
// 部分初始化,age 使用默认
Person p2{"Bob", /*age=*/{}, {"LA", 90001}, {}};
// p2.name="Bob", age=30, address.city="LA", zip=90001, scores all=0.0
// 结构化绑定(C++17)
auto [nm, ag, addr, scrs] = p1;
std::cout << "Bound: " << nm << "@" << addr.city << "\n";
return 0;
}
八、小结
- 聚合类 是无需编写构造函数即可用
{}
一步初始化所有成员的类型。 - 随 C++ 标准演进,聚合规则有所放宽,但核心要求仍是"无用户构造、无私有/受保护成员、无虚特性"。
- 正确利用聚合初始化,能使类型定义更简洁、初始化更直观,但应注意成员声明顺序和默认初始化的配合。