Pointer to iterator II

Kontynuując temat konwersji wskaźnika do iteratora z poprzedniej notki, chciałbym rzucić nieco więcej światła i nowych spostrzeżeń jakie mnie naszły po popełnieniu wspomnianego wpisu, po którym nie mogłem spokojnie zasnąć ;)

Rewidując ponownie ten problem pod katem wydajnościowym i nie tylko doszedłem do wniosku, że w ogóle taka operacja jest bez sensu, uzycie takiej operacji w kodzie to bad design. Tak, świadczy to o złym projekcie i należałoby się dobrze zastanowić co dalej zrobić.

Gdy mamy opcje: wskaźnik czy iterator, powinniśmy bez namysłu wybrać iterator i z niego korzystać. W sytuacji, gdzie jakiś powodów mamy sam wskaźnik, a potrzebnym nam jest iterator to należy pomyśleć o zmianie projektu lub umożliwić wykonanie danej usługi bezpośrednio na wskazywanym obiekcie, o ile oczywiście jest to możliwe do zrealizowania.

Do potwierdzenia bezsensowności rozważmy przedstawione w poprzedniej notce sposoby wyznaczania iteratora na podstawie wskaźnika przy wykorzystaniu algorytmy find i find_if z biblioteki standardowej, przy założeniu, że implementacja wykorzystuje wskaźniki wewnątrz iteratorów, a nasz wskaźnik wskazuje na istniejący w kontenerze element.

Końcowa wersja funkcji z find po wygenerowaniu i rozwiniecie kodu przez kompilator, mogłaby wyglądać następująco:

Obj* find(Obj* first, Obj* last, const Obj& val) {
 
	while (first != last && *first != val)
		++first;
	return first;
 
}

Jako argument val podajemy nasz wskaźnik, funkcja przechodzi przez wszystkie elementy do napotkania elementu na jaki wskazuje nasz wskaźnik, po czym zwraca ten sam wskaźnik.

W wyniku czego otrzymujemy wskaźnik, ten sam wskaźnik jaki mieliśmy, wiec nic nie otrzymaliśmy. Prócz wygenerowania zbędnego kodu i narzutu w czasie wykonania. Dodatkowo, prócz zbędnej pętli, której wykonanie rośnie liniowo ze wzrostem rozmiarów kontenera i dereferencjach w każdej iteracji, wykorzystywany jest operator porównania typu Obj, co przy „cięższym” typie może być trochę kosztowne.

Wersja z find_if będzie trochę lepsza:

Obj* find_if(Obj* first, Obj* last, const Obj* val) {
 
	while (first != last && first != val)
		++first;
	return first;
 
}

Tutaj tylko porównanie wskaźników, ale nadal wykonanie pętli jest zbędne, bo z góry znamy wynik.

Oczywiście sprawa wygląda całkiem inaczej, gdy wskaźnik nie wskazuje na żaden element kontenera tylko na inny obiekt, a my chcemy znaleźć wystąpienie tego elementu w kontenerze i otrzymać odpowiedni iterator.

W innym wypadku (konwersja) jest to czysta głupota, świadczącą o źle zaprojektowanym projekcie. Powinniśmy wystrzegać się takich bezsensownych operacji.

Dobrze, że w moim projekcie, łatwo da się rozwiązać problem wskaźnik -> iterator.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *