在 C++ 中,struct 与 class 都定义类类型,可以具有构造、析构、成员函数、继承和模板。两个关键的默认差异是:struct 的成员和基类访问默认为 public,class 的这两项默认为 private。
C 的 struct 则是另一门语言中的结构体类型。不要把“C++ 的 struct”理解为只能保存 C 风格数据的简化 class。下面分别使用 C11 和 C++17 编译示例。
对照时先确定语言
跳转到“对照时先确定语言”| 能力 | C11 struct | C++17 struct |
|---|---|---|
| 数据成员 | 支持;没有 public/private 访问控制 | 支持;默认 public,可改访问区段 |
| 成员函数、构造和析构 | 不支持;可保存函数指针作为数据 | 支持,与 class 一样 |
| 继承和虚函数 | 没有对应的语言机制 | 支持;默认 public 继承 |
| 类型名称 | 通常写 struct Point,也可用 typedef 别名 | 通常直接写 Point |
| 模板 | 不支持 C++ 模板 | 可以定义类模板 |
| 初始化 | 聚合初值、赋值等 C 规则 | 可聚合初始化,也可调用构造函数,取决于类型定义 |
C 的结构体标签与普通标识符使用不同的名字空间,这里的“名字空间”是 C 标识符分类规则,不是 C++ 的 namespace 语法。typedef struct Point Point; 可以在 C 中建立普通类型别名,之后便能写 Point p;。C 结构体声明
完整 C11 程序:标签、别名与函数指针
跳转到“完整 C11 程序:标签、别名与函数指针”#include <assert.h>
struct Point { int x; int y;};typedef struct Point Point;
struct Calculator { int (*apply)(int, int);};int add(int left, int right) { return left + right; }
struct Envelope { struct { int x; int y; }; /* C11 的匿名结构体成员 */ int kind;};
int main(void) { struct Point first = {10, 20}; Point second = first; second.x = 30; assert(first.x == 10 && second.x == 30);
struct { int x; int y; } point = {3, 4}; assert(point.x + point.y == 7);
struct Calculator calculator = {add}; assert(calculator.apply(2, 3) == 5);
struct Envelope envelope = {{5, 6}, 1}; assert(envelope.x == 5 && envelope.y == 6 && envelope.kind == 1); return 0;}Calculator 中的 apply 是一个函数指针字段,不是 C++ 那样的成员函数;它没有自动传入的 this。结构体本身不能把一个函数类型直接当作数据成员。
无名类型,不等于匿名成员
跳转到“无名类型,不等于匿名成员”struct { int x; int y; } point; 定义了一个没有类型标签、但变量名叫 point 的对象,标准 C 与 C++ 都可以使用这个形式。原笔记将它概括成“C 可能需要扩展支持”并不准确。
上例 Envelope 中的 struct { int x; int y; }; 则没有成员名,允许直接访问 envelope.x;这是 C11 的匿名结构体成员。它不是标准 C++17 的对应功能,某些 C++ 编译器仅作为扩展接受。C++ 的匿名 union 又有单独规则,不能与匿名 struct 混为一谈。GCC 对 ISO C11 无名字段的说明
完整 C++17 程序:默认访问、构造与多态
跳转到“完整 C++17 程序:默认访问、构造与多态”#include <cassert>#include <type_traits>
struct MyStruct { int value; explicit MyStruct(int input) : value(input) {} int doubled() const { return value * 2; }};
class MyClass { int value_; // 默认 privatepublic: explicit MyClass(int input) : value_(input) {} int value() const { return value_; }};
struct Base { virtual ~Base() = default; virtual int value() const { return 1; }};struct PublicChild : Base { // 默认 public 继承 int value() const override { return 2; }};class PrivateChild : Base {}; // 默认 private 继承
template<class T>struct Box { T data;};
int main() { MyStruct object{10}; object.value = 12; assert(object.doubled() == 24); assert(MyClass{10}.value() == 10);
static_assert(std::is_convertible_v<PublicChild*, Base*>); static_assert(std::is_base_of_v<Base, PrivateChild>); static_assert(!std::is_convertible_v<PrivateChild*, Base*>); PublicChild child; const Base& base = child; assert(base.value() == 2);
Box<int> box{42}; assert(box.data == 42); struct { int x; int y; } point{3, 4}; assert(point.x + point.y == 7);}是否写 struct 不限制多态能力,使用 class 也不要求所有成员都私有;显式的 public/private/protected 声明会覆盖默认值。默认成员访问、默认基类访问
虚函数的常见实现使用虚表,但具体虚表布局不是这两个关键字提供的标准保证。跨 C/C++ 边界共享数据时,还必须约定字段类型、对齐、填充与 ABI;把 class 改写成 struct,并不会自动获得可移植的文件或网络二进制格式。