Ch 21: 继承和多态
- 深入理解 Python 和 C++ 继承的本质差异
- 掌握虚函数和运行时多态的机制
- 理解纯虚函数和抽象类的设计意义
- 学会设计清晰的类层次结构
- 掌握虚析构函数的重要性和菱形继承问题
21.1 Python 继承回顾
Section titled “21.1 Python 继承回顾”Python 继承基础
Section titled “Python 继承基础”# Python - 简单直接的继承class Animal: def __init__(self, name): self.name = name
def speak(self): raise NotImplementedError("Subclasses must implement speak()")
class Dog(Animal): def speak(self): return f"{self.name} says woof!"
class Cat(Animal): def speak(self): return f"{self.name} says meow!"
# 使用d = Dog("Buddy")print(d.speak()) # Buddy says woof!
# 多态 - 运行时确定def make_speak(animal): print(animal.speak())
make_speak(Dog("Rex")) # Rex says woof!make_speak(Cat("Whiskers")) # Whiskers says meow!Python 的动态特性
Section titled “Python 的动态特性”# Python - 方法在运行时查找class Animal: pass
class Dog(Animal): pass
# 可以在运行时添加方法def bark(self): return "woof!"
Dog.speak = bark # 给 Dog 类添加方法
d = Dog()print(d.speak()) # woof!
# C++ 无法做到这种动态性Python 的 MRO(方法解析顺序)
Section titled “Python 的 MRO(方法解析顺序)”class A: def method(self): return "A"
class B(A): def method(self): return "B"
class C(A): def method(self): return "C"
class D(B, C): pass
d = D()print(d.method()) # B(C++ 的虚函数表是编译时决定的)
# Python 使用 C3 线性化算法print(D.__mro__) # 显示方法解析顺序21.2 C++ 继承基础
Section titled “21.2 C++ 继承基础”#include <string>#include <iostream>
// 基类class Animal {public: // 构造函数 Animal(const std::string& name) : name_(name) {}
// 虚函数 - 子类可以覆盖 virtual std::string speak() const { return "..."; }
// 虚析构函数(重要!) virtual ~Animal() = default;
protected: std::string name_;};
// 派生类class Dog : public Animal {public: Dog(const std::string& name) : Animal(name) {}
// override 说明符(C++11) std::string speak() const override { return name_ + " says woof!"; }};
class Cat : public Animal {public: Cat(const std::string& name) : Animal(name) {}
std::string speak() const override { return name_ + " says meow!"; }};
int main() { Dog d("Buddy"); Cat c("Whiskers");
std::cout << d.speak() << "\n"; // Buddy says woof! std::cout << c.speak() << "\n"; // Whiskers says meow!
// 多态:通过基类引用调用派生类实现 Animal& ref = d; std::cout << ref.speak() << "\n"; // Buddy says woof!
// 基类指针 Animal* p = &c; std::cout << p->speak() << "\n"; // Whiskers says meow!
return 0;}继承访问控制
Section titled “继承访问控制”#include <string>#include <iostream>
// public 继承 - 保持成员的访问级别class Base {public: int public_data;protected: int protected_data;private: int private_data; // 派生类不能直接访问};
class PublicDerived : public Base { // public_data 保持 public // protected_data 保持 protected // private_data 不可访问 void access() { public_data = 1; // OK protected_data = 2; // OK // private_data = 3; // ❌ 编译错误 }};
// protected 继承 - public 成员变为 protectedclass ProtectedDerived : protected Base { void access() { public_data = 1; // OK(变为 protected) protected_data = 2; // OK }};
// private 继承 - 所有成员变为 privateclass PrivateDerived : private Base { void access() { public_data = 1; // OK(变为 private) protected_data = 2; // OK(变为 private) }};Python 对比
Section titled “Python 对比”# Python 没有访问控制修饰符(只有约定)class Base: public_data = 1 _protected_data = 2 # 约定受保护,但仍可访问 __private_data = 3 # 名称改写,但仍可访问
class Derived(Base): def access(self): print(self.public_data) # 可访问 print(self._protected_data) # 可访问(约定不访问) print(self._Base__private_data) # 可访问,但不推荐21.3 虚函数和多态
Section titled “21.3 虚函数和多态”#include <iostream>#include <string>
class Base {public: // 虚函数 - 通过 vtable(虚函数表)实现多态 virtual std::string identify() const { return "Base"; }
// 非虚函数 - 静态绑定,编译时确定 std::string identify_nonvirtual() const { return "Base (non-virtual)"; }
virtual ~Base() = default;};
class Derived : public Base {public: std::string identify() const override { return "Derived"; }
std::string identify_nonvirtual() const { return "Derived (non-virtual)"; }};
int main() { Derived d;
// 静态类型和动态类型 Derived* dp = &d; // 静态类型 = Derived* Base* bp = &d; // 静态类型 = Base*,动态类型 = Derived*
// 虚函数 - 运行时动态分派 std::cout << dp->identify() << "\n"; // Derived(运行时确定) std::cout << bp->identify() << "\n"; // Derived(通过 vtable)
// 非虚函数 - 编译时静态分派 std::cout << dp->identify_nonvirtual() << "\n"; // Derived (non-virtual) std::cout << bp->identify_nonvirtual() << "\n"; // Base (non-virtual)
// 引用的情况 Base& br = d; std::cout << br.identify() << "\n"; // Derived(多态)
return 0;}虚函数表(vtable)概念
Section titled “虚函数表(vtable)概念”对象布局:┌─────────────────────────┐│ Base vptr (指向 vtable) │├─────────────────────────┤│ 基类成员 │└─────────────────────────┘
vtable:┌─────────────────┐│ Base::identify │ ← Base 的实现│ ~Base() │└─────────────────┘
Derived 对象:┌─────────────────────────┐│ Base vptr (指向 Derived vtable)├─────────────────────────┤│ 基类成员 │├─────────────────────────┤│ Derived 额外成员 │└─────────────────────────┘
Derived vtable:┌─────────────────┐│ Derived::identify│ ← 覆盖了 Base 的实现│ ~Base() │└─────────────────┘override 说明符
Section titled “override 说明符”#include <iostream>#include <string>
class Base {public: virtual void foo(int x) const { std::cout << "Base::foo(int)\n"; }
virtual void bar() const { std::cout << "Base::bar()\n"; }};
class Derived : public Base {public: // ✅ 正确:签名匹配 void foo(int x) const override { std::cout << "Derived::foo(int)\n"; }
// ✅ 正确:添加 const 也算覆盖 void bar() const override { std::cout << "Derived::bar()\n"; }
// ❌ 错误:签名不匹配 // void foo(double x) const override { } // 基类没有这个签名
// ❌ 错误:非虚函数不能 override // void baz() override { } // 基类没有 baz() 虚函数};
int main() { Derived d; d.foo(42); // Derived::foo(int) d.bar(); // Derived::bar()
return 0;}final 说明符(C++11)
Section titled “final 说明符(C++11)”// 禁止进一步重写class Base {public: virtual void foo() const { std::cout << "Base::foo()\n"; }};
class Middle : public Base {public: void foo() const override { // OK,可以重写 std::cout << "Middle::foo()\n"; }};
class Final : public Middle {public: // ❌ 错误:foo() 在 Middle 中已经是 final // void foo() const override { }};
// 或者直接让函数 finalclass A {public: virtual void method() const final { // 禁止子类重写这个方法 }};
// 类 final(C++17)class B final : public A { // ❌ 错误:B 不能被继承};21.4 纯虚函数和抽象类
Section titled “21.4 纯虚函数和抽象类”#include <iostream>#include <string>#include <vector>
// 抽象基类 - 包含纯虚函数class Shape {public: // 纯虚函数 = 0 使类成为抽象类 virtual double area() const = 0; virtual double perimeter() const = 0;
// 抽象类也可以有具体函数 void print() const { std::cout << "Area: " << area() << "\n"; }
virtual ~Shape() = default;};
class Rectangle : public Shape {public: Rectangle(double w, double h) : w_(w), h_(h) {}
double area() const override { return w_ * h_; }
double perimeter() const override { return 2 * (w_ + h_); }
private: double w_, h_;};
class Circle : public Shape {public: explicit Circle(double r) : r_(r) {}
double area() const override { return 3.14159 * r_ * r_; }
double perimeter() const override { return 2 * 3.14159 * r_; }
private: double r_;};
int main() { // ❌ 错误:不能实例化抽象类 // Shape s;
Rectangle r(3, 4); Circle c(1);
std::cout << "Rectangle area: " << r.area() << "\n"; std::cout << "Circle area: " << c.area() << "\n";
// 多态:存储在抽象类指针容器中 std::vector<Shape*> shapes = {&r, &c};
double total_area = 0; for (const Shape* s : shapes) { total_area += s->area(); } std::cout << "Total area: " << total_area << "\n";
return 0;}#include <string>#include <iostream>
// 接口 = 纯虚函数 + virtual 析构函数class Printable {public: virtual void print(std::ostream& os) const = 0; virtual ~Printable() = default;};
class Serializable {public: virtual std::string serialize() const = 0; virtual ~Serializable() = default;};
class Data : public Printable, public Serializable {public: Data(const std::string& content) : content_(content) {}
void print(std::ostream& os) const override { os << content_; }
std::string serialize() const override { return content_; }
private: std::string content_;};
int main() { Data d("Hello, World!");
Printable* p = &d; Serializable* s = &d;
d.print(std::cout); std::cout << "\n" << s->serialize() << "\n";
return 0;}抽象类作为参数和返回类型
Section titled “抽象类作为参数和返回类型”#include <iostream>#include <string>#include <vector>
class Animal {public: virtual std::string speak() const = 0; virtual ~Animal() = default;};
class Dog : public Animal {public: std::string speak() const override { return "Woof!"; }};
class Cat : public Animal {public: std::string speak() const override { return "Meow!"; }};
// 接受抽象类引用(推荐)void make_speak(const Animal& animal) { std::cout << animal.speak() << "\n";}
// 接受抽象类指针(当需要可选时)bool feed(Animal* animal) { if (!animal) return false; std::cout << "Feeding " << animal->speak() << "\n"; return true;}
// 返回抽象类指针(工厂模式)Animal* create_animal(const std::string& type) { if (type == "dog") return new Dog(); if (type == "cat") return new Cat(); return nullptr;}
// 返回智能指针(现代 C++)#include <memory>
std::unique_ptr<Animal> create_dog() { return std::make_unique<Dog>();}
int main() { Dog d; make_speak(d); // 多态
feed(&d); // OK feed(nullptr); // 安全返回 false
auto animal = create_animal("cat"); if (animal) { std::cout << animal->speak() << "\n"; }
auto dog = create_dog(); make_speak(*dog);
return 0;}21.5 虚析构函数
Section titled “21.5 虚析构函数”为什么要用虚析构函数
Section titled “为什么要用虚析构函数”#include <iostream>
// ❌ 错误的设计:非虚析构函数class Base {public: Base() { std::cout << "Base constructed\n"; }
~Base() { std::cout << "Base destructed\n"; } // 非虚!};
class Derived : public Base {public: Derived() { std::cout << "Derived constructed\n"; } ~Derived() { std::cout << "Derived destructed\n"; }};
int main() { Base* p = new Derived(); // Derived constructed // Base constructed
delete p; // Base destructed ← 危险!Derived 析构函数未被调用! // 资源泄漏!
return 0;}#include <iostream>
// ✅ 正确:虚析构函数class Base {public: Base() { std::cout << "Base constructed\n"; }
virtual ~Base() { std::cout << "Base destructed\n"; }};
class Derived : public Base {public: Derived() { std::cout << "Derived constructed\n"; } ~Derived() { std::cout << "Derived destructed\n"; }};
int main() { Base* p = new Derived(); // Derived constructed // Base constructed
delete p; // Derived destructed ← 正确!先调用派生类析构函数 // Base destructed
return 0;}// 只要类可能作为基类,就声明虚析构函数class Base {public: virtual ~Base() = default; // 最简单的方式};
// 抽象基类也需要虚析构函数class AbstractBase {public: virtual void pure_virtual() = 0; virtual ~AbstractBase() = default; // 必须!};21.6 多重继承
Section titled “21.6 多重继承”基本多重继承
Section titled “基本多重继承”#include <iostream>#include <string>
class Printable {public: virtual void print(std::ostream& os) const = 0; virtual ~Printable() = default;};
class Serializable {public: virtual std::string serialize() const = 0; virtual ~Serializable() = default;};
class Document : public Printable, public Serializable {public: Document(const std::string& content) : content_(content) {}
void print(std::ostream& os) const override { os << content_; }
std::string serialize() const override { return content_; }
private: std::string content_;};
int main() { Document doc("Hello");
Printable* p = &doc; Serializable* s = &doc;
doc.print(std::cout); std::cout << "\n" << s->serialize() << "\n";
return 0;}钻石继承问题
Section titled “钻石继承问题”#include <iostream>
// Base// / \// A B// \ /// C
class Base {public: int data = 42; virtual ~Base() = default;};
// 普通继承:A 和 B 各有一份 Base 子对象class A : public Base {};class B : public Base {};
class C : public A, public B { // C 有两份 data!};
int main() { C c; // c.data; // ❌ 编译错误!data 不明确(两份)
c.A::data = 1; // 明确指定 c.B::data = 2;
std::cout << "A::data = " << c.A::data << "\n"; std::cout << "B::data = " << c.B::data << "\n";
return 0;}虚继承解决菱形继承
Section titled “虚继承解决菱形继承”#include <iostream>
// Base// / \// A B// \ /// C
class Base {public: int data = 42; virtual ~Base() = default;};
// 虚继承:共享同一个 Base 子对象class A : virtual public Base {};class B : virtual public Base {};
class C : public A, public B { // C 只有一份 Base 子对象};
int main() { C c; c.data = 100; // OK!只有一份 data
c.A::data = 1; // 也可以,但效果相同(同一份) c.B::data = 2; // 覆盖上面的
std::cout << "data = " << c.data << "\n"; // 2
return 0;}虚基类构造函数规则
Section titled “虚基类构造函数规则”#include <iostream>#include <string>
class Base {public: Base(const std::string& name) : name_(name) { std::cout << "Base(" << name_ << ")\n"; }
protected: std::string name_;};
class A : virtual public Base {public: A() : Base("A") {} // 即使不直接使用,也需要调用};
class B : virtual public Base {public: B() : Base("B") {} // 即使不直接使用,也需要调用};
class C : public A, public B {public: // C 的构造函数需要直接调用虚基类构造函数 C() : Base("C") {} // 只有 C 需要调用};
int main() { std::cout << "Creating C:\n"; C c; // 只调用 Base("C") 一次 // Base(C) // A // B
return 0;}21.7 完整示例:图形系统
Section titled “21.7 完整示例:图形系统”#include <iostream>#include <vector>#include <memory>#include <cmath>
// 抽象基类:可绘制图形class Drawable {public: virtual void draw(std::ostream& os) const = 0; virtual double area() const = 0; virtual std::string name() const = 0; virtual ~Drawable() = default;};
// 抽象基类:可移动class Transformable {public: virtual void move(double dx, double dy) = 0; virtual void scale(double factor) = 0; virtual ~Transformable() = default;};
// 图形:同时继承多个接口class Shape : public Drawable, public Transformable {public: Shape(double x, double y) : x_(x), y_(y) {}
void move(double dx, double dy) override { x_ += dx; y_ += dy; }
std::string position() const { return "(" + std::to_string(x_) + ", " + std::to_string(y_) + ")"; }
protected: double x_; double y_;};
class Circle : public Shape {public: Circle(double x, double y, double r) : Shape(x, y), radius_(r) {}
double area() const override { return 3.14159 * radius_ * radius_; }
void draw(std::ostream& os) const override { os << "Circle at " << position() << " with radius " << radius_ << " (area=" << area() << ")"; }
std::string name() const override { return "Circle"; }
void scale(double factor) override { radius_ *= factor; }
private: double radius_;};
class Rectangle : public Shape {public: Rectangle(double x, double y, double w, double h) : Shape(x, y), width_(w), height_(h) {}
double area() const override { return width_ * height_; }
void draw(std::ostream& os) const override { os << "Rectangle at " << position() << " " << width_ << "x" << height_ << " (area=" << area() << ")"; }
std::string name() const override { return "Rectangle"; }
void scale(double factor) override { width_ *= factor; height_ *= factor; }
private: double width_; double height_;};
int main() { std::vector<std::unique_ptr<Drawable>> shapes;
shapes.push_back(std::make_unique<Circle>(0, 0, 5)); shapes.push_back(std::make_unique<Rectangle>(10, 10, 4, 3)); shapes.push_back(std::make_unique<Circle>(3, 3, 2));
std::cout << "=== All Shapes ===\n"; for (const auto& s : shapes) { s->draw(std::cout); std::cout << "\n"; }
// 计算总面积 double total_area = 0; for (const auto& s : shapes) { total_area += s->area(); } std::cout << "\nTotal area: " << total_area << "\n";
// 移动所有图形 std::cout << "\n=== After Moving ===\n"; for (auto& s : shapes) { // 向下转型为 Transformable if (auto* t = dynamic_cast<Transformable*>(s.get())) { t->move(5, 5); t->scale(1.5); } s->draw(std::cout); std::cout << "\n"; }
return 0;}21.8 章节总结
Section titled “21.8 章节总结”| 概念 | 说明 |
|---|---|
virtual | 虚函数,运行时多态 |
override | 明确覆盖基类虚函数(编译时检查) |
final | 禁止进一步重写(C++11) |
= 0 | 纯虚函数,类成为抽象类 |
virtual ~Base() | 虚析构函数,确保派生类析构函数被调用 |
| 多重继承 | 从多个基类继承 |
虚继承 virtual | 解决菱形继承问题 |
设计原则:
- 作为基类的类,声明虚析构函数
- 优先使用
override说明符 - 接口类只有纯虚函数
- 多重继承慎用,优先考虑组合
下章预告:ch22 学习运算符重载,实现自定义类型的运算符行为。