Итераторы - это тема, граничащая с безумием. Мы вовремя остановились на активном итераторе, шаблоне, не вложенном, с семантикой указателей. А ведь их можно вкладывать (т.е. объявлять класс итератора внутри класса коллекции), связывать с курсорами, перегружать их операторы, изменять семантику, вводить многопоточность, создавать внутри (!) итератора мгновенную частную копию коллекции и это только начало. По счастью, о нас уже позаботился Алексей Степанов, и подарил нам Библиотеку Стандартных Шаблонов - Standart Template Library, полную итераторов, равно коллекций и алгоритмов. Немного о них можно почитать на этом же сайте в разделе VC++-›STL у Артема Каева, а много - в MSDN.
Так же добавлю, что пользуюсь при подготовке Шагов компилятором BC3.1, а он поддерживает шаблоны не вполне так, как это делают современные компиляторы. То есть, если Вы просто скопируете код, вероятно он сразу даже не откомпилируется. Так что предупреждаю - если собираетесь пользоваться шаблонами - проверьте, что на эту тему думает компилятор (а так же насчет исключений и операторов вида xxxxxxx_cast‹›).
Мне же итератор нужен был исключительно для следующих Шагов, а совпадения фамилий, характеров и событий прошу считать случайными.
Шаг 27 - Умные указатели. Перегрузка operator*, operator,operator-›*.
Пробегая по верхам интересных идиом я упустил одну важную вещь. Поначалу она была не так важна, но пришло время замучать и ее. Я имею в виду то, что наши замечательно умные указатели, smart pointers, вообще-то имеют неполную семантику. То есть, они не полностью имитируют обычные, настоящие указатели. За примерами не надо ходить далеко - попробуем разыменовать смарт или вызвать функцию по указателю:
obj = *(smart_ptr);
(obj-›*ptr_to_funct) (some_parameter);
С первой проблемой рассчитаться легко. Если Вы НЕ читаете сейчас этот Шаг, не беспокойтесь - решение придет само, в тот момент, когда задача возникнет.
//Ясно, это реализации перегруженных операторов-селекторов.
CSmth* operator-› const { return prt_real; }
CSmth& operator* const { return *ptr_real; }
operator CSmth* const { return ptr_real; }
А что вторая проблема? Да, тут ситуация намного серьезнее, и если Вы опять-таки не читаете этот Шаг, то нужно немедленно прочесть его - или первоисточник - статью Мейерса в Dr. Dobb's Journal. Только там придется продираться через тучные стада шаблонов и долгих рассуждений. Без шаблона конечно не обойтись, но нужно ухватить хотя бы идею. Поэтому сделаем так, как нормальный человек читает детективы Марининой: первые и последние две страницы.
Сначала, кто такой operator-›*. Это который вызывает функцию-член по указателю. Такую функцию нужно вызывать с указанием объекта, если из другой функции-члена, то в виде (this-›*mpf) или (*this).*mpf.
// Этот класс используется так же дальше
class CSmth {
public:
int a;
int pf (void) {return a;}
};
typedef int (CSmth::*PF)(void);
Если мы нарисуем умный указатель на объект класса CSmth, определять operator-›* нужно самостоятельно. Что он должен вернуть? Нечто такое, к чему можно применить operator. То есть, это снова proxy-объект. Мейерс называет его "незавершенный вызов функции-члена" (Pending Member Function Calling). Он должен знать, к какому объекту применяется, и знать об указателе на функцию, то есть он должен иметь в себе указатели на них обоих, и инициализировать их в конструкторе. А operator должен возвращать уже нужный нам int, или все что угодно другое, что может вернуть указываемая функция.
// класс незавершенного вызова. Это самое важное.
class pmfc {
private:
// два указателя - на объект и на функцию
CSmth* m_smth;
PF m_pfunct;
public:
// конструктор
pmfc (CSmth*& _smth, PF& _pfunct) : m_smth(_smth), m_pfunct(_pfunct) {}
// вызов конечной функции из оператора
int operator const { return (m_smth-›*m_pfunct); }
};
// класс умного указателя.
class CPtr {
private:
CSmth* a;
public:
CPtr { a = new CSmth; }
~CPtr { delete a; }
CSmth* operator-› const { return a; }