
- •Граф. Ориентированный граф. Представления графа. Обход графа в глубину и в ширину.
- •Топологическая сортировка. Подсчет числа путей в орграфе.
- •Сильно связные компоненты.
- •Поиск кратчайших путей в графе. Алгоритм Флойда. Алгоритм Дейкстры. Алгоритм Форда-Беллмана.
- •Алгоритм a*. Эвристики.
- •Минимальное остовное дерево. Алгоритм Прима. Биномиальная куча.
- •Амортизационная стоимость. Фибоначчиева куча. Амортизационная стоимость (анализ)
- •Метод потенциалов
- •Фибоначчиева Куча
- •Структура
- •Время работы
- •Система непересекающихся множеств. Алгоритм Крускала.
- •Наивная реализация
- •Эвристика сжатия пути
- •Эвристика объединения по рангу
- •Потоки, Форда-Фалкерсона.
- •Декартово дерево
- •Дерево Фенвика. Дерево отрезков и динамическое программирование для rmq.
- •Сведение rmq к lca и наоборот.
- •Препроцессинг
- •Ответы на запросы
- •Алгоритм
- •Доказательство
- •Сложность
- •Алгоритм
- •Минимум внутри блока
- •Результат
- •Поиск нескольких минимумов на отрезке. Задача поиска подстрок. Алгоритм Рабина-Карпа. Конечный автомат. Алгоритм Бойера-Мура.
- •Метод хеширования
- •Алгоритм
- •Время работы
- •Алгоритм Бойера-Мура
- •Поиск со звездочками. Алгоритм Кнута-Морриса-Пратта.
- •Время работы
- •Алгоритм Ахо-Корасика.
- •Дерево ключевых слов (бор)
- •Пример дерева ключевых слов (бора)
- •Построение бора
- •Поиск строки в бору
- •Автомат Ахо-Корасик
- •Суффиксное дерево, Алгоритм Укконена.
- •2. Квадратичный алгоритм
- •3. Линейный алгоритм
- •Суффиксный массив.
- •Наивный алгоритм поиска
- •Алгоритм, использующий префиксы циклических сдвигов
- •Исключения
- •Шаблоны
- •Stl: итераторы
- •Сортировка и поиск в stl. Алгоритмы stl
- •Куча в stl. Алгоритмы stl.
- •Ассоциативный массив. Интерфейс, варианты реализации.
Исключения
Какие методы сообщения об ошибках есть в языке?
Возврат кода ошибки
Бросание исключения
Генерирование и перехват исключений
Если в некотором разделе кода обнаруживается исключительная ситуация, генерируется исключение.
В другом разделе кода это исключение перехватывается, и выполняются соответствующие действия.
#include <stdexcept>
double divideNumbers (double inNutnerator, double inDenominator) {
if (inDenominator ==0) throw std::exception();
return (inNumerator / inDenominator);
}
При выполнении инструкции trow функция будет немедленно завершена без возврата какого бы то ни было значения. Если вызов функции заключить в try-catch блок, как показано в следующем коде, то инициатор вызова получит исключение и сможет его обработать.
#include <iostream>
#include <stdexcept>
int main(int argc, char** argv) {
try {
std::cout « divideNumbers(2.5, 0.5) « std::endl;
std::cout « divideNurnbers (2.3, 0) « std::endl;
} catch (std::exception exception) {
std::cout « "Исключение перехвачено! " << std::endl;
}
}
Throw-списки.
Язык С++ позволяет указывать исключения, которые предполагают сгенерировать функция или метод. В этом случае составляются списки типов генерируемых исключений (trrow-списки), или спецификация исключений.
void readlntegerFile(const strings fileName, vector<int>& dest) { … }
преобразуем в:
void readlntegerFile(const strings fileName, vector<int>& dest)
throw (invalid argument, runtime error) { … }
В throw-списке просто перечисляются типы исключений, которые могут быть сгенерированы из этой функции. Обратите внимание на то, что throw-список должен быть включен и в объявление функции, если её реализация находится в коде отдельно.
Функция без throw-списка может генерировать исключения любого типа.
Функция с пустым throw-списком не должна генерировать исключения вообще.
Хотя throw-списки не мешают функциям генерировать исключения не указанных в них типов, они не позволяют таким исключениям выйти заграницы функций.
Если функция генерирует исключение, которое отсутствует в ее throw-списке, С++ вызывает специальную функцию unexpected(). Встроенная реализация функции unexpected() просто содержит обращение к функции terminate(). Но точно так же, как вы устанавливали собственный обработчик типа terminate_handler, вы сможете установить собственный обработчик типа unexpected_handler. В отличие от обработчика terminate_handler, в обработчик unexpected_handler вы можете действительно заложить выполнение действий, отличных от простого завершения программы.
Изменение throw-списков в переопределенных методах
При переопределении виртуального метода в подклассе можно изменить throw-список, если вы сделаете его более ограничительным, чем throw-список в суперклассе. Более ограничительными считаются следующие изменения:
Удаление исключений из списка.
Добавление подклассов исключений, которые имеются в throw-списке суперкласса.
Следующие изменения не считаются более ограничительными:
Добавление в список исключений, которые не являются подклассами исключений, указанных в throw-списке суперкласса.
Полное удаление throw-списка
Если вы изменяете throw-списки при переопределении методов, помните, что любой код, который вызывается версией метода, определенной суперклассом, должен быть способен вызвать версию метода подкласса. Это означает, что исключения добавлять нельзя.
Обработка ошибок в конструкторах и деструкторах
Несмотря на невозможность конструктора возвращать значение, вы можете из него сгенерировать исключение.
С помощью исключения нетрудно уведомить клиента о результате выполнения конструктора: успешном или нет.
Здесь, однако, есть одна существенная проблема: если исключение "покидает" пределы конструктора, то деструктор для созданного объекта никогда не будет вызван.
Рассмотрим пример конструктора класса, модифицированного с использованием обработки исключения:
GameBoard::GameBoard(int inWidth, int inHeight) throw (bad_alloc): mWidth(inWidth), mHeight(inHeight) {
int i, j;
mCells = new GamePiece* [mWidth];
try {
for (i = 0; i <mWidth; i++) {
mCells[i] = new GamePiece[mHeight];
}
} catch (...) {
/* Освобождаем память, которую мы уже выделили, поскольку деструктор для объекта никогда не будет вызван. Верхняя граница Еог-цикла соответствует индексу последнего элемента в массиве тСе11в, который мы пытались создать динамически (но попытка "проваливается"). Мы должны освободить память с помощью указателей, которые мы успели сохранить в массиве до возникновения исключения, используя все "занятые" индексы этого массива */
for (j = 0; j < i; j++) {delete [] mCells [j ] ;}
delete[] mCells;
//Любое исключение приводим к типу bad_alloc
throw bad alloc();
}
}
Необходимо обрабатывать все ошибочные ситуации, возникающие в самих деструкторах. Причины:
Деструкторы могут работать, пока обрабатывается другое исключение. Если сгенерировать исключение из деструктора в то время, пока еще активно другое исключение, программа будет завершена.
Подумайте о своих клиентах. Клиенты не вызывают деструкторы явно: они вызывают оператор delete, который вызывает деструктор. Если вы сгенерируете исключение из деструктора, то какие действия должен, по вашему, предпринять?
Деструктор — это ваш шанс освободить память и другие ресурсы, используемые объектом. Если вы утратите этот шанс в результате преждевременного выхода из функции деструктора из-за исключения, вы никогда не сможете вернуться назад и освободить ресурсы.
Поэтому нужно стараться делать деструкторы как модно более простыми. Например, использовать только функции delete и delete[], которые не могут кинуть исключения.