Глобальный объект, когда ты создаешься?


Рассмотрим ситуацию:

 

// Файл file1.cpp

class CA a();

class CB b();

 

Допустим, что в приведенной программе единичный экземпляр класса CB объект b зависит от данных объекта a (именно объекта a, а не класса CA). Поскольку имеется такая зависимость, можно утверждать, что для корректности данных объекта b важен порядок конструирования объектов (только если эта зависимость проявляется на этапе конструирования объекта b).

 

Компилятор, скорее всего, будет создавать объекты в порядке их определения: cначала a, затем b. В этом случае проблемы последовательности нет. Однако стоит поменять местами строки class CA a(); и class CB b(); - и данные объекта b будут некорректными. Такая закономерность будет наблюдаться в каждом файле проекта. Но, если проанализировать последовательность создания объектов не в отдельно взятом файле, а сразу в нескольких, то нам не удастся однозначно определить порядок создания всех объектов из нескольких файлов: спецификацией языка не определен порядок конструирования (вызов конструктора) и удаления (вызов деструктора) глобальных объектов.

 

Спецификацией языка не определен порядок конструирования (вызов конструктора) и удаления (вызов деструктора) глобальных объектов.

 

Отсюда вытекает следующее правило: если мы не хотим получить таких нежданных «паразитов», не следует создавать подобных связей между глобальными (по объявлению) объектами.

 

Вот наглядный пример из практики: класс СA -- контейнерный класс для массива некоторых объектов класса CD, расширяющий возможности простого массива для использования в конкретной предметной области. Экземпляр класса CA объект a в программе выполняет роль основной библиотеки (хранилища) объектов класса CD. Он отвечает за их сохранение при выходе из приложения и загрузку при запуске.

 

Выполнять загрузку и сохранение удобнее и проще при конструировании и удалении объекта a. Следовательно, если какой-либо объект (b) использует библиотечные данные объекта a, то он прямо зависит от того, что раньше создано - библиотека или ее клиент.

 

За этим вопросом кроется целый «букет» проблем.

 

Рассмотрим такую ситуацию: та же библиотека, те же клиенты, что и в предыдущем примере. Что будет, если библиотека удалится раньше, чем клиенты? После удаления библиотеки ее клиенты будут «вылетать» при попытке обращения (для чтения, записи, удаления*) к ее данным по полученным из нее ранее указателям, которые теперь стали некорректными.

 

* Иногда, в целях оптимизации скорости или объема ресурсов допускается доступ некоторых объектов (клиентов) к управлению элементами иных объектов (библиотек, серверов).

 

Все ли беды происходят от связей? Нет. У глобальных объектов (по объявлению, а не по выполняемой роли) вообще множество недостатков:

  • они сильно увеличивают размер выполняемого файла;
  • они «замуливают» некоторые возможные недоработки класса. Дело в том, что спецификация предписывает компилятору обнулять глобальные переменные. Это значит, что перед конструированием все члены объекта будут обнулены. Вы можете забыть принудительно обнулить некоторые члены (указатели) и в дальнейшем, при создании неглобального экземпляра данного класса, получить скандал в виде чтения/записи по запрещенному адресу, или, что хуже*, по адресу другого объекта.

* Поскольку труднее «отлавливается».

 

Методы решения подобных проблем


Следует ли в спецификации определить порядок конструирования? Но ведь это значит -- направить программиста по простой дорожке, которая ведет к окостенению программы, потере динамичности! Это одно из тех ограничений, которые не поощряют написание на C++ программ в не свойственном C++ стиле.

 

Наличие в программе менеджера инициализации/удаления позволит сделать объявление объектов не глобальным (а точнее, вообще избежать объявления) с сохранением их глобального смысла. То есть, объявление глобальных указателей, которые будут ссылаться на объекты, позволит работать с ними так же, как с глобальными объектами, и в то же время контролировать последовательность конструирования и удаления объектов менеджером.

 

Итак, что может быть проще: скрыть глобальное объявление, объявив объекты через указатели. Попробуем?

 

class CA * a = new CA();

class CB * b = new CB();

//Где-то при выходе:

delete a;

delete b;

 

Получаем глобальный указатель на объект и полуглобальный (?) объект. И... опять наступаем на те же грабли. Определенности нет, как и раньше. Снова нельзя сказать, какой объект будет инициализирован (сконструирован) первым, несмотря на то, что такая форма может считаться последовательной программой. Действительно, такая программа должна выполняться последовательно. Так и будет - в теле одного файла. Но никак не двух и более. К тому же мы создаем и удаляем объекты по разным логическим схемам (создаем в глобальном блоке еще до получения управления главной функцией, а удаляем в произвольном месте сами).

 

Вот как должна выглядеть правильно работающая программа:

 

// Файл file1.cpp

class CA * a; // по спецификации будет обнулен

class CB * b; // по спецификации будет обнулен


// В менеджере при инициализации

a = new CA();

b = new CB();


// В менеджере при удалении

delete a;

delete b;

 

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

 

И, вообще, мне, нет, да, нет, тьфу! Мне совсем не нравится использовать глобальные объекты и указатели!

(c) Капитан Смолетт

 

Стоит ли писать «= 0» (инициализация присваиванием) для глобальных объектов?


Спецификация предусматривает автоматическое «обнуление» глобальных объектов, обнуление (в прямом смысле) переменных простых типов и вызов конструктора для экземпляров неких классов. Поэтому кажется излишним писать еще и «= 0» или «= 0.0»…

 

Однако делая так, вы подчеркиваете, что для этой переменной важно ее исходное значение, и что она используется до возможного принятия нового значения далее в тексте программы.


А.П. Алексеев, ведущий программист ООО “Булат”

alexeyalexeev@hotmail.com

www.boolatgames.com

www.lethaldreams.com

2004.04.09
19.03.2009
В IV квартале 2008 г. украинский рынок серверов по сравнению с аналогичным периодом прошлого года сократился в денежном выражении на 34% – до $30 млн (в ценах для конечных пользователей), а за весь календарный год – более чем на 5%, до 132 млн долл.


12.03.2009
4 марта в Киеве компания Telco провела конференцию "Инновационные телекоммуникации", посвященную новым эффективным телекоммуникационным технологиям для решения задач современного бизнеса.


05.03.2009
25 февраля в Киеве компания IBM, при информационной поддержке "1С" и Canonical, провела конференцию "Как сохранить деньги в условиях кризиса?"


26.02.2009
18-19 февраля в Киеве прошел юбилейный съезд ИТ-директоров Украины. Участниками данного мероприятия стали ИТ-директора, ИТ-менеджеры, поставщики ИТ-решений из Киева, Николаева, Днепропетровска, Чернигова и других городов Украины...


19.02.2009
10 февраля в Киеве состоялась пресс-конференция, посвященная итогам деятельности компании "DiaWest – Комп’ютерний світ" в 2008 году.


12.02.2009
С 5 февраля 2009 г. в Киеве начали работу учебные курсы по использованию услуг "электронного предприятия/ учреждения" на базе сети информационно-маркетинговых центров (ИМЦ).


04.02.2009
29 января 2009 года в редакции еженедельника "Computer World/Украина" состоялось награждение победителей акции "Оформи подписку – получи приз!".


29.01.2009
22 января в Киеве компания "МУК" и представительство компании Cisco в Украине провели семинар для партнеров "Обзор продуктов и решений Cisco Small Business"

 

 
 
Copyright © 1997-2008 ИД "Комиздат".