The QObject

As described in the introduction, the QObject is what enables mant of Qt’s core functions such as signals and slots. This is implemented through introspection, which is what QObject provides. QObject is the base class of almost all classes in Qt. Exceptions are value types such as QColor, QString and QList.

A Qt object is a standard C++ object, but with more abilities. These can be divided into two groups: introspection and memory management. The first means that a Qt object is aware of its class name, its relationship to other classes, as well as its methods and properties. The memory management concept means that each Qt object can be the parent of child objects. The parent owns the children, and when the parent is destroyed, it is responsible for destroying its children.

The best way of understanding how the QObject abilities affect a class is to take a standard C++ class and Qt enables it. The class shown below represents an ordinary such class.

The person class is a data class with a name and gender properties. The person class uses Qt’s object system to add meta information to the c++ class. It allows users of a person object to connect to the slots and get notified when the properties get changed.

  1. class Person : public QObject
  2. {
  3. Q_OBJECT // enabled meta object abilities
  4. // property declarations required for QML
  5. Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
  6. Q_PROPERTY(Gender gender READ gender WRITE setGender NOTIFY genderChanged)
  7. // enables enum introspections
  8. Q_ENUMS(Gender)
  9. // makes the type creatable in QML
  10. QML_ELEMENT
  11. public:
  12. // standard Qt constructor with parent for memory management
  13. Person(QObject *parent = 0);
  14. enum Gender { Unknown, Male, Female, Other };
  15. QString name() const;
  16. Gender gender() const;
  17. public slots: // slots can be connected to signals, or called
  18. void setName(const QString &);
  19. void setGender(Gender);
  20. signals: // signals can be emitted
  21. void nameChanged(const QString &name);
  22. void genderChanged(Gender gender);
  23. private:
  24. // data members
  25. QString m_name;
  26. Gender m_gender;
  27. };

The constructor passes the parent to the superclass and initializes the members. Qt’s value classes are automatically initialized. In this case QString will initialize to a null string (QString::isNull()) and the gender member will explicitly initialize to the unknown gender.

  1. Person::Person(QObject *parent)
  2. : QObject(parent)
  3. , m_gender(Person::Unknown)
  4. {
  5. }

The getter function is named after the property and is normally a basic const function. The setter emits the changed signal when the property has changed. To ensure that the value actually has changed, we insert a guard to compare the current value with the new value. Only when the value differs we assign it to the member variable and emit the changed signal.

  1. QString Person::name() const
  2. {
  3. return m_name;
  4. }
  5. void Person::setName(const QString &name)
  6. {
  7. if (m_name != name) // guard
  8. {
  9. m_name = name;
  10. emit nameChanged(m_name);
  11. }
  12. }

Having a class derived from QObject, we have gained more meta object abilities we can explore using the metaObject() method. For example, retrieving the class name from the object.

  1. Person* person = new Person();
  2. person->metaObject()->className(); // "Person"
  3. Person::staticMetaObject.className(); // "Person"

There are many more features which can be accessed by the QObject base class and the meta object. Please check out the QMetaObject documentation.

TIP

QObject, and the Q_OBJECT macro has a lightweight sibling: Q_GADGET. The Q_GADGET macro can be inserted in the private section of non-QObject-derived classes to expose properties and invokable methods. Beware that a Q_GADGET object cannot have signals, so the properties cannot provide a change notification signal. Still, this can be useful to provide a QML-like interface to data structures exposed from C++ to QML without invoking the cost of a fully fledged QObject.