C 中 struct 与 class 的区别从默认访问权限到设计语义一、引言看似相同实则不同在 C 中struct和class都可以用来定义类——它们都能包含数据成员、成员函数、构造函数、析构函数、继承关系甚至虚函数。这让很多初学者困惑既然两者功能几乎完全相同为什么 C 要保留两个关键字答案是它们几乎完全相同唯有一个技术差异但背后承载着完全不同的设计语义。理解这个差异和它所代表的设计思想是写出地道的 C 代码的重要一步。二、核心结论速览| 维度 | struct | class ||------|--------|-------|| 唯一技术差异 | 默认访问权限为public| 默认访问权限为private|| 默认继承方式 |public继承 |private继承 || 功能层面 | 完全相同(都能有成员函数、继承、虚函数等) | 完全相同 || 设计语义 | “数据的集合”强调数据结构 | “对象的封装”强调行为与接口 || 常见用途 | POD 类型、简单聚合数据、模板元编程 | 面向对象设计、封装复杂行为 || C 兼容性 | 与 C 的 struct 兼容 | C 中不存在 class |三、唯一的技术差异默认访问权限3.1 默认成员访问权限struct MyStruct { int data; // 默认 public void func() { } // 默认 public private: int secret; // 显式 private }; class MyClass { int data; // 默认 private void func() { } // 默认 private public: int publicData; // 显式 public }; int main() { MyStruct s; s.data 10; // OK: struct 成员默认 public s.func(); // OK MyClass c; // c.data 10; // 错误class 成员默认 private // c.func(); // 错误 c.publicData 10; // OK: 显式 public }3.2 默认继承方式struct Base { int x; }; // struct 默认 public 继承 struct DerivedStruct : Base { }; // 等价于: struct DerivedStruct : public Base { }; // class 默认 private 继承 class DerivedClass : Base { }; // 等价于: class DerivedClass : private Base { }; int main() { DerivedStruct ds; ds.x 10; // OK: public 继承Base::x 在 DerivedStruct 中仍是 public DerivedClass dc; // dc.x 10; // 错误private 继承Base::x 变成 private }3.3 差异总结图structclass定义类型使用哪个关键字?默认访问权限: public默认继承方式: public默认访问权限: private默认继承方式: privatestruct 适合:1. 简单数据聚合2. POD 类型3. 与 C 交互的数据结构class 适合:1. 封装复杂对象2. 面向对象设计3. 需要隐藏实现细节四、设计语义的区别4.1 struct数据的集合struct在 C 中延续了 C 语言的语义传统——它是数据的容器强调“这是什么数据”// struct 通常用于简单数据聚合 struct Point { double x; double y; }; struct Color { unsigned char r; unsigned char g; unsigned char b; unsigned char a; }; struct StudentInfo { std::string name; int age; double gpa; }; // 使用方式直接访问数据 Point p{3.0, 4.0}; double length std::sqrt(p.x * p.x p.y * p.y);4.2 class行为的封装class强调封装和信息隐藏关注“对象能做什么”而非“对象包含什么数据”// class 通常用于封装复杂行为 class BankAccount { private: std::string accountNumber; double balance; std::vectorstd::string transactionHistory; public: BankAccount(const std::string number) : accountNumber(number), balance(0.0) { } void deposit(double amount) { if (amount 0) { balance amount; transactionHistory.push_back(Deposit: std::to_string(amount)); } } bool withdraw(double amount) { if (amount 0 amount balance) { balance - amount; transactionHistory.push_back(Withdraw: std::to_string(amount)); return true; } return false; } double getBalance() const { return balance; } // accountNumber 和 transactionHistory 对外完全隐藏 };4.3 语义选择决策图主要是存放数据不变量很少或没有是否否: 全部数据公开是: 部分数据需要隐藏复杂行为逻辑需要严格封装模板元编程类型萃取需要定义新类型类型的主要用途是什么?是否需要与 C 代码交互?使用 struct保证 C 兼容性是否需要私有成员?使用 struct数据聚合使用 class封装内部状态使用 class面向对象设计使用 struct惯例: 所有成员 public五、C 语言兼容性5.1 C struct 与 C struct 的兼容// C 兼容的 structPOD 类型(Plain Old Data) struct CCompatible { int x; double y; char name[32]; }; // 可以在 C 和 C 之间安全传递 // C 独有的 struct 特性(C 不支持) struct CppOnly { int x; std::string name; // C 中没有 std::string void print() { } // C 中没有成员函数 virtual ~CppOnly() { } // C 中没有虚函数 };5.2 混合编程中的使用// C 头文件中 // data.h #ifdef __cplusplus extern C { #endif struct SensorData { int id; double value; unsigned long timestamp; }; void processSensorData(const struct SensorData* data); #ifdef __cplusplus } #endif // C 代码中直接使用 #include data.h SensorData d{1, 23.5, 1234567890}; processSensorData(d);六、常见使用场景6.1 struct 的典型场景场景一POD 类型// 纯粹的数据载体无行为 struct Vec3 { float x, y, z; }; static_assert(std::is_trivially_copyable_vVec3); // 确保是 POD场景二模板元编程中的类型萃取// STL 中广泛使用 struct templatetypename T struct is_pointer : std::false_type { }; templatetypename T struct is_pointerT* : std::true_type { }; // 使用 bool result is_pointerint*::value; // true场景三函数返回多个值struct SearchResult { bool found; size_t index; std::string value; }; SearchResult findItem(const std::vectorstd::string items, const std::string target) { for (size_t i 0; i items.size(); i) { if (items[i] target) { return {true, i, items[i]}; } } return {false, 0, }; }6.2 class 的典型场景场景一RAII 资源管理class FileHandle { private: FILE* fp; public: explicit FileHandle(const char* path) : fp(fopen(path, r)) { if (!fp) throw std::runtime_error(Cannot open file); } ~FileHandle() { if (fp) fclose(fp); } // 禁止拷贝允许移动... FileHandle(const FileHandle) delete; FileHandle operator(const FileHandle) delete; };场景二面向对象的多态体系class Shape { public: virtual double area() const 0; virtual void draw() const 0; virtual ~Shape() default; }; class Circle : public Shape { double radius; public: explicit Circle(double r) : radius(r) { } double area() const override { return 3.14159 * radius * radius; } void draw() const override { /* 绘制圆形 */ } };场景三复杂的业务逻辑封装class OrderProcessor { private: InventoryManager inventory; PaymentGateway payment; NotificationService notifier; bool validateOrder(const Order order); bool reserveInventory(const Order order); bool processPayment(const Order order); void sendConfirmation(const Order order); public: OrderProcessor(InventoryManager inv, PaymentGateway pay, NotificationService notif) : inventory(inv), payment(pay), notifier(notif) { } bool process(const Order order) { if (!validateOrder(order)) return false; if (!reserveInventory(order)) return false; if (!processPayment(order)) return false; sendConfirmation(order); return true; } };七、何时用 struct何时用 class| 使用 struct | 使用 class ||-------------|-----------|| 主要是公共数据的简单集合 | 有复杂的内部状态和不变量 || 没有或很少有不变式(不变量) | 需要严格控制数据修改方式 || 数据可以直接被外部读写 | 需要通过公共接口间接访问数据 || 与 C 代码互操作的数据结构 | 面向对象设计中的多态类型 || 模板元编程中的策略类/萃取类 | 资源管理类(RAII) || 所有成员都是 public | 有 private 或 protected 成员 |八、常见误区澄清8.1 误区一struct 不能有成员函数// struct 完全支持成员函数 struct Rectangle { double width; double height; double area() const { return width * height; } double perimeter() const { return 2 * (width height); } };8.2 误区二struct 不能有继承struct Base { int id; virtual ~Base() default; }; struct Derived : Base { std::string name; void print() { std::cout id : name std::endl; } }; // struct 的继承默认是 public8.3 误区三class 不能有 public 数据成员// class 当然可以有 public 数据成员 class Point3D { public: double x, y, z; // 完全合法 Point3D(double x_, double y_, double z_) : x(x_), y(y_), z(z_) { } }; // 但这通常不是 class 的推荐设计风格九、总结struct和class在 C 中的关系可以浓缩为以下几点技术上只有一个区别struct的默认访问权限和默认继承方式是publicclass是private。除此之外它们在功能上完全等价。语义上有明确分工struct暗示“这是一个数据集合”常用于 POD 类型、简单数据聚合、与 C 交互的数据结构class暗示“这是一个封装的对象”常用于面向对象设计、资源管理、复杂业务逻辑封装。设计上的指导原则如果类型主要是公开数据的集合几乎没有行为逻辑和不变量用struct如果类型有私有状态、需要保护不变量、通过接口控制数据访问用class。一种常见的一致风格用struct定义那些在概念上是“数据块”的类型用class定义那些在概念上是“活动对象”的类型如果一个struct开始需要构造函数来强制不变量考虑改用class正如 C 之父 Bjarne Stroustrup 所说“选择 struct 或 class 来表达你的设计意图。如果你只是想把一些数据放在一起用 struct如果你需要建立不变量用 class。”这个简单的原则能够帮助你和阅读代码的人快速理解每个类型的角色和预期用途。