Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Штерн В. - Основы C++. Методы программной инженерии - 2003

.pdf
Скачиваний:
267
Добавлен:
13.08.2013
Размер:
28.32 Mб
Скачать

Щ 50^ 3 Чосш I ^ Введение в програмтмрошаиие на С+^

но нечасто можно было услышать от них верный ответ. По крайней мере, для этого требовались многочисленные намеки. Вы узнаете ответ, когда лучше освои­ те язык.

При вызове функции вызываюш,ая функция (клиент) приостанавливает свое выполнение и передает управление вызываемой функции (серверу). Ее операторы выполняются последовательно. Когда достигается закрываюш^ая скобка в теле функции-сервера, эта функция завершает работу и управление возвраш,ается вызываюш^ей функции (вот почему завершение функции называется return — возврат). Затем функция-клиент возобновляет выполнение.

Теперь рассмотрим функцию с параметрами. В листинге 2.9 показана еш,е одна версия первой программы C+ + . Она содержит функцию displayResultsO, реализуюшую операции локального блока из предыдуш^ей версии. Функции пере­ дается параметр типа double, и она выводит его на экране с дополнительными сообш^ениями.

Листинг 2.9. Первая программа C + + с двумя функциями-серверами

#include

<iostream>

 

 

 

#include

<cmath>

 

 

 

using

namespace std;

 

 

 

const double PI = 3.1415926;

 

 

 

void

displaylnitialGreetingO

endl; }

/ /

заголовок функции

{ cout «

"Добро пожаловать в мир C++! «

/ /

тело функции

void displayResults(double у)

« у «

/ /

заголовок функции

{ cout «

"Вэтом мире pi в квадрате равно

endl;

 

cout «

"Приятного дня!" « endl;}

 

/ /

тело функции

int main(void)

 

 

 

double х=Р1, у=1, z;

 

 

 

displaylnitialGreetingO;

 

/ /

вызов функции

z = у + 1;

 

 

 

у = pow(x,z);

 

 

 

displayResults(y);

 

/ /

еще один вызов функции

return

0;

 

 

 

Функция displayResultsO не возвраидает никакого значения вызывающей функции (у нее возвращаемый тип void), но список параметров не пуст. Как видно, определение параметра аналогично определению переменных. Програм­ мист выбирает имя параметра и задает его тип. Результат этого определения на этапе выполнения аналогичен результатам определения переменной: при вызове функции для параметра соответствующего типа выделяется память. Адрес данной области памяти используется при упоминании имени параметра в теле функции (например, ссылка на переменную у в первом операторе cout). Значение парамет­ ра инициализируется значением фактического аргумента в вызове функции.

В вызове функции указываются ее имя и список фактических аргументов в круглых скобках. Здесь в списке только один аргумент (его имя совпадает с именем формального параметра, но это совпадение). Заметим, что фактический аргумент — это выражение, а не определение переменной. Следующая форма вызова функции будет некорректной:

displayResults(double у);

/ / ошибка

Глава 2 # Быстрый старт: краткий обзор С+4-

51

Далее рассмотрим функцию, возвращающую отличный от void результат и имеющую несколько параметров. Листинг 2.10 содержит еще одну функцию для первой программы С++ (на самом деле это можно сделать несколькими способа­ ми). Здесь введена функция computeSquareO с двумя параметрами типа double. Функция возвращает значение типа double.

Листинг 2.10. Первая программа C + + с тремя функциями-серверами

#include

<iostream>

 

#include

<cmath>

 

using namespace std;

 

const double PI = 3.1415926;

 

void displaylnitialGreetingO

// возвращает тип void

{ cout «

"Добро пожаловать в мир C++! «

endl; }

double computeSquare(double x, double y)

// возвращает непустой результат

{ double z;

// локальная переменная

z= у + 1;

у= pow(x,z);

return y; }

// обязательный оператор возврата

void displayResults(double y)

// заголовок функции

{ cout «

"Bэтом мире pi в квадрате равно

« у « endl;

cout «

"Приятного дня!" « endl;}

// тело функции

int main(void)

 

double х=Р1, у=1;

// вызов функции

displayInitialGreeting();

у = computeSquare(x,y);

// еще один вызов функции

displayResults(y);

// и еще вызов

return 0;

 

}

 

 

