
Штерн В. - Основы C++. Методы программной инженерии - 2003
.pdfI 40 I |
Часть ! ^ Введение в програттшрошаитв на С^^ |
Для выполнения программы не важно, насколько далеко определяется пере менная. Главное сделать это до ее использования. Выполнение приведенной выше программы даст тот же результат, как на рис. 2.1. Расстояние от определения до использования переменной значимо скорее для читателя, особенно если перемен ная используется один-два раза без какой-либо интерпретации. Если следующее использование переменной отделено от первого, а программисту, сопровождаю щему программу, нужно проверять определение переменной, то удобнее всего размещать определение в начале, а не в середине функции.
Еще один знакомый многим термин — это объявление, или описание (declaration). В некоторых других языках объявление и определение — синонимы. В C++ они несколько различаются — это унаследовано из языка С. Если при определении имя переменной ассоциируется с ее типом и для переменной выде ляется место в памяти, то объявления лишь ассоциируют имя и тип, а память для переменной может выделяться где-либо еще. Так происходит, например,
впрограмме, состоящей из нескольких файлов, когда переменная определяется
водном файле, а используется в другом. В этом случае с помощью ключевого слова extern она определяется в использующем ее файле как внешняя:
extern int count;
Теперь можно использовать переменную count в исходном коде данного файла. Все ссылки на переменную count в данном файле преобразуются в ее адрес, опре деленный в другом файле.
Еще одно различие определений и объявлений состоит в том, что определения должны быть в программе уникальными, а объявления могут повторяться сколько угодно раз. Например, такие определения недопустимы (даже если это желательно):
int а; int а; • / / синтаксическая ошибка
С другой стороны, допускаются следующие описания:
extern int count; extern int count; / / ошибки нет
Хотя это выглядит не более разумно, чем предыдущий пример, но бывают ситуа ции, когда может потребоваться что-либо подобное, а если сделать это по ошибке, компилятор ошибку не покажет. Далее мы увидим, как такие фундаментальные принципы обобщаются для функций и типов.
О с т о р о ж н о ! Определение должно быть уникальным, а объявление может повторяться.
После определения (или объявления) переменных программа может работать с ними. Прежде чем использовать значения переменных в программе, им нужно присвоить эти значения, иначе возникнет такая ситуация, как применение неини циализированных переменных — весьма распространенная ошибка.
Существуют два способа снабдить переменную значением: операция присваи вания и инициализация. В данном примере используются операции присваивания (каждая завершается точкой с запятой):
double X, у, z; X = PI; у = 1;
Как можно заметить, в примере первой программы на C++ переменные х и у (но не z) инициализировались:
double X = PI, у = 1, z;
Глава 2 • Быстрый старт: краткий обзор С4-+ |
41 |
Результат будет таким же: переменная х получает значение 3,1415926536, пере менная у — значение 1, а переменная z остается неинициализированной. Хотя мы имеем дело с переменными примитивных типов, разница между присваива нием и инициализацией с практической точки зрения не важна. Для объектов, определяемых программистом, эта разница более суш,ественна. Когда будет об суждаться инициализация объектов, все станет гораздо интереснее.
Переменные разрешается инициализировать только в определении, но не
вобъявлении. Например, переменная count может инициализироваться только
втом файле, где она определена (там, где выделяется память для переменной). В файле, где переменная объявляется (как внешняя), ее можно без ограничений присваивать и обраш,аться к ней, но не инициализировать. Так, следуюш^ая попыт ка даст ошибку:
extern int count = 0; |
/ / синтаксическая ошибка |
Это было только введение в типы данных C + + . В главе 3 о них рассказывается подробнее. Там же говорится об операциях со значениями разных типов.
Операторы и выражения
Оператор — программная единица, выполняемая как логическое целое, т. е. отдельные компоненты, шаги скрыты от программиста — эти детали не должны привлекать его внимание (по крайней мере, не сейчас). Оператор программы — средство абстракции. Он позволяет концентрироваться на том, что должно де латься, а не на том, как это происходит.
Описания и определения, о которых рассказывалось выше, представляют собой операторы. Детали распределения памяти нас не интересуют. Например, не важно, находится ли переменная а за переменной b или за переменной с, начи нается ли слово со старшего байта и т. д. Нужно лишь знать, что память выделена для всех трех целочисленных переменных:
int а, Ь, с;
Показанное в предыдуш,ем разделе присваивание представляет второй тип оператора. Целевая часть присваивания (переменная, получаюш^ая значение) находится слева, а выражение, определяюш,ее присваиваемое переменной значе ние,— справа. Первая программа C++ содержала следуюилую операцию при сваивания:
Z = у + 1 ;
При выполнении этого оператора сохраненное в переменной у значение скла дывается с 1 и результат сохраняется по адресу, соответствующему переменной z. На содержимом у такая операция не отражается. Переменная изменяется только тогда, когда ее имя указывается в левой части операции присваивания.
Стоит повторить, что определения в начале листинга 2.1 содержат инициализа цию переменных, но не присваивания. Хотя синтаксис похож^ для объектов C+ + вызываются разные функции:
double х=Р1, у=1, z; |
/ / х и у инициализируются, но не присваиваются |
Правая часть операции присваивания содержит выражения. Они состоят из операций и операндов. Операндами могут быть переменные, числовые литералы или другие выражения. В примере первой программы C++ выражение, исполь зуемое для установки значения переменной z, содержало операнды у и 1. Если необходимо, для структурирования сложных выражений используются скобки, например:
Z = (у + 1) * (у - 1); / / выражение с подвыражениями
42 |
Часть I * Введение в nporpor-' |
.^ниеыаС'^'^^ |
||
|
Здесь операнды |
у + 1 и у - 1 представляют собой небольшие выражения. |
||
|
Каждое выражение возвращает типизированное значение, которое может исполь |
|||
|
зоваться в операции присваивания или в другом выражении. |
|||
|
В выражениях допускается применение 55 различных операций С + 4-, включая |
|||
|
арифметические операции " + ", "-", "*", "/'\ сравнения, "<", ">" и др. 55 опера |
|||
|
ций изучить сложно. Поскольку символов для них не хватит, в С + + часто приме |
|||
|
няются двухсимвольные операции (например, для сравнения — операция " = = "). |
|||
|
Операции разбиты на 18 уровней старшинства, что тоже нелегко запомнить. Для |
|||
|
указания порядка выполнения операций многие программисты предпочитают ис |
|||
|
пользовать скобки, а не полагаться на старшинство операций. Подробнее об этом |
|||
|
рассказано в следующей главе. |
|
||
|
Существует важное различие между компонентами в левой и в правой части |
|||
|
присваивания. В выражениях C + + можно указывать и имена переменных, и лите |
|||
|
ральные значения. Если задается имя переменной (у), то в выражении использует |
|||
|
ся не ее адрес, а хранимое по этому адресу значение. Если указывается значение, |
|||
|
то оно используется непосредственно, хотя хранится по некоторому адресу. Это |
|||
|
кажется не очень простым, но суть такова: литеральное значение не может нахо |
|||
|
диться в левой части операции присваивания. Например, следующее уравнение |
|||
|
. в C + + |
недопустимо: |
|
|
|
1 = Z - |
у |
/ / в |
C++ не разрешается |
Третий тип оператора — вызов функции. В нем указывается имя выполняемой функции, используемые ею аргументы (если они есть) и возвращаемое значение (если таковое имеется).
Приведенный выше пример первой программы на C + + состоит лишь из одной функции (main). Она использует (вызывает) функцию pow. Чтобы отличать имена функций от прочих имен C + + , программисты и технические писатели используют общее соглашение: независимо от числа аргументов после имени функции указы ваются скобки, например main() или pow().
Библиотечная функция pow() использует два аргумента — возводимое в степень число и значение степени. После возведения в степень возвращаемый результат сохраняется в первом значении. Возвращаемое значение может использоваться как компонент выражения. Вот почему функция вызывается следующим образом:
у = pow(x,z);
Если первый аргумент — 3,1415926, а второй — 2, то возвращается значе ние 9,869604. (В других языках вычисления с нецелыми числами приближенные).
Когда функция вызывается при выполнении программы, выполнение вызыва ющей функции приостанавливается и начинает исполняться код вызванной функ ции. После завершения вызываемой функции выполнение вызывающей функции возобновляется. При вызове другой функции этот процесс повторяется.
Вызовы библиотечных функций ввода-вывода в C + + пояснить труднее, чем другие библиотечные вызовы. Они используют предопределенные классы, объек ты и перегруженные (overloaded) операции, о которых подробнее будет рассказано далее. Давайте попробуем. Используем библиотечные объекты cout (для вывода)
иGin (для ввода) с двумя видами двузначных операций. С библиотечным объектом cout применяется операция вставки "<<", а вывод направляется на экран. Для отображения используются имена переменных, литеральные числовые значения
истроки в двойных кавычках. С библиотечным объектом cin применяется опера ция извлечения ">>". В этом случае ввод с клавиатуры сохраняется в перемен ных, имена которых заданы в качестве операндов.
Звучит сложно, но основные принципы ввода-вывода очень просты. Поскольку каждый оператор ввода-вывода должен задавать свой собственный объект (cout или
cin), их нельзя использовать в одном операторе. Следовательно, операции ">>" и "<<" в одном операторе не применяются. Листинг 2.5 показывает пример ввода двух целых чисел с клавиатуры и вывода на экран их суммы.
|
|
|
|
Глава 2 • Быстрый старт: краткий обзор C-f4- |
[ |
43 |
| |
|||
Листинг 2.5. |
Интерактивная программа с операторами ввода и вывода |
|
|
|
||||||
#include |
<iostream> |
|
|
|
|
|
|
|||
using namespace std; |
|
|
|
|
|
|
||||
int main(voicl) |
|
|
|
|
|
|
|
|||
{ |
|
|
|
|
|
|
|
|
|
|
int |
a, |
b, |
c; |
|
|
/ / |
определения переменных |
|
|
|
cout |
« |
"Введите два целых числа и нажмите Enter |
"; |
|
|
|
|
|||
Gin » |
а » |
b; |
|
|
/ / |
два вызова функции: извлечение |
|
|||
с = а + Ь; |
|
|
|
|
|
|
|
|
||
cout « |
"Ихсумма равна " « с « endl; |
|
|
|
|
|
|
|||
return 0; |
|
|
|
|
|
|
|
|
||
Введите два целых |
числа |
и нажмите Enter: 22 33 |
Вывод ЭТОЙ программы представлен на рис. 2.3. |
|
||||||
|
Ка>кдое использование операций << и >> |
|
||||||||
Их сумма равна 55 |
|
|
представляет |
вызов функции. |
Библиотечный |
|
||||
|
|
|
|
|
компонент endl — это так называемый манипу- |
|
||||
гис. Z . 3 . Результат |
программы |
лятор. Ка>едый элемент вывода, включая строки |
|
|||||||
з двойных кавычках (и символы в одинарных), |
|
|||||||||
|
с иптперактпивным вводом-выводом |
|
, |
. |
|
|
|
|||
|
|
|
|
|
литералы (числовые значения), переменные или |
|
||||
|
|
|
|
|
выражения, должен иметь свой собственный |
|
||||
|
|
|
оператор. То же самое относится к каждому элементу вывода. Применять запятые |
|
||||||
|
|
|
или пробелы для разделения компонентов ввода-вывода некорректно. Например, |
|
||||||
|
|
|
такой оператор даст ошибку: |
|
|
|
|
|
||
|
|
|
cout « "Их сумма равна ", |
с endl; |
/ / типичная ошибка: запятая |
и пробел |
|
Еще хуже, что компилятор часто не может корректно диагностировать источ ник проблемы. В сообщениях об ошибках обычно говорится об отсутствующей точке с запятой, пропущенных аргументах, несоответствии параметров и других интересных проблемах. Вот еще одна причина, почему не стоит тратить особенно много усилий на расшифровку сообщения компилятора об ошибках. Их следует понимать так, что в программе имеется ошибка, и уповать на собственную логику.
О с т о р о ж н о ! Каждый компонент ввода и вывода должен иметь свою собственную операцию >> или <<. Применение запятых или пробелов
для разделения компонентов не допускается. Не разрешается также комбинировать операции ввода и вывода в одном операторе.
Библиотека lost ream содержит мощные и гибкие функции, однако форматиро ванный вывод в ней достаточно разнообразен. В C + + поддерживается также альтернативный набор стандартных библиотечных функций: printfO, scanf() и их вариации. Они заимствованы из С и весьма распространены в унаследован ных программах С и в коде C+ + . Чтобы использовать их, следует включить заголовочный файл stdio.h. Легче написать код для форматированного вывода с помощью этих функций, чем с гюмощью функций lost ream. Между тем при применении функций stdio.h возникает больше ошибок. Библиотека iostream популярнее, чем эти старые функции — они более не являются стандартными. К сожалению, нельзя просто позабыть про библиотеку stdio. h и полностью пере ключиться на библиотеку iostream, так как функции stdio.h часто применяются в графическом интерфейсе Windows и для обработки строк.
Функции, как и операторы других типов, представляют собой инструмент абст ракции. В исходном коде клиента (как, например, в приведенной выше первой программе C+ + ) задается, что должно быть сделано, и без лишних описаний, как это делается.
I |
44 |
I |
|
|
|
|
|
|
|
|
|
|
До сих пор рассматривались три популярных вида операторов: определения |
||||
|
|
|
|
(и объявления), присваивания и вызовы функций. Четвертым является определе |
||||
|
|
|
|
ние типа. Оно комбинирует компоненты в составной тип, такой, как структура |
||||
|
|
|
|
или класс. Со значениями составного типа можно работать так, как будто это |
||||
|
|
|
|
единое целое. Кратко об определениях типа будет рассказано в разделе "Функции |
||||
|
|
|
|
и вызовы функций". Основная же часть данной книги посвящена программирова |
||||
|
|
|
|
нию с использованием классов. |
|
|
|
|
|
|
|
|
Последний тип оператора — составной |
оператор, или операторный |
блок. |
||
|
|
|
|
Это последовательность операторов в фигурных скобках. Составной оператор |
||||
|
|
|
|
может использоваться там же, где [фименяется обычный оператор (включая другой |
||||
|
|
|
|
составной оператор). Например, в приведенном примере программы C + + |
можно |
|||
|
|
|
|
скомбинировать три последних оператора в операторный блок (см. листинг 2.6). |
||||
|
Листинг 2.6. |
Первая программа на C + + с операторным блоком |
|
|||||
|
#inclucle |
<iostream> |
/ / |
директива |
препроцессора |
|
||
|
#inclucle |
<cmath> |
|
|
|
|
||
|
using |
namespace std; |
|
|
|
|
||
|
const |
double |
PI = 3.1415926; |
/ / |
определение константы |
|
||
|
int main(void) |
|
|
|
|
|||
|
{ |
|
|
|
// определение переменных |
|
||
|
double x=PI, y=1, z; |
|
||||||
|
cout « |
"Добро пожаловать в мир C++!" « |
endl; |
|
|
|
||
|
z = у + 1; |
|
|
|
|
|||
|
у = pow(x, |
z); |
// начало операторного блока |
|
||||
|
{ |
|
|
|
|
|||
|
cout « |
"Вэтом мире pi в квадрате равно " « |
у « endl; |
|
||||
|
cout « |
"Приятного дня!" « endl; |
|
|
|
|
||
|
|
return 0; |
/ / |
конец операторного блока |
|
|||
|
|
|
|
|
|
|||
|
|
|
|
|
/ / |
конец блока функции |
|
|
|
|
|
|
Здесь он мало что меняет: программа будет выполняться, как panbuie. Однако |
||||
|
|
|
|
есть немало операторов C-f-f- (например, условия и циклы), где желательно вмес |
||||
|
|
|
|
то одного оператора использовать группу. Если логику вычислений нельзя сжать |
||||
|
|
|
|
в один оператор, будет проблема. Применение блока операторов ее решает. |
||||
|
|
|
|
Односимвольные ограничители блока { и } должны быть парными, иначе воз |
||||
|
|
|
|
никнет синтаксическая оилибка. Они открывают и закрывают область действия, |
||||
|
|
|
|
являясь структурным элементом программы. Функция main() также разграничи |
||||
|
|
|
|
вается фигурными скобками, как исходный код любой функции. Разница между |
||||
|
|
|
|
составным оператором и телом функции в том, что составной оператор не имеет |
||||
|
|
|
|
имени (это неименованный блок), а тело функции — именованный блок. |
|
|||
|
|
|
|
Операторы в программе C + + |
выполняются последовательно — сверху вниз. |
|||
|
|
|
|
В идеале каждый оператор размещается на отдельной строке с таким отступом, |
||||
|
|
|
|
чтобы структура программы была очевидна для читателя. Например, в листин |
||||
|
|
|
|
ге 2.6 операторы функции main() имеют отступ вправо относительно директив |
||||
|
|
|
|
препроцессора и заголовка функции main(). Операторы во вложенном блоке |
||||
|
|
|
|
сдвигаются вправо относительно других операторов функции main(). |
|
|||
|
|
|
|
Если операторы представляют собой последовательные шаги для достижения |
||||
|
|
|
|
одной цели, то не возбраняется поместить их на одну строку: |
|
|||
|
|
|
|
Z = у + 1; у = pow(x,z); |
|
/ / |
два подшага алгоритма |
|
Операторы, находяш,иеся на одной строке, выполняются слева направо. Сколько операторов можно поместить на одну строку кода? Это зависит от удобства чте ния программы — строки не должны быть слишком д/шнными. Однако если для
Глава 2 » Быстрый старт: краткий обзор С^^^ | 45 |
каждого оператора выделяется отдельная строка, программа вытягивается в дли ну. Читатель не запомнит, что он видел на предылунлих страницах — придется "листать" исходный код. Размещение нескольких операторов на одной строке делает проблему не такой острой — программа станет компактнее, хотя есть опасность пропустить действие, "спрятанное" в строке между другими оператора ми. По большому счету, все это дело вкуса. Во многих случаях лучше, когда опера торы, размеш,аемые на одной строке, реализуют какую-то обш^ую цель.
В C+-I- каждый оператор должен завершаться точкой с запятой. В некоторых других языках точка с запятой разделяет операторы, так что последний оператор в последовательности ее не имеет. В С4-+ точкой с запятой заканчивается каж дый оператор. Или почти каждый — составные операторы точкой с запятой не завершаются. Если посмотреть пример программы, то видно, что nocvie фигурных скобок точки с запятой нет.
Фактически точка с запятой может превратить любое выражение в оператор. Например, в С+Н- допустимо:
У + 1;
Конечно, это совершенно бесполезное выражение. Другие языки подобного не допускают. Однако в С и в СН-+ такая запись вполне законна. Зачем думать о том, насколько законна бессмысленная веил^ь, если незачем так писать? Нет, это не так уж глупо, как кажется. Если такая ошибка будет сделана случайно (напри мер, пропущена левая часть в присваивании), компилятор ее проглотит и не сооб щит о том, что что-то не так. Такая ошибка не проявится как синтаксическая (в других языках так и было бы). Она превратится в ошибку этапа выполнения
иможет быть выявлена только в процессе отладки.
Вкачестве одного из видов операторов можно рассматривать управляющие конструкции. Они изменяют ход выполнения программы. Есть три вида операто ров управления выполнением программы:
•Условные операторы
•Циклы
•Вызовы функции
Внимание в данном разделе рассматривается только небольшая часть операторов управления. Подробнее о них рассказано в главе 4.
Простейший оператор условия — это оператор if. Он имеет обшую форму:
i f (выражение) выполняемый_оператор;
Синтаксически if представляет собой один оператор. Если выполняемый_оператор, один, то if завершается точкой с запятой. В составном операторе после закрыва ющей фигурной скобки точки с запятой не требуется. Круглые скобки для выра жения обязательны.
Следующий за выражением оператор выполняется, если выражение истинно, а если оно ложно — пропускается. Для выделения управляющих структур часто используются отступы. Ниже в сегменте кода проверяется температура по Фарен гейту. Если она выше точки замерзания, то выводится сообщение. В противном случае сообщение на экране не появляется:
i f (fahr |
> 32) |
/ / выражение обычно следует на отдельной |
строке |
cout « |
"О запуске двигателя авто можно не беспокоиться" « |
endl; |
Во второй форме оператора if используются две ветви: одна выполняется, если условие истинно, а другая — если ложно. Каждая ветвь содержит один
t |
46 |
1 |
Часть ! ^ ВввА^^^^ в програтшг/ |
Kukf |
^^3 ^>^^'i^ |
||
|
|
|
|
|
|
||
|
|
|
оператор (завершаемый точкой с запятой) или составной блок операторов (без |
||||
|
|
|
точки с запятой в конце). Например: |
|
|
||
|
|
|
i f (fahr |
> 32) |
|
/ / ключевого слова then в C++ нет |
|
|
|
|
cout |
« |
"О запуске двигателя авто можно не беспокоиться" « endl; |
||
|
|
|
else |
|
|
|
|
|
|
|
cout |
« |
"Утром будьте осторожны" « endl; |
||
|
|
|
Ключевое слово then в С+4- отсутствует: оно подразумевается. Ключевое сло |
||||
|
|
|
во else в этой конструкции должно использоваться. Заметим, что отступы служат |
||||
|
|
|
для выделения управляющей структуры. |
|
|||
|
|
|
Простейшим оператором цикла является оператор while: |
||||
|
|
|
while (выражение) выполняемый_оператор; |
||||
|
|
|
Синтаксически тело цикла — один оператор. Для простого выполняемого опе |
||||
|
|
|
ратора цикл while завершается точкой с запятой. Если тело цикла — составной |
||||
|
|
|
оператор, то точки с запятой после закрывающей правой скобки не требуется. |
||||
|
|
|
Выражение обязательно заключается в круглые скобки. |
||||
|
|
|
Тело цикла, выполняемый_оператор, выполняется, если выражение истинно. |
||||
|
|
|
Затем выражение (условие цикла) проверяется снова. Если оно становится лож |
||||
|
|
|
ным, то тело цикла пропускается и управление передается следующему оператору |
||||
|
|
|
(когда он присутствует). |
|
|
||
Числа |
Их квадраты |
|
Программа в листинге 2.7 вычисляет квадраты чисел 8, 9, 10 и 11, |
||||
|
а на экран в виде таблицы выводятся сами числа и их квадраты (резуль |
||||||
|
|
|
|
|
тат представлен на рис. 2.4). Сначала выводится заголовок таблицы |
||
|
|
|
|
|
и пустая строка, а затем в качестве переменной цикла применяется |
||
|
|
|
|
|
переменная num. Перед выполнением цикла num инициализируется |
||
Приятного дня |
|
|
значением 8 — оно используется при первом проходе цикла. В теле |
||||
|
|
цикла num увеличивается на 1. В условии цикла проверяется, что num |
|||||
|
|
|
|
|
все еще меньше 12. Если это так, то выполняется тело цикла (в фи |
||
Рис. |
2.4. Вывод цикла, |
|
гурных скобках), а значение num снова увеличивается. Выполнение |
||||
|
цикла продолжается, пока значение num не станет равным 12. Когда |
||||||
|
|
вычисляющего |
|
||||
|
|
|
условие цикла становится ложным, тело цикла пропускается и выпол |
||||
|
|
квадраты чисел |
|
няется последний оператор программы.
Листинг 2.7. Пример программы с циклом и форматированием вывода
#include <iostream> #inclucle <iomanip> using namespace std; int main (void)
{
int num = 8, square; |
/ / |
перед циклом инициализируется num |
||
cout « |
"Числа и их квадраты" « endl « |
endl; |
|
|
while (num < 12) |
/ / |
num используется как переменная цикла |
||
{ square = num * num; |
/ / |
num используется в теле |
||
cout « " " « |
num « " " « square « |
endl; |
|
|
num = num + 1; |
|
/ / |
переменная модифицируется в конце цикла |
|
} |
endl; « |
"Приятного дня" « endl; |
|
|
cout « |
|
|||
return |
0; |
|
|
|
}
Когда оператор << передает символы на экран с помощью объекта cout, они отображаются без разделяющих пробелов. При завершении преобразования
Глава 2 • Быстрый старт: краткий обзор C-fнн |
47 |
одного значения (например, num) из двоичного вида в символьный |
и переходе |
к преобразованию другого значения (например, square) промежуточные пробелы также не добавляются. Такой неформатированный вывод, конечно, неудобен. Для быстрого форматирования вывода можно вставить между компонентами промежу точные пробелы. Именно это и делает оператор cout в листинге 2.7.
Такой метод форматирования редко подходит. При разных проходах цикла выводятся значения с разным числом символов. Столбцы будут невыровненными. Проблему можно устранить с помош^ью манипулятора setw, задающего число позиций (по ширине) для каждого компонента. Этот манипулятор нужно вставить в поток вывода в операции << точно так же, как любой другой компонент вывода. Например, при вставке setw(4) для следующего компонента выделяется 4 пози ции вывода. Если нужно форматировать несколько компонентов, то перед каждым необходим свой манипулятор setw (даже если ширина вывода для каждого компо нента одна и та же).
Заменим оператор cout в листинге 2.7 на следующий: cout « setw(4) « num « setw(10) « square « endl;
Чтобы это работало, в программу нужно включить заголовочный файл iomanip (см. листинг 2.7). Вывод данной версии программы показан на рис. 2.5. Для чис ловых значений вывод выравнивается вправо на заданную величину. Для строк символов он выравнивается влево. Если выводимое значение не по мещается в заданных позициях, то объект cout задействует на экране столько позиций, сколько требуется, а остальная часть выводится
справа. Вывод никогда не усекается из-за недостатка позиций. Нужно хорошо разобраться с элементами конструкции и с итера
циями цикла. Корректно написанный цикл while должен содержать:
|
• |
Инициализацию переменной цикла перед началом |
|
|
его выполнения |
Рис. 2 . 5 . Вывод цикла |
Использование текущего значения переменной цикла |
|
с |
заданными |
в его теле |
для |
каждого |
Изменение (увеличение) текущего значения в теле цикла |
элементна |
||
позициями |
(часто в конце цикла) |
В листинге 2.7 в качестве переменной цикла применяется переменная num. Она инициализируется перед циклом, при определении. Ее значение используется в теле цикла. Оно увеличивается в конце цикла и служит для принятия решения о завершении цикла.
Функции и вызовы функций
Разбиение программы на модули-функции дает возможность распределить работу по реализации ПО между программистами. Группы функций помещаются в разные исходные файлы, и для каждого файла назначается программист. Это позволяет программистам работать параллельно. Очевидно, программист может работать и над несколькими функциями в разных файлах, но несколько человек не могут заниматься одной функцией. Если функция настолько велика, что требует усилий нескольких программистов, ее следует разбить на разные функции.
Другие преимущества использования функций перечислены ниже.
• В вызывающем коде применяются вызовы функций, имена которых могут отражать смысл операций. Это делает исходный код более читабельным, чем при включении в него большого числа операций нижнего уровня.
48Часть I * Введение в програг^г^ировоние на C+'i^
•Размер исходного (и объектного) кода можно уменьшить:
если в разных частях программы выполняются одни и те же операции, надо оформить их как вызов функции, а не повторять в исходном (и объектном) коде как операции нижнего уровня.
•Применение стандартных библиотек (и включение в библиотеки проекта специфических для проекта функций) увеличивает возможности повторного использования кода в одном или нескольких проектах.
Если это делается корректно, разбиение программы на отдельные функции изменяет структуру программы, но не ее вывод. При этом качество программы в значительной степени зависит от того, как именно ее разбить на модули. Незави симые функции упрощают понимание и сопровождение программы.
Рассмотрим различные реализации программы, представленной в листинге 2.1. Поскольку эта программа невелика, примеры не демонстрируют преимуществ, связанных с удобством чтения, размером программы и ее повторным использова нием. Между тем они позволят показать синтаксис и семантику (смысл) примене ния функций.
Первое изменение (см. листинг 2.8) включает в себя вывод начального привет ствия— это делает отдельная функция с именем displaylnitialGreetingO. Данная функция вызывается из функции main(). Следовательно, main() — это клиент, а сама функция является для main() сервером.
Листинг 2.8. Первая программа C + + с одной функцией-сервером
#inclucle |
<iostream> |
|
|
||
#inclucle |
<cmath> |
|
|
||
using namespace std; |
|
|
|||
const double PI = 3.1415926; |
|
|
|||
void |
displaylnitialGreetingO |
/ / |
заголовок функции |
||
{ |
|
|
|
/ / |
тело функции |
cout « |
"Добро пожаловать в мир C++!" « endl; |
||||
|
|
|
|
/ / |
конец блока функции |
int |
inain(void) |
|
|
||
{ |
|
|
|
|
|
double |
x=PI, y=1, z; |
/ / |
вызов функции |
||
displaylnitialGreetingO; |
|||||
z = у + 1; |
|
|
|
||
у = pow(x,z); |
endl |
|
|||
// |
cout « |
"B этом мире pi в квадрате равно " « у « |
|
||
// |
cout « |
"Приятного дня!" « endl; |
|
|
|
return |
0; |
|
/ / конец блока функции |
||
} |
|
|
|
Конечно displaylnitialGreetingO; —довольно глупая функция. Она содер жит только один оператор. Тем не менее каждая такая функция демонстрирует, что применение функций C + + требует координации трех элементов программы:
•Заголовка функции
•Тела функции
•Вызова функции
Заголовок функции определяет ее интерфейс: тип возвращаемого значения, имя функции, список параметров (в скобках) с типами и имена формальных пара метров. Если функция не использует параметров, то список параметров в скобках будет пустым. Если функция не возвращает значения, то возвращается тип void.
Глава 2 # Быстрый старт: к р а т к и й обзор Снн^ |
49 |
Это название (void — пусто) описывает смысл обработки и следует популяр ным соглашениям, предусматриваюш^им комбинирование поясняюидих действие глаголов (display — вывод на экран) и суш,ествительного, указываюш.его объект действия (InitialGreeting — начальное приветствие). В соответствии с популяр ными соглашениями по программированию первое слово функции записывается в нижнем регистре, а первые буквы других слов — с буквы в верхнем регистре.
Как видно, функция displaylnitialGreetingO не возвраш,ает никакого значе ния (возвраш,ает тип void). Вот почему оператор возврата значения (return) в ней не требуется. Если необходимо, его можно включить в функцию, но без возвраидаемого значения. Эта функция также не имеет параметров (список параметров пуст). Круглые скобки в заголовке все равно указываются. Чтобы показать отсут ствие параметров, можно использовать ключевое слово void:
void displaylnitialGreetingO |
/ / |
заголовок функции |
{ |
|
|
cout « "Добро пожаловать в мир C++!" « endl; |
|
/ / тело функции |
return; |
/ / |
избегайте ненужного кода |
} |
|
|
Тело функции — это последовательность операторов в фигурных скобках. Таким образом, тело функции представляет собой операторный блок (составной оператор). Каждый оператор завершается точкой с запятой, но сам блок не имеет завершаюш,ей точки с запятой. Если нужно, тело функции может содержать определения и объявления переменных, необходимых для вычислений в ее теле. Каждое тело функции имеет собственное пространство имен (область действия). Это означает, что определенные в этой функции переменные (локальные) не будут конфликтовать с именами других переменных, определенных в других функциях. Разработчику функции не требуется координировать эти имена с остальными.
Подобно языку С, в С+Ч- не допускается вложенность определений функций. Следовательно, имена функций являются в программе глобальными и обязаны быть уникальными. Разработчику функции нужно согласовывать ее название
сдругими проектировш,иками, независимо от того, вызывают они ее или нет.
Вфункции displaylnitialGreetingO никаких локальных переменных не опре деляется. Чтобы подчеркнуть границы тела функции, открываюш.ая и закрываю- ш,ая фигурные скобки располагаются на отдельных строках. Некоторые полагают, что это увеличивает размер программы по вертикали, не давая никаких удобств при чтении, и не выделяют для фигурных скобок отдельных строк. Тем не менее они оставляют пустые строки между функциями:
void displaylnitialGreetingO |
/ / |
заголовок функции |
{ cout « "Добро пожаловать в мир C++!" « |
endl; } |
|
|
/ / |
тело функции |
Третий элемент, касаюш,ийся использования функции, ее вызов, состоит из имени функции и списка фактических параметров в круглых скобках. Если аргу ментов нет, скобки все равно нужны. В отличие от заголовка функции, ключевое слово void в вызове функции использоваться не может:
displaylnitialGreeting(void); |
/ / некорректный вызов функции |
Это еш.е одна унаследованная из С и удивительная черта. Для создателей языка С простота никогда не была приоритетом. Зачем запоминать, что void можно ис пользовать в заголовке функции, но не в вызове? Они сбросили со счетов тот факт, что накопление таких особенностей только путает программистов.
На самом деле эксперт по языку С знает ответ на этот вопрос и, вероятно, назовет данное решение простым. Но это не так. Я вел семинары по СН-+ среди большого числа программистов на С. Многие их них — хорошие профессионалы.