Шаг 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;
}

Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Albert Makhmutov.