【经典书籍】C++ Primer 第13类继承精华讲解
今天我们就来生动、易懂、接地气地详细讲解《C++ Primer》第13章的内容。
📘 《C++ Primer》第13章:类继承(Class Inheritance)
🎯 一句话概括本章是干嘛的?
第13章教你如何在 C++ 中让一个类(子类/派生类)继承另一个类(父类/基类)的特性,实现“代码复用 + 功能扩展”的超能力!
你可以理解为:
“儿子继承爸爸的财产和基因,同时还能发展自己的特长。”
→ 类继承就是:子类继承父类的成员(数据+函数),并可以新增或重写功能。
🧩 一、为什么要用继承?(现实意义)
想象这样一个场景:
你正在开发一个游戏,里面有各种各样的角色:
-
普通角色:有名字、血量、可以行走、可以说话
-
战士:是角色的一种,除了基本功能,还能攻击
-
法师:也是角色的一种,会施法
-
盗贼:会潜行、偷窃
如果为每个角色都从头写代码,那 name、hp、walk()、talk() 这些重复的代码你得写好几遍!
👉 继承就是让你把公共的部分(比如角色的基本属性和行为)放到一个“父类”里,然后“子类”继承它,并加上自己独有的技能!
🧠 二、核心概念(用大白话讲清楚)
我们来逐个击破第13章的几个关键概念,用生活化 + 代码示例的方式帮你彻底理解!
1️⃣ 什么是继承(Inheritance)?
继承就是:让一个类(子类/派生类)基于另一个类(父类/基类),自动拥有它的成员,并可以扩展或修改。
🧬 类比:家族遗传
-
父类(基类):就像你爸,他有名字、年龄、会走路、会说话
-
子类(派生类):就像你,你继承了他的名字、年龄、走路说话能力,但你还会打篮球、弹吉他
在 C++ 中:
// 父类(基类)
class Person {
public:void walk() { std::cout << "Walking...\n"; }void talk() { std::cout << "Talking...\n"; }
};// 子类(派生类)
class Student : public Person { // 👈 继承 Person
public:void study() { std::cout << "Studying...\n"; }
};
👉 Student “继承”了 Person 的 walk() 和 talk(),同时还能 study()!
2️⃣ 继承的语法:class 派生类 : 访问级别 基类
语法格式:
class 派生类名 : 访问修饰符 基类名 {// 派生类特有的成员
};
其中,访问修饰符可以是:
| 关键字 | 含义 |
|---|---|
public | 父类的 public 成员在子类中仍然是 public,protected 同理 |
protected | 父类的 public/protected 成员在子类中都变为 protected |
private | 父类的所有成员在子类中都变为 private(很少用) |
🔒 最常用的是 public 继承,表示“是一个(is-a)”的关系,比如“学生是一个人”。
3️⃣ 什么是“is-a”关系?(继承的核心思想)
继承表达的是一种“是一个”的关系,而不是“有一个”的关系。
| 正确用法(is-a) | 错误用法(has-a,应该用组合而不是继承) |
|---|---|
| 学生是一个人 | 车有一个引擎(应该用成员变量,不是继承) |
| 战士是一个角色 | 房子有一扇门 |
| 信用卡是一个账户 |
🔑 记住:能用继承的,一定是“子类 is a kind of 父类”。
🧩 三、继承后,成员的访问权限变化(重点!)
这是第13章一个比较绕,但非常重要的部分👇
父类的成员有三种访问级别:
| 访问级别 | 谁能访问? |
|---|---|
public | 任何人(类外也可以通过对象访问) |
protected | 本类 + 子类 可以访问,类外不行 |
private | 只有本类内部可以访问,子类也不行! |
继承方式会影响父类成员在子类中的访问权限!
| 父类成员原本是 | 使用 public 继承 | 使用 protected 继承 | 使用 private 继承 |
|---|---|---|---|
public | 在子类中仍是 public | 变为 protected | 变为 private |
protected | 在子类中仍是 protected | 仍是 protected | 变为 private |
private | 不可访问(子类也看不到) | 不可访问 | 不可访问 |
✅ 最佳实践:绝大多数情况下,使用
public继承!
🧩 四、派生类新增功能 & 重写(override)基类方法
1. 派生类可以新增自己的成员函数和数据
比如:
class Student : public Person {
public:void study() {std::cout << "Student is studying.\n";}
};
2. 派生类可以重写(override)基类的方法
比如,基类有个 speak(),子类想改成自己风格的“说话”:
class Person {
public:virtual void speak() { // 注意这里用了 virtual(后面会讲)std::cout << "I am a person.\n";}
};class Student : public Person {
public:void speak() override { // 重写基类的 speakstd::cout << "I am a student!\n";}
};
⚠️ 如果希望允许子类重写,基类方法一般要加
virtual,这样才支持多态(第13章后半部分和第14章重点!)
🧩 五、访问基类的成员(this-> 或 直接调用)
在派生类中,你可以直接访问基类的 public 和 protected 成员:
class Person {
protected:int age;
};class Student : public Person {
public:void printAge() {std::cout << "Age is: " << age << std::endl; // 直接访问基类的 protected 成员}
};
如果名字冲突,可以用 Person:: 来显式指明:
Person::someFunction();
🧩 六、继承体系下的构造与析构(重要!)
1. 构造顺序:先基类,后派生类
class Person {
public:Person() { std::cout << "Person()\n"; }
};class Student : public Person {
public:Student() { std::cout << "Student()\n"; }
};// 调用:
Student s;
// 输出:
// Person()
// Student()
2. 析构顺序:先派生类,后基类(倒过来)
// 析构顺序:
// ~Student()
// ~Person()
3. 如果基类有带参数的构造函数,派生类必须手动调用它:
class Person {
public:Person(std::string n) { std::cout << "Person: " << n << "\n"; }
};class Student : public Person {
public:Student(std::string n) : Person(n) { // 必须显式调用基类构造std::cout << "Student: " << n << "\n";}
};
🧩 七、继承与多态(为第14章铺路 👣)
虽然第13章主要讲继承基础,但它为下面这个超级重要的概念打下了基础 👇
多态(Polymorphism):让基类指针/引用可以指向不同的子类对象,并调用正确的函数。
关键就是:
-
基类方法加
virtual -
派生类可以重写(override)
-
通过基类指针或引用调用时,会根据实际对象类型来决定调用谁的版本
我们会在第14章深入,但第13章已经埋下伏笔 ✅
📚 本章小结(超精简版)
| 主题 | 说明 | 是否重点 |
|---|---|---|
| 继承概念 | 子类继承父类,实现代码复用和功能扩展 | ✅ 核心 |
| 继承语法 | class 派生类 : public 基类 | ✅ 必会 |
| is-a 关系 | 只有“是一个”关系才适合继承 | ✅ 理解本质 |
| 访问控制 | public/protected/private 继承影响基类成员在子类中的可见性 | ✅ 重要细节 |
| 成员访问 | 派生类可以直接访问基类的 public/protected 成员 | ✅ 常用 |
| 重写方法 | 派生类可以重写基类方法(通常配合 virtual) | ✅ 多态基础 |
| 构造/析构顺序 | 先基类构造,后派生类;析构则相反 | ✅ 重要 |
| 多态铺垫 | 为第14章的虚函数和动态绑定打基础 | ✅ 后续重点 |
🎁 学完这一章,你就能:
-
✅ 理解什么是继承,为什么要用继承
-
✅ 会写
class Derived : public Base这种语法 -
✅ 知道基类和派生类成员的访问规则
-
✅ 会正确构造和初始化继承体系中的对象
-
✅ 为后续学习多态、虚函数、抽象类打下坚实基础
📌 想深入?接下来你可以:
-
继续学习第14章:虚函数与多态(超级重要!)
-
动手写个小项目:比如游戏角色系统、图形类层次(Shape -> Circle / Square)
-
尝试自己设计一个简单的继承体系,比如:动物 -> 狗 / 猫
🔥 总结一句话:
继承让你站在巨人的肩膀上,不用重复造轮子,还能定制自己的超能力!
我们接着深入讲解《C++ Primer》第13章关于类继承(Class Inheritance)的更多细节,尤其是那些你容易忽略但又特别重要的点,还会用更生动的例子、图示和类比帮你彻底掌握!🔥
🧠 一、继承的核心目的回顾(再巩固一下)
继承的本质是:代码复用 + 功能扩展,建立“is-a(是一个)”的层次关系。
你可以把继承想象成一个家族技能树或者公司职位层级:
| 类比 | 说明 |
|---|---|
| 家族 | “程序员”是一个“员工”,程序员继承了员工的基本属性(姓名、工资),还有自己的技能(写代码) |
| 游戏角色 | “战士”是一个“角色”,继承了角色的基本属性(血量、名字),还有自己的技能(挥剑攻击) |
| 公司 | “经理”是一个“员工”,继承了员工的基本信息,同时还有管理职能 |
🏗️ 二、继承体系中的构造与析构(再深入)
这是第13章一个非常关键、面试常考,也是实际开发中容易出错的点 👇
✅ 1. 构造函数的调用顺序:先基类,后派生类
🧩 例子:
#include <iostream>
using namespace std;class Base {
public:Base() { cout << "Base Constructor\n"; }~Base() { cout << "Base Destructor\n"; }
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor\n"; }~Derived() { cout << "Derived Destructor\n"; }
};int main() {Derived d; // 创建一个派生类对象return 0;
}
🖨️ 输出结果:
Base Constructor ← 先调用基类构造
Derived Constructor ← 再调用派生类构造
Derived Destructor ← 析构时,先派生类
Base Destructor ← 后基类
❗ 结论:
构造顺序:基类 → 派生类
析构顺序:派生类 → 基类(倒过来!)
析构顺序很重要,比如基类里开了资源,得等派生类清理完后再收基类的资源!
✅ 2. 如果基类没有默认构造函数,派生类必须手动调用基类构造!
❌ 错误示范:
class Base {
public:Base(int x) { /*...*/ } // 基类没有默认构造
};class Derived : public Base {
public:Derived() { /*...*/ } // ❌ 编译错误!不知道怎么构造 Base
};
✅ 正确做法:在派生类构造函数的初始化列表里显式调用基类构造
class Base {
public:Base(int x) { cout << "Base(int): " << x << endl; }
};class Derived : public Base {
public:Derived() : Base(42) { // ✅ 手动调用基类构造函数cout << "Derived()\n";}
};
🎯 记住:派生类构造时,若基类没有默认构造,必须在初始化列表里显式调用基类的某个构造函数!
🧠 小知识:成员初始化顺序
-
先按继承顺序初始化基类
-
再按声明顺序初始化派生类自己的成员变量
-
最后执行派生类构造函数的函数体
🧩 三、继承与访问控制(再缕一缕)
这是第13章一个容易混淆的点,我们再清晰梳理一遍 👇
1. 基类成员有三种访问级别:
| 访问级别 | 谁能访问? |
|---|---|
public | 任何地方(对象、子类、函数等)都可以访问 |
protected | 只有本类和子类可以访问,外部代码不行 |
private | 只有本类内部可以访问,子类和外部都不行 |
2. 继承方式会影响基类成员在派生类中的访问权限
| 基类中成员原本是 | 使用 public 继承 | 使用 protected 继承 | 使用 private 继承 |
|---|---|---|---|
public | 在子类中仍是 public | 变为 protected | 变为 private |
protected | 在子类中仍是 protected | 仍是 protected | 变为 private |
private | 不可访问(子类也看不见) | 不可访问 | 不可访问 |
✅ 最常用:public 继承
表示一种 “是一个(is-a)”关系,并且保持基类接口的开放性。
class Base { public: void foo(); };
class Derived : public Base { }; // ✅ 推荐:foo() 在 Derived 中仍是 public
⚠️ 不推荐:private / protected 继承(特殊用途,一般用组合代替)
-
private继承:表示“根据基类实现”(is-implemented-in-terms-of),但一般用 成员变量(组合) 更合适 -
protected继承:几乎不用,了解即可
🧩 四、继承与函数重写(Override)——为多态铺路
1. 什么是重写(Override)?
子类可以重新定义(override)从基类继承来的同名函数,提供自己的实现。
🧩 例子:
class Animal {
public:void speak() { cout << "Animal sound\n"; }
};class Dog : public Animal {
public:void speak() { cout << "Woof!\n"; } // 重写了基类的 speak
};
但注意:如果基类函数不是 virtual 的,那不叫多态意义上的重写,只是隐藏。
2. 虚函数(virtual)与多态(下章重点,第14章)
如果希望通过基类指针或引用调用到真正子类的函数,就要用到:
virtual 返回类型 函数名(参数);
我们会在第14章深入讲解 虚函数、动态绑定、override关键字、final 等高级特性,第13章是为它打下基础!
🧩 五、继承体系中的名字查找(Name Lookup)
问题:如果子类和基类有同名函数或变量,会发生什么?
👉 C++ 名字查找规则是:先在派生类里找,找到了就停,不再去基类找!
🧩 例子:
class Base {
public:void func() { cout << "Base::func\n"; }
};class Derived : public Base {
public:void func() { cout << "Derived::func\n"; }
};int main() {Derived d;d.func(); // 调用的是 Derived::func
}
输出:
Derived::func
❗ 如果你还想调用基类的版本,可以显式指定:
d.Base::func(); // 明确调用基类版本
🧩 六、继承的经典应用场景(你一定要会!)
| 场景 | 说明 | 是否推荐用继承 |
|---|---|---|
| 游戏角色系统 | 比如“角色”基类,“战士”、“法师”等继承它 | ✅ 推荐,is-a 关系 |
| 图形类层次 | 比如“形状 Shape”基类,“圆形 Circle”、“矩形 Rectangle”继承 | ✅ 推荐,多态绘图 |
| 动物类系统 | “动物”基类,“狗”、“猫”等继承,有不同叫声 | ✅ 推荐 |
| 员工系统 | “员工 Employee”基类,“经理 Manager”、“工程师 Engineer”继承 | ✅ 推荐 |
| 汽车与引擎 | “车”有一个“引擎”,这是 has-a,不该用继承!应该用组合 | ❌ 不推荐,该用成员变量 |
🧠 判断标准:如果是“是一个”,用继承;如果是“有一个”,用组合(成员变量)!
🎓 七、本章学完你就能掌握什么?(能力清单 ✅)
| 技能 | 是否掌握 |
|---|---|
理解继承的基本概念与语法:class Derived : public Base | ✅ |
| 知道为什么要用继承:代码复用 + 功能扩展 | ✅ |
| 能正确使用 public/protected/private 继承 | ✅ |
| 理解继承后的成员访问权限变化 | ✅ |
| 知道构造函数与析构函数的调用顺序 | ✅ |
| 会手动调用基类构造函数(初始化列表) | ✅ |
| 理解函数重写(Override)与虚函数基础 | ✅(为第14章铺路) |
| 能区分“is-a”和“has-a”,知道何时用继承、何时用组合 | ✅ |
| 能设计简单的类继承体系(比如图形、动物、角色等) | ✅ |
📌 下一步学什么?强烈推荐 👇
✅ 继续学习第14章:虚函数与多态(Virtual Functions and Polymorphism)
这是 C++ 面向对象编程的核心中的核心!
你将学到:
-
什么是多态(同一接口,不同行为)
-
什么是虚函数(virtual function)
-
什么是动态绑定(运行时决定调用谁)
-
override 和 final 关键字
-
抽象类与纯虚函数(interface 的雏形)
🎯 继承让类有层次,多态让类有灵魂!
🎁 想进一步动手?可以试试这些小练习:
-
设计一个图形类层次:
-
基类:Shape(有 area() 和 draw())
-
派生类:Circle、Rectangle、Triangle
-
用基类指针调用不同子类的 draw()
-
-
设计一个游戏角色系统:
-
基类:Character(有 name, hp)
-
派生类:Warrior、Mage、Archer,各有不同技能
-
-
实现一个简单的员工管理系统:
-
基类:Employee
-
派生类:Manager、Engineer、Intern
-
✅ 总结一句话:
继承让你可以基于已有类快速构建新类,既省代码,又有层次,是面向对象设计的基石之一!
继续跟我一起探索 C++ 的面向对象魔法吧! 🚀🎯
既然想继续深入、动手实践,那我们就一鼓作气,继续生成更多实用、生动、可运行的内容,帮助你真正掌握 C++ 的类继承(第13章),并自然过渡到多态(第14章)的核心概念。
🎯 接下来我们生成什么?
为了学得更爽、更深入、更实战,我们规划了以下几个部分,层层递进,边学边练:
🧩 一、动手实践:图形类继承体系(Shape → Circle / Rectangle)
✅ 目标:用继承实现一个简单的图形类层次结构,练习基类与派生类关系、方法重写、多态基础。
🎨 1. 问题描述
我们设计一个图形系统,有如下类:
-
基类:Shape(形状)
-
属性:无(或可加名字)
-
方法:
virtual double area() const→ 返回面积(虚函数,便于多态) -
方法:
virtual void draw() const→ 打印图形信息(虚函数,用于演示多态)
-
-
派生类:Circle(圆形)
-
属性:半径 radius
-
重写 area() 和 draw()
-
-
派生类:Rectangle(矩形)
-
属性:宽 width,高 height
-
重写 area() 和 draw()
-
🧱 2. 代码实现(可直接复制运行)
#include <iostream>
#include <cmath>
using namespace std;// 基类:Shape
class Shape {
public:virtual ~Shape() {} // 虚析构函数,防止派生类对象通过基类指针删除时资源泄漏// 虚函数:计算面积virtual double area() const {cout << "Shape area (not implemented)" << endl;return 0.0;}// 虚函数:绘制图形(打印信息)virtual void draw() const {cout << "Drawing a generic Shape." << endl;}
};// 派生类:Circle
class Circle : public Shape {
private:double radius;public:Circle(double r) : radius(r) {}double area() const override {return M_PI * radius * radius;}void draw() const override {cout << "Drawing a Circle with radius " << radius<< ", area = " << area() << endl;}
};// 派生类: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 a Rectangle " << width << "x" << height<< ", area = " << area() << endl;}
};
▶️ 3. 测试代码(main 函数,演示多态)
int main() {// 创建不同图形对象Circle c(5.0);Rectangle r(4.0, 6.0);// 直接调用c.draw();r.draw();cout << "===== 通过基类指针实现多态 =====" << endl;// 基类指针指向派生类对象(多态的关键!)Shape* shapes[] = { &c, &r };for (Shape* s : shapes) {s->draw(); // 动态绑定,调用实际子类版本的 draw()cout << "Area: " << s->area() << endl; // 动态绑定,调用实际子类 area()cout << "-----------------" << endl;}return 0;
}
🖨️ 4. 输出结果(示例)
Drawing a Circle with radius 5, area = 78.5398
Drawing a Rectangle 4x6, area = 24
===== 通过基类指针实现多态 =====
Drawing a Circle with radius 5, area = 78.5398
Area: 78.5398
-----------------
Drawing a Rectangle 4x6, area = 24
Area: 24
-----------------
✅ 这就是多态的雏形! 通过基类指针调用虚函数,实际执行的是派生类自己重写的版本。
🧩 二、进阶练习:动物叫声系统(Animal → Dog / Cat)
✅ 目标:练习继承 + 重写 + 多态,模拟不同动物的叫声
🐾 类设计
-
基类:
Animal-
方法:
virtual void speak() const
-
-
派生类:
Dog→ 重写 speak() 输出 "Woof!" -
派生类:
Cat→ 重写 speak() 输出 "Meow!"
🧱 代码实现
#include <iostream>
using namespace std;// 基类
class Animal {
public:virtual void speak() const {cout << "Animal makes a sound" << endl;}virtual ~Animal() {}
};// 派生类:Dog
class Dog : public Animal {
public:void speak() const override {cout << "Dog says: Woof!" << endl;}
};// 派生类:Cat
class Cat : public Animal {
public:void speak() const override {cout << "Cat says: Meow!" << endl;}
};
▶️ 测试代码
int main() {Animal* animals[] = { new Dog(), new Cat() };for (Animal* a : animals) {a->speak(); // 多态调用delete a; // 记得释放!基类有虚析构,安全}return 0;
}
🐶🐱 输出结果
Dog says: Woof!
Cat says: Meow!
✅ 你可以看到,虽然我们用的是基类指针
Animal*,但实际调用的却是各自子类重写的 speak() 方法,这就是运行时多态(动态绑定)!
🧩 三、继承的最佳实践与常见误区(🎓知识点总结)
| 知识点 | 说明 | 是否掌握 |
|---|---|---|
| 继承语法 | class Derived : public Base | ✅ |
| public 继承 | 表示“是一个(is-a)”关系,最常用 | ✅ |
| 构造顺序 | 先基类构造,后派生类构造 | ✅ |
| 析构顺序 | 先派生类析构,后基类析构;基类应有虚析构函数 | ✅ |
| 成员访问 | 派生类可以直接访问基类的 public/protected 成员 | ✅ |
| 函数重写 | 派生类重写基类函数,通常要加 override(C++11起) | ✅ |
| 虚函数与多态 | 基类函数加 virtual,通过基类指针/引用调用实际子类函数 | ✅(第14章深入) |
| is-a vs has-a | 只有“是一个”关系才用继承,否则用组合(成员变量) | ✅ |
| 不要滥用继承 | 继承是强耦合,优先考虑组合,除非真是层次关系 | ✅ |
🧠 四、为什么要学这些?——因为你即将进入 C++ OOP 的核心!
| 后续内容 | 你将学到 |
|---|---|
| 第14章:虚函数与多态 | 深入 dynamic binding、override、final、抽象类、纯虚函数 |
| 第15章:模板与泛型编程 | 学会写通用代码,比如模板类、模板函数 |
| 第16章:面向对象设计模式 | 比如工厂模式、策略模式,很多都基于继承与多态 |
| 实际项目开发 | 游戏开发、GUI框架、业务系统都重度依赖继承与多态 |
✅ 你现在可以自豪地说:
我已经掌握了 C++ 类继承的核心:
我会用
class Derived : public Base写继承体系我知道构造和析构顺序
我理解 public/protected/private 继承的区别
我会重写基类函数,为多态打基础
我能设计图形类、动物类等实际案例
我理解 is-a 关系,知道什么时候用继承,什么时候用组合
✅ 总结一句话:
继承让你可以基于已有类快速构建逻辑相关的类,搭配虚函数与多态,让你的代码更灵活、更强大、更贴近真实世界的建模!
🚀 现在已经站在了 C++ 面向对象编程的核心门槛上,下一步就是掌握多态与虚函数,彻底释放面向对象的威力!
哈哈!咱们来点轻松幽默的,用 C++ 面向对象编程,特别是继承 + 多态,来搞一个 “动物园管理系统”,而且要整得有梗、有料、有趣味,让代码既实用又有娱乐精神!🦁🐼🐘🎪
🎪 项目名称:“疯狂动物园”管理系统(Crazy Zoo Management System)
🐒 一、项目背景(故事情节)
有一天,你穿越到了一个平行世界,成为了“疯狂动物园”的首席程序员兼园长助理。
这个动物园里有各种动物,比如:
-
🦁 狮子(会吼叫)
-
🐼 熊猫(会卖萌吃竹子)
-
🐘 大象(会喷水)
-
🦒 长颈鹿(会吃树叶)
-
🐧 企鹅(会吐槽天气冷)
你的任务是:用 C++ 面向对象的方式,设计一个“动物管理系统”,让每种动物都能自我介绍、做动作,而且代码还要优雅、可扩展、有幽默感!
🧠 二、设计思路(技术角度,依然严谨)
✅ 你会用到的核心技术:
| 技术 | 说明 | 幽默翻译 |
|---|---|---|
| 继承(Inheritance) | 每种动物都是“动物”这个基类的孩子 | 所有动物都说自己是“动物界优秀毕业生” |
| 多态(Polymorphism) | 不同动物调用相同函数,却做出不同行为 | 同一个指令:“表演一个才艺”,结果狮子吼、熊猫吃、企鹅抖 |
| 虚函数(virtual) | 基类定义“接口”,子类实现“个性” | 父类说:“来秀一个!”,子类各显神通 |
| 纯虚函数 + 抽象类 | 基类定义“必须做的事”,但不做具体实现 | 动物园规定:每个动物都必须会“自我介绍”,但爱咋介绍咋介绍 |
🏗️ 三、代码实现(幽默版动物园系统)
1️⃣ 第一步:定义基类 —— 动物(Animal)
#include <iostream>
#include <vector>
#include <memory>
using namespace std;// 基类:动物(抽象类,不能直接实例化)
class Animal {
public:// 纯虚函数:自我介绍(每个动物都必须会!不然开除!)virtual void introduce() const = 0;// 纯虚函数:表演才艺(每个动物都得有点本事)virtual void perform() const = 0;// 虚析构函数,防止内存泄漏virtual ~Animal() {// 动物走了,也要优雅地离开舞台}
};
🎭 解说:这个基类就像动物园园规,告诉所有动物:“想进园?先学会自我介绍和表演!”
2️⃣ 第二步:定义各种动物子类(幽默版)
🦁 狮子类(会吼)
class Lion : public Animal {
public:void introduce() const override {cout << "🦁 嗷呜~ 我是森林之王狮子!我的鬃毛比你的发量还多!" << endl;}void perform() const override {cout << "🦁 ROAR!!! (震耳欲聋的狮吼功,游客吓得捂耳朵)" << endl;}
};
🐼 熊猫类(卖萌吃竹子)
class Panda : public Animal {
public:void introduce() const override {cout << "🐼 咕噜~ 我是国宝熊猫,我每天只干三件事:吃、睡、卖萌!" << endl;}void perform() const override {cout << "🐼 啃竹子中... 咔嚓咔嚓,萌翻全场!" << endl;}
};
🐘 大象类(喷水绝技)
class Elephant : public Animal {
public:void introduce() const override {cout << "🐘 嗡~ 我是大象,鼻子长,力气大,还会喷水洗澡!" << endl;}void perform() const override {cout << "🐘 🌊 扭动鼻子,哗—— 一股清泉滋向天空!(游客欢呼)" << endl;}
};
🦒 长颈鹿类(优雅吃树叶)
class Giraffe : public Animal {
public:void introduce() const override {cout << "🦒 哒哒~ 我是长颈鹿,脖子最长,看风景不用排队!" << endl;}void perform() const override {cout << "🦒 🌿 优雅地嚼着树叶,俯视你们这些矮子~" << endl;}
};
🐧 企鹅类(吐槽天气)
class Penguin : public Animal {
public:void introduce() const override {cout << "🐧 哇凉哇凉~ 我是企鹅,生活在南极,你们那叫春天?我叫冬天!" << endl;}void perform() const override {cout << "🐧 🥶 摇晃着走路,‘这破天气,冻得我企鹅都穿羽绒服了!’" << endl;}
};
3️⃣ 第三步:主函数 —— 动物园表演开始!
int main() {cout << "========== 欢迎来到疯狂动物园!==========\n";// 用智能指针管理动物对象(防止内存泄漏,优雅!)vector<unique_ptr<Animal>> animals;// 各路动物入园报道animals.push_back(make_unique<Lion>());animals.push_back(make_unique<Panda>());animals.push_back(make_unique<Elephant>());animals.push_back(make_unique<Giraffe>());animals.push_back(make_unique<Penguin>());// 每个动物上台:先自我介绍,再表演才艺for (const auto& animal : animals) {animal->introduce(); // 自我介绍cout << "---- 接下来表演 ----" << endl;animal->perform(); // 才艺表演cout << "==========================" << endl;}cout << "🎉 感谢观看疯狂动物园的表演,动物们今晚加鸡腿! 🍗" << endl;return 0;
}
🎭 四、运行效果(幽默版输出,脑补画面 😂)
========== 欢迎来到疯狂动物园!==========
🦁 嗷呜~ 我是森林之王狮子!我的鬃毛比你的发量还多!
---- 接下来表演 ----
🦁 ROAR!!! (震耳欲聋的狮吼功,游客吓得捂耳朵)
==========================
🐼 咕噜~ 我是国宝熊猫,我每天只干三件事:吃、睡、卖萌!
---- 接下来表演 ----
🐼 啃竹子中... 咔嚓咔嚓,萌翻全场!
==========================
🐘 嗡~ 我是大象,鼻子长,力气大,还会喷水洗澡!
---- 接下来表演 ----
🐘 🌊 扭动鼻子,哗—— 一股清泉滋向天空!(游客欢呼)
==========================
🦒 哒哒~ 我是长颈鹿,脖子最长,看风景不用排队!
---- 接下来表演 ----
🦒 🌿 优雅地嚼着树叶,俯视你们这些矮子~
==========================
🐧 哇凉哇凉~ 我是企鹅,生活在南极,你们那叫春天?我叫冬天!
---- 接下来表演 ----
🐧 🥶 摇晃着走路,‘这破天气,冻得我企鹅都穿羽绒服了!’
==========================
🎉 感谢观看疯狂动物园的表演,动物们今晚加鸡腿! 🍗
🧠 五、你学到了什么?(技术 + 幽默双丰收)
| 技术点 | 你做到了啥 | 幽默解读 |
|---|---|---|
| 继承 | 所有动物都继承自 Animal 基类 | 所有动物都持证上岗,都是“动物界公务员” |
| 多态 | 同样的 introduce() 和 perform(),不同动物不同表现 | 同一个舞台,不同的秀,这才是动物园的魅力 |
| 纯虚函数 + 抽象类 | Animal 是抽象类,不能直接创建对象 | 园长说了:想混动物园?先学会自我介绍和表演! |
| override | 每个子类都重写了虚函数 | 每个动物都交出了自己的“绝活”作业 |
| 智能指针 | 用 unique_ptr 管理动物对象,避免内存泄漏 | 动物园讲究环保,连代码都绿色! |
🎁 六、Bonus:如何扩展这个系统?(脑洞时间 😎)
| 想法 | 实现思路 |
|---|---|
| 🦓 加个斑马? | 派生个 Zebra 类,自我介绍:“我是黑白条纹控!” |
| 🐒 加个猴哥? | 派生 Monkey 类,表演:“模仿游客拍照!” |
| 🎤 动物园主持人? | 单独搞个 Host 类,负责串场:“下面有请下一位动物明星!” |
| 📅 按时间表表演? | 用 map 或 vector 按时间调度动物表演 |
| 🏆 最佳表演奖? | 加个评分系统,游客可以打分,年底颁奖! |
✅ 总结一句话:
继承让动物们成为“一家人”,多态让它们个个是“戏精”,而 C++ 让这场疯狂动物园秀,既优雅又搞笑!
🚀 下一步挑战(如果你意犹未尽)
-
为动物添加 年龄、名字属性
-
让游客可以选择想看的动物
-
做个简单的菜单系统(控制台交互)
-
用 文件存储 动物信息,实现存档功能
-
把它做成带图形界面(Qt / GUI)的动物园游戏!
🎉
