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

C++ 继承与多态详解

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

月光旅人发布于 2026/3/26更新于 2026/7/1848 浏览

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. 总结与进阶方向
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 爬虫入门教程:从零开始学习网络数据采集
  • Stable Diffusion 提示词高阶用法实战:从精准控制到风格迁移
  • 人形机器人:从万物可机器人化到终极通用平台的百万亿赛道逻辑
  • Spring Boot 启动引导类:命名约定与底层启动原理
  • Poe 平台:Quora 推出的 AI 模型整合工具
  • Python 数据清洗实战:缺失值、异常值与格式处理
  • 鸿蒙系统 PC 版安装与使用指南
  • GESP C++ 一级考试全流程及编程题核心模板
  • WSL2 环境下 Git 与 Gitee 代码管理实战
  • Android 应用视觉优化:10 个 UI 技巧
  • MySQL 核心原理与实战进阶指南
  • Qwen1.5-0.5B-Chat 轻量模型本地部署与 WebUI 实战
  • 使用 Dexie 操作前端 IndexedDB 数据库教程
  • AR/VR 展项与实体展陈融合施工工艺及交互校准技术
  • 主流大模型介绍(GPT、Llama、ChatGLM、Qwen、deepseek)
  • 单链表应用:经典算法题与通讯录实现
  • GitHub 代码文件抓取与数据可视化实践(Python 实现)
  • 基于 SpringBoot 的协同过滤电影推荐系统
  • OpenClaw 对接飞书机器人配置问题排查:消息不回复与 Gateway 断开
  • 使用星辰 RPA 构建小红书自动发文机器人

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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