Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
КП - 2 часть - Лекция 2. Исключительные ситуаци...docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
342.41 Кб
Скачать

1.3 Ловля исключений

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

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

Пример:

#include "stdafx.h"

#include <stdio.h>

#include <exception>

#include <iostream>

#include "Person.h"

using namespace std;

int main()

{

try // try добавили, поскольку люди плохо понимали без этого ключевого слова этот блок.

{

// много кода,

// который может вызвать исключения.

Person person("Travolta", -2);

}

catch (const exception& exception) // обработчик исключения

{

cerr << "Error: " << exception.what() << "\n";

return 1;

}

return 0;

}

Как видите, try {} catch {} выходит на сцену. Если где-то в теле try произошло исключение, то выполнится блок catch, исключение можно будет поместить в некоторую переменную. Также у класса исключение есть конструктор, принимающий на вход строчку с ошибкой, метод what(), который эту строчку возвращает.

Что можно делать в блоке catch? Выйти из программы, обратиться к a, b.

void Test(int a)

{

int b;

try

{

int c;

}

catch (const std::exception& ex)

{

}

}

Итак,

Исключения ловятся только по (чаще всего константной) ссылке. Их можно ловить и другими способами, но это будет плохо по разным причинам. Почему исключения ловят по константной ссылке? Если их ловить по значению (catch (exception e)), то будет создаваться копия, поскольку обработчик исключения как функция работает. А если пришел экземпляр наследника? Произойдет усечение (slicing), копия будет создана с использованием конструктора копирования базового класса, в который придет наследник. Конструктор копирования исключения также потенциально может кинуть исключение.

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

Блоков catch может быть несколько. Пример:

#include "stdafx.h"

#include <stdio.h>

#include <exception>

#include <iostream>

#include "Person.h"

using namespace std;

int main()

{

try

{

// много кода,

// который может вызвать исключения.

Person person("Travolta", -2);

}

catch (const bad_alloc& exception) // 1

{

cerr << "There's not enough memory! Error: " << exception.what() << "\n";

return 1;

}

catch (const exception& exception) // 2

{

cerr << "Error: " << exception.what() << "\n";

return 1;

}

catch (int errorCode)

{

cerr << "Error: " << errorCode << "\n";

return 1;

}

catch (...) // 4

{

cerr << "Unknown error occured\n";

return 1;

}

return 0;

}

Если их много, то как решается, какой блок вызвать? Происходит проверка с первого по последний блок catch, какому отлавливаемому типу соответствует тип брошенного исключения. Например, если бросили bad_alloc, публично унаследованный от exception, то ему соответствуют 3 блока catch: 1, 2 и 4. Отработает первый, поскольку он расположен выше. Блок catch 4 словит все виды исключений. Но поскольку конкретный тип не известен, то и данных никаких о том, что произошло, нельзя будет получить. На практике используют подход, как в первоначальном примере.

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

Пример:

#include "stdafx.h"

#include <stdio.h>

#include <exception>

#include <iostream>

#include <Windows.h>

using namespace std;

void SendEmail()

{

throw std::exception("Cannot send e-mail. Internet connection is missing.");

}

void SendEmailFailover()

{

const int triesCount = 10;

const int waitBetweenTriesMsec = 100;

for (int i = triesCount; i > 0; i--)

{

try

{

SendEmail();

break; // выходим, если получилось.

}

catch (...) // мы не можем обработать исключение,

// но нам важно почистить память, выделенную динамически.

{

if (i > 1)

{

Sleep(waitBetweenTriesMsec);

}

else

{

throw; // последняя попытка. Пробрасываем исключение.

}

}

}

}

int main()

{

try

{

SendEmailFailover();

}

catch (const std::exception& e)

{

cerr << e.what() << endl;

system("pause");

return 1;

}

system("pause");

return 0;

}