什么是继承
“这个类继承了 QObject 类”这句话,是 Qt 编程中最重要的一句话。用一句话说清本质就是:
THintsPlugin 不仅拥有了 QObject 的所有“家产”(属性和函数),更重要的是,它获得了 QObject 的“超能力”(内存管理、信号槽、运行时类型识别),从此被纳入 Qt 强大的对象生态系统。
1. 什么是“继承”?(类与类之间的父子关系)
在 C++ 中,继承 表示 “是一种(is-a)” 的关系。
- 父类(基类):更通用、更基础的概念(比如:“动物”)。
- 子类(派生类):更具体、更丰富的概念(比如:“狗”)。
通俗比喻:你继承了亲生父亲的家族姓氏和房产(属性和方法),同时你还能自己买新车、学新技能(添加自己的新属性和方法)。当你遇到麻烦时,你还可以调用父亲留给你的“人脉接口”(Qt 的信号槽机制)来解决问题。
2. 为什么 Qt 中的类要抢着继承 QObject?(它给了什么超能力)
普通的 C++ 类(比如你写的 class Person {};)只是一个冰冷的数据结构。而只要公有继承(public)了 QObject,这个类就获得了 Qt 框架赋予的 三大核心特权:
- 对象树(Object Tree)与自动内存管理:当你给一个 QObject 设置
parent(父对象)时,父对象被删除时,Qt 会自动帮你删除它所有的子对象。从此你再也不用担心delete泄露内存了。 - 信号与槽(Signal & Slot):这是 Qt 最伟大的发明。对象之间可以像“电台广播”一样通信(比如:“按钮被点击”信号发出,触发“关闭窗口”槽函数),而无需写复杂的回调函数。
- 元对象系统(Meta-Object):支持运行时类型识别(
qobject_cast)、属性系统(Q_PROPERTY),以及让 Qt Creator 能识别你的类。
3. 详细举例说明(从简单到复杂)
为了让你彻底弄懂,我们从最简单的 Qt 类继承开始看。
示例一:只为获得“自动内存管理”(最基础)
你写了一个日志类,只想让它自动被父对象管理。
#include <QObject>
#include <QDebug>
// 1. 继承 QObject(一定要 public 继承)
class MyLogger : public QObject
{
Q_OBJECT // 注意:这个宏是必须的,用来开启元对象功能(虽然这里没用信号槽,但习惯性加上)
public:
// 构造函数必须把 parent 传给 QObject
explicit MyLogger(QObject *parent = nullptr) : QObject(parent) {
qDebug() << "日志记录器已创建";
}
~MyLogger() {
qDebug() << "日志记录器被自动销毁了!";
}
void writeLog(const QString &msg) {
qDebug() << "[LOG]" << msg;
}
};
int main() {
QObject *parent = new QObject; // 创建一个父对象
// 创建子对象,把 parent 传进去
MyLogger *logger = new MyLogger(parent);
logger->writeLog("程序启动了");
delete parent; // 删除父对象时,Qt 自动遍历子对象,logger 也会被自动 delete!
// 输出会看到 "日志记录器被自动销毁了!"
return 0;
}
结论:你不需要写 delete logger,Qt 帮你搞定。
示例二:自定义“按钮”并发送信号(继承实现信号槽)
假设你要做一个带“点击计数”的特殊按钮。
#include <QObject>
#include <QDebug>
class MyCounterButton : public QObject
{
Q_OBJECT // 必须要有这个宏,否则信号槽无法工作
private:
int m_count = 0;
public:
explicit MyCounterButton(QObject *parent = nullptr) : QObject(parent) {}
// 模拟用户点击的行为
void click() {
m_count++;
qDebug() << "按钮被点击了,当前次数:" << m_count;
// 【核心】发射自定义信号,告诉外界“我被点了,顺便带上次数”
emit clickedCount(m_count);
}
signals: // 这是 Qt 特有的关键字,表示这里定义信号(只需声明,不需实现)
void clickedCount(int times);
};
// 定义一个“显示器”类,用来接收信号
class Display : public QObject
{
Q_OBJECT
public slots: // 这是槽函数,用来响应信号
void showTimes(int t) {
qDebug() << "显示器收到通知:按钮被点了" << t << "次";
}
};
int main() {
MyCounterButton btn;
Display disp;
// 【核心】连接:按钮的信号 连接到 显示器的槽
QObject::connect(&btn, &MyCounterButton::clickedCount,
&disp, &Display::showTimes);
btn.click(); // 输出:按钮被点击了... 显示器收到通知...
btn.click(); // 继续输出,完美联动
return 0;
}
结论:普通的 C++ 类绝无可能这样方便地解耦通信。全靠继承 QObject 带来的 signals 和 slots 能力。
示例三:像你代码里的真实插件场景(多态与类型识别)
Qt 在加载插件时,经常用父类指针指向子类对象。
// 假设有一个接口(抽象类)
class MyPluginInterface {
public:
virtual void doWork() = 0;
};
// 你的插件既继承了接口(功能),又继承了 QObject(获得 Qt 超能力)
class THintsPlugin : public QObject, public MyPluginInterface
{
Q_OBJECT
public:
explicit THintsPlugin(QObject *parent = nullptr) : QObject(parent) {}
void doWork() override {
qDebug() << "插件正在执行高强度任务...";
}
};
// 在 Qt 主程序中,你经常能看到这种代码:
void loadPlugin(QObject *obj) {
// 即使传进来的是 QObject* 父类指针,也能安全地转换为子类插件指针
THintsPlugin *plugin = qobject_cast<THintsPlugin*>(obj);
if (plugin) {
plugin->doWork(); // 成功调用!
}
}
结论:只有继承了 QObject,才能使用 qobject_cast 进行安全的类型转换,这是 Qt 插件系统的基础。
4. 继承 QObject 时必须遵守的“铁律”(新手易错点)
如果你要在代码里继承 QObject,必须遵守以下规则,否则编译器会报错或功能失效:
- 必须在类的最开头写上
Q_OBJECT宏(就是那行Q_OBJECT)。写了它,Qt 的“元对象编译器(MOC)”才会为你的类生成额外的魔法代码,让信号槽生效。 - 构造函数必须接收
QObject *parent参数,并在初始化列表中传给QObject(parent)。就像你代码里写的那样,这是为了把对象挂到对象树上。 - 最好使用
public继承。写成class MyClass : public QObject,极少情况下用private继承(因为那样会失去多态性,Qt 不推荐)。
&spm=1001.2101.3001.5002&articleId=162955162&d=1&t=3&u=5cfb04631823437aa36ffa4a85323976)
7915

被折叠的 条评论
为什么被折叠?



