Программирование. Принципы и практика использования C++ Исправленное издание, стр. 107
#include "std_lib_facilities.h" // здесь содержится объявление // потока coutint main(){ cout << f(i) << '\n';}Теперь осталось только две ошибки, вызванных отсутствием определения идентификаторов. При создании реальных программ большинство определений размещают в заголовочных файлах. Именно там определяются интерфейсы полезных функциональных возможностей, которые сами определяются “в другом месте”. В принципе объявление лишь устанавливает, как некая сущность может быть использована; оно определяет интерфейс функции, переменной или класса. Следует помнить об одном очевидном, но невидимом преимуществе такого использования объявлений: мы можем не беспокоиться о деталях определения потока
cout<<#includecoutОднако нам по-прежнему необходимо объявить переменные
fi#include "std_lib_facilities.h" // здесь содержится объявление // потока coutint f(int); // объявление переменной fint 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); // другое объявление функции sqrtdouble sqrt(double); // еще одно объявление функции sqrtint sqrt(double); // ошибка: несогласованное определение
sqrtdoubleintdoubleКлючевое слово
externx
Почему в языке С++ предусмотрены как объявления, так и определения? Различие между ними отражает фундаментальное различие между тем, что нам необходимо, чтобы использовать некую сущность (интерфейс), от того, что нам необходимо, чтобы нечто делало то, для чего оно предназначено (реализация). Объявление переменной устанавливает ее тип, но лишь определение создает реальный объект (выделяет память). Объявление функции также устанавливает ее тип (типы аргументов и тип возвращаемого значения), но лишь определение создает тело функции (выполняемые инструкции). Обратите внимание на то, что тело функции хранится в памяти как часть программы, поэтому правильно будет сказать, что определения функций и переменных выделяют память, а объявления — нет.
Разница между объявлением и определением позволяет разделить программу на части и компилировать их по отдельности. Объявления обеспечивают связь между разными частями программы, не беспокоясь об определениях. Поскольку все объявления должны быть согласованы друг с другом и с единственным объявлением, использование имен во всей программе должно быть непротиворечивым. Мы обсудим этот вопрос в разделе 8.3. А здесь мы лишь напомним о грамматическом анализаторе выражений из главы 6: функция
expression()term()primary()expression()