用户工具

站点工具


cplus:第六章继承与多态

第六章 继承与多态

本章将深入探讨C++的面向对象核心特性——继承与多态,包括继承类型、虚函数、纯虚函数、抽象类等概念。

6.1 继承基础

6.1.1 继承的概念

继承允许创建基于现有类的新类,实现代码复用和层次化设计。

#include <iostream>
#include <string>
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 <iostream>
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 <iostream>
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 <iostream>
#include <string>
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 <iostream>
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 <iostream>
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 <iostream>
#include <string>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
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 <iostream>
#include <typeinfo>
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<Derived1*>(b);
    if (d1) {
        cout << "Successfully cast to Derived1" << endl;
    }
 
    Derived2* d2 = dynamic_cast<Derived2*>(b);
    if (!d2) {
        cout << "Cannot cast to Derived2" << endl;
    }
 
    // 引用类型的dynamic_cast失败时抛出异常
    try {
        Derived2& r2 = dynamic_cast<Derived2&>(*b);
    } catch (bad_cast& e) {
        cout << "Cast failed: " << e.what() << endl;
    }
 
    delete b;
    return 0;
}

6.8 协变返回类型

#include <iostream>
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;
    }
};

下一章

继续学习:第七章 运算符重载

cplus/第六章继承与多态.txt · 最后更改: 127.0.0.1