cs106l-lecture-5-6-inheritance-and-template-clas

Course: 斯坦福大学: C++标准库编程 | C++ Standard Library Programming


🎮 CS106L Lecture 5.6 — Inheritance and Template Classes

🏛 课程:Stanford CS106L — Standard C++ Programming Language

📍 主题:继承与模板类(Inheritance & Template Classes)

🎯 目标:理解**继承(动态多态)模板(静态多态)**的本质差异,并学习如何定义类模板,实现可扩展的泛型类型系统。


这节课是 CS106L 面向对象 (OOP)泛型编程 (Generic Programming) 的过渡讲。

从“类层次继承”走向“模板类型系统”,

C++ 的两种核心抽象方式在此汇合。

💡 “继承让对象共享行为;模板让行为适配类型。”


Inheritance|继承

Dynamic Polymorphism|动态多态

Template Class|模板类

Compile-time Polymorphism|静态多态

Concepts|类型约束(C++20)

Pure Virtual Function|纯虚函数

Virtual Destructor|虚析构

Template Parameter|模板参数


“继承让你在运行时决定做什么;

模板让你在编译时决定怎么做。”



📦 示例 1:抽象基类

class Shape {
public:
    virtual void draw() const = 0;  // 纯虚函数
    virtual ~Shape() = default;     // 虚析构,防止内存泄漏
};

📦 子类实现:

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Circle\n";
    }
};

📦 多态调用:

void render(const Shape& s) {
    s.draw();  // 动态绑定:运行时决定调用哪个函数
}

🧠 总结:

  • = 0 表示纯虚函数,类成为抽象类(不能实例化)
  • 通过基类指针或引用实现 运行时多态
  • 析构函数必须是 virtual,否则销毁基类指针会触发 UB

“继承是一种运行时契约。”


📦 示例 2:构造 / 析构执行顺序

class Base {
public:
    Base()  { std::cout << "Base ctor\n"; }
    virtual ~Base() { std::cout << "Base dtor\n"; }
};

class Derived : public Base {
public:
    Derived()  { std::cout << "Derived ctor\n"; }
    ~Derived() { std::cout << "Derived dtor\n"; }
};

📤 输出:

Base ctor
Derived ctor
Derived dtor
Base dtor

🧠 口诀:

“构造由上到下,析构由下到上。”


特性动态多态(继承)静态多态(模板)
绑定时间运行时 (runtime)编译期 (compile-time)
实现机制虚表 (vtable)模板实例化
性能稍慢零开销
灵活性支持运行时替换类型固定但快速
示例virtual draw()template<typename T>

📦 示例 3:两种多态

// 动态多态
Shape* s = new Circle();
s->draw(); // 运行时决定

// 静态多态
template<typename T>
void draw(T& shape) { shape.draw(); } // 编译时决定

🧠 结论:

“继承适合行为差异化;模板适合类型参数化。”


📦 示例 4:类模板定义

template<typename T>
class Box {
public:
    explicit Box(const T& value) : data(value) {}
    const T& get() const { return data; }
private:
    T data;
};

📦 使用示例

Box<int> b1(42);
Box<std::string> b2("CS106L");
std::cout << b1.get() << " " << b2.get() << "\n";

📤 输出:

42 CS106L

🧠 讲解:

  • 模板类是一种“类型工厂”。
  • 编译时根据传入的 T 自动生成具体类。
  • 模板的函数和类定义通常写在头文件中(模板需可见定义)。

📦 示例 5:继承模板类

template<typename T>
class Base {
public:
    void print() const { std::cout << "Base: " << value << "\n"; }
protected:
    T value = {};
};

template<typename T>
class Derived : public Base<T> {
public:
    void set(const T& val) { this->value = val; }
};

📦 使用:

Derived<int> d;
d.set(100);
d.print();  // 输出 Base: 100

🧠 注意:

  • 子类访问基类模板成员时需使用 this->Base<T>:: 显式指明作用域。

📦 示例 6:模板类默认参数

template<typename T, typename Container = std::vector<T>>
class Stack {
public:
    void push(const T& item) { data.push_back(item); }
    void pop() { data.pop_back(); }
    const T& top() const { return data.back(); }
private:
    Container data;
};

📦 使用:

Stack<int> s1;                   // 默认 std::vector<int>
Stack<int, std::deque<int>> s2;  // 自定义容器类型

🧠 讲解:

  • 模板参数可设置默认类型。
  • 支持多种底层容器结构的“策略模式”。

📦 示例 7:模板类实现

template<typename T, typename Compare = std::less<T>>
class PriorityQueue {
public:
    void push(const T& item) {
        data.push_back(item);
        std::sort(data.begin(), data.end(), comp);
    }
    const T& top() const { return data.back(); }
    void pop() { data.pop_back(); }
    bool empty() const { return data.empty(); }
private:
    std::vector<T> data;
    Compare comp;
};

📦 使用示例

PriorityQueue<int> pq;
pq.push(3);
pq.push(1);
pq.push(5);
std::cout << pq.top(); // 输出 5

🧠 讲解:

  • 通过模板参数传入比较器,实现通用排序逻辑。
  • 默认比较函数为 std::less<T>,行为与 std::priority_queue 一致。

📦 示例 8:静态 vs 动态复用

// 动态多态
class Animal { public: virtual void speak() const = 0; };
class Dog : public Animal { public: void speak() const override { std::cout << "Woof\n"; } };

// 静态多态(模板版)
template<typename T>
void speak(const T& animal) {
    animal.speak(); // 编译期多态
}

🧠 讲解:

  • 继承:关注对象“能做什么”(行为共性)
  • 模板:关注类型“是什么”(泛型约束)

“继承偏向抽象接口;模板偏向抽象类型。”


📦 示例 9:Concepts 示例

#include <concepts>

template<typename T>
concept Comparable = requires(T a, T b) {
    { a < b } -> std::convertible_to<bool>;
};

template<Comparable T>
bool smaller(const T& a, const T& b) {
    return a < b;
}

🧠 讲解:

  • concept 限制模板参数类型的可用操作。
  • 提升可读性与编译期错误诊断。

📦 调用:

smaller(3, 5);       // ✅ OK
// smaller("a", 1);  // ❌ 编译时报错:类型不满足 Comparable

“继承让行为复用,模板让类型泛化;

动态决定谁干活,静态决定谁能干。”


练习 1:抽象类继承

实现 Shape 抽象类与两个子类 Circle, Square,并通过多态绘制图形。

练习 2:模板类 Stack

实现 Stack<T> 模板类,支持 push/pop/top 并使用不同底层容器。

练习 3:比较器模板参数

PriorityQueue 添加自定义排序函数模板参数。

练习 4:Concepts 限制

定义 concept Printable 并编写仅接受支持 << 的类型的函数模板。


  • 抽象类中必须有 virtual ~Base(),否则多态删除会出错。
  • 若类不打算继承,应声明为 final
  • 模板类定义通常应放在 .h 文件中(编译期实例化需要可见定义)。
  • 避免模板函数重复定义冲突(可通过命名空间隔离)。
  • 优先使用 Concepts 代替 SFINAE,提升编译错误可读性。
  • 模板 + Lambda 可替代部分传统继承结构。

概念本质示例阶段
抽象类行为抽象virtual draw() = 0运行时
虚函数动态多态speak()运行时
模板类类型抽象template<typename T>编译期
模板参数泛型机制<T, Compare>编译期
Concepts约束模板类型requires Comparable编译期

“继承让你定义‘共性’,模板让你提炼‘模式’。

二者结合,是 C++ 的抽象之美。”