Анимация
JavaScript
|
Главная Библионтека Теперь при компиляции оператора if (sp) в действие вступит оператор Tester*. Этот оператор возвращает нулевое значение тогда и только тогда, когда переменная pointee равна нулю. Класс Tester блокирует оператор delete, поэтому, если какой-нибудь код вызовет оператор delete sp, возникнет ощибка компиляции. Обратите внимание на то, что определение самого класса Tester находится в закрытом разделе класса SmartPtr, поэтому клиентский код ничего не знает о нем. Класс SmartPtr решает проблемы, связанные с проверкой равенства и неравенства, следующим образом. • Определяются операторы == и ! = двух видов (шаблонные или нешаблонные). • Определяется оператор !. • Если допускается автоматическое преобразование интеллектуального указателя в тип, на который он ссылается, то определяется дополнительное преобразование в тип void*, преднамеренно создающее неоднозначность при вызове оператора delete. В противном случае определяется внутренний класс Tester, объявляющий закрытый оператор delete и определяющий преобразование класса SmartPtr в тип Tester*, возвращающее нулевой указатель тогда и только тогда, когда объект, на который ссылается интеллектуальный указатель, является нулевым. 7.9. Отношения порядка к операторам отнощения порядка относятся операторы <, <=, > и >=. Все эти операторы можно свести к одному оператору <. Допускать ли упорядоченность интеллектуальных указателей - вопрос интересный сам по себе. Он основан на двойственной природе указателей, которая часто смущает профаммистов. Указатели одновременно являются итераторами и моникерами (monikers). Будучи итераторами, указатели могут перемещаться по массиву объектов. Арифметика указателей, включая операции сравнения, поддерживает их итеративную природу. В то же время указатели являются моникерами - удобным способом быстрого доступа к объектам. Эту ипостась указателей обеспечивают операторы разыменования * и ->. Двойственная природа указателей иногда может приводить в замешательство, особенно когда указатель нужно использовать только в одном качестве. Для работы с векторами может понадобиться как перемещение по массиву, так и разыменование, в то время как перемещение по связанному списку или манипуляция индивидуальными объектами используют только операцию разыменования. Отношения порядка между указателями определены только для указателей, принадлежащих непрерывному участку памяти. Иными словами, сравнивать между собой можно лишь те указатели, которые ссылаются на элементы одного и того же массива. Определение отношений порядка для интеллектуальных указателей порождает вопрос: могут ли интеллектуальные указатели ссылаться на объекты, хранящиеся в одном и том же массиве? На первый взгляд нет. Основное свойство интеллектуальных указателей заключается в том, что они управляют правами владения объектами, а объекты с разными правами владения не могут принадлежать одному и тому же массиву. Следовательно, было бы рискованно выполнять бессмысленные сравнения. Если отношения порядка действительно необходимы, всегда можно применить явный доступ к обычному указателю, хранящемуся внутри интеллектуального. Здесь снова нужно искать наиболее безопасный и выразительный способ сравнения. В предыдущем разделе мы пришли к выводу, что неявное преобразование интеллектуального указателя в обычный - вопрос выбора. Если пользователь класса SmartPtr позволяет неявное преобразование, то приведенный ниже код будет успешно скомпилирован. SmartPtr<Something> spl, sp2; if (spl < sp2) превращаем интеллектуальные указатели spl и sp2 в обычные, а затем сравниваем их Это значит, что блокировать отношения порядка следует явно. Для этого достаточно объявить их, но не определять. При попытке выполнить такую проверку во время редактирования связей возникнет ошибка. template <c1ass т> class SmartPtr { ... }; template <c1ass T, class u> bool operator<(const SmartPtr<T>&, const u&); He определен template <c1ass т, class u> bool operator<Cconst T&, const SmartPtr<u>&); He определен Благоразумнее выразить остальные операторы через оператор <, оставив его неопределенным. Таким образом, если пользователь класса SmartPtr все же захочет установить между его объектами отношение порядка, ему останется лишь определить соответствующий оператор <. ликвидатор неоднозначностей template <c1ass т, class и> bool operator<Cconst SmartPtr<T>& Ihs, const SmartPtr<u>& rhs) { return Ihs < Getlmpl(rhs); Bee остальные операторы template <c1ass т, class u> bool operator>CsmartPtr<T>& Ihs, const U& rhs) { return rhs < Ihs; ... аналогично для остальных операторов ... Обратите внимание на ликвидатор неоднозначностей. Теперь, если какой-нибудь пользователь библиотеки полагает, что объекты класса SmartPtr<widget> должны быть упорядочены, он может написать следуюший код. inline bool operator<Cconst Smartptr<widget>& Ihs, const widget* rhs) return Getlmpl(Ihs) < rhs; nline bool operator<Cconst widget* Ihs, const SmartPtr<widget>& rhs) return Ihs < Getlmpl(rhs); Жаль, что пользователь должен определять два оператора, а не один, но это все же лучше, чем если бы он определял все восемь операторов. На этом обсуждение отношения порядка между интеллектуальными указателями можно было бы закончить. Ничего интересного в этой проблеме обнаружить не уда-«иось. Иногда очень полезно установить отношение порядка между произвольно раз-Глава 7. Интеллектуальные указатели 201 мешенными в памяти объектами, а не только между объектами, хранящимися в одном и том же массиве. Например, иногда нужно хранить вспомогательную информацию о каждом объекте и быстро ее извлекать. Упорядоченный ассоциативный массив (тар) адресов этих объектов - очень эффективное решение этой проблемы. Стандарт языка С++ позволяет легко воплощать такие замыслы. Хотя сравнение указателей на произвольно размещенные объекты не определено, стандарт гарантирует, что оператор std: :less даст осмысленные результаты, если применить его к двум указателям, имеющим одинаковый тип. Поскольку стандартные ассоциативные контейнеры используют оператор std: :less в качестве отношения порядка, заданного по умолчанию, можно вполне безопасно пользоваться ассоциативными массивами, ключами которых являются указатели. Класс SmartPtr также поддерживает эту идиому. Следовательно, класс SmartPtr осуществляет специализацию оператора std: :less. Эта специализация заключается в простой переадресации вызова оператору std: :less для обычных указателей. namespace std { template <class т> struct less<SmartPtr<T> > public binary function<SmartPtr<T>, SmartPtr<T>, bool> bool operatorO const SmartPtr<T>& Ihs, const SmartPtr<T>& rhs) const { return less<T*>OCGetImpl (Ihs) , Getlmpl (rhs)) ; Итак, класс SmartPtr не определяет операторы упорядочения по умолчанию. В нем объявляются (но не реализуются) два обобщенных оператора <, а остальные операторы выражаются через них. Пользователь может использовать либо специализированную, либо обобщенную версии оператора <. Класс SmartPtr осуществляет специализацию оператора std::less, устанавливая отношение порядка между произвольными интеллектуальными указателями. 7.10. Обнаружение и регистрация ошибок в разных приложениях требуется разная степень безопасности, обеспечиваемая интеллектуальными указателями. Некоторые программы выполняют интенсивные вычисления, поэтому нужно оптимизировать их быстродействие, другие (фактически большинство) интенсивно осуществляют операции ввода-вывода информации и нуждаются в эффективных средствах проверки данных, не снижающих их производительность. Чаше всего в приложении нужны обе модели: низкая безопасность, высокая скорость в некоторых критических местах и высокая безопасность, низкая скорость - в остальных частях программы. Вопросы, связанные с проверкой интеллектуальных указателей, можно разделить на две категории: инициализация проверки и проверка перед разыменованием. 7.10.1. Проверка во время инициализации Может ли интеллектуальный указатель принимать нулевое значение? 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |