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的幫助文檔就是最好的學習資料,一切盡在其中。

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