Все неэлементарные программы включают в себя
несколько раздельно компилируемых единиц (их называют просто файлами). Покажем, как раздельно
откомпилированные функции могут обращаться друг к другу.
Присутствие всей программы в одном файле обычно невозможно, так как коды стандартных библиотек
и операционной системы располагаются где-то в другом месте. При этом хранить весь текст программы
в одном файле обычно непрактично и неудобно. Так как
единицей компиляции служит файл, то во всех случаях,
когда в файле производятся изменения, весь файл
необходимо компилировать заново. Даже для небольшой программы время, затрачиваемое на перекомпиляцию, можно заметно сократить с помощью разбиения
программы на файлы подходящих размеров.
Покажем пример с калькулятором. Он был представлен одним исходным файлом. Если он набит, то наверняка были трудности с размещением описаний в правильном порядке и необходимо было бы применить по
меньшей мере одно «фальшивое» описание, чтобы компилятор обрабатывал взаимно рекурсивные функции
expr(), term() и prim(). Программа заключает в себе четыре части (лексический анализатор, программа синтаксического разбора, таблица имен и драйвер), но это никак не было отражено внутри программы. В общем,
калькулятор был написан по-другому. Так это не делается; даже если в этой программе «на выброс» пренебречь всеми соображениями методологии программи-
рования, эксплуатации и эффективности компиляции, следует разбить эту программу в 200 строк на
несколько файлов, чтобы рограммировать было
приятнее.
Программа, которая состоит из нескольких раздельно компилируемых файлов, должна быть согласованной в смысле применения имен и типов, так же, как
и программа, которая состоит из одного исходного
файла. Вообще это может обеспечить и компоновщик.
Компоновщик представляет собой программу, которая
стыкует отдельно скомпилированные части вместе.
Компоновщик часто именуют загрузчиком. В UNIX’е
компоновщик именуется ld. Но компоновщики, которые имеются в большинстве систем, обеспечивают
очень слабую поддержку проверки согласованности.
Программист способен скомпенсировать недостаток поддержки со стороны компоновщика, предоставив
дополнительную информацию о типах (описания). После этого согласованность программы осуществляется
проверкой согласованности описаний, которые располагаются в отдельно компилируемых частях. Средства,
которые это осуществляют, обеспечивают, в C++ разра-
ботаны так, чтобы способствовать такой явной компоновке.
Если оговорено иное, то имя, которое не является
локальным для функции или класса, в любой части программы, компилируемой отдельно, должно относиться
к определенному типу, значению, функции или объекту.
То есть в программе может существовать только один
нелокальный тип, значение, функция или объект с данным именем.