Чаще всего этот прием я видел в отношении оператора operator<<. Точнее, не чаще, а всегда. На нем и разберем. Пусть у нас есть иерархия классов, и мы хотим определить диагностическую функцию Dump(). Она должна вываливать диагностику в заданное что-то (CDestination). У нас есть два варианта: или сделать функцию виртуальной в иерархии классов:
Код:
class CBase
{
virtual void Dump( CDestination& ds ) = 0;
};
class CFirst : public CBase
{
void Dump (CDestination& ds);
};
class CSecond : public CBase
{
void Dump (CDestination& ds);
};
Или перегружать ее для каждого класса иерархии или в классе, или в свободной функции:
CDestination
{
void Dump ( CFirst& fs);
void Dump ( CSecond& sc);
};
void Dump (CDestination& ds, CThird& td);
void Dump (CDestination& ds, CFourth& fr);
Ясно, первый вариант предпочтительнее. Во-первых, он обеспечивает полиморфное поведение. Во-вторых, своей диагностикой класс занимается сам, что тоже большой плюс. А второй способ почти невозможен: переписывать класс вывода каждый раз при появлении нового потомка в иерархии нереально (в двойной диспетчеризации дело другое, там просто нет иного выхода); в конце концов, он может быть в купленной библиотеке.
Но у второго варианта есть одно преимущество: функцию Dump() можно обозвать оператором operator<<, и это будет выглядеть весьма презентабельно:
Код:
// Это декларация
CDestination
{
CDestination& operator<< ( CFirst& fs);
};
CDestination& operator<< (CDestination& ds, CSecond& sc);
// А это применение
dStream << dObject;
Как сделать так, чтобы сохранить замечательное полиморфное поведение первого варианта, и применить эту радость идиота operator<< ? Легко: пусть operator<< вместо реальной работы просто вызывает виртуальную Dump(). Именно так сделано в MFC - объект afxDump вызывает виртуальную Dump() именно через operator<<. (Можно что угодно говорить про Microsoft, но факт есть факт - огромное число полезных и интересных приемов использовано в их продуктах и "... взять их у нее - наша задача!").
Код:
#include <iostream.h>
class CNoChange;
class CBase
{
public:
virtual void passTo (CNoChange& _cb)
{cout << "base passed" << endl; };
};
class CFirst: public CBase
{
public:
void passTo (CNoChange& _cb)
{ cout << "first passed" << endl; };
};
class CSecond: public CBase
{
public:
void passTo (CNoChange& _cb)
{ cout << "second passed" << endl; };
};
class CNoChange
{
public:
int a;
// Это вариант с оператором - членом класса.
CNoChange& operator<< (CBase& _cb)
{ _cb.passTo(*this); return *this;};
};
// а это - свободная функция.
//CNoChange& operator<< (CNoChange& _nc, CBase& _cb)
// { _cb.passTo(_nc); return _nc;};
// проверить надо.
int main ()
{
CNoChange nc;
CFirst fs;
CSecond sc;
nc<<fs;
nc<<sc;
return 0;
}