Бывает особенно приятно, когда занимаешься теорией. Занимаешься, думаешь: "ну никакой связи с жизнью, хоть бы минимум пользы"... и вдруг раз! и польза является во всей своей красе, блистая в лучах солнца и хрустя пачками денег. Что чувствовал Менделеев, когда после долгих изысканий, жутких таблиц, являвшихся ему в ночных кошмарах, вдруг получил-таки нормальную, не паленую, 40-градусную водку? Или Эйлер, когда, после терзаний и депрессий, извлек таки сопротивляющийся, визжащий и цепляющийся щупальцами и жвалами квадратный корень из минус единицы? К чему это я? А вот к чему: концепция smart-указателей может предложить простые и прозрачные решения для некоторых сложных задач; и если Вы считаете, что поддержка транзакций (а так же многоуровневой отмены и повтора) есть сложная задача, то смарты помогут Вам с замечательной легкостью.
Вспомним для начала, что делает транзакция:
Начнем с первого пункта. Если мы хотим закрепить или отменить проведенные изменения, нужно хранить состояние объекта на заданный момент - начало транзакции, и в момент принятия решения или уничтожать предыдущее состояние (закрепление) или возвращаться к нему (отмена). Пусть обслуживанием занимается smart-указатель. Кладем в него два указателя - один на текущий объект, а второй - на объект, представляющий его предыдущее состояние, и три функции - старт, закрепление, отмена.
#include <iostream.h> // Некий скромный класс. class CSomeClass{ public: int x; int y; }; // Его оплетка: smart-pointer с поддержкой отмены. class CSimpleTr{ public: CSomeClass* that; // текущее значение CSomeClass* previos; // предыдущее значение public: // конструктор-деструктор-присваивание-копирование CSimpleTr():previos(NULL), that(new CSomeClass){}; CSimpleTr(const CSimpleTr& _st): that(new CSomeClass(*(_st.that))), previos(NULL) {}; ~CSimpleTr(){delete that; delete previos;}; CSimpleTr& operator=(const CSimpleTr& _st) { if (this != &_st) { delete that; that = new CSomeClass(*(_st.that)); } return *this; }; // начало транзакции void BeginTrans() { delete previos; previos = that; that = new CSomeClass(*previos); }; // закрепление void Commit () { delete previos; previos = NULL; }; // отмена транзакции void Rollback() { if (previos != NULL) { delete that; that = previos; previos = NULL; } }; // реализация указателя CSomeClass* operator->() {return that;}; }; int main (void) { // проверим быстренько CSimpleTr lvPair; lvPair->x = 5; lvPair->y = 8; lvPair.BeginTrans(); lvPair->x = 7; lvPair->y = 11; lvPair.Rollback(); lvPair.BeginTrans(); lvPair->x = 7; lvPair->y = 11; lvPair.Commit(); return 0; }
Что может быть проще? Семантика значений, очевидно. Классы должны иметь полный набор обязательных функций, как обычно; в нашем случае класс CSomeClass больно уж тривиален, поэтому сойдет и так. Класс CSimpleTr имеет смысл переписать в виде шаблона, если не хотите его заново переписывать для каждого нового клиента, да еще добавить функцию isStarted(). Функциональность на Ваш вкус и фантазию. MTS, например, восстановит отмененную транзакцию, если Вы после отмены сделаете закрепление: SetAbort(); SetComplete(); сработает так, как будто SetAbort(); не было вовсе.