
- •5.Препроцессор. Директивы препроцессора.
- •7.Работа с файлами. Текстовый и двоичный режим.
- •8.Указатели. Адресная арифметика.
- •10.Перечислимый тип. Структуры. Объединения.
- •11.Поразрядные операции.
- •13.Спецификаторы класса памяти.
- •14.Пространства имён.
- •15.Компоновка. Правило одного определения.
- •16.Понятие класса.
- •17.Функции-члены класса. Указатель this.
- •18.Конструкторы. Деструкторы.
- •19.Преобразования объектов класса.
- •20.Доступ к членам класса.
- •21.Статические члены класса.
- •22.Друзья класса.
- •23.Совместное использование.
- •24.Перегрузка операций.
- •25.Шаблоны.
- •26.Обработка исключительных ситуаций.
- •27.Производные классы.
- •28.Виртуальные функции. Абстрактные классы.
- •29.Указатели на члены класса.
- •30.Множественное наследование.
- •31.Структура dll-библиотеки.
- •32.Статическое и динамическое подключение dll-библиотек.
- •34.Регистры процессора.
- •35.Использование стека. Команды работы со стеком в языке ассемблера.
- •36.Арифметические команды в языке ассемблера.
- •37.Команды сравнения и перехода в языке ассемблера.
- •38.Команды работы с битами в языке ассемблера.
- •39.Процедуры в языке ассемблера. Передача параметров в процедуру.
- •40.Процедуры в языке ассемблера. Возврат результата. Локальные данные.
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 определяется. Если файл будет включён в единицу компиляции второй раз, его содержимое игнорируется.Все стандартные заголовочные файлы содержат стражей включения.