QML学习笔记(五十)QML与C++交互:QML中单例C++对象

前言

上一节中,我们可以在qml侧实现C++类的实例,将C++实例对象的创建和生命周期交给qml侧,但除了这种直接创建对象的方式,还有一种很常用的设计模式------单例模式。

单例模式简单来说就是在我们的程序中,这个C++类只能存在一个实际的对象,不可以new多个。它通常会将构造函数放到private下,然后提供一个static修饰的intance接口来获取唯一的对象。单例模式一般会用于各种配置类、管理类的实现上。如果单例模式还不熟悉的话,建议先自行了解一下。

接下来,将介绍如何在qml中实现一个c++的单例对象。

一、代码演示

先实现我们带有单例设计模式的自定义类:

cpp 复制代码
#ifndef MYSINGLETON_H
#define MYSINGLETON_H

#include <QObject>
#include <QtQml>
#include <QDebug>

class MySingleton : public QObject
{
    Q_OBJECT

    // QT6
//    QML_SINGLETON
//    QML_ELEMENT

public:
    static MySingleton *instance();   // 全局访问点

    Q_INVOKABLE void doSomething() const{
        qDebug()<<"Doing something...";
    }

signals:

private:
    explicit MySingleton(QObject *parent = nullptr);
    static MySingleton *m_instance;


};
cpp 复制代码
#include "mysingleton.h"

MySingleton *MySingleton::m_instance = nullptr;

MySingleton *MySingleton::instance()
{
    if (!m_instance) {
        m_instance = new MySingleton;
        qDebug()<<"create MySingleton...";
    }

    return m_instance;
}

MySingleton::MySingleton(QObject *parent) : QObject(parent)
{

}

这里实现了最简单的单例创建和一个任务函数的接口。

然后是main函数:

cpp 复制代码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "mysingleton.h"

// 包装函数:Qt5 要求返回 QJSValue
static QObject *singleton_provider(QQmlEngine *, QJSEngine *)
{
    qDebug()<<"singleton_provider";
    return MySingleton::instance();
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // 1
    qmlRegisterSingletonType<MySingleton>("com.mycompany", 1, 0, "Singleton", singleton_provider);
    
    // 2 (QT6可以,但QT5不允许传入对象指针,只能传入函数指针,
//    MySingleton *singleton = MySingleton::instance();
//    qmlRegisterSingletonType<MySingleton>("com.mycompany", 1, 0, "Singleton", singleton);
    

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

这里先是设计了一个函数指针,可以提供一个单例对象(实际上只会创建一次)。

cpp 复制代码
static QObject *singleton_provider(QQmlEngine *, QJSEngine *)
{
    qDebug()<<"singleton_provider";
    return MySingleton::instance();
}

然后我们使用qmlRegisterSingletonType方法,将这个单例的对象,准确来说是实现这个单例对象的提供器函数指针设置进去,我们可以设置这个单例对象的别名,还有模块名和版本号。

接着,我们就可以在qml侧使用它啦。

cpp 复制代码
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.12
import com.mycompany 1.0   // 模块名

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Singleton demo")

    Button{
        id: buttonId
        text: "Click"
        onClicked: function(){
            console.log("button click...")
            Singleton.doSomething()
        }
    }
}

使用时无需创建对象,直接调接口就行了Singleton.doSomething()

如果想要像其他类对象那样创建,是不行的哦。

cpp 复制代码
    Singleton {
        id: singleton
    }

在高版本qt6中,可能会有不同的表现。

运行效果:

从打印效果得知,我们是从按钮按下的那一刻,才会出发提供单例对象的函数,然后才创建了单例对象,执行了我们希望的单例对象内的函数。

实际上这种方式是单例模式中的懒汉式,第一次使用的时候才new它。

二、QT6可能的改进

一个就是刚才说的,我们也可以尝试饿汉式的单例加载,也就是:

cpp 复制代码
MySingleton *singleton = MySingleton::instance();
qmlRegisterSingletonType<MySingleton>("com.mycompany", 1, 0, "Singleton", singleton);

不传函数指针,直接传实现构建好的对象。

另一个就是直接使用两个宏:

cpp 复制代码
QML_SINGLETON
QML_ELEMENT

这样的话就可以不用qmlRegisterSingletonType来进行注册了,缺点是无法指定别名、模块名和版本名,这一点和上一节中注册对象的是一样的。

三、总结

本节我们介绍了在qml侧使用c++单例对象的方式,这可能会给我们接下来的开发提供思路和帮助。

相关推荐
码途漫谈5 小时前
Easy-Vibe开发篇阅读笔记(四)——前端开发之结合 Agent Skills 美化界面
人工智能·笔记·ai·开源·ai编程
John_ToDebug5 小时前
隐于无形,触手可及:Chrome 互动滚动条的六个设计密码
chrome·windows·ui
酿情师5 小时前
yihan:一款面向连续网页学习的智能侧边栏插件
学习·学习方法·工具·学习工具
九转成圣6 小时前
Java 性能优化实战:如何将海量扁平数据高效转化为类目字典树?
java·开发语言·json
SmartRadio6 小时前
ESP32-S3 双模式切换实现:兼顾手机_路由器连接与WiFi长距离通信
开发语言·网络·智能手机·esp32·长距离wifi
laowangpython6 小时前
Rust 入门:GitHub 热门内存安全编程语言
开发语言·其他·rust·github
我叫汪枫6 小时前
在后台管理系统中,如何递归和选择保留的思路来过滤菜单
开发语言·javascript·node.js·ecmascript
_.Switch6 小时前
东方财富股票数据JS逆向:secids字段和AES加密实战
开发语言·前端·javascript·网络·爬虫·python·ecmascript
软件技术NINI6 小时前
webkit简介及工作流程
开发语言·前端·javascript·udp·ecmascript·webkit·yarn
Brendan_0016 小时前
JavaScript的Stomp.over
开发语言·javascript·ecmascript