Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2005.03.20;
Скачать: [xml.tar.bz2];

Вниз

Перегрузка операторов в Pascal   Найти похожие ветки 

 
dr Tr0jan   (2005-02-26 15:22) [0]

Возможно ли перегрузить стандартны оператор в Borland Pascal, Object Pascal, Delphi?
Если можно, то как?


 
Просто Джо ©   (2005-02-26 15:25) [1]

В Делфи, АФАИР, низзя. Никак. Можно во FreePascal, кажется.


 
TUser ©   (2005-02-26 15:27) [2]

Судя по документации, во FreePascal"е можно. Синтаксис надо посмотреть в доке, я его наизусть не помню.

И не стал бы я этим пользоваться. Совместимость с Delphi теряется, а выгоды незначительные. Проще писать все в префиксной записи, вызывая функции.


 
kaZaNoVa ©   (2005-02-26 15:40) [3]

не тормози, перезагрузи .. )


 
dr Tr0jan   (2005-02-26 15:58) [4]

> Проще писать все в префиксной записи, вызывая функции.
Спасибо, дельный совет.

Да мне вообще-то это надо было в Borland Pascal"e. Вот постфиксной попробую.


 
вредитель ©   (2005-02-26 17:58) [5]

...а выгоды незначительные

Перегрузка операторов очень помогает медитативным практикам.

Дабы не быть голословным, приведу отрывок из книго Лдефри Элджера с шуточным названием "C++" (надеюсь он меня простит), ибо не смогу объяснить этого лучше, чем он.

Множественная передача
Самый  распространенный   пример  гомоморфной   иерархии  — набор  классов,
соответствующих различным  видам чисел: целым,  комплексным, вещественным и
т.д. Класс-предок такой иерархии может  называться Number и иметь интерфейс
следующего вида:

class Number {
public:
 virtual Number operator+(const Number&) = 0;
 virtual Number operator-(const Number&) = 0;
 // И т.д.
};

class Integer : public Number {
private:
 int i;
 public:
 Integer(int x) : i(x) {}
 virtual Number operator+(const Number&);
 // И т.д.
};

На бумаге  все выглядит  проще,  чем в  действительности.  Как  реализовать
Integer::operator+(Number&), если нам  не известно, что в скобках находится
вовсе не  Number, а  некоторый производный  класс?  Для каждой  пары типов,
участвующих в сложении,  существует  свой алгоритм.  Суммирование Complex +
Integer отличается от  Integer + Real, которое, в  свою очередь, отличается
от Integer + ArbitraryPrecisionNumber.  Как программе разобраться, какой из
алгоритмов   следует   использовать?  Что-что?  Кто  сказал:   «Запросить у
аргумента оператора + его настоящий тип»? Немедленно встаньте в угол.

class Number {
protected:
 int type; // Хранит информацию о настоящем типе
 int TypeOf() { return type; }
 // И т.д.
};
// Где-то в программе
switch (type) {
 case kInteger: ...
 case kComplex: ...
}

Именно этого знания типов  мы постараемся избежать.  Кроме того, все прямые
реализации    подобных   схем не   отличаются   особой   элегантностью.  Вы
когда-нибудь   видели  код,  генерируемый   компилятором   для  конструкции
switch/case? Ни красоты, ни эффективности. Вместо этого мы объединим знания
компилятора о типах с чудесами современной технологии — v-таблицами.

Двойная передача

В обобщенном виде задачу  можно представить в виде  матрицы, строки которой
соответствуют типам левого операнда, а столбцы - всевозможным типам правого
операнда.  В  каждой  ячейке  матрицы  находится  конкретный   алгоритм для
обработки   сочетания  типов.  Чаще  всего  такая  ситуация  возникает  для
гомоморфных иерархий вроде нашей, но вообще типы левого операнда не обязаны
совпадать с типами  правого операнда.  Конечно, возможны  силовые решения -
например, запрятать в  каждом экземпляре сведения  о его типе. Однако более
элегантное  решение (и  обычно более  эффективное)  решение  носит название
двойной передачи (double dispatch).

class Number {
protected:
 // Диспетчерские функции для оператора +
 virtual Number& operator+(const Integer&) = 0;
 virtual Number& operator+(const Complet&) = 0;
 // Ит.д. для всех производных типов
public:
 virtual Number& operator+(const Number&) = 0;
 virtual Number& operator-(const Number&) = 0;
 // Ит.д.
};

class Integer : public Number {
private:
 int I;
protected:
 virtual Number& operator+(const Integer&);
 virtual Number& operator+(const Complex&);
public:
 Integer(int x) : i(x) {}
 virtual Number& operator+(const Number&);
 // Ит.д.
};

Number& Integer::operator+(const Number& n)
{
 return n + *this; // Поменять местами левый и правый операнд
}
Number& Integer::operator+(const Integer& n)
{
 // Ниже приведен псевдокод
 if (i + n.i слишком велико для int) {
   return ЦелоеСПовышеннойТочностью
 }
 else return Integer(i + n.i);
}

С этим  фрагментом  связана  одна   нетривиальная  проблема,  к  которой мы
вернемся позже, а пока сосредоточьте  все внимание на концепции. Она похожа
на  стереограмму  -  чтобы  скрытая   картинка   проявилась,  вам  придется
расслабить глаза и некоторое время рассматривать код. Когда клиент пытается
сложить  два Integer,   компилятор  передает  вызов   Integer::operator+(),
поскольку  operator+(Number&) является  виртуальным -  компилятор правильно
находит     реализацию    производного    класса.  К  моменту    выполнения
Integer::operator+(Number&)   настоящий тип  левого операнда  уже известен,
однако правый операнд все еще остается загадкой. Но в этот момент наступает
второй этап  двойной передачи:  return n +  *this. Левый и  правый операнды
меняются местами, а компилятор приступает  к поискам v-таблицы n. Однако на
этот раз он ищет  переопределение  Number::operator+(Integer&),  так как он
знает, что  *this в  действительности  имеет  тип  Integer. Это  приводит к
вызову   Integer::operator+(Integer&),    поскольку  типы  обоих  операндов
известны и можно  наконец произвести  вычисления. Если  вы так и не поняли,
что же происходит, прогуляйтесь на  свежем воздухе и попробуйте снова, пока
не  поймете.   Возможно,  вам  поможет   следующая   формулировка:   вместо
кодирования типа в  целой переменной  мы определили  настоящий тип Number с
помощью v-таблицы. Такое  решение не только элегантно.  Вероятно, оно еще и
более  эффективно,   чем  те,  которые  приходили  вам  в  голову.  Скажем,
приходилось ли вам  видеть код,  генерируемый компилятором  для конструкции
switch/case?    Он   некрасив  и  вдобавок   куда  менее   эффективен,  чем
последовательное индексирование двух v-таблиц.
Несмотря на всю элегантность, двойная передача довольно дорого обходится по
объему кода и сложности:  
 o Если у вас имеется m  производных классов и n
операторов,   то  каждый   производный   класс  должен  содержать   m*(n+1)
виртуальных   функций,  да еще  столько  же чисто  виртуальных   заглушек в
классе-предке. Итого мы  получаем (m+1)*m*(n+1)  диспетчерских функций. Для
всех иерархий, кроме самых тривиальных, это довольно много.
 o Если оператор  не является  коммутируемым (то есть  ему нельзя передать
повторный вызов  с аргументами,  переставленными  в обратном  порядке), это
число удваивается, поскольку вам придется реализовать отдельные функции для


 
вредитель ©   (2005-02-26 17:58) [6]

двух вариантов  порядка  аргументов. Например,  y/x - совсем  не то же, что
x/y; вам  понадобится   оператор / и  специальная  функция   DivideInto для
переставленных     аргументов.  
 o Клиенты базового  класса видят все  устрашающие защищенные  функции, хотя
это им совершенно не нужно.

Тем не  менее, в простых  ситуациях  двойная  передача  оказывается  вполне
разумным  решением  -  ведь  проблема,  как ни  крути,  достаточно  сложна.
Специфика  ситуации  неизбежно требует  множества  мелких  фрагментов кода.
Двойная  передача  всего  лишь заменяет  большие,   уродливые,  немодульные
конструкции     switch/case    более  быстрой   и  модульной    виртуальной
диспетчеризацией. Как правило, количество функций удается сократить, но при
этом приходится в той или  иной степени идти на  компромисс с нашим строгим
правилом  - никогда  не  спрашивать  у объекта,  каков  его настоящий  тип.
Некоторые  из этих  приемов  рассматриваются  ниже.  Видимость  производных
классов для клиентов Number  тоже удается ликвидировать  минимальной ценой;
об этом будет  рассказано в главе 12.  Как и во многих  проблемах дизайна в
С++, в  которых  задействованы  матрицы  операций, вам  придется  на уровне
здравого    смысла   решить,   стоит  ли  повышать    модульность  за  счет
быстродействия или объема кода.


Гетероморфная двойная передача
Двойная  передача   обычно  возникает  в  ситуациях,  когда  оба  аргумента
происходят от общего предка, но это не обязательно. Левый и правый операнды
могут принадлежать к разным классам, не имеющим общего предка.
Один из моих любимых  примеров относится к обработке  событий в графических
средах. Существует множество  возможных событий:  операции и мышью, события
от клавиатуры,  события  операционной  системы и  даже такая  экзотика, как
распознавание      голоса   или   световое   перо.  С  другой   стороны,  в
пользовательский  интерфейс  обычно входят  разнообразные  виды, панели или
окна (терминология зависит от операционной системы и используемого языка) -
внешние окна с  заголовками и  кнопками  закрытия, поля для  редактирования
текста и области, в  которых можно  рисовать красивые  картинки. Для каждой
комбинации конкретного события с  конкретным типом вида может потребоваться
уникальная реализация.  Возникает та же проблема,  что и с иерархией чисел,
хотя на этот раз  события и  виды не имеют  общего базового  класса. Тем не
менее, методика двойной передачи все равно работает.

class Event { // Чисто виртуальный базовый класс для событий
public:
 virtual void Process(View* v) = 0;
};

class MouseClick : public Event {
public:
 virtual void Process(View* v) { v->Process(*this); }
};

class View { // Чисто виртуальный базовый класс для видов
public:
 virtual void Process(MouseClick& ev) = 0;
 virtual void Process(Keystroke& ev) = 0;
 // Ит.д.
};

Хотя на первый взгляд кажется, что  проблема отличается от иерархии Number,
присмотритесь  повнимательнее.  Реализация  функции Process()  класса Event
всего лишь  "разворачивает"   операцию и  перенаправляет  вызов.  Поскольку
функция Event::Process() является виртуальной, когда дело доходит до класса
View, точный  тип Event  уже  известен, и  компилятор  вызывает  правильную
перегруженную версию View::Process().
Каждый раз, когда вам захочется забить  код типа в переменную класса, чтобы
узнать, с каким  производным классом  вы имеете дело,  подумайте, нельзя ли
переложить хлопоты на компилятор с  помощью двойной передачи (или одного из
описанных ниже вариантов).


 
вредитель ©   (2005-02-26 18:00) [7]

книго Лдефри Элджера

Простите
должно быть: книги Джефри Элджера


 
jack128 ©   (2005-02-26 20:11) [8]

AFAIK в дельфи8 и дельфи2005(возможно только для .NET - платформы) перегрузка операторов есть


 
Mystic ©   (2005-02-26 20:22) [9]

Лично моя точка зрения:

Если после перегрузке в выражениях мы используем 1-2 оператора, то функциональная запись понятнее. А если больше, то проще написать свой интерпретатор, не ограниченый синтаксисом того языка, который мы используем.


 
Piter ©   (2005-02-26 22:51) [10]

вредитель ©   (26.02.05 17:58) [5]

MF используешь? :)


 
вредитель ©   (2005-02-26 23:17) [11]

MF используешь? :)

CodeGenie 3.11

А что есть MF?


 
вредитель ©   (2005-02-26 23:23) [12]

Ой, недогнал. А как вы догадались?


 
GuAV ©   (2005-02-26 23:40) [13]

Можно использовать варианты своих типов. Для них возможно переопределение операторов.


 
VMcL ©   (2005-02-27 00:30) [14]

>>GuAV ©   (26.02.05 23:40) [13]

Дополню: начиная с D6.


 
kaZaNoVa ©   (2005-02-27 00:32) [15]

вредитель ©   (26.02.05 23:23) [12]
так он встроенный детктор юзает ..типа он онлайн, а Питер уже всё о тебе знает)


 
kaZaNoVa ©   (2005-02-27 00:33) [16]

kaZaNoVa ©   (27.02.05 0:32) [15]
упс .. у меня пол 4-го, всё перепутал ..
оффтопик сплошной


 
Piter ©   (2005-02-27 01:15) [17]

вредитель ©   (26.02.05 23:23) [12]
Ой, недогнал. А как вы догадались?


по тому, как автоматически разбит твой пост [5] и [6] :)
Человек бы не стал так разбивать :)


 
TUser ©   (2005-02-27 07:58) [18]

Книга, судя по всему, в эл. виде есть. Где качал?


 
вредитель ©   (2005-02-27 10:13) [19]

Книга, судя по всему, в эл. виде есть. Где качал?
Точно не помню, но вот один раз наткнулся на это:
http://pv.bstu.ru/?topic=oop
Еще Голуба почитайте.


 
wicked ©   (2005-02-27 15:51) [20]


> Еще Голуба почитайте

Пашку, что-ли?... ;)


 
вредитель ©   (2005-02-27 15:59) [21]

Пашку, что-ли?... ;)

Неа. Алена. Тоже шутник, как и Элджер.



Страницы: 1 вся ветка

Форум: "Потрепаться";
Текущий архив: 2005.03.20;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.53 MB
Время: 0.052 c
14-1109618499
dmk
2005-02-28 22:21
2005.03.20
D2005 и скорость работы приложений


6-1105643974
Бульдозер
2005-01-13 22:19
2005.03.20
Сетевая программа на чистом WinSock


1-1109859927
mrAld
2005-03-03 17:25
2005.03.20
THTTPRIO - результат запроса


1-1110027236
Checist [root]
2005-03-05 15:53
2005.03.20
И снова TWebBrowser


4-1107719981
aha
2005-02-06 22:59
2005.03.20
чтение порта принтера ....где ошибка моя ?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский