第13章 程序的“血缘关系”(类继承)
探索类与类之间的“血缘”——继承。学习如何重用代码,并掌握面向对象编程中最强大的特性:多态。
环节 1:代码重用与继承
继承允许一个新类(派生类)获得一个已存在类(基类)的所有特性,并添加自己的新功能。这是一种强大的代码重用机制,模拟了现实世界中的“is-a”(是一种)关系。
// 基类 "父辈"
class Pet {
private:
std::string name;
public:
Pet(const std::string& n) : name(n) {}
void show() const;
};
// 派生类 "子代"
class Dog : public Pet { // "is-a" Pet
private:
std::string breed;
public:
// 派生类构造函数,通过初始化列表调用基类构造函数
Dog(const std::string& n, const std::string& b)
: Pet(n), breed(b) {} // 把 n 传给 "父辈"
};
派生类构造模拟器 🏗️
`Dog` 对象在构造时,必须先调用 `Pet` 的构造函数来完成“父辈”部分的初始化。观察这个过程。
Dog 对象
Pet 基类部分
name: ...
breed: ...
> 等待构造...
环节 2:多态 —— 一种指令,多种行为
多态是OOP的魔力所在。通过**虚函数 (virtual function)**,我们可以使用一个基类指针来调用不同派生类对象的同名方法,程序会在**运行时**智能地选择正确的版本。这就是**动态联编**。
多态行为观察室 🔬
我们有一个基类指针 `Pet* ptr`。让它分别指向不同的动物对象,然后调用 `ptr->show()`,观察会发生什么。
🐈
Pet
🐕
Dog
🐅
Tiger
当前: Pet* ptr = &pet_obj;
> Console Output:
class Pet {
public:
// virtual 关键字开启了动态联编
virtual void show() const;
};
class Dog : public Pet {
public:
void show() const override; // 重写基类方法
};
class Tiger : public Pet {
public:
void show() const override; // 重写基类方法
};
// main 函数中
Pet* ptr;
Dog my_dog;
ptr = &my_dog;
ptr->show(); // 运行时发现ptr指向Dog, 调用Dog::show()
终点站:编程挑战
练习 1:宠物与狗
任务:
定义一个 `Pet` 基类和一个 `Dog` 派生类。`Dog` 类继承 `Pet`,并添加自己的 `breed`(品种)成员。为 `Dog` 定义构造函数,使用初始化列表正确地初始化基类部分。
预期知识点:
公有派生,基类构造调用,成员初始化列表。
点击查看参考答案
#include <iostream>
#include <string>
using namespace std;
class Pet {
private:
string name;
public:
Pet(const string& n) : name(n) {}
void show() const { cout << "Name: " << name; }
};
class Dog : public Pet {
private:
string breed;
public:
Dog(const string& n, const string& b) : Pet(n), breed(b) {}
void show() const {
Pet::show();
cout << ", Breed: " << breed << endl;
}
};
练习 2:多态报表
任务:
修改 `Pet` 基类中的 `show()` 方法,使其成为虚函数。使用 `Pet` 指针指向 `Dog` 对象,再次调用 `show()` 方法,验证动态联编是否生效。
预期知识点:
虚函数 `virtual`,动态联编,`override` 关键字。
点击查看参考答案
// Pet 类需要修改
class Pet {
public:
// 1. 将 show 声明为虚函数
virtual void show() const { /* ... */ }
// 2. 基类析构函数也应为虚函数
virtual ~Pet() {}
};
// Dog 类可以添加 override 关键字 (好习惯)
class Dog : public Pet {
public:
void show() const override { /* ... */ }
};
int main() {
Dog my_dog("Rocky", "Bulldog");
Pet* pet_ptr = &my_dog;
// 因为 show() 是虚函数, 这里会调用 Dog::show()
pet_ptr->show(); // 输出 Rocky 和 Bulldog
return 0;
}