Теперь добавим третий класс и посмотрим, какие изменения придется внести в исходные классы:
class Derived_c: public Base { public:
virtual bool Operator(const Base &R)const { return R.Operator(*this); } virtual bool Operator(const Derived_a &R)const { cout << "C-A" << endl; return true; } virtual bool Operator(const Derived_b &R)const { cout << "C-B" << endl; return true; } virtual bool Operator(const Derived_c &R)const { cout << "C-C" << endl; return true; }
};
Этот класс реализует нам операцию с левым операндом типа Derived_c. Отметим, что ни класс Deri ved_a, ни класс Deri ved_b не работают с аргументом типа Derived_c. Не будем вносить никаких изменений в эти классы и выполним небольшой фрагмент программы:
Derived_a A; Derived_b В; Derived__c С; Base &гА = А; Base &гВ = В; Base &гС = С;
cout << A.Operator(C) << endl; cout << В.Operator(С) << endl; cout << rA.Operator(rC) << endl; cout << rB.Operator(rC) << endl;
Никаких ошибок компиляции нет! Несмотря на то, что мы не определяли в классах Derived_a и Derived_b операции с аргументом типа Derived_c, следующие вызовы не вызывают ошибок компиляции:
cout << A.Operator(С) << endl; cout << В.Operator(С) << endl;
Причина в том, что в данном случае работает принцип подстановки. Ввиду отсутствия-метода с аргументом типа Derived_c выбирается метод класса с параметром-ссылкой базового типа и на его место подставляется ссылка на наследника. Далее выполняется механизм двойной диспетчеризации. Точно так же работают вызовы:
cout << rA.Operator(rC) << endl; cout << rB.Operator(rC) << endl;
Если нас это устраивает, никаких изменений в классы Derived_a и Derived_b вносить нет необходимости. Если же требуется специальный вид операции A. Operator (С), то нужно добавить соответствующий виртуальный метод в класс Deri ved__a.
Как уже было сказано, подобная методика работает и для классов, не являющихся родственниками. Построим следующую иерархию классов (листинг 10.11).
Листинг 10.11. Мультиметоды для классов, не являющихся родственниками
class В; // объявление корня второй иерархии
class А // корень первой иерархии
{ public: virtual ~А(){}
virtual void Operator(В*)=0:
}:
class D1A; // объявления классов первой иерархии
class D2A;
class В // корень второй иерархии
{ public:
virtual void Operator(D1A& rA) = 0: virtual void Operator(D2A& rA) = 0:
};
// классы иерархии A class D1A: public A { public:
virtual void 0perator(B* R) { R->0perator(*this); }
};
class D2A: public A { public:
virtual void 0perator(B* R) { R->0perator(*this); }
}:
// классы иерархии В class DIB: public В { public:
virtual void Operator(D1A& rA) { cout << "D1A!" << endl: }; virtual void Operator(D2A& rA) { cout << "D2A!" << endl; };
};
class D2B: public В { public:
virtual void Operator(D1A& rA) { cout << "D1A!" << endl; }; virtual void Operator(D2A& rA) { cout << "D2A!" << endl; };
};
Две иерархии — А и В — не имеют общих родственников. Тем не менее методика последовательных виртуальных вызовов, описанная ранее для одной иерархии, работает точно так же. В данном случае все требуемые действия реализованы во второй иерархии, а первая только осуществляет первый шаг двойного переключения по типу. Небольшой фрагмент программы иллюстрирует необходимые переключения:
D1A al; D2A а2;
В *pb = new D1B(); // указатель на базовый класс <- адрес наследника
al.Operator(pb); // работают 2 виртуальных вызова
а2.Operator(pb); // работают 2 виртуальных вызова
pb->0perator(al); pb = new D2B(); al.Operator(pb); a2.Operator(pb); pb->Operator(al);
// работает 1 виртуальный вызов
// работают 2 виртуальных вызова // работают 2 виртуальных вызова // работает 1 виртуальный вызов
Двойное переключение по типу — это основа паттерна Visitor (посетитель). Это — один из наиболее трудных для понимания паттернов, недаром в [17] он описан последним.
Немного о кофемашинах для программистов
Как же великолепен терпкий запах кофе, который был свежесварен. Можно сидеть над чашкой часами и вдыхать данный просто божественный аромат. С давних времен считается, что кофе – это напиток для настоящих эстетов. Он согревает и душу и тело. Дарит блаженство и обостряет чувства. Уже давно ушли те времена, когда люди варили кофе в капельных кофеварках или турках. Их вытеснили различные кофемашины http://coffee-man.com.ua/, которые помогают экономить не только лишь время, но и количество совершенных действий. Посредством центробежной силы молотый кофе попадает в заварную группу, куда и поступает пар под определенным давлением. И уже через минуту можно пить вкуснейший свежесваренный кофе, который абсолютно не теряет своих качеств.
Вы уже давно подумываете над покупкой кофемашины себе домой либо в офис. Однако при этом, ваш бюджет ограничен, а вы так хотите наслаждаться свежеприготовленным настоящим капучино либо эспрессо не выходя из дома?
В нашем интернет магазине по продаже кофе и разных кофемашин, вы найдете качественные б/у кофемашины и кофеварки по низким ценам.
Обширный ассортимент наших машин http://coffee-man.com.ua/catalog/bu и кофеварок регулярно обновляется. Даже если вы не нашли тут на нашем сайте кофемашину, которая интересует вас, тогда звоните и наши менеджеры обязательно проконсультируют вас по всевозможным аналогам и закажут для вас подходящий вариант кофемашины из-за границы. |