142
return
*this;
}
operator Type&() { return current; }
void
Snapshot()
{
image
=
current;
have_image
=
true;
}
void Commit() { have_image = false; }
void
Rollback()
{
current = image;
have_image
=
false;
}
bool HaveImage() { return have_image; }
};
Этот шаблон работает со всеми классами, которые удовлетворяют двум условиям:
1. Тип, используемый в качестве параметра, имеет конструктор без аргументов. Он используется в
конструкторе
AutoImage
для инициализации
current
и
image
.
2. Тип, используемый в качестве параметра, допускает присваивание с помощью оператора
=
по
умолчанию, предоставленного компилятором, или перегруженного варианта для данного типа.
Используется в функциях
Shapshot()
и
Rollback()
.
Все встроенные типы (такие как
int
и
double
) удовлетворяют этим условиям.
Подходят и другие
классы, имеющие конструктор без аргументов и рабочий оператор
=
. Чем дольше я имею дело с C++,
тем чаще мне кажется, что нарушение этих требований — проявление злостного непрофессионализма,
за которое следует наказывать парой лет каторжного программирования на BASIC. Заодно я бы издал
закон о том, чтобы конструкторы копий
всегда работали так, как им положено.
Конструктор копий
AutoImage
следует примеру
ImagePtr
и
ImageStackPtr
— он использует
конструктор без аргументов для
создания фиктивного объекта
image
и присваивает
have_image
значение
false
. Оператор
=
делает то же самое, однако в нем не удается
найти удобный способ
уничтожить объект переменной
image
. Мы выбираем меньшее из двух зол — объект остается без
изменений и попросту игнорируется, поскольку переменная
have_image
равна
false
. Если вас это не
устраивает и вы действительно хотите оставить объект
image
неинициализированным до тех пор, пока
в нем не появится настоящий образ, и уничтожить
его после присвоения
false
переменной
have_image
, имеются два возможных решения:
1. Изменить тип
image
с
Type
на
Type*
и выделять для него память оператором
new
. Это
увеличит накладные расходы по сравнению с автоматическими объектами, однако вы сможете
в полной мере контролировать процесс создания и уничтожения.
2. Воспользоваться идиомой «виртуальных конструкторов» из главы 13. Не вдаваясь в
подробности, скажу, что это
позволит вам объявить
image
чем-то приятным для глаза —
например,
unsigned char image(sizeof Type)
— нежели вызывать конструктор и
деструктор Type вручную. Компиляторы С++ недолюбливают подобные фокусы, поэтому,
прежде чем пускаться на авантюры, внимательно прочитайте главу 13.
Если
AutoImage
будет использоваться только для структур или классов, добавьте оператор
->
:
Type* operator->() { return ¤t; }
Обратите внимание: в отличие от предыдущих версий
->
этот оператор не
может быть константной
функцией, поскольку
current
находится внутри
*this
и мы не можем гарантировать, что
->
не будет
использоваться для обращений к неконстантным функциям
current
.