Символы кириллицы выводит некорректно. Как сделать чтобы выводился читабельный текст на русском языке? Тип приложения - не Qt, Qt Creator 4.5.0 основан на Qt 5.10.0. Win7.
|
Собственные классы в Qt. Создание элементов графического интерфейса
14.2 Управление памятью. Иерархии объектов
Как мы уже знаем, каждый объект может иметь "родительские" и "дочерние" объекты. Таким образом объекты организуют в иерархические структуры напоминающие дерево. Каждый из объектов может содержать дочерние объекты, а также иметь родительский объект. Отношение между объектами можно задать либо воспользовавшись параметром конструктора (при создании), или же с помощью метода QObject::setParent(). Обычно пользуются первым способом, то есть задают родительский объект при конструировании.
//Задаём родительский виджет с помощью конструктора QLabel * lLabel = new QLabel ( lSomeWidget ); //Задаём родительский виджет с помощью метода QObject::setparent ( ) QPushButton * lPushButton = new QPushButton; lPushButton->setParent ( lSomeWidget );
Если виджет содержит компоновщики, то и компоновщики и все размещённые в нём виджеты автоматически получат "родителя" (ведь каждый визуальный элемент наследует от QWidget, а тот в свою очередь наследует от QObject). Объекты также могут менять "родителя", для этого достаточно вызвать метод QObject::setParent (QObject *) с указателем на другой родительский объект. Объектную иерархию можно увидеть наглядно, воспользовавшись методом dumpObjectTree() класса QObject. Он выводит в стандартный поток вывода тип и имя всех дочерних объектов. Зададим имена для объектов lLabel и lPushButton:
lLabel ->setobjectName ( " childLabel " ); lPushButton->setobjectName ( " child PushButton " );
Если теперь вызвать метод dumpObjectTree() для объекта lSomeWidget, то получим вывод:
SomeWidget : : QLabel::childLabel QPushButton::childPushButton
Когда родительский объект удаляют, дочерние объекты тоже будут удалены из памяти перед удалением родительского. То же самое происходит и при удалении виджетов — все дочерние виджеты в визуальной иерархии тоже удаляются. Это позволяет управлять высвобождением памяти в программе.
Управление памятью важно, поскольку неуправляемое выделение памяти приводит к утечке памяти в программе — ситуации, когда программа не освобождает память. Если такое неуправляемое выделение памяти повторяется периодически, а сама программа выполняется относительно долго, то со временем программа будет занимать всё больше места в оперативной памяти. Наконец, таким образом можно исчерпать всю доступную память, что приведёт к аварийному завершению программы и исчерпанию ресурсов операционной системы.
Например, при использовании ключевого слова new память выделяется в динамически распределяемой памяти (так называемой "куче" — heap). Динамическая память должна быть освобождена от созданных объектов с помощью ключевого слова delete для того, чтобы избежать утечки памяти (memory leak). Объекты, созданные в динамически распределяемой памяти могут существовать столько, сколько необходимо. Динамическая память не будет освобождена автоматически, поэтому программист должен самостоятельно следить за высвобождением выделенной памяти. Рассмотрим следующий пример:
#include <QApplication> #include <QWidget> #include <QLabel> #include <QPushButton> int main ( int arg c, char * argv [ ] ) { QApplication lApplication ( arg c, argv ); //(1) Память в динамически-распределяемой памяти (heap) QWidget _lSomeWidget = new QWidget ( 0 ); //Окно //Задаём родительский виджет с помощью конструктора QLabel * lLabel = new QLabel ( lSomeWidget ); //Задаём родительский виджет с помощью метода QObject::setparent ( ) QPushButton _ lPushButton = new QPushButton; lPushButton->setParent ( lSomeWidget ); lLabel ->setText ( " Label " ); lLabel ->move ( 10, 10 ); lPushButton->setText ( " Button " ); lPushButton->move ( 50, 10 ); //(1) Память в динамически-распределяемой памяти (heap) lSomeWidget->resize ( 150, 50 ); lSomeWidget->show ( ); return lApplication.exec ( ); }
Хотя с первого взгляда программа может показаться корректной (она компилируется и выполняется), в ней есть ошибка, связанная с некорректным управлением памятью. Для того, чтобы проанализировать использование памяти в программе, воспользуемся программой Valgrind. Это свободно-распространяемая программа для Linux с открытым кодом. Среда Qt Creator поддерживает работу с этой программой для анализа использования памяти и оптимизации программ на Qt.
Перейдите в режим анализа Analyse (Анализ) воспользовавшись кнопкой на панели переключения режимов работы слева или воспользуйтесь комбинацией клавиш Ctrl+6. Снизу под окном редактирования появится дополнительная панель. В выпадающем списке выберите Valgrind Memory Analyzer (Анализатор памяти Valgrind) и запустите процесс анализа (нажмите на кнопку с пиктограммой треугольника на панели или выберите в главном меню Analyze->Valgrind Memory Analyzer (Анализ->Анализатор памяти Valgrind). Начнётся анализ работы программы и программа запустится. Затем закройте окно программы. После завершения работы на панели появится сообщение об утечке памяти (см. рис. 14.1).
Сообщение указывает на строку:
QWidget _lSomeWidget = new QWidget ( 0 ); //Окно
Это происходит из-за того, что для lSomeWidget была выделена динамическая память, но не была корректно высвобождена с помощью оператора delete. Для того, чтобы корректно освободить память, модифицируем последние строки программы:
int exitCode = lApplication.exec ( ); //(1) Память в динамически-распределяемой памяти (heap) delete lSomeWidget; return exitCode;
Скомпилируем программу — после повторного анализа сообщение об утечке памяти исчезнет. Конечно, в этом примере утечка памяти не приводит к негативным последствиям. Выделение памяти происходит только раз и после завершения работы вся использованная оперативная память высвобождается операционной системой. Но в крупных проектах утечки памяти могут стать серьёзной проблемой. Инструменты для анализа памяти, такие как Valgrind, позволяют локализовать и исправить их.
Можно выделить память для родительского объекта в стеке. Память для объектов, созданных в стеке, освобождается автоматически как только объект выходит за пределы области видимости. Поэтому объекты, созданные в стеке, удаляются как только они выходят из области видимости. Для того, чтобы освободить память автоматически, достаточно только родительский объект создать в стеке. Как только родительский объект удаляется, будут автоматически удалены и все дочерние объекты. Мы можем модифицировать последний пример таким образом, чтобы память для родительского объекта была выделена в стеке. Для этого закомментируйте все части программы обозначенные (1) и измените текст программы:
//(2) Память в стеке SomeWidget lSomeWidget; //Задаём родительский виджет с помощью конструктора QLabel * lLabel = new QLabel(& lSomeWidget ); //Задаём родительский виджет с помощью метода QObject::setparent ( ) QPushButton * lPushButton = new QPushButton; lPushButton->setParent (& lSomeWidget ); ..... //(2) Память в стеке lSomeWidget.resize ( 1 5 0, 5 0 ); lSomeWidget.show ( );
При описании собственных классов необходимо учитывать следующие рекомендации:
- задавать для конструкторов класса параметр, который принимает указатель на родительский объект и имеет значение по умолчанию 0;
- создавать дополнительный конструктор, который принимает только параметр с указателем на родительский объект;
- параметр с указателем на "родителя" желательно должен быть первым параметром среди параметров со значением по умолчанию (если параметров со значением по умолчанию несколько);
- используйте ключевое слово this как указатель на родительский объект при создании объектов внутри своего класса. Например:
#include <QWidget> class CustomWidget : public QWidget { Q_OBJECT public : explicit CustomWidget ( QWidget * parent = 0 ); }; //Конструктор CustomWidget::CustomWidget ( QWidget * parent ) : QWidget ( parent ) { //Задаём родительский виджет — this то есть экземпляр класса CustomWidget QPushButton * lPushButton = new QPushButton ( this ); lPushButton->setGeometry ( 50, 50, 200, 30 ); }