Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.03.20;
Скачать: CL | DM;

Вниз

Перегрузка операторов в 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;
Скачать: CL | DM;

Наверх




Память: 0.54 MB
Время: 0.057 c
4-1107333369
GMan
2005-02-02 11:36
2005.03.20
Определение хэндлов процесса


1-1109948969
Denya
2005-03-04 18:09
2005.03.20
TWebBrowser


14-1109598664
ser35
2005-02-28 16:51
2005.03.20
Есть л русский HELP в Delphi6?


10-1086094163
Tormoz
2004-06-01 16:49
2005.03.20
Диапазон ячеек в Excel


1-1110045541
Leeechhhh
2005-03-05 20:59
2005.03.20
Как сделать дрэг/дроп