Анимация
JavaScript
|
Главная Библионтека до полного вычисления самого большого вмещающего выражения. Если ваш компилятор следует этому правилу, то строку { return *(new Integer(&(va1ue + i.value)); } можно записать в виде { return Integer(va1ue + i.value); } Аналогично создается и PNumber. Возвращаемое значение будет оставаться действительным внутри вычисляемого выражения. Любая ссылка, которая может существовать за пределами вмещающего выражения, должна быть получена вызовом функции makeClone(). Эта функция создает PNumber в куче или присваивает другой Number виртуальным оператором = для невидимых ведущих указателей, о которых говорилось выше. Чтобы ликвидировать эти раздражающие мелкие утечки памяти, можно воспользоваться приемами уплотнения и сборки мусора, рассмотренными в части 4. Самомодификация и переходимость Невидимый ведущий указатель, как и любой умный указатель, может интерпретироваться как переходный тип. Если просто заменить указываемый объект каким-нибудь производным классом, вы фактически изменяете тип всей видимой клиенту комбинации. На этом основано решение проблемы оператора +=, которая требует самомодификации левого операнда, а также возможного оперативного изменения типа «на ходу». Если правый операнд Complex складывается с левым операндом Integer, тип левого операнда приходится менять. В файле number.h class NBase; Клиентам об этом ничего знать не нужно class Number { protected: Number(const Number&) {} Number() {} public: virtual NBase& AddTo(const NBase&) = 0; virtual Number& operator+(const Number&) = 0; И т.д. В файле number.cpp class Integer; class Real; class PNumber : public Number { private: NBase* number; protected: virtual NBase& AddTo(const NBase& n) const { return number->AddTo(n); } #2 public: PNumber(NBase* n) : number(n) {} virtual Number& operator+(const Number& n) const number = &(n.AddTo(*number)); #1 - замена return *this; class NBase : public Number { Промежуточный базовый класс Традиционная двойная передача в NBase public: virtual NBase& operator+=(const Integer&) const = 0; virtual NBase& operator+=(const Rea1&) const = 0; И т.д. virtual NBase& AddTo(const NBase&) const = 0; virtual Number& operator+(const Number& n) const { return Integer(0); } Заглушка не вызывается class Integer : public NBase { private: int value; protected: virtual NBase& operator+=(const Integer& i) const if (value + i.value достаточно мало) { value += i.value; return *this; else { ArbitraryPrecisionInteger api(value); api += i.value; delete this; return api; public: Integer(int i) : value(i) {} virtual NBase& AddTo(const NBase& n) const { return n + *this; } #3 class Real : public NBase { ... }; Все как и раньше, разве что операторы + превратились в +=, а двойная передача теперь проходит через +=(левый, правый) и AddTo(правый , левый), чтобы мы могли различать два порядка аргументов. Это важно, поскольку в конечном счете мы хотим заменить указываемый объект левого операнда новым. Это происходит в двух местах: 1. Операторная функция PNumber::operator+=(const Number&) автоматически заменяет число полученным новым значением. 2. Операторная функция Integer::operator+=(const Integer&) возвращает управление, если ей не приходится изменять тип; в противном случае после удаления своего объекта она возвращает новый объект другого типа. По вполне понятным причинам я назову вторую из этих функций заменяющей. Заменяющие функции обладают одной экзотической (если не выразиться сильнее) особенностью: нельзя рассчитывать, что адрес объекта перед вызовом остается действительным и после вызова. Разумеется, пользоваться этим обстоятельством можно лишь в том случае, если эту логику удастся запрятать в самую глубокую и темную дыру, чтобы никто в нее не сунулся, но если это удается сделать, хлопотные алгоритмы невероятно упрощаются. Показанный пример надежно работает, пока PNumber действует как ведущий указатель и пока можно гарантировать, что ни один объект, производный от NBase, не будет существовать без ссылающегося на него PNumber. В нашем случае, когда все прячется в файле .cpp, дело обстоит именно так. Для такой простой проблемы программа получилась довольно большой. Я не утверждаю, что ваши хлопоты оправдаются во всех проектах. Мое решение в основном предназначено для ситуаций, в которых вы тратите много времени на разработку иерархии классов многократного использования и можете позволить себе потратить время на повышение модульности. Я привел его, поскольку оно соответствует основной идее книги - выжать из С++ все возможное и невозможное и щедро разбросать головоломки, представляющие интерес даже для самых выдающихся экспертов. Множественная двойная передача Множественная передача и все ее разновидности тоже имеют свои аналоги в мире невидимых указателей, но я бы не рискнул рекомендовать их для использования в реальных проектах. Применение невидимых указателей В оставшихся главах речь пойдет об управлении памятью. Сейчас я забегу вперед и перечислю некоторые стратегии управления памятью, которые упрощаются за счет применения невидимых указателей. Кэширование Кэширование уже упоминалось в контексте обычных умных указателей, однако для невидимых указателей оно приобретает дополнительное значение. Невидимый указатель может содержать адрес указываемого объекта на диске и в последнюю секунду перед тем, как передавать полномочия объекту, считывать объект. Все это происходит незаметно для клиента, поэтому со схемой можно экспериментировать, обходясь без изменений больших объемов кода. Распределенные объекты и посредники Раз уж мы заговорили об этом, стоит ли ограничиваться диском, если доступны и другие компьютеры? Благодаря невидимым указателям клиент не заботится о том, находится ли объект, к которому он обращается, на том же компьютере или же он затерян в глобальной сети где-то в горах Тибета. Когда невидимый указатель используется для делегирования удаленному объекту, он называется посредником (proxy) для этого объекта. Нетривиальные распределенные архитектуры В некоторых распределенных архитектурах посредник должен содержать локальный кэшированный образ удаленного объекта. Это приводит к снижению сетевого трафика для редко изменяемых объектов. В частности, в данной стратегии нередко используется схема с главнгм маркером (master token): чтобы обновить объект, вы должны сначала получить его копию у процесса и компьютера, которым она принадлежит в настоящий момент. Все это может происходить незаметно для клиента, с использование невидимых указателей и различением константных и неконстантных функций. Невидимые указатели - это замечательная лаборатория, в которой можно поэкспериментировать с различными стратегиями и выяснить, какая из них работает лучше. Методики, в которых реализуются переходные типы, также позволяют оперативно заменять одно представление объекта другим. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 [ 58 ] 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |