C++语言基础6:什么是继承(结合Qt应用讲解) 什么是继承“这个类继承了 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 loggerQt 帮你搞定。示例二自定义“按钮”并发送信号继承实现信号槽假设你要做一个带“点击计数”的特殊按钮。#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_castTHintsPlugin*(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 不推荐。一句话总结继承QObject相当于给你的 C 类买了一张 Qt 世界的“永久居民身份证”让它不再是一个孤立的 C 对象而是成为 Qt 运行时大家庭中活生生的一员。