• Qt小技巧8.利用反射机制通过类名创建Qt对象


    1 需求描述

    在项目开发过程中可能会有这样一种需求,就是我连头文件都没有只知道类的名字,在这种情况下需要将对象实例化出来,同时还要调用类中的方法。想想有点不可思议,但在Qt的世界里,这些是很容易实现的。

    2 实现过程

    举一个简单例子,一个基类Person,一个子类Student。

    1. Person类
      构造函数需要用Q_INVOKABLE声明一下,这样元对象系统才可以调用。
    #include <QObject>
    class Person : public QObject
    {
        Q_OBJECT
    public:
        Q_INVOKABLE explicit Person(QObject *parent = nullptr);
    
    public slots:
        void show();
    };
    
    #include "Person.h"
    #include <QDebug>
    Person::Person(QObject *parent) : QObject(parent)
    {
    }
    void Person::show()
    {
        qDebug() << __FUNCTION__ << this->metaObject()->className();
    }
    
    1. Student类
      同样的,构造函数Q_INVOKABLE声明一下:
    #include "Person.h"
    class Student : public Person
    {
        Q_OBJECT
    public:
        Q_INVOKABLE explicit Student(QObject *parent = nullptr);
    };
    

    到这里,两个测试类就准备完毕了。

    注意细节,需要是QObject子类,同时需要声明Q_OBJECT,这样才能享受到元对象系统带来的福利,还是免费的。

    1. 保存QMetaObject元对象指针
      这里仅做简单示例,直接就在MainWindow里写了。
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
        void saveMetaObject(const QMetaObject *metaObject);
        QObject *createPerson(const QString &className);
    
    private:
        Ui::MainWindow *ui;
        QMap<QString, const QMetaObject*> map;
    };
    
    #include "Person.h"
    #include "Student.h"
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
        saveMetaObject(&Person::staticMetaObject);
        saveMetaObject(&Student::staticMetaObject);
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::saveMetaObject(const QMetaObject *metaObject)
    {
        map.insert(metaObject->className(), metaObject);
    }
    
    QObject *MainWindow::createPerson(const QString &className)
    {
        if (!map.keys().contains(className))
        {
            return nullptr;
        }
        return map.value(className)->newInstance();
    }
    

    简单解释下,元对象主要用于创建对应的对象,有了它就可以通过类名实例化对象了,上面的newInstance就是干这事。
    4. 轻轻的试一下效果

    #include "MainWindow.h"
    #include <QApplication>
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        QObject *obj = w.createPerson("Person");
        QMetaObject::invokeMethod(obj, "show");
    
        obj = w.createPerson("Student");
        QMetaObject::invokeMethod(obj, "show");
    
        return a.exec();
    }
    

    结果打印信息如下:

    main函数中并没有Person、Student类的头文件,但是却成功创建了Person、Student对象,并成功调用了类的成员函数(Qt的槽函数自动就可以“元调用”了)。咦,这不就是工厂模式嘛,无意之中就实现了。

    3 简单总结

    Qt的元对象系统很强大,这里只用到一点皮毛,利用好Qt元对象系统反射机制往往可达到事半功倍的效果,最后提一下,Qt的帮助文档就是最好的学习资料,一切尽在其中。

    © 版权声明
    文章版权归作者所有,未经允许请勿转载。【QQ交流:115124903】
    THE END
  • 相关阅读:
    java中原子操作的实现分析
    AQS源码分析
    Java JNDI 学习
    门面模式、代理模式、适配器模式的区别
    tomcat源码阅读之StandardWrapper解析
    tomcat源码阅读之过滤器
    tomcat源码阅读之SingleThreadModel
    tomcat源码阅读之单点登录
    技术人员的八条宝贵经验,时刻提醒自己,共勉之
    tomcat源码阅读之安全机制
  • 原文地址:https://www.cnblogs.com/luoxiang/p/14769945.html
Copyright © 2020-2023  润新知