Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00098 007725 10468441 na godz. na dobę w sumie
C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty - książka
C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty - książka
Autor: Liczba stron: 688
Wydawca: Helion Język publikacji: polski
ISBN: 83-7361-173-8 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c++ - programowanie
Porównaj ceny (książka, ebook, audiobook).

C++ nie jest tylko rozszerzeniem języka C, ale wprowadza zupełnie nowy model programowania. Stopień skomplikowania C++ może być przytłaczający nawet dla doświadczonych programistów C, jednak zazwyczaj nie sprawia im problemów napisanie i uruchomienie małego, niebanalnego programu w C++. Niestety, brak dyscypliny dopuszczalny przy tworzeniu małych programów, zupełnie nie sprawdza się w dużych projektach. Podstawowe użycie technologii C++ nie wystarczy do budowy dużych projektów. Na niezorientowanych czeka wiele pułapek.

Książka ta opisuje metody projektowania dużych systemów wysokiej jakości. Adresowana jest do doświadczonych programistów C++ próbujących stworzyć architekturę łatwą w obsłudze i możliwą do ponownego wykorzystania. Nie zawarto w niej teoretycznego podejścia do programowania. W tej książce znajdują się praktyczne wskazówki wypływające z wieloletnich doświadczeń ekspertów C++ tworzących ogromne systemy wielostanowiskowe. Autor pokazuje, jak należy projektować systemy, nad którymi pracują setki programistów, składające się z tysięcy klas i prawdopodobnie milionów linii kodu.

W książce opisano:

