Samo pisanie iteratorów prawie nigdy nie jest piękne. Najprostszym sposobem dodania iteratora do twoich zajęć jest ponowne użycie istniejącego. Powinieneś być świadomy, że wskaźniki są na przykład tak samo dobre jak iteratory w C++, więc istnieje wiele sposobów na zapewnienie iteratora bez konieczności pisania własnych.
Jest to zasadniczo sposób działania C++ na wiele sposobów. Próbuje uczynić język łatwym i łatwym dla użytkowników końcowych, nakładając wiele obciążeń na autorów bibliotek. To znaczy. autorzy bibliotek mogą pisać wszystkie niepretetyczne rzeczy, więc użytkownik końcowy nie musi tego robić. Iteratory są zwykle częścią biblioteki.
Mimo, że tutaj jest rzeczywisty udział brzydki:
Aby móc pisać własne iteratory, oto kilka rzeczy, które trzeba znać.
Rodzaj cechy:
typu cechy są prostym mechanizmem, aby dodać aditional informacji typów w C++, która działa nawet z typów, które nie mogą być zmieniane siebie. Na przykład dla iteratora ważne jest, aby wiedzieć, co on iteruje (tj. Typ zamknięty). Sposób uzyskania tej informacji dla danego iteratora zależy w dużej mierze od iteratora. W przypadku iteratorów, które w rzeczywistości są obiektami, można dodać typedefs do klasy i używać ich, ale w przypadku iteratorów będących wskaźnikami, które należy wywnioskować z typu wskaźnika. Aby było to możliwe, informacja jest przechowywana w typie cechy, a więc istnieje jedno miejsce, w którym kod może wyglądać na tę informację. Jest to cecha typu std::iterator_traits
.
std::iterator_traits
pracować na wszystko, co pochodzi od szablonu std::iterator
, a także na każdym rodzaju wskaźnika, bez żadnych zmian. Tak często najlepiej jest używać std::iterator
jako podstawy, aby uniknąć konieczności pisania specjalizacji własnych cech. Jeśli nie możesz tego zrobić, nadal możesz podać niezbędne cechy, ale będzie to trudniejsze.
Tag klasy i typy iteracyjnej:
Istnieje kilka różnych rodzajów iteratorów dostępnych w języku C++, które mają różne zachowania i może/nie może zrobić wiele różnych rzeczy. Spójrz na http://cplusplus.com/reference/std/iterator/, aby zobaczyć, jakie rodzaje iteratorów są dostępne i co mogą zrobić. Schematy nie mają być zorientowane obiektowo (tj. input_iterator
nie jest ani podrzędną, ani podstawową klasą forward_iterator
), ale raczej jako rodzaj derywacji API. To znaczy. możesz użyć wszystkich algorytmów zapisanych dla iteratora wejściowego również z iteratorem forward. W tabeli na stronie dowiesz się, jakie metody musisz podać dla każdej kategorii.
Ponieważ te kategorie nie są w rzeczywistości podklasami od siebie (nie powinny być, szczególnie gdy pochodzą z różnych typów kolekcji), inny mechanizm służy do określenia możliwości każdego iteratora. Pusta klasa znaczników jest również zawarta w std::iterator_traits
opisującej każdy iterator, która mówi, co ten iterator może zrobić i czego nie może zrobić. Jeśli nie piszesz własnych cech, musisz dostarczyć tę klasę znaczników do szablonu std::iterator
po utworzeniu instancji.
przykład:
Przykład pochodzi z cplusplus.Sekcja com na iteratorów:
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;}
};
ten iterator naprawdę nie ma sensu, ponieważ tylko owija wskaźnik, który może również być stosowany bezpośrednio. Jednak może służyć jako wyjaśnienie. Iterator wywodzi się z std::iterator
jako input_iterator
, dostarczając odpowiedni znacznik. Również szablon jest informowany, że iterator iteruje ponad int
s. Wszystkie pozostałe typy, które są potrzebne, są automatycznie wnioskowane przez szablon za pomocą następujących szablonów: difference_type
, reference
, poiner
. W niektórych przypadkach może być sens ręczna zmiana niektórych z tych typów (np. std::shared_ptr
musi być czasem używana jako pointer
). Również cechy potrzebne do tego iteratora będą istniały automatycznie, ponieważ są już wyprowadzone z std::iterator
, a std::iterator_traits
wie, gdzie znaleźć wszystkie niezbędne informacje.
Najprostszym sposobem jest prawdopodobnie wykorzystać 'iterator_facade' w [Boost.Iterator] (http://www.boost.org/doc/libs/release/libs/iterator/doc/iterator_facade.html). –
Jeszcze prostsze byłoby użycie systemu boost :: filesystem i unikanie konieczności pisania kodu ... –
OK, powinienem był wiedzieć, że powinienem dodać informacje, że nie chcę używać boost;) Głównie, ponieważ Chcę zrozumieć, jak działa iterator. – Ben