Zrobiłem kolekcję, dla której chcę zapewnić iterator o swobodnym dostępie w stylu STL. Szukałem przykładowej implementacji iteratora, ale nie znalazłem żadnej. Wiem o potrzebie przeciążeń stałych []
i *
operatorów. Jakie są wymagania, aby iterator był „w stylu STL” i jakich innych pułapek należy unikać (jeśli w ogóle)?
Dodatkowy kontekst: dotyczy biblioteki i nie chcę od niej zależeć, chyba że naprawdę muszę. Piszę własną kolekcję, aby zapewnić zgodność binarną między C ++ 03 i C ++ 11 z tym samym kompilatorem (więc nie ma STL, który prawdopodobnie by się zepsuł).
c++
iterator
const-iterator
Tamás Szelei
źródło
źródło
Odpowiedzi:
http://www.cplusplus.com/reference/std/iterator/ ma poręczną tabelę, która szczegółowo opisuje specyfikację § 24.2.2 standardu C ++ 11. Zasadniczo iteratory mają znaczniki opisujące prawidłowe operacje, a znaczniki mają hierarchię. Poniżej jest czysto symboliczny, te klasy tak naprawdę nie istnieją jako takie.
Możesz albo specjalizować się
std::iterator_traits<youriterator>
, albo wstawiać te same typy typedef w samym iteratorze lub dziedziczyć postd::iterator
(który ma te typy typedef). Wolę drugą opcję, aby uniknąć zmiany rzeczy wstd
przestrzeni nazw i dla czytelności, ale większość ludzi dziedziczystd::iterator
.Zauważ, że iterator_category powinno być jednym z
std::input_iterator_tag
,std::output_iterator_tag
,std::forward_iterator_tag
,std::bidirectional_iterator_tag
, lubstd::random_access_iterator_tag
, w zależności od wymagań swoich iterator spełnia,. W zależności od iterator, można wybrać specjalizacjęstd::next
,std::prev
,std::advance
, astd::distance
także, ale jest to rzadko potrzebne. W niezwykle rzadkich przypadkach możesz chcieć się specjalizowaćstd::begin
istd::end
.Twój kontener prawdopodobnie powinien mieć również
const_iterator
iterator (ewentualnie zmienny) do stałych danych, który jest podobny do twojego,iterator
z wyjątkiem tego, że powinien być domyślnie możliwy do zbudowania ziterator
a użytkownicy nie powinni mieć możliwości modyfikacji danych. Często wskaźnik wewnętrzny jest wskaźnikiem nietrwałych danych iiterator
dziedziczy po nimconst_iterator
, aby zminimalizować powielanie kodu.Mój post w Writing Your STL Container ma bardziej kompletny prototyp kontenera / iteratora.
źródło
std::iterator_traits
lub samodzielnego definiowania typedefs, możesz także po prostu wyprowadzić się zstd::iterator
, co definiuje je dla Ciebie, w zależności od parametrów szablonu.const_iterator
. Czego jeszcze brakowało w moim poście? Wydaje się, że sugerujesz, że jest coś więcej do dodania do klasy, ale pytanie dotyczy konkretnie implementacji iteratorów.std::iterator
został zaproponowany jako przestarzały w C ++ 17 ; tak nie było, ale nie liczyłbym na to, że będzie tu dużo dłużej.std::iterator
była przestarzała.operator bool
jest niesamowicie niebezpieczne. Ktoś spróbuje użyć tego do wykrycia końca zakresuwhile(it++)
, ale tak naprawdę sprawdza tylko, czy iterator został zbudowany z parametrem.Dokumentacja iterator_facade z Boost.Iterator zapewnia coś, co wygląda jak fajny samouczek na temat implementowania iteratorów dla połączonej listy. Czy możesz to wykorzystać jako punkt wyjścia do zbudowania iteratora o dostępie swobodnym nad kontenerem?
Jeśli nic więcej, możesz przyjrzeć się funkcjom członkowskim i typedefsom dostarczonym przez
iterator_facade
i użyć go jako punktu wyjścia do budowania własnych.źródło
Thomas Becker napisał przydatny artykuł na ten temat tutaj .
Było też (być może prostsze) podejście, które pojawiło się wcześniej w SO: Jak poprawnie zaimplementować niestandardowe iteratory i const_iterators?
źródło
Oto próbka surowego iteratora wskaźnika.
Nie powinieneś używać klasy iteratora do pracy z surowymi wskaźnikami!
Surowe obejście oparte na zakresie wskaźnika. Proszę mnie poprawić, jeśli istnieje lepszy sposób na utworzenie pętli opartej na zakresie z surowego wskaźnika.
I prosty test
źródło
Przede wszystkim można zajrzeć tutaj , aby uzyskać listę różnych operacji poszczególne typy iteracyjnej potrzebują wsparcia.
Następnie, gdy utworzysz klasę iteratora, musisz albo się
std::iterator_traits
w niej specjalizować i podać niezbędnetypedef
s (jakiterator_category
lubvalue_type
), albo alternatywnie wyprowadzić ją zstd::iterator
, która określatypedef
dla ciebie potrzebne s i dlatego może być używana z domyślnąstd::iterator_traits
.zrzeczenie się odpowiedzialności: Wiem, że niektórym ludziom się to nie podoba
cplusplus.com
, ale dostarczają naprawdę przydatnych informacji na ten temat.źródło
Byłem / jestem na tej samej łodzi co ty z różnych powodów (częściowo edukacyjnych, częściowo ograniczeń). Musiałem przepisać wszystkie pojemniki ze standardowej biblioteki i pojemniki musiały być zgodne ze standardem. Oznacza to, że jeśli wymienię kontener z wersją stl , kod będzie działał tak samo. Co również oznaczało, że musiałem ponownie napisać iteratory.
W każdym razie spojrzałem na EASTL . Oprócz nauki tony o pojemnikach, których nigdy nie nauczyłem się za pomocą pojemników STL lub moich kursów licencjackich. Głównym powodem jest to, że EASTL jest bardziej czytelny niż odpowiednik stl (znalazłem to po prostu z powodu braku wszystkich makr i prostego stylu kodowania). Jest tam kilka obrzydliwych rzeczy (np. #Ifdefs za wyjątkami), ale nic cię nie przytłacza.
Jak wspomniano inni, spójrz na referencję cplusplus.com dotyczącą iteratorów i kontenerów.
źródło
Próbowałem rozwiązać problem z możliwością iteracji kilku różnych tablic tekstowych, z których wszystkie są przechowywane w dużej bazie danych rezydentnej pamięci
struct
.Poniższe informacje opracowano przy użyciu Visual Studio 2017 Community Edition w aplikacji testowej MFC. Podaję to jako przykład, ponieważ ten post był jednym z kilku, które natknąłem się, które pod warunkiem, że pomoc była jeszcze niewystarczająca na moje potrzeby.
Te
struct
zawierające dane rezydentami pamięci wyglądał mniej więcej tak. Usunąłem większość elementów ze względu na zwięzłość i nie uwzględniłem również używanych definicji Preprocesora (używany zestaw SDK jest przeznaczony zarówno dla C, jak i C ++ i jest stary).Chciałem mieć iteratory dla różnych
WCHAR
dwuwymiarowych tablic zawierających ciągi tekstowe dla mnemoników.Obecne podejście polega na użyciu szablonu do zdefiniowania klasy proxy dla każdej z tablic, a następnie posiadania pojedynczej klasy iteratora, której można użyć do iteracji po określonej tablicy za pomocą obiektu proxy reprezentującego tablicę.
Kopia danych rezydentnych pamięci jest przechowywana w obiekcie, który obsługuje odczytywanie i zapisywanie danych rezydentnych pamięci z / na dysk. Ta klasa,
CFilePara
przedstawiona na matrycy klasy proxy (MnemonicIteratorDimSize
i klasy sub, z którego ona pochodzi,MnemonicIteratorDimSizeBase
) oraz klasę iteracyjnej,MnemonicIterator
.Utworzony obiekt proxy jest dołączony do obiektu iteratora, który uzyskuje dostęp do niezbędnych informacji poprzez interfejs opisany przez klasę podstawową, z której pochodzą wszystkie klasy proxy. W rezultacie powstaje jeden typ klasy iteratora, który może być używany z kilkoma różnymi klasami proxy, ponieważ wszystkie różne klasy proxy udostępniają ten sam interfejs, interfejs podstawowej klasy proxy.
Pierwszą rzeczą było stworzenie zestawu identyfikatorów, które byłyby dostarczane do fabryki klas w celu wygenerowania konkretnego obiektu proxy dla tego typu mnemonika. Te identyfikatory są używane jako część interfejsu użytkownika w celu identyfikacji konkretnych danych udostępniania, które użytkownik jest zainteresowany przeglądaniem i ewentualną modyfikacją.
Klasa proxy
Templowana klasa proxy i jej klasa podstawowa są następujące. Musiałem pomieścić kilka różnych rodzajów
wchar_t
tablic tekstowych. Dwuwymiarowe tablice miały różną liczbę mnemoników, w zależności od typu (celu) mnemonika, a różne typy mnemoniki miały różne maksymalne długości, wahające się od pięciu znaków tekstowych do dwudziestu znaków tekstowych. Szablony dla pochodnej klasy proxy były w naturalny sposób dopasowane do szablonu wymagającego maksymalnej liczby znaków w każdym mnemoniku. Po utworzeniu obiektu proxy używamySetRange()
metody do określenia rzeczywistej tablicy mnemonicznej i jej zakresu.Klasa Iterator
Sama klasa iteratora wygląda następująco. Ta klasa zapewnia tylko podstawową funkcjonalność iteratora do przodu, która jest wszystkim, co jest potrzebne w tym momencie. Jednak oczekuję, że to się zmieni lub rozszerzy, gdy będę potrzebować czegoś dodatkowego.
Fabryka obiektów proxy określa, który obiekt ma zostać utworzony na podstawie identyfikatora mnemonicznego. Obiekt proxy jest tworzony, a zwracany wskaźnik jest standardowym typem klasy bazowej, aby mieć jednolity interfejs niezależnie od tego, które z różnych sekcji mnemonicznych są dostępne.
SetRange()
Sposób stosuje się w celu wskazania obiektu proxy poszczególnych elementów tablicy proxy reprezentuje oraz szereg elementów macierzy.Korzystanie z klasy proxy i iteratora
Klasa proxy i jej iterator są używane, jak pokazano w poniższej pętli, w celu wypełnienia
CListCtrl
obiektu listą mnemoników. Używamstd::unique_ptr
tak, że gdy klasa proxy nie jest już potrzebna, astd::unique_ptr
poza zasięgiem, pamięć zostanie wyczyszczona.Tym kodem źródłowym jest utworzenie obiektu proxy dla tablicy w obrębie
struct
odpowiadającego podanemu identyfikatorowi mnemonicznemu. Następnie tworzy iterator dla tego obiektu, używa zakresufor
do wypełnieniaCListCtrl
formantu, a następnie czyści. Są to nieprzetworzonewchar_t
ciągi tekstowe, które mogą być dokładnie liczbą elementów tablicy, więc kopiujemy ciąg do tymczasowego bufora, aby upewnić się, że tekst jest zerowany.źródło
A teraz iterator kluczy dla pętli opartej na zakresie.
Stosowanie:
Właśnie tego szukałem. Ale wydaje się, że nikt go nie miał.
Dostajesz moje wyrównanie kodu OCD jako bonus.
Jako ćwiczenie napisz własne
values(my_map)
źródło