Собственные классы в Qt. Создание элементов графического интерфейса
Как мы уже знаем, каждый объект может иметь "родительские" и "дочерние" объекты. Таким образом объекты организуют в иерархические структуры напоминающие дерево . Каждый из объектов может содержать дочерние объекты, а также иметь родительский объект . Отношение между объектами можно задать либо воспользовавшись параметром конструктора (при создании), или же с помощью метода QObject::setParent() . Обычно пользуются первым способом, то есть задают родительский объект при конструировании.
Если виджет содержит компоновщики, то и компоновщики и все размещённые в нём виджеты автоматически получат "родителя" (ведь каждый визуальный элемент наследует от QWidget , а тот в свою очередь наследует от QObject ). Объекты также могут менять "родителя", для этого достаточно вызвать метод QObject::setParent (QObject *) с указателем на другой родительский объект . Объектную иерархию можно увидеть наглядно, воспользовавшись методом dumpObjectTree() класса QObject . Он выводит в стандартный поток вывода тип и имя всех дочерних объектов. Зададим имена для объектов lLabel и lPushButton :
Если теперь вызвать метод dumpObjectTree() для объекта lSomeWidget , то получим вывод :
Когда родительский объект удаляют, дочерние объекты тоже будут удалены из памяти перед удалением родительского. То же самое происходит и при удалении виджетов — все дочерние виджеты в визуальной иерархии тоже удаляются. Это позволяет управлять высвобождением памяти в программе.
Управление памятью важно, поскольку неуправляемое выделение памяти приводит к утечке памяти в программе — ситуации, когда программа не освобождает память . Если такое неуправляемое выделение памяти повторяется периодически, а сама программа выполняется относительно долго, то со временем программа будет занимать всё больше места в оперативной памяти. Наконец, таким образом можно исчерпать всю доступную память , что приведёт к аварийному завершению программы и исчерпанию ресурсов операционной системы.
Например, при использовании ключевого слова new память выделяется в динамически распределяемой памяти (так называемой "куче" — heap ). Динамическая память должна быть освобождена от созданных объектов с помощью ключевого слова delete для того, чтобы избежать утечки памяти ( memory leak ). Объекты, созданные в динамически распределяемой памяти могут существовать столько, сколько необходимо. Динамическая память не будет освобождена автоматически, поэтому программист должен самостоятельно следить за высвобождением выделенной памяти. Рассмотрим следующий пример:
Хотя с первого взгляда программа может показаться корректной (она компилируется и выполняется), в ней есть ошибка, связанная с некорректным управлением памятью. Для того, чтобы проанализировать использование памяти в программе, воспользуемся программой Valgrind . Это свободно-распространяемая программа для Linux с открытым кодом. Среда Qt Creator поддерживает работу с этой программой для анализа использования памяти и оптимизации программ на Qt .
Перейдите в режим анализа Analyse ( Анализ ) воспользовавшись кнопкой на панели переключения режимов работы слева или воспользуйтесь комбинацией клавиш Ctrl+6 . Снизу под окном редактирования появится дополнительная панель. В выпадающем списке выберите Valgrind Memory Analyzer (Анализатор памяти Valgrind) и запустите процесс анализа (нажмите на кнопку с пиктограммой треугольника на панели или выберите в главном меню Analyze->Valgrind Memory Analyzer ( Анализ ->Анализатор памяти Valgrind). Начнётся анализ работы программы и программа запустится. Затем закройте окно программы. После завершения работы на панели появится сообщение об утечке памяти (см. рис. 14.1).
Сообщение указывает на строку:
Это происходит из-за того, что для lSomeWidget была выделена динамическая память , но не была корректно высвобождена с помощью оператора delete . Для того, чтобы корректно освободить память , модифицируем последние строки программы:
Скомпилируем программу — после повторного анализа сообщение об утечке памяти исчезнет. Конечно, в этом примере утечка памяти не приводит к негативным последствиям. Выделение памяти происходит только раз и после завершения работы вся использованная оперативная память высвобождается операционной системой. Но в крупных проектах утечки памяти могут стать серьёзной проблемой. Инструменты для анализа памяти, такие как Valgrind, позволяют локализовать и исправить их.
Можно выделить память для родительского объекта в стеке. Память для объектов, созданных в стеке, освобождается автоматически как только объект выходит за пределы области видимости. Поэтому объекты, созданные в стеке, удаляются как только они выходят из области видимости. Для того, чтобы освободить память автоматически, достаточно только родительский объект создать в стеке. Как только родительский объект удаляется, будут автоматически удалены и все дочерние объекты. Мы можем модифицировать последний пример таким образом, чтобы память для родительского объекта была выделена в стеке. Для этого закомментируйте все части программы обозначенные (1) и измените текст программы:
При описании собственных классов необходимо учитывать следующие рекомендации: