Порядок инициализации - PullRequest
       4

Порядок инициализации

2 голосов
/ 03 января 2012

Мой вопрос касается порядка инициализации глобальных объектов. В некоторой степени это обсуждается здесь: C ++: когда (и как) вызываются глобальные статические конструкторы C ++?

Что я хотел бы знать, так это как гарантируется, что некоторые объекты системной библиотеки (хотя и не можем придумать пример) инициализируются перед использованием в приложении? (Я знаю, что решение состоит в том, чтобы обернуть такой объект в функцию, но я хочу понять, как он обрабатывается сегодня в компиляторах)

Это также обсуждается в книге Джона Левина "Линкеры и загрузчики", когда речь идет о разделах .init и .finit, но текущее состояние неясно.

Ответы [ 3 ]

4 голосов
/ 03 января 2012

Примером будет cout.

Компилятор на самом деле не гарантирует, что эти объекты построены до их использования.Возьмите этот код:

struct Foo
{

    Foo();
}foo;

#include <iostream>

Foo::Foo()
{
    std::cout << "Hello World";
}

int main() {}

Это segfaults, потому что объект foo создается перед cout.Когда он пытается использовать cout, случаются плохие вещи.

По сути, все глобальные объекты будут вставлены в код для их построения, который выполняется перед main.Как только вы в основном, вы в безопасности, все объекты построены.До этого это зависит от порядка, в котором они определены.Вот почему я могу разбить его, включив iostream после объявления foo global.

2 голосов
/ 03 января 2012

Атрибуты компилятора могут использоваться:

В стандарте C ++ объекты, определенные в области имен, гарантированно инициализируются в порядке в строгом соответствии с определением их в данной единице перевода.Не дается никаких гарантий для инициализации через единицы перевода.Однако GNU C ++ позволяет пользователям контролировать порядок инициализации объектов, определенных в области имен, с помощью атрибута init_priority, указав относительный приоритет, постоянное интегральное выражение, в настоящее время ограниченное от 101 до 65535 включительно.Меньшие числа указывают на более высокий приоритет.

Some_Class  A  __attribute__ ((init_priority (2000)));
Some_Class  B  __attribute__ ((init_priority (543)));

Я предполагаю, что под 101 зарезервировано для реализации, для таких вещей, как cout.

http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes

0 голосов
/ 03 января 2012

Существует несколько подходов, чтобы убедиться, что глобальные переменные инициализируются перед их использованием.Тем не менее, они, как правило, зависят от платформы, а детали отличаются в зависимости от системы.Типичным примером глобальных объектов, которые должны быть инициализированы перед выполнением любого пользовательского кода, включая инициализацию глобальной переменной, являются объекты глобальных потоков, в частности std :: cout.Есть три подхода, с которыми я играл, чтобы убедиться, что std :: cout инициализируется рано:

  1. Стандартная библиотека уже определяет std::ios_base::Init, который подсчитывает, как часто она создавалась.Имея static std::ios_base::Init _Init; в заголовке <iostream>, объекты глобального потока могут быть инициализированы с использованием размещения new, когда счетчик изменяется с 0 на 1, и явно уничтожаются, когда счетчик изменяется с 1 на 0 во время уничтожения.Чтобы избежать двойной инициализации, определение std::cout просто зарезервирует необходимое пространство, например, char std::cout[sizeof(_Standard_output_stream)];.Этот подход может сломаться, если пользовательский код определяет объекты до включения <iostream>.
  2. . При загрузке общих библиотек выполняется некоторый код инициализации до того, как объекты в общей библиотеке становятся доступными (аналогично некоторый код уничтожения при выгрузке общей библиотеки).).Помещая соответствующие объекты в общую библиотеку, код инициализации может быть запущен до того, как объект будет доступен.Основным недостатком является то, что это требует использования разделяемых библиотек, что не всегда желательно.
  3. Компоновщик эффективно формирует список инициализаций.В некоторых системах гарантируется, что порядок, в котором создаются различные объектные файлы, является обратным порядком видимых объектных файлов.То есть размещение, например, std::cout в последнем объектном файле в последней библиотеке [C ++] приводит к инициализации этого объекта перед другими объектами.Так как пользователи имеют контроль над линией ссылок, это не обязательно работает.

Самый безопасный подход в этом списке - поместить объекты в общую библиотеку.Наилучший подход, вероятно, состоит в том, чтобы избежать проблемы путем доступа к глобальному объекту через функцию.Все подходы требуют дополнительной работы, если потоки могут быть запущены до начала построения.Кроме того, я уверен, что есть подходы, специфичные для платформ, которые занимаются этим.Общее наблюдение состоит в том, что глобальные объекты являются злом: старайтесь не использовать их!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...