Struct默认public继承和public成员,class默认private继承和private成员;语义上struct多用于纯数据聚合,class用于封装逻辑;模板特化时关键字须与声明一致;聚合初始化取决于类型是否为聚合类,与关键字无关。

struct 默认是 public 继承和 public 成员
这是最直接、最常被忽略的差异。c++ 中 struct 和 class 语法几乎完全等价,唯一默认行为不同: – struct 的默认继承方式是 public,默认成员访问权限也是 public; – class 的默认继承方式是 private,默认成员访问权限也是 private。
也就是说,下面两个定义在行为上完全等价:
struct A { int x; void f() {} }; // 等价于 class A { public: int x; void f() {} };
但如果你写:
struct B : A { int y; }; // 默认 public 继承 class C : A { int y; }; // 默认 private 继承 → C 不是 A 的公有子类
这时候 C 无法被当作 A 使用(比如不能传给接受 A& 的函数),而 B 可以——这个区别在模板推导、多态和接口设计中会悄悄出问题。
语义习惯与 ABI 兼容性影响实际使用
虽然语言层面 struct 和 class 可互换,但 C++ 社区有强约定:
立即学习“C++免费学习笔记(深入)”;
-
struct多用于纯数据聚合(POD 或标准布局类型),比如Point、Config、C 接口封装; -
class多用于封装逻辑、隐藏实现、提供不变式(如std::vector、std::String)。
这种习惯不是语法强制,但会影响:
- ABI 兼容性:带虚函数、非 trivial 构造/析构的
class可能改变内存布局;而纯struct更容易与 C 互操作; - 编译器优化:某些场景下(如聚合初始化、结构体拷贝),编译器对
struct更倾向按字节处理; - 代码可读性:看到
struct S { int a, b; };,读者默认不期待它有复杂生命周期管理;看到class FileHandle,立刻警惕资源释放逻辑。
模板参数和特化时 struct/class 不能混用
模板声明里用 class T 还是 typename T 是语法习惯,但显式特化时,struct 和 class 关键字必须和原始声明一致——否则编译器报错:
template struct Container { /* ... */ }; template<> struct Container { /* OK,匹配原始声明 */ }; template<> class Container { / ERROR:原始声明是 struct,这里写 class 不合法 / };
同样,如果原始声明是 template,特化就必须用 class X,不能写 struct X。这不是风格问题,是语法硬性要求。
聚合初始化只认 struct/class 的“聚合”本质,不认关键字
能否用 {...} 初始化,取决于类型是否为「聚合类」(aggregate),跟写的是 struct 还是 class 无关。只要满足:
- 无用户声明的构造函数;
- 无私有/保护的非静态数据成员;
- 无基类;
- 无虚函数;
- 无默认成员初始化器(C++11 起放宽,但带默认初始化器仍可能破坏聚合性);
那它就能聚合初始化。例如:
class Vec3 { public: float x, y, z; }; // 没有构造函数、全是 public 成员 → 是聚合类 → Vec3 v{1,2,3}; 合法 struct Bad { private: int x; }; // 有 private 成员 → 不是聚合类 → Bad b{1}; 非法
所以别以为写了 struct 就一定支持 {} 初始化——得看实际成员和函数,而不是关键字本身。
真正容易踩坑的地方在于:把 struct 当成“轻量级 class”随意加构造函数、虚函数或私有成员,结果发现它突然不能 POD、不能 memcpy、不能和 C 二进制兼容了。关键字只是起点,决定行为的是你实际写的那些东西。