C++语言基础6:什么是继承(结合Qt应用讲解)

什么是继承

“这个类继承了 QObject 类”这句话,是 Qt 编程中最重要的一句话。用一句话说清本质就是:

THintsPlugin 不仅拥有了 QObject 的所有“家产”(属性和函数),更重要的是,它获得了 QObject 的“超能力”(内存管理、信号槽、运行时类型识别),从此被纳入 Qt 强大的对象生态系统。


1. 什么是“继承”?(类与类之间的父子关系)

在 C++ 中,继承 表示 “是一种(is-a)” 的关系。

  • 父类(基类):更通用、更基础的概念(比如:“动物”)。
  • 子类(派生类):更具体、更丰富的概念(比如:“狗”)。

通俗比喻:你继承了亲生父亲的家族姓氏和房产(属性和方法),同时你还能自己买新车、学新技能(添加自己的新属性和方法)。当你遇到麻烦时,你还可以调用父亲留给你的“人脉接口”(Qt 的信号槽机制)来解决问题。


2. 为什么 Qt 中的类要抢着继承 QObject?(它给了什么超能力)

普通的 C++ 类(比如你写的 class Person {};)只是一个冰冷的数据结构。而只要公有继承(public)QObject,这个类就获得了 Qt 框架赋予的 三大核心特权

  1. 对象树(Object Tree)与自动内存管理:当你给一个 QObject 设置 parent(父对象)时,父对象被删除时,Qt 会自动帮你删除它所有的子对象。从此你再也不用担心 delete 泄露内存了。
  2. 信号与槽(Signal & Slot):这是 Qt 最伟大的发明。对象之间可以像“电台广播”一样通信(比如:“按钮被点击”信号发出,触发“关闭窗口”槽函数),而无需写复杂的回调函数。
  3. 元对象系统(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 带来的 signalsslots 能力。


示例三:像你代码里的真实插件场景(多态与类型识别)

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,必须遵守以下规则,否则编译器会报错或功能失效:

  1. 必须在类的最开头写上 Q_OBJECT(就是那行 Q_OBJECT)。写了它,Qt 的“元对象编译器(MOC)”才会为你的类生成额外的魔法代码,让信号槽生效。
  2. 构造函数必须接收 QObject *parent 参数,并在初始化列表中传给 QObject(parent)。就像你代码里写的那样,这是为了把对象挂到对象树上。
  3. 最好使用 public 继承。写成 class MyClass : public QObject,极少情况下用 private 继承(因为那样会失去多态性,Qt 不推荐)。

一句话总结:继承 QObject,相当于给你的 C++ 类买了一张 Qt 世界的“永久居民身份证”,让它不再是一个孤立的 C++ 对象,而是成为 Qt 运行时大家庭中活生生的一员。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大皓皓非常然

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

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

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

打赏作者

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

抵扣说明:

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

余额充值