【Qt 小白必看】C++ 单例类注册 QML 调用(逐行超详细注释+CSDN精品笔记)

这篇笔记是纯小白向,把你写的全部代码逐行、逐字符拆解,讲清单例模式原理 + C++ 与 QML 交互,直接复制就能发布 CSDN!


一、项目整体介绍

这是一个 Qt Quick 项目,实现两个核心功能:

  1. C++ 单例类:全局唯一实例,QML 可直接调用
  2. C++ 与 QML 交互:QML 调用 C++ 函数、显示数据、打印日志
  3. 同时包含单例注册 + 上下文对象注册两种 Qt 交互方式

二、backendclass.h 头文件 逐行解析

作用:声明类、函数、宏,不写具体逻辑

// 头文件保护宏
// 防止同一个头文件被多次包含,导致编译报错(必须写)
#ifndef BACKENDCLASS_H
#define BACKENDCLASS_H

// 包含 Qt 所有对象的基类
// 必须继承 QObject,才能使用信号槽、QML 交互
#include <QObject>

// 包含 QML 引擎头文件
// 用于把 C++ 类注册到 QML 环境
#include <QQmlEngine>

// 自定义类 BackendClass,公开继承 QObject
class BackendClass : public QObject
{
    // Qt 核心宏
    // 不加这行:无法使用信号槽、Q_INVOKABLE、注册到 QML
    Q_OBJECT

public:
    // 显式构造函数
    // explicit:禁止隐式类型转换,规范写法
    // parent = nullptr:父对象为空,Qt 自动管理内存
    explicit BackendClass(QObject *parent = nullptr);

    // Q_INVOKABLE:Qt 关键宏
    // 作用:让这个 C++ 函数可以在 QML 中直接调用
    // 函数功能:返回 int 类型数字
    Q_INVOKABLE int getnum();

    // QML 可调用函数:控制台打印日志
    Q_INVOKABLE void printSomething();

    // 静态成员函数
    // 属于类,不属于对象,用于注册当前类到 QML
    static void registerClass();

    // 单例提供者函数
    // Qt 固定格式:给 QML 返回唯一的单例对象
    static QObject* singletonProvider(QQmlEngine *, QJSEngine *);

signals:
    // 信号声明区:本案例没有使用信号
};

#endif // BACKENDCLASS_H

三、backendclass.cpp 源文件 逐行解析

作用:实现头文件里声明的所有函数

// 引入自定义头文件,让编译器认识 BackendClass
#include "backendclass.h"

// 引入 Qt 日志打印头文件
#include <QtDebug>

// 静态全局指针:存储单例对象
// static:作用域仅限当前文件
// 初始化为空:表示还没有创建对象
static BackendClass* m_object=nullptr;

// 构造函数实现
// 初始化列表:调用父类 QObject 的构造函数
BackendClass::BackendClass(QObject *parent) : QObject(parent)
{
    // 空构造函数
    // 单例对象初始化可以写在这里
}

// 实现 getnum 函数
// 功能:返回固定数字 42,给 QML 显示
int BackendClass::getnum()
{
    return 42;
}

// 实现 printSomething 函数
// 功能:控制台打印 hello world
void BackendClass::printSomething()
{
    qInfo()<<"hello world";
}

// 实现注册函数:将 C++ 类注册为 QML 单例
void BackendClass::registerClass()
{
    // Qt 官方函数:注册单例类到 QML
    // 参数1:模块名 com.reddit
    // 参数2:主版本 1
    // 参数3:次版本 0
    // 参数4:QML 中使用的对象名 MyBackend
    // 参数5:单例提供函数,返回唯一实例
    qmlRegisterSingletonType<BackendClass>("com.reddit",1,0,"MyBackend",singletonProvider);
}

// 单例模式核心函数
QObject *BackendClass::singletonProvider(QQmlEngine *, QJSEngine *)
{
    // 第一次调用:对象为空 → 创建实例
    if(m_object==nullptr)
    {
        m_object=new BackendClass();
    }
    // 后续调用:直接返回已创建的对象
    // 保证全局只有一个实例 → 单例模式
    return m_object;
}

四、main.cpp 程序入口 逐行解析

作用:程序运行的起点,初始化 Qt、注册 C++ 类、加载 QML 界面

// Qt 界面应用基类
#include <QGuiApplication>

// QML 应用引擎
#include <QQmlApplicationEngine>

// QML 上下文:用于注册普通 C++ 对象
#include <QQmlContext>

// 引入辅助类(你项目里的其他类)
#include "backendhelper.h"

// 引入单例类
#include "backendclass.h"

// main 函数:程序唯一入口
int main(int argc, char *argv[])
{
    // 设置 Qt 属性:启用高 DPI 缩放
    // 适配高分屏,界面不模糊
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    // 创建 Qt 应用程序对象
    QGuiApplication app(argc, argv);

    // 创建 QML 引擎:管理 QML 界面
    QQmlApplicationEngine engine;

    // 创建 BackendHelper 类对象(普通对象,非单例)
    BackendHelper obj;

    // 关键代码!
    // 调用单例类的注册函数,把 C++ 单例注册到 QML
    BackendClass::registerClass();

    // 把普通 C++ 对象注册到 QML 上下文
    // cBackendHelper:QML 中使用的对象名
    // &obj:C++ 对象地址
    engine.rootContext()->setContextProperty("cBackendHelper",&obj);

    // 定义 QML 文件路径:从资源文件加载 main.qml
    const QUrl url(QStringLiteral("qrc:/main.qml"));

    // Qt 连接:监听 QML 对象创建完成
    // 如果加载失败,自动退出程序
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);

    // 加载 QML 界面
    engine.load(url);

    // 启动 Qt 事件循环,程序保持运行
    return app.exec();
}

五、main.qml 界面文件 逐行解析

作用:写 UI 界面,直接调用 C++ 单例函数

// 导入 QtQuick 基础控件模块
import QtQuick 2.12

// 导入按钮、输入框等高级控件
import QtQuick.Controls 2.5

// 导入 C++ 注册的单例模块
// 对应 C++ 中的 com.reddit 1.0
import com.reddit 1.0

// 应用程序主窗口
ApplicationWindow {
    id: window             // 窗口唯一标识
    visible: true          // 窗口显示
    width: 640             // 宽度
    height: 480            // 高度
    title: qsTr("Stack")   // 窗口标题

    // 文本控件:显示 C++ 返回的数字
    Text{
        id:mytext                  // 唯一标识
        anchors.centerIn: parent   // 居中显示
        font.pointSize: 15         // 字体大小

        // 核心:调用 C++ 单例函数
        // MyBackend:注册的单例对象名
        // getnum():C++ 函数,返回 42
        text: MyBackend.getnum()
    }

    // 按钮控件
    Button{
        id:myButton
        text: "Click Me"                  // 按钮文字
        anchors.top: mytext.bottom        // 位于文本下方
        anchors.horizontalCenter:parent.horizontalCenter  // 水平居中
        anchors.topMargin: 10             // 间距 10px

        // 点击事件
        onClicked: {
            // 调用 C++ 函数:打印日志
            MyBackend.printSomething()
        }
    }
}

六、小白必懂:单例模式到底是什么?

1. 单例模式定义

一个类,全局只能创建唯一一个对象,全程共享这个对象。

2. 本项目单例实现步骤

  1. 定义一个静态指针 m_object 保存对象
  2. 第一次调用时 new 创建对象
  3. 之后所有调用直接返回已创建的对象
  4. QML 无论在哪里使用 MyBackend,都是同一个 C++ 对象

3. 单例优点

  • 节约内存
  • 全局数据统一
  • 避免多次创建对象

七、小白必背核心关键字

  1. Q_OBJECT:启用 Qt 元对象系统
  2. Q_INVOKABLE:QML 调用 C++ 函数必备
  3. qmlRegisterSingletonType:注册 C++ 单例到 QML
  4. static:实现单例的核心关键字
  5. singletonProvider:Qt 单例提供函数
  6. setContextProperty:注册普通 C++ 对象到 QML

八、项目运行效果

  1. 界面显示文本:42(来自 C++ 单例函数)
  2. 界面显示按钮:Click Me
  3. 点击按钮 → 控制台打印:hello world
  4. C++ 单例对象全程只创建一次

九、小白常见报错解决

  1. QML 提示 MyBackend is undefined
    → main.cpp 未调用 BackendClass::registerClass()
  2. C++ 函数 QML 调用失败
    → 函数未加 Q_INVOKABLE
  3. 编译报错undefined reference to vtable
    → 类未加 Q_OBJECT
  4. 单例失效
    → 静态指针未判断空

十、总结

这是 Qt 入门 C++ 与 QML 交互 + 单例模式 最标准的实战代码:

  1. C++ 单例类 + 注册到 QML
  2. QML 直接调用 C++ 函数
  3. 全局唯一实例,高效安全
  4. 同时学会单例注册 + 上下文对象注册两种交互方式

逐行吃透这篇笔记,你就掌握了 Qt 开发最核心的基础技能!


更多推荐