You are on page 1of 30

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Jzyk C++. Standardy kodowania.

101 zasad, wytycznych


i zalecanych praktyk
Autorzy: Herb Sutter, Andrei Alexandrescu
Tumaczenie: Przemysaw Szeremiota
ISBN: 83-7361-849-X
Tytu oryginau: C++ Coding Standards: 101 Rules,
Guidelines, and Best Practices
Format: B5, stron: 320
Czytelny i przejrzysty kod to podstawa sprawnego tworzenia aplikacji. W przypadku
pracy zespoowej stosowanie wsplnego standardu kodowania to konieczno. Pisanie
kodu w oparciu o okrelone standardy kodowania przyspiesza powstawanie programu,
uatwia komunikacj pomidzy czonkami zespou i pozwala na szybkie wdroenie
nowych programistw do projektu. Oczywicie, w kadej firmie lub zespole mona
ustali wasny standard kodowania wane jest jednak, aby opiera si na okrelonych
reguach, wynikajcych ze specyfiki jzyka programowania.
Ksika Jzyk C++. Standardy kodowania. 101 zasad, wytycznych i zalecanych
praktyk zawiera opis wspomnianych regu. Przedstawia zasady pisania kodu
rdowego i standaryzowania okrelonych zapisw, operacji i sposobw wykorzystania
elementw jzyka C++. Kada z zasad jest szczegowo omwiona i poparta
praktycznymi przykadami. Ksika prezentuje najlepsze ze znanych praktyk zarwno
starych, jak i tych, ktre cakiem niedawno ulegy standaryzacji, oraz opisuje techniki,
o ktrych nie syszeli nawet programici z wieloletnim dowiadczeniem.
Organizacja kodu
Styl projektowy i styl kodowania
Skalowalno kodu
Racjonalna i efektywna obsuga bdw
Prawidowe stosowanie elementw jzyka
Odpowiednie korzystanie z STL
Bezpieczestwo typw
Usprawnij prac, stosujc standardy kodowania gdy za par miesicy bdziesz
musia wrci do swoich dzisiejszych programw, przekonasz si, e byo warto.

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Spis treci
Wstp.........................................................................................................................7
Rozdzia 1. Kwestie organizacyjne ..............................................................................13
Wytyczna 0. Nie bd maostkowy (czyli czego nie standaryzowa) .......................................... 14
Wytyczna 1. Dbaj o bezbdn kompilacj przy najwyszym poziomie ostrzee kompilatora.. 17
Wytyczna 2. Korzystaj z automatycznych systemw kompilacji ................................................ 20
Wytyczna 3. Korzystaj z systemu kontroli wersji ........................................................................ 22
Wytyczna 4. Nie oszczdzaj na wzajemnej rewizji kodu............................................................. 24
Rozdzia 2. Styl projektowy ........................................................................................27
Wytyczna 5. Jednej jednostce jedno zadanie ............................................................................... 29
Wytyczna 6. Przede wszystkim poprawno, prostota i przejrzysto......................................... 31
Wytyczna 7. Jak i kiedy kodowa z uwzgldnieniem skalowalnoci........................................... 33
Wytyczna 8. Wystrzegaj si przedwczesnej optymalizacji .......................................................... 36
Wytyczna 9. Wystrzegaj si przedwczesnej pesymizacji............................................................. 39
Wytyczna 10. Minimalizuj ilo danych globalnych i wspuytkowanych................................ 41
Wytyczna 11. Ukrywaj informacje .............................................................................................. 43
Wytyczna 12. Niepotrzebna rywalizacja to niezdrowa rywalizacja ............................................. 45
Wytyczna 13. Zagwarantuj opiek nad zasobami przez obiekty.
Stosuj RAII i inteligentne wskaniki .................................................................... 49
Rozdzia 3. Styl kodowania.........................................................................................53
Wytyczna 14. Lepsze bdy kompilacji i konsolidacji od bdw czasu wykonania ................... 54
Wytyczna 15. Nie bj si stosowania const ................................................................................. 57
Wytyczna 16. Unikaj makrodefinicji ........................................................................................... 59
Wytyczna 17. Unikaj magicznych numerkw .......................................................................... 62
Wytyczna 18. Zmienne deklaruj najbardziej lokalnie, jak to moliwe......................................... 64
Wytyczna 19. Kada zmienna powinna zosta zainicjalizowana................................................. 66
Wytyczna 20. Unikaj rozwlekych funkcji, wystrzegaj si gbokich zagniede ..................... 69
Wytyczna 21. Unikaj zalenoci inicjalizacji w rnych jednostkach kompilacji ....................... 71

Spis treci
Wytyczna 22. Redukuj zalenoci definicyjne i unikaj zalenoci cyklicznych .......................... 73
Wytyczna 23. Niech pliki nagwkowe bd samowystarczalne ................................................. 75
Wytyczna 24. Pamitaj o wewntrznych barierach plikw nagwkowych,
unikaj barier zewntrznych ................................................................................... 77
Rozdzia 4. Funkcje i operatory...................................................................................79
Wytyczna 25. Parametry przyjmowa odpowiednio przez warto,
(inteligentne) wskaniki albo referencje ............................................................... 80
Wytyczna 26. Zachowuj naturaln semantyk przecianych operatorw .................................. 82
Wytyczna 27. Preferuj kanoniczne postaci operatorw arytmetycznych i przypisania................ 84
Wytyczna 28. Preferuj kanoniczne postaci operatorw ++ i -- oraz ich wersje przedrostkowe ... 86
Wytyczna 29. Przecianie w miejsce niejawnej konwersji typw.............................................. 88
Wytyczna 30. Unikaj przeciania operatorw &&, || i operatora , (przecinka) .......................... 90
Wytyczna 31. Nie uzaleniaj poprawnoci kodu od kolejnoci ewaluacji
argumentw wywoania funkcji............................................................................ 93
Rozdzia 5. Projektowanie klas i dziedziczenie .............................................................95
Wytyczna 32. Ustal rodzaj definiowanej klasy ............................................................................ 96
Wytyczna 33. Lepsze klasy minimalistyczne ni monolityczne .................................................. 98
Wytyczna 34. Lepsza kompozycja od dziedziczenia ................................................................. 100
Wytyczna 35. Nie dziedzicz po klasach, ktre nie zostay przewidziane jako bazowe.............. 103
Wytyczna 36. O wyszoci interfejsw abstrakcyjnych ............................................................ 106
Wytyczna 37. Dziedziczenie publiczne daje wymienialno ..................................................... 109
Wytyczna 38. Uprawiaj bezpieczne przesanianie ..................................................................... 111
Wytyczna 39. Niech metody wirtualne bd niepublicznymi, a publiczne niewirtualnymi.. 114
Wytyczna 40. Unikaj udostpniania konwersji niejawnych....................................................... 117
Wytyczna 41. Skadowe klas, z wyjtkiem klas prostych agregatw, powinny by prywatne .. 120
Wytyczna 42. Nie trwoni tego, co wasne................................................................................ 123
Wytyczna 43. Zachowaj umiar w implementacjach prywatnych ............................................... 126
Wytyczna 44. Warto polubi zwyke funkcje nieskadowe i niezaprzyjanione................... 129
Wytyczna 45. Zawsze udostpniaj komplet: new razem z delete............................................... 131
Wytyczna 46. Jeli przecia new dla klasy, to porzdnie z wszystkimi standardowymi
formami operatora............................................................................................... 133
Rozdzia 6. Konstrukcja, destrukcja i kopiowanie....................................................... 135
Wytyczna 47. Porzdek inicjalizacji skadowych danych powinien by zgodny
z porzdkiem ich deklaracji ................................................................................ 136
Wytyczna 48. W konstruktorze lepsza inicjalizacja od przypisania........................................... 138
Wytyczna 49. Unikaj wywoa metod wirtualnych w konstruktorach i destruktorach .............. 140
Wytyczna 50. Destruktory klasy powinny by albo publiczne i wirtualne,
albo niewirtualne i zabezpieczone ...................................................................... 143
Wytyczna 51. Operacje destrukcji, dealokacji i podmiany nigdy nie zawodz.......................... 146
Wytyczna 52. Usuwaj, co skopiujesz......................................................................................... 149
Wytyczna 53. Jawnie udostpniaj i blokuj kopiowanie ............................................................. 151

Spis treci

Wytyczna 54. Unikaj skrawania obiektw rozwa zastosowanie duplikacji


w miejsce kopiowania w klasach bazowych ....................................................... 153
Wytyczna 55. Przyzwyczaj si do kanonicznych implementacji przypisania ............................ 156
Wytyczna 56. Tam, gdzie to zasadne, udostpniaj bezpieczn (niezgaszajc wyjtkw)
operacj podmiany.............................................................................................. 158
Rozdzia 7. Moduy i przestrzenie nazw...................................................................... 161
Wytyczna 57. Typ i nieskadowe funkcje jego interfejsu powinny rezydowa
w tej samej przestrzeni nazw .............................................................................. 162
Wytyczna 58. Typy i funkcje, jeli nie s przeznaczone do kooperacji, powinny by
rozmieszczone w oddzielnych przestrzeniach nazw ........................................... 165
Wytyczna 59. Wystrzegaj si deklaracji i dyrektyw using w plikach nagwkowych
i plikach kodu rdowego przed dyrektyw #include........................................ 167
Wytyczna 60. Pami powinna by przydzielana i zwalniana w tym samym module............... 171
Wytyczna 61. Nie definiuj w plikach nagwkowych jednostek podlegajcych
czeniu zewntrznemu....................................................................................... 173
Wytyczna 62. Nie pozwalaj na propagacj wyjtkw pomidzy moduami .............................. 176
Wytyczna 63. Interfejs moduu powinien korzysta z dostatecznie przenonych typw ........... 179
Rozdzia 8. Szablony i programowanie uoglnione ...................................................... 183
Wytyczna 64. cz zalety polimorfizmu dynamicznego i statycznego...................................... 184
Wytyczna 65. Jeli umoliwia dostosowywanie, to celowo i jawnie ....................................... 187
Wytyczna 66. Wystrzegaj si specjalizacji szablonw funkcji .................................................. 192
Wytyczna 67. Unikaj przypadkowych uszczegowie kodu w zamierzeniu uniwersalnego .... 195
Rozdzia 9. Wyjtki i obsuga bdw ........................................................................ 197
Wytyczna 68. Asercje wietnie dokumentuj wewntrzne zaoenia i niezmienniki kodu........ 198
Wytyczna 69. Ustal racjonalne zasady obsugi bdw i cile ich przestrzegaj........................ 201
Wytyczna 70. Odrniaj bdy od stanw nimi nie bdcych.................................................... 204
Wytyczna 71. Projektuj i pisz kod wolny od bdw................................................................. 208
Wytyczna 72. Bdy najlepiej zgasza za pomoc wyjtkw ................................................... 212
Wytyczna 73. Zgaszaj wartoci, przechwytuj referencje .......................................................... 217
Wytyczna 74. Bdy trzeba waciwie sygnalizowa, obsugiwa i tumaczy.......................... 219
Wytyczna 75. Unikaj specyfikacji wyjtkw............................................................................. 221
Rozdzia 10. Kontenery STL...................................................................................... 225
Wytyczna 76. Domylnie stosuj kontener vector. Inne dobieraj odpowiednio do potrzeb......... 226
Wytyczna 77. Stosuj vector w miejsce tablic............................................................................. 229
Wytyczna 78. W wymianie danych z interfejsami spoza C++ stosuj vector (i string::c_str) ..... 231
Wytyczna 79. W kontenerach najlepiej przechowywa albo wartoci,
albo inteligentne wskaniki do nich.................................................................... 233
Wytyczna 80. Sekwencj najlepiej rozwija metod push_back ............................................... 235
Wytyczna 81. Od operacji na pojedynczych elementach lepsze s operacje na sekwencjach.... 237
Wytyczna 82. Do faktycznego przycinania kontenerw i faktycznego usuwania elementw
najlepiej stosowa sprawdzone idiomy ............................................................... 239

Spis treci
Rozdzia 11. Algorytmy STL ...................................................................................... 241
Wytyczna 83. Korzystaj z udogodnie kontrolnych implementacji STL................................... 242
Wytyczna 84. Algorytmy s lepsze od ptli............................................................................... 245
Wytyczna 85. Wybieraj z STL waciwe algorytmy wyszukiwania .......................................... 249
Wytyczna 86. Wybieraj z STL odpowiednie algorytmy sortowania.......................................... 251
Wytyczna 87. Predykaty powinny by funkcjami czystymi ...................................................... 254
Wytyczna 88. W wywoaniach algorytmw miejsce funkcji powinny zajmowa
obiekty funkcyjne ............................................................................................... 256
Wytyczna 89. Zadbaj o poprawno obiektw funkcyjnych...................................................... 258
Rozdzia 12. Bezpieczestwo typw .......................................................................... 261
Wytyczna 90. Zamiast przeczania pomidzy typami stosuj polimorfizm ............................... 262
Wytyczna 91. Polegaj na typach, nie na reprezentacjach........................................................... 265
Wytyczna 92. Unikaj rzutowania reinterpret_cast ..................................................................... 267
Wytyczna 93. Unikaj rzutowania static_cast na wskanikach ................................................... 269
Wytyczna 94. Zachowuj const przy rzutowaniu ........................................................................ 271
Wytyczna 95. Nie korzystaj z rzutowania znanego z C ............................................................. 273
Wytyczna 96. Nie wolno brutalnie kopiowa obiektw typw innych ni proste POD............. 276
Wytyczna 97. Unie nie su do reinterpretacji reprezentacji .................................................... 278
Wytyczna 98. Nie stosuj zmiennych list argumentw (trzykropkw)........................................ 280
Wytyczna 99. Nie korzystaj z niepoprawnych obiektw i niebezpiecznych funkcji.................. 282
Wytyczna 100. Nie wykorzystuj tablic polimorficznie .............................................................. 284
Dodatek A Bibliografia ............................................................................................. 287
Dodatek B Podsumowanie ....................................................................................... 295
Skorowidz ............................................................................................................... 313

Rozdzia 2.

Styl projektowy
Gupcy ignoruj zoono. Pragmatycy od niej cierpi. Niektrzy potrafi jej
unika. Geniusze za j eliminuj
Alan Perlis

Ale wiedziaem te, i zapomniaem, o powiedzeniu Hoarea o tym, e


przedwczesna optymalizacja to rdo wszelakiego za w programowaniu
Donald Knuth, z The Errors of Tex [Knuth98]

Trudno w peni rozdzieli styl kodowania od stylu projektowania. Dlatego w tym rozdziale postaralimy si uwzgldni te wytyczne, ktre umykaj uwadze, kiedy mowa
o waciwym kodowaniu.
Niniejszy rozdzia powicony jest zasadom i praktykom dajcym si zastosowa szerzej
ni do pojedynczej klasy czy funkcji. Klasycznym przykadem jest zachowanie rwnowagi pomidzy prostot a przejrzystoci kodu (patrz wytyczna 6.) czy unikanie przedwczesnej optymalizacji (wytyczna 8.), a take przedwczesnej pesymizacji (wytyczna
9.). Owe trzy wytyczne mona stosowa nie tylko na poziomie kodowania funkcji, ale
rwnie na poziomie wyszym, obejmujcym kwestie projektowania klas i moduw
oraz decyzje co do architektury aplikacji (owe wytyczne obowizuj wszystkich programistw uwaajcy inaczej powinni zerkn raz jeszcze na stwierdzenie Donalda
Knutha i sprawdzi, kog on z kolei cytowa).
Wiele wytycznych z tego i nastpnych rozdziaw odnosi si do aspektw zarzdzania zalenociami kamienia wgielnego inynierii oprogramowania i rwnoczenie
zagadnienia powracajcego w tej ksice wielokrotnie. Pomyl przez chwil nad dowoln
dobr technik inynierii oprogramowania dowoln dobr technik. Jakakolwiek
by ona bya, w ten czy inny sposb polega na redukcji zalenoci. Dziedziczenie?
Zmniejsza zaleno kodu pisanego pod ktem klasy bazowej od klas pochodnych.
Redukcja liczby zmiennych globalnych? To jawna redukcja rozcigych zalenoci
w stosunku do widocznych rozlegle danych. Abstrakcja? To eliminacja zalenoci
pomidzy kodem manipulujcym pojciami a kodem implementujcym te pojcia.

28

Rozdzia 2. Styl projektowy

Ukrywanie informacji (hermetyzacja)? Czyni kod uytkownika mniej zalenym od


szczegw implementacyjnych danej jednostki. Waciwa waga przykadana do
zarzdzania zalenociami przejawia si te w unikaniu wsplnych danych o stanie
(wytyczna 10.), zaleceniu hermetyzacji informacji (wytyczna 11.) i wielu innych.
Naszym zdaniem najcenniejsz porad w tym rozdziale zawiera wytyczna 6. Przede
wszystkim poprawno, prostota i przejrzysto. W istocie, nic doda, nic uj.

Wytyczna 5. Jednej jednostce jedno zadanie

29

Wytyczna 5.
Jednej jednostce jedno zadanie
Wytyczna 5. Jednej jednostce jedno zadanie

Streszczenie
Lepiej robi jedn rzecz, a dobrze. Wedle tej zasady naleaoby nadawa poszczeglnym
jednostkom programu (zmiennym, klasom, funkcjom, przestrzeniom nazw, moduom,
bibliotekom) jasno okrelone i rwnoczenie ograniczone zadania. W miar rozrostu
jednostki zakres jej zada w sposb naturalny si zwiksza, nie powinien jednak obejmowa coraz to nowych obszarw.

Uzasadnienie
Powiada si, e dobry pomys na biznes to taki, ktry mona uj w jednym zdaniu.
Podobna regua tyczy si jednostek programu, ktre powinny mie konkretne i jasno
okrelone zadania.
Jednostka odpowiadajca za wicej ni jedno zadanie jest zwykle nieproporcjonalnie
trudniejsza w uyciu ni zestaw jednostek prostszych o mniejszej odpowiedzialnoci,
poniewa jej implementacja obejmuje wicej ni tylko sum intelektualnego wysiki,
zoonoci i bdw w stosunku do jej poszczeglnych skadowych funkcjonalnych.
Taka jednostka jest wiksza (zwykle niepotrzebnie) i trudniejsza do stosowania i ponownego wykorzystania. Zwykle te jednostka taka udostpnia okrojone interfejsy
kadego z zada okrojone z racji czciowego pokrywania si rnych obszarw
zadaniowych i rozmycia wizji implementacji kadego z nich.
Jednostki o czonych zadaniach s zwykle trudniejsze z punktu widzenia projektowego i implementacyjnego. Mnoga odpowiedzialno oznacza wtedy zazwyczaj
mnog osobowo kombinacyjn liczb rnych moliwych stanw i zachowa.
Dlatego zalecamy stosowanie prostych i jasnych, jednozadaniowych funkcji (patrz te
wytyczna 39.), prostych klas oraz moduw o cile ograniczonym zakresie zada.
Abstrakcje wyszego poziomu naley konstruowa z prostszych abstrakcji niszego
poziomu. Nie warto w adnym razie grupowa wielu abstrakcji niskiego poziomu
w wikszym i bardziej zoonym konglomeracie niskiego poziomu. Implementacja
zoonego zachowania na bazie szeregu prostszych jest bowiem atwiejsza ni implementacja odwrotna.

Przykady
Przykad 1. wywoanie . W standardowym jzyku C  to jeden
z typowych przykadw uomnego projektu. Funkcja  ma zdecydowanie za
duo zada: dla wskanika pustego przydziela pami, dla zerowego argumentu rozmiaru zwalnia wskazywan pami, za dla pozostaych wartoci argumentw zmienia

30

Rozdzia 2. Styl projektowy

rozmiar przydzielonej pamici, przy czym nowy przydzia w czci pokrywa si w przestrzeni adresowej z poprzednim, a jeli jest to niemoliwe, wykonywany jest zupenie
nowy przydzia. Trudno o lepszy przykad wadliwego projektu funkcji.
Przykad 2. 

 . Klasa
 

  to w standardzie jzyka C++
rwnie niesawny przykad monolitycznego projektu klasy. Klasa ta zostaa rozepchana
zbyt wielk liczb (nawet uytecznych i przyjemnych) dodatkw przez to, cho
aspiruje do miana kontenera, nie jest nim do koca, nie moe bowiem wybra pomidzy
indeksowaniem a iteracj i rwnoczenie powiela wiele standardowych algorytmw, nie
zostawiajc za to za wiele miejsca na rozszerzenia (patrz przykad do wytycznej 44.).

rda
[Henney02a]  [Henney02b]  [McConnell93] 10.5  [Stroustrup00] 3.8, 4.9.4,
23.4.3.1  [Sutter00] 10, 12, 19, 23  [Sutter02] 1  [Sutter04] 3740

Wytyczna 6. Przede wszystkim poprawno, prostota i przejrzysto

31

Wytyczna 6.
Przede wszystkim poprawno,
prostota i przejrzysto
Wytyczna 6. Przede wszyst kim poprawno, pros tota i przejrzysto

Streszczenie
Wedle zasady KISS (Keep It Simple Software parafraza Keep It Simple, Stupid, czyli
jak najprociej, gupku) im prociej, tym lepiej. Proste jest niemal zawsze lepsze od
zoonego. Przejrzyste za jest lepsze od niejasnego. No i bezpieczne jest lepsze od
niebezpiecznego (patrz wytyczne 83. i 99.).

Uzasadnienie
Trudno przeceni znaczenie prostoty projektu i przejrzystoci kodu. Programista tworzcy kod czytelny i zrozumiay bdzie cieszy si wdzicznoci ze strony przyszego
opiekuna tego kodu. Powiniene przy tym pamita, e opiek nad kodem czsto sprawuj jego twrcy i, majc to na uwadze, dba o swoje samopoczucie w przyszoci.
Std klasyczne prawdy w rodzaju:
Programy musz by pisane tak, aby day si czyta przez ludzi, ewentualnie
od czasu do czasu wykonywa przez maszyny
Harold Abelson i Gerald Jay Sussman

Pisz programy przede wszystkim dla ludzi, potem dla komputerw


Steve McConnell

Najtaszymi, najszybszymi i najbardziej niezawodnymi komponentami


systemu komputerowego s te, ktrych w nim nie ma
Gordon Bell

Owe brakujce komponenty s rwnie najdokadniejsze (nigdy si nie myl),


najbezpieczniejsze (nie da si do nich wama) i najprostsze w projektowaniu,
dokumentowaniu, testowaniu i konserwacji. Nie sposb przeceni prostoty
projektowej
Jon Bentley

Wiele wytycznych prezentowanych w tej ksice ma ukierunkowa czytelnika na kod


i projekty atwe w modyfikacji; przejrzysto i zrozumiao to najbardziej podane
cechy prostych w konserwacji i rozbudowie programw. Trudno zmieni to, czego si
nie rozumie.
Najsilniejsza sprzeczno zachodzi pomidzy przejrzystoci kodu a jego optymalizacj (patrz wytyczne 7., 8. i 9.). Kiedy (a nie jeeli!) staniesz w obliczu pokusy przedwczesnej optymalizacji kodu pod ktem wydajnoci, a kosztem przejrzystoci, przypomnij sobie sens wytycznej 8. duo atwiej jest przyspieszy poprawny program,
ni poprawi szybki.

32

Rozdzia 2. Styl projektowy

Unikaj wic zaukw jzyka programowania i stosuj zawsze najprostsze z efektywnych technik.

Przykady
Przykad 1. unikaj zbdnego (cho efektownego) przeciania operatorw. Jedna
z (niepotrzebnie) udziwnionych bibliotek graficznego interfejsu uytkownika wymagaa, celem dodania do widgetu  elementu sterujcego , napisania wyraenia 
(zobacz wytyczn 26.).
Przykad 2. w roli parametrw konstruktorw stosuj zmienne nazwane, nie tymczasowe. Pozwala to na uniknicie niejednoznacznoci deklaracji. Pozwala te na lepsz
prezentacj zadania realizowanego przez kod i tym samym uproszczenie konserwacji
programu. Jest te niejednokrotnie bezpieczniejsze (zobacz wytyczne 13. i 31.).

rda
[Abelson96]  [Bentley00] 4  [Cargill92] pp.9193  [Cline99] 3.0506  [Constantine95] 29  [Keffer95] p. 17  [Lakos96] 9.1, 10.2.4  [McConnell93]  [Meyers01] 47  [Stroustrup00] 1.7, 2.1, 6.2.3, 23.4.2, 23.4.3.2  [Sutter00] 4041,
46  [Sutter04] 29

Wytyczna 7. Jak i kiedy kodowa z uwzgldnieniem skalowalnoci

33

Wytyczna 7.
Jak i kiedy kodowa
z uwzgldnieniem skalowalnoci
Wytyczna 7. Jak i kiedy kodowa z uw zg ldnieniem s kalowalno ci

Streszczenie
Wystrzegaj si wybuchowego rozrostu kodu unikajc przedwczesnej optymalizacji,
kontroluj rwnoczenie zoono asymptotyczn kodu. Algorytmy dziaajce na danych
uytkownika powinny cechowa si liniow zoonoci, czyli liniowym przyrostem
czasu wykonania przy przyrocie iloci przetwarzanych danych. Tam, gdzie optymalizacja okae si niezbdna, i zwaszcza gdy zostanie wymuszona zwikszeniem iloci
danych, skupiaj si raczej na uzyskaniu sensownej zoonoci obliczeniowej algorytmu
ni na urywaniu tu i wdzie po jednej instrukcji maszynowej.

Uzasadnienie
Niniejsza wytyczna ilustruje punkt rwnowagi pomidzy wytycznymi 8. i 9. (unikaj
przedwczesnej optymalizacji i unikaj przedwczesnej pesymizacji). Z tego wzgldu t
wytyczn do ciko sformuowa tak, aby nie myli jej sensu z sensem wytycznej 8.
Ale do rzeczy.
Oto to zagadnienia: pojemnoci pamici ulotnych i dyskw twardych rosn wykadniczo; w latach od 1988 do 2004 pojemno dyskw rosa o 112 procent rocznie (co
daje w cigu dekady wzrost blisko 1900-krotny), podczas gdy prawo Moorea zakada
przyrost zaledwie 59-procentowy (100-krotny w cigu dekady). Jedn z konsekwencji
tej dynamiki jest to, e czynnoci realizowane dzi przez kod mog jutro obejmowa
znacznie wiksze iloci danych znacznie wiksze. Jeli stosowane do ich przetwarzania algorytmy bd cechowa si kiepsk asymptotyczn zoonoci obliczeniow,
wczeniej czy pniej przestan si nadawa do wykorzystywania nawet na najwydajniejszych systemach komputerowych to tylko kwestia iloci danych, ktrymi te
algorytmy bd karmione.
Obrona przed t wtpliw karier algorytmu polega na unikaniu wbudowywania
w projekt takich elementw, ktre w obliczu koniecznoci przetwarzania plikw
wikszych ni dzi przewidywane (wikszych baz danych, wikszej liczby pikseli,
wikszej liczby okien, wikszych szybkoci transmisji) oka si jego wskimi gardami.
W przypadku biblioteki standardowej jzyka C++ elementami zabezpieczajcymi
przyszo s choby gwarancje co do zoonoci obliczeniowej algorytmw i operacji na kontenerach.
Oto wniosek: nie powinnimy przedwczenie optymalizowa programu przez zastosowanie w nim mniej przejrzystego algorytmu, jeli spodziewany przyrost iloci przetwarzanych danych nie jest pewny. Ale rwnie niewskazana jest przedwczesna pesymizacja

34

Rozdzia 2. Styl projektowy

algorytmu, polegajca na zamykaniu oczu na jego niekorzystn asymptotyczn zoono


obliczeniow (rozumian jako koszt wykonania oblicze w funkcji liczby przetwarzanych elementw).
Uzasadnienie to mona by podzieli na dwie czci. Po pierwsze, nawet przed poznaniem
docelowego woluminu danych i oszacowaniem, czy jego rozmiar bdzie mia istotny
wpyw na wydajno danego kodu, naley unika takich algorytmw operujcych na
danych uytkownika, ktre kiepsko si skaluj, chyba e dziki zastosowaniu takiego
algorytmu zyskamy na czytelnoci albo przejrzystoci kodu (patrz wytyczna 6.). Zbyt
czsto programici s jednak zaskakiwani pisz kod z myl o tym, e nigdy nie
przyjdzie mu operowa na olbrzymich zbiorach danych (i w dziewiciu przypadkach na
dziesi nie myl si). Jednak w tym jednym na dziesi przypadku wpadaj w puapk
braku wydajnoci zdarzao si to nam i z pewnoci zdarzy si (prdzej czy pniej)
rwnie czytelnikowi. Z pewnoci mona wtedy opracowa poprawk i dostarczy
j klientom, ale znacznie lepiej byoby unikn tego rodzaju zakopotania i wysiku.
Wic, jeli pozostae cechy kodu (w tym czytelno i przejrzysto kodu) na tym nie
ucierpi, od pocztku warto:
 w miejsce tablic o staych rozmiarach stosowa elastyczne, przydzielane

dynamicznie struktury danych. Statyczne tablice wiksze, ni kiedykolwiek


bd potrzebne to obraza dla poprawnoci i bezpieczestwa programu
(patrz wytyczna 77.). S one do zaakceptowania jedynie wtedy, kiedy rozmiar
danych jest faktycznie ustalony i stay!
 zna faktyczn zoono algorytmu szczeglnie grone s takie algorytmy,

ktrych zoono obliczeniowa jest z pozoru liniowa, ale ktre wywouj


wewntrznie inne operacje o liniowej zoonoci, dajc w efekcie zoono
kwadratow (przykad w wytycznej 81.).
 wszdzie tam, gdzie to moliwe, preferowa algorytmy o zoonoci liniowej

i lepszej najlepsza byaby staa zoono w funkcji liczby elementw, jak


w przypadku operacji 
  na kontenerach albo operacji wyszukiwania
w tabeli haszowanej (patrz wytyczne 76. i 80.). Nieza jest zoono
logarytmiczna (O(logN)), osigana midzy innymi w operacjach na kontenerach

 i  czy operacjach   i   z iteratorami swobodnego


dostpu (patrz wytyczne 76., 85. i 86.). Do zaakceptowania jest zoono
liniowa (O(N)), jak w operacjach   
 albo algorytmie  
(zobacz wytyczne 76., 81. i 84.).
 tam, gdzie to zasadne, unika algorytmw o zoonoci gorszej ni liniowa

na przykad w obliczu algorytmu o zoonoci rzdu O(N log N) albo O(N2)


warto spdzi troch czasu na poszukiwaniu rozwiza o mniejszej zoonoci
obliczeniowej, celem uniknicia puapki wydajnociowej wynikajcej
z przewidywanej dynamiki wzrostu iloci przetwarzanych danych. Z tego wanie
powodu w wytycznej 81. doradzamy preferowanie metod przetwarzajcych
cae sekwencje elementw (metody te cechuj si zoonoci liniow)
zamiast wywoa ich odpowiednikw przetwarzajcych pojedyncze elementy
(poniewa w przypadku wywoania w operacji o liniowej zoonoci innej
takiej operacji otrzymujemy zoono kwadratow; patrz te przykad 1.
w ramach wytycznej 81.).

Wytyczna 7. Jak i kiedy kodowa z uwzgldnieniem skalowalnoci

35

 nie stosowa nigdy algorytmu wykadniczego, chyba e nie ma innego

w obliczu koniecznoci zastosowania algorytmu o wykadniczej zoonoci


obliczeniowej nie wolno szczdzi wysiku na poszukiwania alternatywy,
poniewa w tym przypadku nawet nieznaczne zwikszenie rozmiaru
przetwarzanych danych stanowi istn barier wydajnociow.
Z drugiej strony, po wykonaniu pomiarw dowodzcych, e optymalizacja jest zasadna
i wana, zwaszcza z uwagi na rosnce iloci danych do przetworzenia, powinnimy
skupi si na redukcji zoonoci obliczeniowej, nie prbujc szuka ratunku w pomniejszych optymalizacjach, urywajcych tu i wdzie po jednej czy par instrukcji
maszynowych.
Reasumujc wszdzie tam, gdzie to moliwe, korzystaj z algorytmw o zoonoci
liniowej albo lepszej. Staraj si unika algorytmw o zoonoci wielomianowej; jak
ognia wystrzegaj si za algorytmw o zoonoci wykadniczej.

rda
[Bentley00] 6, 8, dod. A  [Cormen01]  [Kernighan99] 7  [Knuth97a]  [Knuth97b]
 [Knuth98]  [McConnell93] 5.14, 10.6  [Murray93] 9.11  [Sedgewick98]
 [Stroustrup00] 17.1.2

36

Rozdzia 2. Styl projektowy

Wytyczna 8.
Wystrzegaj si
przedwczesnej optymalizacji
Wytyczna 8. Wystrzegaj si przedwczesnej optymalizacji

Streszczenie
Nie bodzie si chtnego wierzchowca. Przedwczesna optymalizacja jest rwnie uzaleniajca, jak bezproduktywna, pierwsza regua optymalizacji mwi bowiem: zaniechaj
jej. Druga regua (dla ekspertw) mwi za: powstrzymaj si jeszcze. Jedn optymalizacj trzeba poprzedzi dwoma pomiarami dowodzcymi jej koniecznoci.

Uzasadnienie
We wstpie do [Stroustrup00] 6 znajdziemy wietne cytaty:
Przedwczesna optymalizacja to rdo wszelkiego za
Donald Knuth (cytujcy z kolei z Hoarea)

Z drugiej strony, nie moemy ignorowa efektywnoci


Jon Bentley

Hoare i Knuth maj (jak zwykle) racj (patrz wytyczna 6. i niniejsza). Tak jak i Bentley
(wytyczna 9.).
Przedwczesn optymalizacj zdefiniowalibymy jako zwikszanie zoonoci projektu
albo kodu, a przez to zmniejszenie ich czytelnoci, w imi wydajnoci, ktrej potrzeba
zwikszenia nie zostaa jeszcze dowiedziona (na przykad pomiarami i porwnaniem
ich wynikw z zaoonymi celami) jako taka optymalizacja ta nie wnosi do projektu
adnych korzyci. Czsto przedwczesna i niepoparta pomiarami optymalizacja, mimo
woonego w ni wysiku, nie daje dosownie adnego efektu wydajnociowego.
Warto wic zapamita, e:
Znacznie atwiej przyspieszy poprawny program, ni poprawi szybki!
Nie naley wic od pocztku skupia si na szybkoci kodu w pierwszej kolejnoci
powinna nas interesowa raczej jego przejrzysto i czytelno (zgodnie z wytyczn 6.).
W kodzie czytelnym atwiej o poprawno, zrozumienie jego dziaania, wprowadzanie
poprawek i zmian, i wreszcie optymalizacj. Komplikacje, nieodzowne dla optymalizacji,
zawsze mona wprowadzi pniej i tylko wtedy, gdy s niezbdne.
Przedwczesna optymalizacja czsto nie daje spodziewanych efektw z dwch gwnych
powodw. Po pierwsze, programici stale myl si w swoich szacunkach co do szybkoci
danego kodu i typowania jego wskich garde. Dotyczy to nas, autorw tej ksiki,
i dotyczy najprawdopodobniej rwnie Ciebie. Wspczesne komputery realizuj

Wytyczna 8. Wystrzegaj si przedwczesnej optymalizacji

37

potwornie skomplikowany model maszyny obliczeniowej, obejmujcej niekiedy kilka


czy kilkanacie potokw przetwarzania obsugiwanych wspbienie, z rozbudowan
hierarchi pamici podrcznych, predykcj rozgazie programu i wszystko to
w jednym procesorze! Kompilatory, bazujce na tych moliwociach, rwnie staraj
si transformowa kod rdowy tak, aby wynikowy kod maszynowy jak najlepiej wpasowa si w architektur procesora. Dopiero na bazie kompilatora operuje programista,
i jeli dla poparcia swoich decyzji ma jedynie niecise szacunki i swoj intuicj, to
szansa, e wprowadzane przez niego mikrooptymalizacje bd miay znaczcy wpyw
na program, jest prawie adna. Jak wida, optymalizacj naley koniecznie poprzedzi
odpowiednimi pomiarami. Dopki nie uda si w ten sposb dowie istotnej potrzeby
optymalizacji, naley skupi si na kwestii najwaniejszej, czyli tworzeniu zrozumiaego i czytelnego kodu (jeli kto zada optymalizacji programu, daj dowodw jej
koniecznoci).
Dalej, we wspczesnych programach efektywno znacznej czci operacji nie jest ju
ograniczana wydajnoci procesora. Znakomita cz z nich znacznie bardziej ograniczona jest efektywnoci dostpu do pamici, szybkoci transmisji w sieci, czasem
dostpu do napdw pamici masowych, czasem oczekiwania na odpowied serwera
WWW czy serwera baz danych. Wobec tego optymalizacja kodu aplikacji wykonujcej wszystkie te operacje spowoduje jedynie, e aplikacja bdzie szybciej na nie czeka.
A to oznaczaoby, e programista zmarnowa sporo czasu, ulepszajc to, co ulepszenia
nie wymagao, zamiast ulepsza to, co faktycznie kuleje.
Oczywicie nadejdzie wreszcie ten dzie, kiedy kod trzeba bdzie nieco zoptymalizowa. W takim przypadku w pierwszej kolejnoci naley szuka ratunku w zmniejszeniu zoonoci obliczeniowej algorytmu (wytyczna 7.) i rwnoczenie prbowa
hermetyzowa i ogranicza zasig optymalizacji (na przykad do funkcji albo klasy
patrz wytyczne 5. i 11.) oraz koniecznie opatrzy kod stosownymi komentarzami,
wyjaniajcymi biece potrzeby optymalizacji i odnoszcymi si do zastosowanych
algorytmw.
Powszechnym bdem pocztkujcych programistw jest pisanie z dum! nowego
kodu z obsesyjn myl o jego jak najwikszej wydajnoci, kosztem czytelnoci i zrozumiaoci. Najczciej efektem takiej pracy jest kod spaghetti, ktry nawet jeli
poprawny utrudnia analiz i ewentualne modyfikacje (wytyczna 6.)
Nie jest przedwczesn optymalizacj przekazywanie argumentw i wartoci zwracanych
przez referencj (patrz wytyczna 25.), preferowanie przedrostkowych wersji operatorw
inkrementacji (wytyczna 28.) i tym podobne idiomy, ktre w naturalny sposb wpasowuj si w tok pracy programisty. Nie s to optymalizacje przedwczesne, poniewa nie
komplikuj kodu pozwalaj za to unikn przedwczesnej jego pesymizacji (patrz
wytyczna 9.).

Przykady
Przykad ironia  . Oto ilustracja ukrytego kosztu przedwczesnej mikrooptymalizacji. Ot narzdzia profilujce su do tego, aby na podstawie licznika wywoa
funkcji informowa programist o tym, ktre z funkcji nadaj si do rozwijania w miejscu

38

Rozdzia 2. Styl projektowy

wywoania, a nie zostay jako takie oznaczone. Niestety, nawet najlepsze takie narzdzie
nie bdzie w stanie wskaza takich funkcji, ktre zostay oznaczone jako rozwijane
w miejscu wywoania (), cho nie powinny nie bdzie bowiem adnej moliwoci okrelenia liczby wywoa teje funkcji w kodzie wynikowym. Zbyt czsto
programici w imi optymalizacji decyduj si na rozwijanie wielu funkcji w miejscu
wywoania, co mao kiedy przynosi rzeczywiste korzyci (zakadajc, e kompilator
nie ignoruje zupenie sowa  patrz [Sutter00], [Sutter02] czy [Sutter04]).

Wyjtki
Twrca kodu biblioteki ma zadanie utrudnione o tyle, e nie bardzo moe przewidzie, ktre z jej elementw bd w przyszoci wykorzystywane w kodzie czuym na
wydajno wykonania. Ale nawet twrcy bibliotek powinni poprzedzi optymalizacj
testami na szerokiej bazie klientw-uytkownikw biblioteki.

rda
[Bentley00] 6  [Cline99] 13.0109  [Kernighan99] 7  [Lakos96] 9.1.14
 [Meyers97] 33  [Murray93] 9.910, 9.3  [Stroustrup00] 6 (wprowadzenie)
 [Sutter00] 30, 46  [Sutter02] 12  [Sutter04] 25

Wytyczna 9. Wystrzegaj si przedwczesnej pesymizacji

39

Wytyczna 9.
Wystrzegaj si
przedwczesnej pesymizacji
Wytyczna 9. Wystrzegaj si przedwczesnej pesymizacji

Streszczenie
Jeli pozostae czynniki (jak choby czytelno kodu czy jego zoono) nie ucierpi
na tym, to pewne wzorce projektowe, praktyki programistyczne i tym podobne idiomy programistyczne naley uzna za o tyle naturalne, e ich wprowadzenie nie wie
si dla programisty ze zwikszonym wysikiem i niejako same wychodz spod jego
palcw. Nie uznajemy ich za przedwczesn optymalizacj, a raczej za unikanie niepotrzebnej pesymizacji.

Uzasadnienie
Unikanie przedwczesnej pesymizacji nie moe oznacza wzrostu efektywnoci, jeli
osiga si go znacznym kosztem. Przedwczesn pesymizacj bdziemy rozumie jako
niepotrzebne potencjalne ograniczenia efektywnoci, takie jak:
 definiowanie parametrw jako przekazywanych przez warto tam,

gdzie mona by je przekazywa przez referencj (patrz wytyczna 25.);


 stosowanie przyrostkowych wersji operatorw inkrementacji tam,

gdzie mona by zastosowa wersje przedrostkowe (patrz wytyczna 28.);


 wykonywanie przypisa w ciele konstruktora, a nie w licie inicjalizacyjnej

(patrz wytyczna 48.).


Nie jest rwnie przedwczesn optymalizacj redukowanie liczby niepotrzebnych
tymczasowych kopii obiektw, zwaszcza w ptlach wewntrznych i zwaszcza wtedy,
kiedy ta redukcja nie wpywa na zoono kodu. Co prawda wytyczna 18. zachca do
deklarowania zmiennych jak najbardziej lokalnie, wskazuje jednak na wyjtki, w obliczu
ktrych lepiej deklaracj zmiennej przenie poza ptl. W wikszoci przypadkw
takie przesunicia nie maj znaczenia dla przejrzystoci kodu, a nawet pozwalaj na
lepsze uwidocznienie operacji wykonywanych w ptli i wyodrbnienie niezmiennikw
tej ptli. Oczywicie najlepiej w miejsce jawnych ptli stosowa algorytmy STL (patrz
wytyczna 84.).
Przejrzysto godzi si z efektywnoci przez stosowanie abstrakcji i bibliotek (patrz
wytyczne 11. i 36.). Na przykad, korzystajc ze standardowych elementw bibliotecznych jzyka C++ (kontenerw  , 
, , algorytmw
 czy  ), projektowanych i implementowanych przez wiatowej klasy ekspertw, nie tylko zwikszamy
przejrzysto kodu, ale niejednokrotnie znacznie go przyspieszamy.

40

Rozdzia 2. Styl projektowy

Unikanie przedwczesnej pesymizacji jest szczeglnie istotne dla twrcw bibliotek.


Zazwyczaj nie maj oni moliwoci przewidzenia wszystkich kontekstw, w ktrych ich
kod zostanie wykorzystany, powinni wic przesun nieco rodek cikoci w kierunku
efektywnoci i moduowoci (przydatnoci do ponownego wykorzystania), wystrzegajc si jednak przesady w zwikszaniu efektywnoci, jeli przyrost ten odczuje jedynie
niewielki odsetek potencjalnych uytkownikw biblioteki. Wyznaczenie punktu cikoci
to oczywicie zadanie programisty, ale zgodnie z wytyczn 7., nacisk naley pooy
raczej na uzyskanie rozsdnej skalowalnoci, ni na mikrooptymalizacje polegajce na
urywaniu pojedynczych cykli procesora.

rda
[Keffer95] pp. 1213  [Stroustrup00] 6 (wprowadzenie)  [Sutter00] 6

Wytyczna 10. Minimalizuj ilo danych globalnych i wspuytkowanych

41

Wytyczna 10.
Minimalizuj ilo danych globalnych
i wspuytkowanych
Wytyczna 10. Min imalizuj ilo danych globalnych i wspu yt kowanych

Streszczenie
Wspuytkowanie oznacza rywalizacj naley wic unika danych wsplnych,
zwaszcza globalnych. Zwikszaj one powizanie kodu kosztem atwoci konserwacji,
a niejednokrotnie i wydajnoci.

Uzasadnienie
Niniejsza wytyczna jest pewnym uoglnieniem wytycznej 18.
Chodzi o unikanie stosowania danych (wizanych zewntrznie) o zasigu pokrywajcym si z zasigiem przestrzeni nazw albo wystpujcych w postaci statycznych skadowych klas. Komplikuj one logik programu i ucilaj zwizki pomidzy rnymi
(i, co gorsza, odlegymi) elementami programu. Wspuytkowanie danych zmniejsza
moliwoci testowania jednostki programu, poniewa poprawno kodu odwoujcego
si do takich danych jest mocno uzaleniona od historii zmian tych danych i warunkw
wykonania dalszego, nieznanego bliej kodu, ktry si pniej do tych danych odwouje.
Nazwy obiektw w globalnej przestrzeni nazw zamiecaj t przestrze, zwikszajc
ryzyko kolizji nazw.
Jeli ju trzeba zastosowa obiekt globalny, obiekt o zasigu pokrywajcym si z zasigiem przestrzeni nazw albo statyczny obiekt klasy, naley starannie go zainicjalizowa. Porzdek inicjalizacji tego rodzaju obiektw w rnych jednostkach kompilacji
jest niezdefiniowany i aby zapewni jego poprawno, trzeba wdroy specjalne techniki
(odsyamy do rde). Reguy kolejnoci inicjalizacji s subtelne lepiej unika koniecznoci zagbiania si w te subtelnoci, a jeli jest to niemoliwe, warto je przynajmniej
dobrze pozna i starannie stosowa.
Obiekty o zasigu przestrzeni nazw, skadowe statyczne oraz obiekty dzielone przez
wiele wtkw albo procesw redukuj zrwnoleglenie w rodowiskach wielowtkowych
i wieloprocesorowych i s czstymi wskimi gardami wydajnoci i skalowalnoci
(patrz wytyczna 7.). Optuj za zasad jak najmniej wsplnego zamiast danych
wsplnych (wspuytkowanych) stosuj komunikacj pomidzy uytkownikami danych
(np. kolejki komunikatw).
Cao sprowadza si za do unikania cisych zalenoci i do minimalizacji interakcji
pomidzy klasami (patrz [Cargill92]).

42

Rozdzia 2. Styl projektowy

Wyjtki
Za wyjtki mona uzna takie mechanizmy, jak obiekty  ,  i , implementowane celowo jako obiekty globalne. Dalej, np. fabryka (generator obiektw wedug
wzorca projektowego Factory) musi utrzymywa rejestr funkcji do wywoania celem
utworzenia obiektu danego typu i zwykle w programie znajduje si jeden taki rejestr
(powinien by on jednak obiektem wewntrznym fabryki, a nie wspuytkowanym
obiektem globalnym; patrz wytyczna 11.).
Kod zakadajcy wspuytkowanie obiektw przez wiele wtkw powinien zawsze szeregowa wszelkie odwoania do owych obiektw (patrz wytyczna 12. oraz [Sutter04c]).

rda
[Cargill92] pp.126136, 169173  [Dewhurst03] 3 [Lakos96] 2.3.1  [McConnell93] 5.14  [Stroustrup00] C.10.1  [Sutter00] 47  [Sutter02] 16, dod. A
 [Sutter04c]  [SuttHysl03]

Wytyczna 11. Ukrywaj informacje

43

Wytyczna 11.
Ukrywaj informacje
Wytyczna 11. U krywaj informacje

Streszczenie
Nie eksponuj wewntrznych informacji jednostki stanowicej abstrakcj.

Uzasadnienie
Minimalizacja zalenoci pomidzy wywoujcym, manipulujcym pewn abstrakcj,
a wywoywanym, czyli implementacj tej abstrakcji, wymaga ukrywania danych wewntrznych tej implementacji. W przeciwnym razie wywoujcy moe si do owych
informacji odwoywa (albo, co gorsza, manipulowa nimi) z pominiciem implementacji abstrakcji. Eksponowa naley raczej sam abstrakcj (nawet, jeli ma ona jedynie
posta akcesorw
 - ), a nie jej dane.
Ukrywanie informacji zmniejsza koszt projektu, skraca harmonogram realizacji lub
(i) ryzyko jego przekroczenia, dziki:
 ograniczaniu zasigu zmian ukrywanie informacji redukuje efekt domina

w przypadku zmian, a wic redukuje ich koszt.


 wzmocnieniu niezmiennikw przez ograniczanie kodu odpowiedzialnego

za zachowanie (albo i zamanie) niezmiennikw programu (patrz wytyczna 41.).


Nie powinno si eksponowa danych adnej jednostki, ktra stanowi abstrakcj (patrz
te wytyczna 10.), konkretne dane s bowiem charakterystyczne jedynie dla jednego
z moliwych wciele abstrakcji, jednego z jej konceptualnych stanw. Jeli skupi si
na koncepcjach, a nie ich reprezentacjach wewntrznych, to dla tej samej abstrakcji
i wsplnego interfejsu mona udostpni cakowicie rne implementacje (na przykad
obliczenia z buforowaniem wynikw w jednej albo realizowane w locie w innej),
wykorzystujce odmienne reprezentacje wewntrznych danych (na przykad wsprzdne w ukadzie biegunowym albo kartezjaskim).
Powszechnie uwaa si, e nie naley dokonywa ekspozycji skadowych danych klas
przez oznaczanie ich jako publicznych (wytyczna 41.) albo przez udostpnianie ich
wskanikw czy uchwytw (wytyczna 42.). Tyczy si to jednak rwnie jednostek
wikszych od klas, takich jak bibliotek, ktre rwnie nie powinny eksponowa danych
implementacyjnych. Moduy i biblioteki powinny raczej udostpnia interfejsy definiujce abstrakcje i transfery midzy nimi pozwala to na bezpieczniejsze komunikowanie si z wywoujcym i mniej cise powizanie wywoujcego z bibliotek, ni
to ma miejsce przy stosowaniu danych wspuytkowanych.

44

Rozdzia 2. Styl projektowy

Wyjtki
Wyjtkiem moe by kod testujcy, niejednokrotnie wymagajcy swobodnego dostpu
do danych testowanych klas i moduw.
Regule ukrywania danych nie podlegaj rwnie agregaty wartoci (np. znane z jzyka C
struktury), stanowice jedynie zlepek danych, dla ktrych nie przewidziano abstrakcji
behawioralnej dane te stanowi wtedy rwnoczenie swj wasny (jedyny) interfejs
(zobacz wytyczna 41.).

rda
[Brooks95] 19  [McConnel] 6.2  [Parnas02]  [Stroustrup00] 24.4  [SuttHysl04a]

Wytyczna 12. Niepotrzebna rywalizacja to niezdrowa rywalizacja

45

Wytyczna 12.
Niepotrzebna rywalizacja
to niezdrowa rywalizacja
Wytyczna 12. Niepotr zebna rywalizacja to niezdrowa rywalizacja

Streszczenie
Bezpieczestwo wtkowe to podstawa jeli aplikacja wykorzystuje wiele wtkw
czy procesw, programista musi wiedzie, jak ma minimalizowa wspuytkowanie
obiektw (zobacz wytyczna 10.) i jak bezpiecznie uytkowa te, ktre musz pozosta
wsplne.

Uzasadnienie
Wtki to obszerne zagadnienie. Jego waga wymaga potwierdzenia w wydzieleniu dla
niego osobnej wytycznej. W ramach jednej takiej wytycznej nie sposb jednak uj
wszystkiego, co zwizane z programowaniem wtkw, ograniczymy si wic do podsumowania kilku kwestii zasadniczych po szczegy odsyamy za do rde. Za
kwestie najwaniejsze uwaamy za unikanie zakleszcze, unikanie zawaszczania
zasobw i unikanie szkodliwej rywalizacji w dostpie do zasobw (i ich uszkodzenia
w wyniku niewystarczajcego blokowania).
Standard jzyka C++ nie powica wtkom ani sowa. Mimo tego jzyk ten jest rutynowo i powszechnie wykorzystywany do pisania solidnych, wielowtkowych aplikacji.
Jeli wic Twj program dzieli dane pomidzy wtki, niech robi to bezpiecznie:
 Sprawd w dokumentacji platformy docelowej dostpno elementarnych

mechanizmw synchronizacji lokalnej od niepodzielnych maszynowych


operacji na wartociach cakowitych po bariery pamiciowe i blokady
wewntrzprocesowe i midzyprocesowe.
 Sprbuj uj owe elementarne mechanizmy we wasnych abstrakcjach

to dobry pomys, zwaszcza jeli program ma docelowo dziaa na wielu


platformach. Alternatywnie mona skorzysta z gotowych bibliotek tego
rodzaju (np. biblioteki pthreads [Butenhof97]).
 Upewnij si, e wykorzystywane typy mog by bezpiecznie stosowane w programie

wielowtkowym w szczeglnoci kady z takich typw powinien:




gwarantowa niezaleno obiektw niewspuytkowanych. Dwa wtki


powinny mc swobodnie korzysta z dwch rnych obiektw.

dokumentowa wymagania odnonie do wywoujcego, jeli ten chce


odwoywa si do tego samego obiektu z rnych wtkw. Cz typw
wymaga szeregowania dostpu do tak wspuytkowanych obiektw, inne
obchodz si bez takiej synchronizacji. W przypadku tych ostatnich brak
koniecznoci blokowania i synchronizacji dostpu wynika zazwyczaj

46

Rozdzia 2. Styl projektowy

z projektu typu, ewentualnie z zastosowania synchronizacji wewntrznej


w ktrym to przypadku programista powinien by wiadom ogranicze
owego wewntrznego blokowania i zna jego szczegowo.
Zauwa, e powysze reguy dotycz wszelkich typw, bez wyrniania
typw acuchowych, kontenerw, kontenerw STL czy jakichkolwiek
innych (zauwaylimy bowiem, e niektrzy autorzy wyrniaj tutaj
kontenery STL jako w jaki sposb szczeglne, tymczasem obiekty te nie
wyrniaj si niczym w tym zakresie). W szczeglnoci, gdy zamierzamy
wykorzysta w programie wielowtkowym komponenty biblioteki
standardowej, powinnimy sprawdzi w dokumentacji biblioteki,
czy jej implementacja daje tak moliwo.
Tworzc wasne typy przeznaczone do wykorzystywania w programach wielowtkowych, musimy zadba o te same dwa elementy: po pierwsze, zagwarantowa niezaleno (niewymagajc blokowania) rnych egzemplarzy tego danego typu (podpowied:
typ z modyfikowaln skadow statyczn nie daje takiej gwarancji); po drugie, udokumentowa wymagania co do uytkownikw typu w zakresie stosowania wsplnego
obiektu tego typu w rnych wtkach. W tej kwestii zasadnicze znaczenie ma problem
rozoenia pomidzy klas a jej uytkownikami odpowiedzialnoci za poprawne wykonanie programu. Mamy w tym zakresie trzy podstawowe moliwoci:
 Blokowanie zewntrzne za blokowanie odpowiedzialny jest wywoujcy

(uytkownik). W tym ukadzie kod korzystajcy z obiektu jest w peni


odpowiedzialny za synchronizacj odwoa do tego obiektu, jeli jest
on wykorzystywany w wielu wtkach. Z blokowania zewntrznego
korzystaj zazwyczaj typy acuchowe (czsto uciekaj si te
do niezmiennoci, zobacz opis trzeciej opcji).
 Blokowanie wewntrzne kady obiekt samodzielnie szereguje odwoania

do niego, zwykle przez blokady wszystkich metod publicznych, zwalniajce


uytkownikw z odpowiedzialnoci za synchronizacj dostpu do obiektu.
Przykadowo, w kolejkach producentw-konsumentw stosowane jest
blokowanie wewntrzne, poniewa obiekty te s z zasady przeznaczone
do wspuytkowania przez wtki i ich interfejsy s zaprojektowane
z uwzgldnieniem blokowania niezbdnego do bezpiecznego wykonania kadej
z metod. Ta opcja jest waciwa w przypadkach, kiedy z gry wiadomo, e:


obiekty danego typu bd wykorzystywane niemal wycznie jako


wspuytkowane w wielu wtkach jeli nie jest to pewne, blokowanie
wewntrzne bdzie w znacznej czci zbdne. Wypada zauway,
e niewiele typw spenia to wymaganie znakomita wikszo obiektw
nawet w programie silnie wielowtkowym nie podlega wspuytkowaniu
przez wtki (co nie jest bynajmniej zarzutem zobacz wytyczna 10.).

blokowanie poszczeglnych metod pozwoli osign odpowiedni


szczegowo synchronizacji, odpowiedni dla wikszoci uytkownikw.
W szczeglnoci interfejs typu powinien faworyzowa operacje
gruboziarniste, samowystarczalne. Jeli typowy uytkownik bdzie
musia z zasady blokowa kilka operacji zamiast jednej, to indywidualne
blokowanie metod nie zda egzaminu. Bdzie musiao by poparte

Wytyczna 12. Niepotrzebna rywalizacja to niezdrowa rywalizacja

47

blokowaniem oglniejszym (prawdopodobnie zewntrznym, pozostajcym


w gestii uytkownika), pozwalajcym na szeregowanie nie pojedynczych
operacji, a caych transakcji. Wemy choby typ kontenera zwracajcego
iterator i problem uniewanienia iteratora przed jego uyciem. Albo typ
udostpniajcy w postaci metody algorytm   zwracajcy wynik, ktrego
poprawno zostanie zniesiona w czasie pomidzy utworzeniem obiektu
a wywoaniem metody. Albo kiedy uytkownik obiektu  pewnego typu
zechce wykona operacj  
  (wicej
przykadw w [Sutter02]). W takich przypadkach uytkownik bdzie
musia mimo wewntrznego szeregowania dostpu do poszczeglnych
metod wdroy na wasn rk blokad, ktrej czas ycia obejmuje
wiele kolejnych wywoa metod. W takim ukadzie ich blokowanie
wewntrzne traci zupenie sens.
Jak wida, wewntrzne blokowanie ma cisy zwizek z publicznym interfejsem
typu: jest waciwe, kiedy poszczeglne operacje tego interfejsu s kompletne,
czyli kiedy poziom abstrakcji typu zostanie podniesiony i wyraony bardziej
precyzyjnie (na przykad kolejka producent-konsument zamiast oglnego
tablica). Interfejsy te cz elementarne manipulacje na typie do postaci
operacji znaczcych i uytecznych samych w sobie. Jeli liczba takich kombinacji
jest nie do przewidzenia i nie sposb wychwyci kombinacji najczstszych,
celem wyodrbnienia ich do wikszych operacji, mamy dwie moliwoci:
(a) zastosowa model oparty na wywoaniach zwrotnych (kiedy uytkownik
wywouje pojedyncz metod, przekazujc do niej obiekt funkcyjny bd funkcj,
ktra ma posuy do realizacji rozleglejszego zadania patrz wytyczne
od 87. do 89.) albo (b) wyeksponowa mechanizmy blokowania w interfejsie.
 Bez blokowania, za to z zaoeniem niezmiennoci (w przypadku obiektw

niemodyfikowalnych). Mona tak zaprojektowa typy obiektw, aby ich


blokowanie nie byo w ogle potrzebne (patrz rda). Przykadem takiego
projektu s obiekty niemodyfikowalne szeregowanie dostpu do nich jest
zbdne, poniewa nie mona ich zmienia. Przykadem moe by typ
niemodyfikowalnego acucha znakw, ktrego obiektu nie mona zmienia
w czasie ycia, a kada operacja na acuchu powoduje utworzenie nowego
obiektu z nowym acuchem znakw.
Warto pamita, e uytkownik niekoniecznie musi dysponowa wiedz co do szczegw implementacji danego typu (zgodnie z wytyczn 11.). Jeli Twj typ wykorzystuje ukryte mechanizmy zarzdzajce wspuytkowaniem obiektw (np. opnianie
kopiowania, tzw. kopiowanie przy zapisie), nie musisz bra odpowiedzialnoci za
wszelkie moliwe kwestie zwizane z wielowtkowoci, ale nie moesz zignorowa
odpowiedzialnoci za zapewnienie bezpieczestwa wtkowego w stopniu wystarczajcym do zapewnienia poprawnoci odwoa do obiektu przez uytkownika, jeli ten
dopeni swoich zwykych obowizkw typ powinien by co najmniej tak bezpieczny,
jak byby, gdyby nie stosowa utajonych mechanizmw wspuytkowania (zobacz
[Sutter04c]). Wszystkie prawidowo zdefiniowane typy powinny pozwala na manipulowanie osobnymi, niezalenymi egzemplarzami z poziomu niezalenych wtkw bez
potrzeby jakiejkolwiek synchronizacji pomidzy tymi egzemplarzami.

48

Rozdzia 2. Styl projektowy

Twrca biblioteki przeznaczonej do powszechnego uytku powinien szczeglnie rozway zabezpieczenie obiektw biblioteki przed interakcjami w rodowisku wielowtkowym. Powinien do tego podej w sposb opisany powyej, ale tak, aby zabezpieczenia
te nie powodoway znacznych narzutw w rodowiskach wielowtkowych. Jeli na
przykad piszesz bibliotek zawierajc typ stosujcy kopiowanie przy zapisie i z tego
wzgldu wykorzystujcy rwnie jakie wewntrzne blokady, blokady te naley tak
zaaranowa, aby w kompilacjach dla rodowisk jednowtkowych byy niewidoczne
(mona uciec si wtedy do dyrektyw  i pustych implementacji).
Zakadajc wiele blokad, powiniene unika zakleszcze i ukada kod tak, aby we
wszystkich miejscach pozyskiwania tego kompletu blokad kolejno ich zakadania
bya zawsze taka sama (zwalnianie blokad moe by wtedy realizowane w dowolnym
porzdku). Rozwizaniem problemu staej kolejnoci zakadania blokad moe by ich
zakadanie wedug rosncych adresw w pamici bazujc na adresach, moesz atwo
ustali porzdek blokowania wsplny dla caej aplikacji.

rda
[Alexandrescu02a]  [Alexandrescu04]  [Butenhof97]  [Henney00]  [Henney01]
 [Meyers04]  [Shmidt01]  [Stroustrup] 14.9  [Sutter02] 16  [Sutter04c]

Wytyczna 13. Zagwarantuj opiek nad zasobami przez obiekty. Stosuj RAII

49

Wytyczna 13.
Zagwarantuj opiek nad zasobami
przez obiekty. Stosuj RAII
i inteligentne wskaniki
Wytyczna 13. Zagwarantuj opiek nad za sobami przez obie kty. S tosuj RAII

Streszczenie
Nie walaj rk, jeli masz narzdzia idiom pozyskanie zasobu to jego inicjalizacja
(RAII, od resource acquisition is initialization) to wietne narzdzie poprawnej obsugi
zasobw. RAII pozwala kompilatorowi na udostpnianie silnych i automatycznych
gwarancji, ktre w innych jzykach wymagaj karkoomnych sztuczek programistycznych. Przydzielajc surowy zasb, bezzwocznie przeka go do obiektu, ktry ma by
jego dysponentem. I nigdy nie przydzielaj wicej ni jednego zasobu w pojedynczej
instrukcji.

Uzasadnienie
Jzyk C++, wymuszajcy symetri wywoa konstruktorw i destruktorw, odwzorowuje w niej symetri charakterystyczn dla par funkcji pozyskujcych i zwalniajcych
obiekty, takich jak  i 
,  i  czy operatorw  i .
Dziki temu przydzielany w pamici stosu (albo implementowany ze zliczaniem odwoa) obiekt z pozyskujcym zasoby konstruktorem i zwalniajcym je destruktorem
jest znakomitym narzdziem automatyzacji zarzdzania zasobami.
Automatyzacja ta jest prosta w implementacji, elegancka, mao kosztowna i w swej
istocie odporna na bdy. Jej odrzucenie oznacza obcienie samego siebie niebanalnym
i angaujcym zadaniem rcznego parowania wywoa pozyskujcych i zwalniajcych
zasoby, z uwzgldnieniem wyjtkw i wynikajcych z logiki programu rozgazie
przepywu sterowania. Tego rodzaju przywizanie do jzyka C i charakterystycznego
dla niego mikrozarzdzania operacjami zwalniania zasobw jest nie do zaakceptowania
w jzyku C++, w ktrym owe czynnoci s automatyzowane za porednictwem RAII.
Gdy mamy do czynienia z zasobem wymagajcym parowania wywoa funkcji pozyskujcych i zwalniajcych, powinnimy w zasb hermetyzowa w obiekcie, skadajc
zadanie zwalniania zasobu na barki jego destruktora. Na przykad w miejsce wywoa
pary funkcji !"  i #
"  wypadaoby rozway takie rozwizanie:



  
 

 

 



 

  

 !
      
"


50

Rozdzia 2. Styl projektowy


 

 


 " 
 !   #$%
!


&!
  '

%!


 !"#$(( )%
!


 
 %!

* '
+ 

Mona rwnie korzysta z gotowych bibliotek implementujcych ten schemat (zobacz


[Alexandrescu00c]).
Implementujc idiom RAII, musimy uwaa na konstruktory kopiujce i operatory
przypisania (zobacz te wytyczna 49.); generowane przez kompilator wersje tych metod
nie bd raczej poprawne. Jeli kopiowanie obiektw hermetyzujcych zasoby nie ma
sensu semantycznego, powinnimy jawnie zablokowa moliwo korzystania z tych
operacji, czynic je skadowymi prywatnymi i niezdefiniowanymi (patrz wytyczna 53.).
W pozostaych przypadkach naley zadba o to, aby konstruktor kopiujcy wykonywa
duplikat zasobu (ewentualnie zwiksza licznik odwoa do niego), a operator przypisania robi to samo, po uprzednim ewentualnym zwolnieniu zasobu przetrzymywanego
dotychczas. Klasycznym przeoczeniem jest zwolnienie poprzednio przetrzymywanego
zasobu przed skutecznym wykonaniem duplikatu nowego (wytyczna 71.).
Upewnij si te, e wszystkie zasoby nale do odpowiednich obiektw. Zasoby przydzielane dynamicznie najlepiej przechowywa za porednictwem wskanikw inteligentnych, a nie zwykych. Warto te kady jawny przydzia zasobu (np. wywoanie
operatora ) wyodrbnia do osobnej instrukcji, w ktrej nowo przydzielony zasb
natychmiast wdruje pod opiek swojego dysponenta (np. wskanika
  );
inaczej atwo o wycieki zasobw spowodowane nieoczekiwanym porzdkiem ewaluacji
parametrw funkcji (porzdek ten jest bowiem niezdefiniowany patrz wytyczna 31.).
Oto przykad:
 %&   !'
 " (  !'
 " #

%&   !'
 "
  (  !'
 "
  

Powyszy kod nie jest bezpieczny. Standard jzyka C++ daje twrcom kompilatorw
znaczn swobod w zakresie porzdkowania wyrae reprezentujcych argumenty
wywoania funkcji. W szczeglnoci kompilator moe przeplata obliczanie obu wyrae i najpierw wykona przydzia pamici dla obu obiektw (operatorem ), a potem
dopiero wywoa (w dowolnej kolejnoci) konstruktory obiektw klasy $  . W takim
ukadzie bardzo atwo o wyciek pamici, poniewa jeli jeden z konstruktorw zgosi
wyjtek, to pami drugiego z obiektw nie zostanie nigdy zwolniona (po szczegy
odsyamy do [Sutter02])!
Ten subtelny problem ma proste rozwizanie: wystarczy pilnowa, aby w pojedynczej
instrukcji nie przydziela wicej ni jednego zasobu, a kady przydzia realizowa
jawnie, z natychmiastowym przekazaniem zasobu do obiektu-dysponenta (np. wskanika
  ). Jak tutaj:

Wytyczna 13. Zagwarantuj opiek nad zasobami przez obiekty. Stosuj RAII

51

 
'
 (
'
 
%& (

Inne zalety stosowanie takiej konwencji przedstawione zostan w wytycznej nr 31.

Wyjtki
atwo o naduycie inteligentnych wskanikw. Zwyke wskaniki wietnie sprawdzaj
si w kodzie, w ktrym wskazywane zasoby s widoczne jedynie w ograniczonym fragmencie kodu (np. wycznie wewntrz klasy, jak w przypadku wewntrznych wskanikw nawigacji wrd wzw w klasie %).

rda
[Alexandrescu00c]  [Cline99] 31.0305  [Dewhurst03] 24, 67  [Meyers96]
910  [Milewski01]  [Stroustrup00] 14.34, 25.7, E.3, E.6  [Sutter00] 16
 [Sutter02] 2021  [Vandervoorde03] 20.1.4

You might also like