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的帮助文档就是最好的学习资料,一切尽在其中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章