前面写过 C++ 类与构造函数,能把数据和函数打包成一个类了。但实际开发里经常出现一堆"长得差不多"的类:学生和老师都有姓名年龄,猫和狗都要吃饭睡觉。一个个重复写太累,这时就该继承出场了。

继承:子类直接拥有父类的成员

#include <iostream>
#include <string>
using namespace std;

class Person {                     // 基类,也叫父类
public:
    string name;
    int age;
    void introduce() {
        cout << "我叫" << name << ",今年" << age << "岁" << endl;
    }
};

class Student : public Person {    // 冒号表示继承 Person
public:
    string school;
    void study() {
        cout << name << "在" << school << "上课" << endl; // 直接用父类的 name
    }
};

int main() {
    Student s;
    s.name = "小林";       // name 来自 Person,不用重复定义
    s.age = 18;
    s.school = "三中";
    s.introduce();         // 输出:我叫小林,今年18岁
    s.study();             // 输出:小林在三中上课
    return 0;
}

class Student : public Person 一句,Student 就把 Person 的 public 成员全部拿过来了。公共的姓名、年龄、自我介绍只在 Person 里写一遍,所有"人"类型的类都能白嫖。

虚函数与重写:多态的核心

继承只是省代码,多态才是重头戏。看这个例子:

class Animal {
public:
    virtual void speak() {         // virtual:允许子类重写
        cout << "..." << endl;
    }
};

class Dog : public Animal {
public:
    void speak() override {        // override:告诉编译器我在重写父类函数
        cout << "汪汪" << endl;
    }
};

class Cat : public Animal {
public:
    void speak() override {
        cout << "喵喵" << endl;
    }
};

void makeSound(Animal& a) {  // 形参写成父类引用
    a.speak();               // 谁传进来就执行谁的版本
}

int main() {
    Dog d; Cat c;
    makeSound(d);  // 输出:汪汪
    makeSound(c);  // 输出:喵喵
    return 0;
}

同一个 makeSound 函数,传 Dog 出汪汪、传 Cat 出喵喵——同一个接口,不同对象各有各的行为,这就是多态。实现要点有两个:

  1. 父类函数前加 virtual,不开这个口子,a.speak() 就永远执行 Animal 的版本,输出两个 "..."
  2. 子类重写时补上 override,函数名拼错了编译器会直接报错,少排查半天

构造与析构的顺序

创建子类对象时,构造函数是先父类后子类;销毁时反过来,先子类后父类:

class A {
public:
    A() { cout << "A 构造" << endl; }
    virtual ~A() { cout << "A 析构" << endl; }  // 父类析构函数要加 virtual
};

class B : public A {
public:
    B() { cout << "B 构造" << endl; }
    ~B() { cout << "B 析构" << endl; }
};

int main() {
    B b;
    return 0;
}
// 输出顺序:A 构造 → B 构造 → B 析构 → A 析构

可以理解为:造房子先打地基(父类)再砌墙(子类),拆的时候先拆墙再挖地基。父类析构函数记得加 virtual,否则通过父类指针 delete 子类对象时,子类的析构函数不会执行,容易漏释放资源。

子类往父类构造函数传参

父类没有默认构造函数时,子类必须用初始化列表显式调用:

class Person {
public:
    string name;
    Person(string n) : name(n) {}          // 只有带参构造
};

class Teacher : public Person {
public:
    string subject;
    Teacher(string n, string sub) : Person(n), subject(sub) {}
    // 冒号后的 Person(n) 就是把 n 转交给父类构造函数
};

int main() {
    Teacher t("王老师", "数学");
    cout << t.name << "教" << t.subject << endl; // 输出:王老师教数学
    return 0;
}

小结

  • 继承用 class 子类 : public 父类 声明,子类白得父类的 public 成员
  • 父类函数加 virtual、子类重写加 override,配合父类引用就能玩转多态
  • 构造先父后子,析构先子后父,父类析构函数要写成 virtual
  • 子类通过初始化列表给父类构造函数传参

继承和多态是 C++ 面向对象的后半场,练熟之后再去看纯虚函数、抽象类和虚函数表,思路就顺了。