面向过程和面向对象,是组织程序的两种方式:一个按步骤组织(时间轴视角),一个按角色组织(谁负责什么)。它们不是"旧"和"新"的关系,而是"小"和"大"的关系——程序小、流程清楚时,面向过程又快又直白;程序大、变化多时,面向对象能扛住改动。本节把两种方式摆在一起对比,为下面三篇分述铺垫。
9.1 说了"要换一副眼镜看程序",本节把这副眼镜和旧眼镜并排放在一起看:同一个需求,两种写法各是什么样、各有什么代价。接下来三篇就是本节的展开:9.2.1 单独讲面向过程、9.2.2 单独讲面向对象、9.2.3 讲面向对象的三大特点(封装/继承/多态)。前面八章写的所有代码,都是面向过程的例子——所以本节其实是"回看自己走过的路"。
一、同一个需求,两种看法
需求:图书馆借书——读者拿着书到柜台,管理员登记,读者带走。
面向过程的写法(把注意力放在"流程"上):
① 检查读者证是否有效
② 检查这本书是否在架上
③ 在借阅记录里加一条
④ 把书的状态改成"已借出"
⑤ 打印一张凭条
一眼看清"做了哪几步",像个流程图。
面向对象的写法(把注意力放在"角色"上):
读者对象:能出示证件、能借书
图书对象:知道自己在不在架上、能改自己的状态
图书馆对象:能登记借阅记录
重点是"系统里有哪些角色、各自负责什么",流程从角色之间的配合中自然产生。
分辨口诀:面向过程画出来的是一张流程图;面向对象画出来的是一张关系图(谁认识谁)。
二、两种组织方式的根本差异
| 面向过程 | 面向对象 | |
|---|---|---|
| 组织中心 | 步骤 / 函数 | 对象(数据 + 方法) |
| 数据与方法的关系 | 两分离:数据是素材,函数是工序 | 绑在一起:数据和方法属于同一个对象 |
| 数据的可见性 | 敞开,谁都能改 | 藏起来,只能通过对象自己的方法改 |
| 需求变了怎么办 | 顺着流程改,改动容易扩散 | 谁变了就改谁,改动范围收敛 |
| 思考顺序 | 先想"第一步做什么" | 先想"有哪些角色" |
| 适合的场景 | 流程清晰、变化少、规模小 | 角色多、变化多、规模大 |
| 典型代表 | C、早期的程序、算法题、脚本 | C++、Java、C#、Python 的 OOP 部分 |
最要紧的一行:面向过程以"动作"为第一视角(先做什么),面向对象以"角色"为第一视角(谁来做)。
三、为什么面向对象能扛住改动
这是两种思路最实际的差别,用 9.1 那个"学生成绩管理"的例子算一笔账:
| 变化 | 面向过程的代价 | 面向对象的代价 |
|---|---|---|
| 学生加一个"手机号" | 结构体改 1 处,但所有直接操作学生数据的函数都要复查(可能十几处) | 只改"学生"这个对象内部,外面调用的人不用动 |
| 新增"研究生"类型 | 复制一份结构体 + 复制一批函数 | 用继承:研究生"是一种"学生,只写多出来的部分(9.2.3) |
| 换一种排序算法 | 找到所有调用点,逐个确认 | 换掉对象内部的方法实现,外面的调用方式不变 |
核心原理:面向对象把"变化的范围"锁在了对象内部。就像一个电视机——你想换屏幕,不需要把家里的墙拆了。 因为电视机自己封装了"显示"这件事,你只通过遥控器(对外接口)跟它打交道。
四、别理解成"新打败旧"
这是零基础最容易走偏的地方,必须说清楚:
- 不是"面向对象更先进所以要用面向对象"。写一个"求 1 到 100 的和",用对象是自找麻烦——一个循环三行搞定,包成类是十几行。
- 两者常常混着用。真实程序里,对象负责"宏观的角色分工",对象内部的方法依然是面向过程的步骤。就像一家公司:部门分工是"面向对象",每个部门内部的作业手册还是"第一步第二步"。
- 面向过程的软肋是"规模"。10 行程序的流程改动,脑子里一转就改完了;10000 行程序的一处数据格式变更,可能牵动几十个函数。问题不在方法本身,在"程序长大的时候"。
选择的标准不是"哪个高级",而是"这个程序会长多大、会变多少次"。
五、本节之后三篇的分工
| 小节 | 讲什么 |
|---|---|
| 9.2.1 面向过程编程 | 单独看它:核心画面、优势、软肋、什么时候正合适 |
| 9.2.2 面向对象编程 | 单独看它:对象怎么来、类与对象的关系、消息怎么传 |
| 9.2.3 面向对象的特点 | 封装 / 继承 / 多态——三大特点各自解决什么问题 |
六、动手 Demo:同一件事,两种组织
没有新语法,用"伪代码"把两种组织方式的骨架各写一遍,重点是看"数据和方法的位置"。
面向过程版(数据和函数各站一队):
// —— 数据 ——
double balance; // 账户余额(谁都能碰)
// —— 工序 ——
void deposit(double amount) { balance += amount; } // 存
void withdraw(double amount) { balance -= amount; } // 取
void show() { cout << balance << endl; } // 查
int main() {
balance = 100;
deposit(50);
withdraw(30);
show(); // 120
balance = -9999; // ⚠️ 没人拦得住这一行
show(); // -9999(余额成了负数)
return 0;
}
面向对象版(数据和本事装进同一个"账户"里,余额不给外人碰):
class Account { // 先看得懂就好,下一章正式学
private:
double balance; // 余额藏起来(外人不能直接改)
public:
void deposit(double amount) { balance += amount; }
void withdraw(double amount) {
if (amount > balance) { cout << "余额不足" << endl; return; }
balance -= amount;
}
void show() { cout << balance << endl; }
};
int main() {
Account a;
a.deposit(100);
a.withdraw(150); // 余额不足 → 被挡住了
a.show(); // 100
// a.balance = -9999; // ❌ 编译不过:private 不许碰
return 0;
}
看到什么算成功:面向过程版最后打出 -9999(数据敞开的后果);面向对象版打出 余额不足 然后 100(数据被保护住了)。
30 秒实验:把面向对象版里那句 a.balance = -9999; 前面的 // 去掉,重新编译——编译器直接报错(大意是 balance 是私有成员,不能访问)。这个报错不是坏事,是保护:它把"可能出错的写法"挡在了编译阶段。面向对象的第一大好处,就是"让错误尽早暴露"。
七、常见误区
- "面向对象就是把函数塞进结构体里"——形式像,但关键在"数据有没有被保护"。如果成员全是公开的、外面想改就改,那只是"换了个摆放方式",没拿到面向对象最核心的好处。
- "面向过程已经过时了"——没有。操作系统内核、嵌入式、算法实现、脚本工具,大量场景依然首选面向过程。它是"就事论事"的正路。
- "用了
class就是面向对象"——不一定。写了一堆class,但方法全在操作外面的全局数据、成员全公开,那只是"穿着对象外衣的面向过程"。判断标准是"数据和方法有没有绑在一起、数据有没有被保护"。 - "面向对象会让程序更快"——恰恰相反,它带来的是"好维护"不是"跑得快"(甚至有一点点额外开销)。要拼极致速度的地方,反而是面向过程/底层写法的主场。
自测三问(点开对答案)
1. 面向过程和面向对象的"第一视角"分别是什么?——面向过程是**动作**(先做什么、再做什么);面向对象是**角色**(谁负责什么、谁跟谁配合)。 2. 为什么说面向对象"能扛住改动"?——因为它把**数据和操作数据的方法绑在对象内部**,变化被**锁在对象里**;对外只留接口。**改内部不影响外面的调用者。** 3. 小工具程序该选哪种写法?——**往往面向过程更合适**。选择标准是"程序会长多大、会变多少次";小、稳、流程清楚的需求,一个函数比一个类更直白。- ☐ 9.2.1 面向过程编程(下一节)
相关笔记
- 面向对象概述 —— 上一节:为什么要换角度
- 面向过程编程 —— 下一节:单独看面向过程
- 面向对象的特点 —— 三大特点:封装/继承/多态
- 结构体 —— 数据的第一次打包
- 🔷 C++基础 · 索引 —— 全课路线图