Dodatki do książki opisują przydatny wzorzec projektowy -- hierarchię protokołów, implementowanie interfejsu C++ zgodnego ze standardem ANSI C oraz pakiet służący do określania i analizowania zależności.

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 C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty Autor: John Lakos T³umaczenie: Wojciech Moch (rozdz. 1 – 4), Micha³ Dadan (rozdz. 5, 6), Rados³aw Meryk (rozdz. 7 – 10, dod. A – C) ISBN: 83-7361-173-8 Tytu³ orygina³u: Large-Scale C++ Software Design Format: B5, stron: 688 Przyk³ady na ftp: 52 kB C++ nie jest tylko rozszerzeniem jêzyka C, ale wprowadza zupe³nie nowy model programowania. Stopieñ skomplikowania C++ mo¿e byæ przyt³aczaj¹cy nawet dla doġwiadczonych programistów C, jednak zazwyczaj nie sprawia im problemów napisanie i uruchomienie ma³ego, niebanalnego programu w C++. Niestety, brak dyscypliny dopuszczalny przy tworzeniu ma³ych programów, zupe³nie nie sprawdza siê w du¿ych projektach. Podstawowe u¿ycie technologii C++ nie wystarczy do budowy du¿ych projektów. Na niezorientowanych czeka wiele pu³apek. Ksi¹¿ka ta opisuje metody projektowania du¿ych systemów wysokiej jakoġci. Adresowana jest do doġwiadczonych programistów C++ próbuj¹cych stworzyæ architekturê ³atw¹ w obs³udze i mo¿liw¹ do ponownego wykorzystania. Nie zawarto w niej teoretycznego podejġcia do programowania. W tej ksi¹¿ce znajduj¹ siê praktyczne wskazówki wyp³ywaj¹ce z wieloletnich doġwiadczeñ ekspertów C++ tworz¹cych ogromne systemy wielostanowiskowe. Autor pokazuje, jak nale¿y projektowaæ systemy, nad którymi pracuj¹ setki programistów, sk³adaj¹ce siê z tysiêcy klas i prawdopodobnie milionów linii kodu. W ksi¹¿ce opisano: • Tworzenie programów wieloplikowych w C++ • Konstruowanie komponentów • Podzia³ projektu fizycznego na poziomy • Ca³kowit¹ i czêġciow¹ izolacjê, regu³y jej stosowania • Tworzenie pakietów i ich podzia³ na poziomy • Projektowanie funkcji • Implementowanie metod Dodatki do ksi¹¿ki opisuj¹ przydatny wzorzec projektowy — hierarchiê protoko³ów, implementowanie interfejsu C++ zgodnego ze standardem ANSI C oraz pakiet s³u¿¹cy do okreġlania i analizowania zale¿noġci. 5RKUVTGħEK 2TGFOQYC  9RTQYCFGPKG W.1. Od C do C++ ...................................................5...................................................5.........15 W.2. Jak używać C++ do tworzenia dużych projektów ...................................................5...16 W.2.1. Zależności cykliczne...................................................5.....................................16 W.2.2. Nadmierne zależności na etapie konsolidacji ..................................................18 W.2.3. Nadmierne zależności na etapie kompilacji...................................................5..20 W.2.4. Globalna przestrzeń nazw...................................................5.............................22 W.2.5. Projekt logiczny i fizyczny ...................................................5...........................23 W.3. Ponowne użycie...................................................5...................................................5.....25 W.4. Jakość ...................................................5...................................................5....................25 W.4.1. Kontrola jakości...................................................5............................................27 W.4.2. Zapewnienie jakości ...................................................5.....................................27 W.5. Narzędzia......................................5...................................................5............................27 W.6. Podsumowanie ...................................................5...................................................5......28 úħè+ 2QFUVCY[F 4QFKCđ 9UVúR  1.1. Programy wieloplikowe w C++ ...................................................5.................................31 1.1.1. Deklaracja a definicja...................................................5.......................................31 1.1.2. Konsolidacja (łączenie) wewnętrzna a zewnętrzna .............................................33 1.1.3. Pliki nagłówkowe (.h) ...................................................5......................................36 1.1.4. Pliki implementacji (.c) ...................................................5....................................37 1.2. Deklaracje typedef ...................................................5...................................................5..38 1.3. Instrukcje sprawdzające ...................................................5.............................................39 1.4. Kilka słów na temat stylu...................................................5...........................................40 1.4.1. Identyfikatory...................................................5...................................................541 1.4.1.1. Nazwy typów...................................................5.......................................41 1.4.1.2. Nazwy identyfikatorów składające się z wielu słów...............................42 1.4.1.3. Nazwy składowych klas ...................................................5......................42 1.4.2. Kolejność ułożenia składowych w klasie ...................................................5.........44 1.5. Iteratory...................................................5...................................................5...................46 1.6. Logiczna notacja projektu ...................................................5..........................................52 1.6.1. Relacja Jest...................................................5...................................................5....53 1.6.2. Relacja Używa-W-Interfejsie ...................................................5...........................54 1.6.3. Relacja Używa-W-Implementacji ...................................................5....................56 1.6.3.1. Relacja Używa...................................................5.....................................58 1.6.3.2. Relacje Ma i Trzyma ...................................................5...........................58 1.6.3.3. Relacja Był ...................................................5..........................................59 6 C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty 1.7. Dziedziczenie i warstwy...................................................5.............................................60 1.8. Minimalizacja...................................................5...................................................5..........61 1.9. Podsumowanie ...................................................5...................................................5........62 4QFKCđ 2QFUVCYQYGTGIWđ[ 2.1. Przegląd ...................................................5...................................................5..................65 2.2. Dostęp do pól klasy...................................................5...................................................5.66 2.3. Globalna przestrzeń nazw ...................................................5..........................................70 2.3.1. Dane globalne...................................................5...................................................570 2.3.2. Wolne funkcje ...................................................5..................................................72 2.3.3. Wyliczenia, stałe i deklaracje typedef ...................................................5..............73 2.3.4. Makra preprocesora...................................................5..........................................74 2.3.5. Nazwy w plikach nagłówkowych...................................................5.....................75 2.4. Kontrola dołączeń ...................................................5...................................................5...77 2.5. Dodatkowa kontrola dołączeń...................................................5....................................79 2.6. Dokumentacja ...................................................5...................................................5.........84 2.7. Sposoby nazywania identyfikatorów...................................................5..........................86 2.8. Podsumowanie ...................................................5...................................................5........87 úħè++ 2TQLGMVHK[EP[F 4QFKCđ -QORQPGPV[  3.1. Komponenty a klasy...................................................5...................................................593 3.2. Reguły projektów fizycznych...................................................5...................................100 3.3. Relacja ZależyOd...................................................5...................................................5..108 3.4. Zależności implikowane...................................................5...........................................112 3.5. Wydobywanie rzeczywistych zależności ...................................................5.................117 3.6. Przyjaźń ...................................................5...................................................5................119 3.6.1. Przyjaźń na odległość i zależności implikowane...............................................122 3.6.2. Przyjaźń i oszustwo...................................................5........................................124 3.7. Podsumowanie ...................................................5...................................................5......126 4QFKCđ *KGTCTEJKCHK[EPC 4.1. Metafora dla testowania oprogramowania ...................................................5...............129 4.2. Złożony podsystem ...................................................5..................................................130 4.3. Problemy z testowaniem „dobrych” interfejsów ...................................................5......134 4.4. Projektowanie zorientowane na testowalność ...................................................5..........136 4.5. Testowanie pojedynczych modułów ...................................................5........................138 4.6. Acykliczne zależności fizyczne...................................................5................................140 4.7. Numery poziomów...................................................5...................................................5142 4.7.1. Źródła numeracji poziomów...................................................5...........................142 4.7.2. Używanie numerów poziomów w oprogramowaniu .........................................144 4.8. Testowanie hierarchiczne i przyrostowe ...................................................5..................147 4.9. Testowanie złożonych podsystemów ...................................................5.......................153 4.10. Testowalność kontra testowanie...................................................5.............................154 4.11. Cykliczne zależności fizyczne...................................................5................................155 4.12. Suma zależności komponentów ...................................................5.............................156 4.13. Jakość projektu fizycznego ...................................................5....................................161 4.14. Podsumowanie ...................................................5...................................................5....167 4QFKCđ 2QFKCđPCRQKQO[ 5.1. Niektóre przyczyny cyklicznych zależności fizycznych .............................................169 5.1.1. Rozszerzenie ...................................................5..................................................170 5.1.2. Wygoda ...................................................5...................................................5.......172 5.1.3. Wewnętrzna zależność ...................................................5...................................176 4QFKCđ Spis treści 7 5.2. Wyniesienie...................................................5...................................................5...........178 5.3. Obniżenie ...................................................5...................................................5..............187 5.4. Nieprzezroczyste wskaźniki...................................................5.....................................199 5.5. Głupie dane ...................................................5...................................................5...........206 5.6. Redundancja...................................................5...................................................5..........216 5.7. Wywołania zwrotne ...................................................5.................................................219 5.8. Klasa-menedżer...................................................5...................................................5.....231 5.9. Faktoring...................................................5...................................................5...............235 5.10. Wynoszenie enkapsulacji ...................................................5.......................................249 5.11. Podsumowanie ...................................................5...................................................5....260 +QNCELC  6.1. Od enkapsulacji do izolacji ...................................................5......................................262 6.1.1. Koszty powiązań na etapie kompilacji ...................................................5...........266 6.2. Konstrukcje w języku C++ a zależności na etapie kompilacji ....................................267 6.2.1. Dziedziczenie (Jest) a zależności na etapie kompilacji .....................................268 6.2.2. Podział na warstwy (Ma/Trzyma) a zależności na etapie kompilacji ................269 6.2.3. Funkcje inline a zależności na etapie kompilacji...............................................270 6.2.4. Składowe prywatne a zależności na etapie kompilacji ......................................271 6.2.5. Składowe chronione a zależności na etapie kompilacji.....................................273 6.2.6. Funkcje składowe generowane przez kompilator a zależności na etapie kompilacji ...................................................5..............................................274 6.2.7. Dyrektywy include a zależności na etapie kompilacji.......................................275 6.2.8. Argumenty domyślne a zależności na etapie kompilacji ...................................277 6.2.9. Wyliczenia a zależności na etapie kompilacji ...................................................5277 6.3. Techniki częściowej izolacji ...................................................5....................................279 6.3.1. Rezygnacja z dziedziczenia prywatnego ...................................................5........279 6.3.2. Usuwanie osadzonych danych składowych...................................................5....281 6.3.3. Usuwanie prywatnych funkcji składowych ...................................................5....282 6.3.4. Usuwanie składowych chronionych ...................................................5...............290 6.3.5. Usuwanie prywatnych danych składowych...................................................5....299 6.3.6. Usuwanie funkcji generowanych przez kompilator...........................................302 6.3.7. Usuwanie dyrektyw include ...................................................5...........................302 6.3.8. Usuwanie argumentów domyślnych...................................................5...............303 6.3.9. Usuwanie wyliczeń ...................................................5........................................305 6.4. Techniki całkowitej izolacji ...................................................5.....................................307 6.4.1. Klasa protokołu ...................................................5..............................................308 6.4.2. W pełni izolująca klasa konkretna...................................................5..................317 6.4.3. Izolujące komponenty otaczające...................................................5...................322 6.4.3.1. Pojedyncze komponenty otaczające ...................................................5..323 6.4.3.2. Wielokomponentowe warstwy otaczające............................................331 6.5. Interfejs proceduralny ...................................................5..............................................338 6.5.1. Architektura interfejsu proceduralnego ...................................................5..........339 6.5.2. Tworzenie i usuwanie nieprzezroczystych obiektów ........................................341 6.5.3. Uchwyty ...................................................5...................................................5......342 6.5.4. Uzyskiwanie dostępu do nieprzezroczystych obiektów i manipulowanie nimi ....346 6.5.5. Dziedziczenie a nieprzezroczyste obiekty ...................................................5......351 6.6. Izolować czy nie izolować ...................................................5.......................................354 6.6.1. Koszt izolacji...................................................5..................................................354 6.6.2. Kiedy nie należy izolować...................................................5..............................356 6.6.3. Jak izolować ...................................................5...................................................5360 6.6.4. Do jakiego stopnia należy izolować ...................................................5...............366 6.7. Podsumowanie ...................................................5...................................................5......373 8 C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty 4QFKCđ 2CMKGV[  7.1. Od komponentów do pakietów ...................................................5................................378 7.2. Zarejestrowane prefiksy pakietów...................................................5............................385 7.2.1. Potrzeba stosowania prefiksów ...................................................5......................385 7.2.2. Przestrzenie nazw...................................................5...........................................387 7.2.3. Zachowanie integralności pakietu ...................................................5..................391 7.3. Podział pakietów na poziomy ...................................................5..................................393 7.3.1. Znaczenie podziału pakietów na poziomy...................................................5......393 7.3.2. Techniki podziału pakietów na poziomy...................................................5........394 7.3.3. Podział systemu...................................................5..............................................396 7.3.4. Wytwarzanie oprogramowania w wielu ośrodkach...........................................398 7.4. Izolacja pakietów ...................................................5...................................................5..399 7.5. Grupy pakietów...................................................5...................................................5.....402 7.6. Proces wydawania oprogramowania ...................................................5........................406 7.6.1. Struktura wydania ...................................................5..........................................408 7.6.2. Łaty ...................................................5...................................................5.............413 7.7. Program main...................................................5...................................................5........415 7.8. Faza startu ...................................................5...................................................5.............421 7.8.1. Strategie inicjalizacji ...................................................5......................................423 7.8.1.1. Technika „przebudzenia” w stanie zainicjowania ................................424 7.8.1.2. Technika jawnego wywoływania funkcji init.......................................424 7.8.1.3. Technika wykorzystania specjalnego licznika......................................426 7.8.1.4. Technika sprawdzania za każdym razem..............................................431 7.8.2. Porządkowanie ...................................................5...............................................432 7.8.3. Przegląd...................................................5...................................................5.......433 7.9. Podsumowanie ...................................................5...................................................5......434 úħè+++ CICFPKGPKCFQV[EæEGRTQLGMVWNQIKEPGIQ  4QFKCđ 2TQLGMVQYCPKGMQORQPGPVÎY 8.1. Abstrakcje i komponenty ...................................................5.........................................439 8.2. Projekt interfejsu komponentu ...................................................5.................................440 8.3. Poziomy enkapsulacji...................................................5...............................................444 8.4. Pomocnicze klasy implementacyjne...................................................5.........................454 8.5. Podsumowanie ...................................................5...................................................5......459 4QFKCđ 2TQLGMVQYCPKGHWPMELK  9.1. Specyfikacja interfejsu funkcji...................................................5.................................462 9.1.1. Operator czy metoda?...................................................5.....................................462 9.1.2. Wolny operator czy składowa klasy? ...................................................5.............468 9.1.3. Metoda wirtualna czy niewirtualna?...................................................5...............472 9.1.4. Metoda czysto wirtualna czy nie czysto wirtualna? ..........................................476 9.1.5. Metoda statyczna czy niestatyczna? ...................................................5...............477 9.1.6. Metody stałe czy modyfikowalne? ...................................................5.................478 9.1.7. Metody publiczne, chronione czy prywatne...................................................5...483 9.1.8. Zwracanie wyniku przez wartość, referencję czy wskaźnik? ............................484 9.1.9. Zwracanie wartości typu const czy nie-const? ..................................................487 9.1.10. Argument opcjonalny czy obowiązkowy?...................................................5....488 9.1.11. Przekazywanie argumentów przez wartość, referencję lub wskaźnik .............490 9.1.12. Przekazywanie argumentów jako const lub nie-const .....................................495 9.1.13. Funkcja zaprzyjaźniona czy niezaprzyjaźniona?.............................................496 9.1.14. Funkcja inline czy nie inline?...................................................5.......................497 Spis treści 9 9.2. Typy podstawowe użyte w interfejsie ...................................................5......................498 9.2.1. Użycie typu short w interfejsie...................................................5.......................498 9.2.2. Użycie kwalifikatora unsigned w interfejsie ...................................................5..501 9.2.3. Zastosowanie typu long w interfejsie ...................................................5.............505 9.2.4. Zastosowanie typów float, double oraz long double w interfejsie.....................507 9.3. Funkcje specjalnego przeznaczenia...................................................5..........................508 9.3.1. Operatory konwersji...................................................5.......................................508 9.3.2. Semantyka wartości generowanych przez kompilator.......................................512 9.3.3. Destruktor...................................................5...................................................5....513 9.4. Podsumowanie ...................................................5...................................................5......515 4QFKCđ +ORNGOGPVQYCPKGQDKGMVÎY 10.1. Pola ...................................................5...................................................5.....................521 10.1.1. Wyrównanie naturalne...................................................5................................522 10.1.2. Użycie typów podstawowych w implementacji.............................................524 10.1.3. Użycie konstrukcji typedef w implementacji.................................................526 10.2. Definicje funkcji ...................................................5...................................................5.527 10.2.1. Samokontrola ...................................................5.............................................527 10.2.2. Unikanie przypadków szczególnych ...................................................5..........528 10.2.3. Podział zamiast powielania ...................................................5........................530 10.2.4. Zbytnia przebiegłość nie popłaca ...................................................5...............533 10.3. Zarządzanie pamięcią...................................................5.............................................533 10.3.1. Wartości stanu logicznego i fizycznego ...................................................5.....538 10.3.2. Parametry fizyczne ...................................................5.....................................541 10.3.3. Systemy przydziału pamięci...................................................5.......................545 10.3.4. Zarządzanie pamięcią na poziomie klasy ...................................................5...551 10.3.4.1. Dodawanie własnych mechanizmów zarządzania pamięcią ...........555 10.3.4.2. Zablokowana pamięć ...................................................5...................558 10.3.5. Zarządzanie pamięcią na poziomie obiektu...................................................5562 10.4. Użycie szablonów w dużych projektach ...................................................5................567 10.4.1. Implementacje szablonów ...................................................5..........................567 10.4.2. Zarządzanie pamięcią w szablonach...................................................5...........568 10.4.3. Wzorce a szablony...................................................5......................................577 10.5. Podsumowanie ...................................................5...................................................5....579 QFCVMKF QFCVGM# 9QTGERTQLGMVQY[ō2TQVQEQN*KGTCTEJ[ Protocol Hierarchy — struktury klas..................................................5................................585 Cel ...................................................5...................................................5.......................585 Znany też jako ...................................................5...................................................5.....585 Motywacja...................................................5...................................................5...........585 Zakres zastosowania...................................................5...............................................589 Struktura...................................................5...................................................5..............590 Elementy składowe...................................................5.................................................590 Współpraca...................................................5...................................................5..........591 Konsekwencje ...................................................5...................................................5.....591 Implementacja ...................................................5...................................................5.....592 Przykładowy kod...................................................5...................................................5.603 Znane zastosowania...................................................5................................................607 Pokrewne wzorce ...................................................5...................................................5608 10 C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty QFCVGM$ +ORNGOGPVQYCPKGKPVGTHGLUW IQFPGIQ GUVCPFCTFGO#05+  B.1. Wykrywanie błędu alokacji pamięci...................................................5........................611 B.2. Definiowanie procedury main (tylko ANSI C)...................................................5........618 QFCVGM  2CMKGVQMTGħNCPKCKCPCNKQYCPKCCNGľPQħEK  C.1. Korzystanie z poleceń adep, cdep i ldep...................................................5..................622 C.2. Dokumentacja wiersza polecenia ...................................................5............................633 C.3. Architektura pakietu idep ...................................................5........................................643 C.4. Kod źródłowy...................................................5...................................................5.......646 QFCVGM  .GMU[MQP D.1. Definicje...................................................5...................................................5...............647 D.2. Główne reguły projektowe ...................................................5......................................652 D.3. Poboczne reguły projektowe ...................................................5...................................653 D.4. Wskazówki...................................................5...................................................5...........654 D.5. Zasady ...................................................5...................................................5..................657 $KDNKQITCHKC  5MQTQYKF  Rozdział 9. 2TQLGMVQYCPKGHWPMELK Celem projektowania funkcji jest zapewnienie łatwego i wydajnego dostępu do operacji zdefiniowanych przez abstrakcję. Język C++ zapewnia swobodę definiowania interfejsu na poziomie funkcji. Czy funkcja ma być operatorem, metodą lub wolnym operatorem, w jaki sposób będą przekazywane argumenty oraz w jaki sposób będą przekazywane wyniki — to elementy należące do tego etapu procesu projektowania. Styl programo- wania to tylko jeden z elementów, które odgrywają rolę w podejmowaniu tego typu de- cyzji projektowych. Wiele z nich zostanie omówionych w niniejszym rozdziale. W języku C++ mamy do dyspozycji wiele odmian podstawowych typów reprezentują- cych liczby całkowite (jak np. UJQTV, WPUKIPGF, NQPIitp.). Typy te zapewniają dodat- kową swobodę. Nierozważne ich wykorzystanie może skomplikować lub nawet osłabić interfejs. Operator konwersji dla typów definiowanych przez użytkownika umożliwia kompilato- rowi wykonywanie niejawnej konwersji na lub z typu definiowanego przez użytkownika. Uważne projektowanie wymaga przeanalizowania możliwych zalet niejawnej konwersji w zestawieniu z niejednoznacznościami oraz możliwością powstania błędów w związku z obniżeniem bezpieczeństwa typów. Niektóre inne funkcje, w przypadku gdy nie zostaną określone jawnie, gdy zajdzie taka potrzeba mogą być zdefiniowane automatycznie przez kompilator. Podjęcie decyzji dotyczących dopuszczalności generowania definicji funk- cji przez kompilator wymaga uważnej analizy. W tym rozdziale opiszemy szkielet projektowania interfejsu komponentu na poziomie pojedynczej funkcji. Omówimy obszerną przestrzeń projektową dostępną dla autorów komponentów i wskażemy decyzje projektowe, które są korzystne lub niekorzystne. Przekonamy się, ile poziomów swobody w przestrzeni projektu interfejsu funkcji można wyeliminować bez strat w skuteczności. Uzyskany szkielet pomoże nam opracowywać interfejsy prostsze, bardziej jednolite i łatwiejsze do utrzymania. 462 Część III  Zagadnienia dotyczące projektu logicznego 5RGE[HKMCELCKPVGTHGLUWHWPMELK Zgodnie z podstawowymi zasadami przedstawionymi w rozdziale 2., podczas określa- nia interfejsu funkcji w języku C++ należy zwrócić uwagę na kilka aspektów:  Czy funkcja jest operatorem, czy nim nie jest?  Czy jest wolnym operatorem, czy elementem składowym klasy?  Czy jest to metoda wirtualna, czy niewirtualna?  Czy jest to metoda czysto wirtualna, czy też nie czysto wirtualna?  Metoda stała czy modyfikowalna?  Metoda typu EQPUV czy nie-EQPUV?  Metoda publiczna (RWDNKE), chroniona (RTQVGEVGF) czy prywatna (RTKXCVG)?  Wynik zwracany przez wartość, referencję czy wskaźnik?  Zwracany wynik typu EQPUV czy nie-EQPUV?  Argument opcjonalny czy obowiązkowy?  Argumenty przekazywane przez wartość, referencję czy wskaźnik?  Przekazywane argumenty typu EQPUV czy nie-EQPUV? Istnieją także dwa aspekty dotyczące organizacji, które należy wziąć pod uwagę, pomi- mo tego, że nie są one częścią logicznego interfejsu:  Czy jest to funkcja zaprzyjaźniona, czy też nie?  Funkcja typu inline, czy nie typu inline? Pomiędzy tymi aspektami istnieje wiele wzajemnych zależności. Zazwyczaj odpowiedź na jedno z pytań ma wpływ na odpowiedź na inne. W dalszej części niniejszego rozdziału omówimy wymienione zagadnienia osobno i podamy wskazówki umożliwiające podjęcie optymalnych decyzji projektowych1. 1RGTCVQTE[OGVQFC! Oprócz operatorów generowanych przez kompilator (np. przypisania), jedynym powo- dem utworzenia z funkcji operatora jest wygodna notacja wewnątrz klientów. Zwróćmy uwagę, że inaczej niż w przypadku notacji typowej dla funkcji, notacja operatorowa nie zależy od kontekstu — wywołanie funkcji w wyniku interpretacji operatora wywołane- go przez metodę będzie takie samo jak w przypadku wywołania w zasięgu pliku2. Roz- ważne wykorzystywanie przeciążania operatorów ma naturalną i oczywistą przewagę nad notacją funkcyjną — w szczególności w przypadku typów logicznych i arytmetycz- nych definiowanych przez użytkownika. k Zobacz też meyers, pozycja 19, str. 70. 1 2 ellis, punkt 13.4.1, str. 332. Rozdział 9.  Projektowanie funkcji 463 Przeanalizujmy dwa różne modele składni pokazane na listingu 9.1 odpowiadające dwóm różnym interfejsom dla komponentu zbioru liczb całkowitych RWDAKPVUGV. Na listingu 9.1a pokazano skuteczny sposób zastosowania notacji operatorowej. Natura abstrakcji zbioru powoduje, że znaczenie tych operatorów staje się intuicyj- ne nawet dla tych programistów, którzy nie znają pokazywanego komponentu. Na listingu 9.1b pokazano odpowiednik składni z zastosowaniem bardziej nieporęcznej notacji funkcyjnej3. .KUVKPIDwa modele składni dla abstrakcji zbioru liczb całkowitych KPENWFGRWDAKPVUGVJ KPENWFGKQUVTGCOJ KPENWFGRWDAKPVUGVJ KPENWFGKQUVTGCOJ KPVOCKP ] RWDA+PV5GVCDEFGH C C C C  D D D D  EC DFC DGCD HC D E D E F E F G EQWVHGPFN TGVWTP _ KPVOCKP ] RWDA+PV5GVCDEFGH CCFF  CCFF  CCFF  CCFF   DCFF  DCFF  DCFF  DCFF   ERWDA+PV5GVQT CD  FRWDA+PV5GVCPF CD  GRWDA+PV5GVUWD CD  HRWDA+PV5GVQT RWDA+PV5GVQT RWDA+PV5GVCPF RWDA+PV5GVCPF CD  E   RWDA+PV5GVCPF RWDA+PV5GVCPF DE  F    RWDA+PV5GVCPF RWDA+PV5GVCPF EF  G    RWDA+PV5GVRTKPV EQWVH GPFN TGVWTP _ (a) z przeciążaniem operatorów (b) bez przeciążania operatorów Podstawowym powodem stosowania mechanizmu przeciążania operatorów powinna być czytelność (w większym stopniu niż łatwość używania). k 3 Niektóre metody zdefiniowano jako statyczne, aby umożliwić tę samą symetryczną niejawną konwersję argumentów, jak w przypadku odpowiadających im operatorów (patrz punkt 9.1.5). Styl akapitów głęboko zagnieżdżonych wywołań funkcji na rysunku 9.1b zapożyczono z takich języków, jak LISP i CLOS, gdzie takie konstrukcje występują bardzo często. 464 Część III  Zagadnienia dotyczące projektu logicznego W omawianej aplikacji obsługi zbioru liczb całkowitych notacja operatorowa w oczy- wisty sposób poprawia zarówno czytelność, jak też łatwość używania. Przez czytelność rozumiemy zdolność inżyniera oprogramowania do rozróżniania w szybki i precyzyjny sposób znanego kodu treści funkcji od nieznanego kodu źródłowego. Łatwość używania dotyczy tego, jak łatwo programista może skutecznie wykorzystać obiekt w celu utwo- rzenia nowego oprogramowania. Zazwyczaj kod źródłowy odczytuje się więcej razy, niż się go zapisuje („w przypadku większości dużych, wykorzystywanych przez długi okres czasu systemów oprogramowania, koszty utrzymania przekraczają koszty wytwa- rzania od 2 do 4 razy4”). Semantyka przeciążonych operatorów powinna być naturalna, oczywista i intuicyjna dla klientów. Podczas projektowania często można otrzymać zgrabne i łatwe w użyciu aplikacje ope- ratorów, które nie mają intuicyjnego znaczenia dla programistów, nie znających naszego komponentu. Nierozsądne stare przyzwyczajenia, jak np. zdefiniowanie jednoargu- mentowego operatora ` jako składowej klasy 5VTKPI w celu odwrócenia kolejności zna- ków w ciągu, jest nie na miejscu w środowisku projektowym dużej skali. Papierkiem lakmusowym odpowiadającym na pytanie czy zastosować notację operatorową powin- no być stwierdzenie, czy istnieje naturalne i intuicyjne znaczenie — natychmiast zro- zumiałe dla nowych klientów, które poprawia poziom czytelności (lub przynajmniej go nie pogarsza)5. Syntaktyczne właściwości przeciążonych operatorów dla typów definiowanych przez użytkownika powinny być lustrzaną kopią właściwości zdefiniowanych dla typów podstawowych. Na poziomie semantycznym dość trudno dostarczyć szczegółowych wskazówek odno- śnie tego co jest, a co nie jest intuicyjne. Jednak na poziomie syntaktycznym, biorąc za podstawę implementację podstawowych typów języka, można sformułować kilka zde- cydowanych i dobrze zdefiniowanych stwierdzeń. Wzorowanie syntaktycznych właściwości operatorów zdefiniowanych przez użytkownika na predefiniowanych operatorach C++ pozwala na uniknięcie niespodzianek i sprawia, że sposób ich używania jest łatwiejszy do przewidzenia. W języku C++ każde wyrażenie ma wartość. Istnieją dwa podstawowe typy wartości — tzw. lwartości (ang. lvalue) oraz pwartości (ang. rvalue)6. Lwartość to taka wartość, dla której można wyznaczyć adres. Jeżeli lwartość może się znaleźć po lewej stronie wyra- żenia przypisania, mówi się o niej, że jest modyfikowalna, w innym przypadku określa się ją jako lwartość niemodyfikowalną7. Do pwartości nie można przypisać wartości, ani k 4 5 6 7 sommerville, punkt 1.2.1, str. 10. Patrz też cargill, rozdział 5., str. 91. Pojęcia te pochodzą z klasycznego języka C: pojęcie lwartość oznacza, że wartość wyrażenia może się znaleźć po lewej stronie instrukcji przypisania, natomiast pwartość może się znaleźć wyłącznie po jej prawej stronie. Wraz z pojawieniem się konstrukcji EQPUV w języku C++ i ANSI C, lwartości dzieli się teraz na dwie odmiany: modyfikowalne i niemodyfikowalne (patrz stroustrup, punkt 2.1.2, str. 46 – 47). ellis, podrozdział 3.7, str. 25 – 26. Rozdział 9.  Projektowanie funkcji 465 nie można pobrać jej adresu8. Najprostszą lwartością jest identyfikator zmiennej. Jeżeli zmienna nie jest zadeklarowana jako EQPUV, jest to lwartość modyfikowalna. Niektóre operatory, jak np. operator przypisania () i jego odmiany (   @ ^ ` ), preinkrementacji ( Z) i predekrementacji (Z) zastosowane do ty- pów podstawowych, zwracają modyfikowalne lwartości. Operatory te zawsze zwracają zapisywalną referencję do modyfikowanego argumentu. Np. hipotetyczna definicja ope- ratorów dla podstawowego typu FQWDNG (w przypadku jego implementacji jako klasy C++) może mieć postać taką, jak pokazano na listingu 9.2. .KUVKPIHipotetyczna implementacja podstawowego typu double ENCUUFQWDNG]7YCICPKGRQRTCYPGYLG[MW  RWDNKE FQWDNG ]_ FQWDNG KPV  FQWDNG EQPUVFQWDNG  `FQWDNG ]_ FQWDNGQRGTCVQT EQPUVFQWDNGF  FQWDNGQRGTCVQT  EQPUVFQWDNGF  FQWDNGQRGTCVQT EQPUVFQWDNGF  FQWDNGQRGTCVQT  EQPUVFQWDNGF  FQWDNGQRGTCVQT EQPUVFQWDNGF  FQWDNGQRGTCVQT RTGKPMTGOGPVCELC Z FQWDNGQRGTCVQT RTGFGMTGOGPVCELCZ FQWDNGQRGTCVQT KPV RQUVKPMTGOGPVCELCZ FQWDNGQRGTCVQT KPV RQUVFGMTGOGPVCELCZ FQWDNG QRGTCVQT LGFPQCTIWOGPVQY[QRGTCVQTCFTGUW EQPUVFQWDNG QRGTCVQT EQPUVLGFPQCTIWOGPVQY[QRGTCVQTCFTGUW _ FQWDNGQRGTCVQT EQPUVFQWDNGF LGFPQCTIWOGPVQY[ FQWDNGQRGTCVQT EQPUVFQWDNGF LGFPQCTIWOGPVQY[ KPVQRGTCVQT EQPUVFQWDNGF LGFPQCTIWOGPVQY[NQIKEP[QRGTCVQTPQV KPVQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT^^ EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  FQWDNGQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  FQWDNGQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  FQWDNGQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  FQWDNGQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT  EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  KPVQRGTCVQT  EQPUVFQWDNGNGHVEQPUVFQWDNGTKIJV  k 8 Pola bitowe są wyjątkiem w tym sensie, że mogą się znaleźć po lewej stronie wyrażenia przypisania, ale zgodnie z ARM (ellis, podrozdział 9.6, str. 184) nie można wyznaczyć ich adresu. Zasada ta dotyczy także zmiennych tymczasowych typów zdefiniowanych przez użytkownika, które nie posiadają nazwy (patrz punkt 9.1.2). 466 Część III  Zagadnienia dotyczące projektu logicznego Inne operatory pokazane na listingu 9.2 zwracają pwartość, ponieważ nie ma możliwości zwrócenia odpowiedniej lwartości. W przypadku symetrycznych operatorów binarnych (jak np. oraz ) wartość do zwrócenia nie jest ani argumentem lewym, ani prawym, ale nową wartością uzyskaną na podstawie obydwu, a zatem wynik musi być zwrócony przez wartość9. Operatory równości (, ) oraz operatory relacyjne (, , , ) zawsze zwracają pwartość typu KPVo wartości 0 lub 1, a zatem i w tym przypadku żaden z argumentów wejściowych nie jest odpowiednią wartością do zwrócenia. Operatory postinkrementacji i postdekrementacji to interesujący przypadek specjalny w tym sensie, że są to jedyne operatory, które modyfikują obiekt, a zatem nie ma odpowiedniej lwartości do zwrócenia: FQWDNGFQWDNGQRGTCVQT KPV ] FQWDNGVOR VJKU  VJKU TGVWTPVOR _ FQWDNGFQWDNGQRGTCVQT KPV ] FQWDNGVOR VJKU  VJKU TGVWTPVOR _ Jako bardziej subtelny przykład przeanalizujmy dwa modele składni odpowiadające abs- trakcji ogólnej tabeli symboli, pokazane na listingu 9.3. W obu przypadkach na podsta- wie parametru typu KPV tworzona jest tabela symboli, dodawane są dwa symbole, a na- stępnie poszukuje się parametru foo według nazwy. Ponieważ możliwe jest, że w tabeli nie ma symbolu o określonej nazwie, zatem funkcja wyszukująca nie powinna zwracać wyniku przez wartość, ani przez referencję — tak więc wynik jest zwracany przez wskaź- nik (zwróćmy uwagę na to, w jaki sposób i do jakiego stopnia wykorzystano zalety enkap- sulacji). Porównajmy tę sytuację z zastosowaniem operatora =? w odniesieniu do tablicy wartości KPV. Spodziewamy się uzyskać referencję do poindeksowanej wartości, nie zaś wskaźnik, który może mieć wartość PWNN. Ta różnica w składni pomiędzy zastosowa- niem operatora =? na listingu 9.3a oraz zastosowaniem operatora =? dla typów podstawo- wych powoduje, że notacja z wywołaniem funkcji pokazana na listingu 9.3b jest w tym przypadku lepsza. Zarezerwowanie notacji operatorowej dla tych przypadków, kiedy składnia stanowi lustrzane odbicie odpowiadającej jej składni dla typów podstawowych, wzmacnia skuteczność przeciążania operatorów. .KUVKPIDwa modele składni dla abstrakcji ogólnej tabeli symboli KPENWFGIGPAU[OVCDJ KPENWFGIGPAU[OVCDJ KPVOCKP ] IGPA5[O6CDKPV U U HQQ QRGTCVQT U DCT  N[RQO[UN EQPUVKPV XCNU=HQQ?  KPVOCKP ] IGPA5[O6CDKPV U UCFF HQQ  UCFF DCT  EQPUVKPV XCNUNQQMWR HQQ   (a) z przeciążaniem operatorów (b) bez przeciążania operatorów Na listingu 9.4. zestawiono deklaracje większości operatorów języka C++ w postaci, w jakiej użyto by ich w odniesieniu do podstawowych typów języka (podstawowe ope- ratory    nie dają zbyt wielu informacji). k 9 Bardziej szczegółowe objaśnienie znajduje się w meyers, pozycja 23, str. 82 – 84. Rozdział 9.  Projektowanie funkcji 467 .KUVKPIPodsumowanie właściwości niektórych podstawowych operatorów QRGTCVQT[QRQFQDP[EJFGMNCTCELCEJ ENCUU6] 6QRGTCVQT  ZZ RTGHKMU 6QRGTCVQT KPV Z Z RQUVHKMU 6 QRGTCVQT Z LGFPQCTIWOGPVQY[ EQPUV6 QRGTCVQT EQPUVZ LGFPQCTIWOGPVQY[ 6QRGTCVQT EQPUV6    @^ _ 6QRGTCVQT EQPUV6  ` LGFPQCTIWOGPVQYG KPVQRGTCVQT EQPUV6  LGFPQCTIWOGPVQY[ 6QRGTCVQT EQPUV6EQPUV6    @^ KPVQRGTCVQT EQPUV6EQPUV6    KPVQRGTCVQT EQPUV6EQPUV6 ^^ YRT[RCFMWV[RQYYRQUVCEKYUMCPKMQY PR26 ENCUU2] 6QRGTCVQT=? KPV EQPUVFQUVGRFQKPFGMUQYCPGLVCDNKE[ FYWCTIWOGPVQY[ 6QRGTCVQT EQPUVW[UMCPKGYUMC[YCPGLFCPGL LGFPQCTIWOGPVQY[ _ YRT[RCFMWV[RQYYRQUVCEKYUMCPKMQYPCUVCNG PR2 EQPUV6 ENCUU2 ] EQPUV6QRGTCVQT=? KPV EQPUVFQUVGRFQKPFGMUQYCPGLVCDNKE[ FYWCTIWOGPVQY[ EQPUV6QRGTCVQT EQPUVW[UMCPKGYUMC[YCPGLFCPGL FYWCTIWOGPVQY[ _ Zwróćmy uwagę, że operatory jednoargumentowe, które nie modyfikują argumentów, nie muszą być składowymi. Przykładowo, jednoargumentowy operator  z powodzeniem działa dla typu zdefiniowanego przez użytkownika, jak np. QUVTGCO, pomimo tego, że dla tego typu nie zdefiniowano operatora : KPENWFGKQUVTGCOJ XQKFI QUVTGCOQWV ] KH QWV ] EGTTUVTWOKGēY[LħEKQY[LGUVPKGYđCħEKY[GPFN TGVWTP _  _ Kod pokazany powyżej działa dlatego, ponieważ typ QUVTGCO „wie”, w jaki sposób ma się przekształcić na podstawowy typ (XQKF ), dla którego zdefiniowano operator . Gdyby operator  potraktowano jako składową hipotetycznej definicji klasy XQKF , nie można byłoby wykonać zdefiniowanej przez użytkownika konwersji, a wykonanie pokazanego powyżej kodu zakończyłoby się błędem kompilacji. Aby wyłączyć możliwość niejawnej konwersji argumentów „wolnego” jednoargumentowego operatora , należy zdefiniować operację  jako metodę — np. QDLPQV zamiast operatora10. k 10 Patrz też murray, podrozdział 2.5, str. 44. 468 Część III  Zagadnienia dotyczące projektu logicznego 9QNP[QRGTCVQTE[UMđCFQYCMNCU[! Decyzja dotycząca tego, czy funkcja definiująca operator ma być składową klasy, czy wolną funkcją, zależy od tego, czy pożądana jest niejawna konwersja typu skrajnego lewego operandu. Jeżeli operator modyfikuje ten operand, wówczas taka konwersja nie jest pożądana. Język C++ sam w sobie jest obiektywnym i właściwym standardem, według którego należy modelować operatory definiowane przez użytkownika. Zastanówmy się nad tym, co mogłoby się zdarzyć, gdybyśmy zdefiniowali operator konkatenacji ( ) dla klasy 5VTKPI jako wolną funkcję, zamiast emulacji podejścia zapo- życzonego z typów podstawowych. Zgodnie z tym, co pokazano na rysunku 9.1, utworze- nie operatora  jako wolnej funkcji umożliwiło niejawną konwersję jego lewego operandu EQPUVEJCT na tymczasowy obiekt RWDA5VTKPI (oznaczony tu jako V) o wartości foo. Chociaż w przypadku typów podstawowych ta zmienna tymczasowa byłaby pwartością, to właśnie do tymczasowego obiektu RWDA5VTKPI dołączono wartość „bar” (bez powstania błędu kompilacji)11. Ponieważ takie działanie zaskoczyłoby i zdenerwowało użytkow- ników, dobrze byłoby, aby je wyłączyć. 4[UWPGM Efekt implementacji operator+= jako wolnej funkcji k 11 Obecnie w języku C++ dozwolone jest modyfikowanie nienazwanych tymczasowych zmiennych typu zdefiniowanego przez użytkownika nieposiadających nazwy. Patrz murray, punkt 2.7.3, str. 53 – 55. Rozdział 9.  Projektowanie funkcji 469 Z drugiej strony spodziewamy się, że niektóre operatory (np. oraz ) będą działać bez względu na kolejność ich argumentów. Przeanalizujmy operator , który służy do kon- katenacji dwóch ciągów znaków i zwraca wynik swojego działania przez wartość. Język C++ pozwala na zdefiniowanie operatora jako składowej lub nieskładowej. To samo dotyczy operatora . Jeżeli zdecydujemy się na zdefiniowanie tych operatorów jako składowych, wówczas narazimy się na możliwość następującego, nienormalnego dzia- łania naszych klientów: XQKFH ] RWDA5VTKPIU HQQ V   KPVK VU DCTFQDTG VDCT UDNCF KUDCTFQDTG KDCTUDNCF _ Problem polega na tym, że deklaracje: RWDA5VTKPIQRGTCVQT EQPUV5VTKPITKIJV oraz RWDA5VTKPIQRGTCVQT EQPUV5VTKPITKIJV umożliwiają niejawną konwersję typu EJCT na RWDA5VTKPI po prawej stronie za pomocą konstruktora następującej postaci: RWDA5VTKPIRWDA5VTKPI EQPUVEJCT natomiast taka konwersja dla argumentu po lewej stronie nie jest możliwa12. Jeżeli ope- ratory te będą wolne, problem symetrii dla klasy RWDA5VTKPI zostanie rozwiązany, do czasu dodania operatora konwersji: RWDA5VTKPIQRGTCVQTEQPUVEJCT EQPUV Na listingu 9.5 pokazano problem powstały w wyniku dodania operatora konwersji (rzu- towania) z typu RWDA5VTKPI na EQPUV EJCT . Co jest dość dziwne, dwa niewątpliwie podobne operatory  oraz nie są identyczne pod względem przeciążania, w co (niestety naiwnie) chcielibyśmy wierzyć. Różnica polega na tym, że teraz istnieją dwa sposoby interpretacji operatora :  Jawna konwersja typu EJCT na RWDA5VTKPI i porównanie za pomocą QRGTCVQT EQPUV5VTKPIEQPUV5VTKPI .  Niejawna konwersja typu RWDA5VTKPI na EQPUVEJCT i porównanie za pomocą wbudowanego operatora  dla typów wskaźnikowych. Taki problem nie istnieje dla operatora , ponieważ w języku C++ nie ma sposobu „dodania” dwóch typów wskaźnikowych i dlatego w tym przypadku nie ma niejedno- znaczności. k 12 ellis, punkt 13.4.2, str. 333. 470 Część III  Zagadnienia dotyczące projektu logicznego .KUVKPINiejednoznaczności wynikające z zastosowania dwóch operatorów konwersji RWDA5VTKPIJ  ENCUURWDA5VTKPI]  RWDNKE RWDA5VTKPI EQPUVEJCT REE   QRGTCVQTEQPUVEJCT EQPUVPQY[QRGTCVQTMQPYGTULK _ KPVQRGTCVQT EQPUV5VTKPINGHVEQPUV5VTKPITKIJV  5VTKPIQRGTCVQT EQPUV5VTKPINGHVEQPUV5VTKPITKIJV   KPENWFGRWDA5VTKPIJ XQKFH ] RWDA5VTKPIU HQQ V   KPVK VU DCTFQDTG VDCT UFQDTG KUDCTDNCF PKGLGFPQPCEPG KDCTUDNCF PKGLGFPQPCEPG KUVTNGP U FQDTG _ W przypadku klasy 5VTKPI wykorzystywanej w praktyce nie będziemy polegać na nie- jawnej konwersji w celu uzyskania wartości znakowej, ze względu na obawę, że taka dodatkowa konstrukcja i destrukcja spowoduje zbytni spadek wydajności. Zamiast tego zdefiniujemy osobne przeciążone wersje operatora , których zadaniem będzie jak naj- bardziej wydajna obsługa każdej z trzech możliwości, a tym samym rozwiązanie pro- blemów niejednoznaczności. Niespójności powstałe w wyniku przeciążania operatorów mogą być oczywiste, denerwujące i kosztowne dla użytkowników. Zgodnie z tym, co pokazano na listingu 9.6, dla umożliwienia zaakceptowania wartości EQPUV EJCT po lewej stronie operatora  jesteśmy zmuszeni do zdefiniowania co najmniej jednej z funkcji operatorów porównania jako wolnej funkcji. .KUVKPIWynik zaimplementowania operatora == jako funkcji składowej ENCUURWDA5VTKPI]  RWDNKE RWDA5VTKPI EQPUVEJCT REE  QRGTCVQTEQPUVEJCT EQPUV KPVQRGTCVQT EQPUVEJCT REE EQPUVN[RQO[UN CU[OGVT[EP[ 7OQNKYKCMQPYGTULGFGHKPKQYCPGRTGW[VMQYPKMCV[NMQ FNCCTIWOGPVQYRQRTCYGLUVTQPKGQRGTCVQTC Rozdział 9.  Projektowanie funkcji 471 _ KPVQRGTCVQT EQPUVRWDA5VTKPINGHVEQPUVRWDA5VTKPITKIJV  KPVQRGTCVQT EQPUVEJCT NGHVEQPUVRWDA5VTKPITKIJV  7OQNKYKCU[OGVT[EPGMQPYGTULGFGHKPKQYCPGRTGW[VMQYPKMC FNCCTIWOGPVQYCTQYPQRQRTCYGLLCMRQNGYGLUVTQPKGQRGTCVQTC UVTWEV(QQ] (QQ  QRGTCVQTEQPUVRWDA5VTKPI EQPUV 0KGLCYPCMQPYGTULCV[RW(QQPCRWDA5VTKPI _ UVTWEV$CT] $CT  QRGTCVQTEQPUVEJCT EQPUV 0KGLCYPCMQPYGTULCV[RW$CTPC EQPUVEJCT  _ XQKFI ] (QQHQQ $CTDCT KH DCTHQQ ]FQDTG$CTPC  EQPUVEJCT (QQPC  EQPUVRWDA5VTKPI _ KH HQQōDCT ]DNCF(QQ0+   EQPUVRWDA5VTKPI $CTPC  EQPUVEJCT _ _ Na czym polega problem zdefiniowania jednej wersji funkcji QRGTCVQT jako składowej w przypadku, gdy zdefiniowano wszystkie trzy wersje tego operatora? Otóż problem polega na tym, że brak symetrii mógłby zaskoczyć użytkowników. W przypadku gdy jeden obiekt może być niejawnie przekształcony na RWDA5VTKPI, a drugi na EQPUVEJCT , spodziewamy się, że porządek porównania jest nieistotny. Jeżeli zatem konstrukcja DCTHQQ kompiluje się bez problemów, podobnie powinno być w przypadku zapisu HQQDCT (wyniki wykonania obu konstrukcji powinny być identyczne). Jednak jeżeli wersja: KPVQRGTCVQT EQPUVRWDA5VTKPIEQPUVEJCT  nie będzie dostępna jako wolna funkcja, wówczas nie będzie sposobu przeprowadzenia następującej konwersji: 472 Część III  Zagadnienia dotyczące projektu logicznego Wniosek jest taki, że operator zawsze powinien być zdefiniowany jako wolna funk- cja niezależnie od zastosowania innych funkcji. Te same powody dotyczą innych ope- ratorów dwuargumentowych, które nie modyfikują żadnego z operandów i zwracają swój wynik przez wartość. Przykład, jaki daje sam język, jest bezstronnym i użytecznym modelem, który może służyć klientom do tworzenia podstawowych syntaktycznych i aksjomatycznych wła- ściwości operatorów. Celem modelowania podstawowych operacji nie jest umożliwia- nie niejawnych konwersji samo w sobie, ale raczej zapewnienie symetrii po to, by unik- nąć niespodzianek. W przypadku zastosowania przeciążania operatorów w szerokim zakresie należy się spodziewać, że abstrakcja może być wykorzystywana wielokrotnie w wielu sytuacjach. Użytkownicy komponentów wielokrotnego użytku docenią spójny i profesjonalny interfejs niezależnie od syntaktycznych niespodzianek. Zwróćmy uwagę na to, że w języku C++ wymagane jest, aby zdefiniować jako składowe następujące operatory13:  =?  6 PGY przypisania, indeksu, dostępu do składowej klasy, wywołania funkcji, konwersji, przydział pamięci (statyczny), FGNGVG zwolnienie pamięci (statyczne). /GVQFCYKTVWCNPCE[PKGYKTVWCNPC! Dynamiczne wiązanie umożliwia definiowanie metod, do których dostęp odbywa się za pomocą klasy podstawowej, przez rzeczywisty podtyp obiektu, w odróżnieniu do typu wskaźnika lub referencji użytej w wywołaniu. W celu zapewnienia dynamicznego wią- zania funkcję należy zadeklarować jako wirtualną (XKTVWCN). W języku C++ wirtualne mogą być tylko metody. Jednak wniosek, że polimorficzne działanie operatora wymaga, aby stał się funkcją składową, gdy w innym przypadku byłby wolną funkcją, jest błędny. W celu osiągnięcia polimorficznego działania operatorów nie trzeba naruszać zagadnień syntaktycznych, jak np. symetrycznej niejawnej konwersji operatorów dwuargumentowych. Na rysunku 9.2 pokazano, w jaki sposób operatory symetryczne mogą i powinny pozo- stać operatorami wolnymi pomimo zastosowania poliformizmu. Zamiast przekształca- nia każdego z sześciu operatorów porównań i relacji na wirtualne metody klasy, opra- cowano jedną wirtualną metodę porównawczą. Te sześć operatorów w dalszym ciągu będzie działać symetrycznie bez względu na niejawną konwersję dowolnego typu. k 13 ellis, podrozdział 12.3c, str. 306; stroustrup94, punkt 3.6.2, str. 82 – 83. Rozdział 9.  Projektowanie funkcji 473 4[UWPGM Polimorficzne porównanie figur geometrycznych za pomocą wolnych operatorów IGQOAUJCRGJ KHPFGH+0 .7 A) 1/A5*#2 FGHKPG+0 .7 A) 1/A5*#2 ENCUUIGQOA5JCRG] RWDNKE XKTVWCN`IGQOA5JCRG  XKTVWCNEQPUVXQKF ENCUU+F EQPUV XKTVWCNKPVEQORCTG EQPUVIGQOA5JCRGUJCRG EQPUV  YTCECNKEDGWLGOPCGTQNWDFQFCVPKCCNGPKGQFVGIQ E[QDKGMVIGQOA5JCRGLGUVQFRQYKGFPKQOPKGLU[TQYP[E[ YKGMU[QF QMTGUNQPGIQKPPGIQQDKGMVWIGQOA5JCRG _ KPNKPGKPVQRGTCVQT ENCUUIGQOA5JCRGNGHVENCUUIGQOA5JCRGTKIJV ] TGVWTPNGHVEQORCTG TKIJV _ KPNKPGKPVQRGTCVQT ENCUUIGQOA5JCRGNGHVENCUUIGQOA5JCRGTKIJV ] TGVWTPNGHVEQORCTG TKIJV _ KPNKPGKPVQRGTCVQT ENCUUIGQOA5JCRGNGHVENCUUIGQOA5JCRGTKIJV ] TGVWTPNGHVEQORCTG TKIJV  KPNKPGKPVQRGTCVQT ENCUUIGQOA5JCRGNGHVENCUUIGQOA5JCRGTKIJV ] TGVWTPNGHVEQORCTG TKIJV _ KPNKPGKPVQRGTCVQT  ENCUUIGQOA5JCRGNGHVENCUUIGQOA5JCRGTKIJV ] TGVWTPNGHVEQORCTG TKIJV  _ KPNKPGKPVQRGTCVQT ENCUUIGQOA5JCRGNGHVENCUUIGQOA5JCRGTKIJV ] TGVWTPNGHVEQORCTG TKIJV   GPFKH Operatory porównań często mają sens nawet wtedy, gdy operatory relacji nie mają sensu (weźmy pod uwagę abstrakcję punktu). Czasami sortowanie heterogenicznych kolekcji pozwala na uzyskanie wydajniejszego dostępu. W takich przypadkach przydaje się wy- korzystanie porządkowania (dowolnego typu). Wirtualna metoda ENCUU+F pokazana na rysunku 9.2 pozwala na zdefiniowanie własnego identyfikatora typu fazy wykonania14. Dzięki wykorzystaniu tego identyfikatora można sortować figury tego samego typu wy- korzystując zdefiniowany dla nich indywidualny porządek, natomiast sortowanie innych typów można zdefiniować za pomocą innego (zupełnie dowolnego) porównania. Imple- mentację klasy IGQOA KTENG wykorzystywanej do porządkowania figur pokazano na li- stingu 9.7. Metody wirtualne implementują różnice w działaniu, natomiast pola — różnice w wartościach. k 14 Patrz ellis, podrozdział 10.2, str. 212 – 213. 474 Część III  Zagadnienia dotyczące projektu logicznego .KUVKPIImplementacja polimorficznego porównania dla klasy geom_Circle IGQOAEKTENGJ KHPFGH+0 .7 A) 1/A +4 . FGHKPG+0 .7 A) 1/A +4 . KHPFGH+0 .7 A) 1/A5*#2 KPENWFGIGQOAUJCRGJ GPFKH ENCUUIGQOA KTENGRWDNKEIGQOA5JCRG] UVCVKEEQPUVXQKF FAENCUU+FAR FQWDNGFATCFKWU RWDNKE IGQOA KTENG FQWDNGTCFKWU FATCFKWU TCFKWU ]_ IGQOA KTENG EQPUVIGQOA KTENGEKTENG FATCFKWU EKTENGFATCFKWU ]_ `IGQOA KTENG EQPUVIGQOA KTENGEKTENG  IGQOA KTENGQRGTCVQT EQPUVIGQOA KTENGEKTENG ]
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

C++. Projektowanie systemów informatycznych. Vademecum profesjonalisty
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ą: