1. (*2) Определите семейство списков с двойной связью, которые
будут двойниками списков с одной связью, определенных в $$8.3.
2. (*3) Определите шаблон типа String, параметром которого является
тип символа. Покажите как его можно использовать не только для
обычных символов, но и для гипотетического класса lchar, который
представляет символы не из английского алфавита или расширенный
набор символов. Нужно постараться так определить String, чтобы
пользователь не заметил ухудшения характеристик программы по
памяти и времени или в удобстве по сравнению с обычным строковым
классом.
3. (*1.5) Определите класс Record (запись) с двумя членами-данными:
count (количество) и price (цена). Упорядочите вектор из таких
записей по каждому из членов. При этом нельзя изменять функцию
сортировки и шаблон Vector.
4. (*2) Завершите определения шаблонного класса Map, написав
недостающие функции-члены.
5. (*2) Задайте другую реализацию Map из $$8.8, используя списочный
класс с двойной связью.
6. (*2.5) Задайте другую реализацию Map из $$8.8, используя
сбалансированное дерево. Такие деревья описаны в $$6.2.3 книги
Д. Кнут "Искусство программирования для ЭВМ" т.1, "Мир", 1978 [K].
7. (*2) Сравните качество двух реализаций Map. В первой используется
класс Link со своей собственной функцией размещения, а во второй
- без нее.
8. (*3) Сравните производительность программы подсчета слов из
$$8.8 и такой же программы, не использующей класса Map. Операции
ввода-вывода должны одинаково использоваться в обеих программах.
Сравните несколько таких программ, использующих разные варианты
класса Map, в том числе и класс из вашей библиотеки, если он там
есть.
9. (*2.5) С помощью класса Map реализуйте топологическую сортировку.
Она описана в [K] т.1, стр. 323-332. (см. упражнение 6).
10. (*2) Модифицируйте программу из $$8.8 так, чтобы она работала
правильно для длинных имен и для имен, содержащих пробелы
(например, "thumb back").
11. (*2) Определите шаблон типа для чтения различных видов строк,
например, таких (предмет, количество, цена).
12. (*2) Определите класс Sort из $$8.4.5, использующий сортировку
по методу Шелла. Покажите как можно задать метод сортировки
с помощью параметра шаблона. Алгоритм сортировки описан в [K]
т.3, $$5.2.1 (см. упражнение 6).
13. (*1) Измените определения Map и Mapiter так, чтобы постфиксные
операции ++ и -- возвращали объект Mapiter.
14. (*1.5) Используйте шаблоны типа в стиле модульного
программирования, как это было показано в $$8.4.5 и напишите
функцию сортировки, рассчитанную сразу на Vector и T[].
* ГЛАВА 9
Я прервал вас, поэтому не прерывайте меня.
- Уинстон Черчилл
В этой главе описан механизм обработки особых ситуаций и некоторые,
основывающиеся на нем, способы обработки ошибок. Механизм состоит
в запуске особой ситуации, которую должен перехватить специальный
обработчик. Описываются правила перехвата особых ситуаций и
правила реакции на неперехваченные и неожиданные особые ситуации.
Целые группы особых ситуаций можно определить как производные
классы. Описывается способ, использующий деструкторы и обработку
особых ситуаций, который обеспечивает надежное и скрытое от
пользователя управление ресурсами.
9.1 Обработка ошибок
Создатель библиотеки способен обнаружить динамические ошибки, но не
представляет какой в общем случае должна быть реакция на них.
Пользователь библиотеки способен написать реакцию на такие ошибки,
но не в силах их обнаружить. Если бы он мог, то сам разобрался бы
с ошибками в своей программе, и их не пришлось бы выявлять
в библиотечных функциях. Для решения этой проблемы в язык введено
понятие особой ситуации Ь.
Ь Только недавно комитетом по стандартизации С++ особые ситуации были
включены в стандарт языка, но на время написания этой книги они еще
не вошли в большинство реализаций.
Суть этого понятия в том, что функция, которая обнаружила ошибку и не
может справиться с нею, запускает особую ситуацию, рассчитывая, что
устранить проблему можно в той функции, которая прямо или опосредованно
вызывала первую. Если функция рассчитана на обработку ошибок некоторого
вида, она может указать это явно, как готовность перехватить данную
особую ситуацию.
Рассмотрим в качестве примера как для класса Vector можно
представлять и обрабатывать особые ситуации, вызванные выходом за
границу массива:
class Vector {
int* p;
int sz;
public:
class Range { }; // класс для особой ситуации
int& operator[](int i);
// ...
};
Предполагается, что объекты класса Range будут использоваться как
особые ситуации, и запускать их можно так:
int& Vector::operator[](int i)
{
if (0<=i && i
throw Range();
}
Если в функции предусмотрена реакция на ошибку недопустимого значения
индекса, то ту часть функции, в которой эти ошибки будут перехватываться,
надо поместить в оператор try. В нем должен быть и обработчик особой
ситуации:
void f(Vector& v)
{
// ...
try {
do_something(v); // содержательная часть, работающая с v
}
catch (Vector::Range) {
// обработчик особой ситуации Vector::Range
// если do_something() завершится неудачно,
// нужно как-то среагировать на это
// сюда мы попадем только в том случае, когда
// вызов do_something() приведет к вызову Vector::operator[]()
// из-за недопустимого значения индекса
}
// ...
}
Обработчиком особой ситуации называется конструкция
catch ( /* ... */ ) {
// ...
}
Ее можно использовать только сразу после блока, начинающегося служебным
словом try, или сразу после другого обработчика особой ситуации. Служебным
является и слово catch. После него идет в скобках описание, которое
используется аналогично описанию формальных параметров функции, а именно,
в нем задается тип объектов, на которые рассчитан обработчик, и,
возможно, имена параметров (см. $$9.3). Если в do_something() или в
любой вызванной из нее функции произойдет ошибка индекса (на любом
объекте Vector), то обработчик перехватит особую ситуацию и будет
выполняться часть, обрабатывающая ошибку. Например, определения следующих
функций приведут к запуску обработчика в f():
void do_something()
{
// ...
crash(v);
// ...
}
void crash(Vector& v)
{
v[v.size()+10]; // искусственно вызываем ошибку индекса
}
Процесс запуска и перехвата особой ситуации предполагает просмотр
цепочки вызовов от точки запуска особой ситуации до функции, в которой
она перехватывается. При этом восстанавливается состояние стека,
соответствующее функции, перехватившей ошибку, и при проходе по всей
цепочке вызовов для локальных объектов функций из этой цепочки вызываются
деструкторы. Подробно это описано в $$9.4.
Если при просмотре всей цепочки вызовов, начиная с запустившей
особую ситуацию функции, не обнаружится подходящий обработчик, то
программа завершается. Подробно это описано в $$9.7.
Если обработчик перехватил особую ситуацию, то она будет обрабатываться
и другие, рассчитанные на эту ситуацию, обработчики не будут
рассматриваться. Иными словами, активирован будет только тот обработчик,
который находится в самой последней вызывавшейся функции, содержащей
соответствующие обработчики. В нашем примере функция f() перехватит
Vector::Range, поэтому эту особую ситуацию нельзя перехватить ни в
какой вызывающей f() функции:
int ff(Vector& v)
{
try {
f(v); // в f() будет перехвачена Vector::Range
}
catch (Vector::Range) { // значит сюда мы никогда не попадем
// ...
}
}
Достарыңызбен бөлісу: |