- •2 Часть
- •1 Приближение
- •1.1 Бросание(генерация) исключений
- •1.2 Пример 1. Поддержка инкапсуляции
- •1.3 Ловля исключений
- •2 Приближение
- •2.1 Развитие способов обработки ошибок
- •1. Хочется, чтобы приложение как можно быстрее упало и не успело сделать ничего плохого.
- •2. Хочется, чтобы перед своей кончиной приложение напечатало нечто, позволяющее понять, что не так.
- •2.2 Преимущества использования исключений
- •2.4 Пример 2. Исключение и память, проброс
- •3.3 Отладка и исключения в Visual Studio. Stack trace
- •3.4 Как развивался концепт с исключениями дальше?
- •3.4.2 Сборщик мусора
- •3.5 Общие замечания по созданию исключительных ситуаций
- •3.6 Код всегда должен писаться, чтобы быть устойчивым к исключениям [1]
1. Хочется, чтобы приложение как можно быстрее упало и не успело сделать ничего плохого.
#include "stdafx.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
FILE* OpenFileRead(const char* fileName)
{
FILE *ptr_file = fopen(fileName, "r");
if (!ptr_file)
throw 13;
return ptr_file;
}
int main()
{
FILE *ptr_file = OpenFileRead("output.txt");
// ...
fclose(ptr_file);
return 0;
}
throw завершит приложение сразу же. Ни одна инструкция больше не выполнится.
2. Хочется, чтобы перед своей кончиной приложение напечатало нечто, позволяющее понять, что не так.
// Вариант с кодом ошибки
#include "stdafx.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <iostream>
using namespace std;
FILE* OpenFileRead(const char* fileName)
{
FILE *ptr_file = fopen(fileName, "r");
if (!ptr_file)
throw 13;
return ptr_file;
}
int main()
{
try
{
FILE *ptr_file = OpenFileRead("output.txt");
// ...
fclose(ptr_file);
}
catch (int errorCode)
{
cerr << "Приложение упало с кодом ошибки " << errorCode
<< ". Приносим извинения за доставленные неудобства. Обратитесь в слжубу поддержки по телефону 1234544.\n";
return 1;
}
return 0;
}
Как видите, на самом верхнем уровне добавился блок по отлавливанию исключений. В качестве исключений можно бросать какие-то данные, затем их ловить и перед завершением приложения показывать пользователю. Код при этом остается компактным.
// Вариант с текстовым сообщением.
#include "stdafx.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <iostream>
using namespace std;
FILE* OpenFileRead(const char* fileName)
{
FILE *ptr_file = fopen(fileName, "r");
if (!ptr_file)
throw "Failed to open input file";
return ptr_file;
}
int main()
{
try
{
FILE *ptr_file = OpenFileRead("output.txt");
// ...
fclose(ptr_file);
}
catch (const char* errorMessage)
{
cerr << "Ошибка: " << errorMessage << "\n";
return 1;
}
return 0;
}
В качестве исключения стали бросать строки и другие данные, которые могли легко идентифицировать ошибку.
3. Хочется, чтобы в качестве исключения был отдельный тип данных. В котором было бы некоторое строковое представление произошедшей проблемы и любая дополнительная информация, которая поможет ее идентифицировать.
#include "stdafx.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include <exception>
#include <string>
#include <sstream>
using namespace std;
class IOException : public exception
{
private:
string m_message;
public:
IOException(const char* message, int errorCode)
{
stringstream ss;
ss << message << ". Error code: " << errorCode << ".";
m_message = ss.str();
}
virtual const char* what() const
{
return m_message.c_str();
}
};
FILE* OpenFileRead(const char* fileName)
{
FILE *ptr_file = fopen(fileName, "r");
if (!ptr_file)
throw IOException("Failed to open input file", errno);
return ptr_file;
}
int main()
{
try
{
FILE *ptr_file = OpenFileRead("output.txt");
// ...
fclose(ptr_file);
}
catch (const exception& exception)
{
cerr << "Ошибка: " << exception.what() << "\n";
return 1;
}
return 0;
}
Завели класс exception, который получает в конструкторе текст ошибки, имеет виртуальный метод what(), который возвращает этот текст во внешний мир. В примере выше был сделан наследник от класса, который добавляет дополнительную информацию. Он выводит не только текст, но и код ошибки. Как видите, отлавливание ошибок ловит как исключения базового класса, так и его наследников.
К сожалению старые подходы с бросанием различных типов данных оставили (кроме экземпляров exception и наследников от него), поскольку развитие С++ происходит исключительно методом расширения языка..., чтобы написанный ранее код мог продолжать компилироваться.
