# QObject
如介绍中所述,QObject
是实现Qt核心功能(如信号和插槽)的关键。这是通过内省实现的,而QObject
提供了内省。。 QObject
是Qt中几乎所有类的基类。值类型是个例外,例如QColor
、QString
和QList
。
Qt对象是标准的C++对象,但具有更多功能。这些功能可以分为两组:内省和内存管理。内省意味着Qt对象知道自己的类名、自己与其他类的关系,以及自己的方法和属性。内存管理概念意味着每个Qt对象都可以是子对象的父对象。父对象拥有子对象,当父对象被销毁时,它负责销毁其子对象。
了解QObject
的能力是如何影响类的最佳方式是:使用Qt使能标准的C++类。下面显示的类代表一个这样的普通类。
Person类是具有名字、性别属性的数据类。 Person类使用Qt的对象系统向c++类中添加元信息。它允许Person对象的用户连接到槽并在属性更改时得到通知。
class Person : public QObject
{
Q_OBJECT // 启用元对象能力
// QML所需的属性声明
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(Gender gender READ gender WRITE setGender NOTIFY genderChanged)
// 启用枚举自省
Q_ENUMS(Gender)
// 使得在QML里面可以创建这个类型
QML_ELEMENT
public:
// 带有父对象进行内存管理的标准Qt构造器
Person(QObject *parent = 0);
enum Gender { Unknown, Male, Female, Other };
QString name() const;
Gender gender() const;
public slots: // 可以被连接到信号的槽,或被调用
void setName(const QString &);
void setGender(Gender);
signals: // 可以被发射的信号
void nameChanged(const QString &name);
void genderChanged(Gender gender);
private:
// 数据成员
QString m_name;
Gender m_gender;
};
构造函数将父对象传递给超类并初始化成员。Qt的值类型会自动初始化。在本例中,QString
将初始化为空字符串(QString::isNull()
),而成员gender将显式初始化为Person::Unknown
。
Person::Person(QObject *parent)
: QObject(parent)
, m_gender(Person::Unknown)
{
}
getter函数以属性命名,通常是一个简单的const
函数。当属性发生更改时,setter会发出更改的信号。为了确保值实际上已经更改,插入了一个守护功能来比较当前值和新值。只有当值不同时,才将其分配给成员变量并发出更改的信号。
QString Person::name() const
{
return m_name;
}
void Person::setName(const QString &name)
{
if (m_name != name) // guard
{
m_name = name;
emit nameChanged(m_name);
}
}
有了一个从QObject
派生的类,可以使用metaObject()
方法探索获得更多的元对象能力。 例如,从对象中检索类名。
Person* person = new Person();
person->metaObject()->className(); // "Person"
Person::staticMetaObject.className(); // "Person"
QObject
基类和元对象可以访问的更多功能,请查看QMetaObject
文档。
提示
QObject
和Q_OBJECT
宏有一个轻量级的兄弟:Q_GADGET
。可以将Q_GADGET
宏插入到非QObject
派生类的私有部分,以暴露其属性和可调用的方法。请注意,Q_GADGET
对象不能有信号,因此属性不能提供被更改的通知信号。尽管如此,这对于从C++暴露到QML的数据结构提供类似于QML的接口还是很有用的,而无需消耗调用完全成熟的QObject
的成本。