Покажем несколько схем реализации мультиметодов. Обычно классы, которые участвуют в работе мультиметодов, являются «родственниками», однако в некоторых случаях возможна реализация и между неродственными классами. Пусть у нас есть базовый абстрактный класс Base и три наследника: Deri ved_a, Deri ved_b и Derived_c. Нам требуется выполнять некую операцию со всеми возможными сочетаниями параметров. В базовом классе определяется чистый абстрактный метод, который реализуется в производных классах.
В общем случае для трех типов, А, В и С, число сочетаний любых двух типов равно девяти:
1. Derived_ a — Den ved_ a.
2. Derived_ a — Deri ved_ _b.
3. Derived_ a — Den ved_ _c.
4. Derived_ _b — Den i ved_ a.
5. Derived_ _b — Den ved_ _b.
6. Derived_ _b — Den ved_ _c.
7. Derived_ с — Den ' ved_ a.
8. Derived_ _c — Den ved_ _b.
9. Derived с — Derived c.
В конкретных ситуациях сочетаний бывает меньше, так как операция может оказаться коммутативной для некоторых сочетаний. Например, операция умножения скаляра и вектора — коммутативна, так же как и операция умножения скаляра на матрицу. Метод, выполняющий двойное переключение по типу, реализуется с параметрами базового класса — используется принцип подстановки. Двойное переключение эмулируется при помощи цепочек if...else с использованием преобразования dymami c_cast<>. Покажем сначала реализацию для двух классов (листинг-10.8), а потом добавим третий.
Листинг 10.8. Двойной диспетчер для двух типов
// требуемая операция
// определение деструктора
продолжение &
// определения классов class Base { public: virtual ~Base()=0;
virtual bool Operator(const Base &R) = 0;
};
Base::~Base(){} class Derived_a: public Base
Листинг 10.8 {продолжение) { public:
virtual bool Operator(const Base &R);
};
class Derived__b: public Base { public:
virtual bool Operator(const Base &R);
};
// реализация методов двойной диспетчеризации bool Derived_a::0perator(const Base &R)
{ if (const Derived_a *pra = dynamic_cast<const Derived_a*>(&R)) { // правый аргумент типа А; обработка варианта "А-А" cout << "А-А" << endl; return true;
}
else if (const Derived_b *prb = dynamic_cast<const Derived__b*>(&R)) { // правый аргумент типа В; обработка варианта "А-В"
cout << "А-В" << endl;
return true;
>
else throw exception ("Error! Incorrect type argument!");
}
bool Derived_b::0perator(const Base &R)
{ if (const Derived__a *pra = dynamic_cast<const Derived_a*>(&R)) { // правый аргумент типа А; обработка варианта "В-А" cout << "В-А" << endl; return true;
}
else if (const Derived_b *prb = dynamic_cast<const Derived_b*>(&R)) { // правый аргумент типа В; обработка варианта "В-В"
cout << "В-В" << endl;
return true;
}
else throw exception ("Error! Incorrect type argument!");
}
Мультиметоды устроены достаточно просто: проверяется правый операнд, и в зависимости от его типа выполняется обработка. Если же правый операнд неизвестного типа, то генерируется исключение. Добавим третий класс и покажем механизм использования оператора typeidO для распознавания типа (листинг 10.9). |