Анимация
JavaScript
|
Главная Библионтека копий указываемого объекта. Существует множество всевозможных комбинаций и вариаций, так что выбор зависит главным образом от вашего настроения. Простой указатель образов На нескольких ближайших страницах показано простейшее решение этой проблемы. А пока лишь скажу, что мы имеем дело с ведущим указателем, удаляющим объекты, на которые он ссылается. template <c1ass Type> class ImagePtr { private: Type* current; Текущий образ, предоставляемый компоненту Type* undo; Предыдущий образ public: ImagePtr() : undo(NULL), current(new Type) {} ImagePtr(const ImagePtr<Type>& ip) : current(new Type(*(ip.current))), undo(NULL) {} ~ImagePtr() { delete current; delete undo; } ImagePtr<Type>& operator=(const ImagePtr<Type>& ip) if (this != &ip) { delete current; current = new Type(*(ip.current)); return *this; void Snapshot() delete undo; На случай, если был старый образ undo = current; current = new Type(*undo); void Commit() delete undo; undo = NULL; void Ro11back() if (undo != NULL) { delete current; current = undo; undo = NULL; Type* operator->() const { return current; } Указатель всегда возвращает «текущий» образ как значение оператора ->, однако за кулисами он прячет предыдущую версию указываемого объекта. Функция Shapshot() создает образ с помощью конструктора копий указываемого объекта. Если клиент передумает и захочет отказаться от изменений, он вызывает функцию Ro11back(); если изменения его устраивают, он вызывает функцию Commit(). Конструктор копий и оператор = поддерживают семантику ведущих указателей, но не создают снимков во время конструирования или сразу же после присваивания. Помните: нас интересует состояние указываемого объекта, а не указателя. Когда мы создаем новую копию объекта для поддержания семантики ведущего указателя, для этой копии еще не существует предыдущих образов, которые необходимо отслеживать. Деструктор предполагает, что если клиент не вызвал функцию Commit() , что-то было сделано неверно, и уничтожает копию. Специалисты по базам данных - отъявленные пессимисты; если происходит что-то непредвиденное, они всегда предполагают самое худшее. Во многих приложениях при уничтожении указателя вместо Ro11back() следовало бы просто вызвать Commit(), предполагая, что до настоящего момента все происходило вполне разумно. Стеки образов Для многоуровневой отмены вам может понадобиться стек предыдущих образов. Один из вариантов реализации - хранить стек в каждом указателе образов. В следующем фрагменте предполагается, что у вас имеется параметризованный класс Stack с функциями Empty(), Push(), Pop() и DeleteAll (). Функция Pop() возвращает верхний элемент стека или NULL, если стек пуст. Функция De1eteAl1() опустошает стек и уничтожает все объекты по мере их извлечения. Каждый указатель хранит стек предыдущих образов. Если стек пуст, значит, образы не создавались. Если стек не пуст, в его нижней части находится исходный объект. Функция Ro11back() находит и восстанавливает этот объект. Конструктор копий и оператор = работают так же, как и в описанном выше простейшем указателе образов. template <c1ass Type> class ImageStackPtr { private: Type* current; Текущий образ, предоставляемый клиенту Stack<Type> history; Предыдущие образы public: ImageStackPtr() : current(new Type) {} ImageStackPtr(const ImageStackPtr<Type>& ip) : current(new Type(*(ip.current))) {} ~ImageStackPtr() { delete current; } ImageStackPtr<Type>& operator=(const ImageStackPtr<Type>& ip) if (this != &ip) { history.DeleteAl delete current; current = new Type(*(ip.current)); return *this; void PushImage() history.Push(current); current = new Type(*current); void Commit() { history.De1eteAl1(); } void PopImage() Вернуться на один уровень if (!history.Empty()) { delete current; current = history.PopO; void Ro11back() Вернуться к самому старому образу Type* old = history.PopO; Type* oldere = NULL; if (old != NULL) { Хотя бы один раз while ((older = history.PopO) != NULL) { delete old; old = older; delete current; current = old; Type* operator->() const { return current; } Хранение отдельного стека в каждом указателе оправданно для транзакций, в которых участвует небольшое количество объектов. Но если одной транзакции приходится отслеживать множество обновляемых объектов, кучу мелких стеков лучше объединить в один большой. Мы сделаем это позднее, когда речь пойдет о транзакциях. Образы автоматических объектов Концепцию образа можно немного обобщить, чтобы она распространялась не только на объекты, созданные оператором new и обслуживаемые *-указателями, но и автоматические объекты. Автоматическими считаются стековые объекты, а также переменные и компоненты базовых классов вмещающего объекта независимо от того, выделялась память под вмещающий объект динамически или нет. template <c1ass Type> class AutoImage { private: Type current; Type image; bool have image; Истина, если образ существует public: AutoImage() : have image(fa1se) {} AutoImage(const AutoImage<Type>& ai) : current(ai.current), image(), have image(fa1se) {} AutoImage<Type>& operator=(const AutoImage<Type>& ip) if (this != &ip) { current = ip.current; have image = false; return *this; AutoImage<Type>& operator=(const Type& t) current = t; 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 |