КАТЕГОРИИ:
Итераторы не являются частью языка. Это концепция, используемая в программировании. Итераторы предоставляют способ последовательного доступа ко всем элементам составного объекта, не раскрывая его внутреннего представления.
Давайте посмотрим, как мы заполняли массив matrix на предыдущем занятии:
for(size_t i=0;i<m.dim1();++i)
for(size_t j=0;j<m.dim2();++j)
m(i,j)=rand();
Для этого мы организовали два вложенных цикла. При этом нам понадобилось использовать две индексные переменные, вызывать две функции, а также следить за тем, чтобы переменная, наращиваемая до dim1() (это i), была первым индексом в m(i,j), а переменная, наращиваемая до dim2() (здесь это j), была вторым индексом.
Однако перебор элементов можно организовать с помощью специального объекта – итератора. Имея в своем распоряжении итератор, мы можем сделать две вещи:
а) получить доступ к текущему элементу,
б) перейти к следующему элементу.
Изменим класс Matrix, добавив в него определение итератора и две функции:
typedef int* iterator;
iterator begin()const{return m;}
iterator end()const{return m+size();}
Функции begin() и end() возвращают значения, которые являются «границами» матрицы для итератора. Теперь заполнение массива мы можем организовать так:
for(Matrix::iterator p=m.begin();p!=m.end();++p)
*p=rand();
Здесь с помощью итератора p мы
а) получаем доступ к текущему элементу путем разименования указателя,
б) переходим к следующему элементу, производя инкремент указателя.
Следуя идиоме итерационного перебора, напишем функцию, печатающую матрицу:
void print_iter(const Matrix& m){
for(Matrix::iterator p=m.begin();p!=m.end();++p)
std::cout<<*p<<',';
}
Следуя эффективной, идиоматичной схеме, программист избегает обычных ловушек, таких как ошибка, связанная с выходом за границы массива.
Теперь сделаем итератор для перебора элементов матрицы в обратном направлении. По прежнему для перехода к следующему элементу (элементу с меньшим адресом) будем использовать оператор инкремента. Теперь нам не подходит указатель, так как при вызове ++p итератор должен идти в сторону уменьшения. Поэтому нам придется определить класс итератора:
Так как этот класс используется исключительно с классом Matrix, то определим этот класс внутри класса Matrix
class Matrix{
...
class reverse_iterator{
public:
reverse_iterator(int *p):_p(p){}
int& operator*(){return *_p;}
void operator++(){--_p;}
bool operator!=(reverse_iterator r)const{return _p!=r._p;}
private:
int *_p;
};
...
};
Функции-члены класса Matrix, возвращающие граничные значения для данного итератора:
reverse_iterator rbegin()const{return m+size()-1;}
reverse_iterator rend()const{return m-1;}
Теперь перебор в обратном порядке выглядит так:
for(Matrix::reverse_iterator r=m.rbegin();r!=m.rend();++r)
*r=rand();
Недостатки приведенной концепции: необходимо определять по две дополнительные функции-члены контейнера (begin,end) для каждого типа итератора.
Другая идиома для итератора, не имеющая этих недостатков:
class m_iter{
public:
m_iter(Matrix& m):_begin(m.m),_end(m.m+m.size()){}
void First(){_p=_begin;}
bool NotDone()const{return _p!=_end;}
int& CurrentItem()const{return *_p;}
void Next(){++_p;}
private:
int *_begin, *_end, *_p;
};
Использование:
m_iter mi(m);
for(mi.First();mi.NotDone();mi.Next())
mi.CurrentItem()=rand();
Однако класс такого итератора должен иметь доступ к представлению контейнера, поэтому его нужно сделать другом контейнера:
class Matrix{
...
friend class m_iter;
};
Дата добавления: 2014年01月11日; Просмотров: 383; Нарушение авторских прав?; Мы поможем в написании вашей работы!