第 2 章回答一个哲学问题:写数据结构之前,先想清楚「它对外承诺什么」。这个承诺就是 ADT(抽象数据类型)。
🎫 ADT:先定合同,再盖楼
ADT 只规定「能做什么」(接口),不规定「怎么做到」(实现)。书里最爱用的例子是股票交易记录:buy、sell、查询最赚钱的交易——用户只关心合同条款,不管内部是数组还是链表。
封装(encapsulation) 就是把「怎么做」藏进私有区域:
class StockDB {
private:
// 内部实现:随便换,用户无感
public:
void buy(...); // 合同条款
int bestProfit() const; // 合同条款
};
用户代码只依赖 public 部分——将来你把数组换成哈希表,用户代码一行不改,这就是接口的力量 ✨。
🧬 继承与多态:家族企业
- 继承(inheritance):子类自动获得父类的成员,「is-a」关系。
Circle : public Shape——圆是一种形状; - 多态(polymorphism):同一个
draw()调用,Circle对象画圆、Square对象画方块——运行时自动分派。
C++ 的实现机制是虚函数:
class Shape {
public:
virtual void draw() const = 0; // 纯虚 = 只定合同
virtual ~Shape() {} // 虚析构!多态基类必加 ⚠️
};
class Circle : public Shape {
public:
void draw() const override { /* 画圆 */ }
};
⚠️ 新手必踩的坑:通过基类指针 delete 派生类对象时,如果析构函数不是 virtual,只析构一半——内存泄漏 + 未定义行为。书里专门拿红字标了这条。
🎨 模板:一招吃遍所有类型
排序 int 数组的代码,排序 double 时要复制粘贴一遍?模板(template)让类型变成参数:
template <typename T>
T findMax(const vector<T>& arr) {
T best = arr[0];
for (const T& x : arr)
if (x > best) best = x;
return best;
}
编译器会按需生成 findMax<int>、findMax<string> 等版本——一套代码,处处编译。STL(C++ 标准模板库)就是这个思想的巅峰之作。
💥 异常:优雅地报警
数据结构经常遇到非法操作:弹空栈、越界访问。C++ 的答案是三件套:
throw std::out_of_range("stack is empty"); // 报警
try { s.pop(); } // 监听
catch (std::out_of_range& e) { /* 处理 */ } // 接住
设计原则:函数发现问题时「抛」,调用者决定怎么处理——责任划分清晰,程序不至于崩得莫名其妙。
🎯 本章通关清单
- [ ] 能用自己的话说出 ADT 和实现的区别
- [ ] 解释
virtual析构函数为什么必须存在 - [ ] 写出一个函数模板
- [ ] 理解 throw/try/catch 的责任分工
下一章开始,真正的数据结构登场:数组、链表、递归!🧱