// потока 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()
.
Объявление, которое полностью описывает объявленную сущность, называют
int a = 7;
vector
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); // ошибка: несогласованное определение
sqrt
, принимающих аргумент типа double
и возвращающих значения разных типов (int
и double
).