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

void Ro11back()

if (have image) {

current = image; have image = false;

bool HaveImage() { return have image; }

Если вам захочется еще немного автоматизировать этот шаблон, добавьте вызовы Snapshot() в обе операторные функции operator=().

Указателей расплодилось слишком много, и терминология начинает запутываться. Термин «указатель образов» обозначает указатель, в котором содержится несколько образов объекта (и который почти всегда является ведущим), тогда как термин «образы указателей» относится к классу наподобие показанного выше, в котором хранится несколько предыдущих значений самого указателя.

Комбинации и вариации

В нашей коллекции скопилось уже немало «строительных блоков», а возможности их комбинирования безграничны. Выберите по одному варианту в каждой позиции, и вы получите некое специализированное решение для работы с образами:

• простые/ведущие указатели;

• образы автоматические/созданные оператором new;

• один образ/стеки образов;

• образы объектов/ образы указателей;

• спонтанное создание образа/ ручной вызов функции Snapshot().

Приведенный список ни в коем случае не исчерпывает всех возможных вариантов. Скорее это представительный перечень концепций, сосредоточенных вокруг общей темы - управления образами. Все концепции ориентированы на С++ с его уникальным синтаксисом и семантикой (в частности, конструкторами и операторами). Пошевелите мозгами и подумайте, как эти концепции подходят к вашей конкретной задаче. Именно этим мы и займемся в оставшейся части этой главы.

Транзакции и отмена

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

template <c1ass Type> class ImagePtr { private:

Type* current; Текущий образ, предоставляемый компоненту Type* undo; Предыдущий образ

public:

ImagePtr();

ImagePtr(const ImagesPtr<Type>& ip); ~ImagePtr();

ImagePtr<Type>& operator=(const ImagePtr<Type>& ip); void Snapshot(); void Commit();



void Ro11back();

Type* operator->() const;

Для мира транзакций придется внести ряд изменений:

• Объект в любой момент времени может быть заблокирован не более чем одной транзакцией.

• Объект не может быть изменен при снятой блокировке.

• Заблокировнный объект может быть измене лишь объектом, который принадлежит транзакции, установившей блокировку.

Следовательно, нам придется создать некоторое представление для транзакции, а заодно - поразмять мышцы С++ и построить соответствующую семантику. Транзакции будут представлены классом Transaction. Для блокировок мы воспользуемся специальным обновляющим указателем. Иначе говоря, обычные клиенты работают с умным указателем, не допускающим обновления, а клиенты транзакции-владельца получают доступ к другому указателю с возможностью обновления. Ниже приведена прямолинейная (хотя необязательно самая эффективная) реализация этой архитектуры. Позднее мы снимем эти упрощающие ограничения и расширим архитектуру:

1 . Нас интересует только отмена изменений в существующих объектах, а не отмена создания и уничтожения объектов в процессе транзакции.

2. Вопрос о том, когда именно должна устанавливаться блокировка объекта выходит за рамки описанной упрощенной архитектуры.

Транзакции и блокировки

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

В файле Transaction.h class Lock {

friend class Transaction; protected:

Transaction* transaction; Транзакция, которой принадлежит this

Lock() : transaction(NULL) {} void RegisterLock(Transaction* t)

if (transaction != NULL) {

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

cerr << "Lock::RegisterLock - already locked" << endl;

else {

t->AddLock(this); transaction = t;

virtual ~Lock() {}

virtual void Ro11back() = 0;

virtual void Commit() = 0;

class Transaction {

friend class Lock; Чтобы предоставить доступ к AddLock()



private:

SafeSet<Lock>* locks;

void AddLock(Lock*); Включить блокировку в транзакцию

public:

~Transaction();

void Commit(); Закрепить все образы

void Ro11back(); Отменить все образы

bool OwnsLock(Lock*); Истина, если блокировка

принадлежит транзакции

Класс Transaction поддерживает коллекцию блокировок с помощью гипотетического шаблона Collection. Функция RegisterLock() включена в базовый класс Lock и потому может обратиться к закрытой функции AddLock() класса Transaction. Дружба не наследуется, поэтому объявление другом класса Lock не делает друзьями его производные классы. Реализации выглядят довольно просто.

void Transaction::AddLock(Lock* lock)

*1ocks += lock; Использует перегрузку += для коллекции

void Transaction::Commit()

SafeSetIterator<Lock>* iter = 1ocks->Iterator(); while (iter->More())

iter->Next()->Commit(); delete iter;

void Transaction::Ro11back()

SafeSetIterator<Lock>* iter = 1ocks->Iterator(); while (iter->More())

iter->Next()->Ro11back(); delete iter;

bool Transaction::OwnsLock(Lock* lock)

return *1ocks >= lock;

Предполагается, что шаблон Collection содержит функцию De1eteAl1() для удаления всех объектов; что перегруженный оператор += (операторная функция operator+=(Type*)) включает элемент в коллекцию; что перегруженный оператор >= определяет принадлежность к коллекции, а функция Iterator() возвращает вложенный итератор. Это обобщенные условия; используйте те, которые действуют в вашем случае.

Класс ConstPtr

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

template <c1ass Type>

class LockPtr; Ссылка вперед на класс, производный от Lock



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