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



бет13/15
Дата16.07.2016
өлшемі1.19 Mb.
#204086
түріИнформация
1   ...   7   8   9   10   11   12   13   14   15

{

public :

enum ErrorCode {

NO_MEMORY,

DATABASE_ERROR,

INTERNAL_ERROR,

ILLEGAL_VALUE

};

Exception(ErrorCode errorKind,

const String&errMessage);

ErrorCode GetErrorKind(void )const

{return kind;};

const String&GetErrorMessage(void )const

{return msg;};

private :

ErrorCode kind;

String msg;

};

Создание исключительной ситуации будет выглядеть следующим образом:



if (connect(serverName)==false )

throw Exception(Exception::DATABASE_ERROR,

serverName);

А проверка на исключительную ситуацию так:



try {

...

}catch (Exception&e){

cerr <<"Произошла ошибка "<

<<"Дополнительная информация:"

<

}

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



throw AnotherException;

то блок catch будет пропущен: он ожидает только исключительных ситуаций типа Exception . Это особенно существенно при сопряжении нескольких различных программ и библиотек – каждый набор классов отвечает только за собственные ошибки.

В данном случае код ошибки записывается в объекте типа Exception . Если в одном блоке catch ожидается несколько разных исключительных ситуаций, и для них необходима разная обработка, то в программе придется анализировать код ошибки с помощью операторов if или switch .

try {

...

}catch (Exception&e){

cerr <<"Произошла ошибка "<

<<"Дополнительная информация:"

<

if (e.GetErrorKind()==Exception::NO_MEMORY ||

e.GetErrorKind()==

Exception::INTERNAL_ERROR)

throw ;

else if (e.GetErrorKind()==

Exception::DATABASE_ERROR)

return TRY_AGAIN;

else if (e.GetErrorKind()==

Exception::ILLEGAL_VALUE)

return NEXT_VALUE;

}

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


Рис. 16.1.  Пример иерархии классов для представления исключительных ситуаций.

В приведенной на рисунке 16.1 структуре классов все исключительные ситуации делятся на ситуации, связанные с работой базы данных (класс DatabaseException ), и внутренние ошибки программы (класс InternalException ). В свою очередь, ошибки базы данных бывают двух типов: ошибки соединения (представленные классом ConnectDbException ) и ошибки чтения (ReadDbException ). Внутренние исключительные ситуации и разделены на нехватку памяти (NoMemoryException )и недопустимые значения (IllegalValException ).

Теперь блок catch может быть записан в следующем виде:

try {

}catch (ConnectDbException&e ){

//обработка ошибки соединения с базой данных

}catch (ReadDbException&e){

//обработка ошибок чтения из базы данных

}catch (DatabaseException&e){

//обработка других ошибок базы данных

}catch (NoMemoryException&e){

//обработка нехватки памяти

}catch (…){

//обработка всех остальных исключительных

//ситуаций

}

Напомним, что когда при проверке исключительной ситуации на соответствие аргументу оператора catch проверка идет последовательно до тех пор, пока не найдется подходящий тип. Поэтому, например, нельзя ставить catch для класса DatabaseException впереди catch для класса ConnectDbException – исключительная ситуация типа ConnectDbException совместима с классом DatabaseException (это ее базовый класс), и она будет обработана в catch для DatabaseException и не дойдет до блока с ConnectDbException .

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

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



class Database

{

public :

Open(const char*serverName)

throw ConnectDbException;

};

Такое описание говорит о том, что метод Open класса Database может создать исключительную ситуацию типа ConnectDbException . Соответственно, при использовании этого метода желательно предусмотреть обработку возможной исключительной ситуации.

В заключение приведем несколько рекомендаций по использованию исключительных ситуаций.


  1. При возникновении исключительной ситуации остаток функции или метода не выполняется. Более того, при обработке ее не всегда известно, где именно возникла исключительная ситуация. Поэтому прежде чем выполнить оператор throw , освободите ресурсы, зарезервированные в текущей функции. Например, если какой-либо объект был создан с помощью new , необходимо явно вызвать для него delete .

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

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

17 Bвод-вывод


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

В языке Си++ нет особых операторов для ввода или вывода данных. Вместо этого имеется набор классов, стандартно поставляемых вместе с компилятором, которые и реализуют основные операции ввода-вывода.

Причиной является как слишком большое разнообразие операций ввода и вывода в разных операционных системах, особенно графических, так и возможность определения новых типов данных в языке Си++. Вывод даже простой строки текста в MS DOS, MS Windows и в X Window настолько различен, что пытаться придумать общие для всех них операторы было бы слишком негибко и на самом деле затруднило бы работу. Что же говорить о классах, определенных программистом, у которых могут быть совершенно специфические требования к их вводу-выводу.

