数据和操作分开放,到底有什么问题?本节从你已经熟悉的 struct 出发,理解"面向对象"到底解决了什么。
假设你要写一个程序管理学生信息。最开始你可能这样写:
| 1 | struct Student { |
| 2 | string name; |
| 3 | int score; |
| 4 | }; |
| 5 | |
| 6 | // 打印学生信息——放在 struct 外面 |
| 7 | void PrintStudent(Student s) { |
| 8 | cout << s.name << " : " << s.score << endl; |
| 9 | } |
| 10 | |
| 11 | void SetScore(Student& s, int val) { |
| 12 | s.score = val; // ⚠️ 没有保护,传什么就存什么 |
| 13 | } |
这样写能跑,但随着程序变大,你会碰到三个麻烦:
s.score = -9999,没有任何拦截,数据随时可能被意外破坏。
在 C++ 里,struct 已经比 C 强大很多——它也可以定义成员函数。但它有一个先天不足:所有成员默认对外完全公开。
| 1 | struct Student { |
| 2 | string name; |
| 3 | int score; |
| 4 | |
| 5 | void Print() { // struct 也可以有函数 |
| 6 | cout << name << " : " << score << endl; |
| 7 | } |
| 8 | }; |
| 9 | |
| 10 | int main() { |
| 11 | Student s; |
| 12 | s.score = -999; // ⚠️ 没有任何阻拦,直接改掉了 |
| 13 | s.Print(); |
| 14 | } |
class 与 struct 的语法几乎一模一样,最关键的区别只有一个:成员默认是私有的(private),外部代码看不见也改不了。
| 1 | class Student { |
| 2 | private: // 🔒 外部无法直接访问 |
| 3 | string name; |
| 4 | int score; |
| 5 | |
| 6 | public: // 🔓 对外公开的接口 |
| 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 | |
| 15 | int main() { |
| 16 | Student s; |
| 17 | // s.score = -999; ❌ 编译错误!private 成员不能直接访问 |
| 18 | s.SetScore(95); // ✅ 只能通过公开接口修改 |
| 19 | s.Print(); |
| 20 | } |
public 接口与类交互;private 数据只有类内部的函数才能访问。s.score = -999 会让编译器报错,在写代码阶段就阻止了这类错误。
private 不是"这行代码会出错",而是"编译器直接拒绝编译"。错误在写代码的时候就被发现,而不是等到程序运行出 bug 才知道。这是一个很大的优势。
很多同学以为 struct 和 class 是两种完全不同的东西。其实在 C++ 里,它们几乎完全相同,唯一的区别是默认访问权限。
| 比较项 | struct | class |
|---|---|---|
| 成员默认权限 | public(对外公开) |
private(对外隐藏) |
| 能否定义成员函数 | ✅ 可以 | ✅ 可以 |
| 能否使用 public / private | ✅ 可以,但要手动写出来 | ✅ 可以,private 是默认值 |
| 继承时的默认权限 | public 继承 | private 继承 |
| 竞赛中的惯例用法 | 纯数据打包(存边、存坐标等) | 数据 + 行为封装在一起 |
struct 即可,代码更简洁。需要封装行为、保护数据时再用 class。两者混用也完全没问题——选更自然的那个就行。
讲了这么多,用一个生活中的例子来把这些概念串起来。
private 成员——用户看不见也摸不着;public 方法——这就是对外提供的"接口"。class 封装思想的核心:隐藏复杂性,对外只暴露简洁的接口。
private 成员变量(外部无法直接访问)public 成员函数(对外提供的操作)