Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Программирование с использованием gtkmm 3. Управление памятью

Оригинал: Programming with gtkmm 3
Авторы: Murray Cumming, Bernhard Rieder, Jonathon Jongsma, Ole Laursen, Marko Anastasov, Daniel Elstner, Chris Vine, David King, Pedro Ferreira, Kjell Ahlstedt
Дата публикации: 15 Октября 2013 г.
Перевод: А.Панин
Дата перевода: 10 Апреля 2014 г.

25.Управление памятью

25.1. Виджеты

25.1.1. Обычный механизм управления памятью языка программирования C++

gtkmm позволяет разработчику контролировать время жизни (т.е., этапы создания и уничтожения) объектов каждого из виджетов таким же образом, как это делается при работе с любым другим объектом языка программирования C++. Такая гибкость позволяет вам использовать операторы new и delete для динамического создания и уничтожения объектов, а также использовать обычные члены класса (которые уничтожаются автоматически в момент уничтожения класса) или использовать локальные экземпляры (которые уничтожаются в момент, когда экземпляр покидает область действия). Подобная гибкость недоступна для некоторых тулкитов, предназначенных для создания графических интерфейсов с использованием языка программирования C++ и ограничивающих разработчика рамками подмножества функций для управления памятью языка C++.

Ниже приведены примеры выполнения обычных операций управления памятью средствами языка программирования C++.

25.1.1. Виджеты из области действия класса

В том случае, если разработчику не требуется динамически резервировать память, могут использоваться автоматические виджеты из области действия класса. Одним из достоинств автоматических виджетов из области действия класса является группировка функций управления памятью в одном месте. Разработчик не подвергается риску, связанному с возникновением утечек памяти в случае пропуска оператора delete для уничтожения объекта виджета.

Главным недостатком использования виджетов из области действия класса является раскрытие реализации класса вместо предоставления исключительно интерфейса в рамках заголовочного файла класса.
#include <gtkmm/button.h>
#include <gtkmm/window.h>
class Foo : public Gtk::Window
{
private:
  Gtk::Button theButton;
  // будет уничтожен в момент уничтожения объекта Foo
};

25.1.1.2. Виджеты из области действия функций

В том случае, если разработчику не требуется виджет из области действия класса, он также может использовать виджет из области действия функции. Достоинствами области действия функции по сравнению с областью действия класса являются повышение степени сокрытия структур данных и сокращение зависимостей.

{
  Gtk::Button aButton;
  aButton.show();
  ...
  app->run();
}

25.1.1.3. Динамическое резервирование памяти с помощью операторов new и delete

Несмотря на то, что в большинстве случаев при работе с контейнерными виджетами разработчик предпочтет использовать механизм автоматического уничтожения дочерних виджетов, реализованный с помощью метода Gtk::manage() (о котором будет сказано ниже), он не обязан использовать метод Gtk::manage(). Традиционные операторы new и delete также могут использоваться в таких случаях.
Gtk::Button* pButton = new Gtk::Button("Тест");

// выполнение каких-либо полезных действий с объектом pButton

delete pButton;

В данном случае разработчик использует оператор delete по отношению к объекту pButton для предотвращения утечки памяти.

25.1.2. Управляемые виджеты

Альтернативным решением является предоставление контейнерному виджету контроля за дочерними виджетами в процессе его уничтожения. В большинстве случаев вы посчитаете разумным поддержание существования виджета в течение такого долгого периода, в течение которого существует контейнер, в котором он размещен. Для делегирования прав управления временем жизни виджета его контейнеру сначала следует создать виджет с помощью метода Gtk::manage() и упаковать его в контейнер с помощью метода Gtk::Box::pack_start() или любого другого аналогичного метода. После этого виджет будет уничтожаться в тот момент, когда будет уничтожаться его контейнер.

25.1.2.1. Динамическое резервирование памяти с помощью методов manage() и add()

gtkmm предоставляет функцию manage() и методы add() для создания и уничтожения виджетов. Каждый виджет, за исключением виджета окна верхнего уровня, должен помещаться в контейнер для последующего показа. Функция manage() устанавливает флаг для виджета, в соответствии с которым в момент, когда этот виджет добавляется в контейнер, контейнер становится ответственным за уничтожение этого виджета.
MyContainer::MyContainer()
{
  Gtk::Button* pButton = Gtk::manage(new Gtk::Button("Test"));
  add(*pButton); //добавление виджета *pButton в контейнер MyContainer
}

Теперь, в момент уничтожения объектов типа MyContainer объект виджета кнопки также будет уничтожен. Больше не требуется уничтожать объект pButton для освобождения зарезервированной для виджета кнопки памяти; право на уничтожение этого объекта было делегировано объекту MyContainer.

Конечно же, контейнер верхнего уровня не может быть добавлен в другой контейнер. Разработчик должен уничтожать объект контейнера верхнего уровня самостоятельно с помощью одной из традиционных техник языка программирования C++. Например, ваше окно верхнего уровня может быть представлено экземпляром соответствующего класса, объявленным в рамках функции main().

25.2. Разделяемые ресурсы

Некоторые объекты, такие, как объекты буферов пикселей на основе класса Gdk::Pixbuf или объекты шрифтов на основе класса Pango::Font извлекаются из разделяемого хранилища. Таким образом, вы не сможете создать свои собственные экземпляры подобных классов. Эти классы обычно наследуются от класса Glib::Object. Вместо требования от вас создания и удаления ссылок на эти объекты, gtkmm использует умный указатель Glib::RefPtr<>. Cairomm предоставляет свой собственный умный указатель Cairo::RefPtr<>.

Такие объекты, как объекты на основе класса Gdk::Pixbuf, могут создаваться исключительно с помощью функции create(). Например, это может быть сделано следующим образом:
Glib::RefPtr<Gdk::Pixbuf> pixbuf = Gdk::Pixbuf::create_from_file(filename);
У вас нет возможности получения самого объекта на основе класса Gdk::Pixbuf. В данном примере pixbuf является умным указателем, поэтому вы можете работать с ним практически так же, как и с обычным указателем:
int width = 0;
if(pixbuf)
{
  width = pixbuf->get_width();
}

В момент, когда указатель pixbuf покинет область действия функции, будет осуществлен вызов функции unref() незаметно для пользователя, что позволит вам больше не беспокоиться о нем. Оператор new не использовался, следовательно оператор delete также не должен использоваться.

В том случае, если вы копируете умный указатель Glib::RefPtr, к примеру, следующим образом
Glib::RefPtr<Gdk::Pixbuf> pixbuf2 = pixbuf;

или же в том случае, если вы передаете его в качестве аргумента метода или возвращаемого значения, в рамках объекта на основе класса Glib::RefPtr будут осуществлены все необходимые подсчеты ссылок для получения гарантии того, что экземпляр класса не будет уничтожен до того момента, пока последний умный указатель не покинет область действия функции.

Обратитесь к приложению для получения подробной информации об умном указателе на основе класса Glib::RefPtr.

Если же вы хотите узнать больше об умных указателях, вы можете обратиться к следующим книгам:
  • Bjarne Stroustrup, "Язык программирования C++", четвертое издание - раздел 34.3
  • Nicolai M. Josuttis, "Стандартная библиотека языка программирования C++" - раздел 4.2

Следующий раздел : Glade и Gtk::Builder.