/* ConstructStatic — демонстрация однократного */
/* создания статических объектов */
#include
#include
#include
using namespace std ;
class DoNothing
{
public :
DoNothing( int initial )
{
cout << "DoNothing сконструирован со значением "
<< initial
<< endl ;
}
} ;
_________________
209 стр. Глава 17. Аргументация конструирования
void fn( int i )
{
cout << "Функции fn передано значение " << i << endl ;
static DoNothing dn( i ) ;
}
int main( int argcs , char* pArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать кириллицы */
fn( 10 ) ;
fn( 20 ) ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
После запуска этой программы на экране появится следующее:

Функции fn передано значение 10
DoNothing сконструирован со значением 10
Функции fn передано значение 20
Press any key to continue...
Обратите внимание, что сообщение от функции fn( ) появилось дважды, а сообщение от конструктора DoNothing — только при первом вызове fn( ) .
Все глобальные объекты создаются до вызова main( )...210
Все глобальные объекты входят в область видимости программы. Таким образом, все они конструируются до того, как управление передаётся функции main( ) .

«При отладке такой порядок может привести к неприятностям. Некоторые отладчики пытаются выполнить весь код, который находится доmain( ) , и только потом передать управление пользователю. Это прекрасно подходит для С, поскольку до входа в функциюmain( ) там не может быть никакого кода, написанного пользователем. Однако в С++ это может стать причиной большой головной боли, поскольку тела конструкторов для всех глобальных объектов к моменту передачи управленияmain( ) уже выполнены. Если хоть один из этих конструкторов содержит серьёзный "жучок", программа погибнет до того, как начнёт выполняться!»
[ Атас! ]
Существует несколько подходов к решению этой проблемы. Первый заключается в том, чтобы проверять каждый конструктор на локальных объектах перед тем, как использовать его для глобальных. Если это не поможет решить проблему, можно попытаться добавить команды вывода сообщений в начало всех конструкторов, которые, по вашему предположению, могут иметь ошибки. Последнее сообщение, которое вы увидите, вероятно, будет сообщением конструктора с ошибкой.
Порядок создания глобальных объектов не определён...210
Локальные объекты создаются в порядке выполнения программы. Для глобальных же объектов порядок создания не определён. Как вы помните, глобальные объекты входят в область видимости программы одновременно. Возникает вопрос: почему бы тогда компилятору не начать с начала файла с исходной программой и не создавать глобальные объекты в порядке их объявления? ( Честно говоря, я подозреваю, что на самом деле большинство компиляторов так и поступают. ) Увы, такой подход отлично работал бы, но только в том случае, если бы программа всегда состояла из одного файла.
_________________
210 стр. Часть 3. Введение в классы
Однако большинство программ в реальном мире состоят из нескольких файлов, которые компилируются каждый в отдельности, а уже затем связываются в единое целое. Поскольку компилятор не управляет порядком связывания, он не может влиять на порядок вызова конструкторов глобальных объектов в разных файлах.
В принципе в большинстве случаев порядок создания глобальных объектов не так уж и важен. Тем не менее иногда это может привести к ошибкам, которые потом очень сложно отследить ( такое случается довольно часто, чтобы обратить на это внимание в книге ). Разберём приведённый ниже пример.
class Student
{
public :
Student ( unsigned id ) : studentId( id ) { }
const int StudentId ;
} ;
class Tutor
{
public :
Tutor ( Student & s ) : tutoredId( s.studentId ) { }
int tutoredId ;
} ;
/* Создаём студента */
Student randy( 1234 ) ;
/* Назначаем студенту учителя */
Tutor jenny( randy ) ;
В этом примере конструктор Student присваивает студенту идентификатор, а конструктор класса Tutor записывает этот идентификатор студента, которому нужен учитель. Программа объявляет студента randy , а затем назначает ему учителя jenny .
Читать дальше