Анимация
JavaScript
|
Главная Библионтека 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 |