Лекции (1129116), страница 24

Файл №1129116 Лекции (Лекции) 24 страницаЛекции (1129116) страница 242019-05-11СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 24)

реакция_на_оставшиеся_исключения

end

Есть специальная форма блока, в которой также есть оператор finally, но мешать except и finally нельзя:

try

finally

end

Точно также, компилятор требует, чтобы тип исключения был выведен из типа Exception (если это не тип Exception). Естественно, что соответствующие типы данных должны быть описаны в зоне видимости.

3. Возникновение.

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

Мы будем говорить об исключениях, которые возбуждает сам программист. В языке Ада исключение возбуждается с помощью оператора raise имя_исключения. В С++ (и в Java) для этой цели выбрано несколько грубоватое слово throw, потому что слово raise слишком занятое слово в библиотеках обработки сигналов. Синтаксис возбуждения таков: throw выражение. Заметим, что в Аде исключение идентифицируется именем, а в С++ и в Java исключение определяется типом. Например, throw 5 означает возбуждение исключения для типа int. Сложнее дело обстоит с классами. Пусть есть некий класс MyException.

if (что-то_плохо) throw MyException(…);

Рекомендуется явный вызов конструктора, в который нужно передавать необходимые параметры. Это хороший стиль, именно с точки зрения обработки исключительных ситуаций. Если, например, объявить где-то выше переменную класса MyException и вставить ее в оператор throw, то возникает вопрос: как компилятор будет обрабатывать этот оператор? Может сложиться такая ситуация, что в тот момент, когда найдется обработчик исключения, объект уже перестанет существовать. Особенно нужно боятся передачи указателя на исключения. Представим себе ситуацию, когда мы возбуждаем такое исключение: throw NoMemoryException. В какой памяти тогда размещать это исключение? В динамической памяти уже нельзя. С другой стороны, в языке Delphi, все классы по определению размещаются в динамической памяти. Разработчики соответствующей run-time системы предусмотрели этот случай, и на самом деле, система всегда держит некоторый объем динамической памяти "про запас", как раз для вызова исключений. Аналогичная ситуация и в языке Java.

4. Распространение и обработка.

Если вернуться к истории, то традиционным способом обработки был т.н. ремонт на месте, как в Basic:

on ситуация оператор resume

Обычно, после обработки исключения выполнение программы возобновлялось с той точки, в которой она была прервана. Понятно, что такой ремонт полезен, только если повреждения не слишком велики. Такой подход плох тем, что действия по восстановлению локализованы достаточно близко к точке возникновения ошибки. Как правило, в точке возникновения ошибки не хватает информации, чтобы оценить серьезность этой ошибки и адекватным образом ее исправить. Если файл не открывается, то что делать? Пытаться ли снова открыть файл? Или заново создать этот файл? В точке возникновения ошибки информации для исправления не хватает.

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

try {

f();

g();

} catch(int) {…}

catch(char*) {…}

В данном примере никакого throw в данном try-блоке нет, но с динамической точки зрения все иначе. Например функция f() может выглядеть следующим образом:

void f(){

if(…) throw 1;

if(…) throw "help";

}

В этой функции все наоборот: throw – есть, а try-блока – нет. Исключение отлавливается в объемлющем try-блоке. Если возникает авария, то происходит т.н. процесс распространения исключения. Система управления исключениями отыскивает первый try-блок, который динамически объемлет соответствующее возникновение исключения. Далее происходит поиск соответствующей ловушки в найденном try-блоке сверху вниз. Если в данном try-блоке ловушка для данного исключения не найдена, то ищется следующий динамически объемлющий try-блок. Это и называется процессом распространения исключения. Аналогичный порядок и в языке Ада. Распространение происходит либо до самого верха, тогда возникает ситуация необработанного исключения, либо где-то по стеку найдется нужная ловушка, и исключение будет обработано (тогда распространение прекращается). Если исключение обработано, то ошибка считается исправленной. С какой точки возобновлять выполнение программы? Очевидно, сразу за тем try-блоком, в котором нашлась нужная ловушка.

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

При свертке стека должны вызываться деструкторы существующих объектов. Поэтому есть смысл обработать исключение прямо в том статическом блоке, где оно возникло, а затем перевозбудить его для дальнейшего распространения. Для этого в соответствующем обработчике используются операторы raise (в Аде и Delphi) и throw (в С++ и Java) без параметров.

Как быть с неперехваченными исключениями? В Delphi есть процедура Messages(), которая перехватывает все неперехваченные исключения и выдает информацию о них, и после этого они считаются перехваченными. В языке Java в этом случае происходит аварийная остановка программы, и виртуальная машина выдает всю статистику по свертыванию стека.

Лекция 20

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

B1->B2->B3->…

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

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

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

C++

C++ обязывает программиста программировать аккуратно.

try {

p= new T;

throw | f( ); // здесь вызывается исключение

} catch ( );

Возникает вопрос, что делать с указателем p, если внутри блока исключение не обрабатывается и блок покидается? C++ отвечает так: если есть выделение ресурсов, и вы планируете, что исключение может распространиться во вне от блока, то вы должны чистить ресурсы, хотя бы частично:

try {

} catch ( ) {if (p) delete p; throw; } //мы убрали p и «продолжили» исключение дальше.

Либо избегать в коде подобного динамического создания объектов, а размещать их локально:

T t(…);

в этом случае во время свертки стека будет выполнен деструктор для локальных объектов (t).

Это очень хорошая техника программирования. В частности, в MFC стандартная техника выглядит так:

void OnClick(…) {

CMyDialog dlg;

dlg.ShowModal;

}

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

Java. Delphi.

Программисту на Java эти проблемы не страшны – так как там есть динамический сборщик мусора, и следить надо, пожалуй, только за открытиями/закрытиями файлов.

Но все равно при создании ресурса:

new имя_класса

может же быть открыт файл, порт, tcp/ip порт, вставка освобождения которых в финализатор будет немного некорректна, так как реально они будут освобождены, когда будет освобождена память. Момент наступления этого события – неизвестен. Поэтому все учебники по Java рекомендуют предусматривать методы очистки ресурсов. Отличие от C++ здесь в том, что программист на C++ всегда знает, когда будет выполнен деструктор.

В Delphi мы должны сами вызывать соответствующий конструктор/деструктор:

p:=T.Create(…);

p.Free

программист должен сделать это явно, так как в Delphi нет такой интеграции деструкторов с языком, как в C++ (а в Java нет и самих деструкторов).

В этих языках есть конструкция finally, которая призвана помогать в данной ситуации:

try

[<block> Exit ]

finally

<code>

end;

это означает, что при завершении <block> (по Exit, по завершению блока или по исключению) будет выполнен <code>, прямое назначение которого – чистка ресурсов.

Все пособия под Delphi рекомендуют следующую схему:

procedure P;

var p: T;

begin

p:=T.Create( );

try

<что-то делать с p>

finally

p.Free;

end;

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

В Java ситуация такая же. Правда, немного другой синтаксис:

try {

} finally {<code>};

Тут, правда, возникает соблазн для программистов, который не возникает в C++. Когда мы говорили про исключения, то там есть два подхода: что это некая аварийная ситуация или еще одна достаточно хитрая конструкция.

Представим себе ситуацию, когда у нас есть несколько динамически вложенных процедур:

p1->p2->p3

пусть мы хотим выползти из процедуры p3 и попасть сразу в p1. Первое, что приходит на ум – сделать исключение, которое ловится в p1.

Например, p1 может выглядеть так:

p1: try {

for (int i=0; i<N; i++) {

p2();

} catch (Exit_Ex) { };

В p2 есть тоже какой-то цикл, вызывающий p3.

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

p3: for (int k=0; …) {

throw (Exit_Ex);

};

причем Exit_Ex может нести в себе какую-то содержательную информацию.

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

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

Итак, мы обсудили те ситуаций, которые мы обработали. А что же будет, если какое-то исключение не обрабатывается? Понятно, что исключение всплывает на самый верхний уровень. А что дальше? В Ada существует некий стандартный обработчик, который осмысленно (с осмысленной диагностикой) прибивает программу. В Delphi подход менее жесткий, там всегда существует «обработчик последней надежды», который показывает сообщение об ошибке и продолжает работать дальше. Программа на Java гарантирует прекращение выполнение с сообщением, где представлена полная трасса свертки стека, в результате, мы можем сделать подробный анализ и все исправить.

В C++ априорной схемы не существует, но у программиста есть возможность установить свои функции:

typedef void PVF ( );

PVF *set_unexpected(PVF * new ul_handle);

То есть мы можем сами установить свой обработчик. Определить, например, класс:

class SUE {

Характеристики

Тип файла
Документ
Размер
1,12 Mb
Материал
Тип материала
Высшее учебное заведение

Список файлов лекций

Свежие статьи
Популярно сейчас
А знаете ли Вы, что из года в год задания практически не меняются? Математика, преподаваемая в учебных заведениях, никак не менялась минимум 30 лет. Найдите нужный учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
6314
Авторов
на СтудИзбе
312
Средний доход
с одного платного файла
Обучение Подробнее