🔷 第九章 面向对象编程基础
📄 一句话概括

面向对象不是一种新语法,而是"看程序的一种新角度"。 前面八章我们写的每一行代码,都属于"面向过程"的思路:先做什么、再做什么,一步一步往下推。从这一章开始,要换一副眼镜看同一个程序——把它看成一堆"对象"在互相配合:谁负责什么、谁向谁要东西、谁把结果交出去。这一章几乎不学新语法,学的是换角度。

📝 本节和前面几节的关系

回头看:第七章的指针与引用、第八章的结构体,其实已经在为这一章铺路了——结构体就是"类的雏形"(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 的。"数据和方法各归一摊",这就是面向过程写法写大以后最难维护的地方。

七、常见误区

  1. "面向对象就是一种新语法"——不是,它是组织程序的思路。同样一个程序,两种思路都能写;class 只是"面向对象思路"最方便的语法支撑。先懂思路,语法才有意义。
  2. "面向对象 = 写类"——不准确。用不用 class 不是判断标准:关键是"数据和方法有没有绑在一起、数据有没有被保护起来"。用 struct + 指针也能拼出半拉子对象(本节 Demo 就是),只是又别扭又不安全。
  3. "面向对象一定比面向过程好"——不成立。写一个"算圆面积"的小程序用不用对象?不用,直接一个函数最清楚。选择看规模与变化(9.2.1 会讲什么时候面向过程反而更合适)。
  4. "对象就是结构体"——差一步。结构体只打包数据;对象既打包数据,也打包操作数据的本事,而且不许外人乱碰数据。本节 Demo 就是停在"只差一步"的位置上。
  5. "这一章可以跳过,等学类的时候再一起看"——不建议。跳过概念直接学 class 语法,是零基础学 OOP 最常见的翻车方式——会写 class 但不知道为什么要写,最后只能靠背规则。
自测三问(点开对答案) 1. 面向过程和面向对象,最核心的区别是什么?——**面向过程按"步骤"组织程序**(先干什么后干什么),**面向对象按"对象"组织程序**(谁负责什么、谁跟谁配合)。 2. 一个"对象"由哪两部分组成?——**数据(属性)**和**操作这些数据的方法(行为)**,而且两者要**打包在一起、数据不许外人乱碰**。 3. 为什么说"结构体是类的雏形"?——结构体已经能**把一条记录的多个数据打包成一个整体**(第八章),只是还没有(1)**把操作数据的方法也包进去**、(2)**把数据锁起来**(`private`)。补上这两点就是 `class`。

相关笔记