跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 继承与多态详解

C++ 继承与多态的核心概念及实现机制。涵盖继承的定义、访问控制、多重继承与虚继承;多态的分类、虚函数表原理、override/final 关键字及抽象类。结合 Qt 框架实战,展示自定义控件、信号槽多态、模型视图架构及插件系统中的多态应用。同时介绍观察者、策略、工厂等设计模式在 Qt 中的具体实践,提供完整代码示例与最佳实践建议,帮助开发者掌握面向对象编程精髓。

月光旅人发布于 2026/3/26更新于 2026/9/469 浏览

C++ 继承与多态详解

1. 前言

继承(Inheritance)和多态(Polymorphism)是 C++ 面向对象编程(OOP)的两大支柱。

  • 继承:允许一个类(派生类/子类)从另一个类(基类/父类)获取属性和方法,实现代码复用和层次结构。
  • 多态:允许同一接口在不同对象上表现出不同行为,实现'一个接口、多种方法'。

继承提供'是什么'(is-a 关系),多态提供'怎么做'的灵活性。在现代 C++(C++17/20)中,继承与多态常用于 GUI(如 Qt QWidget 继承链)、游戏引擎(如 Unreal Actor)、插件系统(如动态加载 DLL)。

2. 继承的概念与分类

2.1 继承的基本定义

继承语法:

class Derived : public Base {
    // public 继承(最常用)
    // 派生类成员
};

示例(简单继承):

#include <iostream>
using namespace std;

class Animal {
public:
    void eat() { cout << "动物在吃...\n"; }
};

class Dog : public Animal {
public:
    void bark() { cout << "汪汪汪!\n"; }
};

int main() {
    Dog d;
    d.eat(); // 继承自基类
    d.bark(); // 派生类自身
    return 0;
}

输出:

动物在吃... 汪汪汪! 

2.2 继承的访问控制

基类成员public 继承protected 继承private 继承
publicpublicprotectedprivate
protectedprotectedprotectedprivate
private不可访问不可访问不可访问

推荐:99% 场景用 public 继承(保持基类 public 接口公开)。

2.3 多重继承与菱形继承问题

多重继承:一个类继承多个基类。

问题:菱形继承导致基类成员二义性。

class A {
public:
    int x = 1;
};
class B : public A {};
class C : public A {};
class D : public B, public C {};

int main() {
    D d;
    // cout << d.x; // 错误:二义性(两个 x)
    return 0;
}

2.4 虚继承(virtual inheritance)

解决菱形问题:用 virtual 继承,确保基类只存在一份。

class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

int main() {
    D d;
    cout << d.x; // OK,只有一个 x
    return 0;
}

3. 多态的概念与实现

3.1 多态的分类

  • 静态多态(编译期):函数重载、模板
  • 动态多态(运行期):虚函数 + 继承 + 基类指针/引用

3.2 虚函数与 vtable 机制

示例(动态多态):

class Animal {
public:
    virtual void speak() const {
        cout << "动物在叫...\n";
    } // 虚函数
    virtual ~Animal() = default; // 虚析构(必须!)
};

class Dog : public Animal {
public:
    void speak() const override {
        cout << "汪汪汪!\n";
    }
};

int main() {
    Animal* a = new Dog(); // 基类指针指向派生类
    a->speak(); // 动态多态:调用 Dog::speak()
    delete a; // 虚析构:先 Dog 析构,再 Animal
    return 0;
}

vtable 原理(底层):

  • 每个有虚函数的类有一个 vtable(虚函数表),存放函数指针
  • 对象头有一个 vptr(虚指针),指向类 vtable
  • 调用:a->vptr -> vtable[speak_offset] -> Dog::speak()

3.3 override / final 关键字

class Cat : public Animal {
public:
    void speak() const override final {
        // override 强制检查,final 禁止子类重写
        cout << "喵喵喵!\n";
    }
};

4. 继承与多态的结合实践

完整 Demo(VS 项目:PolymorphismInheritanceDemo)

animal.h

#pragma once
#include <string>
#include <iostream>

class Animal {
public:
    virtual ~Animal() = default;
    virtual void speak() const = 0;
};

dog.h

#pragma once
#include "animal.h"

class Dog : public Animal {
public:
    void speak() const override {
        std::cout << "汪汪汪!我是狗狗。\n";
    }
};

main.cpp

#include "dog.h"
#include <memory>
#include <vector>

int main() {
    std::vector<std::unique_ptr<Animal>> zoo;
    zoo.push_back(std::make_unique<Dog>());
    // zoo.push_back(std::make_unique<Cat>()); // 假设有 Cat 类
    for (const auto& animal : zoo) {
        animal->speak(); // 继承 + 多态
    }
    return 0;
}

5. 常见陷阱与最佳实践

  • 陷阱:基类无虚析构 → delete 基类指针时派生析构不调用
  • 陷阱:多重继承无 virtual → 数据重复
  • 最佳实践:基类虚函数 + 虚析构
  • 最佳实践:用 override 检查重写
  • 最佳实践:优先 unique_ptr 管理多态对象

6. 总结

继承实现代码复用,多态实现行为灵活。两者结合是 C++ OOP 的精髓,在 Qt、Unreal 等框架中广泛应用。

7. Qt 中继承与多态的基本原则

Qt 框架大量使用继承与多态,以下是必须记住的几条铁律:

  • 所有 QObject 派生类都隐含虚析构(即使不写 virtual ~QObject(),moc 也会生成)
  • 只要类中定义了信号或槽,必须写 Q_OBJECT 宏(否则 moc 不工作)
  • 自定义控件通常继承 QWidget、QAbstractItemView、QAbstractItemModel 等
  • 多态指针/智能指针管理对象时,必须用 virtual 析构函数
  • Qt 不鼓励在信号槽中抛出异常(跨线程行为未定义)

8. 最常见的继承场景:自定义控件(QWidget 继承链)

// MyButton.h
#pragma once
#include <QPushButton>

class MyButton : public QPushButton {
    Q_OBJECT
public:
    explicit MyButton(const QString &text, QWidget *parent = nullptr);
protected:
    void paintEvent(QPaintEvent *event) override; // 重写绘制
    void mousePressEvent(QMouseEvent *event) override;
};
// MyButton.cpp
#include "MyButton.h"
#include <QPainter>
#include <QMouseEvent>

MyButton::MyButton(const QString &text, QWidget *parent) : QPushButton(text, parent) {
    setFixedSize(120, 40);
}

void MyButton::paintEvent(QPaintEvent *event) {
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); // 自定义圆角背景
    painter.setBrush(QColor("#4CAF50"));
    painter.setPen(Qt::NoPen);
    painter.drawRoundedRect(rect(), 20, 20); // 调用基类绘制文字
    QPushButton::paintEvent(event);
}

void MyButton::mousePressEvent(QMouseEvent *event) {
    // 按下时变色
    setStyleSheet("background-color: #45a049;");
    QPushButton::mousePressEvent(event);
}

在主窗口使用(多态):

MyButton *btn = new MyButton("自定义按钮", this);
btn->move(50, 50);

9. 信号槽中的多态(运行时动态绑定)

Qt 信号槽本质上是运行时多态的一种实现形式。

class Sensor : public QObject {
    Q_OBJECT
public:
    explicit Sensor(QObject *parent = nullptr) : QObject(parent) {}
signals:
    void valueChanged(double value);
};

class TemperatureSensor : public Sensor {
    Q_OBJECT
public:
    void simulate() {
        double temp = 25.0 + (rand() % 10);
        emit valueChanged(temp);
    }
};

class PressureSensor : public Sensor {
    Q_OBJECT
public:
    void simulate() {
        double press = 101.3 + (rand() % 5);
        emit valueChanged(press);
    }
};

在主窗口连接(多态):

Sensor *sensor = new TemperatureSensor(this); // 基类指针
connect(sensor, &Sensor::valueChanged, this, [](double v) {
    qDebug() << "传感器数值:" << v;
}); // 即使换成 PressureSensor,也能正常工作
sensor = new PressureSensor(this);
connect(sensor, &Sensor::valueChanged, this, [](double v) {
    qDebug() << "传感器数值:" << v;
});

10. 模型 - 视图架构中的多态(QAbstractItemModel)

这是 Qt 中最经典的多态实践之一。

自定义简单表格模型

// SimpleTableModel.h
#pragma once
#include <QAbstractTableModel>

class SimpleTableModel : public QAbstractTableModel {
    Q_OBJECT
public:
    explicit SimpleTableModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex &parent = {}) const override;
    int columnCount(const QModelIndex &parent = {}) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
private:
    QVector<QStringList> m_data;
};

在主窗口使用(多态绑定)

QTableView *view = new QTableView(this);
view->setModel(new SimpleTableModel(this)); // 基类指针指向派生类

11. 插件系统中的多态(QPluginLoader + 接口)

// plugininterface.h
#pragma once
#include <QtPlugin>

class PluginInterface {
public:
    virtual ~PluginInterface() = default;
    virtual QString name() const = 0;
    virtual void execute() = 0;
};
#define PluginInterface_iid "org.example.PluginInterface/1.0"
Q_DECLARE_INTERFACE(PluginInterface, PluginInterface_iid)

具体插件(编译成 .dll / .so)

// myplugin.cpp
#include "plugininterface.h"
#include <QDebug>

class MyPlugin : public QObject, public PluginInterface {
    Q_OBJECT
    Q_PLUGIN_METADATA(IID PluginInterface_iid)
    Q_INTERFACES(PluginInterface)
public:
    QString name() const override {
        return "示例插件";
    }
    void execute() override {
        qDebug() << "插件执行中...";
    }
};

加载插件(多态)

QPluginLoader loader("plugins/myplugin.dll");
if (QObject *plugin = loader.instance()) {
    if (auto* myPlugin = qobject_cast<PluginInterface*>(plugin)) {
        qDebug() << "加载成功:" << myPlugin->name();
        myPlugin->execute();
    }
}

12. 完整示例项目:多态形状编辑器

项目名称:ShapeEditor

shape.h

#pragma once
#include <QObject>
#include <QPainter>

class Shape : public QObject {
    Q_OBJECT
public:
    explicit Shape(QObject *parent = nullptr) : QObject(parent) {}
    virtual ~Shape() = default;
    virtual void draw(QPainter *painter) const = 0;
    virtual QString type() const = 0;
};

circle.h

#pragma once
#include "shape.h"

class Circle : public Shape {
    Q_OBJECT
public:
    Circle(qreal x, qreal y, qreal r, QObject *parent = nullptr) : Shape(parent), m_x(x), m_y(y), m_r(r) {}
    void draw(QPainter *painter) const override {
        painter->drawEllipse(m_x - m_r, m_y - m_r, m_r * 2, m_r * 2);
    }
    QString type() const override {
        return "圆形";
    }
private:
    qreal m_x, m_y, m_r;
};

mainwindow.cpp(核心多态部分)

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "circle.h"
#include <memory>
#include <vector>

class Canvas : public QWidget {
public:
    std::vector<std::unique_ptr<Shape>> shapes;
protected:
    void paintEvent(QPaintEvent *) override {
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing);
        for (const auto& shape : shapes) {
            shape->draw(&painter); // 多态调用
        }
    }
};

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    setWindowTitle("多态形状编辑器");
    Canvas *canvas = new Canvas(this);
    setCentralWidget(canvas);
    QPushButton *btn = new QPushButton("添加圆", this);
    connect(btn, &QPushButton::clicked, this, [=]() {
        canvas->shapes.push_back(std::make_unique<Circle>(100, 100, 50));
        canvas->update(); // 触发重绘
    });
    // ... 其他按钮、布局省略
}

13. 常见陷阱与最佳实践

陷阱后果最佳实践
基类无虚析构delete 基类指针时派生析构不调用基类始终 virtual ~Base() = default;
信号槽连接写错类名运行时无反应使用新式连接语法:&Class::signal
QAbstractItemModel 未正确实现视图显示空白至少实现 index、parent、rowCount、columnCount、data
多重继承未用虚继承数据成员重复菱形继承时用 virtual 继承
插件接口未声明 Q_DECLARE_INTERFACEqobject_cast 失败必须定义 IID 并使用 Q_DECLARE_INTERFACE

14. 总结

Qt 中的继承与多态是框架的基石:

  • 继承:QWidget、QObject、QAbstractItemModel 等继承链
  • 多态:信号槽、模型视图、插件系统、自定义控件
  • 推荐模式:策略模式(支付方式)、工厂模式(形状创建)、观察者模式(信号槽本身)

15. Qt 中设计模式实践

内容覆盖 Qt 开发中最常用、最有代表性的几种设计模式。

15.1 观察者模式(Observer)—— Qt 信号槽的本质

Qt 中的观察者模式 = 信号槽机制 Qt 把经典观察者模式做到了极致:松耦合、跨线程、动态连接/断开、一对多、多对多。

典型实践:自定义信号 + 槽函数

// sensor.h
#pragma once
#include <QObject>

class Sensor : public QObject {
    Q_OBJECT
public:
    explicit Sensor(QObject *parent = nullptr) : QObject(parent) {}
    void simulateData() {
        double value = 20.0 + (rand() % 20);
        emit dataReady(value);
    }
signals:
    void dataReady(double value); // 被观察者发出信号
};
// mainwindow.cpp(观察者)
connect(sensor, &Sensor::dataReady, this, [](double v) {
    ui->valueLabel->setText(QString("当前值:%1").arg(v));
});

高级用法:动态连接多个观察者

QObject::connect(sensor, &Sensor::dataReady, ui->gaugeWidget, &GaugeWidget::updateValue);
QObject::connect(sensor, &Sensor::dataReady, logger, &Logger::logValue);

Qt 特色:

  • 跨线程安全(QueuedConnection)
  • 动态断开(disconnect)
  • Lambda 槽(C++11+)

15.2 策略模式(Strategy)—— 动态切换算法

适用场景:支付方式、排序算法、绘图渲染策略等。

示例:多种支付方式

// paymentstrategy.h
#pragma once
#include <QObject>

class PaymentStrategy : public QObject {
    Q_OBJECT
public:
    virtual ~PaymentStrategy() = default;
    virtual bool pay(double amount) = 0;
    virtual QString name() const = 0;
};
// wechatpay.h
#pragma once
#include "paymentstrategy.h"

class WeChatPay : public PaymentStrategy {
    Q_OBJECT
public:
    bool pay(double amount) override {
        qDebug() << "微信支付成功:" << amount;
        return true;
    }
    QString name() const override {
        return "微信支付";
    }
};

上下文类(使用策略)

class PaymentContext : public QObject {
    Q_OBJECT
private:
    std::unique_ptr<PaymentStrategy> strategy_;
public:
    void setStrategy(std::unique_ptr<PaymentStrategy> s) {
        strategy_ = std::move(s);
    }
    bool execute(double amount) {
        if (strategy_) {
            return strategy_->pay(amount);
        }
        return false;
    }
};

使用:

PaymentContext ctx;
ctx.setStrategy(std::make_unique<WeChatPay>());
ctx.execute(100.0); // 输出:微信支付成功:100

15.3 工厂模式(Factory)—— 对象创建抽象

典型应用:动态创建形状、插件、控件等。

shape.h

#pragma once
#include <QObject>

class Shape : public QObject {
    Q_OBJECT
public:
    virtual void draw(QPainter *p) const = 0;
    virtual QString type() const = 0;
};

factory.h

#pragma once
#include <memory>
#include "shape.h"

class ShapeFactory {
public:
    static std::unique_ptr<Shape> create(const QString &type);
};

shape_factory.cpp

#include "factory.h"
#include "circle.h"
#include "rectangle.h"

std::unique_ptr<Shape> ShapeFactory::create(const QString &type) {
    if (type == "circle") return std::make_unique<Circle>();
    if (type == "rectangle") return std::make_unique<Rectangle>();
    return nullptr;
}

使用:

auto shape = ShapeFactory::create("circle");
if (shape) shape->draw(&painter);

15.4 适配器模式(Adapter)—— 接口转换

场景:老系统接口与 Qt 不兼容,或第三方库需要适配。

oldlogger.h(模拟旧接口)

class OldLogger {
public:
    void log(const char* msg) {
        printf("[OLD] %s\n", msg);
    }
};

qtloggeradapter.h

#pragma once
#include <QObject>
#include "oldlogger.h"

class QtLoggerAdapter : public QObject {
    Q_OBJECT
private:
    OldLogger old_;
public:
    explicit QtLoggerAdapter(QObject *parent = nullptr) : QObject(parent) {}
    void info(const QString &msg) {
        old_.log(msg.toUtf8().constData());
        emit messageLogged(msg);
    }
signals:
    void messageLogged(const QString &msg);
};

使用:

QtLoggerAdapter adapter;
connect(&adapter, &QtLoggerAdapter::messageLogged, this, [](const QString &m) {
    ui->logBrowser->append(m);
});
adapter.info("用户登录成功");

15.5 完整示例项目:多态形状编辑器(综合多种模式)

项目名称:ShapeEditorQt

核心类结构:

  • Shape(抽象基类) ← Circle / Rectangle(具体形状)
  • ShapeFactory(工厂模式)
  • DrawCommand(命令模式,支持撤销)
  • CanvasWidget(继承 QWidget,重写 paintEvent)

shape.h(抽象接口)

#pragma once
#include <QObject>
#include <QPainter>

class Shape : public QObject {
    Q_OBJECT
public:
    explicit Shape(QObject *parent = nullptr) : QObject(parent) {}
    virtual ~Shape() = default;
    virtual void draw(QPainter *p) const = 0;
    virtual QString type() const = 0;
    virtual Shape* clone() const = 0; // 支持复制
};

circle.h

#pragma once
#include "shape.h"

class Circle : public Shape {
    Q_OBJECT
public:
    Circle(qreal x, qreal y, qreal r, QObject *parent = nullptr);
    void draw(QPainter *p) const override;
    QString type() const override { return "圆形"; }
    Shape* clone() const override { return new Circle(*this); }
private:
    qreal m_x, m_y, m_r;
};

mainwindow.cpp(综合使用)

// 添加形状(工厂 + 多态)
void MainWindow::on_addCircle_clicked() {
    auto circle = std::make_unique<Circle>(100, 100, 50);
    canvas->addShape(std::move(circle));
    canvas->update();
}

// 撤销(命令模式)
void MainWindow::on_undo_clicked() {
    canvas->undo();
}

16. 常见问题与最佳实践总结

陷阱后果最佳实践
基类无虚析构delete 基类指针内存泄漏所有接口类写 virtual ~Base() = default;
Q_OBJECT 宏遗漏信号槽失效凡是需要信号/槽的类必须加 Q_OBJECT
多重继承未用虚继承数据重复、二义性菱形继承时用 virtual public Base
插件接口未定义 IIDqobject_cast 失败必须写 Q_DECLARE_INTERFACE + IID 宏
在信号槽中抛异常跨线程未定义行为槽函数内捕获异常,转为 error 信号

17. 总结与进阶方向

Qt 中的继承与多态几乎无处不在:

  • 继承:控件体系(QWidget → QPushButton → MyButton)
  • 多态:信号槽、模型视图、插件系统、自定义绘制

进阶方向:

  • QML + C++ 多态(QAbstractListModel + ListView)
  • Qt State Machine(状态机 + 多态行为)
  • Qt Plugin 动态加载多态组件
  • 结合 C++20 concept 约束接口

目录

  1. C++ 继承与多态详解
  2. 1. 前言
  3. 2. 继承的概念与分类
  4. 2.1 继承的基本定义
  5. 2.2 继承的访问控制
  6. 2.3 多重继承与菱形继承问题
  7. 2.4 虚继承(virtual inheritance)
  8. 3. 多态的概念与实现
  9. 3.1 多态的分类
  10. 3.2 虚函数与 vtable 机制
  11. 3.3 override / final 关键字
  12. 4. 继承与多态的结合实践
  13. 5. 常见陷阱与最佳实践
  14. 6. 总结
  15. 7. Qt 中继承与多态的基本原则
  16. 8. 最常见的继承场景:自定义控件(QWidget 继承链)
  17. 9. 信号槽中的多态(运行时动态绑定)
  18. 10. 模型 - 视图架构中的多态(QAbstractItemModel)
  19. 11. 插件系统中的多态(QPluginLoader + 接口)
  20. 12. 完整示例项目:多态形状编辑器
  21. 13. 常见陷阱与最佳实践
  22. 14. 总结
  23. 15. Qt 中设计模式实践
  24. 15.1 观察者模式(Observer)—— Qt 信号槽的本质
  25. 15.2 策略模式(Strategy)—— 动态切换算法
  26. 15.3 工厂模式(Factory)—— 对象创建抽象
  27. 15.4 适配器模式(Adapter)—— 接口转换
  28. 15.5 完整示例项目:多态形状编辑器(综合多种模式)
  29. 16. 常见问题与最佳实践总结
  30. 17. 总结与进阶方向

更多推荐文章

查看全部
  • ResNet18 镜像:内置权重、WebUI 与极速推理方案
  • C++ 跨平台开发的挑战与应对策略
  • MySQL 深度性能优化配置实战指南
  • 多模态大模型垂直微调实战:Qwen3-VL-4B-Thinking 与 Llama Factory
  • MATLAB 2025a 发布:深色模式与 Copilot 编程助手功能详解
  • OpenClaw 核心逻辑解析:重新定义 AI 数字员工执行力
  • 使用 Biopython 快速解析 FASTA 与 GenBank 基因数据
  • Flutter 与 Web 混合开发技术实践
  • GPT 时代算法工程师的职业发展与 35 岁危机应对
  • 前端拖拽排序实现详解:从原理到实践
  • FPGA 光通信 Aurora 64B/66B 协议开发指南
  • 腾讯 Hunyuan-MT-7B 翻译模型技术详解与部署指南
  • 基于 YOLO 与相机运动补偿的单目无人机车辆测速:加权滑动平均速度优化
  • 开源 MaxKB 企业级智能体平台技术解析
  • QClaw 基于 AI 与 OCR 的微信红包管理场景应用解析
  • LangChain 架构演进与功能扩展:流式事件处理、事件过滤及回调策略
  • 趣味 AI 投降网页:三步体验地球统治仪式
  • Foxglove 开发环境完整搭建指南 (含常见坑位)
  • AI 大模型在线免费学习资源汇总
  • Retinaface+CurricularFace 人脸识别服务 Kubernetes StatefulSet 部署实战

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online