Lesson 0009 · 结构体与面向对象入门

C++ 进阶 — 第 2 课

上节课(0008 STL):用 sort、set、map 处理一堆散装的数据。
这节课:把相关的数据捆成一个整体。名字、分数、身高本来就是同一个人身上的事,为什么要分三个变量存?这就是从“面向过程”走向面向对象的第一步。
三句话速览
  1. struct 把相关数据捆成一个新类型。
  2. 访问成员用点号,比如 s.score。
  3. class 多了成员函数,默认是私有的。

1. 为什么需要结构体

假设你要管理学生信息。没有结构体的写法:

vector<string> names;
vector<int> scores;
vector<double> heights;

// 三个数组同步操作,稍有不慎就对乱了
names.push_back("Alice"); scores.push_back(95); heights.push_back(1.68);
// Alice 的成绩到底是 scores[0] 还是 scores[2]?全靠记忆力!

有了结构体:

struct Student {
    string name;
    int score;
    double height;
};

Student s;
s.name = "Alice";
s.score = 95;
s.height = 1.68;

// 或者一行初始化
Student s2 = {"Bob", 87, 1.75};

一个人的所有信息绑在一起,不会散。

2. struct 语法

struct 类型名 {
    成员1类型 成员1名;
    成员2类型 成员2名;
    // ...
};                     // ← 别忘了分号!

// 访问成员用 . (点号)
Student s;
s.name = "Alice";
cout << s.score;

3. 结构体数组和 vector

// C 风格数组
Student arr[100];
arr[0] = {"Alice", 95, 1.68};
cout << arr[0].name;

// vector(更好)
vector<Student> students;
students.push_back({"Alice", 95, 1.68});
students.push_back({"Bob", 87, 1.75});

for (Student s : students) {
    cout << s.name << ": " << s.score << endl;
}

4. 结构体排序

// 按成绩从高到低排序
bool cmpByScore(Student a, Student b) {
    return a.score > b.score;       // a 的分数 > b 的分数 → a 排前面
}

sort(students.begin(), students.end(), cmpByScore);
sort + 自定义比较函数:返回 true 表示 a 应该排在 b 前面。想升序就 a.score < b.score。

5. 从 struct 到 class

struct 和 class 几乎一样,唯二区别:

对比项structclass
默认访问权限publicprivate
习惯用法纯数据的打包数据 + 行为绑定
class Student {
public:                              // class 默认 private,必须加 public
    string name;
    int score;

    void print() {                   // 数据和行为绑在一起
        cout << name << ": " << score;
    }
};

Student s;
s.name = "Alice";
s.print();                           // Alice: 95
什么时候用 class 而不是 struct?
如果有成员函数(比如 print()),用 class。
如果只是捆数据(比如坐标点、日期),用 struct。
这是 C++ 社区的惯例,编译器不管。

6. 实战示例:学生管理系统

struct Student {
    string name;
    int score;
};

int main() {
    vector<Student> stu;
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        string name; int score;
        cin >> name >> score;
        stu.push_back({name, score});
    }

    // 按分数降序排
    sort(stu.begin(), stu.end(),
         [](Student a, Student b) { return a.score > b.score; });

    for (auto& s : stu)
        cout << s.name << " " << s.score << endl;
}

7. 坑点清单

8. 小测

Q1. struct Point { int x, y; }; Point p = {3, 5}; 后 p.y 的值?
Q2. sort 的自定义比较函数什么时候返回 true?
Q3. struct 和 class 最关键的代码区别是什么?

9. 动手练习

题目 1:学生排序
定义 struct Student { string name; int score; };
读入 n 个学生,按分数从高到低排序输出。分数相同的按名字字典序升序。
提示:比较函数里先用 score 判断,相等时返回 a.name < b.name。
题目 2(洛谷 P1093 奖学金)
多关键字排序的经典题。总分 → 语文 → 学号的优先级排序。
提示:这题正好练“比较函数按优先级一层层往下判断”的写法。
卡住时的自查顺序
  1. 编译报错先看第一个:十有八九是 struct 结尾少了分号,或者成员名字拼错。
  2. “无法访问”类报错:class 里那个成员前面有 public: 吗?
  3. 排序结果顺序不对:把比较函数单独拿出来,手工算一下 cmp(a, b) 对两个样本该返回什么。
  4. 还不对:把 students 在排序前后各打印一遍,看看是数据本身错了还是排序错了。

本课小结

下一课预告
0010 递归 —— 函数调用自己。写法更贴近数学定义,也是树、图这些结构的核心工具。这一课会讲清楚“两要素”:终止条件、缩小规模,以及为什么递归太深会把栈撑爆。

← 上一课:STL 算法与容器 · 课程总览