Отметим, что на типы параметров и возвращаемые значения никаких ограни­ чений нет. В этом примере они все имеют тип double просто по совпадению.

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

double computeSquare(double х, у)

/ / синтаксическая ошибка

{ double z;

 

z = у + 1;

 

у = pow(x,z);

 

return у; }

 

Внимание Параметры и возвращаемые значения могут быть одного типа. Тип каждого параметра нужно указывать отдельно, даже если он совпадает.

Так как заголовок функции computeSquareO задает непустое возвращаемое значение (в данном случае double), тело функции должно содержать оператор return. Для этого используется ключевое слово return, а в качестве аргумента указывается double. Некоторые программисты помещают возвращаемое значение в круглые скобки, но в этом нет необходимости. В теле функции computeSquareO

щ 52

I

Часть I« Введение в nporpoivii^HpoBaHHe на C^-^i-

определяется локальная переменная z, ей присваивается значение, вычисляется значение переменной у, и результат возвращается вызывающей функции. (См. главу 5.) Когда код клиента вызывает данную функцию, он передает ей значе­ ния аргументов и эти значения используются внутри функции. Поскольку функция возвращает значение, его можно включать в коде клиента в выражения точно так же, как обычное значение данного типа (в нашем случае double):

у = pow(x,z);

Нужно различать термины параметр и аргумент. Параметр — переменная, определенная в заголовке функции и используемая в ее теле, а аргумент — переменная, определенная в функции-клиенте и используемая в вызове функции. Часто говорят о формальных и фактических аргументах.

В этом примере в целях простоты для формальных параметров и соответствую­ щих фактических аргументов функции computeSquare() используются одни и те же имена. В реальной жизни такое случается редко. В крупной программе функ­ цию-клиент разрабатывает один программист, а функцию-сервер — другой. Им нет никакой необходимости координировать имена параметров. Программисту, работающему над клиентом, достаточно знать типы параметров и возвращаемых значений.

Часто функция-сервер разрабатывается раньше, чем функция-клиент. Она помещается в библиотеку, а ее исходный код недоступен. Но, даже если он и до­ ступен, нет нужды обременять программиста, занимающегося клиентом, изучени­ ем кода сервера и имен формальных параметров. Имена фактических аргументов никак не связаны с именами формальных параметров. Вот еще одна версия функ­ ции computeSquareO, которую можно использовать в листинге 2.10. От этого ничего не меняется, что подчеркивает данную независимость:

double computeSquare(double base,

double exponent)

 

{ double power = exponent

+

1 ;

/ /

локальная переменная

return pow(base,power);

}

 

/ /

оператор возврата

Во всех этих примерах определения функций-серверов размещаются перед вызовами функций в клиенте. Весьма похоже на ситуацию с обычными перемен­ ными: определение лексически предшествует использованию переменной. Что произойдет, если разместить функции в исходном коде в другом порядке? Здесь опять вступают в действия ограничения C+-f, унаследованные от С: если опреде­ ление функции не предшествует лексически вызову функции, то компилятор его не разглядит и сообщит, что не знает идентификатор displaylnitialGreetingO и др. Некорректная версия программы приведена в листинге 2.11.

Листинг 2.11. Некорректная программа C + + с функциями, следующими за вызовами

#include

<iostream>

 

#include

<cmath>

 

using namespace std;

 

const double PI = 3.1415926;

 

int main(void)

 

{

 

 

double x=PI, y=1;

// синтаксическая ошибка

displaylnitialGreetingO;

у = computeSquare(x,y);

// еще одна синтаксическая ошибка

displayResults(y);

// и еще ошибка

return 0;

}

Глава 2 ^ Быстрый старт: краткий обзор С++

53

void displaylnitialGreetingO

{ cout « "Добро пожаловать в мир C++!" « endl; }

double computeSquare(double base, double exponent)

{double power = exponent + 1; return = pow(base,power); }

void displayResults(double

y)

{ cout

«

"B этом мире pi в квадрате равно " « у « endl;

cout

«

"Приятного дня!"

« endl;}

// определение функции

// тело

/ /

локальная

переменная

/ /

оператор

возврата

/ /

заголовок

функции

/ /

тело функции

Компилятор пытается дать полезную информацию о функциях и элементах, требующих переопределения:

Compiling...

 

 

 

 

 

c:\data\ch02.cpp

 

 

 

 

 

c:\data\ch02.срр(7)

: error

C2065: 'displaylnitialGreeting'

: undeclared identifier

c:\data\ch02.срр(7)

: error

C2064: term does not evaluate to a function

c:\data\ch02.срр(8)

: error

C2065: 'computeSquare'

: undeclared

i d e n t i f i e r

c:\data\ch02.срр(8)

: error

C2064: term does not evaluate to a function

c:\data\ch02.срр(9)

: error

C2065: 'displayResults'

: undeclared

i d e n t i f i e r

G:\data\ch02.срр(9)

: error

C2064: term does not evaluate to

a function

c:\data\ch02.срр(13) : error C2371: 'displaylnitialGreeting' : redefinition; different basic types

c:\data\ch02.срр(17) : error C2371: 'computeSquare' : redefinition; different basic types

c:\data\ch02.срр(23) : error C2371: 'displayResults' : redefinition; different basic types

DEMO.EXE - 9 error(s) 0 warning(s)

Конкретный вид данных сообщений зависит от типа применяемого компилято­ ра, но это не важно. Обычно начинающий программист сначала не верит: "Как идентификатор может быть не определен? Он определен — вот здесь. Эй, компи­ лятор, ты ослеп?" Компилятор не ослеп. Это все тот же компромисс между инте­ ресами разработчиков компилятора и интересами пользователей. Пользователь компилятора упустил из виду главное правило: все, с чем работает программа, предварительно должно быть определено. Речь идет действительно обо всем: включая имена переменных и функций.

Цель применения такого требования к вызовам функций очевидна: важно, чтобы компилятор проверил правильность написания имени функции, коррект­ ность числа аргументов и их типов. Разумеется, сообщения об ошибках могли бы быть более конкретны. И конечно, такую проверку можно было бы сделать, не требуя чего-то от программиста. Второй проход по исходному коду (как делается

внекоторых старых компиляторах) полностью устранил бы проблему.

ВC++ программисты решают данную проблему с помощью прототипов функции. Прототип функции имеет ту же синтаксическую форму, что и заголовок функции. Единственное отличие в том, что прототип должен завершаться точкой

сзапятой. На размещение прототипов никаких ограничений не налагается. Они лишь должны лексически предшествовать первому вызову функции. В соответ­ ствии с общепринятым соглашением, прототипы функции размещаются в начале исходного файла с функциями-клиентами.

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

54

"1i«<C IЬ i '^ Введение в nporpoiviftrtiipOBaHiie на C-^-^

При применении прототипов функций нет необходимости включать определе­ ния в тот же файл, где находится функция-клиент. Наличие прототипов вполне достаточно Д/1Я компилятора. В листинге 2.12 показана корректная версия листин­ га 2.11 с прототипами функции-сервера в начале файла и перенесенными в другое место определениями функций.

Листинг 2.12. Корректная программа C + + с прототипами, предшествующими вызовам функций

const double PI = 3.1415926;

 

 

void displaylnitialGreetingO

/ /

прототипы функции

double computeSquare(double x, double y);

 

 

void displayResults(double y);

 

 

int main(void)

 

 

{

 

 

double x=PI, y=1;

/ /

вызовы функции

displaylnitialGreetingO;

у = computeSquare(x,y);

 

 

displayResults(y);

 

 

return 0;

/ /

конец блока функции

 

Даже этот небольшой пример показывает преимущество использования функ­ ций. Из функции mainO убраны детали операций — здесь видно, что делается, а не как. Чтобы ознакомиться с деталями, программисту, сопровождающему программу, достаточно обратиться к исходному коду функций-серверов в разных файлах. Поскольку каждая функция находится в отдельном файле, его внимание не будет рассеиваться на ненужные детали. Программисту не надо даже знать имен исходных файлов с функциями-серверами. Между тем объектный код этих функций должен компоноваться с объектным кодом main(). Исчезли и файлы #include. Они включены в исходный код функций-серверов, вызывающих биб­ лиотечные функции. Программисту, занимающемуся клиентом, не требуется знать, какой сервис использует сервер.

Если к серверной функции обращаются разные функции-клиенты в разных файлах, то в каждый такой файл должен включаться прототип данной функции. Часто программисты помещают прототипы в отдельные заголовочные файлы

ивключают их в файлы при реализации функций-клиентов. Разница между включением стандартных библиотечных файлов типа lost ream и определяемых программистом заголовочных файлов состоит в использовании имен маршрутов

идвойных кавычек. В листинге 2.13 прототипы перемещены в файл по маршруту c:\data\cppbook\ch02\diisplay.h:

Листинг 2.13. Программа C + + с прототипами в отдельном файле

const double PI = 3.1415926;

 

 

#include "c:\data\cppbook\ch02\display.h"

/ /

прототипы

int main(void)

 

 

{

 

 

double x=PI, y=1;

// вызовы функций

displaylnitialGreetingO;

у = computeSquare(x,y);

 

 

displayResults(y);

 

 

return 0;

 

 

}

/ /

конец блока функции

Глава 2 « Бьютрый старт: краткий обзор С^-^-

[ 55 Р

Использовать имена параметров в прототипах функций необязательно. Компи­ лятор не контролирует по ним корректности вызовов функций: анализируются только типы аргументов. Эти имена могут быть полезными для документирования. Если имена параметров не используются, то файл c:\clata\cppbook\ch02\cliisplay.h может выглядеть так:

void

displaylnitialGreetingO;

 

double computeSquare(double, double);

/ / нет имен параметров

void

displayResults(double);

 

Классы

Теперь приступим к описанию синтаксиса, позволяющего комбинировать функции с данными в составной тип данных. В C-I-+ для этого предусматриваются ключевые слова struct и class, а также правила слияния отдельных компонентов в единое целое. В определении класса задаются типы и имена элементов данных (полей) и заголовки или тела функций-членов (обращающихся к полям данных). Имя класса может использоваться в коде клиента как имя типа. Это означает, что можно определять переменные данного типа (объекты), передавать их функциям как параметры и т. д.

Представим время дня как комбинацию часов и минут. Желательно, чтобы объект мог хранить данные времени и выводить сохраняемые значения. Код кли­ ента может запрашивать время в разном формате (18:45 или 6:45 P.M.).

Для этого создадим описание класса с двумя целочисленными полями данных: hours и munutes, к которым можно обращаться извне класса. Сначала описывается композиция объектов класса (экземпляры и переменные), которые позднее будут определяться и использоваться в программе:

struct TimeOfDay

/ /

используется ключевое слово struct

{

 

 

 

int

hours;

/ /

один компонент данных: целое

int

minutes;

/ /

другой компонент данных: целое

};

Всоответствии с общим соглашением, имена классов записываются с буквы

вверхнем регистре, а каждая составляющая имени также начинается с буквы

вверхнем регистре. Открывающая и закрывающая фигурные скобки отмечают область действия класса — границы, отделяющие содержимое класса от того, что находится вне его. В отличие от других случаев использования области действия

вфигурных скобках, здесь точка с запятой после закрывающей скобки обязатель­ на. Так что не думайте, будто в СН-+ все очень просто.

Определения полей данных в классе аналогичны определениям переменных. Они ассоциируют тип и имя поля. Когда создается объект (экземпляр переменной) класса TimeOfDay, для каждого поля в соответствии с его типом выделяется память.

TimeOfDay time1, time2;

/ / распределяются два объекта

Если поля одного типа, то можно использовать только одно имя типа, а поля разделять запятыми. Это аналогично определениям переменных встроенных типов C+ + .

struct TimeOfDay

{

int hours, minutes;

/ / другой элемент данных: целое

} ;

Опять же можно применять этот синтаксис для определения переменных и полей данных класса, но не для параметров функции.

56

Часть I # Введение в програ^^ирован1^е но C^

Поля данных должны быть закрытыми. Они недоступны вне класса. Далее понадобятся функции-члены (методы), обращающиеся к полям данных от имени клиента. Эти функции должны быть общедоступными, чтобы в коде клиента их можно было использовать для установки и вывода времени в двух форматах. Например, реализуем функции setTimeO, displayMilitaryTimeO и displayTimeO. Функция setTime() должна иметь два параметра (часы и минуты). Другие функции параметров не имеют. Они просто выводят на экран значения, хранящиеся в объекте. В листинге 2.14 представлен синтаксис определения класса с элемен­ тами данных и функциями-членами.

Листинг 2.14. Первый класс C + + , комбинирующий функции и данные

#include

<iostream>

 

 

using

namespace std;

 

 

class

TimeOfDay

{

 

/ /

используется ключевое слово class

private:

 

 

/ /

ключевое слово private делает данные скрытыми

 

int

hours,

minutes;

 

public

 

 

 

 

 

void setTime(int hrs, int min)

 

 

 

{ hours = hrs; minutes = min; }

 

void

displayMilitaryTime(void)

 

 

 

{ cont «hours«":"«minutes; }

 

void

displayTime(void)

 

 

 

{ i f (hours

> 12)

 

 

 

cout

«

hours-12«":"«minutes«"P.M.";

 

 

else

 

 

 

 

 

 

cout

«

hours « " ; "«minutes«A.M."; }

} ;

Функция setTimeO копирует значения параметров в поля данных объекта. Функция displayMilitaryTimeO выводит часы и минуты. Функция displayTimeO проверяет, не превышает ли значение hours (часы) 12. Если да, то она вычитает из hours 12 и выводит разницу с минутами и P.M. (после полудня). Если нет, hours выводится с minutes и А.М. (до полудня). В данном примере используется библио­ тека C++ lost ream.

Эти функции находятся в самом классе, а следовательно, могут работать с его данными без ограничений — устанавливать значения и обращаться к ним в соот­ ветствии с целями клиента. Собственно, они и нужны для клиентского кода, а не для класса. Вот почему функции-члены определены как общедоступные (public). Их можно вызывать в клиентском коде. Для этого клиентский код определяет объ­ екты класса с помощью стандартного синтаксиса определения переменных C+ + : клиент ассоциирует имя типа (TimeOfDay) и имена переменных (например, time1, time2).

Определение класса сохранено в файле c:\data\cppbook\ch02\time.h. Чтобы можно было использовать этот класс, нужно включить его в данный заголовочный файл. В противном случае компилятор сообщит, что имя TimeOfDay не определено. В листинге 2.15 показан пример исходного кода клиента, в котором определяются объекты TimeOfDay, анализируется возвращаемое функциями класса значение и форматируется вывод. (Здесь включается заголовочный файл.)

Глава 2 • Быстрый старт: краткий обзор С-^нн

57

Листинг 2.15. Код клиента для первого класса C + + , комбинирующего функции и данные

#include <iostream> using namespace std;

#inclucle "c:\clata\cppbook\Gh02\time.h"

int main(voic))

TimeOfDay time1, time2;

int hours = 19, minutes = 15; time1,setTime(7,35); time2.setTime(hours, minutes); cout « "Первое время: "; time1.displayMilitarylimeC); cout « endl « "Первое время: timel.displayTimeO;

cout « e n d l « "Второе время: " time2.displayMilitaryTime(); cout « endl « "Второе время: time2.tJisplayTime(); return(O);

/ /

экземпляр класса

/ /

целочисленные

переменные

/ /

инициализация

объектов

// сообщение первому объекту

// сообщение второму объекту

Переменные timel и time2 имеют тип TimeOfDay. Они определены аналогично переменным встроенного типа. По сундеству, определение класса расширяет до­ ступный в C++ набор типов и добавляет в него наш тип TimeOfDay. Несмотря на этот скромный вклад, классы — весьма мош,ное средство в C+ + . Они открывают огромные возможности и делают язык более мондным. Связь между кодом клиента и кодом класса показана на рис. 2.6. Когда клиенту нужно обращаться к закрытым частям класса (для установки значения времени или его получения), он не обраща­ ется непосредственно к данным (пунктирная линия на рисунке), а вызывает функ­ ции-члены setTimeO, displayTimeO, displayMilitaryTimeO, сплошные линии на рис. 2.6. Эти функции обращаются к закрытым данным класса от имени клиента.

 

ri^?i

 

 

 

It//

<<f'

 

 

 

 

 

setTime

 

hours

 

 

 

 

 

1

 

minutes

 

 

 

 

 

 

dIsplayTime

 

/

^^^-«>^

^

1

 

/

 

displayMilitaryTime

Использование

Зсжрытые

 

 

i к

жные

 

 

 

общедоступных функций

тграница класса

 

допускается

Общедоступные

 

операции

 

Рис. 2.6. Код клиента

использует функции

дост,упа,

а не обращает^ся к данным непосредственно

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

58

Часть i • Введение в прогромлдит

Первое время: 7:35 Первое время: 7:35А.М. Второе время: 19:15 Второе время: 7:15А.М.

Рис. 2J. Вывод функций доступа TimeOfDay

Если целевым объектом является time1, то выводятся поля данных hours и minutes объекта time1. Если целевой объект — time2, то выводятся значения из time2. Вывод программы показан на рис. 2.7.

В отличие от других, показанных ранее функций, функции-члены нельзя вызывать просто по имени (с указанием аргументов, если необходимо):

displayTime();

//синтаксическая ошибка

Какое время здесь нужно вывести? Из какого объекта? Ничего этого не задает­ ся, и компилятор не знает, что программист имеет в виду. Вот почему такой вызов функции даст ошибку. Нужно указать цель сообщения (получателя), но данная цель должна быть того же типа, что и вызываемая компонентная функция. Пере­ менную другого типа в качестве цели использовать нельзя. Например, следующая запись некорректна:

hours.displayTimeO; / / синтаксическая ошибка

Переменная hours имеет тип int. С целочисленными выражениями можно выполнять такие операции, как вычитание, сложение и пр., но не операцию displayTimeO. Именно поэтому оператор указывает на ошибку. Цель должна иметь верный тип.

Расширение языка C++ с помощью классов поддерживается такими средства­ ми, как композиция классов и наследование. Можно было бы дать простые приме­ ры композиции и наследования, но это сделало бы введение слишком длинным. Оно и без того достигло цели, познакомив читателя с C+ + . Это вполне позволяет писать небольшие программы — не очень элегантные, но надежные и работаю­ щие. Еще важнее, что первое короткое знакомство послужит основой для даль­ нейшего изучения C+ + .

Прежде чем приступать к более подробному ознакомлению с C+ + , следует рассмотреть вопросы компоновки и выполнения программ C+ + .

Применение инструментальных средств разработки программного обеспечения

Как уже упоминалось, для создания исходного кода C++ используется тексто­ вый редактор. Полученный исходный код обрабатывается компилятором C+ + . В случае синтаксических ошибок генерируются соответствующие сообщения, а если ошибок нет, можно запускать программу. В этом разделе рассматриваются более развитые инструментальные средства, которые можно применять для создания и выполнения программ C+ + , поясняется роль каждого инструментального средства и способы их наиболее эффективного использования. Естественно, спе­ цифика зависит от того, поддерживает ли этог инструментарий режим командной строки (как, например, компилятор GNU в UNIX) или представляет собой интег­ рированную среду разработки (как Microsoft IDE в Windows).

Многие среды разработки требуют (или предполагают), что в дополнение к со­ хранению исходных файлов добавляются к проекту файлы (проект может содер­ жать несколько исходных файлов). Файлы проекта, интерпретируемые как единое целое, можно применять для сбора информации о программе, которая пригодится для ее анализа и отладки. Кроме того, поставщики интегрированных сред разра­ ботки ПО часто предусматривают возможность генерации многофайловой базо­ вой структуры, позволяющей добавлять код приложения или класса к "скелетам" функций и классов, генерируемых самим инструментарием. Для опытного про­ граммиста это может быть очень полезным, но при изучении языка будет только мешать. Вместо того чтобы сосредоточиться на конкретных темах языка, начинаю­ щий программист будет изучать сгенерированный для него исходный код, а в нем могут применяться незнакомые ему средства.

Глава 2 # Быстрый старт: к р о т к и й обзор С-^-^

\

59

|

С о в е т у е м Чтобы избежать лишних сложностей и упростить задачи управления проектом, во время экспериментирования с языком полезно придерживаться однофайловых программ. Некоторые интегрированные среды создают проект по умолчанию, хотите вы того, или нет. Остается с этим смириться, но при изучении C++ лучше избегать использования сгенерированного средой программного кода, что упростит вам жизнь.

Полезно сохранять свои файлы после редактирования и перед тестированием программы. Если не сделать этого, а программа содержит ошибки этапа выполне­ ния, способные привести к аварийному завершению работы системы, то введен­ ные изменения будут потеряны. Сегодня лишь немногие записывают операторы на бумаге, прежде чем набрать их с клавиатуры, и в результате аварийного заверше­ ния системы можно потерять значительную часть работы. Сохранение файла на диске (под другим именем) перед компиляцией сведет опасность к минимуму. Некоторые программы-редакторы выводят на экран информацию о статусе файла, которая может оказаться полезной. Если код изменен, а файл еш,е не сохранен, редактор помечает имя файла в окне редактирования звездочкой. После сохране­ ния файла звездочка (или сообш^ение) исчезает.

Другие интегрированные среды разработки не доверяют здравому смыслу программиста и сохраняют исходный файл, как только получают команду на компиляцию, даже если изменения носят "экспериментальный" характер и могут подлежать отмене.

Если исходный файл не содержит синтаксических ошибок, компилятор генери­ рует объектный файл, имя которого совпадает с именем исходного файла. В зави­ симости от системы, имя объектного файла получает расширение . о или . obj. Если код содержит синтаксические ошибки, объектный файл не создается. Компилятор выводит сообнления об ошибках: показывает, на какой строке они найдены и что это за ошибки, В старых инструментальных средствах приходилось считать строки исходного кода или полагаться на номера строк, выводимые редактором. Новый инструментарий устраняет необходимость трассировки номеров строк, хотя и указывается, в какой строке произошла ошибка. Достаточно ш^елкнуть мышью сообш^ение об ошибке, и курсор позиционируется в окне редактора в том месте, где эта ошибка найдена. Очень полезное средство.

Если освоиться с языком, то сообщения об ошибках станут ясными и полезны­ ми. По крайней мере, некоторые из них. До той же поры лучше полагаться на пра­ вило: никогда не понимать сообш,ения компилятора буквально, не анализировать их, или, по крайней мере, остановиться при первой же трудности. Не упорствуйте. Не стоит.

Как минимум 90% сообш^ений об ошибках ничего не говорят начинаюидим. Есть ли исключения? Конечно. Около 10% сооби;ений об ошибках вполне понят­ ны, но это как раз те случаи, когда сообш.ение становится понятным, если посмот­ реть на строку исходного кода, где содержится ошибка. Вот почему лучше не тратить время, энергию и нервы на сообидения об ошибках. Если ближе позна­ комиться с их терминологией, они будут более полезны, подскажут, в каком направлении двигаться. На первых же этапах изучения языка чтение сообш^ений об ошибках принесет больше вреда, чем пользы.

Причина в том, что разработчики компилятора хотели дать программистам наиболее полезную информацию и детальный анализ ситуации. Не удивительно, что в их сообш^ениях используется сложная терминология C++, незнакомая новичку. Порой бывает так, что проблема вызвана ошибками совсем иного рода, и у компилятора нет ни времени, ни "знаний" на детальный анализ и корректное указание причины ошибки. Таким образом, сообш,ение с техническими и специфи­ ческими терминами часто имеет очень мало общего с фактической ошибкой.

Соседние файлы в предмете Программирование на C++