前面写过 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 出喵喵——同一个接口,不同对象各有各的行为,这就是多态。实现要点有两个:
- 父类函数前加
virtual,不开这个口子,a.speak()就永远执行 Animal 的版本,输出两个 "..." - 子类重写时补上
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++ 面向对象的后半场,练熟之后再去看纯虚函数、抽象类和虚函数表,思路就顺了。