147
private:
SafeSet*
locks;
void
AddLock(Lock*);
//
Включить блокировку в транзакцию
public:
~Transaction();
void
Commit();
//
Закрепить все образы
void
Rollback();
//
Отменить все образы
bool
OwnsLock(Lock*);
//
Истина, если блокировка
// принадлежит транзакции
};
Класс
Transaction
поддерживает коллекцию блокировок с помощью гипотетического шаблона
Collection
. Функция
RegisterLock()
включена в базовый класс
Lock
и потому может обратиться к
закрытой функции
AddLock()
класса
Transaction
. Дружба не наследуется, поэтому объявление
другом класса
Lock
не делает друзьями его производные классы. Реализации выглядят довольно
просто.
void Transaction::AddLock(Lock* lock)
{
*locks += lock;
// Использует перегрузку += для коллекции
}
void Transaction::Commit()
{
SafeSetIterator* iter = locks->Iterator();
while
(iter->More())
iter->Next()->Commit();
delete
iter;
}
void Transaction::Rollback()
{
SafeSetIterator* iter = locks->Iterator();
while
(iter->More())
iter->Next()->Rollback();
delete
iter;
}
bool Transaction::OwnsLock(Lock* lock)
{
return *locks >= lock;
}
Предполагается, что шаблон
Collection
содержит функцию
DeleteAll()
для удаления всех
объектов; что перегруженный оператор
+=
(операторная функция
operator+=(Type*)
) включает
элемент в коллекцию; что перегруженный оператор
>=
определяет принадлежность к коллекции, а
функция
Iterator()
возвращает вложенный итератор. Это обобщенные условия; используйте те,
которые действуют в вашем случае.
Класс ConstPtr
Классы, производные от
Lock
, должны ссылаться на нечто близкое к указателям, доступным только
для чтения, с которыми на страницах книги вы уже познакомились.
template
class LockPtr;
//
Ссылка вперед на класс, производный от Lock
148
template
class ConstPtr {
friend class LockPtr;
private:
Type*
old_image;
//
Образ перед транзакцией
LockPtr*
lock;
//
Текущая блокировка, если она есть
~ConstPtr() { delete old_image; }
ConstPtr& operator=(const ConstPtr& cp)
{ return *this; } // Присваивание не разрешается
public:
ConstPtr() : old_image(NULL), lock(NULL) {}
ConstPtr(const
ConstPtr&
cp)
: old_image(new Type(*(cp.old_image))), lock(NULL) {}
const Type* operator->() const { return old_image; }
LockPtr& Lock(Transaction* t);
};
template
LockPtr& ConstPtr::Lock(Transaction* t)
{
if (lock != NULL && !t->OwnsLock(lock))
//
Конфликт – уже имеется другой владелец
else
{
lock = new LockPtr(t, this);
return
*lock;
}
}
Новый объект
ConstPtr
можно сконструировать на базе старого (хотя новый создается без
блокировки), однако нам придется внести изменения для присваивания, которое разрешено только для
LockPtr
, но не для
ConstPtr
. Для этой цели мы определяем фиктивный оператор
=
и делаем его
закрытым, чтобы до него никто не добрался. Поскольку указатель является ведущим, его удаление
приводит и к удалению указываемого объекта (самое радикальное изменение, которое только можно
представить). По этой причине конструктор также объявлен закрытым, чтобы никто не попытался
удалить
ConstPtr
.
Конфликт в функции
Lock
можно обработать разными способами:
•
Инициировать исключение.
•
Изменить интерфейс и возвращать вместе с блокировкой флаг, показывающий, успешно ли
прошла блокировка.
•
Возвращать
LockPtr*
со значением
NULL
, свидетельствующем о неудачной
блокировке.
•
Возвращать конфликтную блокировку с перегруженным оператором
!
, с помощью которого
можно проверить правильность блокировки.
•
Предоставить отдельную функцию
CanLock(Transaction*)
, которая возвращает логическое
значение.
Выбор зависит от стиля. Вариант с исключением не так уж очевиден; неудача при блокировке
представляет собой вполне разумный исход.
Do'stlaringiz bilan baham: |