← 目录 / 第十二章 · 类与面向对象 / 12.1 从 struct 到 class

12.1 从 struct 到 class

数据和操作分开放,到底有什么问题?本节从你已经熟悉的 struct 出发,理解"面向对象"到底解决了什么。

本页目录
12.1.1 面向过程的困境

假设你要写一个程序管理学生信息。最开始你可能这样写:

C++
1struct Student {
2 string name;
3 int score;
4};
5
6// 打印学生信息——放在 struct 外面
7void PrintStudent(Student s) {
8 cout << s.name << " : " << s.score << endl;
9}
10
11void SetScore(Student& s, int val) {
12 s.score = val; // ⚠️ 没有保护,传什么就存什么
13}

这样写能跑,但随着程序变大,你会碰到三个麻烦:

💡
面向过程 vs 面向对象
我们之前写的程序,是把数据(struct)和操作数据的函数分开放的,这叫面向过程编程。
面向对象的思路则是:把数据和相关操作打包在一起,变成一个"对象"——对外只暴露必要的接口,内部细节全部隐藏。
12.1.2 struct 的先天不足

在 C++ 里,struct 已经比 C 强大很多——它也可以定义成员函数。但它有一个先天不足:所有成员默认对外完全公开

C++
1struct Student {
2 string name;
3 int score;
4
5 void Print() { // struct 也可以有函数
6 cout << name << " : " << score << endl;
7 }
8};
9
10int main() {
11 Student s;
12 s.score = -999; // ⚠️ 没有任何阻拦,直接改掉了
13 s.Print();
14}
⚠️
struct 的默认行为:成员变量对外完全暴露,任何地方的代码都能随意修改,很容易出现"数据被意外破坏"的 bug。
12.1.3 class 登场:装上保险锁

classstruct 的语法几乎一模一样,最关键的区别只有一个:成员默认是私有的(private),外部代码看不见也改不了。

C++
1class Student {
2private: // 🔒 外部无法直接访问
3 string name;
4 int score;
5
6public: // 🔓 对外公开的接口
7 void SetScore(int val) {
8 if (val >= 0 && val <= 100) // ✅ 在这里加校验
9 score = val;
10 }
11 int GetScore() { return score; }
12 void Print() { cout << name << " : " << score << endl; }
13};
14
15int main() {
16 Student s;
17 // s.score = -999; ❌ 编译错误!private 成员不能直接访问
18 s.SetScore(95); // ✅ 只能通过公开接口修改
19 s.Print();
20}
访问控制图解
外部代码 class Student private 🔒 name score 只有类内部 才能访问 public 🔓 SetScore() GetScore() Print() 外部调用 ✅ 允许 直接访问 score ❌ 编译错误 内部可访问 🧑 外部代码
外部代码只能通过 public 接口与类交互;private 数据只有类内部的函数才能访问。
试图在外部直接写 s.score = -999 会让编译器报错,在写代码阶段就阻止了这类错误。
📌
关键理解:private 不是"这行代码会出错",而是"编译器直接拒绝编译"。错误在写代码的时候就被发现,而不是等到程序运行出 bug 才知道。这是一个很大的优势。
12.1.4 struct vs class 对比

很多同学以为 structclass 是两种完全不同的东西。其实在 C++ 里,它们几乎完全相同,唯一的区别是默认访问权限

比较项structclass
成员默认权限 public(对外公开) private(对外隐藏)
能否定义成员函数 ✅ 可以 ✅ 可以
能否使用 public / private ✅ 可以,但要手动写出来 ✅ 可以,private 是默认值
继承时的默认权限 public 继承 private 继承
竞赛中的惯例用法 纯数据打包(存边、存坐标等) 数据 + 行为封装在一起
🏆
竞赛惯例:在 CSP-J / NOIP 中,如果只是把几个数据绑在一起(例如存边的起点、终点、权重),用 struct 即可,代码更简洁。需要封装行为、保护数据时再用 class。两者混用也完全没问题——选更自然的那个就行。
12.1.5 用售货机理解 class

讲了这么多,用一个生活中的例子来把这些概念串起来。

自动售货机(class) 内部机械 🔒 库存数量 出货机构 收款系统 (你看不到) 对外按钮 🔓 投币() 选商品() 取货() 🧑 用户 (外部代码) 可以按按钮 不能直接拿货❌ 只需按按钮 不用知道 里面怎么 运转的
售货机内部的机械构造对应 private 成员——用户看不见也摸不着;
机器外面的投币口、按钮、出货口对应 public 方法——这就是对外提供的"接口"。
这正是 class 封装思想的核心:隐藏复杂性,对外只暴露简洁的接口
🎯
记住这个类比:
售货机内部机械private 成员变量(外部无法直接访问)
对外的投币口/按钮public 成员函数(对外提供的操作)
用户 → 外部代码(只通过接口与对象交互)