|
Программирование >> Инициализация объектов класса, структура
const unsigned char INLINE = 128; const unsigned char VIRTUAL = 129; curTok = INLINE; ... Вызов addToken() выглядит так: addToken( curTok ); Тип char реализован как знаковый в одном случае и как беззнаковый в другом. Неверное объявление addToken() приводит к переполнению на той машине, где тип char является знаковым, всякий раз, когда используется лексема со значением больше 127. Если бы такой программный код компилировался и связывался без ошибки, во время выполнения могли обнаружиться серьезные последствия. В С++ информация о количестве и типах параметров функций помещается в имя функции - это называется безопасным связыванием (type-safe linkage). Оно помогает обнаружить расхождения в объявлениях функций в разных файлах. Поскольку типы параметров unsigned char и char различны, в соответствии с принципом безопасного связывания функция addToken() , объявленная в файле lex.C, будет считаться неизвестной. Согласно стандарту определение в файле token.C задает другую функцию. Подобный механизм обеспечивает некоторую степень проверки типов при вызове функций из разных файлов. Безопасное связывание также необходимо для поддержки перегруженных функций. (Mi продолжим рассмотрение этой проблема: в главе 9.) Прочие типы несоответствия объявлений одного и того же объекта или функции в разных файлах не обнаруживаются во время компиляции или связывания. Поскольку компилятор обрабатывает отдельно каждый файл, он не способен сравнить типы в разных файлах. Несоответствия могут быть источником серьезных ошибок, проявляющихся, подобно приведенным ниже, только во время выполнения программы (к в token. C unsigned char lastTok = 0; unsigned char peekTok() { /* ... */ } в lex.C extern char lastTok; примеру, путем возбуждения исключения или из-за вывода неправильной информации). extern char peekTok(); Избежать подобных неточностей поможет прежде всего правильное использование заголовочных файлов. Mi поговорим об этом в следующем подразделе. 8.2.3. Несколько слов о заголовочных файлах Заголовочный файл предоставляет место для всех extem-объявлений объектов, объявлений функций и определений встроенных функций. Это называется локализацией -----token.h ----- typedef unsigned char uchar; const uchar INLINE = 128; ... const uchar IT = ... ; const uchar GT = ...; extern uchar lastTok; extern int addToken( uchar ); inline bool is relational( uchar tok ) { return (tok >= LT && tok <= GT); } -----lex.C ----- #include token.h ... -----token.C ----- #include token.h Пример с addToken() имеет следующий заголовочный файл: ... При проектировании заголовочных файлов нужно учитывать несколько моментов. Все объявления такого файла должны быть логически связанными. Если он слишком велик или содержит слишком много не связанных друг с другом элементов, программисты не станут включать его, экономя на времени компиляции. Для уменьшения временных затрат в некоторых реализациях С++ предусматривается использование предкомпилированных заголовочных файлов. В руководстве к компилятору сказано, как создать такой файл из обычного. Если в вашей программе используются большие заголовочные файлы, применение предкомпиляции может значительно сократить время обработки. Чтобы это стало возможным, заголовочный файл не должен содержать объявлений встроенных (inline) функций и объектов. Любая из следующих инструкций является extern int ival = 10; double fica rate; определением и, следовательно, не может быть использована в заголовочном файле: extern void dummy () {} Хотя переменная i объявлена с ключевым словом extern, явная инициализация превращает ее объявление в определение. Точно так же и функция dummy() , несмотря на явное объявление как extern, определяется здесь же: пустые фигурные скобки содержат ее тело. Переменная fica rate определяется и без явной инициализации: об этом объявлений. Те исходные файлы, где объект или функция определяется или используется, должны включать заголовочный файл. Такие файлы позволяют добиться двух целей. Во-первых, гарантируется, что все исходные файлы содержат одно и то же объявление для глобального объекта или функции. Во-вторых, при необходимости изменить объявление это изменение делается в одном месте, что исключает возможность забыть внести правку в какой-то из исходных файлов. ----- заголовочный файл const int buf chunk = 1024; extern char *const bufp; ----- исходный файл -----объявления константы как extern. Например: char *const bufp = new char[buf chunk]; Хотя bufp объявлена как const, ее значение не может быть вычислено во время компиляции (она инициализируется с помощью оператора new, который требует вызова библиотечной функции). Такая конструкция в заголовочном файле означала бы, что константа определяется каждый раз, когда этот заголовочный файл включается. Символическая константа - это любой объект, объявленный со спецификатором const. Можете ли вы сказать, почему следующее объявление, помещенное в заголовочный файл, ошибка: не должно быть в заголовочном файле вызывает ошибку связывания, если такой файл включается в два различных исходных? const char* msg = ?? oops: error: ; Проблема вызвана тем, что msg не константа. Это неконстантный указатель, адресующий константу. Правильное объявление выглядит так (полное описание объявлений указателей см. в главе 3): const char *const msg = ?? oops: error: ; Такое определение может появиться в разных файлах. Схожая ситуация наблюдается и со встроенными функциями. Для того чтобы компилятор мог подставить тело функции по месту , он должен видеть ее определение. (Встроенные функции были нредставлены в разделе 7.6.) говорит отсутствие ключевого слова extern. Включение такого заголовочного файла в два или более исходных файла одной программа: вызовет ошибку связывания -повторные определения объектов. В файле token.h, приведенном выше, константа INLINE и встроенная функция is relational() кажутся нарушающими правило. Однако это не так. Определения символических констант и встроенных функций являются специальными видами определений: те и другие могут появиться в программе несколько раз. При возможности компилятор заменяет имя символической константы ее значением. Этот процесс называют подстановкой константы. Например, компилятор подставит 128 вместо INLINE везде, где это имя встретится в исходном файле. Для того чтобы компилятор произвел такую замену, определение константы (значение, которым она инициализирована) должно быть видимо в том месте, где она используется. Определение символической константы может появиться несколько раз в разных файлах, потому что в результирующем исполняемом файле благодаря подстановке оно будет только одно. В некоторых случаях, однако, такая подстановка невозможна. Тогда лучше вынести инициализацию константы в отдельный исходный файл. Это делается с помощью явного
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |