Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ 16 ] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

необходимую работу. Для обеспечения безопасности типов эта переменная класса делается невидимой для пользователя. Иногда эта задача не решается так просто; семантика оболочки нередко отличается от семантики переменной.

Рассмотрим знакомый пример со связанным списком UnsafeNode. Вместо закрытого наследования SafeNode от этого класса можно сделать UnsafeNode переменной класса SafeNode. Однако по имеющемуся SafeNode вам не удастся получить следующий SafeNode в списке! Попробуйте сами. Каждый UnsafeNode ссылается на другой UnsafeNode, а не на SafeNode. Возможное решение - использовать разную семантику для оболочки и содержимого.

В SafeList.h

class UnsafeNode; Предварительное объявление

template <c1ass Type>

class SafeList { Безопасная оболочка для UnsafeNode

private:

UnsafeNode* head; public:

SafeList() : head(NULL) {}

~SafeList();

UnsafeNode* Cursor(); Для итераций

Type* Next(UnsafeNode*&); Переход к следующему элементу

void De1eteAt(UnsafeNode*&); Удаление элемента в позиции курсора

void InsertFirst(Type*); Вставка в начало списка

void InsertBefore(UnsafeNode*&); Вставка перед позицией курсора

void InsertAfter(UnsafeNode*&); Вставка после позиции курсора

В SafeList.cpp

class UnsafeNode { ListNode из предыдущего примера

private:

UnsafeNode* next;

void* data; public:

UnsafeNode(void* d, UnsafeNode* n); virtual ~UnsafeNode();

UnsafeNode* Next(); void* Data();

Объект SafeList представляет весь список, а не отдельный элемент. Большинство операций (такие как InsertFirst) относятся к списку в целом, а не к отдельному элементу. Для операций, выполняемых с одним элементом, нам потребуется новая парадигма - курсор (маркер позиции). Чтобы перемещаться по списку, вы запрашиваете у него позицию курсора. Чтобы перейти к следующему элементу, вы передаете ссылку на указатель на курсор, которая обновляется объектом SafeList. Чтобы выполнить операцию с определенной позицией списка, вы передаете курсор, определяющий эту позицию. Обратите внимание: клиенту не нужно знать об UnsafeNode ничего, кроме самого факта его существования - предварительного объявления оказывается вполне достаточно. Концепция курсора будет подробно рассмотрена в следующих главах. А пока вы должны понять, что безопасная оболочка не сводится к нескольким параметрам и символам <>, разбросанным по программе, - мы переопределяем семантику структуры данных. Такая ситуация типична для ненадежных, рекурсивных структур данных и часто встречается в других контекстах.





Исключения


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

Базовый принцип, на котором основана обработка исключений, - восстановление состояния и выбор альтернативных действий в случае ошибки. Предположим, в вашей программе имеется некий блок и вы не уверены, что он доработает до конца. При выполнении блока может возникнуть нехватка памяти, или начнутся проблемы с коммуникациями, или нехороший клиентский объект передаст неверный параметр. Разве не хотелось бы написать программу в таком виде:

if ( блок будет работать) { блок;

else {

сделать что-то другое;

Иначе говоря, вы заглядываете в хрустальный шар. Если в нем виден фрагмент программы, который горит синим пламенем, вы изменяете будущее и обходите этот фрагмент. Не стоит с затаенным дыханием ожидать появления таких языков программирования в ближайшем будущем, но на втором месте стоит обработка исключений. С помощью исключений вы «допрашиваете» подозрительный блок. Если в нем обнаружится ошибка, компилятор поможет восстановить состояние перед выполнением блока и продолжить работу.

Обработка исключений в стандарте ANSI

Хорошая новость: для обработки исключений существует стандарт ANSI - или, как это всегда бывает в C++, предложенный стандарт. Интересно, почему у нас так много предложенных стандартов и ни одного реального? Скорее всего, дело в том, что наша экономика не может вместить всех безработных членов комитетов стандартизации. Лучше оставить им занимательное пожизненное хобби, пока мы будем выполнять свою работу. Впрочем, я отвлекся.

Плохая новость: стандартная обработка исключений все еще не поддерживается многими компиляторами C++. Хорошая новость: все больше и больше компиляторов выходит на передовые позиции. Плохая новость: осталось немало старого кода, предназначенного для старых компиляторов.

Увы.

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

Синтаксис инициирования исключений

Следующая функция шлепнет вас по рукам, если вызвать ее с неверным параметром. Вместо линейки она воспользуется секцией throw. В этой функции могут произойти две ошибки, представленные константами перечисления Gotcha.

enum Gotcha { kTooLow, kTooHigh }; void fn(int x) throw(Gotcha) {



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [ 16 ] 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82