Писать итераторы самостоятельно вряд ли когда-нибудь красиво.Самый лучший способ добавить итератор в ваши классы - это повторно использовать существующий.Вы должны знать, что указатели, например, так же хороши, как итераторы в C ++, поэтому есть много способов предоставить итератор, фактически не создавая свой собственный.
По сути, C ++ работает во многих отношениях.Он пытается сделать язык доступным и простым для конечных пользователей, возлагая большую нагрузку на авторов библиотек.Т.е. авторы библиотек могут писать все несущественные вещи, поэтому конечному пользователю это не нужно.Итераторы, как правило, являются частью библиотеки.
Сказав это, вот собственно уродливая часть:
Чтобы иметь возможность писать свои собственные итераторы, вот некоторые вещи, которые вы должны знатьof.
Черты типа:
Черты типа - это простой механизм добавления дополнительной информации к типам в C ++, который работает даже с типами, которые нельзя изменить самим.Например, для итератора важно знать, что он повторяет (т. Е. Содержащийся тип).Способ получения этой информации для данного итератора во многом зависит от итератора.Для итераторов, которые на самом деле являются объектами, вы можете добавить typedefs в класс и использовать их, но для итераторов, которые являются указателями, вам нужно вывести его из типа указателя.Чтобы сделать это возможным, информация хранится в типовой характеристике, поэтому существует единственное место, в котором код может просматривать эту информацию.Это черта типа std::iterator_traits
.
std::iterator_traits
работает над всем, что происходит от шаблона std::iterator
, а также с любым указателем, без каких-либо настроек.Поэтому часто лучше использовать std::iterator
в качестве основы, чтобы не писать собственную специализацию черт.В случае, если вы не можете сделать это, все еще возможно предоставить необходимые черты, но это будет сложнее.
Классы тегов и типы итераторов:
Есть несколькоразличные типы итераторов, доступные в C ++, которые имеют различное поведение и могут / не могут делать много разных вещей.Посмотрите на http://cplusplus.com/reference/std/iterator/, чтобы увидеть, какие итераторы доступны и что они могут сделать.Диаграммы не предназначены для объектно-ориентированного подхода (т. Е. input_iterator
не является ни подуровнем, ни базовым классом forward_iterator
), а скорее как деривация API-типа.Т.е. вы можете использовать все алгоритмы, которые были написаны для входного итератора также с прямым итератором.Таблица на странице расскажет вам, какие методы вы должны предоставить для каждой категории.
Поскольку эти категории на самом деле не являются подклассами друг друга (их не должно быть, особенно при переходе из разных типов коллекций).), другой механизм используется для определения возможностей каждого итератора.Пустой класс тегов также включен в std::iterator_traits
, описывающий каждый итератор, который сообщает, что этот итератор может делать и что он не может делать.Если вы не пишете свои собственные черты, вам нужно предоставить этот класс тегов для шаблона std::iterator
при создании экземпляра.
Пример:
Этот пример взят израздел cplusplus.com об итераторах:
class myiterator : public iterator<input_iterator_tag, int>
{
int* p;
public:
myiterator(int* x) :p(x) {}
myiterator(const myiterator& mit) : p(mit.p) {}
myiterator& operator++() {++p;return *this;}
myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;}
bool operator==(const myiterator& rhs) {return p==rhs.p;}
bool operator!=(const myiterator& rhs) {return p!=rhs.p;}
int& operator*() {return *p;}
};
Этот итератор на самом деле не имеет смысла, поскольку он содержит только указатель, который также мог бы использоваться напрямую.Однако это может служить объяснением.Итератор выводится из std::iterator
как input_iterator
путем предоставления соответствующего тега.Также в шаблоне сказано, что этот итератор выполняет итерацию в течение int
с.Все другие типы, которые необходимы difference_type
, reference
, poiner
и т. Д., Автоматически выводятся шаблоном.В некоторых случаях может иметь смысл изменить некоторые из этих типов вручную (например, std::shared_ptr
должен иногда использоваться как pointer
).Также черты, необходимые для этого итератора, будут существовать автоматически, поскольку он уже получен из std::iterator
и std::iterator_traits
знает, где найти всю необходимую информацию.