Панель управления
Оплаченные ссылки
Главная » C++ энциклопедия » И » Инициализация глобальных объектов
Категория: C++ энциклопедия » И
- 0
Автор: admin
В спецификации языка порядок конструирования глобальных объектов выглядит довольно сложно. Если же учесть причуды коммерческих компиляторов C++, этот порядок становится и вовсе непредсказуемым. В соответствии со спецификацией должны вызываться конструкторы глобальных объектов, включая конструкторы статических переменных классов и структур, однако многие компиляторы этого не делают. Если вам повезло и ваш компилятор считает, что конструкторы важны для глобальных переменных, порядок конструирования глобальных объектов зависит от воображения разработчика компилятора. Ниже перечислены некоторые правила, которые теоретически должны соблюдаться:
Вот и все. Внешне простая последовательность глобальных объявлений на самом деле полностью подчиняется всем капризам разработчика компилятора. Она может привести к нужному результату или сгореть синим пламенем.
Если бы все это находилось в одном исходном файле, ситуация была бы нормальной. Со строкой Fоо* f = &foo; проблем не возникает, поскольку глобальные объекты одного исходного файла заведомо (хе-хе) инициализируются в порядке их определения. Другими словами, когда программа доберется до этой строки, объект foo уже будет сконструирован. Тем не менее, никто не гарантирует, что глобальные объекты в файле file1.cpp будут инициализированы раньше глобальных объектов в файле file2.срр. Если file2.cpp будет обрабатываться первым, f оказывается равным 0 (NULL на большинстве компьютеров), и при попытке получить по нему объект ваша программа героически умрет.
Лучший выход — сделать так, чтобы программа не рассчитывала на конкретный порядок инициализации файлов .срр. Для этого используется стандартный прием — в заголовочном файле .h определяется глобальный объект со статической переменной, содержащей количество
инициализированных файлов .срр. При переходе от 0 к 1 вызывается функция, которая инициализирует все глобальные объекты библиотечного файла .срр. При переходе от 1 к 0 все объекты этого файла уничтожаются.
К этому нужно привыкнуть. А происходит следующее: файл .h компилируется со множеством других файлов .срр, один из которых - Library.cpp. Порядок инициализации глобальных объектов, встречающихся в этих файлах, предсказать невозможно. Тем не менее, каждый из них будет иметь
свою статическую копию LibraryDummy. При каждой инициализации файла .срр, в который включен файл Library.h, конструктор LibraryDummy увеличивает счетчик. При выходе из main() или при вызове exit() файлы .срр уничтожают глобальные объекты и уменьшают счетчик в деструкторе LibraryDummy. Конструктор и деструктор гарантируют, что OpenLibrary() и CloseLibrary() будут вызваны ровно один раз.
Этот прием приписывается многим разным программистам, но самый известный пример его использования встречается в библиотеке iostream. Там он инициализирует большие структуры данных, с которыми работает библиотека, ровно один раз и лишь тогда, когда это требуется.
- Перед выполнением каких-либо операций все глобальные переменные инициализируются значением 0.
- Объекты, находящиеся в глобальных структурах или массивах, конструируются в порядке их появления в структуре или массиве.
- Каждый глобальный объект конструируется до его первого использования в программе. Компилятор сам решает, следует ли выполнить инициализацию до вызова функции main() или отложить ее до первого использования объекта.
- Глобальные объекты, находящиеся в одном «модуле трансляции» (обычно файле с расширением .срр), инициализируются в порядке их появления в этом модуле. В сочетании с правилом 3 это означает, что инициализация может выполняться по модулям, при первом использовании каждого модуля.
Вот и все. Внешне простая последовательность глобальных объявлений на самом деле полностью подчиняется всем капризам разработчика компилятора. Она может привести к нужному результату или сгореть синим пламенем.
// В файле file1.cpp
Foo foo;
Foo* f = &foo;
// В файле file2.cpp
extern Foo* f;
Foo f1(*f); // Используется конструктор копий
Если бы все это находилось в одном исходном файле, ситуация была бы нормальной. Со строкой Fоо* f = &foo; проблем не возникает, поскольку глобальные объекты одного исходного файла заведомо (хе-хе) инициализируются в порядке их определения. Другими словами, когда программа доберется до этой строки, объект foo уже будет сконструирован. Тем не менее, никто не гарантирует, что глобальные объекты в файле file1.cpp будут инициализированы раньше глобальных объектов в файле file2.срр. Если file2.cpp будет обрабатываться первым, f оказывается равным 0 (NULL на большинстве компьютеров), и при попытке получить по нему объект ваша программа героически умрет.
Лучший выход — сделать так, чтобы программа не рассчитывала на конкретный порядок инициализации файлов .срр. Для этого используется стандартный прием — в заголовочном файле .h определяется глобальный объект со статической переменной, содержащей количество
инициализированных файлов .срр. При переходе от 0 к 1 вызывается функция, которая инициализирует все глобальные объекты библиотечного файла .срр. При переходе от 1 к 0 все объекты этого файла уничтожаются.
// В файле Library.h
class Library {
private:
static i nt c ount;
static void OpenLibrary();
static void CloseLibrary();
public:
Library();
~Library();
};
static Library LibraryDummy;
inline Library::Library()
{
if (count++ == 0)
OpenLibrary();
}
inline Library::~Library()
{
if (--count == 0)
CloseLibrary();
}
// В Library.cpp
int Library::count = 0; // Д елается перед выполнением вычислений
int aGlobal;
40
Foo* aGlobalFoo;
void Library::OpenLibrary()
{
aGlobal = 17;
aGlobalFoo = new Foo;
}
void Library::CloseLibrary()
{
aGlobal = 0;
delete a GlobalFoo;
aGlobalFoo = N ULL;
}
К этому нужно привыкнуть. А происходит следующее: файл .h компилируется со множеством других файлов .срр, один из которых - Library.cpp. Порядок инициализации глобальных объектов, встречающихся в этих файлах, предсказать невозможно. Тем не менее, каждый из них будет иметь
свою статическую копию LibraryDummy. При каждой инициализации файла .срр, в который включен файл Library.h, конструктор LibraryDummy увеличивает счетчик. При выходе из main() или при вызове exit() файлы .срр уничтожают глобальные объекты и уменьшают счетчик в деструкторе LibraryDummy. Конструктор и деструктор гарантируют, что OpenLibrary() и CloseLibrary() будут вызваны ровно один раз.
Этот прием приписывается многим разным программистам, но самый известный пример его использования встречается в библиотеке iostream. Там он инициализирует большие структуры данных, с которыми работает библиотека, ровно один раз и лишь тогда, когда это требуется.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
