144
Конечно, это намного упростило бы жизнь, но назвать эти загадочные
ограничения бесполезными
нельзя — они дают знатокам С++ хорошую тему для разговоров на семинарах с коктейлями.
Раз уж речь зашла об ограничениях С++, упомяну еще об одном. Взгляните на приведенный выше код
класса
Foo
. Работа некоторых его функций сводится к вызову одной и той же функции для всех
переменных класса и в более общем случае — базовых классов. Скажем,
Foo::Commit()
просто
вызывает
Commit()
для всех переменных. Весь повторяющийся код приходится писать вручную; в
языке сильно не
хватает макросредств, которые бы позволяли сказать: «Вызвать функцию
Commit()
для каждой переменной класса». Компилятор знает, как составить список такого рода (и использует его
в конструкторах), но вам ни за что не скажет.
Образы указателей
У шабона
AutoImage
есть одно довольно занятное применение — им
можно воспользоваться для
создания образов
*
-указателя. В некоторых ситуациях не хочется создавать лишние копии
указываемого объекта
только чтобы следить за тем, на что ссылался указатель в прошлой жизни.
Собственно, дело обстоит так каждый раз, когда указатель не является ведущим. Указатель также
помогает следить за объектами, которые были созданы или уничтожены в процессе транзакции.
AutoImage
f;
Теперь вы можете восстановить состояние указателя
f
в начале транзакции, в том числе и
NULL
. Тем не
менее, существует веский довод в пользу создания специализированного шаблона для
*
-указателей —
необходимость перегрузки оператора
->
, чтобы указатель образов можно было использовать в левой
части выражений (что-нибудь типа
ptr->MemberOfPointer();
). Для
*
-указателей
AutoImage
похож
на глупые указатели, с которыми мы рассправились в начале главы 5. Следующий шаблон больше
напоминает обычные умные (но не ведущие!) указатели.
template
class PtrImage {
private:
Type*
current;
Type*
image;
bool
have_image;
//
Истина, если образ существует
public:
PtrImage() : current(NULL), image(NULL), have_image(false) {}
PtrImage(const
PtrImage&
pi)
: current(pi.current), image(NULL), have_image(false) {}
PtrImage& operator=(const PtrImage& pi)
{
if (this != &pi)
current
=
pi.current;
return
*this;
}
PtrImage&
operator=(Type*
t)
{ current = t; return *this; }
operator Type*() { return current; }
Type* operator->() const { return current; }
bool operator!() { return current == NULL; }
void
Snapshot()
{
image
=
current;
have_image
=
true;
}
void Commit() { image = NULL; have_image = false; }