跳到主要内容 C++ 继承与多态详解 | 极客日志
C++ 算法
C++ 继承与多态详解 C++ 继承与多态的核心概念及实现机制。涵盖继承的定义、访问控制、多重继承与虚继承;多态的分类、虚函数表原理、override/final 关键字及抽象类。结合 Qt 框架实战,展示自定义控件、信号槽多态、模型视图架构及插件系统中的多态应用。同时介绍观察者、策略、工厂等设计模式在 Qt 中的具体实践,提供完整代码示例与最佳实践建议,帮助开发者掌握面向对象编程精髓。
月光旅人 发布于 2026/3/26 更新于 2026/4/18 6 浏览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 {
};
#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 继承 public public protected private protected protected protected private 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;
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;
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 ();
delete a;
return 0 ;
}
每个有虚函数的类有一个 vtable(虚函数表),存放函数指针
对象头有一个 vptr(虚指针),指向类 vtable
调用:a->vptr -> vtable[speak_offset] -> Dog::speak()
3.3 override / final 关键字 class Cat : public Animal {
public :
void speak () const override final {
cout << "喵喵喵!\n" ;
}
};
4. 继承与多态的结合实践 完整 Demo (VS 项目:PolymorphismInheritanceDemo)
#pragma once
#include <string>
#include <iostream>
class Animal {
public :
virtual ~Animal () = default ;
virtual void speak () const = 0 ;
};
#pragma once
#include "animal.h"
class Dog : public Animal {
public :
void speak () const override {
std::cout << "汪汪汪!我是狗狗。\n" ;
}
};
#include "dog.h"
#include <memory>
#include <vector>
int main () {
std::vector<std::unique_ptr<Animal>> zoo;
zoo.push_back (std::make_unique <Dog>());
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 继承链)
#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 ;
};
#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. 信号槽中的多态(运行时动态绑定) 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;
});
sensor = new PressureSensor (this );
connect (sensor, &Sensor::valueChanged, this , [](double v) {
qDebug () << "传感器数值:" << v;
});
10. 模型 - 视图架构中的多态(QAbstractItemModel)
#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 + 接口)
#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)
#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. 完整示例项目:多态形状编辑器 #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 ;
};
#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;
};
#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_INTERFACE qobject_cast 失败 必须定义 IID 并使用 Q_DECLARE_INTERFACE
14. 总结
继承 :QWidget、QObject、QAbstractItemModel 等继承链
多态 :信号槽、模型视图、插件系统、自定义控件
推荐模式 :策略模式(支付方式)、工厂模式(形状创建)、观察者模式(信号槽本身)
15. Qt 中设计模式实践 内容覆盖 Qt 开发中最常用、最有代表性的几种设计模式。
15.1 观察者模式(Observer)—— Qt 信号槽的本质 Qt 中的观察者模式 = 信号槽机制
Qt 把经典观察者模式做到了极致:松耦合、跨线程、动态连接/断开、一对多、多对多。
#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) ;
};
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);
跨线程安全(QueuedConnection)
动态断开(disconnect)
Lambda 槽(C++11+)
15.2 策略模式(Strategy)—— 动态切换算法
#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 ;
};
#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 );
15.3 工厂模式(Factory)—— 对象创建抽象 #pragma once
#include <QObject>
class Shape : public QObject {
Q_OBJECT
public :
virtual void draw (QPainter *p) const = 0 ;
virtual QString type () const = 0 ;
};
#pragma once
#include <memory>
#include "shape.h"
class ShapeFactory {
public :
static std::unique_ptr<Shape> create (const QString &type) ;
};
#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 不兼容,或第三方库需要适配。
class OldLogger {
public :
void log (const char * msg) {
printf ("[OLD] %s\n" , msg);
}
};
#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 完整示例项目:多态形状编辑器(综合多种模式)
Shape(抽象基类) ← Circle / Rectangle(具体形状)
ShapeFactory(工厂模式)
DrawCommand(命令模式,支持撤销)
CanvasWidget(继承 QWidget,重写 paintEvent)
#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 ;
};
#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;
};
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 插件接口未定义 IID qobject_cast 失败 必须写 Q_DECLARE_INTERFACE + IID 宏 在信号槽中抛异常 跨线程未定义行为 槽函数内捕获异常,转为 error 信号
17. 总结与进阶方向
继承 :控件体系(QWidget → QPushButton → MyButton)
多态 :信号槽、模型视图、插件系统、自定义绘制
QML + C++ 多态(QAbstractListModel + ListView)
Qt State Machine(状态机 + 多态行为)
Qt Plugin 动态加载多态组件
结合 C++20 concept 约束接口
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,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
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online