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



бет72/124
Дата16.07.2016
өлшемі3.27 Mb.
#204081
түріКнига
1   ...   68   69   70   71   72   73   74   75   ...   124

8.9 Упражнения


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) { // значит сюда мы никогда не попадем



// ...

}

}





Достарыңызбен бөлісу:
1   ...   68   69   70   71   72   73   74   75   ...   124




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет