C++: библиотека программиста



Download 1,95 Mb.
Pdf ko'rish
bet93/144
Sana24.02.2022
Hajmi1,95 Mb.
#223123
TuriРеферат
1   ...   89   90   91   92   93   94   95   96   ...   144
Bog'liq
C -Eldjer-Djeff-for-Real-Programmers-RUS-www.itlibitum.ru

 
163 
Двойная передача 
В обобщенном виде задачу можно представить в виде матрицы, строки которой соответствуют типам 
левого операнда, а столбцы — всевозможным типам правого операнда. В каждой ячейке матрицы 
находится конкретный алгоритм для обработки сочетания типов. Чаще всего такая ситуация возникает 
для гомоморфных иерархий вроде нашей, но вообще типы левого операнда не обязаны совпадать с 
типами правого операнда. 
Конечно, возможны силовые решения — например, запрятать в каждом экземпляре сведения о его 
типе. Однако более элегантное решение (и обычно более эффективное) решение носит название 
двойной передачи (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&)
настоящий тип левого 


 164 
операнда уже известен, однако правый операнд все еще остается загадкой. Но в этот момент наступает 
второй этап двойной передачи: 
return n + *this
. Левый и правый операнды меняются местами, а 
компилятор приступает к поискам v-таблицы 
n
. Однако на этот раз он ищет переопределение 
Number::operator+(Integer&)
, так как он знает, что 
*this
в действительности имеет тип 
Integer

Это приводит к вызову 
Integer::operator+(Integer&)
, поскольку типы обоих операндов известны 
и можно наконец произвести вычисления. Если вы так и не поняли, что же происходит, прогуляйтесь 
на свежем воздухе и попробуйте снова, пока не поймете. Возможно, вам поможет следующая 
формулировка: вместо кодирования типа в целой переменной мы определили настоящий тип 
Number
с 
помощью v-таблицы. 
Такое решение не только элегантно. Вероятно, оно еще и более эффективно, чем те, которые 
приходили вам в голову. Скажем, приходилось ли вам видеть код, генерируемый компилятором для 
конструкции 
switch/case
? Он некрасив и вдобавок куда менее эффективен, чем последовательное 
индексирование двух v-таблиц. 
Несмотря на всю элегантность, двойная передача довольно дорого обходится по объему кода и 
сложности: 
• 
Если у вас имеется m производных классов и n операторов, то каждый производный класс 
должен содержать m*(n+1) виртуальных функций, да еще столько же чисто виртуальных 
заглушек в классе-предке. Итого мы получаем (m+1)*m*(n+1) диспетчерских функций. Для 
всех иерархий, кроме самых тривиальных, это довольно много. 
• 
Если оператор не является коммутируемым (то есть ему нельзя передать повторный вызов с 
аргументами, переставленными в обратном порядке), это число удваивается, поскольку вам 
придется реализовать отдельные функции для двух вариантов порядка аргументов. Например, 
y/x
— совсем не то же, что 
x/y
; вам понадобится оператор 
/
и специальная функция 
DivideInto
для переставленных аргументов. 
• 
Клиенты базового класса видят все устрашающие защищенные функции, хотя это им 
совершенно не нужно. 
Тем не менее, в простых ситуациях двойная передача оказывается вполне разумным решением — ведь 
проблема, как ни крути, достаточно сложна. Специфика ситуации неизбежно требует множества 
мелких фрагментов кода. Двойная передача всего лишь заменяет большие, уродливые, немодульные 
конструкции 
switch/case
более быстрой и модульной виртуальной диспетчеризацией. 
Как правило, количество функций удается сократить, но при этом приходится в той или иной степени 
идти на компромисс с нашим строгим правилом — никогда не спрашивать у объекта, каков его 
настоящий тип. Некоторые из этих приемов рассматриваются ниже. Видимость производных классов 
для клиентов 
Number
тоже удается ликвидировать минимальной ценой; об этом будет рассказано в 
главе 12. Как и во многих проблемах дизайна в С++, в которых задействованы матрицы операций, вам 
придется на уровне здравого смысла решить, стоит ли повышать модульность за счет быстродействия 
или объема кода. 
Гетероморфная двойная передача 
Двойная передача обычно возникает в ситуациях, когда оба аргумента происходят от общего предка, 
но это не обязательно. Левый и правый операнды могут принадлежать к разным классам, не имеющим 
общего предка. 
Один из моих любимых примеров относится к обработке событий в графических средах. Существует 
множество возможных событий: операции и мышью, события от клавиатуры, события операционной 
системы и даже такая экзотика, как распознавание голоса или световое перо. С другой стороны, в 
пользовательский интерфейс обычно входят разнообразные виды, панели или окна (терминология 
зависит от операционной системы и используемого языка) — внешние окна с заголовками и кнопками 
закрытия, поля для редактирования текста и области, в которых можно рисовать красивые картинки. 
Для каждой комбинации конкретного события с конкретным типом вида может потребоваться 
уникальная реализация. Возникает та же проблема, что и с иерархией чисел, хотя на этот раз события и 
виды не имеют общего базового класса. Тем не менее, методика двойной передачи все равно работает. 
class Event {
// Чисто виртуальный базовый класс для событий 
public: 



Download 1,95 Mb.

Do'stlaringiz bilan baham:
1   ...   89   90   91   92   93   94   95   96   ...   144




Ma'lumotlar bazasi mualliflik huquqi bilan himoyalangan ©hozir.org 2024
ma'muriyatiga murojaat qiling

kiriting | ro'yxatdan o'tish
    Bosh sahifa
юртда тантана
Боғда битган
Бугун юртда
Эшитганлар жилманглар
Эшитмадим деманглар
битган бодомлар
Yangiariq tumani
qitish marakazi
Raqamli texnologiyalar
ilishida muhokamadan
tasdiqqa tavsiya
tavsiya etilgan
iqtisodiyot kafedrasi
steiermarkischen landesregierung
asarlaringizni yuboring
o'zingizning asarlaringizni
Iltimos faqat
faqat o'zingizning
steierm rkischen
landesregierung fachabteilung
rkischen landesregierung
hamshira loyihasi
loyihasi mavsum
faolyatining oqibatlari
asosiy adabiyotlar
fakulteti ahborot
ahborot havfsizligi
havfsizligi kafedrasi
fanidan bo’yicha
fakulteti iqtisodiyot
boshqaruv fakulteti
chiqarishda boshqaruv
ishlab chiqarishda
iqtisodiyot fakultet
multiservis tarmoqlari
fanidan asosiy
Uzbek fanidan
mavzulari potok
asosidagi multiservis
'aliyyil a'ziym
billahil 'aliyyil
illaa billahil
quvvata illaa
falah' deganida
Kompyuter savodxonligi
bo’yicha mustaqil
'alal falah'
Hayya 'alal
'alas soloh
Hayya 'alas
mavsum boyicha


yuklab olish