|
||||
|
Шаг 23 - Классы объектов, поддерживающие транзакции. Продолжение. Раз уж взялись, что мешает вставить в наш умный указатель с поддержкой отмены не один, а несколько предыдущих состояний объекта? Ничего. Только чтобы потом несколько раз не переделывать, решим несколько чисто технических моментов: 1. Какую структуру будем использовать в качестве коллекции состояний? Можно взять стек, можно кольцевой буфер, а можно и карту (словарь, хэш-таблицу); стек явно проще, зато за кольцевым буфером можно не следить вообще, пусть устаревшие состояния пропадают бесследно (конечно по желанию). 2. Определимся с семантикой. Смешивать значения и указатели не стоит, верный путь заработать себе геморрой. У меня не оказалось под рукой подходящего стека, и я написал для этого Шага два варианта - один хранит значения, другой - указатели; первый стек сначала казался проще, но использующий его класс указателя оказался ощутимо сложнее по простой причине - функции стека с указателями могут возвращать NULL, а это совсем немало. 3. Оформим все в виде шаблонов; вообще контейнеры просто просятся быть шаблонами, а smart-указатели несомненно являются контейнерами. Код ниже, а сейчас пояснения: Класс CType просто проверочный, чтобы вкладывать в шаблоны; так проще отлаживать шаблон: сначала сделать контейнер-не-шаблон для класса Type, а потом просто приписать сверху объявления строку template‹Type›. Шаблон класса ampstack‹Type› - шаблон стека указателей; push сохраняет указатель, pop достает верхний указатель, isEmpty проверяет на пустоту, emptyAll очищает. Шаблон класса MLTrans - наконец тот, который нам нужен. Указатель that хранит текущее значение, Push сохраняет текущее значение, PopOne делает однократную отмену, Rollback отменяет все изменения, до первоначального, Commit удаляет историю. // Это маленький класс для проверки class CType { int a; public: void set (int _a) { a=_a; } int get (void) { return a; } }; // Шаблон стека template ‹class Type› class ampstack { private: int iTop; // верх стека int iSize; // размер стека Type** array; // массив указателей public: // Конструктор-деструктор ampstack(int size=10) : iTop(0), iSize(size), array(new Type*[size]) {} ~ampstack() { for (int iCounter = 0; iCounter ‹ iTop; iCounter ++) if (*(array+iCounter)!= NULL) delete *(array+iCounter); delete[] array; } // Управление стеком // Направить указатель в стек void push (Type* _t) { array[iTop++]=_t; } // Вынуть указатель из стека Type* pop (void) { if (iTop == 0) return NULL; else return array[--iTop]; } // Стек пуст? int isEmpty (void) { return iTop==0; } // Очистить стек void emptyAll (void) { for (int iCounter = 0; iCounter ‹ iTop; iCounter ++) if (*(array+iCounter)!= NULL) delete *(array+iCounter); iTop = 0; } }; // Шаблон класса с многоуровневой отменой template ‹class Type› class MLTrans { typedef ampstack‹Type› stack; private: Type* that; // Текущее значение stack history; // контейнер предыдущих значений public: // конструктор-деструктор MLTrans(): that(new Type) {} ~MLTrans () { delete that; } // Сохранение текущего значения, aналог SAVE TRANSACTION в SQL серверах void Push() { history.push(that); that = new Type(*that); } // удаление промежуточных состояний void Commit () { history.emptyAll(); } // Откат на одну позицию; уничтожает текущее значение. void PopOne() { if (!history.isEmpty()) { delete that; that = history.pop(); } } // Откат к началу транзакции. void Rollback() { Type* old = history.pop(); Type* older = NULL; if (old!= NULL) { while ((older = history.pop())!= NULL) { delete old; old = older; } delete that; that = old; } } // Переопределенный operator-› Type* operator-›() { return that; } } // проверим работу int main() { int t; MLTrans‹CType› a; a-›set(5); t = a-›get(); a.Push(); a-›set(6); t = a-›get(); a.Push(); t = a-›get(); a-›set(7); t = a-›get(); a.Push(); t = a-›get(); a-›set(9); t = a-›get(); // a.Push(); t = a-›get(); a.PopOne(); t = a-›get(); a.Rollback(); t = a-›get(); return 0; } |
|
||
Главная | Контакты | Нашёл ошибку | Прислать материал | Добавить в избранное |
||||
|