Как сделать так, чтобы сохранить замечательное полиморфное поведение первого варианта, и применить эту радость идиота 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;
}
Шаг 26 - Как сделать массив из чего угодно. Продолжение 2.
Итераторы.
В Шагах 15 и 16 мы повозились с имитацией массива (коллекцией). Мы добились нормальной работы при чтении и записи в ячейки массива. Но работа с массивом этим не ограничивается. Вот захочется нам сделать что-то со всеми элементами массива, а он индексирован по строке.
// Бред
for (string cCounter= "a"; a ‹ "zzzz"; a++) array.[cCounter].doit;
Нет, это неправильно. Нужно сделать так, чтобы коллекция сама себя перебирала.
CIndex index = array.getStart;
while (!array.eof) {
index = array.getIndex ;
array[index].doIt;
array.getNext;
};
Ну вот, на что-то похоже. Появился некий элемент index класса CIndex, без которого в принципе можно обойтись, если коллекция будет хранить текущее значение перебора внутри себя. Но вот беда - если вдруг коллекцию захотят перебрать разные клиенты? Ну глобальная она, существует вместе с программой, а обращаются к ней разные объекты, как себя перебрать бедной коллекции? В общем, подход тут такой же, как и в жизни: тебе надо, ты и шевелись, в смысле перебирай. Упомянутый выше index тут как нельзя кстати. Называем его Зингельшухером… (oops!) Простите - итератором, объявляем его дружественным коллекции, прописываем в него текущую позицию, пишем скромный набор функций навигации типа goFirst, goNext, isLast. В зависимости от того, где мы их пишем, итератор будет или активным - если функции навигации в нем, или пассивным - если они лежат в коллекции.
Итак, что делаем: в шаблон ampstack‹Type› из Шага 23 вписываем дружбу к классу итератора:
friend class ampIter;
и сам шаблон класса итератора:
// Класс итератора, дружественный нашему стеку.
template ‹class Type›
class ampIter {
private:
ampstack‹Type›* m_stack;
int iPosition;
public:
ampIter(ampstack‹Type›* _as = NULL)
: m_stack(_as), iPosition (0) {}
int isLast(void)
{ return iPosition + 1 - m_stack-›iTop; }
void moveStart(void) {iPosition = 0; }
Type* moveNext(void)
{ return m_stack-›array[iPosition++]; }
};