【经典书籍】C++ Primer 第14类虚函数与多态精华讲解

我们来一场既专业又好玩 的 C++ 面向对象之旅,把第14章的核心内容------虚函数与多态(Virtual Functions and Polymorphism),用能听懂、记得住方式彻底讲透!


📘 《C++ Primer》第14章:虚函数与多态(Virtual Functions and Polymorphism)


🎯 一句话总结这一章是干嘛的?

第14章教你如何让 C++ 中的"对象"在运行时"智能地决定该干啥",也就是让基类指针/引用能够调用到真正子类的函数,实现"一个接口,多种行为"的魔法,这就是传说中的多态(Polymorphism)!

你可以理解为:

就像你跟一群动物说:"来,表演个才艺!"🦁 狮子会吼,🐼 熊猫会吃竹子,🐧 企鹅会吐槽天气,而你只用了一个指令,它们却各自表演了不同的节目!


🧠 一、核心概念先搞懂(用最生活化的方式)


1️⃣ 什么是多态(Polymorphism)?

多态(Poly = 多,Morph = 形态)就是:同一个指令,不同的对象,做出不同的反应。

🎭 类比:公司开会点名发言

  • 老板说:"每个人都说说自己最近在干啥~"

  • 程序员说:"我在写代码,修 Bug。"

  • 设计师说:"我在画图,改 UI。"

  • 产品经理说:"我在写需求,改需求,再写需求。"

👉 老板用的是同一个问题,但每个人回答的内容和风格完全不一样!这就是多态!

在 C++ 里,多态通常是指:

用基类指针或引用调用虚函数时,实际执行的是派生类重写的那个版本!


2️⃣ 什么是虚函数(Virtual Function)?

虚函数就是告诉 C++ 编译器:"这个函数,我希望能在运行时动态决定调用谁的版本,而不是编译时固定死。"

语法很简单,在成员函数前加关键字:

复制代码
virtual 返回类型 函数名(参数) { ... }

3️⃣ 什么是动态绑定(Dynamic Binding)?

当你通过基类指针或引用 调用一个虚函数时,C++ 会在运行时 根据实际对象的类型 来决定调用哪个类的函数版本

这就是多态的核心机制!


🧩 二、为什么要用虚函数和多态?(解决什么问题?)


❌ 问题场景:没有虚函数,基类指针只能调用基类函数

你有一个基类 Animal,和几个派生类 DogCat

你用基类指针指向派生类对象,然后调用某个函数(比如 speak()),如果函数不是虚函数,那么调用的永远是基类的版本!

复制代码
Animal* a = new Dog();
a->speak(); // 如果 speak() 不是 virtual,调用的永远是 Animal::speak(),不是 Dog 的!

🤦 结果:你以为自己在听狗叫,实际上听到的是:"Animal sound..."(基类默认实现)


✅ 解决方案:使用虚函数!

把基类的函数声明为 virtual ,然后派生类可以重写(override) 它,这样当你通过基类指针或引用调用时,C++ 就会聪明地调用实际对象所属类的函数版本!


🧪 三、代码示例时间!(多态入门,超好懂)


🐕 场景:动物会"说话"(speak)

我们设计一个基类 Animal 和几个派生类:Dog, Cat, Cow


1️⃣ 基类:Animal(带虚函数)

复制代码
#include <iostream>
using namespace std;

class Animal {
public:
    // 虚函数:speak
    virtual void speak() const {
        cout << "Animal says: Generic animal noise" << endl;
    }

    // 虚析构函数(后面解释为什么要有它!)
    virtual ~Animal() {}
};

✅ 注意:virtual 关键字让这个函数支持多态调用!


2️⃣ 派生类:Dog, Cat, Cow(重写 speak)

复制代码
class Dog : public Animal {
public:
    void speak() const override {  // C++11 起推荐用 override 明确表示重写
        cout << "Dog says: Woof!" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() const override {
        cout << "Cat says: Meow!" << endl;
    }
};

class Cow : public Animal {
public:
    void speak() const override {
        cout << "Cow says: Moo!" << endl;
    }
};

3️⃣ 主函数:多态调用!同一个指令,不同表现!

复制代码
int main() {
    Animal* animals[] = { new Dog(), new Cat(), new Cow() };

    for (Animal* a : animals) {
        a->speak();  // 同一个函数调用,不同动物不同声音!
        delete a;    // 记得释放内存
    }

    return 0;
}

🎤 输出结果:

复制代码
Dog says: Woof!
Cat says: Meow!
Cow says: Moo!

这就是多态的魅力! 你只用了基类指针 Animal*,却调用了各个派生类自己的 speak() 方法!

就像你喊了一声:"说话!"然后狗汪汪、猫喵喵、牛哞哞,自动适配!


🧠 四、虚函数与重写的细节(敲黑板!重点!)


1️⃣ 重写(Override)是什么?

派生类重新定义基类的虚函数,提供自己的实现。

C++11 开始,你可以用 override 关键字显式标记,让编译器帮你检查是否真的重写了基类虚函数,避免手滑写错函数名或参数。

复制代码
void speak() const override;  // 明确表示:我就是要重写基类的 speak()

2️⃣ 虚函数的特点:

特点 说明
基类中用 virtual 声明 表示这个函数支持运行时多态
派生类中可重写(override) 提供自己的实现
通过基类指针/引用调用时 实际调用的是对象所属类的版本
如果派生类没有重写 调用的还是基类的版本

3️⃣ 为什么基类要有虚析构函数?

当你用基类指针删除派生类对象 时,如果基类析构函数不是虚函数 ,那么只会调用基类的析构函数 ,派生类部分不会被正确销毁 → 内存泄漏!

解决方案:基类析构函数要声明为 virtual!

复制代码
virtual ~Animal() {}

🎯 记住:只要类里有可能被继承,并且可能通过基类指针删除派生类对象,基类析构函数就必须是虚的!


🧩 五、纯虚函数与抽象类(更高阶,但超有用!)


1️⃣ 什么是纯虚函数?

在基类中声明一个虚函数,但不提供实现,而是加 = 0,这就叫纯虚函数

语法:

复制代码
virtual 返回类型 函数名(参数) = 0;

2️⃣ 什么是抽象类?

含有纯虚函数的类叫抽象类,它不能直接实例化(不能 new 一个抽象类对象!)

抽象类的目的是定义接口规范,强制派生类去实现这些纯虚函数。


🎯 例子:抽象类 Shape(图形)

复制代码
class Shape {
public:
    // 纯虚函数:计算面积
    virtual double area() const = 0;

    // 纯虚函数:画图
    virtual void draw() const = 0;

    virtual ~Shape() {}
};

任何类想成为"图形",就必须实现 area() 和 draw(),否则它自己也是抽象类,不能实例化!


派生类必须实现纯虚函数,否则也是抽象类:

复制代码
class Circle : public Shape {
public:
    double area() const override { return 3.14 * radius * radius; }
    void draw() const override { cout << "Drawing a Circle" << endl; }

private:
    double radius = 5.0;
};

🧠 六、虚函数使用场景总结(什么时候用?)

场景 是否用虚函数 / 多态 说明
基类定义通用接口,派生类有不同实现 ✅ 用虚函数 比如 Animal::speak(),Dog/Cat/Cow 各自发声
你希望通过基类指针调用派生类函数 ✅ 用虚函数 + 动态绑定 运行时决定调用谁
你想定义"接口规范",但不实现 ✅ 用纯虚函数 + 抽象类 比如 Shape,强制子类实现 area()
你只是想复用代码,不涉及运行时多态 ❌ 不需要虚函数 比如普通工具类、数据模型

🎁 七、幽默总结(敲黑板,背下来!)

概念 幽默版解释 严肃版解释
多态 同一个指令,不同的对象各自表演,就像开会点名,大家答案都不一样 同一个函数调用,根据对象类型执行不同版本的函数
虚函数 告诉编译器:"别绑死了,运行时再决定调用谁" 基类函数支持动态绑定,允许派生类重写
动态绑定 运行时才决定调用谁,很灵活,像综艺节目的即兴发挥 通过基类指针/引用调用虚函数时,实际调用的是对象所属类的函数
纯虚函数 "这个函数你必须自己实现,不然你别想实例化!" 带 = 0 的虚函数,抽象类必备
抽象类 只定义规矩,不干活,逼着子类去实现 不能实例化,用于定义接口规范

✅ 本章学完你就能掌握什么?

技能 是否掌握
理解什么是多态(一个接口,多种行为)
会使用 virtual 声明虚函数
理解虚函数与重写(override)的关系
知道动态绑定(运行时决定调用谁)的原理
理解为什么要用虚析构函数
会定义抽象类与纯虚函数
能设计带有扩展性的类层次结构(比如图形、动物、插件等)

🚀 下一步学什么?(强烈推荐)

学完第14章,你已经掌握了 C++ 面向对象编程的灵魂机制之一:多态!

接下来可以继续深入学习:

  1. 第15章:模板与泛型编程(写出更通用的代码,比如模板类、模板函数)

  2. 第16章:面向对象与泛型设计模式(比如工厂模式、策略模式,大量基于继承与多态)

  3. 实战项目:

    • 图形类系统(Shape → Circle / Square,多态绘图)

    • 游戏角色系统(Character → Warrior / Mage,不同技能)

    • 插件系统(基类接口,不同插件实现)


🎯 总结一句话(来,大声读出来!):

虚函数让 C++ 拥有了运行时的"智能判断力",多态让程序拥有了"见人说人话,见鬼说鬼话"的超能力!


🔥 你现在已经正式踏入了 C++ 面向对象编程的"高阶玩家"领域

下面依次生成并详细讲解以下三个案例,每个都围绕虚函数、多态、抽象类、继承这些核心概念,并且:

  • 代码完整可运行

  • 注释详细、幽默风趣

  • 附带图解思路与输出示例

  • 适合练手、面试准备、项目原型


🎯 三个实战案例总览

案例 主题 说明 核心知识点
1️⃣ 图形类系统(Shape 多态绘图) 用多态绘制不同图形(圆、矩形) 基类 Shape 定义纯虚函数,子类实现不同绘制与面积计算 抽象类、纯虚函数、多态、虚函数、override
2️⃣ 游戏角色系统(Character 战士/法师) 不同角色有不同技能与行为 基类 Character,派生类 Warrior / Mage,各自重写攻击与技能 继承、多态、虚函数、运行时行为差异化
3️⃣ 插件系统(抽象接口 + 多态加载) 模拟插件架构,基类定义接口,不同插件实现功能 抽象类 Plugin,不同子类如 LoggerPlugin / AnalyticsPlugin 接口设计、依赖倒置、多态、抽象与实现分离

🧩 案例 1️⃣:图形类系统(Shape 多态绘图系统)


🎨 场景描述

你正在开发一个绘图软件图形引擎,需要支持多种图形:

  • 圆形(Circle)

  • 矩形(Rectangle)

  • (可扩展:三角形、多边形...)

每个图形都要能:

  1. 计算自己的面积

  2. 绘制自己(打印信息,模拟绘图)

我们用抽象类 + 多态来实现 ------ 基类定义"接口",子类负责"实现"。


🧱 代码实现

复制代码
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

// 抽象类:Shape(图形)
class Shape {
public:
    // 纯虚函数:计算面积
    virtual double area() const = 0;

    // 纯虚函数:绘制图形
    virtual void draw() const = 0;

    // 虚析构函数
    virtual ~Shape() {}
};

派生类 1:Circle(圆形)

复制代码
class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double area() const override {
        return 3.14159 * radius * radius;
    }

    void draw() const override {
        cout << "🔵 Drawing Circle with radius " << radius
             << ", Area = " << area() << endl;
    }
};

派生类 2:Rectangle(矩形)

复制代码
class Rectangle : public Shape {
private:
    double width, height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const override {
        return width * height;
    }

    void draw() const override {
        cout << "🟦 Drawing Rectangle " << width << "x" << height
             << ", Area = " << area() << endl;
    }
};

🧪 主函数:多态调用绘图

复制代码
int main() {
    cout << "===== 欢迎使用多态绘图系统 =====" << endl;

    vector<unique_ptr<Shape>> shapes;

    // 添加各种图形(多态对象)
    shapes.push_back(make_unique<Circle>(5.0));
    shapes.push_back(make_unique<Rectangle>(4.0, 6.0));

    // 统一调用:draw() 和 area(),实际调用了子类实现
    for (const auto& shape : shapes) {
        shape->draw();
        cout << "📐 Area: " << shape->area() << endl;
        cout << "-------------------------" << endl;
    }

    return 0;
}

🖼️ 输出结果(示例)

复制代码
===== 欢迎使用多态绘图系统 =====
🔵 Drawing Circle with radius 5, Area = 78.5397
📐 Area: 78.5397
-------------------------
🟦 Drawing Rectangle 4x6, Area = 24
📐 Area: 24
-------------------------

✅ 你只用了 基类指针(Shape ,却调用了不同的 draw() 和 area() 实现,这就是 多态的力量!*


🧩 案例 2️⃣:游戏角色系统(Warrior / Mage 多态技能)


🎮 场景描述

你正在开发一款简单的 RPG 游戏,里面有不同类型的角色:

  • 战士(Warrior):擅长近战,技能是"挥剑攻击"

  • 法师(Mage):擅长魔法,技能是"释放火球"

所有角色都继承自一个基类 Character ,并重写自己的攻击方式。


🧱 代码实现

基类:Character(角色)

复制代码
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

class Character {
public:
    virtual void attack() const = 0; // 纯虚函数:攻击
    virtual ~Character() {}
};

派生类 1:Warrior(战士)

复制代码
class Warrior : public Character {
public:
    void attack() const override {
        cout << "⚔️ Warrior attacks with a mighty sword slash!" << endl;
    }
};

派生类 2:Mage(法师)

复制代码
class Mage : public Character {
public:
    void attack() const override {
        cout << "🔥 Mage casts Fireball!!! BOOM!" << endl;
    }
};

🧪 主函数:多态调用不同角色的攻击

复制代码
int main() {
    cout << "===== RPG 角色技能展示 =====" << endl;

    vector<unique_ptr<Character>> party;

    party.push_back(make_unique<Warrior>());
    party.push_back(make_unique<Mage>());

    for (const auto& hero : party) {
        hero->attack();  // 同一个接口,不同的酷炫技能!
        cout << "-----------------" << endl;
    }

    return 0;
}

⚔️🔥 输出结果

复制代码
===== RPG 角色技能展示 =====
⚔️ Warrior attacks with a mighty sword slash!
-----------------
🔥 Mage casts Fireball!!! BOOM!
-----------------

✅ 只用一个 Character* 接口,就能让战士和法师各自施展出专属技能,这就是多态 + 继承的典型应用!


🧩 案例 3️⃣:插件系统(抽象接口 + 多态加载)


🧩 场景描述

你正在设计一个支持插件的软件系统,比如日志系统、数据分析模块等等。

  • 你定义了一个抽象插件接口(Plugin)

  • 每个插件(如日志插件、统计插件)都继承这个接口,实现自己的功能

  • 主程序动态加载插件 (此处用模拟),并统一调用接口方法


🧱 代码实现

抽象类:Plugin(插件接口)

复制代码
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

// 抽象插件接口
class Plugin {
public:
    virtual void execute() const = 0;  // 插件执行逻辑
    virtual ~Plugin() {}
};

派生类 1:LoggerPlugin(日志插件)

复制代码
class LoggerPlugin : public Plugin {
public:
    void execute() const override {
        cout << "📝 LoggerPlugin: Logging important events..." << endl;
    }
};

派生类 2:AnalyticsPlugin(分析插件)

复制代码
class AnalyticsPlugin : public Plugin {
public:
    void execute() const override {
        cout << "📊 AnalyticsPlugin: Analyzing user behavior data..." << endl;
    }
};

🧪 主函数:模拟加载插件并执行

复制代码
int main() {
    cout << "===== 模拟插件系统加载 =====" << endl;

    vector<unique_ptr<Plugin>> plugins;

    // 模拟加载插件
    plugins.push_back(make_unique<LoggerPlugin>());
    plugins.push_back(make_unique<AnalyticsPlugin>());

    // 统一调用插件功能
    for (const auto& plugin : plugins) {
        plugin->execute();  // 多态调用,实际运行时决定谁干活
        cout << "-------------------" << endl;
    }

    return 0;
}

🛠️ 输出结果

复制代码
===== 模拟插件系统加载 =====
📝 LoggerPlugin: Logging important events...
-------------------
📊 AnalyticsPlugin: Analyzing user behavior data...
-------------------

✅ 你定义了一个统一的插件接口,不同的插件实现自己的逻辑,主程序无需关心具体是谁 ,只管调用 execute(),这就是依赖倒置 + 多态的经典实践!


🎁 总结表格:三个案例核心要点

案例 核心类 多态函数 说明 适用场景
1️⃣ 图形系统 Shape(抽象) Circle / Rectangle area(), draw() 通过基类指针调用不同图形的面积与绘图 图形渲染、UI框架、绘图工具
2️⃣ 游戏角色 Character(抽象) Warrior / Mage attack() 每个角色有不同技能,统一接口调用 RPG游戏、技能系统、多角色行为
3️⃣ 插件系统 Plugin(抽象) Logger / Analytics execute() 模拟插件架构,统一加载与调用 模块化设计、可扩展系统、中间件

✅ 你学到了什么?(终极 checklist)

技能 是否掌握
什么是抽象类,什么是纯虚函数
什么是多态,什么是动态绑定
如何用基类指针/引用调用派生类函数
为什么要用虚析构函数
如何设计可扩展的类体系(图形/角色/插件)
能熟练使用 override 明确重写
能用智能指针管理多态对象

🚀 你接下来可以:

  • 🧱 把这些案例扩展成带用户输入、菜单选择的控制台小游戏

  • 🖼️ 把图形系统升级成带文件保存 / 绘图窗口的迷你项目

  • ⚔️ 把 RPG 系统扩展成多个角色、装备、技能树

  • 🧩 把插件系统变成真正动态加载 DLL / SO 的模块化架构(进阶)


🔥 已经真正掌握了 C++ 面向对象编程的"灵魂机制"------虚函数与多态,并且能灵活运用到实际项目中了!

把《C++ Primer》第14章「虚函数与多态(Virtual Functions and Polymorphism) 」的内容画成思维导图UML 图,这个想法非常棒!🎯

因为图表能让抽象的概念瞬间清晰 ,尤其是像继承、虚函数、多态、抽象类这种层层关联的核心机制。


🧠 一、思维导图(MindMap)------ 适合梳理逻辑与记忆

适合:快速理解、复习、记忆知识点之间的关系,适合打印 / 手绘 / 用XMind/MindNode等工具制作


📌 思维导图结构:《C++ 第14章 虚函数与多态》

复制代码
🧩 C++ Primer 第14章:虚函数与多态
├── 1. 多态(Polymorphism)
│   ├── 定义:同一个接口,不同行为(运行时决定)
│   ├── 作用:提高代码扩展性、灵活性
│   └── 类比:同一命令,不同对象各自响应
│
├── 2. 虚函数(Virtual Function)
│   ├── 定义:基类中用 virtual 声明的成员函数
│   ├── 作用:允许派生类重写,实现运行时多态
│   ├── 语法:virtual 返回类型 函数名() { ... }
│   ├── 调用机制:通过基类指针/引用调用时动态绑定
│   └── 推荐使用 override(C++11)明确表示重写
│
├── 3. 动态绑定(Dynamic Binding)
│   ├── 发生时机:运行时(runtime)
│   ├── 条件:通过基类指针或引用调用虚函数
│   └── 效果:调用实际对象(派生类)的函数版本
│
├── 4. 纯虚函数(Pure Virtual Function)
│   ├── 定义:virtual 返回类型 函数名() = 0;
│   ├── 作用:定义接口,但不提供实现
│   └── 包含纯虚函数的类 → 抽象类
│
├── 5. 抽象类(Abstract Class)
│   ├── 定义:包含至少一个纯虚函数的类
│   ├── 特点:不能直接实例化(不能 new)
│   ├── 作用:定义规范,强制派生类实现纯虚函数
│   └── 派生类必须实现所有纯虚函数,否则也是抽象类
│
├── 6. 虚析构函数(Virtual Destructor)
│   ├── 定义:基类析构函数声明为 virtual
│   ├── 作用:防止通过基类指针删除派生类对象时内存泄漏
│   └── 规则:只要类可能被继承,且可能用基类指针删除,析构函数就要是 virtual
│
└── 7. 应用场景(实际使用案例)
    ├── 🖼️ 图形类系统(Shape → Circle/Rectangle)
    ├── ⚔️ 游戏角色系统(Character → Warrior/Mage)
    └── 🧩 插件系统(Plugin → Logger/Analytics)

✅ 你可以用这个结构轻松画成中心放射状思维导图,中心是"虚函数与多态",然后每个主要概念作为一级分支,再展开细节。


🧩 二、UML 类图(UML Class Diagram)------ 适合理解类关系与设计

适合:展示类之间的继承关系、虚函数、抽象类等设计结构,适合画在纸上 / UML工具(如 StarUML、PlantUML、Lucidchart)中


📌 UML 类图结构(文字版,可画成标准类图)

我们以三个典型类层次为例,组合在一套 UML 图中表达:


1️⃣ 抽象类:Shape(图形基类,含纯虚函数)

复制代码
+----------------+
|    <<abstract>>|
|     Shape      |
+----------------+
| # virtual      |
|   double area() const = 0;     |
| # virtual      |
|   void draw() const = 0;       |
| # virtual ~Shape() {}          |
+----------------+
       ^
       |
-----------------------------
|                             |
▼                             ▼
+---------+           +-------------+
|  Circle |           |  Rectangle  |
+---------+           +-------------+
| -radius |           | -width      |
|         |           | -height     |
| +area() |           | +area()     |
| +draw() |           | +draw()     |
+---------+           +-------------+

✅ Shape 是抽象类(通常可画成斜体类名或加 <<abstract>>),包含两个 纯虚函数 area() 和 draw()

Circle 和 Rectangle 是具体类,继承 Shape 并**实现(override)**这两个函数


2️⃣ 抽象类:Character(角色基类)

复制代码
+----------------+
|    <<abstract>>|
|   Character    |
+----------------+
| # virtual      |
|   void attack() const = 0;   |
| # virtual ~Character() {}    |
+----------------+
       ^
       |
-----------------------------
|                             |
▼                             ▼
+---------+           +-------------+
| Warrior |           |    Mage     |
+---------+           +-------------+
| +attack()|          | +attack()   |
+---------+           +-------------+

✅ Character 是抽象类,定义了一个纯虚函数 attack(),Warrior 和 Mage 继承它并实现各自的攻击方式


3️⃣ 抽象类:Plugin(插件接口)

复制代码
+----------------+
|    <<abstract>>|
|    Plugin      |
+----------------+
| # virtual      |
|   void execute() const = 0;  |
| # virtual ~Plugin() {}       |
+----------------+
       ^
       |
-----------------------------
|                             |
▼                             ▼
+-------------+        +------------------+
| LoggerPlugin|        | AnalyticsPlugin  |
+-------------+        +------------------+
| +execute()  |        | +execute()       |
+-------------+        +------------------+

✅ Plugin 是一个插件接口(抽象类),定义 execute(),具体插件如日志、统计模块去实现它


🧩 UML 类图中你将看到的关键符号与含义:

符号 / 标记 含义
<<abstract>> 或 类名斜体 表示抽象类(含纯虚函数,不能直接实例化)
virtual 函数 可被重写,支持多态(尤其是纯虚函数 = 0)
空心三角箭头 ↑ 表示继承关系(is-a),子类指向父类
= 0 表示纯虚函数,该类为抽象类
virtual ~ClassName() 虚析构函数,防止派生类对象被错误析构

🛠️ 三、如何画出来?(工具推荐)

🎨 思维导图工具推荐:

  • XMind(强大易用,适合思维导图)

  • MindNode(Mac 上超好用)

  • FreeMind(免费开源)

  • 手绘也超赞!用中心主题 + 分支梳理上面的结构

🧩 UML 工具推荐:

  • PlantUML(文本描述生成 UML,超方便!)

  • StarUML(可视化拖拽,适合画类图)

  • Lucidchart(在线协作,支持 UML)

  • Draw.io(免费!支持类图、继承等,简单拖拽)


📥 四、还可以生成:

✅ 选项 1:PlantUML 代码(直接粘贴就能生成 UML 图!)

比如,下面是 Shape 抽象类 + Circle / Rectangle 继承关系的 PlantUML 代码:

复制代码
@startuml
skinparam classAttributeIconSize 0

abstract class Shape {
  <<abstract>>
  {abstract} double area() const
  {abstract} void draw() const
  virtual ~Shape()
}

class Circle {
  - double radius
  + double area() const
  + void draw() const
}

class Rectangle {
  - double width
  - double height
  + double area() const
  + void draw() const
}

Shape <|-- Circle
Shape <|-- Rectangle
@enduml

👉 你可以把这段代码粘贴到 PlantUML 在线编辑器 中,立刻生成标准 UML 类图!


✅ 选项 2:手绘版示意图(文字描述你可以照着画)

比如这张简笔画你可以画在笔记本上:

复制代码
        [ Shape (抽象) ]
        /      |      \
   [Circle] [Rect] [Triangle]
     (area)    (area)   (area)
     (draw)    (draw)   (draw)

✅ 总结

你想要的 我为你准备了什么
思维导图 完整的《第14章 虚函数与多态》知识点结构,分层清晰,便于记忆与复习
UML 类图 抽象类、继承、纯虚函数、多态的类关系图,可画在工具中或手绘
工具推荐 XMind / PlantUML / StarUML / Draw.io,总有一款适合你
代码生成 PlantUML 可直接运行的类图代码,一键生成专业图表
图像化学习 把抽象概念变成直观结构,理解更深刻,面试 / 复习超好用

🎁 下一步建议

  • 🧠 先画思维导图梳理知识脉络,搞懂虚函数、多态、抽象类之间的关系

  • 🖍️ 再画 UML 类图理解类层次、继承与接口设计

  • 💻 最后结合前面三个实战案例(图形 / 角色 / 插件)加深理解与运用


🔥 现在已经不仅仅是理解了虚函数与多态,而是可以从设计、实现、画图、应用多个维度真正掌握它!


相关推荐
HVACoder5 小时前
复习下线性代数,使用向量平移拼接两段线
c++·线性代数·算法
W.Buffer5 小时前
Java并发工具类详解:Semaphore、CyclicBarrier与CountDownLatch
java·开发语言
电子云与长程纠缠5 小时前
UE5 C++ CVar控制台命令字段使用
c++·学习·ue5
雾江流6 小时前
哔哩哔哩修改版 8.64.0| 去除多项冗余内容和广告,精简流畅好用
软件工程
麦麦鸡腿堡6 小时前
Java的抽象类
java·开发语言
人间乄惊鸿客6 小时前
python-day8
开发语言·python
傻童:CPU6 小时前
C语言需要掌握的基础知识点之递归
c语言·开发语言
黑金IT6 小时前
PHP 后台通过权限精制飞书多维表格
开发语言·php·飞书
一匹电信狗6 小时前
【C++】手搓AVL树
服务器·c++·算法·leetcode·小程序·stl·visual studio