Раз уж взялись, что мешает вставить в наш умный указатель с поддержкой отмены не один, а несколько предыдущих состояний объекта? Ничего. Только чтобы потом несколько раз не переделывать, решим несколько чисто технических моментов:
Код ниже, а сейчас пояснения:
Класс 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; }