Professional Documents
Culture Documents
PRZYKADOWY ROZDZIA
SPIS TRECI
KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG
TWJ KOSZYK
C++. Projektowanie
systemw informatycznych.
Vademecum profesjonalisty
Autor: John Lakos
Tumaczenie: Wojciech Moch (rozdz. 1 4), Micha Dadan
(rozdz. 5, 6), Radosaw Meryk (rozdz. 7 10, dod. A C)
ISBN: 83-7361-173-8
Tytu oryginau: Large-Scale C++ Software Design
Format: B5, stron: 688
Przykady na ftp: 52 kB
DODAJ DO KOSZYKA
CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK
CZYTELNIA
FRAGMENTY KSIEK ONLINE
C++ nie jest tylko rozszerzeniem jzyka C, ale wprowadza zupenie nowy model
programowania. Stopie skomplikowania C++ moe by przytaczajcy nawet dla
dowiadczonych programistw C, jednak zazwyczaj nie sprawia im problemw napisanie
i uruchomienie maego, niebanalnego programu w C++. Niestety, brak dyscypliny
dopuszczalny przy tworzeniu maych programw, zupenie nie sprawdza si w duych
projektach. Podstawowe uycie technologii C++ nie wystarczy do budowy duych
projektw. Na niezorientowanych czeka wiele puapek.
Ksika ta opisuje metody projektowania duych systemw wysokiej jakoci.
Adresowana jest do dowiadczonych programistw C++ prbujcych stworzy
architektur atw w obsudze i moliw do ponownego wykorzystania. Nie zawarto
w niej teoretycznego podejcia do programowania. W tej ksice znajduj si praktyczne
wskazwki wypywajce z wieloletnich dowiadcze ekspertw C++ tworzcych
ogromne systemy wielostanowiskowe. Autor pokazuje, jak naley projektowa systemy,
nad ktrymi pracuj setki programistw, skadajce si z tysicy klas i prawdopodobnie
milionw linii kodu.
W ksice opisano:
Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl
5RKUVTGEK
W.1. Od C do C++ ............................................................................................................... 15
W.2. Jak uywa C++ do tworzenia duych projektw ...................................................... 16
W.2.1. Zalenoci cykliczne........................................................................................ 16
W.2.2. Nadmierne zalenoci na etapie konsolidacji .................................................. 18
W.2.3. Nadmierne zalenoci na etapie kompilacji..................................................... 20
W.2.4. Globalna przestrze nazw................................................................................ 22
W.2.5. Projekt logiczny i fizyczny .............................................................................. 23
W.3. Ponowne uycie........................................................................................................... 25
W.4. Jako .......................................................................................................................... 25
W.4.1. Kontrola jakoci............................................................................................... 27
W.4.2. Zapewnienie jakoci ........................................................................................ 27
W.5. Narzdzia..................................................................................................................... 27
W.6. Podsumowanie ............................................................................................................ 28
1.1. Programy wieloplikowe w C++ .................................................................................... 31
1.1.1. Deklaracja a definicja.......................................................................................... 31
1.1.2. Konsolidacja (czenie) wewntrzna a zewntrzna ............................................. 33
1.1.3. Pliki nagwkowe (.h) ......................................................................................... 36
1.1.4. Pliki implementacji (.c) ....................................................................................... 37
1.2. Deklaracje typedef ........................................................................................................ 38
1.3. Instrukcje sprawdzajce ................................................................................................ 39
1.4. Kilka sw na temat stylu .............................................................................................. 40
1.4.1. Identyfikatory...................................................................................................... 41
1.4.1.1. Nazwy typw..........................................................................................41
1.4.1.2. Nazwy identyfikatorw skadajce si z wielu sw...............................42
1.4.1.3. Nazwy skadowych klas .........................................................................42
1.4.2. Kolejno uoenia skadowych w klasie ............................................................ 44
1.5. Iteratory......................................................................................................................... 46
1.6. Logiczna notacja projektu ............................................................................................. 52
1.6.1. Relacja Jest.......................................................................................................... 53
1.6.2. Relacja Uywa-W-Interfejsie .............................................................................. 54
1.6.3. Relacja Uywa-W-Implementacji ....................................................................... 56
1.6.3.1. Relacja Uywa........................................................................................58
1.6.3.2. Relacje Ma i Trzyma ..............................................................................58
1.6.3.3. Relacja By .............................................................................................59
2.1. Przegld ........................................................................................................................ 65
2.2. Dostp do pl klasy....................................................................................................... 66
2.3. Globalna przestrze nazw ............................................................................................. 70
2.3.1. Dane globalne...................................................................................................... 70
2.3.2. Wolne funkcje ..................................................................................................... 72
2.3.3. Wyliczenia, stae i deklaracje typedef ................................................................. 73
2.3.4. Makra preprocesora............................................................................................. 74
2.3.5. Nazwy w plikach nagwkowych........................................................................ 75
2.4. Kontrola docze ......................................................................................................... 77
2.5. Dodatkowa kontrola docze....................................................................................... 79
2.6. Dokumentacja ............................................................................................................... 84
2.7. Sposoby nazywania identyfikatorw............................................................................. 86
2.8. Podsumowanie .............................................................................................................. 87
3.1. Komponenty a klasy...................................................................................................... 93
3.2. Reguy projektw fizycznych...................................................................................... 100
3.3. Relacja ZaleyOd........................................................................................................ 108
3.4. Zalenoci implikowane.............................................................................................. 112
3.5. Wydobywanie rzeczywistych zalenoci .................................................................... 117
3.6. Przyja ...................................................................................................................... 119
3.6.1. Przyja na odlego i zalenoci implikowane............................................... 122
3.6.2. Przyja i oszustwo........................................................................................... 124
3.7. Podsumowanie ............................................................................................................ 126
4.1. Metafora dla testowania oprogramowania .................................................................. 129
4.2. Zoony podsystem ..................................................................................................... 130
4.3. Problemy z testowaniem dobrych interfejsw ......................................................... 134
4.4. Projektowanie zorientowane na testowalno ............................................................. 136
4.5. Testowanie pojedynczych moduw ........................................................................... 138
4.6. Acykliczne zalenoci fizyczne................................................................................... 140
4.7. Numery poziomw...................................................................................................... 142
4.7.1. rda numeracji poziomw.............................................................................. 142
4.7.2. Uywanie numerw poziomw w oprogramowaniu ......................................... 144
4.8. Testowanie hierarchiczne i przyrostowe ..................................................................... 147
4.9. Testowanie zoonych podsystemw .......................................................................... 153
4.10. Testowalno kontra testowanie................................................................................ 154
4.11. Cykliczne zalenoci fizyczne................................................................................... 155
4.12. Suma zalenoci komponentw ................................................................................ 156
4.13. Jako projektu fizycznego ....................................................................................... 161
4.14. Podsumowanie .......................................................................................................... 167
5.1. Niektre przyczyny cyklicznych zalenoci fizycznych ............................................. 169
5.1.1. Rozszerzenie ..................................................................................................... 170
5.1.2. Wygoda ............................................................................................................. 172
5.1.3. Wewntrzna zaleno ...................................................................................... 176
Spis treci
$% !&
6.1. Od enkapsulacji do izolacji ......................................................................................... 262
6.1.1. Koszty powiza na etapie kompilacji .............................................................. 266
6.2. Konstrukcje w jzyku C++ a zalenoci na etapie kompilacji .................................... 267
6.2.1. Dziedziczenie (Jest) a zalenoci na etapie kompilacji ..................................... 268
6.2.2. Podzia na warstwy (Ma/Trzyma) a zalenoci na etapie kompilacji ................ 269
6.2.3. Funkcje inline a zalenoci na etapie kompilacji............................................... 270
6.2.4. Skadowe prywatne a zalenoci na etapie kompilacji ...................................... 271
6.2.5. Skadowe chronione a zalenoci na etapie kompilacji ..................................... 273
6.2.6. Funkcje skadowe generowane przez kompilator a zalenoci
na etapie kompilacji ................................................................................................. 274
6.2.7. Dyrektywy include a zalenoci na etapie kompilacji....................................... 275
6.2.8. Argumenty domylne a zalenoci na etapie kompilacji ................................... 277
6.2.9. Wyliczenia a zalenoci na etapie kompilacji ................................................... 277
6.3. Techniki czciowej izolacji ....................................................................................... 279
6.3.1. Rezygnacja z dziedziczenia prywatnego ........................................................... 279
6.3.2. Usuwanie osadzonych danych skadowych....................................................... 281
6.3.3. Usuwanie prywatnych funkcji skadowych ....................................................... 282
6.3.4. Usuwanie skadowych chronionych .................................................................. 290
6.3.5. Usuwanie prywatnych danych skadowych....................................................... 299
6.3.6. Usuwanie funkcji generowanych przez kompilator........................................... 302
6.3.7. Usuwanie dyrektyw include .............................................................................. 302
6.3.8. Usuwanie argumentw domylnych.................................................................. 303
6.3.9. Usuwanie wylicze ........................................................................................... 305
6.4. Techniki cakowitej izolacji ........................................................................................ 307
6.4.1. Klasa protokou ................................................................................................. 308
6.4.2. W peni izolujca klasa konkretna..................................................................... 317
6.4.3. Izolujce komponenty otaczajce...................................................................... 322
6.4.3.1. Pojedyncze komponenty otaczajce .....................................................323
6.4.3.2. Wielokomponentowe warstwy otaczajce............................................331
6.5. Interfejs proceduralny ................................................................................................. 338
6.5.1. Architektura interfejsu proceduralnego ............................................................. 339
6.5.2. Tworzenie i usuwanie nieprzezroczystych obiektw ........................................ 341
6.5.3. Uchwyty ............................................................................................................ 342
6.5.4. Uzyskiwanie dostpu do nieprzezroczystych obiektw i manipulowanie nimi .... 346
6.5.5. Dziedziczenie a nieprzezroczyste obiekty ......................................................... 351
6.6. Izolowa czy nie izolowa .......................................................................................... 354
6.6.1. Koszt izolacji..................................................................................................... 354
6.6.2. Kiedy nie naley izolowa................................................................................. 356
6.6.3. Jak izolowa ...................................................................................................... 360
6.6.4. Do jakiego stopnia naley izolowa .................................................................. 366
6.7. Podsumowanie ............................................................................................................ 373
'
(
''
7.1. Od komponentw do pakietw ................................................................................... 378
7.2. Zarejestrowane prefiksy pakietw............................................................................... 385
7.2.1. Potrzeba stosowania prefiksw ......................................................................... 385
7.2.2. Przestrzenie nazw.............................................................................................. 387
7.2.3. Zachowanie integralnoci pakietu ..................................................................... 391
7.3. Podzia pakietw na poziomy ..................................................................................... 393
7.3.1. Znaczenie podziau pakietw na poziomy......................................................... 393
7.3.2. Techniki podziau pakietw na poziomy........................................................... 394
7.3.3. Podzia systemu................................................................................................. 396
7.3.4. Wytwarzanie oprogramowania w wielu orodkach........................................... 398
7.4. Izolacja pakietw ........................................................................................................ 399
7.5. Grupy pakietw........................................................................................................... 402
7.6. Proces wydawania oprogramowania ........................................................................... 406
7.6.1. Struktura wydania ............................................................................................. 408
7.6.2. aty ................................................................................................................... 413
7.7. Program main.............................................................................................................. 415
7.8. Faza startu ................................................................................................................... 421
7.8.1. Strategie inicjalizacji ......................................................................................... 423
7.8.1.1. Technika przebudzenia w stanie zainicjowania ................................424
7.8.1.2. Technika jawnego wywoywania funkcji init .......................................424
7.8.1.3. Technika wykorzystania specjalnego licznika......................................426
7.8.1.4. Technika sprawdzania za kadym razem..............................................431
7.8.2. Porzdkowanie .................................................................................................. 432
7.8.3. Przegld............................................................................................................. 433
7.9. Podsumowanie ............................................................................................................ 434
! "#$
)
8.1. Abstrakcje i komponenty ............................................................................................ 439
8.2. Projekt interfejsu komponentu .................................................................................... 440
8.3. Poziomy enkapsulacji.................................................................................................. 444
8.4. Pomocnicze klasy implementacyjne............................................................................ 454
8.5. Podsumowanie ............................................................................................................ 459
&( #(!&
9.1. Specyfikacja interfejsu funkcji.................................................................................... 462
9.1.1. Operator czy metoda?........................................................................................ 462
9.1.2. Wolny operator czy skadowa klasy? ................................................................ 468
9.1.3. Metoda wirtualna czy niewirtualna?.................................................................. 472
9.1.4. Metoda czysto wirtualna czy nie czysto wirtualna? .......................................... 476
9.1.5. Metoda statyczna czy niestatyczna? .................................................................. 477
9.1.6. Metody stae czy modyfikowalne? .................................................................... 478
9.1.7. Metody publiczne, chronione czy prywatne ...................................................... 483
9.1.8. Zwracanie wyniku przez warto, referencj czy wskanik? ............................ 484
9.1.9. Zwracanie wartoci typu const czy nie-const? .................................................. 487
9.1.10. Argument opcjonalny czy obowizkowy?....................................................... 488
9.1.11. Przekazywanie argumentw przez warto, referencj lub wskanik ............. 490
9.1.12. Przekazywanie argumentw jako const lub nie-const ..................................... 495
9.1.13. Funkcja zaprzyjaniona czy niezaprzyjaniona? ............................................. 496
9.1.14. Funkcja inline czy nie inline?.......................................................................... 497
Spis treci
+
$
% ,(*
10.1. Pola ........................................................................................................................... 521
10.1.1. Wyrwnanie naturalne................................................................................... 522
10.1.2. Uycie typw podstawowych w implementacji............................................. 524
10.1.3. Uycie konstrukcji typedef w implementacji................................................. 526
10.2. Definicje funkcji ....................................................................................................... 527
10.2.1. Samokontrola ................................................................................................ 527
10.2.2. Unikanie przypadkw szczeglnych ............................................................. 528
10.2.3. Podzia zamiast powielania ........................................................................... 530
10.2.4. Zbytnia przebiego nie popaca .................................................................. 533
10.3. Zarzdzanie pamici................................................................................................ 533
10.3.1. Wartoci stanu logicznego i fizycznego ........................................................ 538
10.3.2. Parametry fizyczne ........................................................................................ 541
10.3.3. Systemy przydziau pamici .......................................................................... 545
10.3.4. Zarzdzanie pamici na poziomie klasy ...................................................... 551
10.3.4.1. Dodawanie wasnych mechanizmw zarzdzania pamici ...........555
10.3.4.2. Zablokowana pami ......................................................................558
10.3.5. Zarzdzanie pamici na poziomie obiektu................................................... 562
10.4. Uycie szablonw w duych projektach ................................................................... 567
10.4.1. Implementacje szablonw ............................................................................. 567
10.4.2. Zarzdzanie pamici w szablonach.............................................................. 568
10.4.3. Wzorce a szablony......................................................................................... 577
10.5. Podsumowanie .......................................................................................................... 579
%
&'#
- (. !
&(/!% !"
)
Protocol Hierarchy struktury klas.................................................................................. 585
Cel ............................................................................................................................. 585
Znany te jako ........................................................................................................... 585
Motywacja................................................................................................................. 585
Zakres zastosowania.................................................................................................. 589
Struktura.................................................................................................................... 590
Elementy skadowe.................................................................................................... 590
Wsppraca................................................................................................................ 591
Konsekwencje ........................................................................................................... 591
Implementacja ........................................................................................................... 592
Przykadowy kod....................................................................................................... 603
Znane zastosowania................................................................................................... 607
Pokrewne wzorce ...................................................................................................... 608
10
- (0
$
% #&122
.34$1
B.1. Wykrywanie bdu alokacji pamici........................................................................... 611
B.2. Definiowanie procedury main (tylko ANSI C)........................................................... 618
C.1. Korzystanie z polece adep, cdep i ldep..................................................................... 622
C.2. Dokumentacja wiersza polecenia ............................................................................... 633
C.3. Architektura pakietu idep ........................................................................................... 643
C.4. Kod rdowy ............................................................................................................. 646
'
D.1. Definicje..................................................................................................................... 647
D.2. Gwne reguy projektowe ......................................................................................... 652
D.3. Poboczne reguy projektowe ...................................................................................... 653
D.4. Wskazwki................................................................................................................. 654
D.5. Zasady ........................................................................................................................ 657
0,% #
'
4(
'
Rozdzia 9.
2TQLGMVQYCPKGHWPMELK
Celem projektowania funkcji jest zapewnienie atwego i wydajnego dostpu do operacji
zdefiniowanych przez abstrakcj. Jzyk C++ zapewnia swobod definiowania interfejsu
na poziomie funkcji. Czy funkcja ma by operatorem, metod lub wolnym operatorem,
w jaki sposb bd przekazywane argumenty oraz w jaki sposb bd przekazywane
wyniki to elementy nalece do tego etapu procesu projektowania. Styl programowania to tylko jeden z elementw, ktre odgrywaj rol w podejmowaniu tego typu decyzji projektowych. Wiele z nich zostanie omwionych w niniejszym rozdziale.
W jzyku C++ mamy do dyspozycji wiele odmian podstawowych typw reprezentujcych liczby cakowite (jak np. ,
,
itp.). Typy te zapewniaj dodatkow swobod. Nierozwane ich wykorzystanie moe skomplikowa lub nawet osabi
interfejs.
Operator konwersji dla typw definiowanych przez uytkownika umoliwia kompilatorowi wykonywanie niejawnej konwersji na lub z typu definiowanego przez uytkownika.
Uwane projektowanie wymaga przeanalizowania moliwych zalet niejawnej konwersji
w zestawieniu z niejednoznacznociami oraz moliwoci powstania bdw w zwizku
z obnieniem bezpieczestwa typw. Niektre inne funkcje, w przypadku gdy nie zostan
okrelone jawnie, gdy zajdzie taka potrzeba mog by zdefiniowane automatycznie przez
kompilator. Podjcie decyzji dotyczcych dopuszczalnoci generowania definicji funkcji przez kompilator wymaga uwanej analizy.
W tym rozdziale opiszemy szkielet projektowania interfejsu komponentu na poziomie
pojedynczej funkcji. Omwimy obszern przestrze projektow dostpn dla autorw
komponentw i wskaemy decyzje projektowe, ktre s korzystne lub niekorzystne.
Przekonamy si, ile poziomw swobody w przestrzeni projektu interfejsu funkcji mona
wyeliminowa bez strat w skutecznoci. Uzyskany szkielet pomoe nam opracowywa
interfejsy prostsze, bardziej jednolite i atwiejsze do utrzymania.
462
5RGE[HKMCELCKPVGTHGLUWHWPMELK
Zgodnie z podstawowymi zasadami przedstawionymi w rozdziale 2., podczas okrelania interfejsu funkcji w jzyku C++ naley zwrci uwag na kilka aspektw:
Istniej take dwa aspekty dotyczce organizacji, ktre naley wzi pod uwag, pomimo tego, e nie s one czci logicznego interfejsu:
Oprcz operatorw generowanych przez kompilator (np. przypisania), jedynym powodem utworzenia z funkcji operatora jest wygodna notacja wewntrz klientw. Zwrmy
uwag, e inaczej ni w przypadku notacji typowej dla funkcji, notacja operatorowa nie
zaley od kontekstu wywoanie funkcji w wyniku interpretacji operatora wywoanego przez metod bdzie takie samo jak w przypadku wywoania w zasigu pliku2. Rozwane wykorzystywanie przeciania operatorw ma naturaln i oczywist przewag
nad notacj funkcyjn w szczeglnoci w przypadku typw logicznych i arytmetycznych definiowanych przez uytkownika.
1
463
! " ! # ! $ ! %
! " ! & ! # ! '
! ! ( ! )
! (( (( ((
*
+
*
+
Niektre metody zdefiniowano jako statyczne, aby umoliwi t sam symetryczn niejawn konwersj
argumentw, jak w przypadku odpowiadajcych im operatorw (patrz punkt 9.1.5). Styl akapitw gboko
zagniedonych wywoa funkcji na rysunku 9.1b zapoyczono z takich jzykw, jak LISP i CLOS, gdzie
takie konstrukcje wystpuj bardzo czsto.
464
W omawianej aplikacji obsugi zbioru liczb cakowitych notacja operatorowa w oczywisty sposb poprawia zarwno czytelno, jak te atwo uywania. Przez czytelno
rozumiemy zdolno inyniera oprogramowania do rozrniania w szybki i precyzyjny
sposb znanego kodu treci funkcji od nieznanego kodu rdowego. atwo uywania
dotyczy tego, jak atwo programista moe skutecznie wykorzysta obiekt w celu utworzenia nowego oprogramowania. Zazwyczaj kod rdowy odczytuje si wicej razy,
ni si go zapisuje (w przypadku wikszoci duych, wykorzystywanych przez dugi
okres czasu systemw oprogramowania, koszty utrzymania przekraczaj koszty wytwarzania od 2 do 4 razy4).
Semantyka przecionych operatorw powinna by naturalna, oczywista i intuicyjna
dla klientw.
Podczas projektowania czsto mona otrzyma zgrabne i atwe w uyciu aplikacje operatorw, ktre nie maj intuicyjnego znaczenia dla programistw, nie znajcych naszego
komponentu. Nierozsdne stare przyzwyczajenia, jak np. zdefiniowanie jednoargumentowego operatora jako skadowej klasy w celu odwrcenia kolejnoci znakw w cigu, jest nie na miejscu w rodowisku projektowym duej skali. Papierkiem
lakmusowym odpowiadajcym na pytanie czy zastosowa notacj operatorow powinno by stwierdzenie, czy istnieje naturalne i intuicyjne znaczenie natychmiast zrozumiae dla nowych klientw, ktre poprawia poziom czytelnoci (lub przynajmniej go
nie pogarsza)5.
Syntaktyczne waciwoci przecionych operatorw dla typw definiowanych przez
uytkownika powinny by lustrzan kopi waciwoci zdefiniowanych dla typw
podstawowych.
Na poziomie semantycznym do trudno dostarczy szczegowych wskazwek odnonie tego co jest, a co nie jest intuicyjne. Jednak na poziomie syntaktycznym, biorc za
podstaw implementacj podstawowych typw jzyka, mona sformuowa kilka zdecydowanych i dobrze zdefiniowanych stwierdze.
Wzorowanie syntaktycznych waciwoci operatorw zdefiniowanych przez
uytkownika na predefiniowanych operatorach C++ pozwala na uniknicie
niespodzianek i sprawia, e sposb ich uywania jest atwiejszy do przewidzenia.
W jzyku C++ kade wyraenie ma warto. Istniej dwa podstawowe typy wartoci
tzw. lwartoci (ang. lvalue) oraz pwartoci (ang. rvalue)6. Lwarto to taka warto, dla
ktrej mona wyznaczy adres. Jeeli lwarto moe si znale po lewej stronie wyraenia przypisania, mwi si o niej, e jest modyfikowalna, w innym przypadku okrela
si j jako lwarto niemodyfikowaln7. Do pwartoci nie mona przypisa wartoci, ani
4
Pojcia te pochodz z klasycznego jzyka C: pojcie lwarto oznacza, e warto wyraenia moe si
znale po lewej stronie instrukcji przypisania, natomiast pwarto moe si znale wycznie po jej
prawej stronie. Wraz z pojawieniem si konstrukcji w jzyku C++ i ANSI C, lwartoci dzieli si
teraz na dwie odmiany: modyfikowalne i niemodyfikowalne (patrz stroustrup, punkt 2.1.2, str. 46 47).
465
nie mona pobra jej adresu8. Najprostsz lwartoci jest identyfikator zmiennej. Jeeli
zmienna nie jest zadeklarowana jako , jest to lwarto modyfikowalna. Niektre
operatory, jak np. operator przypisania () i jego odmiany (
!!), preinkrementacji (") i predekrementacji (") zastosowane do typw podstawowych, zwracaj modyfikowalne lwartoci. Operatory te zawsze zwracaj
zapisywaln referencj do modyfikowanego argumentu. Np. hipotetyczna definicja operatorw dla podstawowego typu
(w przypadku jego implementacji jako klasy
C++) moe mie posta tak, jak pokazano na listingu 9.2.
Hipotetyczna implementacja podstawowego typu double
-- ./0, / / 1234 5
--
,
+
6
7 +
6 ! 6
6 ! 6
6 )! 6
6 (! 6
6 -! 6
6 -- 41 8
6 )) -- 41 ))8
-- 41 8
)) -- 41 8))
(6 -- 10/3
(6 -- 10/3
+
6 -- 10/3
) 6 -- 10/3 )
9 6 -- 10/3 023
Pola bitowe s wyjtkiem w tym sensie, e mog si znale po lewej stronie wyraenia przypisania, ale zgodnie
z ARM (ellis, podrozdzia 9.6, str. 184) nie mona wyznaczy ich adresu. Zasada ta dotyczy take zmiennych
tymczasowych typw zdefiniowanych przez uytkownika, ktre nie posiadaj nazwy (patrz punkt 9.1.2).
466
Inne operatory pokazane na listingu 9.2 zwracaj pwarto, poniewa nie ma moliwoci
zwrcenia odpowiedniej lwartoci. W przypadku symetrycznych operatorw binarnych
(jak np. oraz ) warto do zwrcenia nie jest ani argumentem lewym, ani prawym, ale
now wartoci uzyskan na podstawie obydwu, a zatem wynik musi by zwrcony przez
warto9. Operatory rwnoci (, #) oraz operatory relacyjne (!, !, , ) zawsze zwracaj
pwarto typu
o wartoci 0 lub 1, a zatem i w tym przypadku aden z argumentw
wejciowych nie jest odpowiedni wartoci do zwrcenia. Operatory postinkrementacji
i postdekrementacji to interesujcy przypadek specjalny w tym sensie, e s to jedyne
operatory, ktre modyfikuj obiekt, a zatem nie ma odpowiedniej lwartoci do zwrcenia:
,,
! (
(
+
Jako bardziej subtelny przykad przeanalizujmy dwa modele skadni odpowiadajce abstrakcji oglnej tabeli symboli, pokazane na listingu 9.3. W obu przypadkach na podstawie parametru typu tworzona jest tabela symboli, dodawane s dwa symbole, a nastpnie poszukuje si parametru foo wedug nazwy. Poniewa moliwe jest, e w tabeli
nie ma symbolu o okrelonej nazwie, zatem funkcja wyszukujca nie powinna zwraca
wyniku przez warto, ani przez referencj tak wic wynik jest zwracany przez wskanik (zwrmy uwag na to, w jaki sposb i do jakiego stopnia wykorzystano zalety enkapsulacji). Porwnajmy t sytuacj z zastosowaniem operatora $% w odniesieniu do tablicy
wartoci . Spodziewamy si uzyska referencj do poindeksowanej wartoci, nie za
wskanik, ktry moe mie warto . Ta rnica w skadni pomidzy zastosowaniem operatora $% na listingu 9.3a oraz zastosowaniem operatora $% dla typw podstawowych powoduje, e notacja z wywoaniem funkcji pokazana na listingu 9.3b jest w tym
przypadku lepsza. Zarezerwowanie notacji operatorowej dla tych przypadkw, kiedy
skadnia stanowi lustrzane odbicie odpowiadajcej jej skadni dla typw podstawowych,
wzmacnia skuteczno przeciania operatorw.
Dwa modele skadni dla abstrakcji oglnej tabeli symboli
0
3
0
3
0
3;
" --
& --23 3
(< ! =
>
--
0
3;
"
&
(< ! 4
--
467
468
11
Obecnie w jzyku C++ dozwolone jest modyfikowanie nienazwanych tymczasowych zmiennych typu
zdefiniowanego przez uytkownika nieposiadajcych nazwy. Patrz murray, punkt 2.7.3, str. 53 55.
469
Z drugiej strony spodziewamy si, e niektre operatory (np. oraz ) bd dziaa bez
wzgldu na kolejno ich argumentw. Przeanalizujmy operator , ktry suy do konkatenacji dwch cigw znakw i zwraca wynik swojego dziaania przez warto. Jzyk
C++ pozwala na zdefiniowanie operatora jako skadowej lub nieskadowej. To samo
dotyczy operatora . Jeeli zdecydujemy si na zdefiniowanie tych operatorw jako
skadowych, wwczas narazimy si na moliwo nastpujcego, nienormalnego dziaania naszych klientw:
<
0
!
-- 2
!
--
! !!
-- 2
!
!! --
+
oraz
0,,!! 06 0
natomiast taka konwersja dla argumentu po lewej stronie nie jest moliwa12. Jeeli operatory te bd wolne, problem symetrii dla klasy
zostanie rozwizany, do
czasu dodania operatora konwersji:
0,, (
Na listingu 9.5 pokazano problem powstay w wyniku dodania operatora konwersji (rzutowania) z typu na
. Co jest do dziwne, dwa niewtpliwie
podobne operatory oraz
nie s identyczne pod wzgldem przeciania, w co (niestety
naiwnie) chcielibymy wierzy. Rnica polega na tym, e teraz istniej dwa sposoby
interpretacji operatora :
Taki problem nie istnieje dla operatora , poniewa w jzyku C++ nie ma sposobu
dodania dwch typw wskanikowych i dlatego w tym przypadku nie ma niejednoznacznoci.
12
470
0
<
0
!
-- 2
!
-- 2
! !!
-- 122
!
!! -- 122
! -- 2
+
W przypadku klasy wykorzystywanej w praktyce nie bdziemy polega na niejawnej konwersji w celu uzyskania wartoci znakowej, ze wzgldu na obaw, e taka
dodatkowa konstrukcja i destrukcja spowoduje zbytni spadek wydajnoci. Zamiast tego
zdefiniujemy osobne przecione wersje operatora , ktrych zadaniem bdzie jak najbardziej wydajna obsuga kadej z trzech moliwoci, a tym samym rozwizanie problemw niejednoznacznoci.
Niespjnoci powstae w wyniku przeciania operatorw mog by oczywiste,
denerwujce i kosztowne dla uytkownikw.
471
+
!!
06
06 0
!! (
06 0
-- .2/ 332 4/1 2/ 22 234/4
-- 0/ 2/ /1 14 /1
E
E
06
-- F1/ 4/1 3 E
0
+
G
G
(
-- F1/ 4/1 3 G (
+
< 0
E
G
!! -- 2, G !! (
-- E !!
06
+
H -- , E !FI!
06
-- G !! (
+
+
nie bdzie dostpna jako wolna funkcja, wwczas nie bdzie sposobu przeprowadzenia
nastpujcej konwersji:
472
Wniosek jest taki, e operator
zawsze powinien by zdefiniowany jako wolna funkcja niezalenie od zastosowania innych funkcji. Te same powody dotycz innych operatorw dwuargumentowych, ktre nie modyfikuj adnego z operandw i zwracaj swj
wynik przez warto.
Przykad, jaki daje sam jzyk, jest bezstronnym i uytecznym modelem, ktry moe
suy klientom do tworzenia podstawowych syntaktycznych i aksjomatycznych waciwoci operatorw. Celem modelowania podstawowych operacji nie jest umoliwianie niejawnych konwersji samo w sobie, ale raczej zapewnienie symetrii po to, by unikn niespodzianek. W przypadku zastosowania przeciania operatorw w szerokim
zakresie naley si spodziewa, e abstrakcja moe by wykorzystywana wielokrotnie
w wielu sytuacjach. Uytkownicy komponentw wielokrotnego uytku doceni spjny
i profesjonalny interfejs niezalenie od syntaktycznych niespodzianek. Zwrmy uwag
na to, e w jzyku C++ wymagane jest, aby zdefiniowa jako skadowe nastpujce
operatory13:
!
przypisania,
=>
indeksu,
)
wywoania funkcji,
;
konwersji,
/
Dynamiczne wizanie umoliwia definiowanie metod, do ktrych dostp odbywa si za
pomoc klasy podstawowej, przez rzeczywisty podtyp obiektu, w odrnieniu do typu
wskanika lub referencji uytej w wywoaniu. W celu zapewnienia dynamicznego wizania funkcj naley zadeklarowa jako wirtualn (). W jzyku C++ wirtualne
mog by tylko metody. Jednak wniosek, e polimorficzne dziaanie operatora wymaga,
aby sta si funkcj skadow, gdy w innym przypadku byby woln funkcj, jest bdny.
W celu osignicia polimorficznego dziaania operatorw nie trzeba narusza
zagadnie syntaktycznych, jak np. symetrycznej niejawnej konwersji operatorw
dwuargumentowych.
Na rysunku 9.2 pokazano, w jaki sposb operatory symetryczne mog i powinny pozosta operatorami wolnymi pomimo zastosowania poliformizmu. Zamiast przeksztacania kadego z szeciu operatorw porwna i relacji na wirtualne metody klasy, opracowano jedn wirtualn metod porwnawcz. Te sze operatorw w dalszym cigu
bdzie dziaa symetrycznie bez wzgldu na niejawn konwersj dowolnego typu.
13
ellis, podrozdzia 12.3c, str. 306; stroustrup94, punkt 3.6.2, str. 82 83.
473
Polimorficzne
porwnanie figur
geometrycznych
za pomoc wolnych
operatorw
-- 0
F5J.KIK
LIMN
OPAI
F5J.KIK
LIMN
OPAI
0
,
< 70
< < ( ! *
< 0
6 ! *
-- Q/ 2 1 2 22 0
-- 23 4 0
1 / 123 /3 23
/423
-- 40 0 4 0
+
!! 0
6 0
6 0
0 !! * +
9! 0
6 0
6 0
0 9! * +
! 0
6 0
6 0
0 ! *
0
6 0
6 0
0 * +
! 0
6 0
6 0
0 ! * +
0
6 0
6 0
0 *
Operatory porwna czsto maj sens nawet wtedy, gdy operatory relacji nie maj sensu
(wemy pod uwag abstrakcj punktu). Czasami sortowanie heterogenicznych kolekcji
pozwala na uzyskanie wydajniejszego dostpu. W takich przypadkach przydaje si wykorzystanie porzdkowania (dowolnego typu). Wirtualna metoda .&' pokazana
na rysunku 9.2 pozwala na zdefiniowanie wasnego identyfikatora typu fazy wykonania14.
Dziki wykorzystaniu tego identyfikatora mona sortowa figury tego samego typu wykorzystujc zdefiniowany dla nich indywidualny porzdek, natomiast sortowanie innych
typw mona zdefiniowa za pomoc innego (zupenie dowolnego) porwnania. Implementacj klasy
(/
wykorzystywanej do porzdkowania figur pokazano na listingu 9.7.
Metody wirtualne implementuj rnice w dziaaniu, natomiast pola rnice
w wartociach.
14
474
0
5 , 0
< (
,
0
5 ,
+
0
5 0
56 ,
+
70
5 0
56
0
56 ! 0
56
!
( +
< (
+
0
6 -- /
0
56 -- /
+
0
56 0
56 0
0 * +
-- 1 23 $ / 3323
-- 0
0
475
jednej (w peni odizolowanej) klasy /, w ktrej zapisano by jeden z kilku wymienionych kolorw w postaci typu wyliczeniowego. Zastosowanie metod wirtualnych
jest jednak skuteczn technik rozwizywania problemu zalenoci zarwno fazy kompilacji, jak i konsolidacji (patrz punkt 6.4.1). Z tego powodu mona zdecydowa si na
utworzenie konkretnej klasy pochodnej na podstawie oglnej klasy /.
Ukrywanie: Metoda ukrywa funkcj o tej samej nazwie zadeklarowan
w ramach klasy podstawowej lub w zasigu pliku.
Przecianie: Funkcja przecia inn funkcj o tej samej nazwie zdefiniowan
w tym samym zasigu.
Przesanianie: Metoda przesania identyczn metod, ktr w klasie bazowej
zadeklarowano jako wirtualn.
Przedefiniowywanie: Domylna definicja funkcji jest nieodwracalnie zastpowana
inn definicj.
Na koniec zdefiniujemy cztery powszechnie uywane (czsto bdnie), podobne do siebie, pojcia suce do opisania funkcji i efektw ich dziaania na inne funkcje (ukrywanie, przecianie, przesanianie i przedefiniowywanie). O rnych funkcjach o tej samej
nazwie mwi si, e s przecione tylko wtedy, gdy zadeklarowano je w tym samym
zasigu. Jeeli metod klasy pochodnej zadeklarowano z identycznym interfejsem do
metody zadeklarowanej w klasie bazowej jako wirtualn, wwczas mwi si, e ta funkcja przesania metod klasy bazowej. We wszystkich pozostaych przypadkach, nazwa
funkcji ukrywa wszystkie inne funkcje o identycznej nazwie w tym samym zasigu,
niezalenie od ich sygnatur argumentw. Do funkcji ukrytych w danym zasigu nie ma
bezporedniego dostpu, chocia mona uzyska do nich dostp za pomoc operatora
zasigu (44). Jeeli jednak przedefiniujemy funkcj (np. globalny operator
3 lub jednoargumentowy operator klasy ), czyli zamienimy jej definicj, wwczas jej poprzednia definicja nie bdzie ju dostpna16.
Naley unika ukrywania metod klasy bazowej w klasie pochodnej.
Naley uwaa, aby nie ukrywa definicji metod klasy bazowej w obrbie klas pochodnych. Nie naley zwaszcza tworzy nowych definicji dla niewirtualnych metod w klasie pochodnej, poniewa w takim przypadku takie metody bd wraliwe na typy wskanikw oraz adresw, spod ktrych bd wywoywane17. Umoliwienie powstania
zalenoci typu wskanikw lub adresw od tego, ktra funkcja bdzie wywoana, jest
dziaaniem antyintuicyjnym, subtelnym i powodujcym moliwo powstania bdw.
Ukrywanie metod zdefiniowanych w klasach bazowych nie wyklucza moliwoci ich
uycia, a jedynie utrudnia ich wykorzystanie. Aby wywoa ukryt metod, zawsze mona wykona pewne dziaania ze wskanikiem lub posuy si operatorem zasigu. Najlepiej stara si unika ukrywania metod. Przykady wzorcw projektowych obejmujcych wykorzystanie metod wirtualnych, wielokrotnego dziedziczenia oraz identyfikacji
typu w fazie wykonania zaprezentowano w dodatku C.
16
ellis, podrozdzia 10.2, str. 210 oraz podrozdzia 13.1, str. 310.
17