Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции по Программированию.doc
Скачиваний:
49
Добавлен:
11.02.2015
Размер:
1.22 Mб
Скачать

Пример 2. Алгоритмы и функциональные объекты

#include <functional.h>

#include <deque.h>

#include <algorithm.h>

#include <iostream.h>

#include <iomanip.h>

//Create a new function object from unary_function

template<class Arg>

class div2 : public unary_function<Arg, Arg>

{

public:

Arg operator()(const Arg& arg)

{ return arg/2; }

};

int main(int argc, char* argv[])

{

//Initialize a deque with an array of ints

int arr1[5] = {99, 264, 126, 330, 132};

int arr2[5] = {280, 105, 220, 84, 210};

deque<int> d1(arr1+0, arr1+5), d2(arr2+0, arr2+5);

deque<int>::iterator i1;

for(i1 = d1.begin(); i1 != d1.end(); i1++)

cout << setw(6) << *i1 << " ";

cout << endl;

for(i1 = d2.begin(); i1 != d2.end(); i1++)

cout << setw(6) << *i1 << " ";

cout << endl;

transform(d1.begin(), d1.end(), d2.begin(), d1.begin(), multiplies<int>());

cout << "Multiplies result" << endl;

for(i1 = d1.begin(); i1 != d1.end(); i1++)

cout << setw(6) << *i1 << " ";

cout << endl;

cout << "Div result" << endl;

transform(d2.begin(), d2.end(), d2.begin(), div2<int>());

for(i1 = d2.begin(); i1 != d2.end(); i1++)

cout << setw(6) << *i1 << " ";

cout << endl;

return 0;

}

The transform algorithm has two forms. The first form applies unary operation op to each element of the range [first, last), and sends the result to the output iterator result. If the output iterator (result) is the same as the input iterator used to traverse the range, transform performs its transformation in place.

The second form of transform applies a binary operation, binary_op, to corresponding elements in the range [first1, last1) and the range that begins at first2, and sends the result to result. For example, transform can be used to add corresponding elements in two sequences, and store the set of sums in a third. The algorithm assumes, but does not check, that the second sequence has at least as many elements as the first sequence. Note that the output iterator result can be a third sequence, or either of the two input sequences.

transform (InputIterator first, InputIterator last,

OutputIterator result, UnaryOperation op);

transform (InputIterator1 first1, InputIterator1 last1,

InputIterator2 first2, OutputIterator result,

BinaryOperation binary_op);

Функциональный объект unary_functionопределен след. образом:

#include <functional>

template <class Arg, class Result>

struct unary_function{

typedef Arg argument_type;

typedef Result result_type;

};

Лекция 15. Обработка исключительных ситуаций

Существует категория ошибок, которые не способны выявить препроцессоры, трансляторы и программы сборки. К их числу относятся так называемые ошибки времени выполнения. Эти ошибки проявляются в ходе выполнения программы.

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

Различают синхронные и асинхронные исключительные ситуации.

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

Асинхронные исключительные ситуации непосредственно не связаны с выполнением программы. Их причинами могут служить аппаратно возбуждаемые прерывания (например, сигналы от таймера), сообщения, поступающие от внешних устройств или даже от локальной сети.

Реакция на исключительную ситуацию называется исключением.

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

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

Существует целый ряд проблем, связанных с подобным способом организации программного кода. Рассмотрим некоторые из них.

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

С ростом числа вариантов возвращаемых значений становится все более актуальной проблема разделения «положительных» и «отрицательных» ответов.

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

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

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

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

Возбуждение (или генерация) исключения обеспечивается операцией throw. Оператор возбуждения исключения является полноправным оператором и в принципе может располагаться в любом месте программы. Его выполнение за пределами контролируемого блока приводит к завершению процесса выполнения программы.

Операция throw может применяться в сочетании с операндом, каковым может оказаться выражение произвольного типа и значения.

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

throw 1;

throw "Это сообщение об исключении…";

throw 2*2*fVal;

throw (int)5.5;

throw (ComplexType)125.96;

/*

Разумеется, если определен соответствующий конструктор

преобразования или функция приведения.

*/

В качестве исключения может быть использовано значение указателя. Допускаются исключения и такого вида:

throw NULL;

throw (void *) &iVal;

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

try

{

Оператор;

Оператор;

Оператор;

}

СписокРеакций

Это блок операторов, то есть, составной оператор. Его место — тело функции. Этот оператор может входить в любой другой блок операторов.

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

За ним следует, по крайней мере, один элемент списка реакций со своим блоком перехвата. Каждый блок перехвата начинается с заголовка — ключевого слова catch, за которым в круглых скобках располагается объявление ситуации. Синтаксис объявления ситуации напоминает объявление параметра в прототипе функции. Catch-блок содержит код, предназначенный для перехвата исключений.

Вернемся к примеру с классом CStack. Методpopвозвращал значение 0 в случае попытки выталкивания из пустого стека. В случае успеха значение возвращалось через параметр функции. Более естественен способ, когда функция возвращает необходимое значение, а при попытке выталкивания из пустого стека возбуждается исключение. Операцияthrowбудет возвращать экземпляр класса StackException,

Файл CSstak.h

…………

#include <string.h>

class StackException{

public:

char mes[256];

StackException(char* m) {strcpy(mes, m);}

};

template <class Type>

class CStack {

………………………

Type pop(void);

………………………

};

template <class Type>

………………………

Type CStack<Type>::pop(void)

{

if (isempty())

throw StackException("Stack is empty");

return data[--top];

}

………………………

Тестирующая программа:

#include <iostream.h>

#include "CStack.h"

int main(int argc, char* argv[])

{

CStack <int> stack(10);

int a;

for (int i=0;i<5;i++)

stack.push(i);

try

{

for (int i=0;i<6;i++)

{

a=stack.pop();

cout<<a<<endl;

}

}

catch ( StackException e)

{

cout<<e.mes<<endl;

}

cin>>a;

return 0;

}

Программа выводит следующие данные:

4

3

2

1

0

Stack is empty

catch-блок видаcatch(…) перехватывает все виды исключений (ранее не перехваченные).

catch (...)

{

cout<<"unknown exception"<<endl;

}