Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Cpp_Страуструп.doc
Скачиваний:
16
Добавлен:
03.05.2015
Размер:
3.2 Mб
Скачать

4.2 Связывание

Если явно не определено иначе, то имя, не являющееся локальным для

некоторой функции или класса, должно обозначать один и тот же тип,

значение, функцию или объект во всех единицах трансляции данной

программы. Иными словами, в программе может быть только один

нелокальный тип, значение, функция или объект с данным именем.

Рассмотрим для примера два файла:

// file1.c

int a = 1;

int f() { /* какие-то операторы */ }

// file2.c

extern int a;

int f();

void g() { a = f(); }

В функции g() используются те самые a и f(), которые определены в

файле 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 описано дважды, но неопределено. Такие ошибки

(ошибки связывания) транслятор, который обрабатывает файлы

по отдельности, обнаружить не может, но большая их часть

обнаруживается редактором связей.

Следующая программа допустима в С, но не в С++:

// file1.c:

int a;

int f() { return a; }

// file2.c:

int a;

int g() { return f(); }

Во-первых, ошибкой является вызов f() в file2.c, поскольку в этом

файле f() не описана. Во-вторых, файлы программы не могут быть

правильно связаны, поскольку a определено дважды.

Если имя описано как static, оно становится локальном в этом

файле. Например:

// file1.c:

static int a = 6;

static int f() { /* ... */ }

// file2.c:

static int a = 7;

static int f() { /* ... */ }

Приведенная программа правильна, поскольку a и f определены как

статические. В каждом файле своя переменная a и функция f().

Если переменные и функции в данной части программы описаны как

static, то в этой части программы проще разобраться, поскольку не нужно

заглядывать в другие части. Описывать функции как статические

полезно еще и по той причине, что транслятору предоставляется

возможность создать более простой вариант операции вызова функции.

Если имя объекта или функции локально в данном файле, то говорят,

что объект подлежит внутреннему связыванию. Обратно, если имя

объекта или функции нелокально в данном файле, то он подлежит

внешнему связыванию.

Обычно говорят, что имена типов, т.е. классов и перечислений,

не подлежат связыванию. Имена глобальных классов и перечислений

должны быть уникальными во всей программе и иметь единственное

определение. Поэтому, если есть два даже идентичных определения

одного класса, это - все равно ошибка:

// file1.c:

struct S { int a; char b; };

extern void f(S*);

// file2.c:

struct S { int a; char b; };

void f(S* p) { /* ... */ }

Но будьте осторожны: опознать идентичность двух описаний класса

не в состоянии большинство систем программирования С++. Такое

дублирование может вызвать довольно тонкие ошибки (ведь классы

в разных файлах будут считаться различными).

Глобальные функции-подстановки подлежат внутреннему связыванию,

и то же по умолчанию справедливо для констант. Синонимы типов,

т.е. имена typedef, локальны в своем файле, поэтому описания

в двух данных ниже файлах не противоречат друг другу:

// file1.c:

typedef int T;

const int a = 7;

inline T f(int i) { return i+a; }

// file2.c:

typedef void T;

const int a = 8;

inline T f(double d) { cout<<d; }

Константа может получить внешнее связывание только с помощью явного

описания:

// file3.c:

extern const int a;

const int a = 77;

// file4.c:

extern const int a;

void g() { cout<<a; }

В этом примере g() напечатает 77.