Идиомы и стили С++ | страница 34
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; }