强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

C/C++ Linux 开发教程(GCC + CMake) / 继承与多态

继承与多态

1. 继承语法与访问控制

C++ 支持三种继承方式,决定了基类成员在派生类中的可见性。

基类成员 → 继承方式public 继承protected 继承private 继承
public 成员publicprotectedprivate
protected 成员protectedprotectedprivate
private 成员不可访问不可访问不可访问
#include <iostream>
#include <string>

class Animal {
public:
    std::string name;
    void eat() { std::cout << name << " 在吃东西\n"; }

protected:
    int age;
    void grow() { ++age; }

private:
    int id_;  // 派生类无法访问
};

// public 继承:最常用,is-a 关系
class Dog : public Animal {
public:
    Dog(const std::string& n, int a) {
        name = n;       // public → public
        age = a;        // protected → protected
        // id_ = 0;    // 错误:private 不可访问
    }

    void bark() {
        grow();         // 可以调用 protected 成员
        std::cout << name << " 汪汪叫!\n";
    }
};

int main() {
    Dog dog("旺财", 3);
    dog.eat();          // OK:public 继承保持 public
    dog.bark();         // OK
    // dog.grow();      // 错误:protected 不可从外部访问
    // dog.age = 5;     // 错误:protected
    return 0;
}

💡 提示:实际开发中 99% 使用 public 继承。protected/private 继承用于非常少见的实现继承场景。


2. 虚函数(virtual)

虚函数是 C++ 实现运行时多态的核心机制。

#include <iostream>
#include <string>

class Shape {
public:
    // 虚函数:派生类可以覆盖
    virtual void draw() const {
        std::cout << "绘制一个形状\n";
    }

    // 虚析构函数:确保通过基类指针删除派生类对象时正确析构
    virtual ~Shape() = default;
};

class Circle : public Shape {
    double radius_;

public:
    explicit Circle(double r) : radius_(r) {}

    // 覆盖基类虚函数
    void draw() const override {
        std::cout << "绘制圆形,半径=" << radius_ << "\n";
    }
};

class Rectangle : public Shape {
    double width_, height_;

public:
    Rectangle(double w, double h) : width_(w), height_(h) {}

    void draw() const override {
        std::cout << "绘制矩形,宽=" << width_ << " 高=" << height_ << "\n";
    }
};

int main() {
    // 基类指针指向派生类对象 → 多态
    Shape* shapes[] = {
        new Circle(5.0),
        new Rectangle(3.0, 4.0),
        new Shape()
    };

    for (const auto* s : shapes) {
        s->draw();  // 运行时调用实际类型的 draw()
    }

    for (auto* s : shapes) {
        delete s;  // 虚析构确保正确释放
    }

    return 0;
}

输出:

绘制圆形,半径=5
绘制矩形,宽=3 高=4
绘制一个形状

⚠️ 注意:如果基类有虚函数,必须将析构函数声明为 virtual,否则通过基类指针删除派生类对象会导致未定义行为(资源泄漏)。


3. 纯虚函数与抽象类

#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 抽象基类:包含纯虚函数,不可实例化
class Drawable {
public:
    // 纯虚函数 = 0
    virtual void draw() const = 0;
    virtual std::string name() const = 0;
    virtual double area() const = 0;

    // 虚析构函数
    virtual ~Drawable() = default;

    // 抽象类也可以有非纯虚函数
    void describe() const {
        std::cout << name() << ": 面积=" << area() << "\n";
    }
};

class Circle : public Drawable {
    double radius_;

public:
    explicit Circle(double r) : radius_(r) {}

    void draw() const override {
        std::cout << "● 半径=" << radius_ << "\n";
    }

    std::string name() const override { return "圆形"; }

    double area() const override {
        return 3.14159265 * radius_ * radius_;
    }
};

class Triangle : public Drawable {
    double base_, height_;

public:
    Triangle(double b, double h) : base_(b), height_(h) {}

    void draw() const override {
        std::cout << "▲ 底=" << base_ << " 高=" << height_ << "\n";
    }

    std::string name() const override { return "三角形"; }

    double area() const override {
        return 0.5 * base_ * height_;
    }
};

int main() {
    // Drawable d;  // 错误:抽象类不可实例化

    std::vector<std::unique_ptr<Drawable>> shapes;
    shapes.push_back(std::make_unique<Circle>(5.0));
    shapes.push_back(std::make_unique<Triangle>(3.0, 4.0));

    for (const auto& s : shapes) {
        s->draw();
        s->describe();
    }

    return 0;
}

4. override 与 final

关键字作用使用位置
override确认函数确实覆盖了基类虚函数成员函数声明末尾
final禁止派生类进一步覆盖虚函数或类本身
#include <iostream>

class Base {
public:
    virtual void foo() const { std::cout << "Base::foo\n"; }
    virtual void bar() { std::cout << "Base::bar\n"; }
    virtual void baz() { std::cout << "Base::baz\n"; }
};

class Derived : public Base {
public:
    // ✅ 正确覆盖
    void foo() const override { std::cout << "Derived::foo\n"; }

    // ⚠️ 如果拼写错误或签名不匹配,编译器会报错
    // void foo() override { }  // Error: const 不匹配

    // final:禁止进一步覆盖
    void bar() override final { std::cout << "Derived::bar (final)\n"; }
};

class MoreDerived : public Derived {
public:
    void foo() const override { std::cout << "MoreDerived::foo\n"; }
    // void bar() override { }  // Error: bar 已被 final 禁止
};

// final 类:不可被继承
class Sealed final : public MoreDerived {};

// class Illegal : public Sealed {};  // Error: Sealed 是 final 的

int main() {
    MoreDerived md;
    md.foo();  // MoreDerived::foo
    md.bar();  // Derived::bar (final)

    return 0;
}

5. 多态原理(虚表 vtable)

每个含虚函数的类都有一个虚函数表(vtable),每个含虚函数的对象都有一个虚表指针(vptr)。

┌──────────────┐
│  Shape 对象   │
│  vptr ──────────→ Shape vtable
│  其他数据     │       [0] draw()
└──────────────┘       [1] ~Shape()

┌──────────────┐
│ Circle 对象   │
│  vptr ──────────→ Circle vtable
│  radius_     │       [0] Circle::draw()
│  其他数据     │       [1] ~Circle()
└──────────────┘
#include <iostream>
#include <cstddef>

struct A {
    virtual void f() { std::cout << "A::f\n"; }
    virtual void g() { std::cout << "A::g\n"; }
    int x = 1;
};

struct B : A {
    void f() override { std::cout << "B::f\n"; }
    int y = 2;
};

int main() {
    A a;
    B b;

    std::cout << "sizeof(A) = " << sizeof(A) << "\n";  // 16 (vptr + int + padding)
    std::cout << "sizeof(B) = " << sizeof(B) << "\n";  // 24 (vptr + int + int + padding)

    // 通过基类指针调用 → vtable 查找 → 运行时分派
    A* p = &b;
    p->f();  // B::f(通过 vtable 分派到 B 的实现)
    p->g();  // A::g(B 没有覆盖 g,回退到 A 的实现)

    return 0;
}

💡 提示:虚函数调用有微小开销(一次指针间接寻址)。在性能极度敏感的热循环中可考虑 final 类或非虚函数。


6. 菱形继承与虚继承

菱形继承:D 同时继承 B 和 C,而 B 和 C 都继承自 A。

        A
       / \
      B   C
       \ /
        D
#include <iostream>

// ❌ 菱形继承问题:D 中有两份 A 的数据
struct A_bad { int x = 1; };
struct B_bad : A_bad {};
struct C_bad : A_bad {};
struct D_bad : B_bad, C_bad {};

// ✅ 虚继承:共享基类实例
struct A {
    int x = 1;
    virtual void print() { std::cout << "A::x=" << x << "\n"; }
};

struct B : virtual A {  // 虚继承
    void print() override { std::cout << "B::x=" << x << "\n"; }
};

struct C : virtual A {  // 虚继承
    void print() override { std::cout << "C::x=" << x << "\n"; }
};

struct D : B, C {
    void print() override { std::cout << "D::x=" << x << "\n"; }
};

int main() {
    // 菱形继承问题
    D_bad d_bad;
    // d_bad.x = 5;  // 歧义:B_bad::A_bad::x 还是 C_bad::A_bad::x?
    d_bad.B_bad::A_bad::x = 10;
    d_bad.C_bad::A_bad::x = 20;
    std::cout << "B_bad::A_bad::x = " << d_bad.B_bad::A_bad::x << "\n"; // 10
    std::cout << "C_bad::A_bad::x = " << d_bad.C_bad::A_bad::x << "\n"; // 20

    // 虚继承:只有一份 A
    D d;
    d.x = 42;  // 无歧义
    d.print(); // D::x=42

    B* bp = &d;
    bp->print();  // D::x=42(多态)

    return 0;
}

⚠️ 注意:虚继承有额外的内存和性能开销(虚基类表)。设计时应尽量避免菱形继承。


7. dynamic_cast 与 RTTI

#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() = default;  // 需要虚函数才能使用 dynamic_cast
};

class DerivedA : public Base {
public:
    void showA() { std::cout << "DerivedA\n"; }
};

class DerivedB : public Base {
public:
    void showB() { std::cout << "DerivedB\n"; }
};

void process(Base* ptr) {
    // dynamic_cast:安全的向下转换
    if (auto* a = dynamic_cast<DerivedA*>(ptr)) {
        a->showA();
    } else if (auto* b = dynamic_cast<DerivedB*>(ptr)) {
        b->showB();
    } else {
        std::cout << "未知类型\n";
    }

    // typeid:获取运行时类型信息
    std::cout << "类型: " << typeid(*ptr).name() << "\n";
}

int main() {
    DerivedA a;
    DerivedB b;

    process(&a);  // DerivedA
    process(&b);  // DerivedB

    // dynamic_cast 引用转换失败会抛出 std::bad_cast
    try {
        Base& ref = a;
        auto& bRef = dynamic_cast<DerivedB&>(ref);  // 失败
    } catch (const std::bad_cast& e) {
        std::cout << "转换失败: " << e.what() << "\n";
    }

    return 0;
}
转换方式安全性适用场景
static_cast编译期检查,不检查运行时类型已知类型转换
dynamic_cast运行时检查,失败返回 nullptr 或抛异常多态类型安全向下转换
const_cast移除/添加 const与旧 API 交互
reinterpret_cast不安全,底层重新解释位模式系统编程

8. 多态设计模式:策略模式

#include <iostream>
#include <memory>
#include <string>
#include <vector>

// 策略接口
class SortStrategy {
public:
    virtual ~SortStrategy() = default;
    virtual void sort(std::vector<int>& data) = 0;
    virtual std::string name() const = 0;
};

// 具体策略:冒泡排序
class BubbleSort : public SortStrategy {
public:
    void sort(std::vector<int>& data) override {
        std::cout << "使用冒泡排序...\n";
        for (size_t i = 0; i < data.size(); ++i) {
            for (size_t j = 0; j < data.size() - 1 - i; ++j) {
                if (data[j] > data[j + 1]) {
                    std::swap(data[j], data[j + 1]);
                }
            }
        }
    }

    std::string name() const override { return "BubbleSort"; }
};

// 具体策略:选择排序
class SelectionSort : public SortStrategy {
public:
    void sort(std::vector<int>& data) override {
        std::cout << "使用选择排序...\n";
        for (size_t i = 0; i < data.size(); ++i) {
            size_t minIdx = i;
            for (size_t j = i + 1; j < data.size(); ++j) {
                if (data[j] < data[minIdx]) minIdx = j;
            }
            std::swap(data[i], data[minIdx]);
        }
    }

    std::string name() const override { return "SelectionSort"; }
};

// 上下文:持有策略对象
class Sorter {
    std::unique_ptr<SortStrategy> strategy_;

public:
    void setStrategy(std::unique_ptr<SortStrategy> s) {
        strategy_ = std::move(s);
    }

    void doSort(std::vector<int>& data) {
        if (strategy_) {
            std::cout << "策略: " << strategy_->name() << "\n";
            strategy_->sort(data);
        }
    }
};

int main() {
    std::vector<int> data = {64, 25, 12, 22, 11};

    Sorter sorter;
    sorter.setStrategy(std::make_unique<BubbleSort>());
    sorter.doSort(data);

    data = {64, 25, 12, 22, 11};  // 重置数据
    sorter.setStrategy(std::make_unique<SelectionSort>());
    sorter.doSort(data);

    for (int v : data) std::cout << v << " ";
    std::cout << "\n";

    return 0;
}

实际场景:GUI 组件系统

#include <iostream>
#include <string>
#include <vector>
#include <memory>

class Widget {
protected:
    std::string id_;
    int x_, y_;

public:
    Widget(const std::string& id, int x, int y)
        : id_(id), x_(x), y_(y) {}

    virtual ~Widget() = default;

    virtual void render() const = 0;
    virtual void onClick() { std::cout << id_ << " 被点击\n"; }

    bool contains(int px, int py) const {
        return px >= x_ && px <= x_ + width() && py >= y_ && py <= y_ + height();
    }

    virtual int width() const = 0;
    virtual int height() const = 0;
};

class Button : public Widget {
    std::string text_;

public:
    Button(const std::string& id, int x, int y, const std::string& text)
        : Widget(id, x, y), text_(text) {}

    void render() const override {
        std::cout << "[Button " << id_ << "] \"" << text_
                  << "\" at (" << x_ << "," << y_ << ")\n";
    }

    int width() const override { return static_cast<int>(text_.size()) * 8 + 20; }
    int height() const override { return 30; }
};

class Label : public Widget {
    std::string text_;

public:
    Label(const std::string& id, int x, int y, const std::string& text)
        : Widget(id, x, y), text_(text) {}

    void render() const override {
        std::cout << "[Label " << id_ << "] \"" << text_
                  << "\" at (" << x_ << "," << y_ << ")\n";
    }

    void onClick() override {
        std::cout << "Label 不响应点击\n";
    }

    int width() const override { return static_cast<int>(text_.size()) * 8; }
    int height() const override { return 20; }
};

int main() {
    std::vector<std::unique_ptr<Widget>> widgets;
    widgets.push_back(std::make_unique<Button>("btn_ok", 10, 10, "确定"));
    widgets.push_back(std::make_unique<Button>("btn_cancel", 100, 10, "取消"));
    widgets.push_back(std::make_unique<Label>("lbl_title", 10, 50, "用户注册"));

    for (const auto& w : widgets) {
        w->render();
        w->onClick();
    }

    return 0;
}

扩展阅读