|
||||
|
Шаг 14 - Двойная диспетчеризация. Продолжение. В Шаге 4 мы говорили о двойной диспетчеризации. Она очень хорошо подходит при необходимости отображения одних объектов посредством других, но не только; она в общем применима, когда Вам нужно обрабатывать попарные (и более) взаимодействия объектов двух и более разных классов. Получается этакая табличка, на осях которой нарисованы классы, а в ячейках - функции их взаимодействия. Количество функций равно произведению столбцов и строк этой таблички. А если диспетчеризация тройная или выше? Тогда еще умножаем на количество слоев, и дальше и дальше… Как бы упростить жизнь? А вот так - если взаимодействие двух объектов дает один результат, пусть этим и занимается одна функция. Попробуем перевести на человеческий язык: Пусть есть класс CTitanic и класс CIceberg. Их карма в том, чтобы столкнуться. Четыре варианта взаимодействия: Столкновение двух Ctitanic не ведет ни к чему, если вообще возможно, двух CIceberg - у них там свои дела, столкновение CTitanic и CIceberg, как известно, к семи Оскарам, и столкновение CIceberg и CTitanic - к тому же самому. То есть функций всего три. Определим взаимодействие этих классов как функцию hit(). Вот код: #include ‹iostream.h› // Форвардные объявления class CTitanic; class CIceberg; class CFloating; // Абстрактный базовый класс class CFloating { public: virtual void hit(CIceberg&)=0; virtual void hit(CTitanic&)=0; public: virtual void hit(CFloating&)=0; }; // Класс айсберга class CIceberg { public: virtual void hit(CIceberg&); virtual void hit(CTitanic&); public: virtual void hit(CFloating&); }; // Первая диспетчерская функция void CIceberg::hit(CFloating& _co) { _co.hit(*this); } // Две реализации взаимодействия void CIceberg::hit(CIceberg& _ci) { cout ‹‹ "ci+ci" ‹‹ endl; } void CIceberg::hit(CTitanic& _ct) { cout ‹‹ "ci+co" ‹‹ endl; } // Класс Титаника class CTitanic { public: virtual void hit(CIceberg&); virtual void hit(CTitanic&); public: virtual void hit(CFloating&); }; // Еще одна диспетчерская функция void CTitanic::hit(CFloating& _co) { _co.hit(*this); } // А вот эта функция могла бы быть реализацией // но мы ее тоже делаем диспетчерской; // в этом фрагменте диспетчеризация тройная. void CTitanic::hit(CIceberg& _ci) { // cout ‹‹ "co+ci" ‹‹ endl; Это могла быть реализация _ci.hit(*this); } void CTitanic::hit(CTitanic& _ct) { cout ‹‹ "co+co" ‹‹ endl; } // проверим по быстрому, как работает int main () { CIceberg i1; CTitanic t1; CIceberg i2; CTitanic t2; i1.hit(t1); i1.hit(i2); t1.hit(i1); t1.hit(t2); return 0; } Пояснения по коду: взаимодействующие классы надобно определить от одного общего предка, коли они уж плавают и могут друг об друга биться, так и запишем - все варианты взаимодействия должны быть чистыми виртуальными функциями. В общем, количество действительных реализаций функций уменьшается как раз на количество совпадающих. Не так уж и плохо. Есть еще способы уменьшить их количество, основанные на преобразованиях классов - неявных или через конструкторы. Я правда не знаю, что раньше может запутать - количество диспетчерских функций или неявные преобразования; тут, пожалуй, можно только порадоваться появлению в стандарте ограничивающего модификатора explicit, который подавляет неявные вызовы конструкторов. Увы, двойная диспетчеризация в C++ всегда громоздкая и неудобная вещь, и вряд ли будет другой. Если мы добавляем новые классы в диспетчеризацию, приходится переписывать ранее написанные классы; все классы имеют доступ к функциям друг друга или функции должны быть открытыми. Это - плата за отсутствие в C++ функций, виртуальных по отношению к 2 и более классам. |
|
||
Главная | Контакты | Нашёл ошибку | Прислать материал | Добавить в избранное |
||||
|