2013-02-16 1 views
4

Here вы найдете следующие инструкции в разделе Какой заголовок?:Как схема ниже гарантирует, что будет только одно определение объектов cin, cout, ...?

Наконец, <iostream> обеспечивает восемь стандартных глобальных объектов (CIN, COUT и т.д.). Чтобы сделать это правильно, этот заголовок также содержит содержимое <istream> и <ostream>, но ничего больше. В содержания этого заголовка выглядеть

#include <ostream> 
#include <istream> 

namespace std 
{ 
    extern istream cin; 
    extern ostream cout; 
    .... 

    // this is explained below 
    static ios_base::Init __foo; // not its real name 
} 

Теперь, во время выполнения пенальти упоминалось ранее: глобальные объекты должны быть инициализированы перед любыми из вашего собственного кода используют их; это , гарантированный стандартом. Как и любой другой глобальный объект, они должны быть инициализированы один раз и только один раз. Обычно это делается с конструкцией , подобной приведенной выше, и вложенным классом ios_base :: Init является , указанным в стандарте именно по этой причине.

Как это работает? Поскольку заголовок включен до любого из вашего кода , объект __foo создается до любого из ваших объектов. (Глобальные объекты построены в том порядке, в котором они объявлены, и уничтожены в обратном порядке.) При первом запуске конструктора устанавливаются восемь объектов потока .

Мой вопрос: когда я заголовочный файл <iostream> в нескольких .cpp файлов, как это схема выше гарантий, что будет только один определение объектов cin, cout и т.д ...?

+0

«Одно определение» неинтересно - это только часть (скомпилированной) стандартной библиотеки. Что интересно, это «одна инициализация». (Я думаю, что эта техника называется [«Schwartz counter»] (http://stackoverflow.com/q/9251763/596781).) –

+2

AFAIK, 'cout',' cin' и др. объекты просто объявляются как 'extern' в заголовке, фактические определения находятся внутри стандартных файлов lib. Теперь 'ios_base :: Init' объявляется' static', поэтому каждая единица перевода имеет свою собственную копию. Конструкторы/деструкторы объекта «Init» выполняют некоторый подсчет ссылок, чтобы знать, когда инициализировать/уничтожать стандартные потоковые объекты. По крайней мере, как это делает [stdlibC++] (http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/include/std/iostream?view=markup). – jrok

+0

@jrok Все, что вы сказали, мне ясно. Я не понимаю, как «ios_base :: Init» избегает нескольких определений глобальных объектов cin, cout, ..., когда '' включен в несколько файлов '.cpp'. –

ответ

3

Это на самом деле не гарантирует это. Одна проблема определения решается путем простого определения объектов потока один раз в файле .cpp, который является частью библиотеки. Код в вопросе просто содержит объявление стандартных потоков.

Что является гарантией того, что объекты будут инициализированы перед использованием. Одна проблема с глобальными объектами в C++ заключается в том, что, хотя они являются, инициализируются в порядке внутри каждого .cpp-файла, нам неизвестен порядок, по которому компоновщик будет помещать объекты из отдельных файлов. Это может вызвать проблемы, если один объект пытается использовать другой объект до того, как он будет инициализирован, и мы не знаем точного порядка - см. Static initialization order fiasco.

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

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

Это только один из способов сделать это, используя только стандартный язык. Некоторые реализации имеют другие трюки, такие как #pragma или директиву init_priority, чтобы убедить компоновщик поставить код библиотеки перед кодом пользователя. В этом случае он просто работает магией, не используя класс Init.

+0

'Что является гарантией того, что объекты будут инициализированы перед использованием. Одна проблема с глобальными объектами в C++ заключается в том, что, хотя они инициализируются по порядку в каждом файле .cpp, мы не знаем порядка, по которому компоновщик будет помещать объекты из отдельных файлов. Это может вызвать проблемы, если один объект пытается использовать другой объект до того, как он будет инициализирован, и мы не знаем точного порядка. Насколько я понимаю, эта проблема уже решена на C++ 11, по крайней мере в соответствии с на этот ответ http://stackoverflow.com/a/8785008/1042389. –

+0

Единственное отличие состоит в том, что в C++ 11 говорится, что реализация должна вести себя как-если бы был создан объект Init. Так оно и работало на C++ 03, оно просто не было написано (потому что предполагалось, что оно очевидно :-). См. [Отчет об ошибках библиотеки # 369] (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#369) –

+0

'Одна проблема определения решается путем простого определения потока объекты один раз в .cpp-файле, который является частью библиотеки. »Есть ли у вас какие-либо ссылки для поддержки этого? Стандарт говорит об этом? –

 Смежные вопросы

  • Нет связанных вопросов^_^