Литмир - Электронная Библиотека
A
A

#include "std_lib_facilities.h" // здесь содержится объявление

                                // потока cout

int main()

{

  cout << f(i) << '\n';

}

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

cout
и его операторов
<<
; мы просто включаем их объявления в программу с помощью директивы
#include
. Мы можем даже не заглядывать в их объявления; из учебников, справочников, примеров программ и других источников нам известно, как используется поток
cout
. Компилятор считывает объявления из заголовочных файлов, необходимых для понимания кода.

Однако нам по-прежнему необходимо объявить переменные

f
и
i
. И сделать это можно следующим образом:

#include "std_lib_facilities.h" // здесь содержится объявление

                                // потока cout

int f(int); // объявление переменной f

int main()

{

 int i = 7; // объявление переменной i

 cout << f(i) << '\n';

}

Этот код компилируется без ошибок, поскольку каждое имя было определено, но он не проходит редактирование связей (см. раздел 2.4), поскольку в нем не определена функция

f()
; иначе говоря, мы нигде не указали, что именно делает функция
f()
.

Объявление, которое полностью описывает объявленную сущность, называют определением (definition). Рассмотрим пример.

int a = 7;

vector<double> v;

double sqrt(double d) {/* ... */}

Каждое определение — это объявление, но только некоторые объявления одновременно являются определениями. Ниже приведены некоторые примеры объявлений, которые не являются определениями; каждому из них должно соответствовать определение, размещенное где-то в другом месте кода.

double sqrt(double); // здесь функция не имеет тела

extern int a;        // "extern плюс отсутствие инициализатора"

                     // означает, что это — не определение

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

Определение устанавливает, на что именно ссылается имя. В частности, определение переменной выделяет память для этой переменной. Следовательно, ни одну сущность невозможно определить дважды. Рассмотрим пример.

double sqrt(double d) {/* ... */} // определение

double sqrt(double d) {/* ... */} // ошибка: повторное определение

int a; // определение

int a; // ошибка: повторное определение

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

int x = 7;                        // определение

extern int x;                     // объявление

extern int x;                     // другое объявление

double sqrt(double);              // объявление

double sqrt(double d) {/* ... */} // определение

double sqrt(double);              // другое объявление функции sqrt

double sqrt(double);              // еще одно объявление функции sqrt

int sqrt(double);                 // ошибка: несогласованное определение

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Почему последнее объявление является ошибкой? Потому что в одной и той же программе не может быть двух функций с именем
sqrt
, принимающих аргумент типа
double
и возвращающих значения разных типов (
int
и
double
).

Ключевое слово

extern
, использованное во втором объявлении переменной
x
, утверждает, что это объявление не является определением. Это редко бывает нужным. Мы не рекомендуем делать это, но в принципе такие объявления можно встретить в некоторых программах, особенно в программах, использующих слишком много глобальных переменных (см. разделы 8.4 и 8.6.2).

Программирование. Принципы и практика использования C++ Исправленное издание - _063.png

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

Разница между объявлением и определением позволяет разделить программу на части и компилировать их по отдельности. Объявления обеспечивают связь между разными частями программы, не беспокоясь об определениях. Поскольку все объявления должны быть согласованы друг с другом и с единственным объявлением, использование имен во всей программе должно быть непротиворечивым. Мы обсудим этот вопрос в разделе 8.3. А здесь мы лишь напомним о грамматическом анализаторе выражений из главы 6: функция

expression()
вызывает функцию
term()
, которая, в свою очередь, вызывает функцию
primary()
, которая вызывает функцию
expression()
. Поскольку любое имя в программе на языке С++ должно быть объявлено до того, как будет использовано, мы вынуждены объявить эти три функции.

107
{"b":"847443","o":1}