|
Программирование >> Полиморфизм без виртуальных функций в с++
Если разрешить псрефузку . для класса X, то не удастся обычным способом обратиться к его членам; придется использовать указатель и оператор ->, хотя -> и & тоже могут быть переопределены. Эти аргументы являются весомыми, но не решающими. В частности, в 1990 т. Джим Эдкок (Jim Adcock) предложил разрешить перегрузку оператора . точно так же, как оператора ->. Зачем .может понадобиться перегрузка operator. () ? Чтобы создать класс, работающий как описатель (handle) или заместитель (proxy) другого класса, в которо.м и выполняются основные действия. В качестве примера, приводившегося во время первых обсуждений перефузки operator. (), рассмотрим класс целых чисел повышенной разрядности: class Num { ... public: Num& operator=(const Num&); int operator[](int); получить одну цифру Num operator+(const Num&); void truncateNdigits(int); отбросить . . . Я надеялся определить класс RefNum, который ведет себя как Num&, но выполняет некоторые дополнительные действия. Например, если я могу написать void f(Num а, Num b, Num с, int i) { ... с = a+b; int digit = c[i]; с.truncateNdigits(i) ; ... TO хотелось иметь возможность написать и такое: void g(RefNum а, RefNum b, RefNum с, int i) { . . . с = a+b; int digits = с[i]; с.truncateNdigits(i) ; ... Допустим, что operator. () определен полностью аналогично operator-> (). Сначала попробуем очевидное определение RefNum: class RefNum { Num *p; public: RefNum(Num& a) { p = &a; } Num& operator.!) { do something(р); return *p; } void bind(Num& q) { p = &q; } К сожалению, это не дает нужного результата, так как . не указана явно во всех случаях: с = а+Ь; точки нет int digits = c[i]; точки нет c.truncateNdigits(i); вызов operator.!) Придется написать функции-переходники, гарантирующие, что при при.мене-нии операторов к Ref Num выполняются правильные действия: class RefNum { Num *р; public: RefNum(Num& a) { p = &a; } NumSt operator.!) { do something !p) ; return *p; ) void bind!Num& q) { p = &q; } функции-переходники RefNum& operator=!const RefNum& a) { do somethingp); *p=*a.p; return *this; } int operator[]!int i) { do something!p); return (*p)[i]; } RefNum operator*!const RefNum& a) { do something!p); return RefNum!*p+*a.p); } Это чересчур утомительно. Поэтому мы с Эндрю Кенигом и многие другие подумывали о том, чтобы применять operator. () к каждой операции над RefNum. В этом случае нащ пример работал бы правильно и при исходном определении RefNum. Однако если применять operator. () таким образом, то для доступа к любому члену RefNum пришлось бы использовать указатель: void h!RefNum г, Num& х) { r.bind!x); ошибка: Num::bind отсутствует !&r)->bind!x); правильно: вызывается RefNum::bind Пользователи С-ы- разошлись во мнениях относительно того, какая интерпретация operator. () лучше. Я склоняюсь к тому, что если уж разрешать перегрузку operator. (), то он должен вызываться как для явных, так и для неявных операций. В конце концов, определяется он для того, чтобы не писать функции-переходники. Если неявные использования . не интерпретируются с помощью operator. О , то по-прежнему приходится писать множество переходников либо не перегружать этот оператор вообше. Если operator. () перегружен, то эквивалентность а.т и (&а) ->т более не гарантируется. Но ее .можно поддержать, нужным образом определив operator-> (} Р++; p.operator++() + +р; р.operator++() Некоторые, в особенности Брайан Керниган, от.мечали, что такое ограничение неестественно для С и не дает программиста.м определить класс, который можно было использовать вместо обычного указателя. Конечно, при проектировании .механизма перегрузки операторов в С++ я ду-,мал о раздельной перегрузке префиксного и постфиксного инкрементов, но решил, что вводить для этой цели дополнительный синтаксис не стоит. Многочисленные предложения по этому поводу, полученные за несколько лет, доказали мою неправоту. Надо было лишь найти способ выразить различие между префиксной и постфиксной нотацией с помощью минимальных изменений. Я раздумывал об очевидном решении - добавить в С++ ключевые слова prefix и postfix: class Ptr to X { . . . и operators (), так что я не считаю это серьезной проблемой. Однако если поступить так, то получить доступ к членам класса, реализующего умную ссылку, пе удастся вообще. Например, RefNum: :bind () станет недоступной функцией. Важно ли все выщесказанное? Некоторые отвечают: Нет, поскольку умную ссылку, как и обычную, должно быть невозможно привязать к новому объекту . Однако мой опыт показывает, что для умных ссылок операция 1ювторной привязки или какая-то другая часто необходимы. Итак, мы оказались в затруднении: можно либо поддержать эквивалентность а. m и (&а) ->т, либо сохранить доступ к членам класса, реализующего у.мную ссылку, по пе то и другое одновременно. Один из способов разрешить дилемму - использовать operator. () для а .т только в том случае, если класс ссылки сам не содержит члена с именем т. Мне такое решение правится больше всего. Однако по поводу важности перегрузки operator. () нет единого мнения, она не включена в С++, и ожесточенные споры вокруг этого вопроса продолжаются. 11.5.3. Перегрузка операторов инкремента и декремента Перегружать операторы инкремента ++ и декре.мента -- пользователи .могли всегда. Но в версии 1.0 не было .механизма для различения префиксной и 1юст-фиксной нотации. Так, в классе class Ptr { . . . void operator++(); один и тот же Ptr: : operator++ {) использовался в следующих предложениях: void f(Ptr& р) {
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |