Qt中的反射机制之Q_PROPERTY

Hello大家好,本篇文章继续为大家介绍Qt的核心内容。

学习过Java的朋友肯定都听说过“反射”一词,所谓反射,就是对于任意一个类,在运行状态下都能够知道这个类的所有属性和方法,并可以对这些方法动态调用的一种机制。

反射可以用于编写服务框架程序时,通过配置的手段,动态的加载不同方法;也可用于网络传输中传输对象等。

但是,很遗憾,我们所学习的C++本身并没有支持反射这一机制,但是通过Qt中的元对象系统,有了对反射的很好支持。

在讲述反射编程之前,需要先掌握QMetaObject,QMetaProperty,QVariant,和Q_PROPERTY。

本篇文章,就先为大家介绍Q_PROPERTY(…)宏的用法。

Qt之所以能支持反射,是因为在编译C++代码之前,使用了moc元对象编译器。moc读取一个C++头文件,如果它发现一个或多个包含Q_OBJECT宏的类声明,它将生成一个包含这些类的元对象代码的C++源文件。而用以支持元对象系统或是反射机制的代码也包含其中。

所以Qt中使用Q_PROPERTY宏来告诉moc用以支持类的哪些属性。

Q_PROPERTY宏的声明如下:
Qt中的反射机制之Q_PROPERTY

其中,属性名称、属性类型和获取属性的READ函数是必须提供的参数。其他项是可选的,但WRITE函数最好也要有。(这里只对属性名称,READ,WRITE参数作介绍,其它参数等用到了再作详细介绍)

下面,请看一个例子:
新建一个空的控制台工程,取名为MetaProperty
mystudent.h

#ifndef MYSTUDENT_H
#define MYSTUDENT_H

#include <QObject>

/*
 * 学生类
 * 该类继承QObject,并且Q_OBJECT宏被包含在私有段中,以便moc生成可支持属性系统的C++代码
*/
class MyStudent : public QObject
{
    Q_OBJECT

    //id代表属性m_id的名称,类型为QString()
    //推荐属性使用“m_属性名称”的形式
    //READ函数使用“get属性名称”的形式
    //WRITE函数使用“set属性名称”的形式
    Q_PROPERTY(QString id READ getId WRITE setId)
    Q_PROPERTY(QString name READ getName WRITE setName)
    Q_PROPERTY(Sex sex  READ getSex WRITE setSex)

    //用来生成字符串到枚举值的转换函数
    Q_ENUMS(Sex)
public:
    enum Sex//性别
    {
        Man, //男
        Woman//女
    };
    explicit MyStudent(QObject *parent = 0);

    QString getId() const;
    void setId(const QString &id);

    QString getName() const;
    void setName(const QString &name);

    Sex getSex() const;
    QString getSexString() const;
    void setSex(const Sex &sex);
    void setSex(const QString& sex);

private:
    QString m_id;   //学生id
    QString m_name; //学生姓名
    Sex     m_sex;  //学生性别
};

#endif // MYSTUDENT_H

mystudent.cpp

#include <QMetaProperty>
#include <QVariant>
#include "mystudent.h"

MyStudent::MyStudent(QObject *parent) : QObject(parent)
{

}

QString MyStudent::getId() const
{
    return m_id;
}

void MyStudent::setId(const QString &id)
{
    m_id = id;
}

QString MyStudent::getName() const
{
    return m_name;
}

void MyStudent::setName(const QString &name)
{
    m_name = name;
}

MyStudent::Sex MyStudent::getSex() const
{
    return m_sex;
}

QString MyStudent::getSexString() const
{
    return property("Sex").toString();
}

void MyStudent::setSex(const Sex &sex)
{
    m_sex = sex;
}

void MyStudent::setSex(const QString &sex)
{
    //首先获取元对象指针
    static const QMetaObject* meta = metaObject();

    //根据属性名称Sex,查找m_sex属性对象QMetaProperty
    static int propindex = meta->indexOfProperty("sex");
    static const QMetaProperty mp = meta->property(propindex);

    //QMetaProperty中的方法enumerator可以将字符串转化成枚举值
    //如果给定的字符串不与任何枚举值匹配,则返回-1
    QMetaEnum menum = mp.enumerator();
    const char* ntyp = sex.toStdString().c_str();
    m_sex = static_cast<Sex>(menum.keyToValue(ntyp));
}

main.cpp

#include <QVariant>
#include "mystudent.h"

int main()
{    
    MyStudent student;
    student.setId("1");
    student.setName("Tom");
    student.setSex(MyStudent::Man);
    Q_ASSERT(student.getSex()==MyStudent::Man);

    //通过属性名称获取属性值
    QVariant v = student.property("id");
    QString str = v.toString();
    Q_ASSERT("1"==str);

    student.setProperty("id", QVariant("2"));
    Q_ASSERT("2"==student.getId());

    student.setSex("Woman");
    Q_ASSERT(student.getSex()==MyStudent::Woman);
}

使用setProperty()和property()可以设置和获取由Q_PROPERTY宏包含的属性。
关于Q_PROPERTY宏的介绍就先到这里了。

欢迎关注微信公众号"小豆君Qt分享",最新文章都会在公众号第一时间发布,或者你有不懂的问题,关注公众号后,可加好友或进Qt群获得答案。