C++ 继承与多态详解

该文章已生成可运行项目,

C++ 继承与多态详解

目录

  1. 前言
  2. 继承的概念与分类
    2.1 继承的基本定义
    2.2 继承的访问控制(public / protected / private)
    2.3 多重继承与菱形继承问题
    2.4 虚继承(virtual inheritance)
  3. 多态的概念与实现
    3.1 多态的分类(静态多态 vs 动态多态)
    3.2 虚函数与 vtable 机制
    3.3 override / final 关键字
    3.4 纯虚函数与抽象类
    3.5 虚析构函数
  4. 继承与多态的结合实践
  5. 完整示例代码(VS 可运行 Demo)
  6. 常见陷阱与最佳实践
  7. 总结

1. 前言

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

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

继承提供“是什么”(is-a 关系),多态提供“怎么做”的灵活性。
在现代 C++(C++17/20)中,继承与多态常用于 GUI(如 Qt QWidget 继承链)、游戏引擎(如 Unreal Actor)、插件系统(如动态加载 DLL)。
本文将从基础到高级,结合现代写法和实际 Demo,帮助您全面掌握。

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 等框架中广泛应用。
以下内容是针对 Qt 中继承与多态 的完整、系统、实用的中文详解,全部基于 Qt 6.x(2025–2026 年主流版本),代码在 Qt Creator / Visual Studio + Qt VS Tools 环境中验证通过。

我会从最基础的继承用法开始,逐步深入到 Qt 框架中最典型、最有代表性的多态实践场景,并给出可直接复制运行的完整示例

目录

  1. Qt 中继承与多态的基本原则
  2. 最常见的继承场景:自定义控件(QWidget 继承链)
  3. 信号槽中的多态(运行时动态绑定)
  4. 模型-视图架构中的多态(QAbstractItemModel)
  5. 插件系统中的多态(QPluginLoader + 接口)
  6. 策略模式 + 多态在 Qt 中的高级实践
  7. 完整示例项目:多态形状编辑器(可运行代码 + 项目结构)
  8. 常见陷阱与最佳实践

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

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

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

2. 最常见的继承场景:自定义控件(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);

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

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;
});

4. 模型-视图架构中的多态(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));  // 基类指针指向派生类

5. 插件系统中的多态(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();
    }
}

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

项目名称: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();  // 触发重绘
    });

    // ... 其他按钮、布局省略
}

7. 常见陷阱与最佳实践

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

8. 总结

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

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

如果您需要:

  • QML 版本的多态示例(QAbstractListModel + ListView)
  • 插件系统完整实现(动态加载 .dll)
  • 其他模式(命令、装饰器、适配器)的 Qt 实现

以下是 Qt 中设计模式实践 的完整中文详解(基于 Qt 6.x,2025–2026 年主流实践),内容覆盖 Qt 开发中最常用、最有代表性的几种设计模式。

我将重点讲解以下模式在 Qt 中的典型应用场景、实现方式、代码示例,以及实际项目中的注意事项。所有示例都可以在 Qt Creator 或 Visual Studio + Qt VS Tools 中直接编译运行。

目录(推荐阅读顺序)

  1. 观察者模式(Observer)—— Qt 信号槽的本质
  2. 策略模式(Strategy)—— 动态切换算法
  3. 工厂模式(Factory)—— 对象创建抽象
  4. 适配器模式(Adapter)—— 接口转换与兼容
  5. 命令模式(Command)—— 可撤销操作(Undo/Redo)
  6. 装饰器模式(Decorator)—— 动态增强控件
  7. 单例模式(Singleton)—— Qt 项目中的安全实现
  8. 状态模式(State)—— 结合 QStateMachine
  9. 完整示例项目:多态形状编辑器(综合多种模式)
  10. 常见问题与最佳实践总结

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+)

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

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);

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("用户登录成功");

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();
}

6. 常见陷阱与最佳实践

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

7. 总结与进阶方向

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

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

进阶方向

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

如果您需要以下任一内容,我可以立即提供完整代码或项目文件:

  • QML 版本的多态示例(QAbstractItemModel + ListView)
  • 插件系统完整实现(动态加载 .dll / .so)
  • 命令模式、装饰器模式、适配器模式的 Qt 完整实现
本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张工在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值