1 需求描述
在项目开发过程中可能会有这样一种需求,就是我连头文件都没有只知道类的名字,在这种情况下需要将对象实例化出来,同时还要调用类中的方法。想想有点不可思议,但在Qt的世界里,这些是很容易实现的。
2 实现过程
举一个简单例子,一个基类Person,一个子类Student。
- 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();
}
- Student类
同样的,构造函数Q_INVOKABLE声明一下:
#include "Person.h"
class Student : public Person
{
Q_OBJECT
public:
Q_INVOKABLE explicit Student(QObject *parent = nullptr);
};
到这里,两个测试类就准备完毕了。
注意细节,需要是QObject子类,同时需要声明Q_OBJECT,这样才能享受到元对象系统带来的福利,还是免费的。
- 保存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的帮助文档就是最好的学习资料,一切尽在其中。