Анимация
JavaScript
|
Главная Библионтека Истинное, всеобъемлющее и надежное рещение этой дилеммы заключается в полной перефузке всех операторов по отдельности. При таком подходе все операции, имеющие смысл для обычных указателей, окажутся применимыми и ддя интеллектуальных указателей без каких-либо побочных эффектов. Вот как можно реализовать эту идею. template <class т> class SmartPtr { public: bool operatorIC) const Допускает оператор "if C!sp) ..." { return pointee = 0; inline friend bool operator==Cconst SmartPtr& Ihs, const T* rhs) return lhs.pointee == rhs; inline friend bool operator==Cconst т* Ihs, const SmartPtr* rhs) return Ihs == rhs.pointee ; inline friend bool operator!=Cconst SmartPtr& Ihs, const T* rhs) return lhs.pointee != rhs; inline friend bool operator!=Cconst т* Ihs, const SmartPtr& rhs) return Ihs != rhs.pointee ; >; "" . Да, это неприятно, однако этот подход решает проблемы, касающиеся практически всех сравнений, включая сравнение с литеральным нулем. Операторы пересылки, содержащиеся в этом фрагменте кода, передают операторы, которые код пользователя применяет к интеллектуальным указателям, обычным указателям, скрытым внуфи. Это самое реалистичное рещение. Однако проблема рещена не полностью. Если в профамме предусмафивается автоматическое преобразование указателей, остается риск, связанный с неоднозначностью такой операции. Допустим, у нас есть класс Base и класс Derived, производный от него. Тогда в следующем коде будет содержаться неоднозначность. SmartPtr<Base> sp; Derived* р; if (sp == p) {} Ошибка! Существует неоднозначность между проверкой CBase*)sp == CBase*)p и функцией operator==Csp, CBase*)p) Действительно, разработка интеллектуальных указателей - занятие не ддя слабонервных. Но и это еще не все. Кроме определения операторов == и I =, мы можем создать их шаблонные версии. template <class т> class SmartPtr public: как и раньше ... template <c1ass и> inline friend bool operator==Cconst smartPtr& Ihs, const u* rhs) return lhs.pointee == rhs; emplate <class u> nline friend bool operator==Cconst u* Ihs, const SmartPtr& rhs) return Ihs == rhs.pointee ; .. аналогично определенный оператор != ... Шаблонные операторы выполняют сравнения с любым типом указателей, устраняя неоднозначность. Если все так хорошо, зачем сохранять обычные, не шаблонные операторы, рабо-таюшие с типами объектов, на которые ссылаются указатели? Эти операторы никогда не пройдут процедуру сопоставления, потому что шаблоны сопоставляют любые типы указателей, в том числе и типы объектов, на которые ссылаются указатели. Однако "никогда не говори никогда". При выполнении проверки if (sp == 0) компилятор пытается выполнить следующие сопоставления. • Шаблонные операторы (templated operators). Они не проходят проверку, поскольку нуль не является типом указателей. Литеральный нуль можно неявно преобразовать в тип указателей, однако шаблонное сопоставление не предусматривает преобразований типов. • Нешаблонные операторы (nontemplated operators). Исключив шаблонные операторы, компилятор переходит к проверке нешаблонных. Один из этих операторов оказывается подходящим после выполнения неявного преобразования литерального нуля в тип указателя. Если бы нешаблонных операторов не было, проверка завершилась бы сообщением об ошибке. Итак, и шаблонные, и нешаблонные операторы одинаково необходимы. Посмотрим теперь, что произойдет при попытке сравнения двух классов SmartPtr, конкретизированных разными типами. SmartPtr<Apple> spl; SmartPtr<Orange> sp2; if (spl == sp2) Компилятор споткнется на этом сравнении из-за неоднозначности: в каждой из этих двух конкретизации определен оператор ==, и компилятор не знает, который из них выбрать. Мы можем избежать этой проблемы, определив "ликвидатор неоднозначностей". template <class т> class SmartPtr { public: ликвидатор неоднозначностей template <class u> bool operator==Cconst SmartPtr<u>& rhs) const 198 Часть II. Компоненты return pointee == rhs.pointee ; Аналогично для оператора != Новый оператор является членом класса, предназначенным исключительно для сравнения объектов классов SmartPtr<.. .>. Прелесть этого ликвидатора неоднозначностей заключается в том, что он сравнивает интеллектуальные указатели, как обычные указатели. Если сравнить два интеллектуальных указателя на объекты классов Ар-р1е и Orange, код будет, по существу, эквивалентным сравнению обычных указателей на классы Apple и Orange. Если это сравнение имеет смысл, код компилируется, в противном случае выдается сообщение об ошибке. SmartPtr<Apple> spl; SmartPtr<Orange> sp2; if (spl == sp2) Семантически эквивалентно сравнению spl.pointee == sp2.pointee Остался один неприятный артефакт - непосредственная проверка if (sp). Вот это уже действительно интересно! Оператор if применяется только к выражениям арифметического типа или к указателям. Следовательно, чтобы скомпилировать оператор if (sp), нужно определить автоматическое преобразование интеллектуального указателя в арифметический тип или в обычный указатель. Преобразовывать интеллектуальный указатель в арифметический тип не рекомендуется по тем же причинам, которые были указаны при преобразовании в булевский тип. Указатель - это не арифметический тип, и точка! Преобразование интеллектуального указателя в обычный имеет больше смысла, но тут возникают новые проблемы. Если нужно преобразовать интеллектуальный указатель в тип, на который он ссылается (см. предьшущий раздел), у нас есть выбор: либо рисковать, допуская возможность непредвиденного вызова оператора delete, или воздержаться от проверки if (sp). Что выбрать: неудобство или опасность? Следует предпочесть безопасность, поэтому проверку if (sp) выполнять не следует. Вместо этого можно выбрать проверку ifCsp != 0) или более вычурное выражение ifC! !sp). Если вы не хотите предусматривать преобразование интеллектуального указателя в тип, на который он ссылается, можно прибегнуть к интересному трюку, который позволит все же выполнить проверку if (sp). Внутри шаблонного класса SmartPtr нужно определить внутренний класс Tester и преобразование в тип Tester*, как показано ниже. template <class т> class SmartPtr { class Tester { void operator deleteCvoid*); public: operator Tester*C) const { if C!pointee ) return 0; static Tester test; return &test; 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 |