Чтение онлайн

ЖАНРЫ

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

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
).

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

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

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

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

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

double expression; // это лишь объявление, но не определение

double primary

{

// ...

expression;

// ...

}

double term

{

// ...

primary;

// ...

}

double expression

{

// ...

term;

// ...

}

Мы

можем расположить эти четыре функции в любом порядке, потому что вызов одной из функций всегда будет предшествовать ее определению. Таким образом, необходимо предварительное объявление. По этой причине мы объявили функцию
expression
до определения функции
primary
, и все было в порядке. Такие циклические вызовы весьма типичны.

Почему имя должно быть определено до его использования? Не могли бы мы просто потребовать, чтобы компилятор читал программу (как это делаем мы), находил определение и выяснял, какую функцию следует вызвать? Можно, но это приведет к “интересным” техническим проблемам, поэтому мы решили этого не делать. Спецификация языка С++ требует, чтобы определение предшествовало использованию имени (за исключением членов класса; см. раздел 9.4.4).

Помимо всего прочего, существует обычная практика (не программирования): когда вы читаете учебники, то ожидаете, что автор определит понятия и обозначения прежде, чем станет их использовать, в противном случае читатели будут вынуждены постоянно догадываться об их смысле. Правило “объявления для использования” упрощает чтение как для людей, так и для компилятора. В программировании существует и вторая причина, по которой это правило имеет большую важность. Программа может состоять из тысяч строк (а то и сотен тысяч), и большинство функций, которые мы хотим вызвать, определены “где-то”. Это “где-то” часто является местом, куда мы даже не собираемся заглядывать. Объявления, которые описывают только способ использования переменной или функции, позволяет нам (и компилятору) не просматривать огромные тексты программ.

8.2.1. Виды объявлений

Программист может объявить множество сущностей в языке С++. Среди них наиболее интересными являются следующие.

• Переменные.

• Константы.

• Функции (см. раздел 8.5).

• Пространства имен (см. раздел 8.7).

• Типы (классы и перечисления; см. главу 9).

• Шаблоны (см. главу 19).

8.2.2. Объявления переменных и констант

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

int a; // без инициализации

double d = 7; // инициализация с помощью синтаксической конструкции =

vector<int> vi(10); // инициализация с помощью синтаксической

// конструкции

Полная грамматика языка описана в книге Язык программирования С++ Страуструпа и в стандарте ISO C++.

Константы объявляются так же, как переменные, за исключением ключевого слова

const
и требования инициализации.

const int x = 7; // инициализация с помощью синтаксической

// конструкции =

const int x2(9); // инициализация с помощью синтаксической

// конструкции

const int y; // ошибка: нет инициализации

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

Поделиться с друзьями: