====== 第六章 继承与多态 ======
本章将深入探讨C++的面向对象核心特性——继承与多态,包括继承类型、虚函数、纯虚函数、抽象类等概念。
===== 6.1 继承基础 =====
=== 6.1.1 继承的概念 ===
继承允许创建基于现有类的新类,实现代码复用和层次化设计。
#include
#include
using namespace std;
// 基类(父类)
class Person {
protected:
string name;
int age;
public:
Person(string n, int a) : name(n), age(a) {
cout << "Person constructor" << endl;
}
void introduce() const {
cout << "I am " << name << ", " << age << " years old." << endl;
}
void eat() const {
cout << name << " is eating." << endl;
}
void sleep() const {
cout << name << " is sleeping." << endl;
}
};
// 派生类(子类)
class Student : public Person {
private:
string school;
int studentId;
public:
// 派生类构造函数
Student(string n, int a, string s, int id)
: Person(n, a), school(s), studentId(id) {
cout << "Student constructor" << endl;
}
void study() const {
cout << name << " is studying at " << school << "." << endl;
}
void showInfo() const {
introduce();
cout << "School: " << school << ", ID: " << studentId << endl;
}
};
// 另一个派生类
class Teacher : public Person {
private:
string subject;
double salary;
public:
Teacher(string n, int a, string sub, double sal)
: Person(n, a), subject(sub), salary(sal) {}
void teach() const {
cout << name << " is teaching " << subject << "." << endl;
}
};
int main() {
Student stu("Alice", 20, "MIT", 12345);
Teacher tea("Bob", 35, "Mathematics", 5000);
stu.introduce(); // 继承自Person
stu.eat(); // 继承自Person
stu.study(); // Student自己的方法
tea.teach();
return 0;
}
=== 6.1.2 继承类型 ===
| 继承方式 | 基类public成员 | 基类protected成员 | 基类private成员 |
|---------|--------------|------------------|----------------|
| public继承 | public | protected | 不可访问 |
| protected继承 | protected | protected | 不可访问 |
| private继承 | private | private | 不可访问 |
#include
using namespace std;
class Base {
public:
int publicVar = 1;
protected:
int protectedVar = 2;
private:
int privateVar = 3;
};
// public继承(最常用)
class PublicDerived : public Base {
public:
void test() {
cout << publicVar << endl; // OK,保持public
cout << protectedVar << endl; // OK,保持protected
// cout << privateVar << endl; // 错误!不可访问
}
};
// protected继承
class ProtectedDerived : protected Base {
public:
void test() {
cout << publicVar << endl; // OK,变为protected
cout << protectedVar << endl; // OK,保持protected
}
};
// private继承(实现继承)
class PrivateDerived : private Base {
public:
void test() {
cout << publicVar << endl; // OK,变为private
cout << protectedVar << endl; // OK,变为private
}
};
int main() {
PublicDerived pub;
cout << pub.publicVar << endl; // OK
ProtectedDerived pro;
// cout << pro.publicVar << endl; // 错误!变为protected
return 0;
}
===== 6.2 派生类的构造与析构 =====
=== 6.2.1 构造函数调用顺序 ===
#include
using namespace std;
class GrandParent {
public:
GrandParent() { cout << "GrandParent constructor" << endl; }
~GrandParent() { cout << "GrandParent destructor" << endl; }
};
class Parent : public GrandParent {
public:
Parent() { cout << "Parent constructor" << endl; }
~Parent() { cout << "Parent destructor" << endl; }
};
class Child : public Parent {
public:
Child() { cout << "Child constructor" << endl; }
~Child() { cout << "Child destructor" << endl; }
};
int main() {
cout << "Creating Child object:" << endl;
Child c;
cout << "Child object created" << endl;
// 输出顺序:
// GrandParent constructor
// Parent constructor
// Child constructor
// Child object created
// Child destructor
// Parent destructor
// GrandParent destructor
return 0;
}
=== 6.2.2 向基类构造函数传参 ===
#include
#include
using namespace std;
class Person {
protected:
string name;
int age;
public:
Person(string n, int a) : name(n), age(a) {
cout << "Person(" << name << ", " << age << ")" << endl;
}
};
class Student : public Person {
private:
int studentId;
public:
// 向基类构造函数传递参数
Student(string n, int a, int id)
: Person(n, a), studentId(id) {
cout << "Student(" << studentId << ")" << endl;
}
};
class GraduateStudent : public Student {
private:
string researchTopic;
public:
GraduateStudent(string n, int a, int id, string topic)
: Student(n, a, id), researchTopic(topic) {
cout << "GraduateStudent(" << researchTopic << ")" << endl;
}
};
int main() {
GraduateStudent gs("Alice", 25, 12345, "AI");
return 0;
}
===== 6.3 多态性 =====
=== 6.3.1 虚函数 ===
虚函数允许派生类重写基类的方法,实现运行时多态。
#include
using namespace std;
class Shape {
public:
// 虚函数
virtual double getArea() const {
cout << "Shape::getArea" << endl;
return 0;
}
// 虚函数可以有自己的实现
virtual void draw() const {
cout << "Drawing a shape" << endl;
}
// 非虚函数
void print() const {
cout << "This is a shape" << endl;
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
// 重写虚函数(override关键字C++11)
double getArea() const override {
return 3.14159 * radius * radius;
}
void draw() const override {
cout << "Drawing a circle with radius " << radius << endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double getArea() const override {
return width * height;
}
void draw() const override {
cout << "Drawing a rectangle " << width << "x" << height << endl;
}
};
int main() {
Circle c(5);
Rectangle r(4, 6);
// 通过基类指针实现多态
Shape* shape1 = &c;
Shape* shape2 = &r;
shape1->draw(); // 调用Circle::draw
shape2->draw(); // 调用Rectangle::draw
cout << "Area1: " << shape1->getArea() << endl; // 78.5397
cout << "Area2: " << shape2->getArea() << endl; // 24
// 非虚函数没有多态性
shape1->print(); // 调用Shape::print
return 0;
}
=== 6.3.2 虚函数表机制 ===
#include
using namespace std;
class Base {
public:
virtual void func1() { cout << "Base::func1" << endl; }
virtual void func2() { cout << "Base::func2" << endl; }
void func3() { cout << "Base::func3" << endl; }
};
class Derived : public Base {
public:
void func1() override { cout << "Derived::func1" << endl; }
void func2() override { cout << "Derived::func2" << endl; }
void func3() { cout << "Derived::func3" << endl; }
};
int main() {
cout << "sizeof(Base) = " << sizeof(Base) << endl; // 包含虚表指针
cout << "sizeof(Derived) = " << sizeof(Derived) << endl;
Base* b = new Derived();
// 通过虚表调用(运行时绑定)
b->func1(); // Derived::func1
b->func2(); // Derived::func2
// 非虚函数,编译时绑定
b->func3(); // Base::func3
delete b;
return 0;
}
===== 6.4 纯虚函数与抽象类 =====
=== 6.4.1 纯虚函数 ===
#include
#include
using namespace std;
// 抽象类
class Animal {
protected:
string name;
int age;
public:
Animal(string n, int a) : name(n), age(a) {}
// 纯虚函数 = 0
virtual void speak() const = 0;
virtual void move() const = 0;
// 普通虚函数
virtual void info() const {
cout << name << " is " << age << " years old." << endl;
}
// 虚析构函数(重要!)
virtual ~Animal() {
cout << "Animal destructor" << endl;
}
};
// 派生类必须实现所有纯虚函数
class Dog : public Animal {
public:
Dog(string n, int a) : Animal(n, a) {}
void speak() const override {
cout << name << " says: Woof!" << endl;
}
void move() const override {
cout << name << " is running on four legs." << endl;
}
~Dog() {
cout << "Dog destructor" << endl;
}
};
class Cat : public Animal {
public:
Cat(string n, int a) : Animal(n, a) {}
void speak() const override {
cout << name << " says: Meow!" << endl;
}
void move() const override {
cout << name << " is walking silently." << endl;
}
~Cat() {
cout << "Cat destructor" << endl;
}
};
class Bird : public Animal {
public:
Bird(string n, int a) : Animal(n, a) {}
void speak() const override {
cout << name << " says: Tweet!" << endl;
}
void move() const override {
cout << name << " is flying in the sky." << endl;
}
void info() const override {
Animal::info();
cout << name << " can fly!" << endl;
}
~Bird() {
cout << "Bird destructor" << endl;
}
};
int main() {
// Animal a; // 错误!不能实例化抽象类
Animal* animals[] = {
new Dog("Buddy", 3),
new Cat("Whiskers", 2),
new Bird("Tweety", 1)
};
for (Animal* animal : animals) {
animal->info();
animal->speak();
animal->move();
cout << endl;
}
// 如果基类析构函数不是虚函数,这里会内存泄漏!
for (Animal* animal : animals) {
delete animal;
}
return 0;
}
=== 6.4.2 接口类 ===
#include
using namespace std;
// 纯接口类(所有函数都是纯虚函数)
class IPrintable {
public:
virtual void print() const = 0;
virtual ~IPrintable() = default;
};
class ISerializable {
public:
virtual string serialize() const = 0;
virtual void deserialize(const string& data) = 0;
virtual ~ISerializable() = default;
};
// 多重继承实现多个接口
class Document : public IPrintable, public ISerializable {
private:
string title;
string content;
public:
Document(string t, string c) : title(t), content(c) {}
void print() const override {
cout << "Title: " << title << endl;
cout << "Content: " << content << endl;
}
string serialize() const override {
return title + "|" + content;
}
void deserialize(const string& data) override {
size_t pos = data.find('|');
title = data.substr(0, pos);
content = data.substr(pos + 1);
}
};
int main() {
Document doc("Report", "This is the report content.");
IPrintable* p = &doc;
ISerializable* s = &doc;
p->print();
string serialized = s->serialize();
cout << "Serialized: " << serialized << endl;
return 0;
}
===== 6.5 虚析构函数 =====
=== 6.5.1 为什么需要虚析构函数 ===
#include
using namespace std;
// 错误的例子:基类析构函数不是虚函数
class BadBase {
public:
~BadBase() {
cout << "BadBase destructor" << endl;
}
};
class BadDerived : public BadBase {
private:
int* data;
public:
BadDerived() { data = new int[100]; }
~BadDerived() {
delete[] data;
cout << "BadDerived destructor" << endl;
}
};
// 正确的例子:基类析构函数是虚函数
class GoodBase {
public:
virtual ~GoodBase() {
cout << "GoodBase destructor" << endl;
}
};
class GoodDerived : public GoodBase {
private:
int* data;
public:
GoodDerived() { data = new int[100]; }
~GoodDerived() override {
delete[] data;
cout << "GoodDerived destructor" << endl;
}
};
int main() {
cout << "=== Bad Example ===" << endl;
BadBase* bad = new BadDerived();
delete bad; // 只调用BadBase的析构函数,内存泄漏!
cout << "\n=== Good Example ===" << endl;
GoodBase* good = new GoodDerived();
delete good; // 先调用GoodDerived,再调用GoodBase的析构函数
return 0;
}
===== 6.6 多重继承 =====
=== 6.6.1 多重继承基础 ===
#include
using namespace std;
class Camera {
public:
void takePhoto() {
cout << "Taking a photo" << endl;
}
};
class Phone {
public:
void makeCall() {
cout << "Making a call" << endl;
}
};
class Smartphone : public Camera, public Phone {
public:
void browseInternet() {
cout << "Browsing the internet" << endl;
}
};
int main() {
Smartphone sp;
sp.takePhoto(); // 继承自Camera
sp.makeCall(); // 继承自Phone
sp.browseInternet(); // 自己的方法
return 0;
}
=== 6.6.2 菱形继承问题 ===
#include
using namespace std;
class Person {
public:
string name;
Person(string n) : name(n) {
cout << "Person constructor" << endl;
}
};
// 使用虚继承解决菱形问题
class Student : virtual public Person {
public:
int studentId;
Student(string n, int id) : Person(n), studentId(id) {
cout << "Student constructor" << endl;
}
};
class Employee : virtual public Person {
public:
int employeeId;
Employee(string n, int id) : Person(n), employeeId(id) {
cout << "Employee constructor" << endl;
}
};
// 虚继承时,由最派生类调用基类构造函数
class WorkingStudent : public Student, public Employee {
public:
WorkingStudent(string n, int sid, int eid)
: Person(n), Student(n, sid), Employee(n, eid) {
cout << "WorkingStudent constructor" << endl;
}
};
int main() {
WorkingStudent ws("Alice", 12345, 67890);
// 没有虚继承时,name会有两份拷贝
// ws.Student::name = "Bob";
// ws.Employee::name = "Charlie"; // 混乱!
// 使用虚继承后,只有一份name
cout << "Name: " << ws.name << endl;
return 0;
}
===== 6.7 运行时类型识别(RTTI) =====
#include
#include
using namespace std;
class Base {
public:
virtual ~Base() = default; // 需要至少一个虚函数
};
class Derived1 : public Base {};
class Derived2 : public Base {};
int main() {
Base* b = new Derived1();
// typeid:获取类型信息
cout << "Type of b: " << typeid(b).name() << endl; // Base*
cout << "Type of *b: " << typeid(*b).name() << endl; // Derived1
// dynamic_cast:安全向下转型
Derived1* d1 = dynamic_cast(b);
if (d1) {
cout << "Successfully cast to Derived1" << endl;
}
Derived2* d2 = dynamic_cast(b);
if (!d2) {
cout << "Cannot cast to Derived2" << endl;
}
// 引用类型的dynamic_cast失败时抛出异常
try {
Derived2& r2 = dynamic_cast(*b);
} catch (bad_cast& e) {
cout << "Cast failed: " << e.what() << endl;
}
delete b;
return 0;
}
===== 6.8 协变返回类型 =====
#include
using namespace std;
class Document {
public:
virtual Document* clone() const {
return new Document(*this);
}
virtual void print() const {
cout << "Document" << endl;
}
virtual ~Document() = default;
};
class PDF : public Document {
public:
PDF* clone() const override { // 协变返回类型
return new PDF(*this);
}
void print() const override {
cout << "PDF Document" << endl;
}
};
class Word : public Document {
public:
Word* clone() const override {
return new Word(*this);
}
void print() const override {
cout << "Word Document" << endl;
}
};
int main() {
Document* doc1 = new PDF();
Document* doc2 = new Word();
Document* copy1 = doc1->clone(); // 实际是PDF*
Document* copy2 = doc2->clone(); // 实际是Word*
copy1->print(); // PDF Document
copy2->print(); // Word Document
delete doc1;
delete doc2;
delete copy1;
delete copy2;
return 0;
}
===== 6.9 本章小结 =====
本章我们学习了:
* 继承的概念和三种继承类型(public、protected、private)
* 派生类的构造和析构顺序
* 虚函数和运行时多态
* 纯虚函数和抽象类
* 虚析构函数的重要性
* 多重继承和虚继承
* 运行时类型识别(RTTI)
* 协变返回类型
===== 练习题 =====
**基础练习:**
1. 设计一个图形类层次结构:基类Shape,派生类Circle、Rectangle、Triangle,都实现getArea()和draw()方法
2. 创建一个抽象类Employee,派生出SalariedEmployee和HourlyEmployee,实现不同的工资计算方式
3. 解释为什么基类析构函数应该是虚函数,举例说明
4. 使用多重继承设计一个类,同时实现IPrintable和ISerializable接口
**进阶练习:**
5. 实现一个游戏角色系统:基类GameCharacter,派生类Player、NPC、Enemy,使用多态实现不同的行为
6. 设计一个文档管理系统,支持多种文档格式(PDF、Word、Excel),使用工厂模式创建对象
7. 实现一个事件处理系统,使用观察者模式,基类Observer,派生类ConcreteObserver
**思考题:**
8. 比较静态多态(模板)和动态多态(虚函数)的优缺点
9. 虚函数表是如何实现的?研究vtable的布局
10. 什么时候应该使用纯虚函数,什么时候使用普通虚函数?
===== 参考答案 =====
**练习1参考:**
class Shape {
public:
virtual double getArea() const = 0;
virtual void draw() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double getArea() const override {
return 3.14159 * radius * radius;
}
void draw() const override {
cout << "Drawing circle" << endl;
}
};
**练习2参考:**
class Employee {
protected:
string name;
int id;
public:
Employee(string n, int i) : name(n), id(i) {}
virtual double calculatePay() const = 0;
virtual ~Employee() = default;
};
class SalariedEmployee : public Employee {
private:
double monthlySalary;
public:
SalariedEmployee(string n, int i, double s)
: Employee(n, i), monthlySalary(s) {}
double calculatePay() const override {
return monthlySalary;
}
};
class HourlyEmployee : public Employee {
private:
double hourlyRate;
int hoursWorked;
public:
HourlyEmployee(string n, int i, double r, int h)
: Employee(n, i), hourlyRate(r), hoursWorked(h) {}
double calculatePay() const override {
return hourlyRate * hoursWorked;
}
};
===== 下一章 =====
继续学习:[[第七章_运算符重载|第七章 运算符重载]]