"Идиомы и стили С++" - читать интересную книгу автора (Makhmutov Albert)

Шаг 25 - Как сделать виртуальной свободную функцию.

Чаще всего этот прием я видел в отношении оператора operator‹‹. Точнее, не чаще, а всегда. На нем и разберем. Пусть у нас есть иерархия классов, и мы хотим определить диагностическую функцию Dump(). Она должна вываливать диагностику в заданное что-то (CDestination). У нас есть два варианта: или сделать функцию виртуальной в иерархии классов:

class CBase {

 virtual void Dump(CDestinationamp; ds) = 0;

};

class CFirst: public CBase {

 void Dump (CDestinationamp; ds);

};

class CSecond: public CBase {

 void Dump (CDestinationamp; ds);

};

Или перегружать ее для каждого класса иерархии или в классе, или в свободной функции:

CDestination {

 void Dump (CFirstamp; fs);

 void Dump (CSecondamp; sc);

};

void Dump (CDestinationamp; ds, CThirdamp; td);

void Dump (CDestinationamp; ds, CFourthamp; fr);

Ясно, первый вариант предпочтительнее. Во-первых, он обеспечивает полиморфное поведение. Во-вторых, своей диагностикой класс занимается сам, что тоже большой плюс. А второй способ почти невозможен: переписывать класс вывода каждый раз при появлении нового потомка в иерархии нереально (в двойной диспетчеризации дело другое, там просто нет иного выхода); в конце концов, он может быть в купленной библиотеке.

Но у второго варианта есть одно преимущество: функцию Dump() можно обозвать оператором operator‹‹, и это будет выглядеть весьма презентабельно:

// Это декларация

CDestination {

 CDestinationamp; operator‹‹ (CFirstamp; fs);

};

CDestinationamp; operator‹‹ (CDestinationamp; ds, CSecondamp; sc);


// А это применение

dStream ‹‹ dObject;

Как сделать так, чтобы сохранить замечательное полиморфное поведение первого варианта, и применить эту радость идиота operator‹‹? Легко: пусть operator‹‹ вместо реальной работы просто вызывает виртуальную Dump(). Именно так сделано в MFC - объект afxDump вызывает виртуальную Dump() именно через operator‹‹. (Можно что угодно говорить про Microsoft, но факт есть факт - огромное число полезных и интересных приемов использовано в их продуктах и "… взять их у нее - наша задача!").

#include ‹iostream.h›

class CNoChange;

class CBase {

public:

 virtual void passTo (CNoChangeamp; _cb) { cout ‹‹ "base passed" ‹‹ endl; }

};


class CFirst: public CBase {

public:

 void passTo (CNoChangeamp; _cb) { cout ‹‹ "first passed" ‹‹ endl; }

};


class CSecond: public CBase {

public:

 void passTo (CNoChangeamp; _cb) { cout ‹‹ "second passed" ‹‹ endl; }

};


class CNoChange {

public:

 int a;

 // Это вариант с оператором - членом класса.

 CNoChangeamp; operator‹‹ (CBaseamp; _cb) { _cb.passTo(*this); return *this; }

};


// а это - свободная функция.

//CNoChangeamp; operator‹‹ (CNoChangeamp; _nc, CBaseamp; _cb)

// {_cb.passTo(_nc); return _nc;};

// проверить надо.

int main() {

 CNoChange nc;

 CFirst fs;

 CSecond sc;

 nc‹‹fs;

 nc‹‹sc;

 return 0;

}