|
Программирование >> Синтаксис инициирования исключений
void Ro11back() Вернуться к самому старому образу Type* old = history.PopO; Type* oldere = NULL; if (old != NULL) { Хотя бы один раз while ((older = history.PopO) != NULL) { delete old; old = older; delete current; current = old; Type* operator->() const { return current; } Хранение отдельного стека в каждом указателе оправданно для транзакций, в которых участвует небольшое количество объектов. Но если одной транзакции приходится отслеживать множество обновляемых объектов, кучу мелких стеков лучше объединить в один большой. Мы сделаем это позднее, когда речь пойдет о транзакциях. Образы автоматических объектов Концепцию образа можно немного обобщить, чтобы она распространялась не только на объекты, созданные оператором new и обслуживаемые *-указателями, но и автоматические объекты. Автоматическими считаются стековые объекты, а также переменные и компоненты базовых классов вмещающего объекта независимо от того, выделялась память под вмещающий объект динамически или нет. template <c1ass Type> class AutoImage { private: Type current; Type image; bool have image; Истина, если образ существует public: AutoImage() : have image(fa1se) {} AutoImage(const AutoImage<Type>& ai) : current(ai.current), image(), have image(fa1se) {} AutoImage<Type>& operator=(const AutoImage<Type>& ip) if (this != &ip) { current = ip.current; have image = false; return *this; AutoImage<Type>& operator=(const Type& t) current = t; return *this; operator Type&() { return current; } void Snapshot() image = current; have image = true; void Commit() { have image = false; } void Ro11back() current = image; have image = false; bool HaveImage() { return have image; } Этот шаблон работает со всеми классами, которые удовлетворяют двум условиям: 1 . Тип, используемый в качестве параметра, имеет конструктор без аргументов. Он используется в конструкторе AutoImage для инициализации current и image. 2. Тип, используемый в качестве параметра, допускает присваивание с помощью оператора = по умолчанию, предоставленного компилятором, или перегруженного варианта для данного типа. Используется в функциях Shapshot() и Ro11back(). Все встроенные типы (такие как 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. Воспользоваться идиомой виртуальных конструкторов из главы 1 3. Не вдаваясь в подробности, скажу, что это позволит вам объявить image чем-то приятным для глаза - например, unsigned char image(sizeof Type) - нежели вызывать конструктор и деструктор Type вручную. Компиляторы С++ недолюбливают подобные фокусы, поэтому, прежде чем пускаться на авантюры, внимательно прочитайте главу 1 3. Если AutoImage будет использоваться только для структур или классов, добавьте оператор ->: Type* operator->() { return ¤t; } Обратите внимание: в отличие от предыдущих версий -> этот оператор не может быть константной функцией, поскольку current находится внутри *this и мы не можем гарантировать, что -> не будет использоваться для обращений к неконстантным функциям current. Следующий класс демонстрирует возможное использование этого шаблона. Вмещающему объекту Foo незачем создавать свой образ, как в предыдущих указателях на объекты, поскольку все его переменные способны поддерживать свои образы по отдельности. class Foo { private: AutoImage<int> some integer; AutoImage<Bar> bar; public: void Ro11back() some i nteger.Rol 1back(); bar.Ro11back(); void Commit() some i nteger.Commit(); bar.Commit(); void Snapshot() some integer.Snapshot(); bar.Snapshot(); int ProvideInt() const { return some integer; } void ChanheInt(int new va1ue) if (!some integer.HaveImage()) some i nteger.Snapshot(); int&(some integer) = new va1ue; const Bar& ProvideBar() const { return bar; } Bar& UpdateBar() if (!bar.HaveImage()) bar.Shapshot(); return Bar&(bar); Предполагается, что Bar соответствует необходимым условиям. Последние четыре функции перед тем, как обновлять переменную, создают моментальный снимок объекта. Для int получение копии по определению является константным по отношению к копируемой переменной. При вызове функции, изменяющей значение переменной, настает время делать снимок. Для работы с другой переменной, bar, предоставляется как константная, так и неконстантная функция. Конечно, хотелось бы просто перегрузить функцию ProvideBar(), чтобы одна перегруженная версия возвращала const Bar&, а другая - неконстантный Bar&, но тогда их сигнатуры будут совпадать. Помните: две функции не могут иметь одинаковые имена и аргументы и отличаться только типом возвращаемого значения. Я никогда не понимал этого ограничения С++, которое запрещает создавать константную и неконстантную версию оператора ->: const Type* operator->() const; Снимок не создается Type* operator->() const; Создает снимок
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |