SlideShare a Scribd company logo
1 of 21
Download to read offline
IDZ DO
         PRZYK£ADOWY ROZDZIA£

                           SPIS TRE CI   STL w praktyce. 50 sposobów
                                         efektywnego wykorzystania
           KATALOG KSI¥¯EK               Autor: Scott Meyers
                                         T³umaczenie: Adam Majczak (rozdz. 1-5),
                      KATALOG ONLINE     Wojciech Moch (rozdz. 6, 7, dod. A-C)
                                         ISBN: 83-7361-373-0
       ZAMÓW DRUKOWANY KATALOG           Tytu³ orygina³u: Effective STL 50 Specific Ways
                                         to Improve Your Use of the Standard Template Library
                                         Format: B5, stron: 282
              TWÓJ KOSZYK
                                         Standard Template Library to jedno z najpotê¿niejszych narzêdzi programistycznych
                    DODAJ DO KOSZYKA     charakteryzuj¹ce siê z³o¿ono ci¹ i wysokim stopniem komplikacji. W ksi¹¿ce
                                         „STL w praktyce. 50 sposobów efektywnego wykorzystania” — przeznaczonej dla
                                         zaawansowanych i rednio zaawansowanych programistów C++ — znany ekspert Scott
         CENNIK I INFORMACJE             Meyers prezentuje najwa¿niejsze techniki stosowania tego narzêdzia. Poza list¹ zaleceñ,
                                         jak postêpowaæ nale¿y, a jak postêpowaæ siê nie powinno, Meyers przedstawia wiele
                   ZAMÓW INFORMACJE      powa¿niejszych aspektów STL pozwalaj¹cych zrozumieæ, dlaczego pewne rozwi¹zania
                     O NOWO CIACH        s¹ poprawne, a innych nale¿y unikaæ. Do ka¿dej wskazówki do³¹czony jest kod
                                          ród³owy i dok³adne obja nienia, które powinny zainteresowaæ zaawansowanych
                       ZAMÓW CENNIK      programistów.
                                         Je¿eli Twoja wiedza ogranicza siê do informacji dostêpnych w dokumentacji STL,
                                         ta ksi¹¿ka uzupe³ni j¹ o bezcenne wskazówki, które pozwol¹ Ci wykorzystaæ STL
                 CZYTELNIA               w praktyce.
                                         Ksi¹¿ka przedstawia:
          FRAGMENTY KSI¥¯EK ONLINE
                                            • Podstawowe informacje o bibliotece STL i jej zgodno ci z innymi standardami
                                            • Wskazówki dotycz¹ce poprawnego doboru i u¿ywania pojemników
                                            • Opis pojemników typu vector i string
                                            • Omówienie pojemników asocjatywnych
                                            • Metody w³a ciwego korzystania z iteratorów
                                            • Algorytmy wchodz¹ce w sk³ad STL
                                            • Funktory, klasy-funktory i funkcje
                                            • Programowanie za pomoc¹ biblioteki STL
                                         Ksi¹¿kê uzupe³niaj¹ dodatki, w których znajdziesz miêdzy innymi obszern¹ bibliografiê
                                         na temat STL oraz cenne wskazówki dla programistów u¿ywaj¹cych kompilatorów
                                         firmy Microsoft.
Wydawnictwo Helion
ul. Chopina 6                            „STL w praktyce. 50 sposobów efektywnego wykorzystania” to nieocenione ród³o
44-100 Gliwice                           wiedzy na temat jednego z najwa¿niejszych aspektów programowania w C++.
tel. (32)230-98-63                       Je¿eli chcesz w praktyce wykorzystaæ STL, nie obêdziesz siê bez informacji
e-mail: helion@helion.pl                 zawartych w tej ksi¹¿ce.
Spis treści
                Podziękowania................................................................................... 9
                Przedmowa...................................................................................... 13
                Wprowadzenie ................................................................................. 17
Rozdział 1. Kontenery........................................................................................ 29
                Zagadnienie 1. Uwa nie dobierajmy kontenery................................................................30
                Zagadnienie 2. Nie dajmy się zwieść iluzji o istnieniu kodów niezale nych
                  do zastosowanego kontenera........................................................................................35
                Zagadnienie 3. Kopiowanie obiektów w kontenerach powinno być „tanie”,
                  łatwe i poprawne ..........................................................................................................40
                Zagadnienie 4. Stosujmy metodę empty(), zamiast przyrównywać
                  rozmiar size() do zera...................................................................................................43
                Zagadnienie 5. Preferujmy metody operujące na całych zakresach (podzbiorach),
                  bo są bardziej efektywne ni ich odpowiedniki operujące pojedynczymi elementami...45
                Zagadnienie 6. Bądźmy wyczuleni i przygotowani na najbardziej
                  kłopotliwą interpretację kompilatora C++ ...................................................................56
                Zagadnienie 7. Gdy stosujemy kontenery zawierające wskaźniki zainicjowane
                  za pomocą operatora new, pamiętajmy o zwolnieniu dynamicznej pamięci
                  operatorem delete, zanim zawierający je obiekt-kontener sam zostanie usunięty.......59
                Zagadnienie 8. Nigdy nie twórzmy kontenerów zawierających wskaźniki
                  kategorii auto_ptr .........................................................................................................64
                Zagadnienie 9. Uwa nie dobierajmy opcje do operacji kasowania ..................................67
                Zagadnienie 10. Uwa ajmy na konwencje dynamicznej alokacji pamięci
                  i stosowne ograniczenia ...............................................................................................73
                Zagadnienie 11. Zrozumienie uprawnionych zastosowań alokatorów pamięci
                  tworzonych przez u ytkownika....................................................................................80
                Zagadnienie 12. Miejmy umiarkowane i realistyczne oczekiwania odnośnie
                  poziomu bezpieczeństwa wielu wątków obsługiwanych przez kontenery STL ..........84
6                                                                                                                              Spis treści


Rozdział 2. Kontenery typu vector oraz string..................................................... 89
                 Zagadnienie 13. Lepiej stosować kontenery vector oraz string ni dynamicznie
                   przydzielać pamięć tablicom........................................................................................90
                 Zagadnienie 14. Stosujmy metodę reserve, by uniknąć zbędnego przemieszczania
                   elementów w pamięci...................................................................................................93
                 Zagadnienie 15. Bądźmy ostro ni i wyczuleni na zró nicowanie implementacji
                   kontenerów typu string.................................................................................................96
                 Zagadnienie 16. Powinieneś wiedzieć, jak przesyłać dane z kontenerów vector
                   i string do klasycznego interfejsu zgodnego z C........................................................102
                 Zagadnienie 17. Sztuczka programistyczna „swap trick” pozwalająca
                   na obcięcie nadmiarowej pojemności ........................................................................106
                 Zagadnienie 18. Unikajmy stosowania wektora typu vector<bool>...............................108

Rozdział 3. Kontenery asocjacyjne ................................................................... 111
                 Zagadnienie 19. Ten sam czy taki sam — zrozumienie ró nicy pomiędzy
                   relacjami równości a równowa ności ........................................................................112
                 Zagadnienie 20. Określajmy typy porównawcze dla kontenerów asocjacyjnych
                   zawierających wskaźniki............................................................................................117
                 Zagadnienie 21. Funkcje porównujące powinny dla dwóch dokładnie równych
                   wartości zawsze zwracać wartość false......................................................................122
                 Zagadnienie 22. Unikajmy bezpośredniego modyfikowania klucza w kontenerach
                   typu set i multiset .......................................................................................................126
                 Zagadnienie 23. Rozwa my zastąpienie kontenerów asocjacyjnych
                   posortowanymi wektorami.........................................................................................133
                 Zagadnienie 24. Gdy efektywność działania jest szczególnie istotna, nale y bardzo
                   uwa nie dokonywać wyboru pomiędzy map::operator[]() a map::insert()................140
                 Zagadnienie 25. Zapoznaj się z niestandardowymi kontenerami mieszanymi ...............146

Rozdział 4. Iteratory ........................................................................................ 151
                 Zagadnienie 26. Lepiej wybrać iterator ni const_iterator, reverse_iterator
                   czy const_reverse_iterator..........................................................................................151
                 Zagadnienie 27. Stosujmy funkcje distance() i advance(), by przekształcić
                   typ const_iterator na typ iterator ................................................................................155
                 Zagadnienie 28. Jak stosować metodę base() nale ącą do klasy reverse_iterator
                   w celu uzyskania typu iterator?..................................................................................159
                 Zagadnienie 29. Rozwa zastosowanie iteratorów typu istreambuf_iterator
                   do wczytywania danych znak po znaku .....................................................................163

Rozdział 5. Algorytmy...................................................................................... 165
                 Zagadnienie 30. Upewnijmy się, e docelowe zakresy są wystarczająco obszerne .......166
                 Zagadnienie 31. Pamiętajmy o dostępnych i stosowanych opcjach sortowania .............171
                 Zagadnienie 32. Stosujmy metodę erase() w ślad za algorytmami kategorii remove(),
                   jeśli naprawdę chcemy coś skutecznie usunąć z kontenera .......................................177
                 Zagadnienie 33. Przezornie i ostro nie stosujmy algorytmy kategorii remove()
                   wobec kontenerów zawierających wskaźniki ............................................................182
                 Zagadnienie 34. Zwracajmy uwagę, które z algorytmów oczekują
                   posortowanych zakresów ...........................................................................................186
Spis treści                                                                                                                                   7


                Zagadnienie 35. Implementujmy zwykłe porównywanie łańcuchów znaków bez
                  rozró niania wielkości liter za pomocą mismatch() lub lexicographical_compare() ....190
                Zagadnienie 36. Zrozum prawidłową implementację algorytmu copy_if()....................196
                Zagadnienie 37. Stosujmy accumulate() lub for_each() do operacji grupowych
                  na zakresach ...............................................................................................................198

Rozdział 6. Funktory, klasy-funktory, funkcje i inne........................................... 205
                Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość ........205
                Zagadnienie 39. Predykaty powinny być funkcjami czystymi .......................................208
                Zagadnienie 40. Klasy-funktory powinny być adaptowalne...........................................212
                Zagadnienie 41. Po co stosować funkcje ptr_fun, mem_fun i mem_fun_ref?................215
                Zagadnienie 42. Upewnij się, e less<t>() oznacza operator<().....................................219

Rozdział 7. Programowanie za pomocą biblioteki STL ....................................... 223
                Zagadnienie 43. U ywaj algorytmów, zamiast pisać pętle .............................................223
                Zagadnienie 44. Zamiast algorytmów stosujmy metody o takich samych nazwach ......231
                Zagadnienie 45. Rozró nianie funkcji count(), find(), binary_search(),
                  lower_bound(), upper_bound() i equal_range() .........................................................233
                Zagadnienie 46. Jako parametry algorytmów stosuj funktory, a nie funkcje .................241
                Zagadnienie 47. Unikaj tworzenia kodu „tylko do zapisu” ............................................245
                Zagadnienie 48. Zawsze dołączaj właściwe pliki nagłówkowe......................................248
                Zagadnienie 49. Naucz się odczytywać komunikaty kompilatora związane
                  z biblioteką STL .........................................................................................................249
                Zagadnienie 50. Poznaj strony WWW związane z biblioteką STL ................................256

Dodatek A Bibliografia .................................................................................... 263
                Ksią ki napisane przeze mnie .........................................................................................263
                Ksią ki, które nie ja napisałem (choć chciałbym)...........................................................264

Dodatek B Porównywanie ciągów znaków bez uwzględniania wielkości liter ..... 267
                Jak wykonywać porównywanie ciągów znaków bez uwzględniania wielkości liter
                   — artykuł autorstwa Matta Austerna .........................................................................267

Dodatek C Uwagi na temat platformy STL Microsoftu ...................................... 277
                Szablony metod w STL ...................................................................................................277
                MSVC wersje 4 do 6 .......................................................................................................278
                Rozwiązania dla kompilatorów MSVC w wersji 4 do 5.................................................279
                Dodatkowe rozwiązanie dla kompilatora MSVC w wersji 6..........................................280

                Skorowidz...................................................................................... 283
Rozdział 6.
Funktory, klasy-funktory,
funkcje i inne
                                                                              Czy nam się to podoba czy nie, funkcje i podobne do funkcji obiekty — funktory — są
                                                                              częścią STL. Kontenery asocjacyjne stosują je do porządkowania swoich elemen-
                                                                              tów, w algorytmach typu HKPFAKH
 wykorzystywane są do kontroli zachowań tych al-
                                                                              gorytmów. Algorytmy takie jak HQTAGCEJ
 i VTCPUHQTO
 są bez funktorów zupełnie
                                                                              nieprzydatne, natomiast adaptory typu PQV
 i DKPFPF
 słu ą do tworzenia funktorów.

                                                                              Wszędzie gdzie spojrzeć, mo na w STL znaleźć funktory i klasy-funktory. Znajdą się
                                                                              one równie w Twoich kodach źródłowych. Efektywne zastosowanie STL bez umiejęt-
                                                                              ności tworzenia dobrych funktorów jest po prostu niemo liwe. Z tego powodu większa
                                                                              część tego rozdziału będzie opisywać sposoby takiego tworzenia funktorów, aby zacho-
                                                                              wywały się one zgodnie z wymaganiami STL. Jednak jedno z zagadnień opisuje zupeł-
                                                                              nie inny temat. Ten podrozdział spodoba się osobom, które zastanawiały się, dlaczego
                                                                              konieczne jest zaśmiecanie kodu programu wywołaniami funkcji RVTAHWP
, OGOAHWP

                                                                              i OGOAHWPATGH
. Oczywiście mo na zacząć lekturę od tego właśnie podrozdziału, „Za-
                                                                              gadnienie 41.”, ale proszę nie poprzestawać na nim. Po poznaniu przedstawionych tam
                                                                              funkcji konieczne będzie zapoznanie się z informacjami z pozostałych, dzięki czemu
                                                                              Twoje funkcje będą prawidłowo działały zarówno z tymi funkcjami, jak i z resztą STL.



Zagadnienie 38.
Projektowanie klas-funktorów
do przekazywania przez wartość
Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość




                                                                              Języki C i C++ nie pozwalają na przekazywanie funkcji w parametrach innych funkcji.
                                                                              Konieczne jest przekazywanie wskaźników na te funkcje. Na przykład poni ej znajduje
                                                                              się deklaracja funkcji SUQTV
 z biblioteki standardowej.
206                                     Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne


        XQKF SUQTV
XQKF
DCUG UKGAV POGOD UKGAV UKG
                   KPV
EORHEP
EQPUV XQKF
EQPUV XQKF
„Zagadnienie 46.” wyjaśnia, dlaczego algorytm UQTV
 jest zazwyczaj lepszym wy-
      borem od funkcji SUQTV
. Teraz omówimy sposób deklarowania parametru EORHEP
      w funkcji SUQTV
. Po dokładniejszym przyjrzeniu się wszystkim gwiazdkom okazuje
      się, e argument EORHEP, który jest wskaźnikiem na funkcję, jest kopiowany (czyli prze-
      kazywany przez wartość) z miejsca wywołania do funkcji SUQTV
. Jest to typowy przy-
      kład zasady stosowanej w bibliotekach standardowych języków C i C++, czyli prze-
      kazywania przez wartość wskaźników na funkcje.

      W STL obiekty funkcyjne modelowane są podobnie do wskaźników na funkcje, wobec
      czego w STL obiekty funkcyjne są równie przekazywane do i z funkcji przez war-
      tość. Najlepszym przykładem mo e być deklaracja funkcji HQTAGCEJ
 algorytmu po-
      bierającego i zwracającego przez wartość funktory.
        VGORNCVGENCUU +PRWV+VGTCVQT
                 ENCUU (WPEVKQP
        (WPEVKQP                                YTCECPC RTG YCTVQ è
        HQTAGCEJ
+PRWV+VGTCVQT HKTUV
                 +PRWV+VGTCVQT NCUV
                 (WPEVKQP H                   RTGMC[YCPC RTG YCTVQ è

      Tak na prawdę przekazywanie przez wartość nie jest absolutnie wymagane, ponie-
      wa w wywołaniu funkcji HQTAGCEJ
 mo na wyraźnie zaznaczyć typy parametrów.
      Na przykład w poni szym kodzie funkcja HQTAGCEJ
 pobiera i zwraca funktory przez
      referencję.
        ENCUU Q5QOGVJKPI
          RWDNKE WPCT[AHWPEVKQPKPV XQKF ]           Y RQFTQFKCNG CICFPKGPKG 
        QRKUCPC
                                                      QUVCPKG MNCUC DCQYC
          XQKF QRGTCVQT

KPV Z ] _
           
        _
        V[RGFGH FGSWGKPV KVGTCVQT GSWG+PV+VGT     Y[IQFPC FGMNCTCELC V[RGFGH
        FGSWGKPV FK
        
        Q5QOGVJKPI F                                WVYQTGPKG HWPMVQTC
        
        HQTAGCEJGSWG+PV+VGT                          Y[YQ CPKG HWPMELK HQTAGCEJ 
                 Q5QOGVJKPI 
FKDGIKP
               RCTCOGVTCOK V[RW GSWG+PV+VGT
                               FKGPF
                K Q5QOGVJKPI
                               F                      Y VGP URQUÎD F OWUK D[è RTGMCCPC
                                                        K YTÎEQPC RTG TGHGTGPELú

      U ytkownicy STL prawie nigdy nie przeprowadzają tego typu operacji, co więcej,
      pewne implementacje niektórych algorytmów STL nie skompilują się w przypadku
      przekazywania funktorów przez referencję. W pozostałej części tego sposobu będę za-
      kładał, e funktory mo na przekazywać wyłącznie przez wartość, co w praktyce jest
      niemal zawsze prawdą.

      Funktory muszą być przekazywane przez wartość, dlatego to na Tobie spoczywa cię ar
      sprawdzenia, czy Twoje funktory przekazywane w ten sposób zachowują się prawidło-
      wo. Implikuje to dwie rzeczy. Po pierwsze, muszą one być małe, bo kopiowanie du ych
Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość                 207


       obiektów jest bardzo kosztowne. Po drugie, Twoje funktory nie mogą być polimor-
       ficzne (muszą być monomorficzne), co oznacza, e nie mogą u ywać funkcji wirtual-
       nych. Wynika to z faktu, e obiekt klasy pochodnej przekazywany w parametrze typu
       klasy bazowej zostaje obcięty, czyli w czasie kopiowania usunięte będą z niego
       odziedziczone elementy. W podrozdziale „Zagadnienie 3.” opisano inny problem, jaki
       rozczłonkowanie (ang. slicing) tworzy w czasie stosowania biblioteki STL.

       Unikanie rozczłonkowania obiektów i uzyskiwanie dobrej wydajności są oczywiście
       bardzo wa ne, ale trzeba pamiętać, e nie wszystkie funkcje mogą być małe i nie wszy-
       stkie obiekty mogą być monomorficzne. Jedną z zalet funktorów w porównaniu ze
       zwykłymi funkcjami jest fakt, e mogą one przechowywać informację o swoim stanie.
       Niektóre funktory muszą być du e, dlatego tak wa na jest mo liwość przekazywania
       takich obiektów do algorytmów STL z równą łatwością jak obiektów niewielkich.

       Zakaz u ywania funktorów polimorficznych jest równie nierealistyczny. Język C++
       pozwala na tworzenie hierarchii dziedziczenia i dynamicznego wiązania. Te mo li-
       wości są tak samo u yteczne w czasie tworzenia klas-funktorów, jak i w innych miej-
       scach. Klasy-funktory bez dziedziczenia byłyby jak C++ bez „++”. Oczywiście ist-
       nieje sposób umo liwiający tworzenie du ych i polimorficznych obiektów funkcyjnych,
       a jednocześnie umo liwiający przekazywanie ich przez wartość, zgodnie z konwencją
       przyjętą w STL.

       Ten sposób wymaga przeniesienia wszystkich danych i funkcji polimorficznych do in-
       nej klasy, a następnie w klasie-funktorze umieszczenie wskaźnika na tę nową klasę.
       Na przykład utworzenie klasy polimorficznej zawierającej du e ilości danych:
          VGORNCVGV[RGPCOG 6
          ENCUU $2(%                              $2(%  $KI 2QN[OQTRJKE
            RWDNKE                                        (WPEVQT %NCUU
                                                            9KGNMC 2QNKHQTOKEPC
                                                           -NCUC(WPMVQT
               WPCT[AHWPEVKQP6XQKF ]             6C MNCUC DCQYC QUVCPKG
                                                  QDLC PKQPC Y RQFTQFKCNG CICFPKGPKG 
            RTKXCVG
               9KFIGV Y                          6C MNCUC CYKGTC YKGNG FCP[EJ
               KPV Z                             FNCVGIQ RTGMC[YCPKG KEJ RTG
                                               YCTVQ è D[ QD[ PKGGHGMV[YPG
             RWDNKE
               XKTVWCN XQKF QRGTCVQT

EQPUV 6   XCN EQPUV VQ LGUV HWPMELC YKTVWCNPC
          FNCVGIQ
                                                           TQE QPMQYCPKG NG PC PKæ
          YR [PKG
          _

       wymaga utworzenia małej monomorficznej klasy zawierającej wskaźnik na klasę
       implementacji i umieszczenie w klasie implementacji wszystkich danych i funkcji
       wirtualnych:
          VGORNCVGV[RGPCOG 6                     MNCUC KORNGOGPVCELK FNC
          ENCUU $2(%+ORN
            RWDNKE WPCT[AHWPEVKQP6 XQKF ]       OQF[HKMQYCPGL MNCU[ $2(%
          RTKXCVG
            9KFIGV Y                             RTGPKGUKQPQ VWVCL YU[UVMKG FCPG
            KPV Z                                CYCTVG FQ VGL RQT[ Y MNCUKG $2(%
208                                                                                        Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne


                                                                XKTVWCN `$2(%+ORN
                 MNCU[ RQNKOQTHKEPG OWUæ OKGè
                                                                                                   YKTVWCNPG FGUVTWMVQT[
                                                               XKTVWCN XQKF QRGTCVQT

EQPUV 6 XCN EQPUV
                                                             HTKGPF ENCUU $2(%6                  MNCUC $2(% DúFKG OKC C FQUVúR FQ FCP[EJ
                                                             _

                                                             VGORNCVGV[RGPCOG 6
                                                             ENCUU $2(%                            PKGYKGNMC OQPQOQTHKEPC YGTULC
                                                               RWDNKE WPCT[AHWPEVKQP6 XQKF ]      MNCU[ $2(%
                                                             RTKXCVG
                                                               $2(%+ORN6
R+ORN                   VQ Uæ YU[UVMKG FCPG RTGEJQY[YCPG
                                                                                                    RTG MNCUú $2(%
                                                             RWDNKE
                                                               XQKF QRGTCVQT

EQPUV 6 XCNEQPUV  VC PKGYKTVWCNPC OGVQFC RTGMCWLG
                                                               ]                                  Y[YQ CPKG FQ MNCU[ $2(%+ORN
                                                                 R+ORN QRGTCVQT

XCN
                                                               _
                                                               
                                                             _

                                                           Funkcja QRGTCVQT

 w klasie $2(% przedstawia sposób implementacji wszystkich
                                                           niemal wirtualnych funkcji w tej klasie. Wywołują one swoje wirtualne odpowiedniki
                                                           z klasy $2(%+ORN. W efekcie otrzymujemy małą i monomorficzną klasę $2(%, która ma
                                                           dostęp do du ej ilości danych i zachowuje się jak klasa polimorficzna.

                                                           Pomijam tutaj pewne szczegóły, poniewa podstawa naszkicowanej tu techniki jest do-
                                                           brze znana programistom C++. Opisywana jest ona w ksią ce Effective C++ w roz-
                                                           dziale 34. W ksią ce Design Patterns [6] nazywana jest Bridge Pattern, natomiast Sut-
                                                           ter w swojej ksią ce Exceptional C++ [8] nazywa ją Idiomem Pimpl.

                                                           Z punktu widzenia STL podstawową rzeczą, o której nale y pamiętać, jest fakt, e klasy
                                                           stosujące tę technikę muszą rozsądnie obsługiwać kopiowanie. Autor opisywanej wy ej
                                                           klasy $2(% musiałby odpowiednio zaprojektować jej konstruktor kopiujący, tak aby pra-
                                                           widłowo obsługiwał wskazywany przez klasę obiekt $2(%+ORN. Najprawdopodobniej
                                                           najprostszym sposobem będzie zliczanie referencji do niego, podobnie jak w przypadku
                                                           szablonu UJCTGFARVT, o którym mo na przeczytać w podrozdziale „Zagadnienie 50.”.

                                                           Tak naprawdę, na potrzeby tego podrozdziału nale y jedynie zadbać o właściwe zacho-
                                                           wanie konstruktora kopiującego. W końcu funktory są zawsze kopiowane (czyli prze-
                                                           kazywane przez wartość) w momencie przekazywania ich do i z funkcji, a to oznacza
                                                           dwie rzeczy: muszą być małe i monomorficzne.



Zagadnienie 39.
Predykaty powinny być
funkcjami czystymi
Zagadnienie 39. Predykaty powinny być funkcjami czystymi




                                                           Obawiam się, e będziemy musieli zacząć od zdefiniowania pewnych pojęć.
Zagadnienie 39. Predykaty powinny być funkcjami czystymi                                     209


            Predykat to funkcja zwracająca wartość typu DQQN (lub inną dającą się łatwo
            przeło yć na DQQN). Predykaty są funkcjami często wykorzystywanymi
            w STL. Są to, na przykład, funkcje porównujące w standardowych
            kontenerach asocjacyjnych; wykorzystują je równie (pobierają jako
            parametry) ró nego rodzaju algorytmy sortujące, a tak e algorytmy typu
            HKPFAKH
. Opis algorytmów sortujących znajduje się w podrozdziale
            „Zagadnienie 31.”.
            Funkcje czyste to funkcje, których wartość zwracana zale y wyłącznie
            od wartości jej parametrów. Je eli H
 jest funkcją czystą, a Z i [ są obiektami,
            to wartość zwracana przez tę funkcję mo e zmienić się wyłącznie
            w przypadku zmiany w obiekcie Z lub [.
            W języku C++ dane, których u ywa funkcja czysta, są albo przekazywane
            jako parametry albo pozostają niezmienne w czasie ycia funkcji (oznacza
            to, e muszą być zadeklarowane jako EQPUV). Je eli funkcja czysta
            wykorzystywałaby dane zmieniające się między poszczególnymi jej
            wywołaniami, wtedy kolejne wywołania z tymi samymi parametrami mogłyby
            zwracać ró ne wartości, a to byłoby niezgodne z definicją funkcji czystej.

       Powinno to wystarczyć do wyjaśnienia dlaczego predykaty powinny być funkcjami
       czystymi. Teraz muszę jedynie Cię przekonać, e ta rada ma solidne podstawy. W zwią-
       zku z tym będę musiał wyjaśnić jeszcze jedno pojęcie.
            Klasa-predykat jest klasą-funktorem, w której funkcja QRGTCVQT

 jest
            predykatem, co znaczy, e zwraca wartości VTWG lub HCNUG albo wartość,
            którą mo na bezpośrednio przekształcić na wartość logiczną. Jak mo na
            się domyślić, w miejscach, w których STL oczekuje podania predykatu,
            mo na podać albo rzeczywisty predykat albo obiekt-predykat.

       To wszystko. Teraz mogę zacząć udowadniać, e naprawdę warto przestrzegać porad
       przedstawionych w tym podrozdziale.

       W podrozdziale „Zagadnienie 38.” wyjaśniłem, e funktory przekazywane są przez wa-
       rtość, dlatego nale y tak je budować, aby mo na je było łatwo kopiować. W przypadku
       funktorów będących predykatami istnieje jeszcze jeden powód takiego projektowania.
       Algorytmy mogą pobierać kopie funktorów i przechowywać je przez pewien czas, za-
       nim ich u yją. Jak mo na się spodziewać, implementacje niektórych algorytmów oczy-
       wiście korzystają z tej mo liwości. Efektem takiego stanu rzeczy jest fakt, e funkcje
       predykatów muszą być funkcjami czystymi.

       Aby móc udowodnić te twierdzenia, załó my, e budując klasę nie zastosujemy się do
       nich. Przyjrzyjmy się poni szej (źle zaprojektowanej) klasie-predykatowi. Niezale nie
       od przekazywanych jej parametrów zwraca ona wartość VTWG tylko raz — przy trze-
       cim wywołaniu. W pozostałych przypadkach zwraca wartość HCNUG.
          ENCUU $CF2TGFKECVG                         Y RQFTQFKCNG CICFPKGPKG 
          PCLFKGU
            RWDNKE WPCT[AHWPEVKQP9KFIGV DQQN ]      KPHQTOCELG PC VGOCV MNCU[ DCQYGL
          RWDNKE
            $CF2TGFKECVG
VKOGU%CNNGF
 ]_          KPKELCNKCELC OKGPPGL VKOGU%CNNGF
            DQQN QRGTCVQT

EQPUV 9KFIGV
210                                      Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne


          ]
            TGVWTP 

VKOGU%CNNGF  
          _
        RTKXCVG
          UKGAV VKOGU%CNNGF
        _

      Przypuśćmy, e chcielibyśmy u yć tej klasy do usunięcia trzeciego elementu wektora
      XGEVQT9KIFGV :
        XGEVQT9KFIGV XY                           WVYÎT YGMVQT K WOKG è Y PKO
                                                 MKNMC GNGOGPVÎY
        XYGTCUG
TGOQXGAKH
XYDGIKP
              WUW VTGEK GNGOGPV
                           XYGPF
                Y RQFTQFKCNG CICFPKGPKG 
        PCLFKGU
                                                  KPHQTOCELG FQV[EæEG
                           $CF2TGFKECVG
       YKæMW HWPMELK GTCUG
 K TGOQXGAKH

                 XYGPF


      Powy szy kod wygląda zupełnie przyzwoicie, jednak w wielu implementacjach STL
      usunie on nie tylko trzeci, ale i szósty element wektora.

      Aby zrozumieć, jak jest to mo liwe, dobrze jest poznać częsty sposób implementacji
      funkcji TGOQXGAKH
. Nale y pamiętać, e funkcja TGOQXGAKH
 nie musi być implemen-
      towana w ten sposób:
        VGORNCVGV[RGPCOG (YF+VGTCVQT V[RGPCOG 2TGFKECVG
        (YF+VGTCVQT TGOQXGAKH
(YF+VGTCVQT DGIKP (YF+VGTCVQT GPF 2TGFKECVG R
        ]
          DGIKP  HKPFAKH
DGIKP GPF R
          KH 
DGIKP  GPF TGVWTP DGIKP
          GNUG ]
            (YF+VGTCVQT PGZV  DGIKP
            TGVWTP TGOQXGAEQR[AKH


PGZV GPF DGIKP R
          _
        _

      Szczegóły podanego kodu nie są istotne, jednak nale y zwrócić uwagę, e predykat
      R jest przekazywany najpierw do funkcji HKPFAKH
, a następnie do funkcji TGOQXGAEQ
      R[AKH
. W obu przypadkach R jest przekazywany do tych algorytmów przez wartość
      (czyli kopiowany). Teoretycznie nie musi to być prawdą, jednak w praktyce najczęściej
      jest. Więcej informacji na ten temat znajdziesz w podrozdziale „Zagadnienie 38.”.

      Początkowe wywołanie funkcji TGOQXGAKH
 (w kodzie klienta, związane z próbą usu-
      nięcia trzeciego elementu wektora XY) tworzy anonimowy obiekt klasy $CF2TGFKECVG
      zawierający wewnętrzną składową VKOGU%CNNGF inicjowaną wartością zero. Ten obiekt
      (wewnątrz funkcji TGOQXGAKH
 nazywa się R) jest kopiowany do funkcji HKPFAKH
,
      w związku z czym ona równie otrzymuje obiekt klasy $CF2TGFKECVG z wyzerowaną
      zmienną VKOGU%CNNGF. Funkcja HKPFAKH
 „wywołuje” ten obiekt tak długo, a zwróci
      wartość VTWG, czyli trzykrotnie, a następnie przekazuje sterowanie do funkcji TGOQXGA
      KH
. Funkcja TGOQXGAKH
 wznawia swoje działanie i w końcu wywołuje funkcję
      TGOQXGAEQR[AKH
, przekazując jej kolejną kopię predykatu R. Jednak wartość
      zmiennej VKOGU%CNNGF w obiekcie R ma nadal wartość zero. Funkcja HKPFAKH
 nigdy
Zagadnienie 39. Predykaty powinny być funkcjami czystymi                                   211


       nie wywoływała obiektu R, a jedynie jego kopię. W efekcie trzecie wywołanie przez
       funkcję TGOQXGAEQR[AKH
 podanego jej predykatu równie zwróci wartość VTWG. Oto
       dlaczego funkcja TGOQXGAKH
 usunie z wektora XY dwa elementy zamiast jednego.

       Najprostszym sposobem na uniknięcie tego rodzaju problemów jest deklarowanie
       w klasach-predykatach funkcji QRGTCVQT

 jako EQPUV. W tak zadeklarowanych funk-
       cjach kompilator nie pozwoli zmienić wartości składników klasy:
          ENCUU DCF2TGFKECVG
            RWDNKE WPCT[AHWPEVKQP9KFIGV DQQN ]
          RWDNKE
            DQQN QRGTCVQT

EQPUV 9KFIGV  EQPUV
            ]
              TGVWTP 

VKOGU%CNNGF          D æF Y HWPMELCEJ V[RW EQPUV
            _                                   PKG OQ PC OKGPKCè YCTVQ EK UM CFQY[EJ
             
          _

       Ze względu na to, e opisany problem mo na rozwiązać w tak prosty sposób, byłem
       bliski nazwania tego zagadnienia „W klasach-predykatach stosujmy QRGTCVQT


       typu EQPUV”. Niestety, takie rozwiązanie nie jest wystarczające. Nawet funkcje skła-
       dowe oznaczone jako EQPUV mogą u ywać zmiennych pól klasy, niestałych lokal-
       nych obiektów statycznych, niestałych statycznych obiektów klas, niestałych
       obiektów w zakresie przestrzeni nazw i niestałych obiektów globalnych. Dobrze za-
       projektowana klasa-predykat gwarantuje, e funkcje jej operatora QRGTCVQT

 są nie-
       zale ne od tego rodzaju obiektów. Zadeklarowanie QRGTCVQT

 jako EQPUV jest ko-
       nieczne dla uzyskania właściwego zachowania klasy, jednak nie jest wystarczające.
       Co prawda wystarczyłoby tak zmodyfikować składowe, eby nie wpływały na wynik
       predykatu, jednak dobrze zaprojektowany QRGTCVQT

 wymaga czegoś więcej —
       musi być funkcją czystą.

       Zaznaczyłem ju , e w miejscach, w których STL oczekuje funkcji predykatu, za-
       akceptowany zostanie równie obiekt klasy-predykatu. Ta zasada obowiązuje równie
       w przeciwnym kierunku. W miejscach, w których STL oczekuje obiektu klasy-
       predykatu, zaakceptowana zostanie równie funkcja-predykat (prawdopodobnie zmody-
       fikowana przez funkcję RVTAHWP — zobacz „Zagadnienie 41.”). Zostało ju udowod-
       nione, e funkcje QRGTCVQT

 w klasach-predykatach muszą być funkcjami czysty-
       mi, co w połączeniu z powy szymi stwierdzeniami oznacza, e funkcje-predykaty
       równie muszą być funkcjami czystymi. Poni sza funkcja nie jest a tak złym predy-
       katem jak obiekty tworzone na podstawie klasy $CF2TGFKECVG, poniewa jej zastoso-
       wanie wią e się z istnieniem tylko jednej kopii zmiennej stanu, jednak i ona narusza
       zasady tworzenia predykatów:
          DQQN CPQVJGT$CF2TGFKECVG
 EQPUV 9KFIGV    EQPUV 9KFIGV 
          ]
            UVCVKE KPV VKOGU%CNNGF              0KG 0KG 0KG 0KG 0KG 0KG 0KG 0KG
            TGVWTP 

VKOGU%CNNGF               2TGF[MCV[ RQYKPP[ D[è HWPMELCOK E[UV[OK
          _                                        C VCMKG HWPMELG PKG OCLæ UVCPW

       Niezale nie od tego, w jaki sposób tworzysz swoje predykaty, powinny one zawsze być
       funkcjami czystymi.
212                                                                                      Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne



Zagadnienie 40.
Klasy-funktory powinny być adaptowalne
Zagadnienie 40. Klasy-funktory powinny być adaptowalne




                                                         Przypuśćmy, e mamy listę wskaźników 9KFIGV
i funkcję określającą, czy dany wskaźnik
                                                         identyfikuje interesujący nas element.
                                                           NKUV9KFIGV
YKFIGV2VTU
                                                           DQQN KU+PVGTGUVKPI
EQPUV 9KFIGV
RY

                                                         Je eli chcielibyśmy znaleźć pierwszy wskaźnik na interesujący nas element, mo na by
                                                         to zrobić w prosty sposób:
                                                           NKUV9KFIGV
KVGTCVQT K  HKPFAKH
YKFIGV2VTUDGIKP
 YKFIGV2VTUGPF

                                                                                               KU+PVGTGUVKPI
                                                           KH 
K  YKFIGV2VTUGPF
 ]
                                                                                                QRGTCELG YKæCPG  RKGTYU[O
                                                           _                                       KPVGTGUWLæE[O PCU YUMC[YCP[O GNGOGPVGO

                                                         Je eli jednak będziemy chcieli uzyskać pierwszy wskaźnik na element nas nieinteresu-
                                                         jący, ten najbardziej oczywisty sposób nawet się nie skompiluje.
                                                           NKUV9KFIGV

More Related Content

Viewers also liked

Viewers also liked (7)

Optymalizacja systemu Windows
Optymalizacja systemu WindowsOptymalizacja systemu Windows
Optymalizacja systemu Windows
 
Linux. Leksykon kieszonkowy
Linux. Leksykon kieszonkowyLinux. Leksykon kieszonkowy
Linux. Leksykon kieszonkowy
 
Adobe After Effects 6.0. Oficjalny podręcznik
Adobe After Effects 6.0. Oficjalny podręcznikAdobe After Effects 6.0. Oficjalny podręcznik
Adobe After Effects 6.0. Oficjalny podręcznik
 
PC hardware. Almanach. Wydanie III
PC hardware. Almanach. Wydanie IIIPC hardware. Almanach. Wydanie III
PC hardware. Almanach. Wydanie III
 
Cisco. Receptury
Cisco. RecepturyCisco. Receptury
Cisco. Receptury
 
PowerPoint 2003 PL. Ćwiczenia
PowerPoint 2003 PL. ĆwiczeniaPowerPoint 2003 PL. Ćwiczenia
PowerPoint 2003 PL. Ćwiczenia
 
Red Hat Linux 9. Biblia
Red Hat Linux 9. BibliaRed Hat Linux 9. Biblia
Red Hat Linux 9. Biblia
 

Similar to STL w praktyce. 50 sposobów efektywnego wykorzystania

C++. Styl i technika zaawansowanego programowania
C++. Styl i technika zaawansowanego programowaniaC++. Styl i technika zaawansowanego programowania
C++. Styl i technika zaawansowanego programowaniaWydawnictwo Helion
 
Język C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktyk
Język C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktykJęzyk C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktyk
Język C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktykWydawnictwo Helion
 
Język C++. Gotowe rozwiązania dla programistów
Język C++. Gotowe rozwiązania dla programistówJęzyk C++. Gotowe rozwiązania dla programistów
Język C++. Gotowe rozwiązania dla programistówWydawnictwo Helion
 
Asembler. Sztuka programowania
Asembler. Sztuka programowaniaAsembler. Sztuka programowania
Asembler. Sztuka programowaniaWydawnictwo Helion
 
Wyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązań
Wyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązańWyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązań
Wyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązańWydawnictwo Helion
 
Oracle8. Programowanie w języku PL/SQL
Oracle8. Programowanie w języku PL/SQLOracle8. Programowanie w języku PL/SQL
Oracle8. Programowanie w języku PL/SQLWydawnictwo Helion
 
Ajax. Wzorce i najlepsze rozwiązania
Ajax. Wzorce i najlepsze rozwiązaniaAjax. Wzorce i najlepsze rozwiązania
Ajax. Wzorce i najlepsze rozwiązaniaWydawnictwo Helion
 
Programowanie w języku C. FAQ
Programowanie w języku C. FAQProgramowanie w języku C. FAQ
Programowanie w języku C. FAQWydawnictwo Helion
 
Programowanie w języku C. Szybki start
Programowanie w języku C. Szybki startProgramowanie w języku C. Szybki start
Programowanie w języku C. Szybki startWydawnictwo Helion
 
Algorytmy, struktury danych i techniki programowania. Wydanie III
Algorytmy, struktury danych i techniki programowania. Wydanie IIIAlgorytmy, struktury danych i techniki programowania. Wydanie III
Algorytmy, struktury danych i techniki programowania. Wydanie IIIWydawnictwo Helion
 
Struktura organizacyjna i architektura systemów komputerowych
Struktura organizacyjna i architektura systemów komputerowychStruktura organizacyjna i architektura systemów komputerowych
Struktura organizacyjna i architektura systemów komputerowychWydawnictwo Helion
 
C++. 50 efektywnych sposobów na udoskonalenie Twoich programów
C++. 50 efektywnych sposobów na udoskonalenie Twoich programówC++. 50 efektywnych sposobów na udoskonalenie Twoich programów
C++. 50 efektywnych sposobów na udoskonalenie Twoich programówWydawnictwo Helion
 
Oracle9i. Przewodnik dla początkujących
Oracle9i. Przewodnik dla początkującychOracle9i. Przewodnik dla początkujących
Oracle9i. Przewodnik dla początkującychWydawnictwo Helion
 

Similar to STL w praktyce. 50 sposobów efektywnego wykorzystania (20)

C++. Styl i technika zaawansowanego programowania
C++. Styl i technika zaawansowanego programowaniaC++. Styl i technika zaawansowanego programowania
C++. Styl i technika zaawansowanego programowania
 
Język C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktyk
Język C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktykJęzyk C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktyk
Język C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych praktyk
 
Język C++. Gotowe rozwiązania dla programistów
Język C++. Gotowe rozwiązania dla programistówJęzyk C++. Gotowe rozwiązania dla programistów
Język C++. Gotowe rozwiązania dla programistów
 
Asembler. Sztuka programowania
Asembler. Sztuka programowaniaAsembler. Sztuka programowania
Asembler. Sztuka programowania
 
SQL. Szybki start
SQL. Szybki startSQL. Szybki start
SQL. Szybki start
 
Wyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązań
Wyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązańWyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązań
Wyjątkowy język C++. 40 nowych łamigłówek, zadań programistycznych i rozwiązań
 
Uczta programistów
Uczta programistówUczta programistów
Uczta programistów
 
Oracle8. Programowanie w języku PL/SQL
Oracle8. Programowanie w języku PL/SQLOracle8. Programowanie w języku PL/SQL
Oracle8. Programowanie w języku PL/SQL
 
Tcl-Tk. Programowanie
Tcl-Tk. ProgramowanieTcl-Tk. Programowanie
Tcl-Tk. Programowanie
 
Praktyczny kurs SQL
Praktyczny kurs SQLPraktyczny kurs SQL
Praktyczny kurs SQL
 
Ajax. Wzorce i najlepsze rozwiązania
Ajax. Wzorce i najlepsze rozwiązaniaAjax. Wzorce i najlepsze rozwiązania
Ajax. Wzorce i najlepsze rozwiązania
 
Programowanie w języku C. FAQ
Programowanie w języku C. FAQProgramowanie w języku C. FAQ
Programowanie w języku C. FAQ
 
Programowanie w języku C. Szybki start
Programowanie w języku C. Szybki startProgramowanie w języku C. Szybki start
Programowanie w języku C. Szybki start
 
Algorytmy, struktury danych i techniki programowania. Wydanie III
Algorytmy, struktury danych i techniki programowania. Wydanie IIIAlgorytmy, struktury danych i techniki programowania. Wydanie III
Algorytmy, struktury danych i techniki programowania. Wydanie III
 
Algorytmy. Od podstaw
Algorytmy. Od podstawAlgorytmy. Od podstaw
Algorytmy. Od podstaw
 
Struktura organizacyjna i architektura systemów komputerowych
Struktura organizacyjna i architektura systemów komputerowychStruktura organizacyjna i architektura systemów komputerowych
Struktura organizacyjna i architektura systemów komputerowych
 
Algorytmy w C
Algorytmy w CAlgorytmy w C
Algorytmy w C
 
SQL w mgnieniu oka
SQL w mgnieniu okaSQL w mgnieniu oka
SQL w mgnieniu oka
 
C++. 50 efektywnych sposobów na udoskonalenie Twoich programów
C++. 50 efektywnych sposobów na udoskonalenie Twoich programówC++. 50 efektywnych sposobów na udoskonalenie Twoich programów
C++. 50 efektywnych sposobów na udoskonalenie Twoich programów
 
Oracle9i. Przewodnik dla początkujących
Oracle9i. Przewodnik dla początkującychOracle9i. Przewodnik dla początkujących
Oracle9i. Przewodnik dla początkujących
 

More from Wydawnictwo Helion

Tworzenie filmów w Windows XP. Projekty
Tworzenie filmów w Windows XP. ProjektyTworzenie filmów w Windows XP. Projekty
Tworzenie filmów w Windows XP. ProjektyWydawnictwo Helion
 
Blog, więcej niż internetowy pamiętnik
Blog, więcej niż internetowy pamiętnikBlog, więcej niż internetowy pamiętnik
Blog, więcej niż internetowy pamiętnikWydawnictwo Helion
 
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktycznePozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczneWydawnictwo Helion
 
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesieE-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesieWydawnictwo Helion
 
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Microsoft Visual C++ 2008. Tworzenie aplikacji dla WindowsMicrosoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Microsoft Visual C++ 2008. Tworzenie aplikacji dla WindowsWydawnictwo Helion
 
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie IICo potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie IIWydawnictwo Helion
 
Makrofotografia. Magia szczegółu
Makrofotografia. Magia szczegółuMakrofotografia. Magia szczegółu
Makrofotografia. Magia szczegółuWydawnictwo Helion
 
Ajax, JavaScript i PHP. Intensywny trening
Ajax, JavaScript i PHP. Intensywny treningAjax, JavaScript i PHP. Intensywny trening
Ajax, JavaScript i PHP. Intensywny treningWydawnictwo Helion
 
PowerPoint 2007 PL. Seria praktyk
PowerPoint 2007 PL. Seria praktykPowerPoint 2007 PL. Seria praktyk
PowerPoint 2007 PL. Seria praktykWydawnictwo Helion
 
Serwisy społecznościowe. Budowa, administracja i moderacja
Serwisy społecznościowe. Budowa, administracja i moderacjaSerwisy społecznościowe. Budowa, administracja i moderacja
Serwisy społecznościowe. Budowa, administracja i moderacjaWydawnictwo Helion
 
Serwer SQL 2008. Administracja i programowanie
Serwer SQL 2008. Administracja i programowanieSerwer SQL 2008. Administracja i programowanie
Serwer SQL 2008. Administracja i programowanieWydawnictwo Helion
 

More from Wydawnictwo Helion (20)

Tworzenie filmów w Windows XP. Projekty
Tworzenie filmów w Windows XP. ProjektyTworzenie filmów w Windows XP. Projekty
Tworzenie filmów w Windows XP. Projekty
 
Blog, więcej niż internetowy pamiętnik
Blog, więcej niż internetowy pamiętnikBlog, więcej niż internetowy pamiętnik
Blog, więcej niż internetowy pamiętnik
 
Access w biurze i nie tylko
Access w biurze i nie tylkoAccess w biurze i nie tylko
Access w biurze i nie tylko
 
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktycznePozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
Pozycjonowanie i optymalizacja stron WWW. Ćwiczenia praktyczne
 
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesieE-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
E-wizerunek. Internet jako narzędzie kreowania image&#39;u w biznesie
 
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Microsoft Visual C++ 2008. Tworzenie aplikacji dla WindowsMicrosoft Visual C++ 2008. Tworzenie aplikacji dla Windows
Microsoft Visual C++ 2008. Tworzenie aplikacji dla Windows
 
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie IICo potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
Co potrafi Twój iPhone? Podręcznik użytkownika. Wydanie II
 
Makrofotografia. Magia szczegółu
Makrofotografia. Magia szczegółuMakrofotografia. Magia szczegółu
Makrofotografia. Magia szczegółu
 
Windows PowerShell. Podstawy
Windows PowerShell. PodstawyWindows PowerShell. Podstawy
Windows PowerShell. Podstawy
 
JavaScript. Pierwsze starcie
JavaScript. Pierwsze starcieJavaScript. Pierwsze starcie
JavaScript. Pierwsze starcie
 
Ajax, JavaScript i PHP. Intensywny trening
Ajax, JavaScript i PHP. Intensywny treningAjax, JavaScript i PHP. Intensywny trening
Ajax, JavaScript i PHP. Intensywny trening
 
PowerPoint 2007 PL. Seria praktyk
PowerPoint 2007 PL. Seria praktykPowerPoint 2007 PL. Seria praktyk
PowerPoint 2007 PL. Seria praktyk
 
Excel 2007 PL. Seria praktyk
Excel 2007 PL. Seria praktykExcel 2007 PL. Seria praktyk
Excel 2007 PL. Seria praktyk
 
Access 2007 PL. Seria praktyk
Access 2007 PL. Seria praktykAccess 2007 PL. Seria praktyk
Access 2007 PL. Seria praktyk
 
Word 2007 PL. Seria praktyk
Word 2007 PL. Seria praktykWord 2007 PL. Seria praktyk
Word 2007 PL. Seria praktyk
 
Serwisy społecznościowe. Budowa, administracja i moderacja
Serwisy społecznościowe. Budowa, administracja i moderacjaSerwisy społecznościowe. Budowa, administracja i moderacja
Serwisy społecznościowe. Budowa, administracja i moderacja
 
AutoCAD 2008 i 2008 PL
AutoCAD 2008 i 2008 PLAutoCAD 2008 i 2008 PL
AutoCAD 2008 i 2008 PL
 
Bazy danych. Pierwsze starcie
Bazy danych. Pierwsze starcieBazy danych. Pierwsze starcie
Bazy danych. Pierwsze starcie
 
Inventor. Pierwsze kroki
Inventor. Pierwsze krokiInventor. Pierwsze kroki
Inventor. Pierwsze kroki
 
Serwer SQL 2008. Administracja i programowanie
Serwer SQL 2008. Administracja i programowanieSerwer SQL 2008. Administracja i programowanie
Serwer SQL 2008. Administracja i programowanie
 

STL w praktyce. 50 sposobów efektywnego wykorzystania

  • 1. IDZ DO PRZYK£ADOWY ROZDZIA£ SPIS TRE CI STL w praktyce. 50 sposobów efektywnego wykorzystania KATALOG KSI¥¯EK Autor: Scott Meyers T³umaczenie: Adam Majczak (rozdz. 1-5), KATALOG ONLINE Wojciech Moch (rozdz. 6, 7, dod. A-C) ISBN: 83-7361-373-0 ZAMÓW DRUKOWANY KATALOG Tytu³ orygina³u: Effective STL 50 Specific Ways to Improve Your Use of the Standard Template Library Format: B5, stron: 282 TWÓJ KOSZYK Standard Template Library to jedno z najpotê¿niejszych narzêdzi programistycznych DODAJ DO KOSZYKA charakteryzuj¹ce siê z³o¿ono ci¹ i wysokim stopniem komplikacji. W ksi¹¿ce „STL w praktyce. 50 sposobów efektywnego wykorzystania” — przeznaczonej dla zaawansowanych i rednio zaawansowanych programistów C++ — znany ekspert Scott CENNIK I INFORMACJE Meyers prezentuje najwa¿niejsze techniki stosowania tego narzêdzia. Poza list¹ zaleceñ, jak postêpowaæ nale¿y, a jak postêpowaæ siê nie powinno, Meyers przedstawia wiele ZAMÓW INFORMACJE powa¿niejszych aspektów STL pozwalaj¹cych zrozumieæ, dlaczego pewne rozwi¹zania O NOWO CIACH s¹ poprawne, a innych nale¿y unikaæ. Do ka¿dej wskazówki do³¹czony jest kod ród³owy i dok³adne obja nienia, które powinny zainteresowaæ zaawansowanych ZAMÓW CENNIK programistów. Je¿eli Twoja wiedza ogranicza siê do informacji dostêpnych w dokumentacji STL, ta ksi¹¿ka uzupe³ni j¹ o bezcenne wskazówki, które pozwol¹ Ci wykorzystaæ STL CZYTELNIA w praktyce. Ksi¹¿ka przedstawia: FRAGMENTY KSI¥¯EK ONLINE • Podstawowe informacje o bibliotece STL i jej zgodno ci z innymi standardami • Wskazówki dotycz¹ce poprawnego doboru i u¿ywania pojemników • Opis pojemników typu vector i string • Omówienie pojemników asocjatywnych • Metody w³a ciwego korzystania z iteratorów • Algorytmy wchodz¹ce w sk³ad STL • Funktory, klasy-funktory i funkcje • Programowanie za pomoc¹ biblioteki STL Ksi¹¿kê uzupe³niaj¹ dodatki, w których znajdziesz miêdzy innymi obszern¹ bibliografiê na temat STL oraz cenne wskazówki dla programistów u¿ywaj¹cych kompilatorów firmy Microsoft. Wydawnictwo Helion ul. Chopina 6 „STL w praktyce. 50 sposobów efektywnego wykorzystania” to nieocenione ród³o 44-100 Gliwice wiedzy na temat jednego z najwa¿niejszych aspektów programowania w C++. tel. (32)230-98-63 Je¿eli chcesz w praktyce wykorzystaæ STL, nie obêdziesz siê bez informacji e-mail: helion@helion.pl zawartych w tej ksi¹¿ce.
  • 2. Spis treści Podziękowania................................................................................... 9 Przedmowa...................................................................................... 13 Wprowadzenie ................................................................................. 17 Rozdział 1. Kontenery........................................................................................ 29 Zagadnienie 1. Uwa nie dobierajmy kontenery................................................................30 Zagadnienie 2. Nie dajmy się zwieść iluzji o istnieniu kodów niezale nych do zastosowanego kontenera........................................................................................35 Zagadnienie 3. Kopiowanie obiektów w kontenerach powinno być „tanie”, łatwe i poprawne ..........................................................................................................40 Zagadnienie 4. Stosujmy metodę empty(), zamiast przyrównywać rozmiar size() do zera...................................................................................................43 Zagadnienie 5. Preferujmy metody operujące na całych zakresach (podzbiorach), bo są bardziej efektywne ni ich odpowiedniki operujące pojedynczymi elementami...45 Zagadnienie 6. Bądźmy wyczuleni i przygotowani na najbardziej kłopotliwą interpretację kompilatora C++ ...................................................................56 Zagadnienie 7. Gdy stosujemy kontenery zawierające wskaźniki zainicjowane za pomocą operatora new, pamiętajmy o zwolnieniu dynamicznej pamięci operatorem delete, zanim zawierający je obiekt-kontener sam zostanie usunięty.......59 Zagadnienie 8. Nigdy nie twórzmy kontenerów zawierających wskaźniki kategorii auto_ptr .........................................................................................................64 Zagadnienie 9. Uwa nie dobierajmy opcje do operacji kasowania ..................................67 Zagadnienie 10. Uwa ajmy na konwencje dynamicznej alokacji pamięci i stosowne ograniczenia ...............................................................................................73 Zagadnienie 11. Zrozumienie uprawnionych zastosowań alokatorów pamięci tworzonych przez u ytkownika....................................................................................80 Zagadnienie 12. Miejmy umiarkowane i realistyczne oczekiwania odnośnie poziomu bezpieczeństwa wielu wątków obsługiwanych przez kontenery STL ..........84
  • 3. 6 Spis treści Rozdział 2. Kontenery typu vector oraz string..................................................... 89 Zagadnienie 13. Lepiej stosować kontenery vector oraz string ni dynamicznie przydzielać pamięć tablicom........................................................................................90 Zagadnienie 14. Stosujmy metodę reserve, by uniknąć zbędnego przemieszczania elementów w pamięci...................................................................................................93 Zagadnienie 15. Bądźmy ostro ni i wyczuleni na zró nicowanie implementacji kontenerów typu string.................................................................................................96 Zagadnienie 16. Powinieneś wiedzieć, jak przesyłać dane z kontenerów vector i string do klasycznego interfejsu zgodnego z C........................................................102 Zagadnienie 17. Sztuczka programistyczna „swap trick” pozwalająca na obcięcie nadmiarowej pojemności ........................................................................106 Zagadnienie 18. Unikajmy stosowania wektora typu vector<bool>...............................108 Rozdział 3. Kontenery asocjacyjne ................................................................... 111 Zagadnienie 19. Ten sam czy taki sam — zrozumienie ró nicy pomiędzy relacjami równości a równowa ności ........................................................................112 Zagadnienie 20. Określajmy typy porównawcze dla kontenerów asocjacyjnych zawierających wskaźniki............................................................................................117 Zagadnienie 21. Funkcje porównujące powinny dla dwóch dokładnie równych wartości zawsze zwracać wartość false......................................................................122 Zagadnienie 22. Unikajmy bezpośredniego modyfikowania klucza w kontenerach typu set i multiset .......................................................................................................126 Zagadnienie 23. Rozwa my zastąpienie kontenerów asocjacyjnych posortowanymi wektorami.........................................................................................133 Zagadnienie 24. Gdy efektywność działania jest szczególnie istotna, nale y bardzo uwa nie dokonywać wyboru pomiędzy map::operator[]() a map::insert()................140 Zagadnienie 25. Zapoznaj się z niestandardowymi kontenerami mieszanymi ...............146 Rozdział 4. Iteratory ........................................................................................ 151 Zagadnienie 26. Lepiej wybrać iterator ni const_iterator, reverse_iterator czy const_reverse_iterator..........................................................................................151 Zagadnienie 27. Stosujmy funkcje distance() i advance(), by przekształcić typ const_iterator na typ iterator ................................................................................155 Zagadnienie 28. Jak stosować metodę base() nale ącą do klasy reverse_iterator w celu uzyskania typu iterator?..................................................................................159 Zagadnienie 29. Rozwa zastosowanie iteratorów typu istreambuf_iterator do wczytywania danych znak po znaku .....................................................................163 Rozdział 5. Algorytmy...................................................................................... 165 Zagadnienie 30. Upewnijmy się, e docelowe zakresy są wystarczająco obszerne .......166 Zagadnienie 31. Pamiętajmy o dostępnych i stosowanych opcjach sortowania .............171 Zagadnienie 32. Stosujmy metodę erase() w ślad za algorytmami kategorii remove(), jeśli naprawdę chcemy coś skutecznie usunąć z kontenera .......................................177 Zagadnienie 33. Przezornie i ostro nie stosujmy algorytmy kategorii remove() wobec kontenerów zawierających wskaźniki ............................................................182 Zagadnienie 34. Zwracajmy uwagę, które z algorytmów oczekują posortowanych zakresów ...........................................................................................186
  • 4. Spis treści 7 Zagadnienie 35. Implementujmy zwykłe porównywanie łańcuchów znaków bez rozró niania wielkości liter za pomocą mismatch() lub lexicographical_compare() ....190 Zagadnienie 36. Zrozum prawidłową implementację algorytmu copy_if()....................196 Zagadnienie 37. Stosujmy accumulate() lub for_each() do operacji grupowych na zakresach ...............................................................................................................198 Rozdział 6. Funktory, klasy-funktory, funkcje i inne........................................... 205 Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość ........205 Zagadnienie 39. Predykaty powinny być funkcjami czystymi .......................................208 Zagadnienie 40. Klasy-funktory powinny być adaptowalne...........................................212 Zagadnienie 41. Po co stosować funkcje ptr_fun, mem_fun i mem_fun_ref?................215 Zagadnienie 42. Upewnij się, e less<t>() oznacza operator<().....................................219 Rozdział 7. Programowanie za pomocą biblioteki STL ....................................... 223 Zagadnienie 43. U ywaj algorytmów, zamiast pisać pętle .............................................223 Zagadnienie 44. Zamiast algorytmów stosujmy metody o takich samych nazwach ......231 Zagadnienie 45. Rozró nianie funkcji count(), find(), binary_search(), lower_bound(), upper_bound() i equal_range() .........................................................233 Zagadnienie 46. Jako parametry algorytmów stosuj funktory, a nie funkcje .................241 Zagadnienie 47. Unikaj tworzenia kodu „tylko do zapisu” ............................................245 Zagadnienie 48. Zawsze dołączaj właściwe pliki nagłówkowe......................................248 Zagadnienie 49. Naucz się odczytywać komunikaty kompilatora związane z biblioteką STL .........................................................................................................249 Zagadnienie 50. Poznaj strony WWW związane z biblioteką STL ................................256 Dodatek A Bibliografia .................................................................................... 263 Ksią ki napisane przeze mnie .........................................................................................263 Ksią ki, które nie ja napisałem (choć chciałbym)...........................................................264 Dodatek B Porównywanie ciągów znaków bez uwzględniania wielkości liter ..... 267 Jak wykonywać porównywanie ciągów znaków bez uwzględniania wielkości liter — artykuł autorstwa Matta Austerna .........................................................................267 Dodatek C Uwagi na temat platformy STL Microsoftu ...................................... 277 Szablony metod w STL ...................................................................................................277 MSVC wersje 4 do 6 .......................................................................................................278 Rozwiązania dla kompilatorów MSVC w wersji 4 do 5.................................................279 Dodatkowe rozwiązanie dla kompilatora MSVC w wersji 6..........................................280 Skorowidz...................................................................................... 283
  • 5. Rozdział 6. Funktory, klasy-funktory, funkcje i inne Czy nam się to podoba czy nie, funkcje i podobne do funkcji obiekty — funktory — są częścią STL. Kontenery asocjacyjne stosują je do porządkowania swoich elemen- tów, w algorytmach typu HKPFAKH wykorzystywane są do kontroli zachowań tych al- gorytmów. Algorytmy takie jak HQTAGCEJ i VTCPUHQTO są bez funktorów zupełnie nieprzydatne, natomiast adaptory typu PQV i DKPFPF słu ą do tworzenia funktorów. Wszędzie gdzie spojrzeć, mo na w STL znaleźć funktory i klasy-funktory. Znajdą się one równie w Twoich kodach źródłowych. Efektywne zastosowanie STL bez umiejęt- ności tworzenia dobrych funktorów jest po prostu niemo liwe. Z tego powodu większa część tego rozdziału będzie opisywać sposoby takiego tworzenia funktorów, aby zacho- wywały się one zgodnie z wymaganiami STL. Jednak jedno z zagadnień opisuje zupeł- nie inny temat. Ten podrozdział spodoba się osobom, które zastanawiały się, dlaczego konieczne jest zaśmiecanie kodu programu wywołaniami funkcji RVTAHWP , OGOAHWP i OGOAHWPATGH . Oczywiście mo na zacząć lekturę od tego właśnie podrozdziału, „Za- gadnienie 41.”, ale proszę nie poprzestawać na nim. Po poznaniu przedstawionych tam funkcji konieczne będzie zapoznanie się z informacjami z pozostałych, dzięki czemu Twoje funkcje będą prawidłowo działały zarówno z tymi funkcjami, jak i z resztą STL. Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość Języki C i C++ nie pozwalają na przekazywanie funkcji w parametrach innych funkcji. Konieczne jest przekazywanie wskaźników na te funkcje. Na przykład poni ej znajduje się deklaracja funkcji SUQTV z biblioteki standardowej.
  • 6. 206 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne XQKF SUQTV XQKF
  • 7. DCUG UKGAV POGOD UKGAV UKG KPV
  • 10. „Zagadnienie 46.” wyjaśnia, dlaczego algorytm UQTV jest zazwyczaj lepszym wy- borem od funkcji SUQTV . Teraz omówimy sposób deklarowania parametru EORHEP w funkcji SUQTV . Po dokładniejszym przyjrzeniu się wszystkim gwiazdkom okazuje się, e argument EORHEP, który jest wskaźnikiem na funkcję, jest kopiowany (czyli prze- kazywany przez wartość) z miejsca wywołania do funkcji SUQTV . Jest to typowy przy- kład zasady stosowanej w bibliotekach standardowych języków C i C++, czyli prze- kazywania przez wartość wskaźników na funkcje. W STL obiekty funkcyjne modelowane są podobnie do wskaźników na funkcje, wobec czego w STL obiekty funkcyjne są równie przekazywane do i z funkcji przez war- tość. Najlepszym przykładem mo e być deklaracja funkcji HQTAGCEJ algorytmu po- bierającego i zwracającego przez wartość funktory. VGORNCVGENCUU +PRWV+VGTCVQT ENCUU (WPEVKQP (WPEVKQP YTCECPC RTG YCTVQ è HQTAGCEJ +PRWV+VGTCVQT HKTUV +PRWV+VGTCVQT NCUV (WPEVKQP H RTGMC[YCPC RTG YCTVQ è Tak na prawdę przekazywanie przez wartość nie jest absolutnie wymagane, ponie- wa w wywołaniu funkcji HQTAGCEJ mo na wyraźnie zaznaczyć typy parametrów. Na przykład w poni szym kodzie funkcja HQTAGCEJ pobiera i zwraca funktory przez referencję. ENCUU Q5QOGVJKPI RWDNKE WPCT[AHWPEVKQPKPV XQKF ] Y RQFTQFKCNG CICFPKGPKG QRKUCPC QUVCPKG MNCUC DCQYC XQKF QRGTCVQT KPV Z ] _ _ V[RGFGH FGSWGKPV KVGTCVQT GSWG+PV+VGT Y[IQFPC FGMNCTCELC V[RGFGH FGSWGKPV FK Q5QOGVJKPI F WVYQTGPKG HWPMVQTC HQTAGCEJGSWG+PV+VGT Y[YQ CPKG HWPMELK HQTAGCEJ Q5QOGVJKPI FKDGIKP RCTCOGVTCOK V[RW GSWG+PV+VGT FKGPF K Q5QOGVJKPI F Y VGP URQUÎD F OWUK D[è RTGMCCPC K YTÎEQPC RTG TGHGTGPELú U ytkownicy STL prawie nigdy nie przeprowadzają tego typu operacji, co więcej, pewne implementacje niektórych algorytmów STL nie skompilują się w przypadku przekazywania funktorów przez referencję. W pozostałej części tego sposobu będę za- kładał, e funktory mo na przekazywać wyłącznie przez wartość, co w praktyce jest niemal zawsze prawdą. Funktory muszą być przekazywane przez wartość, dlatego to na Tobie spoczywa cię ar sprawdzenia, czy Twoje funktory przekazywane w ten sposób zachowują się prawidło- wo. Implikuje to dwie rzeczy. Po pierwsze, muszą one być małe, bo kopiowanie du ych
  • 11. Zagadnienie 38. Projektowanie klas-funktorów do przekazywania przez wartość 207 obiektów jest bardzo kosztowne. Po drugie, Twoje funktory nie mogą być polimor- ficzne (muszą być monomorficzne), co oznacza, e nie mogą u ywać funkcji wirtual- nych. Wynika to z faktu, e obiekt klasy pochodnej przekazywany w parametrze typu klasy bazowej zostaje obcięty, czyli w czasie kopiowania usunięte będą z niego odziedziczone elementy. W podrozdziale „Zagadnienie 3.” opisano inny problem, jaki rozczłonkowanie (ang. slicing) tworzy w czasie stosowania biblioteki STL. Unikanie rozczłonkowania obiektów i uzyskiwanie dobrej wydajności są oczywiście bardzo wa ne, ale trzeba pamiętać, e nie wszystkie funkcje mogą być małe i nie wszy- stkie obiekty mogą być monomorficzne. Jedną z zalet funktorów w porównaniu ze zwykłymi funkcjami jest fakt, e mogą one przechowywać informację o swoim stanie. Niektóre funktory muszą być du e, dlatego tak wa na jest mo liwość przekazywania takich obiektów do algorytmów STL z równą łatwością jak obiektów niewielkich. Zakaz u ywania funktorów polimorficznych jest równie nierealistyczny. Język C++ pozwala na tworzenie hierarchii dziedziczenia i dynamicznego wiązania. Te mo li- wości są tak samo u yteczne w czasie tworzenia klas-funktorów, jak i w innych miej- scach. Klasy-funktory bez dziedziczenia byłyby jak C++ bez „++”. Oczywiście ist- nieje sposób umo liwiający tworzenie du ych i polimorficznych obiektów funkcyjnych, a jednocześnie umo liwiający przekazywanie ich przez wartość, zgodnie z konwencją przyjętą w STL. Ten sposób wymaga przeniesienia wszystkich danych i funkcji polimorficznych do in- nej klasy, a następnie w klasie-funktorze umieszczenie wskaźnika na tę nową klasę. Na przykład utworzenie klasy polimorficznej zawierającej du e ilości danych: VGORNCVGV[RGPCOG 6 ENCUU $2(% $2(% $KI 2QN[OQTRJKE RWDNKE (WPEVQT %NCUU 9KGNMC 2QNKHQTOKEPC -NCUC(WPMVQT WPCT[AHWPEVKQP6XQKF ] 6C MNCUC DCQYC QUVCPKG QDLC PKQPC Y RQFTQFKCNG CICFPKGPKG RTKXCVG 9KFIGV Y 6C MNCUC CYKGTC YKGNG FCP[EJ KPV Z FNCVGIQ RTGMC[YCPKG KEJ RTG YCTVQ è D[ QD[ PKGGHGMV[YPG RWDNKE XKTVWCN XQKF QRGTCVQT EQPUV 6 XCN EQPUV VQ LGUV HWPMELC YKTVWCNPC FNCVGIQ TQE QPMQYCPKG NG PC PKæ YR [PKG _ wymaga utworzenia małej monomorficznej klasy zawierającej wskaźnik na klasę implementacji i umieszczenie w klasie implementacji wszystkich danych i funkcji wirtualnych: VGORNCVGV[RGPCOG 6 MNCUC KORNGOGPVCELK FNC ENCUU $2(%+ORN RWDNKE WPCT[AHWPEVKQP6 XQKF ] OQF[HKMQYCPGL MNCU[ $2(% RTKXCVG 9KFIGV Y RTGPKGUKQPQ VWVCL YU[UVMKG FCPG KPV Z CYCTVG FQ VGL RQT[ Y MNCUKG $2(%
  • 12. 208 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne XKTVWCN `$2(%+ORN MNCU[ RQNKOQTHKEPG OWUæ OKGè YKTVWCNPG FGUVTWMVQT[ XKTVWCN XQKF QRGTCVQT EQPUV 6 XCN EQPUV HTKGPF ENCUU $2(%6 MNCUC $2(% DúFKG OKC C FQUVúR FQ FCP[EJ _ VGORNCVGV[RGPCOG 6 ENCUU $2(% PKGYKGNMC OQPQOQTHKEPC YGTULC RWDNKE WPCT[AHWPEVKQP6 XQKF ] MNCU[ $2(% RTKXCVG $2(%+ORN6
  • 13. R+ORN VQ Uæ YU[UVMKG FCPG RTGEJQY[YCPG RTG MNCUú $2(% RWDNKE XQKF QRGTCVQT EQPUV 6 XCNEQPUV VC PKGYKTVWCNPC OGVQFC RTGMCWLG ] Y[YQ CPKG FQ MNCU[ $2(%+ORN R+ORN QRGTCVQT XCN _ _ Funkcja QRGTCVQT w klasie $2(% przedstawia sposób implementacji wszystkich niemal wirtualnych funkcji w tej klasie. Wywołują one swoje wirtualne odpowiedniki z klasy $2(%+ORN. W efekcie otrzymujemy małą i monomorficzną klasę $2(%, która ma dostęp do du ej ilości danych i zachowuje się jak klasa polimorficzna. Pomijam tutaj pewne szczegóły, poniewa podstawa naszkicowanej tu techniki jest do- brze znana programistom C++. Opisywana jest ona w ksią ce Effective C++ w roz- dziale 34. W ksią ce Design Patterns [6] nazywana jest Bridge Pattern, natomiast Sut- ter w swojej ksią ce Exceptional C++ [8] nazywa ją Idiomem Pimpl. Z punktu widzenia STL podstawową rzeczą, o której nale y pamiętać, jest fakt, e klasy stosujące tę technikę muszą rozsądnie obsługiwać kopiowanie. Autor opisywanej wy ej klasy $2(% musiałby odpowiednio zaprojektować jej konstruktor kopiujący, tak aby pra- widłowo obsługiwał wskazywany przez klasę obiekt $2(%+ORN. Najprawdopodobniej najprostszym sposobem będzie zliczanie referencji do niego, podobnie jak w przypadku szablonu UJCTGFARVT, o którym mo na przeczytać w podrozdziale „Zagadnienie 50.”. Tak naprawdę, na potrzeby tego podrozdziału nale y jedynie zadbać o właściwe zacho- wanie konstruktora kopiującego. W końcu funktory są zawsze kopiowane (czyli prze- kazywane przez wartość) w momencie przekazywania ich do i z funkcji, a to oznacza dwie rzeczy: muszą być małe i monomorficzne. Zagadnienie 39. Predykaty powinny być funkcjami czystymi Zagadnienie 39. Predykaty powinny być funkcjami czystymi Obawiam się, e będziemy musieli zacząć od zdefiniowania pewnych pojęć.
  • 14. Zagadnienie 39. Predykaty powinny być funkcjami czystymi 209 Predykat to funkcja zwracająca wartość typu DQQN (lub inną dającą się łatwo przeło yć na DQQN). Predykaty są funkcjami często wykorzystywanymi w STL. Są to, na przykład, funkcje porównujące w standardowych kontenerach asocjacyjnych; wykorzystują je równie (pobierają jako parametry) ró nego rodzaju algorytmy sortujące, a tak e algorytmy typu HKPFAKH . Opis algorytmów sortujących znajduje się w podrozdziale „Zagadnienie 31.”. Funkcje czyste to funkcje, których wartość zwracana zale y wyłącznie od wartości jej parametrów. Je eli H jest funkcją czystą, a Z i [ są obiektami, to wartość zwracana przez tę funkcję mo e zmienić się wyłącznie w przypadku zmiany w obiekcie Z lub [. W języku C++ dane, których u ywa funkcja czysta, są albo przekazywane jako parametry albo pozostają niezmienne w czasie ycia funkcji (oznacza to, e muszą być zadeklarowane jako EQPUV). Je eli funkcja czysta wykorzystywałaby dane zmieniające się między poszczególnymi jej wywołaniami, wtedy kolejne wywołania z tymi samymi parametrami mogłyby zwracać ró ne wartości, a to byłoby niezgodne z definicją funkcji czystej. Powinno to wystarczyć do wyjaśnienia dlaczego predykaty powinny być funkcjami czystymi. Teraz muszę jedynie Cię przekonać, e ta rada ma solidne podstawy. W zwią- zku z tym będę musiał wyjaśnić jeszcze jedno pojęcie. Klasa-predykat jest klasą-funktorem, w której funkcja QRGTCVQT jest predykatem, co znaczy, e zwraca wartości VTWG lub HCNUG albo wartość, którą mo na bezpośrednio przekształcić na wartość logiczną. Jak mo na się domyślić, w miejscach, w których STL oczekuje podania predykatu, mo na podać albo rzeczywisty predykat albo obiekt-predykat. To wszystko. Teraz mogę zacząć udowadniać, e naprawdę warto przestrzegać porad przedstawionych w tym podrozdziale. W podrozdziale „Zagadnienie 38.” wyjaśniłem, e funktory przekazywane są przez wa- rtość, dlatego nale y tak je budować, aby mo na je było łatwo kopiować. W przypadku funktorów będących predykatami istnieje jeszcze jeden powód takiego projektowania. Algorytmy mogą pobierać kopie funktorów i przechowywać je przez pewien czas, za- nim ich u yją. Jak mo na się spodziewać, implementacje niektórych algorytmów oczy- wiście korzystają z tej mo liwości. Efektem takiego stanu rzeczy jest fakt, e funkcje predykatów muszą być funkcjami czystymi. Aby móc udowodnić te twierdzenia, załó my, e budując klasę nie zastosujemy się do nich. Przyjrzyjmy się poni szej (źle zaprojektowanej) klasie-predykatowi. Niezale nie od przekazywanych jej parametrów zwraca ona wartość VTWG tylko raz — przy trze- cim wywołaniu. W pozostałych przypadkach zwraca wartość HCNUG. ENCUU $CF2TGFKECVG Y RQFTQFKCNG CICFPKGPKG PCLFKGU RWDNKE WPCT[AHWPEVKQP9KFIGV DQQN ] KPHQTOCELG PC VGOCV MNCU[ DCQYGL RWDNKE $CF2TGFKECVG VKOGU%CNNGF ]_ KPKELCNKCELC OKGPPGL VKOGU%CNNGF DQQN QRGTCVQT EQPUV 9KFIGV
  • 15. 210 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne ] TGVWTP VKOGU%CNNGF _ RTKXCVG UKGAV VKOGU%CNNGF _ Przypuśćmy, e chcielibyśmy u yć tej klasy do usunięcia trzeciego elementu wektora XGEVQT9KIFGV : XGEVQT9KFIGV XY WVYÎT YGMVQT K WOKG è Y PKO MKNMC GNGOGPVÎY XYGTCUG TGOQXGAKH XYDGIKP WUW VTGEK GNGOGPV XYGPF Y RQFTQFKCNG CICFPKGPKG PCLFKGU KPHQTOCELG FQV[EæEG $CF2TGFKECVG YKæMW HWPMELK GTCUG K TGOQXGAKH XYGPF Powy szy kod wygląda zupełnie przyzwoicie, jednak w wielu implementacjach STL usunie on nie tylko trzeci, ale i szósty element wektora. Aby zrozumieć, jak jest to mo liwe, dobrze jest poznać częsty sposób implementacji funkcji TGOQXGAKH . Nale y pamiętać, e funkcja TGOQXGAKH nie musi być implemen- towana w ten sposób: VGORNCVGV[RGPCOG (YF+VGTCVQT V[RGPCOG 2TGFKECVG (YF+VGTCVQT TGOQXGAKH (YF+VGTCVQT DGIKP (YF+VGTCVQT GPF 2TGFKECVG R ] DGIKP HKPFAKH DGIKP GPF R KH DGIKP GPF TGVWTP DGIKP GNUG ] (YF+VGTCVQT PGZV DGIKP TGVWTP TGOQXGAEQR[AKH PGZV GPF DGIKP R _ _ Szczegóły podanego kodu nie są istotne, jednak nale y zwrócić uwagę, e predykat R jest przekazywany najpierw do funkcji HKPFAKH , a następnie do funkcji TGOQXGAEQ R[AKH . W obu przypadkach R jest przekazywany do tych algorytmów przez wartość (czyli kopiowany). Teoretycznie nie musi to być prawdą, jednak w praktyce najczęściej jest. Więcej informacji na ten temat znajdziesz w podrozdziale „Zagadnienie 38.”. Początkowe wywołanie funkcji TGOQXGAKH (w kodzie klienta, związane z próbą usu- nięcia trzeciego elementu wektora XY) tworzy anonimowy obiekt klasy $CF2TGFKECVG zawierający wewnętrzną składową VKOGU%CNNGF inicjowaną wartością zero. Ten obiekt (wewnątrz funkcji TGOQXGAKH nazywa się R) jest kopiowany do funkcji HKPFAKH , w związku z czym ona równie otrzymuje obiekt klasy $CF2TGFKECVG z wyzerowaną zmienną VKOGU%CNNGF. Funkcja HKPFAKH „wywołuje” ten obiekt tak długo, a zwróci wartość VTWG, czyli trzykrotnie, a następnie przekazuje sterowanie do funkcji TGOQXGA KH . Funkcja TGOQXGAKH wznawia swoje działanie i w końcu wywołuje funkcję TGOQXGAEQR[AKH , przekazując jej kolejną kopię predykatu R. Jednak wartość zmiennej VKOGU%CNNGF w obiekcie R ma nadal wartość zero. Funkcja HKPFAKH nigdy
  • 16. Zagadnienie 39. Predykaty powinny być funkcjami czystymi 211 nie wywoływała obiektu R, a jedynie jego kopię. W efekcie trzecie wywołanie przez funkcję TGOQXGAEQR[AKH podanego jej predykatu równie zwróci wartość VTWG. Oto dlaczego funkcja TGOQXGAKH usunie z wektora XY dwa elementy zamiast jednego. Najprostszym sposobem na uniknięcie tego rodzaju problemów jest deklarowanie w klasach-predykatach funkcji QRGTCVQT jako EQPUV. W tak zadeklarowanych funk- cjach kompilator nie pozwoli zmienić wartości składników klasy: ENCUU DCF2TGFKECVG RWDNKE WPCT[AHWPEVKQP9KFIGV DQQN ] RWDNKE DQQN QRGTCVQT EQPUV 9KFIGV EQPUV ] TGVWTP VKOGU%CNNGF D æF Y HWPMELCEJ V[RW EQPUV _ PKG OQ PC OKGPKCè YCTVQ EK UM CFQY[EJ _ Ze względu na to, e opisany problem mo na rozwiązać w tak prosty sposób, byłem bliski nazwania tego zagadnienia „W klasach-predykatach stosujmy QRGTCVQT typu EQPUV”. Niestety, takie rozwiązanie nie jest wystarczające. Nawet funkcje skła- dowe oznaczone jako EQPUV mogą u ywać zmiennych pól klasy, niestałych lokal- nych obiektów statycznych, niestałych statycznych obiektów klas, niestałych obiektów w zakresie przestrzeni nazw i niestałych obiektów globalnych. Dobrze za- projektowana klasa-predykat gwarantuje, e funkcje jej operatora QRGTCVQT są nie- zale ne od tego rodzaju obiektów. Zadeklarowanie QRGTCVQT jako EQPUV jest ko- nieczne dla uzyskania właściwego zachowania klasy, jednak nie jest wystarczające. Co prawda wystarczyłoby tak zmodyfikować składowe, eby nie wpływały na wynik predykatu, jednak dobrze zaprojektowany QRGTCVQT wymaga czegoś więcej — musi być funkcją czystą. Zaznaczyłem ju , e w miejscach, w których STL oczekuje funkcji predykatu, za- akceptowany zostanie równie obiekt klasy-predykatu. Ta zasada obowiązuje równie w przeciwnym kierunku. W miejscach, w których STL oczekuje obiektu klasy- predykatu, zaakceptowana zostanie równie funkcja-predykat (prawdopodobnie zmody- fikowana przez funkcję RVTAHWP — zobacz „Zagadnienie 41.”). Zostało ju udowod- nione, e funkcje QRGTCVQT w klasach-predykatach muszą być funkcjami czysty- mi, co w połączeniu z powy szymi stwierdzeniami oznacza, e funkcje-predykaty równie muszą być funkcjami czystymi. Poni sza funkcja nie jest a tak złym predy- katem jak obiekty tworzone na podstawie klasy $CF2TGFKECVG, poniewa jej zastoso- wanie wią e się z istnieniem tylko jednej kopii zmiennej stanu, jednak i ona narusza zasady tworzenia predykatów: DQQN CPQVJGT$CF2TGFKECVG EQPUV 9KFIGV EQPUV 9KFIGV ] UVCVKE KPV VKOGU%CNNGF 0KG 0KG 0KG 0KG 0KG 0KG 0KG 0KG TGVWTP VKOGU%CNNGF 2TGF[MCV[ RQYKPP[ D[è HWPMELCOK E[UV[OK _ C VCMKG HWPMELG PKG OCLæ UVCPW Niezale nie od tego, w jaki sposób tworzysz swoje predykaty, powinny one zawsze być funkcjami czystymi.
  • 17. 212 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne Zagadnienie 40. Klasy-funktory powinny być adaptowalne Zagadnienie 40. Klasy-funktory powinny być adaptowalne Przypuśćmy, e mamy listę wskaźników 9KFIGV
  • 18. i funkcję określającą, czy dany wskaźnik identyfikuje interesujący nas element. NKUV9KFIGV
  • 19. YKFIGV2VTU DQQN KU+PVGTGUVKPI EQPUV 9KFIGV
  • 20. RY Je eli chcielibyśmy znaleźć pierwszy wskaźnik na interesujący nas element, mo na by to zrobić w prosty sposób: NKUV9KFIGV
  • 21. KVGTCVQT K HKPFAKH YKFIGV2VTUDGIKP YKFIGV2VTUGPF KU+PVGTGUVKPI KH K YKFIGV2VTUGPF ] QRGTCELG YKæCPG RKGTYU[O _ KPVGTGUWLæE[O PCU YUMC[YCP[O GNGOGPVGO Je eli jednak będziemy chcieli uzyskać pierwszy wskaźnik na element nas nieinteresu- jący, ten najbardziej oczywisty sposób nawet się nie skompiluje. NKUV9KFIGV
  • 22. KVGTCVQT K HKPFAKH YKFIGV2VTUDGIKP YKFIGV2VTUGPF PQV KU+PVGTGUVKPI D æF PKG UMQORKNWLG UKú W takim przypadku nale y funkcję KU+PVGTGUVKPI przekazać najpierw do funkcji RVTAHWP , a dopiero potem do adaptora PQV . NKUV9KFIGV
  • 23. KVGTCVQT K HKPFAKH YKFIGV2VTUDGIKP YKFIGV2VTUGPF PQV RVTAHWP KU+PVGTGUVKPI VCM LGUV FQDTG KH K YKFIGV2VTUGPF ] QRGTCELG YKæCPG RKGTYU[O _ PKGKPVGTGUWLæE[O PCU YUMC[YCP[O GNGOGPVGO Tutaj rodzi się kilka pytań. Dlaczego konieczne jest zastosowanie funkcji RVTAHWP na funkcji KU+PVGTGUVKPI przed przekazaniem jej do PQV ? Co i jak robi funkcja RVTAHWP , e umo liwia skompilowanie powy szego kodu? Odpowiedź na te pytania jest nieco zaskakująca. Funkcja RVTAHWP udostępnia jedynie kilka deklaracji V[RGFGH. To wszystko. Są to deklaracje wymagane przez adaptor PQV i z tego powodu bezpośrednie przekazanie funkcji KU+PVGTGUVKPI do PQV nie bę- dzie działać. Symbol KU+PVGTGUVKPI jest jedynie prostym wskaźnikiem na funkcję, dlatego brakuje mu deklaracji wymaganych przez adaptor PQV . W STL znajduje się wiele innych komponentów tworzących podobne wymagania. Ka - dy z czterech podstawowych adaptorów funkcji (PQV , PQV , DKPFUV i DKPFPF ) wymaga istnienia odpowiednich deklaracji V[RGFGH, tak jak i wszystkie inne niestan- dardowe, ale zgodne z STL adaptory tworzone przez ró ne osoby (na przykład te two- rzone przez firmy SGI lub Boost — zobacz „Zagadnienie 50.”). Funktory zawierające te wymagane deklaracje V[RGFGH nazywane są adaptowalnymi, natomiast funkcje ich
  • 24. Zagadnienie 40. Klasy-funktory powinny być adaptowalne 213 nieposiadające nazywane są nieadaptowalnymi. Adaptowalnych funktorów mo na u ywać w znacznie większej ilości kontekstów ni nieadaptowalnych, dlatego, gdy tylko to mo liwe, nale ałoby budować funktory adaptowalne. Taka operacja nie kosz- tuje wiele, a mo e znacznie ułatwić pracę u ytkownikom Twoich klas-funktorów. Zapewne ju się denerwujesz, e cały czas mówię o „odpowiednich deklaracjach V[ RGFGH”, ale nigdy nie określam, jakie to deklaracje. Są to deklaracje: CTIWOGPVAV[RG, HKTUVACTIWOGPVAV[RG, UGEQPFACTIWOGPVAV[RG i TGUWNVAV[RG. Niestety, ycie nie jest całkiem proste, poniewa w zale ności od rodzaju klasy-funktory powinny udostęp- niać ró ne zestawy tych nazw. Tak naprawdę, je eli nie tworzysz własnych adapto- rów (a tego w tej ksią ce nie będziemy opisywać), nie musisz znać tych deklaracji. Wynika to z faktu, e najprostszym sposobem udostępnienia tych deklaracji jest odziedziczenie ich po klasie bazowej, a właściwie po bazowej strukturze. Klasy- funktory, w których QRGTCVQT przyjmuje jeden argument, powinny być wywo- dzone z UVFWPCT[AHWPEVKQP, natomiast klasy-funktory, w których QRGTCVQT przyjmuje dwa argumenty, powinny być wywodzone z UVFDKPCT[AHWPEVKQP. Nale y pamiętać, e WPCT[AHWPEVKQP i DKPCT[AHWPEVKQP to szablony, dlatego nie mo - na bezpośrednio po nich dziedziczyć, ale dziedziczyć po wygenerowanych przez nie strukturach, a to wymaga określenia typów argumentów. W przypadku WPCT[AHWPEVKQP musisz określić typ parametru pobieranego przez QRGTCVQT Twojej klasy funkto- ra, a tak e typ jego wartości zwracanej. W przypadku DKPCT[AHWPEVKQP konieczne jest określenie trzech typów: pierwszego i drugiego parametru QRGTCVQT oraz zwraca- nej przez niego wartości. Poni ej podaję kilka przykładów: VGORNCVGV[RGPCOG 6 ENCUU /GGVU6JTGUJQNF RWDNKE UVFWPCT[AHWPEVKQP9KFIGV DQQN ] RTKXCVG EQPUV 6 VJTGUJQNF RWDNKE /GGVU6JTGUJQNF EQPUV 6 VJTGUJQNF DQQN QRGTCVQT EQPUV 9KFIGV EQPUV _ UVTWEV 9KFIGV0COG%QORCTG UVFDKPCT[AHWPEVKQP9KFIGV 9KFIGV DQQN ] DQQN QRGTCVQT EQPUV 9KFIGV NJU EQPUV 9KFIGV TJU EQPUV _ Proszę zauwa yć, e w obydwu przypadkach typy przekazywane do WPCT[AHWPEVKQP i DKPCT[AHWPEVKQP są identyczne z typami pobieranymi i zwracanymi przez QRGTC VQT danej klasy funktora. Troszkę dziwny jest tylko sposób przekazania typu warto- ści zwracanej przez operator jako ostatniego parametru szablonów WPCT[AHWPEVKQP lub DKPCT[AHWPEVKQP. Zapewne nie uszło Twojej uwadze, e /GGVU6JTGUJQNF jest klasą, a 9KFIGV0COG%QORC TG jest strukturą. Wynika to z faktu, e /GGVU6JTGUJQNF ma składowe opisujące jej wewnętrzny stan (pole VJTGUJQNF), dlatego naturalną rzeczą jest zastosowanie w takiej sytuacji klasy. Z kolei 9KFIGV0COG%QORCTG nie przechowuje informacji o stanie, dlatego
  • 25. 214 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne nie ma potrzeby ukrywania w niej jakichkolwiek danych. Autorzy klas, w których nie ma elementów prywatnych, często deklarują takie klasy jako struktury. Prawdopo- dobnie chodzi o mo liwość uniknięcia wpisywania w takiej klasie słowa kluczowego RWDNKE. Wybór deklaracji takich klas jako klasy lub struktury zale y wyłącznie od prefe- rencji programisty. Je eli cały czas próbujesz wykuć własny styl, a chciałbyś naślado- wać zawodowców, zauwa , e w bibliotece STL wszystkie klasy nieposiadające stanu (na przykład NGUU6 , RNWU6 itd.) deklarowane są jako struktury. Przyjrzyjmy się jeszcze raz strukturze 9KFIGV0COG%QORCTG: UVTWEV 9KFIGV0COG%QORCTG RWDNKE UVFDKPCT[AHWPEVKQP9KFIGV 9KFIGV DQQN ] DQQN QRGTCVQT EQPUV 9KFIGV NJU EQPUV 9KFIGV TJU EQPUV _ Typ przekazywany do szablonu DKPCT[AHWPEVKQP to 9KFIGV, mimo e QRGTCVQT pobiera argumenty typu EQPUV 9KFIGV . Zazwyczaj niebędące wskaźnikami typy prze- kazywane do szablonu WPCT[AHWPEVKQP lub DKPCT[AHWPEVKQP odzierane są ze znaczni- ków EQPUV i referencji. (Nie pytaj dlaczego. Powód nie jest ani dobry, ani interesują- cy. Je eli jednak nadal bardzo chcesz wiedzieć, napisz program testowy i nie usuwaj w nim tych znaczników, a następnie przeanalizuj wynik działania kompilatora. Je eli po tym wszystkim nadal będziesz zainteresowany tematem, zajrzyj na stronę boost.org (zobacz „Zagadnienie 50.”) i przejrzyj na niej teksty dotyczące adaptorów funktorów i cech wywołań). W przypadku gdy QRGTCVQT pobiera wskaźniki jako parametry, opisane wy ej za- sady ulegają zmianie. Poni ej podaję strukturę podobną do 9KFIGV0COG%QORCTG, która posługuje się wskaźnikami 9KFIGV
  • 26. : UVTWEV 2VT9KFIGV0COG%QORCTG RWDNKE UVFDKPCT[AHWPEVKQPEQPUV 9KFIGV
  • 28. DQQN ] DQQN QRGTCVQT EQPUV 9KFIGV
  • 30. TJU EQPUV _ W tym przypadku typy przekazywane do DKPCT[AHWPEVKQP są identyczne z typami pobieranymi przez QRGTCVQT . Wszystkie klasy-funktory pobierające lub zwracające wskaźniki obowiązuje zasada nakazująca przekazywanie do WPCT[AHWPEVKQP lub DKPC T[AHWPEVKQP dokładnie takich samych typów, jakie pobiera lub zwraca QRGTCVQT . Nie mo emy zapomnieć, z jakiego powodu snujemy te opowieści o klasach bazowych WPCT[AHWPEVKQP i DKPCT[AHWPEVKQP — dostarczają one deklaracji V[RGFGH wymaga- nych przez adaptory funktorów, dlatego dziedziczenie po tych klasach pozwala two- rzyć funktory adaptowalne. To z kolei pozwala na pisanie tego rodzaju rzeczy: NKUV9KFIGV YKFIGVU NKUV9KFIGV TGXGTUGAKVGTCVQT K PCLFWLG QUVCVPK GNGOGPV HKPFAKH YKFIGVUTDGIKP YKFIGVUTGPF MVÎT[ PKG RTGMTQE[ PQV /GGVU6TGUJQNFKPV RTQIW Q YCTVQ EK EQMQNYKGM VQ PCE[ 9KFIGV Y CTIWOGPV[ MQPUVTWMVQTC NKUV9KFIGV KVGTCVQT K PCLFWLG RKGTYU[ GNGOGPV HKPFAKH YKFIGVUDGIKP YKFIGVUGPF PCLFWLæE[ UKú RTGF Y
  • 31. Zagadnienie 41. Po co stosować funkcje ptr_fun, mem_fun i mem_fun_ref? 215 DKPFPF 9KFIGV0COG%QORCTG Y Y RQTæFMW UQTVQYCPKC FGHKPKQYCP[O RTG 9KFIGV0COG%QORCTG Gdyby nasze klasy-funktory nie zostały wywiedzione z klasy WPCT[AHWPEVKQP lub DKPC T[AHWPEVKQP, powy sze przykłady nawet by się nie skompilowały, poniewa funkcje PQV i DKPFPF działają tylko z funktorami adaptowalnymi. W STL funktory modelowane są podobnie do funkcji w języku C++, które mają tylko jeden zestaw typów parametrów i jedną wartość zwracaną. W efekcie przyjmuje się, e ka da klasa-funktor ma tylko jedną funkcję QRGTCVQT , której parametry i wartość zwracana powinny zostać przekazane do klas WPCT[AHWPEVKQP lub DKPCT[AHWPEVKQP (wynika to z omówionych właśnie zasad dla typów wskaźnikowych i referencyjnych). A z tego wynika z kolei, e nie powinno się łączyć funkcjonalności struktur 9KFIGV 0COG%QORCTG i 2VT9KFIGV0COG%QORCTG przez utworzenie jednej klasy mającej dwie funkcje QRGTCVQT . Je eli utworzysz taką klasę, będzie ona adaptowalna tylko w jednej wersji (tej zgodnej z parametrami przekazywanymi do DKPCT[AHWPEVKQP). Jak mo na się domyślać, funktor adaptowalny tylko w połowie równie dobrze mógłby nie być adaptowalny w ogóle. W niektórych przypadkach utworzenie mo liwości wywołania funktora w wielu for- mach (a tym samym rezygnacja z adaptowalności) ma sens, co opisano w zagadnie- niach: 7., 20., 23. i 25. Nale y jednak pamiętać, e tego rodzaju funktory są jedynie wyjątkami od zasady. Adaptowalność to cecha, do której nale y dą yć w czasie two- rzenia klas-funktorów. Zagadnienie 41. Po co stosować funkcje ptr_fun, mem_fun i mem_fun_ref? Zagadnienie 41. Po co stosować funkcje ptr_fun, mem_fun i mem_fun_ref? O co chodzi z tymi funkcjami? Czasami trzeba ich u ywać, czasami nie. Co one wła- ściwie robią? Wygląda na to, e czepiają się nazw funkcji jak rzep psiego ogona. Nie- łatwo je wpisać, denerwują w czasie czytania i trudno je zrozumieć. Czy są to artefakty podobne do przedstawionych w podrozdziałach „Zagadnienie 10.” i „Zagadnienie 18.”, czy mo e członkowie komitetu standaryzacyjnego wycięli nam niemiły dowcip? Spokojnie, te funkcje mają do spełnienia naprawdę wa ne zadania i z całą pewno- ścią nie są dziwacznymi artami. Jednym z ich podstawowych zadań jest zamaskowanie pewnych niekonsekwencji syntaktycznych języka C++. Je eli, posiadając funkcję H i obiekt Z, chcielibyśmy wywołać H na rzecz Z i jeste- śmy poza funkcjami składowymi obiektu Z, język C++ pozwala na zastosowanie trzech ró nych składni takiego wywołania. H Z 5M CFPKC PT 5VQUQYCPC Y RT[RCFMW IF[ HWPMELC H PKG LGUV OGVQFæ QDKGMVW Z ZH 5M CFPKC PT 5VQUQYCPC Y RT[RCFMW
  • 32. 216 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne IF[ HWPMELC H LGUV OGVQFæ QDKGMVW C Z LGUV QDKGMVGO NWD TGHGTGPELæ FQ QDKGMVW R H 5M CFPKC PT 5VQUQYCPC Y RT[RCFMW IF[ HWPMELC H LGUV OGVQFæ QDKGMVW C R LGUV YUMC PKMKGO PC QDKGMV Z Teraz załó my, e mamy funkcję sprawdzającą elementy: XQKF VGUV 9KFIGV Y URTCYFC Y K LG GNK PKG CNKE[ QP VGUVW QPCEC IQ LCMQ PKGY C EKY[ i kontener przechowujący te elementy: XGEVQT9KFIGV XY XY RTGEJQYWLG GNGOGPV[ Je eli chcielibyśmy sprawdzić wszystkie elementy w XY, moglibyśmy w prosty sposób wykorzystać funkcję HQTAGCEJ : HQTAGCEJ XYDGIKP XYGPF VGUV 9[YQ CPKG PT UMQORKNWLG UKú Wyobraźmy sobie, e VGUV nie jest zwyczajną funkcją, ale funkcją składową klasy 9KIFGV, co oznacza, e obiekty tej klasy mogą same się sprawdzać: ENCUU 9KFIGV ] RWDNKE XQKF VGUV Y[MQPWLG UCOQURTCYFGPKG LG GNK QDKGMV
  • 33. VJKU PKG RTGLFKG VGUVW _ QPCECP[ LGUV LCMQ PKGY C EKY[ W świecie doskonałym moglibyśmy zastosować funkcję HQTAGCEJ , aby wywołać funkcję 9KFIGVVGUV dla ka dego obiektu wektora XY: HQTAGCEJ XYDGIKP XYGPF 9KFIGVVGUV 9[YQ CPKG PT PKG UMQORKNWLG UKú Je eli świat byłby naprawdę doskonały, moglibyśmy równie zastosować funkcję HQTAGCEJ , eby wywołać funkcję 9KFIGVVGUV w elementach kontenera prze- chowującego wskaźniki 9KFIGV
  • 34. : NKUV9KFIGV
  • 35. NRY NRY RTGEJQYWLG YUMC PKMK PC GNGOGPV[ HQTAGCEJ NRYDGIKP NRYGPF 9KFIGVVGUV 9[YQ CPKG PT TÎYPKG UKú PKG UMQORKNWLG Pomyślmy jednak, co by się działo w tym świecie doskonałym. W przypadku wywoła- nia nr 1 wewnątrz funkcji HQTAGCEJ wywoływalibyśmy zwykłą funkcję, przekazując jej obiekt, czyli konieczne byłoby zastosowanie składni nr 1. W przypadku wywoła- nia nr 2 wewnątrz funkcji HQTAGCEJ wywoływalibyśmy metodę pewnego obiektu, czyli konieczne byłoby zastosowanie składni nr 2. Natomiast w przypadku wywołania nr 3 wewnątrz funkcji HQTAGCEJ wywoływalibyśmy metodę obiektu, do którego od- wołujemy się poprzez wskaźnik, czyli konieczne byłoby zastosowanie składni nr 3. To wszystko oznacza, e musiałyby istnieć trzy ró ne wersje funkcji HQTAGCEJ , a świat nie byłby ju tak doskonały.
  • 36. Zagadnienie 41. Po co stosować funkcje ptr_fun, mem_fun i mem_fun_ref? 217 W świecie rzeczywistym istnieje tylko jedna wersja funkcji HQTAGCEJ . Zapewne nie- trudno się domyślić, jak wygląda jej implementacja: VGORNCVGV[RGPCOG +PRWV+VGTCVQT V[RGPCOG (WPEVKQP (WPEVKQP HQTAGCEJ +PRWV+VGTCVQT DGIKP +PRWV+VGTCVQT GPF (WPEVKQP H ] YJKNG DGIKP GPF H
  • 37. DGIKP _ Proszę zauwa yć, e funkcja HQTAGCEJ wykorzystuje do wywoływania funkcji H składnię nr 1. Jest to ogólnie przyjęta w STL konwencja, funkcje i funktory są wywo- ływane za pomocą składni stosowanej dla zwykłych funkcji. To wyjaśnia, dlaczego mo na skompilować składnię nr 1, ale składni nr 2 i 3 ju nie. Wszystkie algorytmy STL (w tym równie HQTAGCEJ ) wykorzystują składnię nr 1, wobec czego jedynie wywoła- nie nr 1 jest z nią zgodne. Teraz powinno być ju jasne, dlaczego istnieją funkcje OGOAHWP i OGOAHWPATGH . Sprawiają one, e funkcje składowe (które powinny być wywoływane za pomocą składni nr 2 lub 3) są wywoływane za pomocą składni nr 1. Funkcje OGOAHWP i OGOAHWPATGH wykonują swoje zadania w bardzo prosty sposób, spojrzenie na deklarację jednej z nich powinno całkowicie wyjaśnić zagadkę. Tak na- prawdę są to szablony funkcji, istnieje ich kilka wersji ró niących się ilością parame- trów i tym, czy przystosowywana funkcja jest oznaczona jako EQPUV czy nie. Aby po- znać sposób działania tych funkcji, wystarczy zobaczyć kod jednej z nich: VGORNCVGV[RGPCOG 4 V[RGPCOG % FGMNCTCELC HWPMELK OGOAHWP FNC PKGQPCEQP[EJ OGOAHWPAV4 % LCMQ EQPUV HWPMELK UM CFQY[EJ OGOAHWP 4 %
  • 38. ROH PKGRQDKGTCLæE[EJ CFP[EJ RCTCOGVTÎY % VQ MNCUC C 4 VQ YCTVQ è YTCECPC YUMC[YCPGL HWPMELK UM CFQYGL Funkcja OGOAHWP pobiera wskaźnik na metodę (ROH) i zwraca obiekt typu OGOAHWPAV. Jest to klasa-funktor przechowująca wskaźnik na metodę i udostępniająca QRGTC VQT wywołujący tę metodę na rzecz obiektu podanego jako parametr tego opera- tora. Na przykład w kodzie: NKUV9KFIGV
  • 39. NRY RQFQDPKG LCM Y[ GL HQTAGCEJ NRYDGIKP NRYGPF OGOAHWP 9KFIGVVGUV VGTC UKú UMQORKNWLG funkcja HQTAGCEJ otrzymuje obiekt typu OGOAHWPAV przechowujący wskaźnik na funk- cję 9KFIGVVGUV. Dla ka dego wskaźnika 9KFIGV
  • 40. z NRY za pomocą składni nr 1 wy- woływany jest obiekt OGOAHWPAV, a ten natychmiast wywołuje funkcję 9KFIGVVGUV zgodnie ze składnią nr 3. Ogólnie, funkcja OGOAHWP przystosowuje składnię nr 3 wymaganą przy wywołaniach funkcji 9KFIGVVGUV za pomocą wskaźnika 9KFIGV
  • 41. na składnię nr 1 stosowaną przez funkcję HQTAGCEJ , wobec czego nie powinno dziwić, e klasy w rodzaju OGOA HWPAV nazywane są adaptorami obiektów funkcyjnych. W podobny sposób funkcja OGOAHWPA TGH przystosowuje składnię nr 2, generując obiekty-adaptory typu OGOAHWPATGHAV.
  • 42. 218 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne Obiekty tworzone przez funkcje OGOAHWP i OGOAHWPATGH pozwalają nie tylko zakła- dać, e wszystkie funkcje wywoływane są za pomocą tej samej składni, ale równie , podobnie jak obiekty generowanie przez funkcję RVTAHWP , udostępniają odpowiednie deklaracje V[RGFGH. Na temat tych deklaracji była mowa w podrozdziale „Zagadnienie 40.”. Dzięki tym wyjaśnieniom powinno być ju jasne, dlaczego ten kod się skompiluje: HQTAGCEJ XYDGIKP XYGPF VGUV Y[YQ CPKG PT UMQORKNWLG UKú a te nie: HQTAGCEJ XYDGIKP XYGPF 9KFIGVVGUV Y[YQ CPKG PT PKG UMQORKNWLG UKú HQTAGCEJ NRYDGIKP NRYGPF 9KFIGVVGUV Y[YQ CPKG PT PKG UMQORKNWLG UKú Wywołanie nr 1 przekazuje w parametrze funkcję, dlatego nie ma konieczności dosto- sowania składni do wymagań funkcji HQTAGCEJ . Algorytm wywoła otrzymaną funk- cję za pomocą właściwej składni. Co więcej, funkcja HQTAGCEJ nie u ywa adnej z deklaracji V[RGFGH udostępnianej przez funkcję RVTAHWP , więc nie ma potrzeby przekazywania funkcji VGUV za pośrednictwem tej funkcji. Z drugiej strony, udo- stępnienie tych deklaracji na nic nie wpłynie, więc poni szy kod zadziała tak samo jak ten podany wy ej. HQTAGCEJ XYDGIKP XYGPF RVTAHWP VGUV UMQORKNWLG UKú K CFKC C RQFQDPKG LCM Y[YQ CPKG PT Je eli teraz ju nie wiesz, kiedy stosować funkcję RVTAHWP , a kiedy nie — mo esz u ywać jej przy ka dym przekazywaniu funkcji do komponentu STL. Bibliotece nie zrobi to adnej ró nicy, nie wpłynie te na wydajność programu. Najgorsze, co mo e Ci się zdarzyć, to zdziwienie na twarzy osoby czytającej Twój kod pojawiające się w momencie napotkania nadmiarowego wywołania funkcji RVTAHWP . Na ile będzie Ci to przeszkadzać? Chyba zale y to od Twojej wra liwości na zdziwione twarze. Inną strategią dotyczącą stosowania funkcji HWPARVT jest stosowanie jej tylko w przy- padku, gdy zostaniemy do tego zmuszeni. Oznacza to, e w przypadkach, w których konieczna będzie obecność deklaracji V[RGFGH, kompilacja programu zostanie wstrzy- mana. Wtedy będzie trzeba uzupełnić kod o wywołanie funkcji HWPARVT . W przypadku funkcji OGOAHWP i OGOAHWPATGH mamy zupełnie inną sytuację. Ich wywołanie jest konieczne przy ka dym przekazywaniu metody do komponentu STL, poniewa poza udostępnianiem potrzebnych deklaracji V[RGFGH dostosowują one składnię stosowaną przy wywoływaniu metod do składni stosowanej w całej bibliote- ce STL. Brak odpowiedniej funkcji przy przekazywaniu wskaźników na metody unie- mo liwi poprawną kompilację programu. Pozostało nam omówić nazwy adaptorów metod. Okazuje się, e natkniemy się tutaj na historyczny ju artefakt. Gdy okazało się, e potrzebne są takie adaptory, twórcy biblioteki STL skupili się na kontenerach wskaźników (W świetle ograniczeń, jakimi obarczone są te kontenery — opisano je w zagadnieniach 7., 20. i 33. — pewnym zaskoczeniem mo e być fakt, e to właśnie kontenery wskaźników obsługują klasy poli- morficzne, podczas gdy kontenery obiektów ich nie obsługują). Zbudowano adaptor dla metod i nazwano go OGOAHWP . Później okazało się, e potrzebny jest jeszcze adaptor
  • 43. Zagadnienie 42. Upewnij się, że lesst() oznacza operator() 219 dla kontenerów obiektów, więc nową funkcję nazwano OGOAHWPATGH . Nie jest to zbyt eleganckie, ale takie rzeczy się zdarzają. Pewnie ka demu zdarzyło się nadać kompo- nentowi nazwę, którą później trudno było dostosować do nowych warunków. Zagadnienie 42. Upewnij się, że lesst() oznacza operator() Zagadnienie 42. Upewnij się, e lesst() oznacza operator() Jak wszyscy dobrze wiemy, „widgety” mają swoją masę i maksymalną prędkość: ENCUU 9KFIGV ] RWDNKE UKGAV YGKIJV EQPUV UKGAV OCZ5RGGF EQPUV _ Oczywiście naturalnym sposobem sortowania widgetów jest sortowanie ich według ma- sy, dlatego operator mniejszości () w tym przypadku powinien wyglądać następująco: DQQN QRGTCVQT EQPUV 9KFIGV NJU EQPUV 9KFIGV TJU ] TGVWTP NJUYGKIVJ TJUYGKIJV _ Przypuśćmy jednak, e chcielibyśmy utworzyć kontener typu OWNVKUGV9KFIGV , w któ- rym widgety sortowane byłyby według ich prędkości maksymalnej. Wiemy ju , e domyślną funkcją porównującą kontenera OWNVKUGV9KFIGV jest NGUU9KFIGV . Wiemy te , e domyślnie funkcja ta tylko wywołuje operator mniejszości (). Wyglą- da na to, e jedynym sposobem na posortowanie kontenera OWNVKUGV9KFIGV według prędkości maksymalnej jego elementów jest zniszczenie połączenia między funkcją NGUU9KFIGV a operatorem mniejszości (). Mo na to zrobić, nakazując funkcji NGUU9KFIGV kontrolę jedynie prędkości maksymalnej podawanych jej widgetów: VGORNCVG VQ LGUV URGELCNKCELC MNCU[ UVTWEV UVFNGUU9KFIGV UVFNGUU YKæCPC MNCUæ 9KFIGV RWDNKE RQC V[O LGUV VQ HCVCNP[ RQO[U UVFDKPCT[AHWPEVKQP 9KFIGV 9KFIGV DQQN ] DQQN QRGTCVQT EQPUV 9KFIGV NJU EQPUV 9KFIGV TJU EQPUV ] TGVWTP NJUOCZ5RGGF TJUOCZ5RGGF _ _ Nie wygląda to na zbyt dobrą radę i taką nie jest, chyba jednak nie z powodu, o którym myślisz. Czy nie jest zaskakujące, e ten kod w ogóle się kompiluje? Wielu programi- stów zauwa y, e nie jest on tylko zwykłą specjalizacją szablonu, ale jest specjalizacją
  • 44. 220 Rozdział 6. ♦ Funktory, klasy-funktory, funkcje i inne szablonu w przestrzeni nazw UVF. Będą oni pytać: „Czy przestrzeń UVF nie powinna być święta? Dostępna tylko dla twórców biblioteki i będąca poza zasięgiem zwykłych programistów? Czy kompilatory nie powinny zakazywać grzebania w pracach twór- ców C++?”. Zazwyczaj próby modyfikacji komponentów w przestrzeni UVF są rzeczywiście za- bronione, a próby ich wykonania kończą się niezdefiniowanym zachowaniem aplika- cji. Jednak w niektórych przypadkach takie prowizorki są dopuszczalne. W szczegól- ności mo liwe jest specjalizowanie szablonów do obsługi typów zdefiniowanych przez u ytkownika. Niemal zawsze są inne, lepsze wyjścia ni zabawa z szablonami z prze- strzeni UVF, jednak czasami wiele argumentów przemawia właśnie za taką opcją. Na przykład autorzy klas inteligentnych wskaźników chcieliby, aby ich klasy zachowy- wały się jak zwyczajne wskaźniki, dlatego w takich klasach często spotyka się spe- cjalizacje funkcji UVFNGUU . Poni szy kod jest częścią klasy UJCTGFARVT z biblioteki Boost. Jest to właśnie inteligentny wskaźnik, o którym mo na przeczytać w podroz- działach „Zagadnienie 7.” i „Zagadnienie 50.”. PCOGURCEG UVF ] VGORNCVGV[RGPCOG 6 URGELCNKCELC HWPMELK UVFNGUU UVTWEV NGUU DQQUVUJCTGFARVT6 FNC MNCU[ DQQUVUJCTGFARVT6 RWDNKE DQQUV VQ RTGUVTG PCY DKPCT[AHWPEVKQPDQQUVUJCTGFARVT6 DQQUVUJCTGF RVT6 VQ LGUV V[RQYC MNCUC DQQN ] DCQYC QDCE CICFPKGPKG DQQN QRGTCVQT EQPUV DQQUVUJCTGFARVT6 C EQPUV DQQUVUJCTGFARVT6 D EQPUV ] TGVWTP NGUU6
  • 45. CIGV DIGV UJCTGFARVTIGV YTCEC _ Y[M [ YUMC PKM MVÎT[ LGUV QDKGMVGO UJCTGFARVT _ _ Powy sza implementacja nie jest pozbawiona sensu — z cała pewnością nie tworzy adnych niespodzianek, poniewa taka specjalizacja zapewnia jedynie, e sortowanie zwykłych wskaźników i wskaźników inteligentnych odbywa się w ten sam sposób. Niestety, nasza specjalizacja funkcji NGUU w klasie 9KFIGV mo e przysporzyć kilku niemiłych niespodzianek. Programistom C++ mo na wybaczyć, e pewne rzeczy uznają za oczywiste. Na przy- kład zakładają oni, e konstruktory kopiujące rzeczywiście kopiują obiekty (jak wy- kazano w podrozdziale „Zagadnienie 8.”, niedopełnienie tej konwencji mo e prowa- dzić do zadziwiających zachowań programu). Zakładają te , e pobierając adres obiektu, otrzymają wskaźnik na ten obiekt (w podrozdziale „Zagadnienie 18.” opisano problemy, jakie powstają, gdy nie jest to prawdą). Przyjmują za oczywiste, e adapto- ry takie jak DKPFUV i PQV mo na stosować z funktorami (podrozdział „Zagad- nienie 40.” opisuje problemy wynikające z niespełnienia tego zało enia). Zakładają równie , e operator dodawania ( ) dodaje (za wyjątkiem ciągów znaków, ale opera- tor ten jest ju od dawna u ywany do łączenia ciągów), operator odejmowania () odejmuje, a operator porównania () porównuje obiekty. W końcu przyjmują za oczy- wiste, e zastosowanie funkcji NGUU jest równoznaczne z zastosowaniem operatora mniejszości ().
  • 46. Zagadnienie 42. Upewnij się, że lesst() oznacza operator() 221 Operator mniejszości jest nie tylko domyślną implementacją funkcji NGUU , ale we- dług zało eń programistów definiuje on sposób działania tej funkcji. Je eli funkcji NGUU naka emy robić coś innego ni wywołanie operatora mniejszości (), pogwałci- my w ten sposób oczekiwania programistów. To całkowicie zaprzecza „zasadzie najmniejszego zaskoczenia” — takie działanie jest nieprzyzwoite, bezduszne i złe. Tak robić nie wolno. Nie wolno tego robić, szczególnie dlatego, e nie ma ku temu powodów. W bibliotece STL nie ma miejsca, w którym nie mo na by zastąpić funkcji NGUU innym rodzajem porównania. Wracając do naszego początkowego przykładu (czyli kontenera OWNVK UGV9KFIGV sortowanego według prędkości maksymalnej), aby osiągnąć zamierzony cel, musimy jedynie utworzyć klasę-funktor wykonującą potrzebne nam porównanie. Mo na ją nazwać prawie dowolnie, jednak na pewno nie mo na zastosować nazwy NGUU . Oto przykład takiej klasy: UVTWEV /CZ5RGGF%QORCTG RWDNKE DKPCT[AHWPEVKQP9KFIGV 9KFIGV DQQN ] DQQN QRGTCVQT EQPUV 9KFIGV NJU EQPUV 9KFIGV TJU EQPUV ] TGVWTP NJUOCZ5RGGF TJUOCZ5RGGF _ _ Tworząc nasz kontener, jako funkcję porównującą wykorzystamy klasę /CZ5RGGF%QO RCTG i w ten sposób unikniemy wykorzystania domyślnej funkcji porównującej, czyli NGUU9KFIGV . OWNVKUGV9KFIGV /CZ5RGGF%QORCTG YKFIGVU Powy szy kod wykonuje dokładnie te operacje. Tworzy on kontener typu OWNVKUGV elementów 9KFIGV posortowanych zgodnie z definicją zawartą w klasie /CZ5RGGF%QORCTG. Porównajmy to z kodem: OWNVKUGV9KFIGV YKFIGVU Tworzy on kontener typu OWNVKUGV elementów 9KFIGV posortowanych w sposób domyślny. Oznacza to, e do porównań wykorzystywana będzie funkcja NGUU9KFIGV , jednak ka dy programista zało y w tym miejscu, e odpowiadać za to będzie operator mniejszości (). Nie utrudniajmy ycia innym, zmieniając domyślną definicję funkcji NGUU . Niech ka de zastosowanie funkcji NGUU (bezpośrednie lub pośrednie) wią e się z wykorzy- staniem operatora mniejszości. Je eli chcesz posortować obiekty za pomocą innego kryterium, zbuduj do tego specjalną klasę-funktor i nie nazywaj jej NGUU . To prze- cie takie proste.