4.2 Компоновка
4.2 Компоновка
Если не указано иное, то имя, не являющееся локальным для функции или класса, в каждой части программы, компилирумой отдельно, должно относиться к одному и тому же типу, знчению, функции или объекту. То есть, в программе может быть только один нелокальный тип, значение, функция или объект с этим именем. Рассмотрим, например, два файла:
// file1.c: int a = 1; int f() (* /* что-то делает */ *)
// file2.c: extern int a; int f(); void g() (* a = f(); *)
a и f(), используемые g() в файле file2.c,– те же, что определены в файле file1.c. Ключевое слово extern (внешнее) указывает, что описание a в file2.c является (только) описнием, а не определением. Если бы a инициализировалось, extern было бы просто проигнорировано, поскольку описание с иницилизацией всегда является определением. Объект в программе должен определяться только один раз. Описываться он может много раз, но типы должны точно согласовываться. Например:
// file1.c: int a = 1; int b = 1; extern int c;
// file2.c: int a; extern double b; extern int c;
Здесь три ошибки: a определено дважды (int a; является определением, которое означает int a=0;), b описано дважды с разными типами, а c описано дважды, но не определено. Эти вды ошибок не могут быть обнаружены компилятором, который за один раз видит только один файл. Компоновщик, однако, их онаруживает.
Следующая программа не является С++ программой (хотя C программой является):
// file1.c: int a; int f() (* return a; *)
// file2.c: int a; int g() (* return f(); *)
Во-первых, file2.c не С++, потому что f() не была описана, и поэтому компилятор будет недоволен. Во-вторых, (когда file2.c фиксирован) программа не будет скомпонована, посколку a определено дважды.
Имя можно сделать локальным в файле, описав его static. Например:
// file1.c: static int a = 6; static int f() (* /* ... */ *)
// file2.c: static int a = 7; static int f() (* /* ... */ *)
Поскольку каждое a и f описано как static, получающаяся в результате программа является правильной. В каждом файле своя a и своя f().
Когда переменные и функции явно описаны как static, часть программы легче понять (вам не надо никуда больше залядывать). Использование static для функций может, помимо этого, выгодно влиять на расходы по вызову функции, поскольку дает оптимизирующему компилятору более простую работу.
Рассмотрим два файла:
// file1.c: const int a = 6; inline int f() (* /* ... */ *) struct s (* int a,b; *)
// file1.c: const int a = 7; inline int f() (* /* ... */ *) struct s (* int a,b; *)
Раз правило «ровно одно определение» применяется к контантам, inline-функциям и определениям функций так же, как оно применяется к функциям и переменным, то file1.c и file2.c не могут быть частями одной С++ программы. Но если это так, то как же два файла могут использовать одни и те же типы и константы? Коротко, ответ таков: типы, константы и т.п. могут определяться столько раз, сколько нужно, при условии, что они определяются одинаково. Полный ответ несколько более сложен (это объясняется в следующем разделе).