C++ 继承

继承(inheritance)是面向对象的核心机制之一。它让你基于已有的类创建新类,复用代码并扩展功能。更重要的是,继承配合虚函数实现了多态——同一个接口,不同的行为。这一章我们看 C++ 继承的全部要点。

一、继承的基本概念

继承就是"子类拥有父类的全部成员,再加自己的扩展":

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

// 基类(父类)
class Animal {
protected:                    // protected:子类能访问,外部不行
    string name;

public:
    Animal(const string& n) : name(n) {}

    void eat() {
        cout << name << " is eating" << endl;
    }
};

// 派生类(子类):public 继承 Animal
class Dog : public Animal {
public:
    // 子类构造函数:必须调用基类构造函数
    Dog(const string& n) : Animal(n) {}

    void bark() {
        cout << name << " says: Woof!" << endl;
    }
};

int main() {
    Dog d("Buddy");
    d.eat();      // 继承自 Animal:Buddy is eating
    d.bark();     // 自己的方法:Buddy says: Woof!
    return 0;
}

几个要点:

二、三种继承方式

C++ 继承有三种方式,改变基类成员在子类中的访问权限:

实际工程 99% 用 public 继承。后两种很少用,知道存在即可。

三、虚函数与多态(核心!)

多态(polymorphism)是 OOP 的精髓:同一个调用,根据对象实际类型执行不同代码。C++ 用 virtual 实现运行时多态:

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

class Shape {
public:
    // 没有 virtual:静态绑定(根据指针/引用的静态类型调用)
    // 有 virtual:动态绑定(根据实际对象的类型调用)—— 这是多态的关键
    virtual double area() {
        return 0;
    }
    virtual ~Shape() {}   // 虚析构!多态基类必须有
};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() override {     // override(C++11):显式标记重写
        return 3.14159 * r * r;
    }
};

class Rectangle : public Shape {
    double w, h;
public:
    Rectangle(double w, double h) : w(w), h(h) {}
    double area() override {
        return w * h;
    }
};

int main() {
    // 用基类指针指向子类对象——多态
    Shape* s1 = new Circle(5);
    Shape* s2 = new Rectangle(3, 4);

    cout << s1->area() << endl;   // 78.5398(调 Circle::area)
    cout << s2->area() << endl;   // 12(调 Rectangle::area)

    // 用 vector 管理多个不同形状
    vector<Shape*> shapes = {s1, s2};
    for (Shape* s : shapes) {
        cout << s->area() << endl;
    }

    delete s1;
    delete s2;
    return 0;
}

关键点:

四、override 与 final(C++11)

C++11 引入的两个关键字,让虚函数重写更安全:

#include <iostream>
using namespace std;

class Base {
public:
    virtual void f() { cout << "Base::f" << endl; }
    virtual void g() { cout << "Base::g" << endl; }
};

class Derived : public Base {
public:
    // override 让编译器帮你检查"是否真的重写了基类的虚函数"
    // 写错签名(参数、const、名字)会编译错误
    void f() override { cout << "Derived::f" << endl; }  // OK

    // void g(int) override { }    // 编译错误:签名不匹配(不是真正的重写)

    // final(C++11):禁止子类再重写
    void g() override final { cout << "Derived::g (final)" << endl; }
};

class FinalClass final : public Derived {
    // 不能再重写 g(因为被标记 final)
    // void g() override { }   // 编译错误
};

// class SubFinal : public FinalClass {};   // 编译错误:FinalClass 是 final

int main() {
    Derived d;
    Base& b = d;
    b.f();    // Derived::f
    b.g();    // Derived::g (final)
    return 0;
}

五、抽象类与纯虚函数

抽象类(abstract class)是"不能被实例化"的类,只能作为基类被继承。包含纯虚函数的类就是抽象类:

#include <iostream>
using namespace std;

// 抽象类:包含至少一个纯虚函数
class Shape {
public:
    // 纯虚函数:= 0 表示"没有实现,必须由子类提供"
    virtual double area() = 0;
    virtual double perimeter() = 0;

    // 可以有普通成员函数(子类共享)
    void describe() {
        cout << "面积: " << area() << ", 周长: " << perimeter() << endl;
    }

    virtual ~Shape() {}
};

class Circle : public Shape {
    double r;
public:
    Circle(double r) : r(r) {}
    double area() override { return 3.14159 * r * r; }
    double perimeter() override { return 2 * 3.14159 * r; }
};

int main() {
    // Shape s;          // 编译错误:抽象类不能实例化
    Circle c(5);
    c.describe();       // 面积: 78.5398, 周长: 31.4159
    return 0;
}

纯虚函数语法:virtual 返回类型 函数名(参数) = 0;。它定义了"接口"——子类必须提供实现。这是 C++ 实现"接口"概念的方式(Java 有专门的 interface 关键字,C++ 用抽象类)。

六、构造与析构的调用顺序

继承体系下,构造和析构的顺序很重要:

#include <iostream>
using namespace std;

class Base {
public:
    Base()  { cout << "Base 构造" << endl; }
    virtual ~Base() { cout << "Base 析构" << endl; }   // 虚析构
};

class Derived : public Base {
public:
    Derived()  { cout << "Derived 构造" << endl; }
    ~Derived() override { cout << "Derived 析构" << endl; }
};

int main() {
    cout << "--- 局部对象 ---" << endl;
    {
        Derived d;
    }
    // 输出顺序:
    // Base 构造 -> Derived 构造
    // Derived 析构 -> Base 析构

    cout << "--- 用基类指针 ---" << endl;
    Base* p = new Derived();
    delete p;
    // 析构是 virtual:正确调用 Derived 析构,再调 Base 析构
    // 若析构不是 virtual:只调 Base 析构!Derived 析构不调用,资源泄漏!

    return 0;
}

七、多重继承

C++ 允许一个类继承多个基类,但容易引入复杂性:

#include <iostream>
using namespace std;

class A {
public:
    void f() { cout << "A::f" << endl; }
};

class B {
public:
    void f() { cout << "B::f" << endl; }
};

// 多重继承:同时继承 A 和 B
class C : public A, public B {
};

int main() {
    C c;
    // c.f();           // 编译错误:二义性!A 和 B 都有 f()
    c.A::f();           // 显式指定:A::f
    c.B::f();           // B::f
    return 0;
}

// 多重继承的"菱形继承"问题:
//   A
//  / \
// B   C
//  \ /
//   D
// D 里有 A 的两份拷贝,访问 A 的成员会二义
// 解决:用虚继承(virtual public A)

多重继承的问题:

八、组合 vs 继承

Effective C++ 名言:"优先组合,而非继承"。组合(composition)比继承更灵活:

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

// "优先组合,而非继承"——Effective C++ 名言

class Engine {
public:
    void start() { cout << "引擎启动" << endl; }
};

class Wheel {
public:
    void roll() { cout << "轮子转动" << endl; }
};

// Car 不是 Engine,但 Car "有一个" Engine —— 这是组合(has-a)
class Car {
    Engine engine;
    Wheel wheels[4];
public:
    void drive() {
        engine.start();
        for (int i = 0; i < 4; i++) wheels[i].roll();
    }
};

int main() {
    Car car;
    car.drive();
    return 0;
}

// 继承(is-a):Dog 是 Animal
// 组合(has-a):Car 有 Engine
// 优先用组合,组合更灵活、解耦,继承会让子类依赖父类实现

选择原则:

小结

这一章你掌握了 C++ 继承的全部要点:三种继承方式(实际只用 public)、虚函数与多态、override/final、抽象类(纯虚函数)、构造析构顺序、多重继承(慎用)、组合 vs 继承。下一篇我们看 C++ 的另一项大杀器——模板

← 上一篇 C++ 类与对象

下一篇 C++ 模板

✈️💬