149
Класс LockPtr
Ага! Мы подошли к центральной идее всей концепции — указателям, которые разрешают обновление
указываемого объекта. Предтранзакционный (предназначенный для отмены) образ хранится в
ConstPtr
, а текущий обновленный образ доступен только через
LockPtr
. Класс
LockPtr
содержит
уже знакомые функции
Rollback()
и
Commit()
. В функции
Snapshot()
нет необходимости,
поскольку
LockPtr
при необходимости создает образы в операторе
->
.
template
class LockPtr : public Lock {
friend class ConstPtr;
private:
ConstPtr*
master_ptr;
Type*
new_image;
Transaction*
transaction;
LockPtr(Transaction* t, ConstPtr* cp);
virtual
~LockPtr();
virtual void Rollback();
virtual void Commit();
public:
Type* operator->() const { return new_image; }
};
template
LockPtr::LockPtr(Transaction* t, ConstPtr* cp)
: transaction(t), master_ptr(cp), new_image(new Type(*(cp->old_image)))
{
}
template
LockPtr::~LockPtr()
{
//
В сущности происходит откат
delete
new_image;
//
Отказаться от изменений
master_ptr->lock = NULL; // Оставить ConstPtr
}
template
void LockPtr::Rollback()
{
delete
new_image;
new_image = new Type(*(master_ptr->old_image));
}
template
void LockPtr::Commit()
{
delete
master_ptr->old_image;
master_ptr->old_image = new_image;
// Переместить в master_ptr
new_image = new Type(*new_image);
// Нужна новая копия
}
Деструктор объявлен закрытым, чтобы никто не мог напрямую удалить
LockPtr
. Вместо этого
транзакция-владелец должна сделать это через базовый класс
Lock
. Функции
Rollback()
и
Commit()
объявлены виртуальными, чтобы с их помощью можно было решать задачи, относящиеся к
150
конкретному типу (например, создание и уничтожение образов). Обе функции после завершения
оставляют
ConstPtr
заблокированным.
Создание и уничтожение объектов
Пора заполнить кое-какие пробелы. Раз уж наши транзакции достаточно сложны, чтобы для них была
оправдана вся эта возня, они наверняка будут создавать или уничтожать объекты. Операции создания и
уничтожения также должны быть отменяемыми. Если объект создавался, операция отмены должна его
уничтожать, а если уничтожался — возвращать его из мертвых. Для этого придется внести изменения
как в класс
ConstPtr
, так и в класс
LockPtr
. Мы уже сделали первый шаг в этом направлении,
объявив деструктор
ConstPtr
закрытым, чтобы им могли воспользоваться только
ConstPtr
или его
друзья. Давайте разберемся с оставшимися проблемами.
Изменения в классе ConstPtr
Несомненно, создание указываемого объекта представляет собой изменение и потому должно
осуществляться через
LockPtr
. Но для того чтобы получить
LockPtr
, мы должны сначала иметь
ConstPtr
и его функцию
Lock()
. Следовательно, только что описанный конструктор
ConstPtr
работать не будет — он создает уникальный объект перед вызовом
Lock()
.
ConstPtr
должен
находиться в состоянии
NULL
до тех пор, пока
LockPtr
не выделит память под объект и не закрепит
эти изменения. В
ConstPtr
необходимо внести следующие изменения:
•
В конструкторе без аргументов присваивать переменной
old_image
значение
NULL
.
•
Добавить оператор
!
, который проверяет, равен ли адрес значению
NULL
.
•
Инициировать исключение в операторе
->
, если адрес равен значению
NULL
.
•
Либо запретить копирование, либо присвоить копии
old_image
значение
NULL
.
Проблема с обычным конструктором копий
ConstPtr
заключается в том, что он может создать новую
копию указываемого объекта, но не позволит отменить ее создание. Ниже приводится новая версия
конструктора
ConstPtr
. Определения функций, не изменившиеся по сравнению с показанной выше
упрощенной версией не показаны.
private:
ConstPtr(const ConstPtr&) : old_image(NULL), lock(NULL) {}
public:
ConstPtr() : old_image(NULL), lock(NULL) {}
bool operator!() { return old_image == NULL; }
const Type* operator->() const
{
if (old_image == NULL)
//
Исключение
return
old_image;
}
Изменения в классе LockPtr
Отныне
LockPtr
предстоит выполнять намного больше работы:
•
Он должен при необходимости создавать указываемый объект по требованию. Для этого в него
будет добавлена функция
Make()
.
•
Оператор
->
должен инициировать исключение, если адрес равен
NULL
.
Ниже приведены определения только изменившихся функций.
// В объявлении LockPtr
public:
void
Make();
//
Создать новый указываемый объект
void
Destroy();
//
Уничтожить указываемый объект
Do'stlaringiz bilan baham: |