На этом шаге рассмотрим организацию объектных иерархий.
Организация объектов в иерархии снимает с разработчика необходимость самому заботиться об освобождении памяти от созданных объектов.
Конструктор класса QObject выглядит следующим образом:
QObject(QObject* pobj = 0);
В его параметре передается указатель на другой объект класса QObject или унаследованного от него класса.
Благодаря этому параметру существует возможность создания объектов-иерархий. Он представляет собой указатель на объект-предок.
Если в первом параметре передается значение равное нулю или ничего не передается, то это значит, что у создаваемого объекта нет предка, и он будет являться объектом верхнего уровня и находиться на вершине объектной иерархии.
Объект-предок задается в конструкторе при создании объекта, но впоследствии его можно в любой момент исполнения программы изменить на другой при помощи метода setParent().
Созданные объекты по умолчанию не имеют имени. При помощи метода setObjectName() можно присвоить объекту имя. Имя объекта не имеет особого значения, но может быть полезно при отладке программы. Для того чтобы узнать имя объекта, можно вызвать метод objectName().
Рассмотрим пример объектной иерархии:
QObject* pobj1 = new QObject; QObject* pobj2 = new QObject(pobj1); QObject* pobj4 = new QObject(pobj2); QObject* pobj3 = new QObject(pobj1); pobj2->setObjectName("the first child of pobj1"); pobj3->setObjectName("the second child of pobj1"); pobj4->setObjectName("the first child of pobj2");
В первой строке листинга создается объект верхнего уровня (объект без предка). При создании объекта pobj2 в его конструктор передается, в качестве предка, указатель на объект pobj1. Объект pobj3 имеет в качестве предка pobj1, а объект pobj4 имеет предка pobj2. Полученная объектная иерархия показана на рис. 1.
Рис.1. Схема объектной иерархии
При уничтожении созданного объекта (при вызове его деструктора) все присоединенные к нему объекты-потомки уничтожаются автоматически. Эта особенность рекурсивного уничтожения объектов значительно упрощает программирование, т. к. не нужно заботиться об освобождении ресурсов памяти. Именно поэтому необходимо создавать объекты, а особенно объекты неверхнего уровня, динамически, при помощи оператора new, иначе удаление объекта приведет к ошибке при исполнении программы.
Для получения информации об объектной иерархии существуют два метода: parent() и children().
С помощью метода parent() можно определить объект-предок. Согласно рис. 1, вызов pobj2->parent() вернет указатель на объект obj1.
Для объектов верхнего уровня этот метод вернет значение 0.
Чтобы вывести на консоль всю цепь имен объектов-предков какого-либо из объектов, можно поступить так:
for (QObject* pobj = pobj4; pobj; pobj = pobj->parent()) { qDebug() << pobj->objectName(); }
На экране будет отображена следующая информация:
the first child of pobj2 the first child of pobj1
Метод children() возвращает константный указатель на список объектов-потомков. Для приведенного выше примера (рис. 1), метод pobj1->children() возвратит указатель на список QObjectList, содержащий два элемента: указатели pobj2 и pobj3.
Можно осуществлять поиск нужного объекта-потомка при помощи метода findChild(). В параметре этого метода необходимо передать имя искомого объекта. Например, следующий вызов возвратит указатель на объект pobj4:
QObject* pobj = pobj1->findChild<QObject*>("the first child of pobj2");
Для расширенного поиска существует метод findChildren(), возвращающий список указателей на объекты. Все параметры метода не обязательны, может передаваться либо строка имени, либо регулярное выражение, а вызов метода без параметров приведет к тому, что он вернет список указателей на все объекты-потомки. Поиск производится рекурсивно. Следующий вызов возвратит список указателей на все объекты, имя которых начинается с букв th, в нашем случае их три:
QList<QObject*> plist = pobj1->findChildren<QObject*>(QRegExp("th*"));.
Можно воспользоваться также глобальной шаблонной функцией qFindChildren(), которая возвращает все объекты потомков указанного типа:
QList<QObject*> plist = ::qFindChildren<QObject*>(pobj1);
В данном конкретном случае эта функция нам вернет указатели на объекты pobj2, pobj3 и pobj4.
Для отладки программы полезен метод dumpObjectInfo(), который показывает следующую информацию, относящуюся к объекту:
Вся эта информация поступает в стандартный поток вывода stdout.
При отладке можно воспользоваться и методом dumpObjectTree(), предназначенным для отображения объектов-потомков в виде иерархии. Например, вызов dumpObjectTree() для нашего объекта pobj1 покажет:
QObject:: QObject::the first child of pobj1 QObject::the first child of pobj2 QObject::the second child of pobj1
На следующем шаге рассмотрим класс QMetaObject - метаобъектная информация.