Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЯМП ответы.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
710.66 Кб
Скачать

15.Компоновка. Правило одного определения.

Многофайловые программы Файл является традиционной единицей хранения информации в файловой системе и не менее традиционной единицей компиляции. Как правило, невозможно хранить программу в одном файле.Разбиение программы на файлы помогает подчеркнуть её логическую структуру, облегчает её понимание и позволяет компилятору обеспечить эту логическую структуру.

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

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

// file1.cpp

int x = 1;

int b = 0;

extern int c;

// file2.cpp

int x;

extern double b;

extern int c;

В этом примере содержатся три ошибки:

  • переменная x определена дважды;

  • переменная b объявлена дважды с различными типами;

  • переменная c дважды объявлена, но не определена.

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

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

Практическое правило гласит, что заголовочный файл может содержать:

  • именованные пространства имён;

  • определения типов;

  • объявления и определения шаблонов;

  • объявления функций;

  • определения встроенных функций;

  • объявления данных;

  • определения констант;

  • перечисления;

  • объявления имён;

  • директивы включения;

  • макроопределения;

  • директивы условной компиляции;

  • комментарии.

С другой стороны, заголовочный файл никогда не должен содержать:

  • определения обычных функций;

  • определения данных;

  • неименованные пространства имён.

Правило одного определения Каждый конкретный класс, перечисление, шаблон и т.д. должны быть определены в программе ровно один раз.

Правило стандарта, говорящее о том, что должно существовать уникальное определение класса, шаблона и т.д. должно быть изложено в более сложной форме. Это правило называют «правилом одного определения». А именно, два определения класса, шаблона или встроенной функции приемлемы в качестве определения одной и той же сущности тогда и только тогда, когда:

  • они находятся в различных единицах компиляции;

  • они идентичны лексема за лексемой;

  • значения лексем одинаковы в обеих единицах компиляции.

примеры нарушения правила ODR

// file1.cpp

struct S1 { int a; char b; }; // Ошибка – нельзя определить структуру дважды в одной единице компиляции

struct S1 { int a; char b; };

Наилучшей защитой от подобных проблем является создание как можно более самодостаточных заголовочных файлов.

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

// file1.h

#ifndef FILE1_H

#define FILE1_H

...

#endif

Содержимое файла между #ifndef и #endif игнорируется компилятором, если FILE1_H определено. В этом случае при первом просмотре файла во время компиляции его содержимое читается, и FILE1_H определяется. Если файл будет включён в единицу компиляции второй раз, его содержимое игнорируется.Все стандартные заголовочные файлы содержат стражей включения.