面向对象不是一种新语法,而是"看程序的一种新角度"。 前面八章我们写的每一行代码,都属于"面向过程"的思路:先做什么、再做什么,一步一步往下推。从这一章开始,要换一副眼镜看同一个程序——把它看成一堆"对象"在互相配合:谁负责什么、谁向谁要东西、谁把结果交出去。这一章几乎不学新语法,学的是换角度。
回头看:第七章的指针与引用、第八章的结构体,其实已经在为这一章铺路了——结构体就是"类的雏形"(8.7 结尾那句预告在这里正式接上)。第八章我们把"一条记录"打包成了一个整体;这一章要在这个整体上再进一步:不光把数据打包,把"操作这些数据的本事"也一起包进去。9.2 讲面向过程与面向对象两种组织方式的区别,9.3 讲怎么用 UML 把设计画出来,9.4 收束。 学完这一章,你会明白"为什么下一章要学 class"。
一、程序写大了,卡在哪儿?
先看一个真实的困境。假设你写一个"学生成绩管理",一开始只有一个班 30 人,用第八章的结构体数组,写得挺顺:
Student cls[30]; // 30 条记录
double average(Student *p, int n); // 算平均分
void sortByScore(Student *p, int n); // 按分数排序
void printAll(Student *p, int n); // 打印全班
可需求一涨,麻烦就来了:
| 需求变了 | 结果 |
|---|---|
| 学生要加"手机号"字段 | 改 struct Student,然后所有用到学生数据的地方都跟着改 |
| 要支持"研究生"(比本科生多一个导师字段) | 复制一份结构体 + 复制一份函数,改两遍 |
| 别人要调用你的排序函数 | 他必须知道 cls 里数据的具体排布,你的内部实现全暴露在外 |
| 全班 30 个函数都在操作同一批数据 | 谁在什么时候改了数据,没人说得清 |
注意最后一条——这才是真正的病根。第八章我们已经把数据打包了,可数据是"敞开的":任何一个函数都能直接改 cls[i].score。数据和方法是两拨人,各干各的。
二、换一副眼镜:两种看程序的角度
同一个"成绩管理"程序,两种看法:
面向过程的看法(前面八章)——把它看成一串步骤:
① 录入 30 个学生 → ② 遍历求总分 → ③ 除以人数得平均分 → ④ 打印结果
重点是"先干什么、后干什么",程序的主线是流程。
面向对象的看法(本章起)——把它看成一群角色:
一个"班级"对象:它自己知道有哪些学生,它自己会算平均分、会排序、会打印
重点是"谁负责什么",程序的主线是角色之间的配合。
一句话分辨:面向过程问"这一步做什么";面向对象问"这事该谁干"。
三、"对象"到底是什么
对象 = 数据(属性) + 操作这些数据的本事(行为),两者打包在一起,谁也不许绕过它自己动手。
用"书"来打比方:
| 内容 | 大白话 | |
|---|---|---|
| 属性(数据) | 书名、作者、ISBN、是否在架上 | 这本书是什么状态 |
| 行为(方法) | 借出、归还、查位置 | 这本书能做什么 |
关键在于:"借出"这件事,只有"书"自己最清楚怎么做(它要把 在架上 改成 已借出)。于是这个动作就写在书自己身上,别人要用就喊一声"借出",不需要知道它内部是怎么改状态的。
这就叫封装(9.2.3 详讲)——数据藏起来,只留一个门。
四、为什么叫"面向对象"?
"面向"(oriented)的意思是"以……为中心":
- 面向过程:以过程/步骤为中心组织程序。
- 面向对象:以对象为中心组织程序。
所以"面向对象编程"(Object-Oriented Programming,简称 OOP)的核心动作是:
先把系统里有哪些"角色"找出来,把每个角色的数据和本事各自装好,再让它们互相配合。
五、本节的知识路线
本章是 C++ 从"面向过程"转向"面向对象"的分水岭,三篇内容这样分工:
| 小节 | 解决什么 |
|---|---|
| 9.1 面向对象概述(本节) | 为什么要换角度、什么是"对象" |
| 9.2 面向对象编程与面向过程编程 | 两种方式摆在一起比(9.2.1 面向过程/9.2.2 面向对象/9.2.3 三大特点) |
| 9.3 统一建模语言 | 换好角度之后,怎么把想法画给别人看(UML) |
重要提醒:这一章不学新语法,一行
class都不写。 它的任务是把脑子转过来——先明白"为什么要有对象",下一章再学"class这个关键字怎么写"。先有想法,再有语法,顺序反了会学成一堆背不住的规则。
六、动手 Demo:先用结构体"假装"一个对象
class 还没学,但我们其实已经能用第八章的结构体 + 函数,写出一个"很像对象"的东西——体会一下就好,看清它的别扭之处:
#include <iostream>
#include <cstring>
using namespace std;
struct Book { // ① 数据:这本书的状态
char title[50];
bool onShelf; // true = 在架上
};
// ② 本事:把"和这本书有关的操作"放在一起
void borrowBook(Book *b) { // 借出
if (b->onShelf) {
b->onShelf = false;
cout << "《" << b->title << "》已借出" << endl;
} else {
cout << "《" << b->title << "》不在架上" << endl;
}
}
void returnBook(Book *b) { // 归还
b->onShelf = true;
cout << "《" << b->title << "》已归还" << endl;
}
int main() {
Book b1;
strcpy(b1.title, "零基础学C++");
b1.onShelf = true;
borrowBook(&b1); // 借出
borrowBook(&b1); // 再借一次 → 应该借不到
returnBook(&b1); // 归还
borrowBook(&b1); // 又能借了
return 0;
}
看到什么算成功:
《零基础学C++》已借出
《零基础学C++》不在架上
《零基础学C++》已归还
《零基础学C++》已借出
但它有一个"不像对象"的地方:b1.onShelf = true; 这一句在 main 里照样能写——数据还是敞开的,任何人都能绕过 borrowBook 直接把状态改掉,改出个"在架上又借出去了"的怪物状态。
这正是缺的那一步:class 会提供一个叫 private 的锁,把 onShelf 关起来,只留 borrowBook/returnBook 两扇门。到那时,它才真正成为一个对象。记住这个"敞开的别扭感",下一章你一看 private 就懂它是干什么的。
30 秒实验:在 main 里加一行 b1.onShelf = false; 假装"书被偷走了",然后直接调 borrowBook(&b1)——你会发现程序完全不管你有没有"偷书",照样按自己的逻辑执行。因为函数只知道 onShelf 的值,不知道这个值是怎么变成 false 的。"数据和方法各归一摊",这就是面向过程写法写大以后最难维护的地方。
七、常见误区
- "面向对象就是一种新语法"——不是,它是组织程序的思路。同样一个程序,两种思路都能写;
class只是"面向对象思路"最方便的语法支撑。先懂思路,语法才有意义。 - "面向对象 = 写类"——不准确。用不用
class不是判断标准:关键是"数据和方法有没有绑在一起、数据有没有被保护起来"。用struct+ 指针也能拼出半拉子对象(本节 Demo 就是),只是又别扭又不安全。 - "面向对象一定比面向过程好"——不成立。写一个"算圆面积"的小程序用不用对象?不用,直接一个函数最清楚。选择看规模与变化(9.2.1 会讲什么时候面向过程反而更合适)。
- "对象就是结构体"——差一步。结构体只打包数据;对象既打包数据,也打包操作数据的本事,而且不许外人乱碰数据。本节 Demo 就是停在"只差一步"的位置上。
- "这一章可以跳过,等学类的时候再一起看"——不建议。跳过概念直接学
class语法,是零基础学 OOP 最常见的翻车方式——会写class但不知道为什么要写,最后只能靠背规则。
自测三问(点开对答案)
1. 面向过程和面向对象,最核心的区别是什么?——**面向过程按"步骤"组织程序**(先干什么后干什么),**面向对象按"对象"组织程序**(谁负责什么、谁跟谁配合)。 2. 一个"对象"由哪两部分组成?——**数据(属性)**和**操作这些数据的方法(行为)**,而且两者要**打包在一起、数据不许外人乱碰**。 3. 为什么说"结构体是类的雏形"?——结构体已经能**把一条记录的多个数据打包成一个整体**(第八章),只是还没有(1)**把操作数据的方法也包进去**、(2)**把数据锁起来**(`private`)。补上这两点就是 `class`。- ☐ 9.2 面向对象编程与面向过程编程(下一节)
相关笔记
- 结构体 —— 把一条记录打包:本章的地基
- 小结 —— 上一章收束:"结构体=类的雏形"
- 函数概述 —— 函数=把一段活儿打包成一条命令
- 面向对象编程与面向过程编程 —— 下一节:两种方式摆一起比
- 🔷 C++基础 · 索引 —— 全课路线图