Анимация
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

template <c1ass Type>

class ConstPtr {

friend class LockPtr<Type>;

private:

Type* o1d image; Образ перед транзакцией

LockPtr<Type>* lock; Текущая блокировка, если она есть

~ConstPtr() { delete o1d image; }

ConstPtr<Type>& operator=(const ConstPtr<Type>& cp)

{ return *this; } Присваивание не разрешается

public:

ConstPtr() : o1d image(NULL), lock(NULL) {} ConstPtr(const ConstPtr<Type>& cp)

: o1d image(new Type(*(cp.o1d image))), lock(NULL) {} const Type* operator->() const { return o1d image; } LockPtr<Type>& Lock(Transaction* t);

template <c1ass Type>

LockPtr<Type>& ConstPtr<Type>::Lock(Transaction* t)

if (lock != NULL && !t->OwnsLock(1ock))

Конфликт - уже имеется другой владелец else {

lock = new LockPtr<Type>(t, this); return *1ock;

Новый объект ConstPtr можно сконструировать на базе старого (хотя новый создается без блокировки), однако нам придется внести изменения для присваивания, которое разрешено только для LockPtr, но не для ConstPtr. Для этой цели мы определяем фиктивный оператор = и делаем его закрытым, чтобы до него никто не добрался. Поскольку указатель является ведущим, его удаление приводит и к удалению указываемого объекта (самое радикальное изменение, которое только можно представить). По этой причине конструктор также объявлен закрытым, чтобы никто не попытался удалить ConstPtr.

Конфликт в функции Lock можно обработать разными способами:

• Инициировать исключение.

• Изменить интерфейс и возвращать вместе с блокировкой флаг, показывающий, успешно ли прошла блокировка.

• Возвращать LockPtr<Type>* со значением NULL, свидетельствующем о неудачной блокировке.

• Возвращать конфликтную блокировку с перегруженным оператором ! , с помощью которого можно проверить правильность блокировки.

• Предоставить отдельную функцию CanLock(Transaction*), которая возвращает логическое значение.

Выбор зависит от стиля. Вариант с исключением не так уж очевиден; неудача при блокировке представляет собой вполне разумный исход.



Класс LockPtr

Ага! Мы подошли к центральной идее всей концепции - указателям, которые разрешают обновление указываемого объекта. Предтранзакционный (предназначенный для отмены) образ хранится в ConstPtr, а текущий обновленный образ доступен только через LockPtr. Класс LockPtr содержит уже знакомые функции Ro11back() и Commit(). В функции Snapshot() нет необходимости, поскольку LockPtr при необходимости создает образы в операторе ->.

template <c1ass Type> class LockPtr : public Lock { friend class ConstPtr<Type>; private:

ConstPtr<Type>* master ptr;

Type* new image;

Transaction* transaction;

LockPtr(Transaction* t, ConstPtr<Type>* cp); virtual ~LockPtr(); virtual void Ro11back(); virtual void Commit(); public:

Type* operator->() const { return new image; }

template <c1ass Type>

LockPtr<Type>::LockPtr(Transaction* t, ConstPtr<Type>* cp)

: transaction(t), master ptr(cp), new image(new Type(*(cp->o1d image)))

template <c1ass Type> LockPtr<Type>::~LockPtr()

В сущности происходит откат

delete new image; Отказаться от изменений

master ptr->1ock = NULL; Оставить ConstPtr

template <c1ass Type>

void LockPtr<Type>::Ro11back()

delete new image;

new image = new Type(*(master ptr->o1d image));

template <c1ass Type>

void LockPtr<Type>::Commit()

delete master ptr->o1d image;

master ptr->o1d image = new image; Переместить в master ptr new image = new Type(*new image); Нужна новая копия

Деструктор объявлен закрытым, чтобы никто не мог напрямую удалить LockPtr. Вместо этого транзакция-владелец должна сделать это через базовый класс Lock. Функции Ro11back() и Commit () объявлены виртуальными, чтобы с их помощью можно было решать задачи, относящиеся к



конкретному типу (например, создание и уничтожение образов). Обе функции после завершения оставляют ConstPtr заблокированным.

Создание и уничтожение объектов

Пора заполнить кое-какие пробелы. Раз уж наши транзакции достаточно сложны, чтобы для них была оправдана вся эта возня, они наверняка будут создавать или уничтожать объекты. Операции создания и уничтожения также должны быть отменяемыми. Если объект создавался, операция отмены должна его уничтожать, а если уничтожался - возвращать его из мертвых. Для этого придется внести изменения как в класс ConstPtr, так и в класс LockPtr. Мы уже сделали первый шаг в этом направлении, объявив деструктор ConstPtr закрытым, чтобы им могли воспользоваться только ConstPtr или его друзья. Давайте разберемся с оставшимися проблемами.

Изменения в классе ConstPtr

Несомненно, создание указываемого объекта представляет собой изменение и потому должно осуществляться через LockPtr. Но для того чтобы получить LockPtr, мы должны сначала иметь ConstPtr и его функцию Lock(). Следовательно, только что описанный конструктор ConstPtr работать не будет - он создает уникальный объект перед вызовом Lock(). ConstPtr должен находиться в состоянии NULL до тех пор, пока LockPtr не выделит память под объект и не закрепит эти изменения. В ConstPtr необходимо внести следующие изменения:

• В конструкторе без аргументов присваивать переменной o1d image значение NULL.

• Добавить оператор ! , который проверяет, равен ли адрес значению NULL.

• Инициировать исключение в операторе ->, если адрес равен значению NULL.

• Либо запретить копирование, либо присвоить копии o1d image значение NULL.

Проблема с обычным конструктором копий ConstPtr заключается в том, что он может создать новую копию указываемого объекта, но не позволит отменить ее создание. Ниже приводится новая версия конструктора ConstPtr. Определения функций, не изменившиеся по сравнению с показанной выше упрощенной версией не показаны.

private:

ConstPtr(const ConstPtr&) : o1d image(NULL), lock(NULL) {} public:

ConstPtr() : o1d image(NULL), lock(NULL) {} bool operator!() { return o1d image == NULL; } const Type* operator->() const

if (o1d image == NULL)

Исключение return o1d image;

Изменения в классе LockPtr

Отныне LockPtr предстоит выполнять намного больше работы:

• Он должен при необходимости создавать указываемый объект по требованию. Для этого в него будет добавлена функция Make().

• Оператор -> должен инициировать исключение, если адрес равен NULL. Ниже приведены определения только изменившихся функций.

В объявлении LockPtr public:

void Make(); Создать новый указываемый объект

void Destroy(); Уничтожить указываемый объект



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