Библиотека классов для ввода-вывода решает две задачи. Во-первых, она обеспечивает эффективный ввод-вывод всех встроенных типов и простое, но тем не менее гибкое, определение операций ввода-вывода для новых типов, разрабатываемых программистом. Во-вторых, сама библиотека позволяет при необходимости развивать её и модифицировать.

В нашу задачу не входит описание программирования в графических системах типа MS Windows. Мы будем рассматривать операции ввода-вывода файлов и алфавитно-цифровой вывод на терминал, который будет работать на консольном окне MS Windows, MS DOS или Unix.

17.1 Потоки

Механизм для ввода-вывода в Си++ называется потоком. Название произошло от того,что информация вводится и выводится в виде потока байтов – символ за символом.

Класс istream реализует поток ввода, класс ostream – поток вывода. Эти классы определены в файле заголовков iostream.h. Библиотека потоков ввода-вывода определяет три глобальных объекта: cout,cin и cerr. cout называется стандартным выводом, cin – стандартным вводом, cerr – стандартным потоком сообщений об ошибках. cout и cerr выводят на терминал и принадлежат к классу ostream, cin имеет тип istream и вводит с терминала. Разница между cout и cerr существенна в Unix – они используют разные дескрипторы для вывода. В других системах они существуют больше для совместимости.

Вывод осуществляется с помощью операции >>, ввод с помощью операции <<. Выражение

cout << "Пример вывода: " << 34;

напечатает на терминале строку "Пример вывода", за которым будет выведено число 34. Выражение



int x;

cin >> x;

введет целое число с терминала в переменную x. (Разумеется, для того, чтобы ввод произошел, на терминале нужно напечатать какое-либо число и нажать клавишу возврат каретки.)

17.2 Операции << и >> для потоков

В классах iostream операции >> и << определены для всех встроенных типов языка Си++ и для строк (тип char*). Если мы хотим использовать такую же запись для ввода и вывода других классов, определенных в программе, для них нужно определить эти операции.



class String

{

public:

friend ostream& operator<<(ostream& os,

const String& s);

friend istream& operator>>(istream& is,

String& s);

private:

char* str;

int length;

};

ostream& operator<<(ostream& os,

const String& s)

{

os << s.str;

return os;

}

istream& operator>>(istream& is,

String& s)

{

// предполагается, что строк длиной более

// 1024 байтов не будет

char tmp[1024];

is >> tmp;

if (str != 0) {

delete [] str;

}

length = strlen(tmp);

str = new char[length + 1];

if (str == 0) {

// обработка ошибок

length = 0;

return is;

}

strcpy(str, tmp);

return is;

}

Как показано в примере класса String, операция <<, во-первых, является не методом класса String, а отдельной функцией. Она и не может быть методом класса String, поскольку ее правый операнд – объект класса ostream. С точки зрения записи, она могла бы быть методом класса ostream, но тогда с добавлением нового класса приходилось бы модифицировать класс ostream, что невозможно – каждый бы модифицировал стандартные классы, поставляемые вместе с компилятором. Когда же операция << реализована как отдельная функция, достаточно в каждом новом классе определить ее, и можно использовать запись:



String x;

. . .

cout << "this is a string: " << x;

Во-вторых, операция << возвращает в качестве результата ссылку на поток вывода. Это позволяет использовать ее в выражениях типа приведенного выше, соединяющих несколько операций вывода в одно выражение.

Аналогично реализована операция ввода. Для класса istream она определена для всех встроенных типов языка Си++ и указателей на строку символов. Если необходимо, чтобы класс, определенный в программе, позволял ввод из потока, для него нужно определить операцию >> в качестве функции friend.

17.3 Манипуляторы и форматирование ввода-вывода

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

Манипуляторы – это объекты особых типов, которые управляют тем, как ostream или istream обрабатывают последующие аргументы. Некоторые манипуляторы могут также выводить или вводить специальные символы.

С одним манипулятором мы уже сталкивались, это endl. Он вызывает вывод символа новой строки. Другие манипуляторы позволяют задавать формат вывода чисел:

endl

при выводе перейти на новую строку;

ends

вывести нулевой байт (признак конца строки символов);

flush

немедленно вывести и опустошить все промежуточные буферы;

dec

выводить числа в десятичной системе (действует по умолчанию);

oct

выводить числа в восьмеричной системе;

hex

выводить числа в шестнадцатеричной системе счисления;

setw (int n)

установить ширину поля вывода в n символов (n – целое число);

setfill(int n)

установить символ-заполнитель; этим символом выводимое значение будет дополняться до необходимой ширины;

setprecision(int n)

установить количество цифр после запятой при выводе вещественных чисел;

setbase(int n)

установить систему счисления для вывода чисел; n может принимать значения 0, 2, 8, 10, 16, причем 0 означает систему счисления по умолчанию, т.е. 10.



Достарыңызбен бөлісу:
1   ...   7   8   9   10   11   12   13   14   15




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет