Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00101 009740 11027983 na godz. na dobę w sumie
Język C++. Gotowe rozwiązania dla programistów - książka
Język C++. Gotowe rozwiązania dla programistów - książka
Autor: Liczba stron: 696
Wydawca: Helion Język publikacji: polski
ISBN: 83-7361-841-4 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c++ - programowanie
Porównaj ceny (książka, ebook, audiobook).

C++ to popularny i uniwersalny język programowania. Jednak po dłuższym stosowaniu programiści zaczynają zauważać pewne jego niedoskonałości i ograniczenia. System typów, sposób działania niektórych kompilatorów, związki pomiędzy wskaźnikami i tablicami, nieprzewidziane w standardzie zachowania obiektów statycznych i bibliotek dynamicznych to tylko niektóre z nich. Aby je obejść, należy wykorzystywać wiele bardzo zaawansowanych i nieznanych wielu programistom metod.

Książka 'Język C++. Gotowe rozwiązania dla programistów' to podręcznik dla tych programistów C++, którzy zaczęli już dostrzegać ograniczenia tego języka i zastanawiają się, jak sobie z nimi poradzić. Autor pokazuje sposoby ujarzmienia złożoności języka i uzyskania pełnej kontroli nad kodem. Przedstawia najpoważniejsze wady C++ i sposoby rozwiązywania powodowanych przez nie problemów. Opisuje również metody tworzenia stabilniejszego, bardziej uniwersalnego, wydajniejszego i łatwiejszego w pielęgnacji kodu.

Wszyscy programiści, niezależnie od stopnia zaawansowania, znajdą w tej książce wiadomości, które usprawnią i przyspieszą ich pracę.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREĎCI SPIS TREĎCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOĎCIACH O NOWOĎCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl Jêzyk C++. Gotowe rozwi¹zania dla programistów Autor: Matthew Wilson T³umaczenie: Zbigniew Banach, Micha³ Dadan, Tomasz Walczak ISBN: 83-7361-841-4 Tytu³ orygina³u: Imperfect C++: Practical Solutions for Real-Life Programming Format: B5, stron: 696 C++ to popularny i uniwersalny jêzyk programowania. Jednak po d³u¿szym stosowaniu programiġci zaczynaj¹ zauwa¿aæ pewne jego niedoskona³oġci i ograniczenia. System typów, sposób dzia³ania niektórych kompilatorów, zwi¹zki pomiêdzy wskaĥnikami i tablicami, nieprzewidziane w standardzie zachowania obiektów statycznych i bibliotek dynamicznych to tylko niektóre z nich. Aby je obejġæ, nale¿y wykorzystywaæ wiele bardzo zaawansowanych i nieznanych wielu programistom metod. Ksi¹¿ka „Jêzyk C++. Gotowe rozwi¹zania dla programistów” to podrêcznik dla tych programistów C++, którzy zaczêli ju¿ dostrzegaæ ograniczenia tego jêzyka i zastanawiaj¹ siê, jak sobie z nimi poradziæ. Autor pokazuje sposoby ujarzmienia z³o¿onoġci jêzyka i uzyskania pe³nej kontroli nad kodem. Przedstawia najpowa¿niejsze wady C++ i sposoby rozwi¹zywania powodowanych przez nie problemów. Opisuje równie¿ metody tworzenia stabilniejszego, bardziej uniwersalnego, wydajniejszego i ³atwiejszego w pielêgnacji kodu. • Wymuszanie za³o¿eñ projektowych • Cykl ¿ycia obiektów • Hermetyzacja zasobów, danych i typów • Modele dostêpu do obiektów • Obs³uga w¹tków • Korzystanie z obiektów statycznych • Konwersja danych i typów • Zarz¹dzanie pamiêci¹ • Sterowanie dzia³aniem kompilatora Wszyscy programiġci, niezale¿nie od stopnia zaawansowania, znajd¹ w tej ksi¹¿ce wiadomoġci, które usprawni¹ i przyspiesz¹ ich pracê. Spis treści Przedmowa ................................................... .................................. 11 Prolog. Filozofia praktyka niedoskonałego ....................................... 19 Niedoskonałości, ograniczenia, definicje i zalecenia ......................... 29 Część I Podstawy ...................................................d...................37 Rozdział 1. Wymuszanie założeń projektowych: ograniczenia, kontrakty i asercje ................................................... .. 39 1.1. Kilka oczywistych mądrości ...................................................c............................ 40 1.2. Kontrakty kompilacji — ograniczenia ...................................................c............. 41 1.3. Kontrakty wykonawcze: warunki wejściowe, końcowe i niezmienniki ............... 49 1.4. Asercje ...................................................c...................................................c.......... 56 Rozdział 2. Życie obiektów ................................................... ............................ 67 2.1. Cykl życia obiektu ..................................................c............................................. 67 2.2. Kontrola klientów ..................................................c.............................................. 68 2.3. Dobrodziejstwa list inicjalizacji ...................................................c....................... 73 Rozdział 3. Hermetyzacja zasobów ................................................... ................. 81 3.1. Poziomy hermetyzacji zasobów ...................................................c....................... 81 3.2. Typy POD ...................................................c...................................................c..... 82 3.3. Pośrednie typy opakowujące ...................................................c............................ 84 3.4. Typy RRID ...................................................c...................................................c.... 87 3.5. Typy RAII ...................................................c...................................................c..... 92 3.6. RAII — podsumowanie ...................................................c................................... 95 Rozdział 4. Hermetyzacja danych i typy wartości ............................................... 97 4.1. Poziomy hermetyzacji danych ...................................................c.......................... 98 4.2. Typy wartości a typy egzystencjalne ...................................................c................ 98 4.3. Klasyfikacja typów wartości ...................................................c............................ 99 4.4. Typy otwarte ...................................................c.................................................. 101 4.5. Typy hermetyzowane ...................................................c..................................... 103 4.6. Typy wartości ...................................................c................................................. 104 4.7. Arytmetyczne typy wartości ...................................................c........................... 106 4.8. Typy wartości — podsumowanie ...................................................c................... 107 4.9. Hermetyzacja — podsumowanie ...................................................c.................... 107 6 Język C++. Gotowe rozwiązania dla programistów Rozdział 5. Modele dostępu do obiektów ................................................... ..... 113 5.1. Gwarantowany czas życia ...................................................c.............................. 113 5.2. Kopia dla wywołującego ...................................................c................................ 115 5.3. Oryginał dla wywołującego ..................................................c............................. 116 5.4. Obiekty współdzielone ...................................................c................................... 116 Rozdział 6. Zasięg klas ................................................... ............................... 119 6.1. Wartość ...................................................c...................................................c....... 119 6.2. Stan ...................................................c...................................................c............. 124 6.3. API i usługi ...................................................c...................................................c. 128 6.4. Mechanizmy języka ..................................................c......................................... 132 Część II Przetrwanie w świecie rzeczywistym ............................135 Rozdział 7. ABI ................................................... ........................................... 137 7.1. Udostępnianie kodu ...................................................c........................................ 137 7.2. Wymagania ABI C ...................................................c......................................... 139 7.3. Wymagania ABI C++ ...................................................c.................................... 144 7.4. C — i wszystko jasne ...................................................c..................................... 148 Rozdział 8. Obiekty bez granic ................................................... ..................... 157 8.1. Czyżby przenośne tabele funkcji wirtualnych? ................................................. 157 8.2. Przenośne tabele vtable ...................................................c.................................. 161 8.3. Przenośność — podsumowanie ...................................................c...................... 169 Rozdział 9. Biblioteki dynamiczne ................................................... ................ 171 9.1. Jawne wywołania funkcji ...................................................c............................... 171 9.2. Tożsamość — jednostki i przestrzeń konsolidacji ............................................. 174 9.3. Czas życia ...................................................c...................................................c... 175 9.4. Wersjonowanie ...................................................c............................................... 176 9.5. Własność zasobów ...................................................c......................................... 179 9.6. Biblioteki dynamiczne — podsumowanie ...................................................c...... 180 Rozdział 10. Wątki ................................................... ........................................ 181 10.1. Synchronizacja dostępu do wartości całkowitych ............................................. 182 10.2. Synchronizacja dostępu do bloków kodu — regiony krytyczne ........................ 186 10.3. Wydajność operacji atomowych ..................................................c...................... 190 10.4. Rozszerzenia wielowątkowe ...................................................c.......................... 195 10.5. TSS — składowanie danych w wątkach ...................................................c......... 199 Rozdział 11. Obiekty statyczne ................................................... ...................... 207 11.1. Globalne obiekty statyczne ...................................................c............................ 209 11.2. Singletony ...................................................c...................................................c... 214 11.3. Lokalne obiekty statyczne funkcji ...................................................c.................. 222 11.4. Składowe statyczne ...................................................c........................................ 224 11.5. Obiekty statyczne — podsumowanie ...................................................c............. 227 Rozdział 12. Optymalizacja ................................................... ............................ 229 12.1. Funkcje inline ...................................................c................................................. 229 12.2. Optymalizacja wartości zwracanej ...................................................c.................. 231 12.3. Optymalizacja pustych klas bazowych ...................................................c........... 234 12.4. Optymalizacja pustych klas potomnych ...................................................c......... 237 12.5. Zapobieganie optymalizacji ...................................................c........................... 239 Spis treści 7 Część III Kwestie językowe ..................................d......................243 Rozdział 13. Typy podstawowe ................................................... ...................... 245 13.1. Komu bajt? ...................................................c...................................................c.. 246 13.2. Typy całkowitoliczbowe o stałym rozmiarze ...................................................c. 249 13.3. Duże typy całkowitoliczbowe ...................................................c........................ 255 13.4. Typy niebezpieczne ...................................................c........................................ 257 Rozdział 14. Tablice i wskaźniki ................................................... .................... 263 14.1. Nie powtarzaj się ...................................................c............................................ 263 14.2. Degeneracja tablic do wskaźników ...................................................c................ 265 14.3. dimensionof() ...................................................c................................................. 268 14.4. Nie można przekazywać tablic do funkcji ...................................................c...... 270 14.5. Tablice są zawsze przekazywane przez adres ..................................................c.. 273 14.6. Tablice typów dziedziczonych ...................................................c....................... 274 14.7. Brak tablic wielowymiarowych ...................................................c...................... 281 Rozdział 15. Wartości ................................................... ................................... 285 15.1. NULL — słowo kluczowe, którego nie było ..................................................c... 285 15.2. Spadek do zera ...................................................c............................................... 292 15.3. Naginanie prawdy ...................................................c.......................................... 294 15.4. Literały ...................................................c...................................................c........ 296 15.5. Stałe ...................................................c...................................................c............ 302 Rozdział 16. Słowa kluczowe ................................................... ......................... 311 16.1. interface ...................................................c...................................................c....... 311 16.2. temporary ...................................................c...................................................c.... 314 16.3. owner ..................................................c...................................................c............ 317 16.4. explicit(_cast) ...................................................c................................................. 321 16.5. unique ...................................................c...................................................c.......... 326 16.6. final ...................................................c...................................................c............. 327 16.7. Nieobsługiwane słowa kluczowe ...................................................c................... 328 Rozdział 17. Składnia ................................................... .................................... 331 17.1. Układ klasy ...................................................c...................................................c. 331 17.2. Wyrażenia warunkowe ...................................................c................................... 334 17.3. for ...................................................c...................................................c................338 17.4. Zapis zmiennych ...................................................c............................................ 341 Rozdział 18. Definicja typów za pomocą typedef ................................................ 345 18.1. Definicje typu dla wskaźników ...................................................c...................... 347 18.2. Co wchodzi w skład definicji? ...................................................c....................... 349 18.3. Nowe nazwy ...................................................c...................................................c 354 18.4. Prawdziwe definicje typu ...................................................c............................... 356 18.5. Dobre, złe i brzydkie ...................................................c...................................... 361 Część IV Świadome konwersje ...................................................d369 Rozdział 19. Rzutowanie ................................................... ................................ 371 19.1. Niejawna konwersja ...................................................c....................................... 371 19.2. Rzutowanie w C++ ...................................................c......................................... 372 19.3. Przypadek rzutowania w stylu C ...................................................c.................... 373 19.4. Rzutowanie na sterydach ...................................................c................................ 375 19.5. explicit_cast ...................................................c...................................................c 377 19.6. literal_cast ...................................................c...................................................c... 382 19.7. union_cast ...................................................c...................................................c... 385 8 Język C++. Gotowe rozwiązania dla programistów 19.8. comstl::interface_cast ...................................................c..................................... 388 19.9. boost::polymorphic_cast ...................................................c................................ 399 19.10. Rzutowanie — podsumowanie ...................................................c....................... 401 Rozdział 20. Podkładki ................................................... .................................. 403 20.1. Ogarnąć zmiany i zwiększyć elastyczność ...................................................c..... 404 20.2. Podkładki atrybutów ...................................................c...................................... 406 20.3. Podkładki logiczne ...................................................c......................................... 408 20.4. Podkładki sterujące ...................................................c........................................ 409 20.5. Podkładki konwertujące ...................................................c.................................. 411 20.6. Podkładki złożone ...................................................c.......................................... 414 20.7. Przestrzenie nazw a sprawdzenie Koeniga ...................................................c..... 420 20.8. Dlaczego nie typy cechujące? ...................................................c........................ 423 20.9. Dopasowanie strukturalne ...................................................c.............................. 424 20.10. Przełamywanie monolitu ...................................................c................................ 426 20.11. Podkładki — podsumowanie ...................................................c.......................... 428 Rozdział 21. Forniry ................................................... ....................................... 429 21.1. Lekkie RAII ...................................................c...................................................c 430 21.2. Wiązanie danych z operacjami ...................................................c....................... 431 21.3. Przypomnienie założeń ...................................................c.................................. 438 21.4. Forniry — podsumowanie ...................................................c.............................. 440 Rozdział 22. Sworznie ................................................... ................................... 441 22.1. Dodawanie nowej funkcjonalności ...................................................c................. 442 22.2. Wybór skóry ...................................................c...................................................c 442 22.3. Przesłanianie metod niewirtualnych ...................................................c............... 443 22.4. Wykorzystywanie zasięgu ...................................................c.............................. 445 22.5. Symulowany polimorfizm czasu kompilacji — sworznie wsteczne .................. 447 22.6. Parametryzowalne pakowanie polimorficzne ...................................................c. 449 22.7. Sworznie — podsumowanie ...................................................c........................... 451 Rozdział 23. Konstruktory szablonów ................................................... ............. 453 23.1. Ukryte koszty ...................................................c................................................. 455 23.2. Wiszące referencje ...................................................c......................................... 455 23.3. Specjalizacja konstruktorów szablonów ...................................................c......... 457 23.4. Pośrednictwo argumentów ...................................................c............................. 458 23.5. Ukierunkowywanie na konkretne rodzaje argumentów .................................... 460 23.6. Konstruktory szablonów — podsumowanie ...................................................c... 461 Część V Operatory ...................................................d.................463 Rozdział 24. operator bool() ................................................... .......................... 465 24.1. operator int() const ...................................................c......................................... 465 24.2. operator void *() const ...................................................c................................... 466 24.3. operator bool() const ...................................................c...................................... 467 24.4. operator !() — not! ...................................................c......................................... 468 24.5. operator boolean const *() const ..................................................c...................... 468 24.6. operator int boolean::*() const ...................................................c....................... 469 24.7. Stosowanie operatorów w praktyce ...................................................c................ 469 24.8. operator! ...................................................c...................................................c...... 473 Rozdział 25. Szybkie i nieinwazyjne łączenie ciągów znaków ............................. 475 25.1. fast_string_concatenator ...................................................c............................ 476 25.2. Wydajność ...................................................c...................................................c... 484 25.3. Praca z innymi klasami obsługującymi ciągi znaków ....................................... 487 Spis treści 9 25.4. Inicjalizacja łączenia ...................................................c...................................... 488 25.5. Patologiczne nawiasowanie ...................................................c............................ 489 25.6. Standaryzacja ...................................................c................................................. 490 Rozdział 26. Jaki jest Twój adres? ................................................... ................. 491 26.1. Nie można pobrać rzeczywistego adresu ...................................................c........ 491 26.2. Co dzieje się w czasie konwersji? ...................................................c.................. 494 26.3. Co zwracać? ...................................................c...................................................c 496 26.4. Jaki jest Twój adres — podsumowanie ...................................................c.......... 498 Rozdział 27. Operatory indeksowania ................................................... ............. 501 27.1. Przekształcanie wskaźników i operatory indeksowania .................................... 501 27.2. Obsługa błędów ...................................................c.............................................. 504 27.3. Zwracana wartość ...................................................c........................................... 506 Rozdział 28. Operatory inkrementacji ................................................... ............. 509 28.1. Brakujące operatory przyrostkowe ...................................................c.................... 510 28.2. Wydajność ...................................................c...................................................c... 511 Rozdział 29. Typy arytmetyczne ................................................... ..................... 515 29.1. Definicja klasy ...................................................c............................................... 515 29.2. Konstruktor domyślny ...................................................c.................................... 516 29.3. Inicjalizacja (nadawanie wartości) ...................................................c.................. 517 29.4. Konstruktor kopiujący ...................................................c.................................... 519 29.5. Przypisanie ...................................................c...................................................c.. 519 29.6. Operatory arytmetyczne ...................................................c................................. 520 29.7. Operatory porównania ...................................................c.................................... 520 29.8. Dostęp do wartości ...................................................c......................................... 521 29.9. Klasa SInteger64 ...................................................c............................................ 521 29.10. Obcinanie, promocja do większego typu i testowanie warunków ..................... 522 29.11. Typy arytmetyczne — podsumowanie ...................................................c........... 525 Rozdział 30. Skrócona ewaluacja ................................................... ................... 527 Część VI Rozszerzanie możliwości języka C++ ............................529 Rozdział 31. Czas życia wartości zwracanej ................................................... ... 531 31.1. Klasyfikacja problemów związanych z czasem życia wartości zwracanej ........ 532 31.2. Do czego służy zwracanie przez referencję? ...................................................c.. 533 31.3. Rozwiązanie pierwsze — szablon integer_to_string ..................................... 533 31.4. Rozwiązanie drugie — mechanizm TSS ...................................................c........ 536 31.5. Rozwiązanie trzecie — rozszerzanie RVL ...................................................c..... 541 31.6. Rozwiązanie czwarte — statyczne określanie rozmiaru tablicy ......................... 543 31.7. Rozwiązanie piąte — podkładki konwertujące ................................................. 545 31.8. Wydajność ...................................................c...................................................c... 547 31.9. RVL — wielkie zwycięstwo automatycznego przywracania pamięci ............... 548 31.10. Potencjalne zastosowania ...................................................c............................... 549 31.11. RVL — podsumowanie ...................................................c.................................. 549 Rozdział 32. Pamięć ................................................... ...................................... 551 32.1. Rodzaje pamięci ...................................................c............................................. 551 32.2. Najlepsze w obu rodzajach ...................................................c............................. 553 32.3. Alokatory ...................................................c...................................................c.... 565 32.4. Pamięć — podsumowanie ...................................................c.............................. 569 10 Język C++. Gotowe rozwiązania dla programistów Rozdział 33. Tablice wielowymiarowe ................................................... ............. 571 33.1. Udostępnianie składni indeksowania ..................................................c............... 572 33.2. Określanie rozmiaru w czasie wykonywania programu .................................... 573 33.3. Określanie rozmiaru na etapie kompilacji ...................................................c...... 579 33.4. Dostęp blokowy ...................................................c............................................. 582 33.5. Wydajność ...................................................c...................................................c... 586 33.6. Tablice wielowymiarowe — podsumowanie ...................................................c. 589 Rozdział 34. Funktory i zakresy ................................................... ..................... 591 34.1. Bałagan składniowy ...................................................c....................................... 591 34.2. Funkcja for_all() ...................................................c............................................ 592 34.3. Funktory lokalne ...................................................c............................................ 595 34.4. Zakresy ...................................................c...................................................c........ 604 34.5. Funktory i zakresy — podsumowanie ...................................................c............ 612 Rozdział 35. Właściwości ................................................... .............................. 613 35.1. Rozszerzenia kompilatora ...................................................c.............................. 615 35.2. Sposoby implementacji ...................................................c.................................. 616 35.3. Właściwości pola ...................................................c........................................... 617 35.4. Właściwości metody ...................................................c...................................... 624 35.5. Właściwości statyczne ...................................................c.................................... 638 35.6. Właściwości wirtualne ...................................................c................................... 642 35.7. Zastosowania właściwości ...................................................c............................. 642 35.8. Właściwości — podsumowanie ...................................................c..................... 645 Dodatki ...................................................d....................647 Dodatek A Kompilatory i biblioteki ................................................... .............. 649 A.1. Kompilatory ...................................................c...................................................c 649 A.2. Biblioteki ...................................................c...................................................c..... 651 A.3. Inne źródła ...................................................c...................................................c.. 653 Dodatek B Pycha w końcu Cię zgubi! ................................................... .......... 655 B.1. Przeciążanie operatora ...................................................c................................... 656 B.2. Jak zawiodło DRY ...................................................c......................................... 657 B.3. Programowanie paranoiczne ...................................................c.......................... 657 B.4. Do szaleństwa i jeszcze dalej ...................................................c......................... 658 Dodatek C Arturius ................................................... ..................................... 661 Dodatek D Płyta CD ................................................... .................................... 663 Epilog ................................................... ........................................ 665 Bibliografia ................................................... ................................ 667 Skorowidz ................................................... .................................. 675 Rozdział 4. Hermetyzacja danych i typy wartości W poprzednim rozdziale omawialiśmy hermetyzację zasobów jako proces odmienny od hermetyzacji danych. Hermetyzacja zasobów dotyczy bardziej samego mechanizmu niż zawartości zasobów, natomiast hermetyzację danych można w uproszczeniu zdefiniować odwrotnie (choć w konkretnych przypadkach to rozróżnienie często ulega rozmyciu). Hermetyzacja danych niesie ze sobą tradycyjne zalety klasycznej, obiektowej herme- tyzacji: 1. Spójność danych. Stan instancji obiektu można zainicjalizować tak, by utworzyć poprawną całość. Wszelkie modyfikacje instancji za pośrednictwem metod interfejsu są atomowe, co oznacza, że stan składowych będzie spójny przed wywołaniem metody i po jego zakończeniu. 2. Zmniejszenie złożoności. Kod kliencki operuje na instancjach obiektów za pośrednictwem dokładnie zdefiniowanego publicznego interfejsu i nie musi (a w dodatku nie potrzebuje) znać poziomu wewnętrznej złożoności typu. 3. Odporność na zmiany. Kod kliencki jest niezależny od wewnętrznych zmian wprowadzanych w implementacji typu. Możliwe jest też działanie na kilku różnych typach o podobnych interfejsach publicznych, ale różnych postaciach wewnętrznych. Klasy implementujące hermetyzację danych mogą również implementować hermetyzację zasobów (dobrym przykładem są wszelkie klasy napisów), ale w przypadku zasobów chodzi przede wszystkim o sposób hermetyzacji, podczas gdy my zajmiemy się teraz samymi danymi. Dalszym rozwinięciem hermetyzacji danych są typy wartości. W rozdziale omówimy różnicę między typami wartości (ang. value types) a typami egzystencjalnymi (ang. entity types) i przyjrzymy się dokładnie cechom wyróżniającym typy wartości. Zoba- czymy też, jak różne poziomy hermetyzacji wpływają na definicje typów wartości. 98 Część I ♦ Podstawy 4.1. Poziomy hermetyzacji danych W poprzednim rozdziale poznaliśmy różne poziomy hermetyzacji zasobów, od otwar- tych struktur z dostępem poprzez funkcje API po klasy całkowicie hermetyczne. Poziomom hermetyzacji danych odpowiadają dostępne w C++ specyfikatory dostępu. Dane niehermetyzowane są dostępne dla dowolnego innego kontekstu i są definiowane w bloku RWDNKE klasy (lub w bloku domyślnym struktury lub unii). W pełni hermetyczne dane są definiowane w bloku RTKXCVG lub RTQVGEVGF typu klasowego. Przy niewielkiej ilości przykładów może się wydawać, że istnieje ścisły związek mię- dzy poziomem hermetyzacji a stopniem, w jakim dany typ jest typem wartości. Nie jest to jednak regułą i nie musi istnieć żadna bezpośrednia relacja między tymi własnościa- mi. Jest to tylko kolejna myląca kombinacja cech i dobrze mieć świadomość różnicy między tymi dwoma pojęciami. 4.2. Typy wartości a typy egzystencjalne Ujmując rzecz możliwie najprościej — i jest to moja prywatna, robocza definicja — typy wartości można opisać jako rzeczy, które po prostu są, a typy egzystencjalne jako rzeczy, które coś robią. Bjarne Stroustrup podaje świetną definicję typów wartości, nazywając je typami kon- kretnymi1: „Ich celem jest robienie jednej małej rzeczy dobrze i wydajnie. Na ogół nie dają możliwości modyfikacji swego zachowania”. Langer i Kreft [Lang2002] podają bardziej precyzyjne definicje. Typami wartości są „typy posiadające zawartość, których zachowanie nieodłącznie zależy od tej zawarto- ści. Na przykład dwa łańcuchy znakowe zachowują się różnie, jeśli różnią się zawar- tością, a zachowują się tak samo przy takiej samej zawartości (a ich porównywanie wykazuje równość). Zawartość jest ich najważniejszą cechą”. Istotne jest podkreślenie, że równość jest ważniejsza od tożsamości, co moim zdaniem stanowi jeden z najważ- niejszych wyróżników typów wartości. Langer i Kreft definiują typy egzystencjalne jako takie typy, „których zachowanie jest w dużej mierze niezależne od zawartości. Zachowanie jest ich najważniejszą cechą”. Tym samym sprawdzanie równości typów egzystencjalnych jest, ogólnie rzecz biorąc, działaniem bezcelowym. Osobiście wolę prostotę mojej własnej definicji (cóż za nie- spodzianka), ale kwalifikacja typów zawarta w definicji Langera i Krefta jest istotna. i 1 Dla mnie typ konkretny to taki, dla którego można tworzyć instancje, czyli typ kompletny (bo widać definicję) i nieabstrakcyjny (nie ma żadnych metod czysto wirtualnych do wypełnienia). Żeby było śmieszniej, nawet w przypadku typu abstrakcyjnego istnieją różne definicje. Znowu ból mózgu. Rozdział 4. ♦ Hermetyzacja danych i typy wartości 99 W kontekście tego rozdziału będę traktował pojęcie typu egzystencjalnego jako jednolite, choć w rzeczywistości obejmuje ono wielką różnorodność typów, w tym co najmniej typy konkretne, abstrakcyjne, polimorficzne i niepolimorficzne. Wiele z tych pojęć zo- stanie omówionych osobno w dalszych częściach książki. W pozostałej części tego rozdziału przyjrzymy się typom wartości i spróbujemy usta- lić, czy można mówić o tylko jednym ich rodzaju. Jak zwykle zamierzam twierdzić, że jest inaczej. 4.3. Klasyfikacja typów wartości Bjarne Stroustrup [Stro1997] definiuje semantykę wartości (w przeciwieństwie do semantyki wskaźników) jako niezależność skopiowanych typów. Daje to nam świetną podstawę, ale nie wystarczy do definicji. Eugene Gershnik, jeden z recenzentów tej książki, ma własną, niezależną od języka definicję typów wartości. Dany typ jest typem wartości, jeśli: 1. Instancję można utworzyć jako kopię innej instancji lub później ją taką kopią uczynić. 2. Każda instancja ma własną tożsamość. Zmiana jednej instancji nie powoduje wprowadzenia zmian w innej instancji. 3. Instancja nie może polimorficznie zastąpić instancji innego typu w czasie wykonania ani być przez taką instancję zastąpiona. Powyższa definicja jest atrakcyjna, ale zdecydowanie zbyt szeroka jak na mój gust. W dalszej części tego rozdziału nieco ją zawęzimy. Jednym z możliwych podejść do typów wartości jest ocena, czy i w jakim stopniu za- chowują się rozsądnie. Czego na przykład moglibyśmy się spodziewać po poniższych wyrażeniach? 5VTKPIUVT 2KGTYQVP[PCRKU  5VTKPIUVT 0KGFQUMQPCđ[  5VTKPIUVT    EJCTEQPUV EUUVTEAUVT  UVTUVT  UVTwyrażenie 1 KH UVT ]_wyrażenie 2 UVT ORV[ wyrażenie 3 UVTwyrażenie 4 Moim zdaniem wyrażenie 1. spowodowałoby sklejenie UVT,  i UVT (w tej kolej- ności). Wynik zostałby umieszczony w UVT, powodując nadpisanie, rozszerzenie lub zastąpienie zakresu pamięci, w którym w chwili utworzenia UVT został zapisany ciąg 100 Część I ♦ Podstawy 2KGTYQVP[PCRKU2. Stwierdziłbym też, że z chwilą zakończenia wyrażenia 1. wskaźnik EU stałby się niepoprawny i dalsze korzystanie z niego prowadziłoby do zachowania nieokreślonego (oczywiście nie byłoby tego problemu, gdyby metoda 5VTKPIEAUVT była zadeklarowana jako VGORQTCT[ [patrz punkt 16.2], gdyż przypisanie byłoby wtedy niedopuszczalne). Typowym rozumieniem wyrażenia 2. byłoby wykonanie bloku pod warunkiem, że UVT „nie ma”. Problem polega na ustaleniu, co dokładnie oznacza to „niebycie” — może chodzi o brak zawartości, może o zawieranie pustego ciągu , a może o jedno i drugie? Zresztą równie dobrze może chodzić o zawierania ciągu HCNUG! Takie wyrażenie jest niejednoznaczne, a wieloznaczność jest najgorszym wrogiem poprawności i łatwości pielęgnacji. Niestety, nieraz widywałem identyczne konstrukcje w kodzie produkcyjnym. Trzecie polecenie może oznaczać „opróżnij UVT”, ale równie dobrze może znaczyć „zwróć wartość wskazującą, czy ciąg UVT jest pusty”3. Osobiście wybrałbym zawsze pierwsze znaczenie wyrażenia, rozumując zgodnie z zasadą, że nazwami typów i zmien- nych są rzeczowniki, a nazwami metod i funkcji czasowniki. Niestety, biblioteka stan- dardowa nie zgadza się ze mną w tej kwestii, a trudno się nie zgadzać z biblioteką stan- dardową4. Wyrażenie 4. jest pozbawione sensu, gdyż nie przychodzi mi do głowy żaden rozsądny sposób inkrementacji ciągu znaków w C++5 (dodatek B przytacza historię z zamierzchłych czasów, gdy takich zahamowań nie miałem). W przypadku typów wbudowanych nie ma problemu z przewidywalnością zachowa- nia, gdyż jest ono z góry określone i nienaruszalne. Tworząc własne typy, mamy zatem obowiązek zapewnienia przewidywalności ich działania ze zdefiniowanymi operato- rami. Każdy, kto napisze na przykład typ całkowitoliczbowy o zwiększonej precyzji i zdefiniuje QRGTCVQT jako dzielenie modulo, zostanie nieuchronnie zlinczowany. Typy z założenia traktowane jak wartości powinny w miarę możliwości zachowywać się „tak jak KPV-y” [Meye1996]. W pozostałej części rozdziału przyjrzymy się całemu spektrum typów wartości. Wyróż- niam tu cztery poziomy: 1. Typy otwarte — zwykłe struktury z funkcjami API. 2. Typy hermetyzowane — częściowo lub całkowicie hermetyzowane typy klasowe, obsługiwane za pośrednictwem metod. i 2 Jest to wprawdzie wygodne, ale stosowanie operatora do napisów jest pewnym nadużyciem. Dodawanie jest operacją arytmetyczną i stosowanie jej do ciągów znakowych jest pierwszym krokiem na krętej i niebezpiecznej ścieżce (patrz dodatek B). W rozdziale 25. zignoruję jednak moje własne zastrzeżenia w pogoni za chwałą zwycięzcy. 3 Problem polega tu na niejednoznaczności słowa empty w nazwie metody. W pierwszym rozumieniu może to być forma rozkazująca czasownika, czyli polecenie opróżnij, natomiast w drugim rozumieniu jest to przymiotnik pusty — przyp. tłum. 4 W przypadku bibliotek STLSoft musiałem przełknąć swoje zasady i dostosować się do większości: małe litery, podkreślenia, GORV[ itd. 5 W Perlu i niektórych innych językach skryptowych występuje inkrementacja ciągów znakowych polegająca na dokonaniu interpretacji numerycznej ciągu, zwiększeniu wyniku o jeden i konwersji z powrotem na ciąg znaków. W przypadku Perla jest to jak najbardziej na miejscu, ale kod C++ takich rzeczy wyprawiać nie powinien. Rozdział 4. ♦ Hermetyzacja danych i typy wartości 101 3. Typy wartości — całkowicie hermetyzowane typy klasowe, w tym operatory przypisania, równości i nierówności. 4. Arytmetyczne typy wartości — używane do wartości numerycznych, w tym wszystkie operatory arytmetyczne. W zależności od punktu widzenia, za typy wartości można uznać wszystkie powyższe typy lub tylko dwa ostatnie. W klasyfikacji umieściłem jednak wszystkie, gdyż odpo- wiadają one odrębnym i stosowanym w praktyce przedziałom spektrum. 4.4. Typy otwarte 4.4.1. Otwarte typy POD Typami otwartymi nazwiemy typy proste o publicznie dostępnych składowych, najczę- ściej pozbawione metod — innymi słowy, są to agregaty (C++98: 8.5.1; 1). Rozważmy typ WKPVGIGT: UVTWEVWKPVGIGT ] WKPVAVNQYGT8CN WKPVAVWRRGT8CN _ Jest to struktura bardzo prosta i niemal całkowicie bezużyteczna. Jako taka stanowi świetny przykład otwartego typu wartości, gdyż, ogólnie rzecz biorąc, typy takie nie- specjalnie nadają się do użytku. Korzystanie z typów otwartych w charakterze typów wartości wymaga niezwykłej cier- pliwości. Nie mogą one brać udziału w prostych porównaniach, a wykonywanie na nich działań arytmetycznych wymaga ręcznego operowania na ich składowych. WKPVGIGTK WKPVGIGTK DQQND.GUUKKbłąd kompilacji! DQQND SWCNKKbłąd kompilacji! WKPVGIGTKK Kbłąd kompilacji! Powinniśmy jednak być wdzięczni językowi, że z marszu odrzuca wszystkie te ope- ratory, gdyż w ten sposób chroni nas już podczas kompilacji. Porównywanie według składowych byłoby bardzo niebezpieczne — prawdopodobnie dałoby się w wielu przy- padkach określić poprawną równość czy też nierówność, ale skąd kompilator miałby znać priorytety operatorów w sprawdzaniu mniejszości? (Warto wiedzieć, że dla zacho- wania zgodności wstecznej ze strukturami kompilator dopuszcza konstruktory kopiu- jące i przypisanie kopiujące [patrz podrozdział 2.2]). Pomimo poważnych problemów z praktycznym korzystaniem z typów otwartych, czasa- mi praca z nimi jest nieunikniona, najczęściej podczas łączenia się z API systemu opera- cyjnego lub biblioteki, na przykład w celu uzyskania dostępu do operacji arytmetycznych 102 Część I ♦ Podstawy o zwiększonej precyzji [Hans1997]. W celach instruktażowych załóżmy, że z jakichś względów musimy jednak korzystać ze zdefiniowanych wcześniej 64-bitowych liczb całkowitych. W takiej sytuacji trzeba utworzyć API do wykonywania operacji na tych typach. XQKF7+A#UUKIP WKPVGIGT NJUWKPVAVJKIJGT WKPVAVNQYGT  XQKF7+A#FF WKPVGIGT TGUWNVWKPVGIGTEQPUV NJU WKPVGIGTEQPUV TJU  XQKF7+A KXKFG WKPVGIGT TGUWNVWKPVGIGTEQPUV NJU WKPVGIGTEQPUV TJU  KPV7+A QORCTG WKPVGIGTEQPUV NJUWKPVGIGTEQPUV TJU  FGHKPG7+A+U.GUU6JCP RKRK  7+A QORCTG RKRK  FGHKPG7+A+U SWCN RKRK  7+A QORCTG RKRK FGHKPG7+A+U)TGCVGT6JCP RKRK  7+A QORCTG RKRK Z pomocą takiego API, możemy zaimplementować pierwotny kod w całkowicie legalny sposób: WKPVGIGTK WKPVGIGTK DQQND.GUU7+A+U.GUU6JCP KK  DQQND SWCN7+A+U SWCN KK  WKPVGIGTK 7+A#FF KKK  Straszne rzeczy. Na szczęście C++ umożliwia nam znaczne ulepszenie tego rozwiązania. 4.4.2. Struktury danych C++ Zanim wszyscy pobiegniemy do naszego kodu źródłowego i w obiektowym amoku zaczniemy nerwowo zamieniać każde UVTWEV na ENCUU, musimy sobie uświadomić, że dotychczas omówiliśmy tylko te otwarte typy danych, w których poszczególne składo- we stanowią logiczną całość, a wprowadzanie zmian w indywidualnych składowych stanowi ewidentne zagrożenie dla stanu logicznego całej struktury. Istnieją jednak typy otwarte, na których można wykonywać takie operacje i które tym samym nie stanowią żadnego zagrożenia. Odróżnianie typów bezpiecznych od nie- bezpiecznych opiera się na stwierdzeniu, czy wprowadzanie zmian w poszczególnych składowych prowadzi do naruszenia znaczenia całości. Spójrzmy na następujący typ określający walutę: UVTWEV WTTGPE[ ] KPVOCLQT7PKVdolary, złotówki KPVOKPQT7PKVcenty, grosze _ Jest to przykład niebezpiecznego typu otwartego, gdyż istnieje możliwość podania war- tości składowej OKPQT7PKV, która uczyni całą instancję typu WTTGPE[ logicznie błędną. Z kolei poniższy typ jest idealnym przykładem całkowicie bezpiecznego typu otwartego: Rozdział 4. ♦ Hermetyzacja danych i typy wartości 103 UVTWEV2CVTQP ] 5VTKPIPCOGnazwisko  WTTGPE[YCNNGVzasobność portfela _ Nie ma nierozerwalnego związku między nazwiskiem a zawartością portfela, stąd też wprowadzenie dowolnej zmiany w polu PCOG lub YCNNGV nie prowadzi do żadnego oczy- wistego naruszenia spójności logicznej typu 2CVTQP. Takie typy nazywane są strukturami danych C++ [Stro1997]. Jak zawsze w przypadku rozgraniczeń istnieje tu pewna szara strefa, ale powyższe dwa przykłady są całkowicie jednoznaczne: jeden jest czarny, a drugi biały. 4.5. Typy hermetyzowane Otwarte typy wartości POD są kruchymi tworami, a ich zastosowanie ogranicza się do przypadków, w których korzystanie z pojęć wyższego poziomu jest ewidentnie szko- dliwe dla innych wymagań, na przykład wydajności, współpracy między językami czy łączenia typów. Wydaje się oczywistym, że większość typów nie jest użyteczna, dopóki nie odpowiadają one co najmniej kolejnemu poziomowi, jakim są typy hermetyzowane (poniższą definicję można w równym stopniu stosować do typów egzystencjalnych). Definicja: Typy hermetyzowane umożliwiają odczyt i modyfikację stanu instancji obiek- tu za pośrednictwem odpowiedniego interfejsu publicznego. Kod kliencki nie powinien (i nie potrzebuje) mieć bezpośredniego dostępu do składowych takich typów. Typy hermetyzowane pozwalają pojęciowo odgraniczyć stan logiczny od fizycznego. Ogólnie rzecz biorąc, typy hermetyzowane pozwalają w większym stopniu ukryć imple- mentację (niekiedy całkowicie), co zapewnia bezpieczeństwo, oraz dostarczają metody bezpiecznego dostępu do odpowiedniej reprezentacji wartości. Operacje na składowych NQYGT8CN i WRRGT8CN naszego przykładowego typu WKPVGIGT mogłyby się odbywać za pośrednictwem metod klasy w rodzaju #FF , #UUKIP itp. Korzystanie z metod daje kontrolę dostępu i modyfikacji wewnętrznego stanu instancji, stąd też możliwe jest narzu- canie niezmienników klas (podrozdział 1.3), co pozwala znacznie podnieść jakość kodu. Typy mogą też udostępniać metody obsługi typowych lub spodziewanych operacji, by uniknąć ręcznego ich tworzenia w kodzie klienckim. Niestety, zbiór takich operacji jest zawsze otwarty, nawet jeśli (jako dobrzy obywatele) zachowujemy ortogonalność typu. Efekt ten można nieco złagodzić, stosując wolne funkcje w miejsce metod klas wszędzie tam, gdzie jest to możliwe [Meye2000]. Klasowa implementacja naszego przykładowego typu będzie wyglądać mniej więcej tak jak pokazana na listingu 4.1 klasa 7+PVGIGT. Zawiera ona zmienne składowe typu WKPVGIGT, co pozwala wykorzystać gotowe funkcje zdefiniowanego wcześniej API). 104 Listing 4.1. Część I ♦ Podstawy ENCUU7+PVGIGT ] RWDNKE 7+PVGIGT  7+PVGIGT WKPVAVNQY  7+PVGIGT WKPVAVJKIJWKPVAVNQY  KHFGH# / .+$A 1/2+. 4A57221465A$+6A+06 7+PVGIGT WKPVAVNQY  GPFKH ACMELIB_COMPILER_SUPPORTS_64BIT_INT  7+PVGIGT 7+PVGIGTEQPUVTJU    porównania RWDNKE UVCVKEDQQN+U.GUU6JCP 7+PVGIGTEQPUVK7+PVGIGTEQPUVK  UVCVKEDQQN+U SWCN 7+PVGIGTEQPUVK7+PVGIGTEQPUVK  UVCVKEDQQN+U)TGCVGT6JCP 7+PVGIGTEQPUVK7+PVGIGTEQPUVK  operacje arytmetyczne RWDNKE UVCVKE7+PVGIGT/WNVKRN[ 7+PVGIGTEQPUVK7+PVGIGTEQPUVK  UVCVKE7+PVGIGT KXKFG 7+PVGIGTEQPUVK7+PVGIGTEQPUVK  RTKXCVG WKPVGIGTOAXCNWG _ 4.6. Typy wartości Po typach hermetyzowanych już tylko mały krok dzieli nas od typów, które uważam za prawdziwe typy wartości. Głównym wyróżnikiem typu wartości jest moim zdaniem możliwość porównywania, czyli posiadanie cechy EqualityComparable [Aust1999, Muss2001]. Oznacza to zwracanie sensownych wyników porównań pod względem równości i nierówności, zgodnie z definicją Langera-Krefta [Lang2002] (patrz pod- rozdział 4.2). Dla typów klasowych kompilator nie udostępnia domyślnie operatorów porównania, więc musimy je zdefiniować samodzielnie. Przede wszystkim potrzebujemy typów, które zachowują się rozsądnie. Główny problem typów hermetyzowanych polega na niemożności ich użycia w kodzie wykorzystującym porównania pod względem równości. Szczególnie istotna jest możliwość umieszczania typów wartości w kontenerach składujących według wartości, w tym w kontenerach biblioteki standardowej. Możemy wprawdzie tworzyć instancje kontenera UVFXGEVQT 7+PVGIGT i operować nimi, gdyż kontener nie nakłada ograniczeń co do unikalno- ści składowanych elementów, jednak nie możemy wyszukiwać instancji naszego typu za pomocą standardowego algorytmu UVFHKPF : UVFXGEVQT7+PVGIGT XK XKRWUJADCEM K  Rozdział 4. ♦ Hermetyzacja danych i typy wartości 105 XKRWUJADCEM K  XKRWUJADCEM K  UVFHKPF XKUVCTV XKGPF 7+PVGIGT  błąd! brak operatora == dla UInteger64 (UVFXGEVQT przechowuje elementy bez porządkowania, więc nie ma potrzeby defi- niowania operatora  do porównania porządkującego). Przekształcenie naszego typu w typ wartości będzie bardzo proste. W poprzedniej implementacji 7+PVGIGT zdefiniowaliśmy metodę +U SWCN , którą możemy teraz wykorzystać do definicji operatorów równości i nierówności: KPNKPGDQQNQRGTCVQT 7+PVGIGTEQPUVK7+PVGIGTEQPUVK ] TGVWTP7+PVGIGT+U SWCN KK  _ KPNKPGDQQNQRGTCVQT 7+PVGIGTEQPUVK7+PVGIGTEQPUVK ] TGVWTPQRGTCVQT KK  _ Dodatkową zaletą takiego podejścia jest fakt, że uczyniliśmy typ pełnym typem warto- ści przez dodanie funkcji nieskładowych [Meye2000]. Funkcje te poprzednio nie istniały, stąd też na pewno nie istnieje żaden kod wymagający ich obecności (lub nieobecności) i tym samym osiągnęliśmy cel bez tworzenia jakichkolwiek problemów pielęgnacyj- nych (przytyk odautorski: to właśnie bezmyślne wciskanie wszystkiego do klas jest powodem niekontrolowanego puchnięcia wielu środowisk programistycznych i nie- sprawiedliwych opinii na temat braku wydajności C++). Możemy zatem powrócić do naszej definicji typu wartości i zaproponować następującą jej postać6: Definicja: Typ wartości Instancje obiektów nie mogą polimorficzne zastępować instancji innego typu w czasie uruchamiania ani być przez nie zastępowane. Instancje można tworzyć jako kopie innych instancji lub je takimi kopiami później uczynić. Każda instancja ma samodzielną tożsamość logiczną. Zmiana stanu logicznego jednej instancji nie wpływa na stan logiczny innej (stan fizyczny może być współdzielony, jeśli tak wskazują decyzje projektowe dla danej implementacji, ale tylko pod warunkiem, że nie wpływa to na niezależność logiczną). Instancje można porównywać z dowolnymi innymi instancjami (w tym z samymi sobą) pod względem równości i nierówności. Obie te relacje są zwrotne, symetryczne i prze- chodnie. i 6 Eugene twierdzi, że wymaganie sprawdzania równości nie jest konieczne, więc nie mogę ochrzcić definicji mianem definicji typu wartości Gershnika-Wilsona, jak by to gładko nie spływało z języka. 106 Część I ♦ Podstawy 4.7. Arytmetyczne typy wartości Ostatni krok naszej wspinaczki po drabinie typów wartości polega na dodaniu obsługi typowych operatorów arytmetycznych, co pozwoli naszemu przykładowemu typowi uczestniczyć w tradycyjnych działaniach, na przykład: WKPVGIGTK WKPVGIGTK DQQND.GUUKKw porządku DQQND SWCNKKw porządku WKPVGIGTKK Kw porządku KK KK K Najpierw zajmiemy się operatorem mniejszości QRGTCVQT , gdyż dzięki niemu stanie się możliwe porządkowanie instancji typu. Możliwość porównywania pod względem mniejszości jest określana mianem własności LessThanComparable [Aust1999] i jest nieodzowną cechą typów obsługiwanych przez STL — zgodnie ze standardem, jest to jedyna cecha wymagana. Przyznaję, że mam osobistą skłonność do korzystania wy- łącznie z tego porównania, nawet jeśli oznacza to pisanie mniej czytelnego kodu, na przykład pisanie CUUGTV  UKG KPFGZ zamiast CUUGTV KPFGZUKG . Nie jestem jednak wcale pewien, czy jest to praktyka godna polecenia. Możemy zatem dodać operator mniejszości do dotychczasowej definicji naszego typu, implementując go (podobnie jak w przypadku operatorów równości i nierówności) jako funkcję nieskładową. Instancje tak zdefiniowanego typu 7+PVGIGT możemy już umiesz- czać w kontenerach biblioteki standardowej, na przykład UVFUGV czy UVFOCR. W ten sam sposób można dodać wiele innych operatorów arytmetycznych, ale ich wybór zależy od konkretnego typu. Tworzony przez nas typ 7+PVGIGT jest typem całkowi- toliczbowym, stąd też wskazane byłoby zdefiniowanie wszystkich operatorów, a więc , , , , , `, , , , ^, @ i odpowiadających im operatorów przypisania (w rozdzia- le 29. przyjrzymy się sposobom optymalnego implementowania takich operatorów). Świetnym przykładem definicji wolnych operatorów jest klasa szablonowa VTWGAV[RGFGH (podrozdział 18.4), w której wszystkie operatory arytmetyczne dla klasy są zdefinio- wane jako wolne funkcje. Tak samo możemy postępować w przypadku innych typów. Najważniejszą kwestią jest zdefiniowanie tylko tych operatorów, które mają sens. Po- wracając do przykładu typu WTTGPE[ określającego walutę, z pewnością chcielibyśmy mieć możliwość dodawania i odejmowania instancji WTTGPE[ za pomocą operatorów i , ale już mnożenie nie miałoby większego sensu (na przykład $6.50 razy 2.93 PLN). Z kolei przydałaby się możliwość przemnożenia instancji WTTGPE[ przez liczbę, ale już dodawanie i odejmowanie liczb nie byłoby potrzebne. Oczywiście C++ pozwala zdefiniować dowolny operator dla dowolnej klasy i przypi- sać mu dowolne działanie, co może stanowić przedmiot karygodnych nadużyć (patrz dodatek B). W pierwszym przykładzie tego rozdziału widzieliśmy klasę 5VTKPI ze zde- finiowanymi operatorami i . Używanie operatora do łączenia łańcuchów znako- Rozdział 4. ♦ Hermetyzacja danych i typy wartości 107 wych jest błędem,7 gdyż nie oblicza on sumy arytmetycznej argumentów. Jest to jednak tak przydatne zastosowanie, że niemal wszyscy przymykamy oko na jego niemoral- ność i rozkoszujemy się wygodą (może i jest ono wygodne, ale na pewno nie wydajne; w rozdziale 25. zobaczymy, że można tę operację znacząco przyspieszyć). Nadużywanie operatorów jest wprawdzie szeroko akceptowaną praktyką, ale mimo to jest paskudnym zwyczajem i należy unikać tej pokusy. Wiem, bo sam niegdyś zgrze- szyłem. Bardzo zgrzeszyłem (patrz dodatek B). 4.8. Typy wartości — podsumowanie Gdy przyglądam się spektrum typów wartości, nasuwa mi się ciekawa analogia z iterato- rami STL. Najtrudniejszym do naśladowania iteratorem jest iterator dostępu bezpośred- niego, ale obsługują go wskaźniki, w których implementacji wyręcza nas sam język. Podobnie najtrudniejszym w implementacji pojęciem spośród możliwych typów warto- ści jest typ arytmetyczny, który z kolei jest domyślnym typem dla podstawowych typów całkowitoliczbowych. Potęga C++ przejawia się w zezwoleniu nam na własnoręczne definiowanie takich pojęć. Można też zadumać się nad faktem, że naszym szczytowym osiągnięciem jest określenie składni najbardziej podstawowych z typów. 4.9. Hermetyzacja — podsumowanie Dużo mówiliśmy o teoretycznych aspektach typów wartości, ale pozostaje jeszcze kwestia hermetyzacji takich typów. Z podręczników dowiemy się, że jako sumienni, obiektowi obywatele powinniśmy zawsze całkowicie hermetyzować typy. Niestety, rzeczywistość rzadko bywa taka prosta. Zaproponuję siedem możliwych implementacji typu 7+PVGIGT, z których tylko trzy są w pełni hermetyzowane. Wybór odpowiedniej w danym zastosowaniu postaci zależy od odpowiedzi na trzy podstawowe pytania. Czy potrzebujemy implementacji bazującej na istniejącym API w C lub z nim współpra- cującej? Jeśli tak, to nasza implementacja będzie musiała zawierać istniejącą strukturę (postać 2.) lub po niej dziedziczyć (postać 3.), gdyż tylko w ten sposób możemy prze- kazywać odpowiednie elementy klasy do API w C8. Jeśli nie, to wystarczy zawieranie składowych struktury (postać 1.). i 7 Jest to kolejne moje kontrowersyjne stwierdzenie, za które prawdopodobnie nieźle mi się oberwie. Musimy jednak pamiętać, że popularność ani nawet przydatność nie są równoznaczne z poprawnością, a ta książka dostarcza licznych tego przykładów. 8 Przynajmniej bez uciekania się do dyrektyw pakujących, rzutowania i innych paskudnych sztuczek. Lepiej skorzystać z samej struktury C. 108 Listing 4.2. Część I ♦ Podstawy postać #1 ENCUU7+PVGIGT ] metody i operatory typu wartości RTKXCVG WKPVAVNQYGT8CN WKPVAVWRRGT8CN _ postać #2 ENCUU7+PVGIGT ] metody i operatory typu wartości RTKXCVG WKPVGIGTOAXCNWG _ postać #3 ENCUU7+PVGIGT RTKXCVGWKPVGIGT ] metody i operatory typu wartości _ Z premedytacją wybrałem jako przykład 64-bitowe liczby całkowite, gdyż w rzeczywi- stej implementacji mogę oszukiwać, korzystając z konwersji z i na prawdziwe 64-bitowe liczby całkowite, które są używane do faktycznej realizacji operacji arytmetycznych. Gdybyśmy chcieli obsłużyć dowolnych rozmiarów liczby całkowite korzystające z API C (opisane na przykład w [Hans1997]), to musielibyśmy pracować na strukturach C. Czy wszystkie operacje można hermetyzować w ramach typu? Jeśli tak, to do zapew- nienia pełnej hermetyzacji wystarczy prawdopodobnie zadeklarowanie wewnętrznej implementacji jako RTKXCVG (postacie 1 – 3). Jeśli nie, to interakcja z innymi funkcjami czy typami będzie wymagała ujawnienia jakiejś części stanu wewnętrznego (w przy- padku typów możemy skorzystać z deklaracji typów zaprzyjaźnionych za pomocą mo- dyfikatora HTKGPF — patrz punkt 2.2.9). W ustaleniu, czy konieczne jest ujawnianie szczegółów implementacji na zewnątrz, pomoże kilka pytań pomocniczych. Czy będziemy tworzyć typ unikalny, czy jeden z wielu podobnych? Klasycznym przykła- dem może tu być obsługa czasu. W samym C mamy VKOGAV, UVTWEVVO, UVTWEVVKOGXCN, #6 , (+. 6+/ , 5;56 /6+/ i wiele innych typów czasowych, a jeśli dodatkowo uwzględnić typy C++, lista będzie praktycznie nieograniczona. Któż nie implementował własnego typu czasu czy daty? Jeśli nasz typ jest takim właśnie jednym z wielu, to musimy uwzględnić ewentualną potrzebę interakcji i wzajemnej konwersji z typami już istniejącymi. Czy potrzebujemy interakcji z interfejsem w stylu C? W świecie idealnym odpowiedź brzmiałaby zawsze „nie”, ale w świecie rzeczywistym jest nią często „tak”. Każda chyba implementacja klasy daty będzie bazować na jednym z typów C, gdyż w przeciwnym Rozdział 4. ♦ Hermetyzacja danych i typy wartości 109 razie musielibyśmy ręcznie przepisywać cały skomplikowany, żmudny i podatny na błędy kod do obsługi kalendarza. Może komuś lata przestępne, kalendarz gregoriański albo poprawki orbitalne? Dziękuję bardzo. Tylko jak się upewnić, że nasza hermetyza- cja objęła wszystkie potrzebne funkcje? Społeczność C++, ogólnie rzecz biorąc, ignoruje wszelkie informacje związane z tą kwestią. Moim zdaniem z wielką szkodą dla całej społeczności twórców oprogramowa- nia.9 Producenci narzędzi skwapliwie unikają wyprowadzania programistów z błędu, bo im bardziej programista przyzwyczai się do pracy z jakąś „naprawdę przydatną kla- są”, tym mniej będzie się interesować alternatywnymi rozwiązaniami. Implikacje takie- go przyzwyczajenia wykraczają daleko poza preferowanie jednego, wybranego środo- wiska. Z pewnością każdy miał do czynienia z projektami nieodwracalnie związanymi z określonym systemem operacyjnym tylko dlatego, że programiści nie chcieli (lub nie potrafili) wykroczyć poza ramy konkretnego środowiska tworzenia aplikacji. Jest to potężna niedoskonałość C++, z którą rozprawimy się bezlitośnie w części drugiej. W wielu zastosowaniach praktycznych ewidentnie nie mamy wyboru i musimy się za- dowolić częściową hermetyzacją. Można ją osiągnąć na kilka sposobów. Najprościej byłoby zmienić specyfikator dostępu z RTKXCVG na RWDNKE (postacie 4 – 6), ale spowo- dowałoby to ujawnienie całej zawartości klasy. Listing 4.3.  postać #4 ENCUU7+PVGIGT ] metody i operatory typu wartości RWDNKE WKPVAVNQYGT8CN WKPVAVWRRGT8CN _ postać #5 ENCUU7+PVGIGT ] metody i operatory typu wartości RWDNKE WKPVGIGTOAXCNWG _ postać #6 ENCUU7+PVGIGT RWDNKEWKPVGIGT ] metody i operatory typu wartości _ i 9 Niektórzy recenzenci tej książki narzekali nawet, że w publikacji ewidentnie poświęconej C++ nie powinienem omawiać przykładów kodu zgodnego z C. 110 Część I ♦ Podstawy Istnieją sposoby nieco bardziej dyskretnego ujawniania informacji. Dobrze sprawdzają się tu funkcje dostępowe, ale są one
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Język C++. Gotowe rozwiązania dla programistów
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: