Jerzy Grębosz Instytut Fizyki Jądrowej im. H. Niewodniczańskiego Polska Akademia Nauk Kraków
Programowanie w języku C++ orientowane obiektowo
Kraków 2005
i iM g
© JERZY GRĘBOSZ Wszelkie prawa zastrzeżone Opracowanie graficzne: Jerzy Grębosz
Zdjęcie na tylnej stronie okładki: Dr Stanislav Tachenow, GSI Darmstadt WYDANIE PIERWSZE AUTOR ORAZ WYDAWNICTWO DOŁOŻYLI WSZELKICH STARAŃ, BY ZAWARTF W KSIAŻCF INFORMACJE BYŁY RZETELNE I KOMPLETNE. NIE BIORĄ JEDNAK ODPOWIEDZIALNOŚCI ANI ZA ICH WYKORZYSTANIE, ANI ZA ZWIĄZANE Z TYM EWENTUALNE NARUSZENIE PRAW PATENTOWYCH LUB AUTORSKICH. *uaz.r:iNit rKAW
ŻADNA CZĘŚĆ TEJ PUBLIKACJI NIE MOŻE BYĆ REPRODUKOWANA FOTOGRAFICZNIE KSEROGRAFICZNIE ANI W ŻADEN INNY SPOSÓB. NIE MOŻE BYĆ SKŁADOWANA NA ŻAIWTCH (A W SZCZEGÓLNOŚCI ELEKTRONICZNYCH) NOŚNIKACH INFORMACJI NIE MOŻE BYĆ PRZESYŁANA ELEKTRONICZNIE, FOTOGRAFICZNIE, ANI W ŻADEN INNY SPOSÓB RP7 PISEMNEJ ZGODY WŁAŚCICIELA PRAW AUTORSKICH. UDOSTĘPNIANIE l/LUB KOPIOWANIE TEJ PUBLIKACJI ZA POMOCĄ UR7AD7FŃ KOPIUJĄCYCH LUB PRZY WYKORZYSTANIU NOŚNIKÓW ELEKTRONICZNYCH IFST PRZESTĘPSTWEM. WYDAWNICTWO ZASTRZEGA SOBIE PRAWO DO DOCHODZENIA ODSZKODOWANIA ZA PONIESIONE STRATY OD OSÓB NIELEGALNE ROZPOWSZECHNIAJĄCYCH TĘ PUBLIKACJĘ.
ALL OF THE PRODUCTS AND SOFTWARE MENTIONED IN THIS BOOK ARE REGISTERED TRADEMARKS OF THEIR OWNERS WYŁĄCZNY DYSTRYBUTOR (ADRES KORESPONDENCYJNY): WYDAWNICTWO „EDITION 2000”
y k £
UL. STASZICA 3/8 / j
2000
31-162 KRAKÓW TEL. (012) 635-15-00 E-MAIL:
[email protected]
W ydano przy współpracy Oficyny Kallimach
ISBN 83-7366-073-9
III
Spis treści
Spis treści 0
P r o s z ę n i e c z y t a ć t e g o ! ..................................
1 S t a r t u j e m y ! .............................. 1.1 Pierw szy p ro g ra m ................ 1.2 D rugi p r o g r a m ...................... 1.3 Ć w iczenia................................
................................................................... i
2.1 2.1.1 2.1.2 2.1.3 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12
i s t r u k c j e s t e r u j ą c e ......... ............. ................... ...............................................................2 2 P raw da - Fałsz, czyli o w a ru n k a c h ..................... W yrażenie lo g ic z n e ........ Zm ienne logiczne b o o l jako w a ru n e k .......... .................................................................23 Stare dobre sp o so b y z daw nego C + + ............ .................................................................24 Instrukcja w aru n k o w a i f . .„ Pętla w h i l e ............................. Pętla d o . . . w h i l e ................ Pętla f o r .................................. . Instrukcja s w i t c h ................. C o w y b rać s w i t c h czy i f . . . e l s e ? ................. .................................................................37 In stru k q a b r e a k .................. Instrukcja g o t o ...................... Instrukcja c o n t i n u e ............ K lam ry w instrukcjach sterujących........................ Ć w iczenia..................................
3.1 3.2 3.3 3.3.1 3.4
System atyka ty p ó w z języka C + +............................................................................................ 50 Typy fu n d a m e n ta ln e ...................................................................................................................50 D efiniow anie ob iek tó w „w b ie g u " .................................................................................... 56 Stale dosło w n e............................................................................................................................... 58
IV 3.4.1 3.4.2 3.4.3 3.4.4 3.5 3.6 3.7 3.7.1 3.7.2 3.7.3 3.7.4 3.7.5 3.8 3.9 3.9.1
Spis treści Stałe b ęd ące liczbam i całkow itym i ................................................................................ 59 Stałe rep rezen tu jące liczby z m ie n n o p rz e c in k o w e .....................................................61 Stałe z n a k o w e ........................................................................................................................ 62 Stałe tek sto w e, napisy, albo p o p ro stu stringi ............................................................65 T ypy z ło ż o n e ............................................................................................................................... 68 Typ ................................................................................................................................................ Zakres w ażności nazw y obiektu, a czas życia obiektu ................................................. 70 71 Zakres: lo k aln y Zakres: b lo k funkcji.............................................................................................................. 71 Zakres: o b szar p lik u ............................................................................................................. 72 Zakres: o b sz a r k la sy ............................................................................................................. 72 Zakres ok reślo n y p rzez przestrzeń n a z w ......................................................................72 Zasłanianie n a z w ........................................................................................................ Specyfikator (przydomek) c o n s t ............................................................................... Pojedynek: const contra # d e f i n e .....................................................................
3.10 Obiekty r e g i s t e r .............................. - .................................................................... 3.11 3.12 3.13 3.14
Specyfikator v o l a t i l e ................................................................................................. Instrukcja t y p e d e f ....................................................................................................... T ypy wyliczeniowe enum ...................................................................................... Ćwiczenia......................................................................................................................
U 4.1.1 4. 1.2 4.1.3 4.1.4 4.2 4.2.1 4.2.2 4.2.3 4.3 4.31 4.3.2 4.3.3 4.4 4.5 4.6 4.7 4.8 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6
Operatory arytmetyczne Operator %, czyli reszta z dzielenia (modulo) .........................................................96 Jednoargumentowe operatory + i - ..... .....................................................................98 Operatory inkrementacji i dekrementacji ...................... Operator przypisania = .................................................... Operatory logiczne .................................................................. Operatory relacji................................................................ Operatory sumy logicznej I i i iloczynu logicznego && Wykrzyknik ! - czyli operator negacji..................................................................... 104 Operatory bitowe .............................................................................................................105 P rzesunięcie w lew o « ................................................................................................. ^ 06 Przesunięcie w prawo » ......................................................................................... 1u/ Bitowe operatory sumy, iloczynu, negacji, różnicy symetrycznej .................108 Różnica między operatorami logicznymi, a operatorami bitowymi ...................... 109 Pozostałe operatory przypisania ................................................................................... 110 Wyrażenie warunkowe ................................................................................................... m O perator s i z e o f .................................................................................................................. Operatory rzutowania .....................................................................................................114 Rzutowanie według tradycyjnych inie zalecanych) sposobów ...........................115 Rzutowanie za pomocą nowych operatorów rzutowania.................................... 116 Operator s t a t i c _ c a s t ..............................................................................................117 Operator c o n s t _ c a s t .................................................................................................119 Operator d y n a m i c _ c a s t ............................................................................................121 Operator r e i n t e r p r e t c a s t ................................................................................. 121
...................
Ol Ol
Spis treści
V
VI
Spis treści
Przykład z tablicą elem entów ty p u enum .......................................................................... Tablice zn ak o w e Tablice wielowymiarowe Typ w y rażeń zw iązanych z tablicą w ie lo w y m ia ro w ą ............................................ 243 7.6.1 Przesyłanie tablic wielowymiarowych do funkcji 7.6.2 Ćwiczenia.
7.4
skaźniki........................... — 8.2
83 8.5
8.8.1
8 8.2 .
8.8.3 8.8.4 8.8.5
W skaźniki m ogą b ardzo u łatw ić życic D efiniow anie w skaźników Praca ze w skaźnikiem L -w artosc.. Operator rzutowania r e i n t e r p r c t _ c a s t . a w skaźniki.........................................261 W skaźniki typu v o i d Cztery do m en y zastosow ania w sk aźn ik ó w .................................................................... 267 Z astosow anie wskaźników wobec tab lic.......................................................................267 Ć w iczenia z m echaniki ru ch u w s k a ź n ik a ................................................................... 26 U życie w sk aźn ik a w pracy z ta b licą..............................................................................*72 Arytmetyka wskaźników ........................................................................................... P o rów n y w an ie w skaźników Wskaźnik m ożna porów nać z adresem 0 Zastosowanie wskaźników w argumentach funkcji jeszcze raz o przesyłaniu tablic do funkcji O dbieran ie tablicy jako w sk a ź n ik a ................................................................................ 28® Argument formalny będący w skaźnikiem do obiektu c o n s t Z astosow anie w skaźników p rz y dostępie do k o n k retn y ch kom órek p a m ię c i.......292
8.9.1 8.9.2 8.9.3 8.10 8.11 Rezerw acja o b szaró w pam ięci ....................... - ...............'."'T'".'...................................... O p erato ry new i d e l e t e albo O ratorium S tw orzenie Św iata ...........................294 8 . 11.1 Dynamiczna alokacja tablicy 8. 11.2 Tablice w ielow ym iarow e tw o rzo n e operatorem n e w .............................................. 299 8.11.3 Umiejscawiający operator new 8.11.4 Przychodzimy, odchodzimy - cichuteńko, na... 8.11.5 Z ap as pam ięci to nie jest stu d n ia bez dna 8 . 11.6 Funkcja s e t _ n e w _ h a n d l e r ......................................... 8.11.7 _ . « 1 _— M 1 1 /~\/~* ........... ...... . - ......• • • • l / .l ^ Pojedynek: new contra m a l l o c ...................................................................................... ^ 8.11.8 8.12 Stałe w sk aźn ik i 813 Stałe w sk aźn ik i, a w skaźniki d o stałych 8.14 Strzał n a oślep - W skaźnik zaw sze pokazuje na cos ^19 w sk a ź n ik ó w ........................................................................................ 8.15 Sposoby u staw ian ia 8.16 P arada kłam ców , czyli o rzu to w an iu c o n s t _ c a s t 326 8.17 Tablice w sk a źn ik ó w 8.18 W ariacje na tem at C -s trin g o w .............................................................................................. ^ 8.19 W skaźniki d o funkcji Ćwiczenia z definiowania wskaźników do funkcji 8.19.1 W sk aźn ik do funkcji jako arg u m en t innej funkcji 8.19.2 Tablica w sk aźn ik ó w d o funkcji 8.19.3
Spis treści
8.20 8 21
VII
A rgum en ty z lin ii w yw ołania p ro g ram u ........................................................................363 Ć w iczen ia.................................................................................................................................... 366
9.1 Co to znaczy: p rz e ła d o w a n ie ............................................................................................. ...374 9.2 Bliższe szczegóły p rz e ła d o w a n ia .........................................................................................378 9.3 Czy p rz e ła d o w a n ie nazw funkcji jest techniką obiektow o o rientow aną? ..............381 9.4 Linkow anie z m o d u łam i z innych języków ..................................................................... 382 9 5 P rzeładow anie, a zak res w ażności deklaracji funkcji .................................................... 383 386 9.6 R ozw ażania o identyczności lu b odm ienności typów arg u m e n tó w ......... 9.6.1 P rzeład o w an ie, a t y p e d e f i enum ..............................................................................386 9.6.2 Tablica, a w s k a ź n ik ............................................................................................................ 386 9.6.3 Pew ne szczegóły o tablicach w ielow ym iarow ych ................................................... 388 9.6.4 P rzeład o w an ie, a referencja ............................................................................................ 390 9.6.5 Identyczność typów : T, const T, voIatile T ................................................................... 391 9.6.6 P rzeład o w an ie - a typy: T*, volatile T*, const T *....................................................... 392 9.5.7 P rzeład o w an ie - a typy: T&, volatile T&, const T&................................................... 394 9.7 A dres funkcji przeładow anej .............................................................................................. 394 9.7.1 Z w rot rezu ltatu będącego ad resem funkcji p rz e ła d o w a n e j....................................397 9.8 Kulisy d o p aso w y w an ia argum entów d o funkcji p rzeład o w an y ch ........................... 398 9.9 Etapy d o p aso w an ia ................................................................................................................ 400 9.9.1 Etap 1. D o p aso w an ie d o k ła d n e.......................................................................................401 9.9.2 Etap la . D opasow anie dokładne, ale z tzw . tryw ialną konw ersją ......................401 9.9.3 Etap 2. D o p aso w an ie z aw ansem (z p ro m o c ją )..........................................................402 9.9.4 Etap 3. Próba dopasow ania za pom ocą konw ersji stan d ard o w y ch .....................405 9.9.5 Etap 4. Próba dopasow ania z użyciem konw ersji zdefin io w an y ch przez u żytkow nika...........................................................................................................................................406 9.9.6 Etap 5. Próba dopasow ania d o funkcji z w ielokropkiem ....................................... 407 9.9.7 W skaźników nie dopasow uje się inaczej niż d o sło w n ie ..........................................407 9.10 D opasow yw anie w yw ołań z kilkom a a rg u m e n ta m i........................................................408 9.11 Ć w iczenia.................................................................................................................................... 409
10.1 Typy definiow ane p rzez u ż y tk o w n ik a .............................................................................. 412 10.2 Składniki klasy ........................................................................................................................ 414 10.3 Składnik będący o b ie k te m .....................................................................................................416 1 04 E n k ap su lacja............................................................................................................................ 416 10 5 U kryw anie inform acji ....................................................................................................418 10 6 Klasa, a o b ie k t.......................................................................................................................... 421 10 7 Fu nkcje s k ła d o w e ....................................................................................................................424 10 7.1 Posługiw anie się funkcjami sk ład o w y m i...................................................................... 424 10.7.2 D efiniow anie funkcji sk ład o w y ch .................................................................................. 425 10.8 Jak to w łaściw ie jest ? ( t h i s ) .............................................................................................. 431 10.9 O dw ołanie się d o publicznych danych sk ła d o w y c h .......................................................434 10.10 Zasłanianie nazw ................................................................................................................... 435 10.10.1 Nie sięgaj z klasy do obiektów globalnych..................................................................439
VIII
Spis treści
10.11 Przeładowanie i zasłonięcie równocześnie ................................ 10.12 N ow a klasa? O sobny p lik !........................................................ .......... 10.13 Przesyłanie d o funkcji argumentów będących obiektami ...... 10.13.1 Przesyłanie obiektu przez w artość.......................................... 10.13.2 Przesyłanie przez referencję ..................................................... 10.14 Konstruktor-pierwsza wzmianka ................................................. 10.15 Destruktor-pierwsza wzmianka ................................................... 10.16 Składnik statyczny .................................................... ................... 10.16.1 Deklaracja składnika statycznego połączona z inicjalizacją 10.17 Statyczna funkcja składowa .................................................... ••••• 10.18 Do czego m oże nam się p rzy d ać sk ład n ik statyczny w klasie? 10.19 Funkcje składowe typu c o n s t oraz v o l a t i l e ..................... 10.19.1 Przeładowanie, a funkcje składowe c o n s t i v o l a t i l e 10.20 Specyfikator m u ta b le .................................................................... 10.21 Ćwiczenia.............................................................................................
.439 .440 .452 .453 .455 .457 .462 .465 .470 .476 .480 .480 .485 .485 499
503
11.5.2 D ziałanie funkcji składow ej .................................................................................... 11.6 Praca z fragm entem stringu, czyli z su b -strin g ie m ................................................. 11.7 Funkcja sk ład o w a s u b s t r ........................... ........ .... . 11.8 S zukanie z a d a n e g o substrin g u w obiekcie klasy s t r i n g - funkcja f i n d i je) p o k re w n e .............................................................. ;.......................................................................... 11.9 Szukanie ro zp o czy n an e od końca s trin g u .................................................................. 11.10 Szukanie w strin g u jednego ze zn ak ó w z zad an eg o z e s t a w u ............................. 11.11 U su w an ie zn a k ó w ze stringu - funkcje e r a s e ................................................ 11.12 W staw ianie z n a k ó w d o już istniejącego stringu - funkcje i n s e r t ............ 11.13 Z am iana części zn ak ó w na inne zn ak i - r e p l a c e ........................................... 11.14 Z am iana zaw arto ści obiektu k lasy s t r i n g na C -s trin g ................................. 11.15 Z ag ląd an ie d o w n ętrza obiektu klasy s t r i n g funkcją d a t a ....................... 1116 W p o rząd k u alfabetycznym - czyli porów nyw anie s tr in g ó w ...................... 11.16.1 P o ró w n y w an ie stringów funkcjam i c o m p a r e ............................................. 11.16.2 Porównywanie stringów przy użyciu operatorów ==, ! =, <, >, <-, > -
..505 ..511 ..516 ..517 ..519 ..519 ..520 ..521 ..521 ..523 ..524 ..527 ..527 ..528 ..529 ..532 ..533 ..534 ..538 ..539 ..542 ..544 ,.547 .552 .555 .557 .557 .563
IX
Spis treści
11.17 Z am iana treści strin g u na m ałe (lub w ielkie) litery.........................................................565 11.18 K opiow anie treści obiektu klasy s t r i n g do w ybranej tablicy znakow ej - funkcja c o p y 572 11.19 W zajem na za m ia n a treści dw óch obiektów klasy s t r i n g - funkcja s w a p ............ 573 11.20 P rzypisanie d o o b iek tu klasy s t r i n g , funkcja a s s i g n ............................................... 573 11.21 D opisyw anie d o końca stringu za pom ocą funkcji a p p e n a ........................................ 576 11.22 W czytyw anie z k law iatu ry długiego stringu o nieznanej w cześniej długości g e t l i n e ...................................................................................................................................................577 11.22.1 P ułapka - czyli jak g e t l i n e m oże Cię zaskoczyć....................................................581 11.23 Iteratory s tr in g u ........................................................................................................................ 586 11.23.1 Iterator do o b iek tu sta łe g o ............................................................................................... 590 11.23.2 Funkcje sk ła d o w e klasy s t r i n g pracujące z ite ra to ra m i...................................... 592 11.24 Bryk - czyli "pam ięć zew nętrzna" p ro g ra m is ty ...............................................................599 11.25 Ć w iczenia.....................................................................................................................................608
12Dpklararie nrzviaźni | ^ U
w
W
I C i l
12.0. 1 12.0. 2
C l U J C
615
| J l f c y j C I f c M I » « « « » a * « « » « a a « * a« « « « » « a a a « - a a a a a a a a a a a a a a « a a a a a a a a a a a a a a a » a a a a a a a a a a a a a a a a a V
I V
Klasy zap rzy jaźn io n e ....................................................................................................625 Słowo o z a k re s ie .............................................................................................................. 628
13Struktury, Unie, Pola bitowe...............................................................629 13.1 13.2 13.2.1 13.2.2 13.3 13.4 13.5
S tru k tu ra ......................................................................................................................................629 U n ia .............................................................................................................................................. 630 Inicjalizacja u n ii .................................................................................................................632 Unia a n o n im o w a ............................................................................................................... 633 Pola bitow e ................................................................................................................................ 634 U nia i pola b itow e - upraszczają ro zpakow anie s łó w .................................................... 639 Ć w iczenia.....................................................................................................................................647
14Klasa zagnieżdżona lub lokalna ....................................................... 649 14.1 14.2 14.3 14.4
Z agnieżdżona definicja klasy..................... 649 Lokalna definicja klasy ......................................................................................................... 654 Lokalne nazw y ty p ó w ........................................................................................................... 657 Ć w iczenia.....................................................................................................................................658
15Konstruktory i Destruktory ...............................................................660 15.1 15.1.1 15.2 15.3 15.3.1 15.3.2 15.3.3 15.3.4 15-3.5 15-4 15.5 15.6 15.7
K o n stru k to r................................................................................................................................ 660 Przykład program u zawierającego klasę z konstruktoram i........................................ 661 Specyfikator (p rzy d o m ek ) e x p l i c i t .................................................................................675 Kiedy i jak w y w o ły w an y jest k o n stru k to r......................................................................... 675 K onstruow anie obiektów lokalnych ........................................................................... 675 K onstruow anie obiektów globalnych ........................................................................676 Konstrukcja obiektów tw orzonych operatorem new ............................................677 Jaw ne wrywołanie konstruktora ..................................................................................... 678 Dalsze sytuacje, g d y pracuje k o n stru k to r......................................................................680 D estruktor ............................................................................................................................... 681 K onstruktor do m n iem an y ....................................................................................................683 Lista inicjalizacyjna k o n s tru k to ra ........................................................................................ 684 Konstrukcja obiektu, którego składnikiem jest obiekt innej klasy ........................... 688
X
Spis treści
15.8 15.9
K o n stru k to ry nie-publiczne 7 ............................. ................................................................. 695 K o n stru k to r kopiujący (albo inicjalizator k o p iu ją c y ).................................................. 69/ P rzy k ład klasy z k o n stru k to rem k o p iu ją c y m ............................................................699 15.9.1 D laczego p rzez referencję?............................................................................................... 15.9.2 Jak d o stać p iątk ę z C++ ? ......................... 707 15.9.3 K o n stru k to r kopiujący g w aran tu jący n ie ty k a ln o ś ć .................................................709 15.9.4 W sp ó ło d p o w ied z ialn o ść .................................................................................................. 7^ 15.9.5 K o n stru k to r kopiujący g en ero w an y a u to m a ty c z n ie ............................................... 710 15.9.6 K iedy k o n stru k to r kopiujący jest niezbędny? 15.9.7 15.10 Ć w icze n ia............
16Tablice o b ie k tó w ...................... 161 Tablica o b iek tó w definiow ana o p erato rem n e w .............................................................../z z 16.2 Inicjalizacja tablic obiektów ...................................................................................................7JJ 16.2.1 Inicjalizacja tablic obiektów będących a g r e g a ta m i.................................................724 16.2.2 Inicjalizacja tablic nie b ędących agregatam i............................................................. 728 16.2.3 Inicjalizacja tablic tw orzonych w zapasie pam ięci 16.3
Ć w icze n ia...............................................................................
17Wskaźnik do składników klasy 17.1
W skaźniki zw ykłe - r e p e ty to riu m .... ............... 17.2 W skaźnik d o pokazyw ania n a składnik-daną P rzy k ład zastosow ania w sk aźn ik ó w do sk ład n ik ó w k la s y ................................... 740 17.2.1 17.3 W skaźnik d o funkcji składow ej .......................................................................................... 747 Z astosow anie w skaźników d o funkcji s k ła d o w y c h ..................................................749 17.3.1 Tablica w sk a źn ik ó w do dan y ch składow ych klasy ....................................................... 756 17.4 17.5 Tablica w sk aźn ik ó w do funkcji składow ych k l a s y ........................................................ 757 17.6 W skaźniki d o składników statycznych ............................................................................. 758 ............................................................759 17.7 Ć w iczenia.
18 Konwersje...... 18.1 18.2 18.2.1 18.2.2 18.3 18.3.1 18.4 18.5 18*6 18.6.1 18.6.2 18.7 18.8 18.9
Sformułowanie problemu..................... Konstruktory konwertujące..... ........... Kiedy jaw nie, kiedy n ie ja w n ie ...... Przykład konwersji konstruktorem Funkcja k o nw ertująca - o p erato r k o n w ersji..................................................................... 772 N a co konw ertow ać nie m o ż n a ...................................................................................... 778 K tóry w a ria n t konw ersji w ybrać ?....................................................................................... 779 Sytuacje, w których zachodzi k o n w e r s ja .......................................................................... 781 Zapis ja w n eg o w yw ołania konw ersji typów ............ .....................................................782 Advocatus zapisu przypominającego: „wywołanie funkcji" ................................ 783 Advocatus zapisu: „rzutowanie" ...................................................... ........................... 783 Niecałkiem pasujące argumenty, czyli konwersje przy dopasow aniu ..................... 784 Kilka ra d dotyczących konw ersji Ć w iczenia.........................................
XI
Spis treści
.ie operatorów......................... ...................... 19.1 P rzełado w an ie o p erato ró w - definicja i trochę te o rii.................................................... 796 19.2 Moje z a b a w k i.............................................................................................................................800 19.3 Funkcja o p erato ro w a jako funkcja s k ła d o w a ...................................................................801 19.4 Funkcja o p e ra to ro w a nie m usi być przyjacielem klasy ................................................ 805 19.5 O peratory p red efin io w an e ................................................................................................... 806 19.6 A rg u m en to w o ść o p erato ró w ................................................................................................. 807 19.7 O peratory je d n o a rg u m e n to w e ............................................................................................. 807 19.8 O peratory d w u arg u m en to w e ............................................................................................. 810 19.8.1 P rzykład na p rzeład o w an ie o p erato ra d w u a rg u m e n to w e g o ................................811 19.8.2 P rz e m ie n n o ś ć ...................................................................................................................... 812 19.8.3 Choć o p erato ry inne, to nazw ę mają tę sam ą ............................................................ 814 19.9 P rzykład zu p e łn ie nie m atem aty czn y .................................................................................814 19.10 C ztery o p erato ry , k tóre m uszą być niestatycznym i funkcjam i sk ład o w y m i...........827 19.11 O perator p rz y p isa n ia = .......................................................................................................... 827 19.11.1 P rzykład na p rzeład o w an ie o p erato ra przypisania ................................................ 830 19.11.2 Jak konieczność istnienia o p erato ra przypisania - o p ow iedzieć p o to czn ie?.....840 19.11.3 Kiedy o p erato r p rzypisania nie jest generow any au to m aty czn ie ........................842 19.12 O perator [ ] 843 19.13 O perator ( ) ................................................................................................................................ 848 19.14 O perator - > ................................................................................................................................ 850 19.14.1 „Z ręczny w sk a źn ik " - w ykorzystuje przeład o w an ie w łaśnie tego o p erato ra .852 19.15 O peratory new, new [ j ............................................................................................................ 859 19.15.1 P rzykład p rzeład o w an ia o p erato ra n ew .......................................................................860 19.15.2 Przykład p rzeład o w an ia o p erato ra new l ] ................................................................. 862 19.16 O peratory d e l e t e , d e i e t e [ ] ........................................................................................... 863 19.16.1 Prosty p rzy k ła d p rzeładow ania d e l e t e ......................................................................864 19.16.2 Prosty p rzy k ła d p rzeładow ania d e l e t e [ ................................................................ 865 19.17 Program p rzy k ła d o w y na zastosow anie operatorów new, d e l e t e ........................... 865 19.18 Przeładow anie globalnych o peratorów new, new [ ] , d e l e t e , d e l e t e [ ] ..............869 19.19 O peratory postinkrem entacji i postdekrem entacji, czyli koniec z n ie sp raw ied liw o ścią............................................................................................................................. 871 19.20 Rady prak ty czn e dotyczące p rz e ła d o w a n ia ......................................................................874 19.21 Pojedynek: O p erato r jako funkcja składow a, czy globalna............................................876 19.22 Zasłona spada, czyli tajemnica o p erato ra « .................................................................. 878 19.23 Rzut oka w s te c z .........................................................................................................................884 19.24 Ć w iczenia.....................................................................................................................................886
20 Dziedziczenie.......................
890
20.1 Istota d z ie d z ic z e n ia .................................................................................................................. 890 20.2 Dostęp do sk ład n ik ó w ............................................................................................................894 20.2.1 Pryw atne sk ład n ik i klasy podstaw ow ej........................................................................ 894 20.2.2 N iepryw atne składniki klasy po d staw o w ej .............................................................. 896 20.2.3 Klasa pochodna też d ecy d u je...........................................................................................897 20.2.4 Deklaracja d o stęp u u s ir .g - czyli udostępnianie w ybiórcze ..............................899 20.3 Czego się nie d z ie d z ic z y ......................................................................................................... 9 0 3
X II
Spis treści
20.3.1 "Nie dziedziczenie" k o n s tru k to ró w .............................................................................903 20.3.2 "Nie dziedziczenie" o p erato ra przypisania .............................................................904 20.3.3 "Nie dziedziczenie" d e s tr u k to r a ................................................................................... 904 20.4 D rzew o genealogiczne............................................................................................................905 20.5 D ziedziczenie - doskonałe n arzęd zie p ro g ra m o w a n ia ................................................ 906 20.6 Kolejność w yw oływ ania k o n s tr u k to r ó w ..........................................................................909 20.7 P rzypisanie i inicjalizacja obiektów w w arunkach dziedziczenia ........................... 915 20.7.1 K lasa pochodna nie definiuje sw ojego o p erato ra p rzy p isan ia............................. 915 20.7.2 Klasa pochodna nie definiuje sw ojego k o n stru k to ra kopiującego......................917 20.7.3 Inicjalizaqa i przy p isy w an ie w ed łu g obiektu w zorcow ego będącego c o n s t ..917 20.7.4 D efiniow anie k o nstruktora kopiującego i o p erato ra przypisania dla klasy .918 pochodnej ............................................................................................................................... .925 20.8 D ziedziczenie od kilku "rodziców " (czyli w ie lo k ro tn e ).................................. .927 20.8.1 K onstruktor klasy pochodnej p rz y w ielokrotnym dziedziczeniu .......... .929 20.8.2 R yzyko w ieloznaczności p rzy dziedziczeniu .............................................. .932 20.8.3 Bliższe pokrew ieństw o u su w a w ieloznaczność........................................... .932 20.8.4 P o szlak i.................................................................................................................... .933 20.9 Pojedynek: D ziedziczenie klasy, contra zaw ieranie obiektów składow ych .935 20.10 Konwersje standardowe przy dziedziczeniu ............................................... .940 20.10.1 P anoram a korzyści................................................................................................ .943 20.10.2 C zego robić się nie opłaca ................................................................................. .944 20.10.3 T uzin sam ochodów nie jest rodzajem tuzina p o ja z d ó w ............................ ,949 20.10.4 K onw ersje stan d ard o w e w sk aźn ik a do składnika k l a s y .......................... .951 20.11 W irtualn e klasy p o d staw o w e ............................................................................... ,955 20.11.1 Publiczne i p ry w atn e dzied ziczen ie tej samej klasy w irtu aln ej............... ,956 20.11.2 U w ag i o konstrukcji i inicjalizacji w p rzy p ad k u klas w irtualnych ........ ,960 20.11.3 Dominacja klas wirtualnych ........................................................................ 961 20.12 Ć w iczen ia.......................................................................................................................
21 Funkcje wirtualne................................................................................ .968 21.1 P o lim o rfiz m ............................................................................................................................ ...975 21.2 Typy rezu ltató w różnych realizacji funkcji w irtu a ln e j................................................ ...979 21.3 D alsze sz c z e g ó ły .................................................................................................................... ...982 21.4 W czesne i p ó źn e w iązanie ................................................................................................ ...984 21.5 K iedy dla w y w o łań funkcji w irtualnych, m im o w szy stk o , zachodzi w czesn e ...986 wiązanie? ........................................................................................................ ...988 21.6 K ulisy białej m agii, czyli: Jak to jest zrobione ? ......................... ...989 21.7 Funkcja w irtu aln a, a m im o to i n l i n e ..................................... ...990 21.8 P ojedynek - funkcje p rzeład o w an e contra funkcje w irtu aln e ...992 21.9 Klasy abstrakcyjne ............................................................................ ...999 21.10 D estruktor? to najlepiej w ir tu a ln y ! ................................................ .1004 21.11 Co p ra w d a , ko n stru k to r nie m oże być w irtualny, ale............... .1011 21.12 R zutow an ie d y n a m i c _ c a s t jest dla typów polim orficznych .1015 21.13 W szystko, co najw ażniejsze.............................................................. .1017 21.14 Finis c o r o n a to p u s ................................................................................ 1018 21.15 Ć w icze n ia...............................................................................................
Spis treści
X III
22.1 Biblioteka io s tre a m ............................................................................................................... 1022 22.2 S tru m ie ń ..................................................................................................................................1023 22.3 Strum ienie zd efin io w an e stan d ard o w o ....................................................................... 1025 22.4 O peratory » i « ............................................................................................................... 1026 22.5 D om niem ania w p racy strum ieni zdefiniow anych s ta n d a r d o w o ........................... 1027 22.6 U w aga na p r io r y te t.............................................................................................................. 1031 22.7 O peratory « o raz » definiow ane przez użytkow nika ...........................................1032 22.7.1 O perato ró w w staw iania i w yjm ow ania ze strum ienia - nie dziedziczy się ...1038 22.7.2 O peratory w staw ian ia i w yjm ow ania n ie mogą być w irtu aln e. N iestety.........1039 22.8 Sterow anie form atem .........................................................................................................1041 22.9 Flagi stanu fo rm ato w an ia ................................................................................................ 1041 22.9.1 Znaczenie poszczególnych flag sterow ania form atem ........................................1044 22.10 Sposoby zm ian y try b u (reguł) fo rm ato w a n ia............................................................... 1050 22.10.1 Zm iana sp o so b u form atow ania funkcjam i s e t f , u n s e t f ............................ 1052 22.10.2 D odatkow e funkcje do zm iany param etró w fo rm ato w a n ia............................... 1057 22.11 M a n ip u la to ry ..........................................................................................................................1065 22.11.1 M anipulatory b e z a rg u m e n to w e ..................................................................................1066 22.11.2 M anipulatory p a ra m e try z o w a n e ............................................................................... 1072 22.11.3 D efiniow anie sw oich m anipulatorów .......................................................................1076 22.11.4 M anipulator jako fu n k cja...............................................................................................1077 22.11.5 D efiniow ane m an ip u lato ra z p a ra m e tre m ..................................................................1079 22.12 N ieform atow ane operacje w ejścia/w yjścia .................................................................. 1083 22.13 O m ów ienie funkcji wyjm ujących ze stru m ien ia............................................................1086 22.13.1 Funkcje do p racy ze znakam i i s trin g a m i.................................................................1086 22.13.2 W czytyw anie b in arn e - fu n k q e r e a d i read so zn e .............................................1093 22.13.3 Funkcja i g n o r e .............................................................................................................1095 22.13.4 Pożyteczne funkcje pom ocnicze................................................................................... 1096 22.13.5 Funkcje w staw iające do stru m ie n ia .............................................................................1099 22.14 Strum ienie płynące d o lub od p lik ó w ............................................................................. 1102 22.14.1 O tw ieranie i zam ykanie strum ienia ........................................................1104 22.15 Błędy w trakcie p racy strum ienia ....................................................................................1110 22.15.1 Flagi stanu b łęd u strum ienia ............................................................................... 1111 22.15.2 Funkcje do p racy na flagach b łę d u .............................................................................. 1112 22.15.3 Kilka u d o g o d n ie ń ............................................................................................................. 1113 22.15.4 U staw ianie i kasow anie flag błędu stru m ie n ia ........................... 1115 22.15.5 Trzy plagi - czyli „gotow iec", jak rad zić sobie zb łę d a m i...................................... 1119 22.16 Przykład program u pracującego na p lik a c h .................................................................. 1123 22.17 Strum ienie, a tech n ik a rzucania w y ją tk ó w ..................................................................... 1126 22.18 W ybór miejsca czytania lub pisania w p l i k u ..................................................................1130 22.18.1 Funkcje sk ład o w e inform ujące o pozycji w sk aźn ik ó w ...........................................1132 22.18.2 W ybrane fu nkq'e składow e do pozycjonow ania w sk aźn ik ó w .......................... 1132 22.19 Pozycjonowanie w przykładzie w iększego program u ............................................... 1136 22.20 Tie - harm onijna praca dw óch s tru m ie n i........................................................................ 1 143 22.21 Dlaczego tak nie lubim y biblioteki s t d i o ? ..................................................................1145
XIV
Spis treści
1148 22.22 Synchronizaq'a biblioteki i o s t r e a m z biblioteką s t d i o 1150 22.23 S trum ień zapisujący d o obiektu klasy s t r i n g 1155 P rogram p rzy k ład o w y ilustrujący użycie klasy o s t r i n g s t r e a m 22.23.1 22.24 S trum ień czytający z obiektu klasy s t r i n g .................................................................. 1159 P rosty p rzy k ład użycia stru m ien ia istrin g stream .................................................. 1161 22.24.1 22.24.2 W czytyw anie arg u m en tó w w yw oływ ania p r o g r a m u ..........................................1167 1170 22.25 O żenek: stru m ień s t r i n g s t r e a m - czytający i zapisujący do stringu 72 P rzy k ład o w y pro g ram posługujący się klasą s t r i n g s t r e a m 22.25.1 22.26 Ć w iczenia................................................................................................................................. 1174 P rzegląd kilku technik p ro g ra m o w a n ia ......................................................................... 1163 Programowanie liniow e .............................................................................................. 1183 P rog ram o w an ie p ro ced u raln e (czyli "orientow ane funkcyjnie") ......................1183 Programowanie z u k ry w an iem danych .................................................................. 1184 P rog ram o w an ie obiektow e programowanie „b azujące" na o b ie k ta c h ........ 1184 185 P rogram o w an ie O biektow o O rientow ane (OO)
23.1.1 23.1.2 23.1.3 23.1.4 23.1.5 23.2 O w yższości p ro g ram o w an ia obiektow o o rien to w an eg o nad Św iętam i Wielkiej 1186 Nocy 23.3 O biektow o O rientow ane: P ro je k to w a n ie ........................................................................1189 1190 23.4 P raktyczne w skazów ki dotyczące projektow ania p ro g ram u techniką O O .....................1191 Rekonesans - czyli rozpoznanie zagadnienia 23.4.1 1192 Faza projektow ania 23.4.2 E tap 1: Identyfikacja zach o w ań sy ste m u ...................................................................1193 E tap 2: Identyfikacja obiektów (klas o b ie k tó w ).......................................................1193 23.4.4 E tap 3: U system atyzow anie klas obiektów ................................................................1195 23.4.5 1197 E tap 4: O kreślenie w zajem nych zależności klas 23.4.6 E tap 5: Składanie m odelu. O kreślanie sekwencji d ziałań obiektów i cykli 23.4.7 1199 życiowych 1200 23.5 Faza im plem entacji 1201 23.6 Przykład projektowania 23.7 Faza: R o zp o zn an ie naszego z a g a d n ie n ia ........................................................................ 1201 205 23.8 Faza: Projektow anie E tap 1 - Identyfikacja zach o w ań naszego s y s te m u ................................................ 1205 23.8.1 . i. >■ . . < ___ .•______ j ______ :__n n /: E tap 2 - Identyfikacja klas obiektów , z którym i m a m y do czy n ien ia................1206 23.8.2 E tap 3 U system atyzow anie klas obiektów z w ystępujących w n aszy m system ie 23.8.3 1210 2 1 E tap 4 - O kreślenie w zajem nych zależności klas 23.8.4 E tap 5 - S kładam y m odel naszego sy stem u ..............................................................1214 23.8.5 23.9 Im plem entacja m odelu naszego sy ste m u ........................................................................ 1218 23.10 Sym fonia C++, C oda 23.11 P o sło w ie .................................................................................................................................... Y2.7&
A. A.2
D laczego k o m p u ter nie liczy tak jak my? System szesn astk o w y (heksadecym alny)
Spis treści
A .3
XV
Ć w iczenia
...1237
Skorowidz
1239
ifrćiAsn*- w
1
Proszę nie czytać tego!
0
Proszę nie czytać tego ! Zaprzyjaźnijmy się ! eśli m am y z e sobą spędzić p arę g o d zin , to p ro p o n u ję - p rzejd źm y na „ty ". Moje n azw isk o zobaczyłeś już na okładce, d o d a m w ięc tylko, że książka ta wstała na p o d staw ie moich d o św iad czeń jako p rogram isty:
J
♦> w H a h n -M e itn e r-In s titu t1 w B erlin ie-w ó w czas Z ach o d n im (N iem cy), ♦$» w In stitu t d e Recherches S ubatom iques w S trasb o u rg u (Francja),
•t* oraz w G esselschaft fuer S ch w erio n en fo rsch u n g - D arm stad t (N iem cy). W instytutach tych mój m acierzysty In sty tu t Fizyki Jądrow ej im. H enryka N iew odniczańskiego PAN (K raków) o d g ry w a znaczącą rolę w ek sp ery m en tach z d zied zin y badania stru k tu ry jądra atom ow ego m eto d am i sp ek tro sk o p o w ym i. E k sp ery m en ty te w ym agają ro zb u d o w an eg o o p ro g ram o w an ia, w k tó re go tw orzeniu m iałem zaszczyt uczestniczyć. Jestem oczarow any jasnością, prostotą i logiką p ro g ram o w an ia w języ k u C++ i dlatego p ro p o n u ję Ci sp ęd zen ie z e m ną paru chw il w k rain ie p rogram ow ania obiektow o o rientow anego.
Książka ta jest następcą wydanej przed 10 laty książki "Symfonia C++". Tam ta 700 stronicow a książka m iał w iele w y d a ń i cieszyła się o g ro m n ą poczytnością - ale, gdy pojaw ił się d łu g o oczekiw any sta n d a rd języka C ++, należało napisać n ow ą książkę. To w łaśn ie efekt tej pracy. M am nadzieje, że i tę książkę czytelnicy polubią, tak jak tam tą starą "Sym fonię C + + ” 1)
instytut badań jądrowych
2
Proszę nie czytać tego!
Wstępne założenie Książka ta jest napisana z myślą o czytelnikach, którym nieobce są takie pojęcia jak program, instrukcja, dyskietka, monitor. O czyw iście najlepiej by było, gdy byś znał już jakiś inny, choćby najprostszy język programowania - wszystko jedno, czy to będzie BASIC czy język C. Jeśli tak nie jest, to - jak sądzę wystarczy Ci 20 minutowa rozmowa z kolegą, który już programuje, lub przeczytanie jakiejś strony internetowej tłumaczącej, co to znaczy progra m ow ać komputer.
T
N ajk ró cej m ów iąc po leg a o n o n a zm ianie sp o so b u m y ślen ia o p ro g ra m o w a n iu . P rz y p o m n ij sobie jak to jest, g d y m usisz n a p isa ć p ro g ra m w z n a n y c h Ci d o tej p o ry języ k ach p ro g ra m o w a n ia . W szystko je d n o czy to b ęd zie p ro g ra m na s te ro w a n ie lotem sam o lo tu , czy p ro g ram n a o b s łu g ę ek sp ery m e n tu fizycznego. O tó ż n ajp ierw starasz się te n k aw ałek rzeczy w isto ści, k tóry m a sz o p ro g ra m o w ać, w y razić za p o m o c ą liczb - zm ien n y ch w y stęp u jący ch w p ro g ram ie. D ziesią tk i takich z m ien n y ch śą (luźno) ro z rz u c o n e p o T w oim p ro g ra m ie , a s a m o n a p isa n ie p ro g ra m u , to n ap isan ie sekw encji d z ia ła ń na tych liczbach. T en s a m p ro b lem w ujęciu obiektow o o rie n to w a n y m ro zw iązu je się ta k , że w p ro g ra m ie b u d u je się m a łe i w iększe m o d e le o b ie k tó w św iata re a ln e g o , po czy m w y p o sa ż a się je w zach o w an ia i p o p ro s tu p o zw ala śię im d ziałać. O b ie k te m m o że być ster k ie ru n k u sam olotu, c z y te ż u k ła d elek tro n iczn y p r z e t w a rz a ją c y jakiś sygnał. G d y tak ie m odele ju ż w p ro g ra m ie istnieją, w y d a ją sobie n a w z a je m polecenia - ń p . o b iek t d rążek s te ro w y w y d a je p o le cen ie stero w i k ie ru n k u , ab y w y ch y lił się 7 sto p n i w praw o . O b ie k ty n ie pouczają się ja k coś trzeba zro b ić, ty lk o m ó w ią co trz e b a zrobić. P ro g ra m o w a n ie tą te ch n ik ą - po za tym , ż e je st logiczne, bo o d z w ie rc ie d la relacje istniejące w św iecie rzeczy w isty m - p ro g ra m o w a n ie tą te c h n ik ą d o s ta r cza p o p ro stu dobrej z a b a w y .
Proszę nie czytać tego!
3
Argument o dobrej zabawie nie p rz e k o n a łb y oczyw iście n ig d y szefó w d u ży ch zesp o łó w pro g ram istó w — dla nich n ajw aż n iejszy jest fakt, że ta technika p o zw ala, by p rogram iści zn ali się tylko na s w o im k aw ałk u pracy b ez konieczności o p a n o w y w a n ia całości olb rzy m iego p ro jek tu . P ozw ala ona też na b a rd z o łatw e w p ro w a d z a n ie pow ażnych m odyfikacji d o p ro g ram u - bez konieczności an g a ż o w a n ia w tę pracę całości zespo łu . D la te g o ostatniego - p ro g ram o w an ie o b iek to w o o rien to w an e jest w ręcz jakby stw o rzo n e. Te cechy są ła tw o przeliczan e na p ie n iąd ze - n ak ład y finan so w e n a p ra c ę zespołu. Jest tak że p o w ó d , d la którego język C + + zrobił k arie rę n a w e t w śró d p ro g ram i stów p racu jący ch pojedynczo, a tak że w śró d am ato ró w . O tóż język ten p o zw ala na ła g o d n e p rzejście z klasycznych tech n ik p ro g ram o w an ia na technikę o biek tow o o rien to w a n ą. Ta cecha n a z y w a n a jest h y b ry d o w o ścią języka C ++. W p ro g ram ie m o żn a technikę O O sto so w a ć w takim s to p n iu , w jakim się ją o p an o w ało . Tak też d zieje się najczęściej — program iści w y b ierają sobie z niego n ajp ierw tylko sam e „ro d zy n k i" i je sto su ją, a w k ażd y m n astęp n y m p ro g ram ie sięgają p o coś więcej. W języku C ++ m o żn a pisać n a w e t p ro g ra m y nie m ające z techniką O O nic w spólnego. T y lko, że to tak, jakby zw ied z ać G ra n a d ę m ając zam k n ięte oczy^. Dość na tym . O technice O O p o ro zm a w iam y jeszcze w ielokrotnie. T eraz chciałbym w y tłu m a c z y ć się z p a ru sp raw .
Dlaczego ta książka jest taka gruba? N ie w y n ik a to w cale z faktu, by język C + + był tak tru d n y . U zn ałe m tylko, ż e to, co uczy n a p ra w d ę - to p rzy k ład y . W książce w ięc o p ró cz „teorii" są setki przy k ład o w y ch p ro g ram ó w , a k a ż d y je st szczegółow o o m ó w io n y . P rzy k ład o m tow arzyszą w y d ru k i pokazujące w y g lą d ekranu po w y k o n a n iu p ro g ram u . To w szystko w ła śn ie sp raw ia, że k siążk a jest tak o b szern a. Je d n a k w y d ru k i te załączam w p rześw iad cz en iu , że często łatwiej zo rie n to w a ć się „co p ro g ra m robi" - rzucając o k iem na taki w y d ru k (ekran). D opiero p o te m ra d z ę an alizo w ać sam program . Do w y k o n an ia p rzy k ład o w y ch p ro g ra m ó w uży łem d w ó c h k o m p ilato ró w : ♦> w sy tem ie operacyjnym Linuks: G N U g++ w ersja 3.3.3
*X* w sy tem ie operacyjnym W in d o w s: M icrosoft V isu al C ++ w ersja 6.0 O czyw iście, w k siążce zajm ujem y się sa m y m językiem C ++, w ięc p o w in n a Ci ona pom óc n iezależn ie od tego, z k tó ry m k o m p u terem i ty p e m k o m p ilato ra m asz d o czynienia. 2)
"...kobieto, daj jałmużnę ślepcowi, bo nie ma większego nieszczęścia niż być ślepcem w Granadzie..."
4
Proszę nie czytać tego!
Wersja języka Sam język C++ ciągle się unow ocześnia. Opisuję go tu bez tzw. templates i exception handling, jako że jest to temat na tyle obszerny iż poświęciłem im osobną książkę pt. "Pasja C++"ł . Absolutnym autorytetem w sprawie języka był dla mnie dokument standardu języka C++ (International Standard ISO/IEC 14882).
W razie jakichkolwiek niejasności odsyłam d o tego tekstu. (Uprzedzam jednak, że jest napisany w bardzo sform alizowanym stylu i nie nadaje się jako podręcz nik do nauki). W tekście czasem wspom inam o wersji języka. Pamiętać należy, ż e termin wersja języka nie ma nic w spólnego z wersją kompilatora. Wersja kompilatora to wersja konkretnego dostarczonego Ci produktu. Tymczasem wersja języka to reguły gry, w edług których ten kompilator postępuje.
Pióro Ktoś kiedyś powiedział, że są dwa style pisania - pierwszy to: „-popatrzcie jaki! ja jestem mądry" - a drugi to: „-popatrzcie jakie to proste". Ja w tej książce wybieram ten drugi wariant w przeświadczeniu, że prędzej zaprowadzi do celu. Z drugiej strony wiem, że bezpośredni, wręcz kolokwialny styl tej książki, zupełnie naturalny w książkach na Zachodzie - w Polsce może zaskoczyć nie których czytelników.
Dla kogo jest ta książka? Pisząc tę książkę musiałem najpierw określić sobie czytelnika, dla którego jest ta książka. Otóż jest ona dla tzw. szerokiego grona czytelników. Pisałem ją tak, by była podręcznikiem dla czternastoletniego programisty amatora, jak i dla zawo dow ego informatyka. Trudno te sprawy pogodzić, więc są w niej kompromisy w obu kierunkach. Co do jednego z takich kompromisów mam najwięcej obaw: i
Czołem bracia angliści! Gdy słyszałem, jak początkujący programiści wymawiają występujące w języku C++ angielskie słowa takie jak np. „unsigned", „volatile", „width" — postanowiłem w miejscach, gdzie się pojawiają po raz pierw szy - zamieście adnotacje o ich wymowie. Wymowa podana jest nie w transkrypcji fonetycznej, ale za pomocą polskich liter. Wiem, że fakt ten może razić wielu czytelników Proszę wtedy o wyrozumiałość - pamiętajcie, że ta książka ma służyć równie: maluczkim. Wymowa podana jest chyłkiem - w przypisie, w nawiasie, a w dodatku jeszcze w cudzysłow ie- więc nie trzeba jej koniecznie czytać. Spodzie wam się, że fakt zamieszczenia wymowy nie będzie przeszkadzał Czytelnikom Są w niej omówione szablony funkcji, szablony klas, klasy pojemnikowe i obsługa sytuacj wyjątkowych. Zajrzyj do niej jeśli już przeczytasz „Symfonię C++ standard" i ją polubisz.
5
Proszę nie czytać tego!
k tó rzy ję zy k iem angielskim p o słu g u ją się na co d z ie ń o d la t - ale na p ew n o w z b u d z i p ro te s t tych, k tórzy an g ielsk ieg o nauczyli się p rzed w czo raj. A sw oją d ro g ą , to n aw et w śró d m oich kolegów fizyków i p ro g ram istó w - nie sp o tk ałem p o p ra w n ie w ym aw iających słowo: „width".
A teraz o strukturze tej książki M ożna ją p o d z ie lić na dw ie z a sad n icze części - tę k lasy czn ą, opisującą zw y k łe n a rz ę d z ia p ro g ram o w an ia, i d ru g ą (zaczynającą się o d ro zd ziału o klasach) opisującą n a rz ę d z ia d o p ro g ram o w an ia obiektow o o rien to w an eg o . Po tym w sz y stk im n astę p u je ro zd ział om aw iający operacje w ejścia/w y jścia - czyli sposo b y p racy z takim i u rz ąd z en iam i zew n ętrzn y m i jak k law iatura, ekran, d y sk i m a g n ety cz n e. W reszcie n astęp u je rozdział o p ro jek to w an iu p ro g ram u ob iek to w o o rien to w a n eg o - zaw ierający szczegółow y in stru k taż. U znałem te sp ra w y z a b a rd z o w ażne, g d y ż często p ro g ram ista m ając w ręce to d o sk o n ałe n a rz ę d z ie p ro g ra m o w a n ia , jakim jest C ++, nie w ie, co z n im począć. .
■
Hi
f J ł .v • :
.f
'
i
. h . ^ .Ł*
a
Metodą kolejnych przybliżeń N ie liczę n a to, ż e czytając tę k siążk ę zro zu m iesz w sz y stk o od razu. W ręcz p rzeciw n ie - w tekście w ielokrotnie su g eru ję, byś o p u śc ił n iektóre p arag rafy p rz y p ie rw s z y m czytaniu książki. N ie w szystkie a s p e k ty są w ażn e ju ż na sam ym p o czą tk u nauki. Lepiej m ieć n ajp ierw ogólny p o g lą d , a do p iero p o tem zagłębiać się w tru d n iejsze szczegóły. T e fragm enty p o p rz e d z a zw y k le zn aczek X Licząc n a to, ż e p o p ierw szym czy tan iu całości w n ie k tó re miejsca w rócisz p e w n e fra g m e n ty tekstu o p atrzy łem ta k że adnotacją: „ d la w tajem niczonych". Są tam u w a g i w ybiegające nieco d o p rz o d u , ale d o ty c zące sp raw , k tó re p o p ie rw szy m czy ta n iu będziesz ju ż p rzecież znał. N ie szan u j tej książki. P rzygotuj sobie kolorow e (tzw . niekryjące) flam astry i czytając zak reślaj w ażn e dla C iebie w y ra z y czy m yśli. N a m arg in esach p isz ołów kiem sw o je u w a g i i kom entarze. C h o ciażb y m iały to b y ć teksty ty p u „C o za bzdura!" a lb o „ N ie rozum iem d laczeg o ..." albo „p o ró w n aj d w ie stro n y w cześ niej". Ta a k ty w n o ść zaprocentuje Ci naty ch m iast, bo m ając tak osobisty s to s u nek d o te k stu , ła tw iej koncentrow ać n a n im u w ag ę - co je st w a ru n k ie m sine qua non szybkiej nauki.
Jeśli znasz język C „klasyczny” (czyli tzw. ANSI C) to za p e w n e p ie rw sz e rozdziały będ ą d la Ciebie w y ra ź n ie z a łatw e. M im o to rad zę je p rzejrzeć, chociaż p o b ieżn ie, g d y ż w y stęp u ją p e w n e ró żn ice w językach C i C ++ - n aw et na ty m etap ie (oczyw iście na k o rzy ść C++). G orąco n a to m ia st zachęcam d o u w a ż n e g o p rzeczy tan ia ro z d z ia łu o w sk a ź n i kach. Z d o św iad cze n ia w iem , ż e te s p ra w y zna się zw y k le najgorzej. T y m cza sem zaró w n o w klasycznym C jak i w C ++ w skaźnik je s tb a rd z o w ażn y m i p o ż y tecznym n arzę d ziem . D latego te n ro z d z ia ł tak ro zb u d o w ałe m .
6
Proszę nie czytać tego!
Szukajcie, a znajdziecie N a koniec uw ag a zec ersk a . W tekście k siążk i często w ystępują p rz y p isy u dołu stro n y . Jeśli - z p rz y c z y n ty p o g raficzn y ch —jakiś przy p is nie b ę d z ie m ógł się pojaw ić się u d o łu stro n y , n ależy g o sz u k a ć n a stronie następnej.
Podziękowania W inien jestem w d z ię c z n o ść p rzyjaciołom , k tó rz y w jakiś sposób p rzy czy n ili się d o p o w stan ia tej k siążk i. Byli to: Sven B laser, O laf Boebel, Jo h a n n e s Diemer, Jeffrey E rxm eyer, B engt Skogvall, Kai S o m m e r, K laus Spohr, Joachim Steiger i D etlef W eid en h am m er. Z u p e łn ie w y ją tk o w e p o d zięk o w an ia n a le ż ą się Sycylij czy k o w i P ierpaolo F ig u era za w iele g o d z in , k tó re mi pośw ięcił. P oniew aż ta k siążk a pisana b y ła o d ra z u p o p o lsk u , d la te g o ża d e n z nich n ig d y jej nie czytał. W szy stk o , co d o b re w tej książce, jest je d n a k ich zasługą; w sz y stk o , co złe - to w y łącz n ie moja w in a . Jestem w in ien w d z ię c z n o ść p ierw szy m czy teln ik o m tej książki: M irkow i Z ięb liń sk iem u i B ożenie P o tem p ie. Ich u w ag i k ry ty cz n e sp ra w iły, ż e książka sta ła się lep sza, ich u w a g i p o z y ty w n e - z a w s z e w p raw iały m nie w d o b ry nastrój. O czyw iście na p e w n o n ie je d n o da się w tej k siążc e popraw ić. Jeśli b ęd ziesz m iał jakieś u w ag i, to p ro sz ę p rzy ślij mi je po cztą elek tro n iczn ą na a d re s: j e r z y . g r e b o s z 0 i f j .e d u .p l lu b p o cztą trad y cy jn ą na a d re s w y d aw n ictw a. Z gó ry w y rażam m oją w dzięcz ność z a n aw et n a jd ro b n iejsze przyczynki. K od źró d ło w y p rz y k ła d o w y c h p ro g ra m ó w z tej książki m o ż n a sobie łatw o s p ro w a d z ić z m oich stro n W W W w In ternecie. Jej obecny a d re s to: h t t p : / / w w w .i f j . e d u .p l/~ g r e b o s z N a w e t jeśli ten a d re s się k ied y ś zm ieni - ła tw o zn aleźć n o w ą lokalizację za p o m o c ą intern eto w ej w y sz u k iw ark i pod ając h a sło "SYMFONIA C ++", lub moje n azw isk o . N a k o n iec w y p a d a mi się w ytłum aczyć z ty tu łu tego w stęp u . C hciałem tu p o w ie d z ie ć p arę w ażn y ch , ale i trochę n u d n y c h spraw . W iedząc, ż e czytelnicy najczęściej o p u szczają w stę p y - d ałem taki ty tu ł podejrzew ając, ż e zak azan y ow oc najlepiej sm akuje.
Rozdz. 1. Startujem y! Pierwszy program
1
7
Startujemy! by pisać, trzeb a pisać.... - Taką ra d ę zw ykło się d a w a ć początkującym li teratom . T ę sam ą zasadę m ożna zastosow ać do piszących program y. P ro gram ow ać w d a n y m języku m ożna n auczyć się tylko pisząc w nim program y. Dlatego b ez d alszy ch w stępów n ap iszm y nasz
A
1.1
P ierw szy p ro g ra m t i n c l u d e < io str e a m > i n t m a in ()
{ std ::c o u t «
"Witamy na p o k ła d z ie " ;
)
Wykonanie tego programu spowoduje pojawienie się na ekranie tekstu: Witamy na p o k ła d z ie
Przyjrzyjmy się bliżej temu programowi W każdym p ro g ram ie w języku C++ m u si być specjalna funkcja zw ana ma i nA O d tej funkcji zaczy n a się w y k o n y w an ie program u. Treść tej funkcji - jej ciało — czyli innym i sło w y instrukcje w y k o n y w an e w ram ach tej funkcji - za w a rte są m iędzy dw om a n aw iasam i klam row ym i: {j W naszym p rz y p a d k u w funkcji m a in jest tylko jedna instrukcja 1)
ang. main - główna [czytaj: „mejn"].
8
Rozdz. 1. Startujem y! Pierwszy program s t d : : c o u t << "W itam y n a p o k ł a d z i e " ;
która sprawia, ż e na standardow ym urządzeniu wyjściowym c o u t - czyli pc prostu na ekranie - ma się pojawić tekst, zam ieszczony tu w cudzysłowie. Skrót c o u t 2 w ym aw ia się p o polsku Si-aut. (Błagam, tylko nie: Ś i-a u ł ! ) Co oznacza s t d : : ? Na razie trudno mi to dobrze wyjaśnić. Ogólnie mówiąc, jest to powiedze nie, że nazwa co u t - jest nazwą z biblioteki standardowej. Na razie niech to wystarczy, wrócimy do tej spraioy. Znaki << oznaczają w łaśnie akcję, którą ma podjąć c o u t - czyli wyprowadzić na ekran tekst. U m ieszczony na końcu średnik jest znakiem końca instrukcji.
Operacje wejścia/wyjścia N ależy tu wyraźnie podkreślić, że operacje związane z wprowadzaniem i wy prow adzaniem informacji na urządzenia takie, jak np. ekran - czyli tzw. opera cje w ejścia/w yjścia - nie są częścią definicji języka C++. Funkcje odpowiedzial ne za te czynności są w jednej ze standardowych bibliotek, w które zwykle w yposażane są kompilatory. Abyśmy m ogli z takiej biblioteki skorzystać w pro gramie, m usim y na początku umieścić linijkę # in c lu d e < io s tr e a r a >
która oznacza, że życzym y sobie, by kompilator przed przystąpieniem do pracy nad dalszym i linijkami programu w staw ił ^ w tym miejscu tak zwany plik nagłów kow y biblioteki io s tr e a r a , a następnie zapoznał się z nim. Dla zainteresowanych dodam, że tenże plik nagłówkowy biblioteki zawiera dokładne deklaracje funkcji bibliotecznych, z których ewentualnie można korzystać. Dzięki temu, że kompilator zapoznaje się z tym i deklaracjami może od tej pory sprawdzać nas, czy posługujemy się tym i funkcjami poprawnie. To bardzo korzystna cecha. B iblioteką tą zajm iem y się bliżej p o d k o n iec tej książki. /S IO ' 1
L łłT t li
ODOi
y V
Wolny format zapisu programu A teraz uwaga natury ogólnej. Program p isze się umieszczając kolejne instru kcje w linijkach jedna pod drugą. Otóż, w niektórych językach programowania (np. FORTRAN'ie, BASICu) obowiązują ścisłe reguły określające pozycję (w linijce), na której dany składnik instrukcji m oże się znaleźć. •
2) 3)
Np. w FORTRAN'ie - Jeśli chcemy umieścić znak komentarza, to stawiamy go w kolumnie pierwszej, jeśli ma to być numei etykiety - to do tego służą kolum ny 1-5, jeśli chcem y umieścić znak kontynuacji z poprzedniej linijki — to um ieszczam y gc w kolumnie szóstej.
skrót od ang. C-onsolc OUT-put ang. mclude [czytaj: „inklud"] - wstaw,włącz (np. fragm ent tekstu).
9
Rozdział. 1. Startujem y Pierwszy program •
P o d o b n ie w BASIC'u - linia instrukcji m u si się zacząć od n u m e ru etykiety.
W języ k u C ++ je st inaczej. Język C + + (p o d o b n ie jak C) jest językiem o tzw. w o ln y m fo rm acie. K rótko m ów iąc - nie ma żad n y c h p rzy m u só w . W szystko m oże zn aleźć się w k ażdym miejscu linii, a n a w e t zostać ro zp isan e na 10 linijek. Poza n ie licz n y m i sytuacjam i, w d o w o ln y m m iejscu in s tru k q i m ożna przejść do now ej linii i ta m k o n ty n u o w ać p isanie. To d latego, ż e ko n iec instrukcji okreś lany jest nie p r z e z koniec linii, ale p rz e z śred n ik , k tóry staw iam y na końcu.
Białe znaki W ew n ątrz in stru k c ji m ożna p o staw ić d o d a tk o w e zn ak i spacji i tabulatory, czy n aw et zn ak i n o w ej linii. Są to tzw. b iałe zn ak i - białe, b o na p ap ierze objawiają się jako n ie z a d ru k o w a n e . Znaki te n a p o tk a n e w e w n ą trz instrukcji są p raw ie zaw sze ig n o ro w a n e . Z atem , n asza instru k cja rów nie d o b rz e m o głaby w y g ląd ać tak: stć::cout << "w itam y na p o k ł a d z i e ” i W staw ian ie b ia ły c h zn ak ó w służy n ie k o m p ilato ro w i, lecz program iście. P o m a ga w ty m , żeb y p ro g ra m w yglądał czytelnie. Jeśli nam n a ty m w cale nie zależy , to m o żem y ró w n ie d obrze napisać p ro g ra m tak: tinclude
i n t m a in () { s t d : : c o u t « " w ita m y na p o k ł a d z i e " ; }
N ik t ro z s ą d n y je d n a k tak nie robi z d w ó c h p o w o d ó w : <♦ p ro g ra m staje się w ted y niep rzejrzy sty , ♦> k o rzy stając z różnych specjalnych n arzę d zi d o u ru c h a m ia n ia p ro g ra m ó w (tzw . d ebuggerów )4 m a m y m ożliw ość ś le d z e n ia w y k o n y w a n ia p ro g ra m u krokow o: linijka za linijką. D o b rze je st w ięc m ieć w je d n e j linijce ty lk o jedną instrukcję.
Narzędzia do edycji tekstu programu M am y zatem n a p isa n y gotow y tekst p ro g ra m u . Z araz, z a ra z ... A w łaściw ie to jak m y śm y go n ap isali? N apisaliśm y go p o słu g u jąc się tak z w a n y m ed y to re m te k stu . W k o m p u te rz e e d y to r tekstu to n a rzę d zie p rzezn aczo n e w łaśn ie do p is a n ia tek stó w . P o s łu g u jąc się ed y to rem tek stu m ożna napisać list d o kolegi, te k s t a rty k u łu p o p u la rn o n au k o w eg o lu b n ap isać tekst p ro g ram u C++. Z w yk le m a m y d o w yb o ru kilka różnych ed y to ró w . C zy k a ż d y z n ich n a d a je s ię d o pisania p ro g ra m u ? 4)
ang. biig - owad; stąd ang. dcbuggcr [czytaj: „debager"! - jakby: „odpluskw iacz"
10
Rozdz. 1. Startujem y! Pierwszy program N ie, n ie k a ż d y . T o d latego, ż e te k st naszego p ro g ram u (po zakoń czen iu p is a n ia go) m a zo stać z ło ż o n y na d y sku p o staci tzw . pliku te k sto w e g o zaw ierająceg o ty lk o te znaki, które nap isaliśm y i nic więcej. P o m y ślałeś p e w n ie te ra z : "-T o m oże b y ć coś więcej?" M oże. N ie w ła śc iw y e d y to r m ó g łb y p ró b o w a ć d o d a ć d o tekstu n aszeg o progra m u jakieś d o d a tk o w e z n a k i sterujące - n a p rz y k ła d m ów iące o ty m , że cały tekst n a p is a n y tak ą, a n ie in n ą czcionką (lub tłu sty m drukiem ). E d y to r m o ż e też d o d a w a ć na p o czątk u p lik u jakieś inform acje o rozm iarach p a p ie ru czy o d leg ło ści tek stu o d m a rg in e su stro n y . T ym czasem m y tych w szys tkich d o d a tk ó w nie ch cem y . Jeśli masz jakieś doświadczenia np. z edytorem tekstu zwanym Microsoft Word, to jest to właśnie przykład edytora, który do pisania programu C++ sianie nadaje. Ten edytor został stworzony z myślą o pracy sekretarki, a nie pracy programisty. Jak ro z p o z n a ć czy e d y to r nad aje się d o p is a n ia pro g ram ó w ? P ro sty m sposobem je st sp ra w d z e n ie czy tw o rz ą c d o w o ln y te k s t m o ż esz go p o tem za p isa ć na dysku z ro zszerze n iem " .c p p " . C zyli czy m o ż e sz stw o rzy ć plik o przykładow ej; n a z w ie " p r ó b a . c p p " . Jeśli p ro g ram n a rz u c a Ci jakieś w łasn e ro zszerzen ie - j c h a ra k te ry sty c z n e d la tego ed y to ra - to n ajp ra w d o p o d o b n ie j o z n a c z a , m a on sw ój w ła s n y s ta n d a rd z ap isu . T akiego e d y to ra n ie w ybieraj. Jak w y b rać? Jeśli n ie jesteś p ew ien , k tó ry e d y to r w y b rać - w y b ie rz najprostszy. T aki, k tó ry nie d aje m o żliw o ści zm ian y czcio n k i, p isania tłu sty m d ru k ie m itd. N ajlepiej je d n a k zap y tać k o g o ś, kto na p o d o b n y m k o m p u terze, u ży w ają c p o d o b n e g o sy stem u op eracy jn eg o p is z e p ro g ra m y w C++. K o leg a m oże Ci p o ra d z ić coś b a rd z o d o b reg o , bo są ed y to ry specjalnie tw o rz o n e do p isa n ia p ro g ra m ó w C++. M am y z a te m n a p isa n y i zło żo n y na d y sk u p lik z tek stem p ro g ra m u . C z y m ożna ju ż taki p ro g ram u ru ch o m ić? Nie!
Kompilator P ro g ram w takiej p o staci, jak to w łaśnie n ap isaliśm y , jest d la k o m p u te ra n iezro zu m iały . Są w n im litery i jakieś słow a w języ k u C++, ale p rzec ież p rocesor n aszeg o k o m p u tera n ie zareag u je na tak ie litery. M ów im y że, w takiej postaci, je st to te k s t źró d ło w y programu. (Kod źró d ło w y ).
jem y sk o m p ilo w an ą w ersję naszego p ro g ra m u . S k o m p ilo w a n ą, czyli p rz e tłu m aczo n ą na język m a szy n o w y .
11
Rozdział. 1. Startujem y Pierwszy program
T aka s k o m p ilo w a n a w ersja jest, co p ra w d a , ju ż w języ k u p rocesora, ale jest jeszcze n ie p e łn a - m u si zostać p o łą czo n a z tak z w a n y m i bibliotekami.
Linker Biblioteki z a w ie ra ją d o d atk o w e in stru k cje p ro g ra m u , k tó re k toś inny dla nas n ap isał, a m y m o ż e m y się nim i po słu ży ć. P rz y k ła d o w o - jeśli w p ro g ram ie sk o rzy stasz z o b liczan ia sin u sa k ąta, to instru k cje obliczające ten sin u s zło żo n e są w łaśn ie w b ib lio tek ach . W p o staci ju ż sk o m p ilo w an ej. Je d n a k w p ro g ra m ie , k tóry w łaśn ie n ap isaliśm y , nie m a o b liczan ia sinusa. Po co n am w ięc b ib lio te k i? R zeczyw iście, n ie p o trze b u jem y tu ż a d n y c h m a tem a ty cznych b ib lio tek , ale zw ró ć u w a g ę , ż e jest w n a sz y m p ro g ra m ie instrukcja w y p isy w a n ia te k s tu na ekranie. T a in stru k cja k o rzy sta z biblio tek i zaw ierającej funkcje (p ro c e d u ry ) pracy z u rz ą d z e n ia m i takim i jak ek ra n czy k law iatu ra. Z atem d o n a s z e g o p ro g ram u m u si zo stać d o łączo n a ta biblioteka.
W ża rg o n ie p ro g ra m istó w etap te n o k reślan y jest to ja k o lin k o w a n ie. N ie sły sza łem , by ktoś n a z y w a ł to inaczej. P o p ra w n e , la n so w a n e n ie g d y ś o k reślen ie „konso lid acja" i „ k o n so lid o w a n ie " , ch y b a się nie p rzyjęło. M y u ży w ać b ę d z ie m y s fo rm u ło w a n ia linkow anie w z g lę d n ie łączenie. Z au w aż: P rz y łą c z e n ie funkcji b ib lio teczn y ch n a stę p u je d o p ie ro w czasie link o w a n ia . N asza d y re k ty w a (instrukcja) # i n c l u d e z a p o z n a ła k o m p ila to r jed y n ie z s a m y m n ag łó w k iem b ib lio tek i. P o trzeb n y byl o n p o to, by k o m p ilato r m ó g ł s p ra w d z a ć p o p ra w n o ś ć n a sz e g o o d n o s z e n ia się d o biblioteki. N ato m iast s a m a treść funkcji b ib lio te cz n y c h d o łączan a jest d o p ie ro na etap ie lin k o w a n ia . O peracja lin k o w a n ia nie jest tru d n a . Z resztą najczęściej w cale n ie m u sisz m y śleć o lin k o w an iu , b o k o m p ilato r sam u ru c h a m ia ten p ro ces. W te d y jest to ty lk o jed n a k o m e n d a, a w n iektórych k o m p ilato rach - n aciśn ięcie jed n eg o ty lk o klaw isza.
Uwaga: Są kompilatory, które mogą kompilować programy napisane i w klasycznym C, i w C++. Wówczas trzeba im powiedzieć, w jakim języku jest napisany program przedstawiony właśnie do kompilacji. Jednym ze sposobów powiedzenia tego może być rozszerzenie nazwy naszego progra mu. Jeśli rozszerzeniem jest c p p , wówczas kompilator podejdzie do pracy jak do programu w języku C++. Czyli nasz program powinien się nazywać na przykład m o j_ p r o g r . c p p
12
Rozdz. 1. Startujem y! Pierwszy program
Środowisko programowania M oże być jeszcze piękniej. Otóż specjalnie dla programistów stworzono narzę dzia, które służą do tworzenia programów C++. Mamy w ięc jakby edytor, za pomocą którego piszem y program, a w dodatku mamy w nim ikony-przyciski, po naciśnięciu których odbywa się kompilacja i linkowanie naszego programu. Jeśli gdzieś w tekście programu popełniliśm y błąd składni, to kursor podświetla miejsce błędu, a w osobnym okienku pojawia się bliższe wyjaśnienie błędu. Takie narzędzie zw ane jest śro d o w iskiem program ow ania i bardzo przyspie szają pracę programisty. Środowisko miał (w odległych czasach) kompilator Borland C++ 3.1. Dziś są jego nowsze wersje (Borland Builder C++) dostosowane do pracy z systemem operacyjnym W indows. Innym przykładem jest Microsoft Visual C++. Jeśli pracujesz z system em operacyjnym Linuks, to jest tu środowisko o nazwie KDevelop. W nim spędzam najwięcej mojego czasu... Oczywiście te wzm iankowane tutaj przeze mnie nazwy produktów mogą za jakiś czas zostać zapomniane. Jednak sama idea środowiska jest tak dobra, że zaw sze będzie jakieś do dyspozycji. Warto rozejrzeć się (lub popytać), w jakim środowisku tworzenie programu pójdzie najsprawniej.
A teraz do dzieła W rezultacie trzech etapów pracy nad programem: ♦♦♦ edycji, «$♦ kompilacji. linkowania otrzymaliśmy program w wersji nadającej się do uruchomienia. Zachęcam Cię, czytelniku, byś teraz spróbował uruchomić nasz program na swoim komputerze. Mimo że program jest prymitywny. W ażne tu jest byś opanował technikę kompilacji i linkowania. Niestety nie m ogę Ci tu nic pomóc. Istnieje bardzo w iele typów komputerów i w iele różnych typów kompilatorów. Jak Twojego kompilatora używać - musisz przeczytać w swojej dokumentacji lub zapytać kolegę.
Kiedy już program zadziała poprawnie i na ekranie5 pojawi się nasz tekst
Witamy. . . w ów czas możemy spróbować zm odyfikować ten program. 5)
Uwaga: Niektóre systemy operacyjne, (np. MS Windows) natychmiast p o zakończeniu program u zamykają także okienko, w którym pracował program. W tedy trudno jest zobaczyć wydruk ekranu - gdyż system operacyjny od razu go likwiduje. Możesz się przed tym ustrzec, umieszczając na końcu programu, tuż przed klamrą kończącą funkcję m a in , następującą tajemniczą instrukcję: s y s te m ( " p a u s e " ) ;
Rozdz. 1. Startujem y! D rugi program
13
D ziałania te p o zw o lą nam bliżej zap o zn ać się z techniką w ypisyw ania na ekran. To się n am b ard zo szybko p rzyda. A zatem p ie rw szy eksperym ent. W środek tekstu ujętego w cudzysłów w p isz m y znaki \ n "Witamy \nna pokładzie"
Zm iana ta spow oduje, że tekst w y p isan y na ekranie w y g ląd ał będzie następu jąco: Witamy na pokładzie
Znak \n (n - jak: new linę, czyli: now a linia) pow oduje, ż e w trakcie w ypisyw a nia tekstu na ekranie następuje przejście do now ej linii. Dalszy ciąg tekstu w y p isyw any jest poniżej. E w entualna n astęp n a instrukcja w y p ro w ad zan ia tekstu zaczn ie go w ypisyw ać od miejsca, w którym poprzednia skończyła. Z atem dw ie instrukcje std::cout << "Witamy \nna pokładzie"; std::cout « "Lecimy na " « "wysokości 3500 stop";
spow odują pojaw ienie się na ekranie tekstu Witamy na pokladzieLecimy na wysokości 3500 stop ,i ' . • i r b a i r s r . f 6
s
*'*
•
...
••
■’
■
■
N ie ma też zn aczen ia czy napiszem y std::cout << "lecimy na "; std::cout << "wysokości 3500 stop";
czy też m oże złożym y te instrukcje i zapiszem y krócej std::cout << "lecimy na " << "wysokości 3500 stop";
W obu sytuacjach rezultat na ek ran ie będzie ten sam: lecimy na wysokości 3500 stop
Dlaczego w łaściw ie możliwe jest tak w ygodne składanie - n ie m ogę Ci jeszcze teraz wyjaśnić. M usisz być cierpliw y i doczytać do ro zd ziału o bibliotece iostream.
1.2
Drugi p ro g ram Zobaczyliśm y w łaśnie sposoby w ypisyw ania inform acji n a ekranie. N ato m iast z w czytyw aniem danych z k law iatu ry spotykam y się w naszym d ru g im program ie. O to on: Program na przeliczanie wysokości podanej w stopach na wysokosc w metrach.
14
Rozdz. 1. Startujem y! D rugi program Ćwiczymy tu operacje wczytywania z klawiatury i wypisywania na ekranie tinclude int main() int double double
stopy; // Do przechowania danej wejściowej metry; // Do wpisania wyniku przelicznik = 0.3; // przelicznik: // stopy na metry
std::cout << "Podaj wysokosc w stopach: "; std::cin >> stopy; // przyjęcie danej // z klawiatury metry = stopy * przelicznik;
}V/ •'f • \ '<
iitsj
std::cout << "\n";
■ ■'
•l
'v
// właściwe przeliczenie
iA '■
n ' ■ • •:
//to samo, co std::cout «
std::endl;
/ / -----wypisanie w y n i k ó w --------std::cout << stopy << " stop - to jest: " << metry << " metrow\n";
Komentarze Ju ż n a p ie rw sz y r z u t o k a w id ać, że w p ro g ra m ie pojaw iły się o p isy w „lu d zk im ' ję zy k u . Są to k o m e n ta rz e . K o m en tarze są tek stam i z u p e łn ie ig n o ro w an y m p rz e z k o m p ilato r. Są o n e jed n ak b a r d z o p o ży teczn e dla p ro g ra m isty , bo p rz y p o m in ają n am , co w d a n y m m iejscu p ro g ra m u chcieliśm y zrobić. W języku C ++ k o m e n ta rz e m o żn a tw o rz y ć dw ojako. P ierw szy sp o só b to o g ran ic zen ie jakiegoś tekstu z n a k a m i / * (z lewej o raz * / (z p raw ej). Sposób ten zasto so w aliśm y n a p o czątk u p ro g ra m u . K om entarz tak m o że się ciąg n ąć p rz e z w iele linijek - to także w id z im y w p rzy k ład zie. D ru g i sp o só b to z a sto so w an ie z n a k ó w / / (dw a u k o śn ik i). K o m p ilato r p o n ap o tk an iu ta k ie g o zn ak u ig n o ru je resztę zn ak ó w dc końca linijki - trak tu jąc je ja k o k o m en tarz. Jeśli n ie w iesz, jaki sp o só b k o m e n ta rz a w d a n y m m iejscu w y b ra ć - zastosuj / / czyli d w a u k o śn ik i. U w aga: Kom entarze typu / * . . . 7 nie mogą b yć w sobie zag nie żd żan e .
To zn aczy , ż e n ie p o p ra w n a jest tak a fo rm a /* KOMENTARZ "ZEWNĘTRZNY" TAK SIE CIĄGNIE /* komentarz wewnętrzny */ DOKOŃCZENIE ZEWNĘTRZNEGO */
Chciałoby się powiedzieć: niestety! Możliwość zagnieżdżeń komentarzi byłaby czasem bardzo pomocna.
Rozdział. 1. Startujem y! D rugi program
15
Wyobraźmy sobie kawałek większego programu, wyposażonego już w ko mentarze. Nagle chcielibyśmy zrezygnować chwilowo z 10 linijek. Usunąć je z procesu kompilacji, ale nie skasować zupełnie. Naturalnym odruchem jest wtedy przerobienie tego fragmentu na komentarz. Robi się to przez ujęcie tych linijek w symbole komentarza: / * oraz * / - stawiając pierwszy symbol na początku, a drugi na końcu tych 10 linijek. Jeśli jednak w rzeczonym fragmencie programu są ju ż jakieś komentarze typu / * ■ . . . * / , to kompilator (nie pozwalający na zagnieżdżanie ich) uzna to za błąd.
cout << "Uwaga pasażerowie: \n”; /* chwilowo rezygnuje z tych dwóch linijek --------cout « "Pali sie drugi silnik \n"; // opis sytuacji i cout « "Nie ma szans ratunku \n";// niech sie modlą -------------------------------------------------------- */ cout « "Proszę zapiać pasy...\n";
P raca z k o m e n tarzam i jest tak p o w sz ech n a, że ed y to ry d o p isan ia p ro g ra m ó w w C++ oferu ją często m ożliw ości u jm o w an ia w z n a k i k o m en tarza w y b ra n e g o m y szk ą fra g m e n tu tekstu. W ted y nie grozi n am k o n ieczn o ść w p isy w a n ia w każdej z np . pięćdziesięciu linijek zn ak ó w / /.T a k i e d y to r zrobi to z a nas. A g d y się ro z m y ślim y - także s p ra w n ie u su n ie k ażd y z z az n aczo n y ch k o m e n tarzy . JeśliT w ój e d y to r n ied aje Ci takiej m ożliw ości, to m o ż e sz sobie p o rad zić sto su jąc tak z w a n ą kom pilację w a ru n k o w ą . (Patrz rozdz. o p rep ro ceso rze, shr 208). N a koniec d o b ra rada n atu r)' ogólnej:
O pisuj z n a c z e n ie każdej zm ien n ej, opisuj funkcje i ich a rg u m e n ty , o p isu j te ż to, co w d a n y m fragm encie p ro g ra m u robisz. N aw et, jeśli w ted y , g d y to p iszesz, jest to jeszcze d la Ciebie jasne. N ie opisuj je d n a k rzeczy oczyw istych. Jeśli m asz in stru k cję w y p isu jącą na ek ran ie liczbę 7, to nie pisz k o m e n ta rz a / / w y p i s a n i e l i c z b y 7 ,b o z m ę c z y s z się, a p rz e d e w szy stk im szybko zniechęcisz. O pisuj rzeczy w ażne. O pisuj p rz e d e w szystkim w sz y stk ie „ k ru c z k i" , k tó re z a stosow ałeś. D la sam ego siebie i d la tych, k tó rz y m o d y fik o w ać b ę d ą T w o je p ro g ram y w te d y , g d y Ty b ęd ziesz ju ż pisał w języ k u C + + sy stem y o p erac y jn e.
Zmienne D osyć ju ż o k o m en tarzach , w ró ćm y d o naszeg o d ru g ie g o p ro g ra m u . W fu nkcji m a in z a u w a ż a m y linijki:
16
Rozdz. 1. Startujem y! D rugi program int double
stopy; metry;
Są to d e f in ic je zm iennych w y stęp u jąc y ch w p rogram ie. Z m ien n y m tym n a d a liśm y n a z w y : s t o p y o ra z metry. N a z w y te są, jak w idać, tak przez nas w y b ra n e , b y opisyw ały z a sto so w a n ie tych zm ien n y ch . M ożna jednak zap y tać ogólniej:
Co może być nazwą w języku C++ ? M oże to b y ć d o w o ln ie d łu g i ciąg liter, cyfr o raz zn ak ó w p o d k reślen ia N azw a nie m o że się z a c z ą ć o d cyfry. M ałe i w ielkie litery w n a z w a c h są rozróżniane. Odradza się też zaczynać nazwę od znaku podkreślenia, bo często kompila tor używa tego sposobu do swoich celów. Są też mądrzy ludzie, którzy odradzają używanie wielkiej litery O , bo przypomina cyfrę 0 (zero). Odradzają także używanie małej litery l, bo na ekranie przypomina ona cyfrę 1. Jest to chyba ważne, gdyby takiznakmiał wystąpić na samym końcu nazwy - bo najczęściej tam stawia się liczby.
tn eid
N ie trzy m aj się w ięc tej ostatniej rad y k u rc z o w o - p o prostu zw ró ć u w a g ę , by w y m y śla n e p rzez C iebie n azw y , były d la Ciebie w yjaś n ie n ie m , a nie z a g a d k ą .
jfj
N a z w a n ie m o ż e być id e n ty czn a z ż a d n y m ze słów k lu c zo w y c h języka C++. Te sło w a k lu c z o w e to: rr\y/F fl«of asm case const delete else extern friend int new public return static template try union void
auto catch const cast do enum false goto long operator register short static cast this typedef unsigned volatile
break bool ciass char default continue dynamie cast double export explicit for float inline if namespace rautable protected private reinterpret_ cast sizeof signed switch struct true throw typename typeid virtual using while wchart t
P oza ty m d o s ta n d a rd u C++ w p ro w a d z o n o a lte rn a ty w n e "słow ne" re p re z e n ta cje kilku o p e ra to ró w . and compl or eq
and eq not xor
bitand not^_eq xor_eq
bitor or
Z atem , jeśli w y m y śla m y sw oją n azw ę, to m u sim y u n ik a ć p o w y ż sz y c h słów . N az w y są n a m p o trze b n e po to, by z a ich pom ocą o d n o s ić się w p ro g ra m ie d o naszych zmiennych, czy stałych - o g ó ln ie m ów ić b ę d z ie m y : d o o b iektó w .
17
Rozdział. 1. Startujem y! D rugi program
5” I W języku C ++ każda nazwa m usi zostać zadeklarow ana zanim zostanie użyta. Deklaracja m ówi, jakiego typu je st to, co nazywaliśm y daną nazwą.
Tę zło tą m y śl m ożesz napisać so b ie na ścianie. Swoją d ro g ą - jeśli o tym zap o m n isz, to k o m p ilato r Ci n aty ch m ia st o tym p rz y p o m n i. W e w sp o m n ia n y m fragm encie p ro g ra m u deklarujemy, że: •
nazw a stopy o k reśla zm ienną słu żącą d o przechow yw ania liczby ty p u całk o w iteg o int .
D ek laru jem y też, że: •
nazw y m e t r y o ra z p r z e l i c z n i k są n azw am i określającym i zm ienne do p rzec h o w y w an ia liczby rzeczyw istej - inaczej: liczby zm iennoprzecinkow ej. Z m ienne te są w naszym p ro g ram ie ty p u double^ - czyli zm ien n o p rzecin k o w e podw ójnej precyzji.
Definicja, a deklaracja
I Jest su b teln a ró żn ica m ięd zy ty m i d w o m a pojęciam i. Z ałó żm y , że ch odzi o naszą n azw ę s t o p y . D ek laracja, w te d y gdy n ap o tk a ją kom pilator, m ó w i m u: I „Jak b y ś zobaczył k ie d y ś słow o s t o p y , to w ie d z , że oznacza o n o I o b ie k t typu całkow itego."
D efin icja z a ś m ó w i mu: I „A teraz zarezerw uj m i w pam ięci m iejsce na o b ie k t typu calkow iI te g o o nazw ie s to p y " N asz frag m e n t p ro g ram u zaw iera w sobie jed n o cześn ie i je d n ą i d rugą w y p o w iedź. O zn ajm iam y co to za o b ie k t i od razu re z e rw u je m y mu miejsce w pam ięci. Z atem : ■mmmmi
.mm
D afiniria je i s t równocześnie deklaracją. Ale nie odw rotnie. Definicja »
twm■U4 !
M oże być b o w ie m deklaracja, k tó ra nie jest definicją. M o żem y przecież ty lk o zdeklarow ać, ż e konkretna n azw a oznacza obiekt ja k ieg o ś typu, ale o b iek tu tego w d an y m miejscu nie d efiniujem y. C hociażby d la teg o , ż e już jest zd efin io w any w z u p e łn ie innym m od u le p ro g ram u . P rzy definicji o b iek tu p r z e l i c z n i k w idzim y taki z a p is double 6) 7)
przelicznik = 0.3;
ang. integcr [czytaj: „intidżer"] - liczba całkowita ang. double [czytaj: „dabl"l od słów: double prccision - podwójna dokładność.
18
Rozdz. 1. Startujem y! D rugi program Jest to nie tylko zw y k ła definicja rezerw ująca miejsce w pam ięci, ale taka defini cja, która to m iejsce w pam ięci d o d a tk o w o inicjalizuje, w pisując tam wstępną w artość 0.3 W c z y ty w a n ie d a n y c h z k la w ia tu ry
P rzejdźm y dalej. W naszym p ro g ram ie instrukcja std::cin »
stopy;
jest operacją zw iąz an ą z k law iatu rą, czyli - m ówiąc inaczej - ze standardowym urządzeniem wejściowym c i n (skrót od ang.: C-onsole IN -put ^). Instrukcja ta u m o żliw ia w czy tan ie z klaw iatury liczby. W artość tej liczby zostaje um ieszczo n a w zm iennej s t o p y . Przy odrobinie w yobraźni można pow iedzieć, że to w łaśnie su g eru je kierunek strzałek >> D ygresja dla p ro g ram istó w klasycznego C. Programujących w C klasycznym, ucieszył zapewne fakt tak prostego wprowadzania danych z klawiatury. Nie trzeba tu myśleć, kiedy podać do funkcji wczytującej ( s c a n f ) nazwę zmiennej, a kiedy adres zmiennej. Jeszcze kilka tak miłych niespodzianek czeka nas w C++ . N astęp n e linijki program u, to przeliczenie stóp na m etry i w y d ru k wyniku.
□
Przykładowe wykonanie programu spowoduje wydruk na ekranie poniższego tekstu (Tłustszym d ru k iem zaznaczone jest echo tekstu w p isy w an eg o przez użytkow nika p ro g ram u ). Podaj wysokosc w stopach: 3500 3500 stop - to jest: 1050 metrów
•b
Spójrz jeszcze raz na tekst programu... ... i zau w aż jak to się stało, że na ekranie pojawiła się liczba 1050 - będąca obliczoną w artością um ieszczoną w zm iennej m e tr y . W kom entarzach w yjaś nione są poszczególne kroki. N asz pierw szy program m iał tylko jedną instrukcję. Ten d ru g i - m a ich d u żo więcej. K ażda instrukcja zakończona jest średnikiem . (Ile ich zatem mamy?) P raca tego program u polega na kolejnym w y k o n y w an iu poszcze gólnych instrukcji począw szy od początku, czyli od " { " klam ry otw ierającej funkcję m a in , aż do ostatniej instrukcji, tej przed k lam rą zam y k ającą"} ". Z atem kolejność w ydarzeń jest taka: N ajpierw (kolejno) pow ołane są d o istnienia ob iek ty s t o p y , m e tr y , p rz e lic z n ik .
8)
Skrót ten wymawia się po polsku jako „Si-yn". Z twardym „S"!
Rozdz. 1. Startujem y! Ćwiczenia
19
N a stę p n ie w ypisyw anie jest n a ekranie pytanie: "Podaj w ysokosc...'. «$♦ N a stęp n ie w czytyw ana jest z klaw iatury ilość stóp. ♦$* Dalej w id zim y instrukcję dokonującą m nożenia i w stawiającą rezultat do zm ien n ej (obiektu) m e tr y . Dalej d w ie instrukcje - w y p isan ie rezultatu obliczeń. Po kolejnym w y k onaniu tych w szystkich instrukcji - p ro g ram kończy pracę.
Uporczywość s t d : : Zw róć też u w a g ę na instrukcję s td ::c o u t «
" \n " ;
//
s td ::c o u t «
s td ::e n d l;
W k o m en tarzu zaznaczyłem in n y sposób zapisu tego sam ego. Jeśli chcem y na ekran w y p ro w ad z ić sam znak '\n ', to m ożem y go zastąp ić skrótem e n d l - co oznacza: end of linę (koniec linii). Jak w idzisz, n azw a endl jest tutaj tak że p o p rzed zo n a tekstem std: : - zatem łatw o się dom yślić, że jest to nazw a czegoś z biblioteki standardow ej. Z drugiej strony pisanie tego s t d : : tak często, przy każdym co u tm o że wydać się męczące. Mam jednak dobrą wiadomość. Niedługo poznamy sposoby umówienia się z kompilatorem, że gdy zobaczy w naszym progra mie nazwę c o u t, niech przyjmie, iż chodzi nam o s t d : : c o u t.
W Tak więc w y g lą d a ł nasz p ro g ram do przeliczania stó p na m etry. N o cóż - po m y ślałeś zapew ne: -T y le pracy z w p isy w a n iem tekstu p ro g ram u , po to, by o trzy m ać ten wynik... Prościej byłoby obliczyć to „ n a piechotę". M asz rację. C u d program ow ania objaw ia się do p iero w pętlach. M ówiąc szerzej: w instrukcjach sterujących.
1.3
Ć w iczenia Które z poniższych nazw są poprawne, a które niepoprawne idlaczego? a)
predkosc4
b) Predkosc_i_czas c)
4kola
d) wielki.czwartek e) ilosc_powtorzen_cyklu_roboczego f)
zmienna
g)
L IC Z B A K O N W E R T E R Ó W
h) i) j) k) l) m) n)
okno^ekranowe samochod-osobowy auto kwota_zainwestowanych_$ wspolczynik_ (chwilowego) _przyrostu export_pszenicy export
20
Rozdz. 1. Startujem y! Ćwiczenia Jedna z nich jest, co prawda, poprawna, ale odradzana. Która? Wymień zasadnicze etapy niezbędne do stworzenia i uruchomienia programu. Czy jedna instrukcja może być zapisana w trzech liniach? Czy w jednej linii programu można umieścić kilka instrukcji? ••- prt -i Jak rozpoznaje się koniec instrukcji? -i ■ ■.■ Czy definicja jest równocześnie deklaracją - a może odwrotnie? (Jeśli nie jest to dla Ciebie jeszcze jasne, na początku rozdziału o typach wrócimy do tego zagadnienia i wtedy (mam nadzieje) ostatecznie rozwieją się wątpliwości. Co to są tzw. "białe znaki"? Wymień trzy, z którymi do tej pory się spotkaliśmy. Powiedzieliśmy, że w programie każda nazwa, przed jej użyciem, musi zostać zdeklaro wana. W naszych dotychczasowych programach wystąpiła nazwa co u t. Gdzie była jej deklaracja? Zastanów się, które ze znanych Ci edytorów mogą posłużyć do napisania programu w C++.
Do czego służy kompilator C++?
■v-ao
Do czego służy linker? Czy w komputerze, z którym masz do czynienia - istnieją jakieś kompilatory C++ wyposażone w tzw. środowisko? i1 ...... * ' '
" ł~ %
•
. .................. ..
.
..
.... .. . . .
.
............... . .
•
.
»• ■ - t i t —
• -
■ •••-•
• ............................................
.......
'
Napisz program, który z pomocą jednej instrukcji c o u t napisze na ekranie następujący tekst w i t a m y na p o
kładzie
Napisz program pozwalający przeliczyć cale na centymetry (1 cal = 2.54 centymetra). Co określa deklaraq'a danej nazwy? Jakie znasz sposoby umieszczania w tekście programu komentarzy? Czy poprawne są poniższe komentarze? Jeśli nie, to dlaczego? a) / * to jest pierwszy komentarz // drugi // trzeci // czwarty a to dokończenie
★ *★ ★ •*•*★ ****★ *** +***** j
b) // wysokosc = 660;
// próba nieudana
c) szerokość = 23; // wstępna wartość
Rozdział. 1. Startujem y! Ćwiczenia
21
gestosc = 12.55; /* wzięta z tablic */ // dalsze obliczenia ---------------*/
Jeśli wiesz już, jakim edytorem (lub środowiskiem) będziesz się posługiwał - sprawdź czy ma on udogodnienia związane ze stawianiem komentarzy typu li w wielu liniach równocześnie. (Na przykład: zaznaczasz myszką pewien rejon i wciskasz jakąś kombinację klawiszy w rezultacie zostają one zamienione na komentarz).
22
Rozdz. 2.Instrukcje sterujące P raw da - Fałsz, czyli o w arunkach
2
Instrukcje sterujące p o p rzed n im rozdziale zajm ow aliśm y się p ro g ram em do przeliczania stóp n a m etry. Jak pam iętasz, praca tego p ro g ram u polegała na tym , że w szystkie instrukcje, które um ieściliśm y funkcji ma i n , w ykonyw ane były kolej no - o d góry d o dołu. Czy tak m usi być zaw sze? Przecież czasem - w zależności od spełnienia jakiegoś w aru nku - m ożem y chcieć kilka instru kcji opuścić. W in n y m miejscu chcielibyś m y m oże kilka instrukcji w ielokrotnie powtórzyć.
W
N ie m a p ro b lem u , w tym celu w łaśnie do języku p ro g ram o w an ia w p ro w ad z o no instrukcje sterujące. Są to b ard zo przydatne polecenia służące do sterow ania przebiegiem pro g ram u . W instrukcjach sterujących m o żem y podejm ow ać d e cyzje o w y k onyw aniu tego, czy innego fragm entu p ro g ram u .
2.1
P raw da - F ałsz, czyli o w aru n kach W instrukcjach sterujących decyzje podejm ow ane są w zależności od sp ełn ien ia lub niesp ełn ien ia jakiegoś w aru n k u . Inaczej m ów iąc: od p raw dziw ości lub fałszyw ości jakiegoś w yrażenia. N ajpierw więc w yjaśnijm y sobie co to jest p raw d a, a co fałsz w języku C++
2.1.1
W yrażenie logiczne Z nasz z m atem atyki taki zapis nierów ności X
< 18
co m ożem y przeczytać jako: "obecna w artość zm iennej x jest m niejsza o d 18 Czy ta nierów ność jest p raw d ziw a czy n iepraw dziw a (fałszyw a)?
23
Rozdział. 2.Instrukcje sterujące P raw da - Fałsz, czyli o w arunkach
O czyw iście: n ie w iadom o! W za le ż y to przecież od tego, jaką w łaśnie w artość m a zm ien n a x.
*1* Jeśli x obecnie m a w artość np. 6, to w y rażen ie nasze jest praw dziw e. P rzecież zdanie: "6 jest m niejsze od 18" jest zd an iem praw dziw ym . M ów im y w tedy, że w tak im przy p ad k u w artością w yrażenia (x < 18) jest "prawda". ♦> Jeśli z a ś obecnie zm ien n a x m a wartość 200, to w artością w yrażenia (x < 18) jest fałsz. (Zdanie: ”200 jest m niejsze od 18" jest przecież zdaniem fałszyw ym ).
\ W yrażenia logiczne, których wartością jest "prawda" lub "fałsz" - często ] używane są w instrukcjach sterujących. Ich wartość je s t obliczana w chwili, trzeba podjąć decyzje.
W p rz y p a d k u naszego w y rażen ia logicznego - sp raw d zen ie polega na tym , że kom p u ter, w trakcie pracy nad instrukcją sterującą, s p ra w d z a , jaką w artość ma zm ien n a x i p orów nuje ją z liczbą 18.
2.1.2
Zm ienne logiczne b o o l jako warunek P oro zm aw iam y teraz o innym ro d zaj "warunku". Załóżm y, ż e w program ie jest w iele miejsc, gdzie s p ra w d z a się ten sam w a ru nek. Jeśli w arto ści zm iennych, w ystępujących w tym w y rażen iu , nie zm ieniają się często - czasem opłaca się to w yrażenie w aru n k o w e spraw dzić (obliczyć) raz, a re z u lta t (prawda / fałsz) zapam iętać. Z ap am iętać - czyli p rzech o w ać w jakim ś obiekcie. By p rzech o w y w ać takie rezu ltaty (prawda/fałsz), m am y d o dyspozycji specjalny typ zm ien n y ch zw any typem b o o l . Bliżej o tym po ro zm aw iam y w n astęp n y m rozdziale. (P arag raf 3.3, str. 52). •—
-------- ------- r ~i—
— r-
itiibmuu m
Zmienna (obiekt) typu logicznego bool może w ystąpić jako w yrażenie f warunkowe
Jak p rzy g o to w ać taką zm ienną logiczną? Oto prosta ilustracja: int bool // ..
niepełnoletni;
niepełnoletni = (x < 18) ;
/ / «—definicja obiektu / / <—zapamiętanie rezultatu
W idzim y tu definicje dw óch obiektów . •
Obiekt typu int o nazw ie x.
•
Obiekt typu (logicznego) bool o n a z w ie niepełnoletni.
Poniżej w id zim y w kom entarzu w ielokropek - tak z a z n aczam , ż e tu m o ż e być bardzo w iele instrukcji nadających zm iennej x jakąś se n so w n ą w artość.
24
Rozdz. 2. Instrukcje sterujące P raw da - Fałsz, czyli o w arunkach W reszcie n asza ostatnia instrukcja. Istota tego, o czym tu rozm aw iam y. Po p r a w e j stro n ie zn ak u p rzy p isan ia = w idzim y w y rażen ie (x < 1 8 ). K om puter oblicza w ięc jaką w artość m a to w yrażenie - czyli czy jest ono p raw dziw e, czy fałszyw e. K iedy to już obliczy - tę w artość "prawda" lub "fałsz" w staw ia (przypisuje) d o zm iennej logicznej o nazw ie n i e p e ł n o l e t n i . O d tej p o ry ta zm ienna logiczna m a jakąś sen so w n ą w artość (prawda/fałsz). U w aga, u w ag a: ta zm ienna logiczna ( n i e p e ł n o l e t n i ) może teraz w y stęp o w ać w m iejscu w aru n k u w instrukcjach sterujących. Z am iast kazać k o m p u tero wi obliczać jakieś w y rażen ie logiczne - każem y m u po prostu sp raw d zić czy dan a z m ien n a logiczna p rzech o w u je w a rto ść "prawda", czy "fałsz".
2.1.3
Stare dobre sposoby z dawnego C++ W starszy ch w ersjach języka C ++ nie było specjalnego ty p u przeznaczonego dla zm iennych logicznych - czyli takich, które przyjm ują wartości: prawda / fałsz. Po p ro stu d o p rzech o w y w an ia takiej informacji logicznej nadaw ał się p raw ie k ażd y ty p zm iennej. M im o ż e w stan d ard o w ej w ersji języka C++ m am y już do d y sp o zy cji specjalny ty p dla zm iennych logicznych - ten daw ny sposób n adal m oże być u ży w an y . Z asada jest genialnie prosta: Jeśli p o słu g u jem y się w tym celu zm ienną typu i n t - kom puter s p ra w d z a w artość teg o o b ie k tu . Patrzy czy jego w artość jest ró w n a zero, czy różna o d z e ra .
Co ciek aw e - nie m usi to być zaw artość tylko jednego obiektu. M oże to być n aw et bardziej skom plikow ane w yrażenie, które kom pu te r m u si obliczyć, aby p rzek o nać się jaka jest jego w artość liczbowa. Zresztą, w artość ta nie m usi być wcale liczbą. N a w e t obiekt przechow ujący znaki alfan u m ery czn e m oże być w ten sposób sp raw d zan y . S p raw d za się w ów czas kod liczbowy złożonego tam znaku. Jeśli jest różny o d z era , to w y rażen ie o d p o w iad a rezultatow i „prawda", a jeśli k o d jest zerow y (czyli tzw . znak nuli) - o d p o w iad a to rezultatow i „fałsz".
P oznaliśm y więc trzy p rzy k ład y w yrażeń, które m o g ą w ystąpić jako w aru n ek w instrukcjach sterujących. P o ra zająć się instrukcjam i sterującym i.
25
Rozdz. 2.Instrukcje sterujące Instrukcja w arunkow a i f
2.2
i
In s tru k c ja w a ru n k o w a i f Instrukcja i f 1 m o że m ieć dw ie form y: i f (warunek)
instrukcjal;
i f (warunek)
instrukcjal; instrukcja2;
else ’
Jak działa taka instrukcja sterująca? N ajp ierw s p ra w d z a n y jest w aru n ek . <♦ Jeśli jest o n spełniony {prawda), to w y k o n y w a n a jest in stru k cjal. <♦ Jeśli w a ru n e k jest n iespełniony {fałsz), to ta instrukcjal nie jest w y k o n y ... w t. an a.. ■ - . W dru g iej w ersji instrukcji i f w id zim y d o d atk o w o słow o e l s e , co m o żn a p rzetłu m aczy ć jako: „w p rzeciw n y m razie". A zatem jeśli w tej drugiej sytuacji: ,
•
y -
', '
_
-
'
'
<♦ w a ru n e k jest spełniony {prawda), to zostanie w y k o n an a in stru k cjal, w p rzec iw n y m razie (else!), czyli g d y w aru n e k nie by ł spełniony {fałsz), zo stan ie w yk o n an a instrukcja2.
❖
O to p ro sty p rzy k ła d : int i; std::cout « "Podaj jakaś liczbę: std::cin » i;
/ / d e fin ic ja o b ie k tu in t o n a z w ie i
i f ( i - 4) std::cout << " zmienna i miała wartość inna niz 4";
else std::cout «
" zmienna i miała wartość równa 4";
Z ałóżm y, ż e p o d aliśm y liczbę 15. W yrażeniem w aru n k o w y m było t u : i —4. O bliczana je st w ięc jego w arto ść 1 5 - 4 = 11 , a to jest ró ż n e od 0 (zatem : prawda), w ięc w y k o n a n a zostaje in stru k cja pierw sza.
5p.rJ
G dybyśm y p o d a li liczbę 4, w ó w czas d o zm iennej i p o d sta w io n e zo sta ło b y 4. W yrażenie i - 4 m iałoby w arto ść 0 (czyli: fałsz) i w te d y w y k o n a n a z o stałab y instrukcja d ru g a . , ■ C-P > ■ ĆH: ' ' y W f T3 i . J '■ *
Blok instrukcji Często się z d a rz a , ż e chodzi nam o w y k o n an ie w a ru n k o w e n ie jednej in stru k cji, ale całego bloku instrukcji. Stosujem y w ów czas instrukcję składaną zw a n ą in a cze 1) 2)
ang. if -jeśli [czytaj: ,,yf'] [czytaj: „els"]
26
Rozdz. 2. Instrukcje sterujące In stru k cja w arunkow a i f blokiem . Są to p o p ro stu z w y k łe instrukcje ograniczone naw iasam i ( } . Z a u w a ż , że p o klam rze n ie tizeb a staw iać śred n ik a. -if’
1
./ rfł: tełft*
4
*
f b ' M .1
instrl; instr2; instr3;
O to p rz y k ła d p ro g ra m u , w k tó ry m stosujem y instrukcje składane. iinclude
j ^★ ★ ■****************1*r'*r**'****''łr*********llr*****1*’*'****r*,llr*
int main()
1
// definicja dwóch zmiennych H typu int. Obie sq tego samego typu więc II wystarczy przecinek odzielający nazwy
int wys, punkty_karne;
std::cout << "Na jakiej wysokości lecimy ? [w metrach]: cin » wys;
/ / ---------- rozważamy sytuację----------------------i f (wys < 500) * std::cout « "\n" « punkty_karne = 1 ;
wys «
" metrów to za nisko !\n";
else std::cout << "\nNa wysokości " « wys « " metrów jestes juz bezpieczny \n"; punkty_karne = 0;
1 //
•ocena Twoich wyników
std::cout « "Masz " << punkty_karne « " punktów karnych \n"; i f (punkty_karne)std::cout << "Popraw sie !"; ■ .i
.
Jak w id zisz, za pomocą tab u lato ró w w ysunąłem n iektóre instrukcje teg o p ro g ram u nieco w praw o. P rzypom inam , że zró żn ico w an e odstępy o d lew ego m arg in esu (w ypełnione białym i z n a k a m i-s p a c ja m i lub tabulatoram i) nie mają dla kom p ilato ra żadnego znaczenia. Pomagają n ato m iast program iście. D zięki nim p ro g ram staje się bardziej czytelny.
Oto przykładowy wygląd ekranu po wykonaniu tego programu Na jakiej wysokości lecimy ? [w metrach] : 2500 Na wysokości 2500 metrów jestes juz bezpieczny Masz 0 punktów karnych
Jeśli n a zad an e pytanie odp o w iem y inaczej, to ek ra n m o że w y glądać tak: Na jakiej wysokości lecimy ? [w metrach] : 100 100 metrów to za nisko !
27
Rozdział. 2.Instrukcje sterujące Instrukcja w arunkow a i f Masz 1 p u n k tó w k arn y ch Popraw
s ie
Z au w aż jak p ro s to w ypisuje się n a ekranie w artość zm ien n ej wys. W ystarczyła instrukcja scd::cout << wys;
Instrukcje steru jące i f w ystępują w pro g ram ach b ard zo często. Często zag n ież dżają się je d n a w ew n ątrz drugiej.
Zagnieżdżanie if czyli: "Do kogo należy to else, które mam na myśli?" Jed n a in stru k cja i f m oże zaw ierać w sobie m ną instrukcje i f . W ygląda to tak:
Rys. 2-1
if( w a r u n e k
p ie r w s z y )
' V * ■
//
< - elsedla w arunku pierwszego ■ .......... ... .
; .. ■
C o się z a g n ie ż d ż a w czym - p o k azu ją zaciem ow ane prostokąty. D od atk o w o podkreślają to "wcięcia" tabulatorów . Pam iętam y je d n a k , że naw iasy k lam ro w e nie zaw sze m u szą w ystąpić. W ted y m oże p ow stać p roblem , jak rozum ieć następującą konstrukcję: if (w a r u n e k A ) Lf (w a r u n e k _ B ) in stru k c ja 1 ; else
//...
instrukcje dla else
Jak sądzisz - czy instru keje dla e 1 s e zo stan ą w y k o n an e w ted y , g d y w aru n ek B jest nie sp ełn io n y , czy m oże w ted y , g d y w aru n ek A jest n ie spełniony? Inaczej mówiąc: do którego i f należy to e l s e ? Z asada jest taka, że: t
m u-
Jeśli nawiasy klamrowe nie stanowią inaczej, to e l s e odnosi się zawsze do najbliższego poprzedzającego go i f .
Innym i słow y, g d y b y ładniej d o d a ć tam tabulatory, w y g ląd ało b y to tak: if (w a r u n e k A ) if (u> arunek_B ) in stru k c ja !; else
28
Rozdz. 2. Instrukcje sterujące In stru k cja w arunkow a i f {
.
/ / . . . instrukcje dla else
)
i\ « - - j A
n
i r u
a
s
-m
o liw n ą i c . w .v--jn.\
Przypominam, że tabulatory o niczym nie decydują. Są to białe znaki, które kompilator ignoruje. Dla programisty jednak są bardzo pomocne, bo przy ich użyciu, program staje się czytelniejszy. O sp o so b ie z a g n ie ż d ż a n ia d ec y d u ją n a w ia sy klam ro w e, lub ta p o w y ższa zasada.
Wybór wielowariantowy P rz y u ż y c iu sło w a e l s e m ieliśm y w ięc m o żliw o ść d w u w a ria n to w e g o w yboru: - ro b im y to, w p rz e c iw n y m ra z ie ro b im y tamto. M o ż e m y je d n a k pójść d alej - koło słow a e l s e m o żem y p o staw ić n astęp n ą in stru k c ję i f . D zięki te m u z y s k a m y m o żliw o ść w y b o ru w ielo w arian to w eg o : i f ( w a ru n e kD instrukcjal ; e l s e i f ( w arunek2 instrukcja2; e l s e i f ( w arunek3 instrukqa3; e l s e i f ( w arunek4 ) instrukcja4; )
)
/ / dalsza część programu T u n ie u ż y łe m ta b u la to ró w , a le ju ż w iesz, że k a ż d e z w y stęp u jący ch tu else o d n o s i się d o b e z p o śre d n io p o p rzed z ając eg o g o if. Z w y k le nie sto su ję w takim p r z y p a d k u ta b u lato ró w , b o b y łoby ich b a rd z o d u ż o i f {w a ru n e k D instrukcjal; e l s e i f ( w arunek2) instrukcja2; e l s e i f ( w a ru re k 3 ) instrukcja3; e l s e i f (w a ru n e k 4 ) instrukcja4; -y
<
3Vi>T .-i- o : J> :; ,re>?i
Ot bJJaliiirttlbcCj f i l YĄ
MftU &riSMa-l
xi -uU--;
non*
/ / dalsza część programu T a k ą k o n stru k cję m o ż n a p rzec zy tać tak: S p ra w d ź w a r u n e k l. <♦ Jeśli jest sp ełn io n y - w y konaj in stru k c ję l i p rzejd ź do d a lsz e j części p ro g ra m u (czyli z a k o ń c z pracę w tej całej konstrukcji). <$► Jeśli zaś w a ru n e k l nie jest sp ełn io n y , to s p r a w d ź w aru n ek z. <♦ Jeśli ten w a ru n e k 2 je st spełniony, to w y k o n a j in stru k q ę 2 i p rz e jd ź d o dalszej części p ro g ra m u .
*1* Jeśli zaś w aru n e k 2 n ie jest sp ełn io n y to s p r a w d ź w aru n ek 3 . Jeśli o n jest sp ełn io n y to w y k o n aj instrukcję2 i p rzejd ź d o d alszej części p r o g r a m u . Jak w id a ć , po w staje tu m o żliw o ść w y b o ru je d n e g o z w ielu w aria n tó w . O d ra z u się m o ż e n a su w a ć p o m y sł w y k o rzy stan ia takiej k o n stru k c ji w p ro g ra m o w a n iu m e n u , z k tó reg o u ż y tk o w n ik p ro g ram u m ógłby w y b ra ć jak ąś opcję. J e d n a k zaczek ajm y jeszcze, b o w krótce p o z n a m y in n y sp o só b d o k o n a n ia w y b o ru w ielo w a ria n to w e g o (za pom ocą jest instrukcji s w i t c h ) .
29
Rozdz. 2.Instrukcje sterujące Pętla w h i l e
2.3
P ętla while Instrukcja steru jąca program ow ej.
*i 2fi Ł*:
while^
p o zw ala
na
realizację
ta k
zwanej
pętli
T aka pętla program ow a p o leg a na tym , że p e w n a instrukcja (lub blok instrukcji) w y k o n y w an e są 'w kółko", d o p ó k i spełniony jest jakiś w aru n ek .
Instrukcja steru jąca while ma formę: while (warunek) instrukcjal;
—
S?
Oczywiście zamiast tego, co oznaczyliśmy tu umownie jako in s tru k c ja l, może być instrukcja składana. Inaczej: może tam być blok instrukcji, czyli kilka instrukcji ujętych w klamry ( }. P rzystępując d o w y k o n an ia instrukcji w h i l e - k o m p u ter najp ierw sp raw d za w arunek. ♦> Jeśli nie je st spełniony, w ów czas instrukcjal nie jest w cale w ykonyw ana. -ji :u ♦J» Jeśli je d n a k w aru n ek jest sp ełn io n y , w ów czas w y k o n y w a n a jest instrukCja1, po czy m ponow nie s p ra w d z a n y jest w aru n ek .
•
•
r Jeśli tym razem w a ru n e k ten n a d a l jest sp ełn io n y , w ów czas p o n o w n ie w y k o n y w an a jest instrukcjal - i ta k dalej, w ielokro tn ie dopóki (whilel) w a ru n e k jest spełniony.
Jeśli w końcu, za k tó ry m ś obiegiem pętli, w a ru n e k stanie się niespełniony, w ów czas d o p iero p raca pętli zo stan ie p rz e rw a na.
Z w racam u w ag ę, ż e pierw sze s p ra w d z e n ie w aru n k u (obliczenie jego w artości) odbyw a się p rz e d w ykonaniem instrukcjil #include int m a i n ()
{ int ile; std::cout << "Ile gwiazdek ma mieć kapitan ? : std::cin » ile; std::cout « «
"\n No to narysujmy wszystkie " ile « " :
// pętla while rysująca gwiazdki
w h i l e (ile) { 3)
ang. while - podczas gdy, dopóki [czytaj: „łajl"]
30
Rozdz. 2. Instrukcje sterujące Pętla d o . . . w h i l e . . . s t d ::cout « ile = ile - 1; //n a d o w ó d , że tn m ł p r a w o p r z e r w a ć pętlę std::cout << "\n Teraz zmienna ile ma wartość
n
«
ile;
A oto przykładowy wygląd ekranu po wykonaniu tego programu Ile gwiazdek ma mieć kapitan ? : 4 No to narysujmy wszystkie 4 : **** Teraz zmienna ile ma wartość 0
2.4
Pętla d o . . .while. . . Słow a te oznaczają po an g ielsk u : Rób... D opóki.. realizację innego rodzaju p ętli program ow ej.
A
Instrukcja ta po zw ala na
Pętla taka m a form ę do instrukcjal while { w a r u n e k ) ;
C zyli jakby p o polsku rób instrukcjal dopóki lPiuCrc n A
ńjSEWoW
( w a r u n e k );
Q jS M 7 ?('V j
'
D ziałanie jej jest takie: N ajp ierw w y k o n y w an a jest in s tru k c ja l . N astęp n ie sp raw d zan y jest w aru n ek . Jeśli jest o n spełniony, to w y k o n an ie instrukcji 1 zo stan ie pow tórzone, p o czym z n o w u sp ra w d z a n y jest w aru n ek ... i tak w k ó łk o , dopóki w aru n ek będzie spełniony. Jak w id ać, d ziałan ie tej pętli p rzy p o m in a tę o p isan ą poprzednio. R óżnica polega tylk o na tym , że w aru n ek sp raw d zan y jest nie p rzed , ale po w y k o n an iu instrukcji-!. W ynika stąd , że instrukcjal zo stan ie w y k o n an a co najm niej raz. C zyli n aw et w tedy, g d y w a ru n e k nie będzie n ig d y spełniony. N a p rzy k ład : #include int main()
{ char litera; do { std::cout << "Napisz jakaś litere: std::cin » litera; std::cout << "\n Napisałeś: " << litera << " \n"; }w h i l e (litera != ’K');
4)
[czytamy: „du...łajl"J
31
Rozdział. 2.Instrukcje sterujące Pętla f o r std::cou~ << "\n Skoro Napisałeś K to kończymy !";
}
A oto p rz y k ła d o w y w ygląd ekranu po w ykonaniu tego programu Napisz jakaś litere: A Napisałeś: A Napisz jakaś litere: K Napisałeś: K Skoro Napisałeś K to kończymy .
P rogram nasz oczekuje na napisanie (przez użytk o w n ik a) litery. Pętla w czyty w a n ia liter o d b y w a się dopóki nie p o d am y litery K (wielkiej). W ted y to w ykony w a n ie pętli zakończy się. Z w racam u w a g ę - pętla w czytująca znaki zostanie w y k o n an a p rzynajm niej raz. W p ro g ram ie - w m iejscu, które o zn aczy łem jako O - pojaw ił się w w yrażeniu niezn an y n am jeszcze d o tą d op erato r != k tó ry oznacza: „ ró ż n y od . Zatem zap is while(litera != 'K')
ro zu m ian y jest jako „d o p ó k i l i t e r a jest ró żn a o d K ". Tem u i in n y m op erato ro m przyjrzyjm y się d o k ład n ie później.
'
W
Jak w idać, m am y tu d o czynienia z pętlą o nieco in n y m d ziałan iu niż p o p rzed nia. Z naw cy tw ierd zą, że w tej p ętli - częściej robi się błędy. Z atem jeśli nie w iesz, k tó rą z nich się posłużyć, to w y b ierz raczej p ętlę w h i l e .
.5
P ę tla for N ajp raw d o p o d o b n iej tą pętlą będziesz p o słu g iw ał się najczęściej. M a ona form ę for iiiis tr _ m i ; ■warunek ; i n s t r j c r o k ) treść_pętli;
co w przy k ład zie m oże w y g ląd ać choćby tak: for(i=0 ; i < 10 ; i=i+l)
( std::cout «
"Ku-ku !
) W yjaśnijm y, co oznaczają poszczególne człony: <♦ f o r (ang. for - d la ) oznacza: d l a tak ich w a ru n k ó w rób... ❖
in strjn i - jest to instrukcja inicjalizująca p racę p ętli, w y k o n y w an a jednokrotnie, z a n im zostanie w y k o n an a w łaściw a p raca pętli. •
W n aszy m p rzy k ład zie jest to p o d sta w ie n ie i = 0.
32
Rozdz. 2.Instrukcje sterujące Pętla f o r ♦♦♦ warunek - j e s t to w yrażenie, k tóre o bliczane jest p rzed k a ż d y m obiegiem pętli. Jeśli je st p raw d ziw e, to w y k o n y w a n e zostają instrukcje będące treścią pętli. •
U n as w yrażeniem w a ru n k o w y m jest w yrażenie: i < 10 . Jeśli rzeczyw iście ow o i jest m niejsze od 10, w ó w czas w yko n y w a n a zostaje instrukcja b ęd ąca treścią pętli (czyli w ypisanie te k stu "Ku-kul").
♦♦♦ instrjcrok - to instrukcja kroku pętli, w y k o n y w an a n a zakończenie każdego o b ieg u (kroku) pętli. Jest to jak b y ostatnia instrukcja, w ykony w an a b ezp o śred n io przed obliczeniem w y rażen ia warunek. •
U n a s jest to po prostu i = i+1
Co oznacza: do dotychczasowej wartości zmiennej i dodaj 1, a następnie wstaw ten rezultat z powrotem do obiektu i. Innymi słowy - chodzi o to, by obiekt i przy każdym obiegu pętli zwiększał swoją wartość o 1.
Praca tej pętli odbywa się więc jakby według takiego harmonogramu: 1) N a jp ie rw w ykonują się instrukcje inicjalizujące p racę pętli. 2) O bliczan e jest w y rażen ie w arunkow e. •
Jeśli w aru n ek nie jest sp ełn io n y - praca pętli jest p rz e ry w a n a .
•
Jeśli z a ś w aru n ek jest sp ełn io n y , w ów czas w y k o n y w a n e zos tają instrukcje będące treścią pętli.
3) P o w y k o n an iu treści p ę tli w ykonana zostaje instrukcja kroku p ętli ( instjcrok) - p o czy m p o w ta rz a n a jest akcja (2). fc' U H .
O to kilka ciekaw ostek:
in s tr jn i - nie m u si b y ć tylko jedną instrukcją. M oże być ich k ilk a, w ted y o d d z ie lo n e są p rzecin k am i. Podobnie w p rz y p a d k u instrukcji k ro k u pętli (instrjcrok). W yszczeg ó ln io n e elem enty: in strjn i, wyraz_warun, instrjcrok - n i e m u s z ą w ys tąp ić. D o w o ln y z nich m o ż n a opuścić, zac h o w u jąc jed n ak śred n ik o d d zielający g o o d sąsiad a. O p u sz c z e n ie w y ra ż e n ia w aru n k o w eg o tra k to w a n e jest tak, ja k b y stało tam w y ra ż e n ie zaw sze p ra w d z iw e (w arunek sp ełn io n y ). T a k w ięc zap is f°r( ; ; ) l
// tre ść p ę tli
je s t n ie sk o ń c z o n ą p ę tlą . In n y typ nieskończonej p ętli, to oczyw iście:
33
Rozdział. 2.Instrukcje sterujące Instrukcja f o r while(true) { // treść p ę tli
} • lilii
alb o do // treść p ę tli } while(true);
ii r. ;
p
Przykład
- WTUT* C-M
j f W
>j. •
P rzy jrzy jm y się pętli for w program ie #include int main()
{ std::cout << "Stewardzie, ilu leci pasażerów ? inr ile; std::cin >> ile;
//liczba p a s a ż e r ó w
int i;
//lic z n ik o b ie g ó w p ę tli
f o r (i = 1
; i <= ile ; i = i + 1)
//
{ std::cout
. . .
<< "Pasażer nr " « i « ” proszę zapiać pasy ! \n";
. ■ ,-< ih >' ■■■‘
.
s td:rcout «
.
■
"Skoro wszyscy juz zapieli, to ładujemy.
}
3
Jeśli w trakcie w y k o n y w a n ie program u s te w a rd odpow ie, że leci 4 p asażeró w to
na ekranie pojawi się: Stewardzie, ilu leci pasażerów ?4 Pasażer nr 1 proszę zapiać pasy ! Pasażer nr 2 proszę zapiać pasy ! Pasażer nr 3 proszę zapiać pasy ! Pasażer nr 4 proszę zapiać pasy ! Skoro wszyscy juz zapieli, to ładujemy.
Krótki komentarz O D efinicja obiektu o n a z w ie i . To będzie jak b y licznik o b iegów pętli. © Jak w id zisz, w w y rażen iu inicjalizacyjnym n ad ajem y w artość 1, p o czy m przy k ażd y m ro śn ie. Licznik i jest w ięc zm ienną, której pętli. S koro tak, to lepiej p o słu ży ć się następującą
zm iennej (obiektow i) o n azw ie i o b ieg u pętli w arto ść teg o licznika p o trze b u jem y tylko na u ży te k tej sk ła d n ią
f o r (int i = 1 ; i <= ile ; i = i + 1)
{ std::cout
<< "Pasażer nr " << i
34
Rozdz. 2. Instrukcje sterujące In stru k cja s w i t c h << " proszę zapiać pasy ! \n";
Łatw o zauw ażyć, że teraz definicja obiektu i pojawiła się już w instrukcji f o r , w członie, który p rzed tem nazw aliśm y instrukcją inicjalizującą (instr_ini).
Jakie konsekwencje ma definicja w takim miejscu? Otóż:
[oObiekt
zdefiniowany w instrukcji inicjalizacyjnej pętli f o r istnieje (jest do
dyspozycji) tylko w ramach tej instrukcji f o r . dy
To znaczy w tej linii, w której go napisaliśmy, a także w instrukcjach stanowiących treść pętli. Czyli w ramach instrukcji zawartych w naszych klamrach { }. Potem , już po zak o ń czen iu pracy pętli - tak zdefiniow any o b ie k t - staje się niedostępny (przestaje istnieć). Był p rzecież stw orzony do je d n eg o tylko p rzed sięw zięcia (zw anego pętlą f o r ) , w ięc p o jego zakończeniu k o m p u ter go zlikw iduje.
2.6
In s tru k c ja switch s w i t c h **- jak sam a n azw a s u g e r u je - s łu ż y d o po dejm ow ania w ielo w arian to w ych decyzji. P rzyjrzyjm y się p rzy k ład o w em u fragm entow i p ro g ra m u . std::cout
int który; std::cin »
« "Kapitanie, który podzespół sprawdzić? \n" « "nr 10 - Silnik \nnr 35 - Stery \nnr 28 - radar\n << "Podaj kapitanie numer: ";
który;
switch (który)
< case 10: std::cout << "sprawdzamy silnik \n"; break;
case 28: std::cout << "sprawdzamy radar \n"; break;
case 35: std::cout << "sprawdzamy stery \n"; break;
default:
5)
ang. switch - przełącznik [czytaj: „słicz"]
35
Rozdział. 2.Instrukcje sterujące Instrukcja s w i t c h std::cout « "Zazadales nr " « który << " - nie znam takiego ! "; break;
)
//...d a l s z a część p ro g ra m u
W p r z y p a d k u , g d y k a p ita n (czyli Ty!) o d p o w ie, ż e n u m er 35, to
na ekranie będzie następujący tekst:
3
Kapitanie, który podzespół sprawdzić ? nr 10 - Silnik nr 35 - Stery nr 23 - radar Podaj kapitanie numer: 35 sprawdzamy stery
. • Jeśli je d n a k z aż ąd a p o d z e so p łu n r 77, to na ek ra n ie zobaczy:
Kapitanie, który podzespół sprawdzić ? nr 10 - Silnik nr 35 - Stery nr 28 - radar Podaj kapitanie numer: 77 Zazadales nr 77 - nie znam takiego !
O to , ja k taka instrukcja s w i t c h działa: ❖ O b liczan e jest w y ra ż e n ie um ieszczone w n aw iasie p rzy słow ie s w i t c h switch (w y r a ż e n ie w a r u n k o w e )
{ case w rażenie_stałe1:
instrA; break; ‘
,
.
'
.
■
.
*
.
i
• -
case w rażenie_stałe2:
instrB; break; default:
instrC ; break;
} //...d a lsza c zę ść p ro g ra m u
❖ Jeśli jego w artość z g a d z a się z w artością w y ra ż e n ia stałego p o d a n ą obok je d n e g o ze słów k lu czo w y ch case**, w ó w czas w y k o n y w an e są instrukcje p o c z ą w sz y od tego m iejsca. ❖ W y k o n y w an ie ich k o ń czy się p o n ap otkaniu instrukcji b r e a k^. P o w o d u je to w y sk o k z instrukcji s w i t c h - czyli jakby w yjście p o za jej d o ln ą klam rę. N a z a p is 6) 7)
ang. case - przypadek, sytuacja [czytaj: „kejs"l ang. break - przerwij [czytaj: „brejk" ]
36
Rozdz. 2. Instrukcje sterujące In stru k cja s w i t c h c a s e wartość:
mówi się czasem "etykieta case". Nie pom yl tego z prawdziwą "etykietą", o które porozmawiamy za kilka stron. (Paragraf 2.9, str. 41). N ie może być dw óch etykiet c a s e z taką samą wartością. Jeśli tak przez pomyłkę zrobisz, kompilator Ci o tym przypomni sygnalizując błąd kompilacji. Zwracam uwagę, że chodzi o wartość wyrażenia, a nie sam jego wygląd Etykiety c a s e 20: oraz c a s e 19+1:
wyglądają inaczej, ale są etykietami z identyczną wartością. Nie mogą więc wystąpić w tej samej instrukji s w i t c h . bn uf -Jjfssas Jti/ibaf flksj
default Gdyby wartość wyrażenia warunkowego s w it c h nie zgadzała się z żadną z wartości podanych przy etykietach c a s e , wówczas wykonują się instrukcje umieszczone po etykiecie d e f a u l t 8. U nas etykieta ta znajduje się na kon a instrukcji ś w it ch, jednak może być w dow olnym miejscu, naw et na samym je początku. Co więcej, etykiety d e f a u l t m oże nie być wcale. Jeśli wartość wyrażenia warunkowego nie zgadza się z żadną z wartości prz> etykietach c a s e , a etykiety d e f a u l t nie ma wcale, w ów czas wykonywanie programu opuszcza instrukcję s w itc h nie wykonując niczego. (Zaczynają sią wykonywać instrukqe następujące bezpośrednio po tym s w itc h ).
break - nie jest obowiązkowe
A 3,‘,!
Instrukcji następujących po etykiecie c a s e nie musi kończyć instrukcja b rea k Jeśli jej nie um ieścimy, to zaczną się w ykonyw ać instrukcje um ieszczone pod następną etykietą c a s e . Konkretniej: w naszym ostatnim programie brak inst rukcji b r e a k w c a s e 10 spowodowałby, że po wykonywaniu instrukcji dla c a s e 1 0 nastąpiłoby wykonywanie instrukcji z c a s e 28. Nie jest to nieudolność języka C++. Czasem się to przydaje. Lepiej przecież, gdy programista sam może zadecydować czy przerwać (b r e a k ) wykony wanie danych instrukcji, czy też kontynuować. Czasem więc celowo nie umieszczamy instrukcji b r e a k . Oto przykład: ć i . O i h . / / . - - - * i i j y w K f* f j
s w i tc h ( n r ) c a s e 3: s t d : :c o u t « c a s e 2: s t d : : c o u t « c a s e 1: s t d : : c o u t « b reak ;
!'* " ;
ficess.
nr. 8)
ang. default - domniemanie [czytaj: „difolt"]
.v.
37
Rozdz. 2.Instrukcje sterujące Co w ybrać s w i t c h czy i f . . . e l s e ?
Zależnie od wartości zmiennej wydruki na ekran: d la n r = d la n r = d la n r = d la in n eg o
.7
nr
możliwe
sć|
następujące
3 *- ! 2 - ! 1 • n nic się n ie w y d ru k u je
C o w y b ra ć : switch c z y i f .. .else? P o zn aliśm y ju ż d w ie m ożliw ości w yboru w ielo w arian to w eg o . Jedna z nich to w ielo k ro tn a instrukcja i f . . . e l s e , a d ru g a to u ż y c ie instrukcji s w i t c h . Obie mogą nam posłużyć na przykład do realizacji "menu ", czyli tej części programu, gdzie użytkownik wybiera jedną z możliwości (opcji). Jaka jest różnica m ięd zy tym i d w o m a sposobam i d o k o n an ia w y b o ru w ielo w ar ianto w eg o ? Co w ybrać? Z an im o d p o w iem , najp ierw u stalm y n azew nictw o. W obu p rz y p a d k a c h m am y jakiś o b ie k t (zm ienną), k tó reg o w a r tość m u sim y p o ró w n ać z czym ś - a w zależności od re zu ltatu tego p o ró w n an ia - w y k o n an ie p ro g ra m u p rzen o si się d o takich lub innych instrukcji. Dla p rostoty d alszy ch ro zw ażań ten z m ien n y obiekt nazw ijm y sobie obiektem w ybierającym . W róćm y teraz d o różnic m ięd zy i f . . . e l s e , a s w i t c h . P rzy p o m n ijm y je sobie z a p o m o cą p seu d o -k o d u .
Oto pseudo-kod ilustrujący sytuację, gdy postanowiliśmy posłużyć się instrukcją switch: i n t w y b ie r a ją c y ; // te ra z n a stę p u ją in stru k c je , w k tó r y c h u ż y tk o w n ik w y b ie r a ja k ą ś opcję, p r z e z co / / n a d a je o b ie k to w i w y b ie ra ją c e m u ja k ą ś ta m w a rto ść / / in s tr u k c ja sw itc h , k tó ra p r z e n ie s ie ste ro w a n ie w o d p o w ie d n ie m iejsce
sw itch { w y b i e r a j a c y ) case 100:
// <— to j e s t s ta ła w a rto ść
instrukcje dla tej opcji
case 200: instrukcje dla te j opcji
case 200+50:
// <—to j e s t s ta ła w a rto ść
i' s ' r)^ . // <—to j e s t s ta ła w a rto ść
instrukcje dla tej opcji
Jak p am iętam y, w instrukcji s w i t c h n astęp u je sp ra w d z e n ie czy w a rto ś ć obiektu w ybierającego jest ró w n a jednej ze s ta ły c h w artości u m ie sz c z o n y c h p rzy etykietach c a s e .
38
Rozdz. 2. Instrukcje sterujące Co w ybrać s w i t c h czy i f . . . e l s e ?
Teraz chciałbym pokazać pseudo-kod ilustrujący pracę instrukc if .e ls e tn im to 8# b futbyw ...ale mam tu m ały problem, bo nie poznaliśmy jeszcze operatora sprawdza jącego czy dw ie wartości są sobie równe. Operator ten zapisuje się jako dw znaki równości obok siebie (bez odstępu między nimi).
Zatem zapis:
.
.»r*« . . •• -..nwu •
>
i f ( a = - b) ,
.
. •
--.V »
» r 1*
-•••
'
'
-
•**-■.*
••• -
—
•
•
•
oznacza: Jeśli a równe jest b, to... Wiedząc to, m ożem y teraz pokazać nasz pseudo-kod:
-
.
i f (w y b ie r a ją c y « wyrażenieA) instrukcje dla tej o p cji •‘ ■ “V■‘W"i •i e l s e i f (w y b ie r a j ą c y — wyrażenieB) ;i instrukcje dla tej opcji
e l s e i f ( w y b ie r a ją c y =“ wyrażenieC) instrukcje dla tej opcp
Tifildyw (O
Zasadnicze różnice między tymi sposobami? r,v/ ..
-Sąirzy: 'uk) :.!■ UT$9iti3
a t»f
iir-t-m ■'
••
|
;. ■- ' >
1) Co do typi* obiektu wybierającego f!iaL'
s typu całkow itego
w i t ?ć h póWinien to być obiekt (lub wyrażeni
albo takiego, który kompilator potrafi sobie na taki typ całkowity zamienić ♦> W przypadku instrukcji i f . . . e l s e - typ obiektu wybierającego nie nr znaczenia. Chodzi tylko o^tó^by był to typ, k t ó r y P ° ^ c porównaniu i w rezultacie otrzymać odpow iedź czy waruijek j spełniony czy nie (prawda czy fałsz). (ESI*
Przykład. Obiekt wybierający reprezentuje kąt (w stopniach) pod jakim s w chodzi w atmosferę. Kątten może byc 16.5 stopnia lubl7.1 lu b l7 .4 Jeśli tak, ti wyboru wariantu musimy dokonać za pomocą instrukcji sterując** i f . . . e l s e , bo s w i t c h działa na tylko na liczbach całkowitych.
2) Co do tego, z czym porównuje się (obiekt wybierający)... «$♦ W instrukqi s w i t c h obiekt wybierający porównywany jest wartością mi stałym i (całkowitymi), znanym i nam już w trakcie pisania progra mu. u •• (To te wartości, które umieściliśmy zaraz po słowach case) ♦♦♦ Natomiast w instrukcji i f . . . e l s e obiekt wybierający porównać moż • na nawet z wyrażeniem zmiennym, to znaczy takim, którego bieżąc r wartość znana będzie dopiero w ted y, g d y w ykonanie programu dotrz do tej w łaśnie instrukcji.
39
Rozdział. 2.Instrukcje sterujące Co w ybrać s w i t c h czy i f . . . e l s e ? (£§=>
N a p rz y k ła d : O b ie k t w y b ierający re p re z e n tu je n u m e r opcji, k tó rą w ybrał u ży tk o w n ik . T eraz n a s z w y b ó r w ie lo w a ria n to w y m a p rzen ieść sterovyanie d o tych lu b innych in stru k c ji. D ecy zję o w y k o n a n iu d a n y c h działań, jeśli w y b ra n a została n a p rz y k ła d opcja ó sm a - d a się zrealizo w ać w obu ro zw iąz an iac h ( s w i t c h o ra z i f . . . e l s e ) . Ja wtedy wolę posłużyć się instrukcją s w i t c h - b o jest bardziej przejrzy sta. i.
03=
r,
Vl
i' •
r1 -
'r .
U:l„
- f * . L
<:'■ i
Z a łó ż m y je d n ak , ż e o b iek t w ybierający re p re z e n tu je ilość p rzejech an y ch kilo m e tró w p rz e z sam o ch ó d w yścigow y. T rzeba zd e c y d o w a ć o w y k o n a n iu tych lu b in n y c h d ziałań przegląd techniczny: "pobieżny", "normalny" czy "generalny" ? w z a leżn o ści od teg o , c z y ta ilość k ilo m etró w o d p o w iad a jakiejś w artości w y m a g a n e j. T u u w ag a: o w a w artość w y m a g a n a — jest zm ien n a, b o ró żn a na ró ż n y m p o zio m ie g ry . S koro p o ró w n y w a ć m a m y z o biektem (w y rażen ie m ) zm ien n y m - trz e b a w y b o ru w ielo w a ria n to w e g o d o k o n a ć za pom ocą 1 f • • • 6 XóQp
3) Co do samej operacji porównującej... <♦
s w i t c h - jej p o ró w n a n ie to s p ra w d z e n ie czy obiekty w y b ierający je s t ró w n y x
❖
i f . . . e l s e - t u p o ró w n a n ie m oże być d o w o ln y m o p e ra to re m logicz n y m dającym o d p o w ie d ź : prawda /fałsz.
C zyli jeśli chcem y w y b rać w a ria n t zależnie nie ty le o d k onkretnej w arto ści, ale o d p rz e d z ia łu w artości, to sto su jem y in stru k cję steru jącą i f . . . e l s e . f • 1•*' ' i* • ' i ’'I J ' ; ,| J2, ’ * • ’ ***''!' ’• ?*ł| “ N a p rz y k ła d : O b iek t w ybierający re p re z e n tu je tem p eratu rę siln ik a. •
Jeśli ona je st w p rzed ziale (-1 0 0 , 55) sto p n i C elsju sza to podjęte m ają zostać o d p o w ie d n ie kroki.
•
Jeśli zaś te m p e ra tu ra jest w p rz e d z ia le (55,120) trz e b a w y k o nać in n e instrukcje.
•
Jeśli zaś te m p e ra tu ra jest w z a k re sie > 120 sto p n i to jeszcze inne.
G d y m a m y dokonać tak ieg o w y b o ru , to p o słu g u je m y się in stru k c ją steru jącą
if...else
Jak w id a ć instrukcja i f . . . e l s e jest bardziej u n iw e rs a ln a .P o c o z a te m w o g ó le używ ać s w itc h ? A jedn ak :
40
Rozdz. 2.Instrukcje sterujące In stru k cja b r e a k Instrukcja switch jest bardziej przejrzysta, bardziej elegancka. Patrząc na jej zapis od razu rozumie się logikę tej części programu. W wielu przypadkach ta instrukcja po prostu nam wystarcza. Dlatego zapew ne używ ał będziesz jej częściej. Poza tym - d z ia ła ona nieco szybciej.
Zapamiętaj: Do dokonyw ania w yboru w ielow ariantow ego lepiej użyć instrukcji sw itch^j chyba, że się nie da, bo zachodzi przynajm niej jedna z poniższych okoliczności: •
obiekt porów nyw any nie jest typu całkow itego, (ani takiego, który na ten typ całkowity ko m p ilato r potrafi
•
zam ienić), operacja porów nania jest inna niż "czy a jest równe b? ,
•
to, z czym porów nujem y, nie jest w artością stałą znaną już w czasie kompilacji
2.8
In stru kcja break Zapoznaliśm y się pow yżej działaniem instrukcji break - polegającym na przerw aniu w y k o n y w an ia instrukcji switch. Jest jeszcze inne, choć podobne działanie b r e a k w stosunku do instrukcji pętli: for, while, do. . .while. Instrukcja ta pow oduje natychm iastow e p rzerw an ie w ykonyw a nia tych pętli. Jeśli m am y do czynienia z kilkoma pętlam i - zagnieżdżonym i je d n a w ew nątrz drugiej, to instrukcja break pow oduje przerw anie tylko tej p ętli, w której bezpośrednio tkw i. Jest to więc jakby przerw an ie z wyjściem ty lk o o jeden poziom wyżej. Oto, jak instrukcja break przerw ie pętlę while: int i = 7; while(1) std:rcout « i = i -1; if(i < 5)
II <- pętla n ie sk o ń c zo n a !
"Pętla, i = " «
s td :rc o u t « b re a k ;
□
i «
"\n";
"Przerywamy ! " ;
Wykonanie tego fragmentu programu spowoduje wypisanie na ekranie Pętla, i = 7 Pętla, i = 6
41
Rozdział. 2.Instrukcje sterujące Instrukcja g o t o Pętla, i = 5 Przerywamy !
A oto przykład z zagnieżdżonymi pętlami. int i, m; int dluęosc_linii = 3; for(i=0 ; i < 4 ; i = i + l )
{ for(m = 0 ; m < 10 ; m = m + 1) s t d : :cout <<
;
i f i m > d l u g c s c _ l i n i i ibreak; scdrrcout
3
// tu wyskok z for (m...)
<< "NnKontynuujemy zewnętrzna petle" « " for dla i = " « i << »\n ";
Wykonanie tego fragmentu objawi się na ekranie jako: ***** Kontynuujemy zewnętrzna petle for dla i = 0
★ * ** K
Kontynuujemy zewnętrzna petle for dla i = 1 ***** Kontynuujemy zewnętrzna petle fcr dla i = 2 *
*
*
TT *
Kontynuujemy zewnętrzna petle for dla i - 3
To in stru k cja break sp raw iła, że nie było 10 g w ia z d e k w rzędzie, (jakby to w y n ik ało z zap isu pętli w linii O ). Za pom ocą in stru k c ji b r e a k p rz e ry w a n a została ta p ętla, w której b r e a k tkw iło b e z p o ś re d n io .
9
In s tru k c ja
goto
W z a sa d z ie na tym m oglibyśm y skończyć o m a w ia n ie instrukcji sterujących, g d y b y n ie jeszcze jedna, w sty d liw a instrukcja g o t o . 9 M a ona form ę: goto e ty k ie ta ;
. e ty k ie ta :
.
in s tr u k c je
Po n a p o tk a n iu takiej instrukcji w y k o n y w a n ie p ro g ra m u p rzen o si się d o m iejs ca, g d z ie jest d an a etykieta. « * * * f j » r .*•
ig ła c h
r
-
f,
Etykieta je s t to nazwa, po której następuje dw ukropek.
P ow ied zm y jasno: 9)
ang. go to - idź do [czytaj: „goł tu")
Tiim iiUiniliii|ię
42
Rozdz. 2. Instrukcje sterujące In stru k cja g o t o u ży w an ie instrukcji goto z d ra d z a , że się jest złym p ro g ram istą. To dlatego, że program (nad-) używ ający instrukcji g o t o jest dla p ro g ram isty nieczytelny, a dla kom pilatora tru d n y d o eleganckiej kom pilacji. Instrukcji tej praw ie zaw sze d a się uniknąć. W języku C++ nie m o żn a sobie skoczyć z dow olnego p u nktu p ro g ra m u w do w olne inne. Etykieta, d o której przeskakujem y, m usi leżeć w o b o w iązu jący m w danej chwili tzw. zakresie ważności. (O tym pom ów im y na stro n ie 70). O to przykład użycia goto std: :cout « goto aaa; std::cout «
"Cos piszemy \n";
/;_ s(,d
"Tego nie wypiszemy
aaa: std::cout << "Piszemy dalej";
// <-wto miejsce
Ten fragment objawi się na ekranie jako: Cos piszemy Piszemy dalej
P rzypom inam , że to, iż w naszym p rzy k ła d zie etykietę w y su n ą łem bliżej lew e go m arginesu, nie m a żad n eg o znaczenia d la kom pilatora. Jem u jest to w sz y sj tko jedno. N am je d n a k nie. Dla nas chyba lepiej, by etykieta b ard ziej rzucała się w oczy, łatwiej ją o d sz u k a ć w tekście p ro g ram u .
W Mimo tej niesławy są sytuacje, gdy instrukcja goto się przydaje N a p rz y k ła d dla n atychm iastow ego o p u szczen ia w ielokrotnie zag n ieżd żo n y ch pętli lub instrukcji switch. Instrukcją break p rz e rw a ć m ożem y p rzecież tylko tę najbardziej zag n ieżd żo n ą pętlę Dzięki instrukcji goto m ożem y w w yjątkow ych p rz y p a d k a c h od razti w yskoczyć na z e w n ątrz. N a zew nątrz - o zn acza tu - na ze w n ą trz tych zag n ież d żo n y ch pętli. (Z aw sze jednak tylko w ram a ch tego bloku p ro g ra m u , w którym z n a n a jest etykieta). O to przykład: int m, i, k; w h i l e (m < 500)
{ while(i < 20) { f o r (k = 16 ; k < 100 ; k = k+4 )
( / / ......... . , // tu wyskoczymy ! _
_ _ _ u _ _ , 4_
if(blad_operacji)goto berlin;
// O
43
Rozdz. 2.Instrukcje sterujące Instrukcja c o n t i n u e > berlin:
// etykieta
cout <<
O
'Po opuszczeniu wszystkich pętli
Jeśli w ja k iś sposób w trakcie pracy tych pętli z m ien n a blad operacji p rz y b ie rz e w arto ść niezero w ą, w ów czas n astąp i w y sk o k O z pętli, i w y k o n y w an e b ę d ą instrukcje p o cząw szy od etykiety b e r l i n &
.10 In s tru k c ja continue In stru k cja c o ntinue p rz y d a je się w ew n ątrz pętli for, while, do. . .while. P ow oduje o n a zan iech an ie w y k o n y w a n ia instrukcji będących d alszą tre śc ią pętli, jednak (w p rzeciw ieństw ie d o instrukcji brea k) sam a pętla nie zo staje p rz e rw a n a , c o n t i n u e p rzery w a tylko ten o b ie g pętli i p rzy g o to w u je do ro z p o c z ę c ia następnego, k o n ty n u u jąc pracę pętli. O to p rz y k ła d : for(int k = 0 ; k < 12 ; k = k + 1)
I stel: :cout « "A"; if(k > 1) continue; std: :cout « "b\n" ;
)
W rezultacie wykonania tego fragmentu programu na ekranie pojawi się: Ab Ab AAAAAAAAAA
Innym i s ło w y napotkanie instru k cji continue o d p o w ia d a jakby takiem u u ż y ciu in stru k cji goto for (...)
{ continue ;
// goto sam koniec;
samkoniec:
1 czyli o d p o w ia d a skokow i d o ety k iety stojącej b e z p o śre d n io p rz e d zam y k ającą pętlę k la m rą. W rezultacie k o m p u te r „p o m y śli", że ju ż w y k o n ał treść p ętli, i p rzy stąp i d o w ykonyw ania n astęp n e g o obiegu. Z apam iętaj, że: W p rzy p ad k u pętli for oznacza to, ż e na w id o k instrukcji c o n tinue w y k o n y w a n ie bieżącego obiegu p ętli zostaje z an iech an e , a zaczyna się w y k o n y w a n ie instrukcji kroku pętli. W powyższym przykładzie jest to k = k+ 1
44
Rozdz. 2. Instrukcje sterujące K lam ry w instrukcjach sterujących | N astęp n ie dopiero sp raw d zan y jest warunek. Zachow anie pętli w h i l e na widok instrukcji c o n t i n u e m o żn a zilustrow ać podobnie za pom ocą instrukcji g o to while(warunek)
{ continue;
// goto seun_koniec;
sam_koniec:
) P odobnie w p rz y p a d k u pętli d o - w h i l e do
{ continue;
// goto sam_koniec;
sam koniec: JwhTle(warunek); " '• - l i
2.11
4
K la m ry w in s tru k c ja c h s te ru ją c y c h P am iętasz, m ów iłem kiedyś, że język C++ je st językiem o d o w o ln y m formacie. W ynika z tego także, iż klam ry l ) w naszych instrukcjach sterujących m ożem y staw iać w różnych m iejscach. Oto kilka w arian tó w while(i < 4)
{
} while(i < 4)
©
{ ) w h i l e ( i < 4) i
©
) W szystkie trzy sp o so b y są jednakow o d o b re. N am aw iam jed n ak d o przyjęcia je d n eg o stan d ard u . D laczego to takie w ażn e? O tóż jednym z najczęstszych błędów jest zapomnienie o zam knięciu klam ry. G d y stosujem y zap is © i© to w y raźn ie w id zim y , które k la m ry należą d o siebie. W sposobie O tego nie w idać, ale za to je st on o jedną linijkę krótszy. O sobiście stosuję zapis © . D laczego nie p rz e s z k a d z a m i w spom niana w a d a zap isu O ? Z d w ó ch p ow odów :
Rozdział. 2.Instrukcje sterujące Ćwiczenia
45
1) W m oim e d y to rz e jest kom enda, która p o zw ala mi o d szu k ać d rugi naw ias: p o k azu ję k u rso rem na n aw ias lew y, a ed y to r o d szu k u je m i o d p o w iad ający m u praw y. To sam o w p rz y p a d k u klam er. Jeśli n aw et p o g u b ię się z tym i klam ram i w d łu g im program ie, to dzięki tej opcji łatw o z n aleźć błąd. (S praw dź czy i w Tw oim ed y to rz e jest p o d o b n a ko m en d a). 2) M am sposób, k tó ry p raw ie całkow icie p o zw ala mi u n ik n ą ć błędu.
Dawniej robiłem mianowicie tak: P isałem n p . i f (warunek), p o te m otw ierałem k lam rę i d łu g o pisałem w szy stk ie instru k cje, p o czym klam rę u ro czy ście zam y k ałem - o ile tylko jeszcze p am ięta łem , ż e m a m jakąś k lam rę zam k n ąć. Łatw o o tym zapom nieć, szczególnie w ted y , g d y w e w n ętrzu są zag n ieżd żo n e inne insrrukcje z klam ram i.
Teraz robię tak:
M im o w sz y stk o zap ew n e czasem pogubisz się w staw ian iu klam er. R a d z ę Ci: spróbuj to zro b ić św iad o m ie po to, by się p rzek o n ać, jak na to za re a g u je Tw ój ko m p ilato r. A lbow iem jego k o m u n ik a t o błędzie w cale nie m usi m ó w ić o b ra k u klam ry. W d ziałający m program ie u s u ń lu b dodaj jed n ą z zam ykających k la m e r i spróbuj to skom pilow ać. K o m u n ik a t o błędzie m o że być w stylu „zła d e k laracja funkcji" alb o coś w tym rodzaju. D obrze to zapam iętaj, bo g d y p o tem otrzym asz taki sam k o m u n ik at - b ę d z ie s z już w ied ział, ż e przyczyny m o ż n a szu k ać ró w n ież w n aw iasac h k la m ro w y c h .
12
Ć w ic ze n ia W naszy ch ćw iczeniach często zobaczysz frag m en ty p ro g ra m u z a n o to w a n e w tak z w a n y m pseudo-kodzie. P seu d o -k o d - to taki te k st p ro g ra m u , k tó ry zaw iera elem enty (służące ilustracji logiki p ra c y p ro g ra m u ) nie d a ją c e się skom pilow ać. Na p rz y k ła d w jednym z ćw iczeń zam iast p isa ć instrukcje, które m o g ły b y odw rócić Tw oją uw agę od istoty zag ad n ien ia, u ż y łe m słów : jakaś_instrukcja.
46
Rozdz. 2.Instrukcje sterujące Ćwiczenia
Mamy wyrażenie logiczne (y < 0). Jakie mogą być jego wartości? Czy w miejscu, gdzie program spodziewa się wyrażenia logicznego, można postawić na przykład obiekt (zmienną) typu całkowitego int? Jak zareaguje program napotykając taki obiekt zamiast warunku? Załóżmy, że mamy obiekt typu in t o nazwie a. Które z następujących konstrukcji i f są dopuszczalne? int a; // ... n adanie w a r to ś c i ob iekto w i a if(a < 6) jakaś_mstrukcja; i f (a ) jakaś_mstrukcja; if(a + 2) jakaś_instrukcja; if( (a + 7) < ( 1 0 - 2 ) ) jakaś_instnikcja; i f ((a + a ) > {a - 4 )) ja k a ś _ in s tr u k c ja ;
a)
b) c) d) e)
W poniższym pseudo-kodzie trzykrotnie występuje słowo kluczowe else. Do którego if należą tak umieszczone słowa else? Odpowiedz podając nazwę warunku przy danym if. if (w a r u n e k l )
{ i f (w a r u n e k 2 ) ja k a ś _ in s tr u k c ja ; if {w a ru n c k 3 ) ja k a ś _ in s tr u k c ja ; else ja k a ś _ in s tr u k c ja ;
ł else if (w a r u n e k 4 ) if (w a r u n e k 5 ) ja k a ś _ in s tr u k c ja ; else ja k a ś _ in s lr u k c ja ;
Na czym polega pętla programowa? Które z trzech poznanych pętli programowych mogą sprawić, że zawarte w nich instruk cje (instrukcje będące ich treścią) nie będą wcale wykonane? Jaka jest zasadnicza różnica między pętlą programową zrealizowaną za pomocą instrukcji w h ile b ) . instrukcji do-while? Co z tej różnicy wynika? Kiedy w instrukcji fo r sprawdzany jest warunek - przed, czy po obiegu pętli? a)
,
Wewnątrz nawiasu instrukcji for są dwa średniki. Po drugim z nich jest instrukcja, którą roboczo nazwaliśmy instrukcją kroku pętli. Kiedy jest ona wykonywana - przed czy po pojedynczym obiegu pętli? Podaj przykłady realizacji nieskończonej pętli za pomocą trzech poznanych rodzajów pętli (for, while, do-while). Pętla jest nazywana "nieskończoną", gdy jej warunek jest zawsze prawdziwy, więc nie ma szans, by przerwał on pracę tej pętli. Za pomocą jakich instrukcji można (z pozycji programu) zakończyć pracę pętli i spowodować przejście do wykonywania instrukcji po niej bezpośrednio następujących? Podaj dwa sposoby. Jak można zakończyć pracę nieskończonej pętli i przejść do o wiele dalszych instrukcji, a j nawet takich poprzedzających ją? (Czyli jakby "wrócić").
Rozdział. 2.Instrukcje sterujące Ćwiczenia
47
Jaka jest różnica między obiektem zdefiniowanym w w yrażeniu inicjalizacyjnym pętli f o r ' a taklm samym obiektem zdefiniowanym o jedną linijkę powyżej tej pętli fo r ? Czy popraw na jest taka instrukcja s w itc h ? int m; / / ... n a d a n ie w a r to ś c i o b ie k to w i m switch(m)
( case 1: // . . . break: def a u l t :
//.
.
.
case 5-2: // ... break; case 5-4:
//.
.
.
}
Jakie konsekwencje ma nie umieszczenie instrukcji break po instrukcjach danej etykiety VV\boru wielowariantowego można dokonać przy użyciu instrukcji s w i tc h lub _ f . . . e i se. Przypomnij trzy aspekty, w których obie te instrukqe się różnią. Mamy dokonać wy'boru wariantu działania programu zależnie od ilości próbek przeba19 P1262 uk*ac*pomiarowy. Jeśli jest ich 6, to pode m ujem y działaniaA, jeśli jest ich 12, to działaniaB, a w każdym innym przypadku dzialaniaC. Czy można sięw tym celu tu posłużyć instrukcją sterującą switch? Zadanie podobne do poprzedniego, lecz nieco zmienione. Mamy dokonać w yboru wariantu działania programu zależnie od ilości próbek przebadanych przez układ pomiarowy'. Jeśli jest ich 6, to podejmujemy działaniaA, jeśli jest ich 12, to działaniaB, a jes i więcej niż 12, todziałaniaC. Czy można się w tym celu posłużyć instrukcją s w itc h ? Mamy dokonać wyboru wariantu działania programu zależnie od wielkości przychodu wy rażonego w złotych (bez groszy). Są trzy progi podatkowe (stałe) Zależnie od w ielko ści oc odu (między którymi jest progami) podatek oblicza się jedną z czterech proce ur. Czy można się tu posłużyć instrukcją s w i tc h d o wybrania odpow iedniej procedury? Jakie działanie może mieć instrukcja break umieszczona w pętlach for, while, do-while7Jak działa w przypadku, gdy te pętle są zagnieżdżone? Jaką instrukcją sterującą możemy posłużyć się, by w ydostać się z w ielokrotnie zagnieżdżonych pętli? Co to jest etykieta? Jakie działanie w pętlach ma instrukcja c o n tin u e ? Jeśli w trakcie wykonywana treści pętli for napotkana zostanie instrukcja continue, to co najpierw zostanie wykonane - sprawdzenie warunku, czy instrukqa krokowa ?
48
Rozdz. 3. Typy D eklaracje typów
Typy — .——.....
3.1
n»"i— «' «>!■ »
II■■<■»■ ' I
*
»
J
5
D ek la ra c je ty p ó w
K
ażd a n azw a* w C++ zanim zo stan ie użyta, m usi zostać zadeklarow ana. M ógłbyś teraz zapytać: "To znaczy deklarujem y, że co?"
m m !■■■mi
■
Deklarujemy, że dana nazwa ta oznacza obiekt jakiegoś typu.
* „ ,
—
— —
— —
—
—
»
I II IM —
—
.H
M — — — t l i l i ll« » l
m
i l lllt r H—
«—
m m m
I
M oże to być n a p rzy k ła d typ całkow ity ( i n t ) , zm ien n o p rzecin k o w y (d o u b le ) , itd. b.4:-:
Na przykład deklarujemy, że nazwa b oznacza obiekt typu ir, t. To inform uje k o m p ilato r, jak ma w p rz y p a d k u n apotkania obiektu o tej nazw ie postępow ać. W yjaśnijm y to na przykładzie. Z ałóżm y, że kom pilator n ap o tk ał w naszym pro g ram ie w y rażen ie a + b Jest to d o d aw an ie , zatem trzeba u ru ch o m ić specjalny p o d p ro g ra m zajmujący się d o d aw an ie m . (Taki p o d p ro g ram nazyw a się też czasem operatorem dodaw ania). W zw iązku z ty m , ż e w kom puterze liczby całkow ite p rzec h o w y w an e są inacze niż liczby zm ien n o p rzecin ko w e - o p erato r d o d aw an ia m u si inaczej postę pow ać w sto su n k u do liczb całkow itych, a inaczej w sto su n k u d o liczt zm iennoprzecinkow ych. N apotykając ó w zapis - kom pilator m u si więc do k ład n ie w ied zieć czy sym bol fc oznacza u n as liczbę całkow itą, czy zm iennoprzecinkow ą. Skąd to będzie
1)
Z wyjątkiem nazwy użytej w etykiecie.
49
Rozdział. 3. Typy D eklaracje typów
w ie d z ia ł? W łaśnie z deklaracji. D eklarując w cześniej zm ien n ą b jako typu integ er, czy li p isząc i n t b; p o w ie d z ie liśm y k o m p ilato ro w i tak: Jakbyś n ap o tk ał n a z w ę b, to w ied z, że oznacza ona zm ienną (obiekt) ty p u i n t .
I
T utaj d ek laracja jest ró w n o cześn ie definicją. O zn acza to, że nie tylko, iż infor m u jem y k o m p ilato r o tym , co oznacza nazw a b, lecz także żąd am y w ty m m iejscu, b y zarezerw o w ał w pam ięci obszar na tę zm ie n n a - czyli pow ołał ją d o życia.
Definicja ta mówi więc kompilatorowi: A teraz zarezerwuj mi w pamięci miejsce na obiekt typu całkowi tego o nazwie b.
I
N ie z a w s z e jednak ch o d zi o p o w o łan ie d o życia. M o że być przecież sytuacja, g d y z m ie n n a ta już g d zieś w p ro g ram ie istnieje, a tu chcem y tylko p o in fo rm o w ać k o m p ila to r o jej typie. e x te r n i n t b; Słow o e x t e r n (zew nętrzny) inform uje k o m p ilato r, że o b iek t ty p u i n t o n a z w ie b ju ż g d zieś istnieje, na p rzy k ła d na z e w n ą trz p lik u , k tó ry m zajm u je się w łaśn ie ko m pilator. T o „na z e w n ą trz " m oże o zn aczać też jakąś funkcję b ib lio te czną, k tó rą dołączym y d o p ie ro na etapie lin k o w a n ia. W trakcie k o m p ilacji n aszeg o p lik u , kom pilator m u si ju ż jednak w ied zieć, jak ieg o ty p u jest ta „ z e w nętrzn a zm ien n a b. Po to p o trzeb n a m u ta d ek laracja.
Powtórzmy więc różnicę między deklaracją, a definicją D eklaracja - in fo rm u je kom pilator, ż e d a n a n azw a re p re z e n tu je o iekt jakiegoś ty p u , ale nie rezerw u je d la n ieg o m iejsca w p a m ię c i. Definicja zaś - d o d a tk o w o rezerw u je m iejsce. Definicja je st m iej scem, g d zie p o w o łu je się obiekt do życia. O czyw iście definicja jest p rzy okazji zaw sze tak że d ek laracją , bo p rzecież: jeśli rezerw u je o n a miejsce w pam ięci, to m usi o n a p rzec ież k o m p ilato ro w i w y ja ś n ić na co rezerw u je.
Deklarować obiekt można w tekście programu wielokrotnie. D efiniow ać g o (powoływać go do życia) można tylko raz.
Studium języków obcych Różnicę m ięd zy deklaracją a definicją łatw o z ro z u m ie ć i z a p a m ię ta ć tłu m a c z ą c sobie d o sło w n ie te słowa.
Deklaracja:
50
Rozdz. 3. Typy Systematyka typów z języka C++
od łacińskiego clarus: jasny, zrozumiały. Zresztą po polsku też mówi się: „klarmoać coś komuś". De-klarować to jakby: wy-jaśniać. Deklaracja naz wy N jest więc tylko wyjaśnieniem kompilatorowi, co dana nazwa oznacza. Definicja:
pochodzi od łacińskiego słowa fin is - koniec, granica. Defin iować - to jakby zakreślać granicę. W naszym przypadku tę granicę wykreśla się wokół komórek pamięci, które są przydzielane w ten sposób obiektowi. Mówi się: ta, tamta i jeszcze ta komórka - stają się od tej pory obiektem o danej nazwie N. W ten sposób odbyły się narodziny obiektu o nazwie N.
Oto przykłady definicji i deklaracji: in t 1 ic zba; extern int licznik;
3.2
//d e fin icja + deklaracja / / deklaracja (ty lk o !)
S ys te m a ty ka ty p ó w z języka C ++ W deklaracji określa się, że dany obiekt jest jakiegoś typu. Jakie m am y do dyspozycji typy? Jest ich dużo. W prow adźm y w ięc pew ien p o rząd ek .
Możliwych jest wiele podziałów typów, tu omówimy dwa P ierw szy m ożliw y podział, to podział na: ♦♦♦ typy fund am en taln e (jakby najbardziej podstaw ow e), «$♦ typy złożone (czyli takie, które w swej istocie w y k o rzy stu ją istnienie jakiegoś typu fundam entalnego),
Na przykład: gdyby typem fundamentalnym było "jabłko ", to typem złożonym mógłby być typ "kosz jabłek". Drugi możliwy podział, to podział ze względu na autora danego typu: ♦♦♦ typy w b u d o w a n e - to takie, w które język C++ jest stan d ard o w o w yposażony, ♦♦♦ typy zd efin io w an e p rzez użytkow nika - to typy, które m o żesz sobie wymyślić sam em u.
Ta cecha jest chyba jednym z najlepszych pomysłów w języku C++ W tym tomie "Sym fonii” zajm iem y się ty p am i w b u d o w an y m i. N ato m iast ty p o m definiow anym p rzez użytkow nika pośw ięcona jest dalsza część książki. O czyw iście w śród ty p ó w w budow anych m ożna w yróżnić typy fu n d a m e n ta ln e i złożone.
3.3
T y p y fu n d a m e n ta ln e O to ich lista:
Rozdz. 3. Typy Typy fundam entalne
51
T y p y p rz e z n a c z o n e d o p rzec h o w y w an ia i p racy z liczbam i całkow itym i sh o rt in t in t lo n g i n t
inaczej:
sh o rt
inaczej:
lo n g
o ra z tak z w a n y ty p w y liczen io w y enum , o k tó ry m p o ro zm a w iam y n ieb aw em (str. 87). i n t jest sk ró te m od an g ielsk ieg o słow a integer? Jeśli ch cielib y śm y m ieć w p ro g ra m ie zm ien n ą p rzech o w u jącą jakieś w artości całko w ite, to z m ien n a ta m o ż e b y ć jednego z tych trzech w y m ien io n y ch w yżej typów . Jeśli n ie w iesz, k tóry z nich w ybrać, to w y b ie rz ty p i n t . T yp d o p rz e c h o w y w a n ia z n a k ó w alfan u m ery czn y ch 3. char Z n ak am i a lfa n u m ery cz n y m i są n a p rzy k ła d litery. O b iek ty ty p u c h a r * m o g ą p rz e c h o w y w a ć p o d staw o w y z e s ta w takich z n a k ó w
W szystk ie p o w y ż sz e ty p y i n t i c h a r m ogą b y ć w d w ó c h w a ria n ta c h - z e zn ak iem i b e z zn aku. Do w y b ra n ia w arian tu p o słu g u je m y się p rz y d o m k a m i (specy fik ato ram i) s i g n e d lu b u n s i g r . e d 5 np. s ig n e d i n t u n s ig n e d i n t W y p o saże n ie ty p u w zn ak s p ra w ia , ż e obiekt ta k ie g o ty p u m o że p rz e c h o w y w a ć liczbę u je m n ą lub d o d atn ią. T y p bez zn ak u m o ż e p rz e c h o w a ć ty lk o liczbę d o d atn ią. P rzez d o m n ie m a n ie przy jm u je się, ż e zapis int a ;
oznacza, ż e ch o d zi n am o ty p s ig n e d i n t a ; czyli ty p z e znakiem .
2) !) 1) i)
ang. in teger - całkowity [czytaj: "intidżer"] ang. sh o rt - krótki [czytaj: "szort"], ang. l o n g - długi alfanumeryczne - ten termin powstał z połączenia słów: alfabetyczne + num eryczne char - [wym. "kar"]. Jest to skrót od ang. ch a ra c ter (znak pisarski) ang. sig n ed , u n s ig n e d - z e znakiem, bez znaku [czytaj: „sajnd", „ansajnd"]
52
Rozdz. 3. Typy Typy fundam entalne N ato m iast w p rz y p a d k u ty p u c h a r sp raw a n ie jest tak prosta. To, czy przez d o m n iem an ie b ęd ziem y m ieli s i g n e d czy u n s i g n e d - jest ró ż n e w różnych kom pilatorach. M ów im y krótko: zależy to od im plem entacji danego kom pilatora. T yp m ogący p rzech o w y w ać rozszerzony z e sta w znaków alfan u m ery czn y ch w c h a r_ t W p ro w ad zo n y o n zo stał sto su n k o w o n ie d aw n o . Pow odem był fak t, że zw ykły ty p c h a r m oże p rz e w a ż n ie służyć do p rzech o w y w an ia tylko z n a k ó w z p o d sta w o w e g o zestaw u 256 liter. Tym czasem b y w ają przecież języki, w k tó ry ch jest o w iele więcej liter. „ , . ć„ Nazwa w char_ t jest jakby skrótem od angielskiego wide character (znakszeroki). Szeroki, bo obiekty tego typu zajmują zwykle więcej komórek
pamięci, niż zwykły ch a r. Na końcu nazwy tego typu jest dodatek _ t, co jest śladem po chytrym sposobie realizacji tego typu w języku C. W C++ realizacja tego ty p u nie w y m ag ała ju ż żadnej chytrości. T y p ten jest p ełn o p ra w n y m , o d rę b n y m typem . "V *
T ypy rep rezen tu jące liczby zm ien n o p rzec in k o w e flo a t rrj fig i m J u d o l i i i . d o u b le uiri g 'u v / sin fitd y w lo n g d o u b ler\
•' / f i SVri ; >'v-' ■ - ■.*jr.«. u m o żliw iają p racę n a liczbach rzeczyw istych z ró żn ą d o k ład n o ścią. Jak te n azw y zapam iętać? <♦ f l o a t 2 - p o ch o d zi o d słów ang. floating point - zm ien n y p rzecin ek . <♦ d o u b l e 2 p o ch o d zi o d słów ang. doubleprecision - p o d w ó jn a d o k ła d n o ść <♦ l o n g d o u b l e - (ang. long - d łu g i) o zn acza w y d łu ż o n ą , ulepszone w ersję ty p u o p odw ójnej precyzji. C zasem m ów i się na to rozszerzont
dokładność Z ap am iętaj
*«*»«***
Jeśli nie wiesz, którego z tych trzech typów zm iennoprzecinkow ych użyć w definicji swojego obiektu, to użyj typu d o u b l e .
T y p d o rep rezen tacji o b iek tó w logicznych bool
6) 7)
8)
ang. w id e ch a ra c te r - [czytaj: "łajd karakter' J. f l o a t [czytaj: „flołt"]. d o u b le [czytaj: „dabl"].
53
Rozdział. 3. Typy Typy fundam entalne
N a z w a te g o ty p u p o ch o d zi o n n azw isk a sły n n eg o m a tem aty k a, G eorge'a Boola.^ O b iek ty ty p u b o o l m ogą p rz ec h o w y w ać jedną z d w ó c h w artości: prawda lu b fałsz. Z w y k le d o takich o b iek tó w w p isu jem y efekt s p ra w d z e n ia jakiegoś w aru nku lo g iczn eg o , lu b jakąś n aszą decyzję. Na przykład czy decydujemy, by próbki pomiarowe były wirowane czy n ie. Alłx> czy w grze komputerowej ma być tło muzyczne, czy nie. O czyw iście d o m y ś la sz się ch y b a, że w obiekcie ty p u b o o l n ie przechow uje się p o lsk ieg o w y ra z u prawda lu b p o lsk ieg o w y razu fałsz. W C ++ u ży w a się zresztą angielskich o k re śle ń true i false^. Jed n ak n aw et te a n g ie lsk ie słow a nie są w obiekcie ty p u b o o l p rzec h o w y w an e. Z asad a jest b a rd z o p ro s ta - i ro zm aw ialiś m y o niej w p o p rz e d n im ro zd ziale. S ta n fałsz ( f a l s e ) p rz e c h o w y w a n y je st z a p o m o c ą w artości 0, n a to m ia s t stan prawda ( - rue) za pom ocą w arto ści niezerow ej jaką je s t 1. M ożna je d n a k p rz y p isa ć d o ta k ie g o ob iek tu w arto ść t r u e lu b f a l s e bool c z y _ g ra c ; czy_grac = f a l s e ;
// «—definicja obiektu // przypisanie temu obiektowi wartości f a l s e
Jed n ak m im o ta k ieg o p rz y p isa n ia - n a p ra w d ę i ta k w s ta w io n e tam z o stan ie zero, bo tak re p re z e n to w a n y jest s ta n f a l s e .
Podobnie przypisujemy wartość t r u e . Jeśli tak zrobimy, to w rzeczywistości w n aszy m ob iek cie c z y _ g r a c z n a jd z ie się w arto ść 1. W p o p rz e d n im ro zd ziale ro zm a w ialiśm y ju ż nieco o ty p ie b o o l i tam z o b ac zy łeś, jak d o o b ie k tu ty p u b o o l p rzy p isać re z u lta t s p ra w d z e n ia jakiegoś w a ru n k u . P rzy p o m in am : Załóżmy, że obiekt o nazwie x jest typu całkowitego i n d przechowuje ju ż jakąś sensowną wartość. Zatem instrukcja
c z y g r a c = (x < 12);
. • . spowoduje przypisanie obiektowi c z y g r a c wartości t r u e l u b f a l s e , zależnie od obecnej wartości x.
9) 10)
•
Jeśli obecnie w a rto ść p rz e c h o w y w a n a w o b ie k cie x b y ła m n iej sz a od 12, to w y ra ż e n ie x < 12 jest w y ra ż e n ie m p ra w d z iw y m . Z atem w te d y o b ie k to w i c z y g r a c z o s ta n ie p rz y p is a n a w arto ść t r u e .
•
Jeśli zaś w o b iekcie x jest teraz w a rto ść w ię k s z a lu b ró w n a 12, to w y rażen ie x < 12 staje się w y ra ż e n ie m fa łsz y w y m . W tej s y tu a q i d o o b ie k tu c z y g r a c w s ta w io n a (p rz y p is a n a ) zo stan ie w arto ść f a l s e .
b o o l [czytaj: "buul"] - George Bool (1815 - 1864), brytyjski m atem atyk i filozof. t r u e [czytaj: "truu”] - prawdziwy, f a l s e [czytaj: "folls"] - fałszywy.
54
Rozdz. 3. Typy Typy fundam entalne
P oznaliśm y w ięc już ty p y całkow ite i typy zm iennoprzecinkow e. O gólnie m ożna o nich pow iedzieć ty p y arytmetyczne, bo m ożna na nich w ykonyw ać operacje arytm etyczne. Z astan aw iasz się zap ew n e, p o co są aż trzy typy reprezentujące liczby całkow ite oraz trz y ty p y rep rezen tu jące liczby zm iennoprzecinkow e. C hodzi o to, by m ożna było lepiej w ykorzystać m ożliw ości danego k o m p u tera i kom pilatora. Zależnie od tego, ile dany kom pilator przydziela kom órek pam ięci na zapis danej liczby - dysponujem y m niejszą lub w iększą precyzją obliczeń. N ie jest bow iem tak, że w obiekcie typu i n t m o żn a przechow yw ać dow olną, astronom icznie w ielką liczbę całkowitą. O biekty d an eg o typu w pam ięci są realizo w an e za pom ocą skończonej ilości bitów. Widziałeś pewnie kiedyś licznik kilometrów w samochodzie. Jeśli ma on tylko sześć czarnych krążków z namalowanymi cyframi - to znaczy, że największą liczbą, jaką może on pokazać jest 999999. Jeśli przejedziemy jeszcze jeden kilometr, to na liczniku tym pojawi się 000000. Dlatego, żt większych liczb ten licznik pokazać już nie potrafi. Oczywiście problem ten można łatwo rozwiązać konstruując licznik 2 siedmioma krążkami. Tyle tylko, że cena tego licznika nieco wzrośnie. Może nie każdy chce ją zapłacić... P rzed ział liczb m ożliw ych d o przechow yw ania w obiektach różnych typów fu n d am en taln y ch - zależy o d typu kom putera, na k tó ry m pracujesz, a także oc kom p ilato ra, który dokonuje kom pilacji Twojego p ro g ram u . Spójrz n a p o n iższą tabelę. U m ieściłem w niej inform acje o najczęściej s p o ty k a nych w artościach. D otyczą o n e kom pilatorów pracujących 32 bitowo. Oczywiście nie musisz się treści tej tabeli uczyć na pamięć. To tylko tak dlc ciekawości. Spójrz na kolum nę d ru g ą i trzecią:
Szerokość w bitach - oznacza z a pom ocą ilu bitów zapam iętuje się w artość w obiekcie d an eg o typu. (Im w ięcej bitów , tym w ięk sza jest m ożliw a wartoś< przech o w y w an a). Szerokość w bajtach - to szerokość w bitach p o dzielona p rzez 8. Bajt m a 8 bitów .
Z e s ta w ie n ie ty p ó w i m o ż liw y c h d o p r z e c h o w y w a n ia w n ich w a r to ś c i dla k o m p u te ró w /k o m p ila to ró w 32 b ito w y c h
_ Ti v yd y
...-
char
...
-
Szerokość w bitach
Szerokość w bajtach
8
1
N ajczęściej spotykany zakres wartości -1 2 8 d o 127
55
Rozdział. 3. Typy Typy fundam entalne |
-■■■ -
---------
■
Zestawienie typów i możliwych do przechowywania w nich wartości d la kom puterów/kom pilatorów 32 bitowych Szerokość w bitach
Szerokość w baitach
u n s i g n e d char
8
1
0 d o 255
s i g n e d char
8
1
-1 2 8 do 127
wchar t
16
2
0 d o 65535
short
int
16
2
s i g n e d short int
16
2
-32768 d o 32767 ' -32768 d o 32767
u n s i g n e d short
16
2
0 d o 65535
int
32
4
-2147483648 d o 2147483647
s i g n e d int
32
4
-2147483648 d o 2147483647
u n s i g n e d int
32
4
0 d o 4294967295
long
32
4
-2147433648 d o 2147483647
s i g n e d long
32
4
-2147483648 d o 2147483647
u n s i g n e d long
32
4
0 d o 4294967295
f loat
32
4
3.4E-38 d o 3.4E+38
double
64
8
1.7E-308 d o 1.7E+308
l o n g double t ....
80
10
T yp
Najczęściej spotykany zakres wartości
. ..
.
-i 1'
int
-
l
3.4E-4932 d o l.lE + 4 9 3 2
56
Rozdz. 3. Typy Typy fundam entalne
Przykładem jest choćby typ wcha r_ t, którego rozmiar jest lołaśnie zależ ny od implementacji. O tym , jak zapisuje d a n y typ Twój kom pilator, będziesz się m ó g ł przekonać stosując operator s i z e o f (rozmiar). O p erato r ten przed staw im y niebaw em . (N a str. 113). N iezależn ie od tego, z jakim konkretnie k om pilatorem masz d o czynienia m o żesz liczyć na to, że: ♦♦♦ c h a r — ma co najm niej 8 bitów (1 bajt), ♦♦♦ s h o r t i n t — m a co najmniej 16 b itó w (2 bajty), ♦> lo n g — ma co najm niej 32 bity (4 bajty).
3.3.1
D efiniow anie obiektów „w biegu". W naszych dotychczasow ych program ach ju ż kilkakrotnie sp o tk aliśm y się z definicjam i zm iennych. W niektórych językach program ow ania definicje obiek tó w p o w inny nastąpić p rz e d w ykonyw anym i instrukcjam i. Tak też jest w klasy czn y m C. W C++ zasada ta nie obow iązuje. O biekt m o ż n a zdefiniow ać „ w bieg u ", „w locie" Tang. onflight], m ięd zy dw om a instrukcjam i - w tedy, gdy u z n a m y , że jest on n am w łaśnie potrzebny. O to przykład: # in c lu d e < io stream > i n t m ainO flo a t
■
d lu g o s c _ fa li;
std::cout «
// •' v
"Podaj współczynnik załamania:
f l o a t w s p ó łc z y n n ik ; s t d : : c i n » w sp ó łc z y n n ik ; s t d : : c o u t « " Z rozum iałem , w s p ó łc z y n n ik ma b y c: " << w s p ó łc z y n n ik ; //.... dalsze obliczenia
// ®
} W tym p rzy k ład zie w id zim y , że p rzed instru k cjam i w y k o n y w a n y m i w funkcji m a in jest definicja obiektu typu f l o a t O . Jest to definicja w sta ry m , klasycz n y m stylu. T ym czasem w trakcie p isan ia program u d o ch o d z im y d o w n io sk u , ż e p o trz e bujem y jeszcze jednego o b iek tu f l o a t - na w sp ó łczy n n ik załam an ia. M ożem y w rócić d o O i tam d o p isać n astęp n ą definicję, a le m o żem y też zro b ić to tu , g d zie sobie o zm iennej p rzy p o m n ieliśm y 0 , lub g d z ie w y n ik ła k onieczność jej istn ie nia. R obim y w ięc tę definicję w biegu, nie p rz e ry w a ją c n o rm aln eg o to k u pisan ia p ro g ra m u . Ten sposób je st n aw et logiczniejszy. C o p ra w d a , w naszym p rzy k ła d zie w arto ść w czy ty w aliśm y z k la w ia tu ry , jed n ak w innych p rz y p a d k a c h — często w linijce, w której w y n ik ła k o n ieczn o ść istn ien ia obiektu — już d o k ła d n ie w iem y, jaką w arto ść p o w in ien o n w stęp n ie
57
Rozdział. 3. Typy Typy fundam entalne
zaw ierać. M o ż e m y w ięc nie ty lk o zd efin io w ać obiekt, ale o d razu (w tej sam ej instrukcji), n a d a ć m u w artość. T ak ie po stęp o w an ie d aje szy b szy p ro g ram (bo to m niej p ra c y n iż w a ria n t z p o w tó rn y m w racaniem d o zm ien n ej, by jej n a d aw ać w artość).
Definicja na potrzeby pętli for W p o p rz e d n im ro zd ziale (na str. 34) zobaczyliśm y też, że m o żn a d efin io w ać o b iek t ró w n ie ż w instrukcji p ę tli for, w jej części o d p o w ied zialn e j za inicjalizację p ra c y pętli. O to p rzy p o m n ien ie : for(int licznik = 0 ; licznik < 12 ; licznik++)
{
.
.
.
.
.
// tr e ś ć p ę tli. (T u ta k ż e m o ż n a s k o r z y s ta ć z o b ie k tu licznik)
1 // t u o b ie k t licznik je s t j u ż n i e z n a n y
Jak w id z im y , o b iek t o n azw ie l i c z n i k zo stał z d e fin io w a n y w instrukcji pętli for. M a to ta k ie konsekw encje, że: istnieje on, je s t znany i m oże być używany tylko w ram ach tej instrukcji for - i o czyw iście w jej bloku (treści pętli).
Dbiekty definiowane w miejscu warunku i f M ożliw e je st zd efin io w an ie ja k ieg o ś obiektu w w a ru n k u instrukcji if. N a p rzy k ład : ■
if(int rząd = ilosc / ile w rzedzie) cout << "Jesteśmy w " << rząd << " rzedzie" << endl; else
1 cout «
"Zerowy rząd (rzad= " << rząd «
"
«
endl;
) //t u o b ie k t rząd j u ż n ie j e s t d o s tę p n y
Jak w id z isz , w p o w y ższy m w y ra ż e n iu w a ru n k o w y m in stru k c ji i f jest ja k aś 'czasochłonna operacja matematyczna" (u d zielen ie),
n a s to z w y k łe
<$► a jej re z u lta t z ap isa n y z o staje w specjalnie n a te n cel p rz y g o to w a n y m o b iekcie ty p u i n t . (M a o n n a z w ę r z ą d ) . Tak zdefiniow any obiekt, je st d o stę p ny w ew nątrz bloku i f , oraz w e w n ą trz odpow iadającego mu bloku else. |
O czyw iście tu ta j te operacje "m atem aty czn e" b y ły p ro s te (zw y k łe d z ie le n ie ), w ięc k o rzy ść n ie jest d u ża. W y o b raź sobie je d n ak , ż e c h o d z i o n a p r a w d ę czaso chłonn e obliczenie, którego nie c h ce m y w b loku i f p o w ta rz a ć . W te d y o p ła c a się w ykon ać je jed n o k ro tn ie, a r e z u lta t schow ać w c h w ilo w y m o b ie k c ie , k tó ry
58
Rozdz. 3. Typy Stałe dosłowne
m k ts
b ę d z ie n am p rz y d a tn y tylko w tej instrukcji i f (w jej bloku i odpow iadającym jej b lo k u e l s e ) . n r*:; ' r • *!tiG>nr- ... (nt.jiwoą i U&hc.v
Oczywiście mogliśmy postąpić "po staremu", czy li obiekt r z ą d z d e fin io w ać jeszcze p rzed instrukcją i f i tam też w ykonać n a sz ą operację "m atem atyczną". int rząd = i l o s c
/ ile _ w _ r z e d z ie ; if(rz ą d ) { c o u t « " J e s te ś m y w " << rząd << " r z e d z ie " « e n d l ; } e ls e { c o u t << "Zerow y r z ą d (rzad = " << rząd « " ) " << e n d l ;
II tu obiekt r z ą d nadal istnieje i jest dostępny W instrukcji i f sp raw d zalib y śm y tylko w artość tego obiektu r z ą d , a oczyw iście obiekt r z ą d tak że byłby d o stęp n y w bloku i f i bloku e l s e . W szy stk o od b y ło b y się p o d o b n ie - poza jed n y m faktem: Po zak o ń c z e n iu całej in s tru k c ji i f o b ie k t r z ą d istn ia łb y nad al. Jeśli w cale n am na tym jego d alszy m istnieniu nie z a le ż y - lepiej posłużyć się op isan y m tutaj sposobem , czyli zd efin io w ać go w w y rażen iu w aru n k o w y m instrukcji i f . Dzięki tem u, p o jej zakoń fOaUużrr; (Pm czeniu, p rzesta n ie od istnieć.
Obiekty definiowane w wyrażeniu warunkowym instrukcji while P ew n ie się już dom yślasz, bo spraw a jest p odobna: Jeśli w wyrażeniu warunkowym instrukcji w h i l e zdefiniujemy ja kiś obiekt chwilowy, to jest on dostępny w całym bloku (ciele) tej instrukcji w h i l e .
w h i l e ( i n t r z ą d = i l o s c / ile _ w _ rz e d z ie ) * II tu możemy korzystać z obiektu rz ą d //•••
3.4
S ta łe d o s ło w n e W tekście p rogram u często posługujem y się stałym i. M ogą to b y ć napisane do sło w n ie liczby (np. 3.14), m ogą to być znaki (litery) albo napisy, ciąg i znaków przezn aczo n e d o w ypisania na ekran (np. "W itam y na pokładzie"). M ogą to też być słow a t r u e / f a l s e . Takich stałych dosłow nych u żyw am y na p rzy k ła d , by w staw ić je d o jakichś zm ien n y ch
59
Rozdział. 3. Typy Stałe dosłowne lub w ted y , g d y w ystępują one w w yrażeniach ary tm ety czn y ch i = i + 5;
/ / powiększenie obiektu i o siatą 5
albo w ted y , g d y chcem y coś z n im i porów nać i f (rr. > 12)
Z w racam uw agę, że w tym miejscu ch o d zi n a m o stałe dosłow ne, czy li o sam zapis lic z b y (czy tekstu), a nie o jakiś obiekt, który ma a k u ra t taką w artość. P o prostu chodzi o sytuację, gdy dosłownie p iszem y w p ro g ram ie jakąś w artość (liczbę, napis). Z ap o zn am y się tu ze sposobam i zap isy w an ia takich stałych. U .l
Stałe będące liczbami całkow itym i Stałe takie zap isu jem y tak, jak d o tego przyw ykliśm y w szk o le 17 -3 3 0 1000 itd. Jeśli n ato m iast zap is stałej zaczn iem y od cyfry 0 (zero), to k o m p ilato r zro zu m ie, że zasto so w aliśm y zapis liczby w system ie ósem kow ym (oktalnym ). 010 - czyli dziesiątk o w o 1 * 8 + 0 = 8 014 - czyli dziesiątk o w o 1 * 8 + 4 = 1 2 091 - błąd, w zapisie ó sem k o w y m cyfra 9 jest n ied o p u szczaln a. Jeżeli stała z a c z y n a się o d 0x (zero i x) to k o m pilator u z n a , że w sto su n k u d o stałej zasto so w aliśm y zapis szesn astk o w y (heksadecym alny). 0x10 - czyli d ziesiątk o w o 1 * 1 6 + 0 =16 0 x al - czyli dziesiątk o w o 1 0 * 1 6 + 1 =161 0xff - czyli dziesiątk o w o 15 * 16 + 15 = 255 N ie m uszę ch y b a przypom inać, ż e w ystępujące w zap isie szesn astk o w y m znaki a , b , c , d , e , f oznaczają w z a p isie dziesiątk o w y m o d p o w ie d n io 1 0 , 1 1 , 12 , 1 3 , 1 4 , 15. W zapisie szesnastkowym można się posługiwać zarówno wielkimi literami X, A, B, C, D, E, F, jak i małymi. O czyw iście p o n iż sze instrukcje: in t o b ie k t; o b ie k t = ob i e k t = o b ie k t =
15; 0x f ; 017;
// przy użyciu zapisu dziesiątkowego // przy użyciu zapisu szesnastkowego // przy użyciu zapisu ósemkowego
pow odują, ż e w obiekcie znajdzie się taka sam a w artość.
Po co więc sobie utrudniać i pisać coś w zapisach innych niż dziesiątkowy? Pytanie jest tendencyjnie p o staw io n e. O tóż czasem u ż y c ie innego z a p isu jest w łaśnie ułatw ien iem sobie pracy. T ak się dzieje często, g d y ch o d zi n am nie o liczenie typu m atem atycznego, a le o u staw ian ie b itó w w k o m ó rce pam ięci. Ta dygresja je st przeznaczona d la w tajem niczonych, w ięc... ...jeśli to jest Twoje pierwsze spotkanie z programowaniem, to poniższe słowa mogą wydać Ci się niezrozumiałe.
60
Rozdz. 3. Typy Stałe dosłowne
Zatem: dlaczego dla ustawiania bitów w obiekcie - programiści wolą zapis szesnastkowy? Dlatego, że używając tego zapisu szesnastkowego wystarczy wyobrazić sobie pozycje żądanych bitów - a wtedy da się to, niemal automatycznie, szesnastkowo zapisać. Oczywiście trzeba do tego odrobinę wprawy. Gdyby chodziło o ustawienie samego bitu 9 i 10, wprawny programista od razu zapisze obiekt = 0x300;
a gdyby m usiał to samo zapisać dziesiątkowo, to potrzebowałby pewnie kartki papieru, by to sobie przeliczyć (768). C z y te ln ik ó w n ie w ta je m n ic z o n y c h , którzy n ie sp o tk ali się z szesn astk o w y m i ó se m k o w y m sy ste m e m liczen ia z a p ra sz a m te ra z d o p rzeczy tan ia zam ieszczo nego n a k o ń cu k siążk i d o d a tk u A (str. 1228).
Jeśli zapisujemy w programie stałą dosłowną typu całkowitego, to do jakiego konkretnie typu się ona zalicza? Stałe całk o w ite tra k tu je s ię ta k jak ty p i n t , ch y b a że rep reze n tu ją ta k w ielkie liczby, k tó re n ie zm ieściły b y się w i n t . W ó w czas stała taka jest ty p u lo n g . Z atem stałe dosłow ne:
Sł? Cflórt *0 i* ?>ń) v ■'.'foiSzBTieast: są ty p u i n t . M o żn a św ia d o m ie zm ienić ty p n a w e t niew ielkiej stałej - z ty p u i n t n a ty p l o n g . R obi się to p rzez d o p isan ie n a k o ń cu liczby: litery L (lu b 1) OL
• 200L
i',.
■.
‘
‘
M im o ż e liczby te w y starczająco d o b rze m ieszczą się w ty p ie i n t - d o p is a n a na końcu litera L sp ra w ia , ż e są ty p u lo n g . Osobiście zawsze używam tu wielkiej litery L, gdyż mała za bardzo przypomina jedynkę. Jeśli ch cem y , b y d a n a sta ła była ty p u u n s i g n e d , to sp raw i to d o p is a n ie na końcu litery u 277u
P rz y p ise k u m oże w y stą p ić razem z p rz y p isk ie m L 50uL
w ó w c z a s oznacza to, że d a n a stała m a być ty p u u n s i g n e d l o n g .
W O to p rz y k ła d zap isu ty ch stałych w p ro g ram ie: #include int m a i n O { int i :
f3$S9
61
Rozdział. 3. Typy Stałe dosłowne int k, n, m, j; i = 5; k = i + 010;
Uczyli i + 8
scd::cout << "k= " «
k «
std::endl;
m = 100; n = 0x100; j = 0100; std;:cout << "m+n+j= " <<
(m+n+j) «
std::cout
"
«
"Wypisujemy:
«
0x22
«
" " «
022
«
" " «
22 «
"\n";
"\n";
)
W wyniku wykonania na ekranie pojawi się k= 13 m+n+j= 420 Wypisujemy: 34 18 22 •
•
■
Z auw aż, ż e zastosow anie k tó reg o ś z zapisów (dziesiątkow ego, o k taln eg o , heksadecym alnego) jest tylko je d n y m ze sposobów p o w iedzenia, o jaką liczbę nam chodzi. K om puter i tak p rzetłu m aczy to sobie na swój w łasny sp o só b (binarny). T o tak, jakbyśm y rach m istrzo w i pow iedzieli czasem t r z y , czasem d r e i , czasem t h r e e .
$.4.2
Stałe reprezentujące liczb y zm iennoprzecinkow e Stałe takie zap isać m ożna na d w a sposoby. Pierw szy, to n o rm aln y zap is liczby z kropką d ziesiętn ą 12.3
3.1416
-1000.3
-1 2
.
D rugi zap is jest nazyw any notacją w ykładniczą. W zap isie tym w y stę p u e lite ra e, po której następuje w ykładnik p o tęg i o p o d staw ie 10 (tzw . mantysa). A zatem: 8e2 oznacza 8 *102 czyli 800 10.4e8 oznacza 10.4 * 108 czyli 1 040 000 000 5.2e-3 oznacza 5.2 * 10-3 czyli 0.0052 Stałe takie trak tu je się tak, jakby b y ły typu d o u b le . Warto o tym pamiętać, bo jeśli taka stałą będziesz próbował przypisać do obiektu typu f l o a t, to kompilator wypisze Ci ostrzeżenie, że stałą typ u d o u b le próbujesz wpisać do typu f l o a Ł, a typ ten ma mniejszą dokładność niż d o u b le . jeśli chciałbyś, by stała dosłow na (zm ien n o p rzecin k o w a) była typu f l o a t , (a nie d o u b le ) , to um ieść na jej końcu literę F. (M ałą lub w ielką) float x = 12.5F;
62
Rozdz. 3. Typy Stałe dosłowne
3.4.3
Stałe znakow e Stałe zn ak o w e są to stałe reprezentujące na przykład zn ak i alfanum eryczne. Z apisuje się je u jm u jąc d an y znak w d w a apostrofy 'a' ' 7'
-
o zn acza literę a o zn acza cyfrę 7 (cyfrę, nie liczbę)
Z naki w taki sposób zap isan e (takie znakowe stałe dosłowne) są ty p u c h a r . M ożna je w ięc p rzy p isać d o obiektów (zm iennych) typu c h a r . O to przykład: char znak; znak = 'A ' ;
O czyw iście w iesz n a pew no, że k o m p u ter nie potrafi p rzech o w y w ać w swojej pam ięci żadnej litery 'A '. M oże jednak przechow yw ać liczby. D latego w szyst kie litery alfabetu angielskiego i znaki specjalne zostały p o p ro stu po n u m e row ane. To ten n u m e r (kod) danego zn ak u jest p rzech o w y w an y w pam ięci kom putera.
Są różne sposoby numerowania (kodowania) znaków. Jednym z najbardziej popularnych jest chyba kod A S C II.^ Z tabelą kodów ASCII, czyli tym, jakimi liczbami reprezentowane są jakie znaki- spotkałeś się ju ż na pewno przy programowaniu w innych znanych Ci językach programowania. 1 1)
[czytaj „aski"] - jest to skrót od: American Standard of Codę Interchanged Information. Zestaw znaków ASCII nie zawiera jednak wszystkich możliwych liter ze wszystkich języków świata. To jakby najbardziej podstawowy zestaw. (Wdodatku z możliwych 256 znaków, tylko pierwsze 127 jest objęte standardem).
63
Rozdział. 3. Typy Stałe dosłowne
Znaki specjalne Są je d n ak ta k ie z n a k i, k tó ry ch nie d a się w p ro st u m ie śc ić m ięd zy apostrofam i. N ajczęściej d la te g o , że nie m ają o n e fo rm y "d ru k o w a n ej" - słu żą bow iem d o s te ro w a n ia w y p isy w a n ie m tekstu n a e k ran ie (d ru k a rc e ). N a p rzy k ład : przejście d o now ej s tro n y , ta b u lato r, z n a k n o w ej linii. Jeśli m u s im y je "n a p isa ć ' w p ro g ra m ie , to p o m a g a m y so b ie w ted y za pom ocą tzw . o d w ro tn e g o u k o śn ik a [ang. backslash]12. O b o k n ie g o staw iam y u m o w n ą literę, k tó ra p rz y p o m in a zn ac zen ie d a n e g o zn ak u . cofacz \b (ang. Backspace) n o w a stro n a \f (ang. Formfeed) \n n o w a linia (ang. N ew linę) \r p o w ró t k aretk i (ang. carriage Return) \t ta b u la to r p o zio m y (ang. Tabulator) \v ta b u la to r p io n o w y (ang. Vertical tabulator) \a sy g n a ł d ź w ięk o w y (ang. Alarm) O to, jak d o o b ie k tu o n azw ie z z p rz y p isu je m y je d n ą z p o w y ż sz y c h w artości: c h a r z z = ' \ n 1; M im o że m ię d z y ap o stro fam i w id z im y kilka z n ak ó w , z a p is re p re z e n tu je je d e n znak. C z y ta m y to tak: D w a najb ard ziej z e w n ę trz n e ap o stro fy m ó w ią n am , że m am y d o czy n ien ia ze stałą d o s ło w n ą b ęd ącą z n a k ie m . W śro d k u z a ś c z y ta m y \ - o d w ro tn y u k o śn ik : aha, b ę d z ie to coś n ie zw y k łeg o . P o tem n astęp u je np. lite ra n. Skoro coś n ie z w y k łe g o , to p a trz y m y , co na liście niezw y k ło ści o zn ac za litera n - je st to sk ró t o d new linę (n o w a linia). Proste. W sumie oznacza to, że w naszej instrukcji chcemy, by w obiekcie o nazzuie z z znalazł się znak sterujący mogący spowodować przejście do nowej linii. P oniew aż d la z a p isu specjalnych z n a k ó w sterujących w y d r u k ie m (czy w y p is y w aniem na ek ran ) p o m ag aliśm y so b ie stosując z n a k i ta k ie , jak a p o s tro fy , o d w ro tn a k resk a u k o śn a, w ięc p o ja w ia się k ło p o t jak z a p is a ć stałą z n a k o w ą będącą sam y m zn a k ie m ap o stro fu ? U jąć apostrof w d w a in n e ap o stro fy ? char c = ’ "
H
;
Nie. K o m p ilato r p o d ejrz ew ałb y b łą d , m u sim y d o d a ć k re s k ę u k o śn ą: char c = ’
;
1/apostrof
Z apis ten ro zu m iern y tak: d w a z e w n ę tr z n e ap o stro fy - cz y li w e w n ą trz je st z n a k . Potem o d w ro tn y u k o śn ik , czyli „ u w a g a !" , a potem a p o stro f. O d w ro tn y u k o ś n ik (bekslesz) je st w ty m p rz y p a d k u o strzeż en iem , bo z n a k a p o s tro fu jest ju ż r a z w tej konstrukcji u ż y w a n y w in n y m z n a c z e n iu (o g ran iczn ik ). 2)
backslash - [czytaj: „bekslesz"!-
64
Rozdz. 3. Typy Stałe dosłowne P oniżej w idać, że p o d o b n ie radzim y sobie w p rzy p ad k u konieczności zapisu o d w ro tn eg o ukośnika, cu d zy sło w u i w kilku innych p rzypadkach. 'W ' ' \ '' ' \ "' •\ ? ' ' \ 0'
-
o d w ro tn y ukośnik ap o stro f cu d zy słó w p y tajnik - nuli, czyli zn ak o kodzie 0
W śró d zn ak ó w (już niealfanum erycznych - bo nie da się ich zapisać) jest jeszcze je d e n b ard zo w yjątkow y. Jest to znak o k o d zie 0 zw an y znakiem n u li N a liście w id z im y sposób jego z ap isu . Stałe zn ak o w e m ożna tak że zapisyw ać b ezp o śred n io - podając m ię d z y ap o stro fami liczbow y kod z n a k u , zam iast sam ego z n a k u . Kod znaku m usi być liczbą w z a p isie ósem kow ym lu b szesnastkow ym . N p . w kodzie ASCII litera a rep rezen to w an a je st p rzez liczbę 97. T o d lateg o p o n iż sz e zap isy są ró w n o w ażn e: ' a ' - to sam o, co ó sem ko w o ' a ' - to sam o, co szesn astk o w o T ak to w y g ląd a w instrukcji:
\ 0141 \ x 61
c h a r c * ' \ x 6 1 ';
Stałe znakowe typu wchar_t S tałe zn ak o w e z a p isy w a n e tak, jak to w id zieliśm y w tym p arag rafie, są ty p u c h a r . Jeśli chcielibyśm y, b y były one typu w c h a r _ t - ("znak szeroki'), to taką sta łą w y starczy p o p rz e d z ić literą L
L'a' To n ato m iast, co w id z im y m ięd zy dw om a ap o stro fam i znaku szero k ie g o z a le ż y od im plem entacji. M oże tam znaleźć się w ięcej niż jedna litera. W końcu, jeśli jakiś alfabet (np. chiński) ma bardzo wiele znaków - to me wyobrażasz sobie chyba, że można je wszystkie opisać za pomocą dwudziestu kilku znaków alfabetu angielskiego. O to, jak p o słu żen ie się ta k ą stałą znakow ą w y g lą d a w e fragm encie p ro g ra m u : wchar_t litera; litera = L ' a ';
W d ru g ie j linijce w id zim y p rzy p isan ie. O b iek to w i o n azw ie l i t e r a n a d a w a n a jest w a rto ść o d p o w iad ająca szerokiej stałej zn ak o w ej. M o że to w y d ać się zask ak u jące, ale typy p rz e z n a c z o n e do p rz e c h o w y w a n ia z n a k ó w należą d o g ru p y ty p ó w całkow itych, czyli tym sam y m d o ty p ó w ary tm ety c zn y ch . O z n acza to, że m ożna n a nich w y k o n ać o p eracje m a tem a ty czn e . Z atem m o ż liw e jest takie w y ra ż e n ie •b ' + 5
65
Rozdział. 3. Typy Stałe dosłowne
Jak się łatw o d o m y śla ć w artością tego w y rażen ia jest litera b ęd ąca w alfabecie o 5 pozycji d alej n iż litera ' b '. (To litera ' g ' ).
4.4
Stałe tek stow e, napisy, albo po prostu stringi W językach p ro g ra m o w a n ia b ard zo często posługujem y się stały m i tekstow ym i będącym i ciąg am i zn ak ó w . Z w an e są o n e czasem n ap isam i, czasem łańcucham i znak ó w . S potkaliśm y się ju ż z takim i stałym i. O to stała te k sto w a z pierw szego pro g ram u : "Witamy na pokładzie"
W języku a n g ielsk im stała tekstow a n a z y w a się to krótko: strin g . W całej tej k siążce p ie rw o tn ie p o słu g iw ałem się nazw ą „ciąg zn ak ó w ". N azw a ta dziesiątki razy o d m ien ian a była p rz e z w szystkie p rz y p a d k i, co nie zaw sze b yw ało zręczne. W reszcie zrez y g n o w ałem . N ie zn am b o w iem program isty, który by na to „co ś" m ów ił inaczej jak: string. O statn io p recy zu je się to bardziej m ó w ią c C-string. N ale ż y ro zu m ieć to jako "string, w ro z u m ie n iu z języka C". A zatem :
cudzysłów . O to p rzy k ład y : "taki string" "Pożar na pokładzie" "Alarm 3 stopnia"
P oniew aż strin g je st ciągiem znakózu, w ię c obow iązują p o d o b n e zasad y , jak o pisan e p rzy stałych znakow ych: Jeśli ch cem y w tekście (strin g u ) zasto so w ać zn ak nowej linii, to w ystarczy w ż ą d a n y m miejscu napisać: \ n . "Pożar \n na pokładzie"
Z dziw iłeś się d la czeg o nie m a teraz d w ó c h apostrofów p o o b u stro n ach z n a k u \n ? N ic w ty m d z iw n e g o - nie m a ta k że ap o stro fó w o b o k liter: P o z itd. C -strin g i są w pam ięci p rz e c h o w y w a n e jako ciąg z n a k ó w (liter), a na sa m y m końcu tego ciągu d o d a w a n y jest z n a k o k o d zie 0, czyli zn ak nuli. Tak k o m p ilato r o zn ac za sobie koniec C -strin g u . W program ie o g ran iczn ik iem C -stringu są zn a k i c u d zy sło w u P o n ie w a ż cudzy słó w m a takie szczególne z n ac zen ie d la C -stringu, d la te g o nie m o ż n a ju ż g o użyć cu d zy sło w u d o d atk o w o w e w n ą trz C -stringu. W p r z y p a d k u stały ch znakow ych p ro b lem ten m ieliśm y z ap o stro fam i. Do p o m o c y m am y je d n a k identyczny ch w y t z o d w ro tn y m u k o śn ik iem : std::cout
«
"Lecimy promem \"Columbia\" nad Oceanem Spokojnym";
66
Rozdz. 3. Typy Stałe dosłowne co n a e k ra n ie p o ja w i się jako Lecimy promem "Columbia" nad Oceanem Spokojnym
M ó w iliśm y k ie d y ś, ż e w języku C ++ w p raw ie k ażd y m m iejscu instrukcji m oż n a p rz e rw a ć p is a n ie , przejść d o n a stę p n e j linii i k o n ty n u o w a ć instrukcję. T o sło w o „ p r a w ie " d o ty c zy m ię d z y in nym i pisan ia C -strin g ó w . Tutaj nie m o ż n a p rz e rw a ć p isan ia. Jeśli k o m p ila to r zobaczył w linijce cudzysłów o tw ie ra jący C -s trin g , to m u si w tej sa m e j lin ii znaleźć c u d z y s łó w zam ykający.
Co zrobić jeśli string jest tak długi, że nie mieści się w jednej linijce? Jest n a to p ro s ty sp o só b . Spójrz pon iżej: w kilku lin ijk ach zap isaliśm y tu C -strin g , k tó ry k o m p ila to r p o tra k tu je ja k o jed n ą całość. "Cały ten tek" "st jest traktowa" "ny jako jeden dlu"
"gi string"
Jak w id ać, je st tu tek st, k tó ry ciąg n ie się p rz e z trzy linijki. P o szczeg ó ln e "człony są czterem a s a m o d z ie ln y m i C -strin g am i. M ięd zy p o szcze g ó ln y m i członam i nie m a ż a d n y c h p rz e c in k ó w , niczego - o p ró c z białych z n ak ó w . T e cztery czło n y (z a p isa n e w trzech linijkach) k o m p ilato r p o tra k tu je tak, jakbyś m y z a p isa li je jako je d e n d łu g i strin g o treści: "Cały ten tekst jest traktowany jako jeden długi string"
Z apam iętaj: B ezpośrednio przylegające do siebie C -stringi kom pilator p o łą czy i potraktuje jako jeden.
B e zp o śred n io - to z n ac zy , że m ięd zy C -strin g a m i są ty lk o b ia łe zn ak i. W y n ik a st^ d p ro sta rada: Jeśli p isząc C -string (jego treść) zbliżyłeś się d o p raw ej k raw ęd zi s tro n y e k ra n u w sw o im e d y to rz e , a nie ch cesz p isać dalej
bo nie lubisz jak fragmenty Twojego programu są tak szerokie, że "chowajc się" za prawym brzegiem ekranu, to m o ż e sz (n aw et w ś ro d k u w łaśn ie p isa n e g o w y ra z u ) p o staw ić c u d z y s łó w zam ykający C -strin g w tej linii, p o czy m przejść w d o w o ln e miejsce n astę p n e j linii, p o staw ić z n o w u cu d z y słó w (o tw ierający ciąg d a lsz y C -strin g u ) i p isać d alej p rz e rw a n y w y raz ' tek st. K ończysz go o czy w iście cu d zy sło w em . Z a m ia st pisać: std::cout
<< "Moj drogi Kapitanie, który " « "podzespół sprawdzić ?
67
Rozdział. 3. Typy Stałe dosłowne m o ż n a zapisać: std::cout << "Mej drogi Kapitanie, "pcdzespol sprawdzić ?
który
Ja k w id ać, w d r u g im sposobie nie p o trz e b n e jest p o w tó rzen ie o p e ra to ra « .
Dla wtajemniczonych: Jakiego typu jest mój C-string? Aby dobrze zrozumieć poniższych kilka linijek, przydałoby się. wiedzieć, co to jest tablica. Nie mówiliśmy jeszcze o tym, więc nie przerażaj się jeśli coś jest niezrozumiałe. N ie d a w n o d o w ie d z ie liśm y się, że jeśli k o m p ilato r u jrzy w n aszy m p ro g ram ie s ta łą d o sło w n ą 0, to u zn aje, iż jest o n a ty p u i n t . Tu m o ż e m y zad ać p o d o b n e p y ta n ie: Jeśli w p ro g ra m ie jest p o n iż szy C -strin g -
Fi V
’- * • J -
1
- V ] tł*
"AbCd"
to jakiego jest on ty p u ? O tó ż w m yśl s ta n d a r d u , taki C -string je st ty p u c o n s t c h a r [5 ] . Skąd ta p ią tk a ? O czyw iście m a m y tu cztery z n ak i, p o których m u si n a stą p ić kończący te z n a k i zn ak nuli. W s u m ie w ięc jest ich pięć. P o co jednak ten p rz y d o m e k c o n s t ? C h o d z i o to, że g d y k o m p ila to r analizuje n a s z p ro g ram , to m u s i g d zieś sobie w p am ięci z a re z e rw o w a ć d ziałk ę, na której u m ieści te pięć z n a k ó w . T ym czasem w p ro g ra m ie łatw o się d o w ie d z ie ć , g d zie ta jego d ziałk a jest. (N ie m ów iliśm y o ty m jeszcze, ale to n a p r a w d ę proste). Skoro p ro g ra m ista z n a to m iejsce - m ógłby chcieć te zn ak i p o zm ien iać. N ie jest to jeszcze tak trag iczn e. C zęsto jed n ak p rz y c h o d z i m u och o ta d o te g o C -stringu coś d o p isać. To jest ju ż nieszczęście. 4 To tak, jakby - mając gdzieś, w dzielnicy willowej, małą willę na działce budowlanej - nagle zdecydować tę willę przebudować na duży lotniczy hangar. Przecież obok stoją inne wille. Budowa hangaru spowoduje ich zburzenie. Gdy nieświadomy tego programista podejmie rozbudowę - zniszczy sąsiednie obszary pamięci - i program najczęściej się zawiesi. A by tego u n ik n ąć, w s ta n d a rd z ie d o d a n o p rz y d o m e k c o n s t d o ty p u stałej dosłow n ej ty p u C -strin g . D zięki tem u p rz y d o m k o w i w sz e lk ie ro b o ty zie m n e na tej działce są za b ro n io n e . Jeśli zaś c h ce m y m ieć C -strin g , k tó ry b ę d z ie m y ro zb u d o w y w a li - to n ie m oże być stała dosłowna ty p u C -strin g , a le p o w in n iśm y so b ie zdefiniow ać obiekt (zm ienną) ty p u C -strin g . K rótko m ó w ią c m u s im y sobie o d p o w ie d n ią d z ia łk ę b u d o w lan ą kupić. C S5
Z apam iętaj
Jeśli p ro g ram o w ałeś w e w cześniejszych w ersjach C ++, to je st to d la C iebie now ość! D aw niej teg o c o n s t w tym m iejscu n ie było...
68
Rozdz. 3. Typy Typy złożone
C-stringi ze znaków szerokich Z obaczyliśm y tu C-stringi, czyli łań cu ch y znaków ty p u char. W iem y jednak, że m ogą być też tak zw an e znaki szerokie, czyli obiekty typu wchar_t. A by po inform ow ać kom pilator, że chcem y, by dany C-string był złożony w łaśnie z takich zn ak ó w , należy poprzedzić ten C-string wielką literą L. L"tekst międzynarodowy"
Ta stała d o sło w n a jest typu
3.5
const wchar_t [21].
T y p y z ło ż o n e M ów iąc o brazow o: są to typy, których nazw a złożona jest z n azw y innego typu (prostszego), o ra z z jednego z czterech operatorów , k tóre zobaczym y za chwilę. W spom inałem ju ż o tym wcześniej: jeśli ja jk o byłoby typem prostym, to "pojemnik na 6 ja jek" byłby typem złożonym. Występuje w jego nazwie słowo "jajko"- natomiast pojemnik na 6 sztuk" to jakby operator typu złożonego. Podobnie typem złożonym jest "sokow irów ka do jabłek". W języku C++ ty p y złożone to na p rz y k ła d tablica (obiektów jakiegoś typu), czy w skaźnik d o ob iek tó w (jakiegoś ty p u ). Po co nam tak ie typy? M ożem y m ieć kilka „luźnych" o b iek tó w typu int, ale często korzystniej jest je pow iązać w tablicę obiektów ty p u int. Przecież lu ź n e g o d zin y odjazdów pociągów w y g o d n iej pow iązać w tablicę zw aną ro zk ład em jazdy. Tablica obiektów typu int jest typem zło żo n y m , w y korzystu jącym prostszy typ fu n d am e n taln y int. N ie m usisz się jed n ak tą całą sy stem aty k ą przejm ow ać, tak jak m echanik nie m usi m yśleć czy jego tokarka jest ty p em złożonym p o ch o d zący m od... nc w łaśnie, czego? T yp złożony tw orzym y z nazw y ty p u p ro stszeg o (który w ykorzys tują) i operatora deklaracji typu złożonego. Jest to p ro stsze niż się w ydaje. in t a; i n t b [ 10 ] ;
/ / obiekttypu i n t / / tablica 10 obiektów typu i n t (10-elementowa)
Co to jest tablica, tłum aczyć chyba nie trzeba - taki typ o b iek tu istnieje n aw et w języku BASIC czy FORTRAN. Jeśli je d n ak nie w iesz, nie m a problem u: tablicom pośw ięcim y specjalny rozdział. Teraz w y m ien ię jeszcze inne o p e ra to ry d o tw o rzen ia obiektów typów złożonych. Jed n ak nie przerażaj się. W szystkie staną się jasn e w najbliższych rozdziałach.
69
Rozdział. 3. Typy Typy złożone
Oto lista operatorów, które umożliwiają tworzenie obiektów typów złożonych: [] * O &
-
tablica o b ie k tó w d an eg o ty p u , w sk a ź n ik d o p o k azy w an ia na o b ie k ty d an e g o ty p u , funkcja zw racająca w arto ść d a n e g o typu, referen cja (przezw isko) o b iek tu d a n e g o ty p u .
N ie m ó w iliśm y jeszcze o tych typach, b ę d z ie o nich m o w a w o d p o w ied n im czasie* T u ta j w y jaśn im y w ięc k ró tk o , że: Tablica - to in aczej m acierz, alb o w e k to r o b iek tó w d a n e g o ty pu.
tu dobrą ilustracją jest tablica godzin - zwana rozkładem jazdy. W sk aźn ik - to o b iek t, w k tó ry m m o ż n a um ieścić a d re s jakiegoś innego obiektu w p am ięci. Funkcja - cz y li p o d p ro g ra m , k tó ry w y k o n a p e w n e z a d a n ie . Jest to za p e w n e z n a n e C i z innych ję zy k ó w p ro g ra m o w a n ia .
Na przykład funkcja "kopanie ziem niaków". Typem prostym jest "ziemniak", a "kopanie" to nazwa pewnego działania (akcji, funkcji). ♦$» R eferenqa - to ja k b y p rzezw isk o ja k ieg o ś o b ie k tu . D zięk i referencji na tę sam ą z m ie n n ą m o ż n a m ów ić u ż y w a ją c jej d ru g iej n a z w y . A o to p rzy k ła d y ty p ó w fu n d am e n taln y ch : i n t a; s h o rt i n t b; f l o a t x;
// dcf. obiektu typu i n t // dcf. obiektu typu s h o r t // def. obiektu typu f l o a t
O to p rz y k ła d y ty p ó w zło żo n y ch , w y k o rz y stu ją c y c h te p ro s te typy: i n t t [1 0 ] ; f l o a t *p; c h a r f u n c () ;
// tablica 10 elementów typu i n t // wskaźnik mogący pokazać na jakiś // obiekt typu f l o a t // funkcja zwracająca (jako rezultat // wy Konania) obiekt typu c h a r
Boję się, że pojęcia te n a razie m o g ą n ie być z b y t jasne. N ie p rz e jm u j się, w najbliższych ro z d z ia ła c h szczeg ó ło w o o tych c iek aw y ch s p ra w a c h p o ro zm a w iam y . B ardzo zachęcam C ię , drogi czytelniku, abyś osw oił się z d e kla ra cja m i typów złożonych, a w p rzyszłości nauczył się je odczytyw ać. Da Ci to ogrom ną sw obodę w poruszaniu się po królestw ie C ++. Jeśli są ludzie, którzy nie lubią C i C ++, to dlatego, że sta ją b e zrad n i, gdy zobaczą takie deklaracje. 11 ■
D o ćw iczeń w o d c z y ty w a n iu deklaracji je szc ze w ie lo k ro tn ie p o w ró c im y .
70
Rozdz. 3. Typy Typ v o i d
T y p void
3.6
W deklaracjach ty p ó w złożonych m oże się pojawić słow o v o id . [Ang. void próżny]. S łow o to stoi w miejscu, gdzie norm alnie staw ia się nazw ę typu. I tak: void *p;
•
tutaj oznacza to, że p jest w skaźnikiem d o pokazyw ania na obiekt nieznanego typu. (O tym, do czego taki w skaźnik m ożt się przydać, pow iem y sobie w rozdziale o w skaźnikach).
void funkcja();
•
deklaracja ta m ów i, że w artości.
funkcja nie b ęd zie zw racać żad n e
Tak n a p ra w d ę , ty p v o i d jest ty p em fundam entalnym . N ie m oże być jednał* obiektu ty p u v o i d . Słow o v o id m o że w ystąpić tylko jako ty p prosty w deklara cji typu zło żonego. Tutaj, w instrukcji: void *p;
dek laru jem y w sk aźn ik , który m o że pokazyw ać na coś, czego ty p u nie chcemy bliżej określić (nazw ać). Z am iast nazw y tego tajem niczego ty p u umieszczam y słow o v o id .
3.7
Z a k re s w a ż n o ś c i n azw y o b ie k tu , a czas ż y c ia obiektu W iem y już, jak zdefiniow ać lub zad ek laro w ać obiekt jakiegoś typu. P rzykłado w o dla obiektu typu i n t robi się to instrukcją: i n t m; Zajmijmy się te ra z zakresem w ażności n azw y tak zd efin io w an eg o obiektu czasem jego życia.
Czas życia obiektu ♦♦♦ to o k res od m om entu, g d y zostaje on zd efin io w an y , (definicje p rzy d ziela m u miejsce w pam ięci) - do m o m en tu , g d y przestaje or istnieć, (a jego miejsce w pam ięci zostaje zw olnione). -.
,
£;
,
•
...v;
'•
.
.
.
.
ir
v
■
• - >
•-
-
'
Zakres ważności nazwy obiektu ♦♦♦ to ta część program u, w której nazw a obiektu jest z n a n a , czyli obiekt jes d o stę p n y (jeśli zw racam y się do niego używ ając jego nazw y).
71
Rozdział. 3. Typy Zakres ważności nazw y obiektu, a czas życia obiektu
Jaka jest różnica między tymi pojęciami? T aka, że w jakim ś m om encie obiekt m o że istnieć, ale nie b y ć dostępny. To dlateg o , że np. zn ajd u jem y się chw ilow o p o za zakresem w ażn o ści jego nazwy. Z ależn ie od tego, jak zdefiniujem y lu b d ek laru jem y nazw ę, z a k re s jej ważności m o że być ró żn eg o ro d zaju .
.7.1
Zakres: lok alny Z ak res w ażności jest lokalny, gdy św iad o m ie ograniczam y g o d o kilku linijek p ro g ram u . Pisząc p ro g ra m m ożem y w d o w o ln y m m om encie z a pom ocą dw óch klam er >5Ł-. --
prtwess&r
i ■.) a! ■»
?>v.j
** % ' - r, r
-u
$.'*6 1.
u tw o rzy ć tzw. blok. (Po klam rze kończącej blok nie trzeb a sta w ia ć średnika). Z defin io w an e w ta k im bloku nazw y m ają zak res w ażności o g ran iczo n y tylko d o tego bloku. Po p ro stu poza tym blokiem n azw y te n ie są z n an e . # in c lu d e < io s tre a in > i n t m ain () 1 // tu r o b im y ja kieś o b liczen ia
{ i n t x; 1
II
< -
otwieramy lokalny blok
U definiujemy jakieś zmienne // pracujemy na tych zmiennych I I +- zamykamy lokalny blok //p o z a b lo kiem lokalne o b ie k ty sq j u ż n ie zn a n e
1
***
N azw a zm iennej x je st zn an a od m o m en tu , g d y ją zd efin io w aliśm y - do linijki, g d z ie jest klam ra ) k o ń cząca jej lokalny blok.
.7.2
Zakres: blok funkcji Jak na razie, mówiliśmy tylko ofunkcji o nazwie ma in . W jednym z następ nych rozdziałów dowiemy się, że w programie oprócz funkcji m a in — zwykle jest jeszcze wiele innych funkcji. W iem y już, że funkcję (np. m a in ) o tw iera n a w ia s k lam ro w y {, a kończy n aw ias klam ro w y }. Te d w a naw iasy w y zn aczają blok funkcji - m ię d z y nim i są instrukcje składające się na tę funkcję. Z akres w ażności o g ran iczo n y do bloku funkcji m a etykieta. Z n a c z y to, że jest ona zn an a w całej funkcji, naw et w tych linijkach funkcji, k tó re tę ety k ietę poprzedzają. U w aga. Z faktu, ż e etykieta ma zak res w ażności funkcji, w y n ik a p ro sty w niosek:
72
Rozdz. 3. Typy Zakres ważności nazwy obiektu, a czas życia obiektu
Nie można instrukcją
goto przeskoczyć z wnętrza jednej funkcji do wnętrza
W szystkie ety k iety danej funkcji są ju ż poza tą funkcją n iezn an e. Z zew nątrz te funkcji nie m o żn a w ięc do nich skoczyć.
3.7.3
Zakres: obszar pliku N a razie n asze krótkie program y m ieściły się zw ykle w jed n y m pliku teksto w ym . W p rzy szło ści będziem y pisać d łuższe, które dla w y g o d y rozm ieścim y w kilku plikach. Jeśli w jednym z nich, na zewnątrz jakiegokolwiek bloku (także bloku funkcji), (oraz poza tzw . przestrzenią nazw) zadeklarujemy jakąś nazwę, to mówimy wówczas, że taka nazwa jest globalna. Ma ona zakres ważności pliku.
O to p rzy k ład : float fff; main()
{
/ / nazwa fff jest globalna
I I ...
Jednakże ta k a n azw a nie jest od ra z u autom atycznie z n a n a w innych plikach naszego p ro g ra m u . Jej zakres w ażności ogranicza się ty lk o do tego pliku, w którym ją zd ek laro w aliśm y . I to w d o d a tk u jedynie od m iejsca deklaracji, dc końca pliku.
3.7.4
Zakres: obszar klasy To na razie tajem nica. O tym szczegółow o p o ro zm aw iam y w rozd ziale poświęć conym klasom .
3.7.5
Zakres określony przez przestrzeń nazw P rzestrzenie n azw w p ro w ad z o n e zo stały do języka C++ sto su n k o w o n ied aw no. Jest to b a rd z o pożyteczne n arzęd zie. Dzięki nim , m o żn a w prow adzić p ew ien p o rz ą d e k w śró d nazw. Jakby zg ru p o w ać je w ro d z in y . P am iętasz, ż e p rz e d chw ilą ro zm aw ialiśm y o n azw ach globalnych - czyi: znanych w cały m p lik u tekstow ym . Jeśli w d a n y m pliku, w którym p iszem y w łaśn ie p ro g ram , chcem y skorzystać i jakiejś biblioteki, to m usim y n ajpierw zam ieścić deklaracje n a z w elem entów te biblioteki (n p . o b iek tó w czy funkcji). Po tych deklaracjach (czyli w yjaśnienie k o m p ilato ro w i co zn aczy dana n azw a) m o żem y już k o rzy stać ze skarbnicy którą n iew ątp liw ie jest biblioteka. N a z w y elem entów tej biblioteki stały sic bow iem z n a n e w d a n y m pliku.
Rozdział. 3. Typy Zakres ważności nazw y obiektu, a czas życia obiektu
73
M o ż e m y je d n a k p o trz e b o w a ć u ży ć także d ru g ie j biblioteki - dostarczonej nam p rz e z z u p e łn ie in n e g o a u to ra , n a p rz y k ła d p rz e z inną firm ę pro g ram isty czn ą. T rz e b a w ięc w te d y u m ieścić w n aszy m p lik u ró w n ież deklaracje nazw z tej d ru g ie j biblioteki. M o ż e m y też p o trze b o w a ć trzeciej, czw artej itd. W y o b ra ź sobie te ra z, ż e p rz e z p rz y p a d e k w jakiejś bibliotece je st u ży ta nazw a id e n ty c z n a jak n a z w a , k tó rą m y sam i w y m y śliliśm y w n aszy m program ie. P o w sta je p ro b lem : k o n f lik t nazw . N ie m a rad y , m u sim y w te d y zm ienić tę n a sz ą n azw ę n a nieco inną. P oniew aż n a sz p ro g ra m u ż y w a tej n azw y w w ielu m iejscach, w ięc trzeb a przejrzeć cały p ro g ra m - ale w ko ń cu je st to w ykonalne. Z m ien iam y w ięc w sz y stk ie w y stąp ie n ia tej naszej n a z w y i n ie m a więcej ko n flik tu nazw . N ie z a w sz e je d n a k ta k ie p ro ste ro zw iąz an ie jest to m ożliw e. P rzecież konflikt m o ż e się pojaw ić m ię d z y n azw am i z d w ó ch ró żn y ch bibliotek. T ego ju ż nie da się tak łatw o n ap raw ić , b o przecież najczęściej nie m a m y d o stę p u d o tekstu ź ró d ło w e g o tych b ib lio tek . T ak w łaśn ie w y g lą d a ła rzeczyw istość w czasach , g d y n ie by ło n arzęd zia z w a n e g o p rz e strz e n ią nazw '. Co w ted y robili a u to rz y bibliotek, b y un ik ać k o n flik tu n azw ? A n o - u ż y w a li d łu g ich i sk o m p lik o w a n y ch n a z w , bo w ted y sz a n s a p o w tó rzen ia się tak iej sam ej n a z w y w innej bibliotece m aleje. S zan sa maleje, ale nie zn ik a... W d o d a tk u p ro g ra m ista k o rzy stający z d anej b ib lio tek i m u si w sw o im p ro g ram ie z a w sz e u ż y w a ć w łaśn ie tych d łu g ich i sk o m p lik o w a n y ch n azw . Słow em , ro z w ią z a n ie n ie jest d o b re Z a n im w y m y ślim y jak to zrobić lepiej...
Zastanówmy się, dlaczego właściwie mamy te kłopoty P ro b lem polega na ty m , że jeśli już d e k la ro w a liśm y ja k ąś n a z w ę np. biblioteczną, to staw ała się o n a z n a n a i już. W ch o d ziła jak b y d o o b sz a ru n a z w "znanych". Potem , g d y chcielibyśm y p o słu ży ć się in n ą b ib lio tek ą, a b y ła w niej n a z w a id en ty czn a, to ta d r u g a nazw a ró w n ież m iała po p ro s tu w ejść d o o b szaru n a z w "znanych i już". P rzy p o m in a m i to jakby tak i problem : T w o rz y sz e d y to re m jak iś d o k u m e n t te k sto w y , a potem za p isu je sz go na d y sk u w g łó w n y m k atalo g u (p o d n a z w ą na p rz y k ła d życiorys). G d y b y Twój kolega ju tro chciał n ap isać in n y d o k u m e n t i p rz e z p rz y p a d e k d a ł m u ta k ą sam ą n azw ę, to n ie m oże g o b e z k a rn ie zap isa ć na d y s k u w g łó w n y m k atalo g u , bo tam taki d o k u m e n t ju ż jest. K o n flik t nazw . W id zisz już ro zw iązan ie? A leż oczyw iście - w łaśn ie d la te g o na d y s k u m am y nie ty lk o jeden katalo g g łó w n y , ale w y m y ślo n o też p o d k atalo g i. Ja z a p isz ę m ój d o k u m e n t, mój życiorys w jed n y m p o d k a ta lo g u , a mój k o leg a sw ó j życiorys w innym . N ie m a zn aczen ia, ż e d o k u m e n ty m ają ta k ie sam e n a z w y , s k o ro b ę d ą w ró żn y ch podkatalogach. Jeśli k to ś będzie chciał w y ciąg n ąć jakieś d a n e n a m ój te m at, p o w in ie n się o d n ieść do pliku o n a z w ie życiorys b ęd ąc y m w "m oim " k a ta lo g u . Jeśli zaś
74
Rozdz. 3. Typy Zakres ważności nazwy obiektu, a czas życia obiektu ch o d zić b ęd zie o d a n e na te m at m o jeg o kolegi, to - ab y p o z n a ć jego życiorys trzeb a p o d a ć n a z w ę jego p o d k a ta lo g u i nazw ę jego d o k u m e n tu życiorysow ego. To w sz y stk o jest z n a n e se k re ta rk o m u ży w ający m k o m p u te ry - od lat.
Jak tę ideę przenieść do programowania w C++ ? O tóż a u to r b ib lio tek i stara się, a b y jego n azw y nie b y ły - jak dotychczas - "w g łó w n y m k a talo g u ", ale w y m y śla sobie (tw orzy) "p o d k atalo g ", g d zie um ieść sw oje n a z w y . N a p isa łe m "p o d k atalo g " - w cud zy sło w ie, b o nie jest to p o d k a ta log na d y s k u , ty lk o jakby w jego te a trz e w y o b raźn i. T ak i "podkatalog" nazyw a się p rz e s trz e n ią n azw . P o d k a ta lo g o w i tem u , tej p rz e s trz e n i nazw , au to i biblioteki d a je ja k ą ś tam n azw ę. (Z w y k le m a o n a coś w sp ó ln e g o z n azw ą jegc biblioteki). N a stę p n ie a u to r biblioteki ok reśla, k tó re n azw y z jego b ib lio te k i m ają być w te p rzestrzen i. To jest moment zapisania życiorysu do odpowiedniego katalogu na dysku. Jeśli te ra z m y ch cem y się p o słu ży ć tą biblioteką o w eg o p ro g ra m isty , to: •
p o p ierw sze, w n aszej części p ro g ra m u n ajp ierw oczyw iście m u sim y zam ieścić d eklaracje n a z w p o trze b n y ch n arr sk ła d n ik ó w tej b ib lio tek i,
•
p o d ru g ie , w ty c h n aszy ch in stru k c jach , g d z ie m a m j k o rzy stać z tej b ib lio tek i - n a z w y jej s k ła d n ik ó w p o p rz e d z a m ) specyfikacją p rz e s trz e n i n azw , d o k tó rej a u to r biblioteki dan e n a z w ę "zapakow ał".
Może się zdarzyć, że to my sami chcemy umieścić jakieś nazwy w jakiejś "naszej" przestrzeni nazw To b a rd z o ła tw e. O to ilustracja. Z o b ac zy m y tu , jak s p ra w ić , b y n az w y kilku o b ie k tó w g lo b a ln y c h zn alazły się w p rz e s trz e n i n azw o n a z w ie s y m f o n ia . P o p ro s tu z a m ia s t d efin io w ać te o b ie k ty g lo b aln e tak: int double
x; ddd;
U jm u jem y je w b lo k , k tó ry za c z y n a się o d sło w a k lu c z o w e g o n a m e s p a c e , pc k tó ry m n a stę p u je w y b ra n a p rz e z n a s n azw a p rz e strz e n i. namespace symfonia {
int x; double ddd;
W id z im y tu , ż e w e w n ą trz bloku u m ie sz c z o n e są d e fin icje o b ie k tó w . Równie d o b rz e m o g ły b y ta m być d e k la ra q e . C h o d zi p rz e c ie ż o p o w ied zen ie k o m p ila to ro w i, ż e to d a n a nazwa m a być w p ew ien s p o s ó b "chroniona" p rzed n ie p o ro z u m ie n ia m i.
75
Rozdział. 3. Typy Zakres ważności nazw y obiektu, a czas życia obiektu
Jeśli w staw iliśm y n a z w ę x d o p rzestrzen i n a z w symfonia, to od tej pory do n a z w y x m u sim y się o d n o sić jako s y m fonia: :x, (a do n azw y ddd jako s y m f o n i a : :ddd).
1® “
C iek aw o stk a: T aki blok w staw iający n az w y d o p rzestrz en i n azw s y m f o n ia m o ż e sz w p ro g ram ie u ż y ć jeszcze kilk ak ro tn ie. C zy li n a w e t z a ch w ile m oże się p o w tó rzy ć: namespace symfonia
{
,
. . .
char double
znak_zorro; współczynnik;
1 S p ra w ia to, że d o p rz e s trz e n i nazw s y m f o n i a d o d a w a n e są d w ie n astęp n e n a z w y . Inaczej m ów iąc: | D aną p rz e s trz e ń nazw m ożem y "zalu d n iać" sto p n io w o . Dla w tajem n iczo n y ch : W bloku n a m e s p a c e może być też deklaracja lub definicja funkcji, a nawet definicja klasy. W bloku n a m e s p a c e może być nawet dyrektyzoa ttinclude wstawiająca cały plik nagłówkowy z wieloma deklaracjami.
Znamy już pewne nazwy pochodzące z przestrzeni nazw O d n a s z e g o p ierw szeg o p ro g ra m u p o słu g u je m y się ju ż bib lio tek ą w e jś c ia /w y j ścia. Z acy tu ję tu taj n asz p ie rw szy p ro g ram #include int main()
//
są d e k la ra c je n a z w z b ib lio te k i iostream
{ std::cout «
"Witamy na pokładzie";
// * - t u u ż y w a m y n a z w y c o u t
} Linijka #include p o w o d u je , że k o m p ila to ro w i p rz e d s ta w ia n e są d e k la ra c je n a z w z biblioteki iostream. A u to rzy tej b ib lio te k i w y m y ślili w ie le n a z w , a je d n ą z nich jest n azw a cout. Aby u n ik n ą ć w sz e lk ic h m o ż liw y c h k o n flik tó w n a z w , au to rz y tej b ib lio tek i zap ak o w ali te s w o je n a z w y d o p rz e s trz e n i n a z w (jakby d o pod k atalo g u "), k tó ry n azw ali s t d . Jeśli te ra z , w n aszy m p ro g ra m ie , chcem y p o s łu ż y ć się tą n a z w ą cout, to jej w y stą p ie n ie p o p rz e d z a m y n azw ą o d p o w ie d n ie j p rz e s trz e n i n a z w i d w u k r o p kiem , k tó ry jest o p e ra to re m zakresu . s t d : :cout
To w ła śn ie w id zisz w ty m n aszy m p ro g ra m ie .
76
Rozdz. 3. Typy Zakres ważności nazwy obiektu, a czas życia obiektu
Czy to wygodne? I tak i nie. "Tak" _ bo w ten sposób świetnie rozw iązały się konflikty nazw . Obecnie uży w ając danej nazw y m ożem y jasno pow iedzieć, z której m a ona być |
biblioteki. D laczego "nie"? Z au w aż , że w następnych naszych program ach używ aliśm y n a z w y cout b a rd z o często i za k ażd y m razem w ystępow ało ow o s t d : T ro ch ę jednak zaciem nia to tekst p ro g ram u . W dodatku potem przyszła też nazw a c i n . Ją też m usieliśm y po p rzed zać specyfikacją std:: . O czyw iście chodziło nam o to, by nie było konfliktu z innym i bibliotekam i, w których m ogłaby istnieć nazw a cout użyta w zu p ełn ie innym zn aczen iu . B ądźm y jednak realistam i. Przecież nazw a cout jest chyba najsłynniejszą n a z w ą w całym języku C++. Ż aden autor biblioteki nie odw ażyłby się jej użyć w zu p e łn ie innym znaczeniu. Po co więc teraz cała społeczność p ro g ram istó w ma w iecznie pisać to std: : cout zam iast (jak daw niej) po prostu cout ? I na to w ym yślono rozw iązanie. Jest instrukcja, k tóra m oże zdjąć tę klątw ę z jednej lub wielu nazw.
Zobaczymy teraz nasz "pierwszy" program w innej, nowej formie f i n c l u d e < io stream > u s in g naraespace s t d ; i n t m a in () ' cout « }
// // “
"Witamy na p o k ła d z ie " ;
są deklaracje nazw z biblioteki i o s t ream
/ / <-tu używamy nazwy cout ©
Krótki komentarz do tego programu O T utaj jest instrukcja, k tó ra rozw iązuje n am p ro b lem , a którą so b ie tera2 w yjaśnim y. N azy w am y ją dyrektyw ą u s in g . «$♦ A ngielskie słow o u s i n g ^ oznacza używanie. ♦> N astęp n e słow o to n a m e sp a c e H - o zn acza ono: przestrzeń nazw. <♦ Teraz następuje nazw a tej przestrzeni. W idzim y skrót std, co jes zap e w n e skrótem od słow a standard. (W tak n azw anej p rz e strz e n i nazv\ um ieszczone zo stało w iele nazw z bibliotek stan d ard o w y ch ). O g ó ln ie w ięc cała ta d y rek ty w a pow oduje, że n a z w y schow ane w p rzestrzeń n a z w s t d , od tej pory stają się d o stęp n e tak, jak b y n ie były sch o w an e wcale. 13) 14)
ang. using - [czytaj: "juzing"). ang. name space - [czytaj: "nejm spejs' 1
77
Rozdział. 3. Typy Zakres ważności nazw y obiektu, a czas życia obiektu
Wracając do naszej analogii z żi/cia sekretarek—to jakby powiedzenie jej, że jeśli nie znajdzie jakiegoś pliku w głównym katalogu, to niech sprawdzi, czy nie ma go w podkatalogu o nazwie s te l. D y re k ty w ą O p ro g ra m ista m ó w i w ięc k o m p ila to ro w i m niej w ięcej coś takiego: "Od tej p o ry ch ciałb y m u ż y w a ć n a z w z p rz e strz e n i n a z w s t d tak, by ich n a z w n ie m u sieć p o p rz e d z a ć sp ecy fik a to rem s t d : :. Z atem jeśli ja k a ś n azw a jest ci, k o m p ilato rze, n ie z n a n a , to s p ra w d ź ta k ż e czy jej n azw a nie je st sch o w a na w p rz e s trz e n i n a z w s td " . © Tu w id z is z , że n a sk u te k n aszej d y re k ty w y u s i n g m o ż liw e jest teraz u ż y w a n ie n a z w y c o u t bez k o n ieczn o ści p isan ia s t d : : c o u t .
S p o só b te n w y d aje się n a p r a w d ę d o b ry , w ięc... ...od tej pory w program ach przykładow ych w tej książce praw ie za w sze sto s o w a ł będę już tej d yre ktyw y u s i n g pozw a lają ce j na proste od no sze n ie * się d o nazw c o u : , c m .
Są je d n a k lu d zie, k tó rzy u w a ż a ją tak ie p o s tę p o w a n ie za niew łaściw e. T w ie rd z ą , ż e s k o r o au to rz y b iblioteki u m ieścili n azw y w p rz e s trz e n i n a z w , to w ie d z ie li, co ro b ią. U ży cie d y re k ty w y u s i n g - tak, jak to p o w y ż e j p o k a z a łe m - n isz c z y ich w y siłe k (i m ądrość). Jest w tej arg u m en tacji p e w n a logika, bo n a sz a d y re k ty w a u s i n g s p ra w iła , ż e to n ie ty lk o n azw y c o u t , c i n stały się d o stę p n e b ez k o n ieczn o ści u ży c ia k w a lifi k a to ra zak re su s t d : :, a le sta ło się to tak że z w s z y stk im i in n y m i n a z w a m i z p rz e s trz e n i n azw s t d . Jest n a to jed n ak rada. Istn ieje in n a form a in stru k c ji u s i n g - ta k a, d z ię k i k tó rej m o ż e m y osiągnąć ten s a m s k u te k je d y n ie w s to s u n k u d o w y b ra n e j n a z w y . T akiej n a z w y , którą w y ra ź n ie w y m ien im y . In stru k c ję tę n a z y w a m y deklaracją using. N ie dyrektywą u s i n g , ale deklaracją u s i n g . Sam zo b acz, ż e to raczej d e k la ra c ja (nazw y). O to p rz y k ła d o w y p ro g ra m : #include using std::cout; using std::cin;
// //
int raain()
ł cout « "Ilu ir.amy pasażerów?:"; i nt- liczba_pasazerow; 1 i n 3 C; 7 0 r 0 U • int
// < - t u u ż y w a m y n a z w y c o u t
©
cin »
/ / *— t u u ż y w a m y n a z w y c in
O
liczba pasażerów;
78
Rozdz. 3. Typy Zasłanianie nazw
Krótki komentarz O Ta instrukcja (deklaracją using ) um ożliw ia, że o d tej pory - aby o d n ie ść się do n a z w y cout (ukrytej w przestrzeni nazw std) - nie m usim y ju ż podaw ać kw alifikatora zakresu. O tym , że to możliwe, przekonuje nas instrukcja oznaczona jako © . © T a deklaracja using u m ożliw ia to sam o w sto su n k u do nazw y cin (um ieszczo nej w p rzestrzeni nazw std). O ty m , że od tej pory p rz e d nazw ią c i n nie m u sim y już staw iać k w alifik ato ra zak resu przekonuje nas instrukcja oznaczona jako O .
Którego z tych sposobów używać? Ja m am taką zasadę: jeśli ch o d zi o nazw y z p rzestrz en i nazw s t d, to n ie boję się ich. W końcu są one z biblioteki standardow ej, w ięc piszący inne b ib lio tek i znają je i u n ik ają konfliktu z nim i. N ato m ia st w stosunku d o innych, obcych bibliotek staram się raczej n ie używ ać d y re k ty w y u s i n g . W olę w program ie p isać całą nazw ę - łącznie ze specyfikacja określającą, z której to przestrzeni n a z w ona pochodzi. C zasem jednak - g d y zm ęczę się tym ż m u d n y m pisaniem - p o d d a ję się u ż y w a m tej dy rek ty w y using. Przecież m ój p ro g ram składa się z w ielu plików - i jeśli w jed n y m z nich u łatw ię sobie życie d y rek ty w ą using, to efekt d z iałan ia tej d y rek ty w y og ran icza się tylko d o tego jed n eg o pliku. N ie m a w ięc dram atu... To są moje zasady. Ty z a ś - albo w ypracujesz so b ie swoje, albo n a rz u c i Ci j< k iero w n ik dan eg o projektu program istycznego.
3.8
Z a s ła n ia n ie n azw W p ro g ram ie m ożem y zad ek laro w ać nazw ę lo k aln ą, identyczną z istniejąca n a z w ą globalną. N ow o zd efin io w an a zm ien n a zasłan ia w ted y , w danym lo k aln y m zakresie, z m ien n ą globalną. Jeśli w ty m lo k aln y m zakresie o d w o ła m ) się d o danej nazw y, to ko m p ilato r u zn a to za o d n iesien ie się d o zm ien n e lokalnej. S pójrzm y na p ro sty przykład: #include using namespace std; int k = 33;
,,,,,,, . . // U zm ien n a g lo b a ln a (obiekt ty p u in t)
I z*************************************************************j int main()
c o u t << " J e s te m w m a in , k —" << k << " \ n " ; ^ i n t k = 10; ' / / zmienna lokalna c o u t « " po l o k a l n e j d e f i n i c j i k =" « k « e n d l; } / / <---------------------------- -------------------------------------
79
Rozdział. 3. Typy Zasłanianie nazw cout << "Poza blokiem k =" << k << endl;
// ©
}
Wykonanie tego fragmentu programu spowoduje pojawienie się na ekranie: Jestem w main, k=33 po lokalnej definicji k =10 Poza blokiem k =33
Komentarz O D efin ic ja z m ie n n e j g lo b a ln e j k - is tn ie ją c a g d z ie ś w p ro g ra m ie , p o z a jak ąk o lw iek funkcją (tak że poza funkcją m a in ).
0 O d w o ła n ie się d o o b iek tu
k. Jeszcze nie n astąp iła definicja lokalna, w ięc ko m p i la to r uznaje, że ch o d zi n am o obiekt k glo b aln y .
© O tw ie ra m y lo k aln y blok. © D efiniujem y obiekt lo k a ln y o nazw ie k. © L okaln a n azw a k zasło n iła n azw ę k globalną. N a ek ran ie zostaje w ięc w y p isan a w a rto ść zm iennej lokalnej. © Z am k n ięcie lo k alnego bloku. O biekt lo k aln y k p rzestaje istnieć, a jego nazw a p rzesta je być w ażn a. Skończyło się życie o b ie k tu , sk o ń czy ł się tak że zak res w ażn o ści jego n azw y . © O d w o ła n ie się do n azw y k jest teraz ro z u m ia n e p rzez k o m p ilato r jako o d w o ła n ie się d o glo b aln eg o obiektu k.
W Mimo wszystko istnieje jednak możliwość odniesienia się do zasłoniętej nazwy globalnej. P o słu ży nam d o tego tzw . o p erato r zak resu : : (d w a d w u k ro p k i). O to p rzy k ład : #include using namespace std; int k = 33;
// O
z m ie n n a g lo b a ln a (o b ie k t t y p u v t t )
Z *****************************^***#**************************/ int main{)
( cout << "Jestem w main, k = M << k << "\n”;
{ int k = 10; // z m ie n n a lo k a ln a cout « "po lokalnej definicji k ="
« k
0
// ©
<< "\nale obiekt globalny k ="
«
: :k ;
'
// ©
80
Rozdz. 3. Typy Specyfikator (przydomek) c o n s t cout << "\nPoza blokiem k =" «
□
k << endl;
Wykonanie objawi się na ekranie jako: Jestem w main, k =33 po lokalnej definicji k =10 ale obiekt globalny k =33 Poza blokiem k =33
Uwagi O
Definicja obiektu globalnego.
0 Definicja obiektu lokalnego. © O d w o łan ie się od obiektu lokalnego. © O d w o łan ie się w ew nątrz lokalnego bloku do zasłoniętego obiektu globalnego. S praw ia to zapis z operato rem zakresu : : k T en ch w y t możliwy jest tylko w stosunku do zasłoniętego obiektu globalnego: Jeśli nazwa lokalna zasłania inną nazwę lokalną, wówczas nie da się do niej dotrzeć takim operatorem zakresu.
3.9
S p e c y fik a to r (p rzy d o m e k ) const C zasem chcielibyśm y w p ro g ram ie posłużyć się obiektem (np. typu i n t ) , k tó re go zaw arto ści naw et p rzez nieuw agę nie chcielibyśm y zm ieniać. O biekt tego ty p u , to tak zw an y obiekt stały. M ów iliśm y już o stałych dosłow nych. Były to p o p ro stu liczby, k tó re napisane były w tekście p ro g ram u . Tutaj nie chodzi o liczby, ale o obiekty, które mają w sobie jakąś wartość. P arad o k sem byłoby pow iedzieć: chodzi o zm ien n e, które w p ro g ram ie m ają się nie zm ieniać. P rzy k ład em m oże być choćby program na liczenie pola koła, objętości kuli i czegoś jeszcze. W ielokrotnie w takim program ie potrzebow ać będ ziem y liczby n. W ty m celu zdefiniujem y sobie obiekt typu d o u b l e i n ad am y m u w artość o d p o w iad ającą liczbie n. double pi = 3.14;
Jeśli jed n ak chcem y m ieć pew ność, że nigdy, n a w e t p rzez nieu w ag ę nie zm ieni m y w artości naszej liczby pi, w ów czas taką definicję p o p rz e d z a m y słow em (specyfikatorem , m o d yfikatorem ) const. S pecyfikator ten u ściśla zw ykłą definicję tak, że teraz jest to definicja obiektu stałego. const double pi = 3.14;
Z a u w a ż m y , że ró w n o cześn ie inicjalizujemy tu taj n asze pi w arto ścią 3.14 m u sim y to zrobić w łaśnie p rz y definicji. Później - p rzep ad ło ! O d tej p o ry ju ż nie m o żn a p o d staw ić do ob iek tu const żadnej w artości. (N aw et takiej sa m e j!)
Rozdział. 3. Typy Specyfikator (przydomek) c o n s t
81
Słowa, słowa, słowa O sobiście m am w stręt d o takich „m ąd ry ch '" słó w jak: sp e c y fik a to r, m o d y fik a tor, bo g d y kilka takich słó w sp o tk a się o b o k sieb ie w je d n y m z d a n iu - tru d n o to z d a n ie zrozum ieć. D lateg o słow a takie jak c o n s t n azy w am s o b ie p o p ro stu : przydomek. N asz o b iek t p i m a p rzy d o m ek c o n s t - jest w ięc o b ie k te m stały m , na u w ag a językow a: M ów im y „inicjaLIZA cja", a n ie „inicjacja". Jest o g ro m n a ró żn ica m ięd zy ty m i słow am i. Jeśli b ę d z ie sz u p a rc ie m ó w ił „inicjacja", to zajrzyj sobie k ied y ś d o en cy k lo p ed ii i s p ra w d ź , co to s ło w o zn aczy .
82
Rozdz. 3. Typy Specyfikator (przydomek) c o n s t Trochę się pośm iejesz, a potem ju ż zaw sze będziesz m ó w ił tylko: „inicjalizacja".
Inicjow ać m ożna na p rzy k ła d akcję zbiórki złom u.
3.9.1
Pojedynek: c o n s t contra # d e f i n e Jest to p arag raf dla p ro g ram istó w klasycznego C. Jeśli nie p ro g ram o w ałeś w języ k u C, to opuść ten p a ra g ra f i przejdź do n astępnego.
W Jeżeli program ow ałeś w języku C, to zap ew n e p am iętasz, że w k lasy czn y m C stałe najczęściej definiow aliśm y sobie za pom ocą d y rek ty w y p reprocesora. N p. t d e f i n e PI 3.14 Ta fo rm a w C++ jest ta k ż e dopuszczalna. P o k ażem y jednak, d la czeg o jest g o rsza. D ziałan ie d yrektyw y # d e f i n e jest mniej w ięcej takie, jakbyśm y - b e z p o śre d nio p o zakończeniu p isan ia program u - w y d a li edy to ro w i polecenie z a stą p ie n ia k ażdego sło w a "P I" słow em "3.14". N aty ch m iast po tym p rz y stę p u je m y do kom pilacji.
Oto, co straciliśmy: ♦♦♦ N azw a P I jest k o m p ilato ro w i zu p ełn ie n iezn an a. N ig d y się n a w e t nie dom yśli, że w o góle istniała. W p ro g ram ie jest tylko k ilk ak ro tn ie u ży ta liczba 3.14 K o m p ilato r nie skojarzy, ż e w k ażd y m z m iejsc ch o d zi o tę sam ą liczbę n, ch o ciaż człow iek od ra z u b y się tu dom yślił. N ie zaw sze jed n ak sp raw a jest ta k oczyw ista. #define LICZBA_SILNIKOW
4
K o m p ilato r nie zg ad n ie, k tó re z w ystępujących w p ro g ram ie liczb 4 są określe n iem liczb y silników , a k tó re są liczbą pór ro k u , liczbą nóg konia ltd. W zw iązk u z tym , ż e n azw a P I jest k o m p ilato ro w i n ie z n a n a , d lateg o k o m p ilato r nie p o tra fi sp raw d zić czy d a n a n azw a zo stała u ż y ta w ra m ach jej zakresu w ażn o ści. N ie m a tu p rzec ież ż ad n e g o z a k re su w ażn o ści. Są tylko lu ź n o p o ro zrzu ca n e liczby 3.14 (albo liczby 4).
Słała określona przy pomocy dyrektywy procesora łr d e fin ejest znana od linijki wystąpienia dyrektywy t t d e f i n e do linijki H u n d e f lub - gdy takiej nie ma —do końca pliku. Nie ma to jednak nic wspólnego z zakresem ważności. To tylko jakby obszar, na przestrzeni którego wykonujemy edytorem operacji zamiany znaków P I na znaki 3 .1 4
Rozdział. 3. Typy Specyfikator (przydomek) c o n s t
83
Czy dużo straciliśmy? R aczej tak. S traciliśm y m o ż liw o ść św ia d o m e g o w y b o ru zasięg u n azw y . N azw a m o ż e b y ć p rzecież z n a n a w jednej funkcji, a n ie z n a n a w innej. K om p ilato r nie m o ż e te ra z n as o strzec w p rz y p a d k u , g d y b y ś m y p o p ełn ili błąd. P o s łu ż e n ie się # d e f i n e w n aszy m p rz y p a d k u jest jak b y za m ia n ą n azw y na liczb ę (stałą d o sło w n ą). N a to m ia s t z d e fin io w a n ie o b iek tu jako c o n s t s p ra w ia , ż e p o w staje n am w p a m ięci n o rm a ln y o b iek t (n p . ty p u d o u b l e , lu b i n t ) . D o d a tk o w o o b iek t ten m a n a le p k ę : „N ie Z m ien iać P o d Ż a d n y m P o zo rem !". S k o ro je st to o b iek t, to m o ż n a p o zn ać jego a d re s , p o k aza ć n a n ieg o w sk a źn i k iem , itd . G d y b y śm y p o słu ż y li się d y re k ty w ą # d e f i n e , to m ielib y śm y w p ro g ra m ie d o c zy n ien ia z k ilk o m a stały m i d o sło w n y m i. S am a liczba 3.14 nie m a a d re s u , nie m o żn a w ięc p o słu g iw a ć się w o b ec niej w sk a źn ik ie m . O sta tn i z a rg u m e n tó w , k tó ry chcę p rz e d s ta w ić , d o ty czy p o słu g iw a n ia się p ro g ra m a m i u ru c h o m ie n io w y m i, czyli z a n g ie ls k a - d e b u g g e ra m i. P ro g ram taki p o z w a la na p racę k ro k o w ą n aszeg o p ro g ra m u , a w d o d a tk u sp ra w d z e n ie , c o - w d a n y m m o m en cie - tk w i w jak im ś o b iek cie n a sz e g o p ro g ra m u . R obi się to p o d a ją c p o p ro stu n a z w ę d a n e g o obiektu. M o ż e C i się w y d a ć ś m ie sz n e p y ta n ie d e b u g g e ra , co w d a n y m m o m en cie tk w i w s ta ły m o b iek cie P I , je d n a k jeśli m asz stałą l i c z b a _ s i l n i k ó w , b ęd ącą je d n ą z w ie lu stały ch w ty m p ro g ra m ie , to czę sto się z d a rz a , ż e ch ciało b y się zap y tać, co ta m w łaściw ie jest. R o z w a ż m y p o n iż sze d w a w arian ty . (U życie m a ły c h lu b w ielk ich liter w notacji n a z w w y n ik a tylko z trad y cji). # d e fin e id e fin e td e fin e # d e fin e
ROZDZIELCZOŚĆ 8192 KANALOWWBLOKU 128 CZYNNIK (ROZDZIELCZOŚĆ / KANAL_W_BLOKU) DLUGOSC_BUF (CZYNNIK*16*CZYNNIK)
W tak iej sytuacji m o ż e się o k azać k o n ieczn e u p e w n ie n ie się ile w łaściw ie w y n o si DLUGOSC BCJF. P r z y tym sp o so b ie d e fin io w a n ia stałej, je st to n ie m o żliw e . D eb u g g er o d p o w ie , że n ic m u nie w ia d o m o o n a z w ie t>LUGCSC_BUF (P o d o b n ie jak nic nie w ie o n a z w a c h ROZDZIELCZOŚĆ, KANALOW_W_BLOXU, CZYNNIK). Jeśli je d n a k zasto su jem y s p o só b o b iek tam i c o n s t const const const const
in t in t in t in t
r o z d z i e l c z o ś ć = 8192; k an alo w _ w _ b lo k u = 128; c z y n n ik = ( r o z d z i e l c z o ś ć / k a n a lo w _ w _ b lo k u ); d lu g o s c _ b u f = (c z y n n ik * 4 * c z y n n i k ) ;
to z a p y ta n ie d eb u g g era o to , co się kryje p o d n a z w ą d l u g o s c b u f jest z u p e łn ie le g aln e i w rezultacie o trz y m a m y o d p o w ied ź: 16384
84
Rozdz. 3. Typy Obiekty r e g i s t e r
3.10 O b ie k ty register r e g i s t e r to jeszcze jed en typ przydom ka (specyfikatora), który m o że zostać d o d a n y w definicji obiektu. W tym p rzy p ad k u m ożna ten p rz y d o m e k zastosow ać do tzw. zm iennej autom atycznej typu całkow itego. (Por. p a ra g ra f o zm ien n y ch autom atycznych - str. 163). register int i;
D opisując ten p rzydom ek dajem y kom pilatorow i d o zrozum ienia, ż e b ard zo za le ż y nam na szybkim dostępie do tego obiektu (bo np. zaraz b ęd ziem y u ż y w a ć takiego obiektu tysiące razy). K om pilator m oże u w zg lę d n ić n aszą su g estię i przechow yw ać ten obiekt w rejestrze, czyli specjalnej kom órce, do której ma bardzo szybki, niem al natychm iastow y dostęp. N ie m a jednak gw arancji na to, że tak będzie w istocie. N iektóre k o m p ilato ry nie są a ż tak sprytne. Jak pow iedziałem , jest to tylko pobożne ży czen ie, k tóre k o m p ilato r m oże spełnić lub nie. W iększość zn an y ch mi k o m p ilato ró w , takie su g estie bierze pod u w a g ę i program w ykonuje się nieco szybciej. Jeśli deklarujem y zm ien n ą jako r e g i s t e r , to nie m ożem y starać się u z y sk a ć jej a d re su . Rejestr to nie jest kaw ałek pam ięci, w ięc nie adresuje się g o w zw y k ły sp o só b . Jeśli więc m im o w szystko spróbujem y d o w iad y w ać się o te n adres, k o m p ilato r um ieści ten obiekt w zw ykłej pam ięci, czyli tam , g d zie m o że n am o k reślić (i podać) jego ad res.
3.11 S p e c y fik a to r voiatiie Jest to przydom ek, k tó ry m ów i kom pilatorow i, że m a być o stro żn y w k o n ta k tach z d anym obiektem . volatile int ra;
V o la tile 15- znaczy po angielsku: ulotny. Słow o to ostrzega, że o b ie k t m o że się w jak iś niezau w ażaln y d la kom pilatora sposób zm ieniać. K om pilator w ięc me p o w in ie n u p raszczać so b ie sp raw y , tylko za k ażd y m razem , g d y o d w o ła m y się d o te g o obiektu - k o m p ilato r m a rzeczyw iście zw rócić się d o p rzy d zielo n y ch te m u obiektow i kom órek pam ięci. Z ap y tasz: —A czy kied y k o lw iek byw a inaczej? Tak. D la naszego d o b ra tak. W yjaśnijm y to. O tó ż załóżm y, że d ek laru jem y z m ie n n ą określającą sło w o stan u j a k i e g o ś z e w n ętrzn eg o u rząd zen ia. N a przyk ład u rz ą d z e n ia m ierzącego tem p eratu rę oleju w silnikach. int stan miernika;
15)
[czytaj: „woletaili
85
Rozdział. 3. Typy Instrukcja t y p e d e f
T o sło w o m o że się zm ien iać sam o z siebie (bez w ied zy kom pilatora), bo p rz y p u ś ć m y , że d o k o m p u te ra d o ch o d zą p rzew o d y z zew n ętrzn y ch układów p o m ia ru te m p e ra tu ry . D o m y ślasz się już p e w n ie , że ten specjalny o b iek t został u tw o rzo n y w tej części p am ięci k o m p u tera, k tó ra rep rezen tu je u k ła d sprzęgający (interface) kom puter z m iern ik iem te m p e ra tu ry . Z ałóżm y, że ten u k ła d sprzęgający o b sługiw any jest p rz e z taki frag m en t p ro g ram u : int
a = 5, b = 6;
volatile int temperatura; cout « « a = b + b = 0; cout << <<
"Biezaca temperatura = temperatura << endl; 8; "Biezaca temperatura = temperatura « endl;
Jest ryzyko, że k o m p ilato r m ógłby „p o m y śleć" sobie tak: P o b rałem z kom órki temperatura jej w a rto ść i w ypisałem ją na ek ran ie O . P o tem zajm ow ałem się jakim iś n ie zw iązan y m i z te m p e ra tu rą zm ien n y m i a o ra z b , ( © , © ) - a te ra z zn o w u m am w y p isać na ek ran z m ien n ą temperatura. O Z a ra z , zaraz, nie m u s z ę tracić czasu na o d czy ty w a n ie jej z pam ięci, m iałem ją p rz e c ie ż g d zieś tu z a p isa n ą na boku (czytaj: w rejestrze). S koro w ięc jej o d tamtej p o ry nie zm ieniałem , to p o prostu w y p iszę tę w artość. K o m p ilato r w y p isu je, a tu w ybuch! P rzez ten czas, bow iem , p ra w d z iw a w artość o b ie k tu temperatura zm ieniła się bez w ie d z y k o m p ilato ra i p rzek ro czy ła w a rto ść krytyczną. S łow o volatile p rz e strz e g a k o m p ilato r p rz e d takim w ła śn ie sp ry tem . Za k a ż d y m razem m usi o n rzeczyw iście sięg n ąć d o kom ó rk i temperatura, a nie p o le g ać na tym , co so b ie zap isał „n a b o k u ". S pecyfikator -/olatile (czyli: u lo tn y ) o zn ac za, że o b ie k t tak o k reślo n y m o ż e się zm ien iać w sp o só b rzeczy w iście u lo tn y , w y m ykający się czasem sp o d k o n tro li k o m p ilato ra.
12 In s tru k c ja typedef Instru k cja t y p e d e f p o z w a la na n ad an ie d o d a tk o w e j n a z w y ju ż istniejącem u ty p o w i. P rzy k ład o w o instrukcja typedef int cena; sp ra w ia , że m ożliw a staje się taka deklaracja
86
Rozdz. 3. Typy In stru k cja t y p e d e f // co odpow iada: //co odpow iada:
cena x; cena a, b, c;
int x; int a, b,
P o c o r o b ić t a k i e s z tu c z k i? D la c z e g o n ie n a p is a ć p o p r o s tu int? O tó ż t a k a m o ż liw o ś ć je s t b a r d z o p r z y d a t n a . W y o b r a ź s o b ie p r o g r a m , w k t ó r y m w i e l o k r o t n i e w y s t ę p u j ą z m i e n n e ty p u int. N i e k t ó r e z n ic h m a ją o k r e ś la ć c e n ę , w ię c z a s t o s o w a l i ś m y tę i n s t r u k c j ę typedef. P e w n e g o d n ia d e c y d u j e m y , ż e d o k ł a d n o ś ć lic z b c a ł k o w i ty c h n a s n ie z a d o w a l a - i c h c ie lib y ś m y b y c e n y r e p r e z e n t o w a n e b y ły ty p e m double. C o w te d y r o b im y ? O d s z u k u je m y w p r o g r a m i e tę i n s t r u k c j ę t y p e d e f i z a m i e n i a m y ją n a ta k ą typedef double cena; T y m s p o s o b e m w s z y s t k i e m ie js c a w p r o g r a m ie , g d z i e u ż y w a m y n a z w y t y p u n p . d e k l a r u j e m y o b ie k ty t y p u c e n a —z a je d n y m z a m a c h e m z m ie n ia ją s i ę w e w ła ś c iw y sp o só b . T e ra z //o d p o w ia d a : //o d p o w ia d a :
cena x; cena a, b,
double x; double a, b, c;
J a k w i d a ć , je s t to b a r d z o w y g o d n e . T y p , k t ó r y o k r e ś la m y w in s tr u k c ji typedef, n i e m u s i b y ć w c a le ty p e m f u n d a m e n t a l n y m . R ó w n ie d o b r z e m o ż e b y ć to t y p p o c h o d n y . O to k ilk a p r z y k ła d ó w : typedef int * wskaznik_do_int; typedef char * napis; //c z y l i //czyli:
wskaznik_do_int wl; napis komunikat;
int * wl; char * komunikat;
P o n i ż s z a in s tr u k c ja d e f i n i u j e w ię c e j n a z w t y p ó w typedef int calk,
* wskc,
natur;
co u m o ż l i w i a t a k i e k o n s tr u k c je : calk a; wsk c w; na t u r n;
// c zy li: // c z y li: / / c zy li:
int a ; int * w; int n;
Z w r ó ć u w a g ę ja k u m i e s z c z o n a je s t g w ia z d k a w s k a ź n i k a . T o z a s t o s o w a n i e in s tr u k c ji t y p e d e f r a d z ę z a p a m i ę t a ć . J e ś li w p r z y s z ł o ś c i b ę d z i e s z m u s i a ł p o s ł u g i w a ć s ię s k o m p li k o w a n y m i w s k a ź n i k a m i , to ta in s tr u k c ji z a p e w n i C i, ż e z a p i s T w o ic h p r o g r a m ó w b ę d z i e , m i m o w s z y s t k o , c z y t e l n y . N a le ż y p a m ię ta ć , że in s tr u k c ja typedef n ie w p r o w a d z a n o w e g o ty p u , a je d y n ie s y n o n im n a z w y ty p u ju ż is tn ie ją c e g o . IggggglgggBIMMHNNMMMMHMMMMnMMMMMMIMMMlIMMMMMNMMMNMftlNMNMIMMMIMMMIMMMNI1****?’' :
*•
'
le s z c z e je d n o . I n s tr u k c ją t y p e d e f n ie m o ż e m y r e d e f i n i o w a ć n a z w y , k t ó r a ju : s t n i e j e w b ie ż ą c y m z a k r e s i e w a ż n o ś c i. K o n k r e tn ie : je ś li m a m y ju ż d e k l a r o w a n i
87
Rozdział. 3. Typy Typy wyliczeniowe enum
n a z w ę - np. cal k określającą funkcję w ykonującą całkow anie - to nie m ożem y u ż y ć instrukcji typedef int calk; b o n azw a
calk jest ju ż zajęta.
.13 T y p y w y lic z e n io w e enum To b a rd z o ciekaw a rzecz, je st to osobny ty p dla w y b ran eg o p rzez n as zestaw u stały ch całkow itych, a p rzy d aje się on w w ielu sytuacjach. C zęsto z d a rz a się, że w obiekcie typu całk o w iteg o chcem y p rzech o w ać nie tyle liczbę, co raczej p ew ien rodzaj informacji. O czyw iście m u sim y uczynić to w p isu jąc tam liczbę, ale liczba ta m a dla n as szczególne znaczenie. W tedy w arto sk o rzy stać z typu w yliczeniow ego. Jak definiuje się taki ty p w yliczeniow y? Z o b aczm y to od razu na przykładzie. C hcem y za pom ocą liczb określać jakieś d z iałan ie u k ład u pom iaro w eg o . Chce m y m ieć jakąś zm ienną o n azw ie co robic. Do niej będ ziem y w staw iali liczbę określającą żąd an ą akcję. O to, jak te akcje sobie pon u m eru jem y : 0 start_ p o m iaru 1 o d c z y tp o m ia r u 54 zm ian a_ p ró b k i 55 zniszczenie_próbki T y p w yliczeniow y d efin iu je się w ed łu g sch em atu enum n a zw a _ typ u { lista w yliczen iow a }; C o w naszym p rzy p ad k u w y g ląd ać m oże tak: enum akcja { start_pomiaru = 0, odczyt_pomiaru = 1, zmiana_probki = 54, zniszczenie_probki = 5 5 Z definiow aliśm y n o w y ty p o n azw ie
};
akeja.A o to definicja zm ien n ej tego typu:
akcja co_robic; O znacza to, że co_robic jest zm ienną, d o której m o żn a p o d staw ić tylko jedną z określonych na liście w yliczeniow ej akej a w artości. To zn aczy m ożliw e są tak ie operacje: co_robic = zmiana_probki; co_robic = start_pomiaru; a nied o p u szczaln e są o p e ra q e co_robic = 1; co robie = 4;
88
Rozdz. 3. Typy Typy wyliczeniowe enum N ic, co nie je st n a liście w y liczen io w ej tego ty p u w y liczen io w eg o , nie m oże z o s ta ć p o d sta w io n e d o zm ien n ej tego typu akej a.
I
Na
liście
w y lic zen io w ej
zauw ażam y
liczby.
Jednak,
m im o
że
odczyt pomiaru jest - jak w id z im y - re p re z e n to w a n y p rzez liczb ę 1, to te; lic z b y 1 n ie m o g liśm y w s ta w ić d o zm iennej ty p u a k c j a . Tylko te n a z w y , które są n a liście.16 T o b a r d z o w a ż n a cecha. D zięk i te m u n aw et p r z e z n ie u w ag ę nie w p is z e m y dc z m ie n n e j co_robic c z e g o ś inn eg o . N aw et, g d y b y to coś p rz y p a d k o w o p aso w a ło - ja k o w arto ść liczb o w a. L ista w y l i c z e n io w a
R e p re z e n ta c ja liczb o w a e le m e n tó w listy m o że b y ć p rz e z nas w y b ie ra n a w tedy, g d y d e fin iu je m y d a n y ty p w y liczen io w y . N a w ia s e m m ów iąc, g d y b y ś m y w n a s z y m p rz y k ła d z ie n ie n a p isa li tych liczb 0, 1 - to takie w łaśn ie lic z b y byłyby p o d s ta w io n e tam p rz e z d o m n ie m a n ie . O to p r z y k ła d in n eg o ty p u w y liczen io w eg o , g d z ie rep rezen tacje liczb o w e sa inne: enum operacja_dyskowa { czytaj_blok, pisz_blok, przeskocz_blok = 5, przeskocz_znacznik, powrot_do_poczatku, przewin = powrót_do _poczatku, zapisz_naglowek, zapiszzakonczenie = zapisz_naglowek + 3 1; k o le jn e p o zy cje na tej liście m ają n astęp u jące r e p r e z e n ta q e liczbow e ♦♦♦ czytaj blok: 0 - bo jeśli nie określiliśm y inaczej, to w y licza n ie za
czyna się od 0. ♦♦♦ pisz blok: 1 - z n o w u nie było określenia, w ięc z w yliczanki w ynik a, że będzie to liczba następna, c z y li 1. ♦♦♦ przeskocz_blok: 5 - tu w id z im y w y r a ź n e ży czenie, by b y ła to liczbę 5. ♦♦♦ przeskocz_znacznik: 6 - zn o w u n ie b y ło ży czeń , w ięc k o m p ila to i b ie rz e n astęp n ą liczbę, czyli 6 . ♦♦♦ powrot_do_poczatku: 7, bo to n a s tę p n a liczba. ♦♦♦ przewin: 7, bo m ó w im y , ż e m a być to s a m o , copowrot_do_poczatku. (Jak widać, wolno nam posłużyć się przed chwilą zdefiniowaną nazwą)-
16)
Niektóre starsze kompilatory, mimo wszystko, zgadzają się na wstawianie liczb do zmiennej typu enum.
89
Rozdział. 3. Typy Typy wyliczeniowe enum •X*
z a p i s z n a a l o w e k : 8 - z n o w u n ie b y ło życzeń, w ięc k o m p ilato r bierze n astęp n ą liczbę, czy li 8. zapis z_za kończenie: 11 - jest tu w y ra ż e n ie (zapisz jiaglowek + 3). S koro zapisz n a g ł ó w e k m iało w a rto ść 8, to po d o d a n iu liczby 3 o trzy m u jem y w a rto ś ć 11.
Jak zau w aży łeś, te rep rezen tacje liczbow e n ie m u szą koniecznie się różnić. M ogą być na p rz y k ła d na liście d w a elem en ty o n azw ie p r z e w i n tasme o ra z r o z l a d u j tasme, k tó re b ę d ą m iały tę s a m ą rep rezen tację. O czyw iście robim y to celo w o , g d y chcem y u m o ż liw ić n ad a n ie d w u n a z w tej sam ej akcji. To, ja k ie są rep rezen tacje liczbow e - nie m u si n a s w cale obchodzić. Są to jakieś w arto ści. W p o d p ro g ra m ie , k tóry o d p o w ia d a z a pracę z d y sk iem , ż ą d a n ą akcję p o ró w n u je m y nie z liczbam i, tylko zn o w u z elem en tam i tej listy.
Nawzajem się nie znają... Z o b aczy liśm y tu d w a p rz y k ła d y ty p ó w w y liczen io w y ch . Jed en n azy w ał się a k c j a, a d ru g i ope r a c j a d y s kowa. N ie p o trz e b u ję ch y b a d o d a w a ć , ż e te d w a ty p y n ie m ają ze sobą nic w spólnego. Jeśli g d z ie ś w p ro g ra m ie trze b a p o d ać jak ąś z n azw z listy w y liczen io w ej typu a k c j a , to p ró b a p o d a n ia jakiejś n az w y z listy w yliczeniow ej ty p u operacja d y s k o w a b ęd zie p rz e z k o m p ilato r o d rzu co n a .
enum jest typem arytmetycznym To z n ac zy , że o b iek t ty p u enurr. m oże u cze stn iczy ć operacjach ary tm ety czn y ch . W tak iej sytuacji n a stę p u je zam ian a (k o n w ersja) w arto ści d a n e g o o b iek tu ty p u e n u m n a w artość ty p u int. N aw iązu jąc d o p o p rz e d n ie g o p rzy k ła d u , m o ż liw e jest w ięc ta k ie w y rażen ie p r z e s k o c z _ b lo k + 1000 i m a o n o w artość 1005. Jest to w artość ty p u int, a fakt, ż e w arto ść ta p o w stała p rz y u ży ciu jakiegoś tam enum, nie m a zn a c z e n ia .
Typ wyliczeniowy nie musi mieć nazwy Z obaczyliśm y d o tej p o ry ta k ą form ę definicji ty p u w y liczen io w eg o enum
n a z w a _ ty p u
{ sta la A ,
sta la B ,
sta la C ,
ild.... };
M oże być form a jeszcze p ro stsza, to zn aczy nie m ająca n a z w y ty p u . enum
r
M t* ■
{ stalaM , s t a l a N , s t a l a P ,
i ł d . . . .
(*> * « * < •> 4 P M T - '
}; •*. n —
tĄMl
D z ię k i ta k ie j s k ła d n i m a m y w y g o d n y s p o s o b z d e fin io w a n ia k ilk u s ta ły c h
(c a łk o w ity c h ) - w d a n y m z a k re s ie w a ż n o ś c i.
Ta p ro stsza form a jest je d n a k jakby nieco u b o ż s z a . Jaka je st ró żn ica? ❖
Jeśli m am y z d efin io w an y typ w y lic z e n io w y z n a z w ą , to m o ż e m y tw o rzyć sobie jakieś o b ie k ty takiego (n a z w a n e g o ) ty p u .
90
Rozdz. 3. Typy Typy wyliczeniowe enum
Obiekty te będą mogły przyjmować jedną z wartości z właściwej sobie listy wyliczeniowej. ♦> N ato m iast ten ty p b e z n a z w y - m ożliw ości tw o rzen ia ob iek tó w tak ieg o ty p u n am nie daje. N ie m ożem y przecież s tw o rz y ć obiektu, k tó re g o typ n ie m a nazw y... C zasem je d n a k w cale teg o nie chcem y, p o trze b n y ch jest n am tylko kilk a stałych c a łk o w ity ch i już. K ażd a z tych stałych m a sw o ją w łasn ą nazw ę - tę, którą w id z im y na liście w yliczen io w ej, ale w sz y stk ie razem - nie m ają żad n ej w sp ó ln e j n a z w y sw ojego ty p u .
Teraz coś tylko dla dociekliwych: Jaki rozmiar mają obiekty enum? T o z a le ż y o d k o m p ilato ra. O b iek ty tak ie - tak n a p ra w d ę - realizu je o n za p o m o c ą je d n e g o z ty p ó w całkow itych. C ie k a w a rzecz: jeśli z d efin iu je sz swój ty p w y lic z e n io w y (o n azw ie np.
danny), a
n a s tę p n ie
za pomocą tzw. operatora s i z e o f (zob. § 4.7 str. 113) s p ró b u je s z się d o w ied zieć o jego ro zm iar - m o ż e sz być zaskoczony. O tó ż: co p ra w d a , najczęściej k o m p ila to r zrealizu je ten T w ó j ty p danny po p ro s tu za p o m o c ą ty p u int, ale m o g ą być kom p ilato ry , k tó re robią to sprytniej. N a czy m p o le g a ich sp ry t? Po p ro stu s p ra w d z a ją o n e jakie w arto ści u m ieściłeś n a liście w yliczen io w ej. Jeśli na p rz y k ła d są ta m ty lk o w artości: 0 ,1 , 6 —to taki s p r y tn y k o m p ila to r u z n a , że ro zrzu tn o śc ią b y ło b y takie w arto ści p rz e c h o w y w a ć w obiekcie o ro z m ia rz e d w a, lu b n a w e t cztery bajty. W sz y stk ie te w a rto ś c i zm ieszczą się p rz e c ie ż w jed n y m bajcie. T a k też to zrealizu je. O b iek ty tw o je g o ty p u w y lic zen io w eg o o n azw ie danny b ę d ą m iały w te d y ro z m ia r je d n e g o b ajtu . Jakie to m a d la nas zn ac zen ie (o p ró cz o szczę d n o ści p am ięci)? C o n as o b c h o d z ą ta k ie sp raw y ?
I
M o że to m ieć zn aczen ie, g d y w niedalekiej p rz y sz ło śc i, o d k ry jesz s p o so b y u m ie s z c z a n ia w o b iek tach sw o jeg o ty p u danny w arto ści sp o za je g o listy w y lic zen io w ej. Tak, tak - są i n a to sposoby. Jeśli w ięc, ta k na siłę, b ę d z ie sz ch ciał w takim o b ie k cie u m ieścić liczbę 5, lu b 102 - u d a C i się to, bo tak ie w a rto śc i m ieszczą się w je d n y m bajcie. N a to m ia s t, jeśli b ę d z ie s z ch ciał tam "w epchać" liczb ę 44000 - to ta k a liczba je st za d u ż a , jak na o b ie k t jed n o b ajto w y . E fekt w te d y jest n ie z d e fin io w a n y . A le, ja k p o w ia d a m , k ło p o t m o ż e w y stąp ić ty lk o w te d y , g d y k o m p ila to r b ę d z ie rz e c z y w iśc ie sp ry tn y , a d o teg o d o ło ż y m y Tw ój "sp ry t". Jest to jeszcze je d e n a r g u m e n t za ty m , byś d o o b ie k tu ty p u w y lic z e n io w e g o p rz y p is y w a ł ty lk o te n a z w y , k tó re w y stęp u ją n a jego liście w y liczen io w ej.
91
Rozdział. 3. Typy Ćwiczenia
Dla wtajemniczonych T y p y w yliczeniow e n a p ra w d ę bardzo się p rzydają. W mojej praktyce chyba najczęściej przy w y sy łan iu argum entów d o funkcji. Funkcja m oże spodziew ać się a rg u m e n tu typu w yliczeniow ego (np. c p e r a c j a dysKowa) i kom pilator b ę d z ie pilnow ał, aby ty lk o argum ent tego ty p u został tam w ysłany. Jeśli się p o m y lim y id o funkcji w yślem y coś innego (np. d o w olną liczbę i n t lub inny typ w yliczen io w y np. k o l o r ) - w ów czas k o m p ilato r od razu zn ajd zie nam ten b łąd .
Typ wyliczeniowy jest typem definiowanym przez użytkownika, a skoro tak, to można naioet dla niego przeładować operatory. Pon ieważ jednak typ enum nie ma funkcji składowych, więc takie operatory realizuje się w postaci funkcji nie-składowych (np. globalnych)
3.14
Ć w ic z e n ia Deklaracja jest wyjaśnieniem czegoś kompilatorowi na temat naszego programu. Jeśli deklarujemy nazwę nnn, to wyjaśniamy, że co? Wyjaśnij jaka jest różnica między definicją, a deklaracją. Czy są to pojęcia rozłączne, czy też jedno jest szczególnym rodzajem drugiego? Jeśli tak, to które jest rodzajem którego? (W mnemotechnicznym zrozumieniu tego zagadnienia może bardzo Ci pomóc łacińskie pochodzenie słów deklaracja i definicja. Potrafisz to przypomnieć?). Czy możliwe jest powtórzenie tej samej deklaracji w danym zakresie ważności? Czy możliwe jest powtórzenie tej samej definicji w danym zakresie ważności? Co powołuje do życia obiekt o nazwie nnn. Jego deklaracja, czy jego definicja? Jaka jest relacja między typami złożonymi, a typami fundamentalnymi? VII
Wymień typy przeznaczone do pracy z liczbami całkowitymi. Podaj ich nazwy w war iantach "ze znakiem" i "bez znaku”. Jeśli przy deklaracji obiektu typu i n t nie zaznaczyliśmy czy chodzi o wersję "ze znakiem" czy "bez znaku" - który wariant wybierze kompilator?
IX
Co to są znaki alfanumeryczne? Podaj nazwę typu mogącego przechowywać znaki alfanumeryczne - jedynie z podstawowego zestawu (np. ASCII) Jeśli w powyższym typie nie zaznaczyliśmy czy chodzi o wersję "ze znakiem", czy "bez znaku” - który wariant wybierze kompilator? Podaj nazwę typu przeznaczonego do przechowywania rozszerzonego zestawu znaków alfanumerycznych. W języku C typ przeznaczony do przechowywania rozszerzonego zestawu znaków alfanumerycznych uzyskany jest dzięki instrukcji typedef . Instrukcja ta (jak już wiemy) nie tworzy nowego typu, ale nadaje nową, dodatkową nazwę jakiemuś już istniejącemu). Czy w C++ typ ten jest odrębnym typem, czy może stworzonym przez typedef?
92
Rozdz. 3. Typy Ćwiczenia
Wymień trzy typy przeznaczone do pracy z liczbami zmiennoprzecinkowymi. Czy stosuje się do nich specyfikatory signed/unsigned? Wymień nazwę typu przeznaczonego do pracy ze zmiennymi logicznymi. Ile różnych wartości może przyjmować obiekt takiego typu? Jakie to wartości? Jakie naprawdę wartości liczbowe reprezentują możliwe "stany" obiektu typu logicznego. Czy do typu bool można zastosować specyfikator signed? Jeśli dany kompilator rezerwuje dla typu short int dwa bajty pamięci (czyli 16 bitów) to liczbę z jakiego przedziału można w nim zapisać? Podaj w przybliżeniu, w zaokrągleniu do tysięcy. (To dobrze jest pamiętać, by nie próbować tam wpisać za dużej liczby. Co zrobić, jeśli jakaś ujemna liczba nie mieści się obiekcie typu short int? Jakim innym typem można wtedy się posłużyć? Gdzie definiować można obiekty w języku C++: a) jedynie powyżej instrukcji wykonywalnych, b) nawet wśród instrukcji wykonywalnych Jeśli mamy pętlę for, w której instrukcji inicjalizującej jest definicja obiektu o nazwie "i”, to czy w instrukcji postawionej bezpośrednio po zakończeniu pętli możemy zdefiniować nowy obiekt o nazwie "i"? W wyrażeniu warunkowym instrukcji i f można zdefiniować obiekt. Jaki zakres ważności ma ten obiekt? Odpowiedz na to samo pytanie w przypadku Co to jest stała dosłowna? Czy obiekt stały mieszczący sobie wartość 3.14 - to jest stała dosłowna ? Zapisz tę samą wartość np. 17 w postaci stałej dosłownej w zapisie dziesiątkowym, ósemkowym i szesnastkowym. Zapisz wartość 10 w postaci stałej dosłownej typu: int, long, unsigned in t, unsigned long, double, float.
Zapisz liczbę 15.6 w postaci stałej dosłownej typu double oraz float. Użyj najpierw zapisu dziesiętnego (zwykłego), a następnie wykładniczego. Mogą być tylko dwie stałe dosłowne typu bool. Podaj je. XXVIII XXI)
Jakiego typu jest następująca stała znakowa 'w' ? Zapisz stałe dosłowne reprezentujące następujące znaki specjalne: tabulator, znak nowej linii, odwrotny ukośnik, sygnał dźwiękowy ? Zapisz jako stałą dosłowną znak o kodzie (szesnastkowym) 2f(|6j Zapisz jako stałą dosłowną znak o kodzie (ósemkowym) 10(g) Zapisz jako stałą dosłowną literę m, tak by owa stała była typu wchar_t Zapisz w postaci stałej dosłownej typu C-string (ciąg znaków) swoje imię i nazwisko. Następnie między imię i nazwisko wstaw znak nowej linii. Co zrobić, jeśli stała dosłowna typu C-string jest tak długa, że nie możemy jej zmieścić w jednej linii tekstu programu. Czy można dokończyć jej pisanie w linii następnej? Jak to zrobić, by ostatecznie uzyskać i tak jeden długi C-string? Jaka jest relacja między typami złożonymi, a typami fundamentalnymi?
Rozdział. 3. Typy Ćwiczenia
93
Czy v oid to typ fundamentalny, czy złożony? XXXVI XXXVIII
Czy można zdefiniować obiekt typu void? Co to jest czas życia obiektu? Co to jest zakres ważności nazwy obiektu? Jaka jest różnica między powyższymi dwoma pojęciami? W tzw. bloku, czyli fragmencie programu ujętym w znaki klamry { }, definiujemy gdzieś obiekt o nazwie m. Jaki zakres ma ta nazwa, czyli od jakiego miejsca w programie ta nazwa jest znana i kiedy przestaje być znana. Czy do takiej nazwy można się do niej odnieść spoza tego bloku? X To pytanie jest dla wtajemniczonych - wiedzących, co to jest funkcja. Czy za pomocą
instrukcji goto można przeskoczyć do wnętrza innej funkcji? Jaki zakres ma etykieta, czyli od jakiego miejsca w programie ta nazwa jest znana i kiedy przestaje być znana. Co to znaczy, że nazwa jest globalna. Jeśli na program składa się z kilka plików źródłowych, to czy nazwa globalna z jednego z nich jest znana w pozostałych ? W danym programie posłużyliśmy się dyrektywą #in clu d e Co zrobić, by móc odnosić się do nazwy cout bez konieczności używania kwalifikatora zakresu (std: : cout). Podaj dwa sposoby. Na czym polega zasłonięcie nazwy globalnej przez nazwę lokalną?
XLVII XLVII!
Jak z lokalnego bloku, w którym globalna nazwa została zasłonięta przez nazwę lokalną - odnieść się do zasłoniętej nazwy globalnej? Jeśli mamy zagnieżdżone dwa bloki, a w tym głębiej zagnieżdżonym - lokalna nazwa zasłania inną lokalną - to jak możemy odnieść się to tej zasłoniętej nazwy lokalnej? Jaka jest różnica między inicjalizacją, a przypisaniem? Czy można przypisać jakąś wartość do przed chwilą utworzonego obiektu z przydomkiem "const'7 Co daje przydomek "register"?
LI
Jaki jest cel stosowania przydomka " v o la t ile ”. O czym informuje on kompilator? Do czego służy instrukcja typedef? Czy dzięki niej tworzymy nowy typ? Czy instrukcja enum tworzy nowy typ, czy może jej rezultat jest wariantem innego fundamentalnego typu? Jeśli na liście wyliczeniowej pierwszej nazwie nie przypiszemy żadnej wartości, to jaką wartość otrzyma ona przez domniemanie? Jeśli na liście wyliczeniowej jednej z kolejnych nazw nie przypiszemy żadnej wartości, to jaką wartość otrzyma ona przez domniemanie? Czy typ wytworzony przez enum jest typem zmiennoprzecinkowym, czy może całkowitym? Czy na liście wyliczeniowej możemy umieścić stałą dosłowną 3.14? Mamy dwa typy wyliczeniowe. Tak się składa, że w jednym nazwa xxx reprezentuje stałą wartość 89. Jeśli w drugim z nich jest nazwa yyy, która także reprezentuje wartość
94
Rozdz. 3. Typy Ćwiczenia
Rozdział. 4. O peratory O peratory arytm etyczne
95
Operatory w ró ciłeś m oże u w a g ę , że d o tej p o ry n a s z e p ro g ra m y b y ły b a rd z o p ry m ity w n e . To m ię d z y innym i dlateg o , ż e m ilcząco zało ży łe m , iż w iesz, co o zn ac zają sym bole:
Z +
-
*
<
>
Tak je d n a k dalej nie m o żn a. O tych i innych o p e ra to ra c h m u sim y p o ro z m a w ia ć te ra z bliżej. O p e ra to ró w jest w iele ro d zajó w . Ich o p a n o w a n ie n ie w y m a g a jed n ak w ię k sz e g o w ysiłku, b o p rz e w a ż n ie o k reślają o n e p o d s ta w o w e o p eracje a ry tm e ty c z n e i logiczne, z n a n e nam p rzecież ze szk o ły . P rz y s tą p m y z a te m d o rzeczy.
I
O p e ra to ry a ry tm e ty c z n e O p erato ry : + * /
d o d aw an ia , o d ejm o w an ia, m nożenia, i dzielenia
nie w y m ag ają chyba ż a d n y c h w yjaśnień. O to p rz y k ła d y w y ra ż e ń , w k tó ry c h w y stę p u ją te operatory: a a a a
= = = =
b b b b
+ * /
c;
c; c; c;
// dodawanie // odejmowanie // mnożenie // dzielenie
96
Rozdz. 4. O peratory O peratory arytmetyczne O p erato ry te w ykonują działania na dwóch obiektach i dlatego n a z y w a m y j< dwuargumentowymi. Po p ro stu dodają do siebie d w ie liczby, albo d w ie zm ien n t jakiegoś typu. Te m ianow icie, które stoją po o b u stronach sym bolu o p erato ra. Z atem dodaw anie: a + 7
d ziała tu na dw óch argumentach: 1) obiekcie a
(np. zmiennej),
2) na liczbie 7 (stałej dosłownej)
Praktyczna uwaga: Z ap is Tw oich p ro g ram ó w będzie dla Ciebie i in n y ch czytelniejszy, g d y przyj m iesz zasadę, by k ażd y o perator po obu stro n ach m iał spacje. N a dowó< p o ró w n aj dw a identyczne w yrażenia: (a+b+0.32)/c-7.1*(12.4+x)+75.3 (a + b + 0.32) / c - 7 . 1
* (12.4 + x)
+ 75.3
K om pilatorow i jest tu w szy stk o jedno. Ty je d n ak czasem pom yśl te ż o sobie.
4.1.1
%,czyli reszta z d zielenia (modulo)
Operator
T ak że d w u a rg u m e n to w y m operatorem jest o p e ra to r reszty z d zielen ia % (sym boi p rocentu). Skoro jest to operator, dzięki k tó rem u o trzy m u jem y resztę d zielen ia, to w yrażenie: 10 % 3
m a w arto ść 1 - g d y ż taka jest reszta z d zielen ia 10 p rzez 3. N a p rz y k ła d p< w y k o n a n iu takiego frag m en tu program u: int
x = 8, n = 5;
cout «
"wynik = " << (* % n) «
endl;
na ekranie pojawi się wynik = 3
a to dlatego), że 8 dzielone p rz e z 5 daje resztę z d zielen ia ró w n ą 3. I O perator ten sto su je się tylko dla a rg u m e n tó w całkow itych, tak I jest przecież sen s dzielenia z resztą. O p e ra to r ten m oże się p rz y d a ć często w takich n ie -m a te m a ty c z n y c h sytuacjac jak poniżej: #include using namespace std; int m a i n ()
( int i;
Rozdział. 4. O peratory O peratory arytm etyczne
0 8 16 24 32 40 48 56
*
1 9 17 25 33 41 49 57
97
2 10 18 26 34 42 50 58
3 11 19 27 35 43 51 59
4 12 20 28 36 44 52 60
5 13 21 29 37 45 53 61
6 14 22 30 38 46 54 62
7 15 23 31 39 47 55 63
Uwagi O To m iejsce, g d z ie działa n asz o p e ra to r m o d u lo % . W zależn o ści czy w y n ik jego d zia ła n ia jest zerow y, czy n ie zero w y - p o d ejm o w an a je st później o d p o w ie d n ia akcja. W arto ść w yrażenia ■i % 8 ) jest n iezero w a, n a p rz y k ła d , w sytuacjach: 1 * 8
2 % 8
3 % 8
...
7 % 8
n a to m ia st w y n ik jest zero w y , n a przy k ład , w sytu acjach : 0 % 8
8 i 8
16 % 8
24 % 8
© Tę akcję po d ejm u je się, g d y w a rto ść w yrażenia w a ru n k o w e g o jest n iezero w a. Jest to w y p isa n ie na ek ran ie tabu lato ra. © Tę akcję p o d ejm u je się, g d y w arto ść w yrażenia w a ru n k o w e g o jest zero w a. Jest to w y p isa n ie na ekranie zn ak u n o w ej linii. Czyli po p ro s tu przejście d o n o w ej linii. O To jest m iejsce, gdzie o d b y w a się w y p isan ie liczby n a ek ran ie
'
W
'
P rio ry tet o m aw ianych o p e ra to ró w jest taki sam , jak d o tego p rz y w y k liś m y w m atem aty ce. C zyli zap is a + b % c * d - f oznacza to sam o, co a + ( (b % C ) * d) - f
98
Rozdz. 4. O peratory O peratory arytmetyczne In n y m i słow y m nożenie i dzielenie w ykonyw ane jest przed d o daw aniem lub odejm ow aniem .
4.1.2
Jednoargumentowe operatory + i Plus i m in u s m ogą też w y stąp ić jako operatory jednoargum entow e. N ic w tym d ziw n eg o , to także zn am y ze szkoły. O to przykład: + 1 2 .7 -x - (a*b)
Tednoargumentowy o p e ra to r + (plus) w łaściw ie nie robi nic, natom iast jed n o arg u m en to w y o p erato r - (m inus) zam ienia w arto ść danego w y rażen ia na liczbę przeciw ną. intCout << "Oto dwa wydruki: " « i « " oraz cout « "\nA teraz: " « -(-(-(i+1)));
«
-i;
W rezultacie wykonania tego fragmentu na ekranie pojawi się oto dwa wydruki: 5 oraz -5 A teraz: -6
Z w racam uw agę, że nie m a tu żadnego odejm ow ania, tylko tw o rzen ie liczby przeciw nej w stosunku d o w y rażen ia, które zo stało p o d d an e tej operacji. O p erato ry te są jed n o arg u m en to w e, bo działają na tylko jednym arg u m en cie (tym stojącym bezp o śred n io p o praw ej stronie znaku).
4.1.3
Operatory inkrementacji i dekrementacji Inaczej m ów iąc: o p erato ry zw iększenia o jeden i zm niejszenia o jeden. W p ętlach b ard zo często w y k o n y w aliśm y d ziałan ia w rodzaju: // zwiększenie o 1 // zmniejszenie o 1 Z w ięk szen ie o 1 (inkrem entacja) lub zm niejszenie o 1 (dekrem entacja) - jes d ziałan iem tak często sp o ty k an y m w p ro g ram o w an iu , ż e w języku C ++ mam'* dla w y g o d y specjalne o p erato ry . O to one: i++
k—
/ / c z y li to sa m o co: i = i + 1 / / c z y li to sa m o co: k = k - 1
D ygresja: Teraz m ożesz ju ż rozw ikłać zag a d k ę dlaczego C++ n a z y w a si{ w łaśn ie C++. O tó ż, g d y w czasach archaicznych język B ro z w in ą ł się w język C, ża rto w a n o jak b ęd zie się n azy w ał jego n a stę p c a j P roroctw a m ó w iły , że pew nie: Język D. P roroctw a były, jak w idać,
Rozdział. 4. O peratory O peratory arytm etyczne
99
chybione, bo następca C nazywa się C + + , co należy rozumieć jako „lepsza wersja C " Operatory inkrementacji ++ i dekrementacji — są jednoargum entowe. Oba m ogą m ieć d w ie formy: •
przedrostkową (prefix) czyli w ted y , g d y operator stoi z lewej strony argum entu, np: ++a, - - p (czyli przed argumentem)
•
końców kow ą (postfbc), czyli w ted y, g d y operator stoi po pra wej stronie argumentu np.: a + + , p — (po prostu po argumencie)
Jest w tym pew na, bardzo sprytna różnica. R ozw ażm y to na przykładzie operatora inkrementacji (zw iększania). ❖
Jeśli operator inkrementacji (zwiększania) stoi przed zm ienną, to
❖
•
najpierw jest ona zwiększana o 1,
•
następnie ta zw iększona już w artość staje się wartością wyrażenia.
W przypadku, gdy operator inkrementacji stoi za zm ienną, to •
najpierw brana jest stara wartość tej zm iennej i ona staje się wartością w yrażenia,
•
a następnie - jakby na pożegnanie pracy z obiektem zw iększany jest on o 1. Z w ięk szenie to nie w p łyn ęło w ięc jeszcze na w artość sam ego w yrażenia.
M oże brzmi to bardzo zaw ile, jest jednak bardzo proste. Zobaczm y to na przykładzie: #include using namespace std; int m a i n ()
{ int
a b c d
= = = =
5, 5, 5, 5;
cout << "A oto wartość poszczególnych wyrazen\n" << "(nie mylić ze zmiennymi)\n”; cout « « « «
"-n-a "b++ c "d—
= = = =
" " " "
<< « << «
++a b++ — c d—
<< « « «
endl endl endl endl;
r» *
// teraz sprawdzamy, co jest obecnie w zmiennych cout « « « «
"Po obliczeniu tych wyrażeń, "zmienne maja wartości\n" "a = " « a « endl "b = " « b << endl "c = " « c « endl
same
- f i l L ’--...
100
Rozdz. 4. O peratory O peratory arytmetyczne «
"d = " << d << endl;
)
W rezultacie zobaczymy na ekranie: A oto wartość poszczególnych wyrażeń (nie mylić ze zmiennymi) ++a “ 6 b++ = 5 — c = 4 d— = 5 Po obliczeniu tych wyrażeń, same zmienne maja wartości a = 6 b = 6 c = 4 d - 4
O p erato r inkrem entacji stojący przed arg u m en tem nazyw a się często operato rem preinkrementacji, N atom iast stojący za argum entem n azy w a się op era torem postinkremen ta cji. P odobnie, w p rzypadku o peratorów zm niejszania, m ów im y o operatorach predekrementacji i postdekrementacji.
4.1.4
Operator przypisania = To jest chyba najbardziej oczyw iste. Do tej pory w ielokrotnie posługiw aliśm y się ty m operatorem m = 34.88;
Pow oduje on, że d o obiektu stojącego po jego lewej stronie p rzypisana (podstaw iona) zostaje w arto ść w yrażenia stojącego po prawej. Z atem w zm iennej m zn ajd zie się liczba 34.88 Jest to o p erato r d w u arg u m en to w y , gdyż p racu je na dw óch arg u m en tach stojących po jego obu stronach. D obrze w iedzieć i pam iętać, że każde p rzy p isan ie sam o w sobie jest także w y rażen iem m ającym taką w artość, jaka jest p rzy p isy w an a. Z atem w arto ść w yrażenia: (x - 21
jako całości - jest także 2. Z au w aż int a, x = 4; cout << "Wart. wyrażenia przypisania:
" «
(a = x) ;
W rezultacie na ekranie pojawi się napis: Wart. wyrażenia przypisania: 4
Bow iem w y rażen ie (a=x) nie tylko, że w y k o n u je podstaw ienie, ale jeszcze sam o m a w arto ść rów ną tem u , co podstaw ia.
Rozdział. 4. O peratory O peratory logiczne
101
M o że się zd arz y ć, ż e p o obu stro n ach o p erato ra p rz y p isa n ia stać będą a rg u m e n ty ró żn eg o ty p u . N astąp i w ów czas niejaw na z am ian a ty p u wartości p rzy p isy w an ej na ty p zgadzający się typem tego, co stoi p o lew ej stronie. P rzy k ład o w o : int a; a = 3.14; cout << a;
n a stą p i tu zam ian a (m ów im y konwersja) typu zm ien n o p rzecin k o w eg o na typ i n t . Po prostu zo stan ie w zięta pod u w a g ę tylko część całkow ita liczby 3.14 — czy li 3 — i to też zo b aczy m y na ekranie. Konwersja taka nastąpi niejawnie (bo nie wyraziliśmy takiego życzenia). Troskliioy kompilator, w trakcie kompilacji tej instrukcji, może jednak dobrotliwie wypisać nam ostrzeżenie, przypominające o utracie części danych (przecież gubimy tu 0.14). Jak to zrobić jawnie, czyli jak powiedzieć kompilatorowi, że naprawdę zależy nam na tym "obcięciu" kawałka liczby - porozmawiamy kilka stron dalej (str. 114). O konw ersjach b ęd ziem y jeszcze m ów ić w osobnym rozdziale (str. 760).
W arto w spom nieć, że istnieją jeszcze inne o p erato ry p rzy p isan ia specyficzne d la języków C i C++. M o żn a się bez nich obejść, najlepszy d o w ó d , ż e obchodzi się b ez nich m atem atyka. Z drugiej strony jednak b ard zo u łatw iają życie. O tych in n y ch operatorach p rzy p isan ia b ędziem y m ów ić w je d n y m z dalszych parag rafó w .
O p e ra to ry lo g ic zn e Po operatorach arytm etycznych pora na o p erato ry logiczne. Jest ich kilka rodzajów . Łączy je to, ż e rezu ltat ich pracy jest ty p u b o o l (prawda lu b fałsz).
.2.1
Operatory relacji O p erato ry < m niejszy niż <= m niejszy lu b rów ny > w iększy niż >= w iększy lub rów ny są o p eratoram i relacji, w w yniku których otrzym ujem y o d p o w ie d ź typu: prawda lub fałsz. U ży w an ie tych o p erato ró w nie przy sp arza żadnych kłopotów . O to p rzy k ład : i f (a > 5) {
102
Rozdz. 4. O peratory O peratory logiczne cout << " a jest wieksze od 5";
N astęp n y m i operatoram i tego typu są operatory == jest rów ny != jest różny od Z au w aży ć należy, iż o p erato r == składa się z dw óch stojących obok siebie zn a k ó w ' = '. (Nie ma tam w środku spacji). Jest b ard zo częstym błędem om yłkow e postaw ienie tylko jednego - zam iast d w ó ch znaków = = . R ezultat takiej pomyłki m ożem y prześledzić na następu jącym przykładzie int a = 5, b = 100; cout « "Dane sa: a= " << a << ” b= " «
b;
//tu n a stę p u je fa ta ln a lin ijka if (a = b) cout << "\nZachodzi równość \n"; else cout << "\nNie zachodzi równość \n"; cout << "Sprawdzam ze: a= " << a << " b= " << b;
// ©
Po wykonaniu tego fragmentu programu na ekranie ujrzymy Dane sa: a= 5 b- 100 Zachodzi równość Sprawdzam ze: a= 100 b= 100
Dlaczego tak się stało ? O tó ż w instrukcji if O z am iast porów nania (o p erato r = = ) zap isaliśm y p rzy p i san ie (op erato r = ) . W iem y już z poprzednich p arag rafó w , że p rzy p isan ie to nie tylk o p rzy p isan ie, ale w d o d a tk u sam o w y rażen ie przy p isan ia m a w artość ró w n ą w artości będącej p rzed m io tem przypisania - czyli w naszym p rz y p a d k u zap is if(a = b ) ...
o d p o w ia d a zapisow i i f (100)...
100 je st ró żn e od zera, czyli p rz e z instrukcję i f trak to w a n e jest jak o w y n ik „ prawda“ i tym sam ym w y k o n y w an a jest instrukcja 0 . O tym , ż e zam iast p o ró w n a n ia nastąpiło p rzy p isan ie (podstaw ienie) p rzek o n u je nas re z u lta t w y p isa n y n a ek ran instrukcją © . Z n iszczy liśm y sobie p rzez n ieu w ag ę w artość zm ien n ej a. Z d ru g iej jednak stro n y nie został p o p ełn io n y żad e n błąd sk ład n io w y . Po p ro stu zam iast d z ia ła n ia i f (a == b ) ...
Rozdział. 4. O peratory O peratory logiczne
103
w y k o n aliśm y a
=
b;
i f (a)...
Z a te m w naszym p rz y k ła d z ie zrobiliśm y nie to, co chcieliśmy. J e d n a k są sytuacje, g d z ie chcem y w instrukcji i f takiego w łaśnie przypisania, a n ie p o ró w n an ia. C h cem y bow iem zyskać na czasie w ykonania sto su jąc zam iast d w ó c h instrukcji jedną. Jest to sk ładniow o p o p raw n e, jed n ak troskliw e ko m p ilato ry ostrzegają p ro g ram istę, g d y p rz y sp raw d zan iu w aru n k u i f znajdą tam operację przypisa nia. Tak na w szelki w y p ad ek . Przekonasz się, że najczęściej kompilatory mają wtedy rację. Jeśli jednak naprawdę chcesz w takiej instrukcji dokonać przypisania (a nie porównania), to unikniesz tych ostrzeżeń kompilatora umieszczając swoje przypisanie w dodatkowym nawiasie. if((x = y ) ) . . .
.2.2
Operatory sum y logicznej | | i iloczynu logicznego && O p erato ry te realizują II - su m ę logiczną - czyli operację logiczną LUB (altern aty w a) && - iloczyn logiczny - czyli operację logiczną I (koniunkcja) N a przykład: int k = 2; if ( (k == 10)
|| (k == 2) )
H a lte r n a ty w a
{ cout «
"Hurra!
k jest równe 2 lub 10 !
1 co czytam y: jeśli k ró w n e jest 10 lub k rów ne jest 2, to w tedy... P rzy k ład na koniu nkcję: int m = 22, k =77; if( (m > 10) && (k > 0) ) I jk o n iu n k c ja { ■ cout << "Hurra! m jest wieksze od 10 " << "i równocześnie k jest " « "wieksze od zera ! \n";
)
W P rzy p o m in am , że obliczanie wartości w yrażenia logicznego o d b y w a się w ten sposób, że w ynik „ prawda" daje rezultat 1, a w y n ik „fałsz" d aje re z u lta t 0.
104
Rozdz. 4. O peratory O peratory logiczne
W yrażenia logiczne tego typu obliczane są od lewej do praw ej. D obrze pam iętać, że kompilator oblicza wartość w yrażenia dotąd, dopóki na p ew n o nie wie, jaki będzie wynik. O znacza to, że w w yrażeniu (a == 0) && (m == 5) && (x > 32) kom pilator obliczał będzie od lewej do prawej, a jeśli pierw szy czynnik koniu nkcji nie będzie praw dziw y, dalsze obliczanie zostanie przerw ane. N ie m a bo w iem dalszej potrzeby: - co by tam dalej nie zostało obliczone - i tak koniu nkcja nie m oże być już praw dziw a. Kompilator oszczędza sobie więc pracy. W y d aw ać by się mogło, że nie m usim y o tym w szystkim w iedzieć. A jednak przy d aje się to. W yobraź sobie, że chciałeś być taki sprytny i upiec p arę pieczeni na jed n y m ogniu: int i = 6, d = 4; i f ( (i > 0) && (d++) )
{ cout << "warunek spełniony !"
) N ie dość, że wykonujesz operacje logiczne, to jeszcze chciałbyś, by p rz y okazji p racy na obiekcie d - zw iększyć go o 1. O tó ż jest to pułapka, bo jeśli pierw szy człon koniu nkcji nie będzie p ra w d z iw y , to ko m p ilato r uzna już, że nie opłaca się zajm ow ać dalszym i. W ten sposób nie d o jd zie do w ykonania w yrażenia d++, na co m oże tak liczyliśmy. P o d o b n ie jest w przypadku alternatyw y. Jeśli w w yrażeniu: i f ( i II
(k > 4) )
{ // .......
1 p ie rw szy człon alternatyw y (zm ienna i ) jest ró żn y od 0 (czyli „prawda"), to d alsze obliczenia nie są ju ż konieczne. Już w tym m om encie w iad o m o , że altern aty w a ta jest spełniona.
4.2.3
W ykrzyknik ! - czyli operator negacji O p e ra to r negacji jest operatorem jed noargum entow ym . Jego a rg u m e n t stoi p o p raw ej jego stronie. O perator ten ma postać ! (w y krzyknik) !i
P o w y ż sz e w yrażenie ma w ted y w artość „praioda", g d y i jest rów ne zero. O to p rz y k ła d y jak u żyw am y takiego operatora: int i = 0; bool gotowe; if (! i) cout << "Uwaga, bo i jest równe zeroNn";
Rozdział. 4. O peratory O peratory bitowe
105
gotowe = false; i f (! gotowe)
{ cout «
"jeszcze nie gotowe
!\n";
)
L3
O p e ra to ry b ito w e M ów iliśm y o o p erato rach arytm etycznych, operatorach logicznych, czas teraz na op erato ry , które są ch arakterystyczne d la tego specyficznego sposobu prze ch o w y w an ia inform acji w ko m p u terze - jak im jest zapis b in arn y . Jak po w szech n ie w iad o m o - w k o m p u terze inform acje (liczby i znaki) zak o d o w a n e są w poszczególnych kom órkach p am ięci za pom ocą ró żn y ch kombinacji z e r i jedynek. Te elem en tarn e jednostki inform acji nazy w an e są b itam i (ang. b itkaw ałek).
Inni mówią, że słowo bił jest skrótem od angielskiego binary digit (cyfra dwójkowa K o m p u ter nie o d n o si się d o każdego z takich bitów osobno. G ru p u je je w w ięk sze jednostki zw a n e słow am i. Słow o jest jed n o stk ą informacji p rz e tw a rz a n ą p rzez ko m p uter. Z ależnie o d kom putera, ró żn e m ogą być ro zm iary takich słów . Przykładow o: słow o w k om puterze k la sy IBM P C /A T sk ład ało się z kom binacji szesnastu bitów. C zyli z szesnastu z e r lu b jedynek. K o m p u ter p rzetw a rz a cale słow a, w których zw y k le zap isan e są liczby. Jednak nie w szystko w k o m p u terze m usi oznaczać liczby. To tak, jak w kokpicie sam olotu oprócz inform acji liczbowych o w ysokości, prędkości, w znoszeniu itd. - m am y też inform acje logiczne: podwozie schowane lub nie. O św ietlenie sam o lo tu włączone lub nie. W ko m p u terze inform acje takie m ożna zeb ra ć i um ieścić raze m na poszcze gólnych bitach jed n eg o słow a pamięci. Do p racy na tak um ieszczonej inform acji słu żą nam w łaśnie o p erato ry bitowe. O to lista operatorów bitow ych: « p rzesunięcie w lewo » p rzesunięcie w praw o & bitow y iloczyn logiczny (bitow a koniunkcja) bitow a su m a logiczna (bitow a alternatyw a) I A bitow a różnica sym etryczna (bitow e exclusive OR) ~ bitow a negacja W nazw ach tych o p erato ró w przewijają się słow a „bitow y", „ b ito w a ". D latego w przykładach ilustrujących zastosow anie tych o peratorów b ę d z ie m y p o k azy w ali jak działają one n a poszczególne bity. Z ak ład am , że m am y d o czynienia z kom puterem , gdzie o b iek t typu s h o r t i n t k o d o w an y jest na 16 bitach.
W
106
Rozdz. 4. O peratory O peratory bitowe
Dygresja: operator« (wypisujący na ekran) oraz operator» (wczytujący z klawiatury) Sym bole operatorów przesunięcia w lewo i w p raw o przypom inają n am p o zn a ne w cześniej operatory, którym i posługujem y się do w ypisyw ania na ekran i w czytyw ania z klaw iatury. cout << "podaj liczbę: cin >> x;
Z g a d z a się, to są rzeczyw iście te sam e sym bole. P rzez bibliotekę w ejścia/w y jś cia zo stały one tylko w ypożyczone. (N awet tak ie rzeczy da się robić w C++ !) N ie m a jednak ryzyka nieporozum ień. O p erato ry przesunięcia są rzeczyw iście op erato ram i przesunięcia w sytu acjach, gdy p o obu stronach sym bolu « lub » stoją arg u m en ty typu całkow itego. Jeśli arg u m en tem z lew ej stro n y jest c i n lub c o u t , to operatory te oznaczają przesy łan ie informacji z klaw iatu ry , lub na ekran. Ta pożyczka nastąpiła dlate go, ż e w ygląd o p erato ró w « i » dobrze su g eru je akcję, o którą chodzi. W ro zd ziale o tzw. p rzeład o w an iu operatorów d o w iesz się jak łatw o sam em u dla sw oich w łasnych celów robić takie pożyczki.
4.3.1
Przesunięcie w lew o « Jest to d w u arg u m en to w y op erato r pracujący n a argum entach (operandach) typ u całkow itego. zm ienna «
ile_miejsc
Bierze on w zór bitów z a p isa n y w danej zm iennej, przesu w a go o ż ą d a n ą ilość m iejsc w lew o i jako re z u lta t zw raca ten n o w y w zór. Bity z p raw eg o brzegu słow a u zu p ełn io n e zostają zeram i. Bity z lew eg o brzegu zostają zg u b io n e. P rzy k ład o w o na skutek takiej instrukcji short int a = 0x40f2; short int w; w = a << 3;
n astęp u je przesunięcie o trz y miejsca w lewo. O to , jak w yglądają po szczeg ó ln e obiekty. Dla łatwiejszej orientacji poszczególne bity słow a z g ru p o w a łe m w czw órki. a 0100 000011110010 w 0000 0111 10010000 Sam o b ie k t a nie został zm ien io n y . Posłużył on ty lk o jako w artość p o czątk o w a. R e z u lta t został złożony w zm iennej w. C zęsto ch o d zi nam o to, b y p rzesu n ąć bity d an ej zm iennej, a re z u lta t m a się z n aleźć z pow rotem w tej zm iennej. To nic tru d n eg o : a = a << 3;
Rozdział. 4. O peratory O peratory bitowe
107
(N ieb aw em p o zn am y jeszcze lepszy sp o só b na to: operator <<= ). Przypominam, że pokazuję tu działanie tego operatora na przykładzie obiektów typu s h o r t i n t po to, by ograniczyć się do wypisywania tu 16 bitów. Gdybym użył w tych przykładach obiektów typu i n t , a działo się to w komputerze 32 bitowym - musiałbym tu pisać 32 bity.
Przesunięcie w prawo » Jest to dw u a rg u m e n to w y operator pracujący na arg u m en tach (operandach) ty p u całkow itego zm ienna »
ile_m iejsc
B ierze on w zó r bitów zap isa n y w danej zm ien n ej, p rzesu w a g o o żą d a n ą ilość m iejsc w p raw o i jako re z u lta t zw raca ten n o w y w zór. Bity z p ra w e g o brzegu w y ch o d zące p o za z a k re s słow a są gubione. Jest tu jednak coś, co o d ró ż n ia go od o p isan eg o wcześniej kolegi: zachow anie p rz y u zu p ełn ian iu b itó w z lewej strony. O tóż: jeśli n asz op erato r p ra c u je na danej •
u n s i g n e d (bez znaku)
•
s i g n e d (zeznakiem ), ale d a n a zm ien n a m ieści w so b ie ak u rat liczbę nieujem ną
lu b
- w ó w czas bity z lew eg o brzegu są u zu p e łn ia n e zeram i. To jest g w a ra n to w a n e . unsigned short int d = 0x0ff0; unsigned short int r; r = d »
3;
O to , jak w ted y w y g ląd a rozkład bitów: d r
0000 1111 U l i 0000 0000 0001 1111 1110
Je d n a k jeśli pracuje o n na danej ty p u s i g n e d (ze znakiem ) i je st ta m liczba u je m n a, to rezu ltat m o ż e zależeć od typu k o m p u tera, na k tó ry m p racu jem y . M o że nastąpić u z u p e łn ia n ie brakujących z lew ej stro n y bitów z e ra m i, a m o ż e jed y n k am i. To - jako się rzekło - zależy ju ż o d ty p u k o m p u tera. signed short int d = 0xff00; signed short int r; r = d >> 3;
O to, jak w tedy w y g ląd a rozkład bitów - p o k azu jem y tu d w a w a ria n ty :
108
Rozdz. 4. O peratory O peratory bitowe d
1111 11110000 0000
r r'
0001 1111 1110 0000 1111 1111 1110 0000
N a p rzy k ład kom pilator Yisual C++ 6.0 postąpi tak, jak to pokazuje linijka z r '.
K3P
U w aga: Nw musisz się tego wszystkiego uczyć na pamięć z prostego powodu przesuwanie bitów raczej nie jest stosowane do pracy z obiektami typu s ig n e d
Stosuje się je raczej do obiektów, gdzie poszczególne bity mają dla nas jakie, specjalne znaczenie. Czyli nie jest to informacja "arytmetyczna". Skoro tak, przechowuje się ją w obiektach typu u n s ig n e d , a z nimisprawa jes\ prosta, niezależna od implementacji: bity z lewej uzupełniane są zerami już.
4.3.3
B itow e operatory sum y, iloczynu, negacji, różnicy sym etrycznej Te d w u a rg u m e n to w e o p erato ry także działają na arg u m en tach całkow itych, Ich d z iałan ie zilu stru jem y poniżej. (Znow u d la upro szczen ia o g ran iczy m y s it d o 16 bitow ych obiektów s h o r t i n t ) .
ie,VJX$ : jfT&wotn
short int m short int k
0x0fOf; 0x0ff0;
Il
iA t
short int a, b, c, d; a b c d
= m 4 k; = m |k; = ~m; = m A k;
// iloczyn bitowy / / s u m a bitowa / / negacja bitów / / różnica symetryczna (XO R) b itów
A oto, jak wyglądają poszczególne obiekty. Dane wejściowe i wartość wyrażeń: m
oooo n u oooo n u
k
0000 1111 1111 0000
m & k m | k
00001111 0000 0000 1111 1111 1111 0000 1111 0000 00001111
m A k
0000 HU 0000 1111
bitow a k o n iu nkcja bitow a a lte rn a ty w a bitow a negacja bitow e exclusive or
W z a s a d z ie sp raw a jest jasn a i n ie w y m ag a k o m e n ta rz a . P o d k reślić należ] je d n ak , jaka jest:
Rozdział. 4. O peratory Różnica między operatoram i logicznymi a operatoram i bitowymi
.4
109
R ó ż n ic a m ię d z y o p e ra to ra m i lo g ic z n y m i, a o p e ra to ra m i b ito w y m i i d ru g iej k o lu m n y p o n iższej tabeli O p erato ry , logiczne
O p erato ry bitow e
&&
&
11
1
P am iętajm y , że w yn ikie m d z ia ła n ia zw ykłe g o o p e ra to ra logicznego (np. ko n iu n kcja logiczna) jest w ynik „p ra w d a t “ lub J a łs ź ', czyli wynikiem jest słow o z zapisaną tam wartością 1 lub 0.
W przypadku operacji logicznych, czyli np. m && k k o m p ilato ra nie in teresu je rozkład bitów w zm iennej m czy w zm iennej k. S p ra w d z a o n tylko czy jest tam w artość ró w n a zero, czy ró żn a od zera. To sam o z d ru g im arg u m e n tem . W reszcie na tych d w ó ch w artościach ty p u „prawda" lub „fałsz" dokonuje k o n iu nkcji. W ynikiem jest 1 lubO (czyli „prawda" lu b „fałsz" ).
y
Natomiast operatory bitow e np. m & k z a g ląd ają d o w n ętrza d a n e g o słow a. N a p o szcze g ó ln y ch p o je d y n c z y c h bitach ty c h słó w d o k o n u ją o p e ra c ji k o n iu n k c ji. Czyli biorą p ie rw szy b it z jednej zm ien n ej i p ierw szy b it z drugiej zm iennej - i n a tych bitach w y k o n u ją operacji koniunkcji. R ezultatem jest 0 lub 1 i ten re z u lta t w staw iają d o p ie rw szeg o bitu zm ien n ej w ynikow ej. N a stęp n ie to sam o z d ru g im bitem i w szy stk im i dalszym i. W rezultacie działania operatora bitowego, otrzym ujem y w ięc w ynik będący słowem o specyficznym układzie bitów.
T ak i w y nikow y u k ła d b itó w m ożna in terp reto w ać jako liczbę, d la teg o te bitow e o p e ra to ry p rzy p o m in ają o p erato ry arytm etyczne. W n aszy m ostatnim p rzy k ła d zie w y rażen ie m & k m ożna z in te rp re to w a ć jako liczbę 3840
M o żesz się o tym p rzek o n a ć w ypisując w arto ść w yrażenia: cout «
(m & k ) ;
W D la czytelników , k tó rzy są bardziej w zrokow cam i, p rzy g o to w ałem d w a ry su n k i jeszcze raz ilustrujące różnicę m iędzy o p erato ram i logicznym i, a b ito w y m i.
110
Rozdz. 4. O peratory Pozostałe operatory przypisania Tak zatem w ygląda schem atycznie operacja logiczna m && k Rys. 4-1
________ 1) Zamiana na wartość logiczną
i|i|tli!o|o|o[ą
2) Operacja logiczna
Rezultat (typu b o o l)
A oto, jak schem atycznie w y g ląd a to przy operacji bitowej m & k Rys. 4-2
16 operacji na poszczególnych bitach
▼tftłttttTfttttt &
k |olo|o|o|ili|i|i|o|o|o~[oio o|o|o Rezultat (typu s h o r t in t)
Jest b ard zo w ażne, byś d o b rze zrozum iał różnicę m ięd zy o p erato ram i b ito w y mi, a logicznym i. G dybym był asystentem p ro w ad zący m zajęcia z C++, t< byłoby to jedno z p y tań na kolokw ium . Zajrzyj d o ćw iczeń na ko ń cu teg< ro zd ziału - jest tam zad an ie spraw dzające, czy to zagadnienie o p an o w ałeś (Ćwicz. XI, str. 128).
4.5
P o zo s ta łe o p erato ry przypisan ia Z p arag rafe m tym czekałem d o tej pory, m im o że jest banalnie prosty. P oznaliśm y już wcześniej o p erato r przypisania (p o d staw ien ia) = W za sa d z ie m oże on nam w ystarczyć, jednak d la w y g o d y m am y jeszcze dq dyspozycji następujące operatory: x—
T= >>=
<<
Rozdział. 4. O peratory W yrażenie w arunkowe
111
Ich d ziałan ie jest b a rd z o proste - po k ażem y to na przykładzie. Kto zrozum ie z a sa d ę w pierw szej linijce, nie m usi się trudzić z zap am ięty w an iem , bo analogie narzucają się sam e. I tak:
oznacza oznacza oznacza oznacza oznacza oznacza oznacza oznacza oznacza oznacza
i += 2 i -= 2 i
i i i i i i i
*= /= %= »= «= &= 1=
2
2
A=
2
2
2 2 2
2
i i + 2 i = i - 2 i - i * 2 i = i / 2
ss
i
i =
i
“
i
i i
i - i i —i i = i
%2 » 2 « 2 s. 2 1 2 A 2
A n alo g ia ta n ie je st je d n a k zupełna: jeśli i jest w yrażeniem , to w naszym now ym zap isie jest ono obliczane ty lk o jednokrotnie (w starym - dw a razy). M oże to mieć znaczenie, jeśli w yrażenie to m a jakieś d ziałan ie uboczne (np. inkrem entacja).
Zwracam też uwagę, że te operatory w yglądają tak, iż znak ró w n o ści następuje jak o drugi. Jeśli się p o m y lisz i napiszesz odw rotnie i =-2;
// c z y li i = -2;
to zrobisz postaw ienie liczby -2 do zm iennej i .
,
1 C iekaw ostka: W system ie d zie sią tk o w y m jeśli zapis liczby całkowitej p rzesu n ie m y o jedno m iejsce w lewo, to tak, jakbyśm y pom nożyli tę liczbę przez 10. N a przykład: 12 120 1200
<— o ry g in a ln a liczba <- p r z e s u n ię c ie o jed n o m iejsce w lew o <— p r z e s u n ię c ie o d w a m iejsca w lew o
P oniew aż w pam ięci k o m p u tera liczby przechow yw ane są nie w zap isie d zie siątkow ym , ale w zap isie dwójkowym, więc przesu nięcie o jedno m iejsce (jeden bit) w lewo - oznacza pom nożenie przez 2. Przesunięcie o 5 b itó w w lew o oznacza (szybkie) pom nożenie przez 32 (bo 32 = 2 * 2 * 2 * 2 * 2).
mm■■mmmmmm
.6
W y ra ż e n ie w a ru n k o w e Pow tarzam : wyrażenie, a nie instrukcja. Jest to w yrażenie, które o bliczane jest czasem na taką w artość, a czasem na inną. O to jego forma: warunek ? w artości
Przykładow o:
wartość2
112
Rozdz. 4. O peratory W yrażenie w arunkowe w y rażen ie to (jako całość) w zależności od spełnienia lub niespełnienia w aru n k u ( i >4) przyjm uje różną wartość. ♦> Jeśli w arunek jest spełniony — to w artość w yrażenia wynosi 15 ♦> Jeśli zaś nie spełniony — wartość tego całego w yrażenia w ynosi 10
Jak zauważyłeś, ten
w
a r u
n e k
umieściłem w nawiasie (mimo że nie jest to
wymagane). W yrażenie w arunkow e jest bardzo w ygodne, bo daje się "zapakować" d o
w n ętrza innych w yrażeń np. c * (x > y) ? 5 : 12; do zm iennej c zostanie podstaw iona liczba 5, jeśli rzeczyw iście x jest w iększe od y, a liczba 12, jeśli x nie jest większe od y . O to in n y prosty przykład: #include using namespace std; Z/***************************'************ ★ **★ ***★ **★ **★ **★ * int main() { int a; cout << "Musisz odpowiedzieć TAK lub NIE \n" « "jeśli TAK, to napisz 1 \n" « "jeśli NIE to napisz 0 \n" « " Rozumiesz ? Odpowiedz:
Musisz odpowiedzieć TAK lub NIE jeśli TAK, to napisz 1 jeśli NIE to napisz 0 Rozumiesz ? Odpowiedz: 1 Odpowiedziałeś: TAK, prawda ?
Komentarz O Tutaj tk w i to w yrażenie w aru n k o w e. W zależności od tego, czy z m ien n a zero w a czy niezerow a, w y rażen ie to jako całość m a w artość "tak" "N IE "
ajest
- gdy a jest różne od 0 - gdy a jest równe 0
Z au w aż , ż e w naszym p rz y p a d k u w artość w y ra ż e n ia nie jest tu w cale liczbą, tylko alb o takim , albo in n y m C -stringiem (jeśli w olisz: stalą tekstow ą).
Rozdział. 4. O peratory O perator s i z e o f
0
113
G dybyś tutaj, z a m ia s t odpow iedzieć 1, o d p o w ied ział 33, to nie m a problem u sp raw d zan y jest p rzec ież w arunek czy a jest równe, czy różne o d zera. R ezultat będ zie taki sam jak b y ś odpow iedział: 1.
W M oże na p ierw szy r z u t oka om ów ione w y rażen ie w aru n k o w e w ydaje się trochę m ało czytelne. Jed n ak zobaczysz, że w k ró tce bardzo je polubisz, g d y ż zaoszczę d zi Ci pisania.
ł.7
O p e ra to r sizeof O perato r sizeof jest to w ygodny o p erato r, który p ozw ala nam rozpoznać zachow ania k o m p ilato ra i kom putera, z k tórym i przyszło n am pracować. Jest to w ażne z d w ó ch pow odów : ♦> Te sam e ty p y obiektów (np. zm iennych) m ogą mieć w różnych im ple m entacjach ró ż n e wielkości. P rzykładow o: o tym , jaką p rzestrzeń rezer w uje k o m p ilato r na danej m aszy n ie dla obiektu typu int dow iedzieć się m ożem y używ ając w łaśnie teg o operatora. C++ p o zw ala użytkow nikow i na w ym yślanie sobie w łasnych typów obiektów . (B ędziem y o tym m ów ić w późniejszych rozdziałach). Często w ażne jest, b y w iedzieć ile pam ięci m aszyny zajm uje obiekt takiego now o w y m y ślo n eg o typu. O perato r ten stosuje się w ten sposób sizeof (nazwa J y p u ) albo sizeo f (nazwa_obiektu ]
t 2 w rezultacie o trzy m u jem y rozm iar obiektu danego typu p o d a n y w bajtach. O to przykład zastosow ania: #include using namespace std; int main() { int mm; cout << "Godzina prawdy. W tym komputerze " << "poszczególne typy\n"
1) 2)
ang. s i z e o f I s o tn e th i n g ] - rozmiar (czegoś) [czytaj: „sajz of") Dla wtajemniczonych: Rezultat operatora sizeof jest stałą ("bezznakowego”) typu s ize t.Co to oznacza w Twoim kompilatorze - zapisane jest w pliku nagłówkowym
114
Rozdz. 4. O peratory O peratory rzutow ania
cout cout cout cout cout cout cout
«
"maja następujące rozmiary w bajtach: \n";
« << << << << << << «
"typ char: \t" << sizeof(char) << sizeof(int) "typ int: \t" "typ short: \t" « sizeof(short) "typ long: \t" << sizeof(long) « sizeof(float) "typ float: \t" << sizeof(double) \t" « "typ double: \t "typ long double: \t"« sizeof(long endl;
<< endl; << endl; << endl; << endl; « endl; << endl; « ena double)
cout « "Nasz obiekt lokalny mm ma rozmiar: << sizeof(mm) << endl;
W rezultacie wykonania tego programu na komputerze z kompilatorem pracującym "32-bitowo" (rezerwującym dla obiektu i n t - 3 2 bity)
na ekranie ujrzymy tekst: Godzina prawdy. W tym. komputerze poszczególne typy maja następujące rozmiary w bajtach: __ _ char:. i1 typ 4 typ int: 2 typ short: 4 typ long: 4 typ float: typ double: 8 typ long double: 8 Nasz obiekt lokalny mm ma rozmiar: 4
4 .8
O p e ra to ry rzu tow an ia P ozn am y teraz operatory rzu to w an ia, albo inaczej - jaw nego p rzekształcania typu. Są to operatory jednoargum entow e. D ziałają one w ten sposób, ż e biorą w y rażen ie (lub obiekt) jakiegoś typu i jako re z u lta t zw racają w y rażen ie (lub obiekt) in n eg o typu. Innymi słowy operatorami takimi możemy przekształcić na przykład wyra zenie typu f l o a t na i n t , wy rażenie typu c h a r na d o u b le itp. W d a w n e j w ersji języka C++ m ieliśm y już do dyspozycji o p eratory rzu to w an ia. P ozw alają one rzutow ać w łaściw ie "wszystko n a w szystko". O k azało się to jednak z b y t drastyczne, w ięc stan d ard do d ał tu now e, bardziej su b teln e o p era tory. W łaściw ie w ięc o tych tradycyjnych p o w in n iśm y zapom nieć, jed n ak m o żesz się z nim i spotkać analizując cudze p ro g ram y . D latego m usim y o nich porozm aw iać. To jest w zasadzie końcówka tego rozdziału, więc możemy zatrzymać się nieco nad tym zagadnieniem. Nie przejmuj się jeśli nie wszystko tutaj potrafisz sobie jeszcze wyobrazić. Wystąpi tu parę pojęć, o których zasadni czo rozmawiać będziemy nieco później - siłą rzeczy do tych spraw musiel będziemy wrócić w stosowniejszych miejscach.
Rozdział. 4. O peratory O peratory rzutow ania
4.8.1
115
R zutowanie w ed łu g tradycyjnych (nie zalecanych) sp osobów O p erato r rz u to w a n ia (na inny typ) m oże m ieć d w ie form y (nazwa_innego_typu) wyrażenie
w zg lęd n ie nazwa_innego_typu (wyrażenie)
O tym , kiedy której z tych dw óch form użyć, p o ro zm aw iam y w przyszłości (str. 782), na razie lepiej jako zasad ę p rzy jąć stosow anie fo rm y pierwszej. Z obaczm y to na p rzy k ła d zie int duży = Oxffff; char mały; mały = (char) duży;
Jak w idać, m am y tu d w a obiekty różnego ty pu. Jeden jest ty p u i n t , d rugi typu c h a r . W trzeciej lin ii następuje operacja przy p isan ia (rzutow anej) w artości obiektu i n t do o b iek tu c h a r . P oniew aż obiekt typu c h a r nie pom ieści całej inform acji zaw artej w obiekcie typu i n t , d lateg o w rezultacie w obiekcie m a ły znajdzie się następ u jąca wartość: O xff
.
T akie przypisanie zaw artości obiektu ty p u i n t do obiektu typu c h a r m usi spo w o d o w ać u tratę b ardziej znaczących bitów . Inform acja zostaje okrojona do m niej znaczących 8 b itó w (tyle bitów m a d o dyspozycji obiekt typu c h a r ). C o zrobił operator rzu to w an ia ( c h a r ) ? «$♦ W ziął w y rażen ie d u ż y (czyli obiekt typu i n t o n azw ie d u ż y ) i jako rezu ltat zw ró cił obiekt chw ilow y ty p u c h a r . Była w nim już okrojona zaw artość o b iek tu du ży. O krojona d o rozm iaru c h a r . T en (chwilowy) o b iek t typu c h a r p rzy p isan y zostaje do (stojącego po lewej stro n ie operatora p rzypisania) innego obiektu c h a r o n azw ie m a ły .
Gdybyśmy nie zastosowali operatora rzutowania, to owo przypisanie także by nastąpiło (także ze stratą). Po co więc tu operator rzutowania? O d p o w ied ź jest prosta: D obry kom pilator w idząc, że w danej instrukcji nastę p u je ryzyko u traty p ew n ej części informacji pow inien nas ostrzec. O strzeżenie tak ie pow inno pojaw ić się w trakcie kom pilacji tej instrukcji pro g ram u . Kompilator widzi przecież, że zamierzamy tu przelać zawartość wielkiego garnka do małego. (Czyliprzypisać wartość obiektu intdoobiektu char). Jeśli n ato m iast jawnie zastosujem y w tym miejscu o p erato r rzuto w ania, to kom pilator - napotykając go - uzna, że zap ew n e posta wiliśm y g o tam św iadom ie (czyli św iadom i jesteśm y u traty bitów). O strzeżenia w tedy nie będzie.
116
Rozdz. 4. O peratory O peratory rzutow ania
O peracja rzutow ania (czyli jawnej konwersji typu) to b ard zo drastyczna o p era cja. C oś jakby rodzaj oszustw a. Przecież w deklaracji obiektu m ów im y, że obiekt jest jakiegoś typu, a rzutow aniem polecam y kom pilatorow i m im o w szy stk o potraktow ać d a n y obiekt inaczej. Do tej pory kompilator skrupulatnie sprawdzał nasze instrukcje, a nagle każemy mu tego zaniechać i wmawiamy mu, że to, co widzi, to naprawdę jest coś innego. W tak ich miejscach program iści robią najwięcej błędów , bo (na ich w łasną prośbę) czujne oko kom pilatora nad nimi chw ilow o nie czuw a. R zu to w an ia używ a się rzad k o . Jest bardzo niew iele takich sytuacji, g d y trzeb a je zastosow ać. Zatem , jeśli o g arn ie Cię gorączka rzu to w an ia, n ajp raw d o p o d o b niej po p ełn iłeś jakieś zasad n icze błędy w w ym yślaniu sw ego p ro g ram u . Z obaczyliśm y więc, jak m o żn a dokonyw ać rzu to w a n ia w ed łu g tradycyjnych sposobów . Teraz rzu to w an ie zaleca się robić inaczej.
4.8.2
R zutowanie za pomocą nowych operatorów rzutowania P o k azan e pow yżej operatory rzu to w an ia m ogą w łaściw ie rzutow ać "w szystko na w sz y stk o ”. Aby złagodzić tę broń m asow ego rażen ia, now y s ta n d a rd daje nam d o ręki specjalne operatory. s t a t i c _ c a s t ( wyrażeńie) c o n s t _ c a s t < nazwa_in negojtypu > (uryrażeń ie) _ d y n a m i c _ c a s t (wyrażenie)
r e i n t e r p r e t _cast (wyrażenie) W p o w y ższy ch zapisach w id zisz tak zw ane ostre nawiasy (czyli zn ak i w ięk szo ści i m niejszości). Do nich w p isu jem y , na jaki in n y ty p m a nastąpić rz u to w a n ie d an eg o w yrażenia. P oro zm aw iajm y kolejno o tych operatorach rz u to w a n ia . N ie poznam y teraz o nich całej p ra w d y , bo często o pow ieść w ym agałaby w ie d z y o zag ad n ien iach , k tó re w y stąp ią d o p ie ro w dalszych ro zdziałach. P o trak tu jm y w ięc opis tych o p erato ró w , jako ro d zaj u w e rtu ry .
Rozdział. 4. O peratory O peratory rzutow ania
4.8.3
117
Operator static_cast M ów iliśm y n ie d a w n o o takiej sytuacji double pi = 3.14; int m; m = pi;
O statnia z tych instrukcji pow oduje p rzy p isan ie w artości obiektu zm iennoprze cinkow ego d o o b iek tu całkowitego. O czyw iście najczęściej łączy się to z u tratą informacji. W n aszy m p rzy p ad k u , w obiekcie m znajdzie się liczba 3, gdyż taka jest część całkow ita liczby 3.14 N astąpi tu tak z w a n a niejaw na konw ersja, a jeśli m am y troskliw y kom pilator, to pracując nad tą linijką, w ypisze on nam ostrzeżenie o "stratności" tej konwersji. M ożem y jednak u p ew n ić go, że w łaśn ie o to obcięcie części ułam kow ej nam chodzi. Robim y to pisząc jaw nie o p erato r rzutow ania m = static cast(pi); cout «
//O
"obcięta do części całkowitej wartość pi = "
«
stafcie_cast (pi)
« «
"\noryginalna wartość pi = " « endl;
// ©
pi
O W idząc ten zapis, kom pilator rozum ie, ż e w łaśnie o konw ersję n am chodziło, (a nie że po prostu p rz e z zapom nienie p rzypisujem y zaw arto ść obiektu d o u b le do i n t ) . 0 Żebyś nie m yślał, ż e rzutow anie łączy się zaw sze z p rzy p isan iem - spójrz na tę instrukcję. C h odzi tu o pokazanie na ekranie w artości o b ie k tu . N ie w ypisujem y jednak tej w artości tak po prostu, ale najpierw p o d d ajem y ją rzu to w an iu na typ int. R ezultat tego rzutow ania zostaje d o p ie ro p rzek azan y stru m ien io w i cout do w ypisania na ekran.
Oto co pojawi się na ekranie: obcięta do części całkowitej wartość pi = 3 oryginalna wartość pi = 3.14
O pisany tu o p erato r służy w łaściwie do pozbycia się ostrzeżeń kom pilatora, w sytuacjach, gdy konw ersja i tak by nastąpiła, (bo jest m ożliw a). Do takich konwersji należą tak zw an e konw ersje standardow e. (Będziemy o nich bliżej mówić w paragrafie 9.9.4, na stronie 408). ♦♦♦ Są w śród nich takie "bezpieczne" konw ersje - jak zam ian a liczby całko witej 1 na liczbę zm iennoprzecinkow ą 1.0 ♦♦♦ Są też takie konwersje "stratne” - jak om aw iana w łaśnie konw ersja liczby zm iennoprzecinkow ej na liczbę całkowitą. M im o tej "stratności", takie konw ersje mają jakiś sens m atem atyczny.
118
Rozdz. 4. O peratory O peratory rzutow ania O g ó ln ie mówiąc: •
Jeśli m am y jakieś dw a typy X o raz V
•
i m oże niejaw nie nastąpić konw ersja (standardow a) je d n eg o typu na d ru g i (X »-> Y), ale niekoniecznie odw rotnie - czyli ry zy k o w n a jest ko n w ersja (Y ~ X ).
to d o o b u tych sytuacji konw ersji nadaje się o p e ra to r rzu to w an ia static cast. Do obu - czyli i d o tej oczywistej i d o tej ryzykow nej. X x; Y y; y = static cast(x); x = static cast (y) ;
/ / za m ia st le g a ln e g o I I z a m ia s t " n ie le g a ln e g o "
y = x;
x = y;
© W p r z y p a d k u tej sy tu a c ji o czy w iste j (X »-> Y), p o sta w ie n ie o p e r a to r a s t a t i c _ c a s t służy tylko n am , piszącym p ro g ram . Po prostu ten o p e ra to r p rz y p o m in a n am potem , ż e tu w łaśn ie do k o n u jem y konw ersji. O W p rz y p a d k u tej sytuacji ry zy k o w n ej, czyli k o n w ersji (Y p o zw o liłb y na niejaw ną konw ersję
X) - k o m p ilato r nie
x = y;
bo c z a se m m a o n a sens, a czasem nie. D latego k o m p ila to r pow inien na w id o k takiej in stru k cji zaprotestow ać. Jeśli jednak w iem y, że n a p ew n o taka k o n w ersja m a se n s (bo zn a m y d ziałan ie n aszeg o p ro g ram u ) - to aby takiej ko n w ersji d o k o n ać, m u sim y zrobić to jaw n ie, czyli użyć o p e ra to ra static_cast.
Dla niewtajemniczonych, na uspokojenie Przepraszam - wiem, że to wszystko brzmi trochę niejasno. To dlatego, że na razie jeszcze nie rozmawialiśmy o tym, że niektóre typy mogą być rodza jami innych. Tak jak typ samochód jest rodzajem typu pojazd. Dowolny obiekt typu samochód standardowo traktujemy jako pojazd. Do tego nie potrzebna jest jawna konwersja. Natomiast dowolnego pojazdu nie możemy potraktować jako samochód, bo może akurat tym razem ten pojazd to rowerek "Bobo". Jeślijednakwietny, z pewnych źródeł, że w tym konkretnym miejscu programu pod hasłem pojazd kryje się nie rowerek, ale rzeczywiście samochód, to używając operatora s t a t i c _ c a s t zapewniamy kompilator, że ta konwersja jest robiona na naszą wyłączną odpowiedzialność.
Rozdział. 4. O peratory O peratory rzutow ania
119
K rótkie p r z y p o m n i e n i e d la w ta je m n ic z o n y c h
Jak w iesz - d zięk i zw iązkom dziedziczenia - p ew n e typy m ogą być rodzajem innych. Jeśli m a m y hipotetyczny klasę (typ) - P o ja z d i pochodzący od niego typ (klasę) S am o ch ó d , to oczyw iste jest, że w skaźnik d o obiektu klasy Sam ochód m o ż e zostać przypisany d o w skaźnika klasy P o ja z d .
Pojazd p; Samochód s; Pojazd *wsk_poj; Samochód *wsk_sam = &s; wsk_poj = wsk_sam;
//o c z y w is te bo, sam ochód to
ro d za j pojazdu
N atom iast o d w ro tn e przypisanie, czyli przypisanie ad resu znajdującego się w łaśnie w e w sk aźn ik u do pojazdu - d o w skaźnika d o sam o ch o d u , nie zaw sze ma sens. Może przecież właśnie w tej chwili w programie wskaźnik do pojazdu ustawiony jest na obiekt typu rower. Dlatego na w szelki w ypadek kom pilator na takie p rzy p isan ie w skaźników nie pozw ala.
wsk_sam = wsk_poj;
// błąd kompilacji!
M ożliwe jednak, ż e dokładnie w iem y, iż w tym miejscu p ro g ram u w e w skaźni ku w sk _ p o j n ap raw d ę jest zaw sze ad res sam ochodu. Jeśli jesteśmy o tym przekonani, to n a w łasn e ryzyko m ożem y dokonać teg o przypisania, ale jawnie, czyli używ ając operatora rzu to w an ia s t a t i c _ c a s t
wsk_sam = static_cast(wsk_poj); Dokładniej o takich konwersjach rozm aw iam y na str. 935.
4.8.4
Operator const_cast Jak wiesz, niektóre obiekty mogą mieć przydom ek c o n s t .
120
Rozdz. 4. O peratory O peratory rzutow ania O czy w iście nie m a p ro b lem u z p rz y p isa n ie m w artości o b iek tu stałego d o obiek tu z w y k łe g o (niestałego). P rzecież stałą 15.6 m o ż e m y przypisać też zw ykłej zmiennej ty p u d o u b le . const
double stały = 15.6; double zwykły;
zwykły = stały;
S p raw a je st o czy w ista , d o tego nie trz e b a robić żad n e g o rz u to w a n ia . Bywają je d n ak in n e obiekty. W y o b raź sobie o b iek t, w którym p rzec h o w u je się nie stałą liczbę, ale a d re s liczby stałej. W n o rm aln y ch w a ru n k a c h takiego ad resu n ie m o ż n a przepisać d o in n eg o "adresow ego" o b ie k tu - takiego, k tóry stałości ( c o n s t ) by nie g w a ra n to w a ł. W iem , ż e to tro ch ę niejasne - g łó w n ie d latego, że n ie rozm aw ialiśm y o w sk a źn ik ac h , czyli obiektach p rzech o w u jący ch adresy. A b y je d n ak coś z tego p ara g ra fu C ię p rz e k o n a ło , p rzy to czę ta k i obrazek. M o n ik a z n a jakiś sekret. N a p rzy k ła d w ie (zna a d res), g d zie u k ry ta jest zło ta k o ro n a B olesław a K rzyw oustego. M o n ik a trak tu je ją jak n ie ty k a ln ą św iętość, czyli d la niej p o d tym a d re s e m jest obiekt z p rz y d o m k ie m c o n s t . K om u M onika p o d a te n ad res? Tylko oso bie, k tó ra obiekt b ęd ący p o d ty m ad resem ta k ż e trak to w ać będzie jak n ie ty k aln ą św iętość ( c o n s t ) , czyli osobie, k tó ra tej k orony nie p rz e to p i na złoto d en ty sty czn e. Proste. T ak je st w norm alnej sytuacji, a dba o to k o m p ilato r (p rzep raszam : z d ro w y ro zsąd ek M oniki). C zy M onika m o g łab y p o w ierzy ć ten a d re s k o m u ś, kto - o d b ie ra jąc ten ad res nic nie m ó w i o g w aran cji niety k aln o ści? N ie, k o m p ilato r z ap ro testu je . D o ch o d zim y teraz d o p o trzeb y istn ien ia o p erato ra c o n s t _ c a s t . O tó ż m o ż e się zd arzy ć, że M onika u d a się d o d y re k to ra P ań stw o w y ch Z b io ró w S ztuki n a W aw elu . Do takich lu d z i n ie m ów i się: "-P rzy sięg n ij, że tej k o ro n y nie p rzeto p isz n a z ło te zęby". M onika p o p ro s tu p o stan aw ia, ż e jem u m o żn a zau fać. P rzek azu jąc m u a d re s tej k o ro n y , o tym sw o im p o stan o w ien iu in fo rm u je k o m p ila to r za p o m o cą o p erato ra c o n s t _ c a s t . D zięki te m u o p erato ro w i m o ż liw e jest p o d a n ie a d re s u n aw et o b iektow i, k tó ry p rzech o w u je zw y k łe ad resy . T akie rz u to w a n ie m a n astęp u jącą fo rm ę c o n s t _cast
(wyrażenie o typie z const)
N ie p o k a ż ę tutaj p rz y k ła d u , bo najczęściej tego o p e ra to ra u ż y w a się w obec w sk a ź n ik ó w , a o nich jeszcze nie ro zm aw ialiśm y . Z atem w ró c im y d o tego w § 8 .1 6 , na str. 321.
Rozdział. 4. O peratory O peratory rzutow ania
121
O p erato r ten m o że służyć jedynie d o pozbycia się (lub n adania) przydom ka c o n s t . N ie m o ż n a próbow ać u p ie c dw óch całkiem ró żn y ch pieczeni p rz y jednym o g niu - czyli nie tylko, ż e u su w a ć c o n s t , a p rz y okazji rzutow ać na zupełnie inny ty p . Jeśli tego b y śm y chcieli, w ystarczy to zrobić w dw óch etapach. N ajp ierw jedno, zaraz p o te m drugie. O pow iedziałem tutaj długą historię jak pozbyć się p rzy d o m k a c o n s t . A co z p rzy d o m k iem v o l a t i l e ? Spraw a jest bardzo prosta: Omawiany tutaj operator rzutowania c o n s t _ c a s t ma, co prawda, w nazwie c o n s t , ale może posłużyć tak sam o do usunięcia przydom ka v o l a t i l e . 1 mmmmmmmmmmBiMMmmmmmMmimmmmmimmmmmmmmmmmimmmimmtmmmmmmmmmmmmmmmmmmmmammmmmmMfimMmmm
Zatem nie istnieje operator volatile_cast, jego rolę spełnia c o n s t c a s t .
4.8.5
Operator dynam i c _ c a s t W paragrafie pośw ięconym s t a t i c _ c a s t zobaczyliśm y p ew n ą "niesymetry czną" s y tu a c ję - to znaczy konw ersja z typu Sam ochód na ty p Po j a zd ma sens, każdy samochód jest pojazdem, ale o dw rotna jest ryzykow na, więc - co praw da - m ożem y ją jaw nie dokonać, ale tylko na w łasn ą odpow iedzialność, bonie każdy pojazd jest samochodem. O perator rzu to w an ia d y n a m ic _ c a s t jest w łaśnie p o to, by nie było to "wyłącznie na n aszą w łasną odpow iedzialność". O p erato r d y n a m ie c a s t zrobi rzutow anie tylko pod w arunkiem , że w danej konkretnej chw ili ma to sens. Jeśli nie jest możliw e poinform uje nas o tym (już w trakcie w y k o n y w an ia program u). O ile s t a t i c _ c a s t spraw dza nas tylko w trakcie pracy kompilatora, to d y n a m ie c a s t sp raw d za nas już w trakcie pracy programu. Do spraw y tej w rócim y w stosownej, b ardzo odległej chw ili, na stronie 1011.
4.8.6
Operator reinterpret_cast Dobrze by było, b y ś już w iedział, co to są wskaźniki. P oniew aż jeszcze o tym nie m ów iliśm y, w ięc silą rzeczy w yjaśnienia będą ogólnikow e. Bliżej porozm aw ia my o tym d o p iero w § 8.5 na stronie 261. Tutaj więc w yjaśnim y sobie tylko ogólnie sens tego operatora. Otóż niebaw em dow iesz się, że w języku C++ istnieje w sp an iałe narzędzie zw ane w skaźnikiem . W skaźnik to taki obiekt, k tóry (zam iast zw ykłej w artości) przecho w uje adres jakiegoś innego obiektu. Twój notes z ad resam i krew nych i znajom ych jest jakby zb io rem takich w skaź ników. W pam ięci kom putera adres w ygląda nieco inaczej niż w życiu - nie m a miast, ulic i n u m eró w domów. Są tylko num ery dom ów . Przepraszam : num ery komórek pam ięci. To taki num er m oże przechow yw ać w skaźnik.
122
Rozdz. 4. O peratory O peratory rzutow ania Ja często m ó w ię, ż e wskaźnik po ka zu je na inny obiekt. Tak, jak w skaźnikiem na lekcji geo g rafii, m o ż e sz pokazać na coś n a m apie.
Są różne typy wskaźników Ju ż w iesz p ra w ie w sz y stk o o w sk a źn ik ac h - jeszcze tylko je d n a spraw a: otóż w T w o im n o tesie są a d re s y jakichś o b ie k tó w ty p u (klasy) "człow iek”. Tym czasem w pam ięci k o m p u te ra m ogą być o b iek ty b a rd z o różnych ty p ó w . N a przykład ty p u double, alb o typu d e t e k t o r _ p r o m i e n i o w a n i a _ g a m m a , albo o k i e n k o ekranowe. P rzyznaj sam , ż e nie chciałbyś a d re s ó w tak różnych o b ie k tó w m ieć w ty m sam y m notesie. Z atem w języku C++ m o ż e sz zd efin io w ać o so b n e ty p y w skaźni k ó w d o p o k azy w an ia na ró ż n e typy obiektów . In n e są więc dla p o k a z y w a n ia na obiekty ty p u double, inne d la ob iek tó w typu char, a jeszcze inne d la p o k azy w an ia n a obiekty typu dane o
krewnym_lub_znajornym.
D zięki tem u u n ik a się zam ieszania. K o m p ilato r będzie strz e g ł, byś p rzez p o m y łk ę nie u m ie śc ił ad resu obiektu ty p u d o u b l e w e w sk a ź n ik u d o pokazy w a n ia n a ty p w a l e c drogowy.
Dotarliśmy wreszcie do sensu istnienia operatora reinterpret_cast O p e ra to r ten jest w ła ś n ie po to, by n ak ło n ić k o m p ilato r d o p o z w o le n ia nam na ta k k ark o ło m n ą operacje. Zatem, jeśli by istniał w naszym programie typ (klasa) walec drogowy, to dzięki operatorowi r e i n t e r p r e t _ c a s t m oglibyśm y do takiego wskaźnika wpisać adres Cioci Leonii.
Zobaczmy działanie tego operatora na przykładzie pseudo kodu Z a łó ż m y , ż e w o b iek cie o n azw ie a d r _ c h a r m a m y ad res o b ie k tu ty p u char, a o b ie k t adr w a l e c n ad a je się (jedynie) d o p rz e c h o w y w a n ia a d re s ó w obiektów ty p u w a l e c drogowy. K om pilator n ie d o p u ś c i d o takiej in stru k cji: adr walec = adr char;
O p e ra to r r e i n t e r p r e t cast p o stę p u je z e w sk a ź n ik a m i ta k , jak to m ogły ro b ić d o tej p o ry te trad y cy jn e, p o k a z a n e p rz e d te m sp o so b y rz u to w a n ia .
Rozdział. 4. O peratory O perator: przecinek
123
Jest w ięc tak sa m o niebezpieczny, jak te stare sposoby. Jest jednak różnica łatw o go zn aleźć w program ie. Jeśli p ro g ram źle działa, a podejrzew am y, że to z pow odu zb y t śm iałych konw ersji - w ystarczy p o szu k ać ed ytorem w tekście program u słó w ''reinterpret_cast". Jeśli jesteś początkującym program istą, najlepiej tego o p erato ra nie używ aj.
jest 79. Poszczególne w yrażenia obliczane są od lewej do praw ej. Zatem w y ra ż ę nie z praw ej stro n y przecinka m oże już skorzystać z rezu ltatu w yrażenia z lewej. Np.: (x = 6, y = 100 * x) W artością y, (i ty m sam ym całego teg o w yrażenia w naw iasie) jest 600.
4.10
Priorytety operatorów Na zakończenie spójrzm y na o p erato ry zestaw ione w tabeli. Do tej pory unikaliśm y ro zm ó w o priorytecie różnych operatorów . N adm ieniłem tylko, że m nożenie ma p ierw szeństw o przed dodaw aniem . Zestaw iam y w ięc teraz operatory w tabeli, w której na sam ej gó rze są operatory o najw yższym priorytecie. Pod w zg lęd em priorytetów o p erato ry dzielą się na 18 grup. Nie przeraź się, jeśli w tabeli zobaczysz operatory, których nie rozum iesz. N ie m ów iliśm y jeszcze o wszystkich, m im o to taką tabelę d o b rz e jest oglądać w całości.
3)
Uwaga dla wtajemniczonych: W wywołaniu funkcji mamy argumenty oddzielone przecinkiem. Zapamiętaj: tamten przecinek, to nie jest ten operator, o którym rozma wiamy teraz. Gdyby tak było, to do funkcji były wysyłany tylko ten argument najbardziej z prawej...
124
Rozdz. 4. O peratory Priorytety operatorów
I . , ,,,
— —■— ... , ...
",
„ /; --— :
j
.... '
Operatory Pri O rytet
•■v ■Ą t ■ ... • .. ■ N azw a
określenie zakresu lo
nazwa globalna
•. v
. .■•■<;
Z asto so w an ie . *" ** •“ s nazwa_klasy:: składnik nazwa_przcstrzeni_nazw: : składnik :: nazwaglobalna :: nazwaJęwalifikowana
_ f—.—
17
16
: 15 ■
wybranie składnika wybranie składnika element tablicy wywołanie funkcji rzutowanie post inkrementacja post dekrementaq'a identyfikacja typu wg nazwy. identyfikacja typu wyrażenia rzutowanie (spr. w trakcie wykonania) rzutowanie (spr. w trakcie kompilacji) rzutowanie nie sprawdzane konwersja const/volatile
obiekt . składnik wskaźnik -> składnik wskaźnik [ wyrażenie ] funkcja ( lista_argurnentów ) typ ( wyrażenie) Iwartość ++ Iwartość — t y p e i d ( typ) t y p e i d (wyrażenie) dynam i c cast (wyrażenie) s t a t i c _ c a s t < t y p > (wyrażenie ) r e i n t e r p r e t cast (wyrażenie) c o n s t c a s t < t y p > (wyrażeriic) s i z e o f (wyrażenie) s i z e o f (typ)
rozmiar obiektu rozmiar typu pre inkrementacja pre dekrementacja dopełnienie do 2 negacja jednoargumentowy minus jednoargumentowy plus adres czegoś odniesienie się wskaźnikiem stwórz (rezerwuj nowy obiekt) stw órz obiekt i inicjalizuj go stwórz w określ, miejscu stwórz w określ, miejscu i inicjalizuj zlikwiduj (anuluj rezerwację) zlikwiduj tablicę (macierz) rzutowanie (konwersja typu)
++lwartość - - Iwartość ~ wyrażenie ! wyrażenie - wyrażenie +wyrażeriic &Iwartość * wyrażenie new typ n e v typ (lista wyrażeń) new adrjniejsca typ new adrjniejsca typ (lista wyrażeń) d e l e t e wskaźnik d e l e t e [ ] wskaźnik (typ) wyrażenie
Wybór składnika wskaźnikiem: - i nazw ą obiektu - i wskaźnikiem do obiektu
obiekt . * wsk_do_składnika wsk ->* wsk_do_składnika
-------
Rozdział. 4. O peratory Priorytety operatorów
125
O p e ra to ry --------------—— ——--------------------------.
Orytet
N azw a
,4
mnożenie dzielenie reszta z dzielenia (modulo)
wyrażenie * wyrażenie wyrażenie / wyrażenie wyrażenie * wyrażenie
13
dodaj (plus) odejmij (minus)
wyrażenie + wyrażenie wyrażenie - wyrażenie
12
przesunięcie w lewo przesunięcie w prawo
wyrażenie « wyrażenie wyrażenie » wyrażenie
11
mniejsze niż mniejsze lub równe większe od większe lub równe
wyrażenie wyrażenie wyrażenie wyrażenie
10
równe nie równe
wyrażenie == wyrażenie wyrażenie != wyrażenie
9
iloczyn bitowy
wyrażenie & wyrażenie
8
bitowa różnica symetryczna
wyrażenie * wyrażenie
7
bitowa suma
wyrażenie | wyrażenie
6
koniunkcja (AND)
wyrażenie S6 wyrażenie
5
alternatywa (OR)
wyrażenie \ | wyrażenie
4
wyrażenie warunkowe
wyrażenie ? wyrażenie : wyrażenie
3
zwykłe przypisanie mnóż i przypisz dziel i przypisz modulo i przypisz dodaj i przypisz odejmij i przypisz przesuń w lewo i przypisz przesuń w prawo i przypisz koniunkcja i przypisanie alternatywa i przypisanie exclusive or i przypisanie
Iwartość = wyrażenie Iwartość *= wyrażenie Iwartość /= wyrażenie Iwartość %=wyrażenie Iwartość += wyrażenie Iwartość -= wyrażenie Iwartość « = wyrażenie Iwartość » = wyrażenie Iwartość s= wyrażenie Iwartość | = wyrażenie Iwartość *= wyrażenie
—
—
—
I
I
< wyrażenie <- wyrażenie > wyrażenie >= wyrażenie
126
Rozdz. 4. O peratory Łączność operatorów
Operatory
2
rzucenie wyjątku
th io w wyrażenie
1
przecinek
w yra żen ie ,
w y r a ż e n ie
Chciałbym Cię też uspokoić p o raz drugi. Nie m usisz w cale uczyć się tych priorytetów . Bez tego m ożna sobie doskonale d ać rad ę posługując się naw iasam i.
Zapamiętaj tylko, że: 1) M nożenie, dzielenie, dodaw anie, odejmowanie m ają w zględem siebie takie sam e priorytety, jak to pam iętam y ze szkoły. W iedząc o tym, nie będziesz m usiał u ży w ać nawiasów w tak banalnych sytuacjach. 2) Skom plikow ane w yrażenia logiczne lepiej zaopatryw ać w n aw iasy. N ie ty Iko dlatego, że &&jest mocniejsze niż I I. Także dlatego, że w yrażenia takie stają się czytelniejsze. Inaczej będziesz produkow ał zapisy w rodzaju i c b & s s
II a == n
nad którym i zaw sze trzeba się zastanowić, a ry zy k o popełnienia błędu jest kolosalne. 3) Z apam iętaj też, że naw iasy okrągłe ( ) - oznaczające w yw ołanie funkcji oraz klam ry f ] - odniesienie się od elem entu tablicy - m ają b ard zo w ysoki priorytet, w szczególności w yższy niz tajem niczy jed n o arg u m en to w y operator * (czyli odniesienie się do obiektu pokazyw anego przez w skaźnik).
4.11
Ł ąc zn o ść o p erato ró w Z obaczm y to na przykładach. M ów im y, że jednoargum entow y o p erato r ! (negacja) jest: p raw o stro n n ie łączn y - co oznacza, że d ziała na arg u m en t stojący p o jego praw ej stronie !x
Rozdział. 4. O peratory Ćwiczenia
127
Z apam iętaj p ro stą zasadę: Operatory jednoargumentowe są prawostronnie łączne. a*£>
ui
mm* 1 «
W p rz y p a d k u operatorów d w u arg u m en to w y ch łączność określa, w jaki sposób g ru p o w an e je st w ykonyw anie w y rażen ia.
Na przykład: lewostronna łączność operatora + oznacza, że wyrażenie a + b+
c + d + e
odpowiada wyrażeniu ((((a + b) + c) + d) + e)
Zapam iętaj: Operatory d w u a rg u m e n to w e są lewostronnie łącznie, a z tej zasady wyła mują się tylko operatory przypisania (które są prawostronnie łączne).
O to przykład tego w yłam yw ania się: p raw o stro n n a łączność (dw uargum entow ego) o peratora p rzy p isan ia = oznacza, ż e w yrażenie a = b = c = d = e
o d p o w iad a w y rażen iu (a = (b = (c = (d = e))))
W k12 Ćwiczenia Co to znaczy, że operator jest dwuargumentowy lub jednoargumentowy. Wyjaśnij to na przykładzie dwóch operatorów: dwuargumentowego (minus) i jednoargumentowego (minus). Są to dwa różne operatory oznaczane tym samym symbolem. Posługując się operatorem "reszta z dzielenia" napisz program, który wypisywał będzie na ekranie kolejne liczby całkowite począwszy od 1 do 100, a po zakończeniu każdej dziesiątki (10,20,30, ...) wypisze tekst zakończona dziesiątką . Używając operatora "reszta z dzielenia" - napisz program, który bedzie wypisywał na ekranie kolejne litery alfabetu. Pierwszych osiem (A-H) ma być wypisywanych po dwie litery w jednym rzędzie, a co do następnych, to program ma zadać użytkownikowi pytanie (po liter w rzędzie ma wypisywać dalej). Zależnie od tej odpowiedzi następne litery mają być wypisywane według nowej zasady. Wskazówka: kolejne litery alfabetu możesz uzyskiwać w ten sposób, że do obiektu typu c h a r (o nazwie np. z nak) załadujesz literę 'A ana s tę pn ie kolejno będziesz ją inkremen tował (znak++). W ten sposób treścią obiektu znak będzie kolejno litera B, C ,D itd. Jaka jest różnica między operatorem preinkrementacji (++x), a operatorem postinkre mentacji (x++)?
128
Rozdz. 4. O peratory Ćwiczenia Jaka liczba pojawi się na ekranie na skutek działania następujących instrukcji i n t a = 4; c o u t << (a = 7) << e n d l ;
VI
Jaka jest różnica między operatorami arytmetycznymi, a logicznymi - jeśli chodzi o typ rezultatu. Omów różnicę między operatorem = a operatorem == Który z operatorów =, == może się znaleźć w wyrażeniu warunkowym instrukcji i f i Czyli czy poprawne są konstrukcje:
a) i f (x = y) . . . b) i f (x == y ) . . . Co zrobić, by uniknąć tu ostrzeżeń kompilatora? Jaka jest wartość wyrażenia napisanego poniżej (w drugiej linijce). b o o l ru c h = f a l s e ; ! ! ! ruch; W wyrażeniu warunkowym instrukcji i f może wystąpcie nawet kilka operatorów logicznych. Załóżmy, że obiekty a, b, c są typu i n t . W poniższej instrukcji widzimy więc trzy człony alternatywy. i f ( (a < b)
||
(a < 15)
II
(a > (c = 15))
)
{/*...*/
)
Pytanie 1: Który człon obliczony zostanie najpierw i jakie będą następne. Pytanie 2: Wyjaśnij działanie członu trzeciego. Pytanie 3: Oto instrukcja, która logicznie odpowiada powyższej i f ( (a > (c = 15))
II
(a < b)
II
(a < 15) ) { / * . . . * / )
Wytłumacz jaka jest między nimi różnica, jeśli chodzi o końcową wartość obiektu c. To ćwiczenie jest na sprawdzenie zrozumienia różnicy między operatorami logicznymi z bitowymi. W tabelce podane mamy dwa obiekty (dla uproszczenia 16 bitowe) Podaj rozkład bitów wartości wyrażeń występujących w dalszej części tabeli ..I' Rozkład bitów s h o rt in t a;
0
0
0
0
1
1
1
1
0
0
0
0
1
1
1
1 ’r ■'
s h o r t i n t b;
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
Wypełnij rozkłady bitów dla poniższych wyrażeń , a & b i 3 && b r a
| b
a
1| b
—— !1 1
1
'—
i1!
Rozdział. 4. O peratory Ćwiczenia
129
Wyjaśnij działanie operatora += w poniższym fragmencie int m = 3;
m += 2 ;
Podaj jaką wartość na skutek tych instrukcji będzie miał obiekt m. Na argumentach jakich typów pracuje operator przesunięcia w lewo (<<). Czym za pełniany jest bit z prawego brzegu słowa? Zerami czy jedynkami? Co się dzieje z najbardziej znaczącym bitem? Napisz jak będzie wyglądał rozkład bitów w obiekcie rezultat po wykonaniu poniż szych instrukcji. short int a = Of; short int rezultat = a «
2;
Operator przesunięcia >> przesuwa bity danego słowa o żądaną ilość miejsc w prawo. Co, po przesunięciu, dzieje się z najmniej znaczącym bitem? Jeśli chodzi o uzupełnia nie bitów z lewej strony słowa przez operator >>, może to zależeć od implementacji. Są jednak sytuacje, gdy mamy pewność, że najbardziej znaczący bit będzie wypełniany zerami. Z jakim typem obiektu powinniśmy pracować, by mieć tę pewność? (Wiadomo, że musi to być obiekt typu całkowitego, ale z jakim "przydomkiem"?). Jak zmieni się wartość obiektu x na skutek poniższych instrukcji?
v
int x = 0; x =-l; x -=1;
Ile razy trzeba przesunąć w lewo bity poniższego obiektu, by w rezultacie jego wartość została pomnożona przez 1024? int
ddd = 3 ;
Załóżmy, że mamy obiekt typu int o nazwie x. Podaj wartość poniższego wyrażenia, gdy x równa się 100. (x %100)
? "Kraków" : "Londyn"
Mamy obiekt typu double o nazwie aaa. Które z poniższych wyrażeń jest poprawne a) b) c) d)
XXII
sizeof sizeof sizeof sizeof
(aaa) (double) (double + 2) (double) + 2
Mamy obiekt typu double o nazwie mm. Napisz dwie formy zastosowania tzw. trady cyjnego rzutowania tego obiektu na typ int. Na czym polega "stratność" takiego rzutowania? Co jest wartością następującego wyrażenia (55.2 , 12, x++, a = b + 6,
4)
Co to znaczy, że dany operator ma wyższy priorytet niż inny? Jak - nie pamiętając priorytetów operatorów - można mimo wszystko zapanować nad kolejnością obliczania danego skomplikowanego wyrażenia?
130
Rozdz. 4. O peratory Ćwiczenia
Jaka jest łączność operatorów jednoargumentowych? Poniżej widzisz fragment kodu. Ostatnia instrukcja to przypisanie. Ujmij w nawiasy poszczególne człony tego przypisania - tak, aby nawiasy nie zmieniły kolejności obliczania wyrażenia. in t b = d = a =
XXVII
a, b, c, d; 15; 2; b = c = d;
Jaka wartość ostatecznie znajdzie się w poszczególnych obiektach? Ujmij w nawiasy poszczególne człony wyrażenia mnożenia - tak, by nawiasy me zmieniły kolejności obliczania wyrażenia. in t a, b, c, d, r e z u lta t; r e z u lt a t = a * b * c * d ; Jaka jest łączność operatorów dwuargumentowych?
X Ćwiczenia dla zaawansowanych Poniższe ćwiczenia dotyczące rzutowania są raczej dla zaawansowanych. W tym bowiem rozdziale, dokonaliśmy tylko pierwszego przybliżenia do stosowania tych operatorów. Zatem na te poniższe pytania odpowiedzieć powinni tylko "wtajemniczeni
Do czego służy operator con st_cast? Czy operatorem r e in te r p r e t_ c a st można rzutować obiekt typu double na typ int? Mamy dwa typy A i Btakie, że możliwa jest niejawna konwersja obiektu typu B na typ A • Czy zatem kompilator pozwoli, by operatorem s t a t ic _ c a s t dokonywać konwersji odwrotnej? (z typu A na typ B). Czy operatorem s t a t ic _ c a s t można rzutować typ v o l a t i l e in t na typ int? Jak zatem to zrobić? W programie zastosowaliśmy rzutowanie typu X na typ Y za pomocą operatora sta tic_ in t< Y > . Kompilator przy kompilacji tej instrukcji uznał ją za poprawną. Czy istnieje więc jakieś ryzyko, że rzutowanie takie będzie błędem? Mamy dwa wskaźniki. Jeden z nich jest ustawiony na obiekt o nazwie sze sc. c o n s t in t sze sc = 6; c o n s t in t *wsk_c = &szesc, i n t *wsk_no£m; Napisz instrukcję (rzutowania), która pozwoli przepisać adres ze wskaźnika wsk_c do wskaźnika wsk_norm. Mamy trzy klasy. Podstawową (ssak) i jest ona typem polimorficznym, czyli mj przynajmniej jedną funkcję wirtualną. Mamy też dwie klasy pochodne od klasy ssak Nazywają się one: p ie s , kot. Oto definicje obiektów tych klas, a także wskaźników
Rozdział. 4. O peratory Ćwiczenia
131
pies azor; kot mruczek; ssak *wsk_ssaka; pies *wsk_psa; Załóżmy, że dwa powyższe wskaźniki mają już sensowną wartość. Które z poniższych instrukcji w trakcie kompilacji tego fragmentu programu uznane zostaną przez kompilator za błędne? a) b) c)
wsk_ssaka = wsk psa; wsk_ssaka = static_cast (wsk_psa); wsk_ssaka = dynamie cast (wsk_psa);
//... d) e) f)
"
wsk_psa = wsk_ssaka; wsk_psa = static_cast (wsk_ssaka); wsk_psa = dynamic_cast< pies*> (wsk_ssaka) ;
Która z powyższych (poprawnych składniowo) instrukcji może sprawić, że przez pomyłkę w obiekcie wsk psa znajdzie się adres kota? Która z tych instrukcji może sprawić, że w pewnych warunkach (jakich?) w obiekcie wskjpsa znajdzie się adres 0 (NULL) ? Które z czterech operatorów rzutowania sprawdzają słuszność rzutowania w trakcie kompilacji, a które w trakcie wykonania? Kiedy takie sprawdzenie odbywa się w przypadku "tradycyjnych" (czyli nie zalecanych) sposobów rzutowania? Jak można ustawić wskaźnik na konkretny adres znany nam liczbowo z dokumentacji? Czy operator dynamie cast może nam pomóc przy rzutowaniu wskaźników dowolnej hierarchii klas? Jaki warunek musi spełniać klasa podstawowa, by operator dynamic_cast uzyskał od niej potrzebne mu informacje?
"
W
132
Rozdz. 5. Funkcje
—
— i— —
—■
Funkcje
5
T e d n ą z najsym patyczniejszych cech now oczesnych języków pro g ram o w aI nia jest to, że m ożna w nich posługiw ać się podprogramami. P o d p ro g ram to J5kby m ały program we w łaściw ym program ie. D zięki p o d p ro g ram o m m o ż e my jak b y definiow ać swoje w łasn e „instrukcje": Jeśli n ap iszem y sobie p o d p ro g ram realizujący op erację liczenia pola k o ła na p o d staw ie zad an eg o prom ienia - to tak, jakbyśm y języ k p ro g ram o w an ia w y p o sażyli w n o w ą instrukcję um iejącą w łaśnie to obliczać. O d tej pory - ile ra z y w p ro g ram ie potrzebujem y obliczyć pole koła - w y w o łu jem y nasz p o d p ro g ram , a jest to tak proste, jak napisanie zw ykłej instrukcji. P o d p ro g ram , który jako re z u lta t zw raca jakąś w arto ść, n azy w am y po p ro stu funkcją. W języku C++ w szy stk ie p o d p ro g ram y n a z y w a n e są funkcjami. F u n k cję w y w o łu je się przez p o d a n ie jej nazw y i u m ieszczo n y ch w n aw iasie arg u m en tó w .
Oto przykład programu zawierającego funkcję: N azy w a się ona kukułka, a jej z ad a n iem jest kukać ż ą d a n ą ilość razy. To, ile razy - j e s t p a ra m e tre m w ysyłanym d o funkcji. #include using namespace std; int kukułka(int ile);
Z/**********************************
int main()
i
int m = 20; cout << "Zaczynamy" << endl;
133
Rozdział. 5. Funkcje
int kukułka(int ile) {
for(int i = 0 ; i < ile ; i++) ( cout «
"Ku-ku !
)
return 77; Wykonanie tego programu objawi się na ekranie jako: Zaczynamy
Ku-ku ! Ku-ku ! Ku-ku ! Ku-ku ! Ku-ku ! Na koniec m = 77 ^
Przyjrzyjmy się temu programowi O Funkcja ma swoją nazw ę, która ją identyfikuje. Jak pamiętamy z poprzednich rozdziałów, wszelkie nazwy - przed pierwszym odwołaniem się do nich m uszą zostać zadeklarowane. Wymagana jest więc także deklaracja nazwy funkcji. W tym miejscu programu w id zim y deklarację n a zw y funkcji. Deklaracja ta mówi kompilatorowi: kukul ka jest funkcją wywoływaną z argumentem typu int,a zwracającą jako rezultat wartość typu i n t . Powtórzmy: Przed odwołaniem się do nazw y wymagana jest jej deklaracja. Deklaracja, ale niekoniecznie od razu definicja. Sama funkcja może być zd efin io w an a później, n a w e t w zu p ełn ie in n y m pliku. Z defi niować funkcję, to znaczy po prostu napisać jej treść. Definicja funkcji znajduje się w O © W ywołanie funkcji, to po prostu napisanie jej nazwy łącznie z nawiasem, gdzie znajduje się argument przesyłany do funkcji. Ponieważ spodziew am y się, że w rezulacie swojej pracy funkcja zwróci jakąś wartość, dlatego w idzim y przypisa nie tej wartości rezultatu do zmiennej m. © Na dowód, że funkcja rzeczywiście zwróciła jakąś wartość, i że nastąpiło przypi sanie tej wartości do obiektu m- wypisujemy go w tym miejscu na ekran. © Tu się zaczyna definicja funkcji. Definicja ta zawiera tzw. ciało funkcji, czyli wszystkie instrukcje wykonywane w ramach tej funkcji. Dwie klamry © i © określają ten obszar ciała funkcji, czyli po prostu jej treść.
134
Rozdz. 5. Funkcje
0 Jest to m o m en t, g d y funkcja k o ń czy swoją pracę i w ra c a do miejsca skąd został* w y w o łan a [ang. return - p o w ró t].1 Obok słow a r e t u r n w id zim y w artość, którj zdecy d o w aliśm y zw rócić jako rezu ltat w y k o n an ia tej funkcji.
W Z atrzy m ajm y się trochę przy deklaracjach funkcji. O to kilka przykładów : double kwadrat(int bok); void fun(int stopień, char znaczek, int przypadek(void); char znak_x(); void p i n (...);
int nachylenie);
Z au w aż, że na końcu każdej z przed staw io n y ch dek laracji jest średnik. P rzeczytajm y teraz te deklaracje. •
kwadrat jest funkcją (w y w o ły w an ą z jednym arg u m en terą typu int), k tó ra w rezultacie zw raca w arto ść typu double.
•
fun jest funkcją (w yw oływ aną z 3 arg u m en tam i typu: int char, int), k tó ra nie zw raca ż a d n e j w artości. Słow o voi<
[ang. void- p ró żn y ] służy tu w łaśn ie d o w yraźnego z a z n a c z ę nia tego faktu. Po takiej deklaracji - jeśli k o m p ilato r zobaczy, że p rzez za p o m nienie p ró b u jem y uzyskać z tej funkcji jakąś w arto ść ostrzeże nas, sygnalizując błąd. •
przypadek to funkcja, która w y w o ły w a n a jest bez ż a d n e g o
arg u m en tu , a k tó ra zw raca w arto ść ty p u int. •
znak_x to funkcja, która w y w o ły w a n a jest bez żad n y ch a r g u m entów , a w rezultacie zw raca w a rto ść ty p u char.
•
pin jest funkcją, którą w yw ołuje się z bliżej nieo k reślo n y m
(teraz jeszcze) arg u m en tam i, a k tó ra nie zw raca ż a d n e wartości.
Uwaga dla programistów C: Jest tu z m ia n a w sto su n k u d o klasycznego C. P usty n a w ia s w deklaracji funkcji np. f ( ) ; o z n a c z a _ w języku C - d o w olną liczbę arg u m en tó w , czy li to sam o, co f ( . . . ) ♦♦♦ _ w języku C++ - b rak jakichkolw iek a rg u m e n tó w , czyli to sam o , c< f(void) Jeśli m a sz p rzy zw y czajen ia z klasy czn eg o C, to za p a m ię ta j, że o d tąd Deklaracja
1)
[czytaj: „rytem "]
in t f();
-o z n a c z a
i n t f (v o id ) ;
135
Rozdział. 5. Funkcje Funkcja często wywołuje inną funkcję
N a z w y arg u m e n tó w um ieszczone w n aw iasach deklaracji funkcji są nieistotne d la kom pilatora i m o żn a je pom inąć. Pow tarzam : nazw y, a nie typy a rg u m e n tó w . D lateg o deklarację funkcji void fun(int stopień, char znaczek,
int nachylenie);
m o ż n a napisać także jako void fun(int, char, int);
T o dlatego, że w deklaracji p o w iadam iam y k o m p ilato r o liczbie i typie arg u m en tó w . Ich nazw y nie są w tym m om encie istotne. (To będ zie w ażne w definicji). M asz w ięc dw a sp o so b y deklarow ania funkcji. N am aw iam Cię jednak do sto so w a n ia pierw szego sposobu - tego z n azw am i argum entów . P rzem aw iają z a nim d w a w zględy praktyczne: ♦> jest on czytelniejszy dla program isty, p rzy p o m in a bow iem lepiej czym zajm uje się funkcja, ♦> łatwiej taką deklarację napisać. M im o ż e je st dłuższa. Po p ro stu pracując w edytorze p rzen o si się we w łaściw e m iejsce (u nas - na górę pro g ram u ) pierw szą linijkę definicji funkcji i staw ia się na końcu tej linijki średnik.
W Z a u w a ż , że w naszym ostatnim program ie definicję funkcji (czyli to miejsce, g d z ie znajduje się treść, ciało funkcji) - o d d zieliłem od reszty p ro g ram u za pom o cą linijki kom entarza składającą się z rz ę d u gw iazdek. R ad zę Ci robić pod o b n ie. Bardzo to po lep szy czytelność Tw oich p rogram ów . Jeśli m a sz plik, w k tó ry m mieści się trzydzieści definicji funkcji, to łatw iej się w nim orientow ać. Ju ż na pierw szy rz u t oka w idać, gdzie się jed n a funkcja kończy, a zaczyna d ru g a .
W czasie w ykonyw ania funkcji może nastąpić w y w ołanie innej funkcji. W ów czas to w ykonyw anie bieżącej funkcji się chw ilow o zaw iesza, a ro zp o czy n a się w y k o n y w an ie drugiej. Po jej zakończeniu stero w an ie w raca do tej pierw szej funkcji - w miejsce, gdzie została przerw ana, (czyli do instrukcji znajdującej się p o w yw ołaniu funkcji drugiej). Z n am y to z życia codziennego. G dy w kuchni w y konujem y funkcję gotoiwniejpierogćrw - m ożem y dojść do p u n k tu , w któ ry m nie w iem y, co dalej i
136
Rozdz. 5. Funkcje Zw racanie rezultatu przez funkcję
konieczna jest konsultacja ze specjalistą. Dzwonimy wtedy do mamy, czyi wywołujemy funkcję rozmowaJelefoniczna. Po zakończeniu rozmowy telefonicznej wracamy do gotowania - kontynuują< to, co zrobiliśmy do tej pory. Ponieważ funkcja gotowania pierogów nie zakończyła się, a jedynie została ni chwile przerwana - więc wszystkie zmienne lokalne (np. ciasto na pierogi, nadal czekają na nas w stanie, w jakim je przed chwilą zostawiliśmy. Poniżej zobaczymy to na rysunku. Zauważ, że każda z funkcji ma swoją właśni (lokalną) zmienną o nazwie zm ienna.
f u n k c j a B ()
<
double
zmienno;
Rys. 5-1. Funkcja, w trakcie swej pracy, może wywołać inną funkcję
5.2
Z w ra c a n ie rezu ltatu p rze z fu n k c ję W naszym przykładowym programie funkcja w yw oływ ana była z argumen tern i zwracała jakąś wartość. Przyjrzymy się teraz bliżej temu mechanizmów przekazywania. Oto przykład programu liczącego potęgi danej liczby: jfinclude using namespace std ; long p otęga (in t sto p ie ń , long lic z b a ) ;
//*x******-******»************»********************************
in t main() ł in t p o cz, koniec; cout << "Program na o b lic z a n ie potęg lic z b " << "calkowitych\n"
<< "z zadanego p r z e d z ia łu \n" << "Podaj początek p r z e d z ia łu : "; c in » pocz; cout << "\nPodaj koniec p r z e d z ia łu : "; c in >> k on iec; // pętla drukująca wyniki z danego przedziału
137
Rozdział. 5. Funkcje Zw racanie rezultatu przez funkcję for(int i = pocz ; i <= koniec ; i++) cout << << << << << <<
i " do kwadratu = " p o t ę g a (2, i) " a do sześcianu = " p o t ę g a (3, i) endl;
// i - w y w o ł a n i e f u n k c j i //
w y w o ła n ie f u n k c j i
} //***************■********************************************** long potęga(int stopień, long liczba)
.
{
long wynik = liczba; for(int i = 1 ; i < stopień ; i++)
{
.
wynik = wynik * liczba; // z w ię ź le j m o ż n a z a p is a ć to sam o ja ko :
wynik *= liczba;
return wynik;
// ®
/ / *************************************************************
3
Jeśli n a p y tan ia p ro g ra m u o d p o w iem y np. 10 o ra z 14 to
na ekranie pojawi się: Program na obliczanie potęg liczb całkowitych z zadanego przedziału Podaj początek przedziału: 10 Podaj koniec przedziału: 14 10 do kwadratu = 100 a do sześcianu = 1000 11 do kwadratu = 121 a do sześcianu = 1331 12 do kwadratu = 144 a do sześcianu = 1728 13 do kwadratu = 169 a do sześcianu = 2197 14 do kwadratu = 196 a do sześcianu = 2744
O N a jp ie rw zw róćm y u w a g ę , jak odbyw a się z w ra c a n ie w arto ści fu nkcji. W sp o m i n aliśm y już, że robim y to p rzez instrukcję return. Staw ia się p o p ro stu przy niej ż ą d a n ą w artość. U n a s to w y g ląd a w ten sposób: return wynik;
m o g ą być też inne w arian ty : return (wynik + 6) ; return 12.33;
Jeśli sto i tam w y rażen ie (np. wynik + 6), to n ajpierw o bliczana jest jego w arto ść, a następ n ie d o p ie ro ow a w artość jest „ p rz e d m io te m z w ro tu . Jest jeszcze coś zask ak u jąceg o . O tóż jeśli d ek laracja funkcji jest taka: long potęga(int stopień, long liczba);
to z n ac zy , że funkcja ma zw racać jako rezu ltat w arto ść ty p u l o n g (pam iętam y , ż e jest to jeden z typów liczb całkowitych). T y m czasem koło słow a r e t u r n stoi liczba zm ien n o p rzecin k o w a 12.33 C o w te d y ? C zy jest to b łąd ?
138
Rozdz. 5. Funkcje Zw racanie rezultatu przez funkcję N ie zaw sze. N astąp i bow iem p ró b a niejaw nej zam ian y (konw ersji) typu. W n a szym p rz y p a d k u będzie to konw ersja typu zm ien n o p rzecin k o w eg o na ty p 1 ong. K o m p ilato r dom yśli się jak to zrobić i w rezultacie funkcja zw róci w arto śj 12. Nie zawsze jednak taka konwersja może się odbyć. Jak bowiem zamienić tzw. wskaźnik - na liczbę zmiennoprzecinkową? Kompilator wtedy nam nie podaruje i w trakcie kompilacji oznajmi błąd. Z ap y tasz p ew n ie: „A w łaściw ie, co to znaczy, że funkcja z w ra c a jakąś w artość? W iem y ju ż ja k to się robi, ale co to znaczy?!" To b ard zo w a ż n e pytanie. O d p o w ie d ź je st b ard zo prosta, ale d o b rze ją sobie w y ra ź n ie uśw iad o m ić. Z n a czy to, że w y ra ż e n ie będące w y w o łan iem tej funkcji m a —sa m o w sobie - jaką^ w artość. W n a sz y m p rzy p ad k u w y rażen ie
( p o t ę g a (2, 2)
)
m a sam o w so b ie w artość 4. N iezależn ie od tego, że jest to w y w o łan ie funkcji Skoro w ięc ta k ie w yw ołanie jest w y rażen iem m ającym ja k ą ś w arto ść, to m o żn a go użyć w in n y c h , w iększych w y rażen iach . P rzy k ład o w o :
7 + 1 . 5 + pot ega( 2, 2) + 100 o d p o w ia d a u n a s w yrażeniu:
7 + 1 . 5 + 4 + 100
Jeśli funkcja je s t zad ek laro w an a jak o zw racająca ty p v o i d - czyli niezw racająci niczego - a m y , p rz e z nieu w ag ę, u ży jem y ją w tak im w y ra ż e n iu , to ko m p ilato i o strzeże n as, ż e p o p ełn iam y błąd. T o jed n a z w ielu zalet o b o w ią z k o w y c h d ek la racji funkcji. Także, g d y b y ś m y w ew n ątrz definicji takiej funkcji o b o k sło w a r e t u r n p o sta w ili coś o p ró c z śred n ik a return 6;
// błąd, gdy funkcja zwraca v o id
to k o m p ilato r w y k ry je błąd. M ieliśm y b o w iem nic nie z w ra c a ć , a zw racam y ? T akże o d w ro tn ie : jeśli z ad e k laro w aliśm y , że funkcja m a coś zw racać, a prz; słow ie r e t u r n stoi sam śred n ik , k o m p ila to r u zn a to za n a s z b łą d . O biecałeś, żj coś tu b ęd zie, a nie m a? P ew nie o czy m ś z ap o m n iałeś !
Z powodów historycznych... Z a u w aż y łeś ju ż za p e w n e , że w n aszy ch d o ty c h czaso w y ch p ro g ra m a c h funkcj J m a i n m a ty p re z u lta tu o k reślo n y ja k o i n t i n t m a i n () Z ap y tasz p e w n ie :
139
Rozdział. 5. Funkcje Zw racanie rezultatu przez funkcję
D laczeg o z a te m w naszych d o ty c h c z a so w y c h p ro g ra m a c h dotych czas w ciele funkcji m a i n nie b y ło instrukcji return? O d p o w ie m tak: N ie b y ło , bo nie chciałem , b y w y g ląd ało tru d n iej. (Z aw sze to je d n a in strukcja w iecej...). Skorzystałem z te g o , ż e w funkcji m a i n kom pilator p o z w a la na n a m na o p u sz c z e n ie instrukcji return. Jest ta k z p o w o d ó w "h istorycznych" - po p ro s tu d aw n iej nie b y ło to w ym agane. A by te "stare" p ro g ra m y n a d a l m ożna było k o m p ilo w ać - d o s ta n d a r d u w pro w a d z o n o n astęp u jącą z a s a d ę :
S chem aty czn ie m o żn a to po k azać tak: int main()
{
.
..
// wszystkie instrukcje funkcji m ain re tu rn 0 ;
)
//
«- tu kompilator doda sobie tę instrukcję '
Z a te m w main, m o ż e m y tę instrukcję o p u szcza ć. W innych fu n k cjach - nie. F u n k cja m a i n m a po p ro s tu specjalne p raw a. N a p rz y k ła d nie d ek laru jem y jej n ig d z ie . Poza ty m nie m o ż e m y jej w yw ołać z innej funkcji.
Musisz określić typ rezultatu funkcji U w a g a: w starych w ersjach języka C++ m o żn a b y ło w deklaracji funkcji opuścić ty p re zu ltatu . K o m p ilato r, w id ząc taką d e k la r a c ję - u z n a w a ł, ż e fu n k cja zw raca re z u lta t typu int. T eraz, w stan d ard zie, ta p ra k ty k a została p o tę p io n a . Krótko m ó w iąc, m u sisz w y ra ź n ie napisać jaki jest ty p zw racan y .
Dla dociekliwych Z obaczy liśm y , że funkcja m a i n zw raca w arto ść (rezultat). T utaj b y ło to zero, ale p rz e c ie ż m oże być d o w o ln a liczba typu int. Po co jest ta w arto ść rezu ltatu , sk o ro i tak n ikt z niej n ie skorzysta? (Przecież w ra z z zak o ń c zen ie m funkcji m a in , kończy się p ro g ram ). R zeczyw iście, p ro g ram s ię kończy, ale k o m p u te r n ie w yłącza się. K toś w końcu u ru c h o m ił ten p ro g ram . To ten ktoś w łaśnie o trz y m a teraz ten re z u lta t zw ra can y p rz e z funkcję main. K to to jest? Kto u ru c h o m ił program ? Nie, nie m y! M y tylko p o p ro siliśm y o to sy ste m operacyjny (p isząc kom endę, lub klikając na jakąś ikonę). To ten system o p erac y jn y (Linuks, W in d o w s) uruchom ił p ro g ra m i to on o trz y m a ten rezultat. M o żliw e też, że p ro g ram zo stał u ru ch o m io n y p rz e z i nny p ro g ra m . W ów czas to sy ste m operacyjny p rz e k a ż e rezu ltat tam tem u d ru g ie m u p ro g ram o w i.
140
Rozdz. 5. Funkcje Stos
Co może być wartością rezultatu działania funkcji main? Oczywiście liczba całkowita. W artość jej jednak jest d o w olna, możesz tam wpisać, co chcesz. Jeśli rezultat ten m a posłużyć jako w iadom ość dla innego program u, to po prostu trzeba się z osobą piszącą ten d ru g i program um ów ić, jaka liczba oznacza jaką w iadom ość. Jeśli nie m a takich potrzeb, to najczęściej wartość zw racan a oznacza czy p rog ram się w y k o n ał dobrze, czy z b łędam i. Co zaś oznacza błąd - decydujem y my sami.
. Błędem może być na przykład uruchomienie programu w czwartek, a me w niedzielę. Tradycyjnie zw ro t w artości 0 oznacza p opraw ne zakończenie całego p ro g ram u . W artości in n e niż zero mogą oznaczać różnego typu b łęd y w ykonania.
5.3
S to s M oże słyszałeś o czym ś takim w k om puterze, co się n azy w a stos. Jeśli nie, to w yobraź so b ie taki obrazek: W szystkie swoje książki trzy m asz w biblioteczce. G dv którąś potrzebujesz, to idziesz d o biblioteczki w yjm ujesz i czytasz, potem w kładasz z po w ro tem . Wiem, w iem jestem naiw ny. P raw d a jest taka, że najpo trzebniejsze książki leżą na Tw oim b iu rk u w postaci m niejszego lub w iększego sto su . Zdjęcie książki z takiego stosu jest szybsze niż p rzec h ad zk a do biblioteczj ki. Tak sam o p ostępuje kom puter. M a także swój p o d ręczn y stos, na którym trzym a p ew n e dane. Koniec obrazka. Jeśli w obrębie funkcji definiujem y jakieś zm ienne, to są o n e przechow yw ane najczęściej w łaśn ie na stosie - czyli w tej podręcznej pam ięci. Stos m a w iele ciekaw ych w łasności - znają je p rz e d e w szystkim Ci, k tó rz y program ują w asem blerze. Z niektórym i w łasnościam i stosu zap o z n am y się w następnych paragrafach. ..IIHII IIM WII «M ■ ■ ■ ■ '
P rze s y ła n ie a rg u m e n tó w d o funkcji p rzez w a rto ś ć Zajmijmy się teraz sposobem p rz e sy ła n ia argum entów d o funkcji. N ajpierw spraw a n azew n ictw a. Załóżm y, że m a m y funkcję void a l a r m (int stopień, { cout « << « <<
int wyjście)
"Alarm " « stopień "stopnia" " skierować sie do wyjścia nr " wyjście << endl;
} Z ałóżm y też, z e w pro g ram ie w y w o łu jem y tę funkcję tak: int a, m;
//
...
141
Rozdział. 5. Funkcje Przesyłanie argum entów do funkcji przez w artość alarm{l, 1 0 ) ; alarm(a, m ) ;
N a z e w n ic tw o jest ta k ie: n azw y stopień , wyjście
- k tó re w id z im y w p ierw szej linijce definicji fu n k cji są to tzw. argum enty form alne funkcji. C zasem z w a n e p a ra m e tra m i form al nym i. W a ż n e jest tu słow o: formalne. T o n ato m iast, co p o ja w ia się w n aw iasie w m o m en cie w y w o ły w a n ia tej fu n k cjic z y li w n aszy m p r z y p a d k u 1, 10, a, m
| to tak z w a n e argumenty (param etry) aktualne. C z y li takie a rg u m e n ty , z k tórym i ak tu a ln ie funkcja m a w y k o n a ć pracę. Często b ę d ę na to m ó w ił prościej: argumenty w y wołania funkcji - b o z ty m i arg u m e n ta m i fu n k c ję w y w o łu jem y . D la osw ojenia się p o d a jm y obrazek z życia: sk le p to jakby funkcja. void sklep(int klient);
W sklepie o b słu g u je s ię klientów . W sk lep ie m ó w ią o nas - „ m u s z ę obsłużyć klienta". K lient jest a rg u m e n te m fo rm aln y m funkcji sklep. J e d n a k do sk lep u p rz y c h o d z ą jacyś k o n k re tn i lu d zie. G dy d o s k le p u w ch o d zi C la u d ia , to o n a jest a rg u m e n te m a k tu a ln y m tego sklepu. To d la niej w tym m o m en cie pracu je sk lep . W sklepie n ik t n ie n azy w a jej inaczej, jak tylko (b a rd z o ) formalnie: klient(-ka). N aw et n ie zn ają jej n azw isk a, je d n a k aktualnie k lie n tk ą jest C lau d ia. G d y C laudia w y jd zie z e sklepu, a za ch w ilę w ejdzie S ybilla, to o n a staje się a rg u m e n tem a k tu a ln y m tego sk lep u . S klep na nią i tak z n o w u m ów i „k lie n tk a ", ale n ikt nie tw ie rd z i, że to ta sam a osoba. T a k a jest w ięc różnica m ięd zy a rg u m e n tam i ak tu aln y m i, a fo rm aln y m i. I A rg u m e n ty form alne to jest to, jak n a p a ra m e try m ó w i sobie I w śro d k u funkcja, n a to m ia s t I a rg u m e n ty a k tu aln e to to, co a k tu a ln ie stosujem y w k o n k retn y m I w y w o łan iu funkcji.
W KIP
A rg u m e n ty p rzesłan e d o funkcji - tak, jak to w n aszy m p rz y k ła d z ie - są tylko k o p ia m i. Jakiekolw iek d ziałan ie na nich nie d o ty czy o ryginału. O to dow ód: void zwiększ(int formalny) formalny +- 1000; / / z w ię k s z e n ie lic z b y o 1 0 0 0 cout << "W funkcji modyfikuje arg formalny\n\t"
O
142
Rozdz. 5. Funkcje Przesyłanie argum entów przez referencję «
" i teraz arg formalny = " «
formalny « e n d l ;
} Jak widać, w tej funkcji zw iększa się w arto ść argum entu form alnego funkcji, Funkcję tę w yw ołujem y na przykład w tak im fragm encie p ro g ram u . int aktu = 2 ;
cout << "Przed wywołaniem, aktu = " << aktu << enal; zwiększ(aktu); cout << "Po wywołaniu, aktu = " << aktu << endl;
Jeśli wykonamy taki fragment programu to otrzymamy: Przed wywołaniem, aktu = 2 W funkcji modyfikuje arg formalny i teraz arg formalny = 1002 Po wywołaniu, aktu = 2
N ależy u św iad o m ić sobie bardzo w a ż n ą rzecz: Do funkcji przesyłam y tylkc w artość liczbow ą zm iennej aktu (p aram etru aktualnego). W artość ta służy dc inicjalizacji p a ram etru form alnego, czy li zm iennej lokalnej tw orzonej przea funkcję na stosie. Jest to więc jakby zro b ien ie kopii w o b ręb ie funkcji. Funkcja pracuje na tej kopii. Czyli w n a sz y m przykładzie d o d a n ie 1000 (w miej* scu O ) nie n astąp iło do kom órki pam ięci, gdzie tkwi a k t u , ale do tej zm ien n e lokalnej na stosie, g d zie mieści się k o p ia (o nazw ie formalny). Po opuszczeniu funkcji ten frag m e n t stosu jest n iszczony, znika więc też k o p ia , nie m a śladu, z« coś tam robiliśm y.
Bardzo pouczająca przypowieść o babci Jeśli jeszcze to nie jest dla Ciebie, C zy teln ik u , jasne, to ro zw ażm y taki obrazek M y, (program ) - robim y teściowej (zm ien n a) zdjęcie (kopia). Zdjęcie to dajemjl dziecku (funkcja), by się pobaw iło. D ziecko ukochanej b ab ci dorysow uje wąs> (dodanie 1000). K iedy skończy zab aw ę zab aw k i są sp rząta n e, a śmieci wyrzu* cane (niszczenie kopii - zdjęcia). Po skończonej zabaw ie p a trz y m y na naszt (żyw ą) teściow ą: teściow a w ąsów nie m a.
5.5
P rze s y ła n ie a rg u m e n tó w p rz e z referen cję Powyżej o p isa n y sposób przesyłania a rg u m e n tó w znany b y ł program istom C Język C++ p rzy n o si jeszcze inny sp o só b przesyłania arg u m e n tó w . Przez refe rencję. Czyli p rz e z przezw isko. ^ j W dalszej części książki powinniśmy mówić: „przez referencję", jedna dopóki się z tym nie oswoisz - używać będziemy też równolegle terminu „przez przezwisko" O to przykład: #include using namespace std; void zer(int wart,
int &ref);
H
®
143
Rozdział. 5. Funkcje Przesyłanie argum entów przez referencję
^************************************************************/
int m a i n ()
X int
a = 44, b = 77;
cout << "Przed wywołaniem funkcji: zer \n"; cout << "a = " << a << ", b = " << b << endl;
0
zer (a, b ) ;
II
cout << "Po powrocie z funkcji: zer \n"; cout << "a = " << a « ", b = " << b « endl;
U O
} void zer(int wart,
int &ref)
{ cout << "\tW funkcji zer przed zerowaniem \n" cout « "\twart = " « wart << ", ref = " << ref << endl;
// ©
wart = 0; ref = O t
II O
cout << "\tW funkcji zer po zerowaniu \n"; cout << "\twart = " << wart << ", ref = " << ref << endl;
// ©
} 3
;
// ©
W rezultacie działania tego programu na ekranie pojawi się Przed wywołaniem funkcji: zer a = 44, b = 77 W funkcji zer przed zerowaniem wart = 44, ref = 77 W funkcji zer po zerowaniu wart = 0, ref = 0 Po powrocie z funkcji: zer a = 44, b = 0
Komentarz P a trz ą c na ekran z a u w a ż a m y , że funkcja, k tó ra chciała w y zero w ać d w a obiekty a i b w y słan e do niej jako arg u m en ty - zask o czy ła nas. O czyw iście o b ie k t a jest n ietk n ięty . To znam y. Jed n ak obiekt b m a w arto ść 0. D laczego? Jeśli chodzi o zm ienną o n azw ie a - to nic nas tu nie dziw i. Funkcja o d eb rała ją p rz e z w artość. O R zu ćm y w ięc okiem na deklarację funkcji zer. W idzim y, że to fu nkcja, która p rzy jm u je d w a arg u m en ty . Pierw szy z nich jest p rzesy łan y - tak jak p o p rzed n io - p rz e z wartość. D rugi nato m iast jest p rz e sy ła n y p rzez referencję. Z au w aż znak: &. 0 W main m am y dw ie zm ien n e, które w ysy łam y d o funkcji zer. Inaczej mówiąc: w y w o łu jem y funkcję zer z param etram i a k tu a ln y m i a, b
144
Rozdz. 5. Funkcje Przesyłanie argum entów przez referencję © W ew n ątrz fu n k cji zer, na m om ent p r z e d „eg zek u cją" w y p isu je m y jeszcze w arto ść d w ó ch p a ra m e tró w fo rm aln y ch wart, ref © Tu n astęp u je ja k a ś operacja zm ieniająca w a rto ść zm ien n y ch w a r t i r e f . W na szym p rz y p a d k u to w pisanie tam zer. © N a d o w ó d , że ta k się stało w istocie - w y p isu je m y ich w a rto ść na ek ran . 0 K ończym y p ra c ę funkcji. P oniew aż fu n k c ja zw raca ty p v o i d (p o p ro stu nic nie zw raca), d la te g o m ożem y sobie tu o s z c z ę d z ić instrukcji return. G dybyśm y chcieli b y ona k o n ieczn ie była, to lin ijk ę w cześniej n ależało b y n ap isać return;
O Po pow rocie z funkcji, b ęd ąc już w m a i n w y p isu jem y n a ek ra n ie w artości zm ien n y ch a i b. I tu jest cała n ie s p o d z ia n k a . Z treści, k tó ra p o jaw ia się na ek ran ie w id ać, ż e ten arg u m en t, k tó ry fu n k cja p rzy jm o w a ła s ta ry m sposobem (p rzez w artość) n ie został z m o d y fik o w a n y . N ato m ia st ta z m ie n n a , k tó rą fu n k cja o d eb rała p rz e z referencję (p rzezw isk o ) zo stała z m o d y fik o w a n a .
Dlaczego ? O tóż w tym p r z y p a d k u d o funkcji, z a m ia s t liczby 77 (w a rto ść zm ien n ej b), zo stał w y sła n y a d r e s zm iennej b w p a m ię c i k o m p u tera. T en a d re s fu n k cja sobie o debrała i (n a sto sie) stw o rzy ła so b ie referencję. Czyli p o w ie d z ia ła so b ie coś takieg;o: „D o b rze, z a te m kom órce p a m ię c i o p rzy słan y m m i a d re sie n a d a ję p se u d o n im (p rz e z w isk o ) ref. P o d k reślm y jasn o : ta sam a kom órka, n a k tó rą w main m ó w iło się b, stała się te ra z w funkcji z e r z n a n a p o d p rz e z w isk ie m ref. Są to d w ie ró ż n e n azw y , ale określają te n s a m obiekt. W O d o o b iek tu o ty m p rzezw isk u r e f w p is a n o zero. S koro r e f b y ło p rz e z w is kiem o b iek tu b, to znaczy, że o d b y ło się to n a obiekcie b. P o n iew aż, jak p a m ię ta m y , p o z a k o ń c z e n iu d z ia ła n ia fu n k cji lik w id u je się śm ie ci - zo b aczm y , co zo sta ło z lik w id o w a n e . «,♦, 1) B ędąca n a stosie k o p ia z m ie n n e j a . (K tóra to k o p ia p o c z ą tk o w o m iała ^ w a rto ść 44, a p o tem 0). P am iętam y , że te n a rg u m e n t o d e b ra n y b y ł p rzez w arto ść. 2) Drugi a r g u m e n t p rzesy ła n y b y ł p rz e z referencję, w ię c n a sto sie m ieliś m y z a n o to w a n y ad res tego o b ie k tu , k tó ry to o b ie k t w e w n ą tr z funkcji p rz e z y w a liś m y ref. Ten a d re s z o s ta ł z lik w id o w a n y . (Jeśli p o d rzem y k a rtk ę z z a p is a n y m ad resem ja k ie g o ś b u d y n k u , to m im o w szy stk c b u d y n e k te n n a d a l stoi. My, co p r a w d a , tracim y a d r e s te g o b u d y n k u , ale in n i - np. fu n k cja m a i n - m ają te n a d re s u siebie z a n o to w a n y . W n io s e k : P r z e s ł a n i e a rg u m e n tó w fu n k c ji p r z e z r e fe r e n c ję p o z w a la te j fu n k c ji
na m o d yfikow a n ie zm iennych (naw et lokalnych!) z n a jd u ją cych się poza tą funkcją.
Rozdział. 5. Funkcje Kiedy deklaracja funkcji nie jest konieczna?
145
Programistów klasycznego C opanowała na pewno teraz euforia: „-W reszcie jest łatw y sp o só b m odyfikow ania argum entów ! To, n a co nie p o z w alało p rzesyłanie arg u m e n tó w przez w artość, staje się w reszcie możliw e d zięk i przesyłaniu p rz e z referencję!" H ola, hola! C hciałbym C ię tutaj przestrzec. N a p ew n o w innych, n a w e t prym ity w n y ch językach p ro g ram o w an ia, spotkałeś ju ż taki w łaśnie sp o só b przesłania a rg u m e n tó w d o funkcji, m im o że tam nie n azw ał się on przesłaniem przez referencję. D laczego zatem tak w sp a n iały język jak C (klasyczne) nie p o zw alał na to? W idocznie były po w o d y . N ie, nie chodzi o to, że może dla piszących kom pila tory byłoby to tru d n e w realizacji. Pow ody są inne. O tóż przesy łan ie przez referencję jest prostą d ro g ą d o pisania p ro g ram ó w b ardzo tru d n y ch d o później szej analizy. N asze zm ienne w jakim ś fragm encie p ro g ram u zm ieniają się, bow iem , w sp o só b n ie zau w ażo n y n a skutek d ziałan ia jakiegoś innego fragm entu p ro g ra m u (innej funkq'i). N iezauw ażony, bo z w y w ołania funkcji z e r w środku m a in © nie w idać, k tóry arg u m en t p rzesy łan y przez w artość, a k tó ry przez referencję. N ie m a więc ostrzeżenia: aha, ten arg u m en t m o że być tam m odyfikow any! Ten sp o só b przesyłania arg u m en tó w do funkcji pow inien być w ięc zasadniczo u n ik an y . Są jednak sytuacje, kiedy się b ardzo przydaje. To w łaśnie z pow odu takich sytuacji ten sposób przesyłania arg u m e n tó w został d o języka C++ w pro w ad zo n y . O tych sytuacjach będziem y jednak m ów ić dokładniej w dalszych rozdziałach. Tutaj tylko w spom nę, że sposób ten stosuje się d o tak dużych obiektów , że przesłanie ich przez w artość (w ym agające zro b ien ia kopii np. dziesiątków , setek bajtów ) pow odow ałoby znaczące spow olnienie w yw oływ a nia takiej funkcji. W p rzy p ad k u , gdy taka funkcja jest w y w o ły w an a bardzo w iele razy, może to być czynnikiem w ażnym .
Jeszcze innym sposobem przesłania arg u m en tu m oże być p o słu żen ie się tzw. w skaźnikiem . Ten sposób om ów im y bliżej w rozd ziale o w skaźnikach.
K iedy d eklaracja fu n kcji nie je s t k o n ie c zn a ? Jak w spom nieliśm y - k ażda nazw a przed odniesieniem się do niej (po prostu użyciem jej) m usi zostać zadeklarow ana. D otyczy to też nazw funkcji. Funkcje, m uszą w ięc być d eklarow ane i robi się to w sposób, o którym już m ów iliśm y. Jednakże, jak pam iętam y, k ażd a definicja (funkcji) jest także p rz y okazji jej deklaracją.
146
Rozdz. 5. Funkcje Kiedy deklaracja funkcji nie jest konieczna?
Jeżeli w ię c w p lik u d e fin ic ja fu n k cji je s t w c z e ś n ie j (p o p r o s tu w y ż e j) n iz linijka ja k im k o lw ie k w y w o ła n ie m tejże fu n k c ji - to n ie trz e b a o s o b n e j d e k la ra c ji t( funkcji. Jeśli n a to m ia s t fu n k c ja n ie je st o s o b n o d e k la r o w a n a , a w y w o ła n ie n a s tę p u je \ linijce p o w y ż e j d e fin ic ji tej fu n k cji —w ó w c z a s k o m p ila to r z a p r o t e s tu je k o m u n , k a te m o b łę d z ie , (b o n ie b ę d z ie je s z c z e z n a l n a z w y tej fu n k c ji z ż a d n i d e k la ra c ji). O to ilu stra c ja o b u p r z y p a d k ó w : / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ******************** / / O d e f i n i c j a k tó r a v o id f u n k c j a _ g o r n a (void)
ł
// j e s t t e z d e k la r a c ją
// dla uproszczenia pusta funkcja - czemu nie ? Z /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ’ int main()
{
funkcj a _ g o r n a (); funkcja d o l n a ();
)
.-***■***★ ***■******■******* //*★★*★*★**★**★★**★***************'******, void funkcja d o l n a (void) / / O d e fin ic ja k t ó r a t e ż j e s t -
//
deklaracją, ale niestety spóźnioną !
/ / dla uproszczenia pusta funkcja - czemu nie ? //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ■ * * * * ’'* * * * * * * * * * ’ Jeśli s p r ó b u je m y s k o m p ilo w a ć p o w y ż s z y p r o g r a m o tr z y m a m y k o m u n ik a t b łę d z ie k o m p ila c ji w lin ijce © . T o d la te g o , ż e w ty m m ie jsc u k o m p i la t o r n ie z. p o z n a ł się je s z c z e z d e k la ra c ją fu n k c ji f u n k c j a d o l n a , k t ó r a to d e k la r a c (łą c z n ie z d e fin ic ją ) z n a jd u je się k ilk a lin ije k n iż e j O . Z u p e łn ie in a czej je s t z w y w o ła n ie m f u n k c ji f u n k c j a _ g o r n a . K o m p ilu ją c lini k ę © k o m p ila to r z n a ju ż d e k la ra c ję fu n k c ji funkcj a_gorna, b o się n a n: n a tk n ą ł p o w y ż e j, w lin ijce O . N ie c h o d z i tu b y n a jm n ie j o p o z y c ję fu n k c ji w s to s u n k u d o s p e c ja ln e j fu n k c irta in . N a s z a f u n k c ja main m o g ła b y b y ć d o w o ln ą fu n k c ją w y w o łu ją c ą d w in n e . B łą d b y łb y t e n sa m . N a le ż a ło z a te m p a m ię ta ć , ż e jej d e fin ic ja n a s tę p u je p o lin ijc e w y w o ła n ia . T o, cz d a n a fu n k c ja je s t przed- c z y po- ła tw e je s t d o o p a n o w a n ia w n ie w ie lk ic h p ro g r; m a c h . W p r o g r a m a c h w ię k s z y c h n ie r a d z ę n a ty m p o le g a ć i p o p r o s tu d e k la n w a ć w c z e ś n ie j w s z y s t k ie fu n k cje. M o im z d a n ie m n ie m a s e n s u p r ó b o w a ć o s z c z ę d z a ć n a d e k la r a c ja c h . Ł atw o p rz e c ie ż z ro b ić ( k o p iu ją c w e d y to rz e p ie r w s z ą lin ijk ę d e fin ic ji f u n k c ji i u m ies; c z a ją c ją n a g ó r z e p r o g r a m u - z a o p a tr u ją c p r z y o k a z ji w ś r e d n i k ) .
147
Rozdział. 5. Funkcje A rgum enty domniemane
Przyjm ując taką z a sa d ę u w alniam y się od p am iętan ia, która funkcja jest pow y żej której. Uwaga: Niedawno wspominałem, że specjalnej funkcji o nazwie ma in nie deklaruje się. U w a g a d la p ro g ra m istó w k la s y c z n e g o C
Jed n ą z pierw szych niem iłych rzeczy, k tóra W as spotka, g d y p ro g ram y w C będziecie przerabiali na C++, będzie w łaśn ie sp raw a braku deklaracji funkcji. W języku C deklaracje takie (zw ane tam predefinicjam i lu b p ro to ty p am i fun k i i ) były zalecane, a le nie w ym agane. Jeśli ich w ięc nie zam ieszczaliśm y, kom p ilato r robił m ilczące założenia co d o ty p u arg u m en tó w i typu w artości zw racanej p rzez funkcję. Kiedy przerabiałem na C ++ jeden z moich dużych programów w C, taki na 25 tysięcy linijek,-musiałem zrobić deklaracje wszystkich funkcji. Zabrało mi to trochę czasu, ale bardzo szybko się opłaciło, gdyż już w trakcie przeróbki okazało się, że niektóre wywołania funkcji nie zgadzały się dokładnie z definicjami. D zięki tem u, że k o m p ilato r C++ tak krytycznie p atrz y na tekst p ro g ram u , dużo b łę d ó w w y k ry w an y ch jest już na etapie kom pilacji. Stąd też C++ m a opinię tak ieg o języka, w k tó ry m program y - jeśli p rzeb rn ą przez kom pilację - działają od razu , bez żm u d n e g o procesu u rucham iania. ....
m u . , i m II mu T II r
5.7
lim
iim i i iii
imiKrnn m a * * n
ttilu r m n
u
r
— —
. w—
Argumenty domniemane D o pochopnego przesy łan ia argum entów p rz e z referencję nie zachęcałem . Je st z a to w C++ in n a nowość w arg u m en tach funkcji (w sto su n k u do C klasycznego) - d o której zachęcam. Są to tak zw an e arg u m e n ty dom niem ane. W eźm y taki przykład: /Z************************************************************ void temperatura(double stopnie, int skala)
{ cout << "Temperatura komory: switch(skala)
<
) )
case 0: cout « break;
stopnie «
case 1 : cout « break;
(stopnie +273) << " K\n";
case 2: cout « break;
cels_to_fahr(stopnie)
" C\n";
«
" F\n";
148
Rozdz. 5. Funkcje A rgum enty domniemane
F u n k c ja ta , ja k ła t w o s ię z o r ie n to w a ć , s ł u ż y d o w y d r u k u in f o r m a c ji o te m p e r a tu r z e . T e m p e r a t u r a je s t a r g u m e n te m p r z y s y ł a n y m d o f u n k c ji. T e m p e r a tu r a p r z y s y ła n a je s t z a w s z e w s to p n ia c h C e ls ju s z a , je d n a k n a e k r a n c h c e m y tę te m p e r a t u r ę w y d r u k o w a ć c z a se m w s t o p n i a c h C e lsju sz a , c z a s e m w s to p n ia c h K e lv in a , a c z a s e m w s to p n ia c h F a h r e n h e ita . T o , w ja k ie j s k a li m a m y a k u r a t w y p is a ć b ie ż ą c ą te m p e r a tu r ę , z a l e ż y o d d r u g ie g o a r g u m e n tu . J e ś li je s t o n r ó w n y 0 — to w s to p n ia c h C e ls ju s z a , je śli 1 — to w K e lv in a c h , a je śli r ó w n y 2 - to w s t o p n i a c h F a h r e n h e ita . D o z a m ia n y s t o p n i C e ls ju s z a n a F a h r e n h e i ta m a m y ja k ą ś f u n k c ję double
cels_to_fahr(double
stopnie) ;
T o , g d z ie o n a je s t i ja k je s t z r e a liz o w a n a - je s t t e r a z n ie is to tn e . D la u p r o s z c z e n ia z a łó ż m y , ż e je s t w b ib lio te c e . D o tej p o r y n ie b y ło je s z c z e n ic n o w e g o . Z o b a c z m y jak z n a s z e j f u n k c ji t e m p e r a t u r a k o r z y s ta m y . O to p r z y k ł a d o w e w y w o ł a n ia : t e m p e r a t u r a (52.5, 0); t e m p e r a t u r a ( 2 0 .1 , 2); t e m p e r a t u r a ( 5 2 .5, 0) ; t e m p e r a t u r a (100, 0); t e m p e r a t u r a ( 5 2 .5, 0); t e m p e r a t u r a (60, 2 ) ;
J a k d o t ą d , ta k ż e w s z y s t k o je s t p o s t a r e m u . A te r a z z a s t a n ó w m y się: w n a s z y m p r o g r a m i e s e tk i r a z y d r u k u j e m y te m p e r a t u r ę w s k a li C e ls ju s z a , n a to m ia s t b a r d z o r z a d k o w in n y c h s k a la c h . J e d n a k ż e z a k a ż d y m r a z e m , g d y c h c e m y w y d r u k o w a ć w s k a li C e ls ju s z a , m u s i m y ja k o d r u g i a r g u m e n t w y s y ła ć to n ie s z c z ę s n e z e r o . A p r z e c ie ż , g d y p y t a m la b o r a n ta o t e m p e r a t u r ę w r z e n ia w o d y i n ie d o d a je w ja k ie j s k a li, to o n d o m n iem yw a , ż e c h o d z i o s k a l ę C e ls ju s z a . C z y k o m p i l a t o r n ie m ó g łb y s ię te ż in t e l i g e n t n i e d o m y ś lić ? M ó g łb y . P o to s ą w ł a ś n i e a r g u m e n ty d o m n ie m a n e . W y s ta r c z y w n a s z y m p r z y p a d k u d e k l a r a c ję f u n k c ji n a p i s a ć ta k : void
t e m p e r a t u r a ( do u bl e
stopnie,
int
skala
=
0
);
s p r a w i to , ż e je śli w n a s z y m p r o g r a m i e w y w o ł a m y f u n k c ję n p . ta k t e m p e r a t u r a ( 66 .3 );
C z y l i z je d n y m ( ty l k o p ie r w s z y m ) a r g u m e n t e m , k o m p i l a t o r d o m n i e m a , ż e d r u g i a r g u m e n t je s t r ó w n y 0, ta k ja k m u to w d e k la r a c ji p r z y k a z a l i ś m y . P o d k r e ś la m : - o ty m , ż e a r g u m e n t j e s t d o m n i e m a n y , in f o r m u je m y k o m p i l a t o r r a z , w d e k la r a c ji f u n k c ji. J e ś li d e f in ic ja j e s t p ó ź n ie j, to w d e f in ic ji j u ż s ię te g o n ie p o w t a r z a . O d tej p o r y w o ln o n a m tę f u n k c ję w y w o ł y w a ć ta k ż e z j e d n y m a r g u m e n t e m . S ta r y s p o s ó b z d w o m a a r g u m e n ta m i te ż je s t d o p u s z c z a l n y , w t e d y k o m p i la t o r
149
Rozdział. 5. Funkcje A rgum enty domniemane
n ie m u si nic d o m n ie m y w a ć , po prostu ro b i to, co kazaliśm y w w yw ołaniu funkcji. Z a te m p o n iższe s p o so b y w y w o łan ia są le g a ln e temperatura (100.3); //domniemywa się: skala = 0 // poniżej nic się nie musi domniemywać, bo jest napisane jasno temperatura ( 3 6 . 6 , 0 ) ; // chcesz w Celsjuszach, bo 0 temperatura (50.3,1); // chcesz w Keluinach, bo 1 temperatura (159.8,2); // chcesz w Fahrenheitach, bo 2
Jeśli chcem y, by funkcja m iała kilka a rg u m e n tó w d o m n iem an y ch , to arg u m e n ty ta k ie m u szą być na k o ń cu listy int multi( int x, double m, int a = 4, double y = 6.55,
int k = 10);
O s ta tn ie a rg u m e n ty (jako dom niem ane) m o g ą w ięc być w n iek tó ry ch w y w o ła n ia c h tej funkcji o p u szcza n e. O to p rz y k ła d y w y w o ła ń tej funkcji. W k o m e n tarzac h podaję jakie w arto ści mają w ó w c z a s arg u m e n ty a, y, k. A rg u m en tó w x i m nie podaję, bo są to zw ykłe, (n ie-d o m n iem an e) a rg u m e n ty i sp raw a jest jasna: 2 i 3.14 multi multi multi multi
(2, (2, (2, (2,
3.14); 3.14, 7 ); 3.14, 7, 0.3); 3.14, 7, 0.3, 5 ) ;
/ / a = 4, / / a = 7,
y = 6.55, y = 6.55, //a = 7 ,y = 0.3, l/a = 7 ,y = 0.3,
k = 10 k = 10 k = 10 k =5
N ie jest m ożliw e o p u sz c z e n ie d o m n iem an eg o arg u m e n tu a lu b y, a u m iesz cz e n ie arg u m e n tu k. Z a te m w yw ołanie ty p u m u l t i (2, 3.14,
7,
, 5);
// !!!
je st trak to w a n e jako b łą d . N ie trzeba się tu nic uczyć, ab y z a p a m ię ta ć , które kom b in acje są d o p u szcza ln e, a które nie. Z a s a d a jest p ro sta i logiczna: W ję z y k u C + + n ie m o ż e w y s tą p ić ta k a s y tu a c ja , ż e o b o k s ie b ie s to ją dw a p rz e c in k i. Ł t m — uwiii.i»r>Mr>iHiiimTnriMitri"i~Tt'''
tmt- i— t—*—n
r - - •trT rr T -^ -‘^*ŁT ' 1" r
^
•**■«■■■»#■> ■■ »Tinrłi«ifiT> irnm»«nim— ■
mtJt
Taka sy tu acja u w ażan a jest jako b łąd literow y. N ie d la teg o , żeby k o m p ilato r nie potrafił sobie d a ć ra d y z zapisem . To d la teg o , że g d y b y k o m p ilato r zezw olił nam na z ap isy z d w o m a p rzecin k am i to tym sam y m znieczuliłby się na w szy stk ie p rz y p a d k i, gdzie rzeczyw iście zapom nieliśm y o jak im ś arg u m en cie, alb o p o prostu n iep o trzeb n ie napisały nam się d w a przecinki. K o m p ilato r znow u robi to w naszym interesie. N a zakończenie jeszcze raz podkreślm y: A rg u m e n ty d o m n ie m a n e określa się w deklaracji funkcji. W definicji nie. N aw et jeśli w definicji n ap isalib y śm y te sam e w artości d o m n iem an e, d o b ry k o m p ilato r n ie p o w in ien się na ta k ą po w tó rk ę zgodzić. A le: M ów iliśm y k ied y ś, że jeśli funkcja jest w p ro g ram ie n a p isa n a pow yżej jakieg o k o lw iek jej w y w o łan ia, to o d d zieln a deklaracja tej funkcji nie jest
150
Rozdz. 5. Funkcje A rgum enty domniemane p o trzeb n a. Sam a definicja funkcji jest w ted y także jej p ierw szą w y stęp u jąc ą w ty m p ro g ram ie deklaracją. G d z ie w ted y określić a rg u m e n ty dom niem ane? O czyw iście w d ek laracji - a że jest nią tutaj ak u rat definicja funkcji - to robim y to tutaj. Z a s a d a jest taka: C hodzi o to, żeby kom pilator o arg u m en tach d o m n ie m a n y c h danej funkcji d o w ied ział się tylko raz - w ted y , g d y d o w ia d u je się, co to za funkcja (czyli w tedy, g d y dociera do niego deklaracja). K om pilator m u si to już w iedzieć w m om encie, g d y o p raco w u je w y w o łan ia tej funkcji w program ie. N ie tru d n o się do m y ślić, że inform acje te są m u potrzebne w tedy, b y te o p u szczo n e p rz e z nas a rg u m e n ty u zupełnić. N ie m usisz się tą sp ra w ą zu p ełn ie interesow ać - jeśli b ęd ziesz się trzyma! z a s a d y , by d ek laro w ać w szystkie funkcje.
Słowa, słowa, słowa N iek tó rz y zam iast m ów ić: domniemany, m ów ią: domyślny. P am iętaj jed n ak , że domniemany morderca to nie to sam o, co domyślny morderca.
W 5.7.1
C iekaw ostki na temat argumentów dom niem anych T e ra z trochę ciekaw ostek. Jeśli jesteś początkującym , m ożesz ten p a ra g ra f o p u ś cić, b o jest trochę za b ard zo szczegółow y, jak na w stę p n y etap n a u k i. Zateir p rzesk o cz teraz na stro n ę 156.
K ied y ś m ów iliśm y o tym , że deklaracja funkcji inform uje k o m p ila to r jedynie c typie i ilości a rg u m e n tó w , w ięc nazwy tych arg u m e n tó w m o żn a opu szczać. K ró tk o m ów iąc p o n iższe dw ie deklaracje są sobie ró w n o w ażn e: v o id f ( i n t pierwszy, d o u b le drugi); Z o id f l i n t , d o u b le ) ; II opuszczone nazwy argumentów Z ałó żm y , ż e m am y w zw yczaju w deklaracjach funkcji o p u sz c z a ć nazw i a rg u m e n tó w . Pytanie:
Jak zaznaczyć, że argumenty są domniemane, gdy nazwy argumentów s£ opuszczone? O czy w iście tak sam o, jakby nazw y argum entów ta m były. Z ałó żm y , że w po w y ższej funkcji m ają być d w a argum enty d o m n iem an e. O to, jak pow inn; w y g lą d a ć taka deklaracja: v o id f ( i n t = 4, d o u b le = 2 0 );
U opuszczone nazwy argumentów
151
Rozdział. 5. Funkcje A rgum enty domniemane
Z a sk a k u ją c o w y g lą d a ten zapis, p ra w d a ? A je d n a k jest p o p raw n y . Nie bądź zaskoczony, jeśli zobaczysz to w cudzym programie. Ale w swoim własnym - lepiej te nazwy argumentów umieszczać. Przypominają one o znaczeniu tych argumentów. 4
M ó w iliśm y n ie d a w n o , ż e d eklaracja (na p rz y k ła d funkcji) m oże w y stą p ić w ie lo k ro tn ie (n ato m iast definicja tylko je d n o k ro tn ie). W p rz y p a d k u a rg u m e n tu d o m n ie m a n e g o - n ie jest tak łatw o. K o m p ilato r w trakcie kom pilacji d a n e g o plik u m o że (w d an y m z a k re sie w ażn o ści) n ap o tk ać deklarację argumentu domniemanego tylko je d n o k ro tn ie . N a w e t g d y b y ta p o w tó rn a d ek laracja d e k la ro w ała d o k ła d n ie tę sam ą w a rto ść tego a rg u m e n tu - b ęd zie o d rz u c o n a . Z n a c z y to, że b łę d e m b ę d z ie w y stąp ien ie takich d w ó ch deklaracji o b o k siebie w je d n y m pliku.
void funfint a, int b = 6); void fun(int a, int b = 6);
// <—błąd, redeklaracja arg. domniemanego
N a to m ia s t p o p ra w n e jest tak ie p o w tó rzen ie dek laracji
void fun(int a, int b); void fun(int a, int b = 6);
// ok, pierwsza deklaracja arg. domniemanego
c z y w o d w ro tn ej kolejności
void fun(int a, int b = 6) void fun(int a, int b);
U ok, pierwsza deklaracja arg. domniemanego
Z a g a d k a : C zy p o p ra w n e jest takie p o w tó rzen ie deklaraq 'i? Jaki m a efekt?
void g(int a, int b = 8); void g(int a = 4, int b); T ak , jest to p o p ra w n e , bo w każdej z nich - d ek laracja je d n eg o z a rg u m e n tó w d o m n ie m a n y c h jest p o raz pierw szy. N ie ma p o w tó rzen ia. Efekt tej instrukcji je st ta k i sam , jakby w y stąp iła jedna taka deklaracja:
void g(int a = 4, int b = 8); N a to m ia s t taka kolejność deklaracji
void g(int a = 4, int b); void g(int a int b = «'
// a co z wartością
b
1
jest n ie p o p ra w n a . Z a u w a ż , ż e p ierw sza d ek laracja m ów i, że a rg u m e n t o n azw ie a je st d o m n iem an y , n ato m iast nie m ó w i nic o arg u m e n cie b, k tó ry (jako n astę p ju ą c y po d o m n ie m a n y m ) też p rzecież m u s i być d o m n ie m a n y . K ró tk o m ów iąc: jeśli już chcem y sto so w ać tak ą sztu czk ę, ż e d e k laru jem y te a rg u m e n ty "na raty" (p o k aw ałk u , w k ażd ej n astęp n e j p o w tó rzo n ej deklaracji), to m o ż em y tak robić, p o d w aru n k iem , ż e b ę d z ie m y się z ty m i a rg u m e n ta m i d o m n ie m a n y m i p o su w ali w stro n ę p o czątk u , n ie o p u szczając p rz y ty m ż a d nego. Spójrz na p o n iższy z e sta w deklaracji:
152
Rozdz. 5. Funkcje A rgum enty domniemane v o id v o id v o id v o id
f 4 (i n t , in t, in t, in t, f 4 ( i n t , in t , in t, in t f 4 ( i n t , i n t , i n t = 33, f 4 ( i n t =11, i n t , i n t ,
i n t = 5 5 ); = 44, i n t ) ; in t, in t); i n t , i n t ) ; II błąd,
W trakcie kom pilacji pierw sze trzy linijki inform ują k o m p ilato r o coraz to now ym arg u m en cie dom niem anym i w każd ej chw ili k o m p ilato r potrafi podać dom n iem an ą w arto ść każdego z nich. W ostatniej linijce jest błąd. W trakcie p racy nad tą d ek laracją kom pilator d o w iad u je się, co p raw d a, że a rg u m e n t pierw szy jest ta k ż e do m n iem an y (i poznaje tę d o m n iem an ą w artość), ale ro zu m ie w obec te g o , że w szystkie dalsze arg u m e n ty też są dom niem ane. •
O arg u m en tach od trzecieg o do piątego d o w ie d z ia ł się już p o p rzed n io i poznał też ich w artości d o m n iem an e .
•
T u do w iad u je się (p o śred n io ), że arg u m e n t d r u g i też m u si być d o m n iem an y, ale nie zostaje p o in fo rm o w an y o jego dom n ie m anej w artości.
To jest błąd!
Deklaracja argumentu domniemanego, a zakres ważności
Nie tylko wartości liczbowe, bo także wyrażenia, (nawet funkcje)... W d o ty ch czaso w y ch p rzy k ła d ach w arto ścia m i d o m n ie m a n y m i a rg u m e n tó w b y ły liczby. M ogą je d n ak w ystąpić tam ta k ż e w y rażen ia. N a p rz y k ła d (x + 2 ) alb o (x * y ) P am iętać jed n ak należy, że w w y ra ż e n ia c h tych n ie m o g ą w y stąp ić o b ie k ty lo k aln e (au to m aty czn e).
I
D o w y ra ż e ń n ależą na p rzy k ła d w y w o ła n ia funkcji (zw racający ch w arto ść o d p o w ie d n ie g o ty p u ).
153
Rozdział. 5. Funkcje A rgum enty domniemane
Jeśli zamierzmy z tego skorzystać-to w myśl powyższej zasady - w argu mentach wyioołania tych funkcji nie powinno być obiektów lokalnych automatycznych. P o p ra w n o ść s k ła d n io w a tak ich w y rażeń s p ra w d z a n a jest o czy w iście w trakcie kom p ilacji, n a to m ia st o w a w arto ść d o m n ie m a n a a rg u m e n tu - o b liczan a jest już w c z a sie w y k o n a n ia p ro g ra m u .
Oto długo oczekiwany przykład: #include using namespace std ; void funkcja(int a = 2, int
= 6);
int kwadrat(int x) ( return x * x; } int globalnyl = 32 ;
// O // © // ©
/Z ************ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * int main()
{
// O
f unkcja();
// <- o tw a rc ie
lo k a ln e g o zakresu
©
cout << "--- Jesteśmy w zakresie lokalnym\n";
// w y w o łu je m y je s z c z e
"po sta rem u "
funkcja();
// ©
int lokalnyl = 2 ;
// O
/ / < - błqd // void funkcja(int a = 3, inc b) ; // void funkcja(int a, int b = lokalnyl ); / /4—błąd void funkcja(int a, int
I I
b = 8);
/ / od tej poru "po staremu" wywołać już nie można
//< -błąd!
//funkcja ();
//oo //00
funkcja(7); globalnyl = 4 + lokalnyl;
// argumentem może być skomplikowane wyrażenie... void funkcja(int a =kwadrat(globalnyl), int);
//O ©
funkcja O ;
//O O
)
// o©
cout << "-- Jesteśmy poza zakresem l o k a l n y m W ; f un k c j a () ;
//O ©
) Z /*** * * * * * * * * * * * * * * * ****************************************** void funkcja(int a, int b) cout << "Naprawdę nastąpiło wywołanie: funkcja(" << a << ", " << b << « endl;
) //**•***•****************★ **************************************
154
Rozdz. 5. Funkcje A rgum enty domniemane
Po wykonaniu tego programu na ekranie zobaczymy tekst Naprawdę nastąpiło wywołanie:
funkcja(2,
-- jesteśmy w zakresie lokalnym Naprawdę nastąpiło wywołanie: f u n k c ja Napraw dę nastąpiło wywołanie:
6);
2, o
funkcja 7, Naprawdę nastąpiło wywołanie: funkcja(36, -- Jesteśmy poza zakresem lokalnym Naprawdę nastąpiło wywołanie: funkcja (2,
» )* 8),
b, ,
Spójrz na ciekawsze punkty tego programu O D eklaracja funkcji o n azw ie f un k e j a z d w o m a argu m en tam i d o m niem anym i. O W yw ołanie tej funkcji bez żadnych arg u m en tó w . Poniew aż funkcja jest gada tliw a", na ek ran ie m o żesz zobaczyć, że rzeczyw iście w y w o łan ie o d było się z uży ciem w artości dom niem anych. © O tw arcie lokalnego b loku, czyli tym sam y m now ego zak resu w ażności. Tutaj m am y szansę: * albo nic w sp raw ie tych arg u m e n tó w d o m niem anych nie zm ieniać, ♦♦♦ albo zrobić w szy stk o od nowa. G d y b y śm y p o stan o w ili nic nie zm ieniać, na tym m ógłby się ten przykład zakończyć. O czyw iście nam - w ż ąd n y m w rażeń - nie o to chodzi. © W lokalnym zak resie w ażności znow u w yw ołujem y f u n k c j e . R zu t oka na ek ran p rzek o n u je n as, że dotychczasow e dom n iem an e a rg u m e n ty są w mocy.
O W lokalnym zak resie m ożem y zdefiniow ać jakiś obiekt au to m aty czn y . © P róba deklaracji funkcji f u n k c j a ze z m ian ą w artości d o m n iem an ej pierw szego arg u m e n tu - o k azu je się błędem (kom pilator protestuje). D laczego. D lateeo że co p ra w d a , d rugi arg u m en t m iał w „bardziej z e w n ę trz n y m " zakre sie w artość d o m n iem an ą , ale skoro tu, w tym zakresie w ażn o ści, pojaw ia się deklaracja tej funkcji, to od tego miejsca tam te w artości zo stają zapom niane. Z atem tu taj cała zabawa z d ek laro w an iem arg u m en tó w d o m n iem an y c h musi zaczać sie od now a. A od now a nie m o ż n a zaczynać d ek laru jąc jako dom nie m a n y a rg u m e n t pierw szy , a d rugi nie. To w łaśnie w y w o łu je tu protest kom p ilato ra. . © Skoro trzeba zacząć o d d ru g ieg o a rg u m e n tu , zróbm y tak Jak o w arto ść dom nie m a n ą dajem y lokalną zm ienną l o k a l n y 1. Z now u błąd k o m p ilato ra w yraże n ie z lokalną zm ien n ą nie m oże być w artością d o m n iem an ą. © K oniec dziw actw , zró b m y tu grzeczną deklarację funkcji. D ru g i arg u m en t jest d o m n iem an y , a jego w artość u staw iam y na 8. . . . . O O Ż eb y Ci u d o w o d n ić, ż e ustaw ienia z p o p rzed n ieg o zakresu w ażn o ści zostały po deklaracji © zap o m n ian e, um ieściłem to w yw ołanie. Jest to w y w ołanie bez ż a d n e g o arg u m en tu . W bardziej zew n ętrzn y m zakresie by ło o n o m ożliw e (U). T eraz jed n ak jest ju ż błędem , bo w ażna jest deklaracja © .
155
Rozdział. 5. Funkcje A rgum enty domniemane
O 0 W y w o łan ie funkcji z je d n y m argum entem . D ru g i jest d o m n iem an y . Spójrz na e k ra n i przek o n aj się, ż e d ru g i a rg u m e n t m a w arto ść d o m n ie m a n ą określoną d la te g o zak resu . O © K olejna, „bard ziej u ściślająca" deklaracja funkcji. Z acy tu jm y ją: void funkcja(int a =kwadrat(globalnyl), int);
O d tej p o ry ten p ie rw sz y a rg u m e n t także staje się d o m n iem an y . P am iętajm y, że te ra z o b o w iązu je takie zło ż e n ie deklaracji void funkcja(int a, int b = 8); void funkcja (int a =kwadrat(globalnyl), int);
// // V ©
co o d p o w ia d a jakby takiej jednej deklaracji void funkcja(int a =kwadrat(globalnyl), int
b = 8);
O d tej p o ry , ile razy w d alszy c h linijkach w y stą p i jakieś w y w o ła n ie funkcji, w k tó ry m nie b ęd zie p ie rw sz e g o (i tym sam y m d ru g ieg o ) a rg u m e n tu , w tedy z o sta n ie u ru ch o m io n a funkcja kwadrat ( 0 ) z a rg u m e n te m w y w ołania g l o b a l n y l (© ). W artość zw rócona p rzez tę fu n k cję kwadrat sta n ie się w arto ścią w y słan ą jako p ie rw sz y a rg u m e n t do funkcji funkcja. Przypominam, że obiekt 1 o k a l n y l (jako właśnie lokalny) nie mógłby tu być argumentem funkcji kw adra t. o o O to p rz y k ła d takiego w y w o łan ia. Zobacz n a w y d ru k ek ra n o w y , to działa! P rzelicz sobie w pam ięci bieżącą w artość o b iek tu g l o b a l n y l p o d n iesio n ą do k w a d ra tu . To w łaśnie w id z im y na ekranie. O © K ończy się lokalny zak res. Lokalny zakres, w ra z z jego a rg u m e n ta m i d o m n ie m a n y m i —p rzech o d zi d o historii. O © W yw o łu jem y funkcje b ez żadnych a rg u m e n tó w i na ek ran ie w id zim y , że te ra z n a p o w ró t są w m o cy te arg u m e n ty d o m n ie m a n e s p rz e d lokalnego z a k re s u .
Czy to zagadnienie nie przypomina Ci sprawy zasłania nazw? O d leg le chyba tak. M yślę, że tak sam o n a le ż y je p o trak to w ać: jeśli to tylko m o ż liw e - unikajm y tego. Jeśli definiujesz jak ąś w arto ść d o m n ie m a n ą , to staraj się, by zaw sze m iała tę sa m ą w artość i nie rób tak ich sztu czek , jak w ty m p rz y k ła d o w y m program iku. P o prostu b ard zo ła tw o się p o tem p o g u b ić.
M oże się tak zdarzyć, że m a m y pro g ram sk ład ający się z kilku p lik ó w . W je d n y m z nich jest funkcja, k tó rą deklarujem y ja k o m ającą a rg u m e n ty d o m n ie m a ne. W różnych miejscach n aszeg o pro g ram u (czyli w różnych p lik ach ) - chcem y tę fun k cję w yw oływ ać. Jak w obec tego um ieścić jej deklarację? M o żn a na d w a sposoby. P ierw szy sposób:
156
Rozdz. 5. Funkcje A rgum enty domniemane ♦> W k ażd y m pliku umieścić na g ó rze deklarację tej funkcji, razem z okreś leniem dom niem anych w artości jej argum entów . Jeśli się p om ylim y i w jakimś z plików napiszem y nieco inną wartość jednego z arg u m en tó w - kom pilator nie zaprotestuje. W olno nam tak po p ro stu k ażd y plik jest innym zakresem ważności. (Por. § 3.7.3, str. 72). M ożem y przecież naw et w k ażd y m z tych plików ustalić sobie zupełnie inne w arto ści dom niem ane. (O dradzam !) Jest d ru g i bezpieczniejszy sposób: ♦> D eklarację tej fu nkcji (łącznie z w artościam i jej argu m en tó w dom niem a nych) u m ieszczam y w pliku nagłów kow ym , który w trakcie kompilacji, zostanie w staw iony do każd eg o z potrzebujących tej funkcji plików. Dzięki tem u m am y pew ność, że w szystkie pliki b ęd ą m iały tę samą w artość d om niem aną. Ten d ru g i sposób m a jeszcze tę zaletę, że jeśli pew nego d n ia zm ienim y zdanie, co do w artości dom niem anej arg u m en tu - p o p raw k ę w y starczy zrobić w tym jednym pliku nagłów kow ym . O argum entach dom niem anych b ędziem y m ów ić jeszcze kilkakrotnie.
5.8
N ie n a zw a n y a rg u m e n t W yobraźm y so b ie taką sytuację. M ieliśm y funkcję w y w o ły w a n ą z jednym argum entem . Była to na przykład funkcja: void ton(int wysokosc) ;
pow odująca, że k o m p u te r zapiszczy je d n y m z sygnałów ostrzegaw czych. Daj m y na to, że p a ra m e tr o nazw ie w y s o k o s c słu ż y do w yb o ru w ysokości tonu. (Definicji tej funkcji nie przytaczam , bo jej w y g ląd zależy od im plem entacji). Funkcja ta do tej p o ry dobrze nam słu ży ła i w naszym p ro g ra m ie setki razy w yw oływ aliśm y ją w różnych miejscach. P ew nego d n ia je d n a k zapragnęliśm y ciszy, alb o też u zn aliśm y , że program , który piszczy z a d u ż o , nie m a w yglądu n au k o w eg o , albo... K rótko m ów iąc po drastycznym cięciu definicja naszej funkcji w y g ląd a tak: void tonfint wysokosc)
{ )
// funkcja jest teraz pusta
N ie chodzi tu w ty m przykładzie o to, ż e w funkcji nie m a ża d n e j instrukcji - to nie jest problem . W olno nam . C h o d zi o to, że teraz a rg u m e n t form alny w y s o k o s c nie zo stał ani raz użyty. N ie jest to błąd, ale k o m p ila to r będzie nas ciągle od tej p o ry o strzeg ał - sugerując, że m o ż e czegoś zap o m n ieliśm y . Co robić? O d p o w ied ź w y d aje się prosta: w yrzucić ten arg u m e n t z d ek laracji i definicji funkcji.
157
Rozdział. 5. Funkcje Funkcje i n l i n e (w linii)
Ł atw o pow iedzieć. C o się w ted y stanie z tym i setkam i w yw ołań fu n k q i ton w n a s z y m program ie? (a m o ż e n aw et w jeszcze innych, jeśli funkcję ton traktow a liśm y jako biblioteczną). In n y m i słow y, od tej p o ry zam iast jednego ostrzeżenia, będziem y mieli setki k o m u n ik ató w o błędzie nie znalezienia funkcji ton w yw oływ anej z jednym a rg u m e n te m typu int. K ażd y k o m u n ik a t-o d jednego w yw ołania funkcji ton w p ro g ram ie. Tragedia. M iało być lepiej, a jest gorzej. Jest jed n ak wyjście: A rg u m en t nienazw any. O tó ż, zam iast w yrzucać cały arg u m en t z definicji funkcji, w y rzu cam y tylko jego n a z w ę , a typ arg u m en tu zostaje. Jak poniżej: void ton(int) {
} Jest to zn ak dla k o m pilatora, że jest to funkcja w yw oływ ana z jednym arg u m en tem ty p u int, ale tego arg u m e n tu w funkcji nie używ am y. N iech nam więc o szczęd zi ostrzeżeń o ty m , że zapom nieliśm y z nim cokolwiek zrobić. Z w ró ćm y uw agę, że tego cięcia dokonujem y w definicji, a nie w deklaracji funkcji. Jak bow iem p am iętam y - w sam ej deklaracji n azw y argum entów form alnych (nie typy, tylko nazw y) mogą istnieć lu b nie. K om pilator w d eklara cji i ta k nazw y te ignoruje (korzysta tylko z typów ). N a ko n iec zd rad zę Ci, d ro g i czytelniku, że osobiście czasem tru d n o m i definity w n ie w yrzucić nazw ę. N a z w a zaw sze p rzy p o m in a m i do czego d an y arg u m en t m iał służyć. D latego ujm uję nazw ę w znaki kom entarza, co dla kom pilatora jest rów n o zn aczn e, że tam nic nie ma void ton(int /* wysokosc */ )
( } a ja zachow uję w spom nienia.
5.9
F u n k c je inline
(w linii)
Z ałóżm y, że m am y niew ielką funkcję. N iew ielką, to znaczy jej definicja jest b a rd z o krótka, zaw iera niew iele instrukcji. Przykładow o: int zao(double liczba)
{ return (liczba + 0.5);
} Jej deklaracja m ów i nam: zao jest funkcją w y w o ły w an ą z jed n y m arg u m en tem typu double, a zw racającą typ int. Ze spojrzenia na ciało tej funkcji - czyli na instrukcje będące jej treścią w id zim y , że jest to funkcja służąca do "m atem atycznego" zao k rąg lan ia liczb rzeczyw istych do całkowitych. Do liczby ty p u double dodaje się 0.5, a nastę pnie tę w artość przedstaw ia się instrukcji return. Funkcja ta w ie, że ma
158
Rozdz. 5. Funkcje Funkcje i n l i n e (w linii) zw rócić ty p i n t , a w ięc w arto ść stojąca k o ło r e t u r n zam ien ian a jest na typ i n t w najbardziej d ra s ty c z n y sposób, czy li p rz e z odcięcie części ułam kow ej. Zostaje sam a w artość całk o w ita i to w łaśn ie z w ra c a funkcja. Jak to się d zieje w p rz y p a d k a c h d la liczb 6.8 i 6.2 - pokazuje po n iższy zapis. 6.8 + 0.5 = 7.3 ---------> 7 6.2 + 0.5 = 6.7 ---------► 6 Z ałóżm y te ra z, że w n aszy m p ro g ra m ie b a rd zo , b ard zo często posługujem y się tą funkcją. C zytelnicy, k tó rz y m ają jakieś d o św ia d c z e n ie z p ro g ram o w an iem w asem ble rze w iedzą, ż e za w y w o łan ie funkcji tro ch ę się płaci. M usi na p oziom ie języka m aszy n o w eg o w y stą p ić kilka instrukcji, k tó re obsługują to specyficzne przej ście w in n e m iejsce p ro g ram u . T ak ż e p o w y k o n an iu funkcji trzeba trochę posprzątać, w ięc też jest niew ielka p raca d o zrobienia. C o p raw d a, w języ k u C ++ k o szt w y w o ła n ia funkcji jest rela ty w n ie niski, jed n ak jeśli naszą fu n k cję n a p ra w d ę z a m ie rz a m y w yw o ły w ać ty siące razy, to czas zu ży ty na w y w o ła n ie i p o w ró t m o że stać się znaczący. M am y w ięc w ybór: alb o posłu g u jem y się tą funkcja (jak w p o n iż sz y m w yrażen iu ) 1 - zao(m ) + z a o ( n * 1 6 . 7 ) ;
II ®
albo rezy g n u jem y z niej i zao k rą g lam y „ n a p iechotę" w p isu jąc alg o ry tm zaok rąglania w linię w y rażen ia. 1 = (int) (m + 0.5) + (int) ( (n * 16.7) + 0.5);
H ©
T en d ru g i z a p is w y k o n a się szybciej, ale ostatecznie m o że się nam nie chcieć w setkach lin ii z p o d o b n y m i w y ra ż e n ia m i w p isy w ać ten ko d . C o robić? Jest w sp a n iałe w yjście ko m p ro m iso w e. P o zw ala ono na •
jasn o ść zap isu - jak w p rz y p a d k u funkcji (p ierw sze w y raże
•
nie) O szy b k o ść w y k o n an ia - jak w p rz y p a d k u w p is a n ia tego algory tm u zao k rąg lan ia w linię. ©
Tak: w łaśnie w lin ię - czyli po angielsku: in linę.2 N aszą funkcję d efin iu jem y tak: inline int zao(double liczba) return
(liczba + 0.5);
} Różnica ż a d n a , z w y jątk iem tego słow a i n l i n e . C o o n o daje? O tó ż teraz, ile razy w p ro g ram ie u m ieścim y w y w o ła n ie funkcji z a o , k o m p ilato r u m ieści do sło w n ie jej ciało (treść) w linijce, w której to w y w o ła nie nastąpiło. N ie b ęd zie w ięc żad n y c h akcji zw iązan y ch z w y w o ła n ie m i po w ro tem z tej funkcji. W rezultacie taki k o d będzie w y k o n y w a ł się szybciej.
2)
[czytaj: „yn lajn"J.
Rozdział. 5. Funkcje Funkcje i n l i n e (w linii)
159
W y tłu m a c z m y to jeszcze prościej: - słow o i n l i n e sp raw ia, o d tej p o ry m ożem y sto so w a ć zap is jak w O , a k o m p u te r i tak sam za m ie n i sobie to na 0 .
Czy rozmiar programu przez to zmniejszy się, czy zwiększy? To z a le ż y . Jeśli fu nkcja, k tó rą zd efin io w aliśm y je st niew ielka - ta k a jak np. n a s z a - to jej treść m o ż e zająć mniej m iejsca n iż k o d g en ero w an y z w ią z a n y z o b słu g ą w y w o łan ia fu n k cji i p o w ro tem z niej. W te d y p ro g ram m o że być nieco m n iejszy . M ów ię tu o p rz y p a d k u , g d y funkcję zao w y w ołuje się w niew ielu m iejscach w p ro g ram ie, ale za to m iliony razy . W innych p rz y p a d k a c h objętość p ro g ra m u m oże się zw ięk szy ć. To nic, g d y ch o d zi o szybkość i łatw o ść zap isu . J e d n a k ż e p am iętać n ależy , iż:
Z d ru g ie j stro n y , w łaściw ie k ażd ą funkcję m o ż em y zam ien ić n a funkcję inl ine, a to dlatego, ż e nie m a jakichś specjalnych w y m ag ań , co d o w y g lą d u jej ciała. Dla wtajemniczonych: Może ona mieć w swoim ciele definicje obiektów lokalnych, a nawet statycznych! P am iętaj je d n ak , że...
funkcja typu inline może zostać skompilowana tak, jak zwykła funkcja - jeśli d a n y k o m p ilato r nie jest na tyle d o b ry , że potrafi sobie z n ią radzić. D lateg o słow o inline ro zu m ieć należy jako su g estię dla k o m p ilato ra. Z tej s u g e stii m o ż e on sk o rzy stać lub nie. Jeśli nie skorzysta, to zro b i z niej funkcję zw y k łą - zw an ą czasem żarto b liw ie o u t l i n ę - czyli „p o za linią". Inaczej m ów iąc tak ą, w której ciało je st gdzieś p o z a linią, w której się ją w yw ołuje. Plotka głosi, że jeśli w funkcji i n l i n e kompilator napotyka pętlę (do, w h ile , f o r), to najprawdopodobniej zrealizuje tęfunkcję jako o u t l i n e . Są jeszcze in n e sytuacje, g d y funkcja m oże być sk o m p ilo w an a jako o u t li n ę : W te d y m ianow icie, g d y k o m pilujem y ją dla p racy z p ro g ram em u ru ch o m ien io w y m tzw . d ebuggerem . W ów czas k o m p ilato r w szy stk ie n asze fu n k cje typu i n l i n e skom piluje jak o o u t l i n e dlatego, że tak w ygodniej jest p racow ać d eb u g g ero w i. Jeśli jed n ak potem sk om pilujem y pro g ram jeszcze raz „na czy sto ", to kom pilator zach o w a się tak, jak u m ie najlepiej.
Umiejscowienie definicji funkcji typu inline Do tej p o ry w ielokrotnie podkreślaliśm y, że k o m p ilato r m usi zn ać deklarację funkcji w m om encie, g d y n ap o tk a pierw sze w y w o łan ie tej funkcji. P o to, by s p ra w d z ić popraw ność w yw ołania. To tyle. T era z jednak chodzi o sp ra w ę pow ażniejszą. Jeśli funkcja jest typu inline, to k o m p ilato r, napotykając w jakiejś linii jej w y w o łan ie, m usi w tej linii w staw ić w łaściw e instrukcje. Z atem teraz już sam a d ek laracja nie w ystarczy. Definicja
160
Rozdz. 5. Funkcje Funkcje i n l i n e (w linii) ciała (treść) fu n k c ji m u s i ju ż b y ć w ty m m o m en cie k o m p ila to ro w i z n a n a . K o m p ila to r p o w in ie n ju ż m ieć „ n a b o k u " p rz y g o to w a n ą tre ś ć tej fu n k cji - i to w ła śn ie w łą c z y d o d a n e j lin ii p r o g r a m u . W niosek s tą d ta k i, ż e w o b e c tego: F u n k c je ty p u i n l i n e m u s z ą b y ć na sam ej g ó r z e te k stu p ro g ra m u a lb o n a w e t w p lik u n a g łó w k o w y m , g d z ie z n a jd u ją się d e k la ra c je in n y c h , z w y k ły c h fu n k c ji, a k tó ry to p lik d o łą c z a n y jest w c z a sie k o m p ila c ji m o d u łó w n a s z e g o p ro g ra m u . Ich c ia ła m u s z ą b y ć p o p r o s tu ju ż k o m p ila to ro w i z n a n e w d a n y m z a k r e s ie w a ż n o ś c i. A le u w a g a , n ie m o ż e m y o s z u k iw a ć : Jeśli p ro g ra m s k ła d a się z k ilk u p lik ó w (m o d u łó w ), to w k a ż d y m z n ic h p o w in n a o b o w ią z y w a ć ta s a m a w ersja fu n k c ji in lin e. _ To właśnie zapewnia nam umieszczenie jej w pliku nagłówkowym i lołączanie tegoż pliku do w szystkich plików programu, które potrzebują tej fu n k c ji. A o to p r z y k ła d p r o g r a m u z n a s z ą fu n k c ją ty p u i n l i n e : ti n c lu d e < io s tre a m > u s in g n am esp ace s t d ; d o u b le p o c z a te k _ x , p o c z a te k _ y , s k a la _ x = 1 ,
// p o c z ą t e k // s k a l e :
u k ła d u w s p ó łr z ę d n y c h
//
®
p o z io m a i p io n o w a
/ / *^*^ m» * ' * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * i n l i n e d o u b l e w s p x ( d o u b le w s p ó ł r z ę d n a ) // 0 r e t u r n ( (w s p ó łrz ę d n a - p o c z a te k _ x )
* s k a la _ x );
************************************* / /* * * * * * * * * * * * * * * * * * * * * * // © i n l i n e d o u b l e w s p y ( d o u b le w s p ó ł r z ę d n a ) { r e t u r n ( (w s p ó łrz ę d n a - p o c z a te k _ y ) * s k a l a _ y ) ; //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * i n t m a i n () { d o u b le
x 1 = 100, y l = 100;
U p r z y k ła d o w y p u n k t
c o u t << "Mamy w p u n k t o w s p ó ł r z ę d n y c h \ n " ; c o u t << " x = " « w s p x ( x l) << »* y =
<< wspy(yl)
// z m i e n i a m y p o c z ą t e k u k ł a d u p o c z a te k _ x = 2 0 ; p o c z a te k _ y = -5 0 0 ; c o u t << << << «
// //
<< endl;
w s p ó łr z ę d n y c h
"G dy p r z e s u n ie m y u k ł a d w s p ó łr z ę d n y c h t a k , " z e p o c z ą te k z n a j d z ie s i e w p u n k c ie \ n " p o c z a t e k _ x « " , " << p o c z ą t e k y " \ n t o now e w s p ó ł r z ę d n e p u n k tu Yn"
\n ”
161
Rozdział. 5. Funkcje Funkcje i n l i n e (w linii) << "w takim układzie sa: " X = " << wspx(xl)
"
<<
<< " y = " << wspy(yl) << endl;
Mamy w punkt o współrzędnych x = 100 y = 100 Gdy przesuniemy układ współrzędnych tak, ze początek znajdzie sie w punkcie 20, -500 to nowe współrzędne punktu w takim układzie sa: x = 80 y = 600 Gdy dodatkowo zmienimy skale pozioma tak, to ten sam punkt ma teraz współrzędne: x = 40 y = 600
ze skala_x=0.5
Kom entarz Jeśli p ie rw sz e Twoje w rażen ie je st takie, że p o w y ż sz y p ro g ram więcej g a d a niż robi, to m a sz rację. A co w z asad z ie robi? P rzelicza w sp ó łrzęd n e z jednego u k ła d u odn iesien ia na drugi. Nie jest to takie nic-operacje robisię najczęściej przy posługiwaniu grafiką na ekranie. Ekran ma rozdzielczość np. 600 na 800 punktów, a my chcemy tam narysować coś, co u nas w programie ma rozmiary 100 na 100 iw dodatku ma to zająć cały ekran. Trzeba wówczas przeskalować współrzędne. W ty m ro zd ziale istotne jest d la n as to, że takich p rzesk alo w ań p rzy sk o m p lik o w an y m ry su n k u dokonuje się tysiące razy. Tu w łaśn ie d o ch o d zim y d o sytuacji, g d y opłaca się użyć funkcji ty p u i n l i n e , dla p rzy sp ieszen ia d ziałan ia p ro g ram u . Jeśli nie jest dla Ciebie jasne, na czy m polega to p rzesk alo w an ie, to nie zap rzątaj sobie teraz tym głowy. Tutaj w ażn e jest bow iem , ż e zdefiniow aliśm y dw ie funkcje ty p u inline: funkcję w s p x i funkcję w spy (do przeliczania w sp ó łrzę dnej poziom ej oraz pionowej). T e definicje w id zisz w 0 i © . Jak w id ać funkcje te korzystają także z niektórych zm iennych globalnych — zdefiniow anych w O . O czyw iście definicje tych funkcji są - zgodnie z z a s a d ą - pow yżej miejsc, g d zie są p o raz p ierw szy w yw oływ ane. W rezu ltacie za każdym razem , g d y w p ro g ram ie w y w o łu jem y sk alow anie funkcji - u nas w O , © , © , O , © , © w ów czas o d b y w a się to w sposób m aksy m aln ie szybki - m ech an izm em i n l i n e .
162
5 .1 0
Rozdz. 5. Funkcje Przypom nienie o zakresie ważności nazw deklarowanych w ew nątrz funkcji
P rzyp o m n ie n ie o zakresie w ażności n azw d ek la ro w a n yc h w ew nątrz funkcji Z akres w ażności nazw deklarow anych w obrębie funkcji ogranicza się tylko d o bloku tej funkcji. N ie można więc, sp o za funkcji, za pom ocą danej nazw y, próbować d o trzeć do zm iennej będącej w o b ręb ie funkcji. ♦♦♦ N azw ą deklarow aną w obrębie funkcji jest też etykieta. N ie m ożna w ięc do niej skoczyć instrukcją g o t o spoza tej funkcji. ♦> Skoro etykieta jest lokalna dla funkcji, dlatego w dw óch różnych fu n k cjach m ogą istnieć bezkonfliktow o identyczne etykiety.
5.11
W yb ó r z a k re s u w ażności n azw y i czasu ży c ia obiektu Przez sposób, w jaki definiujemy obiekt, można decydow ać o zakresie ważności je g o nazwy i o czasie jego życia.
Poniżej o m ó w im y kilka m ożliw ych sposobów definiow ania obiektów .
5.11.1
Obiekty globalne N azw a zd ek laro w an a na zew n ątrz w szystkich funkcji m a zasięg globalny Znaczy to, że tak nazw any obiekt - do stęp n y jest w ew n ątrz w szystkich funkc; znajdujących się w tym pliku. Z jed n y m zastrzeżeniem : jest zn an y dopiero o d linijki, w której nastąpiła jego deklaracja, w dól, do końca p ro g ram u . O czywiście p rak ty k a jest taka, że deklaracje um ieszcza się na sam ym początku pliku, dzięki czem u obiekt jest d o stę p n y w e w szystkich funkcjach tego pliku. t i n c lu d e < io s tre a m > u s in g n am esp ace s td ; in t lic z b a ; *5i ^ ******************* ^ ***************************** i n t m ain () 1 in t i; li c z b a = 10; i = 4; c o u t « " W a rto ś c i: l i c z b a = " « l i c z b a « " i = " « i; f f f (); y/********************************'*** v o id f f f ( v o i d ) { i n t x; x = 5; lic z b a — ;
Rozdział. 5. Funkcje W ybór zakresu ważności nazwy i czasu życia obiektu
163
O Definicja o b iek tu globalnego o n azw ie l i c z b a . @ P rzy k ład u ży cia zm iennej globalnej w funkcji m a in . © W yw ołanie funkcji f f f. © W funkcji f f f m ożem y zd efin io w ać obiekt lokalny o n azw ie x i go używ ać. © W ew n ątrz funkcji f f f m ożem y ta k że używ ać g lo b aln eg o obiektu l i c z b a . Jest przecież g lo b aln ie dostępny. © K ary g o d n y b łąd . O biekt i nie je st obiektem lokalnym tej funkcji. N iep o p raw n e jest takie o d w o ły w an ie się d o obiektów lokalnych innych funkcji, bow iem zakres w ażn o ści nazw y i nie ro zciąg a się na funkcję f f f . Tutaj nazw a i nie jest zn an a, w ięc kom pilator zaprotestuje.
5.11.2
O biekty automatyczne W n aszy m przy k ład zie zm ien n e lokalne i oraz x są to tak zw an e zmienne autom atyczne. Definiujemy je, a o n e - w m om encie, g d y kończym y blok, w któ rym p o w o łaliśm y je d o życia - auto m aty czn ie przestają istnieć. To d latego, że obiekty au to m aty czn e k o m p u ter przechow uje w łaśn ie na stosie. Jeśli p o raz d ru g i w ejdziem y d o d an eg o bloku (np. p rz y p o w tó rn y m w y w o łan iu funkcji f f f ), to zm ienne takie zo stan ą pow ołane d o życia po raz d rugi. N ie m a żadnej gw arancji, że znajdą się a k u ra t w tych sam ych miejscach w pam ięci, co p op rzed n io . G d y znow u o p u ścim y blok - znow u z o sta n ą zlik w id o w an e.3 W ynikają z tego dw a wnioski: <♦ - sk o ro obiekt ten p rzestaje istnieć, to nie m o ż em y liczyć na to, ż e p rzy po n o w n y m w yw ołaniu tej funkcji zastan iem y g o tam z w artością, którą m iał na m om ent przed unicestw ieniem . Przy ponow nym w y w o łan iu tejże funkcji o b ie k t taki zostanie zdefinio-
Dokładny moment uśmiercenia obiektu może zależeć od rodzaju kompilatora i jego tzw. Ważne jest to, że odtąd nie możemy liczyć, iż obiekt jeszcze istnieje.
o p c ji o p ty m a liz a c ji.
164
Rozdz. 5. Funkcje W ybór zakresu ważności nazwy i czasu życia obiektu w an y na now o, bardzo m ożliw e, że w zupełnie in n y m m iejscu p a rn ię ^ (stosu) A _ sk o ro obiekt ten przestaje istnieć, to nie ma sensu by funkcja zw racał a * jego a d re s 4 A dres po o p u szczen iu takiej funkcji o p isu je kom orkę, k tó ra ju ż n ie należy do d aw n eg o właściciela. Sytuację tę można porównać do takiego obrazka. Właśnie się wyprowadza m y z dotychczasowego mieszkania, a na schodach dajemy jeszcze komuś nasz stary adres. Tymczasem pod tym starym adresem ju ż nas nikt, nawet
za chwilę, nie znajdzie. D okładnie to sam o dotyczy zw racan ia referencji (p rzezw isk a) zm ienne, lokalnej. Inna spraw a. P am iętać należy, że I zm ien n e au to m aty czn e nie są zerow ane w chw ili definicji (czyli w I ch w ili p ow oływ ania d o życia). Jeśli ich sam i nie zainicjalizow aliśm y jakąś w artością, to w tych zm iennych auto m aty czn y ch tkw ią początkow o śmieci Dlatego: Ze zm iennych automatycznych nie należy odczytyw ać w artości - zanim najpierw coś sensownego do nich nie zapiszemy.
■
... _........
Jeśli m im o ty ch ostrzeżeń coś s ta m tą d przeczytasz - b ę d ą to z u p e łn ie p rz y p a d kow e w artości. W ytłum aczenie: zm ien n e au to m aty czn e p rzech o w y w an e są n a stosie. P rzy dziela się im ta m w y m ag an y dla d a n e g o obiektu o b szar - ■m c w iece,. K om pila tor nie inicjalizuje nam tego obszaru - (w pisując tam np. zero). N atom iast: Zm ienne globalne (i zmienne z przestrzeni nazw) - te są zakładane w normalnym obszarze pamięci. Ten obszar przed uruchomieniem programu jest zerowany, zatem zmienna globalna, je ś li jej nie inicjalizowaliśm y specjalnie - ma w artość 0 (stosowną
| < jo ^ ie g o obiektu).
:_
-----------------
■
-y,...... ......
Z obiektam i au to m aty c zn y m i łączy się słow o klu czo w e a u t o staw ian e p rzed definicją o b ie k tu w ew n ątrz jakiegoś bloku (np. bloku funkcji lub bloku lokal nego). Jest o n o jed n ak rzad ko u ż y w a n e , g d y ż ob iek ty tak d efiniow ane są au to m aty c zn e p rz e z dom niem anie. Z atem , jeśli np. w b lo k u hi nkcji definiujem y obiekt: 4) 5)
____ ____________
O zwracaniu rezultatu będącego adresem, porozmawiamy w rozdziale o wskaźnikach. Czyli: - przypominając obrazek o stosie - nie na biurku, ale jakby w biblioteczce
165
Rozdział. 5. Funkcje W ybór zakresu ważności nazwy i czasu życia obiektu auto int m;
to jest to ró w n o w a ż n e definicji int m;
>.11.3
O biekty lokalne statyczne P o w ied zieliśm y , że zm ienne lo k aln e dla jakiejś funkcji p o w o ły w an e są d o życia w m o m en cie ich definicji, a g d y kończy się w y k o n y w a n ie tej funkcji przestają istnieć. P o n o w n e w yw ołanie tej funkcji pow oduje p o n o w n e u tw o rzen ie takiej zm iennej, jej w arto ść stanow ią śm ieci, w ięc takiej zm ien n ej po w in n iśm y znow u n adać jakąś w arto ść początkow ą. C zasem taki p ro ced e r nas zad o w ala, czasem jednak ch ciałoby się, by zm ien n a lokalna dla d a n e j funkcji n ie g in ęła bez śladu, tylko p rz y p o n o w n y m w ejściu do tej funkcji m ia ła taką w artość, jak p rz y ostatnim o p u szcza n iu tejże funkcji. Z ałóżm y, ż e m a m y napisać funkcję, która nic nie robi, ty lk o pisze ile razy ją do tej po ry w yw ołaliśm y. ♦♦♦ M usi m ieć więc w śro d k u jakiś licznik o czasie życia rozciągającym się na cały czas w y k onyw ania program u.
C zyli chcielibyśmy, by zmienna miała taki czas życia, jaki mają zmienne (obiekty) globalne. ♦♦♦ Z d ru g ie j jednak strony chcem y, żeby była z n a n a tylko lokalnie w tej funkcji. G dybyśm y te g o ostatniego w a ru n k u nie postaw ili - w ystarczy ło b y n am użyć zm iennej globalnej. Oto ilustracja. S praw ę rozw iązujem y tu na dw a sposoby. #include using namespace std; / * ------ d e k la ra c je f u n k c j i ------*/
void czerwona(void); void biała(void);
// O
/* ------------------------------------------------------------------------------------------------------ * /
int main()
{
czerwona (); czerwona () ; biała(); czerwona () ; biała();
)
II
to
//*********************»*************************************** void czerwona(void) static int ktory_raz; ktory_raz++; cout << "Funkcja czerwona wywołana "<< ktory_raz « " raz\n";
166
Rozdz. 5. Funkcje W ybór zakresu ważności nazwy i czasu życia obiektu void biała(void) * static int ktory_raz = 100; który raz = ktory_raz + 1; cout « "Funkcja biała wywołana " « ktory_raz « " raz\n";
// ©
Po wykonaniu programu na ekranie pojawi się: Funkcja Funkcja Funkcja Funkcja Funkcja
mfr
czerwona wywołana 1 raz czerwona wywołana 2 raz biała wywołana 101 raz czerwona wywołana 3 raz biała wywołana 102 raz
Przyjrzyjmy się poszczególnym punktom programu O Funkcje p rz e d pierw szym sw y m w yw ołaniem pow inny być ju ż z n a n e ko m p ila torowi. D latego na górze p ro g ram u um ieściliśm y deklaracje tych funkcji. Dzi«?ki temu ko m p ilato r wie, jaka jest liczba i typ a rg u m e n tó w w ysyłanych d o funkcji, a ta k że jaki typ jest zw racan y jako rezultat w y k o n an ia tej funkcji. O d te pory ko m p ilato r m oże już n as sp raw d zać, czy się nie p o m y liliśm y w linijkac w yw ołania tych funkcji.
0 W funkcji m a i n w idzim y serię w yw o łań funkcji cz e r w o n a i biała. © W funkcji czerwona istnieje definicja zm iennej (obiektu) ty p u int, o nazwij który raz. Z au w aż słow o kluczow e static. G W funkcji biała także istnieje definicja takiego sam ego ob iektu. O biekt ma taką samą n azw ę, jak w funkcji czerwona.Nie przeszkadza to n am jednak, gdyżs< to obiekty lokalne, czyli zakres w ażności ich nazw jest lokalny. K ażda m a zakrei w ażności og ran iczo n y do funkcji, w której została zdefin io w an a. Z auw ażyłeś p rzy d o m ek static. To on spraw ia, że m im o iż obiekt jest lokaln] - nie p rzestaje on istnieć w m om encie, g d y kończy się jego zak res w ażności jego nazw y. Słow o to nazyw a się też specyfikatorem albo modyfikatorem. Dzięki niem i definicja obiektu lokalnego m odyfikuje się tak, iż obiekt staje sij „nieśm iertelny". . 0 Zw racam u w a g ę raz jeszcze - kończy się zakres w ażn o ści n azw y obiektu, ab obiekt nie ginie. Jakby zap a d a w stan hibernacji. K iedy p o n o w n ie w ywołani zostaje funkcja, obiekt b u d zi się z zim ow ego snu i ma ta k ą w artość, z jaką g< ostatnio zo staw iliśm y. , Na d o w ó d tego - w rezultacie w ykonania na nim zw ięk szen ia o jedynkę w ypisaniu w artości na ekran - w idzim y, że rzeczyw iście m am y coś w rodzaji licznika. t . . . . . . . , © Definicja obiektu statycznego w funkcji czerwona nie z aw ie ra inicjalizacji, czyi p od staw ien ia w artości początkow ej. O biekty statyczne - jako obiekty przypo m inające czasem życia obiekty globalne - nie są p rzec h o w y w an e na stosie, lec w norm alnej pam ięci. W ynika stąd fakt, że obiekty takie - (w p rz e c iw ie ń s tw :^
Rozdział. 5. Funkcje W ybór zakresu ważności nazwy i czasu życia obiektu
167
d o o b iek tó w n a stosie) - b e z p o śre d n io po definicji n ie zaw ierają żadnych „śm ieci", ty lk o m a ją w sobie w arto ść zero w ą. Jeśli ta w a rto ść n am o d p o w iad a, to nie m u sim y je szc ze raz jaw n ie w sta w ia ć tam tego zera. W p rz y p a d k u d efinicji O ta w arto ść n am jed n ak nie o d p o w ia d a ła , w ięc w staw i liśm y tam liczbę 100. D zięki tem u, n asz licznik zaczął liczyć o d w artości począt kow ej 100 C zy te g o sam eg o efek tu n ie m o g lib y śm y u z y s k a ć za pom ocą z m ie n n y c h g lobalnych? T ego sam ego nie, b o w iem nie m oże być d w ó c h o b iek tó w g lo b aln y ch o identycznej n azw ie . M usielibyśm y w ięc zd efin io w ać d w ie z m ien n e glo b aln e o różniących się n a z w a c h np. int ktory_raz_cz; int ktory_raz_bi = 100;
U ży w an ie z b y t w ie lu zm iennych g lo b aln y ch nie jest e leg an ck im rozw iązaniem i z d ra d z a zły sty l p ro g ram o w an ia. Raz, że trzeba u w ażać, by w ym yślić nazw ę d o tej p o ry jeszcze n ie istniejącą, a d w a, ż e obiekt g lo b a ln y je st d o stęp n y dla k ażd e g o - co z w ię k s z a ryzyko b łęd u .
Co by było, gdybyśmy z naszego programu usunęli słowa s t a t i c ? O bie zm ien n e k t o r y raz staną się w ó w czas ob iek tam i au to m aty c zn y m i. Co to oznacza - ła tw o zobaczyć na ek ran ie p o w y k o n an iu takiej w ersji p ro g ram u Funkcja Funkcja Funkcja Funkcja Funkcja
czerwona wywołana 1259 raz czerwona wywołana 1259 raz biała wywołana 101 raz czerwona wywołana 1259 raz biała wywołana 101 raz
O biekty straciły nieśm iertelność. G iną w m om encie, g d y k o ń czy się ich zakres w ażności, a po p o w tó rn y ch n aro d zin ach , nie pam iętają n iczeg o , czym były do tej pory. W idać to w y raźn ie na tekście w y p isy w a n y m z funkcji biała. Z m ienna jest z a k ład an a za k ażd y m razem o d now a i za k ażd y m raze m n ad aw an a jest w arto ść 100. W rezu ltacie zw iększenia o 1 na ekranie p o ja w ia się za k ażd y m razem liczba 101. Z obiektem z funkcji czerwona jest o w iele gorzej. T ak że i o n staje się au to m a tyczny i, jako tak i, z a k ład an y jest n a stosie. P oniew aż je d n a k n ie nadajem y m u żadnej w artości początkow ej, w ięc są w nim w stęp n ie „śm ieci". Te śm ieci zw ięk szam y o 1 i w y p isu jem y na ekranie. Stąd taka zd u m ie w a ją c a liczba. (N a Tw oim k o m p u terze będzie to na p e w n o jakaś inna liczba, m im o w szy stk o jednak, po zo stało ść p o poprzedniej treści stosu).
Podsumowanie: Jeśli chcem y, by jakaś zm ienna (ogólniej obiekt) d efin io w an a w obrębie funkcji nie była po z ak o ń czen iu pracy funkcji niszczona i z a c h o w y w ała sw oją w artość do „n astęp n eg o ra z u " , to m usim y ją zdefiniow ać jako staty czn ą.
168
Rozdz. 5. Funkcje Funkcje w program ie składającym się z kilku plików Jak p am iętam y, definicja taka w ygląda identycznie, jak definicja zm iennęj autom atycznej, z tą różnicą, że przed definicją stoi słow o s t a t i c static double m = 1.7;
Taka zm ien n a nie jest już lo kow ana na stosie (jak zm ienne autom atyczne), tylko w tym o b szarze pamięci, g d zie zm ienne globalne, zatem nie ma w niej „ ś m ie c i' tylko jest inicjalizow ana zerem . Chyba, że tak jak w naszym p rzy p ad k u , za* żąd am y inicjalizacji inną w artością (1.7) G dy d o funkcji w ejdziem y po raz pierw szy - taka w łaśnie w artość czekał* będzie tam na nas. G dy po zakończeniu funkcji i ew entualnych m odyfikacjach tej zm iennej - opuścim y funkcję, obiekt ten stanie się dla nikogo niedostępny. Jeśli jed n ak ponow nie funkcja ta zostanie w yw ołana, zastaniem y tę staty czn ą zm ien n ą z taką wartością, z jaką ją ostatni raz zostaw iliśm y. (Inicjalizacja w a rto ścią 1.7 o d b y w a się tylko jeden jedyny raz, potem zm ien n a statyczna p am ięta już sw oją ostatnią wartość). Zapam iętaj: O biekty globalne | - są w stępnie inicjalizow ane zeram i O biekty lokalne: • —au to m aty czn e - zakładane są na stosie, a tam nic n ie jesl w stępnie inicjalizow anie, zatem obiekty te w stępnie za w ieraji „śmieci", •
5.12
- statyczne - p o niew aż mają pożyć dłużej, zak ład an e są w tym obszarze pam ięci, co obiekty globalne - a więc są w stęp n i* inicjalizow ane zeram i.
F u n k c je w p ro g ra m ie s k ła d a ją c y m s ię z kilku p lik ó w D opóki nasz p ro g ram jest n ie d u ży nie ma problem u: całość m oże się zm ieścić vv jednym pliku dyskow ym . Ten plik kom pilujem y i lin kujem y (z bibliotekam i system ow ym i) by otrzym ać p ro g ram w wersji gotow ej d o uruchom ienia. P rzy ch o d zi jed n ak taki m om ent, ż e program rozrasta się tak bardzo, że k o m p i lacja trw a d ługo. Jakakolw iek m inim alna p opraw ka w y m ag a znow u tej d łu g ie kom pilacji. W tedy m usisz podjąć decyzję, że o dtąd p ro g ra m nie będzie ju ż W jednym pliku - dzielisz go na d w a lub więcej. Jak to zrobić? P rogram n a p isa n y w jednym pliku m ożna podzielić n a d w a pliki tylko w miej' scu m ię d z y definicjam i funkcji. N ie m ożna więc dzielić tak, że w pierw szym pliku A b ęd zie funkcja pierw sza, d ru g a i pół trzeciej, a w d ru g im pliku B reszta trzeciej, c zw arta i piąta. Funkcja trzecia m usi być albo cała w pliku A, albo cała w pliku B. O czym jeszcze m usim y pam iętać?:
Rozdział. 5. Funkcje Funkcje w program ie składającym się z kilku plików P r z e d e w s z y s tk im o ty m , ż e p o to, b y fu n k c je z p lik u B m ia ły d o s tę p d o ja k ic h k o lw ie k z m ie n n y c h g lo b a ln y c h z p lik u A - trz e b a w p lik u B u m ieścić d e k la ra c je tych z m ie n n y c h . D e k la ra c je - a n ie d efin icje. D efin icje (c z y li re z e rw a c ja na n ie m iejsca) o d b y ły się ju ż w p lik u A . W p lik u B c h ce m y m ieć ty lk o d o nich d o s tę p , c z y li m ó c się d o n ich o d w o ły w a ć . A b y m ó c się o d w o ła ć d o ja k ic h ś n a z w - m u s z ą o n e z o sta ć z d e k la ro w a n e . D o te g o , ja k w ie m y , w p r z y p a d k u o b ie k tó w ta k ic h , jak na p rz y k ła d z m ie n n e - s łu ż ą d e k la ra c je w y k o n y w a n e za p o m o c ą s ło w a e x t e r n . Jeśli w ię c w p lik u A m a m y n a s tę p u ją c e z m ie n n e g lo b a ln e in t n; d o u b le x ; c h a r z;
II
w s z y s t k o są d e fin ic je
to - a b y m ó c z n a z w x ,n z k o r z y s ta ć w plik u B - m u s im y ta m zam ieścić d ek laracje : e x te rn i n t n ; e x te r n d o u b le x ; e x te rn c h a r z;
// deklaracje !
D e k la ra c je te są p o tr z e b n e k o m p ila to r o w i w te d y , g d y b ę d z ie k o m p ilo w a ł p lik B. M ó w ią m u o n e m n iej w ięcej coś ta k ie g o : • Jeśli w b ieżąco k o m p ilo w a n y m p lik u B n a p o tk a s z n a z w ę n, to n a ra z ie d e k la ru ję , ż e o z n a c z a o n a o b ie k t ty p u i n t . G d z ie s ię te n o b ie k t z n a jd u je ? N ie m ó w ię te ra z , b o m o ż e n a w e t n a z e w n ą tr z ( e x t e r n ) te g o p lik u . (A le to n ic p e w n e g o ). •
•
Jeśli w b ieżąco k o m p ilo w a n y m p lik u B n a p o tk a s z n a z w ę x, to w ie d z , ż e o z n a c z a o n a o b ie k t ty p u d o u b l e . T a k ż e n ie o k re ś la m , g d z ie d o k ła d n ie o n jest. Jeśli w b ieżąco k o m p ilo w a n y m p lik u B n a p o tk a s z n a z w ę z... d a le j Ci, c z y te ln ik u , o sz c z ę d z ę ...
D o p ie ro n a e ta p ie lin k o w a n ia (łą c z e n ia ) tych p lik ó w z e s o b ą , k o d z p lik u B „ d o w ie się " , g d z ie ż to w p am ięci n a p r a w d ę b ę d ą o b ie k ty n , x , z. Jeśli c h c e m y , ż e b y z p lik u B m o ż n a b y ło w y w o ła ć ja k ą ś f u n k c ję z p lik u A - to ta k ż e m u s im y u m ie ś c ić w p lik u B d e k la ra c ję tej fun k cji. W p r z y p a d k u d ek laracji n a z w y fu n k cji n ie p o trz e b a ju ż s ło w a e x t e r n - je s t o n o p rz y jm o w a n e jak o d o m n ie m a n e . W s u m ie w ięc, z a n im w p lik u B p o ja w ią się jego fu n k c je , n a jp ie rw m u s z ą w y s tą p ić d e k la ra c je z m ie n n y c h i fu n k cji z p lik u A. N ie w s z y s tk ic h - ty ch , k tó re z p lik u B b ę d ą u ż y w a n e . C z a se m jest w y g o d n e u m ieścić te w s z y s tk ie d e k la ra c je w o s o b n y m p lik u - tak z w a n y m p lik u n a g łó w k o w y m , k tó ry p o p ro stu b e z p o ś r e d n io p rz e d p ro c e se m k o m p ilacji je st w łą c z a n y d o p lik u B (a m o ż e i d alszy c h ). To a u to m a ty c z n e w s ta w ia n ie d o p lik u w y k o n u je z a n as s p e c ja ln a d y re k ty w a # in c lu d e
" n a g łó w e k .h "
169
170
Rozdz. 5. Funkcje Funkcje w program ie składającym się z kilku plików Jest to tzw . d yrektyw a prep ro ceso ra6, która b ezp o śred n io przed ro zp o częciem pracy k om pilatora w staw ia d o naszeg o pliku - in n y plik o danej n azw ie (tu taj plik: n a g ł ó w e k . h) znajdujący się w bieżącym k atalogu (bieżącym - bo n a z w a jest ujęta w cudzysłów ). R o zszerzenie . h jest zw yczajow ym ro z s z e rz e n k m d aw an y m plikom n ag łó w k o w y m (. h to sk ró t o d angielskiego header1 nagłów ek). Co p ra w d a , o tych spraw ach d o p ie ro będziem y m ów ić, jednak chyba ju ż z d ą ż y łeś się o sw oić z linijką tinclude
która to w arzy szy naszym p ro g ram o m od dłu ższeg o czasu, a jest niczym in n y m jak w staw ien iem d o naszych p ro g ram ó w - pliku z deklaracjam i zm ien n y ch i funkcji znajdujących się w bibliotece w ejścia/w yjścia. Nie , nie zapomniałem tu o rozszerzeniu... Najnowsza wersja tej biblioteki oferuje ów plik pod nazwą nie mającą już rozszerzenia *. h
Oto przykład programu, który podzielony został na dwa pliki: W szystkie deklaracje zeb ran o w osobnym pliku n ag łó w k o w y m o n azw ie n a g i . h P lik ten jest w łączany d o obu plików p ro g ram u .
Oto treść pliku afryka.cpp: #include using namespace std; iinclude "nagl.h" int ile_murzynow = 9; int main() cout « "Początek programu\n"; funkcja_francuska() ; funkcj a_niemiecka() ; cout << "Koniec programu \n";
1 Z***********************************’ ★ ★ *★ ★ ****★ ★ ****★ ★ ****★ *** / void funkcja_egipska() cout « "Jestem w Kairze !--- y---\n"; cout << "Na swiecie jest " << ile_murzynow « " murzynów, oraz " « ile_europejeżyków « " europejczyków \n"; /+★******★****★****************** * * * * * * * * * * * * ** * * * ** * * * ** * * * ** / void funkcja_kenijska() cout « cout «
6) 7)
"Jestem w Nairobi ! --------\n"; "Na swiecie jest " « ile_murzynow
Będziemy o tym mówili w następnym rozdziale. ang. hcader - nagłówek [czytaj: 'heder' ].
Rozdział. 5. Funkcje Funkcje w program ie składającym się z kilku plików
171
<< " murzynów, oraz " << ile_europejeżyków << " europejczyków \n";
}
A oto plik europa.cpp: #include using namespace std; #include "nagl.h" int ile_europejeżyków = 8; void funkcja_francuska()
{ cout << "Jestem w Paryżu ! *********************\n »; cout << "Na swiecie jest "<< ile_murzynow << " murzynów, oraz " << ile_europejeżyków << " europejczyków \n"; funkcja_egipska();
y************************************************************* void funkcj a_niemiecka(void)
{ cout << "Jestem w Eerlinie
! *******************\n ";
cout << "Na swiecie jest " << ile_murzynow « " murzynów, oraz " « ile_europejczykow << " europejczyków \n"; funkcja_kenijska();
) |•k•k•k•k^k•k■k■k^k•k^k•k'k■k•k■•k•k•k■k•kit•kit^k•k■kic^^*^k^A■k•k•k'k•k•k•k•kir•k'k■kic•^c■^e•k•k•k•k'k^k•k•k■k•k•k•k•k•k■^e j
A tak wygląda zawartość pliku nagl.h extern int ile_murzynow; extern int ile_europejczykow; void void void void
funkcja_egipska(); funkcja_kenijska(); funkcja_francuska(); funkcja_niemiecka() ;
P o sk o m p ilo w an iu plików europa .c p p i af ryka .cpp, o ra z po połączeniu (zlinkow aniu) ich ze sobą, otrzym am y g o to w y program . P ro g ra m ten po u ru chom ieniu sp o w o d u je, że
na ekranie pojawi się następujący tekst Początek programu jGStsin w Paryżu ! +* + *★★***•★★★*■★★*■*•*■*★★ Na swiecie jest 9 murzynów, oraz 8 europejczyków Jestem w Kairze !-------Na swiecie jest 9 murzynów, oraz 8 europejczyków Jestem w Berlinie ! ******************* Na swiecie jest 9 murzynów, oraz 8 europejczyków Jestem w Nairobi ! --------
172
Rozdz. 5. Funkcje Funkcje w program ie składającym się z kilku plików Na s w i e c i e j e s t 9 m urzynów , o r a z 8 e u r o p e jc z y k ó w K o n ie c p ro g ra m u Isto ta p ro g ra m u p o le g ała n a p o k a z a n iu , że — d z ię k i d ek laracjo m — fu n k c je z je d n e g o p lik u p ro g ra m u m o g ą b y ć w y w o ły w a n e p rz e z funkcje z in n e g o p lik u s k ła d a ją c e g o się na p ro g ra m . T a k ż e z m ie n n e g lo b a ln e z je d n e g o pliku m o g ły być u ż y w a n e w in n y m p lik u (m ó w im y m o d u le p ro g ra m u ). W szy stk o d la te g o , że d o o b u p lik ó w e u r o p a . c p p i a f r y k a . c p p w sta w iliśm y plik n a g łó w k o w y n a g i . h z a w ie r a jący d e k la ra c je funkcji i z m ie n n y c h . - M a m C ię! - zaw o ła łe ś ju ż p e w n ie triu m faln ie. - J a k m o ż n a w łączać p lik , w k tó ry m je s t linijka: e x t e r n i n t i le _ m u rzy n o w ; d o p lik u a f r y k a . c p p , w k tó ry m z a ra z jest d efin icja i n t i l e m urzynów = 9; N a jp ie rw m ó w im y , ż e d e k la ru je m y , iż o b iek t i n t o n a z w ie i 1 e _ m u r z y n o w jest g d z ie ś in d z iej (extemal - z n a c z y p o a n g ielsk u : z e w n ę trz n y ), a z a ra z p o te m d e fin iu je m y z m ie n n ą i n t i l e _ m u r z y n o w w ła ś n ie w ty m p liku! To o s z u s tw o ! j B raw o , b ra w o . T o zn ac zy , ż e jesteś czu jn y . O to m o ja o b ro n a . N ie je st to o s z u s t w e m p o d w a ru n k ie m , ż e n ie b ie rz e m y na se rio s ło w a e x t e r n . T o s ło w o nie o z n a c z a , ż e o b ie k t jest z d e fin io w a n y k o n ieczn ie n a z e w n ą trz . P rz y p o m n ij so b ie n ie d a w n e m oje słow a: ... Gdzie się ten obiekt znajduje? Nie mówię teraz, bo może nawet na zewnątrz ( e x t e r n ) tego pliku. (Ale to nic pewnego)... O tó ż s ło w o e x t e r n z n a c z y tu ty lk o to, że d e k l a r u je m y o b iek ty , a le ich nie d e fin iu je m y . P rz y p o m n ij s o b ie też, ż e m ó w iliśm y , iż o b ie k t m o ż e m ieć d o w o ln ą liczbę d e k la ra c ji, ale ty lk o je d n ą definicję. Z a te m p o p r a w n a jest tak a se k w e n c ja w p ro g ra m ie : e x te rn in t e x te rn in t i n t k; e x te rn in t e x te rn in t
k; k; k; k;
/ /d e k la r a c ja / / / /
/d e k la r a c ja / d e fin ic ja - ty lk o je d n a ! /d e k la r a c ja /d e k la r a c ja
Rozdział. 5. Funkcje Funkcje w program ie składającym się z kilku plików
173
A jeśli i to Cię n ie p rzek o n u je, i od sło w a e x t e r n ż ą d a sz dosłow ności, to zrół teraz d w a pliki n ag łó w k o w e - o sobny dla a f r y k i , a o so b n y d la e u r o p y . Za k a r ę !
Zagadka Jak sąd zisz, czy p o p ra w n y jest taki zapis: extern int m = 4;
Jest w tym p e w n a sprzeczność, bo z jed n ej strony u ż y w a m y sło w a extern - co jest ch ara k tery sty cz n e dla definicji. O to od p o w ied ź: K om p ilato r w id z ą c tak i zapis u zn a, że jest to d efin icja, czy li tak sam o jakby był to zap is in t m = 4;
'.12.1
N azw y statyczne globalne Jeśli w deklaracji n a z w y globalnej p o staw im y p rzy d o m ek s t a t i c - oznacza to, że nie ży czy m y so b ie, by ta nazw a b y ła zn an a w innych plik ach (m odułach) składających się n a n asz program . P od k reślam , że ch o d zi tu o n azw y , które są d ek laro w an e g lo b aln ie (czyli na ze w n ą trz w szystkich funkcji). Z a sp raw ą tego p rz y d o m k a n azw a jest n a d a l globalna, ale m o że być zn an a tylko w tym jed n y m p lik u . Dotyczy to nie ty lk o n azw obiektów , ale także n azw funkcji. W yznam , ż e nie w iem , d laczeg o słu ży d o tego ten sa m p rzy d o m ek static, skoro c h o d z i tu o zu p ełn ie in n e zn aczen ie niż p o p rz e d n io . Sądzę, ż e m usiały tu być jak ieś „w zględy h isto ry czn e". P ow odem , by g lo b aln ą nazw ę funkcji czy obiektu z a o p a try w ać w ten p rzy d o m ek static (czyli tutaj jakby: ściśle tajne) była - najczęściej - chęć, b y inne m o d u ły (pliki) pro g ram u , które d an ej funkcji czy zm ien n ej n ig d y nie m ają u ży w ać - nie m u sia ły dbać o u n ik aln o ść nazw . Nie jest to chyba jasne... Oto przykład: iv większym zespole piszemy program. Ja, pisząc mój fragment, wymyślam jakieś nazwy potrzebne mi globalnie w moim module. Niektóre z tych nazw są rzeczywiście ważne i ustalane ze wszystkimi kolegami - np. nazwy funkcji, które oni mają z mojego modułu wywoływać. Jednak oprócz takich nazw będą też nazwy moich globalnych zmiennych pomocniczych czy nazwy moich pomoc niczych funkcji. Teoretycznie powinienem wszystkich kolegów o tych nazwach także informować - czyli ostrzec, by przez przypadek nie wym y ślili w swojej części programu zmiennej globalnej o identycznej nazwie. W daw n y ch w ersjach języka C++ ten k ło p o t ro zw iązy w ało się staw iając p rz e d takim i globalnym i nazw am i p rzy d o m ek static. S p raw ia on, ż e ow e n azw y nie m ogą być w ó w czas znane w m o d u łach innych niż mój. O d tej pory nie m a w ięc obaw y o kolizję nazw. Sposób ten n a d a l jest d o p u szcza ln y .
174
Rozdz. 5. Funkcje Funkcje rekurencyjne
Nowy standard wprowadził jednak lepsze rozwiązanie tego problemu Rozmawialiśmy już o tym w stępnie w paragrafie (§ 3.7.5, str. 72). T eraz d u ż o le p szy m n arzęd ziem są tak zw an e przestrzenie n azw (namespaces). Z atem , jeśli chcesz by d an a nazw a (obiektu, albo funkcji) zn an a była tylko w tym je d n y m pliku, deklarujesz ją (lub definiujesz) w tak zw anej an o n im o w ej p rzestrz en i nazw . Jest to p o p ro stu przestrzeń nazw , której nie dajesz żad n ej n azw y . O to jak sprawić, by nazw y x, funkc ja, inna_f unkc ja nie m ogły być zn an e w in n y m pliku, niż bieżący: namespace ^
// <- n ie m a n a z w y , w ię c a n o n im o w a
in t x -
//d e fin ic ja o b ie k tu
void funkcja (7 i II-
//d e fin ic ja f u n k c j i
double inna_funkcja (char z);
//d eklaracja f u n k c j i
4
> K ażd y plik m oże mieć swoją anonim ow ą p rzestrzeń nazw . N ie m ają one naw zajem nic w spólnego. Jeśli chcesz zapamiętać ten fakt, to wyobraź sobie, że taka przestrzeń nazw ma nazwę odpowiadającą nazwie pliku, którym jest kompilowana. Inny plik to inna nazwa, więc tym sposobem te przestrzenie nazw są rozłączne, j P od su m o w an ie
A nalizując starsze pro g ram y m ożesz się spotkać z n azw am i globalnym i z p rzy d o m k iem s t a t i c , ale potraktuj to jako ro zw iązan ie ju ż przestarzałe. W sw oich program ach używ aj w tym celu anonim ow ej p rzestrzen i nazw .
5.13
F u n k c je re ku re n cy jn e Jak w iem y, w czasie w y k onyw ania funkcji m oże nastąpić w y w o łan ie innej funkcji. Teraz, poniew aż zbliżam y się już d o końca rozdziału o funkcjach, po ro zm aw iam y o szczególnej sytuacji, g d y funkcja w y w ołuje sam ą siebie. Sytuację taką nazyw am y rekurencją. Słowo to m a d u ż y zw iązek ze słow eni kurier, co oznacza po prostu gońca. Rekurencyjny - to znaczy pow tórnif przebiegany. P oro zm aw iam y teraz o w yw ołaniach rekurencyjnych. N ie jest to zagadnienie dotyczące sam ego języka C++, ale raczej algo ry tm ó w p ro g ram o w an ia. N ie jest to sp raw a tru d n a, ale jeśli jesteś początkującym , ra d z ę nie zap rz ątać sobie teraz tym głow y. O pisyw ane tu zag ad n ien ie nie jest konieczne do zrozum ienia dalszej części książki. Z atem bez w ahania m ożesz opuścić ten p arag raf i przejść na stro n ę 186.
175
Rozdział. 5. Funkcje Funkcje rekurencyjne
X W łaściw ie k a ż d a funkcja m oże w y w o łać sam ą siebie, p ro b le m w tym , że jeśli nie p rzy g o tu jem y jej na taką pracę, to d o p ro w a d z im y do nieskończonej pętli w yw o łań . O to p rz y k ła d funkcji, k tó ra g w a ra n tu je n a m tak ie kłopoty: v o id f ( i n t x) { f (x) ; } A by nie s p o w o d o w a ć nieskończonej p ętli w y w o łań - funkcja p rzezn aczo n a do rek u ren cy jn eg o w y w o łan ia - m a w ś ro d k u g d zieś w a ru n e k , w k tórym de cyduje, czy w y w o ła ć sam ą siebie (po raz kolejny) czy nie. N azy w am y go warunkiem zatrzymującym rekurencję. v o id f u n ( i n t x) { i f ( x > 0) f u n ( x - l ) ; 1 Jak w idać, ta funkcja m a w aru n ek i f (x > 0 ) , dzięki k tó rem u jej w yw oływ anie m a szan se się k ie d y ś zakończyć. Jeśli w y w o łam y tę funkcję tak: f u n (3) to - ja k w id z im y z ciała tej funkcji - w y w o ła on siebie s a m ą z a rg u m e n te m 3 - 1 = 2. Z acznie się, w ięc, p o n o w n e w y k o n y w a n ie funkcji f u n (2) W niej n astąp i k o lejn e w yw ołanie, tym razem z a rg u m e n te m 2 - 1 = 1, czyli f u n (1) R ozpocznie się w y k o n y w a n ie kolejnej w ersji, w której n a stą p i kolejne w y w o ła nie, tym razem z arg u m en tem zero (bo: 1 - 1 = 0 ) f u n (0) Ta w ersja fu n k cji sp raw d zi w aru n ek i f (x>0) - i o k a ż e się o n niespełniony, w ięc d alsze w y w o ła n ie już nie nastąp i. Funkcja w y k o n a się d o końca, po czym pow róci d o p o p rzed n iej, która też w y k o n a się d o końca i tak dalej. O czyw iście, ujem nym
jeśli
nierozsądnie
w y w o łalib y śm y
funkcję
z
argum entem
f u n (-5 0 ) to w aru n ek x > 0 n ig d y nie p rz e rw ie rekurencji. F unkcja f u n będzie się w yw o ły w ała w nieskończoność, czyli d o w yczerpania lim itu pam ięci.
Bezpośrednio, pośrednio... Jeśli funkcja w y w o łu je sam ą siebie - m ów im y, że m a m y d o czynienia z rekurencją bezpośrednią. Rekurencja m o ż e nastąpić ta k że w ted y , gdy funkcja
176
Rozdz. 5. Funkcje Funkcje rekurencyjne w y w o łu je inną funkcję, k tó ra z kolei w y w ołuje tę pierw sza. M ów im y w ted y , ż e jest to rekurencja pośrednia.
Obiekty definiowane w funkcji rekurencyjnej W iem y , ż e w zw y k ły ch sy tu acjach , jeśli je d n a funkcja A w yw oła funkcję B, to lo k a ln e z m ien n e a u to m a ty c z n e fu nkcji A (p rzech o w y w an e na stosie) n ie giną. Jeśli ta funkcja B zd efin iu je jakieś sw oje lo k aln e zm ienne (obiekty) - ta k ż e poja w iają się o n e na stosie. P o zak ończeniu p racy funkcji B jej lo k aln e z m ie n n e a u to m a ty c z n e są u s u w a n e z e sto su , a funkcja A p racuje na tych sw oich, k tó re n a nią czek ały . W p rz y p a d k u w y w o łań rek u ren cy jn y ch jest p o d o b n ie. G dy funkcja A w y w o łu je sie b ie sam ą, (czyli d ru g i raz funkcję A ), na stosie są ju ż zm ie n n e a u to m a ty c z n e będ ące w łasn o ścią p ierw szeg o w y w o łan ia. MPHt
D rugie w yw ołanie spow oduje, że na stosie pojawią się nowe zm ienne autom atyczne - będące w łasnością tego drugiego wywołania. O czyw iście ' b ędą to zupełnie odrębne zm ienne.
C ie k aw o stk a: T ak b ę d z ie w p rz y p a d k u z m ien n y ch au to m aty czn y ch . Jeśli jednak ja k a ś zm ien n a (obiekt) jest z d e fin io w a n a w tej funkcji z p rz y d o m k ie m static (staty czn a), to z n a c z y , ż e jest ona "nieśm iertelna", czyli p rz y kolejnym wejściu d o funkcji m a o n a ta k ą w artość, jaką m iała o statn io w p o p rzed n im . K rótko m ó w ią c - taka... ...zm ienna statyczna je st nie tylko nieśm iertelna (nie ginie po zakończeniu d zia ła n ia funkcji), ale je s t jakby wspólna dla w szystkich rekurencyjnych w yw o ła ń tej funkcji. im nm rn w o n m w w
iiu iim h » i i *
i iM >wiim rr—i t m w
W Z o b a c z y m y teraz p ro g ra m , w k tó ry m są d w ie funkcje rek u ren cy jn e - p ierw sza b a rd z ie j w id o w isk o w a , d r u g a bard ziej p rz y d a tn a .
♦♦♦ Pierwsza funkcja s u m a t o r służy do sumowania liczb naturalnych od ( d o n, g d zie n jest w arto ścią p o d an ą p rz e z u ż y tk o w n ik a p ro g ra m u D ru g a funkcja dwój kowo p ozw ala na w y św ie tlan ie na e k ra n ie liczby w postaci binarnej, (czyli zer i jedynek). I
y*************************************************************
#include using namespace std ; int sumator(int jeszcze_krokow, int suraa_dotychczas); void dwójkowo(int liczba); void schodki(int ile, char znak);
// //
177
Rozdział. 5. Funkcje Funkcje rekurencyjne
int
{
m a i n ()
cout <<
"Ile pierwszych liczb n a t u r a l n y c h " "chcesz posumować?: "; int n = 0 ; cin >> n;
//
©
//
©
cout << " \nSuma pierwszych " << n << " l i c z b naturalnych to = "
<< sumator(n,
0)
<< endl;
// zupełnie inny przykład funkcji rekurencyjnej //-----------------------------------------------------int liczba = 241 cout <<
"\n"
;
<< liczba << " to dwojkowo
dwojkowo(liczba); cout << endl
;
liczba = 30942; cout << "\n" << liczba << " to dwojkowo
dwojkowo(liczba); cout << endl return 0 ;
;
int s u m a t o r (int
.
-
- ■‘
jeszcze_krokow,
i - / •• •
int rezultat = 0
int suma^dotychczas)
•
», •
•
// o // © // ©
;
static int krok_rekurencji krokrekurencji++ ;
;
int t o _ p i e t r o _ n r = krok_rekurencji schodki(to_pietro_nr,
// ©
;
f/o%
cout << suma_dotychczas << "+ " « to_pie t r o _ n r « " = " << (suma_dotychczas + t o _ p i e t r o _ n r ) << e n d l ;
// właściwa operacja sumowania suma _ d o t y chczas
//O ©
+= t o _ p i e t r o _ n r ;
// warunek zatrzym ujący rekurencję
//O ©
i f (j e s z c z e _ k r o k o w > 0 )
// kontynujem y wywołania rekurencyjne rezultat = s u m a t o r (j e s z c z e k r o k o w - 1 , s u m a _ d o t y c h c z a s ) ;
//O ©
} else
{
.
// zatrzym ujem y cout << " ........ to ostatni krok, << endl; rezultat = suma_dotychczas ;
)
wracamy
//O ©
178
Rozdz. 5. Funkcje Funkcje rekurencyjne //++++++++++++++++++++++++++++++++++++ schodki(to_pietro_nr, cout << endl; krok_rekurencji— ; return rezultat ;
//O ®
'<’);
// //
88
z/************************************************************ //O ® < //© int reszta = liczba % 2 ;
" void dwojkowo(int liczba) if{liczba > 1 )
{
dwojkowo(liczba / 2 ) ;
// w a r u n e k z a tr z y m u ją c y
© O
//w y w o ła n ie re k u re n c y jn e
© ©
}
//© € >
cout << reszta ; return ;
y/********************* **************************************** void schodki (int ile, char znak) //©O {
cout « ile << " piętro: " ; for(int m = 0 ; m < ile ; m++)
( cout << znak << " " ;
) cout «
□
" " ;
W trakcie wykonania tego programu na ekranie zadane zosta nie nam pytanie i pojawi się następujący tekst Ile pierwszych liczb naturalnych chcesz posumować?: > 0+ 1 = 1 > > 1 + 2 = 3 3 piętro: > > > 3 + 3 = 6 4 piętro: > > > > 6 + 4 = 10 5 piętro: > > > > > 10+ 5 = 15 6 piętro: > > > > > > 15+ 6 = 21 7 piętro: > > > > > > > 21+ 7 = 28 8 piętro: > > > > > > > > 28+ 8 = 36 9 piętro: > > > > > > > > > 36+ 9 = 45 ........to ostatni krok, wracamy ...... 9 piętro: < < < < < < < < < 8 piętro: < < < < < < < < 7 piętro: < < < < < < < 6 piętro: < < < < < < 5 piętro: < < < < < 4 piętro: < < < < 3 piętro: < < < 2 piętro: < < 1 piętro: < Suma pierwszych 8 liczb naturalnych to = 45 1 piętro: 2 piętro:
241 to dwojkowo 1 1 1 1 0 0 0 1 30942 to dwojkowo 111100011011110
8
179
Rozdział. 5. Funkcje Funkcje rekurencyjne
Przyjrzyjmy się ciekawszym miejscom tego programu © W tym m iejscu p ro g ra m zadaje p y ta n ie ile liczb m a zostać p o d sum ow anych. O W yw ołanie funkcji o nazw ie s u m a to r . Jest ono um ieszczo n e w ram ach instruk cji w ypisującej na ek ran , ale jeśli to w y d a je Ci się za tru d n e w an alizie - w yobraź sobie, że za m ia st tej instrukcji są d w ie takie+
int suma = sumator(n, 0); cout << "\nSuma pierwszych " << n << " liczb naturalnych to = " << suma « endl; Z w róć u w ag ę, że w yw ołując funkcję suma tor nie m usim y się n aw et dom yślać, iż rozw iąże ona zag a d n ien ie m etodą rekurencji. © O to definicja funkcji o nazw ie prog ram u O ).
sumator. (Jej deklaracja je st na samej górze
P rzeczytajm y d ek larację tej funkcji
int sumator(int jeszcze_krokow, int suma_dotychczas); s u m a t o r jest funkcją w y w o ły w an ą z d w o m a arg u m e n tam i typu i n t , a zw racającą rezu ltat ty p u i n t . Rzuć okiem na ciało tej funkcji - jest ta m miejsce 0 0 , g d z ie funkcja w yw ołuje siebie sam ą. In n y m i słowy, jest to funkcja rekurencyjna. Funkcja nie będzie w yw o ły w ała siebie sam ą w nieskończoność, bo jest tam w a ru n e k zatrzym ujący O© O m ów m y sobie je d n a k tę funkcję po kolei.
•i* P ierw szy arg u m e n t tej funkcji to liczba sum ow ań, k tó rą sobie użytkow nik zażyczył. Tym sam ym b ę d z ie to liczba rek u ren cy jn y ch kroków . A rg u m en t ten nazyw am y tutaj j e s z c z e _ k r o k ó w (ile jeszcze kroków rekurencji m a się odbyć). D rugi a rg u m e n t ma nazw ę su m a d o t y c h c z a s i słu ży ł będzie do p rzen iesien ia dotychczasow ego w yniku su m o w an ia z w y w ołania na w yw ołanie. Jak w id zisz w funkcji m ain , w p ierw szy m w yw ołaniu tej funkcji © arg u m entem a k tu a ln y m jest zero, bo u rucham iając tę funkcję po raz pierw szy zaczy n am y d o d aw ać do zera (Jeśli b y śm y tam postaw ili n p . 100, to po prostu w y k o n am y su m o w an ie 100 + 1 + 2 + 3 + 4 +...).
O W funkcji s u m a t o r definiujem y sobie (lokalny) obiekt au to m aty c zn y typu i n t o nazw ie r e z u l t a t . 8)
Ten argument suma dotychczas -to doskonały kandydat na tzw. argum ent domniema ny. Wartością domniemaną powinno być oczywiście zero.
180
Rozdz. 5. Funkcje Funkcje rekurencyjne
Skoro ta zm ienna jest autom atyczna, to znaczy, ż e każde w yw ołanie tej funkcji będzie tw orzyło swoją w łasną, lokalną wer sję tej zmiennej. © W funkcji tej tw orzym y też obiekt typu i n t z przydom kiem s t a t i c . T u sprawa jest zupełnie inna. Przydom ek s t a t i c sprawia, ż e jest to obiekt "nieśmiertelny", (czyli w przeciw ieństw ie do obiektów autom atycznych - nie ginie po zakończeniu wykonania funkcji). Jest w ięc on jakby w spólny dla w szystkich rekurencyjnych (i nie rekurencyjnych) w y w o ła ń tej funkqi. Za pomocą takiego obiektu poszczególne w yw ołania mogą się m iędzy sobą porozum iew ać. O c z y w iśc ie m o g ą też się p o ro z u m ie w a ć z a p o m o c ą a r g u m e n tó w w y w o ła n ia , a le ch ciałe m tu p o k a z a ć ta k ż e te n sp o só b . W naszym przypadku jest to obiekt o nazw ie k r o k _ r e k u r e n c j i i s łu ż y on do tego, by każda funkcja w iedziała, które to już jest kolejne w y w o ła n ie , czyli jakby: na które piętro rekurencji się w łaśnie w sp ięliśm y. © Jak pam iętam y, obiekty s t a t i c w stępnie zawierają zero. Tutaj w id zisz, że od razu po wejściu do funkcji wartość obiektu k r o k _ r e k u r e n c j i jest inkrementowana. Zatem przy p ierw szym w yw ołan iu wartością te zm iennej stanie się 1. . © Definicja obiektu autom atycznego o nazw ie t o _ p i e t r o _ n r . W nim dana funkcja zapamiętać m oże sobie swój numer piętra w tej rekurencji O O A by to w szystko było bardziej przekonujące, przygotow ałem funkcję o nazw ie s c h o d k i . Jej definicję w id zisz w miejscu © O . Spójrz jeszcze raz na ekran. To ta funkcja w łaśnie odpow iada za narysowanie jakby w znoszącej się piram idy w yw ołań . O czyw iście nie ma ona żadnego znaczenia dla obliczania naszej sum y. To tylko po to, byś zobaczył, że rzeczyw iście m am y tu d o czynienia ze spiętrzaniem się w yw ołań . D rugim argumentem w yw ołania jest znak '>'. Ma on nam su gerow ać w stępo w a n ie na dalszy (w yższy) poziom rekurencji. O © W łaściw e działanie tej funkcji to sum ow anie. Tutaj w łaśnie to w id z im y . ♦♦♦ jeśli jest to p ierw szy krok rekurencji, to d o dotychczasow ej sum y (0) dodana jest w artość 1. ♦♦♦ jeśli zaś jest to np. piąte piętro rekurencji, to tutaj do dotychczasow ej sum y dodana jest wartość 5. O © By rekurencja nie odbyw ała się w nieskończoność, gd zieś m u si być w arunek zatrzym ujący rekurencję - czyli warunek, w którym decydujem y c z y ma być następn e w yw ołanie, czy nie. To w łaśnie o d b y w a się tutaj. Jeśli argument form alny j e s z c z e _ k r o k ó w jest różny od zera, to znaczy, że d o jd zie d o kolej n e g o w yw ołania rekurencyjnego. U w aga: N ie traktuj jednak dosłow nie słó w "warunek zatrzymujący". Akurat w tym przypadku w w yrażeniu instrukcji i i 7jest warunek m ów iący c zy kontynuować. N atom iast dopiero seg m en t e l s e tej
181
Rozdział. 5. Funkcje Funkcje rekurencyjne
in stru k c ji w y k o n y w a n y je st w te d y , g d y n astęp u je decyzja o zan ie c h a n iu d alszy ch w y w o łań . O © R ekurencyjne w y w o ła n ie funkcji p rz e z sam ą siebie. Z a u w a ż , ż e funkcja nie w ysyła jako a r g u m e n t tej sam ej ilości k ro k ó w , ale zm n iejszo n ą o jeden.
Czyli jeśli bieżąca funkcja dowiedziała się, że ma być jeszcze 7 kroków, to wywołuje następną mówiąc jej, że ma być jeszcze tylko 6 kroków. D zięki te m u , ż e liczb a dalszych k ro k ó w m aleje - rek u ren cja m a szanse się kiedy ś zak o ń czy ć. D ru g im a rg u m e n te m w yw ołania jest d o ty ch czaso w a s u m a , czy li jakby nasz d o ty ch czaso w y u ro b e k . O © Jeśli zaś n astąp iła d ecy z ja o z a trz y m a n ia rekurencji (czyli w O © ) okazało się, że liczba d alszy c h k ro k ó w nie jest w ięk sza od zera, o m in ęliśm y w y w o łan ie re k u rencyjne i tutaj zap a m ię tu je m y n asz ostateczny urobek (d o ty ch czaso w ą sum ę). T en re z u lta t s u m o w a n ia teraz m usi w ró cić to tego, kto su m o w a n ia zażąd ał. (Z obacz d o linijki © O jak o d b ieran y je st ten rezultat).
Z au w aż , że in stru k c je od początku funkcji, d o w y w o ła n ia rek u ren cy jn eg o były w y k o n y w a n e przed w stąp ien iem n a kolejne p iętro rek u ren cji. (Pam iętasz w y w o łan ie funkcji s c h o d k i z a rg u m e n te m '>'?). T eraz b ęd ą in stru k cje, które w y k o n y w a n e są p rzy z stę p o w a n iu na dół. O © T ak się tu sk ład a, ż e nie m am y tu nic sp ecjaln eg o do zro b ien ia. Je d y n ie w y w o łu jem y tutaj fu n k cję s c h o d k i , teraz z a rg u m e n te m '<' su g e ru ją c y m pow rót. Spójrz na e k ra n , ja sn o tu w idać, że n ajp ierw są w y d ru k i z fazy w stęp o w an ia, a n astęp n ie kolejne zejścia w dół. o o
K ró tk o p rz e d z a k o ń c z e n ie m fu n k cji, n a stę p u je d e k re m e n ta c ja zm ien n ej k r o k _ r e k u r e n c j i . P rzy p o m in am , je st to obiekt staty czn y , czyli p o pow rocie
z w y w o łan ia p o p rz e d n ia w ersja (p o p rz e d n i krok rekurencji) zo b aczy efekt tej dekrem entacji.
U nas akurat funkcje w trakcie powrotu nie interesują się wartością tego obiektu, zatem równie dobrze tej dekrementacji mogło by tu nie być. O © Funkcja zw raca r e z u l t a t su m o w an ia. Jest to także sp o só b k o m u n ik o w a n ia się poszczeg ó ln y ch w y w o łań . R zu t oka na te n fra g m e n t funkcji - o d w y w o łan ia rek u re n c y jn e g o do końca funkcji - w y starcza, by stw ierdzić, ż e kolejne p o w ro ty z w y w o ła ń rekurencyjnych p o w o d o w a ć b ę d ą w y d ru k o w an ie sch o d k ó w i p rz e k a z a n iu (nie zm ienia n eg o już) w y n ik u su m o w an ia. T ak o d b y w ało się su m o w an ie na kolejnych etapach i w re z u lta c ie otrzy m aliśm y o stateczn y w y n ik . D ziałanie tej rek u ren cji m ożna sobie g rafic zn ie w yobrazić tak, jak na p o n iż sz y m rysunku.
182
Rozdz. 5. Funkcje Funkcje rekurencyjne
Co wybrać ? K iedy chw ilę się zasta n o w ić n ad tą funkcją, to łatw o dojść d o w n io sk u , że to sam o su m o w an ie m o ż n a było zrobić z a p o m o c ą zw ykłej pętli f o r - czyli za pom ocą tzw. iteracji. N ie trzeba było u ciek ać się d o rekurencji. Jaką p rzew ag ę m a - m o im zd an iem - rek u ren cja n ad iteracją (np. pętlą for)? P ętla p rzebiega o d jed n ej w artości (początkow ej) d o innej (końcow ej) i n a tym kończy. for(int i = początek ; i < koniec ; i++)...
N ato m ia st rek u ren cja sk ład a się z w y w o ła ń i z p o w ro tó w , cz y li jest jakby zło żen iem dw óch p ętli forfint i = początek ; i <= koniec ; i++) . . . / / p ę tla " w stę p u ją c a fortint i = koniec ; i >= początek ; i— )... / / p ę tla " p o w r o tó w "
N ib y w ięc m o żn a by zro b ić to sam o, za p o m o cą dw óch takich p ę tli, ale zw róć u w a g ę , że - w p rz y p a d k u rekurencji - d a n y poziom m a sw ój w ła s n y zestaw zm ien n y ch , k tó ry m m o ż e n ad ać jakieś w arto ści obliczone p rz y w stę p o w a n iu , a sk o rzy stać z nich w trak c ie pow rotów . W p rz y p a d k u pętli for nie ma takiej m ożliw ości. Tutaj trzeb a by b y ło specjalnie p rz y g o to w a ć ze sta w o b iek tó w na w y n ik i p o śred n ie. A p rz e c ie ż nie zaw sze w stę p n ie w iad o m o ile b ęd zie potrzeba p ię te r zag n ieżd że ń . Jeśli je st taka sytuacja, d ecy d u ję się na rek u ren cję. W ów czas rzec zy w iśc ie u daje s ię uprościć z ag a d n ien ie. O to przykład:
Zobaczmy teraz inną funkcję rekurencyjną B ędzie to funkcja, k tó ra m eto d ą rek u ren cji w y p isu je na e k ra n ie d a n ą liczbę w zap isie b in arn y m . F u n k cja jest b a rd z o p ro sta O © - ale, ab y z ro z u m ie ć , naj p ie rw m u sim y p o ro z m a w ia ć o alg o ry tm ie za m ia n y liczby d zie sią tk o w e j na d w ó jk o w ą. Jeśli m a m y d o w o ln ą liczb ę całkow itą d o d a tn ią , to łatw o m o ż e m y ją p o k aza ć na e k ra n ie binarnie, jeśli zasto su jem y n a stęp u jący algorytm .
183
Rozdział. 5. Funkcje Funkcje rekurencyjne
1. D zielim y tę liczbę p rz e z 2 na za sa d z ie d zielen ia z resztą. O trzy m u jem y w te d y rezu ltat d zielen ia (całkow ity) o raz resztę. Reszta ta m o ż e być albo 1 albo 0. Z ap am iętu jem y tę resztę. N a to m ia s t rezu ltat całk o w ity zastęp u je d o ty ch czaso w ą liczbę. K ró tk o m ów iąc ch o d zi o takie dw a d z iałan ia reszta = liczba %2 ; liczba = liczba / 2 ;
//d zie le n ie je s t c a łk o w ite
W yk o n aw szy to p rzec h o d zim y d o p u n k tu 2. 2. (Warunek zatrzymania rekurencji): S p ra w d z a m y , jaka jest (ta n o w a) liczba. •
Jeśli jest w iększa o d 1, to zap a m ię tu jem y ją i p rzech o d zim y do p u n k tu 3.
•
Jeśli jest inna, p o w tó rn ie w y k o n u jem y czy n n o ści opisane w p u n k cie 1. (W ywołanie rekurencyjne).
3. W y p isu jem y na ek ran ie o w e reszty w o d w ro tn ej kolejności od tej, jak je o trzy m y w aliśm y . P o w stan ie z nich w ó w czas na ekranie liczba w zapisie d w ó jkow ym . W prak ty ce w y g lą d a to po prostu tak, jak na p o n iż szy m grafie. A by go zro zu mieć, przeczytaj o d razu w yjaśnienia poniżej. Rys. 5-3
liczba reszta
74
reszta
O
7
7
3
7
7 "1 4 ( 10)
liczba
7 1 1 1 0
=
(2)
Z lewej stronie na po w y ższy m ry su n k u w id zisz jak to robię n a papierze. Po praw ej jest g raficzn e w yjaśnienie, co się dzieje z ty m i liczbam i.
Omówimy to teraz Jeśli m am y np. liczbę 14 i chcem y ją p rzed sta w ić w za p isie b in a rn y m , to: 1) O bliczam y resztę z d zielen ia p rz e z 2. 14 % 2 = 0, R esztę tę zap am iętu jemy.
(Czyli piszemy ją po prawej stronie pionowej kreski)
184
Rozdz. 5. Funkcje Funkcje rekurencyjne ♦> 2) S p raw d zam y : czy 14 jest w ięk sze od 1 ? Tak, w ięc k o n ty n u u je m y . 3) D zielim y liczbę przez dw a (n a sposób całkow ity) 14 / 2 = 7. T en re z u lta t z astę p u je odtąd naszą p ie rw o tn ą liczbę.
(Piszę ją pod spodem, pod tamtą 14). ♦♦♦ 4) O b liczam y resztę 7 % 2 = 1 i zap a m ię tu jem y ją. ♦♦♦ 5) S p ra w d z a m y czy liczba 7 jest w ięk sza od 1 (Jest!) <► 6) D zielim y liczbę przez dw a ( 7 / 2 = 3). R ezu ltat,czy li liczba 3, z astę p u je d o ty c h czaso w ą liczbę 7. ♦♦♦ 7) O b lic zam y resztę 3 % 2 = 1 i zap a m ię tu je m y ją. a
8) S p ra w d z a m y czy 3 > 1. Tak, w ięc k o n ty n u u jem y . 9) D zielim y 3 / 2 = 1. Ten rezultat 1 zastę p u je d o ty c h czaso w ą liczb ę 3.
❖ *
10) O b liczam y resztę 1 % 2 = 1. Z ap a m ię tu jem y ją. 11) S p ra w d z a m y czy 1 > 1 Nie! - z a te m d ziała w a ru n e k z a trzy m u jący rekurencję.
Z aczynają się te ra z p o w ro ty . W ich trak cie na ekranie (lu b k artce p ap ieru ) w y p is a n e zostają k o lejn e re sz ty z dzielenia, czy li w y n ik i o trz y m a n e w p u n k ta c h 10), 7), 4), 1). W rezultacie n a p isa n a zostaje n a stę p u ją c a liczba dw ój k o w a: 1110.
Zobaczmy jak to zostało zrealizowane w programie. R ealizuje je fu n k q 'a o n azw ie dwój kowo, jej d ek larację w id z im y w © . O © Tu jest definicja tej funkcji. Poniew aż jest k ró tk a , p rz y p o m n ijm y so b ie ją tutaj b ę d z ie łatw iej ro zm aw iać. void dwójkowo(int liczba) *
int reszta = liczba % 2 if(liczba > 1 ) dwójkowo(liczba / 2 )
} c o u t << r e s z t a re tu rn ; }
// warunek zatrzymujący
©O
// wywołanie rekurencyjne
0©
;
met pj y liwćr
P ierw sze w ra ż e n ie je st takie, że funkcja je st raczej p ro sta. Tak powinno być. Funkcja rekurencyjna powinna stanowić proste roz wiązanie skomplikowanego algorytmu. Jeśli więc Twoja funkcja rekuren cyjna nie będzie prosta - zawsze się zastanów czy rzeczywiście uzycu rekurencji w tym algorytmie ma sens...
Rozdział. 5. Funkcje Funkcje rekurencyjne 0 ©
185
D efinicja lo k a ln e g o o b ie k tu , w k tó r y m p rz e c h o w u je m y o tr z y m a n ą tu resztę z d z ie le n ia . O p e ra c ja ta o d b y w a s ię przed w y w o ła n ie m r e k u re n c y jn y m . (N a to m ia s t z tej r e s z ty s k o r z y s ta m y w tra k c ie p o w ro tó w ).
© O W a ru n e k z a tr z y m u ją c y re k u re n c ję . Z a tr z y m a n ie to n a s tą p i w te d y , g d y liczba p r z e s ta n ie b y ć w ię k s z a o d 1
© 0 Jeśli p o w o d u d o z a tr z y m a n ia n ie b y ło - n a s tę p u je w y w o ła n ie re k u re n c y jn e . Jak w id z is z , d o tej w y w o ły w a n e j fu n k q 'i w y s y ła się lic z b ę p o d z ie lo n ą p rz e z d w a . Jest to d z ie le n ie lic z b c a łk o w ity c h , w ię c n a s tę p u je o b cięcie c z ę śc i u ła m k o w ej. N p .: 7 / 2 = 3 0©
In stru k c ja w y k o n y w a n a ju ż po w y w o ła n iu re k u re n c y jn y m , a w ię c b ę d z ie o n a w y k o n y w a n a w tr a k c ie p o w ro tó w . N a s tę p u je tu w y p is a n ie n a e k ra n ie z a p a m ię ta n e j w c z e ś n ie j 0 © re s z ty z d z ie le n ia . D la c z e g o w y p is u je m y d o p ie ro te ra z ? D la te g o , ż e p o sz c z e g ó ln e c y fry liczb y d w ó jk o w e j o trz y m y w a liś m y o d c y fry n ajm n iej z n a c z ą c e j, d o n a jb a rd z ie j z n a c z ą c e j. C zy ja k b y o d p r a w e j d o lew ej. T y m c z a s e m n a e k ra n ie (i n a p a p ie r z e ) p isz e się lic z b ę o d lew ej d o p ra w e j. Jak w id z is z , w ta k i o to c h y try s p o s ó b s k o rz y s ta liś m y z f a k tu , ż e rek u re n c ja n a jp ie rw c o ra z b a r d z ie j się " z a g n ie ż d ż a " , a g d y n a s tę p u je w a r u n e k z a trz y m a n ia , z a c z y n a ją s ię p o w ro ty z ty c h " z a g n ie ż d ż e ń " .
© M iejsce w fu n k c ji m a in , g d z ie k o r z y s ta m y z n aszej fu n k cji. S p ó jrz n a e k ra n , p o ja w iły s ię ta m d w ie liczby w z a p is ie d w ó jk o w y m ( b in a rn y m ).
R e k u re n c ja p o z w a la c z a se m n a p ro s te w y k o n a n ie s k o m p lik o w a n e g o a lg o ry tm u . Jest to ra c z e j z a a w a n s o w a n a te c h n ik a , s to s o w a n a s to s u n k o w o rz a d k o .
Ja, w mojej praktyce, łubie ją stosować do zestawu tak zwanych bramek. Jeśli chciałbym sprawdzić czy jakaś dana z układu pomiarowego spełnia koniunkcję jakichś warunków (zwanych często bramkami), to pytam o to tylko pierwszą bramkę. Natom iast ona, przed odpowiedzią, pyta ewentual ną następną, tamta znowu n a stęp n ą -a ż pytanie dotrze do ostatniej. Potem zaczyna się zwracanie odpowiedzi. Dzięki temu wcale nie muszę pamiętać z iloma bramkami mam rozmawiać. Rozmawiam tylko z jedną, a ona załatwia m i (rekurencyjnie) resztę. Ciekawe, te warunkiem zatrzym ania mojej rekurencji wcale nie m usi być dotarcie pytania do "tej ostatniej" bramki w łańcuchu. Jeśli trzecia bramka sprawdzi, że nie jest spełniona, to ju ż wiadomo, że koniunkcja prawdziwa być nie m oże-zatem w tedy trzecia bramka ju ż nie zadaje pytania czwartej, tylko zaczyna powroty, z przesłaniem odpowiedzi "fałsz". Ostatecznie pierwsza bramka odpowiada mi, że koniunkcja brameknie jest spełniona.
186
5.14
Rozdz. 5. Funkcje Funkcje biblioteczne
F u n kcje b ib lio te c zn e Program ow ać w C czy C++ to w zasad zie żadna sztuka, w y starczy o p an o w ać tych kilkanaście instrukcji, w iedzieć jakie są operatory i jeszcze p a rę drobnych rzeczy. Po k ró tk im czasie nie będziesz m usiał w trakcie p ro g ram o w an ia zag lądać d o p o d ręczn ik a. Jest jednak coś, co zaw sze leżeć będzie na T w oim biurku: opis funkcji bibliotecznych. Funkcje biblioteczne nie są częścią języka C++. Są to po p ro stu funkcje, k tó re ktoś tam napisał, a okazały się tak d o b re i tak często p rzy d atn e, ż e zrobiono z nich stan d ard o w ą bibliotekę. Biblioteka ta stała się tak p o p u la rn a w śró d p ro g ra m istów , że k ażd y p ro d u cen t kom pilatora C++ m usi ją także dostarczyć. M u szą być w niej funkcje, które w ykonują p e w n e stan d ard o w e u słu g i i w d o d atk u poszczególne fu n k cje m uszą nazyw ać się tak samo, jak an alo g iczn e funkcje w innych w ersjach kom pilatora. Przykładow o: K iedyś ktoś napisał funkcję, która zam ienia litery w tak i sposób, że jeśli w y ślem y jej jako a rg u m e n t w ielką lite rę ' W , to w o d p o w ied zi jako re z u lta t dostaniem y m ałą literę 'w 7. C zyli funkcja z wielkich liter ro b i małe. N atom iast m ały m literom nie szk o d zi. Z w raca je jako m ałe. N ie szkodzi też cyfrom , znakom specjaln y m itd. O to realizacja takiej funkcji. N azy w am y ją t o l o w e r (ang. to lo w e r- d o niższej). Dygresja:
Nazwa pochodzi od angielskiego określenia liter małych jaku „lower case (niższa kasetka) - wielkie litery nazywają się „upper case (xvyższa kaset ka). Określenia te pochodzą od pracy zecerów dawniej składających teksty w drukarni ręcznie. Litery takie znajdowały się w dwóch kasetkach z prze gródkami. Kasetka z małymi literami, jako częściej używanymi, leżała bezpośrednio przed zecerem, natomiast po litery wielkie musiał on sięgać wyżej. A oto, jak w y g ląd a n asza funkcja: I /************************************************ ************* char tolower(char znak) int różnica = 'a' - 'A'; if ( (znak >= 'A') && (znak <= 'Z') return (znak + różnica); else return znak;
//
)
************************************************
Jak ta funkcja d z ia ła ? P rzy sy łam y do niej k o d zn ak u (jego rep reze n tację liczbo w ą). N astęp u je s p ra w d z e n ie czy jest to z n a k z p rz ed z iału A - Z . R obim y to p rzez s p ra w d z e n ie czy k o d ASCII przy słan eg o z n a k u jest w ięk szy lu b ró w n y kod o w i zn ak u 'A ' i ró w n o cześn ie m niejszy lu b ró w n y k o d o w i z n a k u Z .
187
Rozdział. 5. Funkcje Funkcje biblioteczne Jeśli ta k , to d o k o d u z n a k u d o d a je m y liczb ę r ó żnica. S k ą d w ie m y , ż e trz e b a d o d a ć w ła ś n ie ty le ? Z ta b lic y k o d ó w z n a k ó w A SC II. Z ta b licy tak iej ła tw o z n a jd z ie m y , ż e A - 65 B - 66 c - 67
a - 97 b - 98 c - 99
Z - 90
z - 122
N ie je st to p r z y p a d k ie m , ż e ró ż n ic a m ię d z y k o d e m lite r y w ielk iej, a k o d e m o d p o w ia d a ją c e j jej litery m ałej je st z a w s z e stała. D z ię k i te m u , a b y z e z n a k u A SC II r e p re z e n tu ją c e g o lite rę w ie lk ą zro b ić z n a k r e p r e z e n tu ją c y lite rę m a łą , w y s ta r c z y d o d a ć d o jego k o d u tę w ła ś n ie ró żn icę) 32
U c z y li:
97 - 65
W e w s z y s tk ic h in n y c h p o w s z e c h n ie s to s o w a n y c h k o d a c h p o w in n a r ó w n ie ż b y ć z a c h o w a n a z a s a d a , ż e k o le jn e lite ry a lfa b e tu r e p r e z e n to w a n e są k o le jn y m i k o d a m i lic z b o w y m i. Jeśli n a to m ia s t p rz y s ła n y d o fu n k cji z n a k nie je st z in te re s u ją c e g o n a s p r z e d z ia łu , to g o p o p r o s tu z w ra c a m y , b e z z m ia n . K toś k ie d y ś n a p is a ł ta k ą fu nkcję. O k a z a ła się tak p r z y d a tn a , ż e te ra z je s t w b ib lio te c e s ta n d a r d o w e j.
Z a łó ż m y je d n a k , ż e in teresu je C ię e w e n tu a ln a z a m ia n a lite r m a ły c h n a w ie lk ie . C o z a te m ro b im y ? N ie, n ie z a b ie r a m y się d o p isan ia! B ie rz e m y d o rę k i o p is fu n k cji b ib lio te c z n y c h d o s tę p n y c h w n a s z y m k o m p ila to rz e . W o p is ie ty m s p is a n e są w sz y stk ie d o s tę p n e s ta n d a r d o w e fu n k c je b ib lio te c z n e . N ie m u s is z ich z n a ć , nie m u s is z ich n a w e t u ż y w a ć . P r a w ie w s z y s tk o m o ż e s z p rz e c ie ż s o b ie n a p is a ć sa m e m u . B ę d z ie to je d n a k w y w a ż a n ie o tw a rty c h d rz w i.
W o p is ie fu n k c ji b ib lio te czn y ch , fu n k c je tak ie są c z a s a m i u s z e r e g o w a n e a lfa b e ty c z n ie , c o d o sw o jej n a z w y , c z a sa m i p o d z ie lo n e n a r o z d z ia ły w e d łu g teg o , d o cz e g o s łu ż ą . O d n a le z ie n ie w łaściw ej n ie jest rzeczą tr u d n ą . W n a s z y m p r z y p a d k u ła tw o d o jd z ie m y , że o b o k fu n k c ji t o l o w e r je st fu n k c ja toupper, k tó r a z a m ie n ia lite rę m a łą na w ielk ą. T eg o w ła ś n ie s z u k a liś m y . J e d n a k p o w ie d z m y jasno: w o p isie n ie z n a jd z ie m y te k stu (ciała) fu n k cji, ta k ie g o jak to n a p r z y k ła d n ap isaliśm y d la fu n k c ji tolower. Jest ta m ty lk o o p is a n e , co ta fu n k cja ro b i, z ja k im i a rg u m e n ta m i n a le ż y ją w y w o ły w a ć i ja k i ty p fu n k cja ta z w ra c a . N a to m ia s t nie m ó w i się jak je s t to z re a liz o w a n e . T y m lepiej, b o k o m u ś ,
188
Rozdz. 5. Funkcje Funkcje biblioteczne kto chce p o słu ży ć się daną funkcją, n ie jest potrzebna zn ajo m o ść w szystkich sprytnych sztu czek , którym i p o słu ży ł się piszący tę funkcję. D odatkow o p rz y opisie danej funkcji bibliotecznej zn ajd ziem y też inform ację, gdzie znajduje się deklaracja tej funkcji. Po co ta inform acja? W zasadzie m ó w iliśm y już o tym kilkakrotnie. P am iętasz zap e w n e, iż każda nazw a w języku C++ m usi b y ć z a d e k la ro w a n a zanim zostanie u ży ta. W naszym p rzy p ad k u t o u p p e r to też taka n azw a. Jest to nazw a funkcji - m u si więc znaleźć się w p ro g ram ie deklaracja tej funkcji. A byśm y nie m usieli pisać jej sami (przepisując z opisu biblioteki), zo stała ona już w p isa n a do pliku nagłów kow ego o nazw ie c t y p e . h W ystarczy w ięc w staw ić ten plik n ag łó w k o w y do tekstu n aszeg o p ro g ra m u za pom ocą d y re k ty w y preprocesora #include
i spraw a jest załatw io n a. Jeśli byśm y o ty m zapom nieli, to k o m p ilato r C ++ nam tego nie p o d aru je i u p o m n i się o nią. (K om pilator klasy czn eg o języka C b y n am to podarow ał, ale ja osobiście taką pobłażliw ość k o m p ilato ra C o k u p iłem ju ż kilkom a g o d z in a m i p o szu k iw ań błędów w p ro g ram ie. D latego w olę te ra z ten bardziej k ry ty c z n y język C++). U w aga dla zaaw an so w an y ch : Jeśli użyjemy właśnie tego pliku nagłówkowego, to nazwa to lo w e r stanie się nazwą globalną. Jeśli jednak zamiast tego pliku nagłówkowego
użyjemny inny: #include
to nazwa tej funkcji pojawi się w przestrzeni nazw s t d : : Krótko mówiąc będziemy musieli używać dłuższej nazwy s t d : : to lo w e r
chyba, że zastosujemy dyrektywę u s i n g n a m e sp a c e . O namespace rozmawialiśmy już na str. 12, w § 3.7.5. Jak w id ać z fu n k cjam i bibliotecznym i s p ra w a jest prosta: n ajp ierw szu k am y funkcji w opisie2, p o tem w staw iam y d o p ro g ram u plik n ag łó w k o w y z jej d ek la racją, a n astęp n ie p o słu g u jem y się tą funkcją w pro g ram ie. T ak, jakby to była n asza w łasna funkcja. Funkcja jest je d n a k zd efin io w an a w plik u bibliotecznym . Plik ten trzeba dołączyć w czasie lin kow ania (czyli łączenia). Jest to zw y k le b a rd z o p ro ste, ale tu niestety nie m o g ę Ci nic pom óc. Jak to zro b ić - zn ajd zie sz w opisie sw ojego kom pilatora. Najczęściej je d n a k jest to tak z in te g ro w a n e z k o m p ilato rem , iż nie trzeba w y d aw ać sp ecjaln y ch rozkazów .__________________
9)
zwanym po angielsku:
R c fc re n c e M a n u a ł
189
Rozdział. 5. Funkcje Ćwiczenia
Co chciałbym, abyś z tego paragrafu zapamiętał: To m ianow icie, że opis (leksykon) funkcji bibliotecznych będ zie Twoim najlep szym przyjacielem . Trochę tak, jak „ściąga" na kolokw ium . Osobiście za w sz e , jak tylko przyjdzie m i pracow ać na no w y m typie kom putera, łapczyw ie rz u c a m się na opisy funkcji bibliotecznych. Z najdują się tam b ardzo różne ciekaw e funkcje. O prócz funkcji n ap raw d ę stan d ard o w y ch takich jak np. obliczenie x d o potęgi 17/23, znaleźć tam m ożna też takie, k tó re posłużą nam do narysow ania n a błękitnym ekranie białego kółka w y p ełn io n eg o deseniem z czerw onych serd u szek .
5.15
Ć w iczen ia W danym miejscu programu wywołujemy funkcję f. Czy przed tym wywołaniem powin na wcześniej nastąpić definicja tej funkcji, czy deklaracja tej funkcji? Jak wygląda operator wywołania funkcji? To znaczy, czym różni się wywołanie funkcji od podania jej nazwy ? Przeczytaj następujące deklaracje funkcji. Które z nich są niepoprawne?
a) b) c) d) e) f) g) h)
in t t a (); v o id f b ( i n t ) ; v o id f c ( v o i d ) ; d o u b le f d ( i n t , v o id ) ; f e ( d o u b le , ch ar) ; v o id f f (d o u b le , i n t , i n t , c h a r, i n t , i n t ) ; v o id f g ( i n t l i c z b a ) ; i n t f h ( d o u b le w sp o lcz, i n t c h a r z n a k );
Mamy deklarację funkcji in t f (); a w ciele tej funkcji znajduje się instrukcja «
re tu rn 4 .2 ; Jaką wartość zwróci funkcja f ? Mamy deklarację funkcji d o u b le f 2 ( )
;
a w ciele tej funkcji znajduje się instrukcja r e t u r n 4; Jaką wartość zwróci funkcja f ? Co się stanie, jeśli w funkcji v o id f 5 ( ) ; znajdzie się instrukcja
190
Rozdz. 5. Funkcje Ćwiczenia return C;
Czy poprawne są następujące deklaracje int mani () man 2 () ; int main();
a) b) c)
Gdzie w programie jest deklaracja funkcji m ain ? Mamy takie dwie realizacje funkcji m a i n in t
m ain
)
return 1 ;
oraz in t
m a i n ()
Która z nich jest poprawna? W naszym programie, oprócz funkcji m ain mamy funkcje f f f o następującym ciele d ou b le
fff(
in t
m =
6 +
3
main ();
Czy to poprawne? Funkcja main jest zdefiniowana w taki sposób int main return 33;
Gdzie dotrze wynik pracy tej funkcji (czyli ta liczba 33)? Co to są argum enty formalne funkcji? XIII
Co to są argum enty aktualne funkcji? Co to są argum enty wywołania funkcji? Oto dwie funkcje: void pierwsza;) ; d o u b le vo id
druga(char
z,
in t
k);
p i e r w s z a ()
char cc = d ou b le
r
x' =
druga(cc,
7);
191
Rozdział. 5. Funkcje Ćwiczenia double y = druga('h', m) ;
Widzisz tu dw a wywołania funkcji druga. Dla obu przypadków wywołań wymień nazwy argum entów formalnych funkcji druga. Dla obu wywołań wymień jej argu menty aktualne. Mamy dwie funkcje void fl(int n)
{ n++;
1 void f 2 (int &n) n++;
Jaki otrzymamy wydruk na ekran pochodzący z poniższego fragmentu programu? void g()
1 int liczba = 1 0 ; fl(liczba); cout << "liczba=
«
f2 (liczba); cout << "liczba=
<< liczba << endl;
liczba << endl;
Wymień dwa poznane dotychczas sposoby przesłania argumentów. Który może zmody fikować wartość obiektu wysłanego do funkcji, a który nie? W programie składającym się z wielu plików, wielokrotnie używana jest funkcja mająca jeden argument domniemany typu double. Jego domniemana wartość to 7.5 Funkcja ta zdefiniowana jest w pliku zzz.cpp Wiemy, że deklaracja tej funkcji musi się znaleźć w każdym pliku, który z tej funkcji zamierza skorzystać, (albo w jednym pliku nagłówkowym włączanym do wszystkich tych plików). W którym miejscu programu powinna się znaleźć informacja o domniemanej wartości tego argumentu? XIX
Czy możliwa jest funkcja, która ma wszystkie argumenty domniemane? Czy możliwa jest funkcja, która ma domniemaną wartość rezultatu? Funkcja ma mieć dwa argumenty, z którch jeden ma mieć wartość domniemaną, drugi jest zwykły. W jakiej kolejności mogą one wystąpić w deklaracji tej funkcji? Dana jest funkcja void f(int a, int b = 2, int c - 3);
Można ją wywołać na przykład tak: f(l, 2, 3);
Wymień wszystkie pozostałe możliwe sposoby wywołania tej funkcji nadające argu mentom formalnym tę samą wartość, jaką nadaje powyższe wywołanie.
192
Rozdz. 5. Funkcje Ćwiczenia Napisz deklarację funkcji o nazwie f x, która zwraca rezultat typu b o o l, a wywoływana! jest z dwoma argumentami. Pierwszy z nich jesttypu char i jego wartością domniemaną 1 jest 'g'. Drugi jest typu d o u b le i jego wartością domniemaną jest 12.5 Jeśli przy pisaniu tej deklaracji pomogłeś sobie wymyślając (dowolne) nazwy tych argumentów, napisz także deklarację, w której nazwy argumentów nie występują (przecież nie muszą, ważne są tylko ich typy). Załóżmy, że poprawna jest deklaracja umieszczona poniżej w pierwszej linii. Czy] poprawne jest wystąpienie zaraz za nią tych następnych w takiej właśnie kolejności? Jeś li nie, wskaż błędne.
a) b) c) d)
v o id v o id v o id v o id
flint flint f (in t flint
a , d o u b le , a, d o u b le , a, d o u b le a, d o u b le
c h a r c ); char c = '2'); — 3, c h a r c) ; = 3, c h a r = ' 6 ' ) ;
Czy poprawny jest następujący prosty program? i i n c lu d e < io stream > u s in g nam espace s t d ; v o id f u n ( i n t a, d o u b le b) { ) d o u b le v — 8 . 8 ; //**************************************************** i n t main() 1 n i n t z e ro = 0 ; d o u b le x = 1 .1 ; f u n (1, 6 . 6 ) ; ( v o id f u n (i n t a = - l , d o u b le b = y ) ; f u n () ; i f ( 2 > z e ro ) { v o id f u n ( i n t a, d o u b le b = x ) ; fun(zero);
Jeśli nie, to dlaczego? Jak go poprawić, by jego działanie się nie zmieniło, ale dawał się j kompilować poprawnie? XXVI
Czy możliwe jest, by funkcja, której deklaracja nie przewiduje żadnych argumentó domniemanych, w jakimś lokalnym zakresie mała zdefiniowane argumenty domni umanę? ia tiv Jeśli zdecydowaliśmy, że jeden z argumentów formalnych funkcji f nie będzie w niej _ użyty mimo że inne funkcje tę funkcję f często z odpowiednim argumentem aktualnym 1 wywołują, to gdzie należy zrobić zmianę określającą dany argum ent jako nienazw any?! W definicji czy w deklaracji funkq'i? ■ W którym miejscu programu składającego się z jednego pliku - należy umieścić definicję funkcji i n l i n e ? Jak postąpić, gdy program składa się z kilku plików? I •
xxvni
Napisz program składający się z funkcji main i funkcji d r u g a będących w dwóch różnych plikach.
I
Rozdział. 5. Funkcje Ćwiczenia
193
Następnie napisz definicję funkcji i n l i n e o nazwie w ię k sz a , która przyjmuje dwa argumenty typu i n t , a jako rezultat zwraca większy z nich. Wywołania tej funkcji mają być zarówno z funkcji m ain, jak i z funkcji d ru g a. Zrób to na dw a sposoby: 1. używając pliku nagłówkowego włączanego dyrektywą # i n c l u d e , 2. bez użycia pliku nagłówkowego. Jaki zakres ważności ma nazwa zdefiniowana w pliku programu poza wszelkimi funkcjami? Jak rozciąga się ten zakres ważności? Porównaj zakres ważności nazw obiektów zdefiniowanych w ciele funkcji bez przydomka s t a t i c i z przydomkiem s t a t i c . Porównaj czas życia obiektów zdefiniowanych w ciele funkcji bez przydomka s t a t i c i z przydomkiem s t a t i c . Napisz program, w którym będzie funkcja v o id f () ; Funkcja ma i n ma wywoływać tę funkcję 100 razy. Funkcja f ma wiedzieć ile razy została już wywołana i - przy co dwunastym wywołaniu - wypisywać na ekranie stosowny tekst (np. "nowy tu z in " ). Czy poniższą funkcję uda się skompilować poprawnie? in t sumuj(int ile) i n t sum; f o r ( i n t i *= 0 ; i < i l e ; i++) ( sum += i ; } r e t u r n sum; Jeśli wywołamy ją tak: sumuj (4) — to czy można powiedzieć jaka będzie wartość zwracana przez nią? Mamy następujące obiekty: globalny, lokalny, automatyczny, statyczny. O których z nich można powiedzieć, że wstępnie inicjalizowane zerami; o których, że zawierają śmieci; a o których nic takiego nie można powiedzieć? Używając dostępnego Ci kompilatora napisz, skompiluj i uruchom program, który będzie się składał z dwóch plików źródłowych *.cpp. W każdym z nich mają być po dwie funkcje- (main to też funkcja). Mają się tak wywoływać, by każda z nich pracowała choć raz. W każdym plików mają być po trzy zmienne globalne typów: i n t , d o u b le , c h a r , (w sumie zatem będzie ich sześć). Niech każda z funkcji tego programu: - najpierw wypisze na ekranie wartość każdej z sześciu zmiennych globalnych, - zmieni wartość ich wszystkich, - ponownie wypisze na ekranie wartość każdej z sześciu zmiennych globalnych. Posłuż się plikiem nagłówkowym ł .h, w którym wystąpią potrzebne deklaracje. Mamy plik nagłówkowy z deklaracjami. Plik ten włączany jest dyrektywą # in c lu d e w trakcie kompilacji kilku plików *.cpp naszego programu (jak w poprzednim zadaniu). Czy można w tym pliku nagłówkowym umieścić instrukcję: e x t e r n i n t zm ienna = 12;
194
Rozdz. 5. Funkcje Ćwiczenia
Jaki odniesie to skutek? (Co na to kompilator? Co na to linker?)
Ćwiczenia dla tych, którzy przeczytali paragraf o rekurencji XXXVIII
X Co to jest rekurencja (bezpośrednia lub pośrednia)7
X Co sprawia, że rekurencja ma szansę się zakończyć? X Napisz program, który posługując się rekurencją obliczy wartość wyrażenia
n!
(n-silnia),
gdzie n będzie wartością podaną przez użytkownika z klawiatury. X W paragrafie omawiającym funkcje rekurencyjne zobaczyłeś funkcję rekurencyjną, realizującą wydruk liczby całkowitej w postaci dwójkowej. Zmień tę funkcję tak, by wypisywała poszczególne bity grupując je w czwórki. Poszczególne czwórki powinny być oddzielone spacjami - czyli by liczba: 10110111110111
została wypisana tak: 10 1101 1111 0111
Jak widzisz, grupowanie zaczyna się od prawej stronie liczby. Oczywiście najbardziej znaczące bity - nie zawsze będą miały pełną (czterobitową) grupę. X W programie przykładowym, w paragrafie omawiającym rekurencję, zastosowaliśmy trzy możliwości komunikowania się między sobą poszczególnych wywołań rekurencyj* nych funkcji sum ator. Wymień te trzy sposoby.
*
Rozdział. 6. Preprocesor N a pomoc rodakom
195
6
Preprocesor reprocesor to jak b y przednia straż kom pilatora. Z anim k o m p ilato r przys tępuje d o akcji, tekst program u jest p rzeg ląd an y p rz e z preprocesor. Z w ykle p rep ro ceso r w kracza do pracy sam , bez jakiejkolw iek inicjatywy z naszej strony. W z a sa d z ie byłby dla nas n iezau w ażaln y i n ieisto tn y , gdyby nie kilka przy słu g , k tóre m oże nam oddać. O tym , co m a zrobić d la nas preprocesor, d ecy d u jem y za p o m o cą tak zw anych dyrektyw preprocesora. D yrektyw y tak ie są um ieszczane p rz e z nas w stoso w nych miejscach p ro g ram u , a ich zn ak iem szczególnym jest to, że pierw szym nie-białym zn ak iem takiej linijki jest z n a k #. P rzed tym zn a k ie m m ogą być w linijce tylko spacje i tabulatory.
P
Z niektórym i tak im i dyrektyw am i już się osw oiliśm y. N a p rz y k ła d w ielokrot nie w ystępow ała ju ż w naszych p ro g ram ach dyrektyw a tinclude
D yrektyw y są żąd an iam i, by prep ro ceso r w ykonał jakąś akcję. Preprocesor podejm uje ją zw y k le na nasze w yraźne żąd an ie. Jest jednak akcja, która zostanie w yk o n an a przez n iego samoczynnie. O ty m m ów i poniższy p aragraf.
5.1
Na p o m o c ro d a k o m Jeśli Twój k o m p u ter m a na klaw iaturze znaki: \
A
[
1
1
1
!
to m ożesz opuścić ten paragraf i zacząć czytać następny. M ów ić tu będziem y o tym , jak preprocesor pom aga program istom , którzy takich z n a k ó w na klaw ia turach nie mają.
196
Rozdz. 6. Preprocesor N a pomoc rodakom
w Zatem, jeśli nie masz na klawiaturze tych znaków, to po pierwsze bardzo Ci współczuję i zastanawiam się, jakim cudem dobrnąłeś w tej książce aż tutaj. Wiemy, jak bardzo ważne i częste są te znaki w programowaniu w C++. N ieste ty, w niektórych krajach komputery mają w miejscu tych znaków swoje znaki narodowe. Przykładem niech będzie Dania, gdzie w tych miejscach są znaki typV: A d E •• e O a o .. a •• o Pewnie nikt by się biednymi Duńczykami nie przejął, gdyby nie to, że twórca, języka C++ Bjarne Stroustrup pochodzi z Danii.1 Także w innych krajach mogą być na klawiaturach jakieś inne znaki —zamiast tych, o które nam chodzi. C o wtedy? Wyjście jest takie: Nieobecne na klawiaturze znaki można w programie uzyskać zastępując je tak zwanymi sekw&ticjawii trzyzYidkowytni. Są one tak dobrane, ż e odlegle przypominają znak, który mają imitować. Np. sekwencja ?? ( oznacza to samo, co znak [ Oto zebrane sekwencje i ich odpowiedniki: ??= ??/
# \
??!
I
??( ??) ??< ??>
1 1 { )
Prosty program wygląda z użyciem tych symboli tak: ? ? = in c lu d e < io s tre a m > u s in g n am esp a ce s t d ; i n t ma i n ( ) ??< int i ; c h a r t a b l ? ? (30??) ; for( ??<
i - 0 ; i < 30 ; i ++) tabl??(i??) = ' a ' + i; c o u t « " z a ł a d o w a n i e do e le m e n tu " « i « " w a r t o ś c i " << t a b l ? ? ( i ? ? ) << e n d l ;
??> cout «
" P o s z u k i wa n i e l i t e r y k l u b m ? ? / n " ;
f o r ( i = 0 ; i < 30 ; i ++) ^
1)
i f ( ( t a b l ? ? ( i ? ? ) ==' k ’ ) ??!??! ( t a b l ? ? ( i ? ? ) ==' m ' ) ) ??< c o u t « " Znak " « t a b l ? ? ( i ? ? ) << " j e s t w e le m e n c ie " << i << e n d l ; ??>
Mieszka w USA i pracuje w AT&T.
Rozdział. 6. Preprocesor Dyrektyw a # d e f i n e
197
??> Jeśli p o w y ższy p ro g ra m w ydaje Ci się m ało czytelny, to nie m a ra d y - m usisz pom yśleć o zm ian ie k law iatu ry Tw ojego kom putera.
6.2
D y re k ty w a #def ine Ta d y rek ty w a prep ro ceso ra ma postać: #de f i ne
wyraz
ciąg znaków zastępujących go
D yrektyw a d e f i n e 2 pow oduje, że w k o m pilow anym pliku k a ż d e następne w y stąp ien ie słow a -wyraz będzie zastęp o w an e w yszczególnionym ciągiem z n a ków . O czyw iście nie m usi być to koniecznie w yraz. M usi być to jednak g ru p a zn ak ó w bez białego znaku w środku. To natom iast, co jest ciągiem znaków zastępujących, m o że m ieć w środku białe znaki. N a p rzy k ład p o n iż szy fragm ent: i d e f i n e CZTERY 4 II . . . f l o a t tablica[CZTERY] ; i = CZTERY + 2 * CZTERY; funkcja(CZTERY- 0 . 3 ) ; c o u t « "Mówiłem c i to CZTERY r a z y "; zostan ie p rzez p rep ro ceso r autom atycznie zam ieniony na: 9
) £ ! r ' ' 1• i ?J *
Vv * ?,i
>t V
'
II ... f l o a t t a b l i c a [4]; i = 4 + 2 * 4; f u n k c j a (4 - 0 . 3 ) ; c o u t << "Mówiłem c i t o CZTERY r a z y "; Z w yczajow o w y razy zastępow ane pisze się w ielkim i literam i po to, b y w tekście pro g ram u p rzy p o m n ieć sobie, że nie ch o d zi tu o nazw ę obiektu, lecz o działanie d y rek ty w y d e f i n e . Pow tarzam - jest to tylko zwyczaj. R ów nie d o b rze m ożna pisać litery małe. Z au w aż, że dyrektyw a d e f i n e nie penetruje w n ętrza stringów (czyli tzw . stałych tekstow ych). Jest to oczyw iście zu p ełn ie logi czne. W ew nątrz stringów bow iem , zupełnie p rzy p ad k o w o mogą się pojaw ić identyczne w y razy , jak ten zastęp o w an y , ale w zu p e łnie innych znaczeniach i kontekstach. Lepiej w ięc, że d e f i n e nie m a d o nich dostępu. O to dalsze p rzykłady: 2)
[czytaj: „dyfajn"]
198
Rozdz. 6. Preprocesor D yrektyw a # d e f i n e # d e f i n e MAX_LICZ_PASAŻER 250 # d e f i n e LICZB_STEWARD 8 i d e f i n e PASAZ_NA_STEWD (MAX__LICZ_PASAZER/ LICZ_STEWARD) Jak w id ać, n astęp n e d y rek ty w y d e f i n e m ogą k orzystać z w łaśnie z d e fin io w a nych w cześniej nazw . D yrektyw ą d e f i n e m o żn a d efiniow ać zastęp o w an ie dow olnego ciągu z n a ków. P rzy k ład o w o - po takiej dyrektyw ie i d e f i n e ZEGAREK ( w h i l e ( ! z a j e t y ) c z a s ( ) ;
)
m ożna w p ro g ram ie zastosow ać zap is funkcja_a ( ) ; i =15 * c z y n n i k ; Z EG A R EK ,
x * 15 * l o g ( 1 7 ) ; co o d p o w ia d a zapisow i f u n k c j a _ a (); i =15 * c z y n n i k ; (while (!za jęty) czas(); ); x = 15 * l o g ( 1 7 ) ; Czyli d y re k ty w ą d e f i n e m o żem y sobie oszczędzić tro ch ę pracy p rzy p isan iu długich, a często w ystępujących instrukcji. Z au w aż, ż e d y rek ty w a d e f i n e nie ma na k o ń cu średnika. To dlateg o , ż e nie jest to n o rm a ln a instrukcja p ro g ra m u , I lecz d y re k ty w a dla preprocesora. Jeśli przez z a p o m n ien ie postaw im y ś re d n ik i na końcu, to zostan ie on u z n a n y jako jeden ze zn a k ó w zastępujących. C zy li w j p rzy p ad k u naszej definicji t d e f i n e CZTERY 4; ten sam fra g m e n t w yglądałby następująco:
II . . . float ta b lic a [4;J; i = 4; + 2 * 4;; f u n k c j a (4; - 0 . 3 ) ; c o u t << "Mówiłem c i to CZTERY r a z y "; O czyw iście na sk u tek w staw ienia tego średnika, z n a la z ł się on w z u p e łn ie n ieo d p o w ied n ich dla siebie m iejscach - w yw ołując w trak c ie kom pilacji k o m u nikaty o b łęd ach . Taki błąd jed n ak w y k ry w a się n a ty c h m ia st, w ięc nie jest to groźne.
Bardzo długie dyrektywy M oże się z d arz y ć, że ciąg zn ak ó w zastępujących jest d łu g i i z tego p o w o d u tru d n o um ieścić d y rek ty w ę w jednej linii. N ie m a p ro b le m u : g d y u z n a m y , że linia p o w in n a się skończyć - um ieszczam y na jej k o ń c u zn ak \ (o d w ro tn y ukośnik, "bekslesz") i konty n u u jem y p isan ie d y rek ty w y w linijce następnej. Tym sposobem d y re k ty w a m oże się ciągnąć p rzez w iele linijek.
Rozdział. 6. Preprocesor Dyrektyw a # d e f i n e
199
tdefine HMI Hahn-Meitner Institut\ fuer KernforschungN W.Berlin 39, Glienickerstr 100
Z w racam u w a g ę , ż e c h w y t ten sto so w ać trzeb a tylko w obec d y re k ty w y p rep ro cesora. Z w y k łą in stru k c ję p ro g ram u m o ż n a p rzecież sw o b o d n ie u m ieszczać w kilku linijkach. To z n a m y od d aw n a.
Definiowanie stałych W języku C d y re k ty w a ta tradycyjnie u ż y w a n a była d o d e fin io w a n ia stałych. W języku C ++ o p ró cz te g o sposobu m am y in ny, lepszy: m ian o w icie obiekty typu
const. O to p o ró w n an ie. S ta ry styl: #define ROZDZIELCZOŚĆ 8192 long widmo[ROZDZIELCZOŚĆ] ;
N o w y styl: const int rozdzielczość = 8192; long w i d m o (rozdzielczości;
O tym , żeb y nie u ż y w a ć define jako sp o so b u d ek laro w a n ia sta ły c h m ów iliś m y już p rz y okazji d efin io w an ia ob iek tó w ty p u const (str. 82). W skrócie Jo m o żn a streścić tak: Stosując defin io w an ie stałych jako obiekty ty p u const dajem y k o m p ila to ro w i w iększe szan se w ykrycia n aszy ch e w en tu a ln y ch om yłek. In n y m p o w a ż n y m zasto so w an iem d y re k ty w y define było d efin io w an ie tak z w an y ch makrodefinicji. To także nie w y trz y m a ło p róby czasu , o c z y m po ro zm a w ia m y za chw ilę. Z asto so w an ie m , k tó re p ró b ę czasu w y trzy m a ło , jest m ięd zy in n y m i definiow a n ie sym boli dla kom pilacji w arunkow ej. T akże o tym b ę d z ie m y m ów ili n ieb aw em .
W N a koniec jeszcze je d n a uw aga. D użo się n ap raco w ałem by w p o ić C i zasadę, że definicja to jest m o m en t, g d y rezerw uje się d la jakiegoś obiektu m iejsce w p a m ięci - czyli inaczej, jest to miejsce w p ro g ram ie, g dzie d a n y o b ie k t się rodzi. T y m czasem tutaj sło w o define znaczy coś z u p ełn ie innego. T utaj ty lk o okreś lam y , że jak n ap o tk a się jeden ciąg zn ak ó w , to należy go zam ien ić n a inny ciąg z n ak ó w . Ż a d e n o b iek t tutaj nie pow staje. Z apytasz: g d zie tu konsekw encja? N ie m a żad n e j konsekw encji i p rzep rasza m C ię za to. N a u sp raw ied liw ien ie d o d a m , że tam to m ó w iłem o kom pilatorze. W tym ro zd ziale ro zm a w iam y o jeg o słu żący m - p rep ro ceso rze, który p rz y c h o d z i jeszcze za n im z jaw ia się p raw d z iw y k o m pilator. N ie w ym agajm y w ięc konsekw encji od jego głu p szeg o służącego. Jak w idać, nieco innym językiem m ó w i się d o p rep ro ceso ra - czyli: p ro sz ę o p o b łażliw o ść w linijkach zaczynających się od zn ak u # .
200
6 .3
Rozdz. 6. Preprocesor Dyrektyw a # u n d e f
D yrektyw a # u n d e f Jeśli d y re k ty w ą d e f i n e określiliśm y jakąś nazw ę, to ow o polecenie (nie chce m i przejść p rz e z g a r d ło - ta: definicja) obow iązuje od m o m en tu w ystąpienia tej linii w p ro g ram ie, a w ażne jest d o końca pliku. C zasem jed n ak m oże nam zależeć, aby preprocesor zapom niał o poleceniu w y d an y m m u dyrektyw ą d e f i n e . W tym celu w y starczy użyć dyrektyw y: # u n d ef wyraz To oczyw iście skrót od ang. undefinr. Począw szy o d tego miejsca w program ie, p rzetw arzan ie dalszych linijek będ zie się o d b y w ało tak, jakbyśm y poprzedniej dyrektyw y # d e f i n e wyraz
.......
nie w y d aw ali W ydaje mi się, że ta dyrektyw a najczęściej p rzy d aje się w trakcie tw orzenia pliku, k tó ry ma być w łączany do innego (za pom ocą # i n c l u d e ) . W tym pliku m ożem y p rzecież sobie roboczo definio w ać jakieś n azw y , lub m akrodefinicje - a jeśli nie chcem y, by obow iązyw ały one n ad al w pliku, d o którego ten nasz zostaje w łączony - na końcu naszego pliku u n ie w aż n iam y je za pom ocą #undef.
6.4
M ak ro d efin ic je P odobnie jak w języku C, tak i tu w C++, d y rek ty w a d e f i n e m oże służyć do tw orzenia m akrodefinicji. R ozw ażm y tak i przypadek: #define KWADR(a)
((a) * (a))
M ów im y, że słow o KWADR jest n a zw ą tej makrodefinicji.
Jak to działa? O tóż p rzed przystąpieniem do w łaściw ej kom pilacji - preprocesor (czyli straż p rzed n ia kom pilatora) - zam ien ia w tekście p ro g ram u w szelkie w ystąpienia w yrażenia KWADR (parametr)
na w y rażen ie ( (parametr) * (parametr)
3)
[Skrót ten czytamy: „andef"].
)
Rozdział. 6. Preprocesor M akrodefinicje
201
P o takiej z a m ia n ie p rz y stę p u je się d o w łaściw ej kom pilacji. Inaczej m ów iąc, n astęp u jące linijki p ro g ra m u : a = K W A D R (c ) + KWADR(x) ; cout << KWARD(m+5.4);
zam ien ią się a u to m a ty c z n ie na linijki: a = ( (c) * (c)) + ((x) * ( x ) ) ; cout « ((m+5. 4) * (m+5.4));
D o czego m o że s łu ż y ć taka m akrodefinicja? N a p rz y k ła d d o teg o , by zam iast stosow ać w linii sk o m p lik o w a n y za p is - u p ro ścić go sobie. W m akro d efin icji m o ż e być też w ięcej p a ra m e tró w #define ILOCZYN(a,b,c)
( (a) * (b) * (c) )
P rzy p o m in am , ż e nie m o że być spacji, ani - ogólniej - białych z n a k ó w w w y ra żeniu ILOCZYN ( a , b , c \ . Biały z n a k bow iem k o ń czy określenie m akrodefinicji, a z ac zy n a określenie tego, czy m ma o n a być zastą p io n a. Czyli jakby o tw iera ja k b y ciało tej „funkcji". I (N a p ra w d ę to ciało n az y w a m y rozwinięciem m akrodefinicji).
inline contra makrodefinicja P arag raf ten p isz ę m ię d z y innym i d lateg o , b y o brzydzić Ci o ch o tę d o uży w an ia m akrodefinicji. M yślę, ż e nie p rzy jd zie m i to tru d n o . N a p ew n o , d ro g i C zy teln ik u , p om yślałeś ju ż o funkcjach ty p u inline, o któ rych ro zm a w ialiśm y w p o p rzed n im ro zd ziale. Z aw ołasz p ew n ie: „To przecież to sam o!" N ie, n ie to sam o. Praw ie to sam o , ale są różnice. To z p o w o d u tych różnic szy k u ję c z a rn ą p ro pagandę. N ajw ażniejsza ró żn ic a to to, że m ak ro d efin icja jest jakby tę p y m narzędziem , m echanicznym zam ien ian iem jed n ego strin gu na drugi. N ie m a tu sp raw d za nia typ ó w p a ra m e tró w (jakby arg u m en tó w ), nie ma s p ra w d z a n ia zak resu w aż ności u ży ty ch n azw . K om pilator nie m o że n as ostrzec czy p o p ełn iliśm y jakiś błąd. D latego m ak rodefinicji nie rad zę u ży w ać. Lepiej zasto so w ać funkcję typu i n l i n e , k tó ra n a m to w szystko zag w aran tu je. Dla wtajemniczonych: IVmakrodefinicja (w przeciwieństwie do funkcji) - nie może mieć wywołań rekurencyjnych, a w dodatku jej nazwa nie może być przeładowywana. D rugi p o w ó d to n ie o c z e k iw a n e efe k ty u b o czn e . R ozw ażm y taki p rz y p a d e k . M am y n aszą m akrodefinicję: #define KWADR(a)
( (a) * (a) )
i zastosujem y ją w tak im w yrażeniu: int x = 4, p; p = KWADR(x++); cout << "p = " << p << ", x teraz = " << x;
202
Rozdz. 6. Preprocesor M akrodefinicje W rezu ltacie w ykonania tego fragm entu otrzy m am y na ekranie: p = 2 0 , x teraz = 6
N ieza u w aż en ie dla nas x zo stało inkrem entow ane dw ukrotnie. W y n ik u sp o d ziew aliśm y się także innego - przecież (4 4 4) = 16. Z atem dlaczego ? To p r o s te . Łatw o to zrozum ieć, gdy ro zp iszem y sobie w y rażen ie, w którym n a stą p iła m akrodefinicja. W ygląda o n o w ted y tak: p = ( (x++) * (x++) ) ;
Jak w id ać inkrem entacja została w ykonana d w u k ro tn ie, m im o że z a m ierza liś m y zro b ić to jednokrotnie. C hciałbym zaw ołać te ra z trium falnie: „ -A nie m ó w i łem?! N ie uży w ajm y m akrodefinicji wcale!". Tak jed n ak nie zaw ołam , g d y ż są (były)...
...sytuacje kiedy makrodefinicja przydaje się4 Z ap y tasz: „-M im o że nie sp ra w d z a nam typu a rg u m e n tó w ?" N ie ty lk o mimo, ale w ręcz w łaśnie dlatego. Są sytuacje, g d y chcem y oszukać kom pilator. N a p rz y k ła d w ted y , g d y nie chcem y, by kom pilator s p ra w d z a ł nam typ a rg u m e n tów . K lasycznym p rzy k ła d em jest taka m akrodefinicja: #define MAX(a,b)
( ( (a) > (b)) ? (a)
:
m o żem y z niej korzystać n iezależn ie czy p o ró w n u jem y ze sobą dw ie liczby c z y d w a a d re sy , czy też znaki. T y p arg u m en tó w n ie jest bow iem s p ra w d z a n y . G d y b y śm y jednak chcieli n ap isać to sam o, jako fu n k cję ty p u i n l i n e , to n a le ż a łoby d o k ła d n ie określić ty p arg u m e n tó w . W ym agałoby to zap ew n e n a p isa n ia kilku w ersji takiej funkcji, z a leżn ie od typu p o ró w n y w an y ch arg u m en tó w . Dla bardzo wtajemniczonych: Szablon jest właśnie takim (o wiele lepszym) narzędziem do automatycznego pisania różnych wersji takiej samej funkcji. Jeśli w ięc zd ecy d u jem y się na p o słu g iw an ie się tą m ak rodefinicją, p am iętajm y , że tak sa m o jak p o p rzed n ia, m o że być o n a ź ró d łe m w sp o m n ian y ch efek tó w uboczn y ch .
Nawiasy O jeszcze jednej rzeczy m u szę w spom nieć: Czy z a u w a ż y łe ś jak gęsto ro z w in ię cia m ak ro d efin icji z a o p a try w a łe m w naw iasy? D o teg o sto p n ia , że w ręcz tru d n o to było czy teln e. N ie bez p o w o d u . W eźm y taką (ry zy k o w n ą) m akrodefinicję #define WYR(a,b,c)
a * b + c
Jeśli u ż y je m y tej m akrodefinicji w taki sposób: 4)
W najnowszych wersjach języka C++ nawet ta sytuacja odpada. Zamiast makrodefinicją lepiej posłużyć się narzędziem zwanym szablonem funkcji. O szablonach napisałem książkę p.t. „Pasja C++". Zajrzyj do niej po przeczytaniu „Symfonii".
Rozdział. 6. Preprocesor Sklejacz nazw, czyli operator ##
203
y = W Y R (2, 1 + 6.5, 0) * 1000;
to w efekcie d z iałan ia prep ro ceso ra linijka ta zam ieni się na taką: y = 2 * l + 6 . 5 + 0 *
1000;
C zyli z am iast obliczyć: (2 * (1 + 6.5)
+ 0 ) * 1000
obliczym y: (2*1)+
6.5 + (0 * 1000)
ELEMENT(śruba) ELEMENT(pudlo)
O d p o w iad a to takiem u zestaw ow i instrukcji: int srubaMaterial; double srubaWaga; double srubaCena; int pudloMaterial; double pudloWaga; double pudloCena;
204
Rozdz. 6. Preprocesor Zam iana param etru aktualnego m akrodefinicji na s t r i n g Jeśli takich elem entów m ają być setki, to może się to opłacić...
Przy "produkcji" jednej nazwy można tego operatora użyć wielokrotnie Idefine BOE(typ,co) boeing_ ## typ ## _ ## co ## _cena
w p rz y p a d k u zapisu cout << BOE(747, skrzydło);
Jest o n zastąpiony przez cout
<< b o e i n g _ 7 4 7 _ s k r z y d l o _ c e n a ;
Jak w id ać, dzięki temu operatorow i zaoszczędzić m ożna pisania. M im o to, p raw ie nigdy go nie u żyw am . (Może trochę bardziej p rzy d atn e użycie tego o p erato ra zobaczym y niebaw em , na str. 212).
6.6
Z a m ia n a param etru aktu aln eg o m a k ro d e fin ic ji na string Z ałóżm y, że w ym yśliliśm y taką makrodefinicję: #define POKAZ(x)
cout «
(x) ;
P ozw ala ona wyświetlić na ekranie w artość jakiegoś obiektu (w yrażenia). Jeśli g d z ie ś w program ie użyjem y tej m akrodefinicji w taki sposób. int obiekt = 5; POKAZ(obiekt) ; POKAZ(obiekt + 7); P O KAZ(12) ; P O KAZ(10 + 2);
□
to w rezultacie na ekranie zobaczymy 5 12
12 12
Jeśli w m akrodefinicji, w rozw inięciu jej u ży jem y p aram etru p o p rzedzonego zn ak iem #, to tym sam y m o trzy m am y strin g będący kopią tego, co tam - jako p aram etr a k tu a ln y - napisaliśm y. Po p ro stu : jeśli naszą m akrodefinicję zm ienim y na taką: Idefine POKAZ(x) cout << #x «
”= " «
a frag m e n t program u będzie n ad al taki sam int obiekt = 5; POKAZ(obiekt) ; POKAZ(obiekt + 7 ) ;
(*) << e n d l
Rozdział. 6. Preprocesor Dyrektywy kompilacji w arunkowej
205
P O K A Z (12); P O K A Z (10 + 2);
to na ekranie zobaczymy taki, ciekawszy tekst obiekt= 5 obiekt + 7= 12
12= 12 10 + 2= 12 Jak w id zisz, p o sta w ie n ie w rozw inięciu m akro d efin icji zn ak u ' # 1 p rz e d p ara m e tre m form alnym - sp o w o d o w ało , że tek st w id o c z n y na ek ra n ie p rz e d zn a k ie m ' = ' w yjaśnia n a m , co w yśw ietlam y. Jak to się stało? O tóż w p rz y p a d k u pierw szego w y w o ła n ia m akrodefinicji w y rażen ie #x ozn acza (w rozw inięciu m akrodefinicji) string " o b i e k t " . <♦ W d ru g im w y w o ła n iu w y rażen ie # x o zn acza strin g " o b i e k t + 7", W ciele m akrodefinicji w id zim y , że strin g ten w y p isy w an y jest n a ekranie d z ię k i stru m ien io w i c o u t . (Jest tam m ięd zy in n y m i c o u t << #x). T a k a m akrodefinicja m o ż e być b ard zo p rz y d a tn a w trakcie p racy n a d p ro g ra m e m . C zęste w y św ie tla n ie zaw artości zm ien n ej w tej części p ro g ra m u , która za c h o w u je się nie tak, jak chcielibyśm y - m o że p om óc w o d s z u k a n iu błędu.
.7
D y re k ty w y k o m p ila c ji w a ru n k o w e j Z d a rz a się, że chcielibyśm y, by pew n e linijki p ro g ram u pojaw iały się w nim ty lk o w ted y , g d y tego zaż ąd am y . N a p rzy k ła d n a etapie u ru c h a m ia n ia p ro g ra m u p rzy d ają się linijki w ypisujące na ek ran ie w y n ik i pośrednie. x = i * czynnik[r] + szereg(x0); cout << "teraz x = " « x << endl; m = funkcja(x);
/ / p o m o c n ic z y w y d r u k
P o tem jednak, g d y p ro g ra m działa p o p raw n ie, w y d ru k i takie nie są ju ż p o trze b n e. T eoretycznie m o ż n a b y więc je usunąć, n o ale - kto w ie - czy k ie d y ś jeszcze p rz y robieniu jakiś m odyfikacji nie p rz y d a d z ą się. W yjściem jest oczyw iście chw ilow e ujęcie ich w zn ak i k o m en tarza. W yjście to n ie jest dobre. Jeśli b o w iem w program ie m am y d u ż o takich w y d ru k ó w ko n tro l n y ch rozsianych po ró żn y ch miejscach, to d u ż o się napracujem y u jm u jąc je w k o m en tarze. D ru g i p o w ó d jest jeszcze w ażniejszy - jak p am ięta sz ko m en tarzy ty p u /*...*/ nie m o ż n a zag n ieżd żać. Jeśli chcemy instrukcję, (albo kilka instrukcji) ująć w ta k i ko m en tarz, a k o m e n tarz typu /*...*/ ju ż w tym fragm encie jakoś jest u ż y w a n y , to jesteśm y bezsilni. Kom pilator, k tó ry nie pozw ala na z ag n ieżd ża n ie k o m e n ta rz y - próbę tak ą u zn a za błąd.
206
Rozdz. 6. Preprocesor Dyrektywy kompilacji w arunkowej Jest in n y sposób. W yjście to n azy w a się kompilacja warunkowa. 1 olega to na tymi, iż, w zależn o ści od sp ełn ien ia p ew n y ch w aru n k ó w , o k reślo n e linie p ro g ra m u s ą k o m p ilo w a n e lub nie. R ealizujem y to za pom ocą d y re k ty w p rep ro ceso ra. To o n p rz y g o to w u je k o m p ilato ro w i m ateriał i to on o k reślo n e linie p ro g ram u m o że o d rz u c ić z procesu kom pilacji. Kompilacja o d b ę d z ie się tak, jakby te linijki n ig d y w p ro g ram ie nie istniały. Jak się to robi? B ardzo p ro sto . O tóż obszar kom pilacj i w a ru nkow ej o g ra n ic z a m y linijk am i b ęd ący m i o d p o w ie d n im i dyrek ty w am i p rep ro ceso ra.
Dyrektywa # i f #if
warunek // linie kompilowane warunkowo
#endif
Jak w id ać, p rzy p o m in a to w p e w n y m sensie z n a n ą n a m instrukcję i f . W arunek jest to stałe w yrażen ie. R o zu m iem to jako w y ra ż e n ie , w k tó ry m k a ż d y e le m e n t jest stały , a jego w artość jest ju ż z n a n a w czasie, g d y p rep ro ceso r p racu je n a d tą linijką. In n y m i słow y, n ie m o ż e tam w ystąpić ż a d e n z ob iek tó w (n p . z m ie n nych) w y stęp u jący ch w p ro g ram ie. W arunek jest w te d y sp ełn io n y , g d y w y ra ż e nie w a ru n k o w e jako całość m a w arto ść różną o d z e ra („prawda ).
Bliżej o warunku Jaki m o ż e być ten warunek? O to kilka p rz y k ła d ó w
•
#if
•
#if NAZWA > 2
• •
#if (N A Z W A _ A == 2 || N A Z W A _ B > 10) #if N A Z W A — 6 && d e f i n e d ( W E R S J A _ R O B O C Z A )
NAZWA = =
NAZWA2
W
o statn iej linijce zo b ac zy łeś użycie je d n o a rg u m e n to w e g o defined, k tó ry m oże m ieć d w ie , m ało różniące się form y: defined
n a zw a
defined
(n a z w a )
o p e ra to ra
Tego o p e ra to ra u ży w a się w w y rażen iach w a ru n k o w y c h d la p re p ro c e so ra . Jak się d o m y śla sz , p rzy jm u je on w a rto ś ć true, jeśli d a n a n a z w a rzeczyw iście b y ła zd efin io w an a w cześn iej d y re k ty w ą #define n a z w a , a n ie o d w o łaliśm y tej definicji d y re k ty w ą
#undef nazwa. Jeśli ch c e m y się up ew n ić, ż e coś nie zostało z d e fin io w a n e , m o ż em y p o p ro stu p o p rz e d z ić te n o p erato r w y k rz y k n ik ie m •
#if N A Z W A ==
6 &&
?d e f i n e d ( W E R S J A _ R O B O C Z A )
Rozdział. 6. Preprocesor Dyrektywy kompilacji w arunkowej
207
Oto przykłady: #define RODZAJ 2 //
. . .
cout << "To jest kompilowane zawsze " << endl;
#if (RODZAJ *= 1) n
// O 0
...
cout « "To jest kompilowane, gdy " "warunek jest spełniony " << endl;
#endif cout « ”To znowu jest kompilowane zawsze "«endl;
// ©
R z u t o k a w y starczy , by stw ie rd z ić , że w a ru n e k n ie jest tu sp ełn io n y i linijka © n ie z o s ta n ie sk o m p ilo w a n a . Po linijce O n a s tą p i b e zp o śred n io linijka © . N a s z ą d y re k ty w ę #endif
m o ż n a też zap isać też jak o #endif
//(RODZAJ == 1)
T u , w k o m e n ta rz u , jest p rz y p o m n ia n y w a ru n e k - b a rd z o się to p rzy d aje. W sy tu a cji, g d y p a rę # i f 'ó w z a g n ieżd żo n y c h jest je d e n w d ru g im , b a rd z o u ła tw ia to ro z e z n a n ie , w k tó ry m m iejscu kończy się jaki o b szar. Starsze kompilatory mogą zgadzać się jeszcze na taką konstrukcję
#endif
(RODZAJ == 1)
gdzie to "przypomnienie" nadal jest tylko przypomnieniem, ale nie ujętym wznakikomentarza. Następuje bezpośrednio po ł e n d i f . Dawniejmożna było tak zapisywać, ale pamiętajmy, że nowy standard ju ż tego nie dopuszcza.
Inne dyrektywy dla kompilacji warunkowej #if warunek instrukcje 1
#else instrukcje 2
tendif Jest d o k ła d n ie tak, jak się dom yślasz: zależn ie od sp ełn ien ia w a ru n k u albo do k o m p ilacji w chodzą instrukcje 1, albo instrukcje 2. N ajczęściej taką w ielo w arian to w ą kom pilację w a ru n k o w ą stosujem y, g d y nasz p ro g ra m m a m ieć w iele w arian tó w . Trzon jest ten sam , ale niek tó re części są w y m ie n ia n e zależnie o d bieżącej wersji. N a p rz y k ła d : piszem y p ro g ra m sterujący w y su w a n ie m p o d w o zia w sam olocie. W ró żn y ch typach sam o lo tu jest różny typ p o d w o z ia , jednak w sz ęd zie zasad a jest p o d o b n a, w ięc tylko n iek tó re funkcje trzeb a w y m ien ić na inne. O czyw iście m o ż n a skopiow ać stary p ro g ra m i zm o d y fik o w ać robiąc o sobny p ro g ra m dla je d n e g o ty p u , osobny d la dru g ieg o . N ie z a w s z e się to jed n ak opłaca. Jeśli b o w ie m pracując nad je d n ą w ersją tego p ro g ra m u w p ad n iem y na jakiś d o b ry
208
Rozdz. 6. Preprocesor Dyrektywy kompilacji w arunkowej
p o m y s ł d o ty c z ą c y tej c z ę śc i, k tó ra je st id e n ty c z n a d la o b u p r o g r a m ó w , to z a m ia n ę trz e b a b ę d z ie w p r o w a d z a ć d w u k r o tn ie - r a z w s ta ry m , a r a z w n o w y m p r o g r a m ie . M o ja p r a k ty k a m ó w i m i, ż e ta k ic h (n ie k o n ie c z n ie o d r a z u g e n ia l n y c h ) p r z e r ó b e k je s t z w y k le w iele. O p ła c a s ię k o r z y s ta ć z d o b r o d z ie js tw k o m p i lacji w a ru n k o w e j. O to ta k i fra g m e n t:
//
n a jp i e r w d e f in u je m y s o b ie d la w y g o d y ta k ie s y m b o le
#define #define #define łdefine
PODWOZIE_707 PODWOZIE_747 PODWOZIE_DC11 PODWOZIE_LIL
1 2 3 4
// t u t a j d e f i n i u j e m y , z k t ó r y m t y p e m // w t y m k o n k r e t n y m p r z y p a d k u #define
TYP_PODWOZIA
m a m y d o c z y n ie n ia
PODWOZIE_747
/ / ---------------------------------------------------------int w y s t a w _ k o l a () 1
#if
(TYP_PODWOZIA == PODW O Ź IE_707) cout << "Tak jest kapitanie, w y s t a w i a m 707\n"; #el i f (TYP_PODWOZIA — PODWOZIE_74 7) cout << "Tak jest kapitanie, w y s t a w i a m 747\n"; #eli f (TYP_PODWOZIA == PODWOZ I E _ D C 1 1) cout << "Tak j est kapitanie, w y s t a w i a m DCll\n"; #el i f (TYP_PODWOZIA — PODWOZIE_LIL) cout << "Tak jest kapitanie, w y s t a w i a m LIL\n"; #else cout << "To n i g d y sie nie p o w i n n o zdarzyć \n"; #error "zle z d efiniow ana wer s j a p r o g r a m u !!!!*' # e n d i f // T Y P _ P O D W O Z I A return 1;
} P o n ie w a ż w id a ć , ż e w e r s ja p r o g r a m u z d e f in io w a n a je st n a t y p p o d w o z ia PODWOŹ I E 7 4 7 , z a te m p o w y ż s z y f r a g m e n t z o s ta n i e k o m p ila to r o w i p r z e d s t a w io n y ja k o i n t w y s t a w _ k o l a ()
cout << "Tak jest r e t u r n 1;
kapitanie,
w y s t a w i a m 747\n";
} W p r z y k ła d z ie ty m z o b a c z y łe ś n o w ą d y r e k ty w ę #eli f
c o je s t ja k b y s k r ó te m o d e 1 s e i f i je st k o n s tru k e ją a n a lo g ic z n ą d o ta k ie j w ła ś n ie k o m b in a c ji in s tru k c ji. W y b a c z m i tę d y r e k ty w ę ł e r r o r , o k tó re j d o tej p o r y je s z c z e n ie w s p o m in a ł e m . Z d e c y d o w a łe m s ię je d n a k w s ta w ić ją tu ta j n a w y p a d e k , g d y b y ś k i e d y ś d o te g o m ie js c a k s ią ż k i s ię g n ą ł s z u k a ją c w z o rc a n a z a s to s o w a n ie k o m p ila c ji w a r u n k o w e j r ó ż n y c h w e rs ji s w o je g o p r o g r a m u .
Rozdział. 6. Preprocesor Dyrektywy kompilacji w arunkowej
209
A sw oją d ro g ą to z a ło ż ę się, że d o m y śla sz się jak d ziała d y rek ty w a # e nie, to i tak p o m ó w im y o ty m n ieb aw em . W arunek
r r or.
Jeśli
#ifdef
D y rek ty w a k o m p ilacji w aru n k o w ej #ifdef nazwa
// rozumieć jako: ifdcfined / / ... instrukcje #endif
n ie sp ra w d z a w a ru n k u , ty lk o sp ra w d z a czy d a n a n azw a została zd efin io w an a. T o zn aczy czy w y stą p iła d y re k ty w a #define nazwa . . .
i nie u n ie w a ż n iliśm y jej d y rek ty w ą #undef nazwa
Jeśli tak, czyli n a z w a jest p rep ro ceso ro w i z n an a , to d z iałan ie jest takie, jakby ch o d z iło tu o w a ru n e k i b y ł on spełniony. ' •
W arunek
fi
#ifndef
A n alo g iczn ie d y re k ty w a #ifndef nazwa2
// rozumieć jako: if NOT defined
/ / .... instrukcje #endif
.
s p ra w d z a czy n a z w a została zd efin io w an a i obow iązuje. Jeśli nie o b o w iązu je to tak, jakby w kom pilacji w aru n ko w ej - w a ru n e k b y ł spełniony. D y rek ty w y kom pilacji w aru n k o w ej m ogą być z ag n ieżd ża n e. O to p rzy k ład : #if (WERSJA == 1) #if (SZYBKOSC == 1)
instrukcje (1) # else
instrukcje (2) #endif #else #if (SZYBKOSC == 1)
instrukcje (3) #else
instrukcje (4) #endif #endif // WERSJA . '
'
...'fi
■'*.-•
,
'
, X.
Jeśli w trakcie kom p ilo w an ia tego fragm entu b ęd ą ju ż w m ocy n astęp u jące dyrektyw y: # d e f in e WERSJA 7
•
#define SZYBKOSC 1
to w rezultacie w skład naszego skom pilow anego p ro g ram u w ejd ą instrukcje (3).
210
6.8
Rozdz. 6. Preprocesor Dyrektyw a # e r r o r
D yrektyw a # e r r o r D yrektyw a ta m a form ę # error tekst
a pow oduje, że po n ap otkaniu jej kompilacja zostaje p rzerw ana i w y p isy w a n y jest ko m u n ik at o błędzie, którego częścią jest inform acja um ieszczona przez n as jako tekst O to, k iedy się to przydaje: #if (WERSJA —
1)
/ / ......... #elif (WERSJA ==2)
/ / ......... #else #error "Musi byc albo wersja 1 albo wersja 2!" #endif
Jeśli zapom nieliśm y zdefiniow ać symbol WERSJA, albo jeśli zdefiniow aliśm y go tak, że ma on taką w artość, iż żad en ze sp raw d zan y ch w aru n k ó w nie jest spełniony, w ów czas preprocesor natknie się na naszą d y rek ty w ę t e r r o r . D yrektyw a ta spow oduje przerw an ie dalszej p racy i w ypisanie w kom unikacie o b łęd zie - zam ieszczonego tekstu: Error directive: "Musi byc albo wersja 1 albo wersja 2!"
6.9
D yrek ty w a # l i n e Ma o n a postać
# lin e
s ta ła
"nazwa_pliku"
P ro p o n o w an a n a z w a p l i k u nie jest obow iązkow a. D yrektyw a ta służy do oszu k iw an ia kom pilatora. Jeśli nakażem y mu #line 128 "fikcja.cpp"
to o d tej pory kom pilator uzna, że jest to linijka 128 p rogram u - m im o że n a p ra w d ę jest to linijka n u m e r 10 (na przykład). D o datkow o ko m p ilato r b ęd zie m yślał, że kom piluje plik o n azw ie "fikcja.cpp". W łaściw ą nazw ę w ty m m o m en cie zapo m in a. D ajm y przykład. M am y p lik TST.cpp z program em , o którym w iem y, ż e m a b łęd y w linijce 10—tej i 20-tej. (W iemy to - bo na użytek tego p rzy k ła d u specjalnie je tam popełniliśm y). G dy kom pilujem y taki program , to o trz y m u jem y inform ację mniej w ięcej takiej treści: P lik
T S T . c p p ma b i e d y
w lin ijc e
10 i
20
N astęp u je teraz bliższe o p isan ie tych błędów. R obim y jed n ak taki ek sperym ent: N a p o czątk u program u d o p isu jem y jeszcze jed n ą linijką takiej treści
Rozdział. 6. Preprocesor W stawianie treści innych plików w tekst kompilowanego właśnie pliku
211
#line 500 "fikcja.cpp"
D op isan ie linijki s p o w o d u je oczywiście, że w szy stk ie dalsze p rz e su n ą się o jed ną lin ię w dół. B łędne są w ięc teraz linie 11 i 21. Spróbujem y teraz skom pilow ać ten sam program . O trz y m a m y znow u kom u n ik a t o błędzie, ale tym razem takiej m niej więcej treści: Plik fikcja.cpp ma biedy w linijce 511 i 521
5.10
W s ta w ia n ie tre ś c i in n ych p lik ó w w tekst k o m p ilo w a n e g o w ła ś n ie pliku D yrek ty w y p rep ro ceso ra # i n c l u d e
5
#include #include "nazwa_pliku_B"
p o w o d u ją, że w tek st kom pilow anego w łaśn ie p ro g ram u , w m iejsce, w którym znajd u ją się takie d y rek ty w y , zostaje w staw io n a treść innego pliku o wyszcze gólnionej nazw ie. Z u p ełn ie tak, jakbyśm y w ty m miejscu w pro g ram ie, będąc jeszcze w edytorze, sp ro w ad zili w to miejsce treść rzeczonego p lik u . D laczego tak w ięc po p ro stu nie zrobić ? Pierw sza o d p o w ie d ź brzmi: z lenistw a. ♦♦♦ Po drugie: jeśli sp ro w ad zan y m plikiem jest plik nagłów kow y, to przy ew entualnych zm ianach jakiejś deklaracji - w ystarczy k o rek ta w jed nym tylko pliku.
Wszystkie inne pliki, które korzystają z tego pliku - zobaczą tę (raz zrobioną zmianę). Bardzo to wygodne! ♦> Po trzecie: - g d y b y śm y chcieli do n aszeg o program u w łączyć w szystkie pliki n ag łów kow e z deklaracjami funkcji bibliotecznych, to nasze pliki p ro g ram o w e rozrastałyby się ogrom nie. Lepiej w ięc plik nagłów kow y w ypożyczyć na chw ilę, na sam ą okoliczność kompilacji. A t e r a z o r ó ż n i c y m i ę d z y p r z e d s t a w i o n y m i f o r m a m i tej d y r e k t y w y
Jeśli p rzy nazw ie pliku użyliśm y cu d zy sło w u , to plik, który nakazujem y w łączyć będzie przez preprocesor n ajpierw poszukiw any w bieżącym katalo g u , a jeśli tam nie zostanie znaleziony, to będzie szu k an y tak, jakby z a m ia st znaków cudzysłow u użyte były tam znaki < > . Jeśli zaś przy nazw ie plików są zn ak i < > to plik będzie poszukiw any w standar d o w y m miejscu, gdzie znajdują się pliki zw y k le w łączane (np. biblioteczne). M iejsca poszukiw ania p lik ó w włączanych tą d y rek ty w ą są jednak zależne od
5)
ang.
in c lu d c
- wstawiać, włączać, wcielać. [Czytaj: „inklud"]
212
Rozdz. 6. Preprocesor W stawianie treści innych plików w tekst kompilowanego właśnie pliku im plem entacji, w ięc należy się zaw sze u p e w n ić , jak w takim p rz y p a d k u postę p u je n asz kom pilator. R eguła jednak jest p rz e w a ż n ie taka: u ży w ając cu d zy sło w u w łącza się pliki, które sam i p is z e m y , n ato m iast z n a k i < > stosuje się w łączając np. pliki n a g łó w k o w e biblio tek. (Są o n e zw y k le z g ro m a d z o n e w jakim ś specjalnym k atalo g u ). D y rek ty w y #i n c l u d e m ogą się zag n ieżd żać. T o znaczy, że g d y tą d y re k ty w ą w łączam y jakiś in n y plik, to w nim m oże b y ć d y re k ty w a # i n c l u d e w łączająca jakiś jeszcze inny plik. P oziom ów z a g n ie ż d ż e n ia m oże być w iele.
Jak zagwarantować sobie jednokrotne włączanie danego pliku? M o że się zdarzyć, ż e d y re k ty w ą # include w łącz am y d o p ro g ra m u p lik i A, B, C , X. T ym czasem w plik u B jest - o czy m nie w iem y lub nie p a m ię ta m y d y re k ty w a # i n c l u d e w łączająca plik X. W zw ią z k u z ty m d o kom pilacji p ro g ra m u u ży te z o sta n ą w chodzące teraz w sk ład naszego p ro g ra m u pliki. A, B, X, C, X. Z atem p lik X b ęd zie k o m p ilo w an y d w u k ro tn ie. Może to spowodować problemy, gdy w pliku X scf fragmenty, które nigdy nie powinny pojawiać są? w kompilacji dwukrotnie. (Np. definicje zmien nych lub definicje funkcji). Jak się p rz e d tym u strzec ? Jest prosty sposób: U życie kom pilacji w aru n k o w e j. P lik X p o w in ien w y g lą d a ć na p rzy k ład tak: #ifndef PLIK_X #define PLIK X //z w y k ła treść p lik u iendif
Jeśli ten p lik zo stan ie w łą c z o n y do kom pilacji choć raz - z d e fin io w a n a zostanie n a z w a PLIK_X. E w e n tu a ln a p o w tó rn a p ró b a w łącz en ia tego plik u o d b ę d z ie się, g d y ta n a z w a już je st zd efin io w an a —czyli na m o cy kom pilacji w a ru n k o w e j (d y re k ty w a tifndef), nic z tego pliku p o ra z d r u g i k o m p ilo w a n e n ie będzie. K o n stru k cję tę m o żn a o b razo w o n azw ać strażnikiem nagłówka. S to so w an ie tego sp o so b u jest b a rd z o p o w sz e c h n ą p rak ty k ą .
Ciekawostka dla zaawansowanych M a ło kto w ie, że d y re k ty w a w łączająca plik m o ż e m ieć b ard ziej o g ó ln ą form ę # include n a z w a jz r o b io n a p r e p r o c e s o r e m
K ró tk o m ów iąc: za p o m o cą p rep ro ceso ra m o żn a k o n s tru o w a ć n a z w ę p lik u , który m a zo stać w łącz o n y d y rek ty w ą #include.
I
W tej k o n stru o w an ej "prep ro ceso ro w ej n azw ie" p o w in n y , o p ró cz n a z w y pliku, w y stą p ić o g ran iczające ją zn ak i < > lub " ". T ego p rzecież o c z e k u je zw y k ła d y re k ty w a # include.
Rozdział. 6. Preprocesor Dyrektyw a pusta
213
O to p rzy k ła d , w k tó ry m b ard zo w ym yślne n azw y pliku b u d u jem y za pomocą n ak ro d efin icji NAGL i u ży w an eg o w niej o p erato ra sklejania n a z w (zob. § 6.5, str. 203). #define SCIEZ_DO_PROJEKTU Idefine PODKATAL_NAGLOWKI Idefine SCIEZKA_DO_NAGLOW Idefine NAGL(x)
c:/lotnisko incl SCIEZ_DO_PROJEKTU/PODKATAL_NAGLOWKI
< ŚCIEŻKA DO NAGŁÓW ## /lotn
II x ##.h>
//a ta k b ę d zie m y z tego k o r z y s ta ć iinclude N A G L (odprawa) Iinclude NAGL(paszport) Iinclude NAGL(bezpiecz) Iinclude NAGL(pokład)
W rezultacie tych d y re k ty w I i n c l u d e d o bieżącego pliku ( w czasie kompilaci), w łączone zostaną następujące pliki: •
c: / lo tn is k o / incl / lotn_odpra w a .h
•
c: / lo tn isk o / incl / lotn_paszpor t.h
•
c :/lo tn isk o /in c l/lo tn _ b ezp iecz.h
•
c :/lo tn isk o /in c l/lo tn _ p o k la d .h
5.11 Dyrektywa pusta Jest to d yrektyw a składająca się z sam ego zn ak u I Jest to jedyny znak w tej linii. Taka d yrektyw a nie ma żadnego działania. Jest przez preprocesor po pro stu ignorow ana.
i.12 Dyrektywy zależne od implementacji D yrektyw y takie zaczynają się od słowa p ra g m a , po którym następuje kom en da charakterystyczna dla danego konkretnego kom pilatora (preprocesora) Ipragma k o m e n d a
D zięki tem u, w p ro w ad zo n a zostaje m ożliw ość używ ania dyrektyw w łaściw ych dla danego kompilatora, zatem szczegółowego opisu tych d y rek ty w trzeba szukać w opisie kom pilatora, którym się posługujem y. Jeśli komenda jest danem u konkretnem u typow i kom pilatora nieznana, napot kaw szy ją w program ie - ignoruje ją.
214
6.13
Rozdz. 6. Preprocesor Nazwy predefiniowane
N azw y p re d e fin io w a n e W trakcie pracy preprocesora, oprócz nazw , które sam i zd efin io w aliśm y , są jeszcze n azw y , które definiuje dla siebie sam preprocesor.
Uwaga typograficzna: W nazw ach tych w ystępują stojące obok siebie d w a zn ak i p o d k re ś lenia, które tu, w d ru k u książki w y g ląd ają niestety jak jedna d łu g a kreska. N ap ra w d ę jest wiec _ _ a n i e __ Oto te p red efin io w an e nazw y: _LINĘ__
•
ta nazw a kryje w sobie n u m er linijki pliku, n ad którą p rep ro ce sor w łaśnie pracuje. Łatw o się dom yślić, że jeśli k o m pilator w ykryw a b łą d i w ypisuje k o m u n ik at o b łęd zie w linijce nr... tc posługuje się w łaśnie tą nazw ą.
•
pod tą n azw ą zapam iętan a jest n azw a w łaśn ie k o m p ilo w an e
NAME
go pliku DATĘ
•
pod tą n azw ą zdefiniow any jest ciąg zn ak ó w odpow iadający dacie w m om encie kompilacji. M ogą być d w ie form y
"Mar 19 2006" "Mar 3 2006"
zależn ie od tego, czy n u m e r dnia jest je d n o - czy d w u cy fro w y . _TIME__
•
ta nazw a kryje w sobie ak tu aln y czas w m om encie kom pilacji M a on postać ciągu zn ak ó w (C -stringu) "hh:mm:ss". Na przykład
"15:45:08"
O istn ien iu tych predefiniow anych nazw m ożna się p rzek o n a ć kom pilując p h k w k tó ry m zn ajd ą się następujące linijki. cout << "Kompilacja tego pliku " « FILE— ; cout << "\n (linijka " « LINĘ << ") \n zaczęła sie: " « __DATĘ__
<< " o godzinie: "
<<
TIME
<< endl;
Po u ru ch o m ien iu takiego fragm entu program u na ek ran ie zostanie w ypisany tekst Kompilacja tego pliku T.C (linijka 10) . , AC co zaczęła sie: Jun 04 2006 o godzinie: 00:45:50
Rozdział. 6. Preprocesor Nazwy predefiniowane
215
N a w sz elk i w y p a d e k p o d k re ślę jeden fakt: Jeśli za 10 m in u t u ru c h o m isz je szc ze raz ten sam p ro g ra m , to informacja o czasie będzie id en ty czn a .__ TIME__ i __ DATĘ__ zaw ie rają b o w iem inform acje, k tó re d o ty czą m o m e n tu czaso w eg o sam ej kom pilacji. W kom pilacji zo stają o n e ta m z a m ro ż o n e na zaw sze. Jeśli zaś chodzi Ci o wypisanie na ekranie bieżącej dały, godziny i minuty, to realizuje się to za pomocą standardowych funkcji bibliotecznych, takich jak tim e ( ) , l o c a l t i m e () itd. Ich deklaracje zebrane są w pliku na główkowym t i m e. h D odatkow o zdefiniowana jest jeszcze nazwa _cplusplus
Jeśli k o m p ilu jesz k o m p ilato rem C++, to z w y k le m oże on na n a sz e życzenie z a c h o w y w a ć się tak, ja k b y byl k o m p ilato rem k lasycznego C. W ów czas ta n a z w a nie jest z d e fin io w a n a , za to z d e fin io w a n a jest n azw a
STDC
(sk ró t o d S T a n d a r D C )
C z a se m się to p rzy d aje. N ajczęściej u ż y w a łe m tej m ożliw ości w o k re sie przej ścio w y m , k ie d y n ieśm iało przerab iałem m oje p ro g ra m y z C n a C++. N ie byłem w ó w c z a s p e w n y i ch ciałem zaw sze m ieć m ożliw ość w ycofania się. D latego u ż y w a łe m w ó w c zas k om pilacji w aru n k o w ej, g d z ie w a ru n k ie m b y ło zdefinio w a n ie lu b n ie z d e fin io w a n ie tej nazw y.
Do czego mogą się przydać takie predefiniowane nazwy N ie p rzy d ają się często, ale ro zw ażm y ta k i p rz y p a d e k P ro g ra m n asz sk ład a się z w ielu plików , n a d k tó ry m i pracują ró żn i p rogram iści d o k o n u ją c ciągłych u le p sze ń . D ajm y n a to, ż e w skład p ro g ra m u w chodzi m o d u ł o bsługujący ra d a r. D ostajem y g o od kolegi już w p o staci binarnej, g o to w ej do zlin k o w a n ia z naszym i m o d u łam i. M o że się czasem o k azać p rzy d a tn e , by w g o to w y m p ro g ra m ie w iedzieć, z k tó rą w ersją części „rad a ro w ej" m a m y d o czynienia. Ja k to zrobić? N a p rz y k ła d w ym agając o d kolegi, by um ieścił w jego pliku fu n k cję w e r s j a _ r a d a r u ( ) , która na ek ra n ie w ypisze tek st inform ujący o n u m e rz e w ersji. P rzy okazji m odyfikacji sw ojego m o d u łu , n a s z kolega p o w in ie n zaw sze zm ien ić treść tego tekstu - zatem na ek ran ie p o jaw iał się b ę d z ie n o w y o pis w ersji. N iestety nasz kolega jest niechluj i m im o d o k o n an ia ja k ich ś m odyfikacji p ro g ra m u - często nie u a k tu aln ia tekstu inform ującego o w ersji. C o robić? Jest wyjście. W ersję poznać m o żem y np. po n azw ie p lik u , w którym d a n e funkcje („ ra d a ro w e ") są um ieszczone, a tak że po m om encie kom pilacji te g o pliku. W y starczy , b y kolega p iszący ten m o d u ł, u m ieścił w nim funkcję void wersja()
{
216
Rozdz. 6. Preprocesor Ćwiczenia cout « "Cessna: " « _ F I L E _ « " " << DATĘ « " " « TIME << endl;
ł D zięki tem u ile razy tę funkcję wywołam y, na ek ra n ie pojaw i się Cessna: crs71.c MAY 10 2006 15:03:47
W ażne, że od tej pory ta informacja nie jest z ależn a od dobrej w o li koleg: piszącego ten plik. Bez w zględu na jego niechlujstw o zaw sze m am y ś la d w p o s taci p raw d ziw ej nazw y jego pliku i tego, kiedy on te n plik ko m p ilo w ał.
W
6.14
Ć w ic ze n ia Jaki znak rozpoczyna dyrektywy preprocesora? Czy musi być pierwszym znakiem w linii? Do czego służą tzw. sekwencje trzyznakowe? (Nie musisz ich zupełnie znać, ale powinie neś przynajmniej wiedzieć, po co są). Wyjaśnij, co robi poniższa dyrektywa preprocesora: #define A B 1 2 Ł.‘ .i-.*
W programie są następujące dyrektywy: idefine LEWY_POKLAD 20 ♦define PRAWY_POKLAD 30 Idefine SSS LEWY_POKLAD + PRAWY_POKLAD
Co, w związku, z tym wypisze na ekranie następująca instrukcja: cout «
(2 * SSS) «
endl;
Masz jakieś uwagi krytyczne do tej ostaniej linijki? Mamy taki fragment programu. Tylko jedna z poniższych instrukcji spowoduje błąd kompilacji. Która i dlaczego tylko ta jedna? const int trzy = 3; Idefine DZIESIEC 10;
//... double sl = trzy / DZIESIEC; double s2 = DZIESIEC + trzy; double s3 = DZIESIEC / trzy;
VI
Co zrobić, jeśli dyrektywa #define jest za długa, by się zmieściła w jednej linii programu? Powyższe ćwiczenia definiowały pewne stałe za pomocą dyrektyw procesora. Przepisz je modyfikując tak, by zamiast tych dyrektyw były stałe obiekty typu const. Czy poprzednie błędy (będące przedmiotem tych zadań) przeniosą się na ten nowy sposób? Który sposób wybrałbyś pisząc swój program?
V III
Czy można dwukrotnie dyrektywą define zdefiniować tę samą nazwę?
Rozdział. 6. Preprocesor Ćwiczenia
217
idefine NNN
10
II... idefine NNN
50
Co by musiało być między tymi dwoma powyższymi dyrektywami, by to było możliwe? Mamy taką makrodefinicję: idefine DZIEL(a,b)
(a/b)
Co pojawi się na ekranie na skutek instrukcji: cout «
DZIEL(2+3, 5.0) «
endl;
Co należy poprawić w makrodefinicji, by na ekranie została wypisana wartość 1? Napisz makrodefinicję DZIEL (z poprzedniego ćwiczenia) w postaci funkcji inline pracującej na argumentach typu double. Mamy taki fragment programu: idefine CZYNNIK(a,b) int d = 0; while(d < 10) 1 cout « cout «
((a)/ { (a) + (b)))
"Czynnik = " « CZYNNIK(++d, ", dla d= " « d « endl;
2.0);
} Ile razy wykonają się instrukcje pętli. (Czyli: ile będzie obiegów pętli?). Czy w makrodefi nicji jest błąd? Zmień kod z powyższego zadania tak, by makrodefinicję CZYNNIK zastąpiła funkcja (inline) void CZYNNIK(double a, double b) ;
Ile razy wykona się wtedy pętla? ■ [K T !!
Napisz makrodefinicję o nazwie MISJA, która przysłany do niej argument (np. "12") zamieni na jeden wyraz np. Apollo_12. (Oczywiście jeśli argumentem będzie 9 to w rezultacie otrzymamy Apollo_9). Napisz makrodefinicję o jednym parametrze formalnym, która będzie wypisywała na ekranie: - numer linii programu, w której ją wywołano, - wyrażenie, które było jej argumentem (ma być ujęte w cudzysłów), - wartość tego wyrażenia. To znaczy: jeśli w programie są dwa wywołania tej makrodefinicji (w linii 120 i 150), to na ekranie pojawić powinien się np. taki tekst: W linii 120, wartość wyrażenia "obiekt" = 12 W linii 150, wartość wyrażenia "obiekt" = 0
Program ma mieć kilka wariantów. W tym celu posługujemy się kompilacją warunkową.
Są już następujące dyrektywy preprocesora idefine GRUDZIEN_2006 #define SIERPIEN_2005
1 2
218
Rozdz. 6. Preprocesor Ćwiczenia Potem jest dyrektywa, w której następuje wybór wersji. Poniżej widzisz, że wybrano tu akurat wersje "grudniową" #define WERSJA EKSPERYMENTU
GRUDZIEN_2006
Potrzebujemy teraz dyrektywy #def in e TYP_SPREZARKI ... Powyżej, w miejscu wielokropka, nazwa ta, w zależności od wybranego typu ekspery mentu, ma mieć wartości - dla eksperymentu grudniowego: 3000 - dla eksperymentu sierpniowego: 2050 Jeśli zaś WERSJA_EKSPERYMENTU została określona inaczej, lub nie została określona wcale, na nastąpić przerwanie kompilacji i wydrukowany komunikat "Nieokreślony został typ eksperymentu" XV! XVII XVIII
Czy można zagnieżdżać dyrektywy preprocesora # if ? Dyrektywa # i f de f ma podobne działanie jak de f i ned. Kiedy się używa jednej, a kiedy drugiej? Która jest "bardziej samodzielna"? Jakie jest działanie poniższych dyrektyw? # in c l u d e # in c l u d e "plikB.h"
XIX
Czy dyrektywy i i n c l u d e mogą być zagnieżdżane? Czy poprawne jest złożenie takich dwóch dyrektyw? # d e f in e PLIK i i n c l u d e PLIK
_ . . a _r_v^ j|- -‘■ * ■' .. •» '■ nm , Napisz plik nagłówkowy, w którym będzie tylko jedna deklaracja funkcji. Ma być to funkcja o nazwie f zwracająca rezultat typu double, a wywoływana z dwoma argu mentami typy in t . Drugi argument ma wartość domniemaną równą zero. Deklaracja taka powinna zasadniczo pojawić się w kompilowanym programie tylko jednokrotnie. (Dlaczego?). Jak to zagwarantować pisząc ten plik nagłówkowy? Jaka jest różnica między tzw. dyrektywą pustą (#), a operatorem preprocesora (#) Czy jest ryzyko nieporozumienia? Pierwszy program z tej książki uzupełnij tak, by oprócz tekstu Witamy na pokładzie pojawiła się na ekranie informacja o tym: 1. Jak nazywa się plik programu, 2. W której jego linii nastąpiła instrukcja wypisująca, 3. Jaka była data kompilacji, 4. Jaka była godzina minuta, itd. tej kompilacji, 5. Czy użyto kompilatora C++.
219
Rozdział. 7. Tablice
Tablice
7
eśli masz do czynienia z grupą zmiennych (ogólniej mówiąc - obiektów), to możesz z nich zrobić tablicę. Tablica to ciąg obiektów tego samego typu,
I
>re zajmują ciągły obszar w pamięci. Korzyść z tego jest taka, że zamiast nazywania każdej ze zmiennych osobno,
wystarczy powiedzieć: odnoszę się do n-tego elementu tablicy. Tablice są typem pochodnym. Znaczy to po prostu, że bierze się jakiś typ dajmy na to i n t - i z elementów takiego typu buduje się tablicę. Jest to wtedy tablica elementów typu i n t .
Jeśli chcemy mieć 20 zmiennych typu i n t , to można z nich zbudować tablicę in t a [20]; Ta definicja rezerwuje w pamięci miejsce dla 20 liczb typu i n t . Uwaga: obiekt a nie jest typu " i n t " . Jest on obiektem typu: "tablica 20 elementów typu in t Rozmiar tak definiowanej tablicy musi być stałą całkowitą (większą od zera) - i to znaną już w trakcie kompilacji. Kompilator bowiem musi już wtedy wiedzieć ile miejsca ma zarezerwować na daną tablicę. Rozmiar ten nie może być więc ustalony dopiero w trakcie pracy programu. cout « "Jaki ch cesz rozmiar t a b lic y ? in t rrr; c in » rrr; in t a [ r r r] ;
//Błąd!!!
Jest to błąd, bo wartość r r r nie jest jeszcze w czasie kompilacji znana. Znana jest dopiero w trakcie pracy programu. Dla wtajemniczonych:
220
Rozdz. 7. Tablice Elementy tablicy
]eśli rzeczywiście zachodzi konieczność zdefiniowania takiej tablicy o niez nanym jeszcze rozmiarze - powinieneś pomyśleć o tzw. dynamicznej alokacji tablicy - str. 298. Inne rozwiązanie, bardziej zaawansowane, to użycie bibliotecznej klasy pojemnikowej v e c to r . A o to in n e p rzy k ład y - będ ą to definicje różnych tablic. D efinicje - czyli miejsca ich n aro d zin w program ie. Definicja jest, jak w iad o m o , tak że d ek laracją, czyli poin fo rm o w an iem kom pilatora o typie d an eg o obiektu. S tąd d la p o niższych definicji podaję w k o m en tarzu jak czyta się deklarację takiej tablicy: // z d a n ie jest tablicą
c h a r z d a n ie [8 0 ] ;
H 8 0 e lem e n tó w ty p u
char
// numer jest tablicą // 9 elementów typu f l o a t
f 1 o a t numer [ 9 ] ;
unsigned long kanał [8192];
//kanał je s t tablicą 8 1 9 2
II elementów typu u n s ig n e d lo n g II ws ka z jest tablicą 20 elementów
i n t *ws ka z [ 2 0 ] ;
//będących w s k a ź n ik a m i (a d re sa m i)
II jakichś obiektów typu i n t O ad resach jeszcze dokładniej nie m ów iliśm y, ale ten o statn i p rz y k ła d przy to czy łem po to, by p o k azać z jak różnych ty p ó w m o żn a z b u d o w a ć tablice. Tablice m ożna tw orzyć z: •
ty p ó w fundam entalnych (z w yjątkiem v o id ) ,
•
ty p ó w w yliczeniow ych
•
w skaźników ,
•
innych tablic;
(enum),
a ta k ż e (o czym d o w iesz się w przyszłości):
_____
7.1
.
•
z ob iek tó w ty p u zdefiniow anego p rzez u ż y tk o w n ik a (czyli
•
klasy), ze w sk aźn ik ó w d o p o kazyw ania na sk ład n ik i klasy.
.
Ifllli<"'‘1111“Mi .IIIMIIIIHIlillTI |T~H flU niM iaM U ltT 1
“T
■
^ ..fr O ^ W * * * * * ^ * ^
E l e m e n t y tablicy N a p o czątk u zajm iem y fu n d am en taln y ch .
się prostym i
tablicam i
tw o rzo n y m i z
typów
Jeśli zdefiniujem y sobie taką tablicę: int t [4];
to jest to tablica czterech elem entów typu i n t . Poszczególne elem enty tej tablicy to: t [0]
Jak w id zisz,
t [1]
t [2]
t [3]
221
Rozdział. 7. Tablice Elementy tablicy
Numeracja elementów tablicy zaczyna się od zera. Jest to bardzo ważne i to trzeba za pam iętać.
W p o czą tk o w y m o k re sie będ ziesz się z a p e w n e często m ylił. T y m bardziej, że w n iek tó ry ch ję zy k ach p ro g ram o w an ia n u m eracja e lem en tó w tablicy zaczyna się od 1. Jeśli m a sz ta k ie p rzy zw y czajen ie, to b ęd zie Ci tru d n iej. P ra w d ę m ó w iąc, n ie m a specjalnej tra g e d ii jeśli z a p o m n isz coś w p isać do elem en tu zero w eg o . T w oja strata. G orzej jeśli zap o m n isz, ż e o sta tn im , czw ar ty m elem en tem n a sz e j tablicy jest e le m e n t t [ 3 ] , a nie e lem en t t [ 4 ] . N u m e ru je m y przecież o d zera ! E lem ent t [ 4 ] n ie istnieje. Próba w p is a n ia czegoś do elem en tu t [ 4 ] n ie b ę d z ie sy g n alizo w a n a jako błąd, g d y ż w języ k ach C oraz C + + nie jest to s p ra w d z a n e . W p isan ie czeg o ś d o nieistniejącego ele m e n tu t [ 4 ] s p o w o d u je zn iszczen ie w o b sz a rz e p am ięci cze g o ś, co n astęp u je b e z p o śre d n io za tablicą. C o d o k ład n ie je st niszczo n e —z a le ż y to od im plem entacji. D la zo b raz o w an ia p o w ie m tylko, że z d a rz a się, iż p rz y ta k ic h definicjach: i n t t [4] ; in t z e t; P o d czas p ró b y z a p is u czegoś d o n ieistn iejąceg o elem entu t [ 4 ] t [4 ] = 15; zn iszc zo n a z o sta n ie treść zm iennej z e t , bo a k u ra t zo stała u m ieszczo n a w p am ięci b e z p o ś re d n io za tablicą t . P o n iew aż typ zm ien n ej z e t z g ad z a się a k u ra t z ty p e m e le m e n tó w tablicy t , d la te g o m ożliw e jest też, że w zm iennej z e t z n ajd zie się liczb a 15. P o w ta rz a m jednak: to, co p o w ied ziałe m , z ależn e jest o d im ple m entacji. W ażn y jest tutaj ty lk o pew nik: coś m im o w o ln ie w p am ię ci zn iszczy liśm y . Należy zatem pam iętać, że tablica A/-elem entow a ma elem enty o indeksach od 0 do A M
(a n ie do N).
T ablicę m o ż n a z a p e łn ić treścią - na p rz y k ła d za pom ocą zw y k łej operacji p rz y p isa n ia . # in c lu d e < io s tr e a m > u s in g n a m e sp a c e s t d ; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ******* i n t m a in ()
1)
Nie traktuj tego jako wadę. Jeśli Ci to nie odpowiada, to w przyszłości będziesz potrafił to zmienić —korzystając z nowego rodzaju tablic — takich, które to sprawdzają. Ale zapłacisz za to czasem dostępu do elementu takiej tablicy. Tablica, która tego nie spraw dza - jest szybsza.
222
Rozdz. 7. Tablice Elementy tablicy int t [4 ]; for(int i = 0 ; i < 4 ; i++) t[i] = 100 * i; cout << "Wydruk treści tablicy: for(int i = 0 ; i < 4 ; i++)
/ I w p is
\n";
{ cout << "Element nr: " << i << " ma wartość " « t [i ] << endl;
} }
W rezultacie wykonania tego programu na ekranie pojawi się Wydruk treści Element nr: 0 Element nr: 1 Element nr: 2 Element nr: 3
tablicy: ma wartość ma wartość ma wartość ma wartość
0 100 200 300
Z au w aż , iż zakres obu pętli for jest taki, że i przebiega od 0 d o 3. T o w łaśnie z p o w o d ó w , o których w spom inaliśm y. Jeśli jesteś ciekaw , to sp ró b u j w drugiej p ętli for - tej od w y p isy w a n ia elem en tó w na ekran - zm ien ić i<4 na i< = 4 . S p o w o d u je to w y pisanie n a ek ran nieis tniejącego elem entu o indeksie t [ 4 ] . P o b ran ie w artości z tego o b s z a ru pam ięci b ezp o śred n io za tablicą nie spow oduje k atastro fy , jedynie w a rto ść będzie bezsensow na. T rag ed ia byłaby dopiero w te d y , g d y b y śm y chcieli coś w to m iejsce wpisać.
Uwaga praktyczna: Tu chciałbym Ci coś p o rad zić. Zw ykle elem en ty tablicy czyta się lu b w y p isu je za po m o cą pętli, na p rz y k ła d pętli f o r . W n aszy m p rz y p a d k u za p isa liśm y to tak: f o r (i = 0 ; i < 4 ; i ++) czyli inaczej f o r (i = 0 ; i
< r o z m ia r
; i++)
M ogliśm y rów nie d o b rz e n ap isać tak: fo rd
= 0 ; i <= 3 ; i ++)
czyli inaczej for(i= 0 ; i <= r o z m i a r - 1
;
i++)
P o d sta w o w a różnica je st tu, jak w idać, w u ży c iu m ocnej lu b słabej nierów ności. Je d n a k oba zapisy sp raw iają , że pracujem y na elem en tach o in d e k sac h 0-3, czyli o b a zap isy są ró w n o w ażn e.
223
Rozdział. 7. Tablice Inicjalizacja tablic
iSSOftaSHMMąMMlIMMNMNMMNMMNMMMMMMMMNMmsnMI
O tó ż ra d z ę C i b y ś z d e c y d o w a ł s ię na je d e n ty p z a p is u i n ig d y n ie u ż yw a ł d ru g ie g o . U n ik n ie s z w te d y o m y łk o w e g o a d re s o w a n ia e le m e n tu t [rozm iar] (któ ry, ja k w ie m y , n ie is tn ie je ). H11— i iiiw i 11 ' r t a t—ii
:.. '
O sobiście p referuję ten pierw szy zap is (z silną nierów nością), bo nie m uszę odejm ow ać jed y n k i o d rozm iaru, a po za tym zap is jest krótszy.
\2
In ic ja liz a c ja ta b lic Inny m sposobem n ad a n ia w artości elem en to m tablicy jest inicjalizacja n ad an ie w artości początkow ych w m om encie definicji tablicy (czyli w m om en cie jej narodzin). P am iętasz zap e w n e, jak robiliśm y inicjalizacje w p rzy p ad k u zw y k ły ch typów: int float
liczba = 237; współczynnik = 0.372;
W p rzy p ad k u tablicy trzeba nadać w artość początkow ą k ażd e m u elem entow i. Służy do tego tzw . inicjalizacja zbiorcza. Dla wtajemniczonych: Możliwa jest ona wtedy, gdy elementy tablic są tak zwanymi agregatami. Nie przejmuj się na razie tą nazwą. Wrócimy do tego na str. 724). W n aszy m p rz y p a d k u w ygląda to tak: int t [4] = { 17, 5, 4, 200 };
Jest to w y g o d n y sp o só b , bo w jednej linijce załatw ia inicjalizację w szystkich czterech elem entów . D zięki tem u t [0] t [1] t [2] t [3]
ma wartość ma wartość ma wartość ma wartość
17 5 4 200
D o zn u d ze n ia b ęd ę przypom inał, że elem en t t [4 ] , p o d o b n ie jak i elem ent t [ 107 ] - nie istnieje. G dybyś jednak w tym m om encie zbiorczej inicjalizacji na liście ujętej k lam ram i { } umieścił o jedną —lub kilka liczb - za d u żo , to tutaj k om p ilato r zasy g n alizu je Ci błąd. W inicjalizaq'i sp raw d za się, czy rozm iar tablicy nie jest p rz y p a d k ie m przekroczony. Tylko przy inicjalizacji. Potem nie. M ożliw a jest też tak a inicjalizacja naszej tablicy: int t [4] = { 17, 5 };
A
Jak w idać liczb jest tu za mało. Inicjalizacja taka spow oduje, że ż ą d a n e w artości początkow e zos taną n a d a n e tylko tym d w ó m p ierw szym elem entom . Elem entom t [ 0 ] i L [ l ] . Pozostałe dwa elem enty będą inicjalizow ane zeram i. Dla w y g o d y istnieje też taki sposób definiow ania i inicjalizacji tablic:
224
Rozdz. 7. Tablice Przekazywanie tablicy do funkcji i n t r [] = { 2 , 10, 15, 16, 3 ) ; Z auw aż, że tu taj w kw adratow ym naw iasie nie p o d aliśm y ro zm iaru ta b lic y K om pilator w ięc sa m sobie przelicza ile to liczb podaliśm y w k lam rze i w efekci < rezerw ow ana jest pam ięć na te elem enty. W naszym p rz y p a d k u p o w s ta n u tablica pięcioelem entow a.
Inicjalizacja tablicy obiektów stałych| Jeśli m am y zd efin io w ać tablicę obiektów stałych, to inicjalizacja zbiorcza jesl jedyną szansą u m ieszczenia w niej w artości. c o n s t i n t d n i w m i e s ią c a c h [12]= { 31, 287 1 1 , 30, 31, 30, 31,
7.3
31, 30, 31, 3 0 , 31};
P rze k a z y w a n ie ta b lic y do fu n k c ji Z ałóżm y, że m a m y tablicę z danym i p om iarow ym i. P róbek p o m iaro w y ch jesl d u żo , bo aż 8192 lo n g i n t w idm o[ 8192]; C hcem y teraz n ap isać sobie funkcję, k tó ra treść każdego elem en tu tej tablicj pom n o ży p rzez 3. Jak to pom nożyć, oczyw iście w iesz - w y sta rc z y n ap isać pętL^ m nożącą p rzez 3 - po kolei w szystkie elem enty tej tablicy o d 0 d o 8191. To jes proste. Jak jed n ak p rzesłać do funkcji tablicę? C zy pam iętasz, jak m ów iliśm y o p rzesy łan iu arg u m en tó w d o funkcji? Z w ykle przesyła się p rz e z w artość, czyli fo tografow any jest k a ż d y a rg u m e n t i jegc zdjęcie (kopia) p rzesy ła n a jest d o funkcji. Tablicy jed n ak nie d a się przesła< p rzez w artość. M o żn a tak przesłać p o jedyncze jej elem enty, ale m e całosc. To nie w yn ik a z n iedoskonałości języka. Po p ro stu - czy w y o b ra ż a sz sobie funkcję, która w w y w o łan iu dostaje 8192 arg u m e n ty ? To tak, jak b y fo to g rafo w ać 8192 razy. Sam o p o jed y n cze w yw ołanie takiej funkcji trw ało b y p ew ien znaczący czas, nie m ów iąc ju ż o trudnościach w realizacji takiego p rz e sła n ia . Z atem zasad a je st taka, że:
| tablice przesyła się podając funkcji tylko ad res p o czą tk u tej tablicy, j Jeśli m am y funkcję v o id f u n k c ja
(flo a t t t t [ ]
);
k tóra sp o d ziew a się jako argum entu: tablicy liczb typu f l o a t , to taką funkcja w yw ołujem y na p rz y k ła d tak: f l o a t t a b l i c a [] = { 7, 8.1, 4, 4.12 ) ; funkcja (ta b lic a ) ;
/ / ^-wywołanie funkcji
P rzy n azw ie tab licy w w yw ołaniu funkcji nie w id zisz ż a d n y c h nawiasów k w ad rato w y ch .
225
Rozdział. 7. Tablice Przekazywanie tablicy do funkcji
Z ap a m ię taj so b ie n a z a w s z e (a najlepiej n a p is z sobie to na k artc e i p rzy le p nad b iu rk iem ), że: W języ k u C++ (ta k jak i w C):
NAZWA TABLICY jest równocześnie ADRESEM ZEROWEGO JEJ ELEMENTU N ie żartu ję. R zeczy w iście w ykuj to z d a n ie n a pam ięć, bo b a rd z o C i to pom oże w sw o b o d n y m p o ru s z a n iu się po kró lestw ie C ++. Jest jeszcze coś ró w n ie sy m p aty czn eg o . M ianow icie w y rażen ie: tablica + 3
je st a d re se m tego m iejsca w pam ięci, g d z ie tk w i elem ent o in d e k sie 3. Elem ent o in d e k sie 3 to in aczej e lem en t tablica[3]
a d re s tak ieg o e lem en tu to Stablica[3]
Z n a k &(a m p e rsa n d ) jest je d n o a rg u m e n to w y m o p erato rem o zn aczający m uzys kiwanie adresu danego obiektu. U w ag a: nie m ylm y tego o p e ra to ra z d w u a rg u m e n to w y m operato rem & oznaczającym bitowy iloczyn logiczny.
A z a te m p o n iż sze d w a z ap isy (w yrażenia) są ró w n o w aż n e tablica + 3
&tablica[3]
M o żn a je sto so w ać w y m ien n ie, co k to lubi. W ró ćm y jed n ak d o rzeczy. W n aszy m w y w o łan iu funkcji n ap isaliśm y sam ą n a z w ę tablicy (bez klam er), w ięc d o funkcji p rzesy łam y ad res. O to przy k ład p ro g ra m u , w k tó ry m d o funkcji - jako je d en z arg u m e n tó w - w y sy łan a jest tablica. #include using namespace std; void potrojenie(int ile, long t []);
//O
int main() const int rozmiar = 8192; long widmo[rozmiar];
// @ //
j j ----- n a d a n ie w a r to ś c i p o c zą tk o w e j
for(int i = 0 ; i < rozmiar ; i ++)
1 widmo[i] = i; , i f (i < 6) //p o k a za n ie p ie r w s z y c h s z e ś c iu cout « "widmo[" « i « "]= " << widmo[i] «
}
// O endl;
226
Rozdz. 7. Tablice Przekazywanie tablicy do funkcji
U -------- uwaga, wywołujemy funkcję !
/ / ©•
potrojenie (rozmiar, widmo) ; (( cout « "Po wywołaniu funkcji \n' for(int i = 0 ; i < 4 ; i ++) cout «
"widmo[" «
i «
"]- " «
// widmo [i] «
O
endl;
} }
/****************** ********★ **********************************/ / / €► void potrojenie (int ile, long t [) ) for(int i = 0 ; i < ile ; i++) { t[i] *= 3;
/ / €►
J
t************** *********+***********************************/’
W wyniku wykonania tego programu na ekranie pojawi się widmo[0]= 0 widmo[1]= 1
widmo[2]“ 2 widmo[3]= 3 widmo(41= 4 widmo[5]= 5 Po wywołaniu funkcji widmo[0]= 0 widmo[11 = 3 widmo[2]= 6 widmo[3]= 9
O Deklaracja funkcji, do której, jako argument, wysyła się tablicę. Zauważ, jak deklaruje się argument formalny. Jest to jakby definicja tablicy typu long o nieznanym rozmiarze. W związku z tym funkcja ta będzie się nadawała do pracy na dowolnej tablicy typu long, której elementy zamierza się potroić. Wewnątrz tej funkcji potrzebna jest nam także liczba elem entów tablicy, któ rych wartość liczbowa ma ulec potrojeniu. Dlatego w ysyłam y sobie tę wartość jako argument. M usimy to zrobić, gdyż - w tej funkcji - na podstawie nazwy tablicy (czyli adresu) nie da się określić ile dana tablica ma elementów.
0 Rozmiar tablicy
widmo definiujemy sobie w programie jako obiekt typu int a przydomkiem const - rozmiaru tej tablicy nie będziemy przecież w trakcia programu zmieniać. Dlaczego słow o const jest tu konieczne - okaże się za
chwilę. © Oto definicja tablicy. Jej rozmiar jest określony przez liczbę schowaną w powyż szym obiekcie typu int z przydomkiem const. To c o n s t sprawia, że kompi lator jest pew ien stałości tej liczby już na etapie kompilacji. Jak pamiętamy, rozmiar tak definiowanej tablicy musi być znany już na etapie kompilacj programu.
227
Rozdział. 7. Tablice Przekazywanie tablicy do funkcji
O Po definicji tablicy n a stę p u je pętla nadająca jej elem entom w arto ści początkow e ró w n e kolejnym liczb o m n atu raln y m . P ie rw sz e 6 elem en tó w tablicy w ypisuje m y na ekran. © W y w o łan ie funkcji. Jak o arg u m e n ty w y sy ła m y liczbę elem en tó w , których za w a rto ść należy p o tro ić, o ra z nazw ę tablicy, n a której ta o p eracja potrojenia ma się odbyć. P rz y p o m in a m , że jeśli w y sy ła m y d o funkcji ad res tablicy, to tak, jak b y śm y w y sy łali a d re s zerow ego jej elem en tu . © Po po w ro cie z funkcji w y p isu jem y w artości ty ch elem entów na ek ran ie.
Co się dzieje wewnątrz funkcji: © O d b ieram y tam a d re s p o czątk u tablicy i s łu ż y on do z b u d o w a n ia w ew n ątrz funkcji takiego a p a ra tu obsługi tablicy t [ ] , ż e o d n iesienie się d o elem entu 1 t [ 3 ] jest d o k ła d n ie ty m sam ym , co o d n ie sien ie się do elem en tu t a b l i c a [ 3 ] . In n y m i słow y, nie p racu jem y tu na żadnej k o p ii tablicy, tylko n a oryginale. Masz rację jeśli myślisz, że mętnie to tłumaczę. Niestety na razie nie mogę powiedzieć całej prawdy. Wszystko stanie się jasne w następnym rozdziale, kiedy to porozmawiamy o wskaźnikach. © W naszej funkcji p o t r o j e n i e p rzeb ieg am y po elem entach tablicy t [ ] z ż ą d a n e g o p rzed z iału 0 - 8191 i m nożym y k a ż d y elem ent p rzez 3. P rzy p o m in am że instrukcja: t[i]
*= 3;
to inaczej to sam o, co: t[i]
= t [i ] * 3;
Do funkcji p rzy słaliśm y a rg u m e n t określający liczbę elem en tó w tablicy, na których należy p rz e p ro w a d z ić operację potrojenia. M usieliśm y to zro b ić dlate go, że jedyne, co w e w n ą trz funkcji w iadom o n a tem at przysłanej tablicy, to to: •
jaki je st a d re s jej początku,
•
i to, iż je st ona typu lo n g .
N ic więcej. W szczególności nie w iadom o, jak i m a rozm iar ta tablica.
W Wysyłanie tylko jednego elementu D o funkcji, której a rg u m e n te m form alnym jest ty p i n t v o id f f f ( i n t x ) ; m o żn a w ysłać a rg u m e n t będący jakimś elem en tem tablicy typu i n t . N ie jest to p rzesłan ie tablicy. O d b y w a się to identycznie, jak w p rzy p ad k u zw ykłej zm ien nej i n t . i n t m; i n t ta b l[1 0 0 ];
228
Rozdz. 7. Tablice Przykład z tablicą elementów typu enum fff (m) . ff f (tabl [38 J);
II w y s ła n ie do fu n k c ji zw y k łe j z m ie n n e j II w y s ła n ie do fu n k c ji e le m e n tu n r 3 8
Takie w y słan ie jednego elem entu odbyw a się oczyw iście p rz e z w artość. Łatwo to zapam iętać tak: Otóż w artością w yrażenia tabl [3 8 ] jest liczba (z ap isa n a w ty m elem enc ii tablicy). N ato m iast wartością w y rażen ia tabl jest adres tej tablicy. To d la te g < m echanizm przesłania jest inny. Inaczej m o ż n a to w ytłum aczyć tak: * Jak w iem y - w yrażenie t a b l jest typu: tablica 100 elementów typu i n t r . ♦♦♦ N ato m iast t a b l [ 38 ] jest jednym z elem entów tej tablicy, a skoro jest t:c tablica elem entów typu i n t , to ten element jest typu i n t (po p r o s tu ! ) M ożna go więc wysłać d o każdej funkcji oczekującej arg u m en tu typ>u in t.
7.4
Przykład z tab lic ą e le m e n tó w typu enum Z obaczym y tu program , w którym w ykorzystana b ę d z ie tablica e le m e n tó w typu o n azw ie e t a p y , który to ty p jest typem w y liczen io w y m (enum). Będzie to jakby program nap isan y z p u n k tu w id zen ia k ie ro w n ik a p ro d u k c ji filmu reklam ow ego. Kierownik taki, jak w iadom o, nie jest a rty stą, w ięc na s c e n y filmu p a trz y tylko pod kątem tego, co już zostało z ro b io n e i ile jeszcze trz e b a w łożyć pracy. #include using namespace std;
// O enum etapy < nic, scenopis, probaczytana, proba_kamerowa, nagranie - o, montaż, postsynchron }; . 4 , (praDv tfl. int ile, int dzień zdgeciowy); int m a i n ()
(
const int liczba_scen = 10; g etapy scena[liczba scen] = '' ” { scenopis, proba_czytana, proba_kamerowa }, int dzien_realizacji = 1;
• scena[2] = nagranie; scena[5] = montaż; scena[9] = nagranie; scena [41 = próba kamerowa; .. stan realizacji (scena, liczba_scen, dzien_realizacji), H ________ n a stę p n e g o d n ia praca p o su n ę ła się dalej -
dzien_realizacj i++; scena [1 ] = proba^kaiuerowa/ scena[5] = postsynchron;
// €w3! , //vJ
229
Rozdział. 7. Tablice Przykład z tablicą elementów typu enum
un era jia i
^ ★
s c e n a [0] = scenopis; s c e n a [8] = scenopis; stan_realizacji(scena, 4 4 4 4 4 4 4 4 + * * 4 * 4 *
liczba_scen,
dzien_realizacji);
+ 4 4 T i r 4 4 4 * 4 4 * * 4 * 4 4 4 4 4 4 * 4 * * 1l r * * * * * * * . * . * . * . . * . * . * . * * * . * . * *
void stan_realizacji(etapy s[], int ile,
int dzień )
//
{ cout << dzień « dzień realizacji filmu reklamowego\n" « "Stan realizacji: \n"; for(int i = 0 ; i < ile ; i++)
{ cout << "Scena nr " << i « ", for (int k = 0 ; k < s (i ] ; k++)
";
// ©
cout << "#"; cout << endl;
}■ } ^*********** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4 * * * * * * + * * * * * * * * * * * / 3
Po wykonaniu tego programu na ekranie zobaczymy tekst 1. dzień realizacji filmu reklamowego Stan realizacji: Scena nr 0, # Scena nr 1 , ## Scena nr 2, ###### Scena nr 3, Scena nr 4 , ### Scena nr 5, ####### Scena nr 6, Scena nr 7, Scena nr 8, Scena nr 9 , ###### 2. dzień realizacji filmu reklamowego Stan realizacji: Scena nr 0, I Scena nr 1, ### Scena nr 2, ###### Scena nr 3, Scena nr 4, *## Scena nr 5, *####### Scena nr 6, Scena nr 7, Scena nr 8, Scena nr 9, *#####
£
Krótki komentarz O Definicja typu w y liczen io w eg o (enum) o n a z w ie e t a p y . Służył o n n am będzie d o ok reślan ia kolejnych eta p ó w realizacji film u. © Definicja tablicy takiego ty p u . W idzisz tu też inicjalizację zbiorczą kilku p ierw szych elem entów . R eszta w ięc będzie zap e łn io n a zeram i. W tym ty p ie w yliczę-
230
Rozdz. 7. Tablice Tablice znakowe niowym w artość zero odpow iada stanow i n i c , to z n a c z y nie m a n a w
j
scenopisu. © Praca z tablicą s c e n y . N adaw anie jej elementom jakichś w artości. O W yw ołanie funkcji s t a n r e a l i z a c j i. W ysyłamy tam tablicę s c e n y (a w łaś ciwie jej adres). Wysyłamy też w artość i n t określającą ile scen m a n asz tu m i który to d zień realizacji. © Oto ta funkcja. Zobacz, jak odbiera ona argum ent pierw szy , będący tablicą ele m entów ty p u e t a p y . W funkcji jest pętla przebiegająca po wszystkich scenach film u. j © Dla każdej sceny jest mniejsza pętla, która drukuje rz ą d e k zn acz ow v zależności o d zaaw ansow ania e ta p u . , Dalsze w yjaśnienia nie dotyczą już tablic, a raczej ty p ó w w y liczen io w y . , Zauważ, że licznik tej pętli k - jest porównywany z wartością liczbową danego etapu. Zatem wyrażenie k
<
s [ i]
porównuje liczbę typu i n t z typem wyliczeniowym (jego reprezentac,ą liczbową). Dany etap ma przecież jakąś tam reprezentację liczbową. ZIj my ją w definicji typu wyliczeniowego e ta p y . M ożna tak porów nyw ać, bo istnieje tak z w a n a konw ersja stan d ar dow a z typów w yliczeniow ych na typ i n t . (P o ro zm aw iam y o tym w przyszłości).
To (praw ie) w szystko, co trzeba w iedzieć na tem at tablic. M yślę, że nic w tym tru d n eg o , b o z tablicami zetk n ąłeś się w innych językach pro g ram o w an ia. W łaściw ie w ięc m ożna by ten ro zd ział tutaj skończyć, g d y b y m e p ew ien b ard zo pożyteczny rodzaj tablic: tablice znakow e.
7 .5
T a b lic e z n a k o w e Specjalnym rodzajem tablic są tablice d o p rzech o w y w an ia zn ak ó w (np. liter D latego też tablicom tym p rzy jrzy m y się bliżej. char zdanie[80];
V\ Ta definicja określa, że z d a n i e jest tablicą 80 elem en tó w b ędących znakam i, tablicy tej m o ż n a um ieścić tek st, dzięki tem u, że k a ż d y z jej elem entów na y się do p rzec h o w y w an ia reprezentacji liczbowej zn ak ó w alfanum erycznyc . ■ p rzy k ład - zn a k ó w zak o d o w an y ch k o d em ASCII. Kod tak i, jak zap ew n e wiesz jest je d n y m ze sposobów k o d o w an ia liter, które w k o m p u te rz e m uszą by< przecież p rzec h o w y w an e w postaci liczb. Jest kilka sp o so b ó w kodow ania i » m y je d n ak m ó w ić będziem y o k o d zie ASCII-
231
Rozdział. 7. Tablice Tablice znakowe
Teksty w tablicach zn ak o w y ch zw y k ło się p rzech o w y w ać tak, że po ciągu liter (a w łaściw ie ich k o d ó w liczbow ych) n astęp u je znak o k o d zie 0. Ten znak zw any jest czasem nuli. Z n a k ten jest po to, by oznaczyć g d zie k o ń czy się ciąg liter. Na taki ciąg liter z a k o ń c z o n y zn ak iem nuli m ó w im y string, lub C-string.
Żegnaj makrodefinicjo
n u ll
D aw niej w języku C ++ funkcjonow ała d y rek ty w a p rep ro ceso ra nadająca słow u NULL w arto ść 0 (zero). Z atem w p ro g ram ie m ożna było s p ra w d z a ć istnienie w d an y m elem encie tablicy znaku nuli - u ży w ając w łaśn ie sło w a NULL. N ow y sta n d a rd tego ju ż n ie p rzew iduje. W iele ko m p ilato ró w n ad al ro zu m ie to słowo, ale m oże się z d arz y ć, ż e Twój k o m p ilato r p o prostu o d p o w ie Ci, ż e nie wie, co to jest NULL. D lateg o lepiej w now ych p ro g ram ach sto so w ać zam iast niego 0 (zero), bo taki jest w łaśn ie kod ASCII zn a k u nuli.
Inicjalizacja tablicy znaków N aszą tablicę z d a n i e m ożna już w trakcie definicji zainicjalizow ać c h a r z a d n i e [80] = { "lot" ); Z auw aż, że o d b y w a się to przez n ap isan ie tekstu ujętego w cudzysłów . W rezultacie w p oszczególnych ko m ó rk ach tablicy zdanie z n a jd ą się n astęp u jące znaki
11
.
i;1{jfejSUJS
Rys. 7-1
t
n u li
Jak w idzisz, w p isa n e zostały trzy litery, a za nim i znak o k o d zie nuli kończący C-string. D alsze elem en ty tablicy nas nie interesują. W arto jed n ak pam iętać, że w m yśl o m ó w io n y ch nied aw n o zasad inicjalizacji zbiorczej tab lic - nie w ym ie nione w n aw iasach { } elem enty - inicjalizuje się zeram i, a ż do końca tablicy. Z n ak nuli został au to m aty czn ie d o p isan y p o ostatniej literze t d zięk i tem u, że inicjalizow aliśm y tablicę ciągiem zn ak ó w o g ran i czo n y m cudzysłow em . Jest to po naszej m yśli, bo w językach C i C++ w szelkie funkcje biblioteczne pracujące na C -strin g ach opierają się na założeniu, że ko n iec C -stringu oznaczony jest z n a k ie m nuli (bajtem zerow ym ). N a przykład: Jeśli chcemy jakiś C-string wypisać na ekran, to wywołujemy standardową funkcję biblioteczną (p u t s - o d : put string) i jako argument przekazujemy jej adres początku C-stringu. Funkcja ta będzie sukcesywnie wypisywała na ekran znaki zawarte w kolejnych komórkach pamięci począwszy od adresu podanego jako początek C-stringu. Akcja zakończy się dopiero po natknięciu się na komórkę ze znakiem nuli, czyli inaczej mówiąc z zapisanym tam bajtem 0.
232
Rozdz. 7. Tablice Tablice znakowe Jest też in n y sposób inicjalizacji tablicy znaków char zdanie[801 * 1 'i'*
'o*,
't' 1;
Z a u w a ż , że w klam rze p o jaw iły się pojedyncze lite ry o g ran iczo n e a p o s tro fa m i. Z apis ta k i jest nie tylko ró w n o w a ż n y trzem instrukcjom : zdanie[OJ = '1 *; zdanie[1] = 'o'; zdanie[2] = ’t';
P o n iew aż nie było tu c u d zy sło w u , w ięc k o m p ilato r nie d o k o ń czy ł te g o z n a k ie m nuli u m ieszczan y m p o p rz e d n io w elem encie z d a n i e [ 3 ]. Z atem s p ra w a w y daje się ry z y k o w n a - po ciąg u liter nie nastąpił z n a k kończący C -strin g . A je d n a k p rzy p o m n ij sobie, co m ów iłem o inicjalizacji zbiorczej - cz y li takiej, g d zie w arto ści p o czątk o w e u m ieszczo n e są w k la m rz e { } . O tó ż jeśli w a rto śc i p o czą tk o w y ch jest m niej niż elem entów tablicy, to reszta jest in icjalizo w an a zeram i. C zyli p ozostałe elem en ty tablicy z d a n i e , a ż d o elem en tu o in d e k sie 79, zo stan ą inicjalizow ane zera m i. W szczególności w elem encie z d a n i e [3 ] te ż zn a jd z ie się zero . A teraz u w ażaj: z n a k nuli ma p rz e c ie ż też w arto ść 0 - z a te m n ie m u sim y się m artw ić, C -strin g w tablicy z d a n i e je st p o p ra w n ie z a k o ń c z o n y .
Pułapka N ie z a w s z e je d n ak życie jest tak piękne. O to chcieliśm y być sp ry tn ie jsi i n a p isa liśm y ta k ą definicję: c h a r z d a n i e [] = ( ' 1 ' ,
'o ',
't '
1;
P a m ię ta m y b o w iem , że jeśli n ie p o d am y ro zm iaru tablicy, to k o m p ila to r p rzeli czy so b ie liczbę in icjalizato ró w i tak ą zare zerw u je tablicę. W n a s z y m p rz y k ła d zie d o liczy się trzech elem en tó w . Z ostanie w ię c z a re z e rw o w a n e tablica 3 e lem en to w a . W p o szczeg ó ln y ch jej elem entach z n a jd ą się zn ak i ' 1 ' ' o ' ' t ', ale z n a k u nuli tam nie będzie. N ie było cu d z y sło w u , w ięc k o m p ila to r n ie sąd ził, że te z n a k i m ają się sk ład ać n a C -string. D o p isan ia re sz ty zera m i n ie b ęd zie, bo ża d n e j re sz ty nie m a. Tablica m a ty lk o trzy elem en ty . P o d k reślam je d n a k - n ie jest to b łą d . M ożliw e, że lite ry te nie m ają być u ż y w a n e jak ciąg z n a k ó w , (czyli strin g ), lecz p o p ro stu są to lu ź n e litery, d o in n y c h celów . T ra k to w a ć jako! C -strin g ich je d n ak nie m o żn a. Z w ró ć u w a g ę na tę deklarację: char zadnie[] = { "lot" 1;
T utaj k o m p ila to r,o b lic z a ją c ile elem en tó w m a z a re z e rw o w a ć n a tablicę, doliczy się trz e c h liter, ale z faktu, ż e są o n e ujęte w c u d z y s łó w w y w n io sk u je , że m ają o n e b y ć u ż y w a n e jak o C -strin g , w ięc z a re zerw u je je szc ze je d e n d o d a tk o w y ele m e n t n a z n a k nuli. Jeśli n ie w ie rz y sz , to zobacz: #include using namespace std;
j ^ ł* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4'*'*****
233
Rozdział. 7. Tablice Tablice znakowe int main()
{ char napisl[] = { "Nocny lot"}; char napis2 [ ] = { ’N \ ’o', ’c', ' ', '1', 'o', 't' };
'n',
'y\
cout << "rozmiar tablicy pierwszej: " << sizeof(napisl) << endl;
rozmiar tablicy pierwszej: 10 rozmiar tablicy drugiej: 9 C zęsto u ż y w a się pojęcia długość C -stringu - jest to ilość lite r należących do tego C -strin g u . P am iętajm y, że rozm iar C-stringu jest w ię k sz y o 1, czyli o znak nu li teraz p y ta n ie ko ntrolne. M am y takie d w ie definicje tablic. C o o nich sąd zisz czy są p o p raw n e? A
char char
tl[ 1 J = { "a" }; t2[1] = { ' a ' };
Pierw sza je st b łęd n a. Tablica jest tu jed n o elem en to w a, a ch ce m y w pisać d o niej strin g zło żo n y z litery ' a ' i zn ak u n u li N a to p o trzeb a d w ó c h elem entów tablicy. Z a te m błąd. W drugiej definicji inicjalizacja polega na w p isan iu d o tablicy ty lk o jednej litery, a w ięc jest p o p ra w n a .
P rzy g ląd aliśm y się p rzed chw ilą jak w p isu je się strin g i d o tablic. N iestety: w p o d an y sp o só b w p isy w ać m ożna do tablicy znakow ej te k st tylko w czasie inicjalizacji. P otem , p ró b a w pisania do niej czegoś tym sp o so b em
zdanie [80] = "nocny lot"; zdanie = "nocny lot";
//B ł ą d ! //także b ł ą d !
jest n iep o p raw n a.
Jak zatem wpisywać tekst do tablic już istniejących? S praw a jest prosta. Trzeba napisać sobie funkcję, która się ty m zajm ie i p o d an y C-string, litera po literze umieści w danej tablicy. Z adanie to je st tak częste, że w stan d ard o w ej bibliotece je st kilka funkcji realizujących to w różnych w ariantach. Przyjrzyjm y się takiej funkcji. Jako arg u m e n ty otrzym uje o n a d w ie tablice - tę, w której jest strin g źródłow y i tę, g d zie m a on później zostać skopiow any.
234
Rozdz. 7. Tablice Tablice znakowe O tym, że tablica może być argumentem funkcji - wiemy ju z z poprzednie go paragrafu. Naprawdę do funkcji przesyłany jest tylko adres począł tablicy, a nie wszystkie jej elementy (których mogły by być tysiące). Jak m a w yglądać sam p ro ces k o p io w an ia? Jest to b a rd z o p ro s te d zięk i naszej zapobiegliw ości. K opiuje się znak p o z n a k u elem enty z tab licy zrodlow e) o tablicy docelow ej ta k d łu g o , aż nie n ap o tk a się na zn ak n u li T en z n a k nuli, ta k ż e n ależy p rzek o p io w a ć - jeśli w ynik m a być p o p ra w n y m C -strin g iem . S próbujm y zatem to napisać. O czyw iście p o słu ży m y się pętlą. v o id s t r c p y ( c h a r c e l [ ] , c h a r z r o d l o []) { . fo r(in t i = 0 ; ; i++) ( c e l[ ii = z ro d lo ji]; i f ( c e l [ i ) == 0 ) b re a k ;
//
O
//
0
// //
O Jest to, jak czy tam y , fu n k cja przyjm ująca jak o a rg u m e n ty d w ie tablice elem en tów ty p u c h a r - czy li d w ie tablice z n a k o w e . T ypem z w ra c a n y m jest ty p v o i c . U czciw ie p rz y z n a m , że m ogłem w y m y ślić to lepiej, ale na razie n ieć ę zie ja jest. © D o przeb ieg n ięcia p o p oszczególnych elem en tach tablicy słu ż y n a m pętla r o r . Pętlą ta zaczy n a się o d i = 0, bo, jak w ia d o m o , n u m eracja e le m e n tó w tablic za c z y n a się od 0. D alej w instrukcji f o r w id z im y p u ste m iejsce - b o n ie w ie rn y ile elem en tó w tablicy b ęd ziem y p rz e p isy w a ć . P u ste m iejsce o z n a c z a aj, ze p ę tla jest n ieskończona. © T o je st w łaściw a akcja p rzep isa n ia p o szczeg ó ln eg o z n a k u z ta b licy z r o d l o do tablicy c e l O S p ra w d z e n ie czy w ła ś n ie p rz e p isa n y z n a k nie jest cza se m z n a k ie m n u li Znak nuli - jak pamiętamy - ma kod ASCII: zero. Jeśli tak , to pętlę p rz e ry w a się in stru k cją b r e a k . Jeśli nie, to w y k o n u jem y n a s tę p n y obieg p ętli. In stru k cją i+ + z w ię k s z a się z m ie n n a i , k tó rą u ż y w a m y o in d e k so w a n ia e le m e n tó w tablicy, p o czy m k o p iu jem y dalej.
Inne sposoby zrobienia tego samego A oto , jak tę sam ą fu n k cję m o żn a z re a liz o w a ć u ży w a ją c pętli d o - w h i l e : v o i d s t r c p y l c h a r c e l l l , c h a r z r o d l o (1) { i n t i = 0; do{ . . c e l [ i j = z ro d lo [ i ] ; // kopiowanie . . }whi l e {c e l [ i ++1 ! = 0) ; // sprawdzenie i przesunięcie
'
. . ,
j
W sk ró cie m o ż n a tre ść tej instrukcji w y p o w ie d z ie ć tak: D o tąd k o p iu j elem enty z ta b lic y d o tablicy, d o p ó k i (ang: while) - w ła śn ie s k o p io w a n y e lem en t ma ko A SC II ró żn y o d 0. (C zy li d o p ó k i jest ró ż n y o d nuli).
235
Rozdział. 7. Tablice Tablice znakowe
P rzy okazji w y k o n u je się p rzesu n ięcia in d e k su tablicy m e to d ą postinkrem enta q i. W y rażen ie p o ró w n u jące: cel[i]
!= 0
je st jeszcze z e sta rą w arto ścią i . B ezp o śred n io po tym n a stę p u je inkrem entacja, czyli zw ięk sze n ie in d e k su o 1 ( p o st-in k rem en ta cja). P am iętasz m oże, jak p rz y okazji o m a w ian ia o p erato ra = m ó w iłem , że w artością w y ra ż e n ia p rz y p is a n ia (p o d staw ien ia) je st w arto ść będąca p rz e d m io te m p rzy p isan ia. Inaczej m ó w ią c w yrażenie: ( i = 15) n ie tylko, ż e w y k o n u je p rzy p isan ie, ale jeszcze sam o, jako całość, m a w artość 15. P o do b n ie w y ra ż e n ie (c e l[i]
= z ro d lo [i])
m a w arto ść ró w n ą k o d o w i ASCII k o p io w an eg o w łaśn ie z n a k u . K orzystając z teg o m o żem y n ap isać naszą funkcję tak: v o id s t r c p y ( c h a r c e l [ ] , c h a r z r o d l o [ ] ) { i n t i = 0; w h ile ( c e l[ i] = z ro d lo (i]) i+ + ; } } P rzeczy tać to m o żn a tak: Jeśli w artość w y ra ż e n ia k o p io w an ia (c e l[i]
= z ro d lo [i])
je st ró żn a o d zera (czyli nuli) to in k rem en tu j indeks i . T eraz w id zisz, jakie to w y g o d n e, ż e zn ak nuli kończący string m a w artość w łaśnie 0 S prytn e, p raw d a? N ie m ów im y: •
m u sisz skoczyć p o gazetę i potem ro zw iąż w niej krzyżów kę,
tylko jeśli gazeta, k tó rą p rzy n io słeś ma jakiś ty tu ł in n y niż nuli, to m o żesz rozw iązać w niej krzyżów kę. C hcąc sp raw d zić, ja k a jest w artość w y rażen ia - k o m p u ter m u si to w yrażenie „obliczyć", czyli w n aszy m p rzy p ad k u w y k o n ać k o p io w an ie z n a k u z tablicy z r o d ł o d o tablicy c e l . Za jed n y m zam ach em robim y w ięc k ilk a rzeczy.
Dwie uwagi (tytułem przestrogi) P o pierw sze: D o w n ętrza instrukcji kopiow ania nie w staw iłem postin k rem en tacji indeksu i . T o dlatego, ż e w w y rażen iu c e l[i]
- z ro d lo [i+ + ];
236
Rozdz. 7. Tablice Tablice znakowe nie m iałbym p ew n o ści k ied y n ap raw d ę by n astąp iła ta p o s tin k re m e n ta c ja . Je>t ry zy k o , że na p rzy k ła d p o wyjęciu zn ak u z tablicy z r o d l o , a p rz e d w sa d z e n ie m go d o tablicy c e l . W tedy k o p io w an ie o d b y ło b y się n a w z ó r takiej instrukcji: c e l [4] = z r o d l o [ 3 ] ; Jak to jest? O tóż k o m p ila to r nie g w aran tu je kolejności o b lic z a n ia zm ien n ej d la te g o bezpieczniej inkrem entację zrobić w głęb i pętli w h i l e . D ru g a u w ag a: N iek tó re bardziej tro sk liw e kom pilatory, g d y zobaczą w y ra ż e n ie w h ile (c e l[ij
= z ro d lo [i]) .. .
czyli inaczej w h ile ( a = b ) . . . n ie do cen ią n aszeg o sp ry tu i pom yślą, ż e p ra w d o p o d o b n ie c h o d z i n am o po ró w n a n ie d w óch e lem en tó w . Czyli p o m y śli, ż e m ó w im y : w y k o n u j d a n ą pętlę d o p ó k i a równa się b. K o m p ilato r taki p rz y p u s z c z a , że p o m y liliśm y p o ró w n a n ie (==) z p rz y p isa n ie m (=), czy li że p rzez p o m y łk ę zam iast d w ó ch z n a k ó w == w s ta w iliś m y tylko ten je d en =. N ie sy g n alizu je b łę d u , jedynie o strz e g a nas. T rzeba je d n ak p rzy zn ać, że najczęściej taki k o m p ila to r m a rację. C h w y tu , k tó ry tutaj zasto so w aliśm y , nie sto su je się ta k często. Za to b a rd z o często zap o m in a się p rz y operacji p o ró w n a n ia o podwój-1 nym z n a k u rów ności. D latego lepiej niech k o m p ila to r n am p atrz y na ręce. M ó w iliśm y ju ż o ty m (§ 4.2.1, str. 103), że m o ż n a k o m p ilato r u s p o k o ić dając m u d o zro z u m ie n ia , iż ro b im y takie p rz y p isa n ie celow o. W y sta rc z y d o d a ć d o d a t k o w ą p a rę n aw iasó w . w h i l e ( (a = b ) ) . . .
W W ró ćm y je d n ak d o n a sz y c h baran ó w . T y m sp o so b e m n a p isa liśm y funkcję s t r o p y , k tóra m o ż e n a m się p rz y d a i b a rd z o często. P rz y k ła d o w o spójrz na ta k i fra g m e n t p ro g ra m u : c h a r s t a r t *[) =
i " t a k i s o b ie zw y k ły t e k s t "
};
c h a r m e ta [ 8 0 ] ; s trc p y (m e ta , s t a r t ) ; c o u t << m e ta ; F ra g m e n t ten w y k o n u je k o p io w a n ie strin g u z tablicy s t a r t d o tablicy m e ta N a s tę p n ie w y p isu je się na ek ra n now ą z a w a rto ś ć tej tablicy.
237
Rozdział. 7. Tablice Tablice znakowe
P rzyjrzy jm y się defin icji tablicy s t a r t . N ie m a w niej ro z m ia ru , zatem k o m p ila tor p rzeliczy so b ie w szy stk ie zn ak i w tekście o g ra n ic z o n y m cu d zy sło w em , n astęp n ie d o d a je d e n (na kończący z n a k nuli ) i tyle k o m ó re k pam ięci z a re z e r w uje na n aszą tablicę. Z tablicą m e ta je st inaczej. Tutaj nie inicjalizujem y jej, w ięc k o m p ila to r chce z n a ć ż ą d a n y ro zm iar. P o d ajem y m u np. 80, bo sp o d z ie w a m y się, ż e nasze tek sty ła d o w an e d o tej ta b lic y n ig d y nie b ę d ą d łu ż sz e niż 79 z n a k ó w .
Ostrożnie ! C o by było, g d y b y ś m y w definicji tab licy m e ta - z a m ia st [8 0 ] p o d ali ro zm iar [ 5 ] i w y k o n ali p ro g ra m ? P am iętasz z a p e w n e , że w naszej fu n k cji s t r e p y k o p io w a n ie o d b y w a się na oślep. P o szczeg ó ln e zn ak i będą b ra n e z tablicy s t a r t i w p isy w a n e d o kolej nych elem en tó w d o tablicy m e ta . T ak b ę d z ie aż d o k o ń ca ciągu zn ak ó w , czyli d o kończącego tre ść tablicy s t a r t z n a k u n u li Jeśli tablica m e ta jest za k ró tk a, b o rn a ty lk o 5 e le m e n tó w (ostatni to me t a [ 4 ] ) - to m im o w sz y stk o dalej b ęd zie o d b y w ało się w p is y w a n ie d o nieistniejących elem en tó w m e ta [ 5 ] , m e t a [ 6 ] ,
....
i tak dalej d o p ó k i C -strin g ze s t a r t u się nie skończy. W iesz o czyw iście, co takie w p isy w a n ie d o nieistniejących elem en tó w znaczy: M im o w o ln ie b ę d ą n iszczone k o m ó rk i pam ięci zn ajd u jące się z a ra z za n aszą tablicą m e t a. T ra g e d ia . Tym w iększa tra g e d ia , że nie m u sisz się zo rien to w ać od razu . Z ależn ie o d tego, co tam a k u ra t by ło - błąd m o ż e u ja w n ić się o w iele później. T y m tr u d n ie j jest to w ykryć. C zy nie m o żn a zab e zp iec zy ć się p rz e d tak im i ew en tu a ln o ściam i? O statecznie m o żn a, ale jest p ro b lem : otóż d o funkcji s t r e p y p rzesy ła m y ty lk o ad res tablicy. Funkcja z d e k laracji a rg u m e n tó w fo rm aln y ch w ie tylko, ż e m a d o czynienia z tablicam i z n a k o w y m i. N ie w ie nic w ięcej. W szczególności nie w ie jaki jest ro zm iar tablicy. N ie m o żn a w e w n ą trz funkcji s t r e p y w sta w ić sobie takiego w yrażen ia: s iz e o f(c e l) Jeśli zatem ch ce m y się p rzed p o m y łk am i zabezpieczyć, to m u sim y p rzysłać do funkcji a rg u m e n t b ęd ący ro zm iarem tablicy, albo - b ard ziej u n iw ersaln ie a rg u m e n t m ó w ią c y o tym , ile m a k sy m aln ie z n a k ó w ży czy m y sobie przek o p io w ać. N a p rz y k ła d ż y c z y m y sobie p rzek o p io w a ć tylko 5 z n ak ó w . Jeśli C -string p rz e z n aczo n y d o k o p io w a n ia będzie b a rd z o krótki, np. 3 literow y: " a c h " , to p rz e kopiuje się cały. Jeśli będ zie długi: " N i e s a m o w ic i e d ł u g i e z d a n i e " , to p rzek o p iu je się ty lk o początek " N i e s a " .N a końcu m u si się oczyw iście znaleźć bajt zero w y (nuli).
W
238
Rozdz. 7. Tablice Tablice znakowe M oglibyśmy się teraz rzucić do pisania i w ym yślić funkcję s t r n c p y . Litera n w środku nazw y m iałaby nam przypom inać, że chodzi po p rzek o p io w a n ie m a sym alnie n znaków , (czyli tylko kaw ałka C -stringu) bez d o d aw an ia n a końcu zn ak u nuli. M oglibyśm y tak zrobić, g d y b y nie to, że te w szy stk ie w sp a m a e funkcje daw no zostały napisane i zaw arte są w standardow ej bibliotece. A by z nich skorzystać w ystarczy włączyć do sw ojego program u plik n ag łó w k o w y z ich deklaracjami. Plik nagłówkow y tej części standardow ej biblioteki, k o ra od pow iada za pracę z C-stringami n azy w a się c s t r i n g . P otem , n a etap ie linkow ania dołączana jest (najczęściej autom atycznie) ta część biblioteki. tinclude using namespace std;
strcpy(komunikat, tekst); cout « komunikat « endl; strncpy(komunikat, "12345£789Qabcdef", 9 ) • cout « komunikat; strcpy(komunikat, "— A ku-ku — !" ); cout « ”\nNa koniec: " « komunikat << endl;
Wykonanie tego programu objawi się na ekranie jako: Uwaga, tarcza została przepalona ! 123456789rcza została przepalona ! Na koniec: — A ku-ku — !
.
O Z au w aż , że źró d łem w cale nie m usi być tablica definiow ana p rz e z nas. M oże byd to stała tekstow a (C -string) będący stałą d o sło w n ą. Taki C -strin g p rzecież ta ze m u si być p rzec h o w y w an y gdzieś w pam ięci kom putera.
D u żo m ów iliśm y tu n a tem at tablic zn ak o w y ch . N ie d lateg o , żeby były ona jakieś specjalnie tru d n e , lecz dlatego, ż e często się ich u ż y w a . N ie m a tu w z a sa d z ie żad n y ch c u d ó w - C -string to p o p ro stu znaki u sta w io n e jeden za d ru g im , a na ich k o ń cu jest nuli, czyli bajt zero w y . To w sz y stk o . Nazwa teg strin g u (n azw a tablicy, g d z ie o n tkw i) jest ró w n o cześn ie a d re se m tego miejsca \ p am ięci, g d zie C -strin g ten się zaczyna. T a m jest w ięc p o czątek tego C -stringu K oniec jest tam , g d z ie jest zn ak nuli.
239
Rozdział. 7. Tablice Tablice znakowe
Jeśli chcemy w ysłać C-string do funkcji, to w ysyłam y tam adres jego początku, czyli samą n azw ę jego tablicy znakowej (bez żadnych naw iasów kwadrato wych). Dzięki temu funkcja dowiaduje się, gdzie w pamięci zaczyna się ten string. G dzie się on kończy - funkcja może sprawdzić sobie sama szukając znaku n u li Do stringów jeszcze powrócimy w rozdziale o wskaźnikach.
W Cierpliwości, czas na standardową klasę string, jest bliski... Kilka razy w tym paragrafie ostrzegałem, byś robił tak, a nie inaczej, byś pamiętał, że... Nie okłamujmy się, nie w szyscy to zapamiętali. To nie tylko Twój problem, to problem w szystkich programistów, którzy pracują z tablicami znakowymi. Tymczasem z tekstami pracuje się w programie bardzo często i nie ma co ukrywać - nie jest to w C i Ć++ w ygodnie rozwiązane. O ile o tych wszystkich sztuczkach nie musim y m yśleć w przypad ku tablic liczb int, double - o tyle praca z C-stringami wymaga skupienia.
W zasadzie w ięc mógłbym o tablicach znakowych nic nie wspominać, ale jest kilka pow odów , dla których, mimo wszystko, będziem y o tym rozmawiać. ♦> 1) Tablice znakowe bardzo dobrze się przydają do nauczenia się popra wnej pracy z tablicami w ogóle. Łatwiej się rozwiązuje problem, gdy się myśli o tablicy c h a r , w której w poszczególnych elementach są litery ze zdania " S r e b r n e d a c h y K rakow a ", niż gdy się myśli o tablicy do u b l e, która zawiera w poszcze gólnych elementach liczby 4 3 . 7 , 1 2 . 8 , 1 6 . 4 , 8 1 . 5 ♦♦♦ 2) M imo że są lepsze, now sze sposoby - dotychczasowy sposób postępo wania z C-stringami (pochodzący jeszcze z języka C) jest nadal aktual ny, w ięc m ożesz się z tymi sposobami wielokrotnie spotkać w cudzych programach.
♦> 3)Klasa string, tok/asa-aoklasach jeszcze nie rozmawialiśmy. Zatem z wprowadzeniem tego zagadnienia wstrzymamy się do czasu, gdy jasne będą różne pojęcia z dziedziny klas. W tedy pożegnamy się z
240
Rozdz. 7. Tablice Tablice wielowymiarowe
niewygodnym sposobem trzymania łańcuchów znaków w tablicach znakowych.
Czyli na razie będziemy to robić tak, jak to się robiło jeszcze w klasycznym języku C.
7.6
Tablice wielowymiarowe Tablice można tworzyć z bardzo różnych typów obiektów. Widzieliśmy już tablice z liczb całkowitych, zmiennoprzecinkowych, tablice znaków. Mogą być także tablice, których elementami są inne tablice. Nazywamy je tablicami wielowymiarowymi. Oto przykład takiej tablicy:
int ddd[4][2]; Definicję tę, która jest przy okazji deklaracją, czytamy tak: ddd jest tablicą 4 elementów, z których każdy jest dwuelementową tablicą liczb typu int. Zauważ charakterystyczną notację. W innych językach często stosuje się zapis typu [i, j] - u nas jest [i][j]. Można myśleć o tym, jako o zwykłej odmienności notacji. Można też twierdzić, że zapis ddd [ i] [ j] pozwala lepiej pamiętać, że ddd to tablica tablic. Czasem ten punkt widzenia się przydaje. Wkrótce się przekonasz. E w en tu aln y b łę d n y zapis d d d [ i , j ] ko m p ilato r wykryje, b o w takiej definicji, w klam rach [ ] ko m p ilato r spodziew a się stałego w y rażen ia. Dla dociekliwych: W wyrażeniach stałych nie może być na przykład wywołania funkcji, albo przecinka. Przypominam jakie ma działanie operator'/ (przecinek): wartością ury ra żenia złożonego z kilku członów oddzielonych przecinkami jest wartość tego członu, który stoi najbardziej z prawej. Nasza tablica ddd [ 4 ] [2] jest dwuwymiarowa. Czasem o takiej tablicy mówi się, że ma 4 rzędy (4 wiersze) i 2 kolumny. P oszczególne elem en ty naszej tablicy to: ddd[ 0 ] [ 0 ] dd d [11 10] dd d [ 2 ] [ 0 ] dd d [ 3 ] [ 0 ]
ddd[0][1] ddd[13 [1] ddd[2][1] ddd[3J[1]
241
Rozdział. 7. Tablice Tablice wielowymiarowe
Wyrażenia te oznaczają wszystkie elementy tej tablicy*. Elementy tablicy wielowymiarowej um ieszczane są kolejno w pa mięci komputera tak, że najszybciej zmienia się najbardziej skrajny prawy indeks. Oznacza to po porostu, że tablica przechowywana jest "rzędami". Zatem poniższa inicjalizacja zbiorcza: int ddd[4][2]
= { 10, 20, 30,
40, 50, 60, 70,
80 };
spowoduje, ż e elementom tej tablicy zostaną przypisane wartości początkowe tak, jakbyśmy to robili grupą instrukcji: d d d [0][0] d d d [0][1] d d d [1] [0 ] d d d [1 ][ 1 ] d d d [2] [0 ] d d d [2] [1] d d d [3][0] d d d [3][1]
= = = = = = = =
10; 20;
30; 40; 50; 60; 70; 80;
Oto przykład programu: Załóżmy, że m am y nasze dane pomiarowe w postaci tablicy 8192 elementów. Takich pom iarów mamy cztery, dla czterech różnych próbek. Zapisane są te dane na dysku w postaci pliku. Uruchamiamy program i wczytujemy te dane do tablicy wielowymiarowej
long widmo[4][8192]; Jak to robimy, to na razie tajemnica, o szczegółach operacji z plikami porozma wiamy w osobnym rozdziale. Teraz tę akcję markujemy jedynie wywołaniem funkcji w c z y t a j _ d a n e . 0 co nam chodzi w tym programie? Załóżmy, że między elementem tablicy o indeksie 500, a elementem o indeksie 540 - zebrały się dane z interesującej nas reakcji. Chcemy w ięc zsumować wszystkie liczby zapisane od elementu o indeksie 500 do elementu o indeksie 540. Chcemy to zrobić dla każdego z czterech pom iarów
#include using namespace std; void wczytaj_dane();
Z *************************************************************/
int main() 1 2)
Jeśli w takich wyrażeniach pomylisz się i np. zam iast ddd [2] [ 1 ] napiszesz ddd [2,1] to kompilator uzna to za poprawny zapis, tak jakbyś napisał ddd [ 1 ] . (Takie jest przedeż działanie przecinka). Sam zapis ddd [ 1 ] nie jest błędny, ale często kontekst wystąpienia takiego wyrażenia będzie błędny. To właśnie może wykryć kompilator.
242
Rozdz. 7. Tablice Tablice wielowymiarowe
long widmo[4][2048]; // tajemnicza funkcja, która wczyta z dysku cztery // zestawy wyriików pomiarowych i dane te umieści w tablicy widmo
wczytaj_dane(); cout « "Jaki przedział widma ma byc integrowany ?\n" « "podaj dwie liczby: "; int pocz, koniec; cin » pocz » koniec; //— P° 4 ró żn ych for(int pomiar = 0 ; pomiar < 4 ; pomiar ++) 1
p ró b k a c h II
V
long suma = 0; . . .. . .... //— p ę tla m t e g r u j ą c a d a n e je d n e ] p r ó b k i for(int i = pocz ; i <= koniec ; i++) II & * suma += widmo[pomiar][i];
II
©
cout « "\nW próbce "« pomiar << " miedzy kanałami " << pocz « " a " « koniec <<" jest ” « suma « " zliczeń";
// O
} // koniec pętli po 4 pomiarach }
**********************************■*************************/
void wczytaj_dane{)
I
H... wczytywanie danych z dysku
Po wykonaniu programu (zależnie od odpowiedzi na pytania] możemy na ekranie otrzymać na przykład następujący tekst:
Jaki przedział widma ma byc integrowany ? podaj dwie liczby: 50 75 W próbce 0miedzy kanałami 50 a 75 jest 493 W próbce 1miedzy kanałami 50 a 75 jest 392 W próbce 2miedzy kanałami 50 a 75 jest 300 W próbce 3miedzy kanałami 50 a 75 jest 172
zliczeń zliczeń zliczeń zliczeń
Uwagi O P oniew aż w p ro g ram ie m am y w ykonać identyczną akcję n a 4 ró żn y ch próbkach dlateg o w y g o d n ie posłużyć się pętlą. © W d an y ch p o m iaro w y ch danej p róbki sum ujem y kolejne elem en ty (sąsiednie kanały). O ty m , która część wi dma m a zostać p o d d an a takiej operacji, zad ecy d o w aliśm y o d p o w iad ając na pytania o początek i koniec. © Jest to m o m en t su m o w an ia elem entów tablicy. Jeśli jeszcze n ie osw oiłeś się z operato rem += to m oże wolisz taki, ró w n o w ażn y tam tem u zap is
suma = suma + widmo[pomiar][i];
243
Rozdział. 7. Tablice Tablice wielowymiarowe O To jest m o m en t w y p isan ia w yniku na ekran.
W Jak w id zisz, nie m a w tablicach d w u - i w ielow ym iarow ych niczego n ad zw y czajnego an i tru d n eg o . D om yślasz się chyba też, jak k o m p u te r oblicza sobie, gdzie w pam ięci je st d an y elem ent tablicy. Jak już w iem y, w pam ięci elem enty u staw ian e są kolejno tak, że najczęściej zm ienia się najbardziej skrajny indeks. Z naczy to, że w naszej tablicy:
long widmo[4] [2048]; elem ent dalej
widmo [ 1 ]
[ 3 ] leży w sto su n k u do początku tablicy o tyle elem entów
(1 * 2048) + 3 widmo fil f j] (i * 2048) + j
ogólniej, elem en t
z tablicy o liczbie k o lu m n 2048 leży o
elem entów dalej n iż początkow y. To b ard zo w ażn y w niosek. Jak w idzisz d o orientacji w naszej tablicy kom pilator p otrzebuje znać liczbę jej kolum n, nato m iast w cale nie m u si używ ać liczby w ierszy. Ja bym sobie w takich stw ierdzeniach n aw et nie zaw racał gło w y w ierszam i i kolum nam i. M o żn a to prościej zap am iętać tak: I Ten w y m iar, który w definicji tablicy jest najbardziej z lewej lo n g widmo [ 4 ] [ 2 04 G] ;
// u itas to liczba 4
nie jest potrzebny k o m pilatorow i do obliczania położenia (adresu) elem en tó w tablicy. Z w ykle o takich sp raw ach nie m usim y w ogóle w iedzieć, k aże m y zrobić op era cję na w y b ran y m elem encie [ i ] [ j ] , a jak sobie to k o m p u ter załatw ia - to już jego spraw a. Jeśli jednak jest się tego św iadom ym , to łatw iej zrozum ieć, jak przekazuje się d o funkcji tablice w ielow ym iarow e.
7.6.1
Typ wyrażeń związanych z tablicą w ielow ym iarow ą
Najpierw przypomnienie Definicja takiej n a p rzy k ład tablicy: in t ta b l[« ); jest oczyw iście rów nocześnie deklaracją tej nazw y t a b l . N a pierw szej stronie tego rozdziału m ów iliśm y jakiego ty p u jest taka nazw a t a b l . Pytanie kontrolne: Potrafiłbyś to teraz w ypow iedzieć? Innym i słow y: g d zieś w program ie m o że w ielokrotnie pojaw ić się w yrażenie t a b l — jakiego jest typu jest to w yrażenie? O dpow iedź:
244
Rozdz. 7. Tablice Tablice wielowymiarowe T y p w y ra ż e n ia t a b l to: I T ab lica n e le m e n tó w (z których każdy jest) ty p u i n t .
N astępne pytanie, również kontrolne, bo mówiliśmy o tym niedawno. W programie wielokrotnie m ożem y posługiw ać się elementami tej tablicy. Na przykład jeśli chodzi o elem ent piąty, to oznacza go wyrażenie t a b l [ 5 ] . Jakiego typu jest wyrażenie
tabl
[5 ] ?
Odpowiedź: Jest to w y ra ż e n ie o zn aczające je d e n (5-ty) elem en t tej tablicy, a skoro jes to tab lica elementów typu i n t , w ięc ty p em tego w y ra ż e n ia jest w łaśni* in t. K oniec p rz y p o m n ie n ia . P r z e jd ź m y te r a z d o ś w ia ta ta b lic w ie lo w y m ia ro w y c h
Wyobraźmy sobie taką tablicę int ttt[15][31;
W id z im y że je st to tablica d w u w y m ia ro w a o w y m iarach 15 rz ę d ó w (w ierszy) 3 k o lu m n J e d n a k tak n a p ra w d ę w języ k u C++ nie m a t a b l i c d w u w y m ia ro w y c h Są tablice tablic. Z atem m o że lepiej p o w in n iśm y p o w ied zieć, że: ( m a m y d o czy n ien ia z 1 5 -elem en to w ą tab licą, w której k ażd y | e le m e n t jest 3 -e le m e n to w ą tablicą o b iek tó w ty p u i n t . Obrazek dla najmłodszych czytelników: Wyobraźmy sobie, że t t t to jest t< wąska szafa na książki, z 15 półkami. Szafa jest tak wąska, że na póło mieszczą się tylko trzy tomy. Książki są cenne, więc każda książka na póło ma suroją przegródkę. W programie wielokrotnie m ożem y spotkać się z takimi (podstawowymi wyrażeniam i używającymi tej nazwy:
J
ttt
ttt[i)
ttt[i][j]
N ie c h o d zi tu o deklaracje, ale sytuacje, g d zie już p o słu g u je m y się tą tablicą Z a sta n ó w m y się jakiego ty p u są te w y rażen ia. Z eb rałem to w tabelkę:
W y raż en ie
!ttt
t t t [l]
w-to
T yp tego w y ra ż• e n ia
Tablica t t t
Tablica 1 5 -eIem en to w a, elem en tó w będących tab licam i 3 -ele m en to w y m i obiektów ty p u i n t .
i-ty rząd tablicy ttt
Tablica 3 -e le m e n to w a ob iek tó w ty p u i n t .
Jeden elem ent l t t t [i] t/3 tablicy t t t i i ----------------------
O biekt ty p u i n t .
245
Rozdział. 7. Tablice Tablice wielowymiarowe
Teraz nie jest m o że d la Ciebie tak b ard zo jasne, dlaczego to jest w ażne. O każe się dopiero, g d y zajm iem y się w sk aźn ik am i p o kazującym i na tablice w ielo w y m iaro w e.
Dla najmłodszych czytelników - kontynuacja obrazka z szafą P rzypom inam , ż e rozm aw iam y o takiej na przykład tablicy..., p rzep raszam szafie na tom y (w olum iny) książek tom t t t [ 1 5 ] [ 3 ] ; O to w yjaśnienie w spom nianych w yżej w yrażeń, zebrane w p o d o b n ą tabelkę: 1"
- '
' O p is
i V;
szafa t t t 1 ttt ■ ---- --'-----------! To, co jest szafie t t t , na i-tej półce t t t [i] 'i
t t t [i] [/]
to, co jest w /-tej p rzeg ró d ce, na i-tej półce w szafie t t t
T
y
p
^
"Y ^
;
;
Szafa 15-półkow a, k ażd a pólka jest zestaw em 3 p rzeg ró d ek na tom y Z estaw 3 przegródek na poszczególne tom y. ____________________________________ 1
Jeden tom.
Z auw aż, co się stan ie, jeśli zam iast w y rażen ia odnoszącego się do drugiego tom u na siódm ej p ó łce t t t [ 7 )[2 ] p rzez pom yłkę nap iszem y t t t [7, 2] D ziałanie o p erato ra przecinek jest takie, że zrozum ie on to w yrażenie jako: t t t [2] a to oznacza d ru g ą półkę. Jest to w y rażen ie popraw ne. Z tym , że najp raw d o p o dobniej w ystąpi o n o w sytuacji, odpow iedniej dla tom u, a nic dla półki. To w łaśnie m oże w y k ry ć kom pilator i nas ostrzec. Takie błędy grożą C i tylko na w stęp n y m etapie nauki C++.
7.6.2
Przesyłanie tablic wielowym iarowych do funkcji Ten tytuł m oże tro ch ę mylić. Tablice, jako całość, nigdy nie są przekazyw ane. To m iędzy innym i d lateg o , że w p rzy p ad k u takiej tablicy: long widmo[4 ) [ 8 1 9 2 ] ;
246
Rozdz. 7. Tablice Tablice wielowymiarowe m u sie lib y śm y d o funkcji p rzesłać 4 * 8192 = ok. 32 ty sią ce liczb (a k a ż d a je st np. 4 bajtow a).
Wiemy już, że tablice przekazuje się w ten sposób, iż przesyłamy do funkcji tylko adres początku. Jak p a m ię ta m y - n azw a tablicy je st ró w n o cześn ie a d re s e m jej p o czą tk u - więc d o h ip o tety cz n ej funkcji f u n p rz e s y ła m y ją tak: fu n (w id m o );
A teraz przyjrzyjmy się jak ją (tablicę) odbieramy w funkcji. Z an im je d n a k to p o k ażem y , u św ia d o m ijm y sobie, co fu n k cja m u si o n asze tablicy w ie d z ie ć p o p ie rw sz e - o czyw iście ty p elem en tó w tej ta b licy (czy f l o a t , czy c h a r itd), p o d r u g ie - ab y funkcja m o g ła ła tw o obliczać so b ie, g d z ie w p am ięci jeso k re ś lo n y elem en t tab licy — na przykład: gdzie w stosunku do początku tablicy jest element [1][15]) - m u s i zn ać liczbę k o lu m n w tej tablicy. D eklaracja tak iej funkcji w y g lą d a tak: v o id f u n (lo n g t [ ] [8192]
);
p o d a n a jest w niej liczba k o lu m n tej tablicy, którą fu n k c ja b ę d z ie o trz y m y w a ć O czyw iście d ek laracja v o id f u n ( l o n g t [ 4 ] [ 8 1 9 2 ] ) ; jest tak s a m o d o b ra, ale z liczby w ie rsz y (tutaj 4) fu n k c ja n ie k orzysta.
Więcej wymiarów P rzez a n a lo g ię łatw o ch y b a się d o m y ślisz , ż e w p r z y p a d k u fu nkcji o trzy m u jąc e tablicę tró jw y m ia ro w ą - d e k laracja takiej funkcji m o ż e w y g lą d a ć na p rzy k łac tak: v o id f u n _ t r z y ( l o n g m[] [2 0 ] [100]
);
a c z te ro w y m ia ro w e j v o id f u n _ c z t e r y ( lo n g x [ ] [ 2 ] [ 1 2 ] [ 3 6 5 ] ) ; T y lk o le w e g o sk rajn eg o ro z m ia ru nie trz e b a d e k la ro w a ć , b o i tak nie b ierze oi u d z ia łu w o b liczan iu pozycji ż ą d a n e g o elem en tu w p am ięci. C ały te n p a r a g r a f n ie słu ży p o to , b y Cię zniechęcić d o p o d a w a n ia w d ek laracj teg o le w e g o s k ra jn e g o w y m ia ru . Jest p o to, byś w ie d z ia ł d la c z e g o deklaracja v o id f f f ( l o n g m [] [ ] [ ] ) ; jest b łę d n a , a d ek laracja v o id g g g ( lo n g z [ ] ) ;
II błąd
247
Rozdział. 7. Tablice Ćwiczenia jest p o p raw n a.
Tablice to d o ść p ro ste pojęcie. N iestety - trzeba tu pam iętać o p a ru rzeczach. Na przykład - by n ie um ieścić czegoś w elem encie, który nie istnieje, albo o tym , że taka tablica jest m a stałe rozm iary, nie m ożem y jej pow iększyć, jeśli doch o d zim y d o w niosku, że p raw ie n am się zapełniła. W bibliotece standardowej C++ um ieszczono więc definicje nowych typów (tzw. klas), które nie tylko imitują tablice, ale mają pewien rodzaj inteligencji. Dbają m ianow icie o to, byśmy z nimi pracowali poprawnie. **
M •'
l^W*1»iim*n***9i*i*0m*itxi**a*c*KtM M
11riri*fr1’W-*' W M M M M M I
No przykład właśnie sprawdzają, czy nie wpisujemy czegoś do nieistnie jącego elementu. Także pozwalają na to, by taka "mądrzejsza" tablica samo dzielnie się powiększyła, gdy zajdzie taka konieczność. Z anim jed n ak zaczniesz korzystać z takich u d o g o d n ie ń m usisz um ieć posługiw ać się zw ykłym i, poczciw ym i tablicami.
N a tym kończy się rozd ział o tablicach, ale nie rozstajem y się z tą tem atyką, bow iem n a stę p n y rozd ział m ów ił b ęd zie o w skaźnikach, co m ocno jest zw iąza ne z tem atyką tablic.
7.1
Ć w ic ze n ia Czym się różni tablica 10 elementów typu int , od 10 obiektów typu int? Czy można utworzyć tablicę 20 obiektów, z których pierwsze 10 będzie typu int, a następne 10 typu unsigned int? Mamy tablicę unsigned
in t
tab[200);
Przeczytaj tę deklarację. Jakiego typu obiekt oznacza nazwa t a b ? Czy poprawne są poniższe definicje: a)
double wspolcz (5.6];
b)
dou b le sign ed
cj
fact[0 ]; in t nom in[-6];
Czy poprawna jest definicja tablicy: int rozmiar = 10; char tabl[rozmiar];
Napisz definicję tablicy dwunastu elementów typu d o u b l e . Czy poprawna jest następująca definicja?
248
Rozdz. 7. Tablice Ćwiczenia void v o i u t a b i[237]; -- ' ■
w definicje typu wyliczeniowego ru c h , który będzie przyjmował trzy wartości Napisz de w t ]J 1tysPto p yNastępnie napisz definicję tablicy 15 elementów takiego typu. J ^ te j definicji, za pomocą inicjalizaqi zbiorczej nadaj pierwszym pięciu elementom tablicy jakieś wartości. Mamy definicję tablicy, w której można przechować tekst:
_ 0 1
char a l f a b e t [3 0 ]; • I^mniitera zbudowana jest tak, że jej elementarne bity przyjmują tylko wartości ™ Jak £ więc możliwe, te tworzymy tablice, w które, można przechować litery?
Co to jest znak n u l i ; po co jest potrzebny, skoro nie ma go w zwykłym alfabecie? Napisz definicje tablicy 14 znaków, która od razu inicjalizowana jest C-stringiem t o p ^ d ^ e tablicy 3 znaków, która od razu inicjalizowana jest odrębnymi znakami 'x', ' y ', ' z ' • Ile elem entów ma tak zdefiniowana tablica
char ta b []
= ( "123" ło
jaka jest długość stringu w powyższej tablicy? Czym się różni długość stringu od rozmiaru stringu? Które z poniższych instrukcji są popraw ne7
n) c h a r t a b l [ 4 ] = 1 " 1 ,2 , 3 " };
W Sar tab2{] - j M; 2. c)
XVI
char t_ab3[3] - ł
A ,
>«,c. b
,
c j,
Mamy tablicę char tablllO] = ł "ABCD" );
Czy poprawne są następujące instrukcje korzystające z tej tablicy?
a) t a b l[ 6 1 = 'm '; b)
t a b l [10]
= ( "GGGG" ) ;
c) t a b l [0 ] = 1 ;'GGGG" 1; ej
f) g)
tabl [ 1 - ' a ' tabl = '7 '; tabl = "7"
Mamy dwie tablice znakowe o identycznej długoSci, W jednej z nich znajduje sie m S i r fankcie która przekopiuje codrugą literę z jędnęj tablicy do drugiej. To znaczy: jeśli*w jednejtablicy jest tekst •' instytut ", to w drugiej powinien sięznalezc C-sh-ing
"‘ ^ “ tablice znaków, w której znajduje sie długi C-string będący tekstem, składającym Tw.lkun.istu wyrazów. Napisz funkcje, która przekopiuje ten C-stnng do dwóch S1? h tablic W tablicy p arzy ste znaleźć mają sie parzyste wyrazy C-stringu a w "T,y i T io a r z y s t e - nieparzyste wyrazy C-stringu. W obu docelowych tablicach wyrazyntiąbyćoddzielone odstępami (spacjami), czyli znakami ■ ■. Funkqa ta ma byc wywoływana z trzema argumentami:
249
Rozdział. 7. Tablice Ćwiczenia - adres tablicy źródłowej, - adres tablicy na wyrazy parzyste, - adres tablicy na wyrazy nieparzyste.
Wyjaśnij, jakie ryzyko niesie w sobie instrukcja przypisania umieszczona w ciele poniższej pętli f o r . char A [10]; char Z [10J;
//
...
for(int i = 0
i < 10 ; )
{ A[i++]
— Z [i++];
1 Czy poprawne jest poniższe wyrażenie warunkowe w instrukcji i f int a [10], b [10]; / / ... if(a[61 = b [8])
{ cout << "Tekst";
} Jakie musiały by być elementy tablic, aby na ekranie pojawił się powyższy tekst? Mamy dwie tablice char tl[] = "Litwo, ojczyzno moja"; char t2[] * "ABC"; char t3[] - "123456789012345678901234567890";
Czy można string z tablicy tl przepisać do tablicy t2 ? Czy można string z tablicy t l przepisać do tablicy t3 ? Czy w funkcji, do której wysłano tablicę, można dowiedzieć się ile ma ona elementów? Napisz funkcję, która do końca stringu będącego w jednej tablicy, dołączy string, będący w drugiej tablicy. Argumentami tej funkcji powinny być - adres tablicy docelowej, - jej rozmiar, - adres tablicy ze stringiem dołączanym. Funkcja powinna przerwać dołączanie stringu w przypadku zapełnienia całej tablicy. Napisz definicję tablicy 5 elementowej, której elementami są tablice 2 elementów typu float.
Mamy definicję char [4][2][2];
Wypisz wszystkie elementy typu (cha r) tej tablicy w kolejności, w jakiej umieszczone są w pamięci komputera. Napisz program, w którym będzie tablica dwuwymiarowa o rozmiarach: 10 rzędów i 3 kolumny. Do elementów zerowej kolumny załaduj kolejne liczby naturalne (i), do elementów następnej kolumny załaduj ich kwadraty (i*i), a do następnej sześciany (i*i*i). Wypisz zawartość tej tablicy na ekranie - właśnie w postaci trzech pionowych kolumn.
250
Rozdz. 7. Tablice Ćwiczenia X XV II
Zdefiniowaliśmy tablicę double t a b [10][5][30];
Które z rozmiarów tej tablicy są niezbędne do tego, by kompilator mógł obliczyć pozycję w pamięci jakiegoś z elementów tej tablicy? Jak myślisz, po co zatem potrzebny jest ten rozmiar, który nie jest konieczny do obliczania adresu elementów? Zdefiniowaliśmy tablice double tab[10][5][30];
W tekście program u mogą pojawić się na przykład następujące wyrażenia związane z tą tablicą. t a b [ U [0][1]/
t a b [6][4],
tab[l],
tab
Wyjaśnij co one oznaczają. W programie mamy tablicę double t a b [10][5][30];
Napisz funkcję, do która wyzeruje wszystkie elementy tej tablicy W układzie pomiarowym eksperymentu fizycznego dane przychodzą z 4 niezależnych kanałów o umownych nazwach FRS, GER, HEC, DGF. W trakcie pomiaru robiona jest statystyka równoczesności wystąpienia (czyli koincydencji) tych sygnałów. Polega to na tym, że jest tablica jednowymiarowa, w której kolejne elementy odpowiadają odpowiednim kombinacjom tych sygnałów. To znaczy na przykład element 3 odpowiada równoczesności sygnałów kanałach FRS i GER. Element 15 odpowiada równoczesności sygnałów FRS, GER, HEC, DGF. Oto odpowiednie kombinacje zebrane w tabelkę K om binacja sygnałów DGF H EC GER
bin arn ie
nr elem entu tablicy
FRS
0
0
0
0
0000
0
0
0
0
1
0001
1
0
0
1
0
0010
2
0
0
1
1
0011
3
0
1
0
0
0100
4
0
1
0
1
0101
... - 5 - - - !,
0
1
1
0
0110
_____ 6_____ i!
0
1
1
1
0111
7
1
0
0
0
1000
8
1
0
0
1
1001
9
1
0
1
0
1010
10
1
0
1
1
1011
n
!
'i
251
Rozdział. 7. Tablice Ćwiczenia
]1 Zasada jest prosta - każdy sygnał ma jakby "swój" bit w nu m erze elementu. Obecność tego sygnału u staw ia ten bit na 1, brak sygnału ustawia na 0. Po spraw dzeniu obecności lub nieobecności w szystkich 4 sygn ałów powstaje słow o, będące num erem elem entu w tablicy statystyki. Skoro taka kombinacja w łaśnie w ystąpiła to tenże elem ent tablicy poddaw any jest inkrementacji. (C zyli będący tam licznik takich sytuacji p o d w y ższa n y jest o jeden). To tyle tytułem w stępu . Załóżm y, ż e p om iar trwa już jakiś czas i w tablicy są już jakieś liczb y określające ile razy poszczególne kom binacje w ystąpiły w ciągu tego czasu. Teraz w ła ś c iw e zadanie: napisz funkcję, która tę statystykę w y św ietli na ekranie w taki mniej w ięcej sp o só b (wartości liczbow e są przykładowe) Poszcz e g ó l n e kombinacje wystąpiły następująca K o m b i n a c j a : FR S -> 346 r a z y Kombinacja: GER -> 652 r a z y K o m b i n a c j a : FRS + G E R -> 7 3 7 razy Kombinacja: HEC -> 277 r a z y K o m b i n a c j a : FRS + HEC - > 4 44 r a z y
...
itd a ż d o k o m b in a c ji F R S + G E R
+ HEC
ilosc
razy
+ D G F ...
Jak w id zisz, jest to w ypisanie kolejnych elem entów tablicy, z tym że temu w ypisow i tow arzyszy opis kombinacji sygnałów . G dyby jakaś kombinacja nie wystąpiła ani raz, (wartość = 0) w ó w c z a s oczyw iście takiej linii statystyki nie w ypisujem y. Uwaga- Ten sym boliczny opis kombinacji (np. "F R S + H E C + DGF') musi być konstruo wany na bieżąco na podstaw ie analizy numeru elem entu tablicy, który właśnie w ypisujem y. O p is ten nie m oże być w ięc "zaszyty" na stałe w program ie. (Wyobraź sobie, że m am y nie 4 sygn ały, ale 10 - m usiałbyś wtedy przygotow ać 1024 takich gotow ych tekstów). Zatem Twoje za d a n ie polega na tym, byś wypisując np. d w u n asty elem ent tablicy, potrafił opisać g o na ekranie podając n azw y odpowiednich sygn ałów . Gdy napiszesz tę funkcję i będzie działała poprawnie - pojawia się jeszcze jedno życzenie eksperym entatora. Chciałby on m ianow icie, aby w ypisyw an e na ekranie kombinacje pojawiały się nie tak, jak są u m ieszczo n e w tablicy (po kolei), ale począw szy od najbardziej skom plikowanych kombinacji do najprostszych. Czyli by najpierw wypisana została linijka ze statystyką kombinacji 4 sy g n a łó w , następnie linijki z różnymi kombinacjami 3 sygnałów , potem z kombinacjami 2 sygnałów , a na końcu wystąpienia sygnałów pojedynczych. Czyli na przykład tak
252
Rozdz. 7. Tablice Ćwiczenia
Poszczególne kombinacje Kombinacja: FRS + GER + Kombinacja: FRS+ GER + Kombinacja: FRS + GER + Kombinacja: FRS + HEC + Kombinacja: GER'+ HEC +
... i t d
wystąpiły następująca ilosc razy FRS + DGF -> 346 razy FRS -> 652 razy DGF -> 737 razy DGF -> 277 razy DGF -> 444 razy
a ż d o k o m b i n a c ji F R S ( p o j e d y n c z e j ) ...
Rozdział. 8. W skaźniki W skaźniki mogą bardzo ułatwić życie
253
Wskaźniki
8
apytano kiedyś Amerykanów: „-Czy można, żyć bez Coca-Coli?". Amerykanie odpowiedzieli: „-Można, ale po co?". Dokładnie to samo można powiedzieć o wskaźnikach. Ich istnienie nie jest konieczne, najlepszy dowód, że jest wiele języków programowania, w których wskaźników nie ma. Z drugiej jednak strony - jeśli języki C i C++ mają opinię tak sprawnych i władnych , to jest to głównie za sprawą wskaźników.
Z
Są ludzie, którzy nie lubią C. Moim zdaniem to dlatego, że nie czują się swobodnie w operowaniu wskaźnikami. Jeśli doskonale znasz język C, a książkę tę czytasz, by poznać C++, to w zasadzie mógłbyś ten rozdział opuścić. Jeśli jednak wskaźniki znasz trochę gorzej, to zachęcam do przeczytania.
8.1
W skaźn iki m o g ą b ardzo u ła tw ić życie Do rzeczy: Wyobraź sobie, że masz w ręce bardzo gruby rozkład jazdy. Taki na 600 stron. Ktoś pyta Cię o pociąg z Paryża do Marsylii. Otwierasz więc rozkład jazdy i szukasz przewracając strony. Wreszcie po dłuższym czasie trafiasz na właściwą stronę i przebiegając palcem kolumny cyfr znajdujesz to, czego szuka łeś. Dumnym głosem mówisz - 9:26 i zamykasz rozkład z trzaskiem. Wtedy ten ktoś pyta: „-A następny?". Bierzesz znowu rozkład jazdy, przewra casz strony, trafiasz na właściwą, potem znowu przebiegasz kolumny i... jest! 13:50
1)
Chdałoby się powiedzieć po angielsku: powerjul
254
Rozdz. 8. W skaźniki W skaźniki mogą bardzo ułatwić życie T ylko, ż e teraz jesteś ju ż sp ry tn ie jszy : p o o d p o w ie d z i, n a w sz e lk i w y p a d e k , trz y m a s z palec w sk azu jący n a zn alezio n ej g o d zin ie o d ja zd u . Jeśli T w ó j p rz y ja ciel z a p y ta Cię teraz: ,,-A następny...?" - b ły sk aw icz n ie p rz e s u n ie s z p alec o jed n ą k o lu m n ę w p ra w o i o d c z y ta s z go d zin ę. Jeśli z a p y ta C ię „ -A o której on jest w Lyonie?" - w te d y p rz e s u n ie sz p alec kilka linijek w dół. P o d a n y p rz y k ła d ilu stru je ż y c ie ze w sk aźn ik am i i bez. C o b y ło w sk a ź n ik ie m w n a sz y m p rz y p a d k u ? Był to T w ój palec, ale nie ty lk o : ta k ż e w ie d z a o tym , jak n a le ż y g o p rz e su n ą ć w p rz y p a d k u pytania: „A następny?".
Przetłumaczmy to na język pojęć, którymi operujemy w C++ : R o zk ład ja z d y to nic in n e g o jak o lb rzy m ia (w ie lo w y m ia ro w a ) ta b lica liczb. G o d z in a o d ja z d u pociągu z P a ry ż a d o M arsylii to p o p ro s tu ele m e n t tej tablicy pociag[Paryz](Marsylia][wyjazd_z_Paryza][0]
O sta tn i in d e k s o zn acza, o k tó ry pociąg w ciągu d o b y n am ch o d zi (w ciągu d o b y m ogą b o w ie m o d je żd żać d o M arsy lii n p 4 takie pociągi). S z u k a n ie inform acji o tym p o c ią g u to obliczanie m iejsca w tablicy, g d z ie z a p isa na jest ta inform acja. P a m ię ta s z z a p e w n e z p o p rz e d n ie g o ro z d z ia łu , jak o blicza k o m p ila to r pozycję e lem en tu w sto su n k u d o p o c z ą tk u tablicy. D la tam tej d w u w y m ia ro w e j tablicy b y ła to zależność: (i * 8192)
+ j
g d z ie 8192 by ło liczbą k o lu m n tablicy. Tutaj, w n a s z y m p rz y k ła d z ie z ro z k ła d e m ja z d y , m a m y d u ż o w ięcej w y m iaró w , w ięc i o b liczan ie b ę d z ie b ard ziej sk o m p lik o w a n e . W n aszy m o b liczan iu b ęd ą aż 3 m n o ż e n ia , a to z ajm u je tro ch ę czasu. G d y w re sz c ie trafiliśm y w ro z k ła d z ie jazd y na w ła śc iw e m iejsce, to o d c z y ta li śm y j e - j e s t to jakby o d c z y ta n ie liczby będącej treścią e le m e n tu n aszej tablicy. P o tem z a m k n ę liśm y ro z k ła d i w te d y n asz p rzy ja ciel w y g ło sił w s p o m n ia n e słow a: „A następny?". N a s tę p n y p o ciąg w naszej ta b licy to pociag[Paryz][Marsylia][wyjazd_z_Paryza][1]
O d n o w a w ięc zajęliśm y się o b liczan iem m iejsca w p am ięci, g d z ie z a p isa n a jesl p o s z u k iw a n a inform acja. Z n o w u n a tru d z iliśm y się w ielce p o w ta rz a ją c ten p roces, a le w reszcie z n a le ź liśm y w łaściw ą in fo rm ację i o d c z y ta liśm y ją. W ted y , ch cąc oszczęd zić so b ie d alszej ew e n tu a ln e j p ra c y , zaz n a c z y liśm y so b ie p alcem to m iejsce i d o p ie ro w te d y p rz y m k n ę liśm y k siążk ę. O zn acza to - z d e f in io w a liś m y w sk a ź n ik (w sk a zu jący p a le c p raw ej ręki) i p o d s ta w iliś m y d o n iego w a rto ść p o c z ą tk o w ą - a d re s o d czy tan eg o w łaśn ie ele m e n tu tablicy (in n y m i sło w y p rz y sta w iliśm y p alec w sk a zu jąc y ta k , by p o k a z y w a ł n a w ła śc iw ą liczb ę na w łaści w ej stronie)
Rozdział. 8. W skaźniki Definiowanie wskaźników
255
K siążkę przy m k n ęliśm y , ale tak, ż e palec m im o w szy stk o tam tkwi. Kiedy n a sz przyjaciel zap y tałb y nas o n astęp n y pociąg, od razu o tw orzylibyśm y rozkład na w łaściw ej stronie. Błyskaw iczny ruch palca w p raw o i ju ż wiem y. Z ap y tan ie o to, k ied y pociąg jest w Lyonie, jest próbą znalezienia elem entu pociag[Paryz][Marsylia][postoj_w_Lyonie][1]
Tutaj w y starczy w iedzieć ile stacji jest m iędzy P aryżem a Lyonem i o tyle linijek p rzesu n ąć palec w dół. ii iwirionnuu i nuwirjmirM i^M iiniw■wwHiminnn mnrmrtthtut- tut.. . J a k i je s t w n io s e k z te j d y k te ry jk i? T a k i, ż e p o s łu g iw a n ie się w s k a ź n ik ie m b a rd z o p rz y s p ie s z a o p e ra c je na ta b lic a c h .
M oże być więcej niż jeden w sk aźn ik pracujący na naszej tablicy. M ożem y bow iem in n y m palcem zaznaczyć sobie inform ację o pociągach z Marsylii do A vignon. Jeśli nam nie w ystarczy palców zorganizujem y sobie zakładki. Z akła dek takich m ożem y mieć d ow olnie dużo. Jeśli po tym ktoś mi pow ie, że nie lubi w skaźników , to nie uw ierzę. M ało rzeczy m oże tak up ro ścić życie.
3.2
Definiowanie w s k a ź n i k ó w W skaźniki nie m uszą koniecznie pokazyw ać na elem enty tablicy. M ożna nim i posłużyć się w obec dow olnej zm iennej, wobec d o w o ln eg o obiektu. Z aczniem y od takich p rzy k ład ó w . Przyjrzyjm y się poniższej definicji int *w;
Jest to definicja w skaźnika m ogącego pokazyw ać na obiekty typu int. Definicję taką odczytujem y jak zw ykle od końca. N a w id o k gw iazdki m ów im y słowa: „jest wskaźnikiem do pokazywania na obiekty typu...". A zatem po w y ższą definicję czytam y na głos: w - jest w skaźnikiem d o pok azy w an ia na obiekty typu - int. Innym i słow y: w tym w skaźniku w m ożem y p rzech o w y w ać adres jakiegoś obiektu typu i n t . Treścią w skaźnika jest inform acja o tym , gdzie w skazyw any obiekt się znajduje, a nie co w tam tym obiekcie się znajduje. W definicji w idzisz znak ' * ', który inform uje nas, że m am y tu do czynienia ze w skaźnikiem . Słowo int w tej definicji inform uje nas, że w skaźnik ten m a służyć d o pokazyw ania na obiekty ty p u int. K©
Nasz w skaźnik nazyw a się w. Samo w, bez gw iazdki. G w iazd k a zaś, m ów i tylko, że to coś, o n azw ie w - jest w skaźnikiem . Słowo int m ó w i, na jakie obiekty może on pokazyw ać.
256
Rozdz. 8. W skaźniki Definiowanie wskaźników P o ró w n a j to z r e s z tą z d efin icją ta b lic y i n t 115] ; T ab lica n a z y w a się t . N a w ia sy [ ] m ó w ią tylko, że to t je st tablicą, i n t - ż< e le m e n tó w t y p u i n t . Jeśli c h c ia łb y ś z d e fin io w a ć w je d n e j linii d w ie tablice lu b d w a w skaźniki, to ro b
się to tak: int tabl_a[10], tabl_b[5]; in t * w s k l, *w sk2; r , k w id z is z , k a ż d a z tab lic m a tu sw oje k la m ry [] - to w y d a je się zro zu m iale P o d o b n ie je s t w p rz y p a d k u d e fin io w a n ia d w ó ch w sk a ź n ik ó w - k ażd y m< sw o ją g w ia z d k ę . Z o b a c z p o n iżej ta k ą linijkę definicji in t
* w s k l, m, t a b [ 6 ] ,
*w sk2, x ;
Je st tu d efin icja: ♦♦♦ w s k a ź n ik a d o i n t , a m a o n n a z w ę w s k l , z w y k łe g o o b ie k tu i n t o n a z w ie m, ♦♦♦ sz e śc io e le m e n to w e j tab licy o b ie k tó w ty p u
i n t , tab lica ta m a nazw *
ta b w s k a ź n ik a d o i n t , a m a o n n a z w ę w sk 2 , z w y k łe g o o b ie k tu i n t o n a z w ie x, Mao’isatem to , b y ś m ó g ł p rz e c z y ta ć taki z a p is w cu d z y m p ro g ra m ie . W sw o im n ig d y tego n ie sto su j. Z b y t ła tw o o p o m y łk ę. S zczególnie p o czątk u jący m y s ią *e ob iek t m b ę d z ie ta k że w sk a ź n ik ie m , bo p rzecież b y ła g w ia z d k a (p rzed o b iek tem w s k l ) . T ym czasem ta g w ia z d k a jest o so b istą w łasn o ścią obiektu ws k l . O b iek tu m o n a ju ż nie d o ty c zy . Jeśli o n tak że ch ciałb y b y ć w sk aźn ik iem -j m u si m ieć sw o ją g w iazd k ę.
W M o za być w s k a ź n ik i d o o b iek tó w ró żn y ch typów : np. c h a r d o u b l e itd , (a n a w e t d o o b ie k tó w , k tórych ty p y s a m i so b ie w y m y ślim y i zd efin iu jem y ). char * wsk_do_znakow; double * wsk_do_doubleow;
7 w róć u w ag ę, ż e w definicji jest p o w ie d z ia n e , że w sk a ź n ik p o k a z u je n a o b ie k ty . R eferenda (p rzezw isk o ) nie jest o b iek tem , d lateg o w ięc n ie m o ż e byc d o m ej w skaźn ik ó w . T ak że nie m oże być w sk a ź n ik ó w d o p o je d y n czy ch b itó w jak ieg o ś słow a - tzw . p ó l bitow ych (zob. str. 634). W naszei definicji stw o rzy liśm y sobie w sk aźn ik , ale nie p o k a z u je on jeszcze n a nir rewelacyjnego. To tak, jak b y śm y na lekcję geografii w y s tru g a li w sk a źn ik z d rew n a i p o ło ży li go na boku. M im o ż e w sk aźn ik leży n a b o k u , to p rzecież jeg o
Rozdział. 8. W skaźniki P raca ze wskaźnikam i
257
koniec zaw sze na coś pokazuje (celuje), ale z tego pokazywania jeszcze nie można się szczycić.
W jfo
BARDZO WAŻNA UWAGA: W skaźnik służący do pokazyw ania na obiekty jednego typu nie nadaje się do pokazyw ania na obiekty innego typu. 2
Konkretnie: wskaźnikiem do int nie można pokazywać na double czy C-string. Próba ustawienia wskaźnika na obiekt innego, niewłaściwego typu w ywoła protest kompilatora.
Praca z e w s k a ź n ik ie m Oto, jak można nadać naszemu wskaźnikowi wartość początkową, czyli spra wić, by na coś pokazywał. Używa się do tego jednoargumentowego operatora &(ampersand). Udostęp nia on nam adres obiektu, który stoi po jego prawej stronie. Tak uzyskany adres można przypisać do wskaźnika. i n t *w; i n t k = 3;
//d efin icja w s k a ź n ik a d o o b ie k tó w t y p u int II d efin icja z w y k łe g o obiektu ty p u i n t z lic zb ą 3
// ustawienie wskaźnika w tak, by pokazywał na obiekt k
Kiedy wskaźnik już pokazuje na żądane miejsce - m ożem y odnieść się do tego obiektu, na który pokazuje. Odnieść się do obiektu - rozumiem jako na przykład: odczytać jego zawartość, czy też coś zapisać do niego. Do takiej operacji służy jednoargumentowy opera tor odniesienia się * (gwiazdka). „ -Z n o w u g w iazd k a?" - zap y tasz pew nie - „nie za d u ż o tego?" O d p o w iem jednak: - Jest w tym logika. Otóż zapamiętaj sobie złotą myśl: W języku C++ definicje obiektów zapisywane są tak, że zapis przypomina sposób, w jaki później dany obiekt występuje w wyrażeniach. hiwiw h iw rur
mrw m rim mmii\»i-~frrwtrwrtr-nreirr~r~-
Definicja tablicy zawierała klamry int t [5] ;
dlatego później, w wyrażeniach, klamry oznaczają pracę na tablicy a = t [1 ] ; 2)
II o d c zy ta n ie p ie rw sze g o elem en tu tablicy.
W rozdziale o dziedziczeniu dowiemy się, że od tej reguły jest chwalebny wyjątek.
258
Rozdz. 8. W skaźniki P raca ze wskaźnikam i Nie inaczej jest w p rzy p ad k u w sk a ź n ik a . Jeśli m am y n asz w sk a źn ik i już u sta w im y go na obiekt k, to treść p o k a z y w a n e g o obiektu o d c z y tu je się jednoarg m entow ym o p erato rem * •nt W
—
/I definicja wskaźniku / definicja zmiennej
* k= 3; r ..
&K *
cout «
1
// ustawienie wskaźnika ii "w obiekcie pokazywanym przez "wskaźnik w jest wartość " « (**) ’•
wykonanie tego fragmentu programu da nam na ekranie W
obiekcie pokazywanym przez wskaźnik w jest wartość 3
Słowem: g w ia z d k a kieruje n a s d o ob iek tu p o k azy w an eg o p rzez wskaźnik N asz w sk aźn ik p o k azy w ał na zm ie n n ą k. Zatem od tej p o ry co k. Skoro zaw arto ścią k b y ła liczba 3, to ta w artość p o ja w iła się na ekrara ■I W niosek: p o u staw ien iu w sk a źn ik a o b ie poniższe in stru k cje są rów now ażne cout << k; cout « *w;
// wypisz na ekran k // wypisz na ekran k
Przyjrzyjm y się teraz takiem u pro g ram o w i: finclude
z /i* * * * * ? * f?* * * * * *^«* ************ *««*«** ****** int maind int zmienna = 8, drugi = 4; int *wskaznik; wskaźnik = Szmienna;
// prosty wypis na ekran; cout « "zmienna = " « zmienna n « "\n a odczytana przez^wskaźnik << *wskaznik << endl; zmienna = 1 0 ; cout << "zmienna = "<< zmienna |f « "\n a odczytana przez wskaźnik << ‘wskaźnik << endl; ‘wskaźnik = 200; cout « "zmienna = " << zmienna _ |( << "\n a odczytana przez wskaźnik — << ‘wskaźnik << endl; wskaźnik = &drugi; cout « "zmienna = "<< zmienna ^ n << "\n a odczytana przez wskaźnik << ‘wskaźnik << endl;
Rozdział. 8. W skaźniki P raca ze wskaźnikam i
259
Po wykonaniu tego programu na ekranie pojawi się zm ienna = 8 a o d c z y ta n a p rz e z zm ienna = 10 a o d c z y ta n a p r z e z zm ienna = 200 a o d c z y ta n a p r z e z zm ienna = 200 a o d c z y ta n a p r z e z
w sk a ź n ik = 8 w sk a ź n ik = 10 w sk a ź n ik = 200 w sk a ź n ik = 4
O Tutaj d efin iu jem y d w a najzw yklejsze obiekty ty p u i n t . O d razu inicjalizujem y je w arto ściam i liczbow ym i. 0 Tutaj d efin iu jem y w skaźnik. Definicję czyta się tak: w s k a ź n i k - jest w sk aźn i kiem (p rzep rasza m !) do p o k azy w an ia na obiekty typu i n t . © Tutaj sp ra w ia m y , że w skaźnik zaczy n a pokazyw ać na coś sensow nego. Robim y to za pom ocą o p erato ra & - u zyskującego ad res obiektu zmienna. Ten ad res jest p o d sta w io n y d o w skaźnika o p erato rem p rzy p isan ia = W z a sa d z ie p o w in ie n em napisać linijkę 0 i © razem stosując skrócony zapis, jednak obiecałem sobie nie p rzeraż ać Cie. Taki skrócony zapis tych dw óch linijek w y g lą d a tak: i n t * w sk a z n ik = szm ien n a;
O W ielokrotnie w trakcie tego p ro g ram u w ypisujem y na e k ran ie w artość tego, na co po k azu je w sk a źn ik . W u p ro szczen iu to po prostu c o u t << * w s k a z n i k ;
© Do zmiennej w p isu jem y now ą w artość. Kolejny w ypis n a ek ran u d o w ad n ia, że w sk aźn ik cały czas pokazuje na ten obiekt i oczyw iście za u w a ż a zm ianę. © To jest b a rd z o w a ż n a linijka. Skoro w yrażenie *wskaznik oznacza obiekt, na który p o k azu je w skaźnik, to zapis "wskaźnik -
200;
oznacza: „d o tego, na co pokazuje w skaźnik, w pisz liczbę 200". Poniew aż w sk aźn ik p o k azy w ał na obiekt zmienna, więc do ob iek tu zmienna zostaje w pisan a liczba 200. Chciałbym, żeb y ś docenił tę historyczną chwilę: O to d o obiektu m o żn a coś w pisać albo u ży w ając jego nazw y (zmienna), albo w sk aźn i ka, który n a ten obiekt pokazuje (*ws ka zni k ). N a d o w ó d , że to działa - zn o w u w yp isu jem y to na ekran. W idzim y n a ekranie d w u k ro tn ie liczbę 200.
O Wskaźnik nie pokazuje raz na zawsze na ten sam obiekt. M ożna go łatw o przestaw ić i od tej p o ry pokazuje na inny obiekt. Tutaj w idzim y ustaw ien ie w sk aźn ik a tak, by p o k azy w ał na obiekt drugi. (Robimy to, jak w idać, w staw iając d o w skaźnika a d re s zm iennej o n azw ie drugi. N astęp n y w y d ru k na ek ran ie pokazuje nam w ięc już liczby 200 i 4. Liczba dw ieście jest treścią z m i e n n e j , natom iast skoro w skaźnik pokazuje teraz na obiekt drugi, to w y rażen ie *wskaznik odnosi się teraz do obiektu drugi.
260
Rozdz. 8. W skaźniki L-wartość P om yślisz pew nie: „C o to za b zdurny program ! Po co d o w p isan ia czy o d czy tan ia czegoś z obiektu u ży w ać w skaźnika, skoro łatwiej u ż y ć n azw ę tego o b iek tu ." M asz rację, p ro g ram jest rzeczyw iście b zd u rn y , w z a sa d z ie w sk aźn ik i m ogły by w nim w ogóle nie istnieć. C hciałem w nim jednak p o k aza ć, że: in nn— rr---Do danego obiektu m ożna odtąd odnosić się na dwa sposoby: albo przez jego nazwę albo przez zorganizowanie wskaźnika, który na ten obiekt pokaże.
Mała dygresja o science-fiction K iedyś tłum aczyłem p racę na w skaźnikach m ojem u kilk u n asto letn iem u p rzy jacielow i, m iłośnikow i fantastyki naukow ej. K iedy doszliśm y d o zap isu
i p ró b o w ałem m u w y tłu m aczy ć,że g w iazdka oznacza... p rz e rw a ł m i i zdecydo w an ie p o w ied ział kończąc jakąkolw iek dyskusję: „T o proste: g w iazd k a oznacza, że lecimy g w iazd o lo tem d o m iejsca, na które p o k azu je w skaźnik w i d o p ie ro tam tym o biektem n ap raw d ę się zajm ujem y M ożesz to, d ro g i C zytelniku, u zn ać za dziecinne, ale takie m nem otechniczne skojarzenia b ard zo po m ag ają w początkow ej fazie rozum ienia. D ru g im skojarzeniem m ojego przyjaciela było, ż e je d n o arg u m en to w y operator & - (znaczek ten po an g ielsk u zw an y jest AM PERSAND) zaczy n a się na literę a - tak sam o jak słow o ADRES. W su m ie m iał on w ięc tak ie regułki: iimitTitiWiiwrun-ttri—rnrr — —.■.*.... gw ia zd olo te m w miejsce, na które pokazuje w a, jak adres obiektu m mi miwjcwt.rrttw
- m
r r !mmmtmmm&tKmmmmmmmmmmmmmmmmemm
P rzy p o m in am , że „g w iaz d o lo t" w ystępuje ty lk o w w y rażen iach . Zupełnym w y jątk iem jest g w iazd k a w linijce definicji w sk aźn ik a. T am m a o n a przypom i nać sposób, w jaki w sk a źn ik a się będzie p o tem u żyw ało.
8.4
L -w a rto ś ć O peracja odniesienia się d o dan eg o obiektu m o że być po praw ej stro n ie znaku (przy p isan ia) a = *wskaznik;
ew en tu a ln ie po jego lewej stronie *wskaznik = 55;
Rozdział. 8. W skaźniki O perator rzutow ania r e i n t e r p r e t _ c a s t , a wskaźniki
261
Jeśli coś m o że stać po lewej stro n ie zn a k u = (m ów iąc m ąd rzej - po lewej stronie operato ra p rzy p isan ia), to takie „co ś" nazyw am y l-w artością. N azw a łatw a d o zap am iętan ia, bo / - jak lewa strona. Po angielsku n azy w a się to l-valutr, a m ów ię o tym d la teg o , że jeśli kiedyś n ap iszesz przez p o m y łk ę instrukcję
II10 is not a l-value !
10 = i ;
To k o m p ilato r zaprotestuje i p o w ie Ci (po an gielsku), że instrukcja ta jest błędna, jako ż e stojące po lewej stro n ie 10 nie jest sły n n ą l-v a lu e . Czyli, ż e liczba 10 nie n ad aje się d o postaw ienia p o lewej stronie. Po p raw ej tak, ale nie p o lewej i = 10;
/ I j e s t p o p ra w n e
A nalogicznie: w yrażenie, k tóre m oże stać po p raw ej stronie n azy w am y ih w artością (o d angielskiego right - praw y). Jednak po praw ej stronie, to stać m oże juz b y le kto, więc być r-w arto ścią to żad en honor. L -w arto ść - to jest coś w yjątkow ego. O czyw iście l-w a rto ść jest także r-w arto ścią. A le n ie o d w rotnie.
Dlaczego to takie ważne, że wyrażenie *wskazni k
jest l-wartością ? D latego, że m a m y w yrażeniem * w s k a ź n ik po słu g iw ać się w takich sam ych sytuacjach jak w yrażeniem z m ie n n a , które tą l-w arto ścią jest. Z atem jeśli w s k a ź n ik ró w n o w ażn e
pokazuje na z m ie n n a , to poniższe zap isy są
II jako l-wartości--------------------------------------------zmienna = 6;
*wskaźnik = 6;
int m;
// jako r-wartości-----------------------------------------------m = zmienna;
k5
m = ‘wskaźnik;
O p erato r rzu to w a n ia reinterpret_cast, a w skaźniki M ów iliśm y n ied aw n o , że: 1 W skaźnik służący do pokazyw ania na obiekty jednego ty p u nie I n a d a je się do pokazyw ania na obiekty innego typu. W yobraź so b ie taką sytuację: m am y następujące trzy w skaźniki int
*wsk_intl, *wsk_int2; double *wsk double;
3)
[czytaj: „el-welju"].
262
Rozdz. 8. W skaźniki O perator rzutow ania r e i n t e r p r e t _ c a s t , a wskaźniki
W tra k c ie p r o g r a m u r o b iliś m y r ó ż n e r z e c z y , n a p r z y k ła d ta k ą o p e ra c ję : wsk_intl
= wsk_int2;
c o o z n a c z a : „ a te r a z n ie c h w s k a ź n i k w s k i n t l w s k a ź n ik w s k _ i n t 2 " .
p o k a z u j e n a to s a m o , c<
N ie m a p r o b l e m u - ta k ie o p e r a c je s ą o c z y w is te . W s k a ź n i k d o i n t m o ż i p o k a z y w a ć n a j p i e r w n a je d e n o b i e k t ty p u int, a p o te m n a in n y o b ie k t ty p i in t. J e d n a k o p e r a c ja wskdouble
= wsk_int2;
//b łą d
b ę d z i e p r z e z k o m p i l a t o r s y g n a l i z o w a n a ja k o b łą d . O p e ra c ja ta o z n a c z a : „ a te ra z n ie c h w s k a ź n i k ws k _ d o u b l e p o k a z u j e n a to s a m e c o w s k a ź n ik w s k i n 1 2 ". 1• • I K o m p ila to r z a p r o t e s tu je : — J a k to , w s k a ź n ik ie m d o p o k a z y w a n i a n a o b i e k t y ty p u d o uble c h c e s z p o k a z a ć n a to s a m o , n a c o p o k a z u je w s k a ź n i k w s k _ i n t 2 c z y l i n a o b ie k t ty p u i n t ? C h y b a s ię p o m y liłe ś ? W ta k im m ie js c u p a m ię c i , g d z i e je s t n a p r a w d ę o b ie k t ty p u i n t , n ie je s t d o b r z e p r a c o w a ć u d a ją c , ż e je s t t a m o b ie k t ty p u double. W z a s a d z ie k o m p i l a t o r m a rację. M o ż e m y o c z y w iś c i e s o b ie ta k w s k a ź n i k i u s ta w ić , a le w t e d y tr z e b a w y r a ź n i k o m p i la t o r o w i d a ć d o z r o z u m ie n i a , ż e n ie r o b im y te g o p r z e z p o m y łk ę , ty lk e ś w i a d o m ie . P o s ł u ż y ć s ię m o ż e m y w t y m c e lu o p e r a c ją r z u t o w a n i a . P am iętasz może, kiedyś m ów iłem (§ 4.8, str. 114), że rzutow anie m ożni robić w sta rym sty lu , z n a n y m jeszcze z języka C, a m o żn a też nowocześnie u żyw a ją c now ych operatorów rzutow ania. P rzy zw y c za ja jm y się na razu do w id o ku obu fo rm ! Potem z e starej fo rm y z re zy g n u je m y . O to ta k ie r z u t o w a n i e w in s tr u k c ji p r z y p i s a n i a w s k a ź n i k ó w . wsk_double = reinterpret_cast
*> (w s k _ i n t 1) ;
w sk
//n c r w y s ty l
// s t a r y s t y l
O p e r a c ję ta k ą m o ż n a p r z e t łu m a c z y ć n a lu d z k i ję z y k ja k o : — W i e m , k o m p ila to r z e , ż e w s k _ i n t l je s t w s k a ź n i k i e m ( d o ) i n n e g o t y p u , a le p o t r a k t u j g o ja k o w s k a ź n ik ( d o ) ty p u d o u b l e i m i m o w s z y s t k o u s t a w w s k a ź n i k w s k d o u b l e n a to s a m o , n a cc p o k a z u j e w ła ś n ie w s k int 1. D o te g o j e d n a k m u s i b y ć r z u t o w a n i e . B ez r z u to w a n i a k o m p i l a t o r u z n a j e t< p rz y p is a n ie z a b łę d n e . O o p e r a t o r z e t y m ju ż w s t ę p n i e r o z m a w i a l i ś m y w § 4 .8 .6 , s tr . 1 2 1 , a le tu n a d s z e d c z a s , g d z i e m o ż e m y z o b a c z y ć g o w s y tu a c ji, d o k tó r e j z o s t a ł w y m y ś lo n y :
Rozdział. 8. W skaźniki O perator rzutow ania r e i n t e r p r e t _ c a s t , a wskaźniki
263
O perator r e i n t e r p r e t _ c a s t służy do konwersji m iędzy typami, m iędzy którymi konw ersja nie może za jść niejawnie. I to nie dow olnym typami: został on w ym yślo n y dla w skaźników. O perator ten pozwala na konw ersję jednego typu w skaźnika na inny typ wskaźnika.
Z ałóżm y, że m a m y d w a ty p y X o raz Y 4 X ob jx ; //d e fin ic ja o b ie k tu ty p u X X *wsk_x = &objx; //def. w s k a ź n ik a i z a p a m ię ta n ie w n im a d r e s u objx Y *w s k_y; //def. w s k a ź n ika d o ty p u Y //------------------p ró b y p r z y p is a n ia a d re su d o in n e g o t y p u w s k a ź n ik a wsk y - wsk x; wsk ~ y = reinterpret_cast (wsk_x) ;
/ / < - n ie d o z w o lo n e !!!
// <- p o p r a w n e
D zięki zasto so w a n iu o p erato ra r e i n t e r p r e t e a s t m o żliw e było p rzep isa nie ad resu p rzec h o w y w an eg o w e w sk _ x do obiektu w sk _ y .
Naprawdę chcesz tego...? U w aga: M ó w im y tu o tym , jak zam ien ić w sk aźn ik je d n eg o ty p u na w skaźnik innego ty p u . N ie chciałbym , byś z ro z u m ia ł to jako z ac h ętę d o takich zam ian. Najczęściej ta k ie zam ian y nie m ają sensu.
Wyobraź sobie, że masz wskaźnik do snopowiązałek pokazujący na jakąś konkretną snopowiązałkę rdzewiejącą w jakiejś stodole. Tymczasem chcesz adres tej snopowiązałki wstawić do wskaźnika do budynków opery, fak często w życiu zdarza Ci się sytuacja, gdy mówisz k o m u ś : M a s z tu na kartce adres snopowiązałki, ale potraktuj to miejsce tak, jakby tam stał budynek Opery " ? Z atem ten p a ra g ra f m ów i raczej o ty m , że co p ra w d a , k o m p ilato r słusznie nie pozw ala na ta k ie bezsensow ne konw ersje, ale jeśli n a p ra w d ę chcesz takiej karkołom nej z a m ia n y dokonać (bo w iesz, ż e w d an y m szczeg ó ln y m p rzy p ad k u m a o n a sens), to jest d o tego specjalny operator. Są jednak sytuacje, g d y sp raw a w y d aje się zaw sze oczyw ista:
Zamiana adresu na liczbę całkowitą (i odwrotnie) O p erato r r e i n t e r p r e t _ c a s t d o d atk o w o ... 4)
Dla wtajemnicznonych: ...a te typy X i Y nie mają ze sobą nic wspólnego, czyli niepowiązane są związkami dziedziczenia.
264
Rozdz. 8. W skaźniki W skaźniki typu v o i d
W n aszy m p ro g ra m ie definiujem y w ięc sobie w skaźnik i ustaw iam y go, by p o k azy w ał na p o d a n y nam liczbow o adres: int *wsk_sygnalu; int numeryczny_adres = 0x0f6a2fl; int schowek; //u s ta w ie n ie w s k a ź n ik a p rze z za ła d o w a n ie d o n ieg o adresu będącego liczb ą całkow itą wsk sygnału = reinterpret_cast (numeryczny_adres); //O d c z y ta n ie a d r e s u p rze c h o w y w a n e g o w e w sk a ź n ik u cout << "Wskaźnik pokazuje na komorke pamięci numer: " « reinterpret_cast(wsk_sygnalu) « endl;
W P rz y p o m in a m :
O p erato r r e i n t e r p r e t _ c a s t postępuje ze w sk aźn ik am i tak, jak to mogły robić d o tej po ry tc tradycyjne sposoby rzu to w an ia, p o c h o d z ą c e jeszcze z języka C. Takie staro m o d n e rzutow anie ma postać na przykład: wsk_sygnalu =
(int*) numeryczny_adres;
Jest w ięc ten o p e ra to r r e i n t e r p r e t _ c a s t t a k sam o n ieb ezp ieczn y , jak te star< sposoby. Jest jed n ak różnica - łatwiej g o znaleźć w program ie. Jeśli program źl< działa, a p o d ejrzew am y , że to z p o w o d u zbyt śm iałych k o n w ersji - w ystarcz] p o szu k ać ed y to rem w tekście p ro g ram u słów " r e i n t e r p r e t _ c a s t " . Jeśli jesteś początkującym program istą, najlepiej tego o p erato ra nie używaj.
8.6
Wskaźniki typu void W skaźnik - jak w ielokrotnie już m ów iliśm y - to adres jakiegoś miejsca w parnię ci, p lus w ied za o ty m , jakiego typu o b iek t pokazuje się. Czyli z definicji: int *wsk;
Rozdział. 8. W skaźniki W skaźniki typu v o i d
265
w y n ik a, ż e ws k to w sk aźn ik , k tó ry m m o żn a p rz e c h o w a ć a d re s jakiegoś o b iektu, a ta w ie d z a , to p ew n o ść, że to a d re s obiektu ty p u int. M ożem y je d n a k zd efin io w ać w sk a źn ik bez tej „ w ie d z y " . M ó w im y w ted y , że jest to w s k a ź n ik ty p u v o i d .
•
void *wsk_v;
„Wiedza" ta - przypominam - służy po to, by można było poprawnie wskazywane miejsce zinterpretować (rozkodować jako obiekt typu i n t , d o u b l e itd). oraz po to, by móc w poprawny sposób wskaźnikiem poruszać po ewentualnych sąsiednich obiektach - gdy mamy je zebrane w tablicę. T eraz m a m y w sk a ź n ik ty p u voi d. Jasn e jest, że sk o ro z tej „ w ie d z y " św iad o m ie rezy g n u jem y , to a u to m aty c zn ie n asz w sk aźn ik n ie m o ż e słu ży ć d o o d czy tan ia tego m iejsca, n a k tó re pokazuje. N ie m o żn a też n im p o ru s z a ć po sąsiadach.
Pytanie: Po co nam wobec tego taki upośledzony wskaźnik? Po to, że c z a sa m i ta w ied za staje się n iep o trzeb n y m b alastem . Ten balast odczuwa się szczególnie w starszych funkcjach bibliotecznych, do których trzeba wysłać adres jakiegoś obiektu w pamięci. Funkcję taką nie interesuje co tam jest, a jedynie gdzie to jest. Z araz w y jaśn ię, n ajp ierw jed n ak m ałe p rzy p o m n ien ie. M ó w iliśm y n ie d aw n o , że aby u s ta w ić w sk a źn ik jednego ty p u , na to sam o m iejsce pam ięci, na k tó re pokazu je w sk a ź n ik innego ty p u , konieczne jest rz u to w a n ie . Bez rzu to w a n ia k o m p ilato r u z n a je to p rzy p isan ie z a błędne. A teraz u w ażaj: Jeśli w sk aźn ik stojący po lewej stro n ie naszeg o p rzy p isan ia (p o d staw ien ia) b y łby typu v o i d , to m im o braku rz u to w a n ia k o m pilator by nie zap ro testo w ał. void *wsk_void; int *wsk int;
II...
~
wsk_void = wsk int;
_ . / / <- przypisanie, które nie wymaga rzutowania
Z apis ten o zn acza: niech w sk aźn ik ty p u void p o k azu je na to sam o miejsce w pam ięci, n a k tó re w łaśn ie p o k azu je w sk aźn ik typu int. M ożem y w ię c w y k o n y w a ć tak ie operacje: // definicje kilku wskaźników void *wv; l/typuvoid char *wc; int *wi; double *wd;
// tutaj w programie ustawia się te wskaźniki na jakieś obiekty
/ j a teraz przypisania do wskaźnika typu void (bez konieczności rzutowania) wv - wd; wv = wc; wv = w i ;
266
Rozdz. 8. W skaźniki W skaźniki typu v o i d v o id * w v _ d ru g i; wv d r u g i = wv;
1/
Tu nieco wybiegnę w przyszłość: Jest jeden warunek: nie można wskaźnikowi typu v o i d przypisać treść wskaźnika do obiektu z przydomkiem c o n s t. To oczywiście dlatego, by zapobiec oszustwom. Po takim przypisaniu można by bezkarnie zmienić obiekt, który miał być przecież c o n s t. Wobec pow yższego sformułujmy wniosek: *• ° . . . . • ' • •• W skaźnik każdego (niestałego) typu^ m ożna przypisać w skaźnikow i typu
vc id
# w skaźnik void <---------dowolny w skaźnik (non—const),
mąpiPWNMWiiwiin iiwwMfwwaamitWM n w m w w n
i«w»tti
-
W trakcie takiego przypisywania przekazywany jest adres, a „w iedza zapominana - i to jest w porządku.
jest
N atom iast nie da się odwrotnie. To znaczy: w skaźnika typu void nie m ożna przypisać w skaźn iko w i „praw dziwem u". Trzeba posłużyć się operatorem rzutowania. dowolny w skaźnik <-------- rzutowanie ------ w skaźn ik v o i d
Dla powyżej zdefiniowanych wskaźników wygląda to tak: // stary styl wd = (double *) wv; wi = (int *) wv; wc = (char *) wv;
// nowy styl wd = r e i n t e r p r e t _ c a s t < d o u b l e * > (w v ); wi = r e i n t e r p r e t _ c a s t < i n t * > (w v ); wc = r e i n t e r p r e t _ c a s t < c h a r * > (w v );
Jak widać, konieczny jest tu operator rzutowania, bo następuje przypisanie double*
in t* c h a r*
v o id* v o id* v o id *
Reasumując: W iedzę o typie obiektu pokazyw anego można ewentualnie nieza uważenie zapomnieć. Nabyć tej w iedzy niezauw ażenie niemożna Trzeba to świadom ie wyspecyfikować operacją rzutowania. Uwaga dla programistów C 5)
Dla wtajemniczonych: do wskaźnika v o id * nie można przypisać żadnego wskaźnika d< funkcji ani wskaźnika do składnika klasy.
Rozdział. 8. W skaźniki Cztery domeny zastosowania wskaźników
267
Jest to jedna z różnic między C++, a AN SI C. W AN SI C było tak, że niezależnie, po której stronie operatora przypisania stał wskaźnik typu v o id n ie trzeba było używać rzutowania. Zatem „wiedzę" nabywało się także niezauważenie. Było to jakby wyjście awaryjne dla umożliwienia łatwiejszego zapisu instrukcji rezerwujących pamięć. (Funkcjami w rodza ju m a l l o c () - metnory allocation). Ponieważ w C++ do tego celu służą dziecinne proste operatory new i d e l e t e , dlatego regułę można zaostrzyć.
Możemy teraz wrócić do pytania: D laczego w y m y ślo n o taki u p o śled zo n y w skaźnik? D latego, że czasam i w iedza o ty p ie pokazyw anego ob iek tu , była n iepotrzeb nym balastem . Ten balast o d czu w ało się szczególnie w starszych funkcjach bibliotecznych, do których trzeba w y słać (lub odebrać) a d re s jakiegoś obiektu w pam ięci. Funkcję taką nie interesuje co tam jest, a jed y n ie g d zie to jest. O dbiera ona (lub zw raca) ten adres za pom ocą w skaźnika void* i już. Stara funkcja malloc () d aw ała n am adres zarezerw o w an eg o miejsca w pam ięci jako w sk aźn ik void*, a m y już m usieliśm y zatroszczyć się o resztę. IW W W I—
.7
W IIIM IIH IM IM W W M lU IK U lllW
m m
IMWIIM— WHriOT
C zte ry d o m e n y z a s to s o w a n ia w s k a źn ik ó w W skaźniki sto su je się w sytuacjach, g d y chodzi nam o: ♦> - u le p sze n ie pracy z tablicam i, ♦♦♦ - funkcje m ogące zm ieniać w artość przysyłanych d o nich argum entów , - d o stę p do specjalnych k o m ó rek pamięci, «$► - rezerw ację obszarów pam ięci. W dalszej części tego rozdziału przyjrzym y się p rzy k ład o m z tych czterech d zied zin zastosow ań.
.8
.8.1
Z a s to s o w a n ie w s k a źn ik ó w w obec tab lic
Ćwiczenia z mechaniki ruchu w skaźnika Jeśli m am y następujące definicje: int *wskaznik; int tablica [10];
U d e fin ic ja w sk a źn ik a / jd e fin ic ja tablicy
to instrukcja wskaźnik = & tablica [w];
//u sta w ien ie w sk a źn ik a
pow oduje, że w skaźnik ustaw ia się na elemencie tablicy o indeksie n. W skaźnik nasz jest, jak w iadom o, w skaźnikiem do pokazyw ania na obiekty typu i n t .
268
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic E lem enty naszej tablicy są w łaśn ie typu i n t , w ięc w sz y stk o się zg ad za. W na szej o statn iej instrukcji po p ro stu w staw iam y d o w sk a ź n ik a ad res n -te g o e em entu tablicy. Instrukcja wskaźnik = &tablica[0];
jest u sta w ie n ie m w skaźnika n a zerow ym elem encie, czy li na p o czątk u tablicy. P o n iew aż (już kiedyś m ów iliśm y o tym, a Ty o b iecałeś p o w iesić s° ble *-° nad b iu rk iem ) - n azw a tablicy je st rów nocześnie a d re s e m jej p o czą tk u , d lateg ró w n ie d o b rz e m oglibyśm y tę ostatnią instrukcję n a p is a ć tak: wskaźnik = tablica;
A te ra z u w ażaj, będzie coś b a rd z o ciekawego: Jeśli u s ta w iliś m y w sk a źn ik na ż ą d a n y ele m e n t tablicy, np. tak: wskaźnik = &tablica(4];
to, ab y g o p o te m p rzesu n ąć tak, by pokazyw ał n a n a s tę p n y elem en t tej tablicy w y starczy d o niego d o d ać 1 wskaźnik = wskaźnik + 1;
czyli krócej wskaznik++;
To jest w ła ś n ie ta prostota p rzesu n ię cia w ro zk ład zie ja z d y palca o jedną kratkę, by o d c z y ta ć n a stę p n y pociąg. A b y w sk aźn ik p rz e s u n ą ć o n elem en tó w w y sta r czy in stru k cja wskaźnik +- n;
// inaczej: w s k a ź n ik = w s k a ź n i k + n ;
Jest to b a r d z o w ażn y fakt:
Dodanie do wskaźnika jakiejś liczby całkowitej powoduje, że pokazuje on tyleż elementów tablicy dalej. Niezależnie od tego, jakie są te ^ m e nty^ Im
m
N ie jest to ta k ie tryw ialne, bo p rzec ież m o g ą być ta b lic e ty p u d o u b l e , k tó ry ch e le m e n ty z a j m u j pam ięci w ię k sz a p rzestrz eń n iż n p . ty p u i n t . A jed n ak w sk a ź n ik jest n a tyle in telig en tn y , że w ie jak się m a p rz e s u n ą ć , ab y przeskoczyć o z a d a n ą liczb ę elem entów . Skąd to w ie? Z e swojej w łasnej definicji! Jest p rzec ież w sk a ź n ik ie m d o p o k a z y w a n ia n a o b ie k ty jakiegoś w y b ra n e g o typu. W ie d z ą c , jak im typem m a si« z ajm o w ać - z n a ro zm iar je d n e g o elem en tu i m oże p rz y ją ć na to p o p raw k ę.
Zobaczmy teraz na przykładzie, jak sprytne jest przesuwanie wskaźników P o n iż sz y p ro g ra m słu ży d o w y d ru k u ad re su , na k tó r y p o k azu ją w sk a źn ik i In tere su je n a s tutaj tylko a d re s , na k tó ry w sk a ź n ik p o k a z u je . C h w ilo w o ni za jm u je m y się tym , co p o d o w y m a d resem się kryje.
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic
269
# in c lu d e < io s tre a m > u s in g n a m e sp a c e s t d ; ^/★ ★ ★ ****-********************************* + *+ ***************** i n t m a in ()
i n t * w i; d o u b le *wd;
// definicje dwóch tablic O // -jedna i n t II-druga d o u b le //dum wskaźniki: © // - do pokazywania na obiekty i n t // - do pokazywania na obiekty d o u b le
wi = &ti [0]; wd = &td[0];
// inaczej: wi = t i ; // inaczej: wd = t d ;
int t i [6] double td[6]
c o u t << << fo r(in t { cout
© ©
"O to , ja k p rz y i n k r e m e n ta c ji w sk a zn ik o w \n " z m ie n ia ją s i e u k r y t e w n ic h a d r e s y : \ n " ; i = 0 ; i < 6 ; i+ + , wi++, wd++) << « « « <<
// ©
" i= "<< i ") wi = " r e i n t e r p r e t c a s tc u n s ig n e d in t > ( w i)
// ©
r e i n t e r p r e t _ c a s t < u n s i g n e d in t> (w d ) << e n d l;
n
O
Po uruchomieniu takiego programu na ekranie pojawi się przykładowo taki tekst O to , j a k p r z y in k r e m e n ta c ji w skaźników z m i e n i a j ą s i e u k r y te w n ic h a d r e s y : 1244984 1245032, 0) wi 1244992 1245036, 1) wi 1245000 1245040, 2 ) wi 1245044, 1245008 3) wi 1245016 1245048, 4) wi 1245052, 1245024 i= 5) wi O D w ie definicje tablic. Tablica ti m a elem enty typu int. Tablica td m a elem enty ty p u double. © Definicje w sk aźn ik ó w . W skaźnik wi m oże p o k azy w ać na obiekty ty p u int, w skaźn ik wd m oże pokazyw ać na obiekty typu double. © i O N a d a n ie w arto ści początkow ych w skaźnikom . Po p ro stu d o każdego w sk a ź nika w sta w ia się adres obiektu, na który ma on p o k azy w ać. W skaźnik wi ma p o k azy w ać n a zerow y elem ent tablicy ti, n ato m iast w sk aźn ik wd m a p o k azy wać na z e ro w y obiekt tablicy t d . Skoro mają p o k azy w ać na początki tych tablic, to rów nie d o b rz e m ożna użyć zap isu , który p o d a n y jest w kom entarzu. © Pętla. N ie m a w niej nic niezw ykłego poza tym , że p o k ażd y m obiegu w y k o n y w ane są: wi++ , wd++; czyli p rzesu n ię cie w skaźników na n astęp n y elem ent tablic, na które pokazują.
270
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic © W y p isa n ie n a e k r a n a d re s u , na k tó ry w sk a źn ik wi p o k azu je. A dres to jakaj liczba. T o, jak a d r e s o w a n e są k o m ó rk i p am ięci w d a n y m k o m p u te rz e - zależ}) o d ty p u k o m p u te ra . M y tu taj, z p o m o c ą operacji rz u to w a n ia , chcem y ad res ten w y p isa ć n a e k r a n ie ta k , jak b y to b y ła w arto ść ty p u unsigned int. Takie rzutowanie może dla nas zrobić operator r e i n t e r p r e t _ c a s t. j
^
O W y p isa n ie te g o s a m e g o o w s k a ź n ik u wd ............ Z a u w a ż , ż e n a e k ra n ie liczby sy m b o lizu ją ce ad resy zm ien ia ją się z różnym sk o k iem , m im o ż e p rzec ież d o d a w a liś m y d o w sk aźn ik a ty lk o jedynki. W tyni w ła śn ie o b ja w ia się in telig en cja w sk a ź n ik a . W ie on jak n a p r a w d ę m a się z m ie nić, b y w s k a z a ć n a k o lejn y e le m e n t tab licy . S k ąd w s k a ź n ik w ie n a jaki ty p p o k a z u je ? S tąd, że p rzecież g o zd efin io w aliśm ) jako: „ w s k a ź n ik s łu ż ą c y d o p o k a z y w a n ia n a elem enty ty p u double" W miejscu wypisywania wskaźnika na ekranie widzisz cout << reinterpret_cast(wd);
Przypomnę, że oznacza to mniej więcej coś takiego: „co prawda wdjest H wskaźnik, czyli zawiera w zasadzie adres, ale przeczytajmy ten adres zamieńmy go na liczbę typu unsi gned in t" (i taką liczbę wydrukujmy na ekranie). P rzy jrzy jm y s ię p o n iż sz e m u o b razk o w i. (Liczby n ad ta b lic a m i oznaczają p r z y k ła d o w e a d r e s y p o szcze g ó ln y ch k o m ó re k pam ięci (b ajtó w ) zajm o w an y cł p rz e z e le m e n ty tablic. Z e w z g lę d ó w estety czn y ch w y b ra łe m b ard ziej o k rąg łt liczby, a n ie te, k tó re p o k a z a ł n a sz o sta tn i p ro g ram ). ___________ 1000 1001 1002 1003
r
:
:
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 .......
) (
:
i
:
wi i
■ 3 (.. •
| J !1
f
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016...
1( wd
'
!
: J
(— *—
:—
i—
i—
a—
)
(
.
t
R y s . 8-1. W sk a źn ik i p rzesu w ają się sto so w n ie do rozm iaru sw o ic h typów. W ska źn il sa m "w ie", ja k d u że są elem enty, na które p o kazu je. Zatem m ó w im y mu tylko: "-sk o ci .n a n astęp ny*elem entM- a w sk a źn ik sam popraw nie się p o zycjo n u je .
E lem en ty ta b licy ty p u double zajm u ją w ięcej m iejsca w p a m ię c i niż e lem en t ty p u int. J e d n a k ż e w sk a źn ik , k tó ry jest p rz e z n a c z o n y d o p o k a z y w a n ia ni d a n e ty p u int, w ie jak ma się z m ien ia ć p r z y p rz e c h o d z e n iu d o sąsiedm eg<
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic
271
e le m e n tu tablicy. P o d o b n ie w sk a ź n ik p rz e z n a c z o n y d o p o k a z y w a n ia na elem e nty ty p u double w ie, jak z a c h o w a ć się p rz y p rzejściu d o n astęp n e g o elem en tu tablicy double. D zięki te m u w y ra ż e n ie
(wd + 3) d aje w su m ie a d re s trzecieg o z kolei elem entu tablicy z a tym , na który w ła śn ie w sk a z u ję w sk a ź n ik wd, a w y ra ż e n ie
P rzy k ład : a d a) W y o b raź sobie b o w iem , że w sk a źn ik p o k azu je na jakiś b ajt w pam ięci2. Jeśli w sk a źn ik jest ty p u i n t , to w ie, ż e ten bajt i trzy n a stę p n e m a in terp reto w ać jako z a k o d o w a n ą liczbę ty p u i n t . Jeśli zaś na ten sam bajt pam ięci p o k a z y w a łb y w sk a źn ik ty p u double, to w ie d z ia łb y on, że ten bajt i sie d e m n astęp n y ch (razem osiem ) m a ro zu m ieć jako zak o d o w an ą liczb ę ty p u double. a d b) K o n ty n u u jąc p o w y ższy p rzy k ład : W p rz y p a d k u tablicy ty p u int, ab y p rzesu n ąć się do n astęp n eg o e lem en tu ty p u int - trzeb a p rzesk o czy ć o te cztery bajty. W p rz y p a d k u tablicy ty p u double trzeb a p rzesk o czy ć rzeczone 8 bajtów . W sk aźn ik i są tak w sp a n iale po m y ślan e, że nie m u sim y o tych sp raw ach w ie dzieć. D o d a n ie 1 d o w sk a źn ik a p rz e su w a go o w łaściw ą ilość bajtów i p o k azu je on n a n a stę p n y elem ent. Tak w ięc p am ięta m y o d tą d , że w y rażen ie (wi++) o zn ac za p rzesu n ięcie w sk aźn ik a d o n astępnego elem en tu tablicy. C o ciek aw sz e - je ś li w p rzyszłości b ęd ziem y sobie w ym yślali tablice z elem en tó w m ających p o 5)
W obu przypadkach sami musimy zadbać o legalność naszych poczynań. Jeśli wf pokazuje na początek tablicy, to wyrażenie (wf - 1) jest jakby adresem nieistniejącego elementu o indeksie: -1 (minus 1). O tych sprawach pomówimy za chwilę przy arytmetyce wskaźników. Zakładam tu, że nasz kompilator na obiekty typu int przeznacza cztery bajty, a na obiekty typu double - osiem. W przypadku Twojego kompilatora może być inaczej.
272
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic kilk aset bajtów , to i tak d o d an ie jedynki d o w skaźnika służącego d o pokazy^ w a n ia na tę tablicę p rzesu n ie popraw nie w sk aźn ik na następny e le m e n t. D ygresja:
Często mówiłem tutaj: wskaźnik „wie" - nadając wskaźnikowi jakąś osobo wość i wolność działania. Oczywiście to nie wskaźnik „wie", tylko kompil ator. Kompilator nasze wyrażenie wi++ zamienia na dodanie odpowiednie liczby do obecnie tkwiącego w wi adresu. Czyli to kompilator jest ta mądry. Jednak - wybacz mi tę słabość - lepiej odpowiada mi interpretacji nadająca pewne cechy osobowości wskaźnikom. Życie w świecie takich wyobrażeń, gdzie wskaźnik to stary znajomy, którego przyzwyczajenia zm się na wylot, uprzyjemnia programowanie.
8.8.2
U życie w skaźnika w pracy z tablicą S koro już w iem y, jaki jest m echanizm p rzesu w an ia w sk aźn ik ó w - zobaczm y jak korzystać z tego, na co w skaźnik bieżąco pokazuje. Z acznijm y od pro g ram u . Będzie to p ro g ram na proste operacje n a tablicach jed n ak w y k o n y w an e będ ą one za pom ocą w skaźników . #include using namespace std; //******************************«***************************** i n t main()
i
int *wi; double *wd; int tabint[10] = i d o u b le t a b d b l [ 1 0 ] ;
0
,
1,2
, 3 , 4 , 5 , 6 , 7 , 8 , 9 );
// ustawienie wskaźnika
wd = &tabdbl[0]; // załadowanie tablicy
d o u b l e wartościami początkowymi for(int i = 0 ; i < 10 ; i++)
II wpis wart. do tablicy double O
* * (Wd++) = i / 10.0; } cout «
"Tresc tablic na poczatku\n";
n
wi = tabint; wd = tabdbl; for(int k = 0 ; k < 10 ; k++) { cout « « wi++;
k « ") \t" « *wd << endl;
wd++; II nowe ustawienie wskaźników-----
*wi «
"\t\t\t\t"
©
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic wi = &tabint[5]; wd = tabdbl + 2;
273
// © U c zy li wd = &tabdbl [2];
©
//wpisanie do tablic kilku nowych wartościfor(int m = 0 ; m < 4 ; m++) // ©
*(wi++) = -222; * (wd++) = -777.5; cout << "Tresc tablic po wstawieniu nowych wartosci\n"; wi = tabint; wd = tabdbl; for(int p = 0 ; p < 10 ; p++) cout << << « << <<
"tabint[" << p << "] = " * (wi++) " \t\ttabdbl[" « p « "] = *(wd++) endl;
"
) i
Po wykonaniu tego programu na ekranie pojawi się: Tresc tablic na początku 0 0} 0.1 1! 0. 2 2) 0.3 3) 0.4 4) 0.5 ' 5) 0.6 6) 0.7 7) 0.8 8) 0.9 9) Tresc tablic po wstawieniu nowych wartości t a b d b l 10] = 0 tabint[0] = 0 t a b d b l [1] = 0.1 tabint[1] = 1 t a b d b l [2] = - 7 7 7 . 5 tabint[2] = 2 t a b d b l [3] = - 7 7 7 .5 tabint[3] = 3 t a b d b l [4] = - 777.5 tabint[4] = 4 t a b d b l [5] = - 7 77 .5 tabint[5] = -222 t a b d b l [6] = 0.6 tabint[6] - -222 t a b d b l [7] = 0.7 222 tabint[7] = t a b d b l [8] = 0.8 tabint[8] = -222 t a b d b l [9] = 0. 9 tabint[9] = 9
Oto ciekawsze szczegóły programu: O Definicja tablicy typu i n t . O d razu następuje inicjalizacja (zbiorcza). © Definicja tablicy typu
double. N ie inicjalizujemy.
© U staw ienie w skaźnika na początkow y elem ent tablicy tabdbl. © Po u staw ien iu w skaźnika posługujem y się nim w p ętli wpisującej d o tablicy t a b d b l w artości początkow e. W pisanie dzieje się z a spraw ą instrukcji
274
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic * (wd++) = i / 10.0;
c o o z n a c z a in a czej * w d = i / 10.0;
wd++;
O d b y w a się w ię c tu ta j w p is a n ie lic z b y d o e le m e n tu ta b lic y p o k a z y w a n e g o w ła ś n ie p r z e z w s k a ź n ik wd, a n a s tę p n ie p r z e s u n ię c ie te g o w s k a ź n ik a n a ele m e n t n a s tę p n y . © Z a r a z b ę d z ie p ę tla w y p is u ją c a n a e k r a n tre ś ć tab lic. P o n ie w a ż c h c e m y to zrobi< m e to d ą p r z e s u w a n ia w s k a ź n ik ó w , d la te g o tu ta j u s ta w ia m y je n a p o c z ą tk a c h ta b lic. Z a s to s o w a liś m y tu d la o d m ia n y in n y s p o s ó b u s ta w ia n ia w sk a źn ik a wi = tabint;
S p o s ó b te n m o ż e n ie je s t ta k o d ra z u ja s n y , a le (p a m ię ta s z ? ) u m ó w il iś m y się, że p r z y k le is z s o b ie n a d b iu r k ie m k a rtk ę z n a p is e m :
T o tłu m a c z y w s z y s tk o . P o p ro s tu w y r a ż e n ia ( t a b i n t ) o ra z ( s t a b i n t [ 0 ] ) s ą ró w n o w a ż n e . © W y p is a n ie n a e k r a n e le m e n tu p o k a z y w a n e g o p r z e z w s k a ź n ik to - jak w ie m y in s tru k c ja ty p u cout << *wi; © W tej lin ijce w id z i m y p r z e s u n ię c ie w s k a ź n ik a n a n a s tę p n y e l e m e n t ta b lic y M o g liś m y to z ro b ić ta k ż e z w ię ź le j w p o p r z e d n ie j lin ijc e o p e r a t o r e m p o s tin k re m e n ta c ji z a p is u ją c cout << *(wi++); © Z a m ie r z a m y w w y b r a n e m iejsca ta b lic w p is a ć n o w e w a rto ś c i. S k o ro c h ce m y u ż y ć d o te g o ce lu s z y b k ie g o s p o s o b u z a p o m o c ą w s k a ź n ik a , d la te g o m u s im y n a jp ie r w u s ta w ić s o b ie w s k a ź n ik . W p r z y p a d k u t a b i n t u s t a w i a m y w s k a ź n ik n a e le m e n c ie t a b i n t [ 5 ] . © N a to m ia s t w p r z y p a d k u ta b lic y t a b d b l z a c z ą ć się to m a o d e l e m e n t u o in d e k sie 2. Z a s to s o w a liś m y tu in n y z a p is , c a ły c z a s p a m ię ta ją c , ż e p r z e c ie ż wd = tabdbl + 2; to to s a m o co: wd
= &tabdbl[2];
© W p is a n ie w te m ie js c a p a m ię c i, g d z ie p o k a z u ją w s k a ź n ik i. P r z y w p is a n iu od r a z u d o k o n u je m y p o s tin k r e m e n ta c ji w s k a ź n ik ó w . W p is u je m y , ja k w id a ć , d c k o le jn y c h c z te re c h e le m e n tó w tab lic. N a s tę p n e lin ijk i p r o g r a m u to p o p ro s tu p o k a z a n i e e fe k tó w p r a c y n a e k ra n ie .
W
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic
275
W id zisz w ięc, że nie m a nic specjalnie tru d n e g o w p o słu g iw an iu się w sk a ź nik am i. Z a p y ta sz p ew nie: „ - A w łaściw ie, p o co to w szystko, sk o ro d a się to zrobić s ta ry m sp o so b em p o słu g u ją c się tab licam i?" M asz rację. M ó w iłem p rzecięż, że b e z w sk a źn ik ó w m o ż n a żyć. Jest jed n ak z a leta . P o słu żen ie się w sk a ź n i k iem d a sz y b sz y p ro g ram . P am iętasz n aszą p rz y p o w ie ść o ro z k ła d z ie jazd y ? N a k a ż d e p y ta n ie „ -A n a s tę p n y ? " o d p o w ia d a liś m y instrukcją wi++; i już b y liśm y p rz y n a stę p n y m elem en cie. Będąc p rz y elem en cie tabint [ 5 ] je d n y m sk o k iem z n a jd o w a liśm y się p r z y elem encie tabint [ 6 ] , bez ż m u d n e g o licze n ia a d re s u . Liczenie a d re su p rzec ież chw ilę trw a. N a z w a tablicy, a w s k a ź n ik
A te ra z w ró ć m y jeszcze d o sp ra w y o m aw ian ej w p u n k cie €). S koro d w ie p o n iż sz e instrukcje u staw iające w sk aźn ik na p o c z ą tk u tablicy są ró w n o w a ż n e . wskaźnik = &tablica[0); wskaźnik = tablica; czyli, ż e w sk a ź n ik o w i m o ż n a p rzy p isać n azw ę tab licy , to w łaściw ie z ac h o w u je się o n a ta k , jak w sk aźn ik . Isto tn ie - są o g ro m n e p o d o b ie ń stw a. Z apis tablica + 4 o z n a c z a to sam o , co {■tablica [4 ] P ytanie: C zy zatem za m ia st naszej złotej regułki: I N azw a tablicy jest rów nocześnie a d re s e m jej zerow ego elem en tu n ie p o w in n iśm y zastąp ić reg u łk ą: N azw a tablicy je st rów nocześnie w s k a ź n ik ie m do jej ze ro w e g o elem entu T ak, m o ż e m y to zrobić p o d w aru n k iem , że b ę d z ie m y pam iętać, iż to n ie zw y k ły w sk a ź n ik , ty lk o taki, k tó reg o n ig d y nie b ęd ziem y p rzesu w ać (czyli: const). P o k a ż m y tę różnicę jaśniej. P o instrukcji wskaźnik = tablica; wskaźnik p o k azu je na p o czą tek tablicy. O ile m o ż em y postąpić tak: wskaznik++; czyli p rz e s u n ą ć w sk aźn ik tak , b y p o k azy w ał n a elem en t n astęp n y , o tyle o p eracja tablica++;
/ / błąd !!!
276
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic M im o tej - b a rd z o m ądrej - konkluzji n ie radzę napisanej p rzed tem złote regułki zm ieniać i zastępow ać now ą. Pojęcie „ad res" p rzeraża m niej m z pojęcie „stały w sk aźn ik ". Przynajm niej na p o czą tk u . O to dalsze konsekw encje. Skoro n azw a tablicy to w łaściw ie w skaźnik, wię< p o n iższe w y rażen ia są ró w n o w ażn e tablica[3] * ( ta b lic a + 3 ) W artością obu jest treść tego elem entu tablicy, który m a in d e k s 3 N ie posądzaj m n ie też, że w d ru g im z tych w yrażeń dokonuje się zabronionego p rzesu w an ia w sk aźn ik a - n azw y tablicy. D odanie +3 nie je st przesunięciem , tylko p o w edzeniem , że chodzi nam o trzy pozycje dalej niż o n teraz pokazuje. ' O to ilustracja: G d y znajom i pytają mnie, w k tó ry m miejscu na długiej ulicy Bulów Strafie m ieszkam , w ów czas m ów ię, że tam, ed z ie chińska restauracja, ty lk o trzy dom y dalej. W skaźnikiem jest tutaj „chińska restauracja". „T rz y dom y dalej" - to operacja, którą p rzep ro w ad za m y w m yśli, b ez rujnow ania chińskiej restauracji. Inn ą różnicą m ięd zy w skaźnikiem , a n a z w ą tablicy jest to, ż e w skaźnik jest jakim ś obiektem w pam ięci, w ięc m ożna ustalić jego w łasny ad res. N ie tego, na co ten w skaźnik pokazuje, ale to, g d zie ten w skaźnik sam się m ieści. Robi się h j stary m sposobem , jeśli m am y w skaźnik: i n t ‘ w sk a ź n ik ;
// definicja wskaźnika
to jego ad res jest w artością w yrażenia: sw sk az n ik N ato m iast nie m o żn a takiej operacji p rzep ro w ad zić w sto su n k u do nazwy tablicy.
0 $
N azw a nie jest obiektem . Tablica tak, ale nazw a nie. To też dobrze zapam iętać la m am na imię Jurek. Sam jestem obiektem (m aterialnym ), n ato m iast moje imi<= obiektem nie jest. Gęśli ktoś na m nie p o k a ż e patykiem , to te n p aty k -w sk azm k jest obiektem m aterialnym ).
8.8.3
Arytmetyka w skaźników M ów iliśm y już, że m ożna d o d aw ać i odejm ow ać liczby całk o w ite do wskażników i w ten sposób p rzesu w ać te w sk aźn ik i po w sk azy w an ej tablicy. Jest tu jed n ak p o d o b n a sytuacja, jak z od n o szen iem się d o nich za pomocą konw encjonalnego - „tablicow ego" zap isu : | N ie jest sp ra w d z a n a legalność takiej operacji. To znaczy: jeśli tablica ma tylko 10 elem entów , a my w sk a ź n ik aktualnie pokazujący na elem en t t a b l i c a [5] p rzesu n iem y o 80 e lem en tó w dalej, ta w sk aźn ik będzie p o k azy w ał na nieistniejący elem ent t a b l i c a [ 8 5 ] . M ożem y to zin terp reto w ać tak: będzie o n p o k azy w ał na takie m iejsce w pam ięci, które
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic
277
z a jm o w a łb y elem en t t a b l i c a [ 8 5 ] , g d y b y śm y ty lk o zd efin io w ali b y li tak d u ż ą tablicę. P o n iew aż je d n a k tego nie zro b iliśm y - w miejscu ty m są z u p e łn ie in n e d a n e . Jeśli m im o w sz y stk o sp ró b u jem y p rz e c z y ta ć to m iejsce w sk a z y w a n e p rz e z w sk a źn ik , o trz y m a m y b ezsen so w n y w y n ik , n ato m iast p ró b a za p isa n ia tam cz e g o ś - zn iszczy istniejącą tam legalnie in n ą d a n ą . O b łę d z ie tak im nie o strz e ż e n as kom pilator. L eg aln o ść p o k azy w an ia w sk a ź n i kiem n ie jest b o w iem s p ra w d z a n a . N a d o d a te k , n iek o n ieczn ie od ra z u po z n isz c z e n iu , p ro g ram m o że w y k azać b łęd n e d z ia ła n ie . D opóki nie k o rzy stam y z tej zn iszczo n ej d an ej - d o tą d w szy stk o w y d a je się w p o rz ą d k u . T akie b łę d y są n ajtru d n iejsz e d o znalezienia, b o w ie m objaw iają się w p ro g ram ie c za se m b a rd z o d alek o o d m iejsca, w k tó ry m je sp o w o d o w aliśm y . O to m o ja rada: Jeśli p ro g ram z z u p ełn ie n ie w iad o m y ch p rzy czy n b łę d n ie d ziała czy zaw iesza się, to zastan ó w się n a d w sk aźn ik am i. N ajczęściej o k aże się, że g d y w p isyw ałeś coś w m iejsce pam ięci p o k a z y w a n e p rzez w sk a źn ik - p o k azy w ał o n na niew łaściw e m iejsce lu b (o zgrozo!) w o g ó le zap o m n iałeś n a d a ć w sk a źn ik o w i jak ąk o lw iek w artość p o czą tk o w ą. W rezultacie p o k a z y w a ł o n w p rz y p a d k o w e miejsce, a Ty coś ta m zapisałeś n iszcząc coś n iezn an eg o .
Oprócz tych operacji można też wskaźniki od siebie odejmować Co z ta k iej operacji w y n ik a? Z astan ó w m y się: jeśli m a m y w sk a źn ik wa, który p o k a z u je n a p iętn asty elem en t tablicy, oraz w sk a ź n ik wb, k tó ry p o k a z u je na d z ie sią ty elem en t tablicy, to jaka jest różnica m ię d z y ty m i w sk a źn ik am i? Czyli jaka je st w arto ść w y rażen ia (wb
-
wa)
Z d ro w y ro zsąd ek p o d p o w ia d a : 5 elem entów . R zeczyw iście w arto ścią tego w y ra ż e n ia jest liczba 5. O djęcie od siebie dwóch w skaźników pokazujących na różne elem enty tej sam ej tablicy daje w rezultacie liczbę dzielących je elem entów tablicy. Liczba ta m oże być ze znakiem ujem nym lub dodatnim .
Dla w tajem n iczo n y ch i dociekliw ych: Rezultat ten jest jakiegoś typu całkowitego (ze znakiem)*- Typ ten nazywa się p t r d i f f t. Jest to zapewne skrót od ang. pointer difference (różnica
wskaźników). O to p r o s ty p rzy k ład : 8)
Co - w przypadku Twojego kompilatora - naprawdę kryje się za tak przezwanym (instrukcją t y p e d e f ) typem - możesz sprawdzić zaglądając do pliku nagłówkowego < cstddef> .
278
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic
#include using naraespace std; Z /*****************,******************************************* i n t main() 1 int t a b l i c a [1 5 ] ; / / d e f i n i c j a ta b l ic y i n t *wsk_a, *wsk_b, *wsk_c; wsk_a = i t a b l i c a [5]; wsk_b = & t a b l i c a [10]; wsk_c = s t a b l i c a [11]; cout << << << « ł
" ( w s k b - wsk a) = "\n(wsk_c - wsk_b) "\n(wsk_a - wsk_c) "\n(wsk c - wsk a) “
" << ( w s k b - wsk_a) = " << (wsk_c - wsk_b) = " << (wsk_a - wsk_c) =" « (wsk_c - wsk_a);
W rezultacie jako wynik otrzymamy (wsk_b (wsk_c (wsk_a (wsk_c
-
wsk_a) wsk_b) wsk_c) wsk_a)
= = = =
5 1 -6 6
C zy zau w aży łeś, ż e w ielokrotnie p o d k reślałem , iż m u szą to być w skaźnik pokazujące na tę sa m ą tablicę? D laczego to takie w ażne?
Przykład z geografii N a ścianie wisi m a p a Europy. Jednym , d re w n ia n y m w sk aźn ik iem p o k azu jem ) na tej m apie na P ary ż, a d ru g im w sk aźn ik iem n a Berlin. Jaka jest różnica tych w skaźników ? (odległość m iędzy tym i w sk aźn ik am i). O d p o w ie d ź jest na p rzy k ład taka: 28 cen ty m etró w , co przy u w z g lęd n ien iu po d ziałk i m a p y m oże ozn a czać tyle-set k ilom etrów . P rzyznasz, że ta o d p o w ied ź m a sens. A teraz d o d a tk o w o na drugiej ścianie zaw ieszam y inną m ap ę, n a przykłac m ap ę nieba. Jed n y m d rew n ian y m w sk aźn ik iem po k azu jem y n a Rzym , a d ru gim na g w iazd o z b ió r O riona. Jak jest różnica tych w sk a ź n ik ó w (odległość m ięd zy tym i w sk aźn ik am i)? To p y tan ie nie m a sensu. M apy m ają inne skale w iszą na p rz y p ad k o w y ch ścianach. Jeśli n a w e t w eźm iem y ta śm ę m ierniczą zm ierzy m y tę o d leg ło ść, to co ona b ęd zie oznaczać? Pam iętajm y w ięc O d ejm o w an ie w skaźników daje w y n ik , k tóry m o ż n a sensow nie z in terp reto w ać tylko w ted y , g d y te w sk aźn ik i p o k azu ją na ele^ m en ty w tej sam ej tablicy. N ie m a sensu m n o ż e n ie dw óch w sk aźn ik ó w (n aw et p o k azu jący ch na tę sam< tablicę) - no bo, co b y to m iało oznaczać? N ie m a też sensu d zielen ie, itd. P odsum ujm y:
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic
279
D ozw olone operacje arytm etyczne na w skaźnikach to: 1) dodaw anie i odejm owanie od nich liczba naturalnych - (daje to przesu w anie wskaźników), 2) odejm owanie dwóch wskaźników pokazujących na tę samą tablicę. ■
.8.4
■ ■
Porów nyw anie w skaźników W sk aźn ik i m o żn a ze sobą porów nyw ać. Dla p o ró w n an ia dw óch w sk aźn ik ó w p o słu g u je m y się o p erato ram i ==
!=
<
>
<=
>=
Jeśli m am y d w a w sk aźn ik i i n t *w skl,
*wsk2;
i zo stały one ju ż u staw io n e tak, że pokazują na jakieś obiekty, to ró w n o ść tych w sk a ź n ik ó w oznacza, że pokazują one na ten sam obiekt. i f ( w s k l == wsk2) c o u t « "oba w s k a ź n ik i p o k a z u ją na t o samo ! " ; Jeśli w sk aźn ik i są różne, to znaczy, że po k azu ją n a różne obiekty. if
(w skl != wks2) c o u t << " w s k a ź n ik i p o k a z u ją na r o ż n e o b ie k ty " ;
Z w ra c a m u w ag ę, że elem en ty tablicy to są też obiekty. E lem ent p iąty je st innym o b iek tem niż elem en t szósty. Jeżeli dw a w sk aźn ik i pokazują na jakieś elem enty tej sam ej tablicy, to w sk aźn ik , który jest m niejszy, pokazuje na o b iek t o mniej sz y m indeksie. # in c lu d e < io stre a m > u s in g nam espace s t d ; !/************************************************************* i n t m a in () 1 i n t t a b l i c a [5 ]; i n t *w sk _ czer, * w s k _ z ie l; in t i; w sk _ c z e r = & t a b l i c a [ 3 ] ; c o u t << "Mamy p ie c io e le m e n to w a t a b l i c e \n " "Wskaźnik czerwony pokazuje na " " e le m e n t o in d e k s i e 3\r." "Na k tó r y e le m e n t ma pokazyw ać " "w sk aźn ik z ie lo n y ? ( 0 - 4 ) : c i n >> i ; i f ( i < 0 | | i > 4) c o u t « " \n N ie ma ta k ie g o e le m e n tu w t e j t a b l i c y ! " ; e ls e { wsk z i e l = & t a b l i c a [ i ] ; _ c o u t « "\nZ p rzep ro w ad zo n eg o p o ró w n an ia w sk a zn ik o w \n " "czerw o n eg o z zielo n y m w y n ik a, ze: \ n " ;
280
Rozdz. 8. W skaźniki Zastosowanie wskaźników wobec tablic
// w ła śc iw a
akcja p o ró w n a n ia ---------- -----------------------
if(wsk_czer > wsk_ziel) cout «
"zielony pokazuje na element " "bliżej początku tablicy";
) else if(wsk_czer < wsk_ziel) cout « h
"zielony pokazuje na element " "o wyzszym indeksie";
// czy li:
se
* cout «
w sk_czer
=-
w s k jz ic l
"zielony i czerwony pokazują " "na to samo\n";
) > >
Po wykonaniu tego programu i przykładowej odpowiedzi 4, na ekranie pojawi się tekst: Mamy piecioelementowa tablice Wskaźnik czerwony pokazuje na element o indeksie i Na który element ma pokazywać wskaźnik zielony ? (0 4): 4 Z przeprowadzonego porównania wskaźników czerwonego z zielonym wynika, ze: zielony pokazuje na element o wyzszym indeksie W przy k ład zie nie m a użycia o p erato ró w <= i >=, ale ich zn ac zen ie jest chyba oczyw iste. W arto znow u p rzypom nieć, że operacje >
<
>=
<=
m ają sens tylko dla w sk aźn ik ó w pokazujących na tę sam ą tablice. Pow ód jes d o k ład n ie taki sam jak przy arytm etyce w skaźników . jeśli tylko w sk aźn ik i są tego sam ego ty p u - czyli inaczej: słu żą d o pokazyw ani! na obiekty tego sam eg o typu (int albo char itd.) - i pokazują o n e na obiekty nn należące do tej sam ej tablicy, to m im o p o w y ższeg o zastrzeżen ia, w olno nam j< porów nać. W olno n am także p orów nać w skaźniki p o kazujące w łaśn ie na zu pełn ie odosobnione zm ienne. Jest tylko kw estia, jaki sens m a ta k ie po ró w n an ie A sens jakiś ma! D ow iadujem y się w ten sposób, jak w pam ięci kom puten u lo k o w an e są w zg lęd em siebie te obiekty. W ynik i jego n a sz a interpretacji zależy tutaj od k o n k retn eg o kom pilatora, którym się p o sługujem y. W olno p o ró w n ać zw ykły w sk aźn ik ze w sk aźn ik iem ty p u v 0 ^ ' albo n a w e t dw a w skaźniki ty p u v o i d ze sobą. N ajczęściej porów n anie tak ie jest rodzajem zap y tan ia: "czy one pokazują teraz na to
samo?"
Rozdział. 8. W skaźniki Zastosowanie wskaźników wobec tablic
8.5
281
W skaźnik m ożna porównać z adresem 0 K ażdy w sk aźn ik m o ż n a p o ró w n ać z ad resem 0 (zwanym dawniej adresem NULL). Jest to p rz y d a tn a w łaściw ość, bo u staw ien ie w sk aźn ik a na ten ad res często służy n am , by zazn aczy ć, że w skaźnik nie p o k azu je na nic sensow nego. W pisujem y tam d o w sk a źn ik a celow o 0:
II lub dawniej: wsk = NULL;
wsk = 0;
Potem m ożem y to ew en tu a ln ie łatw o sp ra w d z ić
if(wsk == 0) cout « "Wskaźnik nie pokazuje na nic sensownego !"; W kompilatorach, które mają zdefiniowaną nazwę NULL (dawniej było to powszechne, ale teraz ten zwyczaj zanika) - to samo sprawdzenie wskaźnika można było wykonać jako if(wsk == NULL)
cout «
"Wskaźnik nie ustawiony!";
albo jeszcze prościej, (nawet bez używania tego zera czy NULLa ) if(!wsk)
cout << "Wskaźnik nie ustawiony!";
Ta o statn ia form a jest m ożliw a, bo W yrażen iem w aru n k o w y m w instrukcji i f m oże być tak że w skaź nik. W ów czas zam ieniany jest o n na typ bool w e d łu g zasady: ad res 0 in n y a d re s
-> false, —» true
Skoro tak, to o d tą d m o żem y spraw dzać u staw ien ie w skaźnika p o p ro stu tak: ♦♦♦ i f (ws k ) - prawda, gdy w skaźnik p o k azu je na "coś "
(na adres nie zerowy), ♦♦♦ i f (! w s k ) - prawda, gdy w skaźnik p o k azu je na "nic"
(na adres zerowy). Zauważ zaprzeczenie: jest tu operator negacji-wykrzy knik.
W Na koniec jeszcze jedno. Spotkaliśm y się już ze słowem...
...n u li pisanym małymi literami. O znaczało ono znak A SC II o kodzie zerowym. (Znak ten u ży w an y jest najczęś ciej d o oznaczenia, że tu o to jest koniec C -stringu).
Tutaj mamy N U L L pisany wielkimi literami O znacza to "adres zerowy", czyli specjalne miejsce, na które skierow uje się w skaźnik, gdy chce się zaznaczyć, że chw ilow o nie będzie p o k azy w ał nic sensow nego.
282
Rozdz. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji W n o w y m stan d ard zie z a u w a ż a się jakby o d w ró t od sto so w an ia tych słów . To znaczy n a d a l tak m ożna m ów ić, ale w p ro g ram ie tych słów się nie używ a. Z am iast nich używ a się zera. M oże się okazać, że d an em u najnow szem u kom pilatorow i n azw a N U LL jest pc prostu n iezn an a. W ted y m ożesz sobie pom óc dyrektyw ą p rep ro ceso ra i d e f i n e NULL
0
ale najlepiej p o zam ien iaj te NULLe ze stareg o program u - n a zera
8.9
Z a s to s o w a n ie w s k a ź n ik ó w w a rg u m e n ta c h fu n k c ji M ów iliśm y, że są 4 do m en y zasto so w an ia w skaźników . K olejną, k tó rą się teraj zajm iem y, to w sk aźn ik i jako a rg u m e n ty funkcji. W zasad z ie m ów iliśm y już < tym trochę w ro zd ziale o funkcjach. T utaj rozszerzym y ten te m at, ale najpierw
Przypomnienie Jeśli m am y funkcję z jednym arg u m e n tem i n t f u n k c j a ( i n t argum) ; i g d y w y w o łam y ją tak: i n t a , x = 5; a = fu n k c ja (x ); to funkcja ta o trzy m u je w ów czas do p racy kopię zm iennej x (a nie oryginał) Jeżeli n aw et w obrębie funkcji na tej k opii dokonujem y jakichś zm ian , to v m om encie o p u szcza n ia funkcji jest ona likw idow ana. F unkcja w ięc nie możi dok o n ać z m ia n zm iennej przysłanej d o niej jako a rg u m e n t - p rzez w artość (Czy p am ięta sz jeszcze tę p rzypow ieść z fotografią teściowej?) Innym i słow y, g d y b y śm y np. chcieli m ieć funkcję, k tó ra p rzy słan y do nie p aram etr zw ięk szy o 130, a jej treść (czyli definicję) n ap isali tak: v o id f u n k c j a ( i n t fo to ) * f o t o += 130; 1
I/ czyli foto =foto+ 130;
to w y w o łan ie funkcji i n t m = 10; f u n k c ja ( m ) ; i t j ; ■): )
n ie s p o w o d u je ja k ie jk o lw ie k zm ia n y m. C o się tu d zieje ? Z m ienna zo staje sfotografow ana, a jej fotografia zn ajd zie się w podręcznynj m ag azy n k u funkcji (na stosie). W trakcie działania funkcji d o tej fotografii zostaje d o d a n a liczba 130, więc obiekt n a stosie m a teraz ju ż w arto ść 140.
Rozdział. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji
283
P o n ie w a ż funkcja nie m a już więcej nic d o ro b o ty , w ięc kończy się ją u p rzątając w sz y stk ie śm ieci z e sto su . W tedy to n a sz a fotografia p rzesta je istnieć. G dy w ró ciliśm y z funkcji p a trz y m y na p ra w d z iw ą zm ien n ą m - je st nietknięta. B aw iliśm y się jej k o p ią , k tó ra została zn iszc zo n a. P rz y p o m n ie liśm y tu ta j m ech an izm p rz e sy ła n ia a rg u m e n tó w p rz e z w artość. T ak p rzesy ła n e są z w y k łe obiekty. (O tab licach m ó w iliśm y już, ż e je st inaczej).
Co zatem zrobić, by funkcja mogła obiekt m zmienić ? N a jp ro stsz e i n a jb ard ziej zalecane w yjście jest takie, by fu n k cja jako swój re z u lta t zw racała w a rto ść , którą m y św ia d o m ie w p iszem y d o o b ie k tu m: O to definicja takiej funkcji:
A ta k tę funkcję w y w o łu je m y w program ie: int m = 10; m = fun2 (m) ; cout << m; P o w y k o n a n iu tego frag m e n tu na ekranie p o ja w i się liczba 140. C o się tu o d b y w a: fo to g rafo w an a jest z m ie n n a m i jej w arto ść (czyli liczba 10) jest u m ie sz c z a n a na sto sie jako obiekt typu int o n azw ie foto. P o w stał więc lo k a ln y o b iek t a u to m a ty c z n y . N astęp n ie d o teg o obiektu foto d o d a w a n e jest 130, co p o w o d u je, ż e w rezultacie w obiekcie foto jest teraz w a rto ść 140. T e ra z funkcja się k o ń c z y - instrukcją return. To, co stoi obok sło w a return, je st to w y rażen ie, k tó re g o w artość staje się rezu ltatem funkcji. W naszym p rz y p a d k u to w y ra ż e n ie składa się tylko z jed n ej zm iennej. Jego w a rto ść to 140. T a w łaśn ie liczba z a m ie n ia n a jest na ty p d e k la ro w a n e g o re z u lta tu zw racan eg o p rz e z funkcję. U n as d ek laro w aliśm y , że funkcja zw raca w arto ść ty p u int, więc k o n w ersja jest w z a s a d z ie niepotrzebna. (G d y b y śm y mieli jed n ak 140.1 to nas tą p iło b y obcięcie d o int czyli w artość 140). P o konw ersji w arto ść tę zap am ię tu je się w jakim ś tajem n iczy m miejscu. Z ac zy n am y sp rzątać śm ieci z e stosu. Li k w id o w a n y jest w ięc o b iek t foto. W racam y d o miejsca w p ro g ra m ie skąd w y w o ła liśm y funkcję. W idzim y tam , że re z u lta t funkcji m a zo stać p rzy p isan y o b ie k to w i m, w k tó ry m n a d a l tkw i w artość 10. O d szu k u jem y sc h o w a n y w bez p ie c z n y m m iejscu re z u lta t funkcji (140) i w staw iam y go do o b ie k tu m. W tym m o m en cie o d b y ła się m odyfikacja obiektu m. S p o só b jest b a rd z o d o b ry dlatego także, iż p a trz ą c na zapis m = fun2(m) ; o d ra z u w id z im y , ż e d o zm iennej m w pisuje się coś now ego, w ięc z m ian a jego w arto ści n ie jest dla n a s niespodzianką. T ak ie w zględy są b a rd z o w a ż n e przy
284
Rozdz. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji analizie cu d zy ch program ów , a n aw et swoich w łasnych, które p rzy kilkunastu tysiącach linii ju ż tru d n o nam opanow ać.
Co jednak zrobić w przypadku, gdy funkcja ma zmienić więcej niż jeder obiekt? O pisanego w y żej sposobu zastosow ać się nie d a - dlatego, że funkcja za pom ocą instrukcji r e t u r n zw raca tylko jed n ą wartość. Tu w łaśnie p rzydają się w sk aź niki. O tóż z a m ia st wysłać do funkcji kopię naszej zm iennej w ysyłam y... P ew nie pom yślałeś „o ryginał". Nie, tego zrobić nie m ożna. W ysłanie arg u m en tó w do funkcji jest jakby n ap isa niem do niej lis tu . W eźmy taki obrazek: W łazience zep su ł się nam k ran . Piszem y list do hydraulika. N ie m ożem y mu w liście przesłać tego k ra n u . M am y d w a wyjścia: ♦> 1) M ożem y mu przesłać fotografię tego zep su teg o kranu. W tedy h y d raulik zn ajd zie u siebie w w arsztacie na stosie rupieci taki sam krar. (kopię naszego). Jeśli naw et sobie go napraw i, to n asz w łasny k ran i tak będzie n a d a l zepsuty. 2) M o żem y mu w liście jed n ak przesłać - uw aga, uw aga! - ADRES tego naszego zepsutego kranu - n azw ę ulicy n u m er d o m u , p iętro i to, g d zie w m ieszk an iu jest ten kran. P iszem y m u w liście, że m a n ap raw ić k ran będący p o d takim adresem . Oto, jak to p rzeło ży ć na język program ow ania:
i in c lu d e using namespace std; void h y d r a u l i k ( i n t *wsk_do_kranu); II O /********»****************************************************/ i n t main() //©
i n t kran = -1;
cout << "Stan techniczny kranu = "<< kran << endl; // © h y d r a u l i k ( Skran ); cout << "Po wezwaniu hydraulika stan te c h n ic z n y kranu = " _ « kran « endl; II U /************************************************************ * / void h y d r a u l i k (in t *wsk_do_kranu) /u/ ** *wsk do kranu = 100;
j j a u k c j a n a p r a w ia n i a
©
}
Po wykonaniu tego programu na ekranie pojawia się: Stan t e c h n i c z n y kranu = -1 Po wezwaniu hydraulika s t a n te chnic zny kranu = 100
u
©
Rozdział. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji
285
Przyjrzyjmy się ciekawszym punktom O D eklaracja funkcji hydraulik. C zytam y ją tak: hydraulik jestfu n k cją wywo ły w an ą z je d n y m a rg u m e n tem będącym w skaźnikiem do obiektu typu int. Funkcja ta zw raca ty p void, czyli nic nie zw raca. 0 Definicja obiektu ty p u int o nazw ie kran. W staw ione tam o d razu -1 oznacza, ż e k ran jest b a rd z o z e p su ty . 0 W yw ołanie funkcji hydraulik, której definicja jest w O . P rześled źm y co tu się z d a rz a . O tóż w z y w a m y h y d rau lik a podając m u listow nie a d re s zm iennej (ope ra to r je d n o arg u m en to w y &oznacza jak w iad o m o „adres"). C o z ty m adresem ro b i hydraulik? O W id zim y , że p rzy słan y ad res służy hydraulikowi do inicjalizaq'i w skaźnika. T ak — bow iem hydraulik definiuje sobie (na stosie) w sk aźn ik d o obiektu ty p u int i daje m u n azw ę wsk_do_kranu. O debrany o d listonosza adres w sk aźn ik pokazuje n a nasz kran. © D o obiektu p o k azy w an eg o przez w skaźnik w staw ia się liczbę 100. W skaźnik p o k azu je na n asz k ran , w ięc to do naszego k ranu w staw ia się tę w artość 100. To jakb y sym bolizuje n a p ra w ę naszego k ranu. © N astęp n ie o p u szcza się funkcję, likw iduje się śmieci, czyli zn iszczo n y zostaje w sk aźn ik do naszego k ran u - hydraulik p o d arł niep o trzeb n ą mu ju ż kartkę z ad resem .
O N a do w ó d , że n ap raw a została dokonana n a p ra w d ę na n aszy m kranie, w ypisu jem y jego zaw arto ść n a ekran. G d y b y śm y za chw ilę w yw ołali funkcję hydraulik podając jej a d re s innego obiek tu typu int, to zad z iała ona na in n y m obiekcie. Jak w życiu: hydraulik n ap raw ia jeszcze inny k ran int kurek = -10; hydraulik(Skurek) ; czyli w staw i on liczbę sto do obiektu kurę k (bow iem adres tego obiektu właśnie w ysłaliśm y). Jeśli natom iast m am y całą baterię kurków int bateria[15]; i chcem y napraw ić czw arty kurek z tej baterii (tablicy), to w y w o łu jem y hydrauliki &bateria[4]) C hy b a C ię ten zapis nie dziw i, oswoiłeś się zap ew n e z tym , ż e tak zapisuje się a d re s elem entu tablicy. N a p ra w a elem entów od 4 d o 8 tej baterii m oże zostać zrealizo w an a przez for(int i = 4 ; i <= 8 ; i++) { hydrauliki 4bateria[i]);
286
Rozdz. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji
8.9.1
Jeszcze raz o przesyłaniu tablic do funkcji Jak pam iętasz, g d y do funkcji w ysyła się tablicę jako całość, to w y sy łan e nie są kopie w szystkich jej elem entów , ale po prostu jej adres. To też sp raw ia, że m ając adres, funkcja m o ż e pracow ać na oryginalnych jej elem entach. Jeśli jednak w y sy łam y d o funkcji jeden elem ent tablicy, lub kilka - w k ażd y m razie nie całość - to funkcja traktuje je jak zw ykłe obiekty p rzesła n e przez w artość. Jeśli ch cem y w ysłać je przez adres, to m usim y pow ied zieć to jasno - tak było w łaśnie p rz y w ysyłaniu elem entów baterii.
Porozmawiajmy teraz jednak o wysyłaniu tablicy jako całości O db y w ało się o n o jakby w ed łu g takiego schem atu. Mając funkcję v o id f u n ( i n t t a b [] ) ; oraz tablicę int tablica[20]; w yw ołanie funkcji w yglądało tak: fun(tablica) ; Przesyłaliśm y d o funkcji ad res tablicy. (Złota regułka: n a z w a tablicy jest ad re sem jej początku). N ato m iast w p oprzednim paragrafie zo b aczy liśm y funkcję, która jest zd o ln a przyjąć jako arg u m en t - adres jakiegoś obiektu ty p u int. O to w y w o łan ie funkcji h y d r a u l i k z arg u m en tem b ęd ący m tablicą (całą): hydraulik(tablica); Jak w idzisz z a p is jest tu identyczny. Czy zatem h y d r a u l i k n a p ra w i całą tablicę? Nie. O n p o prostu tego nie um ie. N apisaliśm y funkcję h y d ra u lik tak, ża n ap raw ia tylko o b iek t o przysłanym adresie. N azw a tablicy jest ad resem je zerow ego elem en tu , w ięc n apraw i on tylko ten zero w y elem en t. A by mógł nap raw iać więcej, m usielibyśm y nauczyć go p rzesu w an ia tego w sk aźn ik a. N ie to jest tu je d n ak istotne. C hodzi o to, ż e tablicę m ożna w ysłać d o funkcji jako tablicę, a od eb rać na dw a sposoby:
8.9.2
•
jako tablicę,
•
jak o w skaźnik.
Odbieranie tablicy jako wskaźnika W rozdziale o tablicach m ów iliśm y o tym , jak d o funkcji w y słać tablicę. P rzypo m inam , że tablicy nie w ysyła się przez kopiow anie w szystkich elem en tó w danej tablicy, bo m oże b y ć ich potw ornie dużo. W ysyła się d o funkcji n a z w ę tej tablicy - która to n azw a, jak w iem y, jest przecież adresem jej p o czątk u . Jednak skoro w y sy łam y d o funkcji ten adres, to m ożem y w o b ec te g o odebrać go jako w skaźnik. O to p rzy k ład kilku rodzajów funkcji:
Rozdział. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji
287
#include using namespace std; j.******** / z***?********************************************************! void funkcja wska(int *wsk, int rozmiar); void funkcja_tabl(int tab[], int rozmiar); void funkcja wsk2(int *wsk, int rozmiar); **********, /** * * * * * * * ;* 7 * * * * * * * ******************************************/ int main() int tafla[4] = ł 5,10,15,20 }; funkcja_tabl(tafla, 4) funkcja_wska(tafla, 4) funkcja wsk2(tafla, 4) /* * * * * * * * * * * * * * * * * * * * * ♦ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // O v o i d f u n k c j a _ t a b l ( i n t t a b [ ) , i n t roz miar ) ^ c o u t « "\nWewnatrz f u n k c j i f u n k c j a _ t a b l \n"; f o r ( i n t i = 0 ; i < rozmiar ; i++) c o u t << t a b [ i ] << " \ t " ;
} / .*+*********************************************** void funkcja_wska(int *wsk, int rozmiar) {
c o u t « "\nWewnatrz f u n k c j i funkcja _wsk a \n" ; f o r ( i n t i = 0 ; i < rozmiar ; i++) c o u t << * (wsk++) << " \ t " ;
v o i d f u n k c j a _ w s k 2 ( i n t *wsk, {
*I II ©
i n t rozmiar)
// @
c o u t « "\nWewnatrz f u n k c j i funkcja_wsk2 \n"; f o r ( i n t i = 0 ; i < rozmiar ; i++) c o u t << w s k [ i ] << " \ t" ;
)
Po wykonaniu tego programu na ekranie otrzymamy Wewnątrz f u n k c j i f u n k c j a _ t a b l
5
10 15 20
5
10 15 20
Wewnątrz f u n k c j i funkcja_wska Wewnątrz funkcji funkcja_wsk2
5
10 15 20
Uwagi: O Funkcję w yw ołujem y podając jej nazw ę tablicy (czyli ad res jej zero w eg o elem en tu). W definicji funkcji funkc ja_t abl O w idzim y, że w ysłany jej ad res tablicy o d eb ran y jest tak że jako tablica. W ew nątrz funkcji p o sługujem y się znanym zap isem „tab lico w y m ". (Wiem, że oszukuję, ale cierpliwości!) © Identycznie w y g ląd ające wywołanie. Tym razem chodzi o in n ą funkcję © . W ew n ątrz tej funkcji przysłany adres służy d o inicjalizacji lokalnego w skaźnika wsk. Tak jakbyśm y w ykonali taką instrukcję int *wsk = tafla;
288
Rozdz. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji W skaźnikiem ty m posługujem y się w ew n ątrz funkcji. W naszej funkcji zastoso w aliśm y w y rażen ie * (wsk++) które (p rzy p o m in am ) o d p o w iad a zło żen iu dw óch takich w y rażeń *wsk
oraz
wsk++
czyli odczytaj coś, a potem przejdź do następnego elementu. © Identyczne w y w o łan ie. Tym razem to funkcja f u n k c ja _ w s k 2 . © Jak w idać z jej definicji, odbiera ona tablicę w ten sam sposób, co funkcja pow yżej. N ajciekaw sze jest to, że p o tem używ a tablicy korzystając z notacji „tablicow ej".
Jakie są wady i zalety tych typów funkcji? Czy lepiej odebrać jako tablicę, czy lepiej jako wskaźnik? M oim zd an iem to kw estia u p o d obania. Pam iętaj, że niezależnie o d tego, jak tę tablicę o dbierzesz - (w skaźnikow o czy tablicow o), spry tn y (czyli now oczesny) kom pilator zrealizu je to i tak za pom ocą stosow nego w sk aźn ik a, d o którego w pisze ad res przesyłanej tablicy. Zatem , n aw et jeśli m yślisz ze odbierasz tablicę jako tablicę - m asz n ap raw d ę do czynienia ze sto so w n eg o rodzaju w sk aźn ik iem do niej. W ciele fu n k q i - jak w idzieliśm y to w n aszy m p rzykładzie - m ożesz sobie u ży w ać składni "tablicowej" n aw et w ted y , g d y tablicę o debrałeś jako wskaźnik. P osługiw anie się sk ład n ią "tablicową" - sp raw ia, że treść funkcji jest bardziej czytelna. M oże to być w ażne w początkow ej fazie nauki. W skaźniki są genial nym n arzęd ziem d o zagm atw ania zap isu . Dla zaaw ansow anych: Tablice wielowymiarowe, moim zdaniem, łatwiej odbiera się w funkcji stosując notację wskaźnikową. Jest to sposób bardziej uniwersalny, bo rozmiary tablicy nie muszą być na stałe zaszyte w funkcji. Wystarczy, jeśli funkcja dowie się o nich dopiero w momencie jej wywołania.
8.9.3
Argument formalny będący w skaźnikiem do obiektu con st P am iętam y, że tablice do funkcji przysyła się nie tak, że funkcja o trzy m u je ko p ie w szystkich elem en tó w tablicy (np. w liczbie 8155), czyli nie p rz e z w artość, ale tak, że funkcja o trzy m u je ad res tablicy. W rezultacie w ięc funkcja pracuje na oryginale tablicy i m o że dow olnie zm ien iać jej elem enty. To b ard zo w y g o d n e, g d y funkcja n a p ra w d ę p o w inna zm ien iać elem en ty tablicy - na p rzykład p o m n o ży ć k ażd y przez 2. M oże być jednak sytuacja całkiem o d w ro tn a. C zasem tablicę d ajem y funkcji tylko po to, by ją sobie poczytała, ale n ie chcem y, żeby w niej cokolw iek zm ieniała. Jak się p rz e d tym zabezpieczyć?
Rozdział. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji
289
Tu w łaśnie p rzy d aje się nam p rzy d o m ek c o n s t , który m o że spraw ić, że ze wskaźnika do obiektu z ro b im y wskaźnik do stałego obiektu. Taki w sk aźn ik w skazu je na obiekty, ale nie p o zw ala na ich m odyfikację. Funkcja definiuje sobie na stosie w skaźnik d o obiektu stałego. O trzy m an y adres obiektu w staw ia w łaśn ie do takiego w skaźnika. Posługując się potem takim w skaźnikiem u n iem o żliw ia sobie samej jakiekolw iek m odyfikacje obiektu, na które on pokazuje. Dinclude using namespace std;
// d e k la r a c je f u n k c j i O yoid pokazywacz(const int *wsk, int ile); yoid zmieniacz(int *wsk, int ile); /*************************♦**********************************/ int main() { int tablica[4] = ( 110,120,130,140); // © pokazywacz(tablica, 4); zmieniacz(tablica, 4); pokazywacz(tablica, 4); cout << "Dla potwierdzenia tablica[3] = " << tablica [3]; Z************************************************************/ yoid pokazywacz(const int *wsk, int ile) // cout << "Działa pokazywacz " << endl; for(int i = 0 ; i < ile ; i ++, wsk++) { // *wsk += 22; l/błcfd! cout « "element nr " « i « " ma wartość " << *wsk « endl; }
O // ©
z************************************************************/ yoid zmieniacz(int *wsk, int ile) // ® ( cout << "Działa zmieniacz " << endl; for(int i = 0 ; i < ile ; i ++, wsk++) *wsk += 500; cout << "element nr " « << *wsk << endl;
// wolno nam! i « " ma wartość "
} )
Po wykonaniu zobaczymy na ekranie Działa pokazywacz element nr 0 ma wartość element nr 1 ma wartość element nr 2 ma wartość element nr 3 ma wartość Działa zmieniacz
110 120 130 140
O
290
Rozdz. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji element nr 0 ma wartość 610 element nr 1 ma wartość 620 element nr 2 ma wartość 630 element nr 3 ma wartość 640 Działa pokaz ywacz element nr 0 ma wartość 610 element nr 1 ma wartość 620 element nr 2 ma wartość 630 element nr 3 ma wartość 640 Dla potwierd zenia tablica[3] = 640
Kilka uwag: O D eklaracje funkcji. Są obow iązkow e, bo w y w o łan ia ich n astęp u ją w program ie w cześniej niż k o m p ilato r zobaczy ich definicje (są w tekście p ro g ra m u później). G dyby nie były k o n ieczn e - i tak bym je napisał, taką już m a m zasad ę . @ W yw ołanie funkcji p o ka zyw ac z . W ysyłam y tam tablicę. C zy n am się to podoba czy nie, funkcja d ostaje adres tej tablicy i m oże n aw et całą c e n n ą zaw artość tablicy zniszczyć. © O to, jak funkcja p o k a z yw acz odbiera ad res tablicy. D ostaje, co p ra w d a , adres tablicy - czyli w sz y stk ie u p raw nienia, jed n ak funkcja d efin iu je w skaźnik z przydom kiem c o n s t i tam chow a ad res tablicy. R ó w n o w a żn e to jest więc instrukcji const int *wsk = tablica; Jest to w sk aźn ik , który u zn aje w sk azy w an y p rz e z sieb ie obiekt za stały. T y m sam ym nie m oże tak ieg o obiektu m od y fik o w ać. Funkcja p o k a z y w a c z odbiera nazw ę jako w skaźnik d o s ta łe j (a raczej do stałych). Innym i sło w y oznacza to, że co p ra w d a d o staliśm y w funkcji u p raw nienia, ale obiecujem y z nich nie korzystać. D efiniując w łaśn ie ta k i w skaźnik, św iad o m ie p o zb aw ia m y się praw a do m o d y fik o w an ia tablicy przysłanej jako arg u m en t. Sam o ry g in aln y obiekt (tablica) nie jest o biektem stały m . Jednak za pom ocą tak zdefiniow anego w sk aźn ik a nie b ę d z ie m y m ogli go zm ieniać. Innym , zw ykłym w sk aźn ik iem o czyw iście m oglibyśm y. O Ta linijka jest w k o m e n tarzu . Jest to p ró b a m odyfikacji ob iek tu w sk azy w an eg o p rzez w skaźnik. Jeśli chcesz się przek o n ać, jak k o m p ilato r strz e ż e obiektu w sk azy w an eg o p rz e z n asz w skaźnik, to zlik w id u j ten k o m e n tarz. Już w czasie kom pilacji o trzy m asz inform ację o błędzie. © O d czy ty w ać z o b ie k tu w sk azy w an eg o p rz e z taki w sk aźn ik oczyw iście m oże m y. N ie jest to b o w iem modyfikacja. © To jest definicja fu n k cji zmieniacz. A d res u ży ty jest d o inicjalizacji w skaźnika zw y k łeg o typu, czyli n i e - c o n s t . Tę s p ra w ę już zn am y - w sk a źn ik ie m takim m o żem y d o w o ln ie zm ien iać p o k azy w an e obiekty.
O O to d o w ó d na p o w y ż sz e stw ierdzenie. M odyfikacja e lem en tó w tablicy. Funkcja zmieniacz m oże sw o b o d n ie dodać liczbę 500 d o elem en tó w tablicy. O tym , ż e
Rozdział. 8. W skaźniki Zastosowanie wskaźników w argum entach funkcji
291
d zieje się to na o ry g in a ln e j tablicy, p rz e k o n u je nas w y d ru k je d n eg o z nich w funkcji m a in .
W sk aźn ik d o o b ie k tu stałego, to nie ty lk o zło żo n a z do b rej w o li obietnica. Są sytuacje, w k tó ry ch je s t p o prostu n ie zb ęd n y . Jeśli m a m y o b iek t, k tó ry n a p ra w d ę jest stały , to nie m o żn a na niego p o k a z a ć in n y m w sk a ź n ik ie m jak tylko w sk a ź n ik ie m d o stałej. To w końcu z ro z u m ia łe — inaczej m o g lib y śm y o szu k iw ać: co p ra w d a sam o b iek t jest stały , ale użyjem y p o d stę p u i zm ien im y so b ie jego zaw arto ść p o słu g u ją c się w sk aźn ik iem . Tak się nie d a . K om pilator nie p o zw o li na d an y m stały m obiekcie u s ta w ić żad n eg o w sk a źn ik a, k tó ry z definicji nie obiecuje go nie zm ieniać. const int pojemność = 5; const int * staly_wsk; int *zwykly_wsk;
II definicja stałego obiektu typu int /I definicja wskaźnika do obj. stałego II definicja zwykłego wsk.
stalywsk = £ pojemność;
// ustawienie wskaźnika na tym obiekcie: // obiekt jest stały i wskaźnik tcź // kompilator się zgadza
zwykly_wsk = & pojemność;
// Błąd: obiekt jest stały, a wskaźnik zwykły // - kompilator zaprotestuje !!
Co to oznacza w stosunku do tablic? Jeśli w n a sz y m o sta tn im p rzy k ład o w y m p ro g ram ie m ielib y śm y d o d atk o w o w funkcji m a in definicję takiej tablicy const int tablica_S[4] = { 10,20,30,40); To w tej funkcji m a in m ożliw e byłoby w y w o łan ie pokazywacz(tablica_S, 4); ale n iem o żliw e b y ło b y w yw ołanie zmieniacz(tablica_S, 4); K o m p ilato r nie p o zw o li bow iem w ysłać tablicy stałych d o funkcji, k tó ra tego nie obiecuje u szan o w ać. K ró tk o m ów iąc: jeśli tablice w funkcji m a m y sobie tylko p o o g ląd ać, to lepiej w funkcji o d eb rać je z p rzy d o m k iem const, d lateg o że w te d y nasza funkcja jest b ard ziej w sz ech stro n n a. N adaje się i dla tablic zw ykłych, i d la tablic stałych. Jeśli tego p rz y d o m k a const p rzy arg u m e n cie nie m a - n ad aje się tylko dla tablic zw y k ły ch (nie-stałych).
292
Rozdz. 8. W skaźniki Zastosowanie wskaźników przy dostępie do konkretnych kom órek pamięci
8.10
Z a s t o s o w a n i e w s k a ź n i k ó w przy dostępie d o konkretnych k o m ó r e k pamięci Trzecią dom eną z asto so w an ia w sk aźn ik ó w je st bezpośredni d o stęp do specjal nie w ybranych kom ó rek pam ięci. C h odzi tu o dostęp do kom órki pam ięci bez p o d aw an ia jakiejkolw iek jej nazw y. D ajm y na to, że w pam ięci jest jakaś k o m ó rk a o adresie 93952. Jest tam cos zu p ełn ie szczególnego. (N p kom órka ta po łączo n a jest zew n ętrzn ie z m ierni kiem tem peratury). M am y z ad a n ie w p isan ia ta m jakiejś w artości lub odczyta nia jej. K om órka ta oczyw iście nie m a nazw y. lak zatem się do niej o d n o sim y ? O czyw iście z a pom ocą w skaźnika! Jak to zrobić konkretnie? N ajp ierw m u sim y ustaw ić w sk a ź n ik tak, by p o k azy w ał na żądaną kom órkę. Robimy to w p isu jąc d o niego k o n k retn y adres kom órki. w skaźnik = a d re s ;
K om pilator jednak nie po zw o li na to, by - tak po prostu - liczbę całkowitą w staw ić do w sk aźn ik a - g d y ż najczęściej ro b im y to przez pom yłkę: C hcieliśm y w p isać liczbę do miejsca, na które pokazuje w skaźnik, a przez p o m y łk ę - w pisujem y liczbę do sam ego w sk aźn ik a, (czyli zm ieniam y m u p rzech o w y w an y w nim adres). Itfsp ^
A by u p ew n ić kom p ilato r, że tym razem n a p ra w ę o to chodzi, czyli że będziem y w sk aźn ik ustawiać za pom ocą adresu w y rażo n eg o liczbą całkow itą - stosujem y rzutow anie. N ie m a problem u - p am ięta sz zapew ne jak m ów iliśm y n ied aw n o w § 8.5, na str. 261, że d o tego słu ży w łaśn ie operator rzu to w an ia reinterpret_cast. int *wsk_temperatury; wsk temperatury = reinterpret_cast
>(93952),
U staw iliśm y w ięc n asz w skaźnik tak, by p o kazyw ał w pam ięci k o m p u tera na kom órkę o adresie 93952. O d tej pory p o sługujem y się już tym w sk aźn ik iem w zn a n y sposób. cout «
"Obecna temperatura - «
*wsk_temperatury;
Q W id zisz jakie to p r o s t e ? _________________ _ _
9J
Niestety, czasami ustawienie wskaźnika na żądany adres może nie być tak proste. W niektórych typach komputerów mogą istnieć niezwykłe sposoby wyrażania adresu. Takie komputery mają jednak kompilatory z narzędziami do łatwiejszego konstruowania takich adresów.
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
8.11
293
R e ze rw a c ja o b s z a ró w p a m ię c i C zw artą d o m e n ą jest zastosow anie w skaźników p rzy rezerw o w an iu jakichś obszarów pam ięci. W iąże się z ty m o p erato r new, który tu w łaśnie będę reklam ow ał. Miłośnikom języka C podpowiem, że operator ten robi to samo, co znana im funkcja biblioteczna m a l l o c (memory allocation). O tej funkcji od dzisiaj należy zapomnieć, gdyż operator new robi to lepiej i łatwiej. O co tutaj ch o d zi: W trakcie pisania p ro g ram u nie zaw sze w iadom o jak d u że będą tablice, k tó ry m i chcemy się posługiw ać. Pow staje p ytanie: czy nie m ożna by zrobić tak, że zara z po starcie p ro g ram u m ów im y p ro g ram o w i jak w ielka ma być d an a tablica? M ożna. D o teg o w łaśnie używ a się operatora new. N ato m iast problem , o którym m ó w im y nazyw a się d y n am iczn ą alokacją (rezerw acją) tablic.
Zanim pokażemy jak to zrobić - inny przykład, kiedy operator new może się przydać: O pracow ujem y program kontroli lotów. Na ekranie o b razo w an e są sam oloty lecące w łaśn ie n ad tym obszarem . Jeśli sam olot w latuje na nasze terytorium , pojawia się n a b rzeg u mapy (ekranu) jako mały znaczek. Stopniow o przesuw a się w trakcie lotu, a kiedy opuszcza obszar - znika. O czyw iście te samolociki m uszą istnieć ju ż jakoś wcześniej w naszym program ie. Tak jak m usim y w tek ście p ro g ra m u zdefiniow ać zm ienną x, jeśli mam y się nią k ied y ś posługiwać. M usim y sobie w ięc gdzieś w program ie napisać definicje obiektów reprezen tujących te sam oloty. Tylko ile ich napisać? 5,10, 20? N a w szelki w ypadek z zapasem d efin iu jem y 25. N iby „n a w szelk i w ypadek", a już w tym momencie ograniczyliśm y działanie program u o d obsługi 25 sam olotów. Po co? Lepiej by było przecież niczego nie ograniczać. W tym p o m o ż e nam właśnie operator new. Jeśli dostaniem y - już w trakcie pracy p ro g ra m u - kom unikat, że sam olot wchodzi n ad n asze terytorium , to dopiero w te d y zdefiniujem y now y obiekt. Także nie m a problem u, gdy będą odbyw ać się p o k azy lotnicze i w grę będzie w chodzić dodatkow ych 100 obiek tów. B ędzie trzeba, to się je - już w trakcie pracy program u - zrobi operatorem new. N ie m a też problem u, jeśli o dbędzie się nalot dy w an o w y - proszę bardzo: now e 4000 obiektów . W szystko to dzięki operatorow i new (i jego satelicie - operatorow i d e l e t e likw idującem u potem te obiekty).
Inna sytuacja, kiedy mogą się jeszcze przydać new i delete. Potrzebujem y wielkiej tablicy. Deklarujem y ją na przykład tak: lo n g t a b l i c a [4*8192];
294
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci a tu w trakcie lin k o w an ia do stajem y inform ację, że jest to błąd, p o n iew aż linker na tak w ielkie tablice się n ie zg ad z a. Łączna su m a kom órek z d an y m i me może dla niego przekroczyć n p . 64 KB i już. Co robić? Jest odpow iedź: O szukać go za pom ocą dynam icznej rezerw acji tablicy ju ż w trakcie w y k o n y w an ia program u.
8 111
Operatory new i d©lete albo Oratorium Stworzenie Świata Po takiej reklam ie pora n a przed staw ien ie. W rolach głów nych w y stąp ią spec jalne o p erato ry new i d e l e t e ^ . O p erato r new zajm uje się kreacją, a d e l e t e , unicestw ian iem obiektów . Do rzeczy: Jeśli m am y zd efin io w an y np. taki w skaźnik: c h a r *wsk; to następująca instrukcja: wsk = new c h a r ; p o w o d u je u tw o rzen ie n o w eg o obiektu ty p u c h a r . N ie m a on n a z w y , ale jego ad res p rzek a zy w a n y jest w sk aźn ik o w i wsk. Z kolei instrukcja d e l e t e wsk; p o w o d u je likw idację tego obiektu. (Z akładam , że w skaźnik w sk n a d a l pokazy w ał na ten obiekt). Inny p rzy k ład : d o u b le *w; w = new d o u b le [ 1 5 ] ; O statn ia instrukcja p o w o d u je utw orzenie piętnastoelem entow ej tablicy typu d o u b l e . Tablica ta oczyw iście nie m a nazw y, ale w skaźnik jest in form ow any o jej adresie. K asow anie tej tablicy realizujem y instrukcją d e le te
[] w;
Zauważtutaj wystąpienie symbolu [] . U ż y w a się go zołaśnie w przypadku kasowania tablic. (Wrócimy do tego w następnym paragrafie).
Cechy obiektów stworzonych operatorem new C ztery sp raw y są tu b a rd z o ważne:________________ 10)
ang. new - nowy [czytaj:„nju"] ang. delete - usuń [czytaj: „dilit"]
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
295
♦> O b ie k ty tak u tw o rz o n e istn ieją od m o m en tu , g d y je u tw o rz y m y o p e ra to re m new, d o m o m en tu , g d y je sk asu jem y o p e ra to re m delete. In a czej m ó w ią c - to m y d e c y d u je m y o czasie ich życia. ♦♦♦ O b ie k t ta k u tw o rz o n y n ie m a n azw y . M o żn a n im op ero w ać ty lk o za p o m o c ą w sk aźn ik ó w . ♦♦♦ S koro o b iek ty tak ie nie m ają nazw - O b ie k tó w tych (ich n azw ) n ie o b o w ią z u ją z w y k łe z a s a d y o zak re sie w a ż n o śc i.
Zasady te określałyby to, w których miejscach programu obiekty te są widzialne, a w których niewidzialne (mimo że istnieją). Jeśli tylko jest w d a n y m m om encie d o s tę p n y choćby jed en w sk a ź n ik , k tó ry na taki o b ie k t pokazuje, to m a m y d o tego obiektu d o stę p . ♦♦♦ T y lk o staty czn e ob iek ty w stę p n ie in icjalizo w an e są zeram i (o ile nie o k reśliliśm y inaczej). N a to m ia st obiekty tw o rz o n e o p erato rem new nie są sta ty c z n e (w ręcz p rzec iw n ie - są d y n am iczn e !) d lateg o zara z p o u tw o r z e n iu tk w ią w n ich je szc ze śm ieci. M u sim y sam i z ad b a ć o z a p i san ie ta m sen so w n y ch w artości. O to p rz y k ła d ilu stru jący pro sto tę p o słu g iw an ia się ty m i operatoram i: iinclude using namespace std; char * producent(void) ; // O /★★★★********************»************************************/ int main() char *wl, *w2,
*w3,
*w4,
// definicje wskaźników
// tworzenie obiektów wl = producent(); w2 = producent(); w3 = producent!); w4 = producent(); =
// ©
// O
*wl = 'H'; *w2
©
'M';
*w3 = 'I'; cout <<"oto 3 znaki:" << *wl « *w2 << *w3 « "\noraz smiec w czwartym:" << *w4 << endl;
// ©
delete wl; delete w2; delete w3;
II kasowanie obiektów
©
// *wl = V
II byłaby tragedia, bo obiekt już nie istnieje!!!
©
/************************************************************ */ char * producent(void) // O { char *w; cout « "Właśnie produkuje obiekt \n";
296
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
w = new char; return w;
P "|
po wykonaniu programu na ekranie pojawi się Właśnie produkuje obiekt Właśnie produkuje obiekt Właśnie produkuje obiekt Właśnie produkuje obiekt oto 3 znaki:HMI oraz smiec w czwartym: 1
Uwagi do programu
o D eklaracja funkcji. C z y ta m y ją tak: p r o d u c e n t jest funkcją w y w o ły w a n ą bez ż a d n y c h arg u m en tó w , a zw racającą, jako rezultat: w skaźnik (*) d o obiektu ty p u char. © D efinicja czterech w sk a źn ik ó w m ogących pokazyw ać na obiekty ty p u char. j 0 W y w o łan ie funkcji producent, w której p ro d u k u je się obiekty ty p u char. © O to definicja funkcji p r o d u c e n t . Jak w idzisz - to tutaj, w funkcji, tw o rzy m y obiek ty . W cale nie m u sieliśm y tutaj, w ystarczyłoby w miejscu « napisać instrukcje w l = new c h a r ; Jednak zrobiłem to celo w o w funkcji - po to, by pokazać, że m im o iż obiekty są tw o rz o n e w ew n ątrz funkcji, to jednak nie znikają p o jej zakończeniu (jak to się z w y k le dzieje z ob iek tam i au tom atycznym i tw orzonym i w fun k q ach ).
Jak pamiętasz, zwykłe obiekty definiowane wewnątrz funkcji są tworzone na stosie. Po zakończeniu pracy tej funkcji obiekty ze stosu są uprzątane... © Z u p ełn ie inaczej jest z o b iek tam i tw orzonym i o p erato rem new. Są o n e tw orzone w o b szarze pam ięci, k tóry p rzy zn a w an y jest p ro g ram o w i do sw o b o d n eg o u ży w an ia . O bszar ten po angielsku n azy w a się „free stare sw o b o d n ie do stęp n y m ag azy n ) lub heap (zapas). T ru d n o to d o k ła d n ie przetłu m a czyć. Będę u ży w ał n a z w y „za p as pam ięci", bo najlepiej oddaje isto tę problem u j °
8
Z atem , dzięki o p erato ro w i new , nasza funkcja w łaśn ie tam , w d o stęp n y m z a p a sie pam ięci, zdefin io w ała now y obiekt, a inform acje o ty m , w którym m iejscu konkretnie (ad res), przysłała jako rezu ltat funkcji. ' Po zak o ń czen iu działania funkcji - n o w y obiekt istnieje sobie nadal. B ędzie istniał aż do m o m en tu , g d y w d o w o ln y m miejscu p ro g ram u n ie skasujem y go o p erato rem d e l e t e . © P raca n a now ych obiektach odbyw a się tak, jak na zw y k ły ch obiektach po k azy j w an y ch p rzez w skaźniki. Tu w idzim y w p isan ie czegoś d o naszych trzech o b iek tó w p o k azy w an y ch przez trzy w skaźniki. O czw arty m obiekcie, p o k azy w a n y m p rzez w sk aźn ik w4, celowo zapom inam y.
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
297
© W y p isu je m y na ek ran treść trze ch obiektów - są ta m oczyw iście litery H M I. W y p isu je m y też c z w a rty o b ie k t, d o którego nic jeszc ze nie w p isaliśm y , w ięc z a w ie ra śm ieci. N a ek ran ie p o ja w ia się jakiś sy m b o l b ęd ący w k o d zie ASCII o d p o w ie d n ik ie m tk w iąceg o ta m p rzy p ad k o w eg o śm iecia. Jeśli u ru c h o m isz ten p ro g ra m jeszcze raz, to śm ieć b ęd zie n ajp ra w d o p o d o b n iej inny. Tak by ło w m o im p rz y p a d k u . Jak śm ieć, to śmieć. © K aso w an ie obiektów . Tu w ła śn ie z arezerw o w an e d la nich miejsce jest o d d a w a n e z p o w ro te m d o zap a su p am ięci. E w en tu aln e n a s tę p n e w y w o łan ia funkcji p r o d u c e n t m o g ą ten o b szar z n o w u otrzym ać. © S koro się o d d a ło o b szar pam ięci z po w ro tem d o z a p a s u , to go ju ż nie m a. N asz w sk a ź n ik , co p ra w d a , n ad al p o k a z u je na to m iejsce, ale tam m oże m ieszk ać już k to ś in n y . P róba zap isan ia ta m czegoś zniszczy teg o e w en tu a ln eg o n o w eg o lo k ato ra. Jak zw y k le - tego ty p u b łąd m oże objaw ić się o w iele później niż sam a k t p rz e stę p stw a .
Inicjalizacja obiektu tworzonego operatorem new O b ie k to w i k reo w an em u o p e ra to re m new m ożna n a d a ć w arto ść ju ż w m o m e n cie stw o rzen ia . i n t * w sk; wsk = new i n t (3 2 ); T ak ie u ży cie o p erato ra n e w sp ra w i, że w zap asie p am ięci zo stan ie stw o rz o n y o b ie k t ty p u i n t , a d o niego zo sta n ie od razu w p isa n a liczba 3 2 .^ S k o ro to je st inicjalizacja, w ięc w te n sposób m ożna n a w e t tw orzyć obiekty stałe. Z obacz: c o n s t d o u b le *w sk_pi; wsk p i = new c o n s t d o u b le ( 3 .1 4 1 6 ) ; I I ... . . *wsk p i = 8 8 8 .1 ; // błąd, modyfikować wartości nic wolno d e l e t e wsk p i ; // skasować oczywiście wolno D efin iu jem y w sk aźn ik d o p o k azy w an ia na obiekty stałe ty p u d o u b l e . In s tru k cją n e w tw o rz y m y w zap asie p am ięci obiekt ty p u c o n s t d o u b l e . Stały o b iek t d o p rz e c h o w y w a n ia w artości d o u b l e . O d razu , w ch w ili tw orzenia, w p isu je m y tam w a rto ść 3.1416 - od tej p o ry w artość ta n ie m o że zostać zm ien io n a. L inijkę w id z isz , że kom pilator nie pozw oli na taką instru k cję p rzy p isan ia. N a to m ia s t oczyw iście m ożna tak i obiekt ostateczn ie skasow ać instrukcją delete.
11)
Zwróć uwagę, że ta liczba 32 jest w nawiasie okrągłym. Jeśli byś, przez pomyłkę, postawił ją w nawiasie kwadratowym, kompilator uzna, że prosisz go o stworzenie 32 elementowej tablicy typu i n t .
298
8.11.2
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
Dynam iczna alokacja tablicy Za pom ocą operatora new m o żn a tw orzyć (kreow ać) nie tylko pojedyncze obiekty, ale także i tablice. S łuży d o tego o p erato r new [ ]. Ciekawostka: Nie jest to inna składnia poprzedniego operatora new, lecz odrębny operator. Jakby brat bliźniak tamtego. U życie tego operatora jest niezw y k le proste. i n t * ta b p tr; t a b p t r = new i n t [rozmiar] ; g d zie rozmiar jest w y rażen iem ty p u całkow itego (o w artości w iększej od zera). W ten sposób stw orzyliśm y n ien azw an ą tablicę elem en tó w typu i n t . W yni kiem działania o peratora new [ J jest w sk aźn ik do początku tej tablicy. Podsta w iam y go d o naszego w sk a źn ik a t a b p t r . P ow yższe dw ie linijki m o żn a n ap i sać krócej jako: * t a b p t r = new i n t [rozmiar]; Z a u w a ż , że rozm iar tablicy nie m u si być stalą. P rzy p o m in am , że p rz y tradycyj n y m sposobie definiow ania tablic rozm iar m usiałby być stałą z n a n ą juz w m om encie kompilacji. i n t t a b l i c z k a [1 5 ]; O p e ra to r new daje n am sw obodę. Tablica definiow ana jest dynam icznie, w trakcie w ykonyw ania p ro g ra m u . cout << "Ile elementów ma mieć tablica ? \n";
i n t rozm; c i n » rozm; i n t * t a b p t r = new in t [ r o z m ] ; //--------- praca z tablicą-------------------------* t a b p t r = 44; II wpisanie do zerowego elementu t a b p t r [0] = 4 4 ; II to samo inaczej
* ( t a b p t r + 3) = 100; // wpisanie do elementu o indeksie 3 t a b p t r [3] = 100; II to samo inaczej M ów iliśm y kiedyś o tym , że zap is w skaźnikow y i tablicow y są w zasadzie w y m ien n e, dlatego m ożem y d o naszej tablicy stosow ać ró w n ie d o b rze zapis „tablicow y". O ba zapisy p o k azałem w pow yższym p rzy k ład zie. Z apis „tablico w y " w ydaje mi się łatw iejszy i bardziej naturalny. Jed n ak uw aga: jeśli pow iedzieliśm y, że w ystarczy n am ro zm iar 2 - to m am y tablicę tylko d w u elem en to w ą. Sami jesteśmy w inni, jeśli p o tem pracujem y na nieistniejącym elem encie czw arty m i zd arzy się trag ed ia (strzał na oślep). A b y zlikwidować tak w y k reo w an ą tablicę, sto su jem y o p erato r d e l e t e []• To, z kolei, brat bliźniak poprzedniego operatora d e l e t e . Ten służy do zwalniania rezerwacji tablic, czyli tego co rezerwowaliśmy przez new [ ] . O to, jak odw ołuje się n im rezerw ację naszej ostatniej tablicy delete [] tabptr;
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
299
w ynik d ziałan ia operatora d e l e t e jest typu v o i d (czyli nie zw racan y jest żad e n typ).
W Nie ma sposobu, by... I£ 3 P
...elem entom tablicy obiektów (typów w bud o w an y ch ) - definiow anej operato rem new - n adać w artości początkow e. Sposób "z naw iasem " - p o k azan y w p o p rz e d n im paragrafie - nadaje sie tylko do inicjalizacji pojedynczego obiektu (ale nie tablicy!). Dla w tajem niczonych: Jeśli chodzi o typy definiowane przez użytkownika, to problem rozwiązuje tu tzw. konstruktor domniemany.
.11.3
Tablice w ielow ym iarow e tworzone operatorem new O p erato rem new [ ] m ożna też stw orzyć tablicę w ielow ym iarow ą. Pam iętać w ów czas należy, iż tylko w ym iar "najbardziej z praw ej", m oże być określony p rzez zm ienną. Inne zaś m uszą być stałym i całkow itym i i to, w d o d atk u , w iększym i od zera. Z obaczm y to w takim fragm encie program u:
int i l e ; //to n ie j e s t II .. . cout « "Podaj rozmiar? "; c in >> i l e ;
-r e z e r w a c ja ta b lic y w ie lo w y m ia r o w e j
//-
int
o b ie k t s t a ł y !
-
( * t a b li c a ) [7] [4] = new i n t [ i l e ] [7] [4 ] ;
/ / -----------------------------------------------------------------------// ta k t e g o u ż y w a m y tablica[21[4][3] = 1000; cout « t a b l i c a [2] [4] [3] « endl;
// a t a k l i k w i d u j e m y d e l e t e [] ta b li c a ;
Kilka uwag: ❖
W artości 7 i 4 są stałym i dosłow nym i, ale oczyw iście m oglibyśm y je zastąpić obiektam i c o n s t i n t o w łaśnie takich wartościach.
❖
Instrukcję
in t
( * t a b l i c a ) [7][4]
=
new i n t [ i l e ] [7] [ 4 ] ;
m ożem y inaczej zapisać jako d w ie instrukcje:
300
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
// d e f i n i c j a w s k a ź n i k a // r e z e r w a c j a t a b l i c y
i n t ( * t a b l i c a ) [7] [4] ; t a b l i c a = new i n t [ i l e ] [ 7 ] [ 4
A le, jeśli i to nie je st d la C iebie czytelne, p rzy jrz y jm y się d o k ład n iej.
Najpierw temu, co stoi po prawej stronie new i n t [ i l e ] [ 7 ] [ 4 ] W ty m w y ra ż e n iu o p e ra to r new [ ] tw orzy TABLICĘ tablic. O p e ra to r new p o z w a la na to, by TABLICA m ia ła w y m iar b ę d ą c y w arto ści, z n a n ą d o p ie ro w tra k c ie w y k o n an ia p ro g ra m u . To w łaśn ie w a rto ść sch o w an . w z m ien n ej i l e . O p e ra to r new [ ] tw o rz y TABLICĘ czegoś-tam . Ilu -elem en to w a to ta b lica , to dl; k o m p ila to ra m niej isto tn e , n ato m iast p o szcze g ó ln e elem en ty tej TABLIC m u s z ą m ieć stały ro z m ia r, z n a n y ju ż w czasie kom pilacji. A czy m są p o sz c z e g ó ln e elem en ty tej TABLICY? Jest to TABLICA tablic. Owi tablice są d w u w y m ia ro w y m i tablicam i o sta ły c h w ym iarach: 7 r z ę d ó w ,, * k o lu m n y . C zyli p o sz c z e g ó ln y m i elem en tam i TABLICY są takie m a łe tablicz i int[7] [4] r Ich w y m ia ry m u s z ą b y ć sta ły m i. . Tylko w tedy kompilator może skompilować różne miejsca w programie gdzie z wielowymiarowej tablicy korzystamy. To dlatego, że o ile, pierwszy rozmiar z lewej tablicy wielowymiarowej nie uczestniczy w obliczami adresu danego elementu tablicy, ale pozostałe - tak!
Zajmijmy się teraz lewą stroną naszej wspaniałej instrukcji in t
( * t a b l i c a ) [7] [4] •• \
IV-
new
T a le w a stro n a istnieje d la te g o , ż e o p e ra to r n e w [ ] zw raca n am p rz e c ie ż a d r ę TA BLICY czeg o ś-tam , w ięc trzeb a ten a d re s g d z ie ś z a p a m ię ta ć . O c z y w iśc i trz e b a g o z a p a m ię ta ć w e w sk a ź n ik u do c z e g o ś-tam . Tak, jak przy tworzeniu operatorem n e w [ ] tablicy typu d o u b l e , trzeb rezultat zapamiętać we wskaźniku do typu d o u b l e d o u b le * ta b d = new d o u b l e [ 7 ] ; Z a te m p o lew ej s tro n ie m a m y zd e fin io w a ć ó w w sk a ź n ik d o cze g o ś-tarr P o w ie d z ie liś m y so b ie ju ż, ż e - u n as - to "cos-tam " o z n a c z a tabliczkę typi i n t [7] [4].
N o w ięc z n o w u w ró ciliśm y d o ćw iczeń z d e k laracja m i. Z a d a n ie , napis, d efin icję w sk a ź n ik a m o g ą c e g o p o k a z y w a ć n a coś-tam ... p rz e p ra s z a m , n tablice d w u w y m ia r o w e (o ro zm iarach [7] r z ę d ó w [4] k o lu m n y ) elem en tó v ty p u i n t . O to ro zw iąz an ie: in t
(*wsk) [7] [ 41;
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
301
Taki w łaśnie w sk a źn ik stoi po lewej stro n ie naszej instrukcji. (C hoć nazyw a się trochę inaczej). i® *
Z w róć u w a g ę na n aw ias okrągły. M usi tu być, bo operatory [ ] są mocniejsze niż o p e ra to r *. Jeśli w ięc zapom nisz o tym naw iasie, to p rz e z pom yłkę zdefin iu jesz tutaj... no, co? Z definiujesz tutaj tablicę d w u w y m iaro w ą [7][4], której elem en tam i będą w sk aźn ik i d o o b iek tó w typu i n t . Innym i słowy, zamiast wskaźnika do tablicy zdefiniujesz tablicę wskaźnikólo. K om pilator w ów czas zaprotestuje m ów iąc, że to, co stoi p o praw ej stronie zn ak u =, nie nadaje się d o przypisania tem u , co po lewej. Z ap o m n ien ie o tym naw iasie, jest głów ną p rzy czy n ą tego, że n iektórzy nie potrafią definiow ać operatorem n e w [ ] tablic w ielow ym iarow ych.
Może też skojarzysz to wzrokowo O to definicja czterow ym iarow ej tablicy i definicja w sk aźn ik a, do którego m ożna schow ać jej ad res:
new typ
[r o z m ia r ]
[100] [12] [56]
typ (*wsk)[100][12][56] Jak w id zisz, w ystarczy tu [rozmiar] zam ienić na (*wskażnik). O czyw iście zam iast te g o słowa "ws k", lepiej dać stosow niejszą n azw ę tej tablicy.
.11.4
Umiejscawiający operator new M yślę, że ten p arag raf m ożesz przy p ierw szy m czytaniu tej książki opuścić. O m aw iane tu zag ad n ien ie nie jest trudne, ale może zbyt szczegółow e. W po czątkow ej fazie nauki lepiej chyba najpierw nabrać ogólniejszego spojrzenia na całość spraw . Zbytnia ilość szczegółów m oże przeszkodzić Ci w odróżnieniu rzeczy w ażnych od niew ażnych. W d o d atk u m oże też Cię tro ch ę zniechęcić, dlatego bez w y rzu tó w sum ienia przejdź d o czytania następ n eg o p aragrafu.
X Porozm aw iam y teraz o szczególnym rodzaju użycia o p erato ra new. Zw ykle operator rezerw uje pam ięć, a następnie p o w o d u je zb u d o w an ie tym miejscu obiektu. Teraz zobaczym y, że operator m oże nie rezerw ow ać pam ięci, jeśli pod am y m u miejsce, które należy już d o nas (jakby z a k u p io n y p rzez nas wcześniej plac budo w y ) - wówczas jedynie w y buduje on na d a n y m miejscu żąd a n y obiekt.
Zapewne ciekawią Cię dwie sprawy: ♦♦♦ 1. Jak kupić w cześniej taki plac budow y?
302
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci •
- B a r d z o p r o s to , n a p r z y k ł a d r e z e r w u ją c w c z e ś n ie j ta b lic ę t y p u char. S łu ż y d o te g o z w y k ła in s tr u k c ja :
char ‘wskaźnik = new char [r o z m i a r ]; 2. C h c e m y m ie ć n o w y o b ie k t ty p u Typ. Ja k p o w i e d z i e ć o p e r a t o r o w i , g d z ie m a g o n a m z b u d o w a ć ? • N a j p i e r w u s ta w ić s o b ie ja k iś w s k a ź n ik p o k a z u j ą c y d o k ła d n ie n a u p a t r z o n e m ie js c e gdzie m a p o w s ta ć n o w y o b i e k t , p o c z y m p o s ł u ż y ć s ię in s tr u k c ją :
*
*wsk = new
T yp
( g d z ie )
T yp;
T a k to w y g l ą d a w d z i a ł a j ą c y m p r o g r a m ie . #include u s i n g n a m e s p a c e std;
^
0
/ i? * ^ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7 i n t m a i n ()
// W s tę p n a r e z e r w a c ja d u ż e g o o b s z a r u p a m ię c i //( c z y l i k u p u j e m y g r u n t n a o s ie d le d o m ó w ) int
*osiedle
for(int
i =
= 0
new ; i
int[5000] ;
. . .
//N iw e lo w a n ie
<
5000
; i++)
//T e r a z = =
//— <<
p r a c a z ty ta b lic y
m
=
0
p ra c a
n o w e g o o b ie k tu
z ty m
o b ie k te m
— ; m
<
3
; m++) =
"
) <<
endl;
gdzie = s o s i e d l e [106]; d o u b l e * w T a b d = n e w (gdzie) H — p r a c a z t y ta b lic y
for(int
n =
wTabd[n]
0 =
— ; n
1 +
<
3
<0.1
n++)
* n);
--
endl;
int[3];
w T a b i [in] — 1 0 0 0 + m; cout « " w T a b i [" < < m < < "] << w T a b i [m] < < " "; cout
_
/ /
&
//
o
n a t y m te r e n ie m o ż e m y t w o r z y ć o b ie k ty
(* w s k i n t ) <<
g d z i e = i o s i e d l e [1 0 2 ] ; i n t * w T a b i = n e w (gdzie) for(int
z d o b y te g o w ła ś n ie o b s z a r u = li
^ ^
=======
Sosiedle[100] ; n e w (gdzie) int;
* w s k i n t = 222; c o u t << " * w s k i n t
//—
,
osiedle[i]
//u m ie s z c z e n ie v o i d *gdzie int *wskint
, , .
d o u b l e [3];
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci cout « «
303
"wTabd[" « wTabd[n] «
n << "] = " " "i
i
cout << endl; II w m i e j s c u o a d r e s i e p o d a r t y m l i c z b o w o
=====================
cout << "Napisz jakiś adres pomiędzy: " << reinterpret_cast(Sosiedle[112])
«
//©
" - "
<< reinterpret_cast(Sosiedle[116]) << "\na ja tam zbuduje ci obiekt: int adres; cin >> adres; gdzie = reinterpret_cast (adres); int * wskA = new (gdzie) int;
//— p r a c a
z t y m o b ie k te m
—
*wskA - 114; cout « "Wartość = " «
(*wskA) «
endl;
cout << "Zobaczmy na te działkę \n"; for(int k = 99 ; k < 116 ; k++) cout « "[" « k « "]=" « osiedle[k] << endl; delete
[] osiedle;
Po wykonaniu tego programu na ekranie pojawi się: *wskint = 222 wTabi[0] = 1000 wTabifl] = 1001 wTabi[2] = 1002 wTabd[0] = 1 w T a b d [1] = 1 . 1 w T a b d [2] = 1 . 2 Napisz jakiś adres pomiędzy: 3614376 - 3614392 a ja tam zbuduje ci obiekt: 3614377 Wartość = 114 Zobaczmy na te działkę [99] =1 [ 1 0 0 ] =222 [ 1 0 1 ] =1 [
102]=1000
[103] =1001 [104] =1002 [105]=1 [106]=0 [107] =1072693248 [108] =-1717986918 [109] =1072798105 [110] =858993459 [1111=1072902963 [ 112 ] =1
[1131=29185 [114] =0 [115] =1
//O 0
304
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
Kilka słów komentarza O A by p o słu g iw ać się um iejscaw iającym operato rem w łączen ie pliku n ag łó w k o w eg o
new, m oże być konieczne
.
0 W stęp n ie rezerw u jem y w iększy obszar pam ięci. W idzim y tu zw ykłe użycie o p e ra to ra new. © Tak p am ięta m y - zd o b y ty o peratorem new obszar nie jest w stę p n ie zerow any, czvli z aw ie ra „śm ieci". D obrze jest ten obszar najpierw u p rzątn ąć. (Choć m e jest to konieczne!) Tu zd ecy d o w ałem , że do każdej z kom órek w staw ię liczbę 1. O O to p o stan aw ia m , że chciałbym stw orzyć jakiś obiekt w miejscu pam ięci, gdzie iest setn y elem ent tablicy osiedle. Tu w id zisz definicję w sk aźn ik a o nazw ie o d z i e W skaźnik od razu inicjalizow any jest ad resem setnego elem entu tabli cy Z a u w a ż , że w sk aźn ik ten m oże być typu void. W skaźnik ten m e musi p rzecież w iedzieć czy ja w tym miejscu będę tw orzył obiekt ty p u g m a c h _ o p e r y , czy b u d k a _ z J o d a m i . W skaźnik ten ma tylko pokazać na m iejsce, g d zie ma zajech ać ekipa b u d o w lan a. © Ta instru k cja jest esencją tego p rzy k ła d u . W szystkie dalsze, to ty lk o „w ariacje te m at". Dla w iększej czytelności rozpiszm y tę instrukcję na d w ie prostsze i n t * w sk in t; wskint - new (gdzie) int;
lak w id ać, jest to definicja w skaźnika do typu int.Takiego bo w iem typu obiek b ęd ziem y za chw ilę b u d o w ali operatorem new. W następnej linijce w idzis: w y w o łan ie operatora new. Tuż za słow em new stoi w n aw iasie w skaźnik p o k azu jący na miejsce, gdzie m a się zacząć b u d o w a. Dalej stoi oczywiści# sło w o int inform ujące o perator, jaki to ty p obiektu m a zb u d o w ać. W rezultacu p o w staje obiekt, a jego adres operator new w pisuje do n aszeg o w skaźnika © O b s t r u k c j e , w których pracujem y z tak zb u d o w an y m obiektem . W idzim y żt w p is a n a zostaje tam w arto ść 222, potem pojaw ia się to na ekranie. N ic w tyn nadzw y czajn eg o . N ad zw y czajn e jest to, g dzie ten obiekt jest. O tym , ze jes n a p ra w d ę tam , g dzie zażądaliśm y, przekonają nas ostatnie instrukcje teg< p ro g ra m u . O M o żem y też zażądać b u d o w y nie pojedynczego obiektu, ale tablicy. Linijkę w cześniej przestaw iliśm y w skaźnik gdzie na inne miejsce. Tutaj w idzis: w y w o ła n ie operatora new w spraw ie stw orzenia trzyelem entow ej tablicy ty p il in t W szystko jest jak zw ykle, jedynym d o d atk iem jest um ieszczenie w skazm ka pokazującego na miejsce, gdzie tablica ma m ieć początek. © A byś nie myślał, że na terenie tego obszaru m ożem y b u d o w ać o p erato rem n ty lk o obiekty typu int, oto przykład budow y tablicy ty p u double. 7 P o n ie w a ż p r a w d o p o d o b n ie c z y ta s z tę k s ią ż k ę p o r a z , co n a jm n ie j, d r u g i w ię c ś m ia ło m o g ę p o w ie d z ie ć , ż e z w y k le b u d u je s ię o b ie k ty ja k ic h ś z d e f in io w a n y c h p r z e z n a s k la s . ( N i k t b y s o b ie p r z e c i e ż n ie z a w r a c a ł g ł o w y u ż y w a n ie m te g o o p e r a to r a w o b e c ty p ó w w b u d o w a n y c h ) .
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
305
© A d res m o ż n a p o d a ć z k la w iatu ry , w p o staci liczby całkow itej. Tu w idzisz, że d o stajem y p ro p o z y c je z b u d o w a n ia czegoś na z a d a n y m obszarze. A b y na ek ran ie w y p is a ć liczbow o ad res, trze b a p o słu ży ć się tu o p erato rem reinter pręt cast. Jak w id zisz, u ż y ty on jest w tej instrukcji d w u k ro tn ie - aby w y p isać p o c z ą te k i koniec d o p u sz c z a ln e g o tutaj o b szaru . © Jeśli u ż y tk o w n ik , na p y ta n ie o a d res, o d p o w ie p o d ając liczbę, m u sim y ją zam ien ić na w sk a ź n ik p o k azu jący na k o m ó rk ę o takim a d resie . Znow u zatem o p e ra to r reinterpret_cast, z tym , że teraz w d ru g ą s tro n ę (z liczby całko w itej na w sk a ź n ik void*). O O S tw orzen ie o b ie k tu w tak w sk a zan y m miejscu.
A teraz chwila prawdy O © S p ra w d z a m y , czy n asze obiekty p o w sta ły w m iejscach, k tó re w skazaliśm y. (O c z y w iś c ie w p r a w d z iw y m
p r o g r a m ie n ie m u s is z te g o s p r a w d z a ć ! ).
N asze s p ra w d z e n ie po leg a na tym , że w y p isu jem y na e k ra n ie fragm ent tablicy osiedle, a k o n k re tn ie w artości w elem en tó w o n u m e ra ch 99-115. Spójrz na e k ra n . Do p ierw szeg o o b iek tu w p isy w a liśm y w a rto ść 222. U m iejsco w io n y m iał o n b y ć na teren ie elem en tu 100. N a ek ran ie w id ać, ż e rzeczyw iście w ty m elem en cie jest taka w artość. M o żem y to tak ła tw o sp raw d zić, bo a k u ra t tablica osiedle jest tak że typu int. Dalej tw o rz y liśm y trzy e lem en to w ą tablicę ty p u int. M iała się zaczynać w m iejscu e le m e n tu osiedle [ 1 0 2 ] . N a ek ran ie w id z im y w pisane d o niej w artości. G d y b y śm y teraz stw o rzy li tablicę in n e g o ty p u , na p rzy k ła d double, lu b „budka_z_lodami", to nie zobaczyliśm y teg o tak w p ro st. Dalej w n a s z y m p rz y k ła d zie b u d o w a liśm y tablicę ty p u double p o cząw szy o d a d re s u e le m e n tu osiedle [ 1 0 6 ] . M ożesz na ek ran ie zobaczyć, że coś tam p o w stało i z o s ta ło z a p isa n e jakąś treścią, ale tej treści tu nie d a się odczytać, bo są
O stateczn ie sp ó jrz w okolice elem entu 112. P o n iew aż celo w o napisałem za pom ocą k la w ia tu ry ad res, który zac zy n a się od k aw ałk a elem entu 112 w ięc częściow o m ój o b iek t teraz zo stał z ało żo n y na fragm encie p arce li nr 112, a reszta n a parceli 113. O czyw iście w y n ik ało to z faktu, że p arcele b y ły typu int, (czyli w m oim p rz y p a d k u - czterobajtow e). O czyw iście z w y k le robi się tak, że rezerw u je się p am ięć w p o staci tablicy typu char (czyli je d e n bajt). G d y b y m tak tutaj p o prostu zrobił - p ro g ram działałb y tak sam o , a le ten o stateczn y w y d ru k n a ek ran n ie był b y tak pouczający (p rzy n ajm n iej jeśli chodzi o u d o w o d n ie n ie Ci w ła ściw eg o um iejscow ienia o b iek tó w ty p u i n t ) .
306
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
Kiedy taki umiejscawiający operator może się przydać? N ajczęściej, g d y m am y p raco w ać z wieloma obiektam i, które mają ciągle pow staw ać i ginąć. . . , . Wyobraź sobie symulację wrzenia cieczy, w której ciągle, w całej objętości, tworzą się nowe bąbelki pary i giną po dotarciu do powierzchni cieczy. C zas zu ży ty na ciągłe rezerw acje i zw alnianie pam ięci m oże być w te d y zn a czący. Lepiej w ted y zro b ić to raz, hurtowo. Z d ru g iej stro n y - zac zy n am y w ted y sami być o d p o w ied zialn i za g o sp o d ark ę tym m iejscem i o d p o w ied zialn i rów nież za to, by obiekty były sen so w n ie na tym o b szarze ro zm ieszczan e (nie kolidow ały ze sobą). N ie jest to zad a n ie dla now icjusza.
8.11.5
"Przychodzimy, odchodzim y - cichuteńko, na..." Za pom ocą operatora delete (lub delete [ ] ) - powinno się kasować tylko obiekty stworzone operatorem new (lub new [ ]). Próba skasowania czego kolw iek innego może się okazać katastrofalna.
N ie b ę d z ie jed n ak nieszczęścia, jeśli zastosujem y o p erato r de1ete w sto su n k u d o w sk a ź n ik a p o k azującego na adres zerow y (daw niej zw a n y NULL ) - takie sy tu acje k o m p u ter sam ro zp o zn aje, bo żaden o b iek t nie m oże m ieć ad resu 0.
Uwaga na pułapkę: rrg p
n
Ł atw o się dom yślić, że n ie n ależy dw ukrotnie k asow ać o b ie k tu . C h o d zi o sy tuacięf g d y o b iek t stw o rzy liśm y operatorem new, p o tem skaso w aliśm y g o opera to rem delete. O biekt ju ż nie istnieje. Tym czasem p rz e z zap o m n ien ie jeszcze raz b ie rz em y w skaźnik pok azu jący na to miejsce w pam ięci i p o w tó rn ie w yko n u je m y kasow anie. R ezu ltat będzie niefortunny. Błąd nie m u si u ja w n ić się od ra z u , d la teg o trudniej g o w ykryć. Ja ra d z ę sobie w takich sytuacjach tak, że łącznie z k aso w an iem o b iek tu , um ie sz c z a m od razu instrukcję ustaw iającą w skaźnik na 0. Jak już w iem y, ew en tu a ln e użycie delete do sk aso w an ia czegoś o ad resie 0 (NULL) nie stw arza problem u, p o p ro stu o p erato r nie robi nic.
w sk = new i n t ; *wsk = 15; d e l e t e wsk; w sk = 0; // ... d e l e t e w sk;
// ( c z y l i // s k o r o
NULL) w s k m a w a r to ś ć 0 , to n i e b ę d z i e t r a g e d i i
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
307
Druga pułapka przy tworzeniu obiektów operatorem new P ow iedzieliśm y, ż e ob iek ty tw o rzo n e z a pom ocą o p erato ra new nie mają nazw Pracujem y z n im i tylko za p o śred n ictw em w skaźników . Jest tu - w zw iązku ; tym - p u ła p k a. S pójrz na ten program : #include ............. ...................................................................
int main() { int *cze, *zol;
//definicja dwóch wskaźników
O
cze = new int; zol = new int;
//tw orzym y obiekt A //tw o rzym y obiekt B
©
*cze = 100; *zol = 200;
//ładujemy 100 do obiektu A //ładujemy 200 do obiektu B
©
«
czerwonym = "<< *cze * zol « endl;
" Na zoltym = " «
cze = zol;
O
//<-- Niefortunna linijka !
cout << " Po przełożeniu - Na czerwonym = "<< *cze « " Na zoltym = " « * zol « endl; *cze = 5; *zol = 1;
//
©
//
©
cout << " Jakiś wpis - Na czerwonym = "<< *cze « " Na zoltym = " « * zol « endl; delete zol; // delete cze;
II Horror !
)
Po wykonaniu programu na ekranie zobaczymy Po wpisaniu: Na czerwonym = 100 Na zoltym = 200 Po przełożeniu - Na czerwonym = 200 Na zoltym = 200 Jakiś wpis - Na czerwonym = 1 Na zoltym = 1
Uwagi O N ajpierw d efin iu jem y sobie dw a w sk aźn ik i d o obiektów ty p u
int.
0 N astęp n ie o p e ra to ra m i new tw orzym y d w a (nienazw ane) o b iek ty typu int, a ich ad resy w sta w ia m y do w skaźników : czerw onego cze i żó łteg o zol. P rzy p o m in am , ż e w artością w yrażenia (new int) jest ad res n o w o w y tw o rzo n eg o obiektu. © D o obiektów p o k azy w an y ch przez żółtego i czerw onego ła d u jem y 100 oraz 200.
308
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
O T a lin ijk a je s t is to tą n a s z e g o p r z y k ła d u . S p ra w ia , ż e w s k a ź n ik c z e r w o n y p o k a z u je o d tą d n a to s a m o , n a c o p o k a z u je w s k a ź n ik ż ó łty . A d r e s m ie jsc a , n a k tó r e p o k a z y w a ł d o tej p o r y w s k a ź n ik c z e r w o n y , z o s ta je p r z e z n ie u w a g ę z n is z c z o n y , z a p o m n ia n y . O d tej p o r y o b ie k t te n s ta je się d la n a s n ie d o s tę p n y . © N ie z a le ż n i e , k tó r y m w s k a ź n ik ie m s ię p o s łu g u je m y , p ra c u je m y te r a z n a ty m s a m y m o b ie k c ie - p o k a z y w a n y m p ie r w o tn ie p r z e z w s k a ź n ik ż ó łty . © N ie p o tr z e b u je m y ju ż o b ie k tó w , w ię c k a s u je m y je. N a jp ie r w te n p o k a z y w a n y p r z e z w s k a ź n ik ż ó łty . N a to m ia s t o b ie k tu A p o k a z y w a n e g o p ie r w o tn ie p r z e z w s k a ź n ik c z e r w o n y m e m o ż n a ju ż n ig d y s k a s o w a ć . O p e r a to r delete ż ą d a b o w ie m p o k a z a n ia m u w s k a ź n ik ie m , k tó r y to o b ie k t m a s k a s o w a ć . T y m c z a s e m m y tę in f o r m a c je s tr a c iliś m y w lin ijce G . T e r a z n a te n o b ie k t n ie p o k a z u je ż a d e n w s k a ź n ik . G d y b y ś m y u s u n ę li z tej lin ijk i k o m e n ta r z , to o d b y ło b y s ię tu ta j k a ta s tr o f a ln e , p o w t ó r n e k a s o w a n ie o b ie k tu ju ż r a z s k a s o w a n e g o . (O b ie k tu B). O p is a n ą s y tu a c ję m o ż n a p r z y r ó w n a ć d o ta k ie g o o b r a z k a : M a ły i p s o t n y J a ś u w ie lb ia p r z e k ł u w a ć s z p ilk ą n a p e łn io n e g a z e m b a lo n ik i. B ie rz e w o b e c te g o z d o m u d w a s z n u r k i o k o lo r a c h ż ó łty m i c z e r w o n y m O , id z ie d o u lic z n e g o s p r z e d a w c y w p a r k u i k u p u je d w a n o w e ( n e w ) b a lo n ik i (o b ie k ty t y p u i n t ) © . S p r z e d a w c a i Jaś p r z y w ią z u ją b a lo n ik i d o s z n u r k ó w . B a lo n ik i są n ie r o z r ó ż n ia ln e , b o m a ją te n s a m k o lo r (n ie m a ją n a z w ) , a le Jaś m a d o n ic h d o s tę p z a p o m o c ą s z n u r k ó w - c z e r w o n e g o i ż ó łte g o . Ja ś m ó g łb y o d r a z u sw o je b a lo n ik i p r z e k łu ć , a le c h c e je s z c z e s ię n im i c h w ilę p o b a w ić . M a lu je n a n ic h lic z b y 100 i 200. © P o te m w p a d a n a n ie r o z w a ż n y p o m y s ł: o d w ią z u je c z e r w o n y w sk a ź n ik o d b a lo n ik a z lic z b ą 100 i d o w ią z u je g o d o b a lo n ik a z lic z b ą 2 0 0 . O B a lo n ik z lic z b ą 200 je s t te r a z n a d w ó c h s z n u r k a c h : c z e r w o n y m i ż ó łty m . - A te n b a lo n ik z lic z b ą 100? N o c ó ż , s z y b u je te r a z k u p r z e s tw o rz o m . N ie o s tr o ż n y Ja ś s tra c ił g o n a z a w s z e . N ie m o ż e g o j u ż te ra z p r z e k łu ć . O s ta ł m u s ię je n o s z n u r ( c z e r w o n y i ż ó łty ) z je d n y m b a lo n ik ie m . C ią g n ijm y d a le j tę o p o w ie ś ć : Jaś w y jm u je z k ie s z e n i ig łę i p r z e k ł u w a to , co je s t n a k o ń c u s z n u r k a ż ó łte g o . B a lo n ik z h u k ie m p ę k a ( d e l e t e ) © . U p a r ty lu b r o z t a r g n io n y Jaś c h c e te r a z p r z e k łu ć to , c o je s t n a k o ń c u s z n u r k a c z e r w o n e g o . T y lk o ż e d r u g i s z n u r e k b y ł p r z y w ią z a n y d o te g o s a m e g o b a lo n ik a (w ła ś n ie p r z e k łu te g o ) . Ja ś d ż g a ig łą w p o w ie t r z e i w m ie js c u , g d z i e d o tej p o r y b y ł w ła ś n ie p r z e k ł u ty b a lo n ik , n a tr a f ia n a o k o M a łg o s i. H o r r o r !
W
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
309
dzie b a rd z o d u ż o . W yczerpie to p rzy zn an y nam o b sz a r zap asu pam ięci {free storę) i ju ż ż a d n y c h now ych o b iek tó w nie będziem y m o g li w naszym p ro g ram ie tw orzyć.
B.11.6
Zapas pam ięci to nie jest studnia bez dna K o m p u tery ciągle się u n o w o cześn iają, mają coraz w ięcej tak zw anej pam ięci operacyjnej, w ięc sytuacja, o której tu będziem y ro zm aw iać, nie jest ju ż tak p o w szech n a jak daw niej. M im o to, zaw sze jest jakaś g ranica. M ożliwe, że n asz
K rótko m ó w ią c - m oże się o kazać, że w p ew n y m m om encie nasz o b szar dostępnej p am ięci się w yczerpie. W ów czas próba u tw o rz e n ia now ego obiektu (np. ty p u d o u b l e ) za pom ocą w y rażen ia w s k a ź n ik
=
new d o u b le ;
nie m oże się u d ać. J a k k o m p u te r p o w ia d o m i n a s o ty m ?
M a na to trz y sposoby: z w ro t ad resu zero - (sposób "dawny"), ♦♦♦ rz u c e n ie w yjątku b a d _ a l l o c - (sposób: "now oczesny"), ♦♦♦ w y w o łan ie jakieś zap ro p o n o w an ej przez nas fu nkcji. D w a z tych sp osobów om ów im y teraz, trzeci w n a stę p n y m paragrafie. ■>al D aw ny s p o s ó b p o w ia d o m ie n ia : zw rot a d re s u z e ro S praw a jest p ro sta. Jeśli operator n e w , nie m oże dla nas stw o rzy ć obiektu, (bo nie m a miejsca w pam ięci) - to zam iast zw rócić a d re s stw o rzo n eg o o b ie k tu - zw raca rezu ltat 0. Ten p ro sty sp o só b był stosow any w (niedaw nych) czasach , g d y nie było jeszcze w języku C + + narzędzia zw an eg o "Obsługą sytuacji wyjątkowych". N ad al m o ż n a otrzym yw ać inform ację o porażce tym sp o so b em , ale nie jest o n sto so w an y p rz e z dom niem anie. Jeśli tak w łaśnie ch cem y , m u sim y w y ra ź n ie to
310
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci z a z n a c z y ć tu ż obok o p e r a to r a new. M u sim y ta m w naw iasie p o staw ić m ag iczn e sło w o s t d : -.n o th r o w 12. J Może też być po prostu n o th r o w - jeśli jest w mocy odpowiednio
dyrektywa n a m e s p a c e . O to, ja k to w y g lą d a : d o u b le *wsk; wsk = new i f (!wsk) *
cout // tu II
(std::nothrow)
____ d o u b l e [10000000] ;
<< "Rezerwacja nieudana " «
endl;
reakcja na tę n ie u d a n a re ze rw a c ję
) W id z im y tu in stru k cję p ró b u ją c ą stw o rzy ć d u ż ą tablicę. M ożliw e je d n a k , ż e już w p a m ię c i s tw o rz y liśm y w cześn iej w iele tak ich tablic, a ta - jest w ła ś n ie kroplą p rz e p e łn ia ją c ą d zb an . W id z im y , ż e tu ż za s ło w e m new p o sta w iłe m słow o n o th r o w , z n a c z y to, ż e nie c h ce m y te g o n o w o cześn ie jszeg o sp o so b u , o k tó ry m z a chw ilę. C h cem y po starem u "! Jeśli s tw o rz e n ie tego o b ie k tu się o p e ra to ro w i n ie u d a , o d p o w ie n a m w staw iając d o w s k a ź n ik a wsk a d re s z ero w y . In stru k cją i f , k tó rą w id zisz z a ra z poniżej, m o ż e m y to s p ra w d z ić i o d p o w ie d n io zare ag o w a ć. Mówię tu "dawny " sposób, ale spotkałem nowy (znany) kompilator, który
zna tylko ten dawny sposób... N ow y s p o s ó b p o w ia d o m ie n ia : rz u c e n ie w y ją tk u
std: :bad_alloc
T u jest m ały kłopot, b o n ie m ów iliśm y o n a rz ę d z iu zw a n y m Obsługa Sytuacp Wyjątkowych. Z atem , o p o w ie m to raczej o b razo w o . O tó ż w p r z y p a d k u , g d y o p e ra to ro w i new nie u d a się stw o rzy ć o b iek tu , "rzu ca" on szczeg ó ln eg o ro d zaju ob iek tem w stro n ę tego, k to go w y w o ła ł. T e n "rzu t" - to n ie jakaś ta m zw y k ła in strukcja g o t o . T o jakby n a p r a w d ę leciał p ro m k o sm iczn y . M y z a ś, licząc się z taką ew en tu a ln o ścią - p o w in n iś m y m u p rz y g o to w a ć m iejsce n a ląd o w an ie. Jeśli ch cem y w n a sz y m p ro g ra m ie stw o rzy ć o p e ra to re m new ja k iś obiekt, ta w y w o łu je m y te n o p e r a t o r new, c z y li- ty m s a m y m -p rz e k a z u je m y m u stero w a n ie. O d tą d sp ra w y się toczą w ram ach instrukcji tego o p e r a t o r a ne* (p rzecież o p e ra to r to je st p o prostu jakaś funkcja). ♦♦♦ G d y stw o rzen ie o b iek tu o p erato ro w i new się u d aje, ste ro w a n ie docho« d zi n o rm a ln y m try b em d o końca tej funkcji o p e r a t o r new , p o czym w raca d o nas. S tw o rzy liśm y obiekt, te ra z m o ż em y p ra c o w a ć dalej. 12)
ang. no throw - (czyaj: "Nou froł") - w swobodnym przekładzie: "żadnego rzucania". Chodzi tu o to, że nie życzymy sobie tzw. rzucenia wyjątku.
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci
311
szam : o b iek t ty p u s t d : : b a d _ a l l o c ) . T en o b iek t zostaje w y strzelo n y (rzucony^). S tero w an ie leci teraz razem z n im i w y ląd u je tam , g d z ie m u p rz y g o to w a liśm y ląd o w isk o . Program b ę d z ie dalej w y k o n y w a n y o d m iejsca, g d z ie ten o b iek t p rz e c h w y c iliśm y ^ , g d z ie w y ląd o w ał. D użo by ło o p isu , a zobacz jakie to p ro ste w praktyce: try { ' wsk = new d o u b l e [10000000]; // udało się, pracujemy na takim obiekcie
c o u t << "Przechwycona nieudana r e z e r w a c ja p am ięci ! ! ! " « e n d l; // reakcja na tę porażkę I I ................................ // Dalsza część programu, wspólna dla obu sytuacji Z an im so b ie to w yjaśnim y, sp ó jrz na p o n iższy ry su n ek , p o k azu jący jak w y k o nuje się ten frag m e n t p ro g ram u w obu w ariantach.
13) 14)
ang. thrcrw - rzucać, (czytaj: "froł"J. ang. catch - łapać, [czytaj: "kecz"].
312
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci
b rak
----------- <
iamięci
/c a tc h (b a d _ a llo c y — c o u t « "Przechwycona nieudana "rezerwacna pam ięciL ! M \ n " ; // reakcja na tę porażkę
V f V // Dalsza część programu, wspólna dla obu sytuacji
Rys. 8-2. Dwa w arianty gramu.Strzałki przerywane
8raT a ' n o c
: B
S
3 . S P 9 _ wówczas sterowanie nie wraca już do new Jak widać - wówczas sterowanie nie wraca Jut do
obrazują p
^ T w
b
bloku tr y .
Tak w id zisz - sytuacja, g d y spodziew am y się kłopotów , ujęta jest w blok, k to r p o p rz e d z a słow o t r y . W tym bloku w yw ołujem y op erato r n e w , aby stw orzyj n a m n o w y obiekt. *
❖
15)
Jeśli m u się to u d a (na rysunku ilustrują to strzałki czarn e ciągłe), .
w ów czas w ykonyw ane są instrukcje do końca bloku t r y ,
.
a p o tem om ijany jest będący poniżej jest blok c a t c h i w ykony w ane są zw ykłe instrukcje, będące już za blokiem c a t c h .
Jeśli stw orzenie obiektu operatorow i new się nie p o w ied zie - (sled teraz na ry su n k u strzałki przeryw ane). •
R zucony zostaje wyjątek ty p u b a d _ a l l o c .
•
Do jego złapania przy g o to w an y został p rzez nas blo c a t c h (bad a l l o c ) w ięc zaczynają się w y k o n y w ać instruk
ang. try - spróbuj, próba [czytaj: "traj"].
Rozdział. 8. W skaźniki Rezerw acja obszarów pamięci •
313
Po ich w y k o n an iu sterow anie p rzech o d zi do instrukcji bę dących b ezp o śred n io za tym blokiem .
Jak w id z isz - w p rzy p ad k u w yjątkow ym nie są w y k o n y w an e dalsze instrukcje w bloku t r y . I słusznie, p rzecież nie udało się stw o rzy ć obiektu, to nie m a z czym p raco w ać.
O bsługa sytuacji w yjątkow ych to bardzo ciekaw e i zło żo n e narzędzie. Pośw ię ciłem m u o b szern y frag m en t w osobnej książce p.t. "Pasja C++". Tutaj nie m usisz te g o d o k ład n ie znać - w ystarczy, że pobieżnie rozum iesz jak s p ra w d z a m y czy rezerw aq 'a pam ięci się udała.
B.11.7
Funkcja set_new_handler W p o p rz e d n im paragrafie m ów iliśm y, że o p erato r new m o że nas p o w iad o m ićo swoich k ło potach (brak pam ięci) na trzy sposoby. Dwa z nich już p o zn aliśm y w tym p arag rafie zobaczym y sposób trzeci. W bibliotece stan d ard o w ej C++ jest do dyspozycji funkcja, dzięki której m oże m y określić, która z naszych (w łasnych) funkcji m a się uruchom ić w takim aw a ryjnym p rz y p a d k u . O to p rz y k ła d o w y program : # in c lu d e < io stre a m > # in c lu d e < c s t d lib > // # in c lu d e // /*************************************************************/ v o id fu n k c ja _ a la rm o w a ( ) ; // @ lon g k; / / C/ /****************************♦********************************/ i n t m ain () s e t new h a n d l e r ( fu n k c ja alarm ow a); f o r ( k = 0 ; ; k++ ) i new i n t ; }
// ©
// tw o rze n ie obiektu
/★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ a*******************************#******/ v o id fu n k c ja _ a la rm o w a () { c o u t << " \ n z a b ra k ło p a m ię c i p rzy k = " « k « ” ! \n " ; e x i t (1) ; II 9 II th ro w bad a l l o c O ; / / vs/
Po wykonaniu na ekranie pojawi się na przykład taki napis z a b r a k ło p a m ię c i p rzy k = 36972 !
314
Rozdz. 8. W skaźniki Rezerw acja obszarów pamięci O czyw iście liczba ta zależy o d bieżącej sytuacji w pam ięci ko m p u tera. Czasem m ó e ł n am p rzy d zielić w ięk szy zapas pam ięci, czasem b ard zo m ały. Zależeć to m o że na p rzy k ład o d tego, czy ak u rat w pam ięci rezydują jakieś in n e program y.
•fr
Kilka uwag O N o w y plik n ag łó w k o w y . D otyczy on tej części biblioteki stan d ard o w ej, gdzie jest funkcja exit k o ń cząca d ziałan ie p ro g ram u . © W ty m pliku n ag łó w k o w y m jest deklaracja funkcji set_new_handler, którą w łaśn ie tu reklam uję. © D eklaracja funkcji f u n k c j a alarmowa. Ta funkcja zostanie o d d eleg o w an a da zad z iałan ia w m om encie, g d y w yczerpie się z ap a s pam ięci (free storę). O O b iek t ty p u long o n azw ie k definiuję jako globalny. To p o to, by był dostępny ta k ż e w naszej funkcji_alarmowej. © Tu jest w łaśnie m o m en t poin fo rm o w an ia k o m pilatora kogo d eleg u jem y do reakcji na w y czerp an ie się pam ięci. Inaczej m ów im y - jest to instalacja now ego p o d p ro g ram u obsługi. Jak w idać, robi się to b ard zo prosto - w yw ołując biblioteczną funkcji set new handler (ang: ustaw jako nowy podprogram obsługi). W naw iasie jakc argumentjest n azw a funkcji. W jednym z następnych p arag rafó w dow iesz się że n azw a funkcji jest jej ad resem w pam ięci. Zatem to ten ad res w łaśnu p o d ajem y funkcji bibliotecznej. © N ieskończona pętla kreująca (tw orząca) obiekty ty p u i n t . W yniku o p eraq i ney nie p o d staw iam y n ig d zie, bo obiekty te n a p ra w d ę nie są n am w tym p rzy k ła d z ie p o trzebne - ch o d zi tu tylko o to, by całkow icie w yczerpać z a p a s pamięci.
O w m om encie, gdy ten cel osiągniem y (w yczerpanie zap a su ), au to m aty czn ą zostaje uru ch o m io n a w yzn aczo n a przez nas f u n k c j a_alarmowa. Funkcja t< w y p isz e na ek ran ie w arto ść zm iennej globalnej k - w ten sp o só b będziem] w ied zieli, p rzy którym z kolei obiekcie zap as się w yczerpał. (Fu nkcja ma dostęp d o k, bo jest ono g lo b aln e - zdefiniow ane poza w szelkim i funkcjam i). © Z am iast w yw oływ ać funkcję exit() m oglibyśm y tutaj sam i spow odow aj rzu ce n ie w yjątku bad_al loc. M amy więc jakby ożenienie d w ó ch sp o so b ó w ., je d n e j stro n y z o stała w y w o łan a n asza fu n k cja a w a ry jn a , coś tam sobią p o załatw iam y , po czym m ożem y rzucić w yjątek bad_a 1loc. W takiej postaci, jak tu widzimy, nasz program nie przygotował bloki c a tc h do złapania wyjątku (czyli lądowiska), nie było też bloku t r y , I więc wyjątku nie da się złapać - czyli program i tak się zakończy z powodt nie złapania wyjątku. Mógłbyś jednak takie bloki przygotować (widziałeś v poprzednim paragrafie, jakie to proste). Wtedy będzie naprawdę połączeni
dwóch sposobów. Z obaczyliśm y tu trzeci sposób w jaki operator new m oże nas pow iadom ić i niem ożliw ości stw o rzen ia obiektu z uw agi na brak pam ięci. T en sposób jes oczyw iście rzadziej sto sow any niż dw a p o p rzed n ie.
Rozdział. 8. W skaźniki Stałe wskaźniki
3.11.8
315
Pojedynek: new contra m a llo c Jakiś czas tem u Jerzy S tu h r zaśp iew ał w O polu k ab areto w ą piosenkę „Ś piew ać każd y m o ż e, trochę lepiej lu b g o r z e j . a kiedy sk o ń czy ł w zruszył ram ionam i i p o w ied ział „W ielkie m ecyje zaśpiew ać w O polu... " To sam o za p e w n e pom yśleli teraz program iści klasycznego C: w zasad z ie o p erato ry new i d e l e t e to przecież to sam o, co d a w n e funkcje biblioteczne: m a l l o c () fr e e ()
new delete
U m e rn o ry allocation 1/ fre e a llo ca łed tn e m o ry
Czyli - „W ielkie mecyje tw orzyć obiekty!" R zeczyw iście. Problem tylko w sło wach: „tro c h ę lepiej lub gorzej". W spom niane funkcje biblioteczne klasycznego C są n a d a l d o stęp n e w C++, w ięc m ożesz sobie je dalej używ ać.
Co jednak przemawia za używaniem new i delete ? Bardzo w a ż n a cecha: O tóż, jeśli kiedyś zdefiniujem y sobie nasz w łasny ty p obiektu, (a b ęd ziem y to robić w następnych rozdziałach), to w m om encie s tw a rzania (kreacji) pojedynczego obiektu danego typu - autom atycznie m o że zostać w y w o łan a nasza specjalna funkcja zw ana kon stru k to rem . (0 szczeg ó łach m ó w ić będziem y w oso b n y m rozdziale, str. 660). Przy zastosow aniu m a l l o c - tej akcji w yw ołania konstruktora oczyw iście nie będzie. N ato m iast p rz y kasow aniu obiektu operatorem d e l e t e autom atycznie w y k o na się in n a nasza funkcja zw an a destruktorem . T eg o w klasycznym C nie m ieliśm y.
Dodatkowo m a l l o c zw raca w skaźnik d o v o i d , czyli w skaźnik d o czegoś nieokreślonego. N ato m iast new jest bezpieczniejszy, bo jako rezu ltat z w ra c a w skaźnik d o ty p u , który w ła śn ie stw arza. D zięki tem u nie m ożem y om y łk o w o stw orzyć o p e ra to rem new np. obiektu d o u b le , a jego adres p rzy p isać w skaźnikow i do i n t . K om pilator od razu nas u ratu je p rzed nieszczęściem sygnalizując błąd
w P oznaliśm y już pobieżnie dzied zin y , gdzie w skaźniki m o g ą się przydać. P o ro z m aw iajm y teraz o sam ych w skaźnikach.
3.12
S tałe w s k a ź n ik i M ów iliśm y nied aw n o o w skaźnikach do obiektów stałych. Są to w skaźniki, które p o k azy w an eg o obiektu nie m ogą zmieniać. T raktują go jako obiekt stały.
316
Rozdz. 8. W skaźniki Stałe wskaźniki, a wskaźniki do stałych Sam obiekt, na k tó ry p o k azu ją, nie m u si być rzeczyw iście stały. W ażn e jest to, ze w sk a ź n ik tak g o trak tu je. Sa jeszcze inne w sk a ź n ik i z p rzy d o m k iem c o n s t . C zy w id ziałeś kiedyś, zw ieS c n ie zn an e m iasto , stojący na ulicy w ielk i plan m iasta, a na m m czerw ona strzałk ę z n ap isem „TU STO ISZ" ? Ta strzałk a to w łaśnie stały w sk aźn ik . N a tej m aDie p o k azu je o n a z a w s z e w to miejsce. M ożesz pokazyw ać na tej m apie rożne obiekty, ró żn y m i w sk a ź n ik a m i, ale nie tą strzałką. Jest ona d o b rz e p rzy ejona w o b aw ie p rz e d d o w cip n isiam i. T a strzałk a to w łaśn ie sta ły wskaźnik. O to definicja takiego ob iek tu i ustaw ienie g o na obiekt: int zoo; int * const wskaż - 6>zoo;
Stały w sk a ź n ik to taki w sk aźn ik , który u staw ia się raz i juz od tej pory n ig d y n ie m ożna go zm ienić. O brazow o m o żn a pow iedzieć, że stały w sk a źn ik to w s k a ź n ik nieruchom y. Z am ro żo n y zostaje ad res, k tó ry w nim jest zap isan y . P om vślisz p ew nie: „ - O co ta cała sp raw a? w ia m y n a jąkiś o b iek t i p o prostu n ig d y g o m asz racie Tyle, ż e za pom ocą tego słów ka ew e n tu a ln y m n ie u w a ż n y m p rzesunięciem
bierzem y zw y k ły w sk aźn ik u sta nie p rzesu w am y !" Rzeczyw iście c o n s t z a b e z p ie c z a m y sie przed w skaźnika. G d y b y śm y go chcrel
p rz e su n ą ć , to k o m p ilato r zasygnalizuje błąd. nracujący nad in n ą częścią p ro g ram u r m e znający tej - p ro b o w al p rzez nieu w a e ę w skm tńik p o r u s z y ć - k o m p ilato r zap ro testu je dla naszeg o w sp ó ln eg o dobra. P rzyjrzyjm y się bliżej pow yższej definicji tego w skaźnika. D efinicję tę czytam y o d n a z w y i p o su w a m y się w lewo: w s k a ż jest to stały ( c o n s t ) w skaźnik <*) pokazujący na o b iek ty ty p u i n t . U w aga:
' Ponieważ jest to stały w sk aźn ik , należy juz w trakcie definicji inicjalizow ać go, czyli nadać m u w artość p o czątk o w ą (Po prostu u staw ić go n a jakiś adres). M ożna to zrobić tylko te ra z albo nigdy. lu ż linijkę'później p ró b a nadania mu jakiejś w artości b ęd zie u z n a n a za pog w ałcen ie zasady, że w sk aźn ik jest stały (nieruchom y). N aw et g d y b y śm y chcie w p isać d o niego te n sam adres, który juz m a. N ie m ożna i koniec. W n aszy m p rz y k ła d z ie w skaźnik jest inicjalizow any a d re se m obiektu zoo.
8.13
Stałe wskaźniki, a wskaźniki d o stałych Jaka jest zasad n icza różnica m iędzy w skaźnikam i stałym i, a w sk aźn ik am i do stałych? ♦> Stały w sk aźn ik to taki, który zaw sze p o k a z u je na to sam o . Nie m ożna nim poruszyć.
Rozdział. 8. W skaźniki Stałe wskaźniki, a wskaźniki do stałych
317
♦> W sk a ź n ik d o sta łe g o obiektu to taki w sk a źn ik , k tóry p o k a z y w a n y o b ie k t u z n a je za s ta ły . N ie m oże go w ięc m odyfikow ać. T e d w a ty p y w sk a źn ik ó w m o ż n a ze sobą ożenić. M am y w te d y stały (n ieru ch o m y) w s k a ź n ik d o stałego (n iezm ien n eg o ) o b ie k tu . W definicji w y stąp i d w a razy słow o c o n s t const double * const p;
definicję ta k ą czy tam y (z n o w u od praw ej d o lewej): p je st stałym ( c o n s t ) w sk a ź n ik ie m (* ) p o k a z u ją c y m na obiekt ty p u d o u b l e trak to w a n y jak o stały ( c o n s t ). U ściślijm y: ...trak to w an y ja k o stały przez ten wskaźnik. In n y , zw y k ły w sk a źn ik p o k a z u ją c y n a ten sam o b ie k t m oże go zm ieniać.
A oto przykłady użycia: najpierw przykład na stały (nieruchomy) wskaźnik: a = 5, b = 100; i n t * wa; i n t * c o n s t s t wsk = &a;
// Zwykły wskaźnik // Nieruchomy wskaźnik
wa ■= &O.r *wa = 1 ; * s t wsk = 2 ;
// ustaw wskaźnik na zmienną a II załadowanie 1 do zmiennej a // załadowanie 2 do zmiennej a
in t
teraz próbujemy ruszyć oba wskaźniki wa = &b;
// przestaw wskaźnik, by pokazywał na zmienną b
s t wsk = &b;
II błąd -bo to jest nieruchomy wskaźnik !!! 1/jest na zawsze ustawiony na zmienną a
A oto przykład ze wskaźnikiem pokazującym na stałą: i n t x [4] = { 0, 1, 2, 3 } i n t tm p; i n t *w; const i n t * wsk od s t;
// ustawienie obu wskaźników na początek tablicy
w = x; wskdost
// Zwykły wskaźnik II Wskaźnik do obiektu stałego. Nie musi // on być od razu ustawiany. Można // nim nawet potem poruszać
= x;
tmp = *w; tmp = *w sk_do s t ;
// odczytanie zerowego elementu tablicy II jak wyżej
H ------------ przesunięcie obu wskaźników na następny element tablicy w++; // poruszać nim wolno wsk do s t ++; H ---------- -będziemy tam wpisywać *w = 0 ; *wsk do s t = 0;
// wpisanie 0 do elementu x [ 1 ] // <---- BŁĄD ! Ten wskaźnik traktuje // to, na co pokazuje, jako obiekt stały.
318
Rozdz. 8. W skaźniki Strzał n a oślep - W skaźnik zawsze pokazuje n a coś
II Za pomocą TEGO wskaźnika obiektu H modyfikować nie wolno
A oto przykład na stały (nieruchomy) wskaźnik do stałego obiektu: in t
m = 6, n = 4, tmp;
c o n st in t
c
^ ponieważ jest to stały (nicrudwmy) wskaźnik to musieliśmy go I I od razu zainicjalizować adresem, na jaki ma pokazywać / / odczytanie wartości z obiektu pokazywanego / / <-----BŁĄD ! - zapisać tam mc możemy. Wskaźnik / / traktuje przecież swój obiekt jako stały. / / <-----BŁĄD ! wskaźnik jest nu dodatek nieruchomy, / I nie można nim pokazać na obiekt inny niż m
8.14
S trz a ł na o ś le p - W s k a źn ik z a w s z e p o k a z u je na c o ś Jedną z p u łap ek , w k tó rą często się w p a d a - jest zapom nienie n ad an ia w skaznlkow i w artości początkow ej. Inaczej m ó w iąc zapom inam y go ustaw ić, by na cos pokazyw ał. To jest ź le pow iedziane, alb o w iem w skaźnik z a w sz e pokazuje na coś, n aw et jeśli to coś nie jest niczym zam ierzo n y m . Tak sam o, jak drew niany w sk aźn ik d o m ap y p o k azu je na coś n a w e t w ted y , g d y leży o d ło ż o n y na boku. P ow iedziałbym n a w e t drastyczniej: celuje na coś, jak leżący na boku pistolet.
iKS= ^
Jeśli w ięc zap o m n im y ustaw ić w skaźnik, to próba odczy tan ia te g o miejsca, na które pokazuje, d a b ezsensow ne i p rz y p a d k o w e rezultaty. G orzej z zapisem . 1 o tak jakby leżący na boku pistolet nagle w ystrzelił. Z apisując coś do takiego (p rzy p ad k o w o p o k azy w an eg o ) miejsca n iszczym y je, a to jest zw y k le fatalne w skutk ach , chociaż b łąd m oże objawić się d u ż o później. Dla ciekaw ości p o d am , że w skaźnik, k tó ry jest zd efin io w an y jak o obiekt staty czn y (to znaczy albo je st globalny, albo co p ra w d a , lokalny, ale za to zo rzy d o m kiem s t a t i c ) - taki w sk aźn ik p ierw o tn ie p o k azu je na ad res z e ro w y ^ . Z takim w sk aźn ik iem p o k azu jący m na ad res z e ro nie m a specjalnego ryzyka, bo o p e ra q e na takim szczególnym adresie k o m p u ter łatw o w y k ry je i ostrzeże nas. Jed n ak że w skaźniki, k tó re definiujem y jako obiekty a u to m aty c zn e pokazują na całkow icie p rz y p a d k o w e adresy. Łatwo sobie ten fakt uprzytomnić. Przypomnijmy: Obiekty automatyczne (a wskaźnik to także obiekt - wszystko jedno czy w komputerze, czy wystrugany z drewna), zatem obiekty automatyczne są przecież tworzone na stosie, a zasada jest taka, że tworzonych na stosie obiektów komputer dla
16)
Pamiętamy, że obiekty statyczne wstępnie inicjalizowane są zerami chyba, że sami je inicjalizujemy inną wartością
Rozdział. 8. W skaźniki Sposoby ustaw iania wskaźników
319
nas nie inicjalizuje. Są tam śmieci, dopóki o inicjalizację nie zatroszczy się sam programista. D obra rad a: Jeśli Twój p ro g ra m z niew iadom ych p rzy czy n zaw iesza k o m p u ter lub p o w o d u je tzw . crash program u (np. "segment uiolation") - czyli k o m p u ter o d m a w ia dalszej pracy z ty m pro g ram em w yrzucając nam na ek ran - ż e tak pow iem - p ro to k ó ł z sekcji zw ło k p ro g ram u (postmortem dump), to jest ogrom ne p raw d o p o d o b ień stw o , że w in ne są jakieś n ieu staw io n e w skaźniki. Pow racając do naszej an alo g ii - to w ystrzeliliśm y z leżącego na boku pistoletu. Kula trafiła kogoś na oślep. Oto, jak p ro sto zrobić taki błąd: v o id f u n ( ) { d o u b le a ; d o u b le *x, *m;
. // Dcf. wskaźników bez nadania wart. początkowe/
m = &a; // Teraz ustawiamy wskaźnik m *m = 1 0 .7 ; II Poprawne wpisanie liczby do obiektu a U --------Wskaźnika x nie ustawiliśmy na nic *x = 1 5 .4 ; //<---- Tragedia!
} Ten w sk a ź n ik x nie był u staw io n y na nic sensow nego, w ięc pokazyw ał na coś p rzy p ad k o w eg o . Tutaj w ięc coś w pam ięci k o m p u tera niszczym y.
3.15
S p o s o b y u sta w ia n ia w s k a źn ik ó w Skoro w sk a źn ik i p rzed ich p ierw szy m użyciem p o w in n y być u s ta w io n e - jak to zrobić? N iek tó re sposoby już poznaliśm y. Zbierzm y jednak w szystkie najw ażniejsze. ♦♦♦ W skaźnik m ożna u staw ić tak, by pok azy w ał n a jakiś obiekt, w staw iając d o niego ad res w y b ran eg o obiektu wsk - i o b i e k t ; ♦♦♦ W skaźnik m ożna ustaw ić rów nież na to sam o, na co pokazuje już in n y w sk aźn ik tego samego typu. Jest to zw ykła operacja przypisania w sk a źn i ków wsk = in n y _ w sk a z n ik ; ♦♦♦ W skaźnik m ożna ustaw ić na to sam o, na co pokazuje już w sk aźn ik innego, zu pełnie obcego typu. (Obcego, ale n ie - c o n s t ) . Jeśli jesteś p rz e k o n an y , że taka operacja nap raw d ę ma sens, to m ożesz ją p rz e p ro w a d z ić za pom ocą rzu to w an ia operatorem r e i n t e r p r e t _ c a s t . (§ 8.5, str. 261) d o u b le *wsk = r e i n t e r p r e t _ c a s t < d o u b l e * > (o b c y _ w sk a z n ik );
320
Rozdz. 8. W skaźniki Sposoby ustaw iania w skaźników ♦♦♦ W s k a ź n i k t y p u v o i d m o ż n a u s t a w i ć n a to s a m o , n a c o p o k a z u je in n y w s k a ź n i k ( n i e - c o n s t ) . D o te g o n ie trz e b a r z u to w a n i a (bo tu "wiedza" jest zapom inana). void
*wsk = i n n y _ w s k _ n i e _ c o n s t ;
♦♦♦ W s k a ź n i k u s t a w i a s ię n a p o c z ą te k ja k ie jś ta b lic y p o d s ta w ia j ą c d o n ie g o jej a d r e s . S k o r o w ie m y , ż e n a z w a ta b lic y je s t r ó w n o c z e ś n ie a d r e s e m jej z e r o w e g o e l e m e n t u , z a te m w z a p i s i e n ie p o tr z e b n y je s t o p e r a to r & P is z e m y p o p r o s tu wsk
= tablica;
W s k a ź n i k m o ż e p o k a z y w a ć t a k ż e n a fu n k c ję . O w s k a ź n i k a c h d o fu n k c ji b ę d z i e m y m ó w ić z a c h w ilę ( s tr . 3 37). T a m te ż d o w i e m y s ię , ż e n a z w a f u n k c ji to t a k ż e jej a d r e s , z a t e m i tu z b ę d n y je s t o p e r a t o r & wskf
= funkcja;
♦♦♦ O p e r a t o r n e w z w r a c a a d r e s w ł a ś n ie s tw o r z o n e g o n o w e g o o b ie k tu . T a k i a d r e s n a t y c h m i a s t w p is u je m y d o w s k a ź n ik a . O d te j p o r y w s k a ź n ik p o k a z u j e n a te n n o w y o b ie k t. d o u b l e *wsk; w s k = n e w double;
♦♦♦ W s k a ź n i k m o ż n a u s ta w ić te ż ta k , b y p o k a z y w a ł n a ja k ie ś k o n k r e tn e m ie js c e w p a m ię c i. N a p r z y k ł a d n a ja k iś a d r e s z n a n y n a m z k s ią ż k i, c h o ć b y in s tr u k c ji o b s łu g i ja k ie g o ś u k ł a d u s p r z ę g a j ą c e g o (in te rfa c e ). T u ta j t r u d n i e j p o d a ć p r z y k ła d , b o b a r d z o z a l e ż y t o o d m e t o d y a d r e s o w a n i a s to s o w a n e j w d a n y m t y p i e k o m p u t e r a . W e ź m y j e d n a k s y tu a c ję n a j p r o s t s z ą - k o m ó r k i w k o m p u t e r z e n u m e r o w a n e s ą p o p r o s tu o d 0 w g ó r ę . J e ś li w ó w c z a s c h c e m y p o k a z a ć n a a d r e s w y r a ż o n y w z a p is ie s z e s n a s tk o w y m ja k o : 0 x 3 0 0 7 3 0 4 , to w s k a ź n i k u s t a w ia s ię p o p r o s t u in s tru k c ją : int
*wsk;
wsk
= reinterpret_cast
*>(0x3007304);
O ty m , dlaczego w pow yższej je st operator r e i n t e r p r e t _ c a s t , roz m aw ia liśm y niedawno w § 8.5 na str. 261. «$♦ W sk a źn ik m o żn a u sta w ić tak, b y p o k a z y w a ł na a d res zero . D o p rzy p isa nia w a r to śc i liczbow ej tego sp e cja ln e g o adresu - n ie trzeb a rzu tow an ia. wsk = 0 ;
// n a a d r e s z w a n y d a w n i e j : N U L L
♦♦♦ J e ś li w s k a ź n i k m a p o k a z y w a ć n a c ią g z n a k ó w b ę d ą c ą s t a ł ą d o s ło w n ą ty p u C - s t r i n g - m o ż n a g o u s t a w i ć w te n s p o s ó b : const wsk —
char *wsk; "taki napis";
T e n s p o s ó b d o p u s z c z a l n y je s t ty lk o d la C - s tr in g ó w - s ta ł y c h d o s ło w n y c h . N ie je s t to kopiowanie C - s tr in g u . T e k s t te n (C - s tr in g ) , is tn ie je p r z e c i e ż g d z ie ś w
Rozdział. 8. W skaźniki P arad a kłamców, czyli o rzutow aniu c o n s t _ c a s t
321
kom pilator chciałby bardzo, byś na to miejsce pokazyw ał w skaźni kiem, który gwarantuje temu miejscu nietykalność (w skaźnik do stałej). N iestety, z u w agi na konieczną zgod n ość ze poprzednim i wersja mi języka C++ (gdzie C-string miał typ char * a nie const char *) - kom pilator m usi (acz niechętnie) pozw alać rów nież na konstrukcje bez tego przydom ka const, czyli: char *wsk2 = "napis";
II<- przestarzałe!
Stosow anie w takiej sytuacji wskaźnika do stałej, jest to jedna z zalecanych przez standard C++ zm ian. Będziem y się tego trzym ać, bo na pew no niedługo stanie się to już obow iązkow e. Jeśli nie użyjesz tego const-niebędziebłędu.Jednakwielekompilatorów przypomni Ci o tym c o n s t, wypisując w trakcie kompilacji w danym miejscu "ostrzeżenie".
Tak, czy owak ustaw iliśm y sobie w skaźnik char* na pew ien C-string. Zwróć jednak u w agę, że nie m ożna om aw ianego tu sposobu zastosować do tablic liczb. i n t * w sk in t = { 1 , 2 ,3 , 4
};
//błąd!!!
B.16 Parada kłamców, czyli o rzutowaniu const_cast Poznaliśm y zatem różne rodzaje w skaźników mogących pokazyw ać na obiekt typu T (i pracować z nim). ♦♦♦ t * — zw yk ły w skaźnik ♦♦♦ const T * — wskaźnik do obiektu stałego ♦♦♦ t * const — w skaźnik stały ♦♦♦ const T* const — stały wskaźnik d o obiektu stałego Z ałóżm y, że chodzi o typ double. Oto m ożliw e wskaźniki zebrane w tabeli. O
322
Rozdz. 8. W skaźniki P arad a kłamców, czyli o rzutow aniu c o n s t _ c a s t
N azw a (opisow o)
Pozw ala m odyfikow ać p o k azy w an e obiekty?
d o u b le * wsk
wskaźnik (zwykły)
c o n st d o u b le * wsk
wskaźnik do obiektu stałego
d o u b le * const w sk
wskaźnik stały (nieruchomy)
/ X _____ ___________ /
const d o u b le * const w sk
wskaźnik stały (nieruchomy) do obiektu stałego
W skaźnik
U L
!
M ożna go przestaw iać?
/ / y ------------- -----------
O czyw iście k ażd y z tych w sk aźn ik ó w zaw iera po p ro stu ad res jakiegoś o b ie k tu . Jed n ak ew en tu aln e p rzy d o m k i c o n s t d o d atk o w o ograniczają m ożliw ości da n eg o w sk aźn ik a. (Co m o że on, a co nie m oże - w id zisz w dw óch ostatnich k o lu m n ach tabeli).
Czy można ustawić wskaźnik jeden z powyższych wskaźników tak, by pokazywał na to samo, co inny? K om p ilato r pozw oli tylko na takie przypisanie, k tóre nie n ad a now em u w sk aź n ik o w i w iększych u p ra w n ie ń w stosunku d o p o k azy w an eg o obiektu. (W ięk szych n iż m iał ten w skaźnik dotychczasow y). Inaczej m ów iąc: K om pilator nie pozw oli na takie o szu stw o , by za pom ocą now ego w skaźnika m o żn a było m odyfikow ać obiekt, którego nie w olno by ło m odyfikow ać za pom ocą w sk aźn ik a dotychczasow ego. M ów iąc jeszcze krócej: P rzy tak im przypisaniu nie m o ż e n iepostrzeżenie zg in ąć to p ie rw sze c o n s t - jeśli było ono w definicji w sk aźn ik a. • ■ ' W skaźnik do obiektu stałego nie może zamienić się na wskaźnik do obiektu n ie -sta łe g o (zmiennego). K om p ilato r się na taką k o n w ersję nie zgodzi. M ożem y jed n ak go to tego zm usić, jeśli p osłużym y się o p erato rem rzu to w an ia c o n s t c a s t . O p erato r ten p o zw ala na tak ą k o n w e rsję ty p ó w , w której p rzy d o m e k c o n s t zostanie zap o m n ian y . J Pozwala też nadać taki przydomek, ale to nic niezwykłego. To można zrobię nawet bez żadnego rzutowania. O to p rzy k ład : f i n c l u d e < io s tre a m > u s in g n a n e s p a c e s t d ;
Rozdział. 8. W skaźniki P arad a kłamców, czyli o rzutow aniu c o n s t _ c a s t
323
/*************** *****★ ****************************************/ int m a i n O
// double zmienna = 0; const double stała = 3.14;
// II
double *wsk_do_zmiennej = &zmienna; const double *wsk_do_stalej = &stala; const double *wsk2_do_stalej; W fiK j
A ‘i\
•
cout «
"Na początku,
stała = " << stała «
endl;
// ©
wsk2 do stałej = wsk_do_zmiennej; // wsk_do_zmiennej
wsk do zmiennej
wsk_do_stalej;
II b łą d
const cast(wsk do stałej)
*wsk_do_zmiennej = -333; cout « " P o t e m ----- > stała = " «
stała «
endl;
// stały (nieruchomy) wskaźnik do zmiennej II usuwamy stałość stałej, a nadajemy stałość (nieruchomość) wskaźnika
IbIW >JJ'
double * const st_wsk_do_zmiennej = const cast (wsk_do_stalej);
// ©
w sk do zmiennej = st_wsk_do_zmiennej;
// O
II ... int * wskint; // © wskint = reinterpret_cast(const_cast(wsk_do_stale]));
/ / . ..
Program nie jest specjalnie rozmowny, więc na ekranie zoba czymy tylko ślad naszego oszustwa Na początku, stała = 3.14 P o t e m -----> stała = -333
•0 *
Za to rozmowa będzie ciekawsza O Definicja zw ykłego w sk aźn ik a do obiektu ty p u double. Jest to zw y k ły w skaź nik, czyli taki, za pom ocą którego m ożna zm ieniać p o k azy w an y obiekt. Defi nicja ta połączona jest z inicjalizacją, czyli ustaw ieniem w sk aźn ik a tak, by pokazyw ał na obiekt o n azw ie zmienna. Jeśli nie przyzw yczaiłeś się jeszcze do tych skom plikow anych zapisów , napiszm y to sam o w dw óch instrukcjach: double *wsk_do_zmiennej ; wsk do zmiennej = &zmienna;
324
Rozdz. 8. W skaźniki P arad a kłamców, czyli o rzutow aniu c o n s t _ c a s t 0
D efinicja w sk a ź n ik a d o s ta łe g o obiektu. To nie zn aczy , że ten pokazyw any" o b ie k t m a n a p ra w d ę być stały . P o p ro stu w sk a źn ik ten będzie go tra k to w a ł jako o b ie k t stały . W tej sam ej in stru k c ji w id zisz inicjalizację tego w sk a źn ik a a d re s e m o b ie k tu . T o sam o w p ro s ts z y m zapisie (dw u lin ijk o w y m ) w y g ląd ało b y tak: c o n s t d o u b le * w s k _ d o _ s ta le j; wsk do s t a ł e j = & s t a l a ;
© W sk a ź n ik d o ob iek tu „ z m ie n n e g o " (czyli z w y k ły w sk aźn ik ) - m o ż n a bez: p rz e s z k ó d p rzy p isać d o w sk a ź n ik a d o obiektu stałego. N ie m a p ro b lem u : P rzez tę operację n o w y w sk a ź n ik nie n ab y w a przecież w ięk szy ch p ra w d o o b ie k tu p o k azy w an eg o . Tu jest nawet odwrotnie: Ten wskaźnik po lewej nie tylko, że nie nabywa większych praw (nawiasem mówiąc - większych już mieć nie można) - on wręcz sam sobie ogranicza te prawa (postanawia, że nie będzie zmieniał). To jednak nie niepokoi kompilatora. ("Jakktoś chce pościć, to jego sprawa "). © N a to m ia s t na o d w ro tn ą sy tu ację kom pilator się n ie zg o d zi. W id zim y tu p ró b ę p rz y p is a n ia treści w sk a ź n ik a d o p o k azy w an ia n a ob iek ty stale - w sk a źn ik o w i z w y k łe m u . Czyli tu p rz y d o m e k c o n s t , strzeg ą cy stałości o b iek tu zo stałb y z g u b io n y .
Gdyby kompilator się na takie przypisania godził, to w następnej linijce moglibyśmy przecież (przez pomyłkę?) napisać instrukcję zmieniającą treść obiektu, który przecież miał być traktowany jako stały. © O sta te c z n ie jed n ak to jest n a s z p ro g ram , w ięc jeśli k o n ieczn ie chcem y, to m oże m y tak zrobić, lecz trzeba u p e w n ić k o m p ilato r, że nie ro b im y te g o p rzez n ie u w a g ę , ale że jesteśm y św ia d o m i tego czy n u . S łuży n am d o teg o w łaśn ie o p e ra to r const_cast. Tu w ła ś n ie w id zisz jego zasto so w an ie. Pozw ala on d o k o n a ć p rzy p isan ia w sk a ź n ik a d o o b iek tu stałego w skaźnikow i, k tó ry tej stałości nie g w a ra n tu je (czyli w sk a ź n ik o w i zw ykłem u). Linijkę p ó źn iej w id zim y n ie w in n ą instrukcję wsk d o zm ien n ej = - 3 3 3 ; Dalej w id z isz w y p isan ą na ek ra n ie bieżącą z a w a rto ść ob iek tu stałeg o o n azw ie stała. I co? Stały obiekt z o s ta ł zm ieniony. N ie w p ro s t oczyw iście, jak b y k u c h e n n y m i schodam i. N a jp ie rw jego ad res z o sta ł (dzięki rz u to w a n iu c o nst cast) p rzek a zan y o szu sto w i, a ten o s z u s t zrobił, co chciał.
O p e ra to r c o n s t_cast m o że słu ży ć d o „ z g u b ie n ia " p rz y d o m k a z a p e w n iając e go stało ść p o k azy w an y m o b iek to m , ale...
można też za jego pomocą równocześnie przydomek const nadać. © O to d efin icja stałego („n ieru ch o m eg o ") w sk a ź n ik a .
Rozdział. 8. W skaźniki P arad a kłamców, czyli o rzutow aniu c o n s t _ c a s t
325
n ig d y . A na co go u s ta w ia m y ? P o n iew aż chcem y g o u s ta w ić na stałą (a na to k o m p ilato r słu sz n ie n ie pozw ala) —m u sim y o czy w iście p o słu ży ć się rz u to w an iem c o n s t _ c a s t . Tu ciek aw e jest to, co je st w ostrych naw iasach teg o o p erato ra. Jak w id a ć , jest tu *
"zap o m n ien ie" p rz y d o m k a c o n s t o d p o w ie d z ia ln e g o za stało ść poka-
chom ość w sk a ź n ik a .
zam ien iliśm y na • n ie ru c h o m y (stały) w sk aźn ik d o zm iennej.
O Jak w id ać, w o d w ro tn ą stro n ę przy p isać m o żn a b ez p o trze b y rz u to w a n ia . A dres ze w sk a źn ik a n ie ru c h o m e g o p rzep isy w an y jest tu d o w sk a ź n ik a zw ykłego. Jakieś c o n s t jest tu zap o m in an e , ale zw ró ć u w ag ę, że to c o n s t nie dotyczy p o k a z y w a n e g o w sk a ź n ik ie m obiektu, tylko sam eg o w sk a ź n ik a (jego nieruchoWracając do obrazka z mapą miasta i ze stałym (nieruchomym) wskaźni kiem "TU STO ISZ". _ (< Patrząc na taką mapę możemy przecież (przyklejony) wskaźnik "TU STO ISZ" przypisać do naszego wskazującego palca (wskaźnik bardzo ruchomy). Następnie możemy powiedzieć: "Tu jesteśmy..." (i przesunąć palec) - "...a mamy dojść tutaj". T akie p rz y p isa n ie nie n a d a ło n am wcale w ięk szy ch u p ra w n ie ń w sto su n k u do p o k a z y w a n e g o o b ie k tu . N ie jest w ięc o szu stw em .
O p e ra to r rz u to w a n ia c o n s t c a s t , m im o ż e w n azw ie m a c o n s t _ c a s t , tak sam o m o że p o s tę p o w a ć w obec przy d o m k a v o l a t i l e . O p e ra to r c o n s t c a s t n ie może jednak zajm o w ać ż ad n y m i in n y m i konw ersja mi. T o zn aczy n ie m o ż n a zam ienić w skaźnika do stałego ob iek tu ty p u d o u b le na w sk a źn ik d o z w y k łe g o obiektu typu i n t . © Jeśli n a m na ty m zależy , należy zrobić to d w u s to p n io w o - o so b n o u su n ą ć c o n s t o p erato rem c o n s t c a s t , a dopiero potem zam ienić w sk aźn ik d o u b le * na w sk a źn ik i n t * za p o m o c ą opertora r e i n t e r p r e t _ c a s t . T a k - j a k to w łaśnie w id z im y w m iejscu © .
W
326
Rozdz. 8. W skaźniki Tablice wskaźników
Obiekt stały i tak pozostanie stały U w aga: Z w ró ć u w ag ę, że rzu to w an ie c o n s t _ c a s t p o w o d u je zm ianę typu wyrażenia u ję teg o w naw ias - tak, by stało się ono wyrażeniem innego typu. N a przy k ład , by to w y rażen ie now ego ty p u ten pasow ało do ty p u obiektu stojącego po lewej stro n ie o p erato ra przypisania:
N a zakończenie jeszcze raz przy p o m n ę, że rzutow anie w sk a źn ik ó w jest zaw sze jakim ś o szu stw em . W bardzo niew ielu sytuacjach n ap raw ę trzeba się do tego uciekać.
8.17
T a b lic e w s k a ź n ik ó w Jak p am iętam y , tablica to ciąg zm ien n y ch tego sam ego ty p u zajm ujących ciągły obszar w pam ięci. Jeśli mogą być tablice zaw ierające zm ie n n e typu i n t , d o u b l e , c h a r itd. - to dlaczego nie m iałoby być tablic, k tó ry ch elem entam i są w skaźniki, czyli a d re s y różnych m iejsc w pamięci. A dresy to w końcu też jakieś liczby. M ożna je przechow yw ać z e b ra n e w tablicę.
Tablica wskaźników do double O to p rzy k ład tablicy do p rzechow yw ania pięciu w sk aźn ik ó w . W szystkie te w sk aźn ik i słu żą d o pokazyw ania na obiekty typu d o u b l e double *tabwsk[5];
Przeczytajm y tę definicję: Z aczynam y o d nazw y tabwsk
i po su w am y się w praw o, bo o p erato r [ ] jest m ocniejszy o d operatora * czytam y więc: [5 ] - jest tablicą pięcioelementową teraz w lew o i n ap o ty k am y gw iazdkę * - wskażnik&w mogących pokazywać na obiekty typu d o u b l e . Tę sam ą definicję m o żn a by napisać tak: double * (tabwsk[5]);
Rozdział. 8. W skaźniki Tablice wskaźników
327
U życie n a w ia su o k rąg łe g o p o k azu je w y raźn iej kolejność (sposób) czytania definicji.
wskaźników do C-stringów A o to definicja in n e j tablicy w sk aźn ik ó w . Jej elem entam i są w sk a źn ik i m ogące p o k a z y w a ć na C -strin g i. char *miasta[6];
rsk aźn ik i te m o ż n a u staw ić tak, by p o k a z y w a ły na jakieś C-stringi. char *na z w y [6] = {
"Kraków", "Berlin", "Paryż", "Oslo", "Los Angeles", "Compostella" };
E lem en tam i tej tab licy n ie są - jak m o ż n a by p rz y p u sz c z a ć - C -stringi z n az w a m i m iast. Są ta m ad resy tych m iejsc w pam ięci, g d z ie k o m p ilato r um ieścił sobie te C -strin g i. P o d o b n a k o n strukcja z liczbam i b yłaby b łęd n a: int *wskint[4]
/////
= { 10,11,12,13 };
zn aczy ło b y to b o w iem , że chcem y ab y w sk a źn ik b ędący p ie rw sz y m elem en tem tablicy p o k a z y w a ł n a ad res 10 itd. D laczego w p r z y p a d k u C -stringów b łęd u nie m a? N a tej sam ej zasad zie, d la której k o n stru k cja const char *w = {"abcde"};
jest p o p ra w n a . G d z ie ś w pam ięci k o m p ilato r m u siał um ieścić sobie ten C -strin g .12 W m o m en cie, g d y d o ch o d zi d o definiq'i i inicjalizacji w sk aźn ik a, p o d sta w ia o n a d re s tego C -stringu d o w sk aźn ik a.
Oto krótki program, w którym posługujemy się tablicą wskaźników: #include using namespace std;
Z *************************************************-************/
int main()
( const char *stacja[] "Wansee", "Nikolassee", "Grunewald", "Westkreuz", "Charlotenburg", "Savigny Piat z", "Zoologischer Garten" }; const char *www[3]; int i; for(i = 0; i < 7 ; i++)
{ cout << "Stacja: "<< stacja[iJ «
endl;
} www[0] = sta c j a [2];
7)
Mimo że nie żądaliśmy tego specjalnie - stale dosłowne typu C-string są przechowywane jako obiekty statyczne.
328
Rozdz. 8. W skaźniki W ariacje na tem at C-stringów w w w [1] = stacja[5] ; WWW[21 = "Taki tekst"; cout << "Oto 3 elementy tablicy: << www[0] << ", «
WWW[ 1 ]
«
<<
W W W [2]
<< endl;
" ,
\n"
"
} Ten p rzy k ła d miał na celu po k azan ie pracy z tablicą w skaźników . W ty m p rz y p a d k u chodziło o C -stringi, ale m ożna też trzym ać w (innej!) tablicy ad resy innego ty p u obiektów . Dzięki um ieszczeniu ad resó w naszych C -stringów w tablicy - m ożem y mow ie: "-T eraz po p ro szę tekst trzeci, a potem tekst pierw szy". W p rz y p a d k u , gdyby to była tablica w skaźników do innych obiektów , to tak, jakbyśmy zebrali ich ad resy i um ieścili w tablicy. Hydraulik mówi: Oto mam tablicę (listę) z sześcioma adresami pęknię tych rur na terenie miasta. Najpierw jadę pod trzeci adres, a potem szukajcie mnie pod pierwszym..."
W W n astęp n y m paragrafie też będziem y m ów ić o C -stringach, ale nie daj się zw ieść, nie chodzi tam o tablice w skaźników . W tym p rzy p ad k u będ ą to " n i e z r z e s z o n e " w skaźniki, czyli nie ustaw ione w tablicę.
3.18
W a ria c je na tem at C -s trin g ó w P oro zm aw iam y teraz o C -stringach (ciągach znaków ). I G łów nym celem tego paragrafu jest p o k azan ie pracy ze w skazm -
I kami.
t_ To że w tym p rzy p ad k u pokazyw ać będą one ak u rat na C -stringi, m oże uczynić sp raw ę nieco przejrzystszą. Łatwiej się myśli o tablicy zaw ierającej litery zd an ia "Z ulicy W enecja do Rynku..." niż o tablicy zaw ierającej 24 liczby zm iennoprzecinkow e. . W przyszłości poznam y inne (może lepsze) sposoby pracy z tekstam i (napisam i), ale dopóki ta chw ila nie nadejdzie - poćw iczm y ten typ pracy. N a w e t jeśli w przyszłości w ybierzem y tam te lepsze sposoby, ten pairagrat m a nau czy ć Cię jak pracuje się ze w skaźnikam i w ogóle. To n ap raw d ę dobrze jest um ieć.
W O C -stringach (ciągach znaków ) m ów iliśm y już - je d n a k obecnie (kiedy juz m am y za sobą w skaźniki) d o spraw y tej w racam y ra z j e s z c z e . Teraz jednak jesteśm y m ądrzejsi. W iem y na przykład, że jeśli d o funkcji w ysyłam y C -string (tablicę znakow ą), to m ożna w tej funkcji odebrać tę tablicę jako
Rozdział. 8. W skaźniki W ariacje na tem at C-stringów
329
c h a r t a b [] lu b ja k o w s k a ź n i k d o o b i e k t ó w ty p u c h a r c h a r * w sk •
M flfr* W Y
' Y
4*łi ii
'z r i& k e . z
•'
•
i4 ' »
O to p r z y k ł a d d w ó c h fu n k c ji: O b ie d r u k u j ą C - s tr in g p r z y s ł a n y d o n ic h , a le ż e b y b y ło c ie k a w ie j, d r u k u j ą g o ta k , ż e p o k o le jn y c h z n a k a c h w s ta w ia ją p a u z y . Z a te m te k s t t o r n a d o w y g l ą d a ł b ę d z i e n a e k r a n ie ta k : t - o - r - n —a - d - o O to p r o g r a m , w k t ó r y m m a m y r e a liz a c ję „ t a b lic o w ą " i „ w s k a ź n i k o w ą " ta k ie j fu n k c ji. F u n k c je te n a z y w a ją s ię o d p o w i e d n i o p r z e d z i e l a c z _ t a b l o r a z 'H p rz e d z ie la c z _ w s k rh
t i n c l u d e < io s tre a m > u s in g n am esp ace s t d ;
c j ł m r l f ‘i i w
.i c i M j w .v ■•iińiel idrrofcr»k> "i
v o id p r z e d z i e l a c z _ t a b l (c o n s t c h a r t a b [ ] ) ; v o id p r z e d z i e l a c z w s k (c o n s t c h a r * w ); Z * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * '* * * * * * * * * * * * * * * * * / m a i n () V/.ń;Tls r y i r . ' J \ . W u j * •> V‘ { c h a r o s t r z e ż e n i e [8 0 ] = { " A la r m t r z e c i e g o s t o p n ina , " >; ó ;■ . ; .• Tl c o u t << " \ n w e r s j a t a b l i c o w a \ n " ; // O p rz e d z ie la c z ta b l( o s tr z e ż e n ie ) ; ;
c o u t << " \ n w e r s j a w s k a ź n ik o w a \ n " ; p rz e d z ie la c z _ w s k (o s trz e ż e n ie ) ; } ★ *★ ★ ★ *★ ★ ★ ★ *■*■★ ★ ★ ★ ★ ★ ★ *'******'**■*■**** ★ .* ★ * * /*■★ v o id p r z e d z i e l a c z _ t a b l (c o n s t c h a r t a b [ ] ) ( i n t i =» 0 ; w_
i
• a*tr ni-.
// o
w h ile (ta b [i]) c o u t << t a b [ i + + ] 52*:
<<
:i .'oiotid*' / ' o w otftbf.-: Z*************************************************************/ v o i d p r z e d z i e l a c z w s k ( c o n s t c h a r *w) // ©
w h i l e ( *w) { cout « )
*
(W++)
«
330
Rozdz. 8. W skaźniki W ariacje na tem at C-stringów
Po wykonaniu programu na ekranie zobaczymy w e r s ja ta b lic o w a A -l-a -r-m - - t - r - z - e - c - i - e - g - o - - s - t - o - p - n - i- a w e r s ja w skaźnikow a A -l-a -r-m - - t - r - z - e - c - i - e - g - o - - s - t - o - p - n - i- a -
Spójrzmy na program O D w a m iejsca, gdzie te funkcje się w yw ołuje. Jak w idać, sposób przesłania tablicy zn ak o w ej jest identyczny w obu przypadkach. 0 R ealizacja tablicow a funkcji. N ie ma tu nic nadzw yczajnego. D efiniujem y lokal n y o b ie k t i po to, by mieć in d ek s do elem entów tablicy. Potem n astęp u je pętla N ajp ierw sp raw d za się, co jest w elemencie t a b [ i ] . Jeśli jest to cokolwiek inn eg o niż zn ak nuli (bajt zerow y) kończący C -string, to następuje w ypisanie tego z n a k u , a potem kreseczki • - 1. Przy okazji w y konuje się postinkrem entacja in d e k s u . To znaczy: już po w ydobyciu znaku z tablicy, indeks i jest zw iększany Z a u w a ż , że w tej funkcji d w u k ro tn ie m usi być liczona pozycja (adres) danego elem en tu tablicy w pam ięci. , , © R ealizacja „w skaźnikow a" jest sprytniejsza. P rzysłana do funkcji tablica znako w a (czyli w łaściw ie adres jej początku) służy do lm cjahzacji lokalnego w skaźni ka. Pokazuje on na początek C -stringu. W ew n ątrz funkcji m am y zn ó w pętlę w h ile . W ykonuje się ona d o tąd , dopoki zn ak w sk azy w an y przez w skaźnik — czyli (*w) — jest różny od n u li Czyli gdy jego kod ASCII jest różny od 0. W vpis n a ekran to znow u w ydobycie z pam ięci elem entu, na który w skaźnik pokazuje. P rzy okazji robiona jest postinkrem entacja w skaźnika, czyli po sp ełn ien iu swojej roli p rzesu w a się on na następną literę C-stringu. Z au w aż , że ani razu nie liczy się tu żm udnie pozycji adresu danej litery w pam ięci. Bierzemy po p ro stu to, na co w skaźnik ju ż pokazuje. Przejście do n astęp n ej litery jest tylko przesunięciem w skaźnika na sąsiada.
W A oto jak w yglądałaby funkcja kopiująca C -string z tablicy do tablicy W ro zd ziale o tablicach w idzieliśm y realizację „tablicow ą". Teraz zobaczm y jak to b ęd zie w yglądało w p rzy p ad k u posłużenia się w skaźnikam i. O to krótki program : # in c lu d e < io stream > u s in g nam espace s t d ; * «at-r-i-rłvlch;i]T *c© l , const c h a r *z r o d l o ) , /**********?***************************'*************** i n t m ain () 1
. V
Rozdział. 8. W skaźniki W ariacje na tem at C-stringów
331
cout << poziom << endl; cout << komunikat << endl;
} J
char * strcpy(char *cel, const char *zrodlo)
// © _
{
char *poczatek = cel;
n»-
•
•.■
• .
// ©
1 ..
■
■
■
!•-
while(* (cel++) = * (zrodlo++)) ; return początek;
// O // ©
}
Po wykonaniu program drukuje Poziom szumu w normie Poziom szumu w normie
ab y u d o w o d n ić, ż e istotnie skopiow ał C -string z jednej tab licy do drugiej.
Kilka wyjaśnień © W yw ołanie funkcji celem skopiow ania C-stringu z tablicy p o z io m do tablicy k o m u n ik a t. 0 Definicja naszej funkcji. C zytam y ją: s t r e p y jest funkcją <$♦ w y w o ły w a n ą z dw om a arg u m en tam i •
- pierw szym : w skaźnikiem do tablicy zn ak o w ej (c h a r * )
•
i - drugim : w sk aźn ik iem do tablicy zn ak o w ej (c o n st ch a r* ).
Funkcja ta m a zw racać w sk aźn ik do tablicy zn ak o w ej ( c h a r * ) Jak w id zim y w O , nasza funkcja została w yw ołana z d w o m a arg u m en tam i aktualnym i: tablicam i p o z io m i kom uni k a t . W ysłano do funkcji nazw y tablic czyli inaczej ad resy ich początków . Tym czasem funkcja s t r e p y definiuje so b ie lokalne w sk aźn ik i z r o d l o i c e l . W skaźniki te są inicjalizow ane p rzy słan y m i adresam i tablic. P o k azu ją więc o n e w stępnie n a ich początki. © N ie pytaj m n ie teraz dlaczego, ale czasem okazuje się p rz y d a tn e , by taka funkcja zw racała w sk a źn ik d o tej tablicy, do której C -string zostaje w p isy w an y . Teraz jeszcze na tę tablicę pokazuje w skaźnik c e l . Poniew aż jednak zam ierzam y nim za chw ilę p o ru szać, d lateg o zap am iętu jem y go sobie w e w sk a źn ik u p o c z ą t e k . Później, instrukcją r e t u r n © , zw rócim y w łaśnie tę zap a m ię tan ą wartość. O Praca całej funkcji polega na w ielokrotnym w ykonyw aniu w y rażen ia ♦cel = *zródlo
332
Rozdz. 8. W skaźniki W ariacje na tem at C-stringów
czyli kopiowaniu znaku pokazyw anego wskaźnikiem z r o d l o w miejsce poka zyw ane ‘w skaźnikiem c g I. P rz y o k azji, p o ro b ie n iu tej akcji, k a ż e m y o b a te w sk a ź n ik i p rz e su n ą ć n a n a s tę p n e p o zy cje (n a s tę p n y e le m e n t tablicy znakow ej). To: „ p rz y okazji to w ła ś n ie p o stin k re m e n ta c ja , z a z n a c z o n a za pom ocą o p e ra to ró w ++, w w y ra ż e n iu : ( * (cel++) = * (zrodlo++)
)
w y ra ż e n ie to jest p rz y p is a n ie m , a w ięc jak o całość m a w arto ść ró w n ą w a rto śc i p rz y p is y w a n e j. C zyli w n a s z y m p ro g ra m ie n ajp ierw w arto ścią te g o w y rażen ia b ę d z ie k o d litery ' P ' , p o te m k o d litery ' o ' , p o te m liter ' z ', ' i \ ' o ' i tak d alej a ż d o zn ak u k o ń c z ą c e g o k a ż d y C -strin g czyli nuli. W te d y w arto ść tego w y ra ż e n ia b ęd zie z ero . P o n ie w a ż w y ra ż e n ie to tk w i jako w a ru n e k p ętli w r u l e , z a te m w te d y w łaśn ie p ę tla ta zo stan ie p rz e rw a n a . A co je st w ła śc iw ą treścią pętli?: N IC! Z a u w a ż , ż e z a ra z za w a ru n kiem s p r a w d z a n y m p rzez w h i l e jest ś re d n ik , czyli p ętla jest pusta. M im o to je d n a k w y k o n u je d la nas pracę. Jak to się dzieje? M ó w iliśm y ju ż k ie d y ś o ty m , p rzy p o m n ijm y je d n ak . O tó ż p ę tla w h i l e zaw sze n a jp ie rw s p ra w d z a so b ie w a ru n e k . Jeśli b ę d z ie on sp ełn io n y , to ew e n tu a ln ie w y k o n a treść pętli. J e d n a k ż e m y jako w a ru n e k d aliśm y jej sk o m p lik o w a n e w y ra ż e n ie , którego w y n ik (w artość) m a o stateczn ie z a d e c y d o w a ć czy robie „ / t l ę czy n ie U n as to w y ra ż e n ie to jest p rz y p isa n ie m - k o p io w a n ie m zn aku. W y ra ż e n ie to m usi zo stać w ięc najp ierw obliczone. W artością p rz y p isa n ia jest k o d k o p io w an eg o z n a k u i on w łaśn ie d ecy d u je czy w y k o n ać o b ieg p ę tli czy m e. W ielk a decyzja to nie jest - jeśli tylko ten zn ak jest inny n iż nuli (m z b ajt zero), to w a ru n e k p ętli jest sp ełn io n y . J P ętla w h i l e w ie ju ż czy m a w y k o n ać sw ą w łaściw ą treść, czy nie. T reścią p ę tli jest ś re d n ik - czyli in stru k cja p u sta - ale to nic nie sz k o d z i, bo k o p io w an ie o d b y ło się przecież ju ż w ch w ili sp raw d zen ia.
Uwaga.
tw skliw y kompilator, to w tym miejscu otrzymasz ostrzeżenie, że możliwe, iż toykonujesz tu niepoprawne przypisanie. Świadczy to dobrze o kompilatorze. Spodziewa on się tu najczęściej po ró w n a ń typu
^
while(a == b)
...
natomiast u nas jest tylko jeden znak = oznaczający przypisanie: while(a = b)
...
Kompilator na wszelki wypadek ostrzega nas wiedząc, że sytuacje z przypi saniem są tu raczej rzadkością. Powinniśmy jeszcze raz się upewnić czy naprawdę chcemy przypisywać, a nie porównywać. M y jednak naprawdę chcemy tu przypisywać, więc na to ostrzeżenie nie reagujemy. Mówiliśmy ju ż kiedyś, że jest prosty sposób, aby uniknąć tego ostrzeżenia. Jeśli wy rażenie to umieścimy w dodatkowej parze nawiasów, to tym samym upewnimy kompilator, że naprawdę chcemy tu dokonywać przypisania
Rozdział. 8. W skaźniki W ariacje na tem at C-stringów
333
w h i l e ( (a = b ) ) . . . Z au w aż jeszcze je d n ą ciekaw ą rzecz. C -string, jak w ia d o m o , m a na końcu zn ak nuli. P o p ra w n e sk o p io w an ie C -strin g u w inne m iejsce w y m a g a oczyw iście sko p io w an ia ta k że i tego w ażn eg o z n a k u . C zy nasza funkcja to robi? Z ałóżm y, że sk o p io w a liśm y o statn ią literę napisu ź ró d ło w e g o . Dzięki p ostinkrem entacji w sk a ź n ik z rodło p rzesk o czy ł na n a stę p n y z n a k i pokazuje na znak nuli. P ętla w h i l e p rzy stęp u je d o p o n o w n eg o ob liczen ia w y rażen ia bę dącego w a ru n k ie m . N astęp u je w ięc kolejne p rz y p isa n ie - czyli sk opiow anie zn ak u nuli. W arto śc ią tego w y rażen ia p rzy p isan ia jest te ra z nuli, a w ięc w tym m om en cie p ętla p rz e ry w a się. Jed n ak z n a k nuli został ju ż w ła śn ie skopiow any.
W Wróćmy do sprawy tego, co zwraca nasza funkcja strepy. Funkcja z w ra c a jak ąś w artość, ale o stateczn ie m ogłaby też nic nie zw racać i być typu void. B yłoby to bez w p ły w u n a ko p io w an ie C -strin g u . Z w racana jest jednak w arto ść ty p u char *
czyli w sk a ź n ik d o obiektu ty p u zn ak o w eg o . N a p rz y k ła d d o C -stringu. Po co tak? Jest ta k a tendencja w w iększości funkcji bibliotecznych zajm u jących się C -stringam i, a b y funkcja zw róciła w sk a źn ik d o miejsca, g d z ie o d b y ło się ko p io w an ie. Co p rzez to z y sk u jem y ? O tóż te ra z , a b y p o procesie k o p io w an ia w ykonać jak ąś o perację na tablicy kom u nikat - (n p . w y p isan ie na ek ran ), - zam iast pisać 2 instrukcje strepy(komunikat, poziom); cout << komunikat;
w y starczy n a p isa ć cout << (strepy(komunikat, poziom) ); Jest to in stru k cja „ w y p isz " . Co m a zo stać w ypisane? To u k ry te jest w w y rażen iu w naw iasie. N a jp ie rw w ięc m u si zostać obliczone w y rażen ie, czyli w y k o n y w a na jest fu n k cja s t r e p y . Z w racana p rz e z nią w artość (w sk aźn ik d o tablicy kom un i k a t ) jest w łaśn ie w artością w y rażen ia. N ato m iast c o u t , w idząc w sk a źnik ty p u c h a r * , ro zu m ie to jako ż ą d a n ie w ypisania C -strin g u znajdującego się p od w s k a z y w a n y m adresem . A w sz y stk o dlatego, że funkcja s t r e p y coś zw raca. S p ry tn e, p ra w d a ? O czyw iście tak je st nie tylko w p rz y p a d k u w y p isy w an ia na ekran. Jeśli np. m am y c h a r p ie r w s z y [ 8 0 ] = { " h u r r a " char d ru g i [80]; char t r z e c i [80];
};
334
Rozdz. 8. W skaźniki W ariacje na tem at C-stringów to zap is: strcpy(trzeci,
stropy(drugi,
pierwszy)
>;
o d p o w ia d a tem u sa m e m u , co: strcpy(drugi, pierwszy); strcpy(trzeci, drugi);
W o b u p rz y p a d k a c h n a jp ie rw C -string z tablicy p i e r w s z y k o p io w an y jest d o I b c y drugi, a n a s t ę p ^ z tablicy drugi d o tablicy t r z e c r . W r e z u lta c e w e w sz y stk ic h trzech tablicach b ęd zie n ap is „ h u rra . M im o ż e naszą funkcję w y p o sa ż y liśm y w ty p z w ra c a n y c h a r * , m o ż em y ją u ż y w a l n a d w a p o w y ż sz e sposoby. N ik t b o w iem m e k aże n am k o rzy stać z r e z u t o t u z w r a c a n e g o p r z e z t a k c j ę . U d ajem y p o p ro s tu , z e fu n k q a zw raca void. In n e p o ż y te c z n e fu n k cje
R o z w a ż m y teraz n a stę p u ją c y p rz y p a d e k char stara [80] = { "zdanie trzecie char n o w a [80]; cout << (strcpy(nowa,
stara+1)
),
);
P y ta n ie : C o zo stan ie w y p is a n e na ekran? C zv li i n a c z e j - c o b ę d z i e t r e ś c i ą ta b lic y
nowa?
Z a u w a ż m y , że w y sy łam y d o funkcji s t r e p y ( s t a r a + 1 ) . C o jest w a rto śc ią tego w y rażen ia? To p ro ste. P am iętasz przecież u a ^ k o ro n n ą z asad ę , ż e n azw ą tablicy jest w sk a źn ik ie m d o ]e) zero w eg o e le m e n tu ? A p am ięta sz , z e d o d a n ie liczby całkow itej n d o w sk a źn ik a p o w o d u re z u lta t p o k a z u je o n e lem en tó w dalej? Słow em : w y ra ż e n ie ( s t a r a * 1) 4 e ś t a d re s e m nie z e ro w e g o elem en h t tablicy, ty lk o p ie rw szeg o . W skaźnik s t a r a p o k a z y w a ł n a lite rę ' z ',a w y ra ż e n ie ( s t a ' d ' T e n w łaśn ie a d re s p o sy ła n y jest d o funkcji s t r c p y . to jakoś p rz e sz k a d z a funkcji s tr c p y ? S k ą d ż e ! Funkcja ta, n ie zale żn ie co je p rz y ś le m y , ro zp o cz y n a k o p io w a n ie o d tego m iejsca w p am ięci a z d o n a p o t C zy
k a n ia nuli. W s u m ie zatem d o tablicy nowa zo stan ie sk o p io w a n y C -strin g . "danie trzecie"
b o w ie m p ie rw sz ą literę ' z ' - przeskoczyliśm y.
streat O to fu n k cja, k tó ra d o p isz e d o jed n eg o C -strin g u d ru g i. P rz y k ła d o w o jeśli p rz e t o p e ra c ją m ieliśm y d w a C -stringi: "Najpierw to " "a teraz tamto"
Rozdział. 8. W skaźniki W ariacje na tem at C-stringów
335
po operacji b ęd ziem y mieli "Najpierw to a teraz tamto" Takie łączen ie n azy w a się konkatenacją, stąd n azw a funkcji s t r c a t . * Z now u p ro s ty program : # in c lu d e < io s tre a m > u s in g n am esp a ce s t d ; ch a r* s t r c a t ( c h a r * c e l, c o n s t c h a r * z r o d l o ) ; Z ************************************************************’ / int main() { char co[] = { "urządzeń sterowych" ); char komunikat[80 ] = { "Alarm: " }; s tr c a t(k o m u n ik a t, c o ); c o u t << "po d o p is a n iu = " « k o m unikat << e n d l ; c o u t << ( s tr c a t ( k o m u n ik a t, ", o godz 1 7 :1 2 " )
);
// // %
) / ************************************************************** *J // © c h a r * s t r c a t ( c h a r * c e l, c o n s t c h a r * z ro d lo ) ( c h a r * p o c z a te k = c e l ; // przesunięcie napisu na koniec C-stringu w h i l e ( * (c e l+ + ) ) ;
// O
II teraz pokazuje o 1 znak za nuli c e l— ;
// ©
// to juz braliśmy przy strcpy w h i l e ( ( * (cel+ + ) = * (z ro d lo + + ))
);
// ©
r e t u r n p o c z ą te k ; )
□
Po wykonaniu tego programu na ekranie pojawi się po d o p is a n iu = Alarm: u rz ą d z e ń stero w y ch A larm : u r z ą d z e ń s te ro w y c h , o godz 17:12
Oto ciekawsze punkty programu: © Jest to funkcja przyjm ująca, jako argum enty, d w a w sk aźn ik i d o tablic zn ak o w ych. Z w raca także w skaźnik d o tablicy znakow ej. Z p o p rzed n ich stron w iem y już dlaczego. O Aby dopisać coś d o C-stringu zło żo n eg o w tablicy c e l , p o w in n iśm y najpierw w skaźnik, p o k azu jący na jej początek, przesunąć tak, by p o k azał na koniec, czyli na k ończący C -string znak nuli. Robimy to w łaśnie tą instrukcją. © W p o p rzed n iej instrukcji pętlą w h i l e znaleźliśm y zn ak nuli, ale p o n iew aż instrukcja zaw ierała postinkrem entację, więc w sk aźn ik c e l pokazuje teraz na
336
Rozdz. 8. W skaźniki W ariacje na tem at C-stringów n a s tę p n y zn ak z a z n a k ie m n u li Jest tam jakiś śm ieć. M u sim y się w ięc cofnąć w sk a ź n ik ie m ta k , b y p o k a z y w a ł o n n a zn ak n u li 0
Jest to id e n ty c z n y p ro ces k o p io w a n ia jak w funkcji s t r o p y . Z n a k i zostają p rz e p is y w a n e tak , że p ie rw s z y z nich zniszczy (zatrze) zn ak nuli kończący C -strin g A larm : ". K olejne z n a k i b ę d ą d o p isy w a n e p o c z ą w sz y od te g o miejsca. W re z u lta c ie o trzy m am y w y d łu ż o n y C -string, a n a k o ń cu o czyw iście znajdzie się z n a k n u li . , ., Widzisz tu dodatkową parę nawiasów. Postawione są one po to, by upewnić kompilator, ie naprawdę w ramach warunku chcemy zrobić przypisanie (a nie porównanie). Dzięki tym nawiasom - unikam y tu ostrzeżeń
kompilatora. O N a d o w ó d , że ta cala o p erac ja n a m się p o w io d ła - w y p isu jem y re z u lta t na ek ran ie. 0 D o ta b licy komuni kat m o ż e m y do p isać jeszcze d a lsz y C -string. T ym razem nie jest o n treścią tablicy, ale w sta w iliśm y go b e z p o śre d n io jako a rg u m e n t o g ra n i czo n y c u d zy sło w am i:
strcat (komunikat, "abc"l; C zv m o ż n a tak? M o żn a - K o m p ilato r bow iem bez n a sz e g o sp ecjaln eg o żąd a n ia u m ie śc ił ten C -strin g g d z ie ś w pam ięci k o m p u te ra . (C -stringi ujęte w c u d z y s ło w y trak to w a n e są tak, jakby były s t a t i c - m ają g d zieś swoje o k re ś lo n e m iejsce w p am ięci - n aw et jeśli tego m iejsca n ie zn am y ). D o funkcji z o s ta n ie w ięc w y słan y a d re s tego specjalnego m iejsca w pam ięci, g d z ie mieści się n a s z C -string. N ie m o ż n a jed n ak zrobić n astępującej operacji strcat ("Uwaga:", "abc"); //straszny błąd ! O ba C -strin e i są w te d y g d z ie ś w pam ięci (z m iejscu n ie n ależący m d o nas). Z d ru g im n ie m a p ro b lem u , b ę d z ie m y go przecież ty lk o czytać. N ato m ia st d o tego p ie rw s z e g o chcem y coś d o p isa ć (w łaśn ie ten d ru g i). T y m czasem na p ie rw szy C -strin g z a re z e rw o w a n o 7+1 b ajtó w i an i jed en więcej. D alej te re n należy d o k o g o ś (czegoś) innego. D o p isy w a n ie cos d o tego C -stn n g u b ę d z ie w ięc n iszczeniem czegoś, co jest b e z p o śre d n io z a ty m C -strm giem . To m a z w y k le fatalne sk u tk i.
Większość, zgodnych ze standardem, kompilatorów do takiego błędu jednak nie dopuści. Dlaczego? o t ó ż - w myśl s ta n d a r d u - s t a ł a d o sło w n a b ęd ąca C -strin g iem (np. "Uwaga : ") jest ty p u c o n s t c h a r [ ] . Czyli: jest to " n ie ty k a ln a " ta b lic a z n a k ó w . T y m czasem p ie rw szy a rg u m e n t fo rm aln y naszej f u n k c jis t r c a t jest o k reślo n y jako c h a r *, a zatem n ie g w a ra n tu je C -strin g o w i n ie ty k aln o ści (nie m a słow a c o n s t ) . K om pilator nie p o w in ie n zg o d zić się w ięc na tak ie w y w o łan ie funkcji. T ak s a m o , jak nie p o w in ie n zg o d zić się na tak ą instrukcję.
char *wsk = "Uwaga:
Rozdział. 8. W skaźniki W skaźniki do funkcji
337
Stare k o m p ilato ry na to się zgadzają, jednak w ed łu g s ta n d a rd u m oże być tylko: const c h a r *wsk
B.19
"Uwaga:
W s k a ź n ik i d o fun kcji Jak w iem y, w sk aźn ik iem m ożna p o kazyw ać na różne obiekty. O kazuje się, że logiczne jest tak że pokazanie na funkcję. To tak, jakbyśm y pow iedzieli: a teraz m asz w y k o n a ć tę funkcję. O statecznie w sk aźn ik zaw iera adres, w ięc czem u nie m iałb y to być adres tego miejsca w pam ięci, gdzie zaczyna się kod będący instrukcjam i żądanej funkcji. O to p rz y k ła d definicji takiego w skaźnika: in t
(*w fu n )( ) ;
Jak się ta k ą deklarację czyta? Z aczy n am y od nazw y. N astępnie p o ru szam y się (o ile można) w p raw o , dlatego że po praw ej stronie m ogą stać tylko operatory () lu b [] - a, jak w iem y, są one najsilniejsze z m ożliw ych. Potem , gdy już się nie d a w p raw o (bo napotkaliśm y naw ias zam ykający), p o ru szam y się w lewo. Jeśli odczy taliśm y w szystko w obrębie dan eg o naw iasu w ychodzim y na zew n ątrz niego i zno w u zaczynam y w praw o. A zatem naszą pierw szą definicję w skaźnika p rzeczy tam y tak: wfun
w p raw o się n ie da, bo jest naw ias zam ykający, w ięc id z iem y w lewo (*wfun)
- jest w sk a ź n ik ie m ... - (załatw iliśm y cale w nętrze naw iasu ,więc w ychodzim y na z e w n ą trz i p o ru szam y się w praw o, gdzie stoi b ard zo m o cn y operator w yw o łania funkcji (*wfun) ()
- do fu n k c ji w y w o ły w a n ej bez żad n y c h arg u m e n tó w (naw ias był pusty) teraz ju ż w lew o - a zwracającą... in t
(*wfun) ()
...w artość ty p u i n t cs=
Bardzo w a ż n e b y ły tu naw iasy. G dybyśm y je opuścili i n a p isali ostatnią defini cję tak: in t * w f ( ) ;
to byłaby to deklaracja funkcji (a nie w skaźnika d o funkcji). W edle pow yższej reguły cz y ta m y ten zapis tak:
338
Rozdz. 8. W skaźniki W skaźniki do funkcji w f ()
w f jest funkcją w y w o ły w a n ą b ez ża d n y ch a r g u m e n tó w , a zw racającą * wf ()
w s k a ź n ik d o in t
Si&fffHJT ->r> •'
* w f()
.<■) u f w o r t i f i i j i n
ty p u i n t
-. rt r.a s k u r ę ip ła
A z a te m co ś z u p e łn ie in n e g o . T o d latego, ż e n a w ia s y są silniej Z o b a c z m y czy m p r ę d z e j, jak stosu je się to w praktyce. O to p r z y k ła d prostegc p rogram u : # in c lu d e < io s tre a m > u s in g n am esp ace s te l; i n t p i e r w s z a { ); i n t d ru g a (); * * */ */ * *O, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / i n t m a i n () 1i tjfw n r| o q in t i ; ‘vrń^ś~ji > 't • V ^ // & i n t (* w s k a z f u n ) ( ) ; rt -■#* ■ . ■■ , : cout << "Na k tóra funkcje ma pokazać wskaźnik ?\n' "pierwsza -\tl \nczy druga - \t2 \n" t J ” n a p i s z n u m e r: " ; // ©i c i n >> i ; s w itc h (i){ c a s e 1: // O w sk a z_ fu n = p ie rw s z a ; b re a k ; j [* /i c a s e 2: w sk a z _ fu n = d ru g a ; b re a k ; d e fa u lt: // © w sk a z _ fu n = 0; b re a k ; } c o u t << " W e d łu g r o z k a z u if(w s k a ż
! \n " ;
//j e ś l i a d r e s
fu n )
fo r(i = 0 ; i < 3 ; { ( * w sk az f u n ) ( ) ;
©
n ie z e r o w y
fr
i++>
//
©
'+ + * * * + * * + * + * * * * * * * * / in t {
p i e r w s z a {) c o u t << " f u n k c j a r e t u r n 9;
int
d r u g a ()
pierwsza
! \n";
Rozdział. 8. W skaźniki W skaźniki do funkcji
339
c o u t << " f u n k c ja d ru g a ! \ n " ; r e t u r n 10 6 ; )
□
Po wykonaniu na ekranie zobaczymy na przykład następujący wydruk Na k t ó r a f u n k c je ma p o k a z a ć w skaźnik ? p ie r w s z a 1 c z y d ru g a - 2 n a p i s z n u m e r :2 Według ro z k a z u ! f u n k c ja d ru g a ! f u n k c ja d ru g a ! f u n k c ja d ru g a !
Kilka słów o tym programie: O D eklaracje d w ó ch funkcji. C zytam y: funkcja p i e r w s z a je st funkcją w yw oływ a ną bez żad n y c h argum entów , k tó ra jako rezultat zw raca w artość typu i n t . Funkcja d r u g a - tak samo. © Definicja w sk aźn ik a m ogącego p o k azy w ać na te wyżej zad ek laro w an e funkq'e. To dlatego, że w m yśl definiqi (czytam y:) w sk az f u n jest w skaźnikiem do p o k azy w an ia na funkcje w y w o ły w an e bez żadnych arg u m en tó w , a zw racające jako re z u lta t w artość typu i n t . © P ytam y uży tk o w n ik a, którą funkcję chce w ykonywać. G Zależnie od o d p o w ied zi (stąd instrukcja s w i t c h ) u staw iam y w skaźnik tak, by p okazy w ał n a żąd an ą funkcję. W praktyce polega to n a w pisaniu do niego adresu danej funkcji. I oto natk n ęliśm y się na inną w ażn ą zasadę:
i& Nazwa funkcji jest inaczej jej adresem w pamięci
Dzięki tem u, tak prosto operuje się w skaźnikiem do funkcji. I U staw ienie go to po p ro stu instrukcja
wskaźnik = nazwaJunkcji; | In n y sposób zrobienia tego samego to
wskaźnik = &nazwaJunkcji; Jak widać, w tym d ru g im zapisie zastosow any jest operator &. N ie jest on konieczny, bo przecież nazwa funkcji jest już jej adresem, ale jeśli go postawisz, kom pilator zrozum ie i w ybaczy. Z auw aż, że nie ma tu żadnych naw iasów tow arzyszących zw ykle nazw ie funkcji. N aw iasy takie rozum iem y jako „wywołaj funkcję o tej nazw ie".
340
Rozdz. 8. W skaźniki W skaźniki do funkcji T y m czasem m y w cale n ie c h c e m y jeszcze jej w y w o ła ć N a razie tylko o niej m C T m y S m a l i a m y się ze w sk a ź n ik ie m , że m a n a * funkcję p o k azy w ać. G d y b y śm y zap o m n ieli i w o m a w ian ej linijce p o staw ili n aw iasy wskazfun = pierwsza () ;
II błąd !
F unkcia zro z u m ia ła b y to jak o z ac h ętę do pracy: „Co? Mówiąc, mnie z nawmsami? To znaczą że mam ruszyć do akcji!". Inaczej m ó w ią c ta linijka o zn aczałab y cos ^ L g o ^ y k o n a j fu n k cję p i e r w s z a , a jej re z u lta t w sta w d o w sk a źn ik a w sk a z _ fu n . R y zy k o je d n a k nie jest ta k ie d u ż e , bo k o m p ilato r n a s s p ra w d z i i n a jp ra w d o p ą d o b n iei d o tego b łę d u n ie d o p u śc i. W ie on, z e funkcja pierw sza ma zw rocie w a r to ś ć t y p i M .n t, a tak iej w arto ści n ie m o ż n a p rzy p isać p o d s aw ,c) do k tó ry s p o d z ie w a się ad resu funkcji. K o m p ilato r w rd ząc te n.ezgod n o ść zap ro testu je.
Zapamiętać G d y m ó w is z k o m u ś o fu n k c ji - to u ż y w a s z s a m e j n a z w y b e z n a w ia s u i a rg u m e n tó w . N a w ia s y są o p e ra to re m w y w o ła n ia tej fu n k c ji 1
nom v Ho każdeeo t y p u w s k a ź n ik a m o ż e m y p o d s ta w ić a d re s zero w y . N ie h n k c ji, ale p o p ro s tu w y g o d n ie sie potem
o b ecn o ść te g o ad resu z e ro w e g o sp raw d za, n
TP^li n ie d o staliśm y o d u ż y tk o w n ik a żad n ej sen so w n e j o d p o w ie d z i - to do w sk a ź n ik a w s t L i h ś m y 0. O czyw iście nie m o ż e m y n a w e t p ró b o w ać u ru c h o m ić fu n k cji o tak im ad re sie . D lateg o s p ra w d z a m y : jeśli jest ^ e. w sk ^ nlku_C^ in n e g o n iż 0, to w te d y u ru c h o m im y tak p o k a z a n ą fu nkcje. Jes p rz e sk a k u je m y ten frag m e n t.
O N ie p rz e ra ż a j się tą in stru k cją. P orów naj d w ie in stru k cje: pierw sza();
/ / c z y l i to sa r n o co:
( p i e r w s z a ) () ;
o ra z (*wskaz_fun) ();
Z d o ty c h czaso w y ch ro z m ó w o w sk aźn ik ach p a m ię ta sz ju ż , że z a p is
*wskażnik o z n a c z a - to, na co w sk a ź n ik p o k azu je". W n a s z y m p rz y p a d k u p o k azu je on n p n a funkcję pierwsza. D latego p o w y ższe d w a z a p isy są ró w n o w a ż n e , e d w a (w tym p rz y p a d k u p u ste ) naw iasy to o czy w iście sy g n ał, ze chcem y by fu n k cja w y starto w ała. To s a m o w y w o łan ie fu n k cji p o k azy w an ej w sk a ź n ik ie m m o ż n a n a p isa ć jeszcze p ro ściej jako: wskaż fun();
Rozdział. 8. W skaźniki W skaźniki do funkcji
341
W y w o łan ie funkcji po k azy w an ej w sk aźn ik iem m ożna zapisać na d w a sposoby:
3.19.1
•
w skaźnik_fu n kcji (argumenty);
•
(*w skaźnik_funkcji) (argumenty);
Ć w iczenia z definiow ania w skaźników do funkcji N ie m a nic tru d n e g o w e w sk aźn ik ach do funkcji. Jeśli jednak początkujący program iści się ich boją, to p o w o d em tego jest, m oim zd an iem , zapis. Z tym i g w iazd k a m i i n aw iasam i trzeba się oswoić. U w ażam , ż e sw o b o d ę o p ero w an ia w skaźnikam i d o funkcji nabyć m ożna tylko w tedy, g d y u m ie się czytać ich definicje i takie definicje sam em u pisać. D latego proponuję: sk o ro już w iem y jak czytać deklaracje (i definicje) w skaźników d o funkcji, to
Spróbujmy sami napisać definicję wskaźnika do pokazywania na określo ny typ funkcji W iem , że jest to trochę n u d n e, ale obiecuję Ci, że jeśli n au czy sz się teraz czytania i zapisu definicji w skaźników (tak że do funkcji) - to b ędziesz się zaw sze czuł pew n ie p rz y p ro g ram o w an iu w C++. W yobraźm y sobie, że m am y g d zieś funkcję i n t m u z y k a( ) ; która o d g ry w a m elodyjkę. C hcem y teraz zdefiniow ać w sk aźn ik m ogący p o k a zyw ać na ta k ą funkcję. W skaźnik m a się nazyw ać na p rzy k ła d www. Piszem y w ięc na środku linijki n azw ę www i będziem y ją o b u d o w y w ać dookoła. M ów im y w ięc www WWW
jest w sk a źn ik ie m (*www)
służącym d o pok azy w an ia na funkcję (*www)()
zw racającą w arto ść typu i n t in t
(*www)();
Gotowe! (czyli trium falny średnik na końcu). Jeśli się już taki wskaźnik ma, to na naszą funkcję m u zy k a ustawia się go choćby taką prostą instrukcją: www = muzyka;
342
Rozdz. 8. W skaźniki W skaźniki do funkcji A te ra z in n y w sk a źn ik . M a się on n a d a w a ć d o p o k a z y w a n ia na fu n k cję double dzielenie(int, int) ;
F u n k cja ta na p rz y k ła d d zieli d w ie liczby całkow ite i zw raca n a m rezu ltat d z ie le n ia . Z b u d u jm y w ię c w sk a źn ik d o niej. N iech się o n n a z y w a d . Z a em m ó w im y d d d i z a p isu je m y ddd
je st w sk a ź n ik ie m (*d d d )
d o fu n k cji w y w o ły w a n ej z d w o m a a rg u m e n ta m i typu i n t (*ddd)(int,
int)
a zw racającej w arto ść ty p u double double
(*ddd)(int,
int);
G otow e!
W N ie d rz y j jeszcze tej książki! W iem , że to w sz y stk o jest su ch e, n u d n e i form alne, a le m a m d la C iebie te ra z p reze n t. P o d am Ci sposób...
Jak nie rozumiejąc niczego, napisać sobie
d e fin icję
wskaźnika mogącego
pokazywać na daną funkcję D ajm y n a to, że c h o d z i o w sk a źn ik m ogący p o k a z a ć na ta k ą fu n k cję double funkcyjka(int, char);
C zy li n a funkcję w y w o ły w a n ą z d w o m a a rg u m e n ta m i (ty p u i n t o ra z c h a r ) , a z w ra c a ją c ą w rezu ltacie w y k o n a n ia ty p double. M ój s p o só b po leg a na ty m , że b ierzem y d ek larację tej fu n k cji i w tej d d c la ra q i n a z w ę fu n k cji - z a s tę p u je m y ujętą w naw ias n a z w ą w sk a ź n ik a z g w ia z d k ą z p rz o d u . C zy li d o k o n u je m y takiej zam iany: nazwa_funkcji
---------------- >
(*nazwa_wskaźnika)
W re z u lta c ie o trz y m u je m y definicję w sk a źn ik a m o g ąceg o p o k a z y w a ć na d a n ą funkcję. Jeśli tak z ro b im y z naszą deklaracją, w ó w c zas w re z u lta c ie o trzy m u je m y z a p is double
(‘nazwa wskaźnika)(int,
char);
k tó ry je st d o k ła d n ie ty m , o co n am ch odziło. Jest to p o s z u k iw a n a definicja w s k a ź n ik a . Żeby się p rzek o n a ć p rzeczy tam y te n zap is. nazwa_wskaznika (*nazwa_wskaznika) (*nazwa_wskazni k a ) (...)
- je s t wskaźnikiem - d o funkcji
Rozdział. 8. W skaźniki W skaźniki do funkcji
343
(*nazwa_wska znika) (int, char) - w y w o ł y w a n e j z d w o m a a r g u m e n ta m i double (*nazwa_wskaznika) (int, char) - a z w r a c a ją c e j t y p double
S p ry tn e , p ra w d a ? M o żn a to zro b ić z u p ełn ie bezm yślnie! N o, m oże praw ie b e z m y śln ie . To d lateg o , ż e p o trz e b n a jest u m iejętn o ść p rzeczy tan ia tego, cośm y zd e fin io w a li. T ak d la k o n tro li.
Podam Ci teraz bardzo ważną rzecz - sposób jak czytać skomplikowane deklaracje O tóż z a s a d a jest taka, że: 1) Z aczy n am y c z y ta n ie o d w ygłoszenia n a z w y , której deklarację czy tam y. 2) N astęp n ie o d tej n a z w y p o su w a m y się w p raw o . W p raw o , d la teg o że tam m o g ą stać najm ocniejsze o p e ra to ry . O p erato r w y w o łan ia funkcji lu b in d e k so w a n ia tablicy. (Te o p e ra to ry , jak p am ięta m y z n ap o tk am y tam , o d czy tu jem y na głos. 3) Jeśli w p ra w o ju ż nic nie m a, lu b n a tk n ie m y się na zam y k ający n aw ias - w ó w c zas zaczy n am y czy tan ie w lewo. C zy tan ie w lew o k o n ty n u u jem y d o tą d , dokąd w sz y stk ie g o nie p rzeczy tam y , lub g d y nie n atk n iem y się na zam ykający n a s naw ias. 4) Jeśli n ap o tk a m y taki naw ias, to w y c h o d z im y na z e w n ą trz i - b ęd ąc już na z e w n ą trz te g o naw iasu - z n o w u zac zy n am y w p ra w o czyli w racam y d o p u n k tu 2) P ro c e d u rę p rz e p ro w a d z a m y d o p ó k i nie p rz e c z y ta m y w szy stk ieg o w tej deklaracji.
Jak "czytać" ? B ardzo p ro sto . Jeśli na naszej d ro d z e n ap o tk am y zn a c z e k • •
* (gw iazdkę) - czytam y: jest w sk a źn ik ie m m ogącym p o k a z y w ać na... ( t y p l , t y p 2 ) -c z y ta m y : jest fu n k cją w y w o ły w an ą z a r g u m en tam i ty p l, ty p 2 (tu czytam y ty p y będące w naw iasie), a zw racającą jako rezultat...
•
[ n ] - czytam y: jest n -elem en to w ą tablicą
D ygresja:
Ze względu na fleksję w języku polskim -n ie jest to do końca tak eleganckie. Czasem zamiast „jest wskaźnikiem" lepiej by było powiedzieć „będący wskaźnikiem". Sądzę jednak, że szybko się nauczysz, jak to wypowiadać zgrabnie. W y p ró b u jm y ten sposób czytając to, co bezm yślnie w y p ro d u k o w a liśm y n ie d aw n o , c z y li nasz zapis double
(*amazonka) (int, char)
344
Rozdz. 8. W skaźniki W skaźniki do funkcji
Ja k w i d z i s z , te r a z s a m ą n a z w ę z m ie n iłe m , b y C z y ta m y :
nam
n ic n ie s u g e r o w a ła .
W e d ł u g p u n k t u 1) m ó w im y n a g ło s: | am azonka W e d ł u g p u n k t u 2) c h c e m y p o r u s z a ć się w p r a w o , a le s ię n ie d a , b o o d r a z u n a p o t y k a m y o g r a n ic z a ją c y n a s n a w ia s . d o u b le
( ‘ am azonka) ( i n t ,
c h a r);
Z a t e m p r z e c h o d z i m y d o p u n k t u 3). W e d ł u g p u n k t u 3) id z ie m y w le w o i n a p o t y k a m y g w ia z d k ę , d o u b le
(* a m a z o n k a ) ( i n t ,
ch a r);
c o o z n a c z a „ je s t w s k a ź n ik ie m m o g ą c y m p o k a z y w a ć n a ..." . M ó w im y to n a g ło s i —w o b e c te g o - n a s z a d o ty c h c z a s o w a w y p o w i e d ź to : I a m a z o n k a je s t w s k a ź n ik ie m m o g ą c y m p o k a z y w a ć n a ... D a le j w l e w o s ię n ie d a , b o n a p o t y k a m y n a z a m y k a ją c y n a s n a w ia s . d o u b le
( ‘ am azonka) ( i n t ,
c h a r);
W y c h o d z im y w ię c na z e w n ą trz tego n aw iasu i z a c z y n a m y zn o w u p o su w a ć s ię w praw o. d o u b le
( ‘ a m a z o n k a ) (int, c h a r ) ;
W p r a w o n a p o t y k a m y z a p is ( i n t , c h a r ) , c o u p o w a ż n i a n a s d o p o w i e d z e n i a n a g ło s : „ f u n k c j a w y w o ł y w a n a z 2 a r g u m e n ta m i ( t y p u i n t i c h a r ) , a z w r a c a ją c a ja k o r e z u l t a t ..." T u p o l o n i s t a d o s ta je z a w a łu s e r c a , a s p o d k r o p ló w k i d o c h o d z i je g o ję k o ty m , ż e p o p r a w n i e p o p o ls k u m a b y ć: a m a z o n k a je s t w s k a ź n ik ie m m o g ą c y m p o k a z y w a ć n a f u n k c ję w y w o ły w a n ą z d w o m a a r g u m e n ta m i ( t y p u i n t i c h a r ) , a z w r a c a j ą c ą ja k o r e z u lta t... W p r a w o je s t ju ż ty lk o ś r e d n i k , w ię c z m ie n ia m y k i e r u n e k i c z y ta m y w le w o . double
(‘ a m a z o n k a ) ( in t,
c h a r);
T a m je s t t y l k o ty p d o u b l e , c o c z y t a m y n a g ło s . T o ju ż k o n ie c , w ię c w s u m i e p o w i e d z i e l i ś m y n a g ło s: a m a z o n k a je st w s k a ź n ik ie m m o g ą c y m p o k a z y w a ć n a f u n k c ję w y w o ły w a n ą z d w o m a a r g u m e n t a m i ( t y p u i n t i c h a r ) , a z w r a c a ją c ą ja k o r e z u l t a t t y p d o u b l e . J e s t to d o k ł a d n i e ta k i w s k a ź n ik , o ja k i n a m c h o d z iło . Z a t e m b e z m y ś l n y s p o s ó b o k a z u j e s i ę d o s k o n a ły . O je g o g e n i u s z u p r z e k o n a l i ś m y s ię ty lk o d z ię k i te m u , ż e n a u c z y l i ś m y s ię d e k la r a c je c z y ta ć . O c z y w iś c ie ju ż c h y b a r o z u m i e s z , ż e z a s to s o w a łe m p o d s t ę p , b o p r z e c ie ż —g d y u m i e s z j u ż c z y ta ć d e k la r a c je - s p o s ó b n ie je s t b e z m y ś l n y .
Rozdział. 8. W skaźniki W skaźniki do funkcji
345
P is a n ie d e k laracji jest ty lk o tro c h ę tru d n iejsze n iż ich czytanie. R óżnica polega n a ty m , ż e n ajp ierw m ó w im y na głos, a p o te m to, co p o w ied zieliśm y , z a p is u je m y . J e d n a k w y d a je m i się, że ła tw ie j od czy ty w ać n iż zap isy w ać, w ięc lepiej u ży ć te g o a u to m a ty c z n e g o s p o so b u d o zapisu - a p o te m przeczy tać, by sp raw d zić. G d y n a u c z y s z się ju ż d o b rz e czytać, czyli z ro z u m ie s z istotę tego czy tan ia, to ty m s a m y m b ęd ziesz ju ż u m ia ł zapisyw ać. D o z a g a d n ie n ia tego w ró c im y jeszcze na stro n ie 748. B a rd z o rz a d k o się z d a rz a , b y definicje w s k a ź n ik ó w d o funkcji były b ard ziej s k o m p lik o w a n e . Tak w ięc raczej „dla h ecy" p rz y to c z ę taki w sk aźn ik - sp ró b u j g o n a jp ie rw sam o d czy tać in t
( * (* fw )(in t, ch a r* )
) [ 2] ;
P o d d a je s z się? —N o to s p ró b u jm y razem . Z a c z y n a m y od śro d k a, g d z ie jest n a z w a , a p o te m w p ra w o ile się d a , potem w lew o ile się da, a jak się już n ie d a, to w y c h o d z im y na z e w n ą trz n a w ia s u i k o n ty n u u jem y . Z atem fw w p r a w o się ju ż nie d a , w ięc w lew o *fw | je st w sk a ź n ik ie m ... w le w o się ju ż nie d a , w ięc w y ch o d zim y na z e w n ą trz n aw iasu i c z y ta m y z p ra w e j (* fw )(in t,
c h a r *)
...do fu n k c ji w y w o ły w a n e j z 2 a rg u m e n ta m i: ty p u i n t i ty p u c h a r * , a zw racającej... ( * ( *fw) ( i n t ,
char *) )
d alej w p r a w o się już nie d a, b o jest naw ias, p ró b u je m y w lew o, a tam jest * czyli c z y ta m y : ...w sk a ź n ik do... w y c h o d z im y na z ew n ątrz n aw ia su i czytam y z p ra w e j ( * (*fw) ( i n t , c h a r *)
) [2]
| ...d w u e le m e n to w e j tablicy... w p r a w o się już nie d a w ięc w lewo, a tam sto i tylko i n t in t
( * ( * f w ) ( i n t , c h a r *) )[2 ]
| ...o b iek tó w ty p u i n t . W s k ró c ie b rzm i to tak: f w jest w skaźnikiem d o fu n k cji w y w o ły w an ej z a rg u m e n ta m i ( i n t , c h a r * ) , a zw racającej w sk aźn ik d o d w u elem en to w ej tablicy ty p u i n t .
346
Rozdz. 8. W skaźniki W skaźniki do funkcji W ty m p r z y k ła d z ie ch odziło m i bardziej o to, byś zo b aczy ł ja k próbow ać ta k ie z a g a d k i ro z w ik ła ć . N ie przejm uj się też jeśli Ci to nie p o szło . W mojej codzien n ej p ra k ty c e n ig d y nie w y m y ślałem tak sk o m p lik o w an y ch w sk a źn ik ó w do fu nkcji.
Zastrzeżenia >11III—II m ini I iiiiin liii—rn iM iBJ IMiimwiw lii i n iiiiiii ■rraw iMii»it rifii[ifn -n^>^riiTr
—
—
W skaźnik do pokazyw ania na funkcje jednego typu, nie może być używany do p o ka zyw a n ia na funkcje innego typu. ,
T yp w s k a ź n ik a d o funkcji m u si się zg ad zać z ty p em funkcji. Jeśli m a m y n p . funkcję c h a r fu n (d o u b le , i n t ,
i n t , d o u b le , c h a r ,
d o u b le ) ;
to nie m o ż e m y n a nią p o k azać w sk aźn ik iem in t
( *wsk) () ;
K o m p ilato r się n a to nie z g o d zi. Do p o k azan ia n a tak ą funkcję nadaje się ty lk o w sk a źn ik char
(* w w w )(double, i n t ,
i n t , d o u b le , c h a r , d o u b le ) ;
Dlaczego wskaźniki nie są uniwersalne? Dlaczego nie jest to po prostu wskaźnik do funkcji i już?! Tak n ie je st - z n o w u dla n aszeg o d o b ra. K om pilator m u si w ied zieć, jaki jest ty p w sk a ź n ik a p o to, by w ykryć czy nie pom yliliśm y się p rz y w y sian iu a rg u m e n tów . P rz y w y w o ła n iu funkcji za pom ocą w sk aźn ik a www( 3 .1 4 ,
1 ,5 );
k o m p ila to r s p ra w d z a nas czy ta lista a rg u m e n tó w z g a d z a się z listą, k tó ra sto i p rzy d efin icji w sk aźn ik a www. A czy ta lista z g a d z a się z listą oczek iw an ą p rz e z w sk a z y w a n ą funkcję? M usi się zg ad z ać, bo inaczej k o m p ila to r o d m ó w iłb y u sta w ie n ia teg o w sk aźn ik a na tę funkcję. Po p ro stu n ie d a ło b y się w y k o n a ć p rz y p isa n ia wsk = f u n ; www = fun;
// Błąd - niezgodność typu wskaźnika z typem funkcji // O .K . - z g a d z a ją się tc t y p y
I jeszcze jed n o : N a w sk aźn ik ach d o funkcji nie w o ln o ro b ić o peracji a ry tm e ty c z nych. T o o czy w iście jest in tu icy jn ie w y czu w aln e. C o b o w ie m m iałoby z n a c z y ć odjęcie o d siebie d w ó ch w sk a źn ik ó w d o funkcji? B ezsens.
W T yle d o tej p o ry m ów iliśm y o definicjach w sk a ź n ik ó w , ż e m ogłeś o d n ie ść w rażen ie, ż e to coś tru d n eg o . W ręcz przeciw n ie. P rz y p o m in a ją o n e p o z n a n e w cześniej w sk a ź n ik i do z w y k ły ch obiektów . W szczeg ó ln o ści trz e b a
Rozdział. 8. W skaźniki W skaźniki do funkcji
347
przy p o m n ieć, że tak, jak w p rzy p ad k u k ażd eg o w skaźnika, i ten nie pokazuje na nic, d o p ó k i nie w staw im y do niego adresu funkcji, na którą m a pokazyw ać. P o d su m u jm y naszą dotychczasow ą w iedzę o w skaźnikach d o funkcji: Podobnie jak w przypadku tablic, nazwa funkcji jest równocześnie adresem jej początku - (czyli adresem miejsca w pam ięci, gdzie zaczyna się kod odpow iadający instrukcjom tej funkcji).
Tę z a sa d ę także p ro p o n u ję przykleić sobie n ad biurkiem . P rzy d a się ona jednak trochę rzadziej, bo w sk aźn ik am i do funkcji nie posługujem y się aż tak często. Jeśli zdefiniow aliśm y sobie w skaźnik int
(*wf)();
to instrukcja wf = muzyka;
sp raw ia, że od tej pory w sk aźn ik zaczyna p o k azy w ać na funkcję m uzyka. A zatem m ożem y tę funkcję w yw ołać teraz za p o m o cą jej p raw d ziw ej nazw y lub za pom ocą w skaźnika. Z au w aż, że są dw a sposoby z użyciem w sk aźn ik a, drugi jest w y ra ź n ie czytelniejszy: tts •>•
m u z y k a ();
//z a pom ocą n a z w y
(*wf) O ; wf () ;
//za pomocą w sk a źn ik a //za pom ocą w sk a źn ik a
W y rażen ie ( * w f ) oznacza: „skocz do miejsca w pam ięci, na które pokazuje w sk a źn ik - zap ew n iam , że jest tam funkcja" - a stojące dalej d w a naw iasy m ów ią: „p ro szę tę funkcję w ykonać". G d y b y jednak n ap raw d ę tak miało w yglądać u ży w an ie w skaźników d o funk cji, to korzyść nie byłaby d u ża.
Kiedy zatem tak naprawdę wskaźnik do funkcji może się przydać ? W sytuacjach po d o b n y ch d o tych, w których najczęściej uży w a się zw ykłych w skaźników : ♦♦♦ P rzy przesyłaniu arg u m en tó w do funkcji. A dres innej funkcji m ożna też w ysłać jako arg u m en t. To tak, jakbyśm y pow iedzieli funkcji: tu masz takie arg u m en ty , a dodatkow o tu jeszcze adres funkcji, k tó rą m asz u siebie w ykonać. (N a przykład w p rz y p a d k u aw aryjnym ). ♦♦♦ Do tw orzenia tablic z e w skaźników d o funkcji; w takiej tablicy m am y jakby listę d ziałań i odtąd m ożem y mówić: - „a teraz w ykonajm y funkcję n u m er 5". O o b u tych spraw ach p o w iem y szerzej w n astępnych paragrafach.
8.19.2
W skaźnik do funkcji jako argument innej funkcji
348
Rozdz. 8. W skaźniki W skaźniki do funkcji W y o b raź sobie, że m asz n ap isać fu n k q ę biblioteczną słu żącą d o dialogu z u ż y t k o w n ik ie m . P iszesz tę funkcję d la in n eg o p ro g ra m isty i w iesz, że b ęd z ie o n u ż y w a ł jej w ielo k ro tn ie w różnych m iejscach sw o jeg o p ro g ram u . Tak je st n a p rz y k ła d , g d y p iszesz funkcję biblioteczną, k tóra b ę d z ie tworzyła na ekranie o k ie n k o d ialo g o w e. A by ła tw iej się nam dalej rozm aw iało , u m ó w m y się, że w naszej o p o w ieści w y stę p u ją trz y osoby d ram atu . ♦♦♦ 1. Ty, k tó ry tę biblioteczną funkcję "okienkow ą" piszesz. «$♦ 2. P ro g ra m ista P ro tazy , k tó ry tej Twojej fu n k cji dialogow ej b ę d z ie u ż y w a ł. ♦> 3. U ży tk o w n ik p ro g ram u U rszula, której n a e k ra n ie pojaw i się to o k n o d ialo g o w e. P ro g ra m ista P ro tazy , chcąc u ży ć Twojej funkq'i ( o k n o _ d ia l o g o w e ) , m a ją w y w o ła ć z a rg u m e n ta m i będ ący m i d w o m a tekstam i: •
p ie rw s z y m - b ęd ący m w yjaśn ien iem u ż y tk o w n ik o w i U rsz u li, dlaczego to o k n o d ialo g o w e w d a n e j sy tu acji się pojaw ia,
•
d ru g im - będący żą d a n ie m u d z ie le n ia p rz e z U rszu lę (u ż y tk o w nika) jakiejś o d p o w ied zi.
N a p is a łe ś w ięc funkcję, dzięki której w y św ietlan e je st n a ek ran ie śliczne o k ie n ko d ia lo g o w e z tek stem w yjaśniającym , a g d zieś p o n iż e j pojaw ia się to ż ą d a n ie p o d a n ia w a rto śc i jakiegoś p a ra m e tru (np. te m p e ra tu ry , ciśnienia, czy k w o ty ). G d y U rs z u la (u ży tk o w n ik p ro g ram u ) zobaczy to o k ie n k o , p rzeczy ta ż ą d a n ie i o sta te c z n ie p o d a w arto ść p a ram etru - zam y k asz o k ie n k o . T eraz Tw oja fu n k cja p o p ro s tu z w ra c a (jako rezu ltat) w arto ść tę p ro g ra m iśc ie P ro tazem u , k tó ry Tw oją fu n k cję d ia lo g o w ą w yw ołał. N ib y proste...
Tu jest właśnie kłopot: Liczba d o c ie ra d o zain tereso w an eg o (p ro g ram isty P ro ta z e g o ), ale on n ie jest z a d o w o lo n y - b o d la n iego nie k a ż d a liczba jest d o b ra : cza se m p o w in n a b y ć te liczba d o d a tn ia , in n y m razem p arzy sta , jeszcze in n y m ra z e m z a w a rta w jak im ś p rz e d z ia le . C o p r a w d a , w y w o łu jąc naszą funkcję P ro tazy d o s ta rc z y ł C -strin g ż ą d a n ia „P o d a j jakąś liczbę k o n ieczn e p arzy stą " albo „P o d a j liczbę z p rz e d z ia łu 32 - 39" Ty, p o k a z a łe ś te n tekst U rszuli (na czerw ono!) w o k ie n k u d ia lo g o w y m - ale co z tego, s k o ro U rsz u la (u ży tk o w n ik ) n ie doczy tała te g o u w a ż n ie i o d p o w ie d z ia ła n a p rz y k ła d : 7. O d e b ra łe ś tę liczbę - i cóż, nie m asz żad n ej sz a n sy o d ra z u s p ra w d z ić c z y aby sp ełn ia o n a ży czen ia P rotazego, b o o n w y ra z ił je w „ lu d z k im " języ k u (w C -strin g u p y ta n ia p rzy słan y m jako a rg u m e n t a k tu a ln y ).
Rozdział. 8. W skaźniki W skaźniki do funkcji
349
Tworząc funkcję biblioteczną, nie możesz przecież przewidzieć wszystkich m ożliw ych żądań i ograniczeń wszystkich programistów. Co robić?
na to sposób: nazywa się wywołanie zwrotne. Robisz tak: teraz Twoją funkcję wywołuje się nie z dwoma argumentami (będącym i C-stringiem „wyjaśnienia" i C-stringiem „pytania"), ale jest jeszcze konieczny trzeci argument. Programista Protazy ma dostarczyć teraz dodatkow o nazwę funkcji, której w yś lesz d o sprawdzenia otrzymaną od Urszuli liczbę. | Nazwijmy sobie tę funkcję: funkcją zatwierdzającą. Ta funkcja, którą w skaże Ci programista Protazy, odpow ie Ci na pytanie czy on tę konkretną wartość uznaje w tej konkretnej sytuacji za poprawną. •
Jeśli wartość zostaje zatwierdzona przez niego jako popra wna, m ożesz zamknąć okienko i wartość tę oficjalnie mu przekazać. • Jeśli zaś Protazy uznaje tę wartość za niewłaściwą - nie zam y kasz okienka dialogowego, lecz informujesz użytkownika Urszulę, że wartość jest zła, po czym ponawiasz pytanie o (poprawną) wartość. ,LB Taką funkcję zatwierdzającą - czasem nazywa się też „walidatorem' Kto m a napisać tę funkcję? Oczywiście ten, kto najlepiej potrafi ocenić czy dana wartość go zadowala, czy nie. Taką osobą jest właśnie programista Protazy posługujący się Twoją funkcją biblioteczną. Teraz w ięc Protazy wywołując Twoją funkcję o k no_dialogowe, powinien dostarczyć jako jeszcze jeden argument - nazw ę (adres) innej, prostej funkcji, która odpow ie na pytanie czy w danych warunkach jakaś wartość jest zadowalająca czy nie. Zobaczym y teraz program. linclude using namespace std;
int okno_dialogowe(
const char * opis, const char * pyt, booi (*wsk fun zatwierdź)(int));
//
O
bool czy_parzysta(int liczba); II g bool czy dozwolona dla detektora (int liczba); I I Hf I /***** *7* ******** *********************************************
18)
ang. to oalidate - uprawomocniać, zatwierdzać, ratyfikować, nadawać moc obowiązującą, nadawać ważność.
350
Rozdz. 8. W skaźniki W skaźniki do funkcji
int m a i n ()
{ int
//
odp; . . .
odp = o k n o _ d i a l o g o w e ( " P omiar widma promieniowania zakonczony\n# Możemy " "przystąpić d o fitowania t l a ” , " P o d a j l i c z b ę z n a c z n i k ó w tla ?: ",
czy p a r z y s t a ); cout
// C/
<< " D o k o n u j e m y f i t o w a n i a tla w z a z n a c z o n y c h << o d p / 2 << " o b s z a r a c h \ n \ n \ n " ;
odp = o k n o d i a l o g o w e ( "Ustawienie temperatury chłodzenia " P o d a j d o c e l o w a t e m p e r a t u r ę : ",
"
detektora",
czy_dozwolona_dla _d e t e k t o r a ) ; cout
<<
"Otwieram zawory z ciekłym azotem " osiągnięcia temperatury " < < o d p << " s t o p n i \ n \ n \ n " ;
// © \ n a z d o chwi l i
”
//---------------------------------------------------------odp = o k n o _ d i a l o g o w e ( "Dialog bez konieczności sprawdzania", " P o d a j j a k a k o l w i e k liczbę: ", 0) ; cout
<<
"Otrzymana
liczba
to
" << o d p <<
//
©
" stopni\n” ;
}
/★★★*★★**★★*•*■★★★*★★★★*•**★*★★★★***★*★*★*•★★★*■*★*★•*•**★★**★★***■★★ * int o k n o _ d i a l o g o w e ( c o n s t c h a r * opis, c o n s t c h a r *pyt, // b o o l (* w s k _ f u n _ z a t w i e r d z ) (int) )
{ cout « << «
" # # # # # # # # # # # # # # O K N O D I A L O G O W E # # # # # # # # # # # # # # # # \n# " opis " \ n# # ---------------------------------------------------- # # \n# "
int o d p o w i e d z _ u z y t k o w n i k a ; b o o l z a t w i e r d z o n e = true;
c o u t << pyt; c i n >> o d p o w i e d z u z y t k o w n i k a ;
//
©
if(wsk_fun_zatwierdz)
//
€>
//
©
{ zatwierdzone = wsk_fun_zatwierdz(odpowiedz_uzytkownika);
} else
{ zatwierdzone
>
= true;
Rozdział. 8. W skaźniki W skaźniki do funkcji
351
Jwhile(!zatwierdzone);
//O O
COUt <"##############################################\n\n"; return odpowiedz_uzytkownika;
)********************************************************** * Z bool czy_parzysta (int liczba)
//V©
{
//O © cout « « «
"# B L A D M ! \n# Podałeś liczbę " liczba ", a to nie jest liczba parzysta\n" "# Ponawiamy probeNn# >>> return false;
} else return true;
^ * *********************************************************1^L fcool czy dozwolona_dla_detektora (int liczba)
<
//O©
~
if(liczba >= -70)
{ cout
. "# BLAD!!! \n# Podałeś liczbę " liczba , ", a detektor powyżej\n# temperatury -70 stopni "ulegnie uszkodzeniu\n# Ponawiamy probe\n# > » return false; « « «
} else return true;
L ***********************************************************/
Po wykonaniu tego programu na ekranie będzie widniał na przykład taki tekst ############## OKNO DIALOGOWE ################ # Pomiar widma promieniowania zakończony # Możemy przystąpić do fitowania tla # # # # #
Podaj liczbę znaczników tla ?: 9 BLAD!!! . . ,. Podałeś liczbę 9, a to nie jest liczba parzysta Ponawiamy probe » > Podaj liczbę znaczników tla ?: 10
############################################## Dokonujemy fitowania tla w zaznaczonych 5 obszarach
############## OKNO DIALOGOWE ################ # Ustawienie temperatury chłodzenia detektora^ # Podaj docelowa temperaturę: -50
352
Rozdz. 8. W skaźniki W skaźniki do funkcji # BLAD!!!
# P o d a łe ś l i c z b ę -5 0 , a d e t e k t o r powyżej # te m p e r a tu r y -7 0 s t o p n i u le g n ie u sz k o d z e n iu # Ponawiamy p r o b e # >>> Podaj d o c e lo w a t e m p e r a tu r ę : -120 ####################################*######### Otwieram zawory z c ie k ł y m azotem
az do c h w ili
o s i ą g n i ę c i a te m p e r a tu r y -120 s t o p n i
############## OKNO DIALOGOWE ################ # Dialog bez konieczności sprawdzania
##------------------------------------------------------------------------## TT Tl ___ # Podaj jakakolwiek liczbę: 777 ####################################*######### Otrzymana liczba to 777 stopni
O D eklaracja funkcji o n azw ie o k n o d ia lo g o w e . W m yśl d ram a tu rg ii ze w stępu d o tego parag rafu , je st to w łaśnie funkcja biblioteczna, k tó rą Ty napisałeś. R eszta tego p ro g ram u , to dzieło n ieznanego Ci p ro g ram isty P rotazego, który Tw oją funkcją biblioteczną się posługuje. C zy potrafiłbyś p rzeczy tać na głos deklarację tej funkcji? Spróbuj... int o k n o d i a l o g o w e (
const char * opis, const char * pyt, bool (*wsk_fun_zatwierdz)(int)
};
A jak nie, to zróbm y to razem . Są tu d w ie tru d n o ści - sam a deklaracja trzeciego a rg u m e n tu , a potem ju ż d ek laraq a całości. Z atem najpierw ten trzeci argum ent. Jego deklaracja b rzm i tak: •
w s k _ f u n _ z a t w i e r d z jest w skaźnikiem m ogącym pokazy w ać n a funkcje w y w o ły w an e z jed n y m a rg u m e n tem typu i n t , a zw racające jako re z u lta t w artość ty p u b o o l .
N ato m ia st cała deklaracja funkcji brzm i tak o k n o d i a lo g o w e jest funkcją m entam i:
w y w o ły w an ą z trzem a arg u
•
1 . - ty p u c o n s t c h a r * (w skaźnikiem d o C -stringu)
•
2 . - t y p u c o n s t char* (w skaźnikiem d o C -stringu)
•
3. - w skaźnikiem m ogącym ... (tutaj to, co powiedzieliśmy już wyżej na temat trzeciego argumentu)
| a zw racającą jako rezu ltat w arto ść typu i n t . O O to m iejsce w p ro g ram ie Protazego, gdzie u ż y ta zostaje ta funkcja. Jak w id ać 4 w arto ść, którą ona zw raca, Protazy p rzy p isu je do obiektu o d p (to jakby sk ró t od sło w a odpowiedź). S am o w y w o ł a n i e funkcji o k n o _ d ia lo g o w e m a tu trzy a rg u m e n ty (aktualne).
Rozdział. 8. W skaźniki W skaźniki do funkcji
353
P ie rw sz y a rg u m e n t to stała d o sło w n a ty p u C -string. Jest tu ogólne w y jaśn ien ie P ro ta z e g o dlaczego o k ie n k o d ialo g o w e na ek ran ie się p o jaw ia. (C-string ten nie zmieścił się Protazemu w jednej linii, więc rozdzielony wj : został na dwa przylegające do siebie C-stritigi - zauważ, że nie ma między nimi przecinka). ♦♦♦ D ru g i a rg u m e n t (ta k ż e stała d o sło w n a ty p u C -string) - to p ro śb a P rota z e g o d o U rsz u li (u ży tk o w n ik a), by p o d a ła jakąś liczbę. N ie jest tu n a p isa n e jasno, że są jakieś szczególne w y m o g i.
Te - tak zwane zn a czn ik i tła, o które pyta Protazy - to punkty ogranicza jące początek i koniec obszaru tła sygnału. Każde dziecko (fizyk) wie, że początek i koniec chodzą parami. Dwa znaczniki (początek i koniec) określa ją jeden obszar tła. Krótko m ów iąc-tu programista Protazy założył, że Urszula (użytkownik) wie, iż oczekuje się od niej podania parzystej liczby znaczników. O m ó w iliśm y w ięc ju ż d w a arg u m e n ty w y w o ła n ia tej funkcji o n azw ie okno_dialogowe. A co z trzecim arg u m e n tem w y w o łan ia? Spójrz jeszcze raz w m iejsce O . Jest tam nazw a „ cz y_parzysta". Co to jest? Jest to n a z w a funkcji "zatw ierdzającej", którą to n a z w ę p ro g ram ista P ro tazy w ysyła d o funkcji okno_dialogowe. Z au w aż , ż e n a z w a czy_parzysta je st p o staw io n a tu b e z n aw ia sów . To d la te g o , ż e jedynie mówimy o fu nkcji czy_parzysta, a nic chcem y, b y w ła śn ie teraz starto w ała. C zy w o ln o P ro tazem u u ż y ć tej nazw y jako a rg u m e n tu w y w o łan ia funkcji
okno_dialogowe? W olno m u - p o d w a ru n k ie m , że deklaracja tej fu n k cji d o d ek laracji trzeciego a rg u m e n tu form alnego.
czy_parzysta pasuje
Jaki jest typ tego argumentu formalnego? - p o w ied zieliśm y sobie p o w yżej. Przypom nę: ' ws k_fun_za twierdz jest w sk a źn ik ie m m ogącym p o k a z y w a ć na funkcje w y w o ły w a n e z jed n y m a rg u m e n te m typu int, a z w raca jące jako re z u lta t w artość ty p u bool.
Natomiast jaka jest w programie deklaracja nazwy czy_parzysta? D eklarację, tę m am y w p u n k cie @ i m ożem y p rz e c z y ta ć ją jako:
czy parzysta jest nazw ą funkcji w y w o ły w a n ej z je d n y m a rg u m entem typu int, a zw racającą jako re z u lta t w arto ść ty p u booj.. N o i co, z g a d z a się? O czyw iście! Zatem tę n a z w ę funkcji „zatw ierd zającej" P ro tazy m o ż e w ysłać d o Tw ojej funkcji okno_dialogowe. O O to, co się w ów czas dzieje w ew n ątrz funkcji d ialogow ej. N ajp ierw zo b aczm y p ie rw szą linijkę - czyli...
354
Rozdz. 8. W skaźniki W skaźniki do funkcji
...jak odebrany zostaje trzeci argument. S k o ro d o funkcji w y sła n a została n a z w a (np. czy_parzysta) - to funkc;a n a sz a d efin iu je so b ie w sk a ź n ik d o funkcji i inicjalizuje g o (p rzy słan y m jak o trzeci a rg u m e n t) a d re s e m funkcji c z y _ p a r z y s t a. Czyli ten u k ry ty m ec an izm o d p o w ia d a jak b y takiej instrukcji bool
(*wsk_fun_zatwierdz)(int) = czy_parzysta;
A b y w y w o łać te ra z fu n k cje p o k a z y w a n ą p rzez te n w sk aźn ik w y sta rc z y n ap isać (*wsk_fun_zatwierdz) (w a r to ś ć ) ;
Jeszcze p ro stszy z a p is w y w o łan ia p o k azy w an ej w sk a źn ik ie m funkcji to. wsk fun zatwierdź (w a r to ś ć ) ;
Jeg o z d a n ie na ten te m a t (jprawda/fałsz) - p rz y p isu je m y d o o b ie k tu ty p u bool o o o
n a z w ie zatwierdzone. N a p e w n o z a u w a ż y łe ś, że w szy stk o d zieje się p ę tli do. . .while. Tu m a m jl k o ń czą ce o bieg w y ra ż e n ie w aru n k o w e. Z n aczy o n o - m ó w ią c w skrócie:
Rozdział. 8. W skaźniki W skaźniki do funkcji
355
G d y U rsz u la (u ży tk o w n ik ) - kiedyś w reszcie o d p o w ie p o p raw n ie, w te d y pętla się sk o ń c z y , o k ie n k o zo sta n ie zam k n ięte (czy w id z is z ten śliczny szlaczek ze z n a c z k ó w 'W ?). N a ko n iec Tw oja funkcja zw ró c i P ro tazem u jako rezu ltat w a rto ść odpowiedz_uzyt kownika. W arto ść ta w ęd ru je d o m iejsca sk ąd naszą funkcję (biblioteczną) okno dia lo gowe p ro g ra m ista P ro ta z y w yw ołał. (Jest to a k u r a t w funkcji m a in ). Tam p rz e p ro w a d z a n e są jego w łaściw e, dalsze ob liczen ia z u d ziałem o trzy m an ej od U rsz u li w artości. Zobaczyliśm y tu więc, ja k d o funkcji okno_dialogowe m ożna w ysłać adres innej funkcji. Aby to było możliwe, argum entem form alnym funkcji okno t i r n r............. — ------------------- ............................................................................................ ........ ' »
O 0 N ie m ó w iliśm y nic o sam ej f u n k q i "zatw ierdzającej", k tó rą tym ra z e m m iała być funkcja czy parzysta. N ie m ów iliśm y, bo je st to n ajzw yklejsza funkcja. Je d y n ą n iezw y k łą rzeczą b y ło tu to, że a d re s tej funkcji (czyli jej nazw ę: czy parzysta) ktoś k o m u ś w ysłał jako a rg u m e n t. N a to m ia s t sam a ta funkcja zatw ierd zająca o trz y m u je jakąś liczbę (jako swój a rg u m e n t) i m a się w y p o w ie d z ie ć czy ta liczba je st d o b ra (true) c zy zła (false). A k u ra t ta k o n k retn a fu n k cja zatw ierd zająca - s p ra w d z a czy p rz y sła n a liczba jest p a rz y s ta (w ted y fu n k cja zw raca w artość true), czy n ie p arzy sta (w ted y z w ra c a w arto ść false). To oczywiście nie jest przedmiotem tego paragrafu, ale sprawdzenie parzy stości czy nieparzystości liczby polega na użyciu operatora %(czyli „reszta z dzielenia", modulo). Jeśli reszta z dzielenia tej liczby przez 2 jest: •
ró żn a od z e ra - to liczba jest n ie p a rz y sta .
•
ró w n a z e ro - to liczba jest p a rz y sta .
Użycie zupełnie innej funkcji zatwierdzającej © O to in n e m iejsce w p ro g ra m ie P rotazego, g d z ie zn o w u w y w o łu je on naszą funkcję (biblioteczną) okno_di a logowe. Z n o w u jest p y ta n ie o jak ąś w arto ść, a w ty m p rz y p a d k u , jako trz e c i arg u m e n t w y słan a zostaje n azw a innej funkcji. Tym ra z e m P rotazy, n a fu n k cję zatw ierdzającą tę k o n k re tn ą o d p o w ie d ź , w y b ie ra funkcję o n azw ie czy_dozwolona_dla_detektora. © O to jej deklaracja. Jak w id ać, funkcja ta d o b rze s p e łn ia w ym ogi funkcyj, które m o żn a w y słać jako „ w a lid a to r" d o Twojej funkcji okno_dialogowe. C o to za w ym ogi?
Ano to, że funkcja ta ma być
356
Rozdz. 8. W skaźniki W skaźniki do funkcji w y w o ły w a n a z jed n y m a rg u m e n te m typu i n t , a w rezu ltacie sw ej j p ra c y z w racać rezu ltat ty p u bool (prawda/fałsz). P o ty m w y w o ła n iu p rzez P ro tazeg o Twojej funkcji okno_dialogowe - jej p ra c a jest tak a s a m a , jak o m aw ialiśm y p o p rzed n io , z tym , że te ra z w yw oła m c z w ro tn e u ru c h o m i in n ą funkcję. O czyw iście teraz będzie to fu n k cja o nazw K
czy dozwolona_dla_dotoktora.
^
>
O O O to definicja tej funkcji. Jak w idać, jest b a rd z o prosta. Funkcja ta (dostarczona p rz e z p ro g ra m istę P ro tazeg o ), s p ra w d z a czy p o d an a liczba je s t m niejsza o
Gdy nie
m a ż a d n e j fu n k cji z a tw ie r d z a ją c e j...
main w id z im y trzecie p o s łu ż e n ie się naszą fu n k cją b ib lio te c z n i okno_dialogowe. Z w ró ć u w ag ę, że teraz, w miejscu trze cieg o arg u m e n tu
Q W funkcji
a k tu a ln e g o jest liczba z e ro 0. C o to znaczy? I Z n ac zy to, ż e zam iast a d re su funkcji z a tw ierd zającej - P ro tazy I w y sy ła d o funkcji okno_dialogowe ad res z e ro w y (tzw. NULL). C zy m o żn a tak? I K o m p ilato r nie zap ro testu je, bo z e ro m o żn a p rz y p is a ć d o k ażd eg o I (niestałego) w skaźnika. A co na to .funkcja okno_dialogowe? O czyw iście m usi się liczyć z tak ą ew en tu aln o ścią. W końcu - g d y k ied y ś b ęd zie chciała sięg n ąć p o d w sk a zan y a d r e s i - jeśli tym a d re se m będzie ad res z e ro w y - ż a d n e w y w o ła n ie n ie n astąpi. F unkcja
okno_dialogowe została p rz e z
nas p rz y g o to w a n a i n a taką sytuacj
U zn aje on^, iż: ...jeśli ktoś (Protazy) p rz y sy ła a d re s zero w y - ja k o a d re s funkcji "zatw ierdzającej" - to z n ac zy , że nie w y m ag a o n ż a d n e g o za tw ie r d zan ia. Po prostu gotowy jest wziąć teraz każdą liczbę wpisaną przez użytkownik ^
Urszulę. S pójrz zn o w u w miejsce 0 . Jest ta m in stru k cja i f s p ra w d z a ją c a ad res tu n * "zatw ierdzającej". Jeśli jest on p rz y p a d k ie m zero w y , to w y k o n a się instrukcja i części else. T am , jak w idać, w p isu jem y d o zm ien n ej zatwierdzone wartość true, d zięk i czem u pętla do. . .while od razu zo stan ie z a k o ń c z o n a - a ta
Rozdział. 8. W skaźniki W skaźniki do funkcji
357
p o d a n a p rz e z u ż y tk o w n ik a U rszu lę b y le-jak a liczba, zo stan ie in stru k cją r e t u r n p rz e k a z a n a p ro g ra m iśc ie P ro tazem u .
W To w sz y stk o . Z o b ac zy liśm y w ięc trzy k ro tn e uży cie naszej funkcji bibliotecznej. W k a ż d y m p rz y p a d k u p rz y sy ła n o d o niej in n y a d res funkcji z a tw ie rd z a ją c e j.
Teraz już coś, dla zupełnych perfekcjonistów N a w ia se m m ó w iąc b y ło b y bardziej elegancko, g d y b y m w ła śn ie ten adres z e ro w y u czy n ił w d ek laracji funkcji o k n o _ d i a l o g o w e - w arto ścią d o m n ie m a n ą ^ trzecieg o a rg u m e n tu (czyli tego w sk a źn ik a funkcji). int okno dialogowe(
const char * opis, const char * pyt, bool (*wsk fun zatwierdź)(int]
G d y b y m tak rzeczy w iście uczynił, to o statn ie w y w o łan ie funkcji o k n o _ d ia lo g o w e m ogło b y w y g lą d a ć p o prostu tak: odp = okno_dialogowe( "Dialog bez konieczności sprawdzania", "Podaj jakakolwiek liczbę: "); / / \)
D laczeg o tak jest lepiej? D lateg o , że p o słu g u jący się n aszą funkcją biblioteczną ♦> p ro g ram iśc i, k tó rz y nie staw iają żad n y c h o g ran iczeń , co do sp o d zie w an ej w arto ści o d p o w ie d z i - w yw ołują n aszą funkcję z tylko d w o m a a rg u m e n ta m i - i już. p ro g ram iści, k tó rz y staw iają p ew n e w a ru n k i - w y w ołują n aszą funkcję z trze m a a rg u m e n ta m i. Trzecim a rg u m e n te m jest n azw a funkcji za tw ierdzającej. O czyw iście d o m n iem an a w arto ść tego a rg u m e n tu nie m usi być zerem . M o że na p rz y k ła d b y ć n a z w ą jakiejś funkcji zatw ierdzającej. Jeśli na p rz y k ła d chciałbyś, by d o m n ie m a n y m s p ra w d z e n ie m było s p ra w d z e n ie czy to liczba p a rzy sta , to d e k la ra q ę funkcji o k n o _ d ia lo g o w e napisz tak: int okno_dialogowe( const char * opis, const char * pyt, bool (*wsk fun zatwierdź) (int) = czy_parzysta) ;
O czyw iście, ab y to było m ożliw e, k o m pilator p o w in ien już zn ać deklarację n azw y fu n k cji c z y _ p a r z y s t a . N ie m a p ro b lem u , w tym celu p o prostu deklarację funkcji c z y p a r z y s t a (© ) przen ieś pow yżej d ek laraq 'i funkq'i o k n o d i a l o g o w e (O ).
____
Przypominam, że wartość domniemaną argumentu umieszcza się w deklaracji funkcji (w definicji mogłoby być za późno).
358
Rozdz. 8. W skaźniki W skaźniki do funkcji
W Podsumujmy
.
W p arag rafie tym , zobaczyliśm y jak w y sy ła się do funkcji adres innej funkcji W szystko o d b y ło się n a tle z życia w zięteg o problem u tzw . w yw ołania zw ro t neg o M ożliw e jed n ak , że dla p o czątkującego - ten p ro b lem był nieco oszała miający. Jeśli tak jest w T w o im p rzy p ad k u , p am iętaj, że programrĄ obowiązkowym tego p arag rafu są je d y n ie trzy instrukcje: •
O - deklaracja funkcji o k n o d i a l o g o w e , a konkretnie je)
•
trzeci argum ent; O - w y w ołanie tej funkcji połączone z w ysłaniem do niej n a z w y funkcji "zatw ierdzającej";
•
8.19.3
© - u ży cie tej funkcji "zatw ierdzającej" w ciele funkcji o k n o j d ia lo g o w e .
Tablica w sk aźników do funkcji W iem y już, że w tablicach m ożna p rzechow yw ać w sk aźn ik i (czyli adresy) d o jakichś obiektów . M ożna też sp o rząd zić tablicę składającą się ze w skaźników d o funkcji. O to p rzy k ład tablicy w sk aźn ik ó w d o funkcji:
)()i
v o id ( * ( t w f [5 ])
Przeczytajm y tę definicję, jak zw y k le zaczynając od śro d k a, czyli od nazw y tw f
-
tw f
[5]
_
...jest 5 elem entow ą tablicą
* ()
_
...w skaźników ... ...do funkcji w yw oływ anej bez żad n y c h argum entów ...
v o id ...a zw racającą ty p v o i d (czyli nic). Jeśli p am iętam y , że operator [ ] jest o wiele m ocniejszy o d operatora *, to sam ą definicję m o żem y napisać po p ro stu tak: v o id (* tw f [5 ])
O;
Z astanów m y się teraz, co nap raw d ę zdefiniow aliśm y i k ie d y m oże się nam t przydać. O tóż m am y tablicę, w której m ożem y p rzech o w ać w skaźniki po k azu Ł e na jakieś w y b ran e funkcje naszego program u. Jest to jak b y lista czynność które m ożna w ykonyw ać. M ożem y załadow ać taką tablicę, a potem kom ende rować: a teraz p ro szę w ykonać funkcję trzecią, a teraz p iątą. Żeby było jaśniej pokażm y to na przykładzie. W p ro g ram ie ty m występują trzy funkcje, na które pokazujem y w skaźnikam i z tablicy. # in c lu d e < io stre a m > u s in g nam espace s t d ;
Rozdział. 8. W skaźniki W skaźniki do funkcji tinclude tinclude
359
// dla: e x i t //dla: s i n u s
v o i d k u r s (); v o i d w i a t r a c z e k () ; void w a h a d l o O ; ^ ^ ^ ************, //************************************* v o i d z w l o k a _ c z a s o w a ( i n t ile) *
f o r (i n t
i =
0 ; i < ile * 10 0 0
; i++);
Z*****************************************’ i nt
main()
void
( * t w f [3] )( )
=
{ wahadło, wiatraczek, k u r s };
//
o
//
©
//
©
i n t co; w h i l e (1 ) {
« " ------------------- M e n u — 7 --------« " \ t 0 - w a h a d l o \ n \ t 1 - w i a t r a c z e k \n\t « "2 - kurs\n\t9 - koniec programu\n\n << "Podaj n u m e r zadanej akcji: > > co;
cout
cin
switch(co)
i f r■
case case case
0: 1:
2:
ijk
.
(* t w f [ c o ] ) (); // t w f [ c o ] (); break;
y
~ .r ,
// < —
3
V'
to samo prościej
// koniec
c a s e 9: e x i t (1 ); default: break;
/****************************************** v o i d k u r s () for(int
i =
0 ; i < 1 0 0 ; i++)
{ cout
<< "kurs " < < (232 + (i % 4)) << " . . . \ r " ; z w l o k a _ c z a s o w a ( 50 0 0 0) ;
cout
«"\nPokazywalem
kurs. . . \n";
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ; * ******** ******* v o i d w i a t r a c z e k () char
s y m b o l []
=
{ * I '»
'/'>
'~ ' /
W
^•
360
Rozdz. 8. W skaźniki W skaźniki do funkcji f o r ( i n t i = 0 ; i < 100 ; i++) { cout « " " « symbol [ i % 4] « z w lo k a _ c z a s o w a ( 1 0 0 0 0 ) ; }
" \r" ;
cout <<"\nWiatraczek sie pokręcił...\n";
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *_ // G v o id w a h a d ło () { c o u t << ' \ n ' ; c h a r w z o re k [2 5 ]; f o r ( i n t i = 0 ; i < 500 ; i++) {
, .. // przygotowanie tablicy spacji f o r ( i n t k = 0 ; k < 25 ; k++) w zo rek [k ] = ' w z o re k [2 4 ] = 0; // koń czacy C-string znak nuli (ma on kod A SCII -> 0)
// wstawienie w jeden z elementów znaczka '* i n t p o z y c ja = 12 * s i n ( i /2 0 .0 ) + 12; w z o r e k [ p o z y c ja ] - ’ * ' ; // wydruk na ekran, a potem powrót karetki do początku linijki c o u t << w zorek « ' \ r ' ; z w lo k a _ c z a s o w a (5 0 0 0 ); } cout « " \n " ; )
Wygląd ekranu po wykonaniu tego programu zależy oczywiś< cie od wyboru „opcji" naszego menu. 0 1 2 9
-
wahadło wiatraczek kurs koniec programu
Podaj numer zadanej akcji: 2 kurs 2 3 5 ... <- tutaj aktualizowany jest kurs Pokazywałem kurs... ----------------Menu -----------------0 - wahadło 1 - wiatraczek 2 - kurs 9 - koniec programu Podaj numer zadanej akcji: 1
<- kręci się wiatraczek
\
Wiatraczek sie pokręcił... 0 1 2 9
-
wahadło wiatraczek kurs koniec programu
Podaj numer zadanej akcji: 0
Rozdział. 8. W skaźniki W skaźniki do funkcji
361
2 - kurs 9 - koniec programu Podaj numer zadanej akcji:
9
O Definicja trzy elem entow ej tablicy w skaźników d o funkcji (funkcji w y w o ły w a nych bez żad n y ch a rg u m e n tó w i zw racających v o i d ) . O d ra z u inicjalizujem y tę tablice tak, że w skaźniki pokazują na n asze funkqe. Z au w aż , że w k lam rze są ty lk o nazw y funkcji - bez naw iasów . P am iętam y , ze n azw a funkcji jest ad resem jej początku. ... © M enu w y p isy w an e n a ek ran ie daje m ożliw ość w y b ran ia żądanej akcji. W tablicy czekają już w sk aźn ik i p o kazujące na różne funkcje. Tutaj tylko p ro sim y o p o d a n ie nu m eru funkcji, k tó rą m am y uruchom ić. © Skoro w ybraliśm y n u m e r funkcji, to pozostaje tylko ją uruchom ić. D zieje się to w łaśn ie tą instrukcją. Z n o w u z a z n a c z a m , że m o m en t użycia w skaźnika p rzy p o m ina sk ład n ię jego definicji (porów naj z © ). Inaczej m o żn a p o sh iży ć się ta k ą (może łatw iejszą?) składnią: tw f[co]O ;
Funkcje, które wywołujemy, nie mają specjalnego znaczenia T ym razem zrobiłem je tak, by raczej zabaw iały, niż w ykonyw ały coś pożytecz nego. N ie m usisz w ięc czytać poniższego opisu, choć m oże kiedyś p rz y d a d z ą U się zasto so w an e tu efek to w n e sztuczki. O Ta funkcja o nazw ie k u r s - słu ży do w yśw ietlania bieżącego kursu lo tu . Inutuje ona w sk azan ia lekko zm ieniającego się kursu. C iekaw sze jest to, ż e w y d ru i następ u ją cały czas w tym sam y m miejscu ek ran u , stąd odnosi się w rażen ie, ze m am y d o czynienia z jed n y m w yśw ietlaczem , k tó ry ciągle zm ienia p o k a z y w a ną w artość. Sztuczka p olega na posłużeniu się znakiem \ r (tzw . powro karetki). D zięki tem u , po w y p isan iu , na p rzy k ład , stringu k u r s 240 nie p rzech o d zim y d o następnej linii ekranu, ale w racam y na p o czątek tej, w której w łaśn ie d o k onaliśm y tego w ypisu. W rezultacie następny w y p is odbę dzie się w tym sam y m miejscu (zacierając p o p rzed n ią w artość). Da to w rażen ie anim acji. Prędkość anim acji reguluje funkcja o n azw ie z w lo k a _ c z a s o w a . © O to in n a funkcja zabaw iająca. Funkcja w i a t r a c z e k zabaw ia u ży tk o w n ik a rysując kręcący się w iatraczek . Jest on robiony p rz e z kolejne ry so w an ie w tym sam y m m iejscu na ek ran ie znaków
362
Rozdz. 8. W skaźniki W skaźniki do funkcji I
\
-
/
Te zn ak i są u m ie sz c z o n e w tablicy zn ak ó w . Z au w aż jak i ch w y t m u siał zostać z asto so w an y , b y m o żn a było u m ieścić tam znak: \ (czyli: o d w ro tn y ukośnik). Kolejne w y p is y w a n ie na ek ran ty c h czterech zn ak ó w je st zro b io n e z a pom ocą operacji "reszta z dzielenia" p rzez 4 (modulo 4). Z m ienna i cały czas rośnie, a m im o to in d ek s ta b licy jest zaw sze z p rz e d z ia łu 0 - 3 . © Z abaw iająca fu n k cja w a h a d ło ry su je na ekranie g o n ią cy ta m i z p o w ro tem w ram ach jed n ej linii sym bol ' * '. R u ch jest n iejed n o stajn y , p rzy p o m in a raczej ruch w a h a d ła : je st najszybszy p r z y przejściu przez ś ro d e k (k o lu m n a 12-ta), a zw aln ia na b rz e g a c h p rz y n a jd a lsz y m w ychyleniu - (k o lu m n a 0, ko lu m n a 24). O czyw iście ta cała w y sz u k an a „m ech an ik a" - zro b io n a jest tu za pom ocą zw ykłej fu n k cji bibliotecznej sin u s.
Jak to jest zrobione? W funkcji w a h a d ł o jest lokalna tab lica o nazw ie w z o r e k . W y p ełn ian a jest o n a 24 spacjam i, a n a końcu w staw ian y je st zn ak końca C -strin g u nuli (znak o kodzie ASCII ró w n y m zero) N astę p n ie w o d p o w ie d n ie m iejsce tej tablicy w sta w ia n y jest znaczek ' * ' a całość w y p is y w a n a jest na ekranie. P rędk o ść an im acji z w ło k a c z a s o w a .
zn o w u
o k reśla
a rg u m e n t
w y w o łan ia
funkcji
Jak oblicza się odpowiednią pozycję symbolu ' * ' ? (N a p ra w d ę n ie m u sisz tego rozum ieć!) i n t p o z y c j a = 12 * s i n ( i / 2 0 . 0 )
+ 12;
Jak w ia d o m o , funkcja sin u s jest fu n k cją okresow ą, w ięc m o ż n a obliczyć sin u s dow olnej w a rto śc i kąta, a i tak w a rto ść sinusa b ę d z ie liczbą rzeczyw istą z zakresu [-1 , +1]. P o n iew aż m y w o lelib y śm y zak res [0, +24] w ięc p o p ro s tu : ♦$» m n o ż y m y ten sin u s p rzez czy n n ik 12 (co d aje z a k re s [-12, +12]), ♦♦♦ a jeśli jeszcze d o d atk o w o d o rezu ltatu d o d a m y 12, to w sum ie o trzy m a m y w a rto ść z zak resu [0, +24]. t j Tak o trz y m u je m y pozycję biegającej g w iazd k i w d a n e j fa z ie ruchu. A nim acja polega n a k o lejn y m w y k o n y w an iu tego sam ego o b liczen ia d la w zrastającego licznika i . Jeg o w arto ść dzielo n a je st najp ierw p rzez 20.0, a b y ruch był bard ziej płynny . To dlatego, że argumentem funkcji sinus jest kąt wyrażony w rad urnach. Skoro360 stopni- to 6.28 (2n) radianów,zatem skoki: 1 radian,2 radiany,3 radiany, 4... (czyli 57 stopni, 114 stopni,...) byłyby zbyt duże . Ruch tak obliczany nie byłby płynny.
W
Rozdział. 8. W skaźniki A rgum enty z linii wywołania program u
363
To menu może być dynamiczne (zmieniane w zależności od sytuacji) Jak w idać, dzięki tablicy w sk aźn ik ó w m ożem y w y d ać polecenia: jeśli tak - to w ykonaj funkcję nr... Jest to jeden ze sposobów sp o rząd zan ia m enu. O czyw iście są tak że in n e sposoby sp o rząd zan ia m enu: switch(co_robic)
{ case 0: wahadło(); break; case 1: wiatraczek)); break; case 2: kurs)); break; default: break;
C zasem takie operacje są przydatne. Jeśli na p rzy k ład m am y w program ie funkcję v o id s y m f o n i a ) ) ; a nastąp i g d zieś w p ro g ram ie instrukcja twf[0] = symfonia;
to o d tej pory p o w y b ran iu w ariantu 0 zam iast funkcji w a h a d ło będ zie się w ykonyw ała funkcja s y m f o n ia . Ta opcja m enu zm ieniła w ięc sw oje działanie. Jest to w ięc sposób na m e n u , którego treść zależy od kontekstu.
1? B ardzo teraz zachęcam b y ś spróbow ał ro zw iązać zadanie, k tóre przy g o to w a łem na końcu ro zd ziału . (Ćwicz. 12 - LXIII , na str. 372). To zad an ie, na p rzy k ład zie p ra c y ro b o ta, pokazuje jak w a ż n y jest fakt dynam icznego tw orzenia listy funkcji d o w ykonania.
J.20
A rg u m e n ty z linii w y w o ła n ia p ro g ram u Spotkałeś na p e w n o p ro g ram y , które u ru ch am ia się pisząc obok n azw y p ro g ra mu d o d atk o w o jakieś opcje. Jest to eleganckie ro zw iązan ie pro b lem u przesłania p aram etró w d o p ro g ram u . R ozw iązanie takie p o zw ala też pisać program y, które w y w ołuje się tak, ja k b y były k o m en d am i system u operacyjnego. W yobraź sobie, ż e nap isałeś ładny program , k tó ry - w chwili, g d y się zaczyna m aluje na ek ran ie piękną kolorow ą planszę ty tułow ą. Pracując nad m odyfikacjam i takiego pro g ram u - w ielokrotnie m usisz go u ru cham iać. O czyw iście za każdym razem najp ierw plansza ty tu ło w a. Jeśli
364
Rozdz. 8. W skaźniki A rgum enty z linii wywołania program u u ru c h a m ia s z p ro g ra m d z ie sią tk i razy , to w p ew n y m m o m en cie ta p lan sza C ię z d e n e rw u je . C hciałbyś, by nie p o jaw iała się w ted y , g d y teg o nie chcesz. O czy w iście jest ro zw iązan ie: p ro g ra m startuje i p y ta „czy mam pokazać planszę? ‘ .; N a o d p o w ie d ź „nie" p rz e sk a k u je w y w o łan ie funkcji zajm ującej się ry so w a n iem p lan szy . Jest to ro zw iązan ie, k tó re nie jest ża d n y m ro z w ią z a n ie m . U ru ch am ia jąc te ra z k ilk a d z ie sią t razy p ro g ra m m usisz k ilk ad zie siąt ra z y o d p o w ia d a ć na p y ta n ie „C zy mam...?" Ś w ięty b y nie w ytrzym ał! P o trzeb n a je st m ożliw ość, ab y ju ż w m om encie u ru c h a m ia n ia p ro g ram u , m ó c p rzesłać d o p ro g ra m u info rm ację o tym , że nie ż y cz y m y so b ie planszy.
Jak zatem przesłać do programu parametry ? S p raw a je st b a rd z o p ro sta. P rz y k ła d o w o jeśli n asz p ro g ra m n azy w a się „ g e n e rato r", to w y w o łu jem y go p isz ą c jego nazw ę, a dalej kolejno p a ram etry generator parami 77.2
w n a s z y m p rz y p a d k u są to: te k st " p a ra m i" , oraz liczba 7 7 . 2 W ysłać p a ra m e try to jeszcze nie w szystko. P ro g ram p o w in ie n u m ieć je o d eb rać To też n ie jest tru d n e. A b y o d e b ra ć tak w y słan e p a ra m e try , funkcję m a i n m u sim y z a p is a ć w tak i sposób: int main(int argc, char *argv[]) //
... n o r m a ln a tre ść f u n k c j i main
} Jak w id z im y , p rzesłan ie p a ra m e tró w do p ro g ram u p o le g a n a tym , że fu n k cja m a i n d o s ta je w p rezencie o d sy stem u operacyjnego d w a a rg u m e n ty . P ierw szy ty p u i n t , a d ru g i nieco b ard ziej sk o m plikow any... To, ja k ie im n a d a m y n a z w y w n a s z y m p ro g ram ie (a rg u m e n ty form alne), z a le ż y w y łącz n ie o d n as. Z w y c z a jo w o te a rg u m e n ty fo rm aln e n azyw ają się: a r g c - od ang. argument counter- licznik a rg u m e n tó w . M ów i n am ile p a ra m e tró w sy stem o p e ra c y jn y w y słał d o p ro g ra m u . L icznik ten m a co najm niej w a rto ś ć 1. (C zy chcem y czy nie - system w y sy ła n am jako p a ra m e tr n a z w ę p ro g ra m u , k tó ry w łaśn ie u ru ch am iam y ). a r g v - od ang. argument uector - tablica a rg u m e n tó w . Jest to w sk a ź n ik d o tablicy, w której p o sz c z e g ó ln y m i elem en tam i są ad resy C -strin g ó w . Te C -stringi, to w ła ś n ie n asze k o lejn e p ara m e try w y w o ła n ia p ro g ram u . B ardziej fo rm a ln ie - zap is char *argv[] czyta się: a r g v je st tablicą w sk a ź n ik ó w d o (ciąg ó w ) zn ak ó w . Z a m ia st d łu g ic h tłu m aczeń p ro p o n u ję spojrzeć na ry s u n e k poniżej.
Rozdział. 8. W skaźniki A rgum enty z linii wywołania program u
365
366
Rozdz. 8. W skaźniki Ćwiczenia Parametr
nr
2
to
C-string:
77.2
x = 8 1 .2
Pamiętajmy jednak, że: ■
; ...w szystkie parametry zostają przysłane jako C-stringi.
i—■Innui iiwrwif'nwwwwwiim Z atem p aram etr * a r g v [ 2 ] - czyli ("77.2") przy słan y zostaje nie jako liczba, ale jako ciąg takich znaków : cyfra 7, cyfra 7, kropka, cyfra 2, nuli. Jeśli chcem y zam ienić taki C-string na liczbę - m ożem y posłużyć się jedną z funkq'i bibliotecznych. N azy w a się ona a t o f - od ang: Ascii TO Float. D eklaracja tej funkcji jest w pliku nagłów kow ym cstdlib. W idzim y, że w program ie zam ieniliśm y ten p aram etr na liczbę i złożyliśm y w zm ien n ej x, na której m ożem y ju ż przeprow adzać operacje m atem atyczne. W ro z d z ia le o operacjach w ejścia/w yjścia po zn am y o wiele w ygodniejsze sp o so b y p rz e k s z ta łc a n ia p rz y s ła n y c h a rg u m e n tó w p ro g ram u na w a rto ś c i liczbow e. (Zob. § 22.24.2, str. 1167).
8.21
Ć w ic zen ia Wskaźnik o nazwie ws k został ustawiony tak, że pokazuje na obiekt o nazwie ob j . Jaką zatem informację zapisano niniejszym do wskaźnika wsk? Zdefiniuj dwa obiekty: obiekt typu c h a r oraz pokazujący na niego wskaźnik. Za pomocą wskaźnika przypisz to tego obiektu wartość ' x ' Oto linijka definicji: u n s ig n e d lo n g * a , b ; Jakie dw a obiekty są tutaj definiowane? (Wypowiedz ich pełne deklaracje). Które z poniższych obiektów mogą być pokazywane wskaźnikami
V
a) zwykły obiekt b) tablica obiektów c) wskaźnik d) referencja e) nazwa f) funkcja g) komórka pamięci komputera h) bit w komórce pamięci Czy wskaźnik do pokazywania na obiekty typu typu d o u b le ? Kiedy mówimy, że wyrażenie jest l-wartościąl
char
nadaje się do pokazania na obiekt
Czy l-wartość jest równocześnie r-wartością - czy odwrotnie?
Rozdział. 8. W skaźniki Ćwiczenia VIII
367
Mamy wskaźnik ws k pokazujący na jakiś konkretny obiekt. Które z poniższych wyrażeń są l-wartościami wsk
*wsk &wsk
Zdefiniuj następujące obiekty obj - obiekt typu char wchl - wskaźnik do obiektów typu char wch2 - wskaźnik do obiektów typu char wd - wskaźnik do obiektów typu double Ustaw wchl tak, by pokazał na obj. Następnie ustaw wch2 tak, by pokazał na to samo, na co pokazuje wchl. Co należy zrobić, by możliwe było ustawienie wskaźnika wd tak, by pokazał na to samo miejsce w pamięci, na które teraz pokazuje wch2. Podaj dwa sposoby (stary i nowy ).
X III
Masz wskaźnik do obiektów typu in t. Napisz instrukcję ustawiającą go tak, by pokazy wał na komórkę pamięci o adresie 0xa2ff 4. . .... Napisz także najprostszą instrukcję ustawiającą go na adres 0x0000. Czym się rozmą te instrukcje? Napisz instrukcję wypisującą na ekranie adres, na który ustawiony jest bieżąco wskaźnik. Mamy dwa wskaźniki: double* oraz void*. Ustawione są one tak że pokazują na ten sam obiekt typu double. Wyjaśnij w czym jeden z tych wskaźników jest lepszy od drugiego. Mamy obiekt typu char, o nazwie cc, i wskaźnik void*, o nazwie wy. Napisz definicję tego wskaźnika wv połączoną z inicjahzacją go tak, by pokazywał na ten obiek cc. Zdefiniuj wskaźnik do typu char o nazwie wc . Ustaw go tak, by pokazywał na to samo, na co pokazuje wskaźnik wv. Mamy tablicę znaków zawierającą litery alfabetu ABCDEFGHIJKLMNOPQRSTUWVZ. Zdefiniuj wskaźnik i ustaw go na drugim elemencie tablicy. Począwszy od tego miejsca - za pomocą pętli i przesuwania tego wskaźnika - wypisuj na ekranie co czwartą pokazywaną literę, dopóki wypisywany znak me będzie miał kodu ASCII większego lub równego kodowi litery Z. Mamy tablicę znaków oraz wskaźnik pokazujący na jakiś jej element. Jak - jedną instrukcją - sprawić, by wskaźnik ten pokazał o 6 elementów dalej . Czy możliwe jes przesunięcie wskaźnika o 100 elementów dalej, mimo ze a lca jes ty o elementowa? Jeśli mamy wskaźnik pokazujący na piąty element tablicy znaków, a przesuniemy go tak, by przesunął się o 30 miejsc w stronę początku - jak na to zareaguje kompilator. Kie y, jeśli w ogóle, objawi się taki błąd? Jak wiadomo, obiekty różnych typów mogą w pamięci zajmować rożną ilość komorek (słów czy bajtów). Mamy dwie tablice char t C [10]; long t L [10];
Napisz definicje odpowiednich wskaźników i ustaw je tak, by pokazy wały na początki tych tablic. Napisz instrukcje przesuwające oba te wskaźniki o 4 elementy tablicy dalej.
368
Rozdz. 8. W skaźniki Ćwiczenia
Mamy tablicę o nazwie
k a l i b r a c j a . W programie potrzebujemy jej adresu. Jak go
Czym się różni nazwa tablicy od wskaźnika ustawionego na jej zerowy element? Mamy tablicę i dwa wskaźniki double t a b [ 5 0 ] ; double *wskA = & t a b [ 1 0 ] ; double *wskB = & t a b [ 4 0 ] ; kB). Podaj wartość wyrażenia (wskB - wskA). Podaj wartość wyrażenia (wskft Załóżmy, że mamy wskaźniki pokazujące na tę samą tablicę. Które z poniższych operacji
na tych wskaźnikach mają sens? a) dodaw anie dwóch wskaźników b) dodaw anie liczby całkowitej do wskaźnika c) odejmowanie dwóch wskaźników d) odejmowanie liczby całkowitej od wskaźnika e) mnożenie dwóch wskaźników f) mnożenie wskaźnika przez liczbę całkowitą
h) dzielenie dwóch wskaźników i) dzielenie wskaźnika przez liczbę całkowitą M am ydw aw skażnikipokazuj, cena te sam , tablice. Wyjaśnij, jaką informacje otrzymu jemy wykonując na nich operacje następującymi operatorami. <= Mamy dw a wskaźniki pokazujące na zupełnie różne obiekty Czego dowiadujemy sie wykonując na tych wskaźnikach operacji porównania (np. <). Wskaźniki w trakcie swej definicji nie zostały przez nas zainicjalizowane. Na co pokazują ^ a f wskaźnik będący lokalnym obiektem automatycznym, b) wskaźnik będący lokalnym obiektem statycznym, c) wskaźnik będący obiektem globalnym, d) wskaźnik z przestrzeni nazw (namespace). Jaki cel ma ustawianie wskaźnika na adres zero? Jak najkrócej sprawdzić instrukcja i £ czy wskaźnik jest ustawiony na adres zerowy? XXVII XXVIII
jak najkrócej sprawdzić instrukcją i f czy wskaźnik jest ustawiony na adres inny niż Załóżmy, że mamy wskaźnik o nazwie w3 k.W starszych programach możesz spotkać się z zapisem if ( w s k
!= NULL)...
Co on oznacza? Jak to samo zapisać bez użycia (makrodefimcji) NULL? M f.tniecie o nazwie ze ro w a n ie , która odbierać będzie argument będący wskazni, . P . • J i Ho ta]e pokazywanego obiektu wpisze zero. Następnie napisz pros y
nr. 0,3,4, 7
Rozdział. 8. W skaźniki Ćwiczenia
XXXI
369
Napisz prosty program mający funkcję, do której można będzie wysłać tablicę typu in t. Funkcja ta ma do każdego elementu tej tablicy dodać liczbę 44. W funkcji m ain tego programu zdefiniuj lokalną tablicę, a następnie wobec tej tablicy zastosuj tę funkcję codającą - raz wobec wszystkich elementów tej tablicy, drugi raz wobec pierwszych 5 elementów. W programie mamy tablicę stałych typu d o u b le . Napisz funkcję, która pokaże na ekranie zawartość tej tablicy. Tworzymy operatorem new obiekt (typu wbudowanego) i nie inicjalizujemy go żadną wartością. Co zawiera wstępnie taki obiekt? Czy pod tym względem przypomina bardziej obiekt globalny czy automatyczny? Napisz program, w którym w funkcji main zostaną stworzone (operatorem new) trzy tablice typu d o u b le o rozmiarach co najmniej 20 elementów. Elementy pierwszych dwóch tablic załaduj jakimiś wartościami W programie powinna być funkcja, która przyjmie wysłane do niej te trzy tablice. Funkcja ta ma na elementach tych tablic wykonać operację mnożenia - taką, że i-ty elementu tablicy pierwszej zostanie pomnożony przez i-ty element tablicy drugiej. Rezultat ma zostać wpisany do i-tego elementu tablicy trzeciej. W funkcji m ain wypisz na ekranie zawartość poszczególnych elementów tablicy pierw szej, drugiej i trzeciej, a następnie skasuj te tablice.
.
Zmodyfikuj powyższy program tak, by teraz tworzeniem każdej tablicy zajęła się funkcja o nazwie k r e a t o r t a b l i c y . Jedno wywołanie tej funkcji ma tworzyć jedną tablicę, a argumentem aktualnym (wywołania) tej funkcji powinien być żądany rozmiar danej tablicy. Wyjaśnij jaka jest różnica między poniższymi instrukcjami int *wskl = new int[10]; int *wsk2 = new int(10);
M B !
W poprzednim ćwiczeniu zobaczyliśmy dwie instrukcje, w których występuje słowo kluczowe new. Czy mamy tu do czynienia z dwoma wersjami składni tego samego operatora, czy też są to dwa odrębne operatory? Napisz instrukcje kasu ące obiekty zdefiniowane w taki sposób. Czy użyć musisz tego samego operatora, czy dwóch odrębnych? U w a g a d la w ta je m n ic zo n y c h : P y ta n ia te na razie w y d a ją się ty lk o ciekaw ostką, n abierają jednak zn a c ze n ia , g d y z a c z n ie m y ro zm a w ia ć o tzw . p rze ła d o w a n iu o p era to ró w
jMTCTffl
Ćwiczenie dla tych, którzy przeczytali paragraf o umiejscawiającym operatorze new. Napisz prosty program, który zarezerwuje (8*8*sizeof(long)) bajtów pamięci Jeśli twoim komputerze typ lo n g ma rozmiar 4 bajty, to będzie to tablica 8*8*4 = 256 elementów typu c n a r. Adres tak stworzonej tablicy przypisz do wskaźnika o nazwie szach o w n ica. Jak zapewne wiesz, szachiści oznaczają pola szachownicy symbolami od Al do H8. Dla wygody przyjmijmy, że indeks będący cyfrą zmienia się tu częściej niż indeks będący literą. Zwróć też uwagę, że numeracja cyfrowa zaczyna się w tym szachowym systemie od 1 (a nie od 0) Na tak zdobytym obszarze, jak na polach szachownicy, tworzone będą obiekty typu lo n g (mają one zwykle 4 bajty). Odbywać się to ma w taki sposób, że program pyta użytkownika: "Na ja k im p o lu sza c h o w n ic y m am u m ie śc ić n o w y obiekt ty p u l o n g ? Podaj s y m b o l lit e r o w y : ". Użytkownik odpowiada np.: F ."P o d a j sy m b o l c y fro w y : ". Użytkownik odpowiada np.: 4.
370
Rozdz. 8. W skaźniki Ćwiczenia
Program tworzy obiekt umiejscawiając go na odjx>wie^i^^zyqi^«chownicy.Wtym przypadku będzie to akurat miejsce od adresie (s z a c h o w n ic a [172], bo«5 8H 3) 4 - ]72. W rezultacie stworzony zostanie obiekt, którego adres zapamiętaj pierwszym wolnym elemencteprzygotowanej w tym celu tablicy wskaźników o nazwie f ig u r y . (Jesl, iszczę rbe czytałeś o tablicach wskaźników, zrób to za pomocą zwykłych ( wolnostoS ' ) wskaźników, a ilość Hgur dla uproszczeni, ogran.cz do trzech). Do obiektu tego wpiszwartość, która będzie przypominała jego adres (w tym przypadku
iS E ^ s ^
XXXVIII
XXXIX
obiektów. Zlikwiduj obiekty i zakończ program. Jaka jest nazwa obiektu stworzonego poniższą instrukcja? d o u b le *wsk = new d o u b le ; Wymień trzy sposoby, w jakie operator new może powiadomić nas o niemożliwości stworzenia nowego obiektu na skutek wyczerpania zapasu dostępnej p a rn io . Który z nich jest zalecany? Jaka jest różnica między wskaźnikiem stałym, a wskaźnikiem do ob.ektu stałego? Czy może istnieć wskaźnik stały do obiektu stałego? Kiedy można wpisywać adres do wskaźnika stałego? Mamy definicje następujących obiektów: i n t * c o n s t wa = new i n t ; *wb = new i n t ( 8 ) ; i n t m = 4;
const i n t
Które z poniższych instrukcji kompilator odrzuci i dlaczego?
a) b) c) d) e) f) $) h)
wa » 10; wb = 10; *wa = 10; *wb = 10; wa = &m; wb = &m; d e l e t e wa; d e l e t e wb;
ryzykowne, mimo że kompila IMUlCiyLll --- - -- J tor nie zaprotestuje? Poniżej widzimy dwa fragmenty kodu. Oba nie wywołają błędu kompilacji. Czy są, mimo to, poprawne? Wyjaśnij, co powodują oba fragmenty. a)
int *wsk; *wsk = 0 ;
b) i n t *wsk = 0; Mamy zapis: const char *wsk = "Napis";
Rozdział. 8. W skaźniki Ćwiczenia
371
Co tu jest definiowane? XLVI
Mamy następujące obiekty int obiekt = 100; int *intw; const int *Cintw; int * const intCw = &obiekt; const int * const CintCw = sobiekt;
Jak widzisz nazwy są wybrane tak, by przypominały o typie obiektu. Np. Cintw to skrót od: const int wskaźnik. Które z poniższych instrukcji zostaną przez kompilator odrzucone jako błędne i dlacze-
a) b) c) d) e)
f) g) n)
i)
intw = Cintw; intw = intCw; intw = CintCw; Cintw = intw; Cintw = intCw; Cintw = CintCw; CintCw = intw; CintCw = Cintw; CintCw = intCw;
Które z powyższych przypisań można przeprowadzić dzięki operacji rzutowania, a które są niemożliwe nawet za pomocą rzutowania? Napisz definicję 100-elementowej tablicy wskaźników do obiektów typu u n sig n e d c h a r. Napisz funkcję, która zarezerwuje 10 elementową tablicę, a jej adres przekaże wskaźni kowi o nazwie 1 i n i j ka. Ma to być tablica, w której można przechowywać wskaźniki do tablic znaków. Następnie za pomocą pętli f o r dla każdego z 10 elementów tej tablicy l i n i j ka: - zarezerwuj operatorem new tablice 80 znaków - jej adres wpisz do kolejnego elementu tablicy l i n i j k a . - w tej 80 znakowej tablicy znaków umieść C-string " to j e s t l i n i j k a n r n gdzie n jest kolejnym numerem linijki (0-9). Po zrobieniu tego wobec wszystkich 10 elementów tablicy l i n i j k a , za pomocą pętli f o r wypisz na ekranie kolejno zawartość tych C-stringów. W tablicy wskaźników l i n i j k a dokonaj takiej zamiany, by zamienić zawartość ele mentu trzeciego z szóstym. Za pomocą takiej samej jak poprzednio pętli f o r , wypisz na ekranie naszych 10 C-stringów. Zlikwiduj wszystkie rezerwowane operatorem new obiekty. Napisz deklarację funkq'i, która przyjmuje dwa argumenty. Pierwszy będący C-stringiem, a drugi będący wskaźnikiem do tablicy wskaźników do obiektów typu d o u b le . Funkcja ma zwracać wskaźnik do typu v o id . Napisz funkcję, która wywoływana jest z dwoma argumentami. Pierwszy to wskaźnik do tablicy znaków, w której jest już jakiś C-string Drugi to rozmiar tej tablicy. Funkcja ta ma do początku bieżącej treści tablicy znaków dopisać tekst " UWAGA: " . Jeśli w rezultacie powstałby C-string dłuższy, niż na to pozwala obecny rozmiar tablicy znaków —niech trzy ostatnie możliwe znaki zostaną zastąpione trzema kropkami.
372
Rozdz. 8. W skaźniki Ćwiczenia Jako rezultat swej pracy funkcja ta ma zwracać wskaźnik do tablicy znaków, na której dokonała zmiany. Przeczytaj poniższe deklaracje: void (*w) (void); void * (*w)(void*J int (*w) (int, double, char*); double (*w)(int, double, char*);
Napisz definicję wskaźnika, który może pokazywać na funkcję wywoływaną z argu mentem typu wskaźnik do tablicy znakowej, a zwracającą rezultat typu wskaźnik void. Napisz deklarację funkcji, która otrzymuje dwa argumenty, a zwraca rezultat typu bool. Argumentami tej funkcji są: 1. wskaźnik do funkcji wywoływanej z dwoma argumentami typu double, a zwracającej rezultat typu bool, 2. wskaźnik do funkcji wywoływanej z jednym argumentem typu double, a zwraca jącej rezultat typu char. Napisz krótki program, w którym będą trzy funkcje wywoływane bez żadnych argu mentów, a zwracające rezultat typu in t . Zdefiniuj dodatkową funkcję F, do której wyślesz adres jednej z tych funkcji. Funkcja F ma wywołać funkcję, której adres otrzymała, a następnie jej rezultat zwrócić jako swój rezultat. W poprzednim zadaniu wystąpiła funkcja F. Napisz definicję wskaźnika mogącego pokazać na taką funkcję. Czy wskaźnikiem typu (double *) można pokazać na funkcję double * f ( ) ? LVIl LVIIJ
Mamy nazwę funkcji. Jakim operatorem można spowodować jej wywołanie? Czytając deklarację wskaźnika do funkcji rozpoczynamy od przeczytania jego nazwy, ą następnie zaczynamy się posuwać w prawo. Dlaczego w prawo, a nie w lewo? Czy na funkcje z argumentem domniemanym void f (int m = 4) ;
można pokazać wskaźnikiem void (*w)(); Jakie operacje arytmetyczne można przeprowadzać na wskaźnikach do funkcji? Czy do wskaźnika do funkcji wolno przypisać zero? Czy argument formalny funkcji będący wskaźnikiem do innej funkcji może mieć wartoś< domniemaną? Napisz program imitujący pracę robota przemysłowego. Powinien mieć on sześć funkcj odpowiadających za elementarne ruchy robota. Wszystkie te funkcje powinny być wywoływane bez żadnego argumentu, a zwraca* rezultat typu bool. Ich ciało zawierać powinno wypis na ekran wyjaśniający, co dana funkcja robi. N« przykład funkcja bool obrot wprawof) powinna wypisać na ekranie teks "Obrót ramienia w prawo".
Wartość rezultatu ma służyć potwierdzeniu, że operacja się powiodła. W naszyrr przypadku - zawsze niech będzie zwracana wartość t rue.
Rozdział. 8. W skaźniki Ćwiczenia
373
Miech gdzieś w funkcji main pojawi się menu pozwalające wybrać jedną z sześciu operacji. Po wybraniu jednej z tych operacji adres odpowiedniej funkcji powinien zostać zapisany w tablicy wskaźników do funkcji. W ten sposób powinno się zapamiętywać skomplikowaną operację składającą się z maksymalnie 20 ruchów robota. W menu powinna być komenda "wykonać", na skutek której wszystkie operacje, w kolejności w jakiej były wybierane z menu i składowane w tablicy, powinny zostać wykonane (czyli ich funkcje uruchomione). Jeśli w menu wybierze się komendę "koniec" - program się kończy. Mając powyższy program zamień go na taki, w którym poszczególne funkcje odpowiadające za elementarne ruchy robota wywoływane są z argumentem typu double. W trakcie wyboru operacji z menu, użytkownik zostanie zapytany o wartość tego argumentu. Potem, w trakcie swej pracy, funkcja powinna wypisać jego wartość na ekranie, (np. "Obrót ram ienia w prawo o 45 stopni"). Napisz program, który na ekranie wypisze swoją nazwę, (nazwę swego pliku wykonywalnego), a w dodatku wszystkie parametry wysłane do niego z linii uruchamiającej ten program.
374
Rozdz. 9. Przeładowanie nazwy funkcji Co to znaczy: przeładowanie
9 Przeładowanie nazwy funkcji ą sytuacje, gdy nazwa funkcji doskonale określa akcję, którą wykonuje. Jeśli więc, Czytelniku, zamierzasz swoim funkcjom nadawać nazwy typu X 24al5c ( ) , to możesz w ogóle nie czytać tego rozdziału.
S
9.1
Co to z n a c z y : p rze ła d o w a n ie W języku angielskim przeładowanie (werloading) jakiegoś słowa oznacza, że ma ono więcej niż jedno znaczenie. Powiedzmy obrazowo: słowo jest przełado wane znaczeniami. Zjawisko to występuje także z nazwami funkcji w języku C++. Na podstawie swoich doświadczeń z innymi językami programowania przy wykłeś zapewne do faktu, iż w programie może być tylko jedna funkcja o danej nazwie. Używając tej nazwy mówiliśmy kompilatorowi, o jaką funkcję nam w danym momencie chodzi. Gdybyśmy mieli w programie dwie funkcje o tej samej nazwie, to kompilator nie wiedziałby, którą z nich w danym m om encie mamy na myśli, i którą z nich ma uruchomić. Kompilator C++ jest inteligentniejszy. Wyobraź sobie takie dwie funkcje: void wypisz_na_ekran(int); void wypisz na ekran(char, double, char);
Pytanie: Gdybyś to Ty był kompilatorem C++ i napotkał w programie w yw o łanie wypisz_na_ekran('A', 3.14,
'E');
to czy miałbyś jakieś wątpliwości, o wywołanie której z dwóch powyższych funkcji chodzi?
375
Rozdział. 9. Przeładowanie nazwy funkcji Co to znaczy: przeładowanie
Koń, jaki jest - każdy w idzi! - m ówi stara encyklopedia. Tę zasadę stosuje się także czasem w programowaniu. Otóż, jeśli przyjąć zasadę, że funkcję rozpo znaje się nie tylko po jej nazwie, ale także po typie argumentów, to w pewnych warunkach m oże istnieć więcej niż jedna funkcja o tej samej nazwie. Byle tylko ie dw ie funkcje różniły się argumentami. To zjawisko nazyw am y przeładowaniem nazw y funkcji. Uściślijmy. i i i n - i n n ” tftr’rŁi'.--rr^ a r “‘r**!—»
j ji■■a
* ~ wi»awrirwmfiTrrnrrr*8
P rz e ła d o w a n ie n a z w y fu n k c ji p o le g a na ty m ,
^
'
m n iin —
że wdanym
z a k re s ie w a ż n o ś c i
je s t w ię c e j n iż je d n a fu n k c ja o ta k ie j s a m e j n a z w ie . T o , k tó ra z n ic h z o s ta je w danym
p rz y p a d k u
u a k ty w n io n a , z a le ż y
od
lic z b y i ty p u
a rg u m e n tó w
w y w o ła n ia je j.
Funkcje takie mają, co prawda, tę samą nazw ę, ale muszą się różnić liczbą lub typem poszczególnych argumentów. Znaczy to, że m oże być np. je na ta a funkcja z jednym argumentem typu int. Próba zdefiniowania w tym samym zakresie w ażności drugiej takiej funkcji o tej samej nazwie i identycznym zestaw ie a rg u m en tó w -czy li tutaj jedynym argumencie int - uznana zostanie
v « i ńrjfilGj za błąd. Dla porządku trzeba dodać, że w pierwotnych wersjach języka obowiązywało specjalne słowo kluczowe o v e r l o a d - ostrzegające kompilator, że zamierzamy \h i XSt50
łtoq -
V’
daną nazwę funkcji przeładowywać. W now szych wersjach języka to słowo nie jest już konieczne. Ze w zględ ów na zgodność jest jednak tolerowane. Zwykle jednak kompilator ostrzega, że jest ono staromodne.
Oto przykład programu z przeładowanymi funkcjami: #include using namespace std; void void void void
wypisz(int liczba); ...... wypisz(char znaki, double x, const char *tekst ), wypisz(int liczba, char znak); 0 wypiszjchar znak, int liczba); ..o.u.,.***************** /
/ ★ ****i * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / int raainO
l
w y p i s z (12345); w y p i s z (8 , 'X'); w yp i s z ( ’D ’, 89.5, w yp i s z ( ’M', 22);
/ / O " stopni Celsiusza
);
/*************************************************************/ void wypisz(int liczba) cout << "Liczba typu int: " << liczba «
endl;
/************************************************************ł/ void wypisz(char znaki, double x, const char *tekst ) {
cout «
"Blok " «
znaki «
" «
x «
tekst «
} * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ^ * * ^ * -4 —
—
endl;
—
‘7
376
Rozdz. 9. Przeładowanie nazwy funkcji Co to znaczy: przeładowanie void wypisz(int liczba, char znak)
< cout << znak << ") " << liczba << endl; } z************************************************************/ void wypisz(char znak, int liczba) { cout << liczba << " razy wystąpił stan " << znak << endl; )
Po wykonaniu tego programu na ekranie ujrzymy Liczba typu int: 12345 X) 8
Blok D: 89.5 stopni Celsiusza 22 razy wystąpił stan M
Komentarz: O Program ten nie byłby niczym zajm ującym , gdyby nie to, ż e posługujem y się w nim czterem a funkcjam i o identycznej nazwie. W id zim y tu deklaracje tych funkcji. N a z w a funkcji w y p is z jest czterokrotnie p rzeład o w an a. Poszczególne funkcje ró żn ią się typem i ilością argum entów . 0 Z astrzeżenie o o d m ien n y m typie arg u m en tó w dotyczy ta k ż e kolejności. M ogą być dw ie funkcje, k tó re pracują na ty m sam ym zestaw ie a rg u m e n tó w - p o ró w naj z deklaracją funkcji © . N ie jest to nic dziw nego. W obu p rzy p ad k ach chodzi o a rg u m e n ty i n t o raz c h a r , jed n ak ich inna kolejność sp ra w ia , że funkq'e są łatw o ro zró żn ian e. Jest tylko jed n a taka funkcja o n a z w ie w y p is z , której pierw szy a rg u m e n t m a ty p i n t , a d ru g i c h a r . P o d o b n ie jest tylko jed n a funkcja w y p is z , której pierw szy a rg u m e n t jest typu c h a r , a d ru g i i n t . O W yw ołania funkcji w y p is z . K om pilator przy g ląd a się arg u m en to m i stąd dobiera funkcje, d o której one p asu ją. O tym , że p rz y c h o d z i m u to łatw o, p rz e k o n u je n a s to , co o trzy m u jem y na ek ran ie w re z u lta c ie w y k o n an ia pro g ram u . P rzed staw io n y p rzy k ład przekonał C ię chyba, jak d ziecin n ie łatw e jest p rzeła d o w y w a n ie n a z w funkcji.
Tu chciałbym zro b ić zastrzeżenie: co p ra w d a to nazwa fu n k cji jest p rzeład o w a na, jed n ak często będ ziem y m ów ić, ż e to po prostu fu nkcja je st przeład o w an a. P odsum ujm y: P rzeła d o w a n ie nazw y funkcji polega na n a d a n iu jej w ielu z n a czeń. Istnieje bow iem kilka funkcji o id en ty czn ej nazw ie. To, k tóra „w e rsja " funkcji jest u ru ch am ian a, zależy o d k o n te k stu , w jakim zo stała u ży ta - czyli od tow arzyszących tej n a z w ie arg u m en tó w w y w o łan ia.
Rozdział. 9. Przeładowanie nazwy funkcji Co to znaczy: przeładowanie
377
T o tak , jak w ż y c iu . M a m y fu nkcję „ w y w o łaj". N a z w a w yw ołaj je st przeład o w a n a z n ac zen ia m i. P o w ied zen ie: „w y w o łaj" z a rg u m e n te m „ d u c h a ro zu m ian e je st inaczej n iż p o w ie d z e n ie : „w yw ołaj" z a rg u m e n te m „szefa z zebrania , a je szc ze co in n eg o z n a c z y „w y w o łaj" z a rg u m e n te m „film ko lo ro w y . N ie ma je d n a k n ie p o ro z u m ie ń , b o k o n tek st jest jasny.
Słowa, słowa, słowa P isząc w N iem czech tę k sią ż k ę nie w ied ziałem , że p ew ien polski au to r, p rz e tłu m a c z y ł te rm in overloaded - n a polski jako "przeciążony". D ziś, w ied zą c ju ż o tym - n a d a l z całą św iad o m o ścią pozostaję p rz y "p rzeład o w ać . W literatu rze m o ż e sz je d n a k sp o tk ać ta k ż e echa tam tego tłu m aczen ia. W a rto d o d ać, że na n iem ieck i p rzetłu m aczo n o ten term in jako iiberladen (prze ła d o w a ć ), a nie iiberlasten (przeciążyć - n p . o b w ó d elektryczny). P odobnie na fra n c u sk i - p rz e tłu m a c z o n o jako "recharger" (n aład o w ać od now a). N a w łoski te n te rm in p rz e tłu m a c z o n o jako sovraccaricare - a słow o to nie ozn acza w cale czeg o ś, co jest ciężkie. (Porów naj: carica - ła d u n e k , caricare ład o w ać [także broń]). Te fa k ty p o tw ierd zają słu szn o ść tłum aczenia: "p rz e ła d o w a ć '. C h o d z i p rz e c ie ż o to, by z d a n e j n a z w y zd jąć je d n o zn ac zen ie i n a ła d o w a ć n o w e .
Kiedy przeładowywać ? P rz e ła d o w y w a ć n a z w ę fu n k cji tylko d latego, że w o ln o - b y ło b y g łu p o tą. Jak to często b y w a - i teg o n a rz ę d z ia należy u ż y w a ć z p ew n ą lo g ik ą. W n aszy m p o p rz e d n im p rz y k ła d z ie p rzeład o w aliśm y n azw ę funkcji w y p is z , d la te g o że w ró ż n y c h w arian tach w y k o n y w a ła ona an alo g iczn ą akcję na ró żn y ch zesta w a c h ob iek tó w . Z aw sze ch o d ziło na w y p isan ie czegoś na ekranie. Te cztery fu n k cje m iały p e w n ą cechę w spólną: w szystkie w y p isy w a ły coś n a ek ran ie. Ta w s p ó ln a cecha staje się najczęściej nazw ą d a n e g o zesta w u funkcji. (N a p rz y k ła d liczenie ś re d n ie j z zad an y ch ró żn y ch obiektów , a lb o w yław ian ie w a rto śc i m aksym alnej, a lb o sortow anie). M o ż n a b y też zap y tać o d w ro tn ie: kiedy n ie p rz e ła d o w y w a ć n a z w y funkcji? O d p o w ie d ź jest prosta: W ted y , g d y nie p o trze b u jem y tej sam ej n a z w y dla ró ż n y c h d ziałań . N ie m a sen su n ad aw an ie tej sam ej n azw y funkcji, k tó ra liczy lo g a ry tm , co funkcji w ygryw ającej m elodyjkę. P ro b le m m o im zd a n ie m n ie jest p o w ażn y i nie m a ryzyka, ż e .
378
Rozdz. 9. Przeładowanie nazwy funkcji Bliższe szczegóły przeładow ania o b lic z _ s re d n ia ( .. .) a praw ie n ig d y n azw ę funkcji xl2_em4 ( . . . ) D yktuje to z m y sł praktyczności. G d y w ięc w innym miejscu program u zechce m y liczyć śre d n ią dla innych obiektów (np. nie dla liczb całkowitych tylko zespolonych), to w ów czas p rzeład o w an ie nazw y funkcji n asu n ie się nam samo. Bez p o w o d u tak ie skojarzenia i p o m y sły raczej nam nie g ro żą.
M yślę, że d o tej p o ry nie u d ało mi się jeszcze nam ów ić Cię na przeładow yw anie nazw funkcji. N ie było to moim zam iarem . Tak n a p ra w d ę , to praw dziw e zastosow anie p rzeład o w an ia nazw funkcji poznam y d o p ie ro później, gdy mó wić b ędziem y o definiow aniu swoich w łasnych typów.
B liżs ze s z c z e g ó ły p rze ła d o w a n ia Jak już w iem y, przeład o w an ie oznacza, że są dw ie (lub więcej) funkcje o identycznej n azw ie, ale różniące się listą argum entów . B łędem by była próba definicji dw óch fu n k q i o identycznej n azw ie i identycznej liście argum entów . in t in t in t in t
r y s u j ( i n t a a a ); r y s u j ( i n t zm ien n a); r y s u j ( d o u b l e x) ; r y s u j ( i n t n, i n t m );
//
Z w racam jed n ak u w ag ę, że to nie p o w tó rn a d ek laraq a w y w o ła błąd. Pam ię tam y, że n aw et b ez żadnego p rzeład o w y w an ia - deklaracja (funkcji czy zm ien nej) m oże w y stąp ić wielokrotnie. To nic nie przeszk ad za. To tak, jakbyśm y kom pilatorow i p rzy p o m in ali coś w ielokrotnie. Coś, o czym o n ju ż d aw n o wie, a n a te dalsze p o w tó rzen ia nie reaguje - sądząc, że m am y sklerozę. O ile m ożliw e są w ielokrotne deklaracje, o tyle definicja m oże być tylko jed n a. Z atem w p rz y p a d k u naszych funkcji r y s u j - kom pilator n ie zareaguje jeszcze p rz y p o w y ższy ch d ek laraq ach . Z ap ro testu je do p iero p rz y definicjach tych fu n kcji. Czyli tam , g d z ie jest ciało (treść) tych funkcji. K on k retn ie w tedy, gdy nap o tk a d ru g ą definicję funkcji o n a z w ie r y s u j , a lista a rg u m e n tó w będzie taka, jaką ju ż w in n ej definicji funkcji r y s u j kiedyś nap o tk ał. Przy przeładowyw aniu ważna jest tylko odm ienność listy argum entów . Natomiast typ zw racany przez funkcję nie jest brany pod uwagę.
Z atem n ie p o p ra w n a jest próba takiego p rzeładow ania: in t a k c ja ( in t) ; d o u b le a k c j a ( i n t ) ;
f/błąd!
W trakcie kom pilacji takie p rzeład o w an ie u z n a n e zostanie z a błąd.
379
Rozdział. 9. Przeładowanie nazwy funkcji Bliższe szczegóły przeładow ania
M o żn a zd efin io w ać funkcje o identycznej n a z w ie i tak im sam y m ty p ie a rg u m e n tó w , p o d w a ru n k ie m , ż e kolejność a rg u m e n tó w b ę d z ie inna. P o n iższe funkcje są z a te m p o p ra w n y m p rzeład o w an iem int fun(int, double); int fun(double, int) ;
A teraz z a g a d k a
M am y ta k i zestaw p rz e ła d o w a n y c h funkcji: void void void void
dru(int) ; dru(double); dru(int, int); drufint, double);
a w p ro g ra m ie w y stę p u je tak ie w y w o łan ie funkcji: d r u (5,
(int) 62.34);
K tóra z ty ch funkcji z o sta n ie u ru ch o m io n a? O d p o w ie d ź jest prosta: P ierw szy
^
__I rz u to w a n ie . Liczba 62.34 zam ien io n a jest o p e ra to re m rzu to w an ia na liczb ę typu i n t (czyli w arto ścią w y ra ż e n ia jest 62). W arto ścią w y rażen ia jest ty p i n t , a z a te m z o sta n ie u ru c h o m io n a w ersja void dru(int,
int);
D ru g a z a g a d k a . C zy p o p ra w n e jest takie p rz e ła d o w a n ie funkcji? void zz(int); void zz(unsigned int);
T ak, p o p ra w n e ! A lbow iem ty p u n s i g n e d i n t o ra z ty p i n t to ró ż n e ty p y . C o zrobić, g d y s ię nie d a p r z e ł a d o w a ć ?
M o że się ta k zd arzy ć, ż e d a n y zb ió r arg u m e n tó w w y stą p ił już w definicji fu nkcji o takiej sam ej nazw ie. Jeśli m im o w szy stk o p o trze b u jem y p o w tó rn ie w łaśnie ta k ie g o z e s ta w u a rg u m e n tó w , to oczyw iście jeśli w y stąp ią o n e w id entycznej kolejności - k o m p ilato r u z n a to za błąd. A by, m im o w szystko, m óc ta k ą now ą fu n k cję zd efin io w ać, n ależy ro zw aży ć zm ia n ę kolejności a rg u m e n tó w . T o zw y kle d a je p o z y ty w n y efek t void przegląd(double, double, char * ) ; void przegląd(double, char*, double);
Jeśli ta k ie ro z w iąz an ie n a m n ie o d p o w iad a, n ależy ro zw aży ć d o d a n ie jakiegoś a rg u m e n tu tak, aby lista stała się unikalna. P rzeład o w an ie w przypadku arg u m en tó w d o m n ie m a n y c h
W y o b ra ź m y sobie tak ą sytuację. M am y tak ie oto w ersje p rz e ła d o w a n e j funkcji
fun:
380
Rozdz. 9. Przeładowanie nazwy funkcji Bliższe szczegóły przeładow ania v o id f u n ( d o u b l e ) ; v o id f u n ( c o n s t c h a r * ); v o id f u n ( i n t , d o u b le = 0) ; A o to w y w o ła n ia funkcji, które k o m p ila to r m u si d o p aso w ać: / / f u n ( d o u b le ) ; / / fu n (c o n st char * ); / / f u n ( i n t , d o u b le = 0] / / f u n ( i n t , d o u b le ) ;
f u n (3 .1 4 ) ; fu n (" n a p is " ) ; f u n (5 ); f u n (5, 6 .5 ) ;
W k o m e n ta rz u p o d a n a jest funkcja, k tó rą w y b ierze k o m p ilato r. S p ra w a w y g lą d a n a oczyw istą, je d n a k trzeb a uw ażać. W p o w y ż s z y m zestaw ie p rz y k ła d o w y c h w ersji funkcji nie m o ż e się zn aleźć taka d efin icja takiej funkcji: v o id f u n ( i n t ) ; g d y ż to p o w o d o w a ło by d w u z n a c z n o ść . W yw ołanie fu n (5 ); p a su je b o w iem je d n a k o w o do o b y d w u p o n iższy ch funkcji hf • b^rwooh< ) 51 ro/fńió/ipwKi S st
wnuH
void fun(int); void funfint, double = 0);
;;...
■; , , , N ie m a tu ż a d n e j preferencji w y n ik ającej z fak tu , iż s k o ro w w y w o ła n iu jest je d e n a rg u m e n t, to z a p e w n e ch o d zi o fu n k cję z je d n y m a rg u m e n te m . P referen cji takiej nie m a , b o sam i z niej z re z y g n o w a liśm y . K iedy? W te d y , g d y z d ec y d o w aliśm y , że d r u g i a rg u m e n t w d e k la r a q i v o id f u n ( i n t ,
d o u b le = 0) ;
jest d o m n ie m a n y . K o m p ilato r ro z u m ie to w te n sp o só b , ż e n a d a łe ś ty m sam y m tej fun k cji id e n ty c z n e p raw o , jak fu n k cji z je d n y m a rg u m e n te m . v o id f u n ( i n t ) ; S p raw ę ła tw o z a p a m ię ta ć u ś w ia d a m ia ją c sobie, że d efin icja je d n ej funkcji z d o m n ie m a n y m i a rg u m e n ta m i (w liczb ie n ) je st jakby ró w n o z n a c z n a n+1 d efin i cjom , w k tó ry ch te a rg u m e n ty w ró żn ej liczbie w y stęp u ją; a w ięc definicja tak z d e k la ro w a n e j fu n k c ji v o id f u n ( i n t ,
d o u b le = 0 ) ;
(gdzie, jak w id a ć , liczb a d o m n ie m a n y c h a rg u m e n tó w je st n = l) o d p o w ia d a ta k im n+1 = 2 d efin icjo m funkcji: v o id f u n ( i n t , d o u b le ) v o id f u n ( i n t ) ; Tu m a sz o d p o w ie d ź n a p y ta n ie, d la c z e g o d o n a sz e g o z e s ta w u fu n k c ji p rz e ła d o w a n y c h n ie d a s ię d o łą c z y ć funkcji: v o id f u n ( i n t ) ; P o p ro s tu d la te g o , ż e tak a funkcja ju ż ta m jest. C o p r a w d a —z a k a m u flo w a n a w d e k laracji z a rg u m e n te m d o m n ie m a n y m , ale o d tej p o ry te n k a m u fla ż ju ż Cię ch y b a n ie z w ie d z ie .
381
Rozdział. 9. Przeładowanie nazwy funkcji Czy przeładowanie nazw funkcji jest techniką obiektowo ori entowaną? --------- ---------------- :----
9.3
—
-------—
----------- •
”
C zy p rz e ła d o w a n ie n a zw fu n kcji je s t tech n iką o b ie k to w o o rie n to w a n ą ? Jak w idać, dzięki m ożliw ości przeładow ania n a z w y funkcji m am y sytuację, w której sam a m aszy n a decyduje, którą funkcję zastosow ać dla d an eg o obiektu. U w aln ia to p rogram istę o d m yślenia o szczegółach leksykalnych. Mówimy: w y k o n aj d ziałan ie n a takim -a-takim obiekcie (liście obiektów ), a w te d y urucho m io n a zostaje funkcja w łaściw a dla danego o b iektu. M ożna b y teraz o d razu krzyknąć „W reszcie n arzę d zie p raw d ziw ie obiektow o orien to w an e! H osanna! P roblem w tym , że ró żn i lu d zie różnie rozum ieją granicę, gdzie n ap raw d ę z ac zy n a się p ro g ram o w an ie obiektow o o rientow ane. O tym jednak, p rzy innej okazji.
Zasłona spada w an a, i że p ro g ram (kom pilator) orientuje się w e d łu g obiektów będących arg u m e n tam i funkcji. B ędziesz pew nie ro zczaro w an y , że to takie prym ityw ne, jed n ak sąd zę, że m u sisz to rozczarow anie przeżyć. W iedząc, jak to jest n ap raw d ę - łatw iej zro zu m iesz d alszą część rozdziału. O tóż: tak n ap raw d ę, to te funkcje mają różne n azw y . Ty, co p raw d a, dałeś dw u funkcjom tę sam ą nazw ę, jednak kom pilator zm ien ia nazw y w szystkim funk cjom Tw ojego p ro g ram u . N ie zapom ina Tw oich nazw , tylko je uzup ełn ia. D opisuje d o nich p o p ro stu , z jakimi arg u m e n tam i jest ta funkcja. P ok ażm y zasad ę tych zm ian. Funkcja void akcja(void);
o trzy m u je przykładowo nazw ę akcja__Fv F o zn acza tu słow o funkcja, litera v oznacza v o i d - p u sta lista arg u m en tó w . Sposób ozn aczan ia m oże być zależny od ty p u kom pilatora.
Z kolei funkcja void akcja(int, double); void akcja(double, L n t ) ;
manazwę manazwę
- akcja akcja
Fid Fdi
czyli - jeśli są argum enty, to nazw y ich typów ta k że doczepiane są d o naszej nazw y. Z am iana ta zostaje zrobiona bez naszej w ied zy . D otyczy ona zarów no definicji i deklaracji funkcji, jak też i w yw ołań funkcji. Zatem w y w o łan ie a k cja(3.14, 100);
zostaje zastąp io n e w yw ołaniem akcja__Fdi(3.14, 100);
382
Rozdz. 9. Przeładowanie nazwy funkcji Linkowanie z modułam i z innych języków
Czyli w rezultacie - w programie są teraz funkcje o zupełnie innych nazwach. To tutaj czar pryska. Nie ma już więcej programu „obiektowo orientowanego" — okazuje się, że kompilator zamienił go sobie na zw ykły „klasyczny" program. Rozumiemy teraz dlaczego dwie funkcje o identycznych nazwach muszą mieć inną listę argumentów. To gwarantuje kompilatorowi, że jeśli nawet trzon nazwy będzie ten sam, to przynajmniej doczepiony fragment opisujący argu menty - rozróżni te nazwy. Dodatkowo rozumiemy dlaczego przeładowane funkcje nie mogą się różnić jedynie typem zwracanym: informacja o typie zwracanym nie jest doczepiana do nazwy. Nie da się więc takich funkcji rozróżnić. '
9.4
........T'
' .......... ............ —
—
1H. I . I . IM IMIIIIJ M M l»
—i
L in k o w a n ie z m o d u łam i z in n yc h ję zyk ó w Ważny jest fakt, że opisanej zmianie nazw podlegają wszystkie funkq'e. Nie tylko te, które są przeładowane, ale naprawdę wszystkie. W zasadzie o tym fakcie można by w ogóle nie myśleć - jest to w końcu prywatna sprawa kompilatora, jak on sobie radzi ze swoją pracą. Niestety, tutaj jest pewien kłopot. Otóż jeśli masz program, na który składają się dwa moduły: jeden stary, dobrze chodzący, napisany i skompilowany (kiedyś) w klasycznym C, a drugi moduł skompilowany kompilatorem C++, to podczas linkowania tych modułów w jeden program - wyniknie problem: Dostaniesz, mianowicie, komunikat, że linker nie odnajduje niek tórych funkcji. Funkcji, o których wiesz na pewno, że tam przecież są! Podajmy przykład takiej sytuacji. Załóżmy, że w „starym" m odule programu (tym z klasycznego C) jest funkcja void mapa(int, double);
a Ty wywołujesz ją z modułu C++. Aby to było możliwe, musisz ją oczywiście wcześniej w tym m odule zadeklarować. extern void mapa(int, double);
Gdy linkujesz taki program, otrzymujesz komunikat, że funkcja mapa (int, d o u b le ) w ogóle nie istnieje. Dlaczego? Powód jest bardzo prosty. Otóż w m odule C++ automatycznie powyższa deklaracja - a także właściwe wywołanie funkcji mapa - uległo zmianie nazwy. Deklaracja ta zmieniła się na taką: extern void mapa__Fid(int, double)
W trakcie linkowania okazało się, że funkcji o nazwie mapa__ F id nie ma zdefiniowanej nigdzie. I słusznie, bo przecież w klasycznym C nie następują żadne zmiany nazw y. Tam funkcja nazywa się po prostu mapa.
Rozdział. 9. Przeładowanie nazwy funkcji Przeładowanie, a zakres ważności deklaracji funkcji
383
; samo będzie jeśli nasz „stary" moduł jest napisany w asemblerze, Pascalu fku innym niż C++. Nie da się takich m odułów zlinkować w jeden
Nie! Język C++ nic by nie był wart, gdyby nie pozwalał na łączenie z modułami pochodzącymi z C, asemblera czy innych języków programowania. Oto wyjście: w module C++ należy zadeklarować funkcję w ten sposób extern "C" void mapa(int, double);
fttli
:
Litera C nie mówi, że to musi być koniecznie funkcja z klasycznego C. Mówi tylko, że nie jest to według konwencji C++. Czyli według takiej konwencji, jak to jest np. w klasycznym C. Innymi słowy postawienie tam symbolu "C" jest jakby powiedzeniem kompilatorowi: -Bardzo proszę nie robić mi żadnych kombinacji z nazwą tej funkcji, albowiem jest to funkcja, która została skompilowana bez modyfikacji nazwy! Jeśli masz zadeklarować więcej takich funkcji, to możesz je umieścić w środku nawiasu klamrowego extern "C" { pierwsza(int) druga(double, char* // ...
int)
}
W środku takiego nawiasu (czyli bloku) może się znaleźć nawet dyrektywa include. extern "C" { #include "raoje_deklar.h"
1
Wtedy wszystkie umieszczone we włączanym pliku deklaracje funkcji też traktowane są jak deklaracje funkcji w klasycznym C.
3.5
P rze ła d o w a n ie , a z a k re s w ażn ości d ek la ra cji fu n k c ji Definiując pojęcie „przeładowanie" powiedzieliśmy, że przeładowanie nazwy funkcji następuje wtedy, gdy w danym zakresie ważności jest więcej niż jedna funkcja o takiej samej nazwie. Nie rozwijaliśmy tego zastrzeżenia o identycz ności zakresów ważności. Pamiętamy, że: najczęściej funkcje mają zakres ważności pliku, w którym je zdefiniowano. Czyli są znane w pliku od linijki ich deklaracji. Jeśli program składa się z kilku plików, to w tych innych plikach funkcja jest nieznana, dopóki nie zostanie tam zadeklarowana. Deklaracja może objąć zakres całego pliku, albo też mieć zakres mniejszy - lokalny. Oto przykład. Załóżmy, że mamy następujący plik:
384
Rozdz. 9. Przeładowanie nazwy funkcji Przeładowanie, a zakres ważności deklaracji funkcji i i n c l u d e < io s tre a m > u s in g n am esp ace s t d ; I y***********************************-*******+******************/,| v o id d ź w i ę k ( i n t a) . { c o u t << a << " n u ty \ n " ;
\ ************************************************************* z' v o id d ź w ię k (d o u b le h) { c o u t << "Dźwięk o c z ę s t o t l i w o ś c i : " << h << " herców \ n " ; /i,************************************************************/ Z aw iera on, jak w idać, definicje dw óch funkcji o n azw ie d ź w ię k . A oto innji plik, w k tó ry m korzystam y z tych funkcji: # in c lu d e < io s tre a m > u s in g n am esp ace s t d ; e x t e r n v o id d z w ie k ( i n t) ; // deklaracja o zasięgu pliku /*************************************************************/ i n t m a in () d ź w ię k ( 1 ) ; { / / <—zakres lokalny e x t e r n v o id dźw ięk ( d o u b le ) ; d ź w ię k ( 2 ) ; d źw ięk ( 3 .1 4 ) ; )
// ©
/ / deklaracja lokalna
dźwięk(5); dźwięk(6.28);
/' !' ^ 11 I ' Sc // v
}
□
Po zlinkowaniu tych plików i wykonaniu programu na ekranie zobaczymy tekst 1 n u ty Dźwięk o c z ę s t o t l i w o ś c i : 2 herców Dźwięk o c z ę s t o t l i w o ś c i : 3.14 herców 5 n u ty 6 n u ty Bliższe p rzyjrzenie się p ro g ram o w i upew nia nas, że ż a d n e p rzeład o w an ie ni$ nastąpiło. S podziew aliśm y się, że będzie w y k o n y w an a ta w ersja fu n k c dźw ięk, która jest właściwa arg u m en to m w yw ołania. T ym czasem tak się ni< stało. D laczego? Pow ód jest jeden. Deklaracje tych funkcji nie mają tego sameg< zakresu w ażności. Zam iast p rzeład o w an ia nastąpiło zasłonięcie
Przyjrzyjmy się ciekawszym punktom programu O Jest deklaracją o zasięgu pliku. © Jest w yw ołaniem fu n k q i d ź w ię k - jedynej znanej w tym m om encie, czyli te zadeklarow anej powyżej.
Rozdział. 9. Przeładowanie nazwy funkcji Przeładowanie, a zakres ważności deklaracji funkcji
385
© O tw ie ra n y jest jak iś blo k lo k a ln y . M oże być to ta k sztu czn e, jak u n as, a m o ż e byt to p o p ro s tu w n ę trz e jakiejś funkcji. © W ty m lo k a ln y m b lo k u d e k la ru je m y , że istnieje g d zieś funkcja dźwięk (dou b l e ) . N a z w a d ź w i ę k z a s ła n ia w szy stk ie in n e m o ż liw e n a z w y d ź w i ę k 2 in n y ch z ak re só w . Stają się n ied o stęp n e. © W y w o ła n ie funkcji d ź w i ę k z a rg u m e n te m ty p u int. Jed y n ą d o stę p n ą fu n k cją c n a z w ie d ź w ię k je st te ra z fu n k cja d ź w i ę k ( d o u b l e ) . T am ta funkcja jest, cc p ra w d a , k o m p ila to ro w i z n a n a , ale jej n a z w a je st teraz zasło n ięta. Z atem k o m p i la to r z a m ie n ia a r g u m e n t ty p u int na ty p double, i u ru c h a m ia tę jedyną m o ż liw ą te ra z funkcję. Z a m ia n a n astęp u je p rz y u ży ciu tzw . k o n w ersji s ta n d a r dow ej. © W y w o ła n ie fu n k cji z a rg u m e n te m ty p u double. Tu nie m a p ro b lem u . T akiegc a rg u m e n tu k o m p ila to r w ła ś n ie oczekiw ał.
O K o ń czy się z a k re s lo k aln y . D eklaracja funkcji d ź w i ę k ( d o u b l e ) zo staje zap o m n ia n a i o d sła n ia d e k la ra c ję dzwiek(int). m o ż liw ą te ra z fu n k cję dźwięk. © N a to m ia s t w y w o ła n ie z a rg u m e n te m ty p u double, to z n o w u d la k o m p ilato ra p e w ie n kło p o t. F unkcji o ta k im arg u m en cie o n ju ż nie z n a (dek larację lo k aln ą p rz e d ch w ilą z a p o m n ia ł). Z a m ien ia w ięc a rg u m e n t ty p u d o u b l e n a ty p int i u ru c h a m ia je d y n ą m o ż liw ą funkcję o n a z w ie dźwięk. S fo rm u łu jm y w n io sek : A by m ieć rzeczy w iście d w ie fu n k cje o tej sam ej n a z w ie d o stę p n e ró w n o c z e śn ie (czyli p rz e ła d o w a n e ) o b ie m u szą m ieć id e n ty czn y z a k re s w ażn o ści. W n a s z y m p rz y k ła d z ie tak b y było, g d y b y o b ie fu nkcje by ły z a d e k la ro w a n e tak, by m ia ły z a k re s w ażn o ści p lik u , czyli g d y b y d ek laracja © b y ła tam , g d z ie © T rze b a je d n a k zaz n aczy ć, ż e k lau zu la o id e n ty czn o ści z a k re su w a ż n o śc i przy p rz e ła d o w a n iu n ie jest byn ajm n iej balastem , z k tó ry m trzeb a n a u c z y ć się żyć! W ręcz p rz e c iw n ie —o tw ie ra n am d ro g ę d o lo k a ln eg o p rz e ła d o w y w a n .a funkcji. W ra m a c h je d n e g o lo k a ln eg o obszaru funkcje m o g ą się p rz e ła d o w y w a ć , a nie m a to ż a d n e g o s k u tk u w o b ec św iata z e w n ę trz n e g o , g d z ie m o g ą b y ć inne lo k a ln e o b szary , w k tó ry ch d o k ła d n ie ta sam a n a z w a funkcji m o ż e być ró w n ież p rz e ła d o w a n a . Je d e n lo k a ln y o b szar nie w c h o d z i w kolizję z d ru g im . W iem , b rz m i to m o że tro ch ę zaw ile. W szy stk o je d n ak stan ie się jasn e, gdy w k ro c z y m y w k ra in ę lo k aln y ch o bszarów jak im i są definicje k las —czyli ty p ó w , k tó re m o ż e m y d efin io w a ć sam i.
Przeładowanie, a przestrzenie nazw C o p r a w d a , p rz e s trz e ń n a z w (namespace) tw o rz y sw o isty z a k re s w ażności, ale - jeśli ch cem y - m o ż e m y p rzy p rz e ła d o w y w a n iu ro z lu ź n ić te g ran ic e za p o m o c ą d ek laracji u s i n g namespace. D zięki takiej d ek laracji, w bieżącym
386
Rozdz. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argumentów z a k re sie w ażności, stan ą się zn an e nazw y dodatk o w y ch "kandydatów , pocho d zący ch z danej p rzestrzen i nazw .
9.6
R o zw a ża n ia o id e n ty c zn o ś c i łub o d m ie n n o ś c i ty p ó w a rg u m e n tó w W ró ćm y na poziom p ro sty ch spraw . Pow iedzieliśm y, że przeładow anie fu n k q i jest m ożliw e w tedy, g d y funkcje różnią się listą arg u m en tó w . Funkcje m ogą m ieć tę sam ą nazw ę - p o d w arunkiem , że arg u m e n ty będą inne.
9.6.1
Przeładowanie, a ty p e d e f i enum P rzy p o m in am y sobie za p e w n e deklarację t y p e d e f (str. 85). W p ro w ad za ona sy n o n im dla jakiegoś istniejącego już typu. N ie tw o rzy ona now ego typu. Z atem p o n iż sz a próba p rzeład o w an ia zostanie w czasie kom pilacji u zn an a za błąd ty p e d e f i n t calk o w ; v o id f u n k c j a l ( i n t ) ; v o id f u n k c ja l (calkow ) ;
//•'
P o n iew aż c a l kow jest tylko innym określeniem tego sam ego ty p u i n t d lateg o m a m y tu, w gruncie rzeczy, d o czynienia z funkcjam i: v o id f u n k c j a l ( i n t ) ; v o id f u n k c ja l ( i n t ) ;
//•
C zy li są to d w ie funkcje o tej sam ej nazw ie i identycznej liście arg u m en tó w , a to jest b łędem . Z u p e łn ie inna spraw a jest z typam i w yliczeniow ym i d efiniow anym i instrukcją enum (str. 87). Typ w yliczeniow y jest n ap raw d ę o d ręb n y m typem . Co praw d a, ty p te n także bazuje na liczbach całkowitych, ale to nie m a znaczenia. (Przypo m in am , że typ i n t o raz u n s i g n e d i n t , to także o d m ien n e typy). Z a te m instrukcją enum definiujem y now y typ (inny o d typu i n t ) i nadajem y m u n azw ę. Ta nazw a m oże być używ ana p rzy p rzeład o w am ach funkqi. enum o p e r a c ja { p i s z = 1, c z y t a j , s k o c z , p rz e w in }; v o id t a ś m a ( i n t ) ; v o id taśm a ( o p e r a c j a ) ; Jest to p o p raw n e p rzeład o w an ie. Typy arg u m e n tó w obu funkcji różnią się.
ł.6.2
Tablica, a w skaźnik Z w ró ć u w ag ę na następujące dw ie deklaracje funkcji v o id f f f ( i n t t a b ( ] ) ; v o id f f f ( i n t * w s k );
Rozdział. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argum entów
387
O b ie fu n k cje p o d k ątem p rz e ła d o w a n ia u z n a w a n e są za id en ty czn e i d la teg o w d a n y m zak re sie w ażn o ści nie m o g ą m ieć tej sam ej n azw y . K o m p ilato r u zn a to za b łą d . Ł atw o to in tu icy jn ie w y czu ć. W yobraź sobie taki fra g m e n t p ro g ram u : i n t t a [ 10 ] ;
f f f (t a ) ;
// definicja tablicy // wywołanie funkcji
Jako a r g u m e n t a k tu a ln y w w y w o łan iu funkcji z n a jd u je się n azw a tablicy, czyli a d re s jej p o czątk u . G d y b y ś b y ł k o m p ilato rem , to k tó rą z w yżej z a d e k la ro w a nych fu n k cji p o w in ie n eś p rz y tak im w y w o łan iu u ru ch o m ić? Z ro z d z ia łu o w sk a źn ik ac h w iem y , że tablice i w sk a ź n ik i m ogą być w zasad z ie tra k to w a n e w y m ien n ie. Z atem obie deklaracje fu n k cji jed n ak o w o p a su ją d o w y w o ła n ia . A to jest błąd. N ie m oże być żad n ej d w u zn aczn o ści. M ó w iąc bard ziej form alnie: za ró w n o i n t ta b [] jak i in t
*wsk
m o g ą m ieć te sam e inicjalizatory (tutaj - a rg u m e n ty ak tu aln e w w y w o łan iu funkcji). Z atem na p o d sta w ie w y g ląd u inicjalizatora n ie m ożna z d e c y d o w a ć d o której w ersji o n się nadaje. N ad aje się bow iem do o b u . D w uznaczności b y ć nie m oże i to w łaśn ie pow ie C i ko m p ilato r w inform acji o błędzie. P o d su m u jm y :
N a s tę p n e p arag rafy m ogą Cię trochę nudzić i m oże w y d a d z ą się sfo rm alizo w a ne. N ie zniechęcaj się jednak. Jeśli czytasz tę k sią ż k ę po raz p ierw szy , to w p e w n y m m om encie z d ec y d o w an ie o d rad zę Ci cz y ta n ie dalszej części tego ro z d z ia łu . Tym czasem p o staraj się jednak m im o w sz y stk o czy tać najbliższe sied em p a ra g ra fó w (są w y jątk o w o krótkie), n aw et jeśli nie w szy stk o w y d a Ci się interesujące. B ędziem y tu n ad al m ów ili o tym , kiedy a rg u m e n ty d ek laro w an y ch funkcji p o zw alają na p rzeład o w an ie, a k iedy nie.
Najmłodszym czytelnikom proponuję takie skojarzenia: Zbiór w szy stk ich funkcji o jednej (przeładow anej) n azw ie , to jak b y m en ażeria z w ie r z ą t- p o jednym okazie różnych g atunków . A rg u m e n t w y w o łan ia funkq'i to jakby p o k arm dla zw ierząt. Z kolei funkcje to s a m e zw ierzęta - niek tó re są
388
Rozdz. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argumentów ro ślin o ż ern e, niek tó re m ięsożerne. O tó ż jeśli chcem y, by zw ierzęta się nie pogryzły, pow inniśm y daw ać zaw szę ta k i p o k a rm , który m o że zjeść tylko jedno z nich. To jest oczyw iste. N iestety , są zw ierzęta, które mogą zjeść to samo, co inne. W następnych p a rag rafa ch p o ro zm a w iam y w łaśnie o tym , jak rozpoznaw ać takie zw ierzęta u n ik a ć konfliktów . C ała tru d n o ść w tych paragrafach polega na tym , że w ielokrotnie pojawia się W nich słow o „inicjalizator". Umówmy się, że ile razy ja napiszę „inicjalizator c z y „ a rg u m e n t w y w o łan ia funkcji", to Ty sobie myślisz: „p o k arm p o d an y z w ierz ęto m ".
9.6.3
P ew n e szczegóły o tablicach w ielowym iarowych M ó w iąc o tablicach w spom nieliśm y o sposobie przesyłania ich do funkcji, P rz y p o m in a m - tablicy nie przesyła się p rzez w artość (bo kto b y przesyłał do fun k cji np. 8192 elem en tó w tej tablicy), ale przesyła się ją p rzez adres. N azw a tablicy, jak w iad o m o w e d łu g egipskich p ap iru só w , jest ad resem jej początku U m ieszczając w w y w o łan iu nazw ę tablicy w ysyłam y więc do funkcji jej adres. <♦ W obrębie funkq'i tak o trzym any a d re s m oże posłużyć d o inicjalizacj w skaźnika, k tó ry m będziem y się sw o b o d n ie poruszali p o elementach tej tablicy. ♦> In n y m sposobem odebrania p rzy słan eg o do funkcji a d re s u tablicy jesl o d ebranie go jako tablicy. To w szy stk o oczyw iście przypom nienie, bo m ów iliśm y o tym w p oprzednim ro z d z ia le , tutaj więc tylko 2 przykłady takich funkcji int tablica[4][2]; //... r void funwsk(int (*wsk)[2J); void funtab(int t [4] [2] );
F unkcje te mają inne n azw y , bo jak w iem y z p o p rzed n ieg o p arag rafu - typy T T[ ] są d la przeład o w an ia nierozróżnialne. To dlatego, że oba m ają identyczny inicjalizator (czyli tutaj: identyczny m ożliw y a rg u m e n t w y w o łan ia) funwsk(tablica); funtab(tablica);
Jeśli jest to dla Ciebie jasne, przeskocz teraz p o n iż sze pół strony, d o najbliższegc "czarnego listka" i tam czytaj dalej.
Jak łatwo takie zagadki rozwikłać? W y starczy pam iętać, że d w u w ym iarow a tablica to n ap raw d ę tablica jed now y m iaro w a , której k ażdym elem entem jest tablica. Z atem tablica: int elektrody[4][2];
to n a p ra w d ę jest czteroelem entow a tablica elem entó "coś-tam" nazw iem y teraz pow ażniej ty p em X.
" łć tó \
Rozdział. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argum entów
389
P o n iew aż o b ie funkcje m ają jako arg u m en t inny ty p ob iek tu , dlatego m ogą m ieć inną n a z w ę . , Innym i słowy, obie funkcje ch a p mają argumenty będące tablicami czegoś-tam. Dla każdej z nich cos-tam oznacza inny typ. (Dla pierwszej "coś-tam" to tablice siedmioelementowe typu in t, a dla drugiej "coś-tam " to tablice dwuelementowe typu in t). I zn o w u zap y tam : —G dybyś b y ł kom pilatorem , to czy m iałbyś tu w ątpliw ości, którą funkcję p rz y d an y m w yw ołaniu uruchom ić? Da się tu bez d w u zn aczn o ści określić, k tó ra funkcja pasuje do danego arg u m en tu . Idźm y dalej. P am iętasz z p arag rafu o przesyłaniu d o funkcji tablic w ielow ym ia row ych (str. 243), że dla funkcji istotne są rozm iary tablicy, ale ro zm iar p ie rw szy od lew ej - nie. D opiero d ru g i i następne. i n t t a b l i c a [4] [5] [ 1 ) ;
H p ie r w s z y od lewej, to ten, gdzie jest 4
390
Rozdz. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argumentów T o d lateg o , ż e te n p ie rw szy od lewej rozm iar nie bierze u d ziału w obliczaniu p o zy cji elem en tu d a n e j tablicy w pam ięci kom putera. (Zw ykła arytm etyka). D lateg o w ięc, jeśli w dw óch funkcjach (o tym sam ym zakresie), argum ent fo rm aln y b ęd ąc y tablicą różniłby się tylko tym w ym iarem „pierw szym odj lew ej", to takie fu n k cje nie m ogą mieć tej sam ej nazwy. N a przykład: void abra(int x[5][10][2] ); void abrafint y [9j[10j [2] );
//błąd, bo identyczna ja k pow yższa
T ak że funkcje void kadabra(int s [3]); void kadabrafint m [8]) ;
N ie m ogą m ieć tej sam ej nazw y. Tablice będące ich argu m en tam i form alnym ró żn ią się jed y n ie w y m iarem pierw szym z lewej. To, że jest to jedyny w ym iar n ie m a znaczenia. P rz y obliczaniu pozycji elem entu w pam ięci nie bierze on u d z ia łu . P o d su m u jm y : Nie możemy mieć przeładowania funkcji w sytuacji, gdy dwie funkcje różnią się jedynie argum entem tablicowym tak, że tylko ten rozmiar najbardziej z lewej jest inny. Dla kompilatora jest to nierozróżnialne - dlatego przy definicji takich funkcji zaprotestuje.
9.6.4
Przeładow anie, a referencja O p iszem y tu kolejną sytuację, gdy przy p rzeład o w an iu arg u m e n ty traktowani są jako identyczne. O to ilustracja: void ggg(int m ) ; void ggg(int &k);
U
// za m ało się ró żn ią
...
int m;
ggg(m); Jak w idać, obie funkq'e różnią się tylko tym , ż e jedna przyjm uje a rg u m e n t prze w artość, a druga p rz e z referencję. Jest to błąd. Takie a rg u m e n ty są p o d kąterffl p rzeład o w an ia identyczne. Tu zn o w u bardzo łatw o wyczuć: dlaczego. Z ap y tam znow u: -G d y b y ś to Ty bj kom pilatorem i zobaczył takie w yw ołanie funkcji:
ggg (m); to którą funkcję należałoby uruchom ić? N o w łaśnie - nie w ia d o m o którą W yw ołanie to pasuje jednakow o do jednej, jak i d o drugiej funkcji. N astępuje niedopuszczalna dw uznaczność. Inaczej mówiąc - oba ty p y argum entu fo rm aln eg o mają taki s a m ty p inicjału tora. Sam zobacz inicjalizację referencji i obiektu:
Rozdział. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argum entów
391
Dygresja dla najmłodszych: O kazu je się, ż e funkcje m ające argum enty fo rm aln e T i T& to d w a gatu n k i zw ie rz ą t m ogących zjeść to sam o. W ypróbow aliśm y to w łaśnie —oba zjadają „inicjalizato r" (pokarm ) b ęd ący obiektem ty p u T. (To nic, że p o tem traw ią go inaczej). A by u n ik n ą ć a w a n tu ry , nigdy nie należy trzym ać w m enażerii dw óch takich zw ierząt. _________________________ _________________ ___
.6.5
Identyczność typów: T, const T, volatile T In n ą sytuacją, gdy n astąp iłab y dw uznaczność jest v o id f ( i n t ) ; v o id f ( c o n s t i n t m) ; v o id f ( v o l a t i l e i n t m );
1/dla przeładowania // nierozróżnialne - błąd!
K ażda z tych funkcji ak ceptuje taki sam a rg u m e n t ak tu aln y (inicjalizator). W k on sek w en cji p rzy p o n iższy m fragmencie p ro g ram u : i n t x = 6; f (x) ; N ie d a ło b y się określić, o k tó rą wersję fu n k q i chodzi. W szystkie - jednakow o d o b rze - p asu ją d o tego w yw ołania. W szy stk ie o trzy m u ją tak sam o argum ent p rzez w artość. Różnica m ięd zy nim i poleg a ty lk o na tym , że: •
pierw sza funkcja jest zw ykłą funkcją,
•
d ru g a - obiecuje, że swojej lokalnej kopii p rzesłan eg o arg u m entu nie będzie zm ieniała,
•
trzecia - obiecuje nie robić żad n y ch optym alizacji z lokalną ko pią.
Te o b iecan k i są jednak p ry w a tn ą spraw ą funkcji. K ażda z nich m oże zainicjali zow ać sw ó j a rg u m e n t form alny tym sam ym inicjalizatorem .
392
Rozdz. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argumentów N asze w y w o łan ie f (x ) jest jednakow o dobre do w yw ołania każdej z nich. Dla kom pilatora następ u je w ięc w ieloznaczność, zatem oznajm ia błąd. Podsum ujm y: Ponieważ dla dowolnego typu zarówno
t
t
ja k i c o n s tT ja k i v o la tile T inicjalizuje się identycznym inicjalizatorem, dlatego tak określone argumenty formalne uznawane są za identyczne.
Dygresja dla najmłodszych: O kazuje się, że trzy funkcje mające arg u m en ty form alne ty p u T, c o n s t i v o l a t i l e T - to trzy gatunki zw ierząt mogących zjeść to sam o. Słow a c o n s t i v o l a t i l e określają tu nie pokarm , ale to, co z niego jest ju ż lokalnie w przew o d zie p o k arm o w y m bestii. To jed n ak nie ma żadnego znaczenia - w ażne, że p rzed spożyciem byl to pokarm ty p u T, o który w szystkie trzy zw ierzaki po gry zły się naw zajem . A by uniknąć a w a n tu ry nie należy n ig d y trzym ać w m enażerii choćby dw óch takich zw ierząt.
9.6.6
Przeładowanie - a typy: T*, volatile T*, const T* Z agadka. C zy m ożliw e jest takie p rzeład o w an ie funkcji? void radio(double *k); . void radio(const double *k) ; void radio(volatile double *k);
Jeśli na p o d staw ie po p rzed n ieg o p arag rafu m ów isz, że nie - to przegrałeś. Jeśl nie p rzychodzi Ci d o głow y żadna o d p o w ied ź, to spróbuj pom yśleć, jak każdą z tych funkcji się w yw ołuje. Jeśli w y w o łan ie pasow ać m oże d o więcej niż jedne funkcji - to takie przeład o w an ie jest niem ożliw e. C o w idzim y w pierw szej funkcji? ♦$» Jest to deklaracja funkcji r a d i o , którą to funkcję w y w o łu je się podają jako arg u m e n t - ad res obiektu ty p u d o u b le . Z atem n a p rzy k ła d tak: double obj = 15.6; radio(&obj);
C o w idzim y w drugiej funkcji? «$♦ Jest to deklaracja funkcji r a d i o w yw oływ anej z a rg u m e n te m będącym adresem obiektu, który jest stały ( c o n s t ) const double pi = 3.14; radio(&pi);
Z aś deklaraq'a trzeciej funkcji mówi:
Rozdział. 9. Przeładowanie nazwy funkcji Rozważania o identyczności lub odmienności typów argum entów
393
to funkcja r a d i o w y w o ły w a n a z a rg u m e n te m b ęd ąc y m ad resem ?ktu ty p u d o u b l e - i to nie z w y k łeg o o b ie k tu , ale tak ieg o , k tó ry ma cech ę v o l a t i l e . volatile double wulkan; radio(&wulkan);
W e w szystkich trzech p rz y p a d k a c h chodzi o ró ż n e ty p y o b iektów . K o m p ilato r n a p o ty k ając n a w y w o ła n ie funkcji z u d z ia łe m je d n e g o z tych o b ie k tó w (jako a rg u m e n tu w y w o ła n ia funkcji) - w ystarczy, ż e sp o jrz y na ten a rg u m e n t i juz w ie, d o której z w ersji fu n k cji r a d i o o n p a su je - a p asu je tu z a w sz e ty lk o do jednej. M o żesz je d n a k zap y tać: - Ja k to, przecież w p o p rz e d n im p a ra g ra fie m ieliśm y p o d o b n ą sy tu ację, a ta m n ie w o ln o było p rz e ła d o w y w a ć . Jak jest ro zm ca? T aka, ż e w p o p rz e d n im ro z d z ia le słow a c o n s t i v o l a t i l e o k reślały lokalny obiekt tw o rz o n y w o b ręb ie funkcji. C zyli k opię. T en o b iek t (kopia) m o że sobie być, ja k i chce - i tak d o jego inicjalizacji n a d a w a ł się d o w o ln y ty p o b iek tu T. To p o w o d o w a ło w ielo zn aczn o ść. Tutaj je s t cjdw rotnie: sło w a c o n s t o ra z v o l a t i l e o k reślają tu ty p o b iek tu , k tó ry słu ży d o inicjalizacji - czyli określają a r g u m e n t w y w o łan ia funkcji. (A n ie jak p o p rzed n io : lokalny o b iek t inicjalizo w an y w e w n ą trz fu n k cji). Skoro aż ta k precyzyjnie ok reśliliśm y ty p a rg u m e n tu w y w o ła nia, to zn aczy , ż e tylko on m oże w y w o łać d a n ą w ersję fu nkcji. P rzy ta k p recy z y jn y m określeniu nie m a m o w y o w ieloznaczności. P o d su m u jm y : P o n ie w a ż dla d o w o ln e g o typu T następujące d eklaracje arg um e n tó w form alnych: T*
const T * m . oraz v o la tile T * wymagają każda odmiennego inicjalizatora (odmiennego argumentu wywołania) - dlatego funkcje różniące się tylko obecnością jed n e j z wersji w spom nianych deklaracji - mogą być przeładowane. Z atem fu n k cje z a d e k la ro w a n e na początku teg o p arag rafu są p o p ra w n y m p rz e ła d o w a n ie m .
Dygresja dla najmłodszych: T u s p ra w a b y ła inna. Słow a c o n s t i v o l a t i l e stoją tak, że określają nie to, co lo k aln ie je st w p rz e w o d z ie p o k arm o w y m z w ie rz a k a , ale są o k reślen iem p o k a r m u - ja k i m a być przed zjedzeniem . O kazuje się, ż e tak ie trzy zw ie rz a k i m ają ściśle sp re c y z o w a n e jaki p o k a rm zjadają. Jed en z w ierz ak je ty lk o b efsztyki k rw iste, d r u g i śred n io p rzy sm aż o n e, a trzeci b a rd z o p rzy sm ażo n e. Jeśli zn asz osobiście jak ieś bestie ż y w iąc e się befsztykam i, to w iesz, że jeśli taki jad a śre d n io
394
Rozdz. 9. Przeładowanie nazwy funkcji Adres funkcji przeładowanej p rzy sm ażo n e, to b rzy d zi się ociekającymi krwią lub spalonym i na węgiel Z atem nie m a konfliktu - po w yglądzie befsztyka kom pilator łatwo zdecyduje kom u go rzu cić n a pożarcie.
9.6.7
Przeładow anie - a typy: T&, volatile T&, const T& D okładnie ta sam a argum entacja, którą omówiliśmy w p o przednim paragrafii dotyczy różnych w ersji referencji. Oto ilustracja. Funkcje: void lad(int &m) ; void lad(const int &m); void lad(volatile int &m);
są w y w o ły w an e - k ażd a z innym rodzajem obiektu const int stała = 12; volatile int elektron = 4; int liczba = 1; lad(stała); lad(elektron) lad(liczba);
//w y w o ła lad (const int &) H w y w o ła lad(volatile int &) I I w y w o ła lad (int &)
albow iem słow a const i volatile określają typ inicjalizatora (czyli typ arg u m en tu w yw ołania). Tak precyzyjne określenie inicjalizatora nie prowadź: d o w ieloznaczności zabójczej dla przeładow ania. P odsum ujm y: Ponieważ dla dowolnego typu T następujące deklaracje argumentów formalnych
T & const T & v o la tile T & wymagają każda odmiennego inicjalizatora (odmiennego argumentu wywołania) - dlatego funkcje różniące się tylko obecnością jednej z wersji wspomnianej deklaracji - mogą być przeładowane.
Dygresja dla najmłodszych: Tłum aczenie jest takie jak p o przednio z tym , że teraz na sam befsztyk zw ierzak m ów i przezw iskiem . N adal jednak 'to ' m a być albo k rw iste albo średnio przy sm ażone albo... Są to upodobania tak w ykluczające się, że n ig d y nie dojdzie d o aw antury. Jedzące to trzy zw ierzaki m ogą być trzym ane w tej sam ej m enażerii.
9 .7
A d res fun kcji p rzeład o w an ej Jeśli w zw ykłym p rzypadku chcem y się posłużyć w sk aźn ik iem d o funkcji, to m usim y go oczyw iście w pew nym m om encie ustaw ić tak, b y n a żąd an ą funkcję pokazyw ał. O to ilustracja:
395
Rozdział. 9. Przeładowanie nazwy funkcji Adres funkcji przeładowanej i n t s p o s o b (d o u b le ); i n t sp o so b (c h a r) ; in t
(* w sk fu n )(c h a r);
-funkcja
//< -
// deklaracja wskaźnika mogącego jl pokazywać na powyżej II oznaczoną funkcję
w sk fu n = s p o s o b ;
~
Jak wiemy nazwa funkcjijesł też adresem początku tej funkcji , stąd też przy ostatniej instrukcji nie potrzeba operatora & (adres). zrobić jeśli funkcja jest (w ielokrotnie!) p rz e ła d o w a n a ? Ta sa m a n a z w a oznaw ó w c z a s ró żn e w ersje funkcji, a w ięc ró ż n e ad re sy .
kompilator będzie wiedział, o adres której wersji nam chodzi? K o m p ila to r p atrz y w ó w czas n a lew ą stro n ę p rz y p isa n ia : w n aszy m p rz y p a d k u stoi ta m w sk a ź n ik d o fu n k cji i to nie byle jakiej fu n k q i, ale takiej, k tó ra jest w y w o ły w a n a z a rg u m e n te m ty p u c h a r . W szy stk o jasne! K o m p ilato r, w śró d p rz e ła d o w a n y c h w ersji fu n k cji s p o s o b , o d sz u k a tę w ersję, k tó ra m a a rg u m e n t ty p u c h a r i ad res tejże fu n k cji p o d staw i d o w sk a ź n ik a . Jeśli k ie d y ś zd efin iu jem y w sk a źn ik in t
( * w s k 2 ) ( d o u b le ) ;
to in stru k c ja p rz y p isa n ia w sk2 = s p o so b ; (m e to d ą iden ty czn ej ded u k cji) sp raw i, że d o w sk a ź n ik a ws k2 w sta w io n y zo sta nie a d r e s w ersji i n t s p o s o b ( d o u b l e ) . S p ry tn e, p ra w d a ? Z a s a d a je st ciągle ta sam a: w p rz y p a d k u b ra n ia adresu funkcji p rzeład o w an ej - sp o śró d w szy stk ich funkcji o tej sam ej n azw ie (i m a się ro zu m ieć - tym sam y m zak resie w ażności) - d o o p eracji w y b ieran a jest ta w ersja funkcji, k tó ra d o k ła d n ie pasuje d o celu p rzy p isan ia. C elem , w p rz y p a d k u p rzy p isan ia, jest w y ra ż e n ie stojące p o lewej stro n ie zn ak u rów ności. To ono zd ecy d o w ało , a d re s której funkcji b ęd zie użyty.
Wysyłanie do funkcji adresu innej, przeładowanej funkcji M ogą je d n a k być in n e sytuacje. N a p rzy k ła d , jeśli chcem y d o jakiejś funkcji w y słać a d re s funkcji, k tó ra jest p rzeład o w an a. O to p rzy k ła d p ro g ram u : l i n c l u d e < io s tre a m > using namespace std;
_
t
II —deklaracje funkcji nazwy funkcji o przeładowanej nazwie - p r z e l a d v o id p r z e l a d ( i n t k) ; v o id p r z e l a d (d o u b le x ) ; II -----deklaracje zwykłych funkcji v o id p ie r w s z a ! v o id ( * a d r f u n ) ( i n t ) ) ; v o id d r u g a ( v o id ( * a d r f u n ) ( d o u b le ) ) ; Z *************************************************************/
396
Rozdz. 9. Przeładowanie nazwy funkcji Adres funkcji przeładowanej int m a i n ()
{
// O
pierwsza(przelad) ; cout << "---------druga(przelad);
-\n";
// ©
} z************************************************** void pierwsza ( void
(*adrfun) (int)
)
< cout «
"Jestem wewnątrz funkcji PIERWSZA\n" "teraz wywołam funkcje której adres przysłano" " jako argument\n"; adrfun(5); cout « "PO wywołaniu funkcji\n"; /*************************************************************V void d r uga( void (*adrfun)(double) )
{ cout << "Jestem wewnątrz funkcji DRUGA\n" "teraz wywołam funkcje której adres przysłano" " jako argument\n"; adrfun(3.14); cout « "PO wywołaniu funkcji\n"; /*************************»»**«*****nr****«********************y
void przelad (int k) cout << "*** Funkcja przelad - wersja: przelad(int) " argument k = " k << endl;
\n"
l*************************************************************^ void przelad (double x)
{
......... cout << "*** Funkcja przelad - wersja: przelad(double) " argument x = " « x << endl;
\n"
)
Po wykonaniu tego programu na ekranie pojawi się - .
.
.
r
.
ł
Jestem wewnątrz funkcji PIERWSZA teraz wywołam funkcje której adres przysłano jako argument *** Funkcja przelad - wersja: przelad(int) . argument k = 5 PO wywołaniu funkcji Jestem wewnątrz funkcji DRUGA teraz wywołam funkcje której adres przysłano jako argument * * * Funkcja przelad - wersja: przelad(double) argument x = 3.14 PO wywołaniu funkcji
Komentarz: W main w id zim y w yw ołania dw óch ró żn y ch funkcji - p i e r w s z a i drugć M ają one jednak ten sam arg u m en t b ęd ący w skaźnikiem d o funkcji. Ską kom pilator wie, o którą w ersję p rzeład o w an ej funkcji p r z e l a d w konkretnyn
397
Rozdział. 9. Przeładowanie nazwy funkcji Adres funkcji przeładowanej
p rz y p a d k u chodzi? (Czyli - ad res której funkcji p r z e l a d ma w y słać jako arg u m en t?) P rzy w y w o łan iu funkcji p i e r w s z a kom pilator s p ra w d z a jakiego w skaźnika do funkcji się p i e r w s z a sp o d ziew a. Z deklaracji, a tak że definicji © o rien tu je się, że sk o ro celem przy p isan ia jest w skaźnik do funkcji z argum entem ty p u i n t to zn aczy , iż m a w ysłać ad res funkcji: v o id p r z e l a d
( i n t k) ;
bo to przecież tak, jakby robić przypisanie: v o id
( * a d rfu n )(in t)
= p r z e la d ;
© P rzy w y w o łan iu funkcji d r u g a spraw dza, jaki jest cel przypisania w ysłanego ad resu funkcji. Celem jest w sk aźn ik do funkcji z arg u m en tem typu d o u b le . A ha, to znaczy , że należy w y słać adres tej wersji: v o id p r z e l a d
.7.1
(d o u b le x ) ;
Zwrot rezultatu będącego adresem funkcji przeładowanej C elem p rzy p isan ia m oże być nie tylko argum ent form aln y funkcji. M oże być też ty p w artości zw racanej p rzez funkcję. To znaczy w instrukcji r e t u r n staw iam y p rz e ła d o w a n ą nazw ę funkcji, a kom pilator decyduje, którą z wersji w ybrać. D ecyduje n a podstaw ie tego, jaki typ zad ek laro w an y jest jako typ zw racany p rz e z bieżącą funkcję. Jeśli funkcja m a na p rzy k ład zw rócić w skaźnik d o takiej funkcji, k tóra jest w y w o ły w a n a z argum entem ty p u i n t —to w y b ran a zostanie wersja v o id p r z e l a d ( i n t ) ; C hciałem o d razu napisać przy k ład o w ą funkcję, k tó ra to w łaśnie robi, ale boję się, że się przerazisz. Z róbm y to więc etapami. Funkcja b ęd zie się nazyw ać z w ro t. Po pierw sze ustalm y, jaka to m a być funkcja. To ustalenie pom oże n am w późniejszym pisaniu deklaracji funkcji. A zatem zw rot ma być funkcją w yw oływ aną bez żadnych arg u m en tó w , a zw racającą w skaźnik d o takiej funkcji, która: •
- w yw oływ ana jest z argum entem typu i n t ,
•
- zw raca ty p v o i d (czyli nic).
C zyli m a to być w skaźnik m ogący pokazać na choćby taką funkcję: v o id f ( i n t )
Korzystając z tych ustaleń zacznijmy budowanie deklaracji. Zatem: z w r o t jest funkcją w yw oływ aną bez żadnych argum entów ... zwrot(void) ■-‘i *X- J' ...a która zw raca wskaźnik...
398
Rozdz. 9. Przeładowanie nazwy funkcji Kulisy dopasowywania argum entów do funkcji przeładowanych ♦zwrot(void)
...do funkcji... (♦zwrot(void) ) (...)
... w yw oływ anej z argum entem typu i n t . . . (♦zwrot(void)) (int)
... i zw racającej ty p v o id . void (*zwrot(void)) (int);
Uff! Zrobione. N a pociechę pow iem , ż e takie funkcje będ ziesz pisał bardz< rzadko. A na p e w n o nie na początku. Skoro już m am y deklarację, to łatw o zap iszem y definicję funkcji - czyli zdefi niujem y ciało tej funkcji void (‘z w rot(void)) (int) cout << "zwracam wskaźnik do funkcji ! \n"; return przelad;
} Istota tego p rz y k ła d u polega na tym, że obok słow a r e t u r n sto i nazw a funkcjin azw a, która, jak n a m w iadom o, jest przeład o w an a. Jest w ięc kilka funkcji < n azw ie p r z e l a d . O tym, którą z w ersji w ybierze k o m p ilato r, decyduj) deklaracja funkcji. W deklaracji bow iem jest jasno po w ied zian e, że życzymy sobie, by zo stał zw ró co n y adres do funkcji, która jest w y w o ły w an a z jedynym arg u m en tem ty p u i n t . To, co sobie zażyczyliśm y otrzym ać, jest jakby celem przypisania. D o teg o celu kom pilator dobierze pasującą w ersję funkc p rz e la d . W niosek z tego p arag rafu jest taki: Przy operacjach zwracania przez funkcję F adresu funkcji przeładowanej P zwracany zostaje adres tej wersji funkcji przeładowanej, która pasuje do celu przypisania. Celem jest w tym przypadku zadeklarowany typ zwracany przez funkcję F
O p isan e p rz y p a d k i nie w yczerpują w szystkich m ożliw ych celów . Dalszyrr takim i sytuacjam i zajm iem y się później. (Dla w tajem niczonych: Innyr m o żliw ym celem m o że być także inicjalizow any obiekt, a tak że argum er form alny o peratora).
9.8
K u lis y d o p a s o w y w a n ia a rg u m e n tó w do fu n k c ji p rz e ła d o w a n y c h P ow tó rzę znow u: w iem y już, że nazw a fu nkcji m oże być p rz e ła d o w a n a , czyli ż m o że być kilka funkcji o tej nazw ie, byle ty lk o ró żn e w ersje tej funkcji różniły si
399
Rozdział. 9. Przeładowanie nazwy funkcji Kulisy dopasowywania argum entów do funkcji przeładowanych
lis tą a r g u m e n t ó w . K ie d y w y w o ł u je m y ta k ą f u n k c ję , k o m p i la t o r p a t r z y n a a r g u m e n t y w y w o ł a n i a f u n k c ji i - z a le ż n ie o d ic h ty p u - w y b ie r a tę je y n ą f u n k c ję , d o k tó r e j d o k ł a d n i e p a s u ją . T a k je s t w p ie r w s z y m p r z y b li ż e n i u . Z a s t a n ó w m y s ię je d n a k , co s ię z d a r z y , g d y a r g u m e n t y n ie b ę d ą ta k ie , ż e d o k ła d n ie p a s u ją d o je d n e j z w e rs ji. D a jm y n a to , ż e p a s u ją p r a w i e z u p e ł n ie , g d y b y n ie ja k iś d r o b n y s z c z e g ó ł. void void
f (double) ; // m a m y t a k i z e s t a w f(char); H a w y w o ł u j e m y ---------------------- ”
f (4 ) ;
// m im o że przecież
v o i d f (i n t )
n ie is tn ie je
C o w ta k ie j s y tu a c ji m a z r o b ić k o m p ila to r ? O to d w a w a r ia n t y te g o , c o k o m p ila to r s o b ie p o m y ś li: w a r i a n t 1): - C zy on oszalał? W yw ołuje m i fu n k cję w sposób, który n ie pasuje do żadnej z fu n k c ji o tej nazwie. Trudno! S yg n a lizu ję błąd kom pilacji, a on niech się w reszcie nauczy programować! w a r i a n t 2): - N o tak, co praw da, w yw ołanie nie pasuje do żadnej z zadeklarowanych fu n k c ji o tej nazw ie, ale z tego co w idzę, to najbliższe to je st takiej-a-takiej wersji. W szystkie inne wersje różnią się o wiele bardziej. Programista zrobił to dlatego, że nie umie jeszcze dobrze programować w C 4 + . M ozę jednak dlatego, że sądzi iż ja, kom pilator, jestem na tyle in te lig e n tn y , ze jednoznacznie d o m yślę się, o którą wersję m oże w tym lekko m e pasującym przypadku chodzić. . P o n i e w a ż k o m p i la t o r C + + r z e c z y w iś c ie m a a m b ic ję , d la te g o r o z u m u j e w e d ł u g w a r i a n t u 2. W n a s z y m p r z y p a d k u n ie z n a jd u ją c fu n k c ji: void
f (int) ;
z a m i e n i 4 n a 4.0 - i u r u c h o m i fu n k cję : void
f(double);
D a ls z a c z ę ś ć te g o r o z d z i a łu p o ś w ię c o n a je s t s z c z e g ó ło m r o z u m o w a n i a , n a p o d s t a w i e k tó r e g o k o m p i l a t o r u s ta la , k tó r a z w e r s ji p r z e ł a d o w a n e j f u n k q i p a s u je n a jb a r d z ie j d o a r g u m e n t ó w w y w o ła n ia . J e ś li n ie p o t r a f i te g o u s ta lić je d n o z n a c z n ie , b o n a p r z y k ł a d s ą d w a w a r ia n ty , k tó r e m o g ą b y ć j e d n a k o w o p r a w d o p o d o b n e , t o d o p ie r o w t e d y s y g n a liz u je b ł ą d . J e ś li z a u w a ż y , iż je d e n z w a r ia n tó w p a s u j e w y r a ź n ie e p ie j m z in n e , w ó w c z a s te n w ła ś n ie z o s ta n i e w y b r a n y . P r z y p i e r w s z y m c z y ta n iu te j k s ią ż k i r a d z ę C i w t y m m ie js c u p r z e r w a ć c z y ta n ie t e g o r o z d z i a ł u i p r z e s k o c z y ć d o n a s tę p n e g o . W y d a j e m i się b o w ie m , z e e p ie j je ś li n a j p i e r w b ę d z ie s z m i a ł o g ó ln y p o g lą d n a ję z y k C + + , a o p ie r o p o e m n a l e ż y z a c z ą ć s tu d io w a ć s z c z e g ó ły .
400
Rozdz. 9. Przeładowanie nazwy funkcji Etapy dopasowania N ie pytaj m n ie też dlaczego, w obec tego, nie um ieściłem tego rozdziału na sam y m ko ń cu książki. To dlatego, że chciałem, aby szczegóły dotyczące p rzeła d o w an ia n a z w funkcji były w jed n y m miejscu. Po to, że g d y b y ś chciał do tęga w rócić, to zn ajd zie sz w szystko w jednym miejscu. T ym czasem sk o k d o rozdziału następnego, mówiącego o klasach (str. 412).
9.9
E tapy d o p a s o w a n ia
Zanim zaczniemy tę opowieść - zastrzeżenie: Pam iętaj, że teraz ro zm aw iam y nie o tym co Ty, program ista, m asz zrobić, ale co kom pilator robi analizując Twój program . Zatem , o p isan e tu etapy to nie jest praca dla Ciebie, lecz dla kom pilatora. W zasad zie w ięc nie m usielibyśm y w ogóle się tym zajm ow ać, z drugiej strony lepiej w iedzieć, czego m ożem y oczekiw ać od kom pilatora w sytuacji, gdy lekkt
J
oszukujem y (czyligdy upraszczamy sobie życie nie dostarczając wszystkich możliwych wersji funkcji o przeładowanej nazwie).
Kiedy k o m p ilato r napotyka w yw ołanie przeładow anej funkcji - pracuje nac nim w kilku etapach. Jeśli w rezultacie znajdzie dokładnie jedną z wersji funkcj: która pasuje d o w yw ołania lepiej niż inne, w ówczas m o żn a pow iedzieć, ż< dopasow anie się udało. Jeśli znajdzie d w ie lub więcej funkcji, które jednakow o zb liżo n e są do tego, c< um ieściliśm y w w yw ołaniu funkcji, to kom pilator u z n a to za b łąd - nie możi być żadnej dw uznaczności.
A oto etapy poszukiwania właściwej funkcji Kom pilator, m ając przed sobą w y w o łan ie funkcji o p rzeład o w an ej nazw ie patrzy na m ożliw e realizacje tej funkcji i rozw aża kolejno: ♦♦♦ 1) dop aso w an ie dokładne, •
a) dopasow anie d o k ład n e z tryw ialną konw ersją,
♦♦♦ 2) dopasow anie na zasadzie aw ansow ania (z aw a n se m , z promocją), •> 3) dopasow anie z użyciem konw ersji stan d ard o w y ch , ♦> 4) dopasow anie z użyciem konw ersji w ym yślonych p rz e z program istę, ♦♦♦ 5) dopasow anie d o funkcji z w ielokropkiem . W yjaśnijmy teraz poszczególne etapy.
401
Rozdział. 9. Przeładowanie nazwy funkcji E tapy dopasowania
9.9.1
Etap 1. D opasow anie dokładne Inaczej m ów iąc k o m p ilato r sp raw d za czy a rg u m e n ty w yw ołania p a su ją dokła d n ie d o arg u m e n tó w form alnych jednej z w ersji p rzeład o w an ej funkcji. N a p rzy k ład : w w y w o łan iu funkcji m am y a rg u m e n t będący tablicą ty p u i n t i n t t a b l i c a [1 0 ]; fu n (ta b lic a ); P asu je to do k ład n ie d o takiej wersji funkcji f u n v o id f u n ( i n t t t t [ ]
);
Jeśli rzeczyw iście dostarczyliśm y taką funkcję, to d o p aso w an ie jest dokładne.
Etap la . D opasow anie dokładne, ale z tzw. trywialną konwersją
).9.2
P rzy k ład o w o : d o w y w o łan ia i n t ta b lic a [1 C ); fu n (ta b lic a ); nie zn alezio n o w etapie 1 funkcji v o id f u n ( i n t t t t [ ]
);
n a to m ia st jest funkcja v o id f u n ( i n t * w sk ta b ); W iem y przecież, że tablicę w ysłaną d o funkcji m ożna tam o d eb rać albo jako tablicę, albo jako w sk aźn ik do niej. To w łaśnie jest ta sytuacja. O d eb ran ie tablicy jako w sk a źn ik a jest tzw . tryw ialną konw ersją.
Oto zestawienie innych konwersji uznawanych za trywialne. T jest sym bolem jakiegoś typu. P rz y k ła d
K o n w e rs ja do
z T T& TH T(argum) T T T» T*
—t
T& T J* (*T)(argum) const T volatile T const T* volatile T*
i —r ił
. ii •-‘i ii i; _ji.
.: w yw ołanie fun]
jej deklaracja fun(int& a); fun(int b); fun(int* wskaźnik); fun((*wskfun)(int)); fun(const int n); fun(volatile int m); fun(const int * www); fun(volatile int * www);
402
Rozdz. 9. Przeładowanie nazwy funkcji Etapy dopasowania W ażny jest tu fakt, ż e obie sytuacje opisane jako etap 1) i eta p l.a) są dopasow a niem d o k ła d n y m . D o k ład n y m dlatego, że argum ent w yw ołania funkcji m o ż e bez żad n y c h p rz e ró b e k - zainicjalizow ać określony a rg u m e n t formalny. C o p raw d a, d o p a so w a n ie bez konw ersji trywialnej w ydaje się dokładniejsze niż takie, w k tó ry m ta try w ialn a konw ersja m usi nastąpić - czyli, że dopasow anie T[]
------ ►
T[]
jest w ydaje się lep sze niż T []
------ ►
T*
— nie m a to jed n ak ż ad n e g o znaczenia. Przecież (jak od n ied aw n a wiemy) takie d w ie funkcje void fun(int tt t [] ); void fun(int *wsktab) ;
p o prostu nie m ogą rów nocześnie w ystąpić w tym sam ym zakresie w ażności. K om pilator nie ma w ięc rozterki: "która z tych funkcji p asu je dokładniej .
W N astęp n e etap y to ju ż nie dop aso w an ie dokładne. Jeśli w ięc, m im o dotychcza sow ych pró b d o p aso w an ia, kom pilatorow i nie u d ało się d o p aso w ać żadnej z funkcji - m usi o n zacząć lekko zm ieniać ty p argum entów z w yw ołania funkcji. Lepiej tak, niż nie dopasow ać wcale.
9.9.3
Etap 2. D opasow anie z awansem (z promocją) Tak! A rg u m en ty w yw ołania aw ansują (są prom ow ane). Tu jest niestety problem językowy, bo słowo "promocja " ostatnio w Polsce używane jest najczęściej w znaczeniu "obniżka ceny" (często: towaru nieco gorszej jakości). Tymczasem nam chodzi o opisanie zmiany w stronę lepszej jakości. Słowa "awans" wydaje mi się więc trafniejsze — nrywołuje lepsze, jednoznaczni skojarzenia.
Uwaga dla formalistów: W dalszych opisach często będziem y m ów ić, że •
jakiś typ A kom pilator zam ienia (aw ansuje) na typ B.
U prszacza to w y p ow iedź, choć n a p ra w d ę pow inniśm y m ów ić, że •
r-wartość typu A ko m p ilato r zam ienia na r—wartość typu B.
Przecież tak n ap raw d ę kom pilator nie zam ienia ty p u obiektu (będącegc argum entem w yw ołania funkcji), ale bierze jego w arto ść i tak tę wartość zam ienia, żeby p>o tej zam ianie m ogła p asow ać d o jakiejś z d o starczo n y ch prze? nas funkcji. Uff! Koniec z tym form alizm em .
403
Rozdział. 9. Przeładowanie nazwy funkcji E tapy dopasowania
Droga awansu dla argumentów zmiennoprzecinkowych je st taka: float k o m p ila to r aw an su je n a ty p double flo a t
» d o u b le
Jeśli p o ta k im aw an sie k o m p ilato ro w i u d a się d o p a so w a ć je d n o z n a c z n ie jakąś fu n k cje — to d o p a so w a n ie się udało. Jak w id a ć , jest to aw a n s d o w iększej d o k ła d n o śc i liczb". T a k a jest zresztą idea aw ansu: nie pow oduje on utraty n aw et częsc inform acji
Dla argumentów całkowitych droga awansu jest taka: T y p y char, signed char, unsigned char, short int lu b unsigned short k o m p ilato r a w a n su je d o typu int, jeśli nie p o w o d u je to u traty inform acji. W p rz e c iw n y m razie aw an s je st d o ty p u unsigned int. Ilu stru je to p o n iż szy ry su n ek .
signed unsigned short unsighed short
char char char int int
a je ś li “stratne to:
unsi
int
Rys. 9-1 . Droga awansu dla w iększości typów całkow itych. Zauważ, że typ y lo n g nie są poddawane awansowi. Po prostu ich nie dałoby się awansować na nic innego bez ryzyka straty inform acji.
Typ bool kompilator awansuj© do typu int.
__ Rys. 9-2. D roga awansu dla typu (całkowitego) bool. Tu sprawa jest oczyw ista.
404
Typ
Rozdz. 9. Przeładowanie nazwy funkcji Etapy dopasowania
w_char
o ra z ty p y
enum...
s a m z o b a c z n a rysunku:
a jeśli “stratne to:
a jeśli “stratne to:
a jeśli “stratne to:
Rys. 9-4. Droga awansu dla typu wchar_t oraz dla definiowanych przez użytkownika typów wyliczeniowych. Jak widać, kompilator podejmuje wiele próby, by nie stracie informacji. To musi się udać.
A w a n s pól b ito w y c h
N ie m ó w iliśm y jeszcze o polach bitow ych (patrz, s tr 634), ale p rzecież zało ży łem , ż e całe to n u d n e z a g a d n ie n ie jest tylko d la w tajem niczonych. P ow iem w ięc, ż e aw a n s n astęp u je d o typu int, a jeśli się to w iąże z u tra tą inform acji, to d o ty p u unsigned int, a jeśli ito s p o w o d o w a ło b y u tratę - i w ów czas a w a n s n ie jest robiony.
p o la b ito w e a jeśli “stratne “ to:
a jeśli “stratne" to
bez awansu! Rys. 9-3. Pola bitowe, t o - ja k dowiemy się później - wygodna forma pracy z wybranym ; bitami w słowach. Nie zawsze musi się udać pokazany tu awans. Jeśli do tego by m ia ła dojść - kompilator poinformuje nas o tym. Nie ma problemu - wówczas sami się zajmiemy tym, by kompilator nie musiał takiej "newralgicznej" funkcji dopasowywać. P o ; prostu dostarczymy właściwą, dobrze pasującą.
P rz y k ła d d o p a s o w a n ia z a w a n s e m
M am y ta k ie d w ie funkcje void fff (int); void fff(float *);
W ów czas w y w o ła n ie
f f f:
405
Rozdział. 9. Przeładowanie nazwy funkcji E tapy dopasowania c h a r znak = ’ A '; fff(z n a k ) ro z w a ż a n e jest etap am i tak: E tap 1 - d o p a so w a n ie dokładne: n ierealn e. E tap la - d o k ła d n e z tryw ialna konw ersją: też nie w y ch o d zi. E tap 2 - aw an s. A rg u m e n t znak k o m p ilato r aw ansuje d o ty p u Po tym aw an sie w y w ołanie pasuje d o funkcji
int
void f f f ( i n t ) ;
:r-f tH;.
P ro b lem w ięc ro zw iązany.
9.9.4
Etap 3. Próba dopasow ania za pom ocą konw ersji standardowych K onw ersjom sta n d a rd o w y m p o d d a w a n y jest oczyw iście arg u m en t w y w o łan ia funkcji. Tu ju ż m o że n astąpić u tra ta części informacji, bo...
Do
k o n w e rsji
sta n d a rd o w y c h ,
o prócz
w sp o m n ia n y c h
w yżej a w a n só w
(które nie powodują utraty informacji), n a le ż ą ta k ż e :
K onw ersja ty p u całkow itego -> un s i g n e d i n t
int
int
unsigned int
♦♦♦ K onw ersja ty p ó w zm iennoprzecinkow ych
float
-» d o u b le
double
->
(to było w etapie 2)
float
K onw ersja m ięd zy ty p am i całkowitym , a zm ien n o p rzecin k o w y m
typ zmiennoprzecinkowy
--------- > typ całkowity (tu może nastąpić utrata informacji)
typ całkowity
--------- > typ zmiennoprzecinkowy (tu może nastąpić utrata dokładności)
K onw ersje ary tm ety czn e • czyli takie konw ersje jak te, które zw y k le robione są a u to m a ty cznie p rzy w ykonyw aniu w y rażeń arytm etycznych. Przykładow o: Jeśli trzeba pom nożyć dw ie liczby: jedną long, a d ru g ą short, to liczba short najpierw zam ieniana jest n a liczbę long, po czym dopiero na d w ó ch liczbach long w y k o n y w ane jest działanie. Takie konw ersje nigdy nie p o w o d u ją utraty jakiejkolw iek czę ści inform acji.
406
Rozdz. 9. Przeładowanie nazwy funkcji Etapy dopasowania ♦♦♦ K onw ersje w skaźników •
0 (zero) m oże być zam ienione na w skaźnik d o adresu zerow e go (dawniej: w skaźnik do NULI)
•
w skaźnik d o dow olnego n ie - c o n s t i n i e - v o l a t i l e typu m oże być zam ieniony na w skaźnik typu v o id *
(dalsze u w a g i dotyczą klas - ciągle zakładam , że czytasz książkę po raz drugi) •
w skaźnik d o klasy pochodnej m oże być zam ieniony na w sk a źnik do klasy podstaw ow ej, od której ow a klasa pochodna się w yw odzi.
♦♦♦ K onw ersja referencji •
referencja d o klasy pochodnej m oże być zam ieniona na refe rencję do klasy podstaw ow ej.
W trzecim etap ie dopasow ania arg u m e n t w yw ołania funkcji podd aw an y bę d zie p rz e z k o m p ilato r po w y ższy m konwersjom stan d ard o w y m . M ożesz w ięc uży ć w w y w o łan iu funkcji (przeładow anej) arg u m en t, którem u (dzięki tym konw ersjom ) k o m pilator znajdzie jakąś pasującą d o niego funkcję. Jednak ra d a jest taka:
Nie licz zbytnio na standardowe konwersje, bo może się zdarzyć, że przy konwersji część informacji zostanie stracona.
P rzykładow o:
void fff(int); void fff(char *); double pi = 3.14; fff(pi); Po d o p aso w an iu do funkcji v o i d f f f ( i n t ) w arto ść 3 .1 4 zostanie p rzez kom pilator zam ien io n a na 3 - Tego chciałeś?
9.9.5
Etap 4. Próba dopasowania z użyciem konwersji zdefiniow anych przez użytkownika. Jak p ew n ie ju ż w iesz - z pierw szeg o czytania tej k siążk i - w zd efin io w an e przez siebie klasie m ożesz um ieścić funkcję p o k azu jącą, jak zam ienić obiekl jakiegoś ty p u , na obiekt typu tejże klasy. (Patrz str. 760). Jeśli a rg u m e n t w yw ołania da się w ed łu g takiej k o n w ersji zam ienić na typ odp o w iad ający argum entow i fo rm aln em u , to d o p aso w an ie jest pom yślne. A le uw ażaj: dla a rg u m e n tu k o m pilator robi tylko jedną taką konw ersję.
407
Rozdział. 9. Przeładowanie nazwy funkcji E tapy dopasowania
Jest to ju ż rozpaczliw a próba. Jeśli do tej po ry k o m pilatorow i nie p o w io d ło się ż a d n e d o p aso w an ie, to szuka o n funkcji mającej w liście arg u m en tó w w ielokro pek. W ielokropek oznacza: d o w o ln a liczba a rg u m e n tó w d o w olnego typu. N p. void fun(int); void fun (double); void f u n (...); U
-------------- a w y w o ła n ie w y g lą d a
w ten sposób
f u n ("napis");
Ż a d n a z zad ek laro w an y ch nie daje się dopasow ać, bo arg u m en t w y w o łan ia to p rzecież w skaźnik. Jest jed n ak funkcja, która ma na liście arg u m e n tó w form al nych w ielokropek. D o p aso w an ie następuje do tejże funkcji. Jeśli na liście jest kilka arg u m e n tó w form alnych, a d o p iero potem w ielokropek, to pam iętajm y , że: I W ielokropek na liście argum entów form alnych funkcji obejm uje I argum enty od tej pozycji, na której stoi, o raz ew en tu aln e dalsze. N a tym zakończyliśm y o m aw ian ie etapów d o p asow ania.
9.9.7
W skaźników nie dopasowuje się inaczej niż d osłow n ie C zy zau w aży łeś, że w całej tej opow ieści m ów iliśm y o różnych typach - np. jak k o m p ilato r aw ansuje typ u nsigned char na ty p int, ale n ig d z ie nie by ło w z m ia n k i o tym, na co k o m p ila to r m oże zam ien ić w sk a ź n ik jakiegoś typu. O tóż s p ra w a jest prosta: NA NIC! •
1. N ie tylko, że na przykład w sk aźn ik char* nie m oże się zam ienić na i n t * .
•
2. N aw et w skaźnik T* nie m oże się zam ienić na tablicę T [ ] (choć o d w ro tn ie to możliwe!)
P o d o b n ie ze w skaźnikam i d o funkcji. •
3. Jeśli arg u m en tem w yw ołania jest w skaźnik do jakiejś fu n k cji, to k o m pilator dopasuje tylko funkcję, która m a do k ład n ie taki sam typ w skaźnika. Ż adnych aw ansów , konw ersji.
Z resztą - jak się chwilę zastan o w ić - w szystko to staje się rozsądne.
408
Rozdz. 9. Przeładowanie nazwy funkcji Dopasowywanie wywołań z kilkoma argum entam i Te p o n iższe w yjaśnienia są tylko dla dociekliwych: a d 1) Jeśli jakiś w skazyw any właśnie adres w pam ięci ma być inter p reto w an y jako zak o d o w an y znak (char), to nie ma sensu inter pretacja tak w skazyw anego obszaru jako obiektu typu int. Tc przecież oznaczałoby, że zaczynam y ro zkodow yw ać bity, które tylko p rzy p ad k o w o sąsiadują z danym zn ak iem char. a d 2) Z kolei w skaźnik typu T* nie ma pojęcia czy pokazuje na jeden "wolnostojący" obiekt, czy też może na tablicę obiektów. Zatem próba zam iany w skaźnika T * na tablicę T [ ] nie jest dopuszczalna, bo tablica pow inna w iedzieć jaka jest d u ża. (M ożna zrobić operację s i ze o f na nazw ie tablicy i otrzym ać w ten sposób informację jaka o n a jest duża). W skaźnik takiej inform acji nie niesie, w ięc k om pi lator nie dokona takiej konwersji. a d 3) A dres funkcji w yw oływ anej z arg u m en tem typu char nie jest rodzajem adresu funkcji w yw oływ anej z arg u m en tem typu int. W zięcie adresu jednej funkcji i u d aw an ie, że jest o ad res innego ty p u funkcji jest ju ż tak karkołom ne, że k o m p ilato r tego d o brow ol n ie nie zrobi. (Tutaj n aw et typ rezultatu tak p okazyw anej funkcji m u si się d o k ładnie zgadzać).
9 .1 0
D o p a s o w y w a n ie w y w o ła ń z kilkom a a rg u m e n ta m i Jeśli w w y w o łan iu funkcji jest kilka argum entów , to w ó w czas pro ced u ra d o p a sow ania a rg u m e n tó w odbyw a się na k ażd y m z nich. Z e w szy stk ich w ersji funkcji k o m pilator w ybiera tę w ersję, d o której p aram etry pasują tak sa m o lub lepiej, niż d o innych wersji. Co to o zn acza ? W yobraźm y sobie, że m a m y w yw ołanie d w u arg u m en to w e f u n (2, 5) ;
a funkcje, s p o śró d których będzie k o m pilator w ybierał to: funfdouble, unsigned int); fun(double, double); fun(int *, double);
O statnia w ersja - ta ze w skaźnikiem od razu o d p a d a . N ie da się przecież konw ersjam i stan d ard o w y m i zam ien ić liczby 2 na w sk a ź n ik int*. Zostają z a te m tylko d w ie funkcje. P ierw szy a rg u m e n t w y w o łan ia, czyli 2, pasuje tak sa m o źle do obu wersji, nc ale w końcu p o s ta n d a rd o w e j konw ersji m ożn a w y trzy m ać. N ato m iast d r u g i a rg u m e n t w y raźn ie lepiej pasuje d o w ersji fun(double,
unsigned int);
W yraźnie lep iej - dlatego, że w y starczy jego aw an s d o ty p u uns i g n e d int. A w an s (etap 2), jak w iem y, jest dla kom p ilato ra le p sz y niż ew en tu aln a
409
Rozdział. 9. Przeładowanie nazwy funkcji Ćwiczenia
konw ersja sta n d a rd o w a (eta p 3) podobna d o tej, jaką kom pilator zrobił z a rg u m e n tem pierw szym . Z atem - skoro p ierw szy a rg u m e n t paso w ał tak sam o (tak sam o źle) od obu w ersji funkcji, natom iast d ru g i arg u m en t p aso w ał lepiej d o w ersji: fun(double, unsigned int);
- to ta w ersja zostanie p rz e z kom pilator w ybrana.
W 9.11
Ć w ic z e n ia Kiedy mówimy o przeładowaniu? Czy potrafiłbyś wyjaśnić o przeładowanie czego czym tutaj chodzi? Wybierz poprawne dokończenia poniższego zdania: Nazwa funkcji jest przeładowana, gdy istnieją dwie lub więcej definicji funkcji o tej samej nazwie... a) w różnych zakresach ważności, b) w tym samym zakresie ważności, c) zakres tych nazw funkcji nie ma znaczenia, d ) funkcje te mają identyczną listę (typ i kolejność) argumentów, e) funkcje te mają różną listę (typ i kolejność) argumentów, f) lista argumentów tych funkcji nie ma znaczenia, funkcje te mają ter sam typ rezultatu, h) funkcje te mają różny typ rezultatu, i) typ rezultatu tych funkcji nie ma znaczenia. Poniżej widzisz fragment kodu, gdzie w skrótowy sposób umieszczone są definicje 4 g)
Które z nich nie mogą znaleźć się w tym samym zakresie ważności? (Bo wywołają błąd kompilacji). typedef int cal; enum akcja { stop, rusz, lewo, prawo } ; enum dni l niedziela, poniedziałek, wtorek, środa, czwartek, piątek, sobota}; a) b) c) d)
void void void void
funk (int liczba) { } funk (akcja a) { l funk (dni a) { } funk (cal a) { }
W powyższym ćwiczeniu widzieliśmy definicje funkcji. Jak by zareagował kompilator, gdyby były to nie definicje, ale deklaracje? W programie znajdują się definiqe kilku poniższych funkcji. Które z nich mogą spowodować problemy i w jakiej sytuacji taki błąd wystąpi? int deutz(int a) ( return 1;) double deutz (double d) (return 1.0;}
410
Rozdz. 9. Przeładowanie nazwy funkcji Ćwiczenia double deutz(char znak, int k = 6) double deutz(int k, char znak = 'x'
{return 1.0;} {return 1.0;}
Załóżmy, że - na etapie linkowania - łączysz swój plik, napisany w C++, z innym plikiem, napisanym w języku C. Z pliku C++ chcesz wy wołać funkcję napisaną w języku C. Ma to być funkcja o nazwie rozruch wywoływana z dwoma argumentami - jeden typu int, drugi typu const char*, a zwracająca typ int. Aby takie wywołanie w pliku C++ było w ogóle możliwe, musi tam znaleźć się deklaracja tej funkcji. Napisz deklarację tej funkcji w postaci, w jaką ma mieć w pliku C++. Deklaracje wszystkich funkcji z pliku napisanym w języku C są zebrane w odpowiednim pliku nagłówkowym o nazwie "dekl.h". Napisz jak można z tego pliku skorzystać w trakcie pisania pliku w języku C++. Która z wypowiedzi a), b), czy c) jest prawdziwa? Jeśli w programie, w którym nazwa funkcji fun jest już przeładowana, otwieramy lokalny zakres, a w nim dodatkowo deklarujemy funkcję o tej samej nazwie to: a) funkcja ta dołącza do zestawu funkcji przeładowujących tę nazwę b) funkcja ta dołącza do zestawu funkcji przeładowujących tę nazwę (pod warunkiem, że takiej funkcji jeszcze nie ma, jeśli jest - następuje błąd kompilacji), c) deklaracja ta unieważnia w tym zakresie przeładowanie tej nazwy fun - dokonu jąc zasłonięcia jej. Spośród poniższego zestawu funkcji przeładowujących nazwę F, wskaż te, które mogą być ze sobą w konflikcie a) b) c) d) e) f) r) g)
void F(char tab[]){ return ;} double F(int t []) { return 1;}; char F(char *wsk) { return 1;}; char F(char tablica[4][5]){ return 1;}; char Fichar (‘tablica) [5)) < return 1;}; char Fichar tablica [6] [5] ) { return 1;}; char Fichar tablica[1 ] [4] [5]) { return l;ł;
Spośród poniższego zestawu funkcji przeładowujących nazwę G, wskaż te, które mogą być ze sobą w konflikcie n) b) c)
void Glint m) ; void G(int *n); void Gjint &r) ;
Spośród poniższego zestawu funkcji przeładowujących nazwę rysuj wskaż te, które mogą być ze sobą w konflikcie a) void b) void c) void d ) void c) void f ) void
rysuj rysuj rysuj rysuj rysuj rysuj
(int m ) ; (int *m) ; (const int n); (const int *n); (volatile int k);
To ćwiczenie jest tylko dla ochotników, a... ...jeśli jesteś wykładowcą C++ obiecaj mi, że nigdy studentom na egzam inie / kolokwium tego nie zadasz. Jeśli zadasz, to studenci znienawidzą m nie, Ciebie, a przede wszystkim C++.
Rozdział. 9. Przeładowanie nazwy funkcji Ćwiczenia
411
Ale serio: Nie powinieneś myśleć, że C++ polega na sztuce pisania deklaracji. To jest zupełny margines. Trzeba to wiedzieć, ale trzeba też wiedzieć, że nie to jest naprawdę ważne. Po tych wszystkich zastrzeżeniach: koledzy ochotnicy od deklaracji "ekstremalnych" * oto wyzwanie dla was: Napisz deklarację funkcji o nazwie fun, która przyjmu;e dwa argumenty. — Jeden z nich jest wskaźnikiem do funkq'i wywoływanych z argumentem typu wskaźnik do char, a zwracających wskaźnik do d ou b le. — Drugi argument jest wskaźnikiem do funkcji wywoływanych z dwoma argumentami typu in t i typu double, a zwracających wartość typu double. — Owa funkcja fun zwraca wskaźnik do funkcji wywoływanych z argumentem typu char* i zwracających rezultat typu char*
Dalsze ćwiczenia przeznaczone są dla tych, którzy przeczytali paragrafy na temat etapów dopasowania. Wymień przynajmniej pierwsze trzy etapy dopasowania. Czy konwersje standardowe mogą doprowadzić w rezultacie do utraty części informacji? Czy tzw. awans może doprowadzić w rezultacie do utraty części informacji? Czy wskaźnik do funkcji v o id fun (char) może zostać zamieniony na wskaźnik do funkcji v o id fun (in t) ?
412
Rozdz. 10. Klasy Typy definiowane przez użytkownika
10
Klasy ak n ap raw d ę, to w szystko, co pisałem do tej p o ry - pisałem z n adzieją, że w reszcie d otrę do tego rozdziału. Tu bow iem zaczniem y m ów ić o chyba n ajw sp an ialszej rzeczy w C++ - czyli o definiow aniu w łasnych typów d an y ch.
T 10.1
T y p y d e fin io w a n e p rze z u żytk o w n ika C zy n ig d y n ie irytow ało Cię, że g d y m asz napisać p ro g ram , to m usisz swój problem , k tó ry dotyczy jakichś realnych obiektów (silników krokow ych, pokoi hotelow ych itd.) zam ienić na lu źn e liczby i luźne p o d p ro g ra m y (funkcje)? R o zw iązy w an ie Twojego pro b lem u polega w ted y n a żonglow aniu liczbam i, które w p ro g ram ie w cale nie są p o w iązan e ze sobą, a n i też nie są p o w iąza n e z funkcjam i. To tylko m y w iem y, że dotyczą one tego sam eg o silnika krokow ego, tej sam ej p ralk i autom atycznej, czy tego sam ego ra c h u n k u bankow ego. Liczba typu d o u b l e reprezentująca tem p eratu rę p o w ierzch n i prom u kosm icznego m oże być p rzez ro ztarg n ien ie program isty w y słan a d o funkcji oczekującej liczby ty p u d o u b l e reprezentującej w ydajność w k w in talach na hektar. K om p ilato r n ie zau w aży takiego b łę d u : m iała być liczb a d o u b l e i jest d o u b a w ięc, o co chodzi? Tyle, że na sk u tek tego, w y n ik b ę d z ie b zdurny.
1 e -
W C++ d an e m ogą zostać p o w iąza n e z funkcjam i - zn a c z y to, że k o m p ilato r nie dop u ści d o tego, by do funkcji oczekującej a rg u m e n tu typu „ te m p e ra tu ra " w ysłać a rg u m e n t typu „ s ta n o s z c z ę d n o ś c i" . Ż eby tak było, m usim y n ajpierw zdefiniow ać sobie ta k i typ. C ++ pozw ala n am na zd efin io w an ie w łasn eg o typu danej. Słowem , oprócz ty p ó w d o u b l e , i n t , c h a r itd., m am y jeszcze n asz w łasny typ - w ym yślony na u ż y te k d an eg o p ro g ram u . Ten
Rozdział. 10. Klasy Typy definiowane przez użytkownika
413
Jaka z tego korzyść ? T ak z d e fin io w a n y ty p m o że b y ć „ m o d e le m " jak ieg o ś rzeczy w isteg o o b iektu. R zec zy w isty o b ie k t —np. p ra lk ę a u to m aty c zn ą —m o ż n a w k o m p u terze opisać z e sp o łe m liczb ❖
i zachow ań. D la p ra lk i a u to m a ty c z n e j te lic z b y to na p rz y k ła d jej cen a, rok p ro d u k cji, w y m iary , k o lo r, czy w y d ajn o ść w y ra ż o n a w k ilo g ram ac h bielizny, k tó rą p ralk a m o ż e p ra c za je d n y m raze m . To tak że liczby rep reze n tu ją ce jej stan w e w n ę trz n y , o p isan y p rz e z p ro g ra m p ran ia, na k tó ry ją w łaśn ie nastaw iliśm y , f 7y też liczb a o pisująca b ieżący etap p ran ia. S ło w em , sk ład n ik iem tak ieg o o b ie k tu je st z b ió r ró żn y ch liczb. Z kolei za ch o w a n ia p ralk i au to m aty czn ej, to zbiór fun k cji, k tó re m oże d la nas w y k o n ać. M o ż e to być p ra n ie , p łu k a n ie , w iro w a n ie itd. Te funkcje są także
a u to m a ty c z n ą - zb ieram y ra z e m i b u d u jem y z nich p e w n ą całość. P o w staje w k o m p u te rz e n o w y ty p danej: p ra lk a au to m aty czn a. M ó w im y ty p , d la teg o że k re u je m y nie jeden k o n k re tn y obiekt, ale raczej k la sę o b iek tó w . C zy li na razie raczej w y m y śliliśm y p ra lk ę au to m aty czn ą - jeszcze ż a d e n k o n k re tn y eg zem p larz tak iej pralk i (obiekt) n ie istnieje. | K lasa - to inaczej m ów iąc: typ. T ak jak ty p e m jest int, d o u b l e , ...
Przyjrzyjmy się teraz, jak się klasę definiuje N ie jest to tru d n e . Definicja k lasy sk ład a się ze sło w a klu czo w eg o c l a s s , po k tó ry m s ta w ia m y w y b ran ą p rz e z n as nazw ę. class
nasz_typ
/ / .............. ciało klasy... //................................
1; P otem n a s tę p u je klam ra, a w niej ciało klasy - czyli o k reślen ie z czego się sk ład a. Z a u w a ż , ż e p o zam ykającej k la m rz e staw ia się śred n ik . P oczątkow o b ęd ziesz o n im często zap o m n iał. Jeśli w p rz y szło ści zechcem y stw o rzy ć eg zem p larz ob iek tu takiej klasy, to w y sta rc z y w ó w c zas p o d ać n a z w ę typu i nazw ę te g o k o nkretnego n o w eg o obiektu. Id e n ty c zn ie jak w p rz y p a d k u typu w b u d o w a n e g o i n t . A by mieć e g z e m p la rz o b iek tu typu int o n azw ie suma piszem y: int suma;
414
Rozdz. 10. Klasy Składniki klasy P rzy ty p ie definiow anym p rzez użytkow nika - spraw a w ygląda analogicznie. O to u tw o rzen ie obiektu a b c , który jest klasy n a s z _ t y p : nasz_typ abc;
D zięki tem u zapisow i w pam ięci maszyny u tw o rzo n y zostaje jeden obiekt klasy n a s z _ t y p i tem u egzem plarzow i nadana jest n azw a ab c. Zapis nasz_typ m;
sp o w o d u je u tw orzenie w pamięci drugiego egzem plarza obiektu klasy n a s z _ t y p . M a on nazw ę m. To tak, jakbyśm y na podstaw ie tych sam ych p la n ó w konstrukcyjnych zbudow ali drugą p ra lk ę autom atyczną. Te "plany k o n stru k c y jn e " to oczyw iście definicja klasy. Skoro m am y typ, to m ożna utw orzyć od niego ty p złożony - czyli na przykład w sk aźn ik do obiektów tego typu: nasz_typ * wsk;
czy też na przykład referencję (przezwisko) obiektu takiego typu nasz_typ obiekcik; nasz_typ & przezw = obiekcik; wmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm m m 'w
10.2
« » » ii M O T iiim n » t h m i w
w
iw . i g w ir i u w r r n i n— w m in n ;
ft^ * * * * *
S k ła d n ik i klasy W definicji naszej klasy w id zim y na razie puste m iejsce zw ane ciałem klasy. W tym w łaśn ie miejscu d eklaruje się s k ła d n ik i k la sy .
Dane składowe S kładnikam i m ogą być ró żn eg o typu dane (n p . i n t , d o u b le , strin g i itd). N azy w am y je danymi składowymi tej klasy. Będę też na nie czasem mówił: s k ł a d n i k i - d a n e . Oto definiqa klasy, w której jest kilka danych składowych: class pralka { public: int nr_programu; double temperatura_prania; char nazwa[8 0 ];
}; (Bardzo proszę nie pytaj m nie jeszcze co znaczy to słow o p u b l i c . P o w iem y o tym d o p iero za chwilę). A by odnieść się do sk ład n ik ó w obiektu m ożem y się posługiw ać jed n ą z po n iż szych notaq'i: •
obiekt.składnik
•
wskaźnik - > składnik
•
referencja.składnik
415
Rozdział. 10. Klasy Składniki klas
Jak w id a ć w y stę p u je tu o p e ra to r (kropka) - o p e ra to r odn iesien ia się do sk ład n ik a o b ie k tu zn an e g o z n a z w y lub referencji. Jeśli m a m y obiekt p o k a z y w an y w sk a ź n ik ie m , to d o o d n ie sien ia się d o s k ła d n ik a tak ieg o obiektu słu ży nam o p e ra to r -> Jeśli m a m y o b ie k t
/ / definicja egzemplarza obiektu / / definicja wskaźnika do pralek / / definicja referencji (przezwiska pralki)
p r a l k a c z e rw o n a ; p r a l k a * w sk aż; p r a l k a & ru d a = c z e rw o n a ; to d o s k ła d n ik a t e m p e r a t u r a od n ieść się ta k (na trzy sposoby):
p r a n l a w o b iek cie c z e r w o n a m o ż em y
c z e r w o n a . t e m p e r a t u r a p r a n i a = 60;
/ / nazwą obiektu
w skaż = & c z e rw o n a ; w skaż -> te m p e r a t u r a _ p r a n i a = 60;
, /i wskaźnikiem
ru d a . t e m p e r a t u r a _ p r a n i a = 60;
// referencją
Funkcje składowe S k ład n ik am i k la sy m ogą być też — u w a g a -u w a g a ! — funkcje. Funkcje te n azy w ać b ę d z ie m y funkcjam i skła d o w ym i. Z a ich p o m o c ą p racujem y zw y k le na d a n y c h sk ład o w y ch . O to k la sa p r a l k a w y p o sa ż o n a w funkcje: c la s s p ra lk a { p u b lic : v o id v o id
IIp i e r z ( i n t p ro g ra m ); w i r u j ( i n t m in u ty ) ;
in t d o u b le char in t
funkcje składowe
/ / -------- dane składowe n r_ p ro g ra m u ; te m p e ra tu ra _ p ra n ia ; n a z w a [8 0 ];
/ / -------- znowu jakaś funkcja składowa k ro c h m a le n ie (v o id );
}; W definicji tej w id zisz d ek laracje funkcji p o m ieszan e z deklaracjam i d an y ch . N ieza leżn ie o d m iejsca zd efin io w an ia sk ład n ik a w e w n ą trz klasy - sk ła d n ik zn an y je st w całej definicji klasy. M ów im y, ż e n a z w y d e k la ro w a n e w klasie m ają z a k re s w ażności — ró w n y o b szaro w i całej klasy. Inaczej n iż to by ło w zw ykłych funkcjach. P rzy w y k liśm y , że jeśli w p o ło w ie funkcji z d e fin io w a liśm y daną, to była ona z n a n a o d miejsca definicji a ż d o końca funkcji. W linijkach p o w y żej zn an a nie była. P rzy k ład : v o id f u n k c j a () 1 in t a ,b ,c ;
416
Rozdz. 10. Klasy Składnik będący obiektem c - 15; a = 4 + c; i n t nnn;
/ / tu jeszcze nazwa nnn nie jest znana— / / < ----- moment definicji obiektu nnn / / t u już nnn jest znane-------------------
nnn = a + 6; // . . . ) D ana składow a m oże być zdefiniow ana n aw et w ostatniej linijce ciała klasy, a i tak jest znana w całości klasy. Po prostu klasa w przeciw ieństw ie do funkcji nie m a początku i końca. T o jakby pudło na składniki. P o tem zobaczym y, że składnikam i klasy m oże być jeszcze w iele innych cieka w ych rzeczy.
10.3
S k ła d n ik będący obiektem S kładnikiem klasy m o ż e być dan a typu w b u d o w an eg o (np. obiekt typu i n t , c h a r [ 20 ] , d o u b le * ) , a może być też obiekt typu zdefiniow anego p rzez uży tkow nika. Innym i słow y: obiekt jakiejś innej klasy. P oczątkow o m oże się to w ydać zawile, w ięc p o słu żm y się analogią: obiekt klasy la m p a , który stoi p rz e d e mną na biurku m a składniki - liczby - takie jak w ysokość, ciężar, ale też jego składnikiem jest inny obiekt: żaró w k a, czy też ob iek t klasy abażur. K ażd y z nich sam jest obiektem jakiejś klasy. O czywiście m ógłbym udać, że o ty m nie wiem i w p isyw ać w szystkie sk ład n ik i obiektu klasy żarów ka (moc, w ielkość bańki) w obrębie definicji klasy lam pa. Tylko, co bym p rzez to zyskał? Nic. A co bym stracił? Straciłbym klasę żaró w k a, która m o że m i się p rzydać w jeszcze innych m iejscach program u. W łaściw ie na tym poleg a jedna z tajem nic program ow ania obiektow ego: aby um iejętnie używ ać klas ju ż kiedyś zdefiniow anych. W niosek jest taki: opłaca się po prostu budow ać klasy składające się z innych obiektów. B udując dom opłaca się skorzystać z gotow ego obiektu cegła, a nie b u d o w ać d o m w ypalając jednocześnie nieopodal cegłę. ""
10.4
mu
■!■» »IM M W —
w
«"K n—
m iM inirMWOT
E n k a p s u la c ja Jak w idzisz, w ciele klasy są dane i funkq'e. Jest to b ard zo w ażny fakt, bow iem w tej definicji, jak w k ap su le, zam knęliśm y d a n e o raz funkcje do p o słu g iw an ia się nim i. P o angielsku takie zam knięcie w k a p su łę n azy w a się: encapsulation. Jeśli cho d zi o zgrabne i p o p ra w n e przetłu m aczenie tego term inu na p o lsk i - p o wielu przem yśleniach p o d d aję się i w ybieram w a ria n t najprostszy: enkapsulacja.
417
Rozdział. 10. Klasy E nkapsulacja
Dlaczego enkapsulacja jest taką w ażną cechą języka C++? Dlatego, że odzwier ciedla nasze codzienne myślenie o obiektach. Na przykład obiekt z e g a r e k to nie tylko trybiki, kółka i wskazówki. To także sposób postępowania z nim i zachowania, które tym składnikom towarzyszą. Ten sposób postępowania jest charakterystyczny dla zegarka. Powiedzenie „nastaw!" w stosunku do zegarka to jest przecież jakaś akqa - charakterysty czna dla zegarka! Powiedzenie „nastaw!" w stosunku do obiektu klasy czajnik spowoduje, ż e uruchomimy akcję, która w naszym mózgu jest składnikiem klasy czajnik. Zauważ: zupełnie innej akcji. Zaraz, zaraz - to chyba już znamy! Czyżby przeładowanie nazwy funkcji? Nie. N ie ma tu m ow y o żadnym przeładowaniu nazw funkcji. Jeśli mówimy: wykonaj na obiekcie klasy zegarek funkcję n a sta w , to kompilator nie próbuje niczego dopasowywać. Sięga na ślepo do klasy zegarek, a tam jest właściwa funkcja n a sta w . To było zdroworozsądkowe tłumaczenie. Można to jednak bardzo krótko w y tłumaczyć na podstawie definicji przeładowania: Przeładowanie następuje wtedy, gdy funkcje o tej samej nazw ie mają identyczny zakres ważności. Jeżeli zaś mają inny zakres ważności, to nie następuje przeładowanie tylko zasłonię cie. Funkcje składowe klasy mają zakres klasy - przecież ich deklaracje tkwią wewnątrz nawiasu klamrowego, który wyznacza lokalny zakres ważności. Mówimy: zakres ważności klasy. Zakresem ważności jednej funkcji n a sta w jest klasa czajnik. Zakresem innej funkcji n a s ta w jest klasa zegarek. Te funkcje mogą się, co najwyżej, nawzajem zasłaniać. Czasem widać jedną, czasem drugą. Wróćmy jednak do istoty rzeczy. Definicja klasy sprawiła, że dane i funkcje, które dawniej mielibyśmy luźnie rozrzucone w programie - teraz zamknięte są w kapsule. Dzięki temu, w momencie definicji pojedynczego egzemplarza obie ktu takiej klasy — dostajemy realizację takiej kapsuły. p ra lk a b i a ł a ;
/ / definicja egzemplarza obiektu klasy pralka
To tak, jak kupujemy pralkę w obudow ie, a nie luźne części. Jeśli chcemy mieć drugi obiekt takiej pralki, to definiujemy: p r a l k a c z e rw o n a ;
// inny obiekt klasy pralka
To ogromna w ygoda. W tradycyjnym programowaniu, jeśli nawet zdefiniowa libyśmy te w szystkie luźne składniki klasy pralka, to przy drugim egzemplarzu musielibyśmy zrobić to samo jeszcze raz. I)
Natomiast nic nie przeszkadza, by w obrębie danej klasy dana funkcja była przeładowana. Zakres tych wersji funkcji jest wtedy ten sam. Może być przecież 15 sposobów nastawiania zegarka.
418
Rozdz. 10. Klasy Ukrywanie inform acji
Kapsułą się łatwiej posługiwać niż rozsypanymi elementami. Jeśli Cię to jeszcze nie przekonuje, to pomyśl dlaczego transport prze stawił się na przewożenie obiektów klasy kontener.
10.5
U k ry w a n ie in fo rm a c ji Skoro, jak powiedzieliśm y, składniki klasy zamknięte są w kapsule - to ta kapsuła może być przeźroczysta lub nie. Coś może być dostępne spoza klasy lub nie. Oto przykład klasy: class nasz_typ { private: int liczba; double temperatura; char komunikat[80] ; int czy gotowe ();
/ / składniki prywatne *************........... *
/ / <— prywatne dane składowe
/ / <— prywatna funkcja składowa / / składniki publiczne *******........ ****......
public: double prędkość; int zrob_pomiar()
/ / <— publiczna dana składowa U <— publiczna funkcja składowa
};
W ciele tej klasy widzimy wyraźnie dw ie grupy - występujące po etykietach p r i v a t e ipublic.
Etykieta private oznacza, że deklarowane za nią składniki (funkcje i dane) są dostępne tylko z wnętrza klasy. W przypadku danych składowych oznacza to, że tylko funkcje będące składnikami klasy, mogą te prywatne dane odczytywać lub do nich zapisywać. W przypadku funkcji oznacza to, że mogą one zostać w yw ołane tylko przez inne funkcje składowe tej klasyk.
Etykieta public Dalej w definicji klasy n a sz ty p widzim y grupę składników pod wspólną etykietą p u b lic . Publiczne składniki-dane mogą być używ ane z wnętrza klasy, a także spoza zakresu klasy. Analogicznie: publiczne funkcje składowe mogą być wywoływane dodatkowo także spoza klasy. Publiczną funkcją składową obiektów klasy pralka jest na przykład funkcja p r a n ie _ k o s z u l, to dlatego, że ja, nie będąc składnikiem klasy pralka, mogę tę 2)
Nie wspominam tu na razie o tzw. funkcjach zaprzyjaźnionych
419
Rozdział. 10. Klasy Ukrywanie inform acji
funkcję w y w o łać. P ry w atn ą funkcją jest na p rz y k ła d jakaś funkcja o b r o c _ b e b e n _ p r a l k i _ w _ l e w o . Tego nie m ogę b e z p o śre d n io w yw ołać. P o m y ślisz p ew n ie tak: sk o ro sk ład n ik i p ry w a tn e są up o śled zo n e, b o nie m a d o nich d o s tę p u z zew n ątrz, to d la c z e g o nie o p atrzy ć w sz y stk ieg o etykietą p u b l i c . A m oże n ie m o ż n a? M ożna! W C + + m o żn a p ra w ie w szystko. Tylko zo b acz, jakie b ęd ą tego konsekw encje. W yobraź sobie, ż e w y tw arzasz klasę o b ie k tó w pod ty tu łem „telew izo r". C h o d z ą one św ietnie. M ają w sobie ok. 50 elem en tó w , które m ożna śru b o k rętem stroić. D ajesz teraz o b ie k t takiej klasy u ży tk o w n ik o w i. Co dostaje? T elew izor b ez o b u d o w y . W szystkie 50 miejsc zag ro żo n e je st jego ochoczą akcją śru b o k rętem . N aw et, jeśli d o starcz an e p rzez Ciebie te lew izo ry są św ietne, to g dy k toś w e ź m ie do ręki śru b o k rę t i zaczn ie kręcić - ro zstro i to tak, że telew izor będzie d ziałał źle. To zepsuje C i opinię. Z kolei in n y u ży tk o w n ik , g d y b ęd zie chciał zw iększyć w telew izo rze jasność, a zobaczy 50 p o k ręteł, to po prostu pow ie: to za tru d n e, lepiej ju ż pójdę d o kina! Jaka jest sy tu acja optym alna? Z ao p a trzy ć obiekt w o b u d o w ę broniącą do stęp u do tych 50 m iejsc, a na z ew n ątrz o b u d o w y w ystaw ić do p u b liczn eg o u ży tk u ' tylko p o k rętła spełniające funkcje: zw ięk sz jasność, p rzełącz kanał. Tak n ależy n au cz y ć się ro zu m o w ać w p rz y p a d k u w y m y ślan ia klasy. My w ym y ślam y klasę, a ktoś inny b ęd z ie m usiał za pom ocą o b iek tó w tej klasy p ro g ram o w ać. T aka jest sytuacja p rz y p ro g ram o w an iu w zesp o łach . Jeśli n aw et jesteś sam o d zieln y m p ro g ram istą am ato rem i to Ty sam b ęd ziesz klasę definio w ał, oraz ty lk o Ty sam będ ziesz z niej korzystał - i tak w szystko to, co p o w ied ziałem , n ad al pozostaje w ażn e. Ktoś, kto am ato rsk o z b u d o w a ł sobie na stole zasilacz, w k ła d a na końcu te w szy stk ie z lu to w an e d r u ty i tranzystory do p u d ełk a p o b u tach . Po to, by - g d y za chw ilę tego zasilacza b ę d z ie u ży w ał przy zasilaniu k o lejki elektrycznej - nie zro b ić p rzy p ad k o w eg o zw arcia m iędzy nóżkam i tran z y sto ró w . A ni, by nie p rzestaw ić o m y łk o w o napięcia zam iast zw rotnicy. A zatem d o w ied zieliśm y się, że:
Istnieją etykiety za pomocą, których można określać dostęp do składników klasy. P oznaliśm y ju ż dw ie, ale jest ich trzy private: protected: public:
O kreślają o n e d o stęp do poszczególnych składników klasy.
Są trzy rodzaje dostępu do składnika klasy Składnik p r i v a t e ♦$» jest d o stęp n y tylko dla funkcji składow ych danej klasy. (Także dla funkcji zaprzyjaźnionych z tą klasą - por. str 615). Jeżeli zależy n am na
420
Rozdz. 10. Klasy Ukrywanie inform acji u k ry c iu inform acji, to w ów czas sk ła d n ik po w in ien być d e k la ro w a n y w ła ś n ie jak o p ry w a tn y . S k ła d n ik p r o t e c t e d ♦♦♦ je st d o s tę p n y tak, jak sk ład n ik p r i v a t e , ale d o d atk o w o je st jeszcze d o s tę p n y d la k las w y w o d z ący ch się o d tej klasy. (O ty m , ż e klasa m o ż e m ieć p o to m k ó w b ę d z ie m y m ów ić w ro z d z ia le o d z ie d z ic z e n iu . Tu ty lk o zapam iętajm y , ż e sk ład n ik i p r o t e c t e d są to s k ła d n ik i z a s trz e ż o n e dla siebie i ro d zin k i). S k ła d n ik p u b l i c ♦♦♦ jest d o s tę p n y bez o g ran iczeń . Z w y k le sk ład n ik am i ta k im i są jakieś w y b ra n e h m k cje sk ła d o w e . To za ich p o m o c ą do k o n u je się z z e w n ą trz o p eracji na d a n y c h p ry w atn y ch . E ty k ie ty te m o ż n a u m ie sz c z a ć w dow olnej kolejności, m ogą też się p o w ta rz a ć , Z a w s z e o zn ac zają , że te sk ła d n ik i klasy, k tó re n astę p u ją b e z p o ś re d n io p o e ty k iecie - m ają tak o k re ślo n y d o stęp .
Domniemanie Z a k ła d a się, że - d o p ó k i w definicji klasy nie w y stą p i ż a d n a z ty c h ety k ie t s k ła d n ik i p rz e z d o m n ie m a n ie m ają d o stęp p r i v a t e . class d z i k a
1
in t a; d o u b le b; v o id fu n l(in t); p ro te c te d : char m; v o id f u n 2 ( v o i d ) ; p u b lic : in t x; v o id f u n 3 ( c h a r * ) ; p riv a te : in t d; public:
v o id
f u n 4 ( v o id ) ;
}; W p o w y ż sz y m p rz y k ła d z ie n astęp u jące s k ła d n ik i k lasy są: •
p ry w a tn e (zastrz eżo n e dla sieb ie) a, b,
•
fu n l, d
p ro tec ted - (czyli za strz e ż o n e d la sieb ie i p o to m k ó w ) m, fu n 2
•
p u b lic z n e (d o stę p n e dla w sz y stk ic h ) x,
fu n 3 ,
fun4
421
Rozdział. 10. Klasy Klasa, a obiekt
Na początku k la sy nie ma żad n ej etykiety, więc zak ład a się, że składniki a, b, f u n l m ają być p ry w atn e. P otem w ystępują już etykiety, któ ry m i regulujem y dostęp d o w y b ran y ch składników . P okazany sp o só b określania do stęp u jest, co p raw d a, p o p ra w n y , jednak nie polecałbym go. Lepiej w szystkie sk ład n ik i o d anym d o stęp ie zg ru p o w ać razem. W ów czas w y starcza jeden rz u t oka na definicję klasy i już w iem y , co jest dostęp ne z zew nątrz.
Często w pracach nad poważnym projektem... ...program iści n arzu cają sobie zasad ę, że: ♦*« - na g ó rze definicji klasy um ieszcza się najpierw sk ład n ik i p u b l i c, bo te są najb ard ziej interesujące dla późniejszego u ży tk o w n ik a klasy; ♦♦♦ - niżej są sk ład n ik i p r o t e c t e d , (porozm aw iam y o nich później); ♦♦♦ - a najniżej są składniki p r i v a t e , bo te już interesu ją tylko tego, kto p racuje n a d tą klasą (a nie, jedynie, użytkuje ją). O czyw iście m a to znaczenie, g d y definicja klasy jest d łu g a i nie mieści się w całości na ek ran ie. W tej książce klasy b ęd ą raczej krótkie, w ięc raczej nie będę stosow ał tej z a sad y .
W Sterow anie d o stęp em nie zabezpiecza jednak p rzed św iad o m y m działaniem nastaw io n y m n a zepsucie. W C++ p raw ie w szystko jest m ożliw e, więc jeśli zechcesz to i tak m o żesz się d o takich pry w atn y ch dan y ch dostać. No, ale to już będzie św ia d o m y m oszustw em .
0.6
K lasa, a o b ie k t W iem y już, co to jest klasa. Definicja klasy, to jakby projekt techniczny nowego typu zm iennej. M ając ju ż zdefiniow aną klasę, m ożem y stw o rzy ć kilka egzem plarzy ob iek tó w d anej klasy. Tak, jak w p rzy p ad k u ty p u (klasy) i n t , m ożem y stw orzyć kilka ob iek tó w typu i n t : i n t a , m, l i c z n i k , cen a, k o lo r ; Ta definicja p o w o d u je utw orzenie pięciu różnych obiektów ty p u (klasy) i n t . Podobnie w p rz y p a d k u typu zdefiniow anego przez nas sam y ch , po definicji klasy m ożem y p rzy stąp ić do definiow ania konkretnych eg zem p larzy obiektów danej klasy. W ym yślm y sobie taką prostą klasę:
422
Rozdz. 10. Klasy Ukrywanie inform acji class osoba { public: void zapamiętaj (const char *, int ); void wypisz (); private: char naz w i s k o [80); int wiek;
}; W k la sie tej w id z im y d w a p ry w atn e sk ład n ik i - są to d an e zaw ierające in fo rm a cje o n azw isk u i w ie k u . P ublicznym i sk ła d n ik a m i są d w ie funkcje z a p a m i ę t a j - p rzy p isu jąca inform acje o n a z w isk u i w ieku
•
d o o d p o w ie d n ic h sk ła d n ik ó w •
w y p is z - d o w y pisania n a ek ran ie inform acji zap isa n ej w cze śniej w obiekcie.
D efiniq'a czterech e g z e m p la rz y obiektów tej klasy to po p ro stu in stru k cja osoba
studentl,
student2, profesor, pilot;
D ygresja: Z a m ia st m ów ić: e g z e m p la rz o b ie k tu d a n e j k la s y m ó w ić też b ęd ziem y po p ro stu : o b ie k t d a n e j klasy. P o n iew aż je d n a k to pierw sze, d łu ż s z e sfo rm u ło w an ie , w y raźn iej p o d k re śla ró żn ic ę m ięd zy klasą, a o b iek tem , d la teg o , p rz e z p ew ien czas, częściej b ęd ę u ży w ał p ie rw szej form y. Z o b ac zy liśm y w ięc, jak p o w stają cztery e g z e m p la rz e o b iek tó w n aszej klasy o s o b a . Jak w id ać, k a ż d y z nich m a sw oją n azw ę. w u n c a w i o t w f l M . n n ip w m t u M u t n i i m
r
.****»
- ^ 'i n u w n m i » mam%w u r w n
i » » * in w m w a w a t
N ależy sobie w yraźn ie uświadom ić, że sam a definicja klasy nie definiuje ża d nych obiektów.
K o n k retn iej - w n a sz y m p rz y p a d k u p o defin icji k la sy nie m a je szc ze w p am ięć ż a d n e j tablicy n a z w i s k o , an i żad n ej z m ie n n e j w ie k . To d o p ie ro definicja czterech e g z e m p la rz y o b ie k tó w tej k la sy sp ra w iła , że w p a m ię c i p o w sta ły c z te ry zesp o ły d a n y c h - cztery tablice d o p rz e c h o w y w a n ia n a z w is k a i cztery z m ie n n e ty p u i n t d o p rz e c h o w y w a n ia in fo rm acji o w iek u osoby. K a ż d y z tych z e sp o łó w d an y ch m o ż e p rzec h o w ać d a n e o jed n ej osobie. Sam a d efin icja klasy to ja k b y ty lk o p ieczątk a. D o p ó k i jej nie o d b ijem y c z te ro k ro tn ie na p a p ie rz e (p am ięć k o m p u te ra ) d o tą d na tym p a p ie rz e nic nie m a. P iec zątk a le ży sobie na b o k u . Ł atw o sobie to u z m y sło w ić , p a trz ą c na ty p w b u d o w a n y , jak im je st ty p i n t T w ó rc y języka z d e fin io w a li ten ty p i n t , a b y s łu ż y ł p ro g ra m isto m d o p rz e c h o w y w a n ia liczb całk o w ity ch . Ta definicja ty p u (k lasy ) i n t ju ż g d z ie ś w ję z y k i
423
Rozdział. 10. Klasy Klasa, a obiekt
C ++ tkw i. D o p ó k i jed n ak nie n a p isz e m y definicji e g z e m p la rz a obiektu tego ty p u , d o tą d w p am ięci nie jest z a re z e rw o w a n a ż a d n a k o m o rk a na ten cel. To d o p ie ro d efin icja int x;
sp raw ia, ż e w p a m ię c i jest jed en o b iek t tego typu. Z apam iętaj: Klasa to typ obiektu, a nie sam obiekt.
T o sam o b a rd z ie j potocznie: Klasa to ja k b y rysunek techniczny obiektu, a nie sam obiekt.
W definicji k lasy sk ład n ik i d a n e nie m o g ą m ieć i n i c j a t o r a . Definicja klasy jest przecież ty lk o ja k b y form ularzem d o w y p ełn ien ia. D o k u m e n t klasy p aszp o , której m a try c a (definicja) jest w d ru k a rn i p ań stw o w ej, nie z a w ie ra ^ y d r u k o nego n a z w isk a a n i d a ty u ro d zen ia. Jest ta m tylko m iejsce n a n a z w s k o . Te d a n e (inicjalizujące go) w p isu je się d o p ie ro d o kon k retn eg o e g z e m p klasy p a sz p o rt. class paszport
{
char nazwisko[40] ; char imię [401; int numer; int wzrost = 176;
//
< - b ł q d !!!
l>;r
G dyb y tak w y g lą d a ła definicja klasy p a sz p o rt, to w szy stk ie p a sz p o rty m iały by n a zaw sze w y d ru k o w a n y w zro st w łaściciela ró w n y 176 cm . N a taki bezsen. k o m p ilato r C + + n ie pozw oli. D ane do o b ie k tu d an ej klasy w p isu je się dopiero, g d y ^kT chś jem y (w y ra b ia m y sobie p aszport, k u p u je m y pralkę) albo p o z n ej, g y J zm ian p o trz e b u je m y (załatw iam y d o p a sz p o rtu jakąś w izę, lu b w sy p u jem y d o p ralk i p ro szek ). O tym jeszcze p o ro zm a w iam y w o so b n y m rozdziale. W arto u z m y sło w ić sobie jeszcze je d n ą rzecz: O tóż, jeśli w n a sz y m p rz y p a d k u zd efin io w a liśm y cztery obiekty klasy o s o b a , to w pam ięci u tw o rz o n e zostały cztery ró ż n e k o m p le ty sk ład n ik ó w d a n y c h tej klasy. To w k o ń cu z ro z u m ia łe g o m u szą b y ć n p . cz te ry tablice do p rzec h o w y w an ia czterech ro ż n y c h nazw isk. Co jed n ak ciek aw e: I fu n k cje sk ład o w e są w p am ięci tylko jed n o k ro tn ie. C ztery o d d z ie ln e k om plety sk ład n ik ó w danych są zro z u m ia łe , bo ' P Tzec^ z k ażd y m a p rz ec h o w y w ać inną inform ację. N ato m iast fu n k q e składow e d k ażd eg o e g z e m p la rz a obiektu danej k lasy działają p rzecież identycznie. Są w ięc w p a m ię c i k o m p u tera jed n o k ro tn ie - po to, by o szczę d zać pam ięć.
424
Rozdz. 10. Klasy Funkcje składowe W z asad z ie o tej sp ra w ie nie m usiałbyś w cale wiedzieć. Pow iedziałem to jednak p o to, byś nie sąd ził, że poszczególny obiekt w pamięci jest b ard zo duży. Taka o b a w a p ro w ad ziłab y d o niechętnego definiow ania now ych egzem plarzy obiektów . Znowu analogia. Składnikiem typu wbudowanego i n t jest na pewno funkcja (składowa) obsługująca mnożenie liczb typu i n t . Nie sądzisz chyba, że za każdym razem, gdy w programie definiujesz zmienną typu i n t przydzielana jest mu w pamięci nowa funkcja obsługująca to mnoże nie. Obiektów tego typu może być sto, a obsługuje je ta sama funkcja. O tym , jak w ielki jest obiekt, m ożesz się łatw o przekonać stosując operator s iz e o f.
10.7
F u n k c je s k ła d o w e Funkcje zd ek laro w an e w ew nątrz definicji klasy są sk ład n ik am i tej klasy. N azy w am y je funkcjam i składow ym i. Funkcje te mają zakres klasy, w której je zadeklarow aliśm y. (Z w ykłe funkcje - jak pam iętam y - mają zakres pliku, w k tó ry m je zadeklarow ano). Funkcje składow e m ają pełny dostęp do w szystkich składników swojej klasy — to znaczy: i do d an y ch (m ogą z nich korzystać) i do innych funkcji (mogą je w yw oływ ać). Do sk ład n ik a swojej klasy odw ołują się podając p o prostu jego nazw ę.
10.7.1
Posługiw anie się funkcjami składow ym i Funkcja składow a jest jakby narzędziem , za pom ocą którego dokonujem y ope racji na danych składow ych klasy. Szczególnie na tych składnikach, które są pr i v a t e i tym sam ym spoza klasy niedostępne. Przyjrzyjm y się jak - dla przed chw ilą zdefiniow anej klasy osoba - m ożem y w yw ołać funkcje składow e. P ow iedzm y ściślej - funkcję wywołuje się dla konkretnego obiektu danej klasy. M usim y więc mieć definicje obiektów. osoba
studentl, student2, profesor, pilot;
O to w yw ołanie funkcji składow ej dla obiektu profesor: profesor.zapamiętaj("Albert Einstein",
55);
Dla pozostałych obiektów podobnie studentl.zapamiętaj("Ruediger Schubart", 26); student2.zapamiętaj("Claudia Bach", 25); pilot.zapamiętaj("Neil Armstrong", 37);
W yw ołanie takie należy rozum ieć tak: na rzecz obiektu p r o f e s o r w ykonaj funkcję zapamiętaj z podanym i argum entam i. Składnia jest więc taka: nazw a obiektu, potem k ropka, a potem n azw a funkcji składow ej z ew entualnym i argum entam i
425
Rozdział. 10. Klasy Funkcje składowe
obiekt, funkcja (argumenty); Z ap y tasz z a p e w n e - Po co ten o b iek t i ta kropka? N ie m o żn a by w yw ołać tej k: funkcji p o p ro stu tak: zapam iętaj("Albert E in stein " ,
// Błąd !
55);
j
*
N ie nie m ożna Z apom inasz, że w pam ięci są już cztery zestaw y danych odp o w iad ające czterem różnym obiektom klasy o s o b a . Funkcja m usi w iedzieć na którym k o n k retn y m obiekcie m a pracow ać. Jeśli funkcja z a p a m i ę t a j ma w pisać nazw isko „A lb ert E instein" do tego konkretnego ob iek tu klasy o s o b a , k tóry to obiekt n azw aliśm y p r o f e s o r , to ę nazw ę p r o f e s o r staw iam y p rzed w yw ołaniem funkcji. M ożem y także w y w o łać funkcję sk ład o w ą dla tego sam eg o obiektu po k azy w a nego w skaźnikiem :
lift
[profesor;
wsk - > z a p a m i ę t a j ( " A l b e r t E i n s t e i n " ,
55) ;
A oto, jak w y w o łać funkcję dla tego sam eg o obiektu p rzez y w an e g o referencją: osoba & b e l f e r = p r o f e s o r ;
/ / definicja referencji
b e l f e r . z a p a m i ę t a j ("Alb er t E i n s t e i n
10.7.2
,
55),
D efin iow an ie funkcji składow ych Do tej p o ry d u ż o m ów iliśm y o funkcji zapamięta j, ale n ig d z ie m e pojaw iła się jeszcze jej definicja, czyli jej treść, ciało - instrukcje składające się na mą.
G d z ie m o ż e b y ć z d e fin io w a n a fu n k cja s k ła d o w a ?
Może się ona zn aleźć w dw óch miejscach.
"Y "
P ierw szy s p o só b : -M o że się znaleźć w ew n ątrz sam ej definicji klasy. Oto taka realizacja: c l a s s osoba
1
char n a z w i s k o [ 8 0 ] ; i n t wiek; pub lic:
H składniki prioate // składniki publiczne
definicje funkcji składowych— v o i d z a p a m i ę t a j ( c o n s t char * n a p i s ,
strcpy( naz wi sk o, wiek = l a t a ; v o i d w y p i s z ()
<
(na pi s ? n a p i s
int
lata)
"Anonim") ) ;
426
Rozdz. 10. Klasy Funkcje składowe
Jest też d ru g i s p o s ó b definiow ania funkcji składow ych: W d efin iq i k la sy u m ieszcza się ty lk o sam e deklaracje ty ch funkcji, n ato m iast definicje są n a p is a n e poza ciałem klasy: c l a s s osoba // składniki private char nazw isko[80]; i n t wiek; public:
// składniki publiczne
/ / ------------------------------ deklaracje funkcji składowych void zapamiętaj (const char * napis, void wypisz () ;
//'ito«
int lata);
Wflsy/////////////////////////////////////////////////////////////^^^^
/+***************»********************************************/ void osoba ::zapamiętaj(const char * napis, int lata)
1 strcpy(nazwisko, wiek = lata;
(napis ? napis
: "Anonim"));
1
.
/★★★★★★★★★★★★★★★★★★★★*****************************************/ void osoba ::wypisz ()
(
.
cout << nazwisko << ", lat:
" << wiek << endl;
) P o n iew aż fu n k c je zn ajd u ją się te ra z p o z a d e fin iq ą k lasy , d la te g o ich n azw a u z u p e łn io n a z o s ta ła n azw ą klasy, d o któ rej m ają należeć. S łu ż y d o tego w id o cz n y o p e ra to r z a k re s u : : . T aki za p is in fo rm u je kom p ilato r, ż e je st to realizacja tej fu n k cji z a p a m i ę t a j , k tó rą z a d e k la ro w a liśm y w definicji k la sy o s o b a . Jeśli b y ś m y o u m ieszczen iu tego p rz e d ro s tk a z ap o m n ieli - k o m p ila to r m o że u zn ać, ż e jest to jak aś tam funkcja z a p a m i ę t a j - jed n a z w ie lu zw y k ły ch w p ro g ra m ie . N a to m ia st w trak cie lin k o w a n ia lin k er nie z n a jd z ie p o sz u k iw a n e j p r z e z sieb ie funkcji o s o b a : : z a p a m i ę t a j i o trz y m a m y k o m u n ik a t, ż e fu n k cja sk ła d o w a z a p a m i ę t a j d la k la s y o s o b a nie jest n ig d z ie zd efin io w an a.
j
N a z w a k la sy i o p e r a to r zak resu są rze c z y w iśc ie ja k b y p rz e d ro s tk ie m n a z w y funkcji. M ó w ię o ty m d lateg o , by u s trz e c C ię p rz e d b łę d e m p o le g a ją c y m n a tym , j
////////////////////////////
R svp i o ln ią f r p
/ / / / / / / / / / / / / / / / / / / / / //
.'P3 S aoedssuieu 6uxsn <6uta^so> spnioui#
O ft
a p n io u j#
:eqoso As b |>|wapAzn z wBj6cud Ajsojd Bpfe|6AM >p[ ‘ojo v *' */ /
i h y u n j o fm o
} () zstc JAm ::eqoso
ptoa
auiiui
l euf f UT o^a^ojs atezsid aAzoeuzez atuzejA/A Xpa]A\ 0 4 eqozj 4 o^jjAjl jazoj/y
£aux"[UT ridAł ApSiu aAq azoui atu Asepj febtuyap ezod eueMOtuyapz eMopei^s eb^ury oSa; aaqo/v\ - :atuA\ad zse^Adez •Asbj>{ ebtuyap ezod tef Auiafntu
-yap 04 'i>jfiuij atMp 34 ziu ezsznfp 4sa( eMopej^s eb^ury qsaf ■ au iiu i aiuzaAłeuiołriB Apa^v\ 4saf (l qosods) Asb[>[ ibtuyap zijbuMa/w Auia(ryuyap abyury 04 'T5f(xuT| atMp ztu (aa3tM atu eui (aMope^s datury ojep ysaf
•
•
:>jeł 3ts AuiMęuift l
0BjqAM
LuełBz q o s o d s Ajopj Ape|>i
•auxxuT o>je( BueM euzn atuzoAłeuioąne 4sa( aiu e b ^ u ry az 'eiMB-ids
(2 qosods) Asej>f febtuyap ezod eafep£q [aMopef^s ib^ury eb iu y aa (Z91 ys '6 £ §) a u t tut ndAą ejAq eb^ury e4 Aqe 'Auiaaip az 'a(euzn jołejiduiojf 04 \ \ qęsods) Ase^ ibtutjap zyteuMaM Auiafntuyapz feMope^s ab^ury uiatMoq ysaf BJO}B|!dlUO>) B|p BOjllZOJ BULUOj6o >JBUp0f łS0p •Asej>{ (a(oA\s M 9>pupeł>is ipi>psAzsM op d a ;s o p AMO^eupaf eu i - uiaqosods u itS ru p Aza uiAzsM jatd U1A4 łsaf euBM Oiuyapz B M ope^s e(a>iury Aza atuza[ezatu :aóiM ib u a M 5jasuo>( m •e q o s o Asej>{ psouze/w aisaj?{ez o ab^jury tefntuijap Aqosods e q o Asei>{ zjyteuMaM eueM O tuyapz e{Aq Aq>je( 's a j^ e z u ies tsjeł a tu p e ^ o p eui e>psoapazjd 0834 ntueMOSo^sez A zjd fesej>[ ezod eueM .oiui.ppz e(a>(unj (
U m \l }
( • • • ) Ce^eTuredcz : :eq o so pTOA
ąAq eui adiM atu M B jd o j •fezszryp fe( tetuAza Aqye( 1 tb y u ry J m z e u fefetujad -azn :: jo^ejado 1 Asej^ bm zbu az 'z s e p tu ie d e z ysaf z s tu p d o d a tu 0834 np^jg (
i pbłS II
U°łnP*l >
( • • *) CB^eTuredez ptOA : : eqoso
P b ^ u r y za z jd AueaejM Z dA4 - U1A4 od o ja id o p e 't^[iui[ n ^ fezao d uiAuies eu zstMB4sn :: J04eaado 1 Asej>[ Smzbu az
aAvopep|s abyunj L it
Asera OT TKizpzoa
428
Rozdz. 10. Klasy Funkcje składowe
class
osoba
( c h a r n a z w i s k o [80] ; i n t wiek; public: v o i d zapamiętaj(const
char
* napis,
int
lata);
/ / -----------------------void
//
w y p i s z ()
{ cout
<<
"\t" «
nazwisko
©
<< w i e k << e n dl;
<<
} };
///// / / / / / / / / / / / / / / / / / / k o n ie c d e fin ic ji k la s y / / / / / / J / / / / / / / f / / / / / / / / Ł o s o b a ::z a p a m i ę t a j ( c o n s t c h a r * napis, int lata) // w v o i d os s t r e p y (nazwisko, w i e k = lata;
(napis
? napis
: "Anonim")
);
z*************************************************************/ Lnt m a i n ( )
[ o s o b a s t u d e n t 1, student2, profesor, pilot; cout
<<
"Dla i n f o r m a c j i pod a j e , ze j e d e n " k l a s y o s o b a \ n m a rozmiar: " << sizeof(osoba) < < " bajty. T o s a m o inaczej: " << s i z e o f ( s t u d e n t l ) << endl;
p r o f e s o r .z a p a m i ę t a j (" A l b e r t
Ei n s t e i n " ,
<<
// O
studentl\n";
cout << "dane z obiektu s t u d e n t 2 .w y p i s z () ;
student2\n";
cout << "dane z o b iektu p i l o t .w y p i s z ();
p i l o t \n";
c o u t < < " Po d a j s w o j e ch a r m a g a z y n e k [80]; cin > > magazynek; cout < < "Podaj int ile; c i n > > ile;
swój
nazwisko
//
©
26);
" P o w p i s a n i u i n f o r m a c j i do o b i e k t ó w . " S p r a w d z a m y : \n"; c out << "dane z o b i e k t u profesorNn"; p r o f e s o r .w y p i s z () ; c out << "dane z o b i e k t u s t u d e n t l .w y p i s z () ;
"
55);
s t u d e n t 1 .z a p a m i ę t a j (" R u e d i g e r S c h u b a r t " , s t u d e n t 2 .z a p a m i ę t a j (" C l a u d i a Bach", 25); p i l o t .z a p a m i ę t a j (" N e i l A r m s t r o n g " , 37); cout
obiekt
"
(tylko n a z w i s k o ) : ";
//<&) wiek:
";
429
Rozdział. 10. Klasy Funkcje składowe
profesor.wypisz(); pilot.wypisz();
I___
'
)
Oto, co po wykonaniu programu, zobaczymy na ekranie Dla informacji podaje, ze jeden obiekt klasy osoba ma rozmiar: 84 bajty. To samo inaczej: 84 Po wpisaniu informacji do obiektów. Sprawdzamy: dane z obiektu profesor Albert Einstein, lat: 55 dane z obiektu studentl Ruediger Schubart, lat: 26 dane z obiektu student2 Claudia Bach, lat: 25 dane z obiektu pilot Neil Armstrong, lat: 37 Podaj swoje nazwisko (tylko nazwisko) : Galileusz Podaj swój wiek: 60 ,^ ___. Oto dane które teraz sa zapamiętane w obiektach profesor i pilot Albert Einstein, lat: 55 (D® Galileusz, lat: 60
O Ten plik n ag łó w k o w y w łączany jest z u w a g i na to, że w p ro g ram ie posługujem y się funkcją biblioteczną stropy. Jej deklaracja jest w łaśnie w tym pliku. © P ry w a tn e d a n e sk ład o w e klasy osoba. P ryw atne przez dom n iem an ie, bo do tej pory nie w y stą p iła jeszcze w tej definicji klasy żad n a etykieta określająca dostęp do składników . © Etykieta m ó w iąca, że następujące po niej składniki b ęd ą m iały dostęp publ i c. O Deklaracja funkcji składow ej. Sama deklaracja. © Definicja (a w ięc ty m sam ym jednocześnie deklaracja) funkcji składow ej wypisz. Funkcja jest tu zdefiniow ana w ew nątrz definicji klasy, więc będzie typu i n l i n e .
Pora się wytłumaczyć z tego wyrażenia warunkowego... ...um ieszczonego jako d ru g i arg u m en t funkcji s t r o p y . Otóż, g d y b y ktoś, w yw ołując tę funkcję, zam iast w sk aźn ik a do popraw nego C -stringu, czyli zam iast: • s t u d e n t 2 . z a p amiętaj("Fred Ameil", 2 5 ) ; •
c o n s t c h a r * w sk n a z = " F r e d A m e i l" ; s t u d e n t 2 . z a p a m i ę t a j ( w s k n a z , 25) ;
430
Rozdz. 10. Klasy Funkcje składowe w y sła ł w s k a ź n ik u s ta w io n y n a tzw . a d r e s z e ro w y , czyli n a p is a ł je d n ą z p o n iż sz y c h (p o p ra w n y c h s k ła d n io w o ) form : • •
s t u d e n t ż . z a p a m ię ta j ( 0 , 2 5 ); c o n s t c h a r * w sk n az = 0; s t u d e n t 2 . z a p a m i ę t a j (w sknaz,
25) ;
— to w ó w c z a s s ta r o m o d n a funkcja b ib lio te c z n a s t r e p y w tra k c ie s w e j pracy w y w o ła ła b y b łą d (m o g ą c y "zaw iesić" p r o g r a m lu b n a w e t k o m p u te r ) . T ru d n o , ta k a ju ż o n a jest, tr z e b a z ty m żyć. A b y te g o u n ik n ą ć , n a s z a fu n k cja z a p a m i ę t a j s p r a w d z a n a jp ie r w c z y o d e b ra n y w sk a ź n ik n a p i s , k tó ry (m a z a ra z w y s ła ć d o s t r e p y ) je st n ie z e r o w y . Jeśli ta k , w y sy ła g o jej. J e ś li nie, to w y sy ła jej C -s trin g "A nonim ". In n y m i sło w y , o d p o w ia d a to takiej in s tru k c ji i f - e l s e if(napis) else
s t r e p y (nazwisko, napis); s t r e p y (nazwisko, "Anonim");
M u s im y n a ra z ie ż y ć z tą fu n k cją strepy, a le w n ie d a le k ie j p r z y s z ło ś c i p o z n a m y w ie le le p sz e s p o s o b y p ra c y z e s trin g a m i. 0
K o n ie c d y g resji. D efin ic ja fu n k cji s k ła d o w e j b ęd ąca p o z a d e fin ic ją klasy . P o n ie w a ż je s t tam o p e r a to r z a k re s u (osoba: :zapamiętaj ) , w ię c k o m p ila to r w ie , ż e je st to d e fin ic ja fu n k cji zapamiętaj, będącej fu n k c ją s k ła d o w ą k la s y osoba.
© T u się p rz e k o n a s z , ja k d u ż y je st o b ie k t k la s y osoba. N a e k r a n ie w id z is z , ż e jest to 84 b ajty . N ic w ty m d z iw n e g o , że w ła ś n ie ty le - w k la sie s k ła d n ik ie m jest p r z e c ie ż ta b lica 80 z n a k o w a o ra z z m ie n n a ty p u int k tó ra m a r o z m ia r 4 b a jty (w m o im k o m p u te rz e ). W s u m ie je st to 84. Z a u w a ż , ż e o p e r a to r sizeof m o ż n a z a s to s o w a ć d o k la s y , c z y li ty p u , jak też i d o k o n k r e tn e g o e g z e m p la r z a o b ie k tu . P o d o b n ie ja k d la ty p u int. int licznik; s i z e o f (int) sizeof(licznik)
© W y w o ła n ie fu n k c ji s k ła d o w e j zapamiętaj n a rz e c z o b ie k tu profesor b ę d ą c e g o e g z e m p la r z e m o b ie k tu k la sy osoba. F o rm ę ta k ie g o w y w o ła n ia ju ż o m a w ia liś m y . © P r z y k ła d w y w o ła n ia f u n k c ji wypis z.T u n a rz e c z o b ie k tu o n a z w ie
student 2.
431
Rozdział. 10. Klasy J a k to właściwie jest? ( t h i s )
© Po w y p isan iu tego, co jest w pro g ram ie, program p ro si o podanie Tw oich danych, b y je zapam iętać w obiekcie. P rosi nas tylko o n azw isk o po to, by b y ł to jeden wyraz.*2 O O O trzy m an e d an e w kładam y d o ob iek tu o nazw ie p i l o t . O © Na d o w ó d , że to się udało - w y p isu jem y za chw ilę na ek ran .
W S ło w a , sło w a , s ło w a
Spotkasz czasem ludzi, którzy na funkcję składow ą m ów ią m etoda . N ie daj się przerazić. W p o n ad siedem set stronicow ym opisie sta n d a rd u języka C++ za w sze u ż y w a n y jest term in funkcja składowa (ang. member functioń). Skąd z a te m w zięła się ta "metoda"? Z innych, daw niejszych języków p ro g ram o w ania obiektow ego. Przynieśli ją d o C++, że tak pow iem , u chodźcy z tam tych języków . A rg u m en tu ją oni, że funkcja sk ład o w a jest jakby m e to d ą postępow ania z obiektem . Ich przeciw nicy m ów ią zaś, że jeśli tak, to m eto d am i m ożna by nazyw ć co najw yżej publiczne funkcje składow e. Przecież w klasie pralka_au tom atyczn a prywatnia funkcja składo wa o b rot_ bebn a _ w_ 2 e wo nie jest metodą postępowania użytkownika z pralką. On przecież nie ma do takiej ' metody dostępu. Nazywanie tej funkcji "metodą" jest niespełnioną obietnicą. Jak już m o ż e zauw ażyłeś, ja sam nie jestem bardzo rygo ry sty czn y jeśli chodzi o n azew n ictw o Bardziej zależy mi, by nie tyle uczyć Cię n azw , ile tego, co one oznaczają. Sam jed n ak staram się nie nazyw ać funkcji sk ład o w y ch m etodam i.
10.8 Jak to właściwie jest ? (this) Jak w iem y , funkcja składow a m oże w ykonyw ać operacje na danych sk ład o w ych. W n aszy m program ie z klasą o s o b a , w idzieliśm y taką definicję funkcji składow ej: void
osoba::zapamiętaj(char
stropy(nazwisko, wiek = lata;
* napis, int lata)
(napis ? napis : "Anonim )),
) W iem y też, że istnieje w pam ięci kilka egzem plarzy obiektów klasy osoba. osoba
3)
studentl, studentż,
profesor, pilot;
To jest wada tego prostego sposobu wczytywania. O tym, jak się wpisuje całe wielowyrazowe zdania - porozmawiamy już niedługo w rozdziale o stringach, a potem w rozdziale o operacjach Wejścia/Wyjścia.
432
Rozdz. 10. Klasy J a k to właściwie jest? ( t h i s ) Są w ięc cztery tablice na nazw isko, cztery zm ienne typu i n t na p rzech o w y w a nie inform acji o w ieku. Jeśli p rzy jrzy m y się funkcji sk ład o w ej, to w id zim y , że jest tam tylko nazw a w ie k i nazw a n a z w is k o . Funkcja sk ład o w a, jak już w spom niałem , jest w pam ięci tylko jed n o k ro tn ie - skąd w ięc w ie on a, do której kom órki w ie k m a, w danym m om encie, coś wpisać? wiek = 44; Czy do w ieku
studental czy też pilota?
O d p o w ied ź jest prosta. Zw róć u w a g ę jak w yw oływ ana je st funkcja student2 .zapamiętaj("Ruediger Schubart", 26);
Jak w idać, w y w o łan a jest na rzecz je d n eg o konkretnego e g z e m p la rz a obiektu s t u d e n t 2 . Bez naszej w iedzy - d o w n ę trz a funkcji p rz e s y ła n y jest w sk aźn ik do tego k o n k retn e g o obiektu. Tym a d re se m funkcja inicjalizuje sobie sw ój "w skaź nik” z w a n y t h i s . 4 W skaźnik ten p o k azu je funkcji, na k tó ry m k o nkretnym eg zem p larzu ob iek tu tej klasy, m a funkcja teraz pracow ać.
o b iek c ie?
O tóż w n ę trz e tej funkcji w ygląda w rzeczyw istości tak: void o s o b a ::zapamiętaj(char * napis, int lata)
strcpy (this->nazwisko, this->wiek = lata;
(napis ? napis : "Anonim"));
) W idzim y coś ja k b y w sk aźn ik this p r z e d nazw ą sk ła d n ik ó w klasy. Ten "w ska źnik" m o g lib y śm y sam i w tych m iejscach tam postaw ić, ale k o m p ilato r oszczę d za nam p is a n ia i w staw ia to sam . G d y b y śm y to, m im o w sz y stk o , zrobili - nie będzie to b łęd em . "W skaźnik" t h i s stojący p rzed s k ła d n ik a m i klasy s p ra w ia , ż e o peracje p rz e p ro w a d z a n e są na sk ład n ik ach tego (this) k o n k retn eg o e g z e m p la rz a obiektu, dla któ reg o tę fu n k cję w yw ołaliśm y. N ie m a żadnej w ielo zn aczn o ści.
X T y lk o d la b a r d z o d o c ie k liw y c h
C zy z a u w a ż y łe ś , że p iszę ostatnio "w sk aźn ik " t h i s - u ż y w a ją c cu d zy sło w u ? T o dlateg o , ż e t h i s - nie jest to taki z w y k ły w skaźnik. Z w y k ły - jest p rzecież tzw . 1 -w arto ścią, czyli m o żn a na p rz y k ła d d o w ied zieć się o je g o w ła sn y adres. ł)
ang. this - ten [czytaj: „wys"].
433
Rozdział. 10. Klasy J a k to właściwie jest? ( t h i s )
To znaczy można dowiedzieć się nie tylko o adres "na co ten wskaźnik (laserowy) teraz pokazuje", ale "gdzie ten wskaźnik (np. laserowy) teraz sam jest . ♦♦♦ N a to m ia st o adres this nie m o żn a pytać. // !H straszny błqd
S th is
♦♦♦ N ie m o ż n a tez p o d m ien iać zaw arto ści t h i s (czyli ja k b y przekierow yw ać go ta k , by pokazał na coś innego. t h i s = inny adres;
// !!! straszny błąd
Ł atw o to z a p a m ię ta ć tak: T raktu j g o po p ro s tu tak, jakby z a m ia st słow a this b y ła tam stała dosłow na określająca p o k a z y w a n y ad res w pam ięci. W yobraź sobie w ięc, że zam iast instrukcji: th is-> w ie k = l a t a ; jestinsfrukqa.^37 >w iek ^ lafca;
// pseudo-kod!!!
N o i co? C zy te ra z - mając takie sko jarzen ie - w p ad ło b y Ci d o głow y, by: •
d o w iad y w ać się od a d re s stałej dosłow nej 0 x 3 1 6 f 3 7 ? fi0x316f 37
•
I/straszny błąd
zm ieniać tę stalą d o sło w n ą (niby jak)?
K oniec tekstu d la dociekliw ych.
Typ w sk a ź n ik a
this
Z w y k ły w sk a ź n ik m ogący p o k azy w ać na obiekty klasy X jest typu:
W skaźn ik this po k azu je na w łaśn ie takie obiekty. U ży w an y jest zaw sze z funkcji sk ład o w y ch tej klasy. [g p
Z atem I W zw y k łej funkcji sk ład o w ej -
this jest ty p u : X*
A le nie k ażd a fu n k cja składow a jest zw ykła. Jakiego ty p u jest t h i s w tych n ie -z w y k ły ch funkcjach, p o ro zm a w iam y w § 10.19, na str. 484. P o c o n a m ten w s k a ź n ik ?
Z obaczyliśm y tu w skaźnik, który w n iew id zialn y sp o só b istnieje w naszych funkcjach sk ład o w y ch . M ógłbyś zapytać: - A po co n am to w iedzieć? P rzyd aje sję, bo czasem z funkcji sk ład o w ej chcielibyśm y p o d a ć k o m u ś obcemu a d re s obiektu, na rzecz k tó reg o ta funkcja została w yw ołana. W ted y w łaśnie m ożem y w y słać m u w sk aźn ik t h i s .
434
Rozdz. 10. Klasy Odwołanie się do publicznych danych składowych Przykład: obiekt klasy o s o b a może mieć jakąś funkcję składow ą o nazw ie
moje wakacj e,w której dokonuje zgłoszenia się na jakąś wycieczkę. Innym i słowy: w tej funkcji składow ej moje_wakacje m usisz w yw ołać jakąś tam funkcję globalną o nazwie zapis_osoby_na_wycieczke. W yw ołując tę (obcą) funkcję m usisz podać jako a rg u m e n t - adres osoby, która na tę w ycieczkę ma jechać (czyli swój adres). W ted y ow a funkcja globa na zapisze ten adres na listę wycieczkowiczów. Jak to zrealizow ać? Bardzo pro sto - w funkcji składow ej klasy osoba um iesz czasz tak ie w ywołanie void osoba::moje_wakacje{) { zapis osoby nawycieczke (this); } Czyli w y sy łasz globalnej funkcji
zapis_osoby_na_wycieczke
adres tegc
obiektu. A teraz p y tanie: jak najpraw dopodobniej w ygląda deklaracja tej funkcji global nej z a p i s _ o s o b y na_wycieczke? O czyw iście nic potrafim y pow iedzieć o jej typie zw racan y m (typie rezultatu). M usi być to jednak funkcja, którą m ożna w yw ołać z jednym argum entem . Jakiego ty p u ? M ówiliśmy o ty m przed chwilą. Skoro: I w fu n k cji sk ła d o w e j moje_wakacje w sk a ź n ik t h i s jest ty P u I osoba*. - zatem funkcja zapis_osoby_na_wycieczke p o w in n a mieć np. taką deklarację: void zapis osoby na wycieczkę(osoba *jej_adres), Dla w tajem niczonych: Jeszcze lepiej, by funkcja z a p i s _ o so b y _ n a _ w y c ie c z k ę zagwaranto wała tej zgłaszanej osobie "nietykalność". Czyli najlepiej, by odebrała ten adres jako adres do stałego o b ie k tu . Krótko mówiąc, by jej deklaracja była
taka: void zapis_osoby_na_wycieczke(const osoba *jej_adres),
0.9 Odwołanie się do publicznych danych składowych D ane s k ła d o w e klasy mogą być zasadniczo p ry w a tn e p rivate i publiczne public. (O trzecim typie - p r o t e c t e d nie b ęd ziem y na razie m ów ić - w n a szym p rz y p a d k u składniki p r o t e c t e d zachow ują się jak private). O to p rz y k ła d : class owoc { int scisle_tajne; public: int pestka;
435
Rozdział. 10. Klasy Zasłanianie nazw void funkcjal() ;
}; Są tu d w ie d a n e składow e. Składnik scisle_tajne jest p ry w atn y . (Przez dom niem anie). Jak o p ry w atn y m oże być u ży ty tylko z zak resu klasy - czyli z w n ętrza funkcji sk ład o w ej funkc jal. N ato m iast sk ład n ik publiczny pestka - oprócz tego, że m oże być dostępny w tej funkcji sk ład o w ej f unkc j a 1, d o stęp n y jest także z ze w n ą trz klasy. Pracując jednak na nim z z ew n ątrz m usim y p o d ać, o który konkretny o b iek t chodzi. owoc cytryna,
gruszka;
//d e f d w ó c h o b iektó w k la s y o w o c
cytryna.pestka = 16; gruszka.pestka = 12; cout « "Moja cytryna ma " << cytryna.pestka << " pestek"; cout « "Moja gruszka ma " << gruszka.pestka << " pestek";
N azw a, jak w id ać, uzupełniona jest nazw ą konkretnego o b iektu, o którego p e s t k ę chodzi. N atom iast, g d y b y śm y napisali tak: cytryna.scisle__tajne = 6;
//Błąd!
to kom pilator zaprotestuje. Wie on, ż e scisle_tajne jest sk ładnikiem p ry w atn y m klasy owoc, a więc nie w olno na nim robić żad n y ch operacji spoza zakresu tej klasy.
10.10 Zasłanianie nazw P oniew aż n azw y sk ładników klasy (danych i funkcji) mają zak res klasy, więc w obrębie klasy zasłaniają elem enty o takiej sam ej nazw ie leżące poza klasą. Z dan y m i sp raw a jest prosta. Zm ienna: int ile;
będąca sk ład n ik iem klasy, zasłania (w obszarze swej klasy) ew entualną zm ienną ile z zak resu globalnego lu b lokalnego. Po raz kolejny przypomnę, że zbytnie poleganie na zasłanianiu wprowadzi nas w tarapaty. Wcześniej czy później zapomnimy co, kiedy i przez co jest zasłaniane. Najlepiej po prostu wymyślać inne nazwy. W ew nątrz klasy sk ład n ik o nazw ie nnn zasłania inne obiekty o tej sam ej nazw ie leżące poza klasą (czy to globalne, czy lokalne). Stają się one w ted y niedostępne. M ożna jed n ak m im o w szystko dostać się d o zasłoniętej n azw y globalnej za pom ocą operatora zakresu. M ożliwe jest jeszcze jedno piętro. O tóż w ew n ątrz zakresu klasy m ożna także zdefiniow ać sobie jakiś zakres lokalny i w nim zdefiniow ać jakąś nazw ę. N azw a taka zasłoni w ów czas nazw ę składnika klasy. Pokażm y na p rzy k ład zie jak to się dzieje i jak m ożna - m im o w szystko - d o zasłoniętych n azw się odnieść. (Z auw aż użycie o p erato ra zakresu).
436
Rozdz. 10. Klasy Zasłanianie nazw
łinclude u s i n g n a m e s p a c e std; int b a l k o n = 77; v o i d ś p i e w () ; /////////////////////// class opera { public: int n; d o u b l e balkon;
// nazwa globalna / / O // deklaracja jakiejś funkcji (globalnej) definicja klasy / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
~ // składnik klasy
"
...
//
void void
f u n k c j a (); ś p i e w ()
// ©
{ cout
<<
"funkcja ś p i e w
(z opery):
tra-la-la
!\n";
) } ////////////////////// v o i d o p e r a : :f u n k c j a ()
koniec definicji klasy / / / / / / / / / / / / / / / / / / / / / / / / I
// v
cout cout
//jeszcze się nic nie dzieje < < " b a l k o n ( s k ł a d n i k klasy) = ” < < b a l k o n < < en d l ; / / tś/ < < " b a l k o n ( z m i e n n a g l o b a l n a ) = " < < : : b a l k o n << e n d l ;
char
// definicja zmiennej lokalnej (lokalnej dla tej funkcji) b a l k o n = 'M ’;
cout cout cout cout
<< << << <<
" \ n P o d e f i n i c j i z m i e n n e j l o k a l n e j --- \n"; "balkon (zmienna lokalna) = " « b a l k o n « endl; " b a l k o n (s k ł a d n i k klasy) = " < < o p e r a : :b a l k o n < < e n d l ; "b a l k o n (zmienna globalna) — " << ::balkon << endl;
/ / ------------------ -wywołanie funkcji ś p i e w (); int
śpiew;
ś p i e w = 7; // ś p i e w ()
// < — błąd w trakcie kompilacji // bo nazwa funkcji —już zasłonięta
cout
<< "Po z a s ł o n i ę c i u d a sie w y w o ł a ć " f u n k c j e ś p i e w t y l k o tak\n"; o p e r a : :ś p i e w () ; // tak można
"
o o
) Z******************************************************’ int m a i n ()
{ opera
Lohengrin;
L o h e n g r i n .b a l k o n = 6; L o h e n g r i n .f u n k c j a () ; ś p i e w ();
1 void
ś p i e w ()
{ cout
<<
"zwykła
funkcja
śpiew
(nie m a j a c a
nic"
437
Rozdział. 10. Klasy Zasłanianie nazw " w sp ó ln e g o z k l a s a ) \n " ; )
Po wykonaniu programu na ekranie zobaczymy b a lk o n ( s k ła d n i k k la s y ) = 6 b a lk o n (zm ienna g lo b a ln a ) = 77
O Definicja zm iennej globalnej o nazw ie b a l k o n oraz deklaracja funkcji globalnej o n azw ie ś p i e w . © W ew n ątrz klasy o p e r a definiujem y d an ą składow ą o n azw ie b a l k o n . © Definicja funkcji ś p i e w , będącą funkcją sk ład o w ą klasy o p e r a . O D efinicja funkcji f u n k c j a , będącej funkcją sk ład o w ą k lasy o p e r a . Dla o d m ian y definicja ta leży poza definicją klasy. © W ew n ątrz funkcji składow ej klasy o p e r a d o składnika klasy o d n o sim y się po prostu: b a lk o n a d o zasłoniętej zm iennej globalnej (o tej sam ej nazw ie) : : b a lk o n © N a scenie pojaw ia się jeszcze jeden obiekt o nazw ie b a l k o n . T ym razem jest to z m ie n n a ty p u c h a r z d e fin io w a n a lo k a ln ie - w e w n ą trz n a s z e j funkcji f u n k c j a. Ta n o w a n azw a zasłania tutaj w szystkie pozostałe n a z w y b a l k o n . W n a s tę p n y c h lin ijk a c h w id z im y , ja k n a le ż y te r a z o d n o s ić s ię d o poszczególnych zm ien n y ch o tej nazwie. Z au w aż, że teraz użycie sam ej nazw y b a l k o n do ty czy tego lokalnego obiektu - bo w tym lokalnym zak re sie (funkcji) w łaśn ie jesteśm y. O to, jak się d o poszczególnych obiektów teraz odnosim y: •
b a lk o n -d o ty c z y zm iennej lokalnej d la funkcji f u n k c j a (tej ty p u c h a r ),
•
o p e r a : : b a l k o n - dotyczy danej składow ej k la sy o p e r a ,
•
: : b a l k o n - dotyczy obiektu globalnego (tego ty p u i n t ) .
O N a z w a ś p i e w określa funkcję składow ą klasy. Tutaj w idzim y w y w o łan ie tej funkcji.
438
Rozdz. 10. Klasy Zasłanianie nazw © D efinicja lo k a ln e g o o b iek tu ty p u i n t o nazw ie ś p i e w . N a z w a ta, o d tej p o ry , z asła n ia (w ty m lo k aln y m zak resie funkcji) n azw ę ś p i e w , będącą n a z w ą funkcji s k ła d o w e j k la sy o p e r a . Z a u w a ż w a ż n ą rzecz: n a z w a zasła n ia inną n azw ę - niezależnie czy ta in n a nazw a jest n a z w ą zm ien n ej, czy funkcji. © P o p rz e z n a z w ę ś p i e w o d n o sim y się te ra z d o obiektu lo k a ln e g o (ty p u i n t ) . © P róba w y w o ła n ia fu n k cji sk ład o w ej ś p i e w u zn an a z o s ta n ie z a p rzestę p stw o . K o m p ila to r z a p ro te s tu je - inform ując n as, że n azw a ś p i e w nie jest n a z w ą funkq'i. S łu sz n ie , b o n a z w a tej funkcji zo sta ła w łaśnie z a sło n ię ta i jest n ie w id o czna. A b y k o m p ilacja się u d a ła - ta linijka m usiała z o sta ć u m ieszczo n a w k o m e n ta rz u . O O Jeśli k o n ie c z n ie ch cem y w y w o łać tę funkcję sk ład o w ą, to m u s im y ją w zb o g acić o o p e ra to r z a k re s u . Z a p is o p e r a : : ś p i e w ()
w y ra ź n ie o k re śla , ż e ch o d zi o tę n a z w ę ś p i e w , k tó ra le ż y w zak resie k la sy opera. O © T o jest p r z y k ła d p o słu ż e n ia się klasą o p e r a . W p o p rz e d n ie j linijce zd e fin io w a liśm y o b ie k t k lasy o p e r a . N a d a liś m y m u n azw ę L o h e n g r i n . T eraz d o s k ła d n ik a b a l k o n w p isu je m y liczbę 6. O © W y w o ła n ie p u b liczn ej funkcji sk ład o w ej klasy o p e r a .
OO T u , w fu n k cji ma i n , z n a jd u je m y się p o za z ak re sem w a ż n o ś c i klasy. O d n ie sie n ie się w ty m m iejscu d o n azw y ś p i e w , p o w o d u je w ięc u ru c h o m ie n ie g lo b a ln e funkcji ś p i e w . T y lk o ta n a z w a ś p i e w je st w tym m o m e n c ie w ażn a.
W T en cały p r o g r a m m o ż n a w lu d z k im ję zy k u o p o w ie d z ie ć tak : Jesteśm y w d o m u - m ó w im y o w yjściu na balkon. W iad o m o , o k tóry b a lk o n n a m c h o d zi. W ieczo rem id z ie m y d o o p ery . W b u d y n k u k lasy o p e r a p o w ie d z e n ie „ b a lk o n " o d n o s i się ju ż d o z u p e łn ie in n e g o b alk o n u . T o o k reślen ie m a z a k re s w ażn o ści te g o b u d y n k u i tu ta j z a s ła n ia n asz d o m o w y b a lk o n . Jeśli je d n a k w e jd z ie m y n a scen ę, g d z ie z b u d o w a n a jest deko racja, to p o w ie d z e n ie „Ju lio , p ro s z ę w ejść na b a l k o n !" - o d n o s i się d o jeszcze in n eg o b a lk o n u . Tego lo k a ln ie z b u d o w a n e g o n a scenie. G d y k u r ty n a s p a d a k o ń czy się z a k re s w a ż n o ś c i lo k a ln e g o b a lk o n u s c e n ic z n e g o i z n o w u o zn acza o n balk o n na w id o w n i - te n z k la sy o p e r a . Jeśli w sw ojej p rak ty c e p o g u b is z się co, k ie d y i p rz e z co zo staje z a s ła n ia n e to m iej p reten sje d o siebie. O s trz e g a łe m C ię. Jeśli ty lk o m o ż n a , to u ż y w a jm y innych n a z w i nie p o le g a jm y n a z a s ła n ia n iu . W id z ie liśm y , ż e fu n k cja sk ła d o w a klasy, za sła n ia fu n k cję g lo b a ln ą o tej sam ej n azw ie . P o w ta rz a m : z a sła n ia ! N ie m a tu m o w y o ż a d n y m p rz e ła d o w a n iu . P rz e ła d o w a n ie n a z w y funkcji m o ż e n a stą p ić tylko w te d y , g d y fu n k cje m ają te n s a m z a k re s w ażn o ści. Funkcja n ie -s k ła d o w a m a z a k re s p lik u , w k tó ry m je st
Rozdział. 10. Klasy Przeładowanie i zasłonięcie równocześnie
439
zadek laro w an a. Funkcja składow a ma zak res swojej klasy. N ie następuje więc p rzeład o w an ie ty lk o zasłonięcie. Jeśli to jeszcze Cię n ie przekonuje, to przyjrzyj się argu m entom funkcji s p i e w tej globalnej i tej sk ładow ej. Są identyczne! (pusta lista) Taka sytuacja, że lista arg u m en tó w jest id en ty czn a - nie jest m ożliw a przy p rzeład o w an iu . Kompila tor zam eldow ałby błąd. Poniew aż jednak jest to zasłonięcie, d lateg o odm ien ność list a rg u m e n tó w przestaje być w ażna.
10.10.1
N ie sięgaj z klasy do obiektów globalnych Zasłanianie n azw jest pew nego rodzaju dobrodziejstw em , bo definiując swoją klasę m ożesz w y m y ślać dla jej sk ład n ik ó w -d an y ch d o w o ln e nazw y, nie przejm ując się czy tak ie nazw y nie (za)istnieją w globalnym zakresie Nigdy przecież nie wiesz, w jakim innym programie Ty, lub Twoi współpracownicy użyją tej Twojej klasy. Jeśli będzie ju ż tak a globalna nazw a - zo stan ie zasłonięta i konfliktu nie będzie. Tutaj dow iedziałeś się jak, ew entualnie d o takiej globalnej nazw y m im o w szyst ko m ożna się odnieść. N ie staraj się o to jednak. N ie staraj się z obszaru klasy pracow ać z n azw am i globalnym i obiektów - w szystko jedno czy nazw am i zasło n ięty m i czy nie zasłoniętym i. Twoja klasa, o ile to możliwe, nie powinna korzystać z nazw globalnych obiektów. . Po prostu powinna być, że tak powiem, samoistna, samowystarczalna.
Jeśli ktoś chce, by Tw oja klasa zrobiła coś, z jakim ś obiektem globalnym - niech Ci go przyśle jako arg u m en t funkcji składow ej. O dbierzesz to pod nazw ą o b i e k t n a _ k tó ry m _ m a m _ p ra c o w a c , i nie będziesz m usiał korzystać z jego n azw y globalnej. W przeciw nym razie Twoja klasa zaw sze będzie w y m ag ała istnie nia tak nazw anego obiektu globalnego. Ktoś, kto w łączy do swoje go p ro g ram u Twoją klasę, nie będzie m ógł go skom pilow ać, gdy nie będ zie miał obiektu globalnego o jakiejś n azw ie zaszytej na
stałe w T w ojej klasie.
10.11 Przeładowanie i zasłonięcie równocześnie W naszej klasie o p e r a jest jedna funkcja składow a ś p i e w , która zasłania globalną funkcję ś p ie w . Może być kilka funkcji składow ych ś p ie w . W tedy - na obszarze klasy - ta nazw a ś p i e w jest przeładow ana. W szystkie te funkcje ś p i e w mają bow iem ten sam zakres w ażności - zakres klasy o p e r a . Ta przeład o w an a już teraz nazw a ś p i e w zasłania nadal globalną n azw ę śp ie w . Inaczej m ów iąc, jeśli m am y takie funkcje
440
Rozdz. 10. Klasy Nowa klasa? Osobny plik! v o id ś p ie w ( ) ;
II globalna II fu n k c je s k ła d o w e k la s y o p e r a
void void void void
opera::śpiew(); opera::śpiew(int); opera::śpiew(double *); opera: .-śpiew(char *);
to przy jakim kolw iek w yw ołaniu funkcji ś p i e w z obszaru klasy o p e r a - przy do p aso w an iu w y w o łan ia do funkcji - brane będą pod u w a g ę tylko funkcje składow e. Funkcja globalna jest zasłonięta. P rzeład o w an ie funkcji składow ych klasy to bardzo częsta p raktyka. O sw oim y się z tym jeszcze.
10.12 Nowa klasa? Osobny plik! Istota p ro g ram o w an ia obiektow o orientow anego polega n a tym , że - do roz w iązan ia naszego ko n k retn eg o p roblem u - tw orzym y p ro g ram , w którym nie posługujem y się ju ż takim i prostym i obiektam i jak c h a r czy d o u b le , ale tw o rzy m y klasy, dzięki którym m am y d o czynienia z obiektam i o d p o w iad a jącym i p ew n y m pojęciom ze św iata rzeczyw istego. W ten sposób niejako od w zo ro w u je się rzeczyw istość w p ro g ram ie kom puterow ym . W ynika z tego p ro ste następstw o: zw y k le w naszym p ro g ram ie w ystępuje wiele klas. To oczyw iście jest w spaniałe - ale pojaw ia się problem :
Jak te klasy porozmieszczać w programie, który sam składa się z wielu plików?
Każda klasa w osobnym pliku.
C o to znaczy? O tóż, jeśli w łaśnie, w trakcie pracy nad p ro g ram e m , doszedłeś do tego, że "Koniecznie przydałoby się m ieć taką klasę, która..." - to w łaśnie znaczy, że teraz p o w inieneś stw orzyć n o w y plik, w k tó ry m d efin iq ę tej klasy um ieścisz. Z ap y tasz pew nie: -A co będzie, jeśli mam w programie 75 klas? Czy mam wobec tego mieć 75 plików? N ie p rzerażaj się tą liczbą 75. N ie p rzerażaj się n aw et liczbą 750. D opóki w pro g ram ie m asz m niej niż tysiąc klas, m o żn a nad tym n ieźle pan o w ać. Szczególnie, że teraz m a m y do dyspozycji ta k zw a n e scalone środow iska programowania. Jest to jakby p o łączen ie edytora z k o m pilatorem i deb u g g erem . Pracując w takim śro d o w isk u , g d zieś na b rzeg u e k ra n u , m asz okienko z nazw am i w szystkich sw oich klas. Są o n e u staw io n e
441
Rozdział. 10. Klasy Nowa klasa? Osobny plik!
w ty m p lik u jest. Z a te m d u ż a liczba p lik ó w nie jest p rz e sz k o d ą . P o w ied ziałb y m , ż e n aw et jest p o m o c n a, bo: *
Jeśli w jed n ej z funkcji sk ład o w y ch jakiejś klasy w p ro w a d z is z p o p ra w kę, to ty lk o te n plik b ęd zie m u sia ł być jeszcze ra z sk o m p ilo w an y . P ozostałe 999 p lik ó w nie. Z atem sk ra c a to proces b u d o w y p ro g ram u .
A le je st jeszcze le p szy p o w ó d : «$♦ G dy w in n y m sw o im (now ym ) p ro g ra m ie zechcesz s k o rz y sta ć z jakiejś z tych sw oich k la s - m asz ją g o to w ą, w o d ręb n y m p lik u . N ie m u sisz nic "chirurgicznie" w y cin ać z reszty te g o stareg o p ro g ra m u . O c z y w i ś c i e , z a s a d a : " K a ż d a k l a s a w o s o b n y m p lik u " - m o ż e m i e ć w y ją tk i.
T o ty sam w p ro w a d z is z te w yjątki. Jeśli n a p rz y k ła d p racu jąc n a d jakąś klasą d o jd z ie sz d o w n io sk u , ż e p rzy d ałab y się jej in n a p o m o cn icza klasa, p o trzebna n a k a ż e Ci u m ieść ją w ty m sam ym p lik u , co tę w łaściw ą. Z o b a c z y m y t e r a z p r o g r a m , w k tó ry m p o w in n y b y ć d e fin icje k la s o n a z w a c h :
osoba i b i l e t . P ro g ra m - jak na razie sk ład a się z d w ó ch plików . •
Je d e n - o n azw ie p i e r w s z y . c p p , w k tó ry m jest funkcja r a a in , (a m oże jeszcze jak ieś inne).
•
W d r u g im pliku, o n a z w ie d r u g i . c p p jest funkcja o nazw ie f u n k c j a _ w _ i n n y m _ p l i k u (i m oże jeszcze jakieś inne).
M am y w ięc d w a pliki: p i e r w s z y , cp p o r a z d r u g i . cp p . P ytan ie:
. . . . I W k tó ry m z nich um ieścić definicję klasy osoba i definicję klasy I bilet?
.
O d p o w ied ź: I W żad n y m ! K lasy te u m ieszczam y w całkiem o so b n y ch plikach.
Z a jm ijm y s ię n a jp ie rw k l a s ą
osoba.
R obim y tak: definicję k lasy u m ieszczam y w pliku o n azw ie o s o b a . h Tak! Definicję klasy um ieszcza się w osobnym pliku nagłówkow ym . #ifndef OSOBA_H #define OSOBA_H
//* * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * *
// Plik: osoba.h
II
442
Rozdz. 10. Klasy Nowa klasa? Osobny plik! //************************************** #include < i o s t r e a m > using namespace std; / / / / / / //////////////// definicja class osoba
klasy ////////////////////////
{ char n a z w i s k o [80]; int wiek; public: void zapamiętaj (const char * napis,
int lata) ;
void wypisz ()
//O© //O©
{ cout << "\t" « << wiek «
nazwisko « endl;
”, lat:
"
} ); / / / / / / ////////////// koniec definicji klasy /////////////////// #endi f
J e st to p lik n a g łó w k o w y , d la te g o że b ę d z ie m y g o w sta w ia li n a p o c z ą tk u w s z y s t k ich p lik ó w n a s z e g o p r o g r a m u , w k tó ry c h c h c e m y p o s łu g iw a ć s ię k la są o s o b a . O O T u , n a sam ej g ó r z e w id z is z k o n s tru k c ję b ę d ą c ą strażnikiem nagłówka, k tó ry o c h ro n i u ż y tk o w n ik ó w tej k la sy p rz e d o m y łk o w y m w s ta w ie n ie m te g o p lik u n a g łó w k o w e g o d w u k r o tn ie . O tym rozmawialiśmy w rozdziale o Preprocesorze, w § 6.10, str. 212. Przypom nę tylko, że polega to na ujęciu całego tego pliku nagłówkowego w dyrektyw y kompilacji warunkowej. Na samym dole tego pliku zauważ dyrektyw ę ł e n d i f . O © P rz y jrz y j się te ra z c ia łu tej k la sy . Ta d e fin ic ja k la s y - ja k o p lik n a g łó w k o w y b ę d z ie z a te m w s ta w ia n a d o w s z y stk ic h p o trz e b u ją c y c h jej p lik ó w n a s z e g o p ro g ram u . „-Jak to? " - zapytasz. "Czy to znaczy, Ż£ widoczna tu ta j definicja (ciało) funkcji składowej w y p i s z w ystępuje potem w programie wielokrotnie? Jest w każdym z tych plików? Przecież w tedy na etapie łączenia tych w szystkich plików w jeden program - linker zaprotestuje!". A je d n a k nie! P r z y p o m in a m , ż e ta k a d e fin ic ja fu n k cji s k ła d o w e j, k tó r a z n a jd u je s ię w o b rę b ie d e fin ic ji k la s y - s p ra w ia , ż e fu n k c ja ta je st tr a k to w a n a ja k o ty p u i n l i n e . In n y m i s ło w y : P o tem , w tra k c ie lin k o w a n ia , n ie m a n ig d z ie fu n k cji w y p i s z . W k a ż d y m m ie jsc u , g d z ie w y w o ła liś m y fu n k c ję w y p i s z k o m p ila to r w s ta w ił p o p r o s tu lin ijk ę cout << „\t" << nazwisko «
„,
lat:
" << wiek << endl;
b ę d ą c ą c iałem tej fu n k c ji. G d y b y je d n a k fu n k c ja s k ła d o w a nie b y ła i n l i n e , czy li: •
z d e f in io w a n a b y ła p o z a d e fin ic ją k la sy ,
• i w d o d a t k u n ie m iała p r z y s w e j d e fin ic ji p r z y d o m k a i n l i n e , to n ie m o ż e z n a le ź ć się w ty m p lik u n a g łó w k o w y m . B łę d e m b y by ło n a p r z y k ła d w s ta w ie n ie jej tu , w p lik u n a g łó w k o w y m z a r a z z a k o ń c e m defin icji k la s y .
443
Rozdział. 10. Klasy Nowa klasa? Osobny plik!
T ak d efin io w a n a funkcja m o ż e ju ż w p ro g ra m ie p o ja w iać się tylko raz. Inaczej n astąp i w sp o m n ia n y p ro te st linkera. Z obacz, ta k a właśnie jest sytuacja z funkcją z a p a m i ę t a j . Jej d efin icja nie jest w śro d k u definicji klasy. N ie m ogłem je d n ak p o staw ić p o n iż ej tej definicji. W łaśnie z tego p o w o d u . C o z a t e m z r o b i ć z d e fin ic ja m i fun k cji s k ł a d o w y c h te j k l a s y ?
D la nich tw o rz y się o so b n y plik - ty m raze m ju ż nie n a g łó w k o w y . Damy mu nazwę o s o b a . c p p W p lik u takim zn ajd u ją się ze b ra n e definicje w sz y stk ic h zw ykłych, (czyli n i e - i n l i n e ) funkcji sk ład o w y ch d an ej klasy. O to, jak w y g lą d a ten plik u nas:
I /★ ***★ ***************r * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *★ *★ *★ *★ // Plik: osoba.cpp //********************* ************************************* //© O #include "osoba.h"
5 /* * !* ? * * * * * * “ * ^* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * void oso b a ::zapamiętaj(const char * napis, strcpy(nazwisko, wiek = lata;
int lata)
(napis ? napis : "Anonim"));
) T u o czyw iście nie trzeba strażnika nagłówka, bo przecież nie jest to plik n a g łó w k o w y . . , ,^ . . , © O Z w ró ć u w a g ę , że n a p o czątk u tego plik u o s o b a . c p p jest d y re k ty w a i n c l u d e , za p o m o c ą której d o tego (zw ykłego) p lik u w staw iam y p lik n ag łó w k o w y oso b a.h N ic w ty m dziw nego. K o m p ilato r pracu jąc n a d ty m plikiem m usi ju ż z n a ć definicję klasy o s o b a . Inaczej od ra z u zap ro testo w ałb y w id z ą c zap is o s o b a : : z a p a m i ę t a j - bo co to jest o s o b a ? N ic o tym n ie wiem ! Z a te m s p ra w y z w ią z a n e z d an a k la są z a ła tw ia ją d w a p lik i. ♦♦♦ 1) Plik n ag łó w k o w y * . h, w k tó ry m jest definicja klasy. *
2) Plik zw y k ły * . c p p , w któ ry m są definicje jego funkcji składow ych (tych n i e - i n l i n e ) .
J a k p o w ie d z ia łe m , w n a s z y m n a z w ie
p r o g ra m ie p o tr z e b u je m y j e s z c z e k lasy o
b ile t - zatem zn o w u tw o rzy m y dw a pliki. O to p lik n ag łó w k o w y b i l e t . h #ifndef BILET_H_ tdefine BILET H
//*** * * * * * * * * * * * **************************
//© O
'jfop ¥ ;teqo ;suoo 'ifs *:teqD qsuoo ) Ce^ayuiEdez::}3 "[tq pxoA ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł *
J »
ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł »
»
ł *
ł ł ł *
ł »
ł »
»
ł l M
f ł ł ł ł ł ł / /
<6uxaąso> spnpouT# .'pqs aoedsauieu 6uxsn apniouj#
Q @ //
apnpouT#
ł ł f i f ł ł ł ł ł ł ł i U
ł ł ł ł ł ł ł ł ł ł ł K
ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł / /
ddD- }e ixq ł ł ł ł ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł ł ł ł »
iu A^Am z m
ł ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł »
»
»
ł ł ł »
//
ł ł ł ł ł ł ł ł ł *
ł ł ł *
ł / /
ddo • } 0Ti q 0jMZBU 0 n>)!|Cl (lllAMO>jMOł6BU-0!U) znf Awezozsejiun ^ 9 i i q Asem fo; 0MopB|>js 0fo>|un^ ©łEjsozod
■(BjBiułSi cb>fuaj e>[eł az 'ijsXuiod aiu 43/ a b u Xp9 iu j o ^ u i j o§tM 'ib^uaf fo ; u ia jsp - ib^unj (aj b i u b j o a y -Xm ai^psAzsM afnd^SBz jOłBiiduiosj z a p a z ij) -auriu-r Bb>[urij 04 4sa( o q 'afrp -sałOidBz aiu Ja^uij 'nuia[qoxd b u i aiu uiajBZ auT^uT >fauiopXzjd feiu p a zid 104S az ' z b m i i b z ajB 'A s b [>[ febiuijap Bzod znf BUBMOiuipp b u o łsaf ipXMopB{>[S ib^unj (a( z (aupaf B.bipp 4sa[ 'Asbm bbiuijap pod z b j b z 'n ^ jd 1x1X4 m az ' z b m i i b z q
04 4sa( aiu) -
t ą c n o f i m
i
t » ( i u z v u } S
q
q
q
•(DBiuaaop zsaiuzoBZ 04 o>(qXzs ajB '>{azbiAA.oqo aiqos Xuibimb4Sm hmouzdśim 'Xm o >{mo {8 bu q q jxpua#
{
.'ood » &Bxood x>(eC
} © © ' ( o o d n6Bxood- ęBzpox)n6BXOod- ęezpoj_uaxuiz : ^a^Tci PTOA a u TTu T ł ł ł ł ( M
H
ł ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł *
ł ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł / /
/////////////////////////////////////////////////////////////// .'OnfBzpoj sxdo * jeip 4 suoo .'feBjood t ^ b Ć n£>Bxood Cezpoa .'BRPflf ^UJ • [08]pe>f°p
•'[0 8 ]pe>[S Jeqo :aqeAXjd .'() zsxdAM px o A .'(ood nfieyood Cezpoa) n 6 exood~ Cezpoa uaxuiz pxoA
•(2 =_T> dux 'ood n 6 exood Cezpoa '>(op * xeqo 4 SU 0 0 '>(S *jeip }suoo) Ce^ayuiedez
pjo a
.'{ saads^a 'Auzsaxdsod 'AuozsaxdsAzad 'A«oqoso a a
//
} n6exood- Cezpoa amua :oxpqnd
^
} 19TTq
Q Q // ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł ł ł *
*
ł ł *
ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł ł ł »
*
ł »
ł ł ł ł ł ł ł ł ł ł ł ł ł »
ssbto ł »
ł ł / /
q-qaixq r^Tia //
jHIld Xuqoso 6kskPI kavo\[ X si?EH "OT ZPZ0M
ppp
445
Rozdział. 10. Klasy Nowa klasa? Osobny plik!
//© ©
r o d z ą j _ p o c ia g u p o c , i n t k l) s trc p y (s k a d , (s k ? sk : "B rak d a n y c h " ) ) ; s t r c p y ( d o k ą d , (dok ? dok : "B rak d a n y c h " ) ) ; ja k i_ p o c ia g = po c; k la s a = k l;
//* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * v o id b i l e t : : w y p i s z () c h a r k r e s k a [] = " [ ---------------------ch ar b rz e g i[ ] = "( cout « « « « « «
•] \ n " ; //© ©
] \ r [";
k r e s k a << b r z e g i " B i l e t : " « sk ą d « " — > " « do k ąd " , k l a s a " « k la s a « " \ n " b rz e g i •' na p o c ią g " « o p is _ r o d z a j u () « k re sk a ;
" \n "
/Z************************************************************* c o n s t c h a r * b i l e t : : o p i s _ r o d z a j u () t // tu kolejność tych nazw powinna odpowiadać kolejności II typu wyliczeniowego rodzaj_pociagu
c o n s t c h a r * n azw y [] = { "osobow y (lu b r e g i o n a l n y ) " , " p r z y s p ie s z o n y ( le k k o ) " , " p o sp ie s z n y " , " I n t e r c i t y e k sp re s" r e t u r n nazwy [ j a k i _ p o c i a g ] ;
II <—zamiana enum na int
//© O
//© ©
/ / ************************************************************* © O O czy w iście d y re k ty w ą i n c l u d e w łą c z a m y tutaj p lik n a g łó w k o w y klasy b ile t. © 0 W p lik u ty m w id z im y definicje ciał w szy stk ich zw ykłych ( n i e - i n l i n e ) funkcji sk ład o w y ch klasy b i l e t . S k o ń c z y l i ś m y z k l a s a m i , t e r a z n a s z e pliki p r o g r a m u .
Jak o się rzekło: są d w a . O to p i e r w s z y . c p p //////////////////////////////////////////////////////// / / p l i k p ie r w s z y p l i k . c p p ////
////////////////7/////////////////////////////////////// # in c lu d e < io s tre a m > u s i n g nam espace s t d ; t i n c l u d e " o s o b a .h " # in c lu d e " b i l e t . h "
// H
18
446
Rozdz. 10. Klasy Nowa klasa? Osobny plik! v o i d prez e n t a c j a ( o s o b a ) ; v o i d fu n k c j a _ w _ i n n y m _ p l i k u ();
//* ** ** * ♦ * * « ,♦ * * * * ł * * * * * * * * * * * * ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ int main () osoba kompozytor,
/ /@ g autor;
//©€
kompozytor.zapamiętaj("Fryderyk Chopin", a u t o r .z a p a m i ę t a j ("Marcel Proust", 34);
36);
// w y w o ł u j e m y f u n k c j e , w y s y ł a j ą c o b ie k ty prezentacja(kompozytor); prezentacja(autor) ; cout << "\nUzywamy w tym pliku tez klasy bilet zolty, niebieski; zolty.zapamiętaj("Frankfurt", z o l t y . w y p i s z (); niebieski.zapamiętaj("Zurich", ... ,. n i e b i e s k i .w y p i s z ();
"Paris",
'bilet'\n";
b i l e t ::ekspres,
//©© 1);
"Genewa", b i l e t ::p rzyspieszony);
niebieski zmien_rodzaj_pociagu(bilet:;pospieszny) ; cout << \nPo zmianę tego biletu...\ n " ; n i e b i e s k i .w y p i s z (); ^ funkc ja_w_innym_pliku () ;
//© ©
//©O
yy****ł*****Hł**ł*********ł**łt44tłtłłłł44łtiłtłłłłłł4łłiłttłł v o i d prezentacja (osoba ktoś) cout «
"Mam zaszczyt przedstawić państwu, "Oto we własnej osobie:"; k t o ś . w y p i s z ();
//©© \n"
© © Je st to p lik , w k tó ry m a k u r a t z n a jd u je się fu n k c ja m a in . © O P o n ie w a ż w ty m p lik u d e fin io w a n e są ja k ieś o b ie k ty k la sy o s o b a , w ię c a b y o m p ila to r w ie d z ia ł tu , co to je s t ta „ k lasa o s o b a " , m u s im y w s ta w ić d o te g o p lik u d y r e k ty w ą i n c l u d e p lik n a g łó w k o w y k la s y o s o b a . T o w s ta w ie n ie w id z isz w linijce © O . O © P o n ie w a ż b ę d z ie m y tu ta k ż e u ż y w a ć o b ie k ty k la s y b i l e t , d la te g o k o n ie c z n e je st ta k ż e w s ta w ie n ie p lik u n a g łó w k o w e g o k la s y b i l e t . @ 0 © O Jeśli p r o g r a m je st d u ż y , to z w y k le n ie w s z y s tk o u m ie s z c z a m y w je d n y m p lik u (ty m s a m y m , co fu n k cja m a in ) . W id z im y tu w y w o ła n ie fun k cji, k tó re j n ie m a w ty m p lik u . Jej d e k la ra c ja je s t ty lk o tu ż p rz e d fu n k c ją m a in . © © W id z im y tu ta k ż e fu n k q 'ę o n a z w ie p r e z e n t a c j a . T ę fu n k c ję o m ó w im y so b ie n a s tę p n y m p a ra g ra fie .
447
Rozdział. 10. Klasy Nowa klasa? Osobny plik! D e f in ic ja te j f u n k c ji( a m o ż e j e s z c z e j a k i c h ś i n n y c h ) j e s t w p lik u
d r u g i , cpp
//////////////////////////////////////////////////////// // plik drugi.cpp //// //////////////////////////////////////////////////////// #include using namespace std; #include "bilet.h"
//<-- bo
vj
b ie ż ą c y m p lik u u ż y w a m y k la s y
bilet
© O
/^★ ★ ★ ★ ★ ★ ★ ***,****************nr********************************ilr*
void funkcja w innym pliku!)
(
-
-
//O©
-
cout «
"\nJestesmy w innym pliku programu, \n" "tu tez używamy biletow\n"; bilet biały; biały.zapamiętaj("Kraków", "Tarnów", bilet::osobowy); biały.wypisz (); cout « "Spieszy sie nam, zmieniamy bilet na taki:" << endl; biały. z m i e n r o d z a j_pociagu (bilet: :pospieszny); biały.wypisz();
//©©
//©O
© 0 P o n iew aż w ty m plik u używ ać b ę d z ie m y o biektów klasy b i l e t , dlatego w id z im y na g ó rz e pliku d y rek ty w ę i n c l u d e w staw iającą n ag łó w ek z definicją tej klasy. P o n iew aż w tym pliku NIE u ż y w a m y obiektów k la sy o s o b a , w ięc N IE jest k o n ieczne w staw ian ie jej pliku n ag łó w k o w eg o .
W Z o b a c z y l i ś m y r o z ł o ż e n i e defin icji k la s w r ó ż n y c h p lik a c h - s z c z e g ó ł o w o , w ię c m o ż e p r z y d a ło b y s ię o g ó ln ie js z e s p o jrz e n ie .
O to sch em at te g o ro zło żen ia plików:
448
Rozdz. 10. Klasy Nowa klasa? Osobny plik!
class osoba
^*M»M***»***M*»»*M»' ewentualnefunkcje inli definiowanepozakiasq
^pierwszy.cpp łinolud* "o»oba.h"> łineludo "bilat.h"^
s. 10-1. Pliki programu, w którym zdefiniowano dwie klasy. Ponieważ w pliku pierwszy.~p p arzystamy z obu klas - należało tam wstawić (dyrektywami Unclude) p (ki nagłówkowe obu klas. lik drugi.cpp korzysta tylko z klasy bilet, więc potrzebny mu jest tylko plik nagłówkowy bilet.h (klasa osoba go nie interesuje).
B ard zo d o b r e n a rz ę d z ie !
Jak zau w aży łeś, w k ażd a klasa m a tu swój plik n ag łó w k o w y * . h i p lik - . c p p w k tó ry m są d efiniqe jej fu n k q i składow ych. Z atem p rzy p ad k u 75 k la s będzie p o trze b a 150 plików. N ie jest to jed n ak żad en kłopot, bo te scalone środowiska programowa nia, o których w spom inałem n ie d aw n o , tę pracę w y k o n u ją za nas.
I
G d y chcesz zdefiniow ać klasę - w ybierasz o d p o w ied n ią ko m en d ę z m e n u (lub klik asz na ikonę) i jesteś p y ta n y o nazw ę tej n o w ej klasy. G dy o d p o w ie s z na p rz y k ła d k r y s z t a ł , z a k ła d a n e są dw a pliki: k r y s z t a ł . h o ra z k r y s z t a ł . c p p . W pliku n ag łó w k o w y m k r y s z t a ł . h au to m aty czn ie b ę d z ie sz juz m iał strażnika tego nagłówka o raz definicję tej k lasy k r y s z t a ł . O czy w iście na razie p u s tą , bez żad n y ch składników . D o d an ie sk ład n ik ó w d an y ch lub funkcji sk ład o w y ch d o tej k la sy - także z ałatw ia n am o d p o w ied n ia kom enda m en u tego scalonego śro d o w isk a . P odajesz n a przy k ład d ek laracje nowej funkcji sk ład o w ej (nazw a, a rg u m e n ty , typ re z u lta tu ) i w ted y ♦> deklaracja ta a u to m aty c zn ie zostaje u m ieszczo n a w o d p o w ie d n im m iejs cu p lik u n ag łó w k o w eg o , ♦♦♦ a ta k że definicja tej funkcji w o d p o w ie d n im miejscu plik u k r y s z t a ł . cp p .
Rozdział. 10. Klasy Nowa klasa? Osobny plik!
449
C iało tej funkcji je st oczyw iście puste, ale teraz w łaśnie m asz w kroczyć Ty, ze sw oją sztu k ą pro g ram o w an ia. W instytutach fizyki jądrowej najczęstszym obecnie systemem operacyj nym - stosowanym w komputerach obsługujących eksperymenty - jest chyba Linuks. Wsystemie tym jest dostępne (darmowe!) środowisko zwane kdevelop. Z tym programem spędzam najwięcej czasu. N a w ia s e m m ó w iąc:
C zy nie od n o sisz w rażen ia, że n ap raw d ę nasza zasada p o w in n a brzmieć: "Każda klasa w osobnych dwóch plikach"
N ie napisałem tak je d n ak , bo przecież m o żesz mieć p ro stą klasę, której w szyst kie funkcje są zd efin io w an e już ciele klasy. W tedy ten jeden plik nagłów kow y w ystarczy.
To w w łaściw ie tyle, o tym m iał być ten p aragraf. Skoro jed n ak ju ż napisaliśm y taki p ro g ram , to go skom pilujm y i w ykonajm y.
Co zobaczymy wtedy na ekranie? Mam Oto Mam Oto
zaszczyt przedstawić państwu, we własnej osobie: Fryderyk Chopin, lat: 36 zaszczyt przedstawić państwu, we własnej osobie: Marcel Proust, lat: 34
Używamy w tym pliku tez klasy 'bilet' [ Bilet: Frankfurt — > Paris, klasa 1 [ na pociąg Intercity ekspres
[ Bilet: Zurich — > Genewa, klasa 2 [ na pociąg przyspieszony (lekko)
Po zmianę tego biletu... [ Bilet: Zurich — > Genewa, klasa 2 [ na pociąg pospieszny
Jesteśmy w innym pliku programu, tu tez używamy biletów [ Bilet: Kraków — > Tarnów, klasa 2 na pociąg osobowy (lub regionalny) Spieszy sie nam, zmieniamy bilet na taki: [ Bilet: Kraków — > Tarnów, klasa 2
450
Rozdz. 10. Klasy Nowa klasa? Osobny plik! na pociąg pospieszny
w programie jest jeszcze
] ]
kilka nowych, ciekawych rzeczy
T y p w y lic z e n io w y w k lasie
O © Oto, jak w klasie można zdefiniow ać sobie typ w yliczeniow y. W naszym p rzy p ad k u opisyw ać będzie on typ pociągu. Poszczególne nazw y z tej listyw yliczeniow ej są dostępne sp o za obszaru klasy - bo ty p w yliczeniow y jest p u b lic . © © Oto p rz y k ła d użycia nazw y z listy wyliczeniowej, jako arg u m en tu funkcji zapamięta j .Jak w idzisz, n azw ę z listy wyliczeniowej trzeba tutaj p o p rzed zić kwalifikatorem zakresu (nazwą klasy i podw ójnym dw ukropkiem ). niebieski .zmien_rodzaj_pociagu(bilet::pospieszny),
M usim y tak , bo w yw ołanie to jest przecież poza zakresem klasy. G dybyśm y byli w zak resie klasy - czyli w ciele jakiejś jej funkcji składow ej, to kw alifikator zakresu k lasy ( b i l e t : : ) byłby oczyw iście niepotrzebny, w ystarczyłaby sam a nazw a p o s p i e s z n y . A r g u m e n t y d o m n i e m a n e w fu n k cji s k ł a d o w e j
© O Z auw aż, ż e funkcja składow a klasy m oże mieć także arg u m e n ty d o m n iem an e (wartości d o m n iem an e argum entów ). O czyw iście um ieszcza się je w deklaracji tej funkcji. Tutaj w idzim y, ż e - p r z e z d o m n ie m a n ie -je śli nie określim y inaczej, będzie to b ilet klasy 2. (Oczywiście klasy kolejowej, nie k lasy C++). © O W funkcji m a in definiujem y kilka obiektów klasy o s o b a i pracujem y na nich. Klasę tę z n a m y z poprzednich p arag rafó w , więc o m ów ienie jest niepotrzebne. © © D efiniujem y tutaj także kilka ob iek tó w klasy b i l e t . O © D efiniga tej klasy pokazuje nam , że jej składnikam i są tablice określające sk ąd i dokąd jest bilet, składnik określający klasę pociągu (kl. 1, czy kl. 2). Jest te z składnik ty p u w yliczeniow ego r o d z ą j _ p o c i a g u , m a ten sk ładnik n a z w ę ja k i_ p o c ia g . O © Deklaracja funkcji składow ej z a p a m i ę t a j - m a, jak m ów iliśm y, a rg u m e n t d om n iem an y , natom iast... © © ...jej definicja już nie pow tarza w artości dom niem anej tego arg u m en tu . R z u t Twojego w p ra w n e g o oka p rzek o n a Cię, że funkcja ta słu ż y d o w pisania in fo r macji na te m a t dan eg o biletu. O © Funkcja z m i e n _ r o d z a j _ p o c i a g u jest deklarow ana tutaj, a (dla ćw iczenia) zdefin io w an a poniżej klasy (© © )• Skoro ma tu p rzy d o m e k i n l i n e to w olno jej być w pliku nagłów kow ym . © © Funkcja sk ła d o w a w y p is z - po p ro stu w ypisuje na ek ran ie zaw arto ść b iletu. Nic w niej specjalnego, poza sp ry tn y m użyciem znaku ' \ r ' , k tóry p o w o u je pow rót d o p o czątk u linii. D zięki tem u chw ytow i u d a je m i się na ek ran ie
451
Rozdział. 10. Klasy Nowa klasa? Osobny plik!
n ary so w ać ła d n ą , ró w n ą ram k ę - n ie zale żn ie od tego jak k ró tk ie /d łu g ie są n a z w y stacji p o c zą tk o w ej i stacji docelow ej. Najpierw rysujemy ramkę, a potem wracamy na początek linijki i zabieramy się do wypisywania w niej nazw stacji. © O Funkcja o n a z w ie o p i s r o d z a j u, słu ż y d o u zy sk an ia sło w n eg o , n a w e t kilkuw y razo w eg o , o p isu d a n e g o biletu. Zauważ też, że jest to prywatna funkcja tej klasy. Jest ona tylko na użytek tej klasy - korzysta z niej funkcja w y p is z . C zy p o trafiłb y ś p rz e c z y ta ć na głos d ek larację obiektu n azw y ? const char *nazwy[] = {
"osobowy (lub regionalny)", "przyspieszony (lekko)", "pospieszny", "Intercity ekspres"
// © O
O czyw iście! n a z w y — to tablica (ilu ś-tam elem en to w a) w sk a ź n ik ó w d o (ciągów) z n a k ó w . W deklaracji jest p rz y d o m e k c o n s t w ta k im miejscu, że o z n a c z a o n , iż w sk aźn ik i te n ie m ają p raw a m o d y fik o w a ć ow ych C -strin g ó w . Ilu e le m e n to w a jest ta tablica? N a w ia s k w a d ra to w y je st pu sty , a to o z n a c z a , ż e k o m p ilato r sam m a te C -stringi przeliczy ć. (Jest ich 4). P rz y p o m in a m , że w id z im y tu 4 stałe d o sło w n e ty p u C -strin g (łań cu ch znaków ). T akie stałe d o s ło w n e k o m p ilato r u m ie sz c z a g d zieś w p am ięci (jako obiekty statyczn e), a d o tej tablicy 'n a zw y ' —w p is z e n am ich ad resy . Z a pom ocą tak p o sia d a n y c h a d re s ó w nie m am y jed n ak p ra w a zm ienić tych C -strin g ó w . (Jest to bow iem : tablica w s k a ź n ik ó w do stały ch ). U ż y c ie o b ie k tu ty p u w y lic z e n io w e g o , ja k o in d e k s u tab lic y
© © Funkcja ta na k o ń cu m a instrukcję: return nazwy [jaki_pociag];
// <~ z a m ia n a enum n a int
P rz y p o m in a m , że j a k i _ p o c i a g jest o b ie k tem typu w y lic zen io w eg o r o d z a j p o c i ą g u . W tych k lam rach [ j a k i _ p o c i a g ] k o m p ilato r o czek u je w artości liczbow ej tego in d e k su tablicy. T y m czasem stoi tam o b iek t enum . N ie m a p ro b lem u , jest k o n w ersja —zw an a (b ezstratn y m ) awansem - k tó ra p o trafi zam ie nić obiekt enum , na o d p o w iad ającą m u w d a n e j chw ili w arto ść liczb o w ą ( było o ty m na str. 4 0 4 r Z atem n a z w a p o s p i e s z n y m oże się zam ien ić na w arto ść 2, b o taką ma przecież n a liście w yliczeniow ej. 5)
Początkującym odradzałem czytanie owego paragrafu.
452
Rozdz. 10. Klasy Przesyłanie do funkcji argum entów będących obiektami Z n o w u p rz y p o m in a m , ż e s ta łe d o s ło w n e b ę d ą c e C -s trin g a m i s ą w y ją tk o w o o b ie k ta m i s ta t y c z n y m i (a n ie a u to m a ty c z n y m i)
Z au w aż też, że instrukcją r e t u r n zw racam y jed en z adresów umieszczonych w lo k a ln ej, autom atycznej tablicy nazw y — ale jest to bezpieczne. Nie jest to ten przypadek, gdy ktoś się udaśnie wyprowadza, a na schodach podaje nam swój (stary adres), który po zakończeniu funkcji przestanie być aktualny. C -stringi te, (te stałe d osłow ne „ I n t e r c i t y e k s p r e s " ) są przecież przez ko m pilator realizow ane jako statyczne, więc nie giną. Są gdzieś poza tą funkcją. Tutaj, p rz y instrukcji r e t u r n , korzystam y z lokalnej tablicy nazw y. Tablica ta (jako autom atyczna) rzeczyw iście za chwilę p rzestan ie istnieć, ale ma ona zap isan e adresy miejsc poza tą funkcją. Tablica zginie, ale nie zginą te obiekty, których adresy zaw ierała. Czyli to tak, jakby ktoś wyprowadzając się podał nam na klatce schodowej adres wieży Eiffla. Ten dom i klatkę schodową, co prawda, za chwilę zburzą, ale wieża Eiffla jest bardzo statyczna - i jej to nie dotyczy.
Zobaczyliśm y w tym paragrafie jak rozm ieszczać klasy w plikach. T u chciałem zrobić zastrzeżenie: co p raw d a tak w łaśnie postępuje w mojej zw ykłej pracy, ais tutaj, n a uży tek tej książki, w tych naszych program ach przy k ład o w y ch sposo bu tego nie będę stosow ał często. Pokazyw ane tu p ro g ram y p rzy k ład o w e są bow iem b ard zo krótkie, klasy w nich użyte - także. Z atem najczęściej w szystko mieści się w jednym bardzo m ałym pliku.
W N a zak ończenie przypom nę, że w tym paragrafie, oprócz p o k azania, jak ro z m ieszcza się w plikach definicje klas, zobaczyliśm y także: ♦$* definicję typu w yliczeniow ego w zakresie klasy i u ży w an ie sk ład n ik ó w tego typu; arg u m e n t dom n iem an y funkcji składow ej; «$♦ kolejne sprytne użycie białego znaku ' \ r '.
10.13 Przesyłanie do funkcji argumentów będących obiektami Jeśli u ż y tk o w n ik zdefiniow ał klasę, to oczywiście z m yślą, że zd efin iu je k o n kretn e eg zem p larze obiektów tego typu. Tak jak p ro jek tan t pralk i m a nadzieję, że fab ry k a, na podstaw ie jego projektu, w y p ro d u k u je kilka eg zem p larzy o b iek tów k lasy pralka. D efiniujem y konkretny obiekt jakiejś klasy i m a m y w ted y jakby n o w ą zm ienną: zm ien n ą typu zdefiniow anego p rzez u ży tk o w n ik a.
453
Rozdział. 10. Klasy Przesyłanie do funkcji argum entów będących obiekt ami
C ały w ysiłek tw ó rcó w języ k a C++ streścił się w tym , by p o słu g iw an ie się takim o b iek tem było id en ty czn e, jak posługiw anie się obiektem ty p u i n t czy d o u b le . M ięd zy innym i m u sim y m ieć m ożliw ość w y słan ia go do funkcji jako argum ent. Z azn aczam , że m ów ić tu będ ziem y jak a rg u m e n t w ysyła się d o fu nkcji w ogóle ue d o funkcji składow ych.
0.13.1
Przesyłanie obiektu przez wartość P rz e z d o m n iem an ie za k ła d a się, że obiekt p rzesy łan y jest d o funkcji przez w artość. C zyli tak sam o, jak to się o d b y w a z d an y m i typu i n t czy d o u b le . P o k ażem y to n a p rzy k ła d zie klasy o s o b a , znanej n am z p o p rzed n ich p arag rafó w . # i f n d e f _ o so b a_ h _ # d e f in e _ o so b a_ h _ //************************************** / / P l i k : o s o b a .h //************************************** # in c lu d e < io s tre a m > u s i n a nam espace s t d ; ////////////////// d e f i n i c j a k la s y / / / / / / / / / / / / / / / / / / / / / / / / / c l a s s o so b a { char nazwisko[80]; i n t wi ek; p u b lic : v o id z a p a m ię ta j( c o n s t c h a r * n a p i s , i n t l a t a ) ; / / -------------------v o id w y p i s z () cout « «
"\t" « w iek «
n azw isko « e n d l;
", l a t :
"
/////////////////// koniec definicji klasy /////////////////// #endif
P lik z d e f i n i c j a m i f u n k c j i s k ł a d o w y c h /z********************************************************** // Plik: osoba.cpp **** //********************************************************** #include "osoba.h" #include //************»************************************************ void osoba: :zapamiętaj (const char * napis, int lata) strcpy(nazwisko, wiek = lata;
)
(napis ? napis : "Anonim"));
454
Rozdz. 10. Klasy Przesyłanie do funkcji argum entów będących obiektami W r e s z c i e n a s z w ł a ś c i w y p lik p r o g r a m o w y :
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /{{{ IIII / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / z / 77777 // plik progr.cpp
finclude using namespace std; #include "osoba.h" II void prezentacja (osoba); // .L Z********************************************************* int main()
(
n
'
I I €)
osoba kompozytor, autor; kompozytor.zapamiętaj("Fryderyk Chopin", 36); autor.zapamiętaj("Marcel Proust", 34);
// wywołujmy funkcje, wysyłając obiekty
// //
prezentacja(kompozytor); prezentacja(autor);
z* *** ** *** ** *** ** *** ** ** ** ** ** *** ** *** ** *** ** *** ** * * * ********* J void prezentacja(osoba ktoś) { . cout << "Mam zaszczyt przedstawić państwu, "Oto we własnej osobie: "; ktoś.wypisz();
// ^ . „ \n
// ©
}
□
Po wykonaniu tego programu na ekranie pojawi się Mam Oto Mam Oto
zaszczyt przedstawić państwu, we własnej osobie: Fryderyk Chopin, lat : 36 zaszczyt przedstawić państwu, we własnej osobie: Marcel Proust, lat : 34
Komentarz O Jeśli chcem y w pliku próg.cpp używ ać klasy osoba, to d y rek ty w ą # include - w sta w ia m y do niego definicję tej klasy (zn ajd u jącą się w p lik u n ag łó w kow ym ). 0 Ta funkcja jest w łaściw ie p rzed m io tem naszego p rz y k ła d u . Jest to funkcja, dc której w ysyła się jeden a rg u m e n t będący o b iek tem klasy osoba. Deklaracja czytam y: prezentacja jest funkcją w y w o ły w a n ą z jednym argumenterr k la sy osoba. Funkcja zw raca ty p void (czyli nic n ie zw raca). Jak w id zisz, jest to zw y k ła g lo balna funkcja - n ie jest ona sk ła d n ik ie m żad n e klasy. To ty lk o jej a rg u m e n t jest dla nas interesujący. © D efinicja d w ó ch k o n k retn y ch obiektów klasy osoba. Z araz p o tem w pisujem y d o tych obiektów inform acje, które mają p rzech o w y w ać. To ju ż o d daw ne zn am y .
Rozdział. 10. Klasy Przesyłanie do funkcji argum entów będących obiekt ami
455
456
Rozdz. 10. Klasy Przesyłanie do funkcji argum entów będących obiektami m n ie, iż w ysyłam o b ie k t d o funkcji p rzez w arto ść. W olno ta k p rzesy ła ć, ale n a w e t sam k o m p ilato r to o d ra d z a - Co robić? Jest w yjście i to sp ecjaln ie w ym yślone w ty m celu. N azy w a się: p rzesy łan ie p rz e z referencję. M ó w iliśm y o takim p rz e sy ła n iu w je d n y m z p o p rz e d n ic h roz d z ia łó w (§ 5.5, str. 142) obiecując sobie ró w n o cześn ie nie n a d u ż y w a ć tego sp o so b u .
T e r a z n a d e s z ł a g o d z in a u ż y w a n i a re fe re n c ji. P o to z o s ta ł y w y m y ś l o n e .
C zy m jest referencja w p rz y p a d k u o b iek tu z d e fin io w a n e g o p rzez u ż y tk o w n ik a ? O czyw iście ty m sa m y m , czy m jest d la ty p ó w w b u d o w a n y c h - czyli po prostu p rz e z w isk ie m . N ie p rz e z w is k ie m kla sy, ale d a n e g o eg zem p larza jej o b ie k tu . W y sy łając tak i e g z e m p la rz obiektu do fu n k cji n a z a sa d z ie " p rz e sła n ia p rzez referencję" - s p ra w ia m y , że nie jest on k o p io w a n y . F unkcja m a d o s tę p do o ry g in a łu . Tyle, że w funkcji m ów i się n a n ie g o u ży w ają c p rz e z w is k a . O to p rz y k ła d funkcji p r e z e n t a c j a w w ersji z p rz e s ła n ie m p rz e z referencję: void prezentacja(osoba & ktoś) { . cout << "Mam zaszczyt przedstawić państwu, "Oto we własnej osobie k t o ś .w y p i s z ();
x \n"
1 P rz y w y k łe ś ju ż ch y b a, ż e je d y n a zm ian a, ja k ą n a le ż y zro b ić w fu n k cji, to w sta w ie n ie z n a k u a m p e rs a n d & p rz y definicji a r g u m e n tu fo rm a ln e g o .
Jeśli ju ż rzuciłeś się do przerabiania poprzedniego programu, to nieśmiało zaznaczę, że poprawkę należy zrobić nie tylko w definicja fu n kcji p r e z e n t a c j a © . Także w jej deklaracji, czyli w miejscu 0 ma być: v o i d p r e z e n t a c j a (o s o b a &) ; (Przepraszam za tak niski poziom tej uwagi). Z a s to s o w a n ie p rz e s ła n ia p rz e z referencję s p r a w ia , że ty m s a m y m fu n k cja p ra cu je te ra z n a o ry g in a le i n ie p rzesy ła d o fu n k cji całeg o 84 b a jto w e g o o b ie k tu . (O ty m , ż e o b ie k t klasy o s o b a m a u n as ro z m ia r 84 b ajty p rz e k o n a liś m y się p a rę s tro n w cześn iej). W e w n ą trz fu n k cji p r e z e n t a c j a n a z w a k t o ś n ie jest te ra z n a z w ą k o p ii o b ie k tu , a le p o p ro s tu p rz e z w is k ie m n a d a w a n y m te m u - n a k o g o rz e c z tę fu n k cję w y w o ła n o . N a p rz y k ła d , g d y w y w o łaliśm y fu n k c ję n a rzecz k o m p o z y t o r a , to ten k t o ś , to jest teraz k o m p o z y to r . Istn iejąca w fu n k cji in stru k cja: k t o ś .w y p x s z ();
je st w y w o ła n ie m fu n k cji w y p i s z na rzecz s a m e g o k o m p o z y t o r a (a n ie na rz e c z je g o k o p ii). N ie m u s z ę d o d a w a ć , że ty m s p o s o b e m fu n k cja p r e z e n t a c j a m o ż e n a w e t d o k o n a ć z m ia n n a o ry g in ale.
457
Rozdział. 10. Klasy K onstruktor - pierwsza w zm ianka
:ruktor - pierwsza wzmianka Z ałó żm y , że m am y n astęp u jącą klasę class numer {
in t
liczba;
public: - f u n k c j e skła d o w e //• void schowaj(int n) liczba = n;
1 int zwracaj() { return liczba;
■: ■.
}
W k lasie m am y definicje d w u funkcji sk ład o w y ch . D ruga definicja jest dla oszczęd n o ści m iejsca za p isa n a w jednej linijce. To sam o oczyw iście m o żn a by zap isa ć w 4 linijkach. K lasa, jak w idać, jest schow kiem na liczbę ty p u i n t . Funkcja sk ład o w a s c h o w aj w k ła d a liczbę d o te g o schow ka, a funkcja sk ład o w a z w r a c a j - pokazuje, co w schow ku jest. Jeśli chcieliśm y stw o rzy ć sobie obiekt ty p u i n t , w którym sch o w am y liczbę 5, to d o tej p o ry w ystarczyła n a m instrukcja int skrytkaA = 5;
G d y to sam o chcielibyśm y zrobić posługując się naszą klasą n u m e r, to potrze b ny jest zapis numer skrytkaB; skrytkaB.schowaj(5);
W id zisz, że w m o m en cie definicji obiektu s k r y t k a B obiekt n ie jest o d razu inicjalizow any. A by z n alazła się w nim w arto ść 5, m usim y w n astęp n ej linijce p o słu ży ć się funkcją sk ła d o w ą s c h o w a j . W ynika z tego, że klasa n u m e r nie jest tak w y g o d n a w użyciu, jak zw y k ły typ w budow any i n t . A je d n ak celem m oim jest przekonanie Cię, ż e ty p y definiow ane p rz e z u żytko w n ik a (klasy) są tak sam o w ładne, jak typy w b u d o w a n e. C z y m o ż n a d efin icję o b ie k tu
i n a d a n ie m u w a rto ś c i z a ła tw ić w jed n e j
in stru k cji?
M ożna. R ozw iązanie jest p r o s te - posługujem y się w tym celu specjalną funkcją sk ład o w ą zw aną k o n stru k to re m . C h arakteryzuje się ona tym , że n a z y w a się tak sam o jak klasa. O to ta klasa w y p o sażo n a w konstruktor: class numer { int liczba;
458
Rozdz. 10. Klasy K onstruktor - pierwsza w zm ianka public:
/ / ----------------------------------------- funkcje składowe numer (int k)
{ liczba = k; )
//<------ konstruktor
void schowaj(int n) < liczba = n; ) int zwracaj () { return liczba; }
}; Z w ró ć u w ag ę, ż c p r z e d ko n stru k to rem n ie m a żad n e g o o k reślen ia ty p u w arto ś ci zw racanej. N ie m o ż e być tam n aw et ty p u v o id . Po p ro stu n ie stoi tam nic. S koro tak, to o czy w iście w ko n stru k to rze nie m oże w y stąp ić in stru k c ja r e t u r n zw racająca ja k ąk o lw iek w artość. O to, jak w p ro g ra m ie posługujem y się k o n stru k to re m klasy numer: nume r a = nume r (15);
d ru g i sp o só b jest taki: nume r b (15 );
P ierw szy sposób w y d a je się dłuższy, ale z a to b ard ziej zro zu m iały . D efiniujem y w pam ięci o b iek t a i d la niego w y w o łu jem y k o n stru k to r z a rg u m e n te m 15. D ru g a definicja o p u s z c z a z n a k rów ności i n a z w ę k o n stru k to ra - p rz e c ie ż n azw a k o n stru k to ra jest id e n ty c z n a z nazw ą klasy . N ajczęściej sto su je się tę w łaśnie form ę. T a k a s o b i e fu n k c ja ...
W z a s a d z ie w k o n stru k to rze n ie m a żad n y ch c u d ó w - tak a sobie funkcja sk ła d o w a . O sobliw ością jest to, że w y w o ły w a n a jest ona a u to m a ty c z n ie , ilekroć p o w o łu je m y d o życia n o w y o b ie k t d anej klasy. Jeśli k lasa n u m e r w y d a je C i się zb y t p ry m ity w n a , to ją ro z b u d u jm y . P rzed e w sz y stk im d o d a m y jeszcze składnik n a z w a o p isu jący z n a c z e n ie liczby, k tórą ta m p rzec h o w u jem y . #include #include using namespace std;
l/bo użyjemy: s trepy ()
//////////////////////////////////////////////////////////////z class numer { int liczba; char n a zwa[40]; public:
/ / ------------------------ funkcje składowe //k o n s tr u k to r - t y l k o d e k la ra c ja numer(int k, const char *opis);
// V
// dalsze funkcje składowe void schowaj(int m)
{ liczba = xn; melduj () ;
}
ł /
*&
459
Rozdział. 10. Klasy K onstruktor - pierwsza w zm ianka // —————— —— int
zwracaj ()
{ return liczba;
}
//
// ©
void melduj()
{
J
cout << nazwa << liczba << endl;
////////////////// k o n ie c d e fin ic ji k la sy I J l f l l l l l l l l l l l I I ! /'' J 1 U . ! I I H numer::numer(int k, const char *opis)
{ liczba = k; strcpy(nazwa,
(opis ? opis :
;
* ***************************************/ z*********************** int main() { numer
numer
samolot(1200, "Biezaca wysokosc "); atmosfera(920, "Ciśnienie atmosferyczne
),
// //
kurs(63, "Kierunek lotu ");
// wstępny raport samolot.melduj O ; kurs.melduj() ; atmosfera.melduj O ; cout «
"\nKorekta lotu ----- \n";
// O
samolot.schowaj (1201);
// ©
// zmiana kursu o 3 stopnic kurs.schowaj( kurs.zwracaj() + 3);
// ©
Hciśnienie spada atmosfera.schowaj (919);
1
Po wykonaniu tego programu na ekranie zobaczymy 3iezaca wysokosc 1200 Kierunek lotu 63 Ciśnienie atmosferyczne 920 Korekta lotu Biezaca wysokosc 1201 Kierunek lotu 66 Ciśnienie atmosferyczne 919
Komentarz O P o n iew aż ko n stru k to r nam się rozbudow ał, to p o stanow iliśm y um ieścić jego definicję na zew n ątrz definicji klasy. W ew n ątrz definicji klasy jest tylko jego deklaracja. Raczej dla o d m ian y niż z konieczności. O Z definicji tego k o n stru k to ra w idzisz, że - jako a rg u m e n t - p rzy sy łan y jest do niego tekst. Tekst ten w k ład a o n do tablicy nazwa - będącej sk ład n ik iem klasy.
460
Rozdz. 10. Klasy K onstruktor - pierwsza w zm ianka Z w ró ć u w a g ę n a nazw ę k o n stru k to ra - n u m e r : : n u m e r. To d latego, że jest on funkcją sk ła d o w ą klasy num er, a w d o d atk u sam (jako k o n stru k to r) nazyw a się tak, jak klasa - czyli n u m er. P rzed nazw ą konstruktora n ie m a żad n eg o okreś lenia typu z w ra c a n e g o (ani i n t , a n i d o u b le , ani n aw et v o id ) . 0 Funkcja sk ła d o w a s c h o w a j w y w o łu je teraz d o d atk o w o in n ą funkq'ę składow ą melduj. © Definicja fu n k cji składow ej m e l d u j . F u n k q a w ypisuje inform acje o tym , co przech o w u je, i jaką to m a w artość. © D la nas n ajw ażn iejszy m miejscem w tym pro g ram ie jest u ż y c ie ko nstruktora. W su m ie d efin iu jem y trzy obiekty k la sy n u m er. Robim y to n a d w a sposoby. 0 D rug i sposób - jest jakby o d p o w ied n ik iem zapisu: int
a = 5, b = 2;
© U życie funkcji m e l d u j w y w o ły w an ej na rzecz p oszczególnych obiektów klasy n u m e r. © D o korekty d a n y c h u ży w am y funkcji s c h o w a j . W ew n ątrz tej funkcji jest ju ż w yw o łan ie fu n k cji m e l d u j , więc n ie m u sim y tego robić o sobno. © To jest nieco b ard ziej sk o m p lik o w an e w y w o łan ie funkcji s c h o w a j . To sam o m o żn a prościej zap isać jako: int pomocnik;
pom ocnik = k u r s . z w r a c a j () + 3; kurs.schowaj(pomocnik);
W K o n s t r u k t o r j e s t f u n k c j ą , p r z y k tó rej n a j c z ę ś c i e j s p o t y k a s i ę p r z e ł a d o w a n i e nazw y
Inaczej m ó w iąc k o n stru k to r m oże m ieć kilka w arian tó w n a ró ż n e ew en tu a ln o ś ci - ró żn e z e s ta w y arg u m e n tó w inicjalizujących obiekt. T o, k tó ry k o n stru k to r zo stan ie u ru c h o m io n y , w ynika (jak to zw y k le p rzy p rz e ła d o w a n iu byw a) — z kolejności i ty p u a rg u m e n tó w w y w o łan ia. W eźm y taką k lasę: class pomiar int sec; public : pomiar(int sekundy)
{ sec = sekundy;
)
pomiar(int minuty, int sekundy) { sec = sekundy + 60 * minuty;
} pomiar(int godziny, (
int minuty,
int sekundy)
461
Rozdział. 10. Klasy K onstruktor - pierwsza w zm ianka
Załóżmy, że klasa ta służy nam do odmierzania czasu jakichś pomiarów. Rzut oka na konstruktory i widzimy, że powodują one wpisanie do składnika sec żądanego czasu w sekundach. Możliwe jest ewentualne przeliczanie godzin i m inut na sekundy. Ten zespól konstruktorów istnieje dla wygody, abyśmy używając tej klasy mogli wygodnie zadawać czas. Oto, jak tworzymy i inicjalizujemy obiekty tej klasy: p o m ia r te m p e r a t (5 , 0 ) ; p o m ia r p rę d k o ś ć ( 1 0 ) ; p o m ia r widma (2, 30, 0 ) ;
// <- użyty konstruktor (int, int) // <- użyty konstruktor (int) // <- użyty konstruktor (int, int, mt)
Każdy z tych trzech obiektów został inicjalizowany przy użyciu innego kon-
go obiektu wynosi 5 * 60 = 300 sekund Czy można obiekt t e m p e r a t zamiast tego inicjalizować jakimś innym kons truktorem? Oczywiście, te różne warianty są po to, by sobie życie ułatwić. Inne sposoby inicjalizacji tego samego obiektu tym samym czasem to: p o m ia r t e m p e r a t (300) ; albo p o m ia r t e m p e r a t ( 0 , 5 , 0 ) ;
Oczywiście tylko jedna z tych ewentualności - konstruktor, bowiem, jest uru chamiany tylko raz - wtedy, gdy obiekt powoływany jest do życia. N a z w a " k o n stru k to r" m o ż e b y ć n ie c o m y lą c a K onstruktor nie "konstruuje" obiektu, tylko nadaje mu wartość początkow ą. W tym sensie może lepiej byłoby go nazywać dekoratorem w nętrz. B ezpośred nio po zbudowaniu dom u w kracza, by go odpow iednio według życzeń klienta urządzić.
Czy konstruktor jest obowiązkowy? Nie. Najlepszy dowód, że do tej pory radziliśmy sobie bez niego. Można więc bez niego żyć. Ale po co? Do sprawy konstruktora jeszcze wrócimy w jednym z najbliższych rozdziałów (§15, str. 660), wtedy przyjrzymy mu się bliżej.
462
Rozdz. 10. Klasy D estruktor - pierwsza w zm ianka
10.15 D e s tru k to r - p ierw sza w zm ian ka P rzeciw ień stw em konstruktora jest destruktor: funkcja sk ład o w a w y w o ły w an a w ted y , g d y obiekt danej klasy ma być zlikw idow any. Do tego, ż e niektóre obiekty W idzieliśm y to w ielokrotnie nątrz funkcji definiow aliśm y gdy fu n k cja kończyła pracę -
są likw idow ane, ju ż ch y b a się przyzw yczaiłeś. w p rzy p ad k u ty p ó w w b u d o w a n y ch . Jeśli wew~ obiekt autom atyczny ty p u int, to w m om encie obiekt przestaw ał istnieć.
D o k ład n ie tak sam o jest w p rz y p a d k u obiektów ty p ó w definiow anych p rz e s u ży tk o w n ik a. W tedy w łaśnie autom atycznie u ru c h a m ia n y jest ich d e stru k to r. D e s tru k to r to ja k b y ta k a s p rz ą ta c z k a , któ ra s p rz ą ta n a k ilk a s e k u n d p rz e d z lik w id o w a n ie m o b ie k tu . N a jw a ż n ie js z e je s t to, że to n ie m y u ru c h a m ia m y tę s p rz ą ta c z k ę . P rz y s tę p u je o n a do p ra c y sa m a i robi to , c o na tę o k o lic z n o ś ć u s ta liliś m y .
D estru k to r to funkcja sk ład o w a klasy. D estruktor n a z y w a się tak sam o, jak k lasa z tym, ż e p rz e d nazw ą m a zn ak ~ (w ężyk). P odobnie jak k o n stru k to r - nie m a o n określen ia ty p u zw racanego. O to k lasa w y p o sa żo n a w d estru k to r. D estruktor ten n iczeg o w zasad zie n ie sp rząta, ty lk o d u żo m ów i. D zięki tem u w id z im y , kiedy obiekty są lik w id o w an e. #include H bo użyjem y: s trepy () #include using namespace std; /////////////////////////////////////////////////////////////// class gaduła { int licz; char tekst [40]; public : //konstruktor gaduła(int k, const char *opis); //destru ktor - deklaracja '-gaduła (void) ;
// O
//inne funkcje składowe int zwracaj() { return licz; } void schowaj(int x) ( licz = x; } void coto() { cout << tekst << " ma wartość "<< licz << endl; } 1; /////////////////////////////////////////////////////////////// gaduła:: gaduła (int k, const char *opis) / / konstruktor {
strepy (tekst, (opis ? opis : licz = k; cout << "Konstruuje obiekt " << tekst «
endl;
Rozdział. 10. Klasy D estruktor - pierwsza w zm ianka
463
/*****************************»******************************!/ gaduła::-gaduła () //destruktor & cout « «
"Pracuje destruktor (sprzata) " tekst « endl;
Z * ,* * * * * * * * * * * * * * * * * * * '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ti
gaduła a(l, "obiekt a :GLOBALNY)") ; U ® gaduła b(2, "obiekt b (GLOBALNY)"); . /*************************************************************/ int main() { a . coto(); b. coto(); { / /
// © // © // ©
Oto, co zobaczymy na ekranie po wykonaniu tego programu (O zn aczo n e p u n k ty o d p o w iad ają odpow iednio o zn aczo n y m p u n k to m w p ro gram ie. D o d atk o w o poniższe „wcięcie" -z o s ta ło przeze m nie zro b io n e sztu cz n ie - p o to, by w y d ru k sta ł się czytelniejszy.) Konstruuje obiekt obiekt a (GLOBALNY) Konstruuje obiekt obiekt b (GLOBALNY) obiekt a (GLOBALNY) ma wartość 1 obiekt b (GLOBALNY) ma wartość 2 Początek lokalnego zakresu ------Konstruuje obiekt obiekt c (lokalny) Konstruuje obiekt obiekt a (lokalny) Co teraz mamy : obiekt a (lokalny) ma wartość 40 obiekt b (GLOBALNY) ma wartość 2 obiekt c (lokalny) ma wartość 30 Do zasłoniętego obiektu globaln można sie jednak dostać obiekt a (GLOBALNY) ma wartość 1 Kończy sie lokalny zakres --------
464
Rozdz. 10. Klasy D estruktor - pierwsza w zm ianka Pracuje destruktor (sprzata) obiekt a (lokalny) Pracuje destruktor (sprzata) obiekt c (lokalny) Juz jestem poza blokiem obiekt a (GLOBALNY) ma wartość 1 obiekt b (GLOBALNY) ma wartość 2 Sam uruchamiam destruktor obiektu a Pracuje destruktor (sprzata) obiekt a (GLOBALNY) K o n ie c
p ro g ram u
© _ © _ _
!! !!!!!!
Pracuje destruktor (sprzata) obiekt b (GLOBALNY) Pracuje destruktor (sprzata) obiekt a (GLOBALNY)
Uwagi © D eklaracja d estru k to ra. Z au w aż brak określenia typu zw racanego. © Definicja d estru k to ra . D estruktor ten jest funkcją sk ład o w ą klasy g a d u ł a , a nazw ę m a - 'g a d u ł a . Razem w ięc m am y g a d u ł a : : - g a d u ł a () Tem u d e stru k to ro w i nie dajem y zw ykłego zad an ia sp rzątan ia, m a on tylko inform ow ać n as, kiedy pracuje. © W idzim y tu definicje dw óch obiektów klasy g a d u ł a . O b iek ty te są zd efiniow a ne poza w szelk im i funkcjami, a w ięc są globalne. N a ekranie w idzim y jak pracują ich k o nstruktory. O W ew n ątrz funkcji ma i n te d w a obiekty są już gotow e i m o g ą się przedstaw ić. © Za pom ocą k la m er definiujem y sztu czn ie pew ien lo k aln y zakres. 0 W tym lo k a ln y m zakresie defin iu jem y dw a obiekty k la sy g a d u ł a . Jeden m a nazw ę c, a d ru g i a. Tym sam y m globalna nazw a a zo staje zasłonięta p rz e z nazw ę lo k aln ą.
O N a d o w ó d teg o zasłonięcia - w id zim y , przy w y p isy w an iu nazw y a, że pracu je m y na o b iekcie lokalnym . T ym czasem nazw a globalna b jest dostępna, bo w zakresie lo k a ln y m nie zad ek laro w an o niczego o n azw ie b , czyli nic nie zasłan ia jej © P okazy w ałem ju ż kiedyś ten sposób d o stan ia się do zasło n iętej nazw y globalnej. G dyby je d n a k zasłonięty obiekt globalny a - nie był glo b aln y , lecz zdefinio w an y w f u n k q i m a in , to spraw a b yłaby stracona - nie m o g lib y śm y się d o te g o obiektu tak d łu g o dostać, jak d łu g o trw ało by zasło n ięcie (czyli do k o ń ca lokalnego zak resu ). © Koniec lo k aln eg o zakresu. K ończy się życie lokalnych o b ie k tó w w idzisz, ż e z a d z iałały ich d estru k to ry .
a ic. N a e k ra n ie
| Sam odzielnie. N ie u ru ch am ialiśm y ich ż a d n ą instrukcją. © O dsło n ięty z o sta ł obiekt globalny a. A by się d o niego o d w o łać, nie trzeba ju ż robić sztu czek z operatorem z a k re su . o o Tu w id zisz niezm iernie rzad k ą rzecz. D estruktor m o ż n a u ru ch o m ić jaw n ie. Z robi w te d y sp rzątan ie, ale oczyw iście nie zlik w id u je o b iektu. To jest ty lk o sp rzątaczk a. O © K ończy się pro g ram . L ikw idow ane są w szystkie istniejące jeszcze o b iek ty . A u to m aty czn ie uru ch am ian e są w te d y ich d estru k to ry . T u w id zim y , że p o raz;
465
Rozdział. 10. Klasy Składnik statyczny
d ru g i ru sz y ł d estru k to r obiektu a. To nas u p ew n ia, że w yw ołując p rzed chw ilą jaw nie d e s tru k to r - nie zlik w id o w aliśm y tego o b iek tu , tylko zro b iliśm y sprzątanie.
Mogą być różnice tego wydruku w stosunku do tego, co zobaczysz u siebie U w aga:
To kiedy dokładnie kompilator likwiduje niepotrzebne już obiekty jest zależne od konkretnego kompilatorać To jest jego osobista sprawa, wiec kompilując ten przykład za pomocą różnych kompilatorów, mozesz otrzy mywać program wypisujący teksty destruktora w nieco innym miejscu.
C zy d estru k to r jest koniecznie potrzebny? Nie.
Do czego więc destruktor może się przydać? W łaśnie p o to, by p rzed likw idacją obiektu p o sp rzątać śmieci. Z araz to w yjaśnię
f illlw k tam i są m iędzy innym i pojedyncze sam oloty lecące nad d an y m obsza rem . Jeśli sam olot ląduje, to obiekt rep rezen tu jący go jest likw idow any. P rzed likw idacją należy zadbać o zm azanie go z ekranu. ♦> P rzy k ład 2. Jeśli obiekt w trakcie swojego istnienia dokonał jakiejś rezer w acji w dostępnym z a p a sie pamięci, to p rzed likwidacją obiektu po w in n iśm y zw olnić tę rezerw ację. Zrobić to najlepiej w destruktorze.
A
P rzy k ład 3. Jeśli o biektem jest m enu nary so w an e na ekranie, a po w y b ran iu opcji m enu jest likw idow ane, to d estru k to r m oże posłużyć do o d tw o rzen ia p o p rzed n ieg o w yglądu ekranu.
D o d estru k to ró w pow rócim y jeszcze w innym ro zd ziale (§ 15.4, str. 681). Tutaj zasy g n alizo w aliśm y ich istnienie.
10.16 S k ła d n ik s ta ty c zn y Jak p am iętam y , k ażd y obiekt d anej klasy m a swój w łasn y zestaw danych. W y o b raźm y sobie klasę, która m a sk ład n ik -d an ą będącą zm ienną typu i n t . G dy zd efin iu jem y tysiąc obiektów tej klasy, w ów czas w pam ięci będzie oczywiście tysiąc o d ręb n y ch miejsc odpow iadających tem uż składnikow i. D zięki tem u, k ażd y eg ze m p la rz obiektu m o że w tym składniku przechow yw ać sobie swoją inform ację. 6)
________ _____________
W rozdziale o konstuktorze porozmawiamy o tym, jak kompilator może sobie pracę optymalizować.
466
Rozdz. 10. Klasy Składnik statyczny Są je d n a k sytuacje, g d y p oszczególne eg zem plarze obiektów danej klasy p o w in n y p o słu g iw ać się tą sam ą d an ą. P otrzeb a tak a zach o d zi w ted y , g d y dana ta dotyczy nie poszczególnych eg zem p la rz y o b ie k tó w tej klasy, ale sam ej klasy jako całości. (Cena pralki a u to m a tycznej d a n e g o ty p u [klasy] jest w spólna dla w szy stk ich pralek tego typu). M oże b y ć to na p rzy k ład z m ie n n a określająca ile ob iek tó w danej k lasy p ow o łano ju ż d o życia, m oże też b y ć to zm ienna określająca ile razy w y k o n an o jakąś funkcję n a w szystkich obiek tach tej klasy (obrazow o: ile w y p ro d u k o w an o pralek te g o ty p u , lu b ile ra z y w y k o n an o na tych p ralk a ch n ap raw g w aran cy j nych). T e inform acje są jak b y bard ziej inform acją o klasie, a nie o k o n k retn y m jej obiekcie. P roblem taki w k lasy czn y m p ro g ram o w an iu ro zw iąz y w ało się za pom ocą zm ien n ej globalnej. N ie je st to jed n ak ro zw iąz an ie najlepsze, a to z dw óch pow odów : 1) Z m ien n a globalna je st d o stęp n a nie ty lk o d la obiektów naszej klasy, a le także dla o b iek tó w innych klas; i w o g ó le dla każdego. W k a ż d y m b o w iem miejscu p ro g ra m u m ożna przecież p o słu ży ć się z m ien n ą g lo b aln ą. N ie m a w ięc m o w y o tym w sp a n ia ły m narzęd ziu jakim jest u k ry w a n ie inform acji. ♦> 2) N aw et, jeśli inform acja zaw arta w takiej zm ien n ej nie jest ściśle tajna n a m n a ż a n ie zm ien n y ch globalnych nie je st eleg an ck ą p rak ty k ą. T rzeba w ó w c zas db ać o to, b y n azw a, jaką n ad ajem y now ej danej globalnej, nie k o lid o w a ła z już istniejącym i. Co robić? R ozw iązaniem jest w łączen ie tej danej w o b ręb klasy w p o staci danej statycznej. D ana, która jest o k reślo n a jako staty czn a m a tę w ażn ą cechę, że jest w pam ięci tw o rz o n a jed n o k ro tn ie i je st w sp ó ln a dla w szy stk ich eg zem p larzy o b ie k tó w danej klasy. C o w ięcej: istnieje n a w e t w te dy, g d y jeszcze nie zd efin io w aliśm y a n i je d n eg o e g z e m p la rz a obiektu tej klasy. D zięki sta ty c z n y m d an y m s k ła d o w y m m ożna w y d a tn ie zm niejszyć liczb ę p o t rzebn y ch zm ien n y ch globalnych. D ana s k ła d o w a staje się staty czn a, g d y p rzed jej d e k la ra c ją w ciele k la sy u m ie ś cim y sło w o s t a t i c. class klasa { p u b lic : i n t x; static int składnik; 1; D eklaracja sk ład n ik a staty czn eg o w ciele klasy n ie je st jego definicją. D efinicję m u sim y u m ieścić g d zieś tak, b y m iała zak res p lik u . C zy li tak , jak b y śm y u m ieszczali d efin icję zm iennej globalnej. D efinicja taka m o ż e z aw ie rać inicjalizację. int klasa::składnik = 6;
467
Rozdział. 10. Klasy Składnik statyczny G d zie to u m ieścić k o n k retn ie?
Jeśli p o stą p iłe ś w e d łu g zasad y : "K ażda klasa w o d rę b n y m pliku (ściślej: w osobnych d w ó c h plikach)", to m a sz tak ie d w a pliki: ♦$* n ag łó w k o w y : k l a s a . h — m ieszczący definicję tej klasy (i m ięd zy in n y m i deklarację te g o sk ład n ik a statycznego) «♦* w łaściw y :
klasa.cpp — m ieszczący definicje jej funkcji sk ład o w y ch .
D e fin ic ję te g o s k ła d n ik a s ta ty c z n e g o p ro p o n u ję u m ie s z c z a ć na s a m e j g ó rz e p lik u w ła ś c iw e g o , te g o z d e fin ic ja m i fu n k c ji s k ła d o w y c h (c z y li tu ta j n a g ó rz e p liku o n a z w ie : klasa.c p p ) _________________
S kład n ik staty czn y m oże b y ć ta k ż e ty p u private. C iekaw e, że tak a inicjalizacja s k ła d n ik a staty czn eg o m o żliw a jest n a w e t jeśli jest on ty p u private. N a to m ia st p o in icjalizacji p ry w a tn y sk ładnik staty czn y nie m oże być przez
Są trzy sposoby odniesienia się do składnika statycznego: ♦♦♦ 1) Z a pom ocą n azw y k lasy i o p erato ra zak resu klasa::składnik ♦♦♦ 2) Jeśli istnieją już jakieś eg ze m p la rz e o b iek tó w klasy, to m o żem y p o słu ży ć się o p e ra to re m ' / obiekt.składnik *{* 3) Jeśli jest z d efin io w an y taki w sk aźn ik d o o b iek tó w klasy k la s a * w s k ;
to sto su jem y o p erato r - > wsk->składnik
Nie muszę chyba przypominać, że wskaźnik trzeba wcześniej ustawić tak, by pokazywał na jakiś dowolny obiekt tej klasy. (Wszystko jedno, na który, bo przecież dana statyczna jest wspólna dla wszystkich obiektów klasy - to tak, jakbyśmy powiedzieli - Ile cylindrów ma ten samochód? - Niezależnie na który egzemplarz Mercedesa 220 pokażemy - i tak dowiemy się tego samego. D w a o s ta tn ie sposoby są w ięc id en ty czn e, jak w p rz y p a d k u zw y k łeg o sk ład n ik a (n ie-sta tycznego).
Zilustrujmy to przykładem Z d e fin iu je m y sobie klasę opisującą pionki do g ry. W klasie d efiniujem y d w a sk ład n ik i staty czn e. Jeden b ęd zie m ó w ił ile istnieje ju ż eg zem p larzy p io n k ó w (ten s k ła d n ik będzie p u b l i c ) , a d ru g i będzie z aw ie rał inform acją o tym , jaka jest pen sja p io n k ó w (to będzie oczyw iście p r i v a t e ) .
468
Rozdz. 10. Klasy Składnik statyczny U w a g a : N ie p o s t ą p i ł e m tu w e d łu g z a s a d y " K a ż d a k la s a w o d r ę b n y m p lik u " . T e p r z y k ł a d o w e p r o g r a m i k i s ą ta k k r ó tk ie , ż e t a k je st p ro ś c ie j.
#include
////? //////? ? //////> /////////////////////////////////////////// class p ion
I private : int pozycja; s t a t i c int pensja; public: static
irrrzz ,.ir z !.* int
roiwscr* o vHHq ©5"k'q sn
//
o
//
o
//
€>
//
O
//
©
//
©
i
ile_pionkow;
/ / fu n k c je składowe // k o n s tr u k to r ■
p i o n () { pozycja — 0; i l e _ p i o n k o w ++;
■
& n9i* r^ C £ / / ------------int ile_zarabia () * ' rwłr-.l-s-JY- n * { //o s z u s t / r e t u r n pensja - 800; } }; /////////////////////////// int p i o n : : p e n s j a = 3000; int p i o n ::ile_pionkow;
J.rOK: Ot
/* * * ■ * * * * * * * * * * * * * * * * * * * * * • * * * * * * * * * * * * * * * * * * * * '* * * * * * * * * * * * * * * * * /
int m a i n O { cout
<< <<
"Poczat-pk p r o g r a m u , p i o n ::i l e p i o n k o w ;
pion
czerwony,
cout
<<
//o d c z y t y w a n i e
s
pionków
pionkow
\n";
■jfi.s.łyłA;
cout
< < " K l a s a m ó w i ze p i o n k ó w j e s t < < p i o n : :i l e _ p i o n k o w « en d l ; << "czerwony, ze " < < c z e r w o n y .i l e j p i o n k o w « endl; < < " z i e l o n y , ze " < < z i e l o n y . i l e p i o n k o w
pion
biały;
cout
=
in f o r m a c ji z a p i s a n y c h w d a n e j s t a t y c z n e j ( p u b lic )
1 cout
jest
//d e f in ic je p i o n k ó w
zielony;
"\nPo definicji
teraz
fł
®
//
©
< < endl;
//d e f in ic ja p io n k a
c o u t << "Po d e f i n i c j i j e s z c z e j e d n e g o < < z i e l o n y .i l e _ p i o n k o w < < e n d l ;
jest
ich
: "
Rozdział. 10. Klasy Składnik statyczny
469
// interesuje nas ile zarabia pionek (dana
II jedyna szansa to zapytać pionka funkcją składową • _ || cout << "Czerwony, ile zarabiacie ? << czerwony.ile_zarabia(); cout << "\nBialy, ile zarabiacie ? " « biały.ile_zarabia();
^
)
U im
Po wykonaniu programu na ekranie zobaczymy Początek programu, teraz jest pionków = 0 Po definicji pionkow Klasa mówi ze pionków jest 2 czerwony, ze 2 Po definicji jeszcze jednego jest ich : 3 Czerwony, ile zarabiacie ? 2200 Biały, ile zarabiacie ? 2200
Komentarz O D eklaracja (tylko deklaracja) p ry w atn eg o składnika statycznego. © D eklaracja (tylko deklaracja) publicznego składnika statycznego. © To b a rd z o ciekaw a rzecz. W ew n ątrz konstruktora um ieszczam y instrukcję in k rem en tu jącą daną ile_pionków. K onstruktor ru sza d o akcji w ted y , g d y defin iu jem y n o w y pionek. D zięki tem u załatw iam y sobie licznik, liczący ile obiektów k la sy pion p o w ołano d o życia. G Funkcja p r z e s u ń sym uluje ru ch pionka. Jest dla n as tylko atrapą, nie b ędziem y się nią zajm o w ali. © Funkcja, k tó rą d o w iadujem y się o w ysokość zaro b k ó w pionka. Ta d an a jest staty czn a, ale pri vate, w ięc z zew n ątrz nie m am y do niej dostępu. N atom iast z w n ę trz a funkcji składow ej jest ona dostępna tak, jak zw y k ła dana składow a. (Jak tu taj w id z im y pionek oszukuje i nie udziela p raw id ło w ej odpow iedzi). 0 M iejsce d e f in ic ji danych staty czn y ch . Są one defin io w an e tu, w miejscu o zasięgu g lo b aln y m . Poza w szelkim i funkcjami. Z au w aż, że: ♦♦♦ N ie m a tu słowa s t a t i c (potrzebne było tylko w ew n ątrz klasy).
470
Rozdz. 10. Klasy Składnik statyczny ♦> U ży ty jest tu operator zakresu - dzięki tem u kom pilator wie, że są to d a n e należące do określonej klasy. (Gdyby tego nie było, to pom yślałby, że to zw ykłe obiekty globalne). «$♦ M im o że pensja jest przecież składnikiem private - jest ona tutaj (p o za zakresem w ażności klasy) inicjalizowana. Inicjalizować wolno. ♦$* D ru g i składnik staty czn y (ten publiczny) m ógłby być rów nie dobrze inicjalizow any, ale nie robim y tego. W zw iązku z tym, podobnie jak zm ien n e globalne, inicjalizow any jest autom atycznie zerem. O Trzy om ów ione sposoby odczy tan ia składnika statycznego. © Na d o w ó d , że liczenie pionków działa, zdefiniow aliśm y jeszcze jeden pionek i znow u sp raw d zam y . © Ta instrukcja jest w kom entarzu. G dyby nie była - nastąpiłby błąd kompilacji. Składnik p e n s j a jest staty czn y m prywatnym, więc kom pilator nie u d o stęp n i go niepow ołanym . M ożna na nim pracow ać tylko za pom ocą funkqi składow ych klasy p i o n . © W yw ołujem y więc funkcję składow ą, aby dow iedzieć się o zarobki. Innej drogi nie m a.
W Jak w idać, składnik statyczny klasy deklarowanej globalnie zachow uje się tak, jakby m iał cechę external - czyli m ożna do n iego sięgnąć naw et z innego pliku składającego się na ten program . Typ sk ład n ik a statycznego nie jest w zbogacony o n azw ę klasy. To znaczy jego typ jest taki, jakby obiekt ten został zdefiniow any jako zw ykła zm ienna globalna. Uwaga: nie mówię tu o jego nazwie, ale o jego typie! D latego n asze oba składniki pensja, ile_pionkow, są p o prostu ty p u ir.t. G dyby nie były statyczne, b y ły b y typu pion::int.
Dla wtajemniczonych:
10.16.1
•
Składnik staty czn y może pojaw ić się jako arg u m e n t dom niem any jakiejś funkcji składow ej tej klasy. S kładnik nie-statyczny (czyli zw ykły) nie m ógłby.
•
Klasy, które są definiow ane lokalnie (czyli na p rzy k ła d w obrębie jednej funkcji - nie m ogą m ieć d an y ch statycznych).
Deklaracja składnika statycznego połączona z inicjalizacją W ty m parag rafie pokażem y n ied aw n o w p ro w ad z o n y d o języka C++ d o d a tk o w y sposób inicjalizacji składnika statycznego. Jest to b a rd z o łatw y parag raf, ale i tak bez w y rzu tó w sum ienia m o żesz go przy p ie rw sz y m czytaniu opuścić.
471
Rozdział. 10. Klasy Składnik statyczny
S z c z e g ó ln ie , ż e m o ż liw e , iż T w ó j k o m p i la t o r n ie m a je s z c z e z a i m p l e m e n t o w a n e j tej " n o w in k i" . (M ój k o m p i l a t o r G N U C + + w e r s ja 3 .3 .3 m a .)
Je śli w k la s i e p o tr z e b u je m y ja k ie ś s ta łe j c a łk o w ite j, ( k t ó r a b ę d z ie c e c h ą tej k la s y , a n ie p o j e d y n c z e g o o b ie k tu ) , to m o ż e m y to z r o b ić n a k i l k a s p o s o b o w . ♦> 1 . Z d e f i n io w a ć w te j k la s ie t y p w y lic z e n io w y , g d z i e n a liśc ie w y lic z e n io w e j u m i e s z c z a m y ( w y m y ś l o n ą p r z e z n a s o c z y w iś c ie ) n a z w ę tej s ta łe ), w r a z z p r z y p i s a n i e m jej k o n k r e tn e j w a r to ś c i. J a k w k la s ie d e f in iu je s ię t y p w y lic z e ń io w y
- z o b a c z y liś m
y w ty m r o z d z ia le
n a s tr o n ie 4 5 0 .
♦♦♦ 2. S tw o r z y ć s k ła d n i k s ta t y c z n y ty p u c a ł k o w i te g o c o n s t . B ę d z ie to s ta ła , w s p ó l n a d la w s z y s tk ic h o b ie k tó w tej k la s y . O c z y w iś c ie g d z ie ś p o z a c i a łe m k la s y te n o b ie k t d e f in i u je m y i n a d a j e m y m u w a r to ś ć p o c z ą tk o w ą ( in ic ja liz u je m y ) . To z n a m y z p o p r z e d n ie g o p a ra g ra fu . T e r a z z o b a c z y m y , ż e m o ż n a to z r o b ić je s z c z e in a c z e j — i p ro śc ie j. M o ż e m y ta k i s k ł a d n i k ( s ta ty c z n y c o n s t ) z d e k l a r o w a ć w k la s ie i ( u w a g a - u w a g a ! ) p r z y tej d e k la r a c ji o d r a z u p o s ta w ić im c ja h z a c ję . T aka
rz e c z
je s t z u p e ł n i e
n ie
do
p o m y ś le n ia w
s k ła d n ik a -d a n e j.
s to s u n k u . , ,,
do
z w y k łe g o •
C z y p a m i ę t a s z j a k k i e d y ś ( s t r . 4 2 3 ) m ó w i ł e m , ż e w c ie le k l a s y s k ł a d n i k i n i e m o g ą z a w ie r a ć in ic ja liz a to r a ? M ó w i ł e m w t e d y , ż e d o k u m e n t k l a s y p a s z p o r t , k t ó r e g o m a t r y c a (d e f i n i c j a )
j e s t w d r u k a r n i p a ń s tw o w e j , n ie z a w ie r a p r z e c ie ż w y d r u k o w a n e g o n a z w i s k a a n i d a t y u r o d z e n ia . J e s t ta m ty lk o m ie js c e n a n a z w is k o . N a to m ia s t d a n e ( in ic ja liz u ją c e g o ) w p i s u j e s ię d o p ie r o d o k o n k r e tn e g o e g z e m p la r z a o b ie k tu k la s y p a s z p o r t. . . N i b y p r a w d a , a l e m y p r z e c i e ż n ie m ó w i m y o n a z w i s k u i d a c i e u r o d z e n i a , ( k t ó r e k a ż d y p o k o r n y o b y w a t e l p o w i n i e n m i e ć i n n e ) , a le m ó w i m y o c z y m ś , c o j e s t w s p ó ln e d la w s z y s t k ic h . N a p r z y k ł a d j e s t to p a s z p o r t k t ó r y m a g d z i e ś s k ła d n ik „ g o d ło " . D la w s z y s tk ic h P o la k ó w s k ła d n ik g o d ło m a s ta łą w a r to ś ć „ O r z e ł w K o r o n ie " . T o j u ż m o ż n a b y w y d r u k o w a ć ... b o n ib y s k ł a d n i k , a le s t a ł y d l a w s z y s t k i c h p a s z p o r t ó w k l a s y P a s z p o r t _ P o la k a .
W s z y s t k o s t a n i e się z a r a z ja s n e , g d y z o b a c z y m y to w p r z y k ła d o w y m p r o g r a m ie , a l e n a j p i e r w z a z n a c z m y , ż e a b y t a k i e ( ła t w e ) p o s ta w ie n ie in ic ja liz a to r a k o ło s k ł a d n i k a d a n e j b y ło m o ż liw e m u s z ą z a j ś ć n a s tę p u j ą c e w a r u n k i . ♦♦♦ S k ł a d n i k te n m u s i b y ć ró w n o c z e ś n ie : •
s ta ty c z n y ,
•
s ta ły (const),
•
ty p u c a łk o w ite g o ,
472
Rozdz. 10. Klasy Składnik statyczny a je g o in ic ja liz a to r m u s i b y ć w y r a ż e n ie m s ta ły m .
O d a ls z y c h z a s tr z e ż e n i a c h i k o n s e k w e n c ja c h ła tw ie j b ę d z i e r o z m a w ia ć p o p rz y k ła d z ie . #include using namespace std; / / Uwaga: Nie wszystkie kompilatory są mają zaimplementowaną tę właściwość standardu. / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
class plansza_chinczyka { public: enum { max_pionow = 16, max_graczy = 4 static const int ile_pol = T 2 0 ; static const int i l e k o l o r o w = 4 ;
};
// © // © // ©
// funkcje składowe ---------void j edna_kol j ka () { for (int i = 0 ; i < max_graczy ; i++) { if (gracz( i ]) cout << "Twój ruch.
private: bool gracz [ max_graczy ] ; double pole( ile poi ]; / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /////,/ const int plansza_chinczyka::ile_kolorow ; // v£J //*********■*•*************■*************************************-* void wydrukuj_z_referencji( const int & ref ) // © ( cout << "(wydruk z referencji) wartość: ) Z************************************************************* void wydrukuj_ze_wskaznika< const int * wsk ) { cout << "(wydruk za pom. wskaźnika) wartość: << *wsk << endl; } Z*************************************************************/ int m a i n () { ................................ // plansza_chinczyka klubowa; // <— nie m usim y mieć obiektów cout << << << << <<
"Zanim zagramy, pytamy ile max graczy " // © plansza_chinczyka::max_graczy ", pol= " << plansza_chinczyka::ile_pol ", kolorow= " << plansza_chinczyka::ile_kolorow endl;
// żadnych problemów, jeśli jest osobna definicja składnika poza ciałem klasy cout << "Adres stałej plansza_chinczyka::ile_kolorow =
473
Rozdział. 10. Klasy Składnik statyczny
wydrukuj z referencji(plansza_chinczyka. ile_kolorow) ; / / O Q wydrukuj zs wskaźnika(&plansza_chinczyka :ile kolorow) ;/ / O ©
// W stosunku do typu wyliczeniowego poniższe instrukcje sq niemożliwe // cout « "Adres stałej plansza_chinczyka::ile_pol // << &plansza_chinczyka::ile_pol « endl; // wydrukuj_z_referencji(za_chinczyka::ile_pol);
11
// Składnik statyczny bez odrębnej definicji-ma te same 'wady", coenum cout << "Adres stałej plansza_chinczyka::max_pionow // << &plansza_chinczyka::max_pionow « endl; // wydrukuj_z_referencji(za_chinczyka::max_pionow); II
H
N ajp ierw oczyw iście rz u t oka na definicję klasy. O O to ty p w yliczeniow y zd efin io w an y w obszarze tej klasy. Jak w iem y, w taki sposób m o ż em y sobie rozw iązać problem stałych w klasie. O typie wyliczeniowym, takim jak tutaj, bez n a zw y, rozmawialiśmy na
str. 89. Z obaczm y jak, z tak zdefiniow anych stałych, m ożna o d razu skorzystać. O O to definicja składnika będącego tablicą. Rozm iar tak definiow anej tablicy ma być stałą. T ak też jest w naszym p rzypadku. Z listy w yliczeniow ej w iem y już, że max g r a c z y reprezentuje stałą w artość 4. © A oto in n y sp o só b definiow ania stałej, która będzie z n a n a w szystkim obiektom tej klasy. Jak w idzisz, jest to sk ładnik statyczny, stały i całkow ity ( i n t ) . W tym paragrafie rozm aw iam y o tym, że taki składnik m oże być o d razu inicjalizow any tak: static const int ile_pol = 120;
PCfcp
W sto su n k u d o zw ykłych składników jest taka składnia nie do pom yślenia. Tu jed n ak m o żn a tak inicjalizować, bo spełnione są w aru n k i, że: «$♦ sk ład n ik ten jest •
1. statyczny ( s t a t i c ) ,
•
2. stały ( c o n s t) ,
474
Rozdz. 10. Klasy Składnik statyczny •
3. ty p u całkow itego (tu: i n t ) ,
♦> a inicjalizujem y go w yrażeniem stałym (tu: 120).
Zagadka: Z a p o p rz e d n ie g o parag rafu w iesz, że składnik statyczny w klasie jest tylko d ek laro w a n y , a g dzieś na z ew n ątrz pow inien zostać zdefiniow any. Teraz z ad a n ie d la Ciebie: spójrz teraz na tekst program u i zn ajd ź tę definicję.
W N o co, nie zn alazłeś? Słusznie - bo jej po prostu nie ma. Zapam iętaj:
Teraz porozmawiamy o tym, co przez to stracisz - i czy Ci z tego powodu będzie żal D la p o ró w n an ia zobaczym y teraz p o d o b n y składnik, k tó ry tak ą osobną defini cję ma. © To jest deklaracja sk ład n ik a staty czn eg o o n azw ie i l e k o lo r ó w . M a o n także inicjalizator. T y m razem decydujem y, że jed n ak zrobim y m u p o za ciałem klasy definicję. (C zyli b ęd zie tak - „po starem u "). © O to ta definicja. S kład n ik n azyw a się oczyw iście i l e k o lo r o w , ale zau w aż, że nie m a już tu ta j inicjalizatora. 1 słusznie. Z asad a jest taka, że: Jeśli ten inicjalizator um ieściliśm y już w ciele klasy, to tutaj nie m o ż em y ju ż go pow tórzyć.
I
Zapytasz: po co właściwie umieszczamy tę definicję, skoro nie jest konieczna? Jeśli u m ieścim y ow ą definicję, to taki składnik sta ty c z n y jest p raw o w i tym o b iek tem , czyli na p rz y k ła d m ożem y k o m u ś p o d a ć tego obiektu adres. ♦♦♦ N ato m iast, jeśli nie um ieścim y tej definiq'i - to k o m p ila to r p o trak tu je ten sk ład n ik staty czn y tak sam o, jak stałą stw o rzo n ą za p o m o c ą ty p u w yli czeniow ego. N ie m ożesz w te d y starać się zap y tać o a d re s takiej stałej.... Z obaczm y to w funkcji m a in . D clf>
U w aga:
Ponieważ rozmawiamy o składnikach statycznych, a one istnieją nawet kiedy jeszcze żaden obiekt tej klasy nie został zdefiniowany, zatem na użytek tego przykładu nie musiałem umieszczać tu definicji żadnego
475
Rozdział. 10. Klasy Składnik statyczny
obiektu naszej klasy p l a n s z a _ c h i n c z y k a . (No, jest - ale w komen tarzu). . © O to in s tr u k d a w ypisująca na e k ra n w artości trzech stały ch , zd efin io w an y ch w naszej k la sie plansza_chinczyka. P oniew aż jesteśm y w funkcji ma in, czyli poza z a k re s e m klasy plansza_chinczyka, w ięc m u sim y p o słu g iw ać się k w alifik ato re m zakresu.
Przypominam: każda z tych trzech stałych jest zrealizowana inaczej. ♦> P ie rw sz a - za pom ocą ty p u w yliczeniow ego (b ez nazw y). ♦♦♦ D ru g a - za pom ocą staty czn eg o stałego całk o w iteg o sk ład n ik a klasy, k tó ry m a o d razu inicjalizator (a nie m a p o te m swojej definicji p o z a k lasą). ♦♦♦ T rzecia - p o d o b n ie jak d ru g a , ale tym raze m d o d a tk o w o n ap isaliśm y jej d efin icję po za ciałem k la s y . Jeśli s p o jrz y s z na ekran, to zobaczysz, że w szy stk ie tak sam o (d o b rze i s p ra w nie) z o s ta ły w y p isan e na ekranie.
Czy zatem wszystkie te rodzaje są tak samo dobre? Z ara z z o b ac zy m y , że są różnice. © O to jest in stru k cja, k tó ra w y p isu je na ek ran ie ad res sk ład n ik a staty czn eg o ile
k o lo r o w . ... To ten składnik, któremu zrobiliśmy (nieobowiązkową) definicję poza
ciałem klasy. A d res te n u zy sk u je się p o p ro stu op erato rem & &plans za_chinczyka::ile_kolorow O O T en sam s k ła d n ik m ożem y ta k ż e w ysłać do funkcji o n a z w ie w y d ru k u j z _ r e f e r e n c j i , która sp o d ziew a się obiektu typu i n t , a ob iera go jako re feren q ę (por. O ). N ie m a problem u! O © A d res te g o sk ład n ik a m ożem y też w ysłać do funkcji spodziew ającej się w sk a ź nika d o stałej i n t . (por. © ) T ak że nie m a p ro b lem u , p o prostu w y sy łam y jej a d re s sta łe g o obiektu, który jest statycznym sk ład n ik iem jakiejś klasy.
To samo w stosunku do enum - jest niemożliwe O © O peracji u z y sk a n ia ad resu stałej, takiej zdefiniow anej n a liście w yliczeniow ej, zrobić n ie m ożna. To d lateg o tę linijkę m usiałem ująć w kom entarz. T o zresztą jest ch y b a oczyw iste. O © T akże n ie m oże być referencji d o nazw y z listy w yliczeniow ej, d lateg o to w y w o ła n ie funkcji jest n iem o żliw e (ująłem w k o m en tarz). O © To sam o w o b ec funkcji oczekującej adresu.
Składnik statyczny bez definicji poza ciałem klasy - także niemożliwe! O © O k azu je się, że p o d obne
476
Rozdz. 10. Klasy Statyczna funkcja składowa
i ...d o w ia d y w a n ie się o a d re s sk ła d n ik a sta ty c z n e g o , ta k ie g o b e z d e fin ic ji poza c ia łe m k la s y , te ż je s t n ie m o żliw e .
O 0 W ysłanie d o funkcji, która oczekuje obiektu (i zrobi sobie do niego referencję) tak że jest niem ożliw e.
Podsumowując - w y g ląd a na to, że jeśli nie zam ieścim y osobnej definicji składnika staty czn eg o , to kom pilator zrealizuje go sobie jako stałą z listy w yliczeniow ej i tak potem go traktuje. Z drugiej stro n y - rzad k o interesuje nas ad res jakiejś stałej. Jeśli w ięc tego nie przew idujesz, m ożesz skorzystać z p ro sto ty tej notacji. A jeśli, p rzez zap o m n ien ie, o ten ad res m im o w szystko zap y tasz, kom pilator w y p isze inform ację o błędzie zachęcając Cię do zam ieszczenia definicji tego sk ład n ik a osobno.
W P rzy p o m in am na koniec, że te w szystkie rozw ażania doty czy ły tylko stałych całkow itych. Jeśli chciałbyś mieć statyczny składnik stały ty p u d o u b l e - m u sisz to zrobić „p o starem u ", tak jak to om aw ialiśm y w p o p rz e d n im paragrafie, czyli bez żad n y ch ułatw ień.
10.17 Statyczna funkcja s k ł a d o w a Istnienie w klasie statycznych funkcji składow ych w ynika z istn ien ia statycz nych danych składow ych. Jeśli w klasie m am y taką funkcję sk ład o w ą, która pracuje ty lk o n a składnikach będącym i sk ład n ik am i statycznym i tej klasy, to taką funkcję możemy zdeklaro w ać jako statyczną.
Co to w zasadzie zmienia ? S pójrz na p o n iższą klasę. Jest to jakby p ry m ity w n a klasa sy m u lu jąca ruch p ió rk a p lotera2 rysującego graf na p ap ierze. Jazda e w en tu a ln y ch obiektów k lasy piórko (np. ró żn y ch kolorów ) jest u w a ru n k o w a n a w sp ó ln y m d la w szyst kich piórek zezw o len iem na pracę. #include using namespace std; #include enum tak czy nie { nie, tak }; //O ////////7///7/////////////////////////////////////////////////Z 7)
Ploter to urządzenie, które po polsku zwane jest: Pisak X-Y
477
Rozdział. 10. Klasy Statyczna funkcja składowa class
piorko
{ i n t p o z _x / p o z _ y ; s t a t i c int z e z w olenie; c h a r k o l o r [30];
// funkcje — ---------------------------------------------------------------public : void jazda(int
x,
int
y)
c o u t < < " T u " << k o l o r < < " p i ó r k o : "; if(zezwolenie)( p o z _ x *= x ; p o z _ y = y; cout « " J a d ę d o p u n k t u (" « p o z _x « ", " « poz_y « ")\n
S c o S t {«
" Nie w o l n o mi
jechac
} //------------------------------- ---------------------------- ---— static
void
c z y _m o ż n a (tak_c z y _m e
;
!!!!!!!!!!!!!
\n";
~ 7
oap)
// s k ł a d n i k
s ta ty c z n y / / b łą d ! bo s k ł a d n i k z w y k ł y
z e z w o l e n i e = odp; // p o z _ x = 5;
}
/ / ---------------------------------- ------------------------- ------------piorko(const
char
s t r e p y (k o l o r , poz_x = poz_y
int
*
kol)
(kol = 0;
? k ol
p i o r k o : :z e z w o l e n i e ;
: "")
) ,'
* * * * * * ***** * * * * * * * **/
i n t m a i n ()
t
p i o r k o : :c z y j n o z n a (tak) ;
// ©
p i o r k o c z a r n e ( " S M O L I S T E " ) , z i e l o n e (" Z I E L O N I U T K I E " ) ; c z a r n e .j a z d a (0, 0 ) ; z i e l o n e .j a z d a (1100, 1 1 0 0 ) ;
// zabraniamy ruchu piórkom p i o r k o : :c z y _ m o z n a ( n i e ) ; c z a r n e .j a z d a (10, 10); z i e l o n e .j a z d a (1020, 1 0 2 0 ) ;
// zezw alam y w taki sposób z i e l o n e .c z y _ m o z n a ( t a k ) ; c z a r n e .j a z d a (50, 50); z i e l o n e .j a z d a (1060, 1060 ) ;
q
478
1 I
Rozdz. 10. Klasy Statyczna funkcja składowa
Po wykonaniu programu na ekranie zobaczymy Tu Tu Tu Tu Tu Tu
SMOLISTE piórko : Jadę do punktu (0, 0) ZIELONIUTKIE piórko : Jadę do punktu (1100, 1100) SMOLISTE piórko : Nie wolno mi jechac !!!!!!!!!!!!! ZIELONIUTKIE piórko : Nie wolno mi jechac !!!!!!!!!!!!! SMOLISTE piórko : Jadę do punktu (50, 50) Z I E L O N I U T K I E p i ó r k o : Jadę do p u n k t u (1060, 1060)
Przyjrzyjmy się ciekawszym punktom programu O Dla w y g o d y d efin iu jem y sobie taki typ w yliczeniow y. © S tatyczna dan a sk ład o w a (deklaracja). © Funkcja sym ulująca ru ch piórka. K orzysta ze zw ykłych d an y ch składow ych, a tak że ze sk ład n ik a staty czn eg o zezwolenie. © Statyczna funkcja sk ład o w a. M oże taką być, bo nie korzysta z n ie-staty czn y ch (czyli zw ykłych) d a n y c h składow ych tej klasy. Z w ró ć u w a g ę na sło w o static rozpoczynające linijkę jej deklaracji. Jako a rg u m e n t - funkcja ta przyjm uje ty p w y liczen io w y , ale ró w n ie d o b rz e m oglibyśm y ją tak zad ek laro w ać, by przyjm ow ała w artość ty p u bool. N ie m a to nic w spólnego z fak tem czy funkcja jest staty czn a czy nie. Jeśli funkq'a jest ju ż zad e k laro w an a jako staty czn a, to nie w o ln o w niej próbo w ać o d n o sić się d o sk ład n ik ó w zw ykłych - ko m p ilato r zasy g n alizu je błąd. © Najważniejsze miejsce w tym programie: funkcję statyczną m o ż n a w yw ołać nie tylko n a rzecz jak ieg o ś obiektu danej klasy, ale także na rzec z sam ej klasy. Tu jest do w ó d . W y w o łu jem y funkcję na rzecz klasy, a jeszcze an i je d en obiekt takiej k lasy nie zo stał zdefiniow any. © O czyw iście m ożna tak że w yw ołać na rzec z k o n k retn eg o o b ie k tu , ale to już znam y , d o tego funkcja m ogłaby być zw y k łą funkcją sk ład o w ą.
W Z ad ek laro w an ie funkcji składow ej jako staty czn ej sp raw ia, ż e n ie z aw ie ra ona w skaźn ik a this. N ie do ty czy ona w ó w czas k o n k retn eg o o b ie k tu , ty lk o klasy obiektów .
Co na tym tracimy? *1* W ew n ątrz takiej fu nkcji nie jest m o ż liw e ja w n e o d w o ła n ie się d o w sk a źn ik a this. N ie jest też m o ż liw e od w o łan ie się d o jakiegoś n ie -s ta ty c z n e g o (czyli zw ykłego) sk ład n ik a tej klasy. To d la teg o , że d an a b ę d ą c a sk ład n ik iem m a zaw sze p rz e d sobą n iew id o czn y w sk a ź n ik this. Pam iętasz chyba, że:
479
Rozdział. 10. Klasy Statyczna funkcja składowa if(zezwolenie){ poz_x = x; //to inaczej : poz_y = y; //to inaczej:
this->poz_x this->poz_y
x; y;
Inaczej m ó w iąc: funkcja staty czn a —n aw et jeśli w y w o ła n a jest na rzecz k o n k re t nego e g z e m p la rz a obiektu d an ej k la sy - nie o trzy m u je w sk a źn ik a t h i s d o tego obiektu. Z a te m n ie m oże p ró b o w a ć pracow ać na d a n y c h sk ład o w y ch c h ara k te ry sty czn y c h d la tego w łaśn ie eg zem p larza. D lateg o w funkcji statycznej c z y _ m o z n a b łęd em byłoby u ży cie instrukcji O p o z _ x = 5 ; g d y ż k o m p ilato r nie m ó g łb y te g o zastąp ić zapisem : t h i s - > p o z _ x = 5 ; N a ra z ie w ięc w id zim y , że sk ła d o w a funkcja staty czn a jest g orszym rodzajem zw ykłej fu n k cji składow ej. A je d n a k nie!
Co zyskujemy przy statycznej funkcji składowej: ♦♦♦ To, że funkcja d o ty c zy nie ty le konkretnego o b ie k tu , ale całej klasy tych o b ie k tó w . M ożna ją w ięc w y w o łać zaró w n o d la jed n eg o obiektu obiekt 1 . funkcja (); jak i d la klasy d o której o n a należy klasa::funkcja();
Oczywiście, w przypadku wywołania dla konkretnego obiektu, funkcja interesuje się tylko tym, do jakiej klasy wywołujący ją obiekt należy. Nie jest ważne, który to konkretny egzemplarz ją wywołuje. ♦♦♦ F u n k cję m ożna w y w o łać n a w e t w tedy, g d y nie istnieje jeszcze ż a d e n o b ie k t tej klasy. Ten o s ta tn i p o w ó d w y d aje m i się najw ażniejszy. A teraz co ś ty lk o dla dociekliw ych.
Czy funkcja statyczna rzeczywiście nie może odwołać się do żadnego zwykłego (nie-statycznego) składnika klasy? O statec zn ie m o że. Nie m a, co p ra w d a , w skaźnika t h i s , k tó ry jej to n o rm aln ie u m o ż liw ia, ale m o żem y się d o sk ład n ik a klasy od w o łać pod ając jaw nie, o k tó ry e g z e m p la rz obiektu nam ch o d zi i to zastąpi nieobecny w sk aźn ik t h i s . W yjaśnijm y to. Jeśli chcem y w fu n k cji statycznej —o d w o łać się d o sk ład n ik a obiektu o b i , a sk ład n ik się n a z y w a się s s s , to w e w n ą trz funkcji m ożem y się odnieść d o n ie g o jako: obi. sss lub, jeśli m a m y w skaźnik p o k azu jący na ten obiekt to wskaznik->sss Już sły szę, jak się w yśm iew asz: Tak to każdy potrafi! Przecież tym sposobem można do składnika odwołać się zawsze —nawet spoza klasy. Funkcja statyczna nie jest tu niczym lepszym niż jakakolwiek funkcja nie związana z klasą!"
480
Rozdz. 10. Klasy Do czego może nam się przydać składnik statyczny w klasie?
O to kilka zastosow ań: ♦> P rzydaje się, g d y egzem plarze obiektów danej klasy mają się porozu m iew ać ze sobą. N a przykład przez flagę - jeden obiekt m oże zaw iado mić inny - (albo w szystkie inne) o jakim ś fakcie.
*1* G dy w szystkie obiekty mają tę sam ą cechę, która m oże się zm ieniać. Jeśli przykładow o obiekty danej klasy mają jakąś cenę, to p o d w y żk a ceny polega na operacji zm iany zaw artości jednego składnika statycznego. N ie trzeba ż m u d n ie zm ieniać w k ażdym poszczególnym obiekcie ocze kującym na sp rzed an ie. ♦$» M oże to być zm ien n a określająca ile ob iek tó w danej klasy ju ż istnieje. Licznik taki um ieszcza się w obrębie klasy, której obiekty się liczy. Licznik jest jeden, m im o że obiektów m o g ą być tysiące. «$♦ Składnik statyczny m oże też służyć do tego, by w szystkie obiekty danej klasy m ogły korzystać z jednego pliku dy sk o w eg o - p rzy d zielo n eg o tej klasie obiektów. Składnikiem statycznym jest w ted y kanał transm isji (strum ień) do pliku. <♦ Składnik statyczny m oże zaoszczędzić też miejsca. Jeśli m am y p rzy k ła dow o 700 sam o lo tó w klasy "Sam olot_z_M ojego_Tow arzystw a_Lotniczego" to lepiej jeśli tę długą nazw ę firm y (w spólną przecież d la w szyst kich egzem plarzy) uczynim y składnikiem statycznym . N a z w a ta będąc nadal p rzy p isan a do klasy - p ojaw i się w pam ięci k o m p u tera tylko raz zam iast 700 razy. O czyw iście innych zasto so w ań m oże być multum. Jeśli tylko zd o b ęd ziesz sw o b o d ę w posługiw aniu się składnikam i statycznym i - z łatw ością w sw oich p ro g ram ach w yłow isz sytuacje, kiedy składniki statyczne up ro szczą C i życie.
10.19 F u n k c je skład o w e typ u const o ra z voiatiie F unkcje składow e danej klasy mogą być zad e k laro w an e z p rz y d o m k ie m c o n s t . Po co? Sprawa jest prosta: Funkcja, k tó ra jest c o n s t , to funkcja, k tó ra obiecuje, że — jeśli się ją w ywoła na rzecz jakiegoś obiektu — n ie będzie m o d y fik o w ać jego danych składow ych.
481
Rozdział. 10. Klasy Funkcje składowe typu c o n s t oraz v o l a t i l e
Je s t to w a ż n e w sy tu a c ja c h , g d y z a m ie rz a m y d e fin io w a ć so b ie sta łe o b ie k ty d a n e j k la sy . (Czyli obiekty z p rz y d o m k ie m c o n s t ) . Jeśli d la ty p u d o u b l e m o g liśm y zd efin io w ać o b ie k t ty p u c o n s t const double pi = 3.1416; to ta k ż e m o ż e m y sobie w y o b razić obiekty, k tóre m ają b y ć stałe. Z aw ie rają p o pro stu d a n e s k ła d o w e u stalo n e r a z na zaw sze. Wyobraź sobie egzemplarz muzealny jakiejś pralki z lat 50-tych X X wieku. Ma on tylko stać i ładnie -wyglądać. Można sobie go pooglądać, ale prać -
nie! # Jeśli o b ie k t d a n e j klasy jest c o n s t , to na jego rzecz m o ż n a w y w o łać je d y n ie te z jego fu n k cji sk ład o w y ch , k tóre zag w a ra n tu ją , że go m e b ę d ą zm ien iać - czyli tylko te , k tó re m ają p rz y d o m e k c o n s t .
Dlaczego musimy koniecznie napisać ten przydomek? Czy kompilator sam nie wie, która funkcja modyfikuje, a która nie? N ie - k o m p ila to r rzeczyw iście m o ż e, w d anej chw ili, te g o jeszcze nie w iedzieć. N a p rz y k ła d : W trak c ie k o m pilacji jakiegoś p lik u (nazw ijm y go s a t u r n . c p p ), w k tó ry m u ż y w a n e są o b iek ty klasy KK (zdefiniow anej w z u p e łn ie in n y m p lik u k k . c p p ) , k o m p ila to r m u si je d y n ie znać deklaracje jej funkcji sk ład o w y ch . To d la teg o p ro g ra m ista tw o rzący tę k lasę KK um ieszcza je w pliku n ag łó w k o w y m tej k la sy (n p . k k . h). . . Ten, kto pisze plik s a t u r n . cpp, używający obiektówklasy KK, powinien wstawić do swego pliku definicję klasy KK. (Dyrektywa: H in c l u d e " k k . h ")■ K o m p ilato r, w trakcie p racy n a d plikiem s a t u r n . c p p - zna w ięc d o p ie ro je d y n ie s a m n ag łó w e k k lasy KK - jed y n ie sam e d e k laracje funkcji sk ład o w y ch klasy KK. Po s a m y c h ta k ic h deklaracjach - k o m p ilato r nie w ie, co funkcja robi w śro d k u . Z atem n ie p o tra fi sam ocenić, k tó ra z nich jest b e z p ie c z n a dla ob iek tu c o n s t KK,a k tó ra nie. N ie w ie w ięc, czy w o ln o ją p ro g ra m iśc ie w y w o ły w a ć n a rzecz w ła ś n ie n a p o tk a n e g o sta łe g o o b ie k tu klasy KK.A b a rd z o chciałby e w e n tu a ln ie o strzec p ro g ra m is tę s a t u r n a . c p p , że m oże p o p e łn ia tu błąd. W niosek ? P ro g ram ista tw o rz ą c y taką klasę KK p o w in ie n to k o m p ilato ro w i p o d p o w ie d z ie ć , czy li w pliku n a g łó w k o w y m - tam , g d zie są sam e d ek laracje funkcji sk ład o w y ch - u m ieścić p rz y d o m e k p rz y tych fu n k c ja c h , k tó re m o g ą być w y w o ły w a n e na rzecz stały ch o b ie k tó w d a n e j k la s y
KK.
Jeśli k o m p ila to r zobaczy p rz y d ek laracji słow o c o n s t , to sp raw a jest d la niego jasna.
• q s u o o o >j b ( u u e M O jB [ > ia p e z ę>Xq a z o u i o S a ł B jQ »iu i b A z o d b 'a iu B J 5 [ a e u a u e p a ( n s id X M o ^ jA j b j o ^ i p A u p S z i j o d s M a iz p B {> [n m
a fn > [ ijA p o u i
's p d A M e b > [ u n j (a iu m ; s a f
r t p p i n d d b A z o d B o t e f r p u a z a jd a i E S B [q o:j b Co A z o d q
OJJBM >|BUpa[ 9B|/V\BLUZOJOd
iTUjoSazazs aiu XuiXzaBqoz aiu atuBj^a
i
iP ł> n /l
bu
nuiBjSojd o§a}
oSaM B^ap oj
c iiu b u o ^ A m
M O '0) unsezjd-uiop // •'(OS 'osJunsazad-sa-pd • (01'V) unsazad-poąoouiBs 1SU0P-MU ih y u n jziu v m o s o }S D Z
//
! () sxdAM’uiop
©
//
() sidAM-sard () stćIAm ■poqoouiBS łsuoo - h m o p u fls ifo y u tif m iwm osoistrz
@
//
^
//
//
•'(OS '0S)uiop eCoAzod qsuoo •'(08 '0C)saTd '(OS 'OHpoąooures
BCoAzod ) ()UTBUI qUT
.'q = A .'b = x
//
{
} (q qut 'e quT)unsazjd::eCoAzod pTOA , ł ł ł ł ł ł f ł ł ł ł ł 1 M M M l ł ł ł ł ł ł ł ł ł » ł ł ł » ł ł ł ł J M M M M H i ł ł M l ł ł it ł ł * ł 4 ł ł ł t ł ł ł ł ł ł ( /
•'TPua >> A >> „ 0
//
>> x »
qnoo
{ }
qsuoo ( )sjdAM::eCoAzod piOA
> V ///////////////////////////////////////////////////////////// •'{ • (q qur 'e qut)unsazjd p j o a .'qsuoo (ptOA) STdAH pjOA O // { .'q - A .'e = x) ( q aut ' b quT) sfoAzod : OTjąnd .'A 'x qux } BCoAzOd SSBfO < ////////////////////////////////////////////////////////////// /p q s aoBdssuiBu fiu jsn aprqouT# :qaXupdzj{ędsM aizpB{5(n m soSazo SbAzod Bofefnstdo e CoAzod
bsb [)j o^ o
pBł>jAzjd BU BJOd eXTdeIOA zb.io qsuoo ndX> avvopiq>|S
Xs«EH OT zPzOM
Rozdział. 10. Klasy Funkcje składowe typu c o n s t oraz v o l a t i l e
483
Z au w aż, ż e sło w o const u m ie sz c z o n e jest na s a m y m ko ń cu deklaracji, tu ż p rzed śre d n ik ie m . Staje się częścią dek laracji tej funkcji. 0 P odob n ie p rz y definicji tejże funkcji. S łow o const jest też n a końcu, tuż p rz e d k lam ram i o tw ie ra jący m i definicję ciała funkcji. Jak w id z isz , m o d y fik ato r (p rzy d o m ek ) c o n s t w y stę p u je zaró w n o w d e k laracji funkcji jak i w jej definicji (jeśli jest o n a um ieszczona p o z a klasą). Ł atw o to z a p a m ię ta ć : ten m o d y fik a to r
const jest p o p ro stu częścią ty p u tej
funkcji. , © W klasie jest te ż funkcja przesuń, k tó ra m o d y fik u je o b ie k t klasy p o z y c j a . Skoro m o d y fik u je , to oczyw iście nie m o ż e być funkcją const. © W m a i n d efin iu je m y d w a o b iek ty klasy pozycja. Jed en z n ich reprezen tu je pozyq'ę s a m o c h o d u , a d ru g i pozycję p sa. Już sam e n a z w y sugerują, ż e bę d ziem y ty m i o b ie k ta m i „ p o ru sz a ć ". W naw iasach w id z im y a rg u m e n ty dla k o n stru k to ra k la sy pozycja. Te a rg u m e n ty rep reze n tu ją w stę p n ą pozycję tych obiektów . © Definicja o b ie k tu o n azw ie dom. P o stan aw iam y , że tego o b ie k tu nie w o ln o p o ru szać i d la te g o staw iam y p rz y n im słów ko c o n s t . D ygresja: Dawno nie czytaliśmy ju ż definicji, więc przeczytajmy to na głos. Czytamy jak zwykle od tyłu: Dom jest obiektem klasy pozycja, a jest w dodatku (obiektem) stałym. Mam nadzieję, że nie dałeś się zwieść nawiasami dla konstruktora i nie zacząłeś czytać: dom jest funkcją... Tak by było, gdyby w nawiasie były nazwy typów, a nie zwykłe liczby - (czy konkretne obiekty) będące argumentami. © W y w o łan ie fu n k cji składow ej const n a rzecz o b iek tó w z w y k ły ch i obiektu const. N ie m a p ro b lem u . To, ż e funkcja obiecuje niczego n ie zm ieniać w obie kcie, n ie p rz e s z k a d z a obiektom z w y k ły m (samochód, pies), n ato m iast o b iek t z p rz y d o m k ie m const (dom ) w p ro w a d z a w b ard zo d o b ry nastrój. O Inaczej w y g lą d a s p ra w a w p rz y p a d k u zw ykłej ( n i e - c o n s t ) funkcji składow ej. W yw o łan ie jej n a rzecz zw ykłych o b iek tó w , jest zw y k łą, z n a n ą nam rzeczą, n ato m iast k o m p ila to r nie d o p u ści d o w y w o łan ia jej na rzecz o b ie k tu c o n s t . Do p rz e s u w a n ia d o m u nie dojdzie.
Wszystko, co powiedzieliśmy o funkcjach składowych typu const, dotyczy także przydomka volatile Jeśli o b ie k t k la s y K zdefiniow aliśm y jako volatile to zn aczy , ż e w y m ag am y dla n ieg o tro sk liw sz e g o traktow ania. D latego k o m p ilato r o d d a g o w ręce tylko takich fu n k cji sk ład o w y ch , k tó re sw o im p rzy d o m k iem volatile to lepsze tra k to w a n ie z a p e w n ią . N ie m u sz ę c h y b a w yjaśniać, że volat ile określa tu te funkcje, k tó re zrezy g n o w ały z o p ty m a liz a c ji i rzetelnie praco w ać b ęd ą zaw sze na p ra w d ziw y ch danych
484
Rozdz. 10. Klasy Funkcje składowe typu c o n s t oraz v o l a t i l e sk ład o w y ch sięg ając p o nie za k a ż d y m razem — n a w e t d o najodleglejszych kom órek p a m ię c i. M im o że robiły to ju ż linijkę w cześniej i d o b rze pam iętają, tam jest. O czyw iście ta k a tro sk liw a funkcja je st ró w n ie dobra d la obiektu zw ykłego (n ie -v o la tile ). P rzy k ład u n ie b ę d ę p o d a w a ł - w e ź p o p rz e d n i p rzy k ła d i z am ień tylko słówkc const na volatile. Pozycja słó w k a w deklaracji i definicji jest identyczna.
Czy funkcja składowa może być równocześnie const i volatile?Czyto ma sens? Ma, bo to nie o b ie k t m a być const i volatile (co n ie m iało by sensu), all funkcja. Jest to p o p ro stu funkcja, k tó ra m a służyć d o p ra c y na obu typacJ obiektów . Z jednej s tro n y obiecuje ona nic n ie pisać, tylko czytać treść d an y ch (to słow< const). Z d ru g ie j stro n y obiecuje, ż e jeśli ju ż będzie czy tać, to zro b i to rzetelni' (volatile). P o d w ó jn a troskliw ość.
Dla dociekliwych R o zm aw ialiśm y k ied y ś (§ 10.8, str. 433) o ty p ie w sk a źn ik a t h i s w zwykłyc! funkcjach s k ła d o w y c h . Teraz m o ż e m y coś dodać. ♦♦♦ Jeżeli fu n k cja sk ład o w a m a p rz y d o m e k const, to w jej ciele this jes ty p u : const X*
czyli t h i s jest tam "wskaźnikiem ", który nie pozwoli modyfikować poka zywanego obiektu. ♦♦♦ Jeżeli fu n k cja sk ład o w a m a p rz y d o m e k volatile, to w jej ciele t h i s jest ty p u : volatile X*
czyli t h i s jest tam "wskaźnikiem", który nakazuje kompilatorowi ostroż ność i nieułatwianie sobie pracy, z tak pokazywanym obiektem. ♦♦♦ Jeżeli fu n k cja sk ład o w a m a ró w n o cześn ie p r z y d o m k i volatile I const, to w jej ciele this je st typu: const volatile X*
czyli jest "wskaźnikiem", który jednocześnie: •
nie pozwoli modyfikoioać pokazywanego obiektu,
•
nakazuje kompilatorowi nieułatwianie sobie pracy przy odczytywa niu tak pokazywanego obiektu.
485
Rozdział. 10. Klasy Specyfikator m u t a b l e
Konstruktory i destruktory nie mogą mieć przydomków c o n s t i v o l a t i l e , a mimo to nadają się do pracy na takich obiektach. Widać to nawet w naszym przykładzie. To oczywiście zrozumiałe. Konstruktor, aby zapisać wartość po czątkową do obiektu c o n s t, musi mieć możliwość pisania do niego. To jeszcze jeden dowód na to, że konstruktory i destruktory godne są tego, by im poświęcić osobny rozdział.
0.19.1
Przeładow anie, a funkcje składow e c o n s t i v o la tile W obrębie klasy funkcja może być przeładowana i niektóre wersje funkcji mogą mieć przydomek c o n s t lub y o l a t i l e , a inne nie. Obowiązuje jednak stara zasada: funkcje o jednej nazwie - aby być przeładowane - muszą się różnić listą argumentów. Kropka. Obecność przydomków nie ma tu znaczenia. Natomiast obecność przydomków ma znaczenie przy dopasowaniu (lekko nie zgadzającego się) wywołania, do jakiejś z konkretnych wersji funkcji. Oto zasada: na rzecz obiektu c o n s t może być wywołana tylko funkcja składowa c o n s t. Wszystkie funkcje, które tego przydomka nie mają są od razu odrzucane. Dopiero spośród tych, które zostają (wszystkie c o n s t ) kompilator próbuje dopasowywać. To samo dotyczy przeładowania funkcji z przydomkiem v o l a t i l e
10.20 Specyfikator mutable Jak wiesz, definiując obiekt jakiejś swojej klasy, możesz zastrzec przydomkiem c o n s t, że ten konkretny obiekt ma być stały. W rezultacie wartość wszystkich składników danych tego obiektu (po zakończeniu konstruktora) - zostaje zam rożona. Obiekt odtąd nie może się zmieniać. Na rzecz takiego obiektu można wywoływać tylko te z jego funkcji składowych, które mają przydomek c o n st. Jeśli kompilator, pracując nad ciałem takiej funkcji składowej c o n s t - zoba czyłby, że próbujesz w niej modyfikować jakąś daną składową tej klasy, zwróci Ci uwagę, że w funkcjach c o n s t, takich rzeczy robić nie można. W ten sposób kompilator strzeże nas, by nasz obiekt stały napraw dę pozostał stały. Okazuje się jednak, że czasami takie podejście wydaje się zbyt rygorystyczne. Dobre jest do obiektów - eksponatów muzealnych, przeznaczonych tylko do pooglądania sobie... Ale nawet i to nie zawsze.
486
Rozdz. 10. Klasy Specyfikator m u t a b l e
Jeśli bowiem takim eksponatem jest na przykład starożytny odbiornik radiowyJ to dobrze by - w tym muzeum - jak na niego patrzą zwiedzający, terma odbiornikowi przynajmniej mrugało tak zwane „magiczne oko".2 Zatem, przydało by się czasem, aby nawet stałe obiekty miały jakąś część, którą można zmieniać. Sprytni programiści C++ i w takiej sytuacji potrafili sobi« poradzić. Dopuszczali się mianowicie podstępu, polegającego na użyciu rzutowania, W ten sposób pozbywali się przydomku c o n s t ze wskaźnika do takiego stałego obiektu - i od tej pory posługując się tym wskaźnikiem mogli ju i zmieniać - nawet wszystko - w stałym obiekcie. Sposób sprytny, ale niebezpieczny. Poza tym - wyrabia złe nawyki. Dlatego w prow adzono do C++ specyfikator mutable 2 , który umożliwia takie „uzmiennienie" wybranego składnika, nawet jeśli cały obiekt jest deklarowany jako stały. Tylko wybranego składnika. Innych składników tego (stałego) obiektu - kompi lator nadal nam będzie nam strzegł. Jeśli ktoś tworzy zwykłe (n ie -c o n s t) obiekty tej k la s y - przydomek ten nie ma znaczenia. ♦♦♦ Jeśli jednak ktoś stworzy stały obiekt tej klasy, (czyli taki, w którym składniki-dane są niejako zamrożone), to owo zam rożenie nie ma doty czyć tego konkretnego składnika mutable. Składnika mutable -j zamrożenie nie obejmuje. Mam tu także inne skojarzenie. Niedawno widziałem jakiegoś światowej sławy magika. W trakcie wielkie go widowiska pozwolił się on zamrozić w olbrzymim prostopadłościanie lodu. Dlaczego w trakcie tego pokazu nie umarł? Dlatego, że mimo iż wszystkie składniki tego prostopadłościanu zostały rzeczywiście zamrożone, ale magik ten swojemu sercu jakimś magicznym j sposobem nadał przydomek m u ta b le (wobec tego jego serce nadal mogło bić). Składnik z takim przydomkiem jest bowiem odporny na zamrażanie.\ [jtS 3
8)
9)
Przydomek (specyfikator) mutable można nadać tylko składnikowi-danej. j Oczywiście - jeśli jakiś składnik-dana ma swój osobisty przydom ek constj określający, że ten składnik ma być zaw sze stały, (niezależnie czy jest to w "Magiczne oko" była to taka świecąca na zielono lampa radiowa, która służyła jako wskaźnik j dobrego dostrojenia do odbieranej stacji. Bardzo to było ładne, szczególnie wieczorem w ciemnym pokoju. ang.: mutable-[czytaj "mjutybyl"] zmienny, w sensie: możliwy do zm ieniania ("zmieniahiy "). (Por. słowo: mutacja)
487
Rozdział. 10. Klasy Specyfikator m u t a b l e
obiekcie z m ie n n y m czy stałym ) - to k o m p ilato r to u s z a n u je i nie pozw oli postaw ić ta m m u t a b l e .
Jak wobec składnika mutable zachowują się funkcje składowe? <♦ Jeśli d z ie je się to w obiekcie, k tó ry jest zw y k ły (n ie -s ta ły ), to p rzy d o m ek ten (sto jący p rz e d sk ład n ik iem d a n ą ) nie m a ż a d n e g o znaczenia.
Obiekt zwykły nie jest przecież „zamrażany". Jeśli n a to m ia s t d zieje się to w obiekcie, k tóry jest stały , to - jak w iem y od d a w n a - nie w o ln o w y w o łać na jego rzecz ż ad n y c h fu n k cji składow ych n ie m ający ch p rzy d o m k a c o n s t . Skoro tak, to o d tej p o ry nasze ro z w a ż a n ia m o żem y o g ran iczy ć d o sam ych funkcji sk ła d o w y c h c o n s t . Jak d o b rze w ie m y , funkcje sk ład o w e z p rzy d o m k iem c o n s t są to takie funkcje, k tóre w n o rm a ln y c h w aru n k ach p o zw ala ją nam sobie ty lk o o b ie k t pooglądać, m oże coś się o d n iego d o w ied zieć, ale oczyw iście nie m ają p ra w a zm ieniać żad n y c h s :ła d n ik ó w -d a n y c h . T e ra z , d o tego o statn ieg o za d n ia , m o żem y d o d ać: "żadnych, z w y ją tk ie m tych o p atrz o n y ch sp ecyfikatorem m u t a b l e " .
Zobaczymy teraz przykład C hciałem , by n ie ty lk o pok azał on klasę, w której u ży to s k ła d n ik a m u t a b l e , ale także, b y ś zo b a c z y ł p ra w d z iw ą sytuację, kiedy taki sk ła d n ik rozw iązuje problem . P o n ie w a ż zbliżam y się d o końca ro zd ziału , m o że s p o d o b a Ci się nieco bardziej ro z b u d o w a n y program , k tó ry coś kon k retn eg o z n ac zy . Teraz nastąpi taka jakby „ literacka" warstwa naszego przykładu. Jest to opowieść z granic mikroświata. Jeśli to Cię nie porywa, nie musisz nad tym się skupiać — możesz teraz przeskoczyć do najbliższego „czarnego listka". Skupisz się dopiero na samym praktycznym zastosowaniu słowa m u t a b l e w definicji naszej przykładowej klasy.
Serca zegarów biją: tik-tak... P roblem m a m taki. P rzep ro w ad zam y ek sp ery m en t z fizyki jądrow ej. W tym celu w y w o łu je m y w iele reakcji ją d ro w y ch - kilkanaście ty sięcy na sekundę. Efektem k a ż d e j takiej reakcji jest em isja tak zw anych kwantów promieniowania gamma, cząstek alfa i fragmentów rozszczepienia. Jeśli nic nie zro z u m ia łe ś - nie ma znaczenia: w ażne jest ty lk o , że k ażd y z tych trzech ro d z a jó w p ro d u k tó w reakcji jądrow ej - jest rejestro w an y p rzez odrębny zespół czu jn ik ó w , (czyli detektorów ). M am y w ięc trzy n iezależn e gałęzie p o m iaru , d o starczające nam trzy stru m ien ie danych. Jak p o te m te inform acje z trzech ró żn y ch gałęzi złożyć razem tak , by m ieć pełny obraz k ażd ej k o n k retn ej reakcji jądrow ej? B ardzo p ro sto .
488
Rozdz. 10. Klasy Specyfikator m u t a b l e K ażda z trzech (odległych od siebie) g ałęzi pom iarow ych mą m o d u ł zeg a ro w y , k tó ry m o dm ierza czas. D zięki tem u, g d y doko n u je p o je d y n czeg o p om iaru sw o im i d etek to ram i, do wyniku p o m iaru d o d aje inform ację o czasie - k ie d y ko n k retn ie ta reakcją zaszła. N a u ż y te k n aszeg o p rz v k ła d u przyjm ijm y, że cz a s jest o d m ierzan y z dokład nością d o m i lis e k u n d y ^ to je d n a tysięczna sek u n d y ). T eo re ty czn ie w ięc w szy stk o je st rozw iązane: Z a m a w ia m y trzy m o d u ły zega row e, k tó re b ęd ą p o zw ala ły d o d a ć d o k ażd eg o e lem en tarn eg o pom iaru in form ację o czasie zajścia d a n e j reakcji. N iestety o b raca m y się w św iecie realnym , a nie in fo rm aty czn y m , w ięc te trzy m o d u ły z e g a ro w e n ig d y nie c h o d z ą d o k ład n ie tak sam o. N iek tó re z nich m ogą m in im a ln ie spieszyć. Są to ró żn ice rzęd u m ilia rd o w y ch części sek u n d y (na se k u n d ę). N ib y to niew iele, ale n asz ek sp ery m en t trw a o k o ło tygodnia, w ięc już p o kilku n a s tu g o d zin ach z e g a ry ta k b ard zo się ro z sy n ch ro n izu ją , że u niem oż liw i to n a m późniejsze d o p a so w y w a n ie d o siebie d a n y c h z tych trzech gałęzi p o m iaro w y c h .
Zapytasz pewnie: Po co używamy trzech zegarów? Gdybyśmy mieli jeden... ...to w szystkie trz y gałęzie p o m iaro w e p o słu g iw ały b y się tym sam y m w sp ó ln y m o d m ierzaniem c z a su i nie byłoby ry zy k a „ro zjech an ia" się czasów . M asz rację, tak by było najlepiej. D odatkow o: tak b y b y ło najtaniej. Jeden m o d u ł z e g a ro w y k o sztu je m niej niż trzy ... N iestety n ie d a się, d lateg o ż e g ałęzie p o m iaro w e są o d d a lo n e o d siebie; jeśli b y śm y m ieli um ieścić ten m o d u ł zeg aro w y g d zieś k o n k re tn ie , to gdzie? Jest to w a ż n e , bo m iędzy z a d a n ie m p y tan ia m o d u ło w i „jaki jest bieżący czas?", a o trz y m a n ie m od niego o d p o w ied zi, mija p e w ie n czas. Jed n a gałąź - jako bliższa - b ę d z ie zad aw ała to p y ta n ie w cześniej o d g ałęzi, k tó ra jest dalej. P rz y tych p rz e d z ia ła c h czasow ych, g ra rolę na w et o p ó ź n ie n ie p rzesy ła n ia im p u lsó w kablem elek try czn y m . K rótko m ów iąc: k ażd a gałąź m u si niestety mieć u sieb ie sw ój w łasn y m o d u ł zeg a ro w y , o d k tó reg o inform ację o bieżącym czasie, o trz y m a ć b ęd zie n a ty c h m iast. A le jak zap ew n im y sy n ch ro n iczn o ść tych trz e c h zeg aró w ? I S tw orzym y czw arty m o d u ł zeg a ro w y , k tó ry ta m te trzy, b ę d ą 10) 11)
W naszym laboratorium naprawdę mierzymy to z dokładnością 50 tys razy większą. Wyjaśnienie dla najmłodszych czytelników: Synchronizacja to - doprowadzenie zm ian kilku wielkości fiz. do synchronizmu, jednoczesności, zgodności w czasie. Synchroniczny - jednoczesny; zestawiający zdarzenia zaszłe w tym samym czasie; ("Słownik w yrazów obcych i zwrotów obcojęzycznych" W ładysawa Kopalińskiego).
489
Rozdział. 10. Klasy Specyfikator m u t a b l e
m ogły zapytać „—Jaki jest twój obecny cza s? ', a po otrzymaniu odpow iedzi, dostroją się do niego. Ten czwarty moduł zegarowy, będzie jakby w zorcowym , nietykalnym obiektem. Wspomniana synchronizacja zegarów (regulowanie ich czasu) nie musi być przeprowadzana często. Wystarczy raz na sekundę.
Tutaj skończyła się beletrystyczna warstwa tego problemu Przechodzim y do przykładu. Występuje w nim klasa m o d u l_ zeg a ro w y . W programie stw orzym y trzy obiekty tej klasy. Każdy odpowiadający jednej z trzech gałęzi pomiarowych. W dodatku stw orzym y czwarty obiekt tej klasy m o d u l_ z eg a r o w y - o nazwie z l o t y . Będzie to obiekt z przydomkiem c o n s t . Z auw aż absurdalność tej sytuacji. Zegar, który będzie c o n s t nie mógłby przecież „chodzić7' (odmierzać czas). A przecież istotą zegara jest to, że jego w skazów ki poruszają się do przodu. To w łaśnie umożliwi przydomek m u ta b le. Zegar zamrożony w bryle lodu, dzięki temu przydomkowi, nadal będzie mógł pokazyw ać upływający czas.
char nazwa[100]; int co ile przyspiesza; public: moduł zegarowy(const char *opis, int niedokl)
// ©
strcpy(nazwa, (opis ? opis : "Bez opisu")); co_ile_przyspiesza = niedokl; mo3_biezacy_czas = 0; 1
/ / ------------------
void mija_milisekunda() const { moj biezacy_czas++; , if( !(moj_biezacy_czas ft co_ile_przyspiesza)) ( moj_biezacy czas++; ) } / / -------------------------------long podaj_milisekundy() const ( return moj_biezacy_czas; ) long podaj_sekundy()
const
// ©
'/,%
// ©
// ©
490
Rozdz. 10. Klasy Specyfikator m u t a b l e
return moj_biezacy_czas / 1000; } void
{
//O O
d r u k u j _ c z a s ()
cout << nazwa << " : " « moj_biezacy_czas / 1000.0 << " s
} / / ------------------void wsk z kim) synchronizuj_sie_z (const modul_zegarowy { int czas_odniesienia = wsk_z kim->podaj_milisekundy(); i f (moj_biezacy_czas != czas_odniesienia) t cout << nazwa < < " : Korekta w " << (czasodni e s i e n i a / 1000) << " s e k . (" << czas odniesienia — moj biezacy_czas << " m s ) " « endl; // w ła śc iw a s y n c h r o n iz a c ja moj_biezacy_czas = czas_odniesienia;
//O 0
//O ©
//O 0
) ł
//OO
void wyzeruj_czas() moj_biezacy_czas = 0; };
I
/ / / / / / / / / / / / / / / / / / / / / /
kon iec d efin icji k la s y
/ / / / / / / / / / / / / / / / / / / / / / / / /
int m a i n () modul_zegarowy gamma ("GAMMA", 4000); modul_zegarowy s e p a r (" SEPAR", 5010); modul_zegarowy alfap (" ALFAP",9200); cout << "Sprawdzenie dokładności zegarow\n"; w h i l e (true) { gamma.mija_milisekunda(); separ.mija_milisekunda(); alfap.mija_milisekunda(); //k o n tro ln ie d r u k u je m y na ekran co 2 0 se k u n d (2 0 0 0 0 m ilise k u n d ) i f ( ! (separ.podaj_milisekundy() % 2 0000)) { gam m a .drukuj_czas(); separ.drukuj_czas(); alf a p .drukuj_czas(); cout << endl; ł if (separ.podaj_sekundy() > 60)
//O ©
//O © //© ©
//© O //© ©
//© €J
491
Rozdział. 10. Klasy Specyfikator m u t a b l e break; cout << "Jak widać, zegary chodzą nierówno,\n "a różnice beda sie cały czas powiekszaly\n"; //cout << "Wprowadzenie obiektu odniesienia-- \n"; const modul_zegarowy zloty("ZLOTY", 9999999);
//© O
while(true)
//© ©
gamma .mija_milisekunda() ;
separ.mija_milisekunda(); alfap.mija_milisekunda(); zloty.mija_milisekunda();
//© ©
if(zloty.podaj_milisekundy() % 1000)
//© ©
* gamma.synchronizuj_sie_z(&zloty); separ.synchronizuj_sie_z(szloty) ; alfap.synchronizuj_sie_z(szloty) ;
//© ©
// zloty.synchronizuj_sie_z(&gamma); if (zloty.podaj_milisekundy () / 1000.0 >- 600) // © © cout << "Podsumowanie po 600 sekundach...\n"; gamma.drukuj_czas(); separ.drukuj_czas(); alfap.drukuj_czas(); cout << endl; break;
i********************************************/
Oto, co pojawi się na ekranie (z ro b iłe m s k ró f) Sprawdzenie dokładności zegarów GAMMA :20.002 s SEPAR : 20 s ALFAP GAMMA :40.003 s SEPAR : 40 s ALFAP GAMMA :60.004 s SEPAR : 60 s ALFAP Jak widać, zegary chodzą nierówno, a różnice beda sie cały czas powiększały Wprowadzenie obiektu odniesienia=== GAMMA: Korekta w 0 sek. (-61003 ms) SEPAK: Korekta w 0 sek. (-61000 ms) ALFAP: Korekta w 0 sek. (-60994 ms) GAMMA: Korekta w 4 sek. (-1 ms) SEPAR: Korekta w 5 sek. (-1 ms) GAMMA: Korekta w 8 sek. (-1 ms) ALFAP: Korekta w 9 sek. (-1 ms) SEPAR: Korekta w 10 sek. (-1 ms) GAMMA: Korekta w 12 sek. (-1 ms)
19.999 s 39.997 s 59.995 s
//© ©
492
Rozdz. 10. Klasy Specyfikator m u t a b l e SEPAR: Korekta w 15 sek. (-1 ms) GAMMA: Korekta w 16 sek. (-1 ms) ALFAP: Korekta w 18 sek. (-1 ms) ----------------------- ----------------------------£ < ----------------------SEPAR: Korekta w 576 sek. (-1 ms) ALFAP: Korekta w 579 sek. (-1 ms) GAMMA: Korekta w 580 sek. (-1 ms) SEPAR: Korekta w 581 sek. (-1 ms) GAMMA: Korekta w 584 sek. (-1 ms) SEPAR: Korekta w 586 sek. (-1 ms) GAMMA: Korekta w 588 sek. (-1 ms) ALFAP: Korekta w 588 sek. (-1 ms) SEPAR: Korekta w 591 sek. (-1 ms) GAMMA: Korekta w 592 sek. (-1 ms) GAMMA: Korekta w 596 sek. (-1 ms) SEPAR: Korekta w 596 sek. (-1 ms) ALFAP: Korekta w 598 sek. (-1 ms) Podsumowanie po 600 sekundach... GAMMA : 600.001 s SEPAR : 600 s ALFAP : 600 s
Zobaczmy teraz ciekawsze miejsca tego programu O Definicja klasy m o d u l z e g a ro w y . © M a o n a kilka składników , najw ażniejszy jed n ak to ten o nazw ie mo j _ b i e z a H c y _ c z a s . To jakby serce zegara. Tu p rzech o w y w an a będzie inform acja o upły' w ający m czasie (w m ilisekundach). © S k ład n ik n a z wa to tablica znaków . Tutaj k a ż d y z czasom ierzy p rzech o w y w ał b ę d z ie sw oją nazw ę - g łów nie po to, by ją w y p isy w a ć na ekran. D zięk i tem u w y p isu jąc na ekranie czas, w ypisze też - z k tó reg o czasom ierza o n pochodzi. © Tu je st sk ład n ik , który w niesie d o naszego p rz y k ła d u nieco p rzy p ad k o w o ści. W nim b o w iem , w trakcie tw orzenia obiektu, zap isz em y inform acje o tym , jak n ie d o k ła d n y ma być ten kon k retn y czasom ierz. N a czym taka nied o k ład n o ść b ęd zie polegała? N a tym , że raz na jakiś czas, "(m ili)sekundnik" tego „zeg ara" p rz esk o czy nie o jed n ą d ziałkę, ale o dw ie. Tu w ięc decydujem y o tym , jak b a rd z o ten zeg ar m a „spieszyć". K onkretnie: w sk ład n ik u o nazw ie c o _ i l e _ p r z y s p i . e s z a będ zie zap isan a w arto ść określająca co ile m ilise k u n d ten cz a so m ie rz m a p rz y sp ie sz y ć o jed n ą m ilis e k u n d ę . © O to k o n stru k to r klasy m o d u l_ z e g a ro w y . Jego p ie rw szy m a rg u m e n te m jes C -strin g m ający być n a zwa tego czasom ierza. D ru g i a rg u m e n t to w ła śn ie om a w ian a w yżej w artość określająca jak często czaso m ierz p rzeskoczy o jednć m ilise k u n d ę więcej, niż pow inien. 0 O to fu n k cja o nazw ie mi j a_ m i l i s e k u n d a . N a p ra w d ę czas w n aszy m ek sp ery m en cie m ierzo n y jest tzw . elektronicznym rezonatorem kwarcowym. N aw et, jeśl tego te rm in u nie ro zu m iesz, w ied z że jeśli „to coś" o d m ierzy u p ły n ięcie m ilise k u n d y , w y w o ła tę w łaśn ie funkcję. Co w ted y zro b i n a sz a funkcja ?
O Tu w id z isz , ż e po p ro stu inkrem entuje ona sk ład n ik m o j _ b i e z a c y _ c z a s .
493
Rozdział. 10. Klasy Specyfikator m u t a b l e
© A b y z a s y m u lo w a ć s p ie s z e n ie s ię n a s z e g o c z a s o m ie rz a , w p r o w a d z a m y ta k ą in s tru k c ję i f , w k tó re j b lo k u je st p o w tó r n a in k re m e n ta c ja z m ie n n e j mo j _ b i e żacy czas. S p ó jrz n a w a r u n e k in stru k c ji i f . if(
' ( mó j b ie z a c y _ c z a s % c o _ i l e _ p r z y s p i e s z a ) ) //. “
P y ta n ie : K ie d y ta k a s y tu a c ja z a jd z ie ? W id z im y o p e r a t o r re s z ty z d z ie le n ia . W d o d a tk u ta o p e ra c ja r e s z ty z d z ie le n ia jest u ję ta w n a w ia s i z a n e g o w a n a . C o to z n a c z y ? O c z y w iś c ie w ie rz ę , ż e ja g łę b o k o sie z a s ta n o w is z , to u d z ie lis z p r a w id ło w e j o d p o w ie d z i. Ja n a to m ia s t ra d z ę C i b y ś z a p a m ię ta ł, ż e je s t to k la sy c z n e w y r a ż e n ie , ! (x % n) k tó r e g o w a rto ś ć jest true (prawda), g d y x je st w ie lo k ro tn o ś c ią n. Je śli to w y ra ż e n ie je st w ja k ie jś p ętli, w k tó re j x ciąg le je st in k re m e n to w a n e , to w y r a ż e n ie to m a w a rto ś ć prawda r a z na n ra z y . W rac ając d o n a s z e g o w y ra ż e n ia - jeśli m o j _ b i e z a c y _ c z a s b ę d z ie m ia ł w a r tość, k tó ra b ę d z i e w ie lo k ro tn o ś c ią w a rto ś c i c o _ i l e _ p r z y s p i e s z a , to w te d y m ilis e k u n d n ik te g o c z a so m ie rz a p rz e s k o c z y d o d a tk o w o o jeszcze je d n ą d z ia łk ę . , . . . . . © To d e fin icja fu n k c ji o n a z w ie p o d a j _ m i l i s e ) c u n d y . Je st o c z y w is te , ze jes s ę b u d u je z e g a r , t o p o to, b y n a n ie g o o d c z a s u d o c z a su s p o jrz e ć i o d c z y ta ć czas. a fu n k c ja je st w ła ś n ie w ty m c e lu . P o d a je n a m cza s z d o k ła d n o ś c ią d o milisekundy. © D efinicja fu n k c ji o n a z w ie p o d a j _ s e k u n d y . W p r a w d z iw y m m o d u l e z e g a row ym n ik t n ie z a p y ta o sekundy, (to ć to d la m ik ro ś w ia ta w ie k i całe...). J e d n a k w n a s z y m p r o g r a m ie , p r z y d a n am się to. Jak wiadomo, sekunda to 1000 milisekund. W ciele fu n kcji wulztsz dziele nie przez 1000. Jest to dzielenie całkowite, a to znaczy, że jeśli minęło 5999 m ilisekund to otrzym am y odpowiedź: 5 sekund (a nie 6). Tak jest lepiej, gdy chce się wiedzieć ile minęło pełnych sekund. OO F u n k c ja o n a z w ie d r u k u j _ c z a s , d z ię k i k tó rej n a e k r a n ie p o ja w ią się ła d n e w y d r u k i o z n a jm ia ją c e ile to sekund i (p o k ro p c e d z ie s ię tn e j) milisekund u p ły n ę ło w e d łu g d a n e g o c z a so m ie rz a . (D la le p s z e g o r o z r ó ż n ie n ia - na p o c z ą tk u c z a s o m ie rz w y p is u je sw o ją n a zw ę). O© P u b lic z n a f u n k c ja s k ła d o w a w y z e r u j _ c z a s , d z ię k i k tó re j w d o w o ln e j ch w ili m o ż n a s p o w o d o w a ć , b y c z a so m ie rz z a c z ą ł liczyć z n o w u o d z e ra . N ie z a b r z m ia ło to m o ż e w y sta rc z a ją c o d r a m a ty c z n ie , w ię c u jm ę to tak: P u b lic z n a fu n k cja s k ła d o w a w y z e r u j _ c z a s , d z ię k i k tórej, w d o w o ln e j c h w ili, b y le k to , m o ż e w tra k c ie w a ż n e g o p o m ia ru w y z e r o w a ć n a m d a n y c z a so m ie rz . N ie b ę d z ie m y u ż y w a li tej fu n k cji w ty m p r z y k ła d o w y m p r o g r a m ie , ale z tego p o c z u c ia z a g r o ż e n ia ro d z i się c z ę s to d e c y z ja , b y ja k iś c z a s o m ie rz o p a trz y ć p r z y d o m k ie m c o n s t .
494
Rozdz. 10. Klasy Specyfikator m u t a b l e O © W funkcji m a in w id z im y definicje trzech m o d u łó w z eg a ro w y ch . Pierw szym i a rg u m e n ta m i k o n stru k to ró w są C -strin g i opisujące n a z w ę c za so m ierza (nazw ę gałęzi p o m ia ro w e j, w której pracuje). N a z w y te są takie a n ie inne, bo (przypo m in a m literack ą w arstw ę) - m a m y d o czy n ien ia z m o d u ła m i zeg aro w y m i w gałęziach p o m ia ru : ♦♦♦ - k w a n tó w g am m a, «+♦ - c z ą stek alfa (ang. alfa particleś), ♦♦♦ - fra g m e n tó w ro zszczep ien ia (za pom ocą se p a ra to ra ) Z a u w a ż , ż e s ta łe d o sło w n e n iek tó ry ch n a z w p o p rzed z iłem spacjam i. T o ty lk o z p o w o d ó w estety czn y ch . R zuć o k iem na w y d ru k ek ran u - je st chyba czytelniej, g d y ró żn e z e g a ry m ają na ek ran ie ró ż n e „w cięcia".
Najpierw sprawdzimy, jak dokładnie chodzą nasze zegary O ® O to pętla w h i l e , która sy m u lu je u p ły w czasu. W k a ż d y m o b ie g u p ętli w yw o łuje się ( 0 © ) kolejno d la w szy stk ich m o d u łó w czaso w y ch funkcję mi j a _ m i lisek u n d a. M u siałem tak to zrobić, bo co p ra w d a , w bibliotece s ta n d a rd o w e j C++ jest funkcja o d m ierzająca rzeczy w isty czas, ale niestety ty lk o z d o k ład n o ścią do jednej se k u n d y . D la n as to za m ała d o k ład n o ść. Z atem n asza p ę tla w h i l e sy m u lu je u p ły w rzeczy w isteg o c za su . Z a u w a ż cieka w ą rzecz. N ig d z ie nie zrobiłem liczn ik a tej pętli. To celo w o , b o - tak jak w p rz y ro d z ie - c z a s sobie płynie, ale s a m się n ie o d m ierza. O d m ie rz a ć p róbujem y g o m y, lu d z ie - z lep szą lub g o rszą d o k ład n o ścią. © O T utaj zd e c y d o w a liśm y , że dla n aszy ch celów , co 20000 m ilise k u n d (które o d m ie rz a m o d u ł czaso w y „ s e p a ra to ra frag m en tó w ")... @ 0 ...w y p isy w ać b ę d z ie m y na ek ran ie czas o d m ierzo n y p rz e z w szy stk ie trzy m o d u ły czaso w e. Spójrzm y na ek ran . W id zim y tam te k st w y p is y w a n y co jakiś czas: GAMMA GAMMA GAMMA
: 20.002 s : 40.003 s : 60.004 s
SEPAR SEPAR SEPAR
: 20 : 40 : 60
s s s
ALFAP ALFAP ALFAP
: : :
19.999 s 39.997 s 59.995 s
Jak w idać, te trz y ze g a ry nie idą w sp ó łb ieżn ie (sy n ch ro n iczn ie). I P ytanie: k tó re zeg ary id ą źle? Jeśli o d p o w ied ziałe ś, że SEPAR id z ie d o b rze, a d w a p o z o s ta łe ź le - to d ałeś się nabrać. O n e w szy stk ie id ą źle! P am iętasz ich definicje? modul_zegarowy gamma moduł zegarowy separ modul_zegarowy alfap
("GAMMA", 4000); (" SEPAR", 5010); (" ALFAP",9200);
// O ©
W d ru g ic h a rg u m e n ta c h w y słan y ch k o n stru k to ro m w s z ę d z ie jest p o d a n e jak b a rd z o d a n y z e g a r m a spieszyć. Jak w id ać, z e g a r SEPA R w c a le nie je st tym n ajlepszy m . (L ep szy jest ten, z a rg u m e n te m 9200 - bo te n p rz y s p ie s z a d o p ie ro ra z na 9200 m ilisek u n d )
495
Rozdział. 10. Klasy Specyfikator m u t a b l e
D laczego z a te m n a ek ran ie o d czaso m ierza „SEPA R ' o trzy m y w a liśm y takie ład n e, o k rą g łe w arto ści? D latego, ż e to jeg o sam eg o u ży w a liśm y d o o d m ierzen ia o d cin k a czasow ego. Był w ięc „ s ę d z ią w swojej w łasnej sp raw ie". To znaczy wystartowaliśmy zegary na 60 sekund. Po tym czasie (odmie rzonym czasomierzem SEPAR) wypisaliśmy ich wartości na ekran. Oczywiste jest, że czasomierz SEPAR miał właśnie 60 sekund, bo przecież pomiar czasu zatrzymaliśmy wtedy gdy to na nim u p ły w a ło 60 sekund, Z tej części p ro g ra m u d o w ied zieliśm y się więc, ż e „n o b o d y is perfecP
.
| P o p ro stu : N asze zeg ary c h o d zą niesy n ch ro n iczn ie.
Rozwiązanie jest takie: T w o rzy m y c z w a rty m o d u ł czasow y, a w d o d atk u u m ieszczam y go w kom orze o stałej te m p e ra tu rz e , by w ah an ia d o b o w e te m p eratu ry w h a li p om iarow ej - nie m iały na n ie g o w p ły w u . © O Jego definicja w y g lą d a tak: const modul_zegarowy zloty
("ZLOTY",
9S99999);
Będzie to n a sz w zo rco w y zegar. Z a u w a ż , że d ru g i a rg u m e n t m a w artość 9999999, a z a te m te n zegar też b ęd zie spieszył. („N ik t n ie je st d o s k o n a l/ ). Z tym , że ta d u ż a liczb a m ów i, że z d a rz a ć m u się to b ęd zie raczej rzadko. W ażne jest je d n a k , że od tej p o ry jest o n dla n as św iętością, czyli n ik t me pow in ien go n a m ro zreg u lo w ać (n aw et p rzez n ieu w ag ę) . D latego w łaśn ie w jego definicji u m ieszczo n e jest słow o c o n s t .
Łatwo taki przydomek postawić, ale to niestety pociąga za sobą wiele konsekwencji. Sam zobacz. © © O to d ru g a p ę tla w h i l e , która sy m u lo w ać będ zie u p ły w czasu w drugiej, ulepszonej w ersji naszego ek sp ery m en tu . © @ W pętli tej m a ta k ż e chodzić zeg a r zloty. Tu w id zisz, ż e i d la niego u p ły w a m ilisek u n d a - czy li następuje w y w o łan ie zloty.mija_milisekunda() ;
Jak w iem y, n a rzecz obiektu stałego k o m p ilato r p o zw o li w y w o ła ć tylko funkcję składow ą, k tó ra m a p rzy d o m ek c o n s t . Zatem , aby m o ż liw e było w y w ołanie funkcji m i ja_mi li s e k u n d a na rzecz stałego o b iek tu zloty - w 0 , czyli w deklaracji tej funkcji, m uszę postaw ić sło w o const T eraz je d n ak k o m p ilato r zaprotestuje, bo w ciele tej funkcji jest instrukcja moj_biezacy_czas++;
12)
II
& ______
"Nikt nie jest doskonały". To jest ostatnie zdanie z filmu Billy Wildera „Pół żartem, poi serio".
496
Rozdz. 10. Klasy Specyfikator m u t a b l e czyli p ró b a m odyfikacji sk ład n ik a stałego o b iek tu . Co zrobić? P rzecież ni< m o ż em y z tego zrezy g n o w ać, bo nasz zegar nie b ę d z ie chodził! Istotą z e g a ra jesl p rzec ież „ ty k an ie". I tu w ła śn ie z pom ocą p rz y c h o d z i nam słowo kluczow< mutable. S taw iam je p rz y sk ład n ik u mutable long moj_biezacy czas;
// ©
D zięki tem u , n aw et jeśli o b ie k t będzie stały - teg o sk ład n ik a to nie d o ty czy . Cc w ięcej: m o ż em y zm ien iać je g o w artość n aw et z ciała funkcji o p rz y d o m k u c o n s t . N ie zap ro testu je o n a, b o wie, że on m oże b y ć m u t a b l e („zm ienialny"). W n a sz y m p ro g ram ie jest jeszcze kilka innych funkcji. © O O to w y w o ła n ie zloty.podaj_milisek u n d y ()
F unkcja ta, sk o ro jest tu w y w o ły w a n a na rzecz sta łe g o obiektu zloty - tez m us m ieć p rz y d o m e k const. Z o bacz, m usiałem go w ię c postaw ić w © . Tu n ie ma je d n a k ż a d n y c h p ro b lem ó w , b o przecież ta funkcja i tak nie m odyfikuje żadne* go z e sk ład n ik ó w . Po p ro stu o g lą d am y sobie tu taj n a sz złoty zegar. Podobnie to samo c o n s t należałoby postawić przy funkcji p o d a j * _sekundy. Ponieważ jednak tak się akurat składa, że w tym programie ani raz nie wywołujemy tej funkcji na rzecz obiektu stałego z l o t y , więc kompilator nie zaprotestował, c o n st wymaga się tylko przy funkcjach, które są uruchamiane dla obiektu cons t. Jak w id z isz - w definicji k la sy p ostaw iłem to s łó w k o const p rzy funkcjach, k tó re n a p ra w d ę niczego nie zm ieniają. D zięki te m u w zbogacił mi się w a c h la rz fu n k q 'i sk ład o w y ch m o żliw y ch d o pracy na rzecz o b ie k tu const.
Wróćmy do naszego problemu ze źle chodzącymi zegarami © O O to tu taj, ra z na 1000 m ilise k u n d , n astąp i sy n ch ro n iza cja zegarów . © 0 T ak w ła śn ie w y g lą d a w y w o ła n ie tej funkcji synch ro n izacy jn ej. g a m m a .synchronizuj_sie_z(&zloty) ;
// © 0
Jest to ja k b y p o w ie d z e n ie m o d u ło w i zeg aro w em u o n a z w ie gamma, by sy n c h ro n iz o w a ł się w e d łu g n aszeg o złotego, najlepiej c h o d z ą c e g o zegara. O © S p ó jrzm y n a definicję tej funkcji. O to pierw sza jej lin ijk a v o id
sy n c h r o n iz u j_ s ie _ z (con st
m o d u l_ z e g a ro w y
*
w sk _ z _ k im )
Jak w id ać, a rg u m e n te m fo rm a ln y m tej funkcji jest w sk a ź n ik d o jakiegoś m o d u łu z e g a ro w e g o , w ed łu g k tó re g o n asz czaso m ierz m a się n areg u lo w ać. I to nie byle ja k i w sk a źn ik , ale taki, k tó ry trak tu je p o k a z y w a n y o b iek t jako o b ie k t stały. („N iety k aln y "). Nie jest to tylko z dobrej woli złożona szlachetna obietnica. Ta funkcja przecież (jak widzieliśmy w © 0 ) ma otrzym ywać adres obiektu z l o ty, który jest obiektem stałym. Zatem niema mowy o dobrej woli. Kompilator nie pozwoliłby wysłać do tej funkcji adresu takiego stałego obiektu z l o t y , gdyby ona nie odbierała go za pomocą tego wskaźnika do „nietykalnego" obiektu.
497
Rozdział. 10. Klasy Specyfikator m u t a b l e
C iek aw a rzecz, s a m a ta funkcja nie m a p rz y d o m k a const. N ie m usi, bo raczej n ie b ęd ziem y jej w y w o ły w a ć na rzecz o b iek tu const (zloty). Bez sensu jest p rzecież re g u lo w a n ie zeg ark a w e d łu g n ie g o sam ego. 0 © G d y b y m je d n ak p o m y lił się i n ap isał n a p rz y k ła d tak: zloty.synchronizuj_sie_z (igaruma) ;
czyli p rz e z p o m y łk ę chciałbym sy n ch ro n izo w ać o d w ro tn ie - czyli zlot y w e d łu g gorzej c h o d z ą c e g o zegara gamma - to k o m p ilato r o d ra z u m i zn ajd zie ten błąd: N a rzec z s ta łe g o obiektu (zloty) tej funkcji sy n ch ro n izacy jn ej w y w o ły w ać nie m o żn a, b o n ik t nie d ał jej p rz y d o m k a const - zezw alająceg o na takie w y w o łan ie. T o d la teg o w p ro g ra m ie ująłem tę fu n k cję w kom entarz.
Zaglądnijmy do ciała tej funkcji synchronizującej P o n iew aż b y ło ju ż to d aw n o tem u , ab y łatw iej n am ro zm aw iać, zacytuję tu ten fragm ent: void synchronizuj sie z (const modul_zegarowy
,. . wsk_z_kim)
// O 0
int czas odniesienia = wsk_z_kim-podaj_milisekundy (), if (moj_biezacy_czas !« czas_odniesienia) cout « << << << <<
nazwa « " : Korekta w " (czas_odniesienia / 1000) " sek. (" czas_odniesienia - moj_biezacy_czas " m s ) " << endl;
// w ł a ś c i w a
s y n c h r o n iz a c ja
moj biezacy_czas = czas_odniesienia;
// O ©
o©
) } O © N ajp ierw d a n y czaso m ierz d o w iad u je się o d czas p o k a z y w a n y p rz e z w zorcow y zegar. O © Jeśli ten czas się ró żn i.... O © ... to d o k o n u je u sta w ie n ia sw ojego c z a su na czas p o k a z y w a n y p rzez zeg ar w zorcow y. O © P rzy okazji w y p is u je na ekranie inform ację o tym , że k o n ieczn a była synchro nizacja.
Ciekawostka Z obacz na ek ran ie, co się stało w zerow ej sekundzie. D o ty ch czaso w e zegary m iały ju ż w so b ie zap isa n y już jakiś u p ły w czasu - czyli m iały jakieś ok. 6000 m ilisek u n d o w e w a rto śc i bieżącego czasu. T ym czasem o b ie k t zloty - dopiero teraz się narodził. M a u sieb ie bieżący czas zero w y . P o n ie w a ż pozostałe czaso m ierze mają się w e d łu g n ieg o synchroni zo w ać - w id z isz n a ekranie tę korektę p rz e p ro w a d z o n ą w sęk u n d z ie 0 (w edług czasu
złotego).
498
Rozdz. 10. Klasy Specyfikator m u t a b l e W p ro w a d z e n ie
o b ie k tu
o d n ie sie n ia — *
GAMMA: Korekta w 0 sek. (-61003 ms) SEPAR: Korekta w 0 sek. (-61000 ms) A L F A P : Korekta w 0 sek. (-60994 ms)
W szy stk ie trz y g rzeczn ie cofnęły się d o w artości, k tó rą m a obecn ie z l o t y (czyi. zero). 0 © N a z a k o ń c z e n ie p o m iaru w id z isz na ek ran ie w y d ru k czasu. Zobacz: m im o że zeg a ry n ie b y ły b a rd z o d o k ła d n e - ch o d z iły s y n c h ro n ic z n ie d zięk i co-sekund o w e m u p o ró w n y w a n iu ich z e „ stały m ", w zo rco w y m zeg a rem . N a e k ran ie u d a ło mi się u ch w y cić sytuację, g d y na z a k o ń c z e n ie tego 10 m inuto w ego p o m ia ru - jeden z z e g a ró w był tu ż p rzed sw o ją synchronizacją, więc jeszcze s p ie s z y ł o jedną m ilisek u n d ę.
W Z o b aczy liśm y tu klasę, która s łu ż y ć m iała d o p ro d u k cji o b ie k tó w zw ykłych, ale także i o b ie k tó w const. O b iek ty const m ają z a m ro ż o n e w sz y stk ie składniki d an e, w ięc n ie m o żn a w nich n ic zm ienić. Jeśli je d n a k p rz y je d n y m z takich sk ła d n ik ó w p o sta w im y sp ecy fik a to r mutable, to te n s k ła d n ik m o że być m im o w sz y stk o z m ie n ia n y - (tak ja k b y to było w zw y k ły m , n i e - c o n s t obiekcie).
Na zakończenie zagadka P am iętasz m o ż e jak n ie d aw n o m ó w iłem , że jeśli ja k iś s k ła d n ik -d a n a jest za pom ocą p rz y d o m k a const o k re ślo n y jako stały, to n ie m o ż n a p rz y takim sk ła d n ik u p o sta w ić sp ecy fik ato ra mutable. Po p ro s tu n ie m a sensu tak ie ro zd w o jen ie jaźni: składnik "zmienialny", ale stały. O to p rz y k ła d o w a definicja klasy c la ss
A
m u ta b le m u ta b le m u ta b le
//.
con st in t * const
in t x; c o n s t w skA i n t * w skB
/ / < — b łq d ! / / < — b łq d ! //< —
poprawne
. .
}; C zy p o tra fisz p o w ied zieć d laczeg o d w a p ierw sze s k ła d n ik i tej k lasy są d ek laro w a n e b łę d n ie, a trzeci p o p raw n ie?
O d p o w ie d ź n ie jest tru d n a. P o w ied zieliśm y , że nie m o ż n a p o sta w ić p rzy d o m k a mutable p rz y d an ej składow ej, k tó ra m a być stała. ♦♦♦ P ie rw s z a d a n a składow a: x — d ek laro w a n a tu je st ja k o stały o b iek t int. S k o ro ch ce m y by był stały , to czem u o d razu "nie ch cem y " - staw iając to mutable? K om pilator p o p ro s i o z d ro w y ro z s ą d e k .
499
Rozdział. 10. Klasy Ćwiczenia
♦♦♦ D ruga d a n a składow a o n azw ie w s k A — jest to stały w skaźnik d o obiektu ty p u i n t . Znow u to sam o: skoro sk ład n ik m a być stały, to dlaczego o d razu staw iam y m u t a b l e ? Błąd! ♦♦♦ Trzecia d a n a składow a o n azw ie w s k B — jest (czytaj u w ażn ie tę dekla rację) wskaźnikiem do statych obiektów tyyu i n t . T e n w sk a ź n ik ten sam n ie je st s ta ły , bo raz m oże p o k azać na taki stały obiekt i n t , a potem na inny stały o b iek t i n t . Skoro w ięc ta dana sk ład o w a nie jest stała - w o ln o przy niej p o staw ić przeciw działajce ew entualnem u zam rażan iu słówko mutable.
10.21 Ć w ic ze n ia Które z poniższych zdań są prawdziwe? a) Klasa to obiekt. b) Klasa to typ. c) Składnikiem klasy może być obiekt typu double. d) Składnikiem klasy może być funkcja. e) Składnikiem klasy może być obiekt innej klasy. Wytłumacz na czym polega enkapsulacja. Jaki zakres ważności mają funkcje składowe klasy? Czy można stworzyć w klasie funkcję składową o takiej samej nazwie, jak istniejąca funkcja globalna? Czy można jej dać takie same argumenty formalne? Jeśli tak zrobimy, to czy nastąpi: a) błąd, b) przeładowanie nazwy, c) zasłonięcie nazwy. Czy w klasie mogą być dwie funkcje składowe o tej samej nazwie? Jeśli tak, to pod jakim warunkiem? Z jakich zakresów ważności dostępne są składniki klasy określone etykietą public? Z jakich zakresów ważności dostępne są składniki klasy określone etykietą private? Czy jeśli na początku definicji klasy nie postawimy etykiety określającej dostęp, to jaki dostęp, przez domniemanie, mają postawione tam składniki: p u b lic , p rotected czy p rivate? Czy w danej klasie funkcja składowa p u b lic może wywołać funkcję składową p rivate? ....... _.............. .........„i Czy, jeśli funkcja składowa klasy, ma ciało zdefiniowane poza definicją klasy - to ma dostęp do składników private? Jaka jest istotna różnica między definiowaniem funkcji składowej wewnątrz definicji klasy, a definiowaniem tej samej funkcji składowej poza ciałem klasy?
500
Rozdz. 10. Klasy Ćwiczenia
Co oznacza używane przez niektórych programistów określenie "metoda danej klasy"? Jeśli na rzecz obiektu A wywołamy zwykłą funkcję składową z jego klasy, na co pokazuje w tej funkcji składowej wskaźnik th is? Jakiego jest on wtedy typu? XIV
W klasie wolno zdefiniować składnik-daną o takiej samej nazwie, jak obiekt globalny o nazwie temperatura. Jak w funkcji składowej tej klasy można odnieść się do takiego składnika globalnego? W funkcji składowej klasy A stworzyliśmy jakiś zakres lokalny (np. blok i f ), a w nim jest definicja obiektu o nazwie (nnn) identycznej z nazwą składnika-danej (nnn) tej klasy, i równocześnie identyczna z nazwą (nnn ) obiektu globalnego. a) Czy mamy tu do czynienia z zasłanianiem nazwy czy z przeładowaniem nazwy? b) Czy można w tym zakresie lokalnym odnieść się do tego obiektu globalnego? Jeśli tak, to jak? c) Czy można w tym zakresie lokalnym odnieść się do tego składnika-danej? Jeśli tak, to jak? d) Czy można w tym zakresie lokalnym odnieść się do tego lokalnego obiektu? Jeśli tak, to jak?
XVI
Czy nazwa obiektu może zasłonić nazwę funkcji? Czy nazwa funkcji może zasłonić nazwę obiektu?
XVIII
Czy w klasie A może nastąpić przeładowanie nazwy funkcji składowej f f f , która już zasłania nazwę funkcji globalnej fff? Jeśli w zakresie tej klasy wywołujemy funkcję o nazwie f f f , a zestaw argumentów wywołania nie pasuje (dokładnie) do deklaracji składowych funkcji f f f , natomiast pasuje do deklaracji globalnej funkcji f f f - co wtedy zrobi kompilator? Przypomnij, przed czym strzeże nas tzw. strażnik nagłówka. Poznaliśmy zasadę "Każda klasa w osobnym pliku", ale oczywiście należałoby ją raczej wymienić na taką "Każda klasa w osobnych dwóch plikach". Na czym to polega?
XXI
Mamy klasę o nazwie TTT. Jednym z jej składników-danych jest 8000-elementowa tablica obiektów typu int. Ma ona nazwę 1 1. Dokończ poprawnie następujące zdanie. Gdy obiekt ob j takiej klasy TTT, wysyłamy przez wartość do jakiejś funkcji to: a) - otrzymuje ona wskaźnik do tablicy tl obiektu ob j. b) - otrzymuje ona oryginał tablicy 1 1 obiektu ob j. c) - otrzymuje ona kopię tablicy t l obiektu ob j . Mamy klasę M. Jaki jest typ rezultatu jej funkcji składowej zwanej konstruktorem tej klasy? Jaka jest pełna nazwa tego konstruktora? Jaka jest pełna nazwa destruktora klasy M? Jaki jest typ rezultatu?
XXIV
Czy w klasie M można dokonać przeładowania konstruktora? Czy w klasie M można dokonać przeładowania destruktora?
XXVI
Mamy klasę K, której publicznym składnikiem statycznym jest obiekt typu bool o nazwie flaga. W funkcji main zdefiniowane są trzy obiekty tej klasy K. Mają nazwy x l, x2, x3. Jeśli obiekt xl wpisał sobie do tego składnika flaga wartość true... a) jak o tym mogą się dowiedzieć obiekty x2 i x3?
501
Rozdział. 10. Klasy Ćwiczenia
XXVII XXVIII
b) jak można się dowiedzieć tego z zakresu funkcji main? (Podaj tu 4 możliwe sposoby) Czy składnik statyczny prywatny może mieć przy swojej definicji (leżącej poza ciałem klasy) - wyrażenie inicjalizujące go? Skoro w definicji składnika statycznego klasy K, leżącej poza ciałem klasy K, me występuje słowo s t a t i c - s k ą d kompilator ma wiedzieć, że jest to składnik statyczny? Klasa K ma składnik nie-statyczny typu in t o nazwie sn oraz składnik statyczny typu in t o nazwie ss. a) Jaka jest pełna nazwa typu składnika sn? b) Jaka jest pełna nazwa typu składnika ss?
Pytania dla tych, którzy przeczytali paragraf 10.16.1, str. 470.
WAYA A
XXXII
Jakie warunki muszą być spełnione, by można było przy deklaracji składnika w klasie od razu umieścić wyrażenie inicjalizujące go. Jeśli wspomnianemu powyżej składnikowi statycznemu w linijce definicji postawimy wartość inicjalizującą różną od tej wartości, która była w linijce jego deklaraq, (w ciele klasy), to którą wartość przyjmie kompilator za obowiązującą. Jeśli w ciele klasy K, przy deklaracji składnika statycznego od razu umieściliśmy inicializator, a składnik ten nie ma swojej definicji poza ciałem klasy, to i tak można korzystać z zapisanej w tym składniku stałej wartości. Jaka operacja będzie jednak niemożliwa?
W Dla wszystkich Załóżmy, że mamy klasę K, a jeszcze nie zdefiniowaliśmy ani jednego obiektu tej klasy. Wybierz poprawne dokończenia następującego zdania. W statycznej funkcji składowej klasy K możemy... a) - pracować ze statycznymi publicznymi składnikami tej klasy b) - pracować ze statycznymi prywatnymi składnikami tej klasy c) - pracować z nie-statycznymi publicznymi składnikami tej klasy d) - pracować z nie-statycznymi prywatnymi składnikami tej klasy XXXIV
W klasie K jest statyczna funkcja składowa o nazwie fun. Jak ją wywołać, jeśli jeszcze nie ma ani jednego obiektu tej klasy? W klasie K jest statyczna funkcja składowa o nazwie f un. Jaki jest w niej typ wskaźnika th is? Czy statyczną funkcję składową można wywołać na rzecz jakiegoś obiektu jej klasy?
XXXVII
Deklaracja funkcji składowej w klasie K oznajmia, że jest to funkcja z przydomkiem con st. , a) W którym miejscu deklaracji umieszcza się ten specyhkator. b) Jeśli funkcja const ma ciało zdefiniowane poza definicją klasy, czy powtarza się tam specyfikator const? Jeśli tak, to gdzie? Jeśli nie, to dlaczego?
502
XXXVIII
Rozdz. 10. Klasy Ćwiczenia
Czy mając stały obiekt klasy K można dla niego wywołać funkcję składową const? Czy mając nie-stały obiekt klasy K, można dla niego wywołać fu nkcję składową const? Czy dla stałego obiektu tej klasy można wywołać funkcję nie-const? Czy dla nie-stałego obiektu klasy K można wywołać funkcję składową nie-const? Z tekstu dla dociekliwych. Mamy klasę K. W niej są funkcje z przydomkiem con st i v o la tile a) Jaki jest typ wskaźnika th is w funkcji składowej const? b) Jaki jest typ wskaźnika t h is w funkcji składowej volatile? c) Jaki jest typ wskaźnika t h is w funkcji składowej co n st volatile? Czy konstruktor, który nie ma przydomka co n st może pracować na rzecz stałego obiektu swojej klasy? Czy przydomek m utable może być zastosowany także do obiektu globalnego? Jakie działanie ma przydomek mutable w obiektach klasy K, które nie są stworzone jako stałe? W klasie K, jeden ze składników-danych ma przydomek m utable- Jeśli stworzymy stały obiekt tej klasy, na co pozwala nam ten przydomek mutable?
W
Rozdział. 11. Biblioteczna k lasa s t d : : s t r i n g do operacji z tekstam i
11
503
B iblioteczna klasa s t d : : s t r i n g do operacji z tekstam i ie w ie m czy zauw ażyłeś, jak łatw o dodać d o siebie dw a obiekty typu in t. i n t a = 2 , b = 3 , c; c = a + b;
A by p rzep isa ć zaw arto ść obiektu b d o obiektu a, w y starczy prosta instrukcja a = b; W ielu lu d z i się m usiało napracow ać, by to było takie łatw e. Dzięki temu m ożem y d o d a w a ć liczby typów w budow anych i n t , d o u b l e , f l o a t itd.
Niestety nie możemy tak łatwo dodać do siebie dwóch C-stringów (ciągów znaków). C zy p am ięta sz , jak w poprzednich rozdziałach ż m u d n ie pisaliśm y funkcję s t r c p y , k tó ra przepisyw ała C -string w e w skazane miejsce? M ożna by zap y tać: „dlaczego"? D laczego z C -stringam i nie m ożem y łatwo robić czegoś, co jest tak oczywiste? O d p o w ied ź jest bru taln a: D latego, z e w językach C i C++ C -string nie jest typem w budow anym . Nie m a w ię c w C ++ o p e ra c ji na C -s trin g a c h . m im m iiiw m ii u — M M H i m iim im r
1 ". . . . . . . . . . . . . . . . . .. * * ‘ * * * l ł
■ ■ ***•
C -string nie jest ty p em w budow anym , ale to nie znaczy, że nie istnieje. Musiał zaistnieć, bo n a k ażd y m poziom ie program ow ania - czy to w asem blerze, czy w
504
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i
językach w y ższeg o rzęd u - istnieje konieczność kom unikacji z człowiekiem. Z atem w C i C ++ m a m y stałe dosłowne typu C-string (ciągi zn ak ó w ). Jeśli jednak chcem y je w staw ić d o jakiegoś obiektu - to specjalnego ty p u obiektu na to nie m am y. R ozw iązan ie je d n ak m usiało się znaleźć. Kolejne litery u m ieszczan e są w tablicy zn ak ó w . Tablica (np. znaków ) to jest już typ w b u d o w a n y . M ożna na niej oczyw iście w y k o n y w a ć jakieś operacje, ale są to takie sam e operacje, jakbyśm y w tej tablicy trzy m ali liczby. T ym czasem tam są nie "luźne" znaki, ale ciągi z n ak ó w , k a ż d y z elem en tó w tej tablicy o so b n o niew iele zn ac zy - d o p iero razem sk ład ają się na jakieś sen so w n e zdanie. N ie o k łam u jm y się, operacje na tablicach nie są w y m y ślo n e pod k ątem "ciągów znaków ". C o p raw d a, u ży w ają c tych operacji, ła tw o m ożem y d o k o n ać w y m ian y litery złożo n ej w 1 5 -ty m elem encie tablicy na in n ą, ale już w staw ien ie jakichś liter m ię d z y literę szó stą, a siódm ą - trzeba robić „ręcznie" ro zsu w ając pozostałe. C o p ra w d a , w sta w ia n ia dod atk o w y ch liczb w środek tablicy liczb nie p rzep ro w a d z a się często, ale w p rzy p ad k u tekstu (trzym anego p rz e c ie ż w tej tablicy zn ak ó w ) - to o p eracja dość pow szechna. D o tego d o ch o d zi najgorsze: cały czas m u sim y pam iętać, że ta k a tablica zn ak ó w m a o k reślo n y ro zm iar, w ięc jeśli z a m ierza m y dopisyw ać coś d o n aszego ciągu zn a k ó w , w końcu tablica się całkow icie zap ełn i. W tedy m u sim y od ło ży ć w szys tk o na bok i zająć się po w ięk szan iem starej tablicy, albo rezerw acją nowej. O czyw iście z a w s z e trzeb a pam iętać o zw o ln ien iu rezerw aq'i. In n y p rzy g n ęb iający p rzy k ła d - d o łą czan ie dw óch C -strin g ó w . A by łączyć ze so b ą d w a C -stringi, trzeb a było albo n ap isać sw oją funkcję, k tó ra ż m u d n ie, znak p o z n ak u , d o k o n a k o p iow ania, albo e w en tu a ln ie s k o rz y s ta ć z funkcji bibliotecznej. T aka funkcja oczekuje o d n a s p rzy g o to w an ia ta b licy na rezu ltat (oczyw iście o d p o w ie d n io dużej). Jeśli się p o m y lim y w ro z m ia rz e - p ro g ram zniszczy coś w p am ięci i „p ad n ie". W ro zd ziale o tablicach i w skaźnikach o m aw ialiśm y te funkcje. Jest to d obre ćw iczen ie, które uczy n a s jak p o ru szać się po pam ięci k o m p u te ra , ale co d o w y g o d y ... N ie, stan o w czo to cale zag ad n ien ie nie jest w y g o d n ie rozw iązane!
Z tego niezadowolenia wielu programistów zrodził się jednak pomysł Co p ra w d a , język C ++ p rzejął o d języka C n ie w y g o d n y sp o só b p ra c y z ciągam i zn ak ó w , ale p rzec ież język C++ daje d o ręk i p ro g ram isto m p o tę ż n e narzędzie: j m ożliw o ść d efin io w a n ia w łasnego ty p u d an ej. W ielu p ro g ra m istó w u zn ało to i za w sp a n iałą szan sę, by uprościć sobie ż m u d n ą p racę z c ią g a m i zn ak ó w . Po p ro stu p o w ied zieli sobie: — S koro w języku C + + - s tr in g i n ie m ają ty p u w b u d o w a n e g o , to ja, m oim p ro g ra m ie sam s tw o rz ę so b ie taki ty p o b ie k tó w (klasę). ; K lasę, k tó ra nie tylko b ęd zie m iała sk ład n ik d o p rz e c h o w y w a n ia w łaściw eg o ciągu zn ak ó w , lecz tak że b ęd zie m ia ła d u ż o funkcji
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przykład program u z użyciem klasy s t r i n g
505
s k ła d o w y c h ułatw iających m i najczęstsze operacje na stringach. W d o d a tk u sam a zajm ie się n iezb ęd n ą „g o sp o d a rk ą pam ięcią . lak pow iedzieli, tak zrobili. Pow stało (niezależnie) w iele takich ro zw iązań klas i szybko staw ały się o n e bardzo p o p u larn e, bo - dzięki m m - m m program iści me m u s iS w y w a ż a ć raz już otw artych d rz w i. Tylko kw estią czasu było w ,ęc pojaw ienie się te g o zag ad n ien ia w bibliotece standardow ej.
Najlepsze cechy najlepszych rozwiązań złożyły się na klasę o nazwie s t r in ” ”S
Klasa ta (i to w a rz y sz ą c e jej funkcje) w eszła do biblioteki stan d ard o w ej. Jej nazw a jest z d e k laro w an a w przestrzeni nazw s t d , zatem pełna n a zw * ‘el klasy brzm i s t d : : s t r i n g W dalszej części tego ro zd ziału porozm aw iam y w łaśnie o tej klasie. , , . , _ , P am iętaj jednak, że nie będ ziem y tu om aw iać języka C++, ale p e w n ą (bardzo znaną) klasę, którą jakiś n iezn an y nam program ,sta nap isał. Klasę, która o k azała się tak pom ocna, ze w szyscy za p ra g n ę li jej używ ać. T u odczuć m o ż esz n ap raw d ę potęgę języ k a C++. Kt^ ' OZW1,“ ' nienie i teraz d aje n am to rozw iązanie w gotow ym p u d elk u , jak radioodbio k w o b udow ie. W iem y oczywiście, ze w ty m pu d ełk u tkw tą jak ieś tam tranzysto ry, cew ki i o p o
3 r , lub odpowiednikifunkcji s t r c p y , s t r c a t . i i i ) .
Jed n ak d la n as - to już jest pew na całość, k tórą w y g o d n ie jest używ ać. W ygodnie; i p ro sto . . . . . . . , . T ak też tę klasę potraktujem y. N ie będziem y zg łębiać jak ona rob, to, czy tamto. Po prostu spróbujem y nauczyć się jej używ ać, bez zaglądania do jej funkcji składowych W dalszej części tej książki, w przykładowych p ro g ram ach , praw ie zaw sze u ży w ać b ę d z ie m y tej klasy. Dobrze się oswajać się z klasą, która na pew o w y stąp i w T w o ich przyszłych program ach. ^
11.1
_______ lUMimmn__ — —
Przykład programu z użyciem klasy string Z obaczm y w p rzy k ład o w y m program ie, jak prosto u ż y w a się klasy ftd ■• s t r i n g N ie przejm uj się jeśli coś Ci tutaj u m k n ie - i tók potem jeszcze raz szczegółow iej będziem y om aw iać poszczególne funkcje składow e l o p erato ry . # in c lu d e < io s tre a m > tin c lu d e < s trin g > u s in g n am esp a ce s td ; v o id p l a k a t (s t r i n g s) ; .* * ,* * * * * * * * * //***************************** ******** i n t m ain O string klubl("Wisła");
// O
506
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przykład program u z użyciem klasy s t r i n g string klub2("Unia") ; string miasl = "Kraków"; string mias2 = "Tarnów";
// ©
string połączenie; połączenie = klubl + klub2; cout << "Dodanie dwóch stringow [" << połączenie « "]\n\n"; string zapowiedz = klubl + "-" + miasl;
// ©
zapowiedz = zapowiedz + " vs. ";
// ©
zapowiedz += klub2; zapowiedz += +mias2);
// ©
cout << "!!! Zapraszamy na mecz plakat(zapowiedz); zapowiedz += "
!!!\n";
//O O
Rezultat meczu ";
cout << "\n\nPodaj wynik meczu string wynik; cin » wynik;
";
cout << "\n\nUWAGA: Wczoraj odbył sie mecz\n"; plakat (zapowiedz + wynik);
} //
★
★
//O ©
//O©
* * * * * * * * * * * * * * * * * * * * * * * * * ,
********************************* void plakat(string tresc) //O © { string gwiazdki (tresc. length () + 6, '*'); //O© cout << gwiazdki << '\n' << gwiazdki << "\r" << »** « <(* tresc << " **\n" //O© « gwiazdki « endl;
Program zapyta o wynik meczu i w ostatecznie na ekranie będziemy widzieli następujący tekst: Dodanie dwóch stringow [WislaUnia] !!! Zapraszamy na mecz !!! *********************************** ** Wisla-Krakow v s . Unia-Tarnow ** ★ *****■*******************, ********
Podaj wynik meczu
3:2
UWAGA: Wczoraj odbył sie mecz ★ ★ ★ ★ ★ ★ nr******** ***************** ** Wisla-Krakow vs. Unia-Tarnow Rezultat meczu 3:2 ** t************** ****★»★********** ★ ****************.******
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przykład program u z użyciem klasy s t r i n g
507
Komentarz Nagłówek O Aby móc k o rzy stać z bibliotecznej klasy s t d : : string m u sim y deklarację tej klasy dołączyć d o p ro g ram u (dyrektyw ą #include). D eklaracja ta jest w pliku nagłów kow ym . Z auw aż, że n azw a tego pliku nie ma żad n eg o rozszerzenia. Zwracam się szczególnie do kolegów, którzy programowali używając przed-standardowej wersji języka C++. Był tam plik nagłówkowy z rozsze rzeniem ( * . h) s t r i n g . h, ale zawierał zupełnie inną treść (deklaracje
funkcji s t r e p y itp.). Zatem n i e pomyl się i nie napisz tu < strin g .h > , bo wtedy standardowa klasa s t d : : s t r i n g okaże się kompilatorowi nieznana. Aby się n ie m ylić, w ystarczy pam iętać, że.
wtsemwMmmtMUm
...w nowoczesnej wersji języka C++ deklaracje klas bibliotecznych są umie ; szczane w plikach nagłówkowych, których nazwy nie mają końcówki . h
© Tw órcy biblioteki stan d ard o w ej postanow ili, że nazw a naszej klasy string m a wejść do p rz e strz e n i nazw s t d . O znacza to, że w naszym program ie pow in niśm y n azw ę tej k lasy w zbogacać o kw alifikator zakresu, czyli pisać tak std::string
Jak pam iętasz, w tej książce przyjęliśm y zasad ę, że n azw y z tak w ażnej i popularnej p rzestrz en i nazw , jaką jest std - chcemy m ieć dostępne bez m ę czącego pisan ia kw alifikatora zakresu, czyli po prostu jako string. Postaw io na tu, w © , d y re k ty w a using, ten prostszy zap is nam w łaśnie um ożliw i. Możliwe jednak, że Ty podejmiesz inną decyzję, (lub taką decyzję podjął kierownik Twojego zespołu) - wtedy oczywiście ta dyrektywa jest niepot rzebna, a w naszym programie wszystkie -wystąpienia nazwy klasy s t r i n g , powinieneś zamienić sobie na s t d : : s t r i n g .
Definiowanie obiektów klasy string © O to definicja n aszeg o pierw szego obiektu klasy string. Jak rozpoznajesz - jest to zw ykła sk ład n ia stosow ana przy definiow aniu obiektów jakichś klas: na zw a _ kla sy n a z w a _ o b \e k \u ( a r g u m e n t_ k o n s tr u k to r a ) ; Czytając tę definicję O string
klubl("Wisia"):
do w iad u jem y się, że k1ub1jest obiektem klasy inicjalizow any tekstem "Wisła".
string,i że od razu ma on być
508
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przykład program u z użyciem klasy s t r i n g
Tu musimy się na chwilę zatrzymać nad naszym nazewnictwem N a ten ciąg zn ak ó w tak i jak np. " W is ła ", d o niedaw na często m ów iliśm y stała dosłowna typu C-string albo po prostu string. T eraz na arenie pojaw iła się klasa biblioteczna, którą n a z w a n o tak samo. C zy nie m a ryzyka nieporozum ienia? N ie, bo - w p rz y p a d k u jakiejkolwiek d w u zn aczn o ści — i
..... r "1111111 i i iii r m r i M f-tif n r n
» ihh
program iści C++ na ciąg znaków zakończony bajtem zero - mówią teraz dla odróżnienia: „ C - s t r i m f - (czyli jakby: "s trin g , w z n a c z e n iu z ję z y k a C " ) .
Jeśli jed n ak ktoś p o w ie string - to skąd w iem y , co m a na m yśli? C zy nie jest to cza se m p o w ó d do zam ieszan ia? T ak m i się w y d aw ało na początku. Po polsku możemy oczywiście sobie pomóc nazywając tę stałą dosłowną słowami „ciąg znaków", „łańcuch znaków", (niektórzy mówią też „napis"). Ale nawet po angielsku nie ma kłopotów, dlatego że... ...najczęściej koło słów ka string sto i dru g ie, które w szelk ie e w en tu alne w ątp liw o ści rozw iew a. Z a te m m ó w im y „klasa string", lu b „obiekt klasy string" i sp raw a n aty ch m ia st jest ja sn a, bo: i• li V. .' łtiir. ; . /■ . ■ i*i* klasa string - istnieje tylko w n o w y m C++, ♦♦♦ obiekt klasy string - t o oczyw iście m u si być o b ie k t k la sy s t d : r s t r i n g a nie "obiekt k la sy C-string". Takiej klasy: C-string - p rzecież nie ma! Z apam iętaj: •
C -strin g i to nie są obiekty, tylko stałe d o sło w n e (czyli ciągi zn a k ó w ) - a stała d o sło w n a nie jest obiektem .
Tak jak liczba 3.14 nie jest obiektem. Co najwyżej, można ją umieścić w obiekcie. •
C -strin g m oże być treścią, w arto ścią, zaw arto ścią tablicy zna ków , ale sam obiektem n ie jest.
W języku C i starym języku C++ ciągi znaków umieszczało są? w tablicy c h a r , a taka tablica to oczywiście nie jest „obiekt string". Z a te m raczej nie b ęd zie n ieporozum ień.
P o ty ch refleksjach n a te m a t nazew nictw a... ,
fi
:
•
.- J
wróćmy do różnych form definicji obiektów klasy s t r i n g © O to definicja in n eg o o b ie k tu klasy s t r i n g . Z a u w a ż : sk ład n ia tej definicji string miasl = "Kraków";
p rz y p o m in a taki zapis: i n t m = 6;
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przykład program u z użyciem klasy s t r i n g
509
O czyw iście n ic d z iw n e g o , tw ó rco m k lasy s t r i n g zależało n a tym , by jak najbard ziej o d p o w ia d a ła o n a p rzy zw y cza jen io m p ro g ra m istó w i po p ro stu p rz y p o m in a ła p o s łu g iw a n ie się p o z o sta ły m i typam i. © O to definicja o b ie k tu o n azw ie p o l a c z n i e , k tóry jest o czy w iście o biektem klasy ___ l i _mich; s t r i n. g . kNr *ie m a tu inicjalizacji, w ięc o b ie k t ten b ęd zie p u sty . T ak , jakby łwta b y ła fn to
string
połączenie ('
Dodawanie stringów O A teraz z o b a c z , jak łatw e jest d o d a w a n ie dw óch strin g ó w . W tej instrukcji n astęp u je d o d a n ie zaw arto ści o b iek tu k l u b l i z a w arto ści k l u b 2 . R ezu ltat w p is a n y jest to s trin g u n azw ie p o ł ą c z e n i e . W n astęp n ej in stru k cji zostaje to w y p isa n e n a e k ra n ie , w ięc m ożesz zo b aczy ć, że to d ziała. D w a ciągi zn ak ó w zo stały d o sie b ie d o k lejo n e i stały się treścią obiektu o n a z w ie p o ł ą c z e n i e . Stało się to z a s p r a w ą użycia zn aczk a +. Jak twórcom klasy s t r i n g udało się zaprząc do pracy ten operator+, dowiemy się w dalszej części tej książki (str. 794, rozdz. 19). Na razie cieszmy się, że to takie proste. © D o d a w a n ie s tr in g ó w m oże być b ard ziej ro z b u d o w a n e . W tej linijce w id zim y definicję o b ie k tu k la sy s t r i n g o n azw ie z a p o w ie d z . Jest o n o d ra z u inicjalizo w a n y ta k im w y ra ż e n ie m . (k lu b l +
+ miasl)
Jak w id ać, c h c e m y tu dodać: •
z aw arto ść obiektu k l u b l
•
C -string:
•
z aw arto ść obiektu m i a s 1 1
W rezu ltacie te g o - w obiekcie z a p o w ie d z zn ajd zie się tak a treść: "Wisla-Krakow”
, :<■<■ ł .V . . v .• • ii fi • V © O to kolejne d o d a n ie . T eraz bierzem y te k st z obiekt z a p o w i e d z , to tego d o d aje m y C - s tr in g " v s . 1,1 i ten rezu ltat m a się stać now ą treścią o b ie k tu z a p o w ie d z . Jest to p ro s ty s p o só b d o d aw an ia czegoś d o końca istn iejąceg o strin g u . © N a to sa m o —je st jeszcze w y g o d n iejszy sposób: u ży cie o p e ra to ra += zapowiedz += klub2;
Tę linijkę m o ż n a w y p o w ied zieć tak: I N ie c h treść z obiektu k l u b 2 zo stan ie d o p is a n a d o k o ń ca bieżącej I z a w a rto śc i obiektu z a p o w ie d z . W n astęp n ej lin ijce w id zisz dalsze o p eracje d o p is y w a n ia cze g o ś d o tekstu za p o w ie d z i. 1)
______________________
vs. to skrót od łac. oersus - przeciwko
510
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przykład program u z użyciem klasy s t r i n g
Przesyłanie stringu do funkcji
00 O to w yw ołanie funkcji plakat. Z jej deklaracji ©, kom pilator w ie, ż e
plakat
je st to funkcja w y w o ły w an a z jednym arg u m e n tem będącym o b ie k tem klasy string.
Jak w idzisz: obiekty klasy string m ożna łatw o p rzesy ła ć do funkcji!
I
O © S pójrzm y na ciało tej funkcji. O dbiera ona string p rzez w artość. C z y li tw orzy so b ie kopię obiektu klasy string. Z upełnie tak sam o, jakby ch o d ziło p rzysła n ie p rzez w artość obiektu typu i n t . O © O to definicja pom ocniczego, lokalnego obiektu k lasy string. M a o n się nazy-l w ać: gwiazdki. Definicja ta w ygląda nieco zaw ile, bo w miejscu p ierw szeg o a rg u m e n tu um ieściłem o d razu w yrażenie. W szy stk o się u p raszcza, jeśli zapo m n im y na chw ilę o ty m w y rażen iu i zap iszm y tę definicję tak: string gwiazdki {ile ,
T akiej form y definicji strin g u jeszcze nie w id zieliśm y . Jest tu o czyw iście inicjalizacja polegająca na p o słu żen iu się k o n stru k to rem z d w o m a a rg u m e n tam i. I Dzięki n iem u m ożem y inicjalizow ać obiekt żąd an ą ilo ścią idenI tycznych zn ak ó w . W n aszy m p rz y p a d k u w obiekcie gwiazdki, zn ajd zie się ciąg z n a k ó w składa-' ją c y się z żąd an ej liczby g w iazd ek . (Będą one n a m po trzeb n e, by o zd o b ić nasz w y d ru k na ekranie). A ile tych g w iazd ek m a być? To u zależn iam y od d łu g o ści strin g u p rzy słan e g o d o naszej funkcji jako a rg u m e n t. Jak się d o w ied zieć o dłu g o ść tego p rzy słan e g o s trin g u ? W ystarczy w y w o łać jego funkcję sk ła d o w ą o n azw ie l e n g t h (czyli: dłu g o ść). tresc.length()
W rezu ltacie tego w y w o łan ia obiekt klasy s t r i n g o n azw ie t r esc przeliczy sw o je zn ak i i o d p o w ie n am jakąś w artością. M y d o tej w artości d o d a je m y tu 6, bo ta k będ zie ładniej. O © M ając ju ż ten obiekt gwi a zdki, m ożem y w y p isać n a ek ran ie z a p o w ie d ź . Spójrz n a w y d ru k ek ran u - a zob aczy sz, po co były n am p o trze b n e g w ia z d k i. T ak d z ia ła n asza funkcja plakat: p rzy sy łam y jej strin g , a ona ła d n ie w y p isu je g o n a ekranie. W ró ć m y d o funkcji m a in . O © D o s trin g u zapowiedz, d o d ajem y tekst "
R e z u l t a t m e c z u ".
Wczytywanie stringu z klawiatury O © K olejn a d o b ra w iad o m o ść. D zięki klasie s t r i n g u p raszcza ją n am się operacje w c z y ty w a n ia strin g u z k la w iatu ry . Sam zobacz: d o p u steg o s trin g u , (który so b ie z d efin io w aliśm y p o w y żej O © ) m o żem y z k la w ia tu ry w p isać ja k iś w ynik m eczu.
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Definiowanie obiektów klasy s t r i n g
511
óć u w ag ę na te n fakt. Twórcy klasy s t r i n g potrafili sprawić, by strumień c i n um iał pracować z obiektami tej klasy.
Z au w aż też jeszcze jed n ą ciekaw ą rzecz: Nie ma nigdzie w stępnego ograniczenia jak długi może być w ystukany przez nas na klaw iaturze w yraz!
O © P ow tórne w y w o ła n ie funkcji p l a k a t - p o to, by znow u w y p isała zapow iedź, tym razem w zb o g aco n ą o w ynik meczu.
W Z obaczyliśm y w ty m program ie, jak łatw o przeprow adza się operacje na stringach. M am y w ięc ogólne pojęcie, zatem teraz łatwiej będzie n am zobaczyć bogactw o ró żn y ch form i m ożliw ości tej klasy. Klasy, która - w ie rz mi - stanie się jed n y m z T w o ich najbliższych przyjaciół.
11.2
Definiowanie o b i e k t ó w klasy
string
Jak w p rz y p a d k u każdej innej klasy - p rz y definiow aniu o b iek tó w klasy s t r i n g p o słu g u je m y się (św iadom ie lu b nieśw iadom ie) k o n stru k to ram i tej klasy. Z obaczm y te ra z p ro g ra m , w którym d efiniow ać i inicjalizować b ęd ziem y różne obiek ty klasy s t r i n g . W y stąp i w n im ta k ż e funkcja, która będ zie nam pom agała w y p isy w ać na ek ran ie z aw arto ść przesłanego jej jako a rg u m e n t - obiektu k la sy s t r i n g . tinclude #include
// O
#include using naraespace std;
/ z********************* *************************************** // © (
void pokaz(string opi3, string właściwy) cout << « << « « «
"Tresc obiektu " setw(15) opis — >" właściwy "<— \n";
/Z****************************** int m a i n O
{ string napisA;
// O
512
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Definiowanie obiektów klasy s t r i n g string napisBl("Jakiś tekst");
// ©
char tablica[20] = [ "Natenczas Wojski" ); string napisB2(tablica);
// 0
string wiadomosc(&tablica[5]) ;
// 0
string ostrzeżenie("Awaria studni", 8);
//
©
string gwiazdki(25,
u
©
string inny = "ABCDEFGH"; string nowy(inny, 4, 2);
//
©
//====== Dla sprawdzenia =========== p o kaz("napisA", napisA); p o kaz("napisBl", napisBl); p o kaz("napisB2", napisB2); pokaz("wiadomość", wiadomość); p o kaz("ostrzeżenie", ostrzeżenie) ; pokaz("gwiazdki", gwiazdki); p o kaz("nowy", nowy); p o kaz("nl", nl); pokaz("n2", n2) ;
}
//********************±********+******************************
Po wykonaniu tego programu na ekranie zobaczymy Tresc Tresc Tresc Tresc Tresc Tresc Tresc
obiektu obiektu obiektu obiektu obiektu obiektu obiektu
napisA: napisBl: napisB2: wiadomość: ostrzeżenie: gwiazdki: nowy:
-><— ->Jakis tekst<— ->Natenczas Wojski<— ->czas Wojski<— ->Awaria s<— _>*************************<->EF<— ®
N a p rz y k ła d zie tego p ro g ram u om ów im y sobie d o stęp n e sposoby definicji czyli p o prostu...
zapoznamy się z zestawem konstruktorów klasy string. Równo w kolumnach, albo estetyka wydruku P o n iższe kilka linijek jest przeznaczonych tylko d la tych, którzy zastanaw iaj* się jak u d ało mi się na ek ran ie tak rów niutko w y p isać nazw y stringów .
W © S pójrzm y na pom ocniczą funkcję pokaz, zd efin io w an ą na górze p ro g ra m u . Jej z a d a n ie m jest w ypisać zaw arto ść przysłanego d o niej stringu. A by by ło to bard ziej pouczające, funkcja ta w ypisuje strin g o g ran iczając jego p o czątek zna i czk am i — >, a koniec znaczk am i <— . D zięki tem u łatw iej zo baczym y, jeśli strin g będzie pusty.
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Definiowanie obiektów klasy s t r i n g
513
© W funkcji tej w idzisz taką konstrukcję cout «
setw(15)
«
obiekt_sting;
Jest toużycie tak zw anego manipulatora set w. Manipulator ten przydaje się nam m iędzy innym i wtedy, gdy chcemy wypisać jakiś tekst równiutko w kolumnach. O manipulatorze tym bliżej rozmawiać będziemy w § 22.11.2, str. 1073. T u wystarczy wiedzieć, że manipulator setw (n ) (skrót od ang. set w i t d h ) sprawia, że następny wypisywany na ekranie string zostanie w ypisany w postaci co najmniej n (tutaj: 15) znaków. •
Jeśli tekst jest krótszy, to z lewej strony dodana zostanie odpo wiednia liczba spacji. Tak, żeby w sumie było n (tu: 15).
•
Jeśli natomiast żądany tekst byłby dłuższy niż te minimalne n (tu: 15) znaków - to trudno, oczywiście zostanie w ypisany cały i rozburzy nam ładną kolumnę. Ale tak jest lepiej. Niech rozburzy - tekst musi pojawić się cały.
A by posłużenie się tym manipulatorem było możliwe, m usiałem do programu w staw ić plik nagłówkowy z jego deklaracją. To właśnie robi dyrektywa i n clude O.
W To były jakby dygresje na temat estetyki wypisywania na ekranie, ale przecież rozm awiać m am y o definiowaniu obiektów różnymi konstruktorami.
Przegląd konstruktorów klasy s t r i n g
G W idzim y tu definicję, która wytwarza nam pusty (na razie) obiekt klasy s tr in g . string napisA;
Wykorzystuje ona istnienie konstruktora s tri n g ();
W rezultacie powstaje obiekt klasy s t r i n g , który nie jest niczym inicjalizowany - czyli (wstępnie) jest pusty. Jego zawartość odpo w iada po prostu C-stringowi " ". Spójrz na ekran - a zobaczysz, że rzeczywiście tym sposobem stw orzony obiekt klasy s t r i n g zawiera pusty string 2)
ang. s e t
w id tli
- ustaw szerokość [czytaj: „set łydf"].
514
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Definiowanie obiektów klasy s t r i n g
© W id z im y tu definicję, k tóra p rz y d a je się, g d y o b ie k t klasy s t r i n g chcem ) inicjalizo w ać treścią istn iejąceg o ju ż gdzieś C -strin g u . W y k o rz y stu je fak t istn ien ia k o n stru k to ra string (const char * c s tr ) ;
Jest to k o n s tru k to r, k tó reg o arg u m en tem jest w sk a ź n ik d o C -strin g u . O to, jak nt p rz y k ła d m o ż n a się p o słu ży ć ty m k o n stru k to rem : string n a p i s B l ("Jakiś tekst");
// ©
albo char tab l i c a [20] = { "Natenczas Wojski" string napisB2(tablica);
); // ©
Są to d efin icje o b iek tó w klasy s t r i n g , które od ra z u in icjalizo w an e są d o s ta ń cz o n y m C -strin g iem . Ten C -s trin g m o ż e m y p o d a ć n a d w a sposoby: •
U m ieszczając w naw iasie ciąg z n a k ó w . (© )
•
P odając a d re s (np. tablicy c h a r [ ] ), g d z ie taki C -strin g o b ec nie jest. (© )
Zagadka: C o b ę d z ie treścią ob iek tu w ia d o m o ś ć , g d y tego s a m e g o k o n stru k to ra u ży je m y tak: char napisl[20] = {"Natenczas Wojski"}; string wiadomość(&napis[5]);
O d p o w ie d ź : P o n ie w a ż k o n s tru k to ro w i w y sy ła m y tutaj a d re s p ią te g o elem en tu tablicy, o d b ie ra o n te n a d re s m ó w iąc d o sieb ie tak: „Oto dostałem adres, począwszy od którego mam sobie poczytać znaki, ai do napotkania kończącego C-stringi bajtu zerowego". T ak też z ro b i. W eźm ie sp o d w sk a z a n e g o a d re s u z n a k (w p ią ty m elem en cie tablicy jest, ja k i w id z im y , z n a k ' c ') . P rzejd zie d alej, g d z ie jest z n a k ' z ' - i tak d o ko ń ca. W re z u lta c ie k o n s tru k to r ten s p ra w i, że w ob iek cie w ia d o m o ś ć z n a jd z ie się z a w a rto ść : " c z a s W o j s k i " . P o n ie w a ż o m a w ia n a tu z a g a d k a z n a la z ła się w te k śc ie p ro g ra m u (O ), m o ż e sz te ra z sp o jrz e ć n a w y d ru k e k ra n u i p rz e k o n a ć się, ż e ta k je st istotnie.
© D efinicja ta k a , ja k o b iek tu o s t r z e ż e n i e , p rz y d a je s ię w sytacji, g d y c h c e m y m ieć o b ie k t k la s y s t r i n g o treści id en ty czn ej, jak p o c z ą tk o w e zn a k i ja k ie g o ś C -strin g u .
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Definiowanie obiektów klasy s t r i n g string o s t r z e ż e n i e ("Awaria studni",
515
8);
W rezu ltacie tej d efin icji do obiektu o s t r z e ż e n i e p rz e k o p io w a n e zostaje ty lk o 8 (p ierw szy ch ) z n a k ó w T aka definicja m o ż liw a jest dzięki k o n stru k to ro w i: s t r i n g ( c o n s t c h a r *cstr, s i z e _ t y p e ile); Jest to k o n s tru k to r w y w o ły w a n y z d w o m a arg u m en tam i: «$♦ cstr - je st w sk a ź n ik ie m d o C -strin g u , ♦♦♦ ile - jest a rg u m e n te m określającym ile z n ak ó w k o n stru k to r m a p rz e c z y tać z te g o C -strin g u . T en d ru g i a rg u m e n t je st tajem niczego ty p u s i z e _ t y p e , ale nie daj się p rz e ra zić. P od ty m o k re śle n ie m kryje się po p ro stu jakiś ty p całk o w ity u n s i g n e d . W T w o im k o m p ila to rz e m oże to być u n s i g n e d i n t lub u n s i g n e d l o n g - n i e m u sisz o ty m w ie d z ie ć - ale pam iętaj ty lk o , że jest to ty p b ez z n a k u . Z a te m d r u g im arg u m en tem w naszej definicji n ie p o w in n a być liczba u je m n a. O czyw iście, p o d o b n ie jak w p rz y p a d k u p o p rz e d n ie g o k o n s tru k to ra , m o ż em y jak o p ie rw sz e g o a rg u m e n tu w y w ołania użyć: •
albo stałej dosłow nej ty p u C -string,
•
albo ad re su , g d zie jakiś C -strin g jest p rz e c h o w y w a n y (np. w tab licy c h a r [ ] ).
© D efinicja zap ełn iająca s t r i n g określoną ilością jed n ak o w y ch z n ak ó w . s t r i n g g w i a z d k i (2 5 , W rezu ltacie takiej definicji treścią ob iek tu o n azw ie g w i a z d k i b ęd zie 25 g w iazd ek . W definicji tej w y k o rzy stu jem y k o n stru k to r s t r i n g ( s i z e _ t y p e ile, c h a r znak) ; Jest to k o n stru k to r, k tó ry u m ożliw ia nam z b u d o w a n ie tekstu sk ład ająceg o się z określonej liczby takich sam ych znaków . A rg u m e n ta m i tego k o n stru k to ra są: ♦♦♦ ile - liczba o k reślająca jak d łu g i m a być te n tekst (czyli ile ra z y te n zn ak m a się p o w tó rzy ć) ♦♦♦ z n a /c -a rg u m e n t ty p u c h a r , określający zn ak , k tó ry m a być w ielo k ro tn ie p o w tó rzo n y . Z p rz y k ła d o w y m u ży ciem tego k o n stru k to ra sp o tk aliśm y się ju ż w n aszy m w stę p n y m p ro g ram ie.
516
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatorów =, +, +=, w pracy ze stringam i © Taka definicja p rzydaje się, g d y chcem y inicjalizow ać now y obiekt na podstaw ia in n eg o obiektu klasy string. Jak widać, oczekuje o n od nas trzech argum en tów . Ich znaczenie najlepiej zobaczm y na przykładzie: string inny string "ABCDEFGH"; string nowy (inny_stnng, 4, 2 );
IK5P
»
;»
W rezu ltacie w obiekcie now y znajdzie się treść "Ę F ". 4 4 W definicji tej w y korzystaliśm y konstruktor: string (const string s ,m n y _ s tr m g , size_type p o zy c ja , size_type i l e ) ;
K o n stru k to r ten, ze w sk azan eg o m u innego obiektu klasy string, bierze znak p o cząw szy od w skazanej pozycji (pozycje n u m e ru jem y od 0) i w su m ie bierze ok reślo n ą liczbę zn ak ó w (arg u m en t ile).
11.3 Użycie operatorów =, +, +=, w pracy ze stringami Jedną z najw ygodniejszych stron istnienia o biektów klasy string, jest możli w ość p o słu g iw an ia się (w pracy z nimi) p ro sty m i o peratoram i. -•:>><*;.*JteAó?! ogotwlb. >! u -r/y ł 5
Operator przypisania Z ob aczm y taki frag m en t kodu: string stary("Mieszko"); string nowy; nowy = stary;
// <- k o p io w a n ie za p o m o c ą operatora =
Jak w id z isz , operator= p o zw ala nam sk o p io w ać zaw arto ść jednego obiektu klasy s t r i n g d o drugiego.
Operator + (plus) dodaje dwa stringi S tringi też m o żn a d o d aw ać za pom ocą o p erato ra + string ścieżka(”c :/katalogA/"); string nazwa_pliku("współczynniki.txt"); string dluga_nazwa; dluga_nazwa = ścieżka + nazwa_pliku; ■
_
W rezu ltacie tej ostatniej instrukcji w obiekcie d lu g a _ n a z w a zn a jd z ie się z aw arto ść "c:/katalogA/wspolczynniki.txt"
K rótko m ów iąc treść d w ó ch stringów zo stan ie z e so b ą połączona i sta n ie się treścią trzeciego.
Dopisywanie do końca stringu operatorem += Jeśli chcielibyśm y do końca istniejącego strin g u d o d a ć zaw arto ść in n eg o , to m o ż em y to zrobić na d w a sposoby:
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatorów =, +, +=, w p racy ze stringam i
517
P osłużenie s ię d w o m a operatoram i. O peratorem + o raz = s trin g z le p e k (" n a z w a " ); s trin g b ( " .e x t" ) ; - • : r. z le p e k = z le p e k + b;
•
•
♦> lub u ży w ają c jednego, w ygodniejszego operatora += 53Ci_z.* ,-*ł. z le p e k += b; eO
Jeśli w stęp n ie za w a rto ść obiektów zlepek i b była taka, jak w pow yższych definicjach, to w obu przy p ad k ach zaw artością strin g u z l e p e k będzie ostatecznie nazw a. e x t " O czyw iście, jeśli d alej napiszem y: z le p e k += " 1 2 3 " ; to d o bieżącej zaw arto ści obiektu zlepek d o p isan y zostanie C -strin g " 1 2 3 ". T eraz obiekt ten b ę d z ie m iał już zaw arto ść " n a z w a . e x t l 2 3 " . D odać też m o żn a n a w e t jeden znak c h a r lu b stałą d o sło w n ą ty p u c h a r . z le p e k += 'M '; W rezultacie takiej operacji w obiekcie zlepek będzie zaw arto ść " n a z w a .e x tl2 3 M "
W Jak widzisz - posługiw anie się tymi operatorami jest niezwykle wygodne.
C zy w y o b rażasz sobie, jak m usielibyśm y się gim nastykow ać, b y te w szystkie operacje zrobić z a pom ocą funkcji s t r c p y , s t r c a t ?
Ll.3.1
Jak um ieścić w tekście liczbę? T en p arag raf jest d la bardziej dociekliw ych
W C zasam i zac h o d zi p o trzeba um ieszczenia, gdzieś w tekście strin g u , jakiejś w arto ści liczbow ej którejś ze zm iennych z naszego pro g ram u . T aka sytuacja najczęściej zachodzi, g d y pracujem y z plikam i dys k o w y m i o nazw ach mających (oprócz liter) jakieś sy m b o le liczbo w e. N a p rzy k ład m a m y w iele plików dyskow ych z p aram etram i p racy jakichś u rz ą d z e ń , a n a z w y tych plików w yglądają tak: " u r z ą d z e n ie 8 2 . p a ra m e try "
518
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatorów =, +, +=, w pracy ze stringam i Ja w idać - w nazw ie, oprócz dw óch słów , w ystępuje też n u m e r urządzenia. Jak taką nazw ę poskładać? K onkretnie - jak spraw ić, by w artość liczbową jakiejś zm iennej z naszego program u została zam ieniona na o dpow iedni ciąg cyfr. W tym p rz y p a d k u byłby to ciąg "82" (cyfra osiem i cyfra dwa). C o p raw da, d o ro zw iązy w an ia takiego p roblem u jest b ardzo dobre n arzęd ziespecjalny "strum ień wyjściowy", który płynie nie na ekran, ale do w y b ran eg o przez nas ob iek tu klasy s t r i n g . N iestety tymi sp raw am i zajm ow ać się b ęd ziem y dopiero w paragrafie 22.23, str. 1151. .J <•/
Do tego czasu proponuję rozwiązanie polegające na prostym posłużeniem się operatorami reszty z dzielenia (%) oraz dzielenia całkowitego (/). O to przykład: for(int i = 0 ; i < 15 ; i ++)
{ string nazwa_pliku("urzadzenie_"); int liczba = i; liczba %= 100; char dziesiątki = '0' + (liczba / 10); liczba %= 10; char jednostki
= '0' + (liczba / 1);
nazwa_pliku +=» dziesiątki; nazwa_pliku += jednostki; nazwa_pliku += ".parametry"; cout << "dla i = " « i « " nazwa_pliku = " << nazwa_pliku << endl;
Wykonanie tego fragmentu programu spowoduje wypisanie na ekranie następującego tekstu: dla dla dla dla dla dla dla dla dla dla dla dla dla dla dla
i i i i i i i i i i i i i i i
= 0 nazwa_pliku = urzadzenie_00.parametry = 1 nazwa_pliku = urzadzenie_01.parametry = 2 nazwa_pliku = urzadzenie_02.parametry = 3 nazwa_pliku = urzadzenie_03.parametry = 4 nazwajpliku = urzadzenie_04.parametry = 5 nazwa_pliku = urzadzenie_05.parametry = 6 nazwa_pliku = urzadzenie_06.parametry = 7 nazwa_pliku = urzadzenie_07.parametry == 8 nazwa_pliku = urzadzenie_08 .parametry = 9 nazwa_pliku = urzadzenie_09.parametry = 10 nazwa_pliku = urzadzenie_10.parametry = 11 nazwa_pliku = urzadzenie_ll.parametry = 12 nazwa_pliku = urzadzenie_12.parametry = 13 nazwa_pliku = urzadzenie_13.parametry = 14 nazwa_pliku = urzadzenie_14.parametry
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu
519
i komentarz Jak w idzisz, posłużyliśm y się tu takim sposobem, że do kodu ASCII cyfry '0' dodajemy jakąś liczbę z zakresu 0 do 9 - i w ten sposób otrzymujemy cyfry (znaki num eryczne) z zakresu '0' - '9'. Obliczenie ile jest dziesiątek i ile jednostek w liczb ie- załatwiają operacje dziele nia całkowitego i reszty z dzielenia. To znaczy - jeśli liczbę z zakresu 0-99 podzielim y przez 10, wówczas: ♦♦♦ całkowita część wyniku dzielenia określa jaka ma być cyfra dziesiątek, ♦♦♦ natomiast reszta z tego dzielenia - określa jaka ma być cyfra jednostek. Jeśli mamy liczbę o wartości 53 liczba / 10 <- to wyrażenie ma wartość 5 (cyfra dziesiątek) liczba % 10 <- to wyrażenie ma wartość 3 (cyfra jednostek)
1.4 Pojemność, rozmiar i długość stringu
■>*
T.
Obiekty klasy string służą do przechowywania stringów, czyli inaczej mó w iąc tekstów. Teksty te mogą mieć oczywiście bardzo różną długość. Jak się łatw o domyślić, klasa string stara się mądrze gospodarować zasobam i pamię ci. To znaczy zależnie od długości bieżąco przechowywanego tekstu rezerwuje odpow iednią ilość pamięci. Porozm awiam y teraz o tych sprawach. Zapoznamy się mianowicie z funkcjami składowym i, które pozwalają nam dowiadywać się o to, jak długi jest przecho w yw any obecnie tekst oraz jak dużo pamięci klasa sobie zarezerwowała. W opisywanych za chw ilę funkcjach wielokrotnie wystąpi typ size_type. Rozm awialiśm y niedaw no, że: pod określeniem size_type kryje się jakiś typ całkowity unsigned. W Twoim kompilatorze może to być unsigned in t lub unsigned long,ale
nie m usisz o tym w iedzieć - pamiętaj tylko, że jest to typ bez znaku.
1.4.1
Funkcje s i z e () i le n g t h () s ize_type s t i n g : :s i z e (); s ize_type s t i n g : :l e n g t h ();
520
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu 3 4 O b ie te funkcje składow e - s i z e i l e n g t h - robią dokładnie to sam< pozw alają nam dow iedzieć się z ilu znaków składa się p rzech o w y w an y i d a n y m obiekcie tekst. N a przykład: string śpiewak("Jan Kiepura"); cout << śpiewak.size() << endl; cout « śpiewak.length() << endl;
W obu przypadkach na ekranie pojawi się liczba 11, bo tyle w łaśn ie znakót (łącznie ze spacjami) m a nasz string.
Uwaga, w C-stringach rozmiar (sizeof) znaczył co innego! Jeśli pamiętasz jeszcze C-stringi, to tam funkcja biblioteczna s t r l e n f c h a r *) także pozwalała nam dowiedzieć się o długość C-stringu. Ale uwaga, gdybyśmy zapytali nie o długość, ale o rozm iar C-stringu np. tak ■>-.m ,v ■ii „ sOpijiit *31’ ■y ffih'4 iłfif ■Ą:jvn*S$1 ilirJi4 riii
char tab(] = "Jan Kiepura"; cout « śizeof(tab);
to w rezultacie na ekranie zostałaby wypisana liczba 12. Oczywiście dlatt go, że na końcu każdego C-stringu jest, jak wiadomo, jeszcze bajt zerow (znak nuli). Czyli dla C-stringu: co innego oznacza długość, a co inneg rozmiar.
rnifewm yt v ar ■ylwoć -en r.jr hnsiti i ś .
Tak było w przypadku C-stringów, ale klasa string jest inna! ■
‘. I ' . ' -
■
j,
.* f
’
.•
• 1
c
1 « • • • rr • i
t y •, j
T utaj i rozm iar stringu i dłu g o ść stringu oznaczają to samo.
I® 5
V/
Z apam iętaj: W przypadku obiektów klasy string, obie funkcje składowe s i z e oraz l e n g t h podają tę sam ą wartość. Jest to w artość mówiąca z ilu znaków skła d a się nasz tekst.
11.4.2
Funkcja składow a em pty bool string::empty{);
3) 4)
ang. size - rozmiar [czytaj: "sajz"]. ang. length - długość [czytaj: "lengf"]
i ?
jt
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu
521
string nic; if(nic.ompty ()) cout << "nic jest na razie pusty" << endl;
1.4.3
Funkcja składow a m a x _ s iz e size_type max_size();
Funkcja nax s i ze^ pozwala nam dowiedzieć się, jaki najdłuższy tekst moglibyśmy ewentualnie przechować w danym obiekcie kla sy s t r i n g . Informuje nas w ięc pośrednio o ilości dostępnej pamięci naszego komputera. Oto prosty przykład: string nic; cout « nic.max size();
W rezultacie tej ostatniej instrukcji mój komputer na ekranie wypisał liczbę 4 294 967 293. Co prawda, obiekt n ic jest na razie pusty, ale „jakby co", to mogę w nim przechować tekst długości ponad 4 miliardów liter. Oczywiście Twój komputer może Ci odpowiedzieć coś innego, ale zapewne będzie to także dość duża liczba.
1.4.4
Funkcja składow a c a p a c i t y size_type i
capacity(); . .i
.
.
•
'
■
Jeśli chcielibyśmy wiedzieć, jak duży obszar zarezerwował sobie dany obiekt (klasy s t r in g ) na przechowywanie znaków - to możemy się tego dowiedzieć wywołując funkcję składową c a p a c i t y 7. 5) 6) 7)
ang. empty - pusty [czytaj: "empty"]. ^ ang. max sizc - skót od maxvnal sizc (największy rozmiar) [czytaj: "maks sajz"]. ang. capacity - pojemność [czytaj: "kapasity"].
522
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu Z p o w y ższeg o w yjaśnienia dom yśliłeś się już chyba, że wielkość tego obszan m oże się zm ieniać. To zrozum iałe: jeśli początkow o nasz string jest p u sty , to oczywiście obield klasy s t r i n g nie m usi rezerw ow ać pamięci. Jednak - wcześniej czy później do tego p ierw o tn ie pustego stringu zaczniem y d o p isy w ać znaki lu b słowa. Tekst będzie się w ydłużał, w ięc obiekt klasy s t r i n g pow inien sobie dla niego od p o w ied n io rezerw ow ać pam ięć. Załóżmy, że dodajem y p o jednej literze Zagadka:
Jak myślisz, czy przy każdym dodaniu znaku - obiekt będzie robił inną rezerwację? Oto fragm ent program u: string poemat; string::siz e t y p e
poprz_poj = poemat.capacity();
for(int k = 1 ; k < 500 ; k++)
{ poemat += 'A';
//d o danie jed n eg o z n a k u
//p y ta m y o obecną p o jem n o ść string::size_type poj = poemat.capacity(); poprz poj)
if{poj { cout
/ / je ś li p o jem n o ść się z m ie n iła
« "Gdy liter =" « k << ", zwiększyłem poj « Poj
«
",
(+"
<< (poj - poprz_poj) << ")" « endl; poprz_poj = poj ;
Jak w idzisz, tw orzym y tu obiekt klasy string n azw ie poemat. Jest on w stęp nie pusty. Dalej jest pętla, w której ciągle dodajem y d o strin g u literę 'A'. Innym i słow y - za k ażd y m obiegiem pętli długość tekstu (1eng t h ())zw iększa się o 1. W p ętli w yw ołujem y też funkcję capacit y (), która m ów i n am ile obecnie miejsca obiekt przygotow ał na p rzech o w y w an ie naszego tekstu. Jeśli ta w artość się różni od poprzedniej (czyli jeśli coś się zm ieniło), to n a ekranie zostanie w ypisany tekst.
□
Zależnie od tego, z jakimi kompilatorem pracujesz, wydruk z tego ekranu może być różny. Oto, co zobaczyłem na moim ekranie:
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu
523
Jak widzisz, tutaj rezerwacja nie następuje za każdym razem. Nasz obiekt poem at rezerwuje sobie na zapas miejsce od razu na ewentualne dodatkowe 32 znaki. Jeśli w końcu i to miejsce zostanie wykorzystane, dokonuje następnego powiększenia miejsca przeznaczonego na tekst. Znowu o 32 znaki. Tak pracuje ten kompilator. Gdy skompilowałem ten program innym kompi latorem - pracował on bardziej oszczędnie, to znaczy na początku wielokrotnie rezerwował miejsce na tylko jeden dodatkowy znak, a po około 128 tak rezerwo wanych znakach, zmieniał taktykę. Od tej pory rezerwował już (na zapas) miejsce na ewentualnych 128 następnych znaków.
Dwulitrowa bańka na mleko nie zawsze mieści w sobie dwa litry mleka. Czasem jest pusta, a czasem może być w niej tylko kwaterka (0.25 l).
1.4.5
Funkcja składow a r e s e r v e Powyżej rozmawialiśmy o tym, że obiekt klasy s t r i n g od czasu do czasu sam sobie rezerwuje dodatkowe miejsce na przechowywanie naszego, coraz dłuż szego, tekstu. Możemy jednak zająć się tym sami. To znaczy - wiedząc, że nasz tekst ostatecznie zajmie na przykład 5000 znaków, możemy już na samym początku podpowiedzieć obiektowi, by od razu tyle zarezerwował. Dzięki temu, obiekt od razu osiągnie właściwą pojemność i nie będzie tracił czasu na wielokrotne powiększanie się małymi kroczkami. Oczywiście tak rezerwowana wartość nie jest ostateczna - obiekt może się potem jeszcze zmieniać. Jeśli kiedyś tekst w tym obiekcie przekroczy wstęp nie zarezerwowane 5000 znaków —nie ma problemu —obiekt znowu sam zajmie się swoim dalszym powiększaniem.
524
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu Aby spraw ić, b y obiekt klasy s t r i n g wcześniej zrobił rezerw ację pam ięci p rzezn aczo n ą na nasz p rzy szły tekst, posługujem y się funkcją sk ład o w ą r e s e r v e 8. v o id r e s e r v e ( s i z e _ t y p e ile); Jeśli chcielibyśm y z góry zarezerw o w ać miejsce, na co najmniej 100 zn ak ó w , to tę w łaśn ie w artość czynim y arg u m en tem jej w yw ołania. O czyw iście nie m a gw arancji, że od tej pory pojem ność będzie w ynosiła d o k ła d nie 100. M ożliw e, że obiekt sobie to zaokrągli d o jakiejś w ygodnej dla siebie w iększej w artości. Zatem p e w n e jest tylko, że b ędziem y mieli zarezerw o w an e miejsce n a co najmniej 100 znaków . P rzykład: s t r i n g poem at; p o e m a t. r e s e r v e (1 0 0 );
11.4.6
r e s i z e - zmiana długości stringu „na siłę" void resize (size_type ile ,
char z n a k = '\0 ') ;
Funkcja r e s i z e 9 pow oduje zm ian ę długości ciągu zn ak ó w p rzech o w y w an eg o w obiekcie klasy s t r i n g . ♦♦♦ P ierw szy arg u m en t ile - określa jaka m a być now a długość ciągu z n a ków . Długość ta m o że być m niejsza lub w ięk sza o d dotychczasow ej. ♦♦♦ D ru g i argum ent znak - po zw ala nam zd ecy d o w ać, jakim znakiem chce m y zapełnić ten n o w e miejsca (po zw iększeniu rozm iaru). Z ałóżm y, że obecnie m am y taki obiekt klasy s t r i n g : string
peleton("1234567890");
Jak w id ać, obiekt ten przechow uje 10 znaków " 1 2 3 4 5 6 7 8 9 0 " . Ze zn ak ó w tych jest rzeczyw iście 10 - m o żn a się przekonać w y w o łu jąc funkcję sk ład o w ą l e n g t h , (to sam o, co s i z e ) cout << peleton.length();
. * — ■« ł_ Jeśli na rzecz takiego obiektu w yw ołam y funkcję r e s i z e , a p ierw szym arg u m en tem b ęd zie liczba m niejsza niż dotychczasow a długość - ciąg z n a k ó w zostan ie „obcięty7' do tego now ego rozm iaru. ,
.
.y
.;
7
f-~
ą .
,
m i
i
l
^
Z atem jeśli w yw ołam y tę funkcję tak: peleton.resize(4) ;
to n asz strin g zostanie obcięty d o 4 znaków , czyli o d tej p o ry b ęd zie z aw ie rał "1234".
8) 9)
ang. reserte - rezerwuj [czytaj: „ryzerw "]. ang. resize - zmień rozmiar [czytaj: "rysajz"].
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu
525
Jeśli zaś p o d a m y n o w y ro zm iar w ięk szy niż d o ty c h czaso w y - to cią g zn ak ó w zo sta n ie w y d łu ż o n y d o ż ą d a n e g o ro zm iaru . Z n o w u m o ż em y to ła tw o s p ra w d z ić fu n k cją sk ład o w ą l e n g t h (to sam o, co s iz e ). p e l e to n .resize(2 0 ); c o u t << p e l e t o n . l e n g t h ( ) ; *5 *0 N a ek ran ie z o b a c z y m y liczbę 20. \
^
i. i * >
r:
f ć f i ;
'
- *-
Tu jest jednak j c u i ia r\ v-/i^r\c4¥» ciekawostka Z w ią z a n a o n a jest z d r u g im a rg u m e n te m fu n k cji r e s i z e . A rg u m e n t ten p o z w a la n am z d e c y d o w a ć jak im zn ak iem ch cem y zap ełn ić te n n o w e m iejsca (po z w ię k sz e n iu ro z m ia ru ).
string a("ABC"); a . r e. s i z e .(7,-
' -X'. )1!. .
1
C if . .
* t
*.
c o u t << " s t r i n g a >" << a « "< ma d lu g o s c " « a . l e n g t h () « e n d l; ąoq >' - '
N a ek ran ie z o sta n ie w y d ru k o w a n y te k st ’ ,, ^ s t r i n g a > A B C x x x x < ma d lu g o s c / Jak n a razie, ż a d n a to ciekaw ostka. Z m ie n ia m y ro zm iar strin g u trzy litero w eg o n a sie d m io lite ro w y , w ięc n o w e cztery d o d a tk o w e litery z a p e łn io n e zostały z n a k a m i ' x ' , jak so b ie tego życzyliśm y. Z o b acz je d n ak , co się stan ie, jeśli zasto su jem y in stru k q 'ę r e s i z e tak: a .r e s i z e (15);
c o u t << " s t r i n g a >" << a « "< ma d lu g o s c " << a . l e n g t h () << e n d l; W re zu ltacie n a e k ra n ie zo b aczy m y tak i zask ak u jący w y d ru k . s trin g a
>ABCxxxx<
ma d lu g o s c 15
Jak ła tw o m o ż n a p rzeliczy ć - zn ak ó w jest n a d a l 7, ale fu n k cja l e n g t h podaje n a m w a rto ść 15. Jak to m ożliw e? O tó ż te ra z sk o rz y s ta liś m y z w artości d o m n ie m a n e j d ru g ie g o a rg u m e n tu . W ar tością d o m n ie m a n a je st bajt zerow y (czyli z n a k T en z n a k rzeczyw iście o ś m io k ro tn ie z o sta ł d o p is a n y d o końca ła ń cu ch a zn ak ó w , w ię c te ra z jest w m m 8 takich z n a k ó w . W su m ie m am y teraz: A, B, C, x, x,
x, n u li, n u li, n u li, n u li, n u li, n u li, n u li, n u li
T ak i ciąg z n a k ó w z o s ta ł w y słan y s tru m ie n ie m c o u t na e k ra n , a strumień c o u t ma taki zw yczaj, że gdy napotka znak nuli (bajt zerowy), to przestaje wypisy w ać taki ciąg znaków . T o d lateg o na e k ra n trafiło tylko 7 liter, a bajty zerow e
już nie. Ż e o n e je d n a k n a p r a w d ę tam są - m o żesz się łatw o przekonać:
526
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Pojemność, rozm iar i długość stringu fo r ( i n t n = 0 ; n < a.lengthl) { cout « (int) a [n] « ",
; n++)
} P ę tla ta w y p isu je n a e k ra n ie kody ASCII p rzech o w y w an y ch z n a k ó w . Oto, c< p o ja w ia się na ekran ie: 65,
66,
67,
120,
120, 120, 120, 0, 0, 0, 0, 0, 0, 0, 0,
Jak w id z isz , p o zastosow aniu funkcji r e s i z e , te d o d a tk o w e 8 m iejsc zo sta ło zapełnione bajtam i zero w y m (o śm io m a znakam i m m //).
_ _ _
iiwurnraiiifrwiniTW u * » i M i n r < r i r r —
Z w ró ć uwagę, że w obiektach klasy s t r i n g bajt zerowy nie m a takiego zn a cze n ia jak w C -stringu. Tutaj może w ystąpić on nawet w środku tekstu. G dzie je s t koniec stringu - nie decyduje zn a k nuli. Po prostu o biekty klasy s t r i n g sam e to pam iętają i nie potrzebują pom ocy n u lla .
Różnica między resize, a capacity D o b rz e jest u św ia d o m ić sobie różnicę m ię d z y p o w ięk szan iem z a pom ocą fu n k cji c a p a c i t y i za pom ocą resize. Z a u w a ż , że k ażd a z ty c h funkcji p o w ię k s z a co innego. <♦ F unkcja c a p a c i t y pow iększa ro z m ia r pojemnika
czyli powiększa rozmiar bańki na mleko. Może sprawić, że bańka z 2 litrowej stanie się 5 litrowa. Nadal jednak będzie w niej ty le sam o m leka (np. kwaterka). <♦ Funkcja r e s i z e pow iększa ro zm iar p rzech o w y w an ej w po jem n ik u zawartości. czyli może sprawić, że w bańce będzie więcej mleka. Jeśli mieliśmy 2 litrową bańkę, w której była kwaterka mleka, to teraz może być tego mleka więcej - na przykład 1.5 litra. Jeśli z a p o m o cą funkcji r e s i z e zechcesz p o w ięk szy ć ro zm iar z a w a rto śc i poje m n ik a p o n a d d o ty c h czaso w y ro zm iar p o je m n ik a - to k lasa s t r i n g zach o w a się in telig en tn ie, czyli p o w ięk szy także ro z m ia r pojem nika. Zatem, jeśli mając kwaterkę mleka zechcesz powiększyć ją do 3000 litrów, to 2 litrowa bańka mleka automatycznie zmieni się w cysternę mleka.
Nie angażuj się w rezerwację pamięci. To nie Twój obowiązek! N a zak o ń czen ie ro zm o w y o funkcji r e s i z e , ch ciałb y m jeszcze ra z p o d k reślić, ż e w sp a n ia ło ść k lasy s t r i n g polega m ięd zy in n y m i na ty m , że n ie m u sim y w og ó le się p rzejm o w ać tak im i sp raw am i, jak reze rw ac ja m iejsca n a n o w e zn ak i. Jeśli zam ierza sz d o d a ć d o końca stringu d o d a tk o w e z n a k i z a p o m o c ą n a p rz y k ła d o p e ra to ra +=, to ten o p e ra to r sam zajmie się powiększaniem stringu.
527
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatora [ ] oraz funkcji a t
w sw ojej praktyce nie musisz używać wcale funkcji r e s i z e (czy r e s e r v e ) , żeby przygotow yw ać miejsce na jakieś nowe znaki. cje składow e klasy s t r i n g i operatory takie ja k +=, +, =, » zawsze to za C iebie. Nie musisz nawet o tym myśleć.
11.4.7
Funkcja sk ład ow a e l e a r void e le a r { ) ;
1ii
^
j{ 1
in -•1 F unkcja sk ład o w a e l e a r , sp raw ia że e w e n tu a ln a dotychczaso w a treść obiektu klasy s t r i n g jest kasow ana i o d tą d o b iek tten za w iera p u s ty string. P rzy k ład użycia: s t r i n g d y r y g e n t ( "W illi Boskovsky"); d y r y g e n t . elear ( ) ; W rezultacie tej d ru g iej instrukcji - w obiekcie d y r y g e n t dotychczasow y tekst " W i l l i B o s k o v s k y ” zam ieniony zostanie na string p u sty Z au w aży łem , ż e n ie w szystkie kom pilatory w sw ych bibliotekach dostarczają n am tę fu n k q ę. Jeśli Twój kom pilator tej funkcji nie m a - m o żesz pow yższą linijkę p o p ro stu zastęp o w ać instrukcją: ŁfP' y j r f o y t o b uin&uuk .. istn C i * dyrygent =
1 1 .5
U ży c ie o p e ra to ra [ ] oraz fu n kcji a t P o d o b n e jak w C -strin g u , tak i w obiekcie klasy s t r i n g , zn ak i um ieszczane są w jakiejś kolejności. Mają więc jakby n u m ery swej pozyqi. M ożesz czasem chcieć zap y tać, jaki zn ak stoi na pozycji piątej, a jaki na zerow ej (oczywiście n u m eru jem y o d zera). M ożesz też czasem chcieć zam ienić zn ak na inny. I S łuży n a m do tego operator [ ] , ew entualnie funkcja składow a a t . Ich deklaracje w y d a ją się na pierw szy rz u t oka skom plikow ane: re fe re n c ja _ d o _ zn a k u r e fe re n c ja _ d o _ zn a k u
operator [] (size_type p o z y c j a ) ; at (size_type p o z y c j a ) ;
♦♦♦ W o b u deklaracjach argum entem jest n u m er pozycji, która nas interesuje. (N a p rz y k ła d interesuje nas znak nu m er 5). R ezultatem tych funkcji mógłby być w zasad zie znak stojący na danej pozycji i już. Tw órcy tej k lasy s t r i n g byli jednak sprytniejsi. 10)
ang. elear - wyczyść (czytaj: "kljer"].
528
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatora [ ] oraz funkcji a t ♦♦♦ D zięki tem u, że rezultatem jest nie znak, ale referencja d o znaku m ożem y nie tylko odczytyw ać znak będący na d an ej pozycji, lecz m o że m y także d o danej pozycji w pisyw ać now ą, zm ien io n ą zaw artość. Czy pamiętasz może jeszcze takie pojęcie jak l-wartość (§ 8.4 str. 260), czyi coś, co może stanąć po lewej stronie znaku przypisania '=' ? Z abrzm iało to m oże trochę tru d n o - ale w praktyce jest niezw y k le łatw e. San zobacz, jak p ro ste jest odczytyw anie i w pisyw anie w y b ran eg o zn ak u .
Oto przykład s t r i n g w y d ("Kallimach"); cout << wyd[l] << endl; cout << wyd.at(l) << endl;
//o d czy ta n ie znaku
W obu p rzy p ad k a ch na ekranie w ypisana zostanie litera 'a '. Jeśli w obec takiego obiektu wyd zastosujem y następujące instrukcje: wyd [0 ] - ’B '; wyd.at(5) * 'c';
II za p is, zamiana nowej wartości
- to obie dokonają zm iany odpow iednich znaków . •
Pierw sza instrukcja zam ienia d o ty ch czaso w y zn ak z pozycji zerow ej (zam ieni w ięc ' K' na zn a k ' B ') .
•
D ruga instrukcja zam ienia d o ty ch czaso w y zn ak z pozycji piątej (zam ieni w ięc ' m' na zn ak ' c ').
W rezultacie tych operacji nasz wyd będzie zaw ierał następującą treść:
11.5.1
D ziałanie operatora [] I O p erato r [] u d o stęp n ia n am żąd an y z n a k b a rd z o szybko, ale za to I n ie sp raw d za legalności naszych po czy n ań . Z naczy to , że jeśli na p rzykład m am y nasz o b iek t k lasy s t r i n g , w którym obecnie je st te k st długości 9 zn ak ó w , a operato rem [] p o p ro sim y nieostrożnie o znak n u m e r 1000000, c h a r zn ak = w yd[1000000]; to obiekt b ły skaw icznie u d o stęp n i nam zaw artość k o m ó rk i pam ięci, która znaj duje się o m ilio n pozycji dalej w sto su n k u do p o czątk u tek stu . N ie w ażn e czy to m a sens, czy a k u ra t jest tam p rz y p ad k o w y śmieć. Jeśli o d c z y ta m y tę zaw artość i b ędziem y ko rzy stać z niej w dalszej części p ro g ram u - m o żem y przez d łu g i czas nie zauw ażyć, ż e p rzeb y w am y w krainie nonsensu.
529
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatora [ ] oraz funkcji a t
by zaś ch o d ziło n am nie o odczytanie, ale o zm ianę w artości tego milionoznaku: wydllOOOOOO;
'M'
//wpisanie nowej wartości (czyliznaku 'M ')
- to in stru k q ą tak ą zniszczym y zaw arto ść kom órki pam ięci, która wcale nie należała d o nas. To m a zw ykle fatalne skutki. Zależnie o d tego, co akurat zniszczyliśm y - p ro g ram m oże przestać pracow ać naty ch m iast, a m oże dopiero za jakiś czas. Jeśli za jakiś czas - tym gorzej, bo w tedy tru d n iej taki błąd znaleźć. Z apytasz pew nie: ¥ -Skoro tyle zagrożeń, to po co w ogóle używać tego operatora [ ] ? .w
M a on tę zaletę, że d ziała szybko. P oza tym są sytuacje, g d y m ożem y łatwo panow ać nad p o p raw n o ścią od n o szen ia się do znaków stringu. Spójrz na poniższą pętlę: f o r ( i n t zn = 0 ; zn < w y d . l e n g t h O
zn++
wyd[zn] = ' x '
D zięki funkcji składow ej l e n g t h nasza pętla przebiega o d początkow ego do końcow ego z n a k u tekstu, będącego treścią obiektu wyd. W szystkie należące do tego stringu z n a k i zo stan ą zastąpione zn ak iem V . W szystkie i tylko te. N ie ma tu ryzyka w p isa n ia d o czegoś innego. W takiej sytuacji bez o baw m ożna p o słu ży ć się operatorem [ ]. N ie zaw sze jed n a sp raw a jest tak oczyw ista. W tedy w arto pom yśleć o posłuże niu się funkcją sk ład o w ą a t {).
11.5.2
D ziałanie funkcji składowej a t P rzypom nijm y jej deklarację: referencja_do_znaku
at(size_type pozycja)
Funkcja składow a a t 11 tym ró żn i się o d operato ra [ ], że sp raw d za czy o d n o sim y się do p o p raw n ej pozycji w d a n y m ciągu znaków . P op raw n e pozycje są oczywiście z p rzed ziału od 0 do l e n g t h () - 1. to -1 jest dlatego, że co prawda, znaków jest tyle, ile zwraca funkcja l e n g t h (),ale liczymy je przecież od zera. Jeśli funkcja a t zau w aży , że chcemy się odnieść do zn ak u sp o za tego przedziału - czyli p o p ro stu d o pozycji gdzieś p o za końcem stringu - zaprotestuje.
Na czym polega jej protest? O tóż p o słu ży się o n a m echanizm em zw a n y m „rzuceniem w y jątk u ". Spotkaliś my się ju ż z tym , p rzy okazji rozm ow y o operatorze new. (Str. 310, § 8.11.6). 11)
'' ’ ang. at - dosłownie: w, na. Tutaj: na pozycji... [czytaj: "et"].
- '-
‘ i
530
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatora [ ] oraz funkcji a t Byl to p arag raf nieobow iązkow y, w ięc m ogłeś g o nie czy tać, z a te m teraz kilki słów w yjaśnienia. R zucenie w y jątk u , to jakby w ystrzelenie k ap su ły z w iad o m o ścią dla tego, ktc d an ą funkcję w yw oływ ał. <♦ Tego w ystrzelenia w yjątku (k ap su ły ) d o k o n a fu n k cja at. ♦> Jeśli n ie złap iem y takiej k ap su ły , to po p ro stu w ty m m iejscu prograir z ak o ń c zy sw oje działanie (ew en tu aln ie inform ując o n ie złap an iu w yjąt ku).
Nie jest to takie złe rozwiązanie. Na pewno lepsze niż praca programu ; bezsensownym „śmieciem". Przecież pracując nad programem - po takin zatrzymaniu - od razu zaczynamy szukać, gdzie popełniliśmy błąd. Błąd oczyw iście p o leg a na tym , że w y w o łaliśm y funkcję a t n ie b ęd ąc pew nym , czy a rg u m e n t jej w yw ołania jest z d o p u szcza ln eg o p rz e d z ia łu . M ożna to sp ra w d z ić n a p rzy k ład tak: string tabliczka(“Palenie Wzbronione"); cout << "Która pozycje Stingu " « tabliczka << " chcesz odczytać? string::size__type poz; cin » poz;
// sprawdzamy poprawność if(poz >= 0 £& poz < tabliczka.length() )
( cout « "Na tej pozycji stoi znak: << tabliczka.at(poz) << endl;
"
1 else
{ cout << "To jest niepoprawna pozycja " << endl;
}
-Jaka zatem różnica? - zapytasz pewnie. -Przecież w przypadku operatora [] też musielibyśmy sprawdzać! M asz rację. Jak n a razie różnica jest ty lk o tak a, że jeśli zap o m n ielib y śm y o sp ra w d z e n iu , to u życie funkcji a t g w a ra n tu je n am , iż nie b ę d z ie m y pracow ać z b ezsen so w n y m z n a k ie m w y d o b y ty m g d z ie ś sp o za strin g u . F unkcja a t d o tego nie d o p u śc i i s p o w o d u je zatrzy m an ie p ro g ra m u . Tego z a trz y m a n ia p ro g ram u m o żn a u n ik n ą ć, jeśli z ła p ie się ta k i rzu co n y przez funkcję a t w y jątek . W racając d o n aszeg o p o ró w n an ia: - g d y z łap ie się k ap su łę (z inform acją) w y strzelo n ą p rzez funkcję a t . Jak to z r o b ić - to ju ż raczej technika d la bard ziej z a a w a n so w a n y c h , w ięc jeśli c zy tasz tę k siążk ę p o raz pierw szy, o p u ść p o n iż sz e zag ad n ien ie. f 7r*‘
'z ? Hpf.+f/T* jer v
X
'i
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Użycie operatora [ ] oraz funkcji a t
531
- (o przechwytywaniu wyjątków) T w órcy k lasy s t r i n g w ym yślili, że jeśli funkcja a t o d k ry je, iż chcem y o d n ieść się d o n ie istn ie jąceg o elem en tu strin g u - to m a o n a rzucić w y jątek . T en w y jątek jest p o p ro stu ob iek tem specjalnie p rz y g o to w a n e j na ten cel klasy o u t o f r a n g ę . P o n iew aż o u t _ o f _ r a n g e to jakaś n a z w a - p o w in n iś m y w n aszy m p ro g ra m ie um ieścić jej deklarację. Jest o n a u m ie s z c z o n a w p lik u n ag łó w k o w y m o n a z w ie s t d e x c e p t , z atem w y starczy , ż e w p lik u , w k tó ry m p rzec h w y ty w ać b ę d z ie m y w y jątek - u m ieści m y n astęp u jącą d y rek ty w ę: #include
S p raw ia o n a , ż e n a z w a o u t _ o f _ r a n g e zostaje z d e k la ro w a n a - i w d o d a tk u u m ieszczo n a w p rz e s trz e n i n a z w std. Nie wszystkie wersje biblioteki s t r i n g wymagają postawienia tej d y r e k ty w y i n c l u d e , bo często nazwa o u t _ o f _ r a n g e je st deklarowana "automatycznie". Tak się dzieje na przykład wtedy, gdy powyższą dyrektywę i n d ude twórcy Twojej biblioteki umieścili ju ż w pliku z deklaracją klasy s t r i n g . Jeśli nie wiesz, jak to jest w Twojej wersji biblioteki - nie przejmuj się. yin uin m&jinr Po prostu użyj w programie nazwy out _of _r an ge . Jeśli kompilator .y m .n 75.1185 powie Ci, że nie wie, co to jest ta nazwa o u t _ o f _ r a n g e - zrozum to jako zachętę do umieszczenia dyrektywy i i n d ude W róćm y d o n a s z e g o zag ad n ien ia. Z atem funkcja a t rzu ciła w łaśn ie w y jątek o u t o f ra n g ę . — . . .. s il ia h ; Jeśli n ie chcem y, by n asz p ro g ram w takiej sy tu a c ji zatrzy m ał się, n a le ż y te n o b iek t złapać. O to p rz y k ła d p o k a z u ją c y jak to m o żn a zrobić: string nakaz("Cisza!");
// O
try for(int pozycja = 0 ; pozycja < 16 ; pozycja++)
{ cout
« "Próba odczytania znaku nak a z [" « pozycja « "] — > "; cout << nakaz.a t (pozycja) « endl;
)
catch(ou t o f ranga) cout
12)
// Tu łapiemy wyjątek klasy out_of_range
<< "\n Takiej pozycji w stringu nakaz n i e m a ’ « endl;
ang. out of rangę - poza zakresem [czytaj: „aut of reńdż").
// 0
532
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i P raca z fragm entem stringu, czyli sub -stringiem
W rezultacie wykonania takiego fragmentu programu na ekranie pojawi się tekst V 7 / '•} •? y
s f n o ? ~~ t j y f ł i l t e 0
Próba odczytania Próba odczytania Próba odczytania Próba odczytania Próba odczytania Próba odczytania Próba odczytania Takiej pozycji
fil/i W j ?yu.
znaku nakaz[0] — > C znaku nakaz[1] — > i znaku nakaz[2] s znaku nakaz[3] — > z znaku nakaz[4] — > a znaku nakaz[5] — > ! znaku nakaz[6] — > w stringu nakaz nie ma
Krótkie omówienie tego fragmentu O Jeśli chcem y nastaw ić się na łapanie w yjątku p o ch o d ząceg o od jakiegoś fragm entu p ro g ram u - fragm ent ten ujm ujem y w tzw . blok t r y . @ W tym bloku try w id zim y , m iędzy innym i, użycie funkcji at, k tó ra m oże rzucić w yjątek typu (klasy) out_of_range. © Jeśli taki w yjątek zo stan ie rzucony, to złap ać go m ożem y za p o m o cą bloki catch (następującego tu po bloku try). Innym i słow y, jeśli d o k o n am y czynu niedozw olonego (o d n iesien ie się dc niep o p raw n ej pozycji stringu), to p ro g ram nie zostanie z atrzy m an y , ale stero w an ie przeniesie się tutaj, do bloku catch, g d zie m o żem y na przykład w y p isać na ek ran ie inform ację o w łaśnie w y k ry ty m p rzestęp stw ie. M ożem y też ew en tu a ln ie próbow ać tę błęd n ą sytuację napraw ić. Dalej opisyw ać nie będę, bo obsługa sytuacji w yjątkow ych jest przecież treścią książki „Pasja C + + ", która jest jakby ciągiem d alszy m „Sym fonii C ++".
11.6
P ra c a z fra g m e n te m s trin g u , czyli z s u b -s trin g ie m P ow yżej zobaczyliśm y sp o so b y odniesienia się do w y b ran eg o jed n eg o zn ak u w strin g u . P raw d ę m ów iąc, nie jest to operacja b ard zo częsta. Jeśli mamy w danym obiekcie klasy s t r i n g treść „Dziadów" A. Mickie wicza, to nie ma specjalnej korzyści z odniesienia się w tym tekście do litery numer 1745. O w iele częściej in teresuje nas nie jeden zn ak , ale z g ru p a zn ak ó w (wyraz, zd an ie) będąca frag m en tem jakiegoś d łu ższeg o tekstu. Jeśli cały tek st n azy w am y stringiem (ciągiem zn ak ó w ), to frag m e n t n azy w a się z ang ielsk a: su b strin g iem (jakby: „podciągiem " znaków ).
13)
To polskie określenie - jest znane matematykom. Ciąg (np. arytmetyczny, geometryczny) może mieć podciąg, który jest po prostu fragmentem danego ciągu.
533
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Funkcja składowa s u b s t r
M ożesz po trzebow ać dow ied zieć się czy jakaś g ru p a znaków , słow o lub z d a n ie (czyli substring) zn ajd u je się w obiekcie nazwa_pliku, a jeśli tak - to w któ ry m miejscu (na jakiej pozycji). •
Jeśli m asz taki w łaśnie problem - p o w in ien eś posłużyć się funkcją składow ą f i n d (znajdź).
Przyjrzyjm y się teraz tym funkcjom.
11.7
F u nkcja s k ła d o w a substr string substr (size_type
p o z y c ja ,
size_type
ile _ zn a k ó w )
;
Funkcja ta p o z w a la nam w ydobyć z d an eg o stringu jakiś jego fragm ent (czyli substring). «$♦ P ierw szy arg u m e n t tej funkcji (pozycja) określa, g d zie m a zaczynać się in teresu jący nas fragm ent. ♦> D ru g i a rg u m e n t określa jak d łu g i m a być ten frag m en t (ilejznaków). Jeśli chciałbyś tu pow iedzieć, ż e chodzi Ci o w szystkie d a lsz e znaki - postaw tu s t r i n g : : npos', co oznacza m niej więcej: „n ajw ięk sza m ożliw a licz b a zn a k ó w ".
Bliższe szczegóły na temat n p o s -poniżej, przy omawianiu funkcji f i n d . <» W rezu 1tacie swej pracy fu nkcja ta zw raca nam tak określony fragm ent „ p rz e p a k o w a n y " do now ego obiektu klasy s t r i n g . Oto ilustracja: string nazwa_pliku("detektor_A12.txt"); string sam_symbol; sam_symbol =
nazwa_pliku. substr (9,
3);
//e fe k t:
"A12"
534
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie zadanego substringu w obiekcie klasy string - funkcja f ind i jej pokrewne W rezultacie tej ostatniej instrukcji - treścią ob iek tu s a m _ s y m b o l będzie "A 1 2 ". Jak w idać, jest to b ard zo p ro ste zadanie. Pam iętać tylko należy, by p ie rw sz y ar g u m en t, czyli pozycja, nie przekroczyła długości s trin g u . G dyby tak sięstało , to funkcja rzuci w yjątek o u t _ o f _ r a n g e . (W spom niany ju ż pow yżej).
1.1.8 Szukanie zadanego substringu w obiekcie klasy string - funkcja f ind i jej pokrewne Jeśli chcem y przekonać się czy zadana g rupa z n a k ó w (w y razó w , za d a ń ) w ystę p u je g d z ie ś w d a n y m obiekcie klasy s t r i n g , m o ż em y się p o słu ży ć funkcją f in d . R ezu ltat pracy tej funkcji je st w artością typu s i z e _ t y p e . Przypominam, że s i z e _ t y p e to w klasie s t r i n g umowna nazwa jakiegoś typu całkowitego bez znaku, np. u n s ig n e d i n t. ❖
Jeśli funkcja f i n d z n a jd z ie w danym strin g u ta k ą g ru p ę - w rezultacie swojej pracy zw raca nam w artość o zn aczającą po p ro stu pozycję, na której w zorcow a g ru p a została zn alezio n a (dokładniej: g d zie jest jej początek).
❖
Jeśli funkcja ta w d an y m stringu n ie z n a jd z ie zad an ej przez nas w zorcow ej g ru p y znaków — w rezu ltacie zw raca w artość
string: :npos.
T aką w łaśn ie w artością o d p o w ied zi fu nkcja f i n d syg n alizu je nie odnalezienie. N ielo g iczn e? Lepsze b y ło b y zero? N iestety nie m o ż n a, b o zero o zn acza przecież sukces - o d n alezienie czegoś na zerow ej pozycji, czyli na sam y m początku stringu. T ym , co n a p ra w d ę kryje się za nazw ą s t r i n g : : n p o s , nie m u sisz się przej m o w ać - p o prostu zap a m ię taj to za pom ocą m n em o tech n iczn eg o skojarzenia, że w n a z w ie ta literka je st jakby skrótem o d słow a: nieskończoność. Z atem jeśli funkcja f i n d zw raca o d p o w ie d ź s t r i n g : :r .p o s to tak, jakby m ów iła p rz e sz u k a ła m o d p o czątk u do nieskończoności - i nie ma!
Mamy do dyspozycji kilka przeładowanych wersji funkcji składowej f i n d O to sto so w n e deklaracje. Są długie, bo zasto so w ałem b a rd z o o p iso w e n azw y a rg u m e n tó w . W zw iąz k u z tym typ rezu ltatu ( s i z e _ t y p e ) um ieściłem w p o p rzed z ając ej linijce.
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie zadanego substringu w obiekcie klasy string - funkcja f ind i jej pokrewne
535
s i z e ty p e f f n d ( c o n s t c h a r *zadany_ C jtr™Z‘ s iz e _ ty p e odkąd_zacząc = 0) ; s i z e ty p e , f i n d ( c o n s t s t r i n g & zadany_strvig, s iz e _ ty p e odkąd_zacząc = 0) ; s iz e _ ty p e f i n d ( c o n s t c h a r *wzór,
s i z e ty p e odkądjuicząc, s iz e _ ty p e długość);
0
© O
f i n d T c h a r znak, s iz e _ ty p e odkądjzacząć = 0) ;
e const P o n iew aż fu n k cje te szukają, nie robiąc, żadnych zm ian w stringu - d lateg o na końcu k ażd e j z tych deklaracji, tuż p rzed średnikiem , p o w in ien em był p o staw ić p rz y d o m e k c o n s t . Oznacza on radosną wiadomość: że funkcje te nadają się nawet doszukania w takich obiektach klasy s t r i n g , które są zdefiniowane jako stałe, niezmienialne ( c o n s t s tr in g ) . N ie n ap isałem jed n ak tego p rzydom ka w pow yższych deklaracjach, bo zaciem niłby n a m za p is - i tak już długi.
Przyjrzyjmy się tym różnym wersjom tej funkcji sin
O Funkcja
x.st lesr u
t A.n
s i z e ty p e ~ f in d (c o n s t c h a r *zadany_C_stmg, s iz e _ t y p e odkądjzacząc = 0) ; szu k a czy w d a n y m obiekcie klasy s t r i n g znajduje się ciąg znaków id en ty cz ny jak zadany C-string (pierw szy argum ent). Z aczyna s z u k a n ie od pozycji określonej przez d ru g i arg u m en t. Jak w idzim y, w artość d o m n ie m a n a tego drugiego argum entu to zero, czyli szukanie zaczn ie się od p o czą tk u stringu. Jeśli w stringu zadana g ru p a znaków zn ajduje się kilkakrotnie funkcja ta znajdzie pierw sze wystąpienie tej g ru p y znaków (licząc od początku stringu). A oto fra g m e n t program u, w którym korzystamy z tej funkcji: s t r i n g l i ś c i k ( " A b y r z u c i e , tr z e b a wykonać zam ach prawa r ę k a " ! ; s t r i n g ; : s iz e _ ty p e p o z y c ja ; p o z y c ja = l i ś c i k . f i n d ("zam ach") ; i f (p o z y c ja { cout « << } e ls e cout «
!= s t r i n g : :npos)
I I <-
li znalezione! .
.
" T e rro ry s ty c z n e słow o na p o z y c ji p o z y c ja << e n d l;
„
" P o d e jrz a n e j grupy znaków n ie ma " «
e n d l;
536
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie zadanego substringu w obiekcie klasy string - funkcja f ind i jej pokrewne
Dalsze funkcje pracują bardzo podobnie © F u n k cja sk ła d o w a ’!
size_type . find(const string & wzór, size_type o d k ą d jz a c z ą ć = 0);
R ó żn i s ię o d p o p rzed n iej ty lk o tym , że z a d a n y c ią g z n a k ó w nie je st C -strin g iem , lecz je st treścią jakiegoś in n e g o obiektu klasy s t r i n g . string liscik("Aby rzucie, trzeba wykonać zamach prawa ręka"); string g r o z n e s l o w o ("zamach"); s t r i n g ::size_type pozycja; pozycja =
liścik.find(grozne_slowo);
$ f-i-* I,
© K olejna w ersja
%'j’h “:;Ą
>*£j: i
•
>40-man
size_type find (const c h a r * C _ s t r m g , s i z e ^ t y p e odkądjMcząc s i ze_t ype d e _ z m k ó w _ C _ s t n n g u ) ;
Ta fu n k c ja jest jak b y u d o sk o n a le n ie m funkcji o p is a n e j ja k o O . R ó żn i się o d niej ty m , ż e p o sz u k iw a n y m c ią g ie m z n a k ó w nie je st (jak w O ) cały C -s t r i n g , lecz ty lk o je g o p o czątk o w y ch n z n ak ó w . Tę w arto ść n o k re śla tu trzeci a rg u m e n t. Jeśli w s trin g u d a n a g ru p a zn a k ó w zn ajd u je się k ilk a k ro tn ie - to fu n k cja ta z n a jd u je p ie rw s z e w y stą p ie n ie tej g ru p y z n a k ó w (licząc o d p o c z ą tk u strin g u ). string::size_type pozycja;
.
.
.
string sentencja("Cala wiedza naszych przodków mieści sie ); const char * słowo = "miedza"; , . ■>>
. :
?•.- Vfub-r;> .
<1 .V
•*
pozycja = sentencja.f i n d (słowo, 0, if(pozycja , i J != string::npos - a r cout « « « «
;
i
: I
k
3);
"Znalezione na pozycji " « " \n czyli od: " sentencja.substr(pozycja) endl;
pozycja
}
//J a k p r z e s u n ą ć so b ie p o c z ą te k ? pozycja = sentencja.f i n d (slowo+1, if(pozycja != string::npos) cout « « << «
)
0,
"Znalezione na pozycji " « "\n czyli od: " sentencja.substr(pozycja) endl;
3);
pozycja
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie zadanego substringu w obiekcie klasy string - funkcja f ind i jej pokrewne
537
W rezultacie wykonania tego fragmentu na ekranie pojawi się: Znalezione czyli od: Znalezione czyli od:
na pozycji 29 mieści sie na pozycji 6 iedza naszych przodków mieści sie
W p o w y ższy m fragm encie d w a razy korzystam y z tej funkcji. Za pierw szym razem z C -stringu b ran e są tylko trzy pierw sze jego znaki (czyli „mie"). Funkcja zn ajd u je je w obiekcie sentencja na pozycji 29. D ru g ie w y w o łan ie tej funkcji jest z zastosow aniem chw ytu, polegającego na tym , że d o w sk aźn ik a do C -stringu s ło w o , dodajem y jeden - czyli jakby tw o rzy m y ad res n astęp n eg o zn ak u w tym C -stringu. Ten adres p rzedstaw iam y funkcji f ind i m ów im y jej, ż e m a poszukać ciągu trzech znaków znajdujących się p o cząw szy od tego ad resu . (Trzech - bo taki jest tu trzeci arg u m en t funkcji f ind). Z atem teraz funkcja szu k a ciągu znaków " ied", i taki ciąg znajduje już na 6 pozycji w obiekcie sentencja.
aetlfilao u :łvvxnn a
Pytanie:
O
€>
&.
P o k azałem funkcję f ind pracującą w taki w y szu k an y sposób z C -stringiem . jak m yślisz, czy teraz p o k ażę p o d obną, pracującą n a w zorcow ym ciągu znaków z ło żo n y m w in n y m obiekcie klasy string? N ie, nie pokażę. Po prostu nie jest to potrzebne. Było potrzebne, bo w sto su n k u d o C -strin g u nie jest łatw o określić jego w ybraną część. N ato m iast w p rzy p ad k u , g d y w zorcow y string złożony jest w jakim ś obiekcie klasy string, to jego fragm ent (substring) m o że m y sobie łatw o zrobić funkcją sk ład o w ą substr. O to p rz y k a d —teraz s ło w o nie jest w skaźnikiem do C -stringu, ale obiektem k la sy string. • *j string sentencja("Cala wiedza naszych przodków mieści sie"); string słowo = "miedza";
// Jak przesunąć sobie początek ? pozycja = sentencja.f i n d (słowo.substr(1, 3), 0); if(pozycja != string::npos) cout << « <<
« "Znalezione na pozycji " << pozycja "\n czyli od: " sentencja.substr(pozycja) endl;
Jak w id ać, p ierw szy m arg u m en tem w yw ołania funkcji f i n d jest w arto ść z w ra c a n a p rz e z funkcję substr - czyli string. Z atem w sum ie - ru szy tu do p ra c y funkcja o p isan a pow yżej jako © , czyli funkcja size type
find(const string & wzór, size_type odkąd_zaczcfć —0) ,
i ’,i‘' fui ,v}j Jitin - lŁiii
❖
538
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie rozpoczynane od końca stringu
O Szukanie pojedynczego znaku w obiekcie klasy s t r i n g realizuje funkcja skła dowa .
.
.
•
s iz e _ t y p e f in d (c h a r szukany_znak, s iz e _ ty p e odkądjzacząć = 0) ;
Służy ona do poszukiwania w danym stringu zadanego znaku. Jeśli taki znak w danym stringu jest, funkcja zwraca pozycję, na której występuje on po raz pierwszy (licząc od początku stringu).
11.9
S zu k a n ie ro zp o czyn an e od końca s trin g u Omówione powyżej wersje funkcji składowej f in d szukały pierwszego wys tąpienia zadanego znaku, lub ciągu znaków. Jeśli chcielibyśmy dowiedzieć się, na której pozycji jest nie pierwsze, ale ostatnie ich wystąpienie - to mamy do dyspozycji zestaw funkcji składowych o nazwie r f i n d 14, które są jakby bliźniakami odpowiednich funkcji f in d . s iz e _ ty p e _ r f i n d ( c o n s t ch ar *zadany_C_string, s iz e _ ty p e odkądjzacząć = npos) ; O s iz e _ t y p e _ r f i n d ( c o n s t s t r i n g & zadany_slring, s iz e _ t y p e odkądjzacząć = n p o s ) s iz e _ ty p e r f i n d ( c o n s t c h a r *wzór, s iz e _ ty p e
s iz e _ ty p e odkądjzacząć, s iz e _ ty p e długość);
r f i n d ( c h a r znak, s iz e _ ty p e odkądjzacząć = n p o s );
Porozmawiajmy o nich kolejno O Funkcja s iz e _ ty p e r f i n d ( c o n s t c h a r *zadany_C_string, s iz e _ t y p e odkądjzacząć = npos) ;
szuka czy w danym obiekcie klasy s t r i n g znajduje się ciąg znaków iden tyczny zadanym ciągiem znaków (C-string). Zaczyna szukanie od pozycji określonej przez drugi argument i posuwa się w stronę początku stringu. Jak widzimy, wartość domniemana tego drugiego argumentu to s t r i n g : :n p o s , czyli wówczas szukanie zacznie się od samego końca stringu. Jeśli w stringu zadana grupa znaków znajduje się kilkakrotnie - to funkcja ta znajdzie ostatnie wystąpienie zadanej grupy znaków. Ostatnie przed pozycją określoną drugim argumentem. 14)
Litera V w nazwie tej funkcji pochodzi zapewne od skrótu ang. przymiotnika rear - tylny [czytaj: „rier"].
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie w stringu jednego ze znaków z zadanego zestawu
.
fśhr
'i- -*■ -
HBlBMyfcw y* ?y •^
539
? ■':
type f . . . , ind (const strinę & z a d a n y ^ s tn n g f size_typ 0 o d ktfd ^za czifc — npos) /
różn i się o d poprzedniej tylko tym , że zadany ciąg zn ak ó w nie jest C -stringiem , ale jest treścią jakiegoś in n eg o obiektu klasy s t r i n g .
V © Funkcja size type ~
rfindfconst char *ivzór, size^_type o d k ą d _ za c zą ć , size_type d łu g o ś ć ) ;
jest ja k b y udoskonaleniem funkcji opisanej jako O . Różni się od niej tym , żze p o szu k iw an y m ciągiem zn ak ó w nie jest (jak w O ) cały C - s t r i n g , lecz tylko jego p o czątk o w y ch n zn ak ó w . O w ą w artość n określa tu trzeci arg u m en t. Jeśli w strin g u zadana g ru p a znaków znajduje się kilkakrotnie —to funkcja ta zn ajd zie ostatn ie w ystąpienie tej grupy znaków . O statn ie p rzed pozycją o k reś loną d ru g im argum entem . '» fe O j l r i i W O k i
v j
i » f f i d
Vd '
. ;.
•
•
© Funkcja size type
rfind(char z n a k , size_type o d k ą d jz a c z ą ć = npos);
słu ży d o poszukiw ania w d an y m stringu zad an eg o zn ak u . Szukanie ro zp o cz nie się o d pozycji określonej drugim argum entem i będzie się p o su w a ło w stro n ę p o czątk u stringu. Jak widzimy, wartość domniemana tego drugiego argum entu to s t r i n g : :n p o s. Oznacza to szukanie rozpoczynane od samego końca
stringu. Jeśli w strin g u zadany zn ak znajduje się kilkakrotnie - to funkcja ta zn ajd zie ostatnie jego w ystąpienie. O statnie przed pozycją określoną d ru g im arg u m en tem .
11.10 S z u k a n ie w stringu je d n e g o ze z n a k ó w z z a d a n e g o ze s ta w u Może zdarzyć się tak, że w obiekcie klasy string chciałbyś odszukać pierwsze (lub ostatnie) wystąpienie jednego z zestawu interesujących Cię znaków.
540
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie w stringu jednego ze znaków z zadanego zestawu
Zwróć uwagę, że chodzi o zestaw znaków, grupę znaków, a nie ciąg. Zestaw liter nie jest traktowany jako string, jest to po prostu grupa odrębnych liter. Co prawda, ten zestaw interesujących nas liter podajemy jako argument w postaci stringu (bo tak jest najwygodniej), ale funkcje, o których tu będziemy rozmawiać - wiedzą, że chodzi o każdą z tych liter z osobna. Na przykład mamy jakieś zdanie i chcielibyśmy znaleźć pierwsze wystąpienie jednej z samogłosek (zestaw: „aoeiouąę"). Do takich zadań mamy funkcje składow e o nazwach: ♦♦♦ f i n d _ f i r s t _ o f - odszukaj pierwszego w ystąpienia jednego ze zna ków z zestawu, ♦♦♦ f i n d _ l a s t _ o f -odszukaj ostatniego wystąpienia jednego ze znaków
z zestawu, ♦♦♦ f i n d _ f i r s t _ n o t _ o f - odszukaj pierwszego w ystąpienia jednego ze znaków spoza zestawu, «$» f i n d _ l a s t _ n o t _ o f - odszukaj ostatniego wystąpienia jednego ze
znaków spoza zestawu, ' • ' 1?'* Przy omawianiu funkcji f in d zobaczyliśmy, że ma ona, dla naszej w ygody, cztery przeładowane wersje. Podobnie było z funkcją r f i n d - dawała ona nam do dyspozycji cztery przeładowane wersje - jakby bliźniacze w stosunku do fin d . Tutaj - w przypadku funkcji bardziej wyszukanych - jest podobnie. Każda z powyższych funkcji ma też cztery bliźniacze przeładowa ne wersje. Znowu typ i kolejność argumentów jest taka sama, jak w przeładowanych wersjach funkcji f i n d i r f in d . Zatem nie trzeba będzie tego na nowo zapamiętywać.
1. Funkcje składowe find first_of - szukające pierwszego wystąpienia jednego ze znaków z zestawu. ...dostarczonego jako pierwszy argument. size type
f ind_f irst_of
(const char *z e s t a w J a k o _ C _ s t r i n g , size type o d k ą d jz a c z ą ć = 0) ;
size type
find_first_of
(const string & zestaw J a k o js trin g , size type o d k ą d _ z a c z ą ć = 0 ) ;
size type
find first_of
(const char * z e sta w J a k o _ C _ s t r i n g , si ze_type o d k ą d j z a c z ą ć , size_type d ł u g o ś ć j z e s t a w u ) ;
size_type
find_first_of
(char s z u k a n y j z n a k , size type o d k ą d j z a c z ą ć = 0 ) ;
541
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Szukanie w stringu jednego ze znaków z zadanego zestawu
2. Funkcje składowe f ind_last_of - szukające ostatniego wystąpienia jednego ze znaków z zestawu... ...d o sta rc z o n e g o jako p ie rw s z y a rg u m e n t. size type find_last_of #»->
t
■■
■
,
( _ r
const char * z e s ta w J a k o _ C _ s t r i n g , s i z e t y p e o d k ą d jz a c z ą ć = npos) ; r~: '*
size type find last of
(
const string & z e s t a w _ ja k o _ s tr in g , size type o d k ą d j z a c z ą ć = npos) ;
size type find_last_of
(
const char *z e s ta w J a k o _ C _ s t r i n g , size_type o d k ą d _ z a c z ą ć , size type d łu g o ś ć j z e s t a w u ) ;
size_type find_last_of
(
char s z u k a n y _ z n a k , size type o d k ą d jz a c z ą ć = npos) ;
sn u rftfitę o ici u ifw n & s tf o gst eirtnno^yw 3. Funkcje składowe find_f irst_not_of - szukające pierwszego wystąpienia jednego ze znaków spoza zestawu... .
'./v
. ..
^
...d o sta rc z o n e g o jako p ie rw s z y a rg u m e n t. size type
find_f irst_not_of (
const char *z e s t a w J a k o _ C _ s t r i n g , size_type o d k ą d jz a c z ą ć = 0 ) ;
size type
f ind_f irst_not_of (
const string & z e s t a w j a k o _ s t r i n g , s iz e_t ype o d k ą d jz a c z ą ć = 0 ) ;
size type
find_f irst_not_of (
const char *z e s t a w J a k o J Z _ s t r i n g , size_type o d k ą d jz a c z ą ć , s i z e t y p e d łu g o ś ć jz e s ta w u ) ;
size_type
f ind_f irst_not_of (
char s z u k a n y _ z n a k , size type o d k ą d jz a c z ą ć = 0 ) ; */f f i '•ij
4. Funkcje składowe f ind_last_not_of -szukające ostatniego wystąpienia jednego ze znaków spoza zestawu... ...d o sta rc z o n e g o jako p ie rw s z y a rg u m e n t. s i z e t y p e f ind_last_not_of
( const char * z e s t a w J a k o _ C S t r i n g , size type o d k ą d jz a c z ą ć = npos) ;
size type find last_not_of
( const string & z e s t a w J a k o j s t r i n g , size type o d k ą d j z a c z ą ć = npos ) ;
s i z e t y p e f ind_last_not_of
( const char * z e s t a w j a k o _ C s t r i n g , size type o d k ą d j z a c z ą ć , s i z e t y p e d łu g o ś ć j z e s t a w u ) ;
size_type find_last_not_of
( char s z u k a n y j z n a k , size type o d k ą d j z a c z ą ć = n p o s ) ;
—
- : f : » ;w o r n c i '
t '
Zobaczmy prosty przykład... ...z u ż y c ie m o statn iej z o m a w ia n y c h funkcji - czy li f i n d _ l a s t _ n o t _ o f
542
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Usuwanie znaków ze stringu - funkcje e r a s e
s tr in g zdanie ("Wszyscy śpią") ; s t r i n g : : siz e _ ty p e pozycja; _ pozycja = zd a n ie. f in d _ l a s t_ n o t_ o f ("aoeiouąęAOElOUĄĘ") ; i f ( p o z y c j a != s t r i n g : : n p o s ) c o u t << " z d a n ie : >" « z d a n ie « , "< ma " « z d a n i e .l e n g t h () « " znakow \n o s t a t n i a N IE -sam ogloska j e s t na p o z y c ji « p o z y c ja « " \n ( j e s t t o l i t e r a " « zdanie[p o z y c ja ] « " ) \n " ; else cout << "W tym stringu sa same samogłoski" «
endl;
>
[" 1
W rezultacie wykonania tego fragmentu programu na ekranie pojawi się
...u w s f e ę s 6 s o Q f Vi óStens e ■oo9Wszyscy śpią< ma 12 znaków ostatnia NIE-samogloska jest na pozycji 9 (jest to litera p)
^!>
•
----------- -— ,— ;-----11.11 U su w a n ie z n a k ó w ze string u - fu n k cje erase Jeśli mamy tekst umieszczony w obiekcie klasy s t r i n g , to czasem możemy potrzebować wymazać jakiś jego fragment. W rezultacie tekst ten stanie sie krótszy. Taką i podobne operacje umożliwia nam funkcja składowa e r a s e dostępne formy:
. Oto jej
s t r i n g i a r a s o ( s iz e _ ty p e pozycja = 0, s iz e _ ty p e ile_znaków = n p o s ) ; ta o a; i t e r a t o r a r a s e j i t e r a t o r wsk_pozyc)t) ;
it e r a t o r e r a s e ( i t e r a t o r odtąd, i t e r a t o r dotąd); .irŁSJ .r.' •. lenoę . » 3 ' ■■■ j. ii.’ 'jr* : • : ■ i.
Omówienie
}K-no- j 3c_^7Qr- • ■
■
O Funkcja ta pozwala nam usunąć ze stringu - począwszy od wybranej pozycji zadaną liczbę znaków. ♦1* Pierwszy argument określa pozycję, od której ma się zacząć usuwanie. Jeśli podam y tu pozycję poza końcem stringu, funkcja rzuci wyjątek o u t_ o f_ r a n g e , 15)
ang. erase - wymazywać, [czytaj, „erejs"]
543
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Usuwanie znaków ze stringu - funkcje e r a s e «g»
D ru g i a rg u m e n t o k reśla ile znaków (p o czą w szy od w ybranej pozycji) m a zo stać u su n ięty ch .
]eśli od wybranego miejsca do końca stringu jest na przykład tylko 10 znaków, a my podamy tu wartość 5 0 -n ie ma problemu - usunięte zostaną wszystkie znaki do końca stringu. W arto ść d o m n ie m a n a tego d ru g ie g o a rg u m e n tu to s t r i n g : :n p o s - czyli „ n ie sk o ń c z o n a ilo ść ". O zn acza to, że chcem y u s u n ą ć ze strin g u zn ak i aż d o jego końca. ♦♦♦ R ezu ltatem p racy tej funkcji jest referencja d o ob iektu, n a rzecz k tó reg o ta fu n k cja sk ład o w a p raco w ała.
Kilka słów o wartościach domniemanych. Jak p e w n ie zau w aży łeś: p ie rw sz y arg u m e n t też m a w arto ść d o m n ie m a n ą (po zycja z ero , czy li p o czątek strin g u ). O czy w iście p a m ię ta m y , ż e - a b y sk o rzy stać z w a rto śc i d o m n iem an ej pierwszego a rg u m e n tu - w y w o ła n ie p o w in n o m ieć p u stą listę ta k ż e d alszy c h a rg u m e n tó w . s tr in g m ("ab c"); m . e r a s e ();
// <- pusta lista argumentów
Jak ła tw o sp ra w d z ić , ta k a fo rm a w y w o łan ia funkcji e r a s e p o w o d u je u su n ięcie „ n iesk o ń c zo n ej" liczby z n a k ó w licząc od p o czą tk u strin g u . In n y m i sło w y : cały te k st z a p is a n y w obiekcie m zo staje w y m azan y .
Dwa rodzaje pomyłek, a bardzo różne skutki Z a u w a ż , że: ♦♦♦ G d y p o m y lisz się w o k re ś la n iu p o zy cji, o d k tó rej m a zacząć się o p eracja i p o d a s z za d u ż y n u m e r (pozycja nieistniejąca) - b ęd zie to p o w a ż n y błąd (p o w o d u jący rzu cen ie w yjątku o u t _ o f _ r a n g e ) . ♦♦♦ N a to m ia s t jeśli p o m y lisz się w o k re ś la n iu ile z n a k ó w n ależy w y r z u c ić czyli p o d a sz za d u ż ą liczbę, operacja z a k o ń c z y się grzeczn ie po u s u n ię ciu o statn ieg o m o żliw eg o d o u su n ięcia z n a k u . Z ap a m ię taj: w y jątek o u t _ o f _ r a n g e jest rzu ca n y , g d y n ie p o p ra w n ie p o d a s z p o zy cję w strin g u . (N ato m iast nie je st rzu ca n y , g d y p o d a s z za d u ż ą liczb ę zn ak ów ). Ł atw iej to z a p a m ię ta ć tak:
_____________
S y tu a c ja k ry ty c z n a je s t w te d y , g d y fu n k c ja n ie w ie g d z ie m a z a c z ą ć p ra c ę .
N a to m ia s t n ie m a p ro b le m u z o k re ś la n ie m k ie d y s k o ń c z y ć , b o tu fu n k c je p o s tą p ią w e d łu g z a s a d y „p ra c u ję ja k d łu g o s ię d a , c z y li d o o s ta tn ie g o z n a k u w s tr in g u ” .
_____
__________
544
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W stawianie znaków do już istniejącego stringu - funkcje i n s e r t r/••[ rofit,'.
Funkcje © oraz © - to funkcje, których arg u m e n tam i jest tak z w a n y iterator, a tym zag ad n ien iem zajm iem y się później, w osobnym p a ra g ra fie przeznaczonym tylko dla bard ziej zaaw ansow anych czytelników . (§ 11.23.2, str. 593).
11.12
W s ta w ia n ie z n a k ó w do ju ż is tn ie ją c e g o s trin g u fu n k c je insert B ardzo często z ac h o d zi potrzeba w staw ienia g ru p y z n a k ó w w określone miej' sce istniejącego tekstu. W rezultacie d a lsz e zn ak i tekstu ja k b y „ p rzesu w ają się' robiąc m iejsce ty m now ym znakom . T ek st staje się o d p o w ie d n io dłuższy. P o n iew aż jest to operacja b ardzo częsta - m am y do d y sp o zy cji w iele przeład o w an y ch w ersji funkcji i n s e r t 16. s tr in g & i n s e r t ( s i z e t y p e gdzie_wstawić, c o n s t s t r i n g
& cojwstawić) ; 0
s t r i n g & i n s e r t (s iz e _ ty p e gdziejwstawić, c o n s t s t r i n g & cojwstawić, s iz e _ ty p e skad_zacząć, s i z e ty p e ile_znaków);
©
s t r i n g & i n s e r t ( s iz e _ ty p e gdziejwstawić, c o n s t c h a r * cojwstawić)
0
s t r i n g & i n s e r t ( s iz e _ ty p e gdzie_wstawić, c o n s t c h a r * cojwstawić, s iz e _ ty p e ilejnaków) ; s t r i n g & i n s e r t ( s iz e _ ty p e gdziejwstawić, s i z e ty p e uejznaków, c h a r znak); M am yj też w ersje z iterato ram i t pracujące r i i t e r a t o r i n s e r t l i t e r a t o r wsk_gdzie, c o n s t v o id i n s e r t ( i t e r a t o r wsk_gdzie, s iz e _ t y p e v o id i n s e r t ( i t e r a t o r wsk_gdzie, ite ra to r ite ra to r
charznak); ilejrazy, c h a r znak); skądJarać, dokądjorać)) ;
Przyjrzyjmy się im kolejno O Funkcja s t r i n g S i n s e r t ( s iz e _ ty p e gdziejwstawić, c o n s t s tr in g & cojwstawić); sp raw ia, że d o n aszeg o stringu, p o cząw szy o d pozycji określonej pierw szym a rg u m e n te m (gdziejwstawić), zostanie w staw io n y inny strin g (d ru g i argum ent). 16)
• iti ang. insert - wstawiać, wkładać, umieszczać
545
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W stawianie znaków do już istniejącego stringu - funkcje i n s e r t
Jako re z u lta t swej p ra c y fu n k cja ta zw raca referencję d o tak z m o d y fik o w a n eg o obiektu k lasy string. P rzy k ład : string urządzenie("detektor_energia") ; string segment("6A3");
,
urządzenie.i n s e r t (8, segment);
■i ’V - iv . ■.
........
// d e
------
•-
-0
@ D ziałan ie funkcji string &
insert < size_type g d z i e j w s t a w i ć , const string & c o j w s t a w i ć , size_type sk n d _ z a c z q ć , size type ile jz n a k ó w ) ;
jest p o d o b n e jak p o w y ższej, a le z dostarczonego (jak o d ru g i a rg u m e n t) strin g u zo sta n ie u ż y ta do w sta w ie n ia ty lk o pew n a część. Jej p o czątek o k reśla a rg u m e n t trzeci (skad_zacząć), a d łu g o ść - arg u m en t czw arty (ile_znaków). Jako re z u lta t swej p racy fu n k cja ta zw raca referen cję n aszeg o , obecnie z m o d y fik o w a n e g o , o b iek tu k la sy s t r i n g . string urządzenie("detektor_energia") ; string segment("6A3"); urządzenie.insert (8, segment,
1, 2 ) ;
// d e te k to r A 3 _ e n e r g ia
/V
Dwoma następnymi funkcjami posługujemy się, gdy przygotowany do wstawienia tekst jest złożony w C-stringu © Funkcja string & insert (size_type g d zie_ w sta u > ić , const char * c o j w s t a w i ć ) ;
s p ra w ia , ż e d o n aszeg o s trin g u , w miejsce o k re ślo n e p ie rw szy m a rg u m e n te m (gdziejiostauńć), w staw io n a zo staje cała zaw arto ść C -strin g u d o starcz o n eg o jako d ru g i a rg u m e n t. Jako re z u lta t swej p ra c y fu n k cja ta zw raca referen cję d o naszego, tak z m o d y fi k o w a n e g o , obiektu k la sy string. string tytuł("Pod Zycie Weroniki"); . t y t u ł .insert (3, "woj ne " ) ; // " P o d w ó j n e Z y c ie W e ro n ik i
Jeśli te k s t p rz e z n a c z o n y d o w staw ien ia (do n a s z e g o strin g u ) jest fragmentem C -strin g u (a nie całym C -strin g iem ) - to p o słu g u je m y się funkcją O .
546
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Zam iana części znaków n a inne znaki - r e p l a c e
s tr in g
&
in s e rt (
size_type gdzie_w staw ić , const char * cojw staw ić, siz e type Uejznaków) ;
Tutaj, z a p o m o c ą trzecieg o a rg u m e n tu ( iłe_ zn a kó w ), m o ż e m y o k reślić ile pierw szych z n a k ó w C -strin g u n ależy u ży ć d o w staw ien ia. Jako re z u lta t sw ej pracy funkcja ta zw raca referencję n a s z e g o zm odyfiko w an e g o o b iek tu k la sy s t r i n g u .
char * w ieszcz =”Adam Mickiewicz - twórca Dziadów"; s tr in g komunikat ( "Poeta wychował s i e . . . " ) ; komunikat, i n s e r t (6, wieszcz, 16) ; / / "Poeta Adam
M ickiewicz wychował się..."
© F unkcją sk ła d o w ą
s tr in g & i n s e r t ( 'Wr
size_type size_type char znak)
gdzie_w stawić, ile_znaków, ;
p o słu g u je m y się, g d y chcem y gdzieś d o n aszeg o strin g u w sta w ić w ielo k ro tn i p o w tó rz o n y te n s a m znak. (N a p rz y k ła d d ziesięć g w ia z d e k ). M iejsce, g d z ie m a n astąp ić to w sta w ie n ie - o k re śla m y p ie rw s z y m a rg u m e n te n (gdzie_wstawić). Ja k i to zn ak - o kreśla trzeci a rg u m e n t (znak), a k ro tn o ść po w tó rż e n ia o k reśla a rg u m e n t d ru g i (ile_znaków).
s tr in g rozmowa("Paryż > Rzym"); rozmowa . i n s e r t (6, 12, ' = ') ; // f'i:w ')to s y T .iq yb g
"Paryż
============> Rzym
;mynqę?2£n ern
■■ ; jufóo
Następne trzy funkcje składowe...
Dis.
S'n$fV/
0 , © o raz © — sto su je m y , g d y miejsce, w k tó re n ależy w sta w ić jak ieś zn ak i, jes o k reślo n e n ie za p o m o cą n u m e ru p o zy cji (jak d o tej p o ry ), ale za pom oc; ite ra to ra (jakby: w sk a ź n ik a ) p o k azu jąceg o n a d a n ą pozycję. T y m z a g a d n ie n ie m zajm iem y się p ó źn iej, w o so b n y m p a ra g ra fie - p rzez n aczo n y m d la z a a w a n s o w a n y c h . (§ 11.23.2, str. 593).
547
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana części znaków na inne znaki - r e p l a c e
ia n a c zęści z n a k ó w na inne z n a k i - replace t ___chcielibyśm y pew n ą („starą") grupę z n a k ó w w obiekcie klasy s t r i n g z a stą p ić in n ą („now ą") g ru p ą znaków - to m o ż em y się w tym celu p o słu ży ć b a rd z o w y g o d n y m i funkcjam i o nazw ie r e p l a c e . W y g o d a polega m iędzy in n y m i na tym, że ta n o w a g ru p a zn ak ó w w c a le nie m u si b y ć tej sam ej długości co ta stara, zastęp o w an a. In n y m i słow y: Można stare dwa znaki zastąpić pięcioma tysiącami nowych. String stanie się wtedy oczywiście o 5000 - 2 - 4998 znaków dłuższy. Może być też odwrotnie. Sto znaków można zastąpić jednym - i wtedy oczywiście dotychczasowy string stanie się o 99 znaków krótszy. P o n ie w a ż jest to operacja b a rd z o częsta - m am y d o dyspozycji w iele p rz e ła d o w a n y c h w ersji funkcji r e p l a c e . s trin g
&
r e p la c e ( s iz e _ t y p e odkąd„zastępować, s iz e _ t y p e de_zastąpić, c o n s t s t r i n g & cojwstawić) ;
s trin g
&
r e p la c e ( s iz e _ t y p e odkąd„zastępować, s i z e ty p e Uejzastąpicć, c o n s t s t r i n g & cojwstawić, s iz e _ t y p e odkąd„brać, s iz e _ t y p e dejznakćw);
s trin g
&
r e p la c e ( s iz e _ t y p e odkąd„zastępoioać, s iz e _ t y p e dejzastąpić, c o n s t c h a r * cojwstawić, s iz e _ t y p e Uejmaków);
s trin g
& r e p la c e ( s iz e _ t y p e odkąd„zastępować, s i z e t y p e Ue_zastąpić, c o n s t c h a r * cojwstawić) ;
s trin g
& r e p la c e ( s iz e _ t y p e odkąd„zastępować, s i z e _ t y p e ile_zastąpić, s i z e ty p e ile_znaków, c h a r znak);
_ “
Dla zaawansowanych: funkcje re p la c e - pracujące na iteratorach:
17)
ite ra to r
r e p la c e !
i t e r a t o r wsk_skąd, i t e r a t o r wsk_dokąd, c o n s t s t r i n g & cojwstawić) ;
ite ra to r
r e p la c e !
i t e r a t o r ivsk_skad, i t e r a t o r wskjiokąd, c o n s t c h a r * cojwstawić, s iz e _ ty p e de„znaków) ;
ang. replace - zastąpić, wymienić [czytaj: "riplejs"]
548
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Zam iana części znaków n a inne znaki - r e p l a c e ite ra to r
re p la c e (
i t e r a t o r wsk_skąd, i t e r a t o r wskjokąd, c o n s t c h a r * co_wstawić)
iterator
replace (
iterator w s k _ s k ą d ,
v o id
i t e r a t o r wskjlokąd, s iz e _ ty p e ilejunaków, QX c o n s t c h a r znak) ; r e p l a c e ( i t e r a t o r gdzie_zacząć, tfŚTii jsJIOO piso2?Y • • i t e r a t o r gdzie_skończyć, *#.)[j i U vr. ' j j -Yti i t u j f n i t e r a t o r skądjbrać, w j , 'rn y .m l i t e r a t o r dokąd_brać) ;
Przyjrzyjmy się im kolejno •.
'
string &
replace (
•
size_type odkąd_ z a s tę p o w a ć ,
s iz e _ ty p e ilejMstąpić, c o n s t s t r i n g & cojwstawić); O Funkcja ta sp raw ia, że w naszym stringu: <♦ frag m en t p o cząw szy od pozycji określonej p ierw szy m argum entem (odkądjzastępować) o długości określonej drugim a rg u m e n tem (ilejzastąpić) «$♦ zostanie zastą p io n y przez in n y strin g - d o starcz o n y jako trzeci arg u m e n t (cojostawić). Jako rezu ltat sw ej p racy funkcja ta zw raca referencję d o tak zm o d y fik o w an eg o obiektu klasy s t r i n g u . ^
. i-
.ł\l
\-t' ‘ ■J J
> -jtiio Tr.noo Przykład: r ; T, ^ 'jM ‘ s t r i n g o p is ( " O b r a z u szk o d zeń DNA"); s t r i n g d z i a ł a n i e ( "ow anie") ; opis . r e p l a c e {5, 10, działanie);
// teraz: "Obrazowanie DNA"
Jak w id ać, 10 z n a k ó w w stringu zostało zam ien io n e na 6 innych.
© Jeśli zn ak i, na k tó re należy dokonać zam ian y , są fragm entem in n e g o obiektu klasy s t r i n g , to m o żem y posłużyć się n astęp u jącą w ersją fu n k cji r e p l a c e : s trin g & r e p l a c e (
s iz e _ ty p e odkąd_zasłępować, s iz e _ ty p e ilejzastąpić, c o n s t s t r i n g & cojwstawić, size_type odkąd_brać, s i z e _ t ype ilejznaków) ;
Z naczenie argum entów : D w a p ierw sze a rg u m e n ty określają: ♦> - gdzie m a się zacząć u su w an a g ru p a z n a k ó w (odkąd_zastępować), «$» - ile ich u s u w a m y (ilejzastąpić).
549
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana części znaków na inne znaki - r e p l a c e
Przykład: string moj("abcdefgh"); string cyfry("0123456789"); moj.replace(3, 2, cyfry,
4, 5);
// "abc45678fgh"
U w aga: Ta w ersja funkcji jest dobrym p rzy k ła d em na najbardziej u n iw ersaln y zestaw arg u m en tó w . S p o tk am y się jeszcze z takim zestaw em kilk ak ro tn ie przy innych funkcjach sk ład o w y ch . Dlatego m oże d o b rz e to jest sobie zap am iętać jako sch em at ogólny - za pom ocą takiego r y s u n k u : ___________________
Rys. 11-1 Jeśli zapamiętasz tę zasadę, według której pojawiają się tutaj argumenty stosować kilka innych funkcji składowych klasy string.
o
2
s^
Z
: ' . T
pomoże Ci to
r
© Jeśli w obiekcie klasy s t r i n g chcemy w ym ienić jakiś jego fragm ent na fragm ent C -strin g u , to m ożem y p o słu ży ć się następującą funkcją string & replace ( size_type o d k ą d _ za stę p o w a ć , size type ile_zasłqpić, const char * cojwstawić, size type ile_znaków);
Z n aczen ie arg u m en tó w : D w a p ie rw sze a rg u m e n ty określają: ♦♦♦ _ g d zie w n aszy m obiekcie klasy s t r i n g m a się zacząć u s u w a n a grupa zn ak ó w (odkądjzastępmwć),
550
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Zam iana części znaków n a inne znaki - r e p l a c e <♦ - ile ich u s u w a m y ( ilejzastąpić).
- W ' .-w . N astęp n e d w a a rg u m e n ty określają zn ak i, które w m iejsce u su n ięty ch należ] w staw ić:
&/Tł
<♦ co wstawić - jest w sk aźn ik iem d o C -stringu, z k tó reg o m ają one pocho rłr ir 5 " ** ‘ ▼ dzić,
'
<♦ ilejm aków - jak d łu g i jest ten n o w y fragm ent.
Przykład: string dział("Bank ogołny"); const char * proces = "spermatogeneza"; dział. r e p l a c e (5, 5, proces, 5);
pc§=>
// "Bank spermy"
// jeśli chcemy od ósmego znaku C-stringu, stosujemy prosty chwyt dział.r e p l a c e (5, 5, -proces + 8, 3); // "Bankgeny" ■ i N ie m a osobnej fu n k cji z arg u m e n tem pozw alającym n a m określić, że zn ak i 2 C -stringu m ają b y ć pobierane nie o d sam eg o p o czątk u , a le o d którejś-tarr pozycji. N ie m a - b o być nie m usi. W o statn iej instrukcji p o w y ż s z e g o p rz y k ła d t w id zisz jak ie to p ro ste . Jeśli chcem y z ac ząć w C -stringu nie o d z n a k u zerow ego ale o d ó sm eg o , w y starczy do w sk a ź n ik a d o d ać liczbę 8. (P rosta, genialna ary tm ety k a w sk a źn ik ó w ). ^ ' T- • (&&
jfe .e l i
.e i
t
O Jeśli n o w o w s ta w io n e zn ak i m ają być cały m C -stringiem , to je st p ro stsz a funkcja p o w y ższej fu n k cji string & replace ( size_type oc . _ __ size_type U e _ za stą p ić , const char * c o j w s t a w i ć );
Z n aczen ie a rg u m e n tó w jest takie sam e, jak w fu n k q i p o w y żej. T y lk o ostatn i a r g u m e n t - je st tu ta j nieobecny.
Od razu zobaczmy przykład użycia, ale p o n ie w a ż fu n k cja ta jest prosta, a n a w e t za p ro sta - s z u k a n ie pozycji tego z a s tę p o w a n e g o fra g m e n tu strin g u z ro b iłe m za p o m o cą p o z n a n e j w cześnie funkcji f ind, (tak d la poćw iczenia jej). string badanie ("Obrazowanie RTG mózgu"); const char * rezonans = "MRJ";
■m// szukam, na której pozycji zaczynają się znaki do wymiany string::size_type p = badanie.f i n d ("RTG"); if(p != string::npos) badanie, r e p l a c e (p, 3, rezonans); // "Obrazowanie MR] mózgu"
O sta tn ia in stru k cja d o k o n u je tutaj z a m ia n y frag m e n tu "RTG" n a C -strin g "MRJ".
551
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana części znaków na inne znaki - r e p l a c e
* -
Łi-Jwstr. ,rOv |sj| -{•yr /b i n o b
-;łf S f>BV'łtiftS.VT5-S _*>tq fłfiłOsa-TT f.r. ? v litr 1r. v.' ,ti \ n cs»Mtt?vw.łv. '■ -1- .r :-ri, .»/- : • O
t -z.
* *t , • -1 ł j X*"-*
1 ; ■; - 1 x~
r y
_ • * y T ]
. Jf-y -y_
A ń-»*1*£%|i; -. i r*-ę ***
"Ł»
f i < .
MRJ to skrót od Magnetycznego Rezonansu Jądrowego. Jest to wspaniała technika obrazowania wnętrza ciała ludzkiego za pomocą bardzo wyszuka nego sposobu wprowadzania obecnych w naszym ciele jąder atomu wodoru (np. z cząsteczek H O) w rezonans magnetyczny. Tak wzbudzone jądra wodoru w różnych miejscach badanych narządów nadają, jakby, swój cichuteńki sygnał radiowy informujący nas o ich gęstości w danym miejscu. Dzięki tem u, możemy zobaczyć trójwymiarową mapę np. mózgu bez potrzeby otwierania czaszki. Zauważ, że bardzo często z tej n a zw y-g a zety usuwają słowo „jądrowy", bo w ich mniemaniu fizyka jądrowa nie może się kojarzyć z niczym pożyte
2
cznym...
© Jeśli chcielibyśm y z a stą p ić ja k iś frag m en t ob iek tu k lasy s t r i n g ciąg iem jed n a k o w y ch z n ak ó w , to m o ż e m y się w tym celu p o słu ż y ć funkcją. string & replace ( size_type size_type size~type char z n a k )
D w a p ie rw s z e a rg u m e n ty określają:
o d k ą d _ z a s tę p o w a ć , ile _ z a s tą p ić , ile _ p o w tó r z e ń _ z n a k u ,
7
■'-łtHob
♦♦♦ g d z ie w n a sz y m ob iek cie klasy s t r i n g m a sie zacząć u s u w a n a g ru p a z n a k ó w {odkąd_zastępować), <♦ ile ich u s u w a m y ( ile_zastąpić). N a s tę p n e a rg u m e n ty o k reślają znaki, k tó re w m iejsce u su n ię ty c h należy w staw ić. «$► ile_powtórzeń_znaku — trzecim a rg u m e n te m o kreślasz k ro tn o ść p o w tó rz e n ia z n a k u , ♦♦♦ znak - c zw arty m a rg u m e n tem o kreślasz jak im (kilkakrotnie p o w tó rz o n y m ) zn a k ie m m a zo stać zrobione ow o zastęp stw o .
Przykład: string inf("Morderca Kuba Rozpruwacz czaił sie"); inf. replace (9,
15, 3 ,
’x ’ );
// "M o rd e r c a x x x c z a ił sie"
Q / O , © , © , © - To funkcje p ra c u jące na tzw . iterato rach . P o ro zm aw iam y o ty m w o so b n y m p a ra g ra fie p rzez n aczo n y m dla w tajem niczonych (§11.23.2, str. 593).
552
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana zaw artości obiektu klasy s t r i n g na C -string
1.14 Z a m ia n a z a w a rto ś c i o b iek tu k la s y string n a C -s trin g O d c z a su , g d y p o z n a liś m y klasę s t r i n g w z a s a d z ie m o g lib y śm y zap o m n ieć p o słu g iw a n iu się n ie w y g o d n y m i C -strin g am i. N iestety je st b ard zo w iele fu n k cji b ib lio teczn y ch , k tó re n ie w ie d z (jeszcze) o istn ien iu klasy s t r i n g . Jeśli m a m y d o n ich w y słać jaki! tekst - m u s i to być jednak C -strin g . O czy w iście m o g lib y śm y w takich sy tu acjach ch w ilo w o z re z y g n o w a ć z ułal w ie ń d a w a n y c h n a m p rz e z klasę s t r i n g i p rz e s ta w ić się n a m o z o ln ą pracę C -strin g iem . A je d n a k - nie w arto , bo p ra c u ją c z C -strin g iem częściej m o żem p o p e łn ić błędy. N a p rz y k ła d b łąd p rz e k ro c z e n ia z a re z e rw o w a n e g o na nas C -strin g o b szaru p am ięci. T ym czasem o b ie k ty k la sy s t r i n g z w a ln ia ją nas o< ta k ic h trosk. In n y m i sło w y ch cielib y śm y , by i w ilk b y ł s y ty i o w ca cała. T w ó rcy k la sy s t r i n g p o m y śleli o tym . O to ich p o m y sł: C ałą p ra c ę z p rzy g o to w an iem te k stu p rz e p ro w a d z a s z korzystając z u ła tw ie ń k la sy s t r i n g , a n a s a m y m k o ń cu k o rz y sta sz z funkcji sk ład o w ej o n a z w ie c _ s t r ( ) , k tó ra o b ecn ą z a w a rto ść tego obiek tu u d o s tę p n i Ci w postaci C -strin g u . O to d ek laracja tej fu n k cji składow ej: c o n s t c h a r*
c _ s tr();
Jak w id a ć , funkcja ta w y w o ły w a n a jest b ez ż a d n e g o a rg u m e n tu . Jej rezultatem jest w sk a ź n ik d o C -strin g u .
Oto prosty przykład użycia tej funkcji Z a łó ż m y , że m a m y ja k ąś staro ży tn ą fu n k cję b ib lio te czn ą, k tó ra w y k o n a dla na jak ąś akcję n a d a n y m p lik u - p o d w a ru n k ie m , ż e o trz y m a jeg o n a z w ę w postać C -strin g u . v o id b i b l i o t e c z n a ( c o n s t c h a r * w sk) ; s t r i n g j a k i _ p l i k ( " n a z w a _ p l i k u .e x t " ) ; b i b l i o t e c z n a ( j a k i _ p l i k . c _ s t r () ) ; W o statn iej linijce w id z im y , że na rzecz o b ie k tu k la sy s t r i n c o nazwii j a k i p l i k - w y w o ły w a n a jest funkcja s k ła d o w a c _ s t r . Z a m ie n ia o n a treś te g o o b ie k tu n a C -strin g , a n astęp n ie te n r e z u lta t staje się tu ta j argum entem w y w o ła n ia funkcji b i b l i o t e c z n a . T en p rz y k ła d w y d aje s ię b a rd z o p ro sty i w z a s a d z ie n ie m a w n im p o w o d u , b] u ż y w a ć tu o b iek tu k la sy s t r i n g - ró w n ie d o b rz e m o ż n a b y p o słu ż y ć się oc ra z u C -strin g iem . N ajczęściej jed n ak nie m a tak łatw o.
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana zaw artości obiektu klasy s t r i n g na C -string
553
Oto przykład, w którym tworzymy skomplikowaną nazwę pliku W mojej p ra k ty c e najczęściej m u sz ę p o słu g iw ać się funkcją sk ład o w ą c s t r p r z y o tw ieran iu p lik ó w d y sk o w y ch . N ie m ó w iliśm y jeszcze o b ib liotece funkcji p racu jący ch z p lik am i d y sk o w y m i, (b ęd zie o ty m specjaln y ro z d z ia ł - str. 1022), d la te g o tutaj p rz e d sta w ię sp raw ę tylko sch em aty czn ie. Jeśli ch ce m y ro zp o cząć cz y ta n ie (lub zapis) inform acji z w y b ra n e g o pliku d y sk o w e g o , to m u sim y g o n ajp ierw „o tw o rzy ć" - m ó w iąc funkcji otw ierającej, jak się n a z y w a in teresu jąc y n as plik. W m o im p rz y p a d k u często o tw ie ra m całą sęrię p lik ó w . S k o m p lik o w an a nazw a k a ż d e g o z nich sk ła d a się z: • czło n u b ę d ą c e g o ogólną n a z w ą czu jn ik a (detektora), •
czło n u b ę d ą c e g o n u m e re m jeg o g ru p y ,
•
czło n u b ę d ą c e g o sym bolem litero w y m seg m en tu .
O to p rz y k ła d y tak ich n a z w p lików miniball_l_A.parametry miniball 8 C.parametry
K ażd y z ta k n a z w a n y c h p lik ó w zaw iera w ś ro d k u z a d a n e p rz e z u ży tk o w n ik a p a ra m e try p racy d a n e g o d etek to ra. Z atem p rz e d ro zp o częciem p o m ia ru k ażd y z tych p lik ó w p o w in ie n e m o tw o rzy ć i w czytać. Jak się ju ż d o m y ślasz - p r z y tw o rzen iu tych z ło żo n y ch n a z w - sk w ap liw ie k o rz y s ta m z d o b ro d zie jstw k la sy s t r i n g . O statec zn ie jed n ak funkcja o tw ie ra jąca p lik m u si o trzy m ać n a z w ę w postaci C -strin g u . -
Jeszcze raz zwracam uwagę na to, co jest istotą tego przykładu: ♦♦♦ n ajp ierw tw o rz ę sk o m p lik o w a n ą n azw ę p lik u p o m ag ając sobie ró żn y m i u d o g o d n ie n ia m i k la sy s t r i n g , ♦♦♦ a g d y n azw a jest g o to w a , funkcją c _ s t r zam ien iam ją na C -strin g i w takiej postaci w y sy ła m funkcji bibiliotecznej.
Oto fragment programu pokazujący jak to robię N a s a m y m p o czątk u jest d ek laracja funkcji im itującej otw arcie p lik u . v o id o tw a r c i e _ p l i k u ( c o n s t c h a r * wsk) ; // deklaracja funkcji .L T & t iłc* ętcf cu.- • ■•• ■ O "1 -j f o r ( i n t g ru p a = 0 ; g ru p a < 5 ; g ru p a+ + ) f o r ( i n t seg m en t = 0 ; segm ent < 3 ; segm ent++) s t r i n g nazwa_pliku("miniball_") ; n a z w a p l i k u += c h a r C l ' + g r u p a ) ; n a z w a _ p lik u += nazwa p l i k u += c h a r ( 'A ' + s e g m e n t) ; n a z w a ~ p lik u += " .p a r a m e tr y " ;
554
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana zaw artości obiektu klasy s t r i n g na C -string otwarcie_pliku(nazwa_pliku. c _ s t r () );
// czytanie tego pliku
3
W rezultacie pracy tego fragmentu programu... ...w obiekcie klasy s t r i n g o nazwie na zw a _ p l i ku stworzone zostały następu jące nazw y plików i - jako C-stringi - kolejno wysłane zostały do funkc „otwierającej". miniball_l_A.parametry miniball_l_B.parametry miniball_l_C.parametry mi niba11_2_A.parametry miniball_2_B.parametry miniball_2_C.parametry miniball_3_A.parametry miniball 3 B.parametry miniball_3_C.parametry miniball_4_A.parametry miniball_4_B.parametry miniball_4_C.parametry miniball_5_A.parametry miniball_5_B.parametry mi n i b a 11_5_C.pa ramę t ry
Zobaczyliśm y tutaj, jak funkcja składowa c _ s t r pozwala nam w ysłać di funkcji bibliotecznej C-string, który stworzyliśm y naprawdę w obiekcie klas; s t r i n g . Jest to jakby wyjście awaryjne. Jeśli jednak m usisz funkcji c_st r () użyć w programie do w ysła nia tekstu do jakiejś swojej własnej funkcji - to znaczy najprawdo podobniej, że jeszcze nie udało Ci się pożegnać ze starymi metoda mi pracy ze stringami. Może warto w tedy przerobić tę swoje starożytną już funkcję...? Uwaga: Jak w idzisz, rezultatem pracy funkcji składowej c _ s t r jest wskaźnik (adres) d< sta łe g o C-stringu. Stałego, zatem w tym C-stringu nie pow inieneś robić żad nych zmian. Jest on tylko p>o to, byś sobie go poczytał. Jest jeszcze jedna rzecz:
Masz sobie ten C-string przeczytać lub przekopiować od razu. -T o oczywiste - przecież właśnie o to m i chodzi! - powiedziałeś pewnie -...A co, może być inaczej?" W yobraź sobie programistę, który wym yślił, ż e sobie ten adres po prosti zapam ięta w jakimś innym wskaźniku, żeby za w sze m ieć pod ręką treść danegc obiektu klasy s t r i n g - również i w tej, "C-stringowej”, postaci. Tak się nie da! Funkcja c_s tr sporządza dla Ciebie rezultat w postaci C-stringi w jakimś specjalnie zarezerwowanym (przez nią) na tę okoliczność miejscu. Jes
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Zaglądanie do w nętrza obiektu klasy string funkcją data
...
......................................................
mmmmmm,
............ii
555
*.......
1.15 Z a g lą d a n ie d o w n ę trz a obiektu klasy string fu n k c ją
data N ie o k ła m u jm y się - nie m a tu żadnej magii. Znaki w obiekcie klasy s t r i n g i tak trz y m a n e są w tablicy zn ak ó w , czyli tak samo, jak znaki C -stringu. jest jednak zasadnicza różnica - klasa s t r i n g dostarcza nam dodatkowo wiele bardzo wygodnych funkcji składowych, dzięki którym na tych (złożo nych w zwykłej tablicy) znakach możemy łatwo pracować. M oże s ię jednak zdarzyć, ż e chciałbyś dotrzeć do tej tablicy zn ak ó w . U m ożliw ia Ci to specjaln ie p rzy g o to w an a w tym celu funkcja składow a const char*
data O ;
Jak w id a ć , jest to funkcja w yw oływ ana bez żadnych arg u m en tó w , a w re z u l tacie zw racająca ad res tablicy, w której obiekt klasy s t r i n g obecnie p rzech o w uje p o w ie rz o n y m u tekst. Ten re z u lta t jest, jak w id zim y , w skaźnikiem do stałej, czyli po zw ala n am na c zy tan ie ta k p o k azy w an eg o fragm entu pamięci, ale nie po zw ala nic tam zap isy wać. N o i słusznie, obiekt klasy s t r i n g nie chce, by ktoś coś m ajstrow ał w jego w arsztacie pracy.
Co się znajduje pod otrzymanym funkcją data adresem? A dres te n p o k azu je na p o czątek tablicy, w której obecnie p rzech o w y w an e są znaki. Ja k p am iętam y , o b ie k t klasy s t r i n g nie m usi stosow ać zasad y , że na końcu te k stu m a być bajt zero w y (zw any znakiem nuli). Z atem m am y tam
556
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Zaglądanie do w nętrza obiektu klasy string funkcją data tablicę ze z n a k a m i. Ile ich jest kon k retn ie? Tego m ożem y się d o w ied zieć funkcj sk ład o w ą l e n g t h (m ów iliśm y o niej na str. 519). W iem y w ięc g d z ie są zn ak i i ile ich j e s t - ale jak du ża jest tablica tych znaków ? Tego m o żem y s ię dow ied zieć za p o m o c ą funkcji składow ej c a p a c i t y (m ówili śm y o niej na s tr. 521). Jeśli m yślisz, ż e o to za pom ocą funkcji d a t a * 8 dotarliśm y d o sed n a, do samegi jąd ra obiektu k la s y s t r i n g , do tego niezm iennego rd zen ia...
...to teraz przeżyjesz rozczarowanie. O tóż ta tablica d o p rzech o w y w an ia z n a k ó w nie jest „n ie z m ie n n y m rdzeniem W trakcie ró ż n y c h operacji z o biektem jest o n a czasem tu , czasem tam . Tak jest po p ro s tu w ygodniej. W y o b raź sobie, że w d a n y m obiekcie klasy s t r i n g p rz e c h o w u je m y obecnie te k st „W esela" S. W y sp iań sk ieg o . Jest to d o b rych kilka ty się cy zn ak ó w . N agle w p a d a s z na pom ysł, b y d o d a ć d o pierw sze stro n y d ziesięcio litero w ą nazw ę w y d aw cy . C zy m y ślisz, że obiekt klasy s t r i n g z ac zn ie te ra z grzecznie p rz e s u w a ć pozostałe k ilk a tysięcy zn ak ó w a dziesięć z n a k ó w dalej, tak by na p o czątk u tablicy zrobiło się w odpow iedni* miejsce? O czyw iście, ż e n ie. Z arezerw u je o n (o p erato rem new [ ] ) n o w ą , w ięk szą tablicę p rz e p isz e on d o niej tek st pierw szej stro n y , d o d a n a z w ę w y d aw cy , i dale d o p isze p o z o sta łą część tekstu. N iep o trz eb n ą już s ta rą tablicę zw o ln (operato rem d e l e t e [ ] ) .
Po co to musimy wiedzieć? Po to, by z d a w a ć so b ie sp raw ę, że jeśli d o w ia d u je sz się fu nkcją sk ład o w ą d a t a c ad res tablicy z n a k ó w , to o trzy m u jesz inform ację o b ieżący m jej ad resie. Jeśli za chw ilę w y k o n a sz n a d a n y m obiekcie klasy s t r i n g ja k ąś fu n k cję sk ład o w ą m odyfikującą je g o treść - to ten a d re s tablicy ju ż n a jp ra w d o p o d o b n ie j nie b ęd zie ak tu aln y . P odsu m o w u jąc: i* ««*>-**p m m
mmMm tM im tK ra tm aww
Jeśli funkcją składową d a t a dostajemy adres tablicy znaków, to najlepiej z niego skorzystać od razu.
i^cĄ
tlili \
>.*. . t;
pi ,
—
Podobnie było przy funkcji c _ s t r , jaka jest zatem różnica? Taka, że tutaj o trz y m u je m y ad res m iejsca, w k tó ry m o b ie k t n a p ra w d ę trzy m a sw ój ciąg z n a k ó w . N ie m u si on w cale b y ć zak o ń czo n y b a jte m z e ro w y m (zna kiem nuli). N ato m iast fu n k cja c _ s t r na ży czen ie p rz y g o to w u je n a m k o p ię tych zn ak ó w (w innej tablicy) w p o staci ciągu z a k o ń c z o n e g o bajtem z e ro w y m (zn ak iem nuli ) . : 8)
ang. data - dane [czytaj: "dejta"]
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów
557
1.16 W p o rzą d k u a lfa b e ty c z n y m - c zy li p o ró w n y w a n ie s trin g ó w Jeśli w sw oim p ro g ram ie b ęd ziesz m usiał szereg o w ać jakieś teksty w p o rz ą d k u alfabetycznym , to p om ogą Ci w tym zad an iu funkcje sk ład o w e o n azw ie com pa r e . M ożesz też p o słu ży ć się w y g o d n y m i o p erato ram i ==, !=, <, >, <=, >=.
Wielkie i małe litery w porównywanych stringach N iezależn ie od tego, k tó ry sp o só b w ybierzesz - pam iętaj, że p rz y takich p o ró w naniach lite ry w ie lk ie i ich m a łe o d p o w ie d n ik i - są tra k to w a n e jak o ró żn e litery. K onkretnie: litera 'A ' to zu p ełn ie in n a litera niż 'a . W szystkie litery w ielkie są w kolejności alfabetycznej p rzed w szy stk im i literam i m ałym i. Jeśli chciałbyś p o ró w n y w ać teksty niezależnie od tego, czy p isan e są w ielkim i literam i czy m ałym i - m o żesz postąpić tak: ♦♦♦ 1. U tw ó rz sobie pom ocnicze (chw ilow e) obiekty klasy s t r i n g , do których załad u jesz teksty zam ienione na w y łączn ie m ałe litery. 2. Te (pom ocnicze) obiekty poddaj p o ró w n an iu . Jak zrealizo w ać p rak ty c zn ie tę zam ianę na m ałe litery ? M asz d w a wyjścia: •
M ożesz p o słu ży ć się zw y k łą pętlą f o r , w której d o k o n asz zam ian y kolejno każdego zn ak u strin g u ;
•
M ożesz p o słu ży ć się tak zw an y m algorytm em , czyli funkcją tra n s fo rm .
Oba te sp o so b y zobaczym y w jed n y m z najbliższych paragrafów (str. 565). Na ra z ie zobaczm y - jaka jest istota p o ró w n y w an ia w ed łu g p o rząd k u alfabetycznego, takiego zw y k łeg o - w rażliw ego n a w ielkość liter.
1.16.1
Porów nyw anie stringów funkcjami com pare
558
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów i n t co m p are ( c o n s t c h a r * cjstring) ;
g in s w y n w ó io o ifv s o — i n t c o m p a r e ( s i z e _ t y p e od_pozvcji, s i z e _ t y p e ile_znakow, c o n s t c h a r * c_string, s iz e _ t y p e ilejm_por = n p o s) ;
V/ uf, ^ ©
• - T-
V*‘5L'h->4.V>l55?oaOM. 9 3 tfi| Z an im o n ich szczeg ó ło w o p o ro z m a w ia m y - zw ró ć u w a g ę , ż e rezu ltateit każdej z n ich jest w a rto ść ty p u i n t . F u n k cje te b o w iem m o g ą zw racać je d n ą : trzech w arto ści (-1 , 0, +1).
T y lk o trz y w artości, b o m o g ą być tylko trz y o d p o w ie d z i p rz y po I ró w n a n iu alfab ety czn y m d w ó c h strin g ó w .
Już to wyjaśniam Z ałóżm y , ż e m a m y jak iś obiekt k lasy s t r i n g z aw ierający te k s t, (np. Nazwiskc „K ant"). T era z chcielib y śm y s p ra w d z ić , jak ten tek st m a się w sto su n k u jak ieg o in n eg o tek stu (np. in n e g o n azw iska). M ożem y to s p ra w d z e n ie zrobić w te n sp o só b , że na rzecz n a s z e g o o b iek tu klas) s t r i n g w y w o ła m y funkcję s k ła d o w ą c o m p a r e , a jej a r g u m e n te m u czynim ) tekst, w z g lę d e m k tó re g o chcielibyśm y n a sz tek st p o ró w n a ć . W rezu ltacie s w e g o p o ró w n an ia fu n k cja c o m p a r e m o że o d p o w ie d z ie ć je d n ą ; trzech w artości: ♦♦♦ -1 , g d y n a sz strin g w e d łu g k o lejn o ści alfa b ety cz n ej u m iejscaw ia sii „ p rz e d " strin g ie m w y słan y m ja k o arg u m en t.
np. w kolejności alfabetycznej nazwisko „Kant" występuje przed nazwis kiem „Spinoza" ♦♦♦ 0, g d y n asz strin g byłby d o k ła d n ie w ty m sa m y m m ie js c u , co ten d ru g i p rz y s ła n y ja k o arg u m e n t.
np. nazwisko „Kant" stoi alfabetycznie w tym sam ym miejscu, co drugi takie samo nazwisko „Kant", ♦♦♦ +1, g d y n a sz strin g w e d łu g kolejności a lfa b ety cz n ej u m iejscaw ia sii „ p o " ty m s trin g u (p rzy słan y m ja k o a rg u m e n t)
np. nazioisko „Kant" w kolejności alfabetycznej następuje po nazwiski „Arystoteles". Ł atw o to z a p a m ię ta ć ry su jąc sobie ja k b y oś liczbow ą.
559
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów W zględem tekstu, przysłanego jako argument, mam stać:
po nim
w tym samym miejscu
przed mm
Rys. 11 -2 Funkcję com pare wywołuje się na rzecz jakiegoś obiektu klasy s t r i n g , a jako argument wysyła się jej tekst, z którym ma nastąpić porównanie. Ten rysunek pom aga skojarzyć odpowiedź liczbową z rezultatem porównania
.
.
.
Z atem w iem y już, co b ęd ą znaczyć od p o w ied zi fu n k q i c o m p a re .
Spójrzmy teraz na różne warianty funkcji compare. I znow u - ta w ielość jest d la naszej w ygody.
O P o ró w n a n ie tekstu, będącego treścią dan eg o o b ie k tu klasy s t r i n g z tekstem b ęd ący m w innym obiekcie klasy s t r i n g , u m o żliw ia n a m fu n k q a składow a. i n t com pare (c o n s t s t r i n g S inny) ; Jak
widać, a rg u m e n tem jest tu inny obiekt klasy
string.
Oto przykład użycia: s tr in g filo z o f ( " K a n t" ) ; s t r i n g k t o ś (" K a r te z j u s z ); c o u t << "Alfabetycznie nazwisko: "
«
filo z o f;
s w i t c h ( f i l o z o f . compare(k to ś) ) ( CĆIS6 “ l i « c o u t << " s t o i p r z e d nazw iskiem " << k to ś ; b re a k ;
..'i \ i j jjJgbTW} 83*1091
CdS6 0» cout « b re a k ; . CĆIS6 1 1 cout « b re a k ;
. •
" j e s t w tym samym m ie js c u co "
«
i k to ś ;
• . " n a s tę p u j ę po n azw isk u " << k to ś ;
}
W rezultacie wykonania tego fragmentu na ekranie pojawi się tekst: A l f a b e t y c z n i e n azw isk o : Kant s t o i p rz e d n azw isk iem K a r te z ju s z
560
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów r,
© Jeśli chcem y, by nie cały nasz string, lecz tylko pew ien jego fragm ent, został p o ró w n an y z jakim ś innym stringiem , to m ożem y p o słu ży ć się następującą wersją funkcji składow ej c o m p a r e : . .. int compare ( size_type od_pozycji, size type ile_znakow, const strings inny) ; *vi:x: je v>‘ ♦> od_pozycji - pierw szy arg u m en tem określam y n u m e r pozycji, od której zacząć m a się fragm ent będący przedm iotem p o ró w n a n ia •
♦♦♦ ile_znaków - d rugi arg u m en t określa jak d łu g i m a b y ć te n p o ró w n y w an y frag m en t ♦> inny - to o b iek t klasy p o ró w n an ie.
s t r i ng,
z którego (całą) zaw arto ścią m a się odbyć
Prosty przykład: string fizyk("Prot. A. Hrynkiewicz"); string inny("Mottelson"); t
cout << "W kolejności alfabetycznej ma byc:\n"; if(fizyk.compare(9, string::npos, inny) == -1) ( cout « fizyk « ", " « inny « endl; Jelse{ cout << inny « ", " « fizyk « endl; } ' oyś.
f\.
'
W rezultacie wykonania tego fragmentu programu na ekranie pojawi się: W kolejności alfabetycznej ma byc: Prof. A. Hrynkiewicz, Mottelson Jak w id zisz, d ru g im argum entem funkcji c o m p a r e u czy n iłem w artość s t r i n g : : n p o s - co oznacza, że chcę poró w n an ia w szy stk ich znaków , do k o ń c a strin g u f i z y k . C o do k o rzy stan ia z rezultatu sp raw d zen ia - jest tu ty lk o sp raw d zen ie czy w artość jest ró w n a -1 („poprzedza"), czy też ró żn a od -1 (zatem 0 lub +1). W tym p rz y p a d k u n ie m a to znaczenia, bo gd y b y n azw isk a b y ły b y te same, to w ypis i tak m iałby sens. (Nie da się przecież na ek ra n ie w ypisać dw óch iden ty czn y ch n azw isk w tym sam y m miejscu).
ilż -
I
S i 5 U tfis rf!
Y
© To najbardziej o g ólny p rzy p ad ek . P o ró w n u jem y fragment n aszeg o stringu, z fragmentem in n eg o stringu. P ozw ala n a to funkcja sk ład o w a:
561
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów
TOsignrtir.-
{ size_type o d _ p o zy c ji, size_type iłe_zn akm u , const string& in n y , size_type jjo z jn n e g o , size type i l e j m j n n e g o ) ;
;.Ffw D n n ]g ~ 0 i; •
Z naczenie argum entów :
od_pozycji —p ierw szy arg u m en tem określam y num er pozycji, od której zacząć m a się frag m en t, będący p rzed m io tem porów nania, <♦ Uejznakórw - określa jak długi ma być ten p o ró w n y w an y fragm ent, *•
inny - to obiekt klasy rów nanie.
string,
z którego fragm entem m a się odbyć po
D w a n a stęp n e arg u m en ty precyzują, jaki to m a być fragm ent, ♦♦♦ poz_innego - a rg u m e n t ten określa początek fragm entu w tym innym strin g u (w zględem którego porów nujem y nasz), *
Ue_zn_innego - określa ile znaków ma m ieć frag m en t tego innego stringu (w zględem którego porów nujem y nasz).
C zy p a m ię ta sz jeszcze ten ry su n ek (str. 549) na k tó ry m n iedaw no pokazyw ałem o gó ln y sch em at arg u m en tó w ? To pasuje d o k ład n ie d o tej wersji funkcji.
Przykład O kreślenie fragm entów p rz y d a się tutaj, by opuścić ty tuły n au k o w e i inicjały im ion, a porów nanie p rzep ro w ad zić tylko na fragm entach będących n azw isk am i string uczonyX["Prof. A. Hrynkiewicz"); string uczonyY("Prof. M. Curie"); co u t << "W k o l e j n o ś c i a lf a b e t y c z n e j ma by c:\ n " ; ^ i f (uczonyX. compare(9, s t r i n g : : n p o s , uczonyY, 9, 100) = - - l , cout «
uczonyX «
uczonyY «
endl;
cout «
uczonyY « ", " « uczonyX « ih O’ ( Ł'W i i 1.
endl;
", " «
W rezultacie wykonania tego fragmentu na ekranie pojawi się tekst W kolejności alfabetycznej ma byc: Prof. M. Curie, Prof. A. Hrynkiewicz
562
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów
Pozostałe dwie funkcje służą do porównania z tekstami będącym C-stringami. O Tej form y u ż y w a m y najczęściej, g d y chcem y p o ró w n ać z C -strin g iem w pisanyrr jako stała d o sło w n a. int compare (const char * c _ s tr in g ) ;
i - _ A rg u m en tem je st tutaj C-string, z k tó ry m chcem y p o ró w n a ć zaw arto ść naszegc obiektu klasy string. igirib Ae* '* v
Prosty przykład takiego wywołania funkcji: string wynalazca("Edison"); int odpowiedz = wynalazca.compare("Szczepanik"); if(odpowiedz == -1)
{ cout << wynalazca << " jest alfabetycznie wcześniej" << endl;
}
:
...
© Je ś li frag m e n t o b ie k tu klasy string chcem y p o ró w n ać z fra g m e n te m C -stringu to p o słu g u jem y się funkcją składow ą:
hęd
int compare ( size_type o d _ p o z y c ji, size_type U e _ zn a k o w , r n s ffH const char * c _ s tr m g , rf./fiJłisfr-ifiti :,n size type tic _ z n _ c s tr = npos);
M ioj&fiśi*.
Z n aczen ie arg u m en tó w : ❖
od_pozycji - n u m er pozycji, o d której zacząć m a się frag m e n t (naszego strin g u ), b ęd ący p rzed m io te m p o ró w n an ia,
❖
Ue_znak&w - jak d łu g i m a być te n p o ró w n y w a n y fra g m e n t,
♦♦♦ cjstring - to C-string, z k tó re g o p o czątk o w y m frag m e n tem chcem y p o ró w n a ć zaw arto ść n aszego o b iek tu k la sy string. ♦♦♦ Ue_zn_cstr - określa ile p o czątk o w y ch zn a k ó w C -strin g u m a w ziąć u d z ia ł w p o ró w n an iu . D o m n ie m an a w artość tego a rg u m e n tu to string: :npos, (czyli „ w sz y stk ie znaki d o końca").
Przykład* string urządzenie("trzy komory DRUTOWE"); char m u s i e [] = "komora JONIZACYJNA"; int odp = urządzenie.compare (12, string::npos, music+7, 2000); if(odp == -1)
( cout
« "Urządzenie typu: " << urządzenie << "\n jest alfabetycznie wcześniej niz: "
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów
563
A co z C -stringiem , z k tó ry m będziem y p o ró w n y w ać?
11.16.2
Porów nyw anie stringów przy użyciu operatorów
,
O to sto so w n e zestaw ienie ♦♦♦ Sl =
s2 czy stringi s l, s2 są takie same?
«$♦ s l ! = s2 czy stringi sl, s2 różne? *
s l < s2 czy wg. kolejności alfabetycznej string sl stoi PRZED stringiem s2?
«$♦ s l > s2 czy wg. kolejności alfabetycznej string sl stoi PO stringu s2? s l <= s2 czy wg. kolejności alfabetycznej string s l stoi na pozycji mniejszej lub
równej pozycji stringu s2 ? s l >= s2 czy wg. kolejności alfabetycznej string s l stoi na pozycji większej lub równej pozycji stringu s2? K ażd y z tych o p erato ró w jest dw u arg u m en to w y , czyli służy d o po ró w n an ia d w ó ch stringów . M ogą to być porów nania: - p orów nanie treści dw óch obiektów klasy s t r i n g , s t r i n g == s t r i n g
_ porów nanie treści obiektu klasy s t r i n g z jakimś C -stringiem . s t r i n g == C-string
564
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W porządku alfabetycznym - czyli porównywanie stringów - p o ró w n a n ie C -stringu z treścią obiektu k lasy s t r i n g .
C-strmg == s t r i n g Z w ró ć u w a g ę , że: P rzynajm niej jeden z porównywanych musi być obiektem klasy s t r i n g , nie można za pom ocą tych operatorów porów nać ze sobą dw óch C-stringów.
C zyli n ie m o żliw e je st poró w n an ie: C-string == C-string N iestety , n ie m o g ę teraz jeszcze w y czerp u jąco w y tłu m aczy ć, jak tw ó rco m klasy s t r i n g u d a ło sięzro b ić, że o p e r a to r y - k tó r e zw y k le u ż y w a liśm y d o porów ny* w a n ia liczb - p o zw alają nam szereg o w ać w ed łu g kolejności alfabetycznej W szy stk o sta n ie się jasn e w ro z d z ia le o p rz e ła d o w a n iu o p erato ró w . (R ozdz. 19, str. 794). i T u taj po pro stu cieszm y się, ż e tw órcy k lasy s t r i n g dali n a m taką m ożliw ość. R ezu ltatem p ra c y tych o p erato ró w jest w arto ść ty p u b o o l - czyli prawda/fałsz O p e ra to re m tak im p y ta m y na p rzy k ła d : —C z y to p ra w d a , że s trin g stojący z lewej s tro n y je st alfabetycznie "w iększy" od tego z p raw ej? O trz y m u je m y o d p o w ie d ź „ Prawda" albo „Fałsz!' P rz y p o m in a m , ż e p o d o b n ie jak w p rz y p a d k u funkcji c o m p a r e , p o ró w n an ie strin g ó w ro z ró ż n ia w ielkie i m a łe litery. W szy stk ie w ielk ie litery w kolejności alfabety czn ej stoją p r z e d w szy stk im i m ały m i.
Zobaczmy przykład użycia tych operatorów: string k("Kraków"); string p ("Piwniczna"); string p 2 ("Piwniczna") ; if(k == p)
{ cout « "Tresc ) if(k != p) { cout << "Tresc ) i f (k < p) ( cout << k « ) if(k > P) { cout << k « } i f (p O p2) { cout << p
obiektów k i p
jest identyczna\n";
_
obiektów k i p
jest rozna\n";
stoi alfabetycznie przed " «
p «
endl;
stoi alfabetycznie po " << p << endl;
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana treści stringu na małe (wielkie) litery
565
if(p >= P2) cout << p . . „ n « " stoi alfabetycznie po, lub ]est równe « p2 << endl;
}
Sprawa jest chyba bardzo łatwa, ale nie zaszkodzi zobaczyć wydruku z tego fragmentu Tresc obiektów k i p
jest rożna
11.17 Z a m ia n a treści s trin g u na m ałe (lu b w ie lk ie ) litery O m ów ione w łaśnie sposoby porów nyw ania alfabetycznego strin g ó w uznają m ałe litery i ich w ielkie od p o w ied n ik i - za całkiem o d m ien n e litery.
Oto p ro ste rozw iązanie: ♦♦♦ 1 Tuż przed porównaniem zam ieniam y w litery stringach na w yłącznie m ałe (lub w yłącznie wielkie) i tak zm ien io n e teksty sk ład am y w obiektach chw ilow ych; <* 2. D opiero te (chw ilow e) obiekty p o d d ajem y poró w n an iu . (Funkcją c o m p a re , lub o p erato ram i =~, k , >, , >—)•
Jak zamienić litery stringu na małe (wielkie)? Z obaczym y tu d w a sposoby. O ba korzystają z funkcji bibliotecznej t o l o w e r , która zam ien ia jeden z n a k n a odpow iadający m u zn ak mały. (S potkaliśm y się już z tą funkcją w § 5.14 na str. 187).
robią to samo, ale p o słu g u ją się innym i sposobam i. #include #include using namespace std; #include #include
H < - d la
II d e k la ra c je f u n k c ji
string zrob_male_p(string dany); string zrob_male_a(string dany);
transform tolower
566
Rozdz. 11. Biblioteczna k lasa s t d : : s t r i n g do operacji z tekstam i Zam iana treści stringu na małe (wielkie) litery //*************************************************★******** int main() i string hl("Hector Villa Lobos"); string h2("hector villa lobos"); cout << "Dwa stringi \n[" << hl « "1 oraz [" « h2 « "]\n"; cout « «
■.
"Przy porównaniu wrażliwym na wielkość liter..." endl;
if(hl != b2)
// O
{ cout << " -> sa rożne " «
cout « «
"Przy porównaniu NIEwrazliwym na wielkość liter..." endl;
i f (zrob_male_p (h l) =
i
endl;
zrob_male_a (h2) )
il O
cout << " - sa identyczne" << endl;
)
//***★*★**★********************************■**★***************** string zrob_male_p(string dany)
// ©
i
for(int i = 0 ; i < dany.length() ; i++)
{ j
.•■(•".'V.' dany[i] = tolower (dany [i]);
. rbf>T I I p r z y p is a n ie " w c i e m n o "
n
O
return dany;
} //***********************************************************★ * //funkcja dla wtajemniczonych string zrob_male_a(string dany) transform( dany.begin(), dany.endO, dany.begin() , tolower); return dany;
}
3
Po wykonaniu tego programu na ekranie zobaczymy Dwa stringi [Hector Villa Lobos] oraz [hector villa lobos] Przy porównaniu wrażliwym na wielkość liter... -> sa rożne Przy porównaniu NIEwrazliwym na wielkość liter... - sa identyczne
// ©
// ©
567
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana treści stringu na małe (wielkie) litery
Komentarz W programie, w funkcji m ain, widzimy definicję dwóch stringów. Oba zawiera ją nazwisko brazylijskiego kompozytora, ale raz pisane z użyciem wielkich liter, a drugim razem - samymi małymi literami. 0 Gdy próbujemy te stringi porównać stosując operator ==, to oczywiście uznaje on, iż są to różne nazwiska. N ic dziwnego - wiemy już, że tak funkcje com pare, jaki i zestaw operatorów ==, !=, <, <=, >, >= - są czułe na wielkość liter. Ponieważ jednak my, w przypadku nazwisk, wolelibyśmy porów nanie nieczułe na wielkość liter - posłużymy się podstępem. Po prostu, tuż przed porównaniem, zamienimy porównywane teksty na ich odpowiedniki pisane małymi literami.
0
Oto miejsce w programie, gdzie operatorowi każemy porównywać ze sobą stringi po zamianie ich na małe litery if(zrob_male_p(hl) == zrob_male_a(h2)) , ’.r
ffi
Jak w idzisz, są tu wywołania dwóch funkcji. Jedna ma nazwę zro b _ m a le_ p , a druga zro b _ m a le_ a . Obie te funkcje robią to samo - czyli obie zamieniają tekst na jego odpowiednik pisany małymi literami, ale każda z tych funkcji robi to inaczej. Ta druga funkcja jest trudniejsza, jakby dla zaawansowanych, więc... ...jeśli czytasz tę książkę po raz pierw szy - udawaj, że po obu stronach operatora == wywołana została ta sama (prostsza) fu nkcja z r ob_ma 1 e_p. Po prostu zmień sobie w wyobraźni tę linię
0 na taką: if(zrob_male_p(hl) == zrob_male_p(h2))
m
© Oto definicja tej funkcji zrob _m ale_p. Literka'p ' na końcu ma przypominać, że ta wersja korzysta z pętli. Co się dzieje w tej funkcji? 3 String-argument odebrany zostaje przez wartość (czyli kop ię). Potem widzim y prostą pętlę fo r . Przebiega ona po wszystkich znakach tego stringu. Jak widzisz, korzystamy tu z funkcji składowej le n g t h - aby się dow iedzieć ile w stringu jest znaków, czyli kiedy pętla ma skończyć pracę.
Co jest treścią pętli? To już zwykła podmiana znaku:
danyfi] = tolower(dany[i]);
568
Rozdz. 11. Biblioteczna k lasa s t d : : s t r i n g do operacji z tekstam i Zam iana treści stringu na małe (wielkie) litery R ozdz. 11 Biblioteczna klasa s t d : : s t r i n g d o o p e ra c ji z tek stam i Z am ian a treści strin g u n a m a łe (lu b w ielk ie) litery Instrukcja ta m ów i, iż zn ak stojący na d an ej pozycji m a zo stać z am ien io n y m sw ój o d p o w ie d n ik z zestaw u liter m ały ch 2®. U słu g ę tę w y k o n u je funkcje tolower.
Nazwę t o l o w e r należy oczywiście gdzieś zdeklarować. Do legi potrzebna była na górze programu instrukcja
#i n c l u d e (Zwróć uwagę, że nazwa tego pliku nagłówkowego ma dwie litery c).
Zagadka: C zy d o k o n u jąc tej operacji p o d m ian y zn ak u funkcją t o l o w e r zniszczyliśm y sobie starą treść o ry g in aln eg o stringu? Nie! P rzecież a rg u m e n t został d o funkcji p rz y sła n y p rz e z w arto ść (kopię) Z atem n asza z a m ia n a zn ak ó w o d była się nie w o ry g in a ln y m obiekcie, ale już -v> jego lokalnej kopii. P o zak o ń czen iu sw ej p racy, fu nkcja z w ra c a jako rezu Itat - o b ie k t k lasy s t r i n g T en o b iek t tym ró żn i się teraz od o ry g in a łu , że w nim w sz y stk ie litery są jua m ałe. Z w ró ć u w a g ę , ż e to te n zw racan y p rz e z tę funkcję obiekt - z o stan ie p rzed sta w io n y o p e ra to ro w i == w linijce 0 .
W z a sa d z ie w iesz ju ż w szystko. M o g lib y śm y n a tym sk o ń czy ć, b o opisane p o w y żej fu n k cję m o żesz stosow ać p o d o w o ln ej stro n ie o p e ra to ró w ==, !=, <, > <=, >=, a także w arg u m e n tach w y w o łan ia funkcji c o m p a r e .
Dla wtajemniczonych - algorytm trans form W d alszej części o p isu naszego p ro g ram u za k ła d a m , że w iesz, co to są iteratory O m aw ia to je d e n z najbliższych p a ra g ra fó w (§11.23, str. 587), w ięc jeśli czytasz tę książk ę p o ra z p ie rw szy - zd e c y d o w a n ie o p u ść p o n iż sz e kilka linijek. P otem , jeśli z d e c y d u je sz się p rzeczy tać (tak że n ad o b o w ią z k o w y ) " m ateriał' a iterato ra ch , p rz y p o m n ę Ci o tym frag m en cie - i w te d y w ró cisz tutaj
P o k ażę teraz sp o só b z am ian y liter w ielk ich n a m ałe k o rzy stający z biblioteczne funkcji t r a n s f o r m , należącej do tak z w a n y c h fu n k cji-alg o ry tm ó w . D obrze ta zobaczy ć, b o w cu d zy ch p ro g ram ach m o żesz sp o tk ać s ię z w łaśnie takim sp o so b e m z a m ia n y w ielkich liter na m ałe. © O to w n a sz y m p ro g ra m ie d ru g a funkcja, k tó ra zro b i z a m ia n ę liter w ielkich naj m ałe. Ta zro b i to in n y m sp o so b em - k o rzy stając z a lg o ry tm u t r a n s f o r m 20)
Gdybyś chciał zamieniać nie na małe, ale na wielkie litery - to zamiast funkcji tolower! powinieneś posłużyć się funkcją toupper.
569
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana treści stringu na małe (wielkie) litery
m a ł e a . T a lite rk a 'a ' w n a z w ie m a tu p r z y p o m in a ć , ż e je st o cą a lg o r y tm u . s trin g
z r o b _ m a le a ( s t r i n g
dany);
Z p o w y ż s z e j d e k la ra c ji w id z im y , ż e je s t to fu n k cja p rz y jm u ją c a je d e n a r g u m e n t b ę d ą c y o b ie k te m k la s y s t r i n g . A r g u m e n t te n p r z y ję ty b ę d z ie p r z e z w a rto ś ć , w ię c f u n k c ja p r a c o w a ć b ę d z ie n a k o p ii. Jak o r e z u lta t s w e j p r a c y fu n k c ja z w ró c i o b ie k t k la s y s t r i n g z a w ie r a ją c y n o w ą treść. Zatem jak do tej pory - w szystko jest tak sam o, jak w poprzedniej wersji te] fu n kcji © W c ie le tej fu n k c ji w id z im y n a s tę p u ją c ą ta je m n ic z ą in stru k c ję . tr a n s f o r m ( d a n y .b e g i n ( ) , d a n y .e n d f ) , d a n y . b e g in ( ) , to l o w e r ) ; J e s t to w y w o ł a n ie n a s tę p u ją c e j fu n k c ji b ib lio te czn ej: ite ra to r
tra n s fo rm f
i t e r a t o r skąd_brać, i t e r a t o r dokądjrrać, i t e r a t o r gdzie_wkładać, o p e r a c j a nazwaJunkcji_konwertującej) ;
M ó w ią c z w ię ź le : ’ fu n k c ja ta na k o le jn y c h z n a k a c h z z a d a n e g o (d w o m a ite r a to r a m i) o b s z a r u , p r z e p r o w a d z a p e w n e o p e ra c je , a re z u lta ty ty c h o p e ra c ji u m ie s z c z a p o c z ą w s z y o d m iejsca w s k a z a n e g o in n y m ite r a to r e m . Z a ra z
s ta n i e
się
to
ja sn e ,
gdy
o m ó w im y
z n a c z e n ie
p o s z c z e g ó ln y c h
a r g u m e n tó w : <$► P ie r w s z y m a r g u m e n te m - skądjbrać - w s k a z u je m y , g d z ie m a się z a c z y n a ć o b s z a r liter, k tó r e ch cie lib y śm y s u k c e s y w n ie p o d d a w a ć jak iejś o p e ra c ji. W naszym przypadku chodziło o operację zamiany znaku na „mały . Chcemy to robić od samego początku strin g u , dlatego postawiliśm y tu d a n y . b e g i n ( ) . (O funkcji begin rozmawiamy w § 11.23.2, 11.24,
str. 593) ♦♦♦ D r u g im a r g u m e n te m — dokąd_brać — w s k a z u je m y m iejsce (z n a k ), p o o s ią g n ię c iu k tó re g o p r o c e s m a się z a trz y m a ć . W a ż n e : p o k a z y w a n y ty m ite ra to re m z n a k — n ie p o d le g a ju ż o p e ra c ji z a m ia n y . (T ak to b y w a p r z y m e to d z ie : „ o d tą d - d o tą d " ) . W naszym przypadku chodziło o zamianę całości stringu —aż do ostatniego znaku, dlatego postawiliśm y tu d a n y . e n d () . Jak wiadomo (z jednego z następnych paragrafów - § 11.23, str. 587), funkcja składowa e n d określa miejsce o jedną pozycję dalej w stosunku do ostatniego znaku stringu. To nam gwarantuje, że ostatni znak też zostanie poddany operacji zam iany. ♦♦♦ T r z e c im a r g u m e n te m —gdziejwkładać —o k re ś la m y ( w in n y m s tr in g u , lub n a w e t w ty m s a m y m ) m iejsce, p o c z ą w s z y o d k tó re g o k o le jn o u m ie s z c z a n e b ę d ą r e z u lta ty p o sz c z e g ó ln y c h o p e ra c ji n a p o je d y n c z y c h znakach.
570
Rozdz. 11. Biblioteczna k lasa s t d : : s t r i n g do operacji z tekstam i Zam iana treści stringu na małe (wielkie) litery
W naszym przypadku chcieliśmy rezultat umieszczać w tym samym strin gu, z którego znaki pobieraliśmy, dlatego postawiliśmy tu
d a n y . b e g in (). <♦ Nazwa_funkcji_konwertującej -
to nazwa
funkcji,
którą funkcją
trans form będzie wywoływała dla każdego pojedynczego znaku
25
przedziału [skądjrrać, dokądJbrać). Funkcja ta ma przyjmować jeden argument (typu pokazywanego iteratorem) i zwracać rezultat takiego sam ego typu. W naszym przypadku jest to nazwa funkcji t o l o w e r . Jako argument przyjmuje ona znak c h a r ,a jej rezultatem jest także wartość typu char. Czas na refleksję: Jak widzisz, ta funkcja transform to jakby sprytniejsza pętla for, która dla każdego znaku z zadanego jej obszaru - w yw ołuje jaką ś (w ybraną przez nas) inną funkcję.
Ostrzeżenie: -nljriiHiKrtill 'liii'
"
Jeśli będziesz posługiw ał się algorytmem trans form- pam iętaj, że zacho w uje się on podobnie, jak poprzednio pokazana pętla. T o znaczy rezultat um ieszcza „na ślepo” w pokazanym iteratorem miejscu - licząc na to, że to m iejsce rzeczyw iście należy do tego obiektu. — w ir n w » liniu j, » j
owwi!
N a przykład: Jeśli przez pomyłkę chciałbyś umieszczać rezultat w stringu, który do tej pory był pusty - (i miał pojemność = 0), lub był za krótki Twój program najprawdopodobniej się zatrzyma sygnalizując po w ażny błąd. To dlatego, ż e gdyby string przeznaczony na rezultat operacji nie miał odpowiedniej pojemności - znaki lądowałyby na „ziemi niczyjej" - lub, o zgrozo, „ziemi obcej".
Łatwo to zapamiętać tak: T ' ** . . I Funkcja transform wkładając znaki w e wskazane miejsce posłu| guje się operatorem [ ].
Robi ona mniej więcej coś takiego jak nasza instrukcja O:
cel [i] = tolower(zrodlofi] ); Operator [ ] działa szybko, bo nie sprawdza czy jego obiekt klasy string ma odpow iednią wielkość. Funkcja transform nie posługuje się w ięc operacjami typu insert (grzecznie powiększającymi rozmiar stringu). Liczy na to, że miejsce jest już przygotowane.
571
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Z am iana treści stringu na małe (wielkie) litery
Ju ż sły sz ę Tw ój pro test: -Zaraz,zaraz! Przecież w paragrafie o funkcji r e s i z e zapewniałeś mnie, że klasa s t r i n g jest taka wspaniała, bo wcale nie musimy się przejmować przygotowywaniem miejsca na nowe znaki. No więc, jak to jest w końcu ? P o d trz y m u ję to, co p o w ied ziałe m w tedy. P am iętaj, że m ó w iłem tam o funkcjach s k ła d o w y c h k lasy strin g . To o n e nas w yręczają z k ło p o tu m yślenia o rezerw acji m iejsca n a n o w e znaki. Tu je st je d n a k inaczej, bo ro zm a w iam y o zupełnie obcej fu n k c ji o n a z w ie t r a n s fo rm . N ie je st ona częścią k la sy s t r i n g . Jeśli c h c e m y k o rzy stać z obcych funkcji, n iestety trzeb a d o sto so w ać się d o ich re g u ł g ry . P ra w d a jest taka, że funkcja t r a n s f o r m nie zare zerw u je n a m m iejsca n a w y n ik sw ej p racy i już. W łaściw ie to z u p e łn ie n ie ob ch o d zą ją s p ra w y n a sz e g o strin g u . M oże (łaskaw ie) w ykonać d la n a s jak ieś z ad a n ie - p o d w a ru n kiem , ż e p rz y g o tu je m y d o tego teren. Zatem , w ta k im p rz y p a d k u s a m i m u sim y z a d b a ć o to, by s t r i n g p rz e z n a c z o n y na re z u lta t jej p ra c y m iał o d p o w ie d n ią d łu g o ść. Jeśli zap o m n im y ... j.j.
.
j
i
,
.1
i
f,
,
i
* M • • •. n •
\
.
L * ,
^
k>
Czy nie zrobiliśmy takiego błędu w naszym programie? N ie, b o u m ie sz c z a liśm y z n a k i w tym sam ym obiekcie, a o n m iał d o k ła d n ie ta k i ro z m ia r, ja k i był p o trzeb n y . Czyli jeśli mamy string o długości 57 znaków, to po zamianie liter na małe potrzebujemy stringu o właśnie 57 znakach.
A co robić w sytuacjach, gdy rezultat pracy funkcji t r a n s fo rm chcemy umieszczać w innym obiekcie? Ja m a m ta k i sp o só b , że: (tu ż p rz e d w y w o łan iem funkcji t r a n s fo rm ) - d o teg o n o w eg o o b iek tu , w p rzy g o to w an eg o na re z u lta t - p rzep isu ję so b ie strin g źró d ło w y . To nic, ż e z a chw ilę ta now a ("źródłow a") treść z o stan ie zn iszczo n a n a sk u te k p racy fu n k c ji t r a n s fo rm . L iczy się tylko to, by tu ż p rz e d rozpoczęciem p ra c y funkcji t r a n s fo r m - ten n o w y strin g osiągnął w ła śn ie ta k i rozm iar, ja k i b ę d z ie za c h w ilę p o trz e b n y na rezu ltat. string oryginalny("ABCdefGHI"); string rezultat;
//...
rezul tat = oryginalny;
//«- aby
u z y s k a ć o d p o w ie d n i ro zm ia r p o je m n ik a
transfoml oryginalny.begin(), oryginalny.end (), rezultat.begin(), tolower);
O czy w iście in n y m sposobem pow iększenia s trin g u d o w łaściw eg o ro z m ia ru jest w y w o ła n ie funkcji: r e z u l t a t .resize(oryginalny.length ()) ;
572
Rozdz. 11. Biblioteczna k lasa s t d : : s t r i n g do operacji z tekstam i Kopiowanie treści obiektu klasy string do w ybranej tablicy znakowej - funkcja copy
11.18 K o p io w a n ie treści obiektu klasy string do w y b ra n e j ta b lic y zn ako w ej - funkcja copy Jeśli chcielibyśm y całość lub fragm ent zaw arto ści naszego obiektu klasy s t r i n g skopiow ać d o fragm entu jakiejś zw ykłej tablicy zn ak ó w ,
czyli jakby: zastąpić dotychczasowo istniejące tam znaki nowymi, naszymi, to p o słu ży ć m ożem y się funkcją składow ą c o p y 21. size_type
copy ( char * wskjablicy_znaków, size_type od_pozycji = 0 ) ;
size_type ile,
Z naczenie argum entów : ♦> wskjablicy_znaków - pierw szym arg u m e n tem określam y ad res, pod k tóry m a się odbyw ać kopiowanie, ♦> ile - drugim arg u m en tem określam y ile zn ak ó w m a zostać skopiow a nych, ❖
odjpozycji- trzeci arg u m en t określa o d której pozycji w n aszy m obiekcie klasy string m a się rozpocząć pobieranie zn ak ó w do sk o piow ania.
UW AGA: Funkcja ta, po skopiowaniu żądanej liczby znaków, nie dodaje po nich niczego, nawet bajtu zerowego (tzw. znaku nuli).
A by to zapam iętać, w y starczy sobie skojarzyć tę funkcję z funkcją replace, k tó ra w określone m iejsce w pisuje tylko kilka zn ak ó w , a następ n y ch - nie niszczy (naw et jeśli te n astęp n e byłyby „śm ieciam i").
Oto prosty przykład użycia tej funkcji: char tablica[100] = { "habent*sua*fata*libelli" string przepis("Olejek bezwonny lej przez lejek, przepis.c o p y (tablica, 15, 1); cout << "Zawartość obiektu tablica: « « endl;
f*l
}; ...");
'" << tablica
Ostatnia instrukcja spowoduje, że na ekranie zobaczymy taki tekst: Zawartość obiektu tablica:
'lejek bezwonny *libelli'
Jak w id ać, w tablicy zo stało w ym ienionych 15 pierw szy ch zn ak ó w w tablicy, a dalej p o zo stał fragm ent dotychczasow ego tek stu łacińskiego. 21)
ang. copy - kopia, kopiuj [czytaj: "kopi"].
573
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W zajem na zam iana treści dwóch obiektów klasy string - funkcja swap
11.19 W z a je m n a za m ia n a treści dw óch o b ie k tó w klasy string - fu n k c ja swap Pow yższy tytuł powiedział prawie wszystko. Robimy to funkcją składową swap22. void swap (string & i n n y j s t r w g ) ;
?rU ■ W rezultacie działania tej funkcji nasz obiekt klasy s t r i n g ("nasz", czyli ten dla którego wywołaliśmy funkcję swap) wymienia się treścią z innym stringiem - przysłanym tej funkcji jako argument.
AA
Zobaczmy przykład, jak takiej zamiany treści można dokonać *?VT*4
string ofiara("Desdemona"); string bestia("Otello"); cout << "Przed zamiana ofiara: " << ofiara « ", bestia: " « bestia << endl; ofiara. swap (bestia); cout « "Po zamianie ofiara: " « ofiara « ", bestia: " « bestia « endl;
Po wykonaniu tego fragmentu, na ekranie zobaczymy tekst: Przed zamiana ofiara: Desdemona, bestia: Otello Po zamianie ofiara: Otello, bestia: Desdemona
Jak widać, obiekty o f i a r a i b e s t i a wymieniły się zawartością.
11.20 P rzy p is a n ie do o b iektu klasy
string, fu n k cja assign
Wiemy od dawna, że aby przypisać nową treść do obiektu klasy s t r i n g możemy posłużyć się wygodnym operatorem . W większości sytuacji ten operator nam wystarcza. Funkcji, o których teraz porozmawiamy, używa się raczej rzadko, więc - jeśli chcesz - to poniższy opis możesz przy pierwszym czytaniu tej książki opuścić.
W .23
Teraz poznam y zestaw funkq'i składowych o nazwie a s s ig n
Pozwalają one na bardzo wyszukane sposoby przypisania. Oto zestaw funkcji składowych, które mamy do dyspozycji. 22) 23)
ang. swap - zamiana, wymiana, [czytaj: "słop"). ang. assign - przypisz, [czytaj: „essajn"]. .
(.i.
i
.
•‘
574
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przypisanie do obiektu klasy string, funkcja assign s t r i n g & a s s ig n ( s t r i n g & innyjtring) ; s t r i n g & a s s i g n ( s t r i n g & inny_string, s iz e _ t y p e odkąd, size_type ilejznakow) ; s t r i n g & a s s ig n ( c h a r * c_string, s iz e _ ty p e ilejmaków) ; s t r i n g & a s s ig n ( c h a r * c_$tring) ; s t r i n g & a s s ig n ( s iz e _ ty p e de_znaków, c h a r znak); string & assign (iterator o d ką d , iterator d o k ą d ) ;
©
Rezultatem wszystkich tych funkcji jest referencja tego obiektu klasy s t r i n g , na rzecz którego funkcje zostały wywołane. M amy już chyba dużo doświadczenia z różnymi formami przeładowanych funkcji składowych klasy s t r i n g , chyba już potrafiłbyś sam powiedzieć, jakie jest znaczenie argumentów w poszczególnych wersjach.
Przyjrzyjmy się im po kolei O Funkcja s t r i n g & a s s ig n ( s t r i n g & inny_strmg);
um ożliwia przypisanie (czyli tutaj: skopiowanie) całej treści innego obiektu klasy s t r i n g do obiektu, na którego rzecz ją wywołujemy.
Na przykład: s t r i n g o c h o tn ik ; s t r i n g s tu d e n tM (" K o n ra d " );
OJL i V'■,-r■ - l *»'« 7 ; ochotnik.assign(studentM);
c o u t << "O c h o tn ik iem j e s t :
□
" << o c h o tn ik ;
Na ekranie wypisany zostanie tekst: Ochotnikiem jest: Konrad
0 Funkcja ta umożliwia przypisanie danemu obiektowi klasy s t r i n g fragmentu treści innego obiektu klasy s t r i n g . s trin g
& a s s ig n (
■
s t r i n g & inną string, size_type o d ką d ,
size_type ile _ z n a k ó w ) ;
Znaczenie argumentów: <♦ inny_string - referencja do obiektu, z którego pochodził będzie fragment. N astępne dwa argumenty uściślają jaki to fragment. ♦♦♦ odkąd- n u m e r z n a k u , od którego zacząć m a się p rz y p isy w a n y frag m en t,
ile_znaków - wartość określająca ile znaków ma ten fragment - czyli ile znaków zostać przypisanych do naszego stringu.
575
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Przypisanie do obiektu klasy string, funkcja assign
string n o w y ("ABCD") ; string wers("Gdy ryczy z bólu ranny lew"); . a s s i g n (wers, 17, 5);
W re z u lta c ie te g o p rz y p isa n ia n o w ą treścią obiektu n o w y stan ie się tekst: "ra n n y . iy io ifiiw ! A© T a w ersja fu n k cji a s s i g n u m o ż liw ia n am p rzy p isan ie n a sz e m u obiektow i k la sy s t r i n g fra g m e n tu tekstu p o c h o d zą ceg o z jakiegoś C -strin g u . string & assign (char * c_stńng, size_type Ue_znaków) ;
Są tu d w a a rg u m e n ty :
‘
~ ł “r i*
K ^\
Łsfł
r-
♦> - p ie rw s z y to ad res (w sk a źn ik ) d o C -stringu; p o c z ą w sz y od tego tak p o k a z y w a n e g o zn a k u k o p io w a n e będą zn ak i, ♦♦♦ - d r u g i a rg u m e n t o kreśla ile (kolejnych) z n a k ó w m a być sk o p io w an y ch d o n a s z e g o obiektu. ■. i n toirti iiiO i Przykład zastosowania: •ws * > s f s t r i n g w y ś w i e t l a c z ("200 M eV "); -b a j ogjti w y ś w i e t l a c z . a s s i g n ("15 MeV n a n u k le o n " , 6 ) ; 4* j Pt} - ■; * ' *' W rezu ltacie treścią obiektu w y ś w i e t l a c z stan ie się te k s t " 1 5 MeV". 4
O Ta fu n k cja p rz y p o m in a funkcję o p isa n ą pow yżej, z tą ró żn icą, że nie m a tu d ru g ie g o a rg u m e n tu , zatem cała treść w sk azan eg o C -strin g u zostanie p rzy p isa na d o n a s z e g o o b iektu. s trin g
& a s s i g n ( c h a r * c_string) ;
♦♦♦ A rg u m e n t to w sk aźn ik (adres) C -stringu, k tó reg o w szy stk ie zn ak i m ają z o stać m ają zostać p rz y p isa n e d o obiektu klasy s t r i n g . P rz y k ła d u n ie b ęd zie. Spójrz d o p rz y k ła d u u m ieszczo n eg o w p o p rzed n io o p i sanej fu n k q i. G d y u su n iem y z n iego d ru g i arg u m e n t (cy frę 6) - w ów czas cała treść teg o C -strin g u zn ajd zie się o b iekcie w y ś w i e t l a c z .
© P rzy p isan ie s e rii jed n ak o w y ch z n a k ó w do obiektu k la sy s t r i n g realizuje funkcja: s trin g
& a s s i g n ( s iz e _ ty p e ilejznak&w, c h a r znak);
♦♦♦ P ie rw sz y a rg u m e n t p o zw ala n a m określić k ro tn o ść p o w tó rzen ia zn ak u . ♦♦♦ D ru g i a rg u m e n t określa jaki to znak m a być w ielo k ro tn ie pow tórzony.
576
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Dopisywanie do końca stringu za pomocą funkcji append
Przykład: s t r i n g rząd("S&&&&&&&") ; r z ą d . a s s i g n (15,
//efekt: "$$$$$$$$$$$$$$$" :. er i
@ Ta funkcja jest dla zaawansowanych - wiedzących, co to są iteratory, a przede wszystkim dla innych funkcji bibliotecznych, które bardzo chciałyby taką wer sję przypisania także mieć. Omówimy tę funkcję w osobnym paragrafie poświę conym iteratorom. (§ 11.23.2, str. 593).
11.21
D o p is y w a n ie do końca s trin g u za p o m o c ą fu n kcji
append Jeśli w obiekcie klasy s t r i n g jest już jakiś tekst, a chcemy coś do jego końca jeszcze „dopisać" - możemy to zrobić posługując się omówionym wcześnie (str. 516) w ygodnym operatorem +=. Operator ten (w połączeniu z funkcją s u b s t r in g ) spełni większość naszych oczekiwań. W bardziej wyszukanych sytuacjach możemy sobie pomóc stosując jeden z wariantów funkcji append. 4 Jest to bardzo łatwe, ale jeśli chcesz, możesz ten paragraf opuścić przy pierwszym czytaniu tej książki. I tak sobie poradzisz stosując w takich sytuacjach operator +=.
f!LJ : s ła b i II*- ,.- y z
Różne wersje funkcji append - jeśli chodzi o zestaw i znaczenie argumentów są takie, jak różne wersje omówionej właśnie funkcji a s s i g n , dlatego nie będę omawiał ich bardzo szczegółowo. Zdążyłeś się już chyba z tymi argumentami oswoić. Oto deklaracje tych funkqi. s t r i n g & append ( s t r i n g & inny_string) ; s t r i n g & append ( s t r i n g & innystring, s iz e _ t y p e odkąd, s iz e _ t y p e ilejznaków) ; s t r i n g & append (c h a r * c_string, s iz e _ ty p e ilejznaków); s t r i n g & append (c h a r * c_string); s t r i n g & a p p e n d ( s iz e _ ty p e ilejznaków, c h a r znak);
Dla wtajemniczonych: s t r i n g & append ( i t e r a t o r odkąd, i t e r a t o r dokąd) ; 24)
ang. append - dołączyć, dodać [czytaj: „epend"].
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
577
Bardzo krótkie omówienie __________ w szystkich tych funkcji jest referencja d o tego obiektu klasy s t r i n g , na rzecz którego funkcje zostają w ywołane. O Funkcja ta u m o ż liw ia dołączenie całej treści innego ob iek tu klasy s t r i n g do obiektu, na k tó reg o rzecz ją w yw ołujem y © Funkcja ta u m o ż liw ia przypisanie d an em u obiektow i klasy s t r i n g fragm entu treści in n eg o o b iek tu klasy s t r i n g . © Ta w ersja funkcji a p p e n d um ożliw ia nam dopisanie do k o ń ca naszego obiektu klasy s t r i n g frag m en tu tekstu pochodzącego z jakiegoś C -stringu. © Ta funkcja p rzy p o m in a funkcję o p isan ą pow yżej, z tą różnicą, że nie m a tu d ru g ieg o a rg u m e n tu , zatem cała treść w skazanego C -stringu zostanie dopisana do końca n aszeg o obiektu. © D opisanie d o k o ń ca naszego obiektu serii jednakow ych znaków . © Tę „iterato ro w ą" w ersję funkcji om ów im y sobie w osobnym paragrafie pośw ię conym iterato ro m . (§ 11.23.2, str. 593).
1.22 W c z y ty w a n ie z klaw iatu ry d łu g ieg o s trin g u o n ie zn a n ej w c z e ś n ie j d łu g o śc i - getline Bywają w p ro g ra m ie sytuacje, gdy użytkow nik p o w in ien z klaw iatury podać nam jakąś inform ację nie-liczbow ą. N a przykład pro sim y , by po d ał nazw ę m iasta. D o tej p o ry robiliśmy tak, że przygotow yw aliśm y jakąś odpow iednio d u żą tablicę z n a k o w ą - a następnie tak zw any strum ień w ejściow y c i n pozw a lał n am w czy tać nazw ę. c h a r m i a s to [ 1 0 0 ] ; c i n >> m i a s t a ;
Sposób ten miał dwie wady W a d a l:
_ N ig d y nie w iem y, czy ab y tekst w pisany na k law iatu rze nie przek ro czy rozm iaru przygotow anej dla n iego tablicy. Tutaj p rzygotow aliśm y na to stosunkow o d u żą tablicę —100 znaków , w ięc to chyba aż za dużo, praw da? A jednak niepraw da! Są w Walii miejscow ości, których n azw y mogą mieć p o n a d dw ieście znaków .
W ada 2: W czy ty w an y tym sposobem tekst mógł być tylko jednym w y ra zem .
Co zrobić? Z p ie rw szeg o kłopotu w ybaw ia nas klasa s t r i n g .
578
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline s t r i n g m ia s to ; c i n >> m ia s to ; Dzięki w spaniałości klasy s t r i n g nie musimy już rezerw ow ać ta b licy „w ciem no”, z zapasem . W ogóle nie musimy się przejm ow ać gospodarką pam ięcią.
Nasz obiekt klasy s t r i n g o nazwie m i a s t o sam z ro b i o d p o w ie d n i re ze rw ac ję wtedy, gdy od strumienia c i n otrzyma znaki. Przeliczy je i zrofc taką rezerwację, by pomieścić je wszystkie. Pozostaje kłopot drugi. Jeśli nazwa miasta to „N ow y Sącz" - to niestety tym sposobem do stringu m i a s t o m ożem y wczytać tylko „Nowy". Spacja spow o duje zakończenie umieszczania tekstu w obiekcie m i a s t o . N ie wczytany drugi człon nazw y („Sącz") pozostanie w buforze klawiatury. Jak to rozwiązać?
Jak wczytać z klawiatury nie jeden wyraz, ale całą linijkę tekstu? Twórcy klasy s t r i n g przygotowali w tym celu funkcję o nazwie g e t l i n e Co prawda, nie jest to funkcja składowa klasy s t r i n g , ale jest to także dzieło autorów klasy s t r i n g . Funkcja g e t l i n e umożliwia nam wczytanie (z klawiatury, czy z pliku dyskowego) nie jeden wyraz, ale w iele, nawet bardzo w iele znaków. Kiedy kończy pracę? Jednym z jej argumentów jest znak nazwany umownie: ogranicznik. To my decydujemy, co ma być tym ogranicznikiem. M oże to być litera ' x ', a może to być znak nowej linii. Gdy funkcja napotka taki znak - przerywa wczytywanie, dotychczasowe znaki umieszcza nam w naszym obiekcie klasy s t r i n g , a ogranicznik - wyrzuca Oto deklaracja funkcji g e t l i n e , jest w niej pewna niezrozumiała nazwa, ale nie przerażaj się: is tre a m
&
getline ( i s t r e a m wej,
s t r i n g szhowek, c h a r ogranicznik = ' \ n ' ) ;
Co to jest ta tajemnicza nazwa i s t r e a m ? Całej prawdy teraz powiedzieć jeszcze nie m ogę, bo o operacjach wczytywania informacji z klawiatury (czy z plików dyskowyc i) porozmawiamy dopiero pod koniec tej książki. Tutaj tylko przyjmij, że i s t r eam jest nazwą klasy, do której należy nasz stary znajomy - strumień wejściowy c in , którym wielokrotnie posługiwaliśmy się ju ż, by wczytywać informację z klawiatury. 25) 26)
ang. gel linę - weź linię, pobierz linię (tekstu). [Czytaj: „get łajn"] Uwaga: Kompilator MS Visual C++ wersja 6.0 ma (w swej bibliotece) tę funkcję błędnie zaimplementowaną i jej działanie nie zgadza się z opisywanym w standardzie.
579
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
na temat typu rezultatu i argumentów formalnych naszej funkcji
w czy ty w an ie zn ak ó w «£• P ierw szy m argum entem (wej) określam y skąd będą pochodzić ImoLi u i w cz y ty w a n e znaki. -UłU- V W naszym przypadku najczęściej chodzi o wczytywanie strumieniem c i n, <♦ D ru g i a rg u m e n t to nazw a obiektu klasy s t r i n g , w którym funkcja m a u m ieścić nam w czytany tekst. ♦> T rzeci a rg u m e n t - to tak zw an y ogranicznik, czyli znak, który w ybieram y jako oznaczający koniec w czytyw anego tekstu. Jeśli funkcja zobaczy taki znak-ogranicznik— zakończy wczytywanie tekstu um ieści d o tych cza so w e znaki w naszym obiekcie klasy string, a o g ra n ic z n ik -w y rz u c i.
W artość d o m n iem an a tego ogranicznika to ' \ n ' - czyli znak nowej linii. G dy ko rzy stam y z tego dom niem ania, to tak jakbyśm y m ów ili, że w czytyw anie ____ 1____ t ___________ i 1 / l « » n o > 7 a " K r i ł o r 11 1 / znaków m a się_____ zakończyć po w ciśnięciu klawisza "Enter Z ab rzm iało to m oże trochę zaw ile, ale w praktyce jest b ardzo proste, zatem
Czym prędzej zobaczmy przykład #include #include using namespace std;
U ---------------------------------------------------------------int main()
{ string miasto("Sajgon"); _ cout << "Podaj nowa nazwę miasta " « miasto « " « endl; getlinefcin, miasto); cout « "Teraz jest: [" « miasto «
"]" «
endl;
) O becnie m iasto Sajgon nazyw a się: Ho Chi Minh cztero w y razo w ą, nazw ę w piszem y...
...na ekranie pojawi się poprawnie tekst: Podaj nowa nazwę miasta Sajgon:
Ho Chi Minh City Teraz jest:
[Ho Chi Minh City]
City. Jeśli
taką,
580
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
Ostrzeżenie zamiast komentarza Skorzystaliśmy tutaj z tego, że ów ogranicznik, to na mocy domniemania - zna nowej linii (klawisz Enter). Uwaga: Niezależnie od tego, czy skorzystamy tutaj z tego domniemania, czy też dosłownie trzecim argumentem uczynimy znak nowej linii ('\n') - możemy wpaść w pułapkę, o której bliżej porozmawiamy w następnym paragrafie. W powyższym programiku - nic nam nie grozi, ale przecież tak krótkie programów w swojej pracy pisał nie będziesz. i«. j.iJ 1
,*<1»•’t
• i •*. o!ł1*>/I •
Zobaczmy teraz przykład, w którym ogranicznikiem będzie znak V . ■teft
•: -•
. .'V;V•
y rg Ó A rftłf
.;:y .v„■*\ . \ .
string miasto;
cout «
ł8t?n? 3*081
;-v- -
-■
"Napisz dowolnie długi tekst, a koniec tekstu " "oznacz znakiem 'v'" « endl;
getline(cin, miasto,
’v ’);
'{bO . '-mu :* cout « "Oto zawartość obiektu miasto << miasto << "]" « endl;
. fi
'1
r
-
["
,1
Oto, co zobaczymy na ekranie: Napisz dowolnie długi tekst, a koniec tekstu oznacz znakiem ’v'
Prawa miejskie mojemu miastu nadal Kazimierz Wielki
w ro k u 1330. M iało w ted y 4 bramy i 21 f u r t . v Oto zawartość obiektu miasto [Prawa miejskie mojemu miastu nadal Kazimierz Wielki w roku 1330. Miało wtedy 4 bramy i 21 furt.]
Tekst, który napisałem na klawiaturze oznaczony jest tłustym drukiem. Zauważ kilka spraw: - opis miasta ciągnął się przez kilka linijek dopóki nie napisałem litery ' v '; a wtedy - bez potrzeby wciskania klaw isza Enter - funkcja za kończyła pracę, - wystukane na klawiaturze znaki - łącznie ze spacjami i znakami nowe linii, (ale bez ogranicznika ' v ') stały się treścią obiektu m ia s to Możesz to sprawdzić dzięki wydrukowi rezultatu na ekranie Wszystkie znaki stringu m ia s to zawarte są m iędzy klamrami [ ] . 27)
Znowu przypominam, że kompilator MS VC++ v. 6.0 nie zachowa się tu poprawnie! Będzie czekał na "Enter".
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
581
W Z ob aczy liśm y w ięc w y g o d n y sposób w czytyw ania n a w e t b ard zo dłu g ich tek s tów , bez konieczności w stęp n eg o zak ład an ia ich p rzy bliżonego rozm iaru.
.1
Pułapka - czyli jak g e t l i n e może C ię zaskoczyć
T eraz m u sim y p o ro zm aw iać o tym , jak to się dzieje, ż e znaki w y stu k an e na k la w ia tu rz e ostatecznie trafiają d o obiektu klasy s t r i n g . Tak naprawdę - to o tym jest specjalny rozdział na końcu tej książki. (Operacje We/Wy - str. 1022). Niestety tak długo nie możemy czekać, bo ‘skurtF! 'icAi zupełnie nie rozumiałbyś dlaczego czasem — mimo iż napiszesz coś na ŚH bob «v ■' klawiaturze - nie trafia to do Twojego obiektu klasy s t r i n g , jak tego
chciałeś. A zatem —k law iatu ra, to in telig en tn e u rząd zen ie elektroniczne. M a ona sw ój procesor. Jeśli coś na niej w y stu k asz, to ta inform acja jest p rzez nią o d b ieran a i jak s tru m ie ń inform acji m oże po p ły n ąć do k o m putera w stro n ę Tw ojego p ro g ra m u. O śro d k iem kontroli p rzep ły w u tej inform aqi jest d la Twojego p ro g ram u obiekt o n a z w ie c i n . . Z atem Tw ój p ro g ram nie k o m u n ik u je się z u rząd z en iem elektronicznym , ale z jego o śro d k iem kontroli. 1 Z naki, które w y stu k ałeś na klaw iatu rze - litery, cyfry, spacje, w ciśnięcia klaw isza „ E n te r" - m ożna z pozycji program u od eb rać zw racając się d o obiektu c i n . R obim y to zw racając się d o niego na przykład o peratorem » lub funkcją
getline. Problem w tym, że >> oraz getline odbierają znaki nieco odmiennie i mogą sobie przeszkadzać Jeśli sk o rzy stasz z o peratora >> (w czytującego znaki d o stringu), a n a stę p n ie będziesz coś chciał w czytać d o innego stringu funkcją g e t l i n e , to m ożesz być zaskoczony. Spójrz na te n niew innie w yglądający fragm ent p ro g ram u : c o u t << "N ap isz p ie rw s z y t e k s t : "; s t r i n g p ie rw s z y ; , , c in » p ie rw s z y ; // <-wczytywanie operatorem » cout << "Napisz drugi tekst: string drugi; getline{cin, drugi, '\n'J;
// <- wczytywanie funkcja getline
cout << "Oto tresc pierwszego: [" « pierwszy << "]\na to tresc drugiego: [" « drugi «
J\n ;
Spójrz jak d ziw n ie zachow a się ten fragm ent p ro g ram u . Oczekujem y sp raw y prostej:
582
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
«$♦ Najpierw mamy zostać zapytani o string p ie r w s z y , (a wtedy wystuka my go na klawiaturze). ♦♦♦ Następnie mamy zostać zapytani o string d r u g i (wówczas też wystu kamy go na klawiaturze). Na ekranie ma zostać wypisana zawartość p ie r w s z e g o d r u g ie g o stringu.
ora;
Sprawy potoczyły się jednak inaczej.
Na dowód - zobaczmy za chwilę wydruk z ekranu Nie widać tego poniżej jasno, więc od razu uprzedzę, że gdy zostałerr poproszony o napisanie pierwszego tekstu - wystukałem na klawiaturze teks "rozum iem " (bez tych cudzysłowów). Zauważ, że po tym wyrazie dodałeir jeszcze (w tej samej linii) trzy spacje. (Poniżej na wydruku zastępują je trzy znak podkreślenia) Nie te trzy spacje jednak spowodowały dziwne zachowanie programu! One raczej pomogą nam zobaczyć, co się stało złego. Napisz pierwszy tekst: rozumiem___ Napisz drugi tekst: Oto tresc pierwszego: a to tresc drugiego: [___ ]
[rozumiem]
Co się stało konkretnie? A'fi. Gdy na polecenie wpisania pierwszego stringu wystukałem na klawiaturze string "rozumiem " - to na ekranie pojawił się tekst zachęcający mnie da wystukania drugiego tekstu, ale program n ie d a ł m i żadnej szansy na w p is a n ie tego drugiego tekstu - od razu uznał, że już to zrobiłem i przystąpił do wypisania obu stringów. Pierwszy z nich zawiera jedno słowo "rozum iem ", a drugi - te trzy spacje. Gdybym tych trzech spacji na klawiaturze nie wystukał, to string d r u g i byłby naprawdę pusty.
Oto wyjaśnienie Pierwszy string wczytywaliśmy operatorem » , czyli tak cin »
pierwszy;
Operator ten - jak do tego już od dawna przywykliśm y - pozwala wczytać z klawiatury do stringu tylko jeden wyraz. Gdy napotka po wyrazie biały znak (czyli spację, tabulator, znak nowej linii itd.) zatrzymuje wczytywanie. To w ięc sprawiło, że operator » u mieścił nam w stringu p ie r w s z y - wczytany wyraz bez żadnych następujących po nim białych znaków. Natom iast białe znaki czekają w strumieniu c i n na ewentualnego chętnego. Gdyby wczytywanie drugiego stringu odbywało się znow u operatorem » , to nie byłoby problemu. Wiemy od dawna, że
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
583
d la s tru m ie n ia c i n o p e ra to r» pracu je w te n sposób, iż n a p o tk a w sz y białe z n a k i PRZED sp o d ziew an y m w y razem - co p ra w d a o d b ie ra je, ale o d razu w yrzuca. D la o p e ra to ra ' » ' d o p iero p ie rw szy n ie-b iały z n a k ro zp o cz y n a w ięc in teresu jąc ą część pracy. K rótko m ó w iąc, g d y b y ś m y d ru g i strin g także chcieli w cz y ta ć o p erato rem >>, to e w e n tu a ln e b iałe z n a k i nie b y łyby przeszkodą. S koro ta k - to d la czeg o tego d ru g ieg o strin g u nie w czy tu jem y tym sa m y m , w y p ró b o w a n y m , sp o so b e m ? P rz y p o m in a m - m a o n d la n as p o d staw o w ą w adę: o p e ra to r » m oże w c z y ta ć n a m ty lk o je d e n w y ra z . M y zaś chcem y w czytać całe z d a n ie , a m o że n a w e t całą stro n ę te k stu . To d la te g o zdecydow aliśm y, że te ra z m u sim y u ży ć funkcji g e tlin e . W ró ćm y d o p rz e b ie g u n aszeg o p ro g ra m u . O to w cz y ta n y zo stał ju ż p ie rw sz y strin g . Z a ch w ilę p ro g ra m w yśle na ekran inform ację z p ro śb ą o w p is a n ie d ru g ie g o tek stu . Z a n im to się stan ie - oceńm y sytuację:
Zastanówmy się, co obecnie jeszcze tkwi (nie wczytane) w strumieniu cin? T ak, o czy w iście te trz y spacje! Ale czy coś jeszcze? G d y p isaliśm y te trz y spacje, to po n ich w cisnęliśm y k la w isz "Enter". A to o zn acza, ż e w strum ieniu na w c zy ta n ie czekają cztery zn ak i: spacja, spacja, spacja, zn ak now ej lin ii (czyli '\ n ') . D obrze. W ie m y ju ż, n a czy m stoim y. Teraz następuje w n aszy m p ro g ra m ie taki fra g m e n t k o d u c o u t << " N a p is z d r u g i t e k s t : s t r i n g d ru g i; g e t l i n e (c in , d ru g i, ’ \ n ') ; N a e k ra n ie p o ja w ia się inform acja zachęcająca d o w p isa n ia d ru g ie g o tek stu : " N a p i s z d r u g i t e k s t : " i w ted y następuje w n a sz y m p ro g ram ie in stru k cja g e tlin e . In stru k c ja ta m ó w i, ż e o to należy dać u ż y tk o w n ik o w i szan se n a p isa n ia na k la w ia tu rz e n a w e t d łu g ieg o w ielow yrazow ego tekstu. Z a k o ń c z e n ie w c z y ty w a n ia te g o tekstu (ogranicznik) ma nastąpić w p rz y p a d k u n a p o tk a n ia zn a k u ' \ n ' (zn ak now ej linii). I tu w ła ś n ie p o ja w ia się problem . W c i n (czyli w stru m ie n iu p ły n ący m z k la w ia tu ry ) c z e k a ją n a o d e b ra n ie trz y spacje i znak now ej linii. F u n k cja g e t l i n e (w p rzeciw ień stw ie do o p erato ra » ) nie p rzesk ak u je s p a c jibo w ie , ż e w tek stach spacje m ają istotne zn aczen ie. Z atem te trzy sp acje u m ie s z c z a w ob iek cie d r u g i . C zy b ę d z ie w czy ty w a ła d alsze znaki? Tak, b ęd zie w czy ty w ała w sz y stk ie d a lsz e , d o p ó k i n ie zobaczy ogranicznika: '\ n '.
584
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
N iestety dla nas - taki znak jest natychmiast po tych trzech spacjach! Funkcja w yjm ie go ze strumienia ina tym zakończy pracę. W stringu d r u g i m zapisał' trzy spacje. Tylko trzy spacje, bo znak ogranicznika, jak pamiętasz, nie jest przez ni dołączany do końca stringu. Po prostu wyrzuca go. G dybym tych trzech spacji nie napisał, to na wczytanie z klawiatury czekałby tylko znak nowej linii, zatem funkcja g e t l i n e od razu napotkałaby ogranicznik i pracę zakończyła. String d r u g i byłby wtedy całkiem pusty. Pora na podsumowanie:
Co sprawiło, że funkcja g e t l i n e nie zachowała się tak, jak na to liczyliś my? Zbieg dwóch okoliczności: • 1. To, że w strumieniu c i n był pozostawiony (po poprzednie operacji) znak '\n '. • 2. To, że zdecydowaliśm y, iż ogranicznikiem tekstu, (czy. trzecim argumentem funkcji g e t l i n e ) miał być akurat zna* nowej linii. W ten sposób funkcja g e t l i n e na samym początku swojej pracy napotkała ogranicznik.
Jakie jest proste rozwiązanie takiego problemu ? ♦♦♦ W tym przypadku wystarczy, by ogranicznikiem uczynić inny znak Znak, którego obecności nie przewidujemy wewnątrz oczekiwanego li * tekstu. Oczywiście informujemy o tym użytkownika programu. Ni przykład: Napisz tekst, a na zakończenie wciśnij klawisz
’v'
:
♦♦♦ Inny sposób - dla wtajemniczonych - to skorzystanie z funkcji i gnoro (poznamy ją dopiero w § 22.13.3 str. 1096). Funkcja ta pozwala wyczyś' cić" zalegający w strumieniu znak nowej linii, lub ewentualne inne, po przedzające go znaki. Tutaj tylko nadmieniam, że chodzi mniej więcej o takie jej wywołam (umieszczone gdzieś przed wywołaniem funkcji g e t l i n e): . f
frfl i \,z
N
•ł
?
\
V
cin.ignore(25,
f
’ .*'!1
•• *
*■--
'i•"
’
’
y>
'\n');
Oznacza to mniej więcej takie żądanie postawione strumieniowi c in : •
A teraz chcę byś pominął (wyrzucił, ignorował) 25 znakóui (czekających, lub takich, które zaraz przyjdą). Z tym , że - jeśl wśród tych 25 znaków napotkasz znak ' \ n ' - to w ted y w y rzuć go także, ale na nim skończ ignorowanie. Zatem ta funkcja ignoruje 25 znaków, lub mniej -je ś li wśród nici znajdzie się znak ' \ n \
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i W czytywanie z klaw iatury długiego stringu o nieznanej wcześniej długości - getline
Skoro rozwiązanie jest takie proste, to dlaczego tak rozmawialiśmy?
585
długo o tym
C hodziło mi o to, byś był św iadom różnicy w przypadku odbierania tekstów z przy użyciu tych dw óch sposobów. v vfT
” *****•' * « * - v * ,-*>* »
« p j « h <-•
mmmmmnm-mmmM.
Zatem powtórzm y jeszcze raz:
1. Gdy odbieramy string operatorem >> ♦♦♦ Pomijane są wstępne białe znaki (w tym znaki nowej linii), a dopiero znaki nie-białe um ieszczane są w obiekcie klasy s t r i n g . ♦♦♦ W czytywanie zakończone jest, gdy ewentualnym następnym znakiem miałby być znak biały. N ie jest on wyjm owany ze strumienia. Co z tego wynika dla nas? Jeśli tym sposobem chciałbyś na przykład do obiektu klasy s t r i n g wczytać nazwisko, a użytkownik programu pomyli się i na klawiaturze wystuka Ci nazwisko oraz imię, po czym wciśnie klawisz "Enter", to do obiektu klasy s t r i n g w pisane zostanie nazwisko, a... ...występująca po nim spacja, imię i znak nowej linii będą nadal tkwiły w strumieniu c i n czekając na to, aż jakaś następna operacja wczytywania ich z tego strumienia nie wyjmie. Wyobraź sobie co będzie, jeśli tą następną operacją okaże się akurat w czytyw anie z klawiatury liczby rzeczywistej. Co prawda ta spacja nie jest w tedy groźna, ale zaraz po niej są litery imienia (a nie cyfry). To zupełnie zablokuje pracę strumienia c in . Horror! Co wtedy zrobić - porozmawiamy w rozdziale: Operacje We/Wy (str.
1022) .
2. Gdy wczytujemy string funkcją getline Jeśli z klawiatury chcesz do obiektu klasy s t r i n g wczytać tekst funkcją g e t l i n e , to pamiętaj, że nie robi ona różnicy m iędzy znakami białymi i nie-białymi. I słu szn ie -w tekście „Pana Tadeusza" tak samo ważne są spacjeJ a k litery 'm ', czy znaki nowej linii. Zatem fu n k c ja g e t l i n e nie przeskoczy żadnych w stępnych spacji czy zn a ków now ej linii. Zacznie je przyjm ow ać ja k pełnopraw ny fragm ent tekstu.
Używając tego sposobu pamiętaj więc, że - jeśli poprzednio wczytywanie odbyw ało się operatorem >> - to, przed właściwym tekstem, dostaniesz jeszcze w prezencie, co najmniej znak nowej linii, którego operator » ze strumienia nie ma zwyczaju wyjmować. A jeśli, o zgrozo, zdecydow ałeś w dodatku, że dla
586
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu get linę o g ran iczn ik iem ma być znak now ej linii - to ten „p rezen cik " o d razu
zak o ń czy C i pracę fu n k cji get linę.
11.23 Ite ra to ry strin g u T en p arag raf z d
e c y d o w a n ie
ra d z ę opuścić p rz y p ierw szy m czy tan iu tej książki.
X Z ro zd ziału o w sk aźn ik ach w iem y, że jeśli trzeba w ykonać jak ąś akcję n a posz czególnych znakach z w y k łeg o C -stringu, to m am y d o w y b o ru dw a sposoby: ♦♦♦ - posłużenie się n u m eram i pozycji tych znaków , ♦♦♦ - posłużenie się w skaźnikiem i p rz e su w a n ie go o d zn ak u d o znaku (operatorem ++). A jak to jest w p rz y p a d k u obiektów klasy string? C zy p o zn aliśm y już p o d o b n e sposoby d o cieran ia do zn ak ó w zapisanych w obiektach klasy string?
Co do num erów pozycji - to oczywiście tak! Test tu w y g o d n y o p e ra to r [] pozw alający nam docierać d o poszczególnych z n a k ó w tkw iących w obiekcie klasy s t r i n g tak sam o, jak b y były częścią C -stringu. Z resztą n u m eram i pozycji posługiw aliśm y się nie tylko p rz y p a d k u operatora [ ]. Także w funkcjach składow ych klasy s t r i n g b ard zo często w y m ag an a b y ło p o d an ie nu m eru pozycji, od której funkcja m a zacząć sw oją pracę. Mówiliśmy: „-od pozycji numer N zacznij wstawianie, poszukiwanie, czy
zastępowanie".
A czy wobec znaków przechowywanych w obiektach klasy string posłu giwaliśmy się wskaźnikami? To znaczy, czy b y w ało tak, że ustaw ialiśm y w skaźnik na jakim ś zn ak u , by w y k o n ać na nim jak ąś operację (zastępow anie, o d czy tan ie itd.), a potem p rzesu w aliśm y go (o p erato rem ++), aby w y k o n ać tę sam ą operację na znaku następ n y m ? P rzy zn aj się, że n a w e t nie przyszło Ci to d o głow y: p rzecież zn ak i są ukrytą w e w n ą trz obiektu k lasy string! C o p raw d a, naw et tak ie u k ry te w e w nętrzu obiektu klasy s t r i n g zn ak i muszi m ieć jakieś swoje ad resy , ale z paragrafu o funkcji data () w iem y, z e obiek k lasy string m oże sw ój tekst przech o w y w ać to tu, to tam - i zm ian y ta n astęp u ją b ardzo często. T ru d n o więc poleg ać na takich adresach... K rótko m ów iąc - ten sposób "w skaźnikow y" nie w ydaje się tak p rzy d atn y .
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
587
Skąd takie określenie? Słowo to pochodzi od łacińskiego iterare (powtarzać), bo istotą iteratora jest to, iż służy do wielokrotnego powtarzania kolejnych operacji (np. odczytu, czy zapisu). W naszym przypadku chodzi o wskazywanie na różne miejsca tekstu przecho w yw anego przez obiekt klasy s t r in g . Zrobimy to w łaśnie iteratorem.
W gruncie rzeczy, iterator stringu to bardzo proste narzędzie O S*
Jeśli w jakimś miejscu programu potrzebujemy go - wystarczy na przykład taka definicja s t r i n g : : i t e r a t o r z ie lo n y ; Jest to definicja obiektu o nazwie z ie l o n y , który jest iteratorem (jakby rucho mym w skaźnikiem ) mogącym pokazywać na różne miejsca dowolnego obiektu klasy s t r i n g . Tym iteratorem z i e l o n y możemy pokazać na znak jakiegoś obiektu klasy s t r i n g , na przykład na znak, który chcemy odczytać (lub zmienić). Potem przesuwam y iterator na następny znak, by znowu odczytać, potem znowu na następny - i tak dalej, i tak dalej... Czy rozumiesz, co chcę powiedzieć? Iteracja to wielokrotne powtarzanie tej samej akcji - po to, by w końcu osiągnąć zamierzony cel. Podobnie jak w przypadku zwykłych wskaźników - nie wystarczy taki iterator zdefiniować, trzeba jeszcze sprawić, by pokazał na coś sensownego. Oczywiś cie, jeśli jest to iterator s t r in g u , to trzeba ustawić go na jakiś znak jakiegoś tam s t r in g u . Jak to zrobić konkretnie? Bardzo pomoże nam w tym jedna z funkcji składo wych klasy s t r i n g .
588
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
,AZ W.-,
N a z y w a się b e g i n 28 i z g o d n ie ze sw oją n a z w ą p o zw ala nam u s ta w ić ite ra to r tak, by p o k aza ł n a p o czątek jak ieg o ś stringu.
.
O to d e k la r a q a tej fu n k cji składow ej . .. #• ; : • iterator
..v*? **• ■'■
:t *
b egin()*
Ja k w id a ć , jest to funkcja: _ k tó rą w y w o łu je m y b ez żad n e g o a rg u m e n tu , *
_ k tó ra w re z u ltacie zw raca w arto ść ite ra to ra p o k azu jąceg o na początko w y z n a k te k stu p rz e c h o w y w a n e g o w d a n y m obiekcie klasy s t r i n g .
T ę w a rto ść m o ż e m y po p ro s tu p rzy p isać do n a s z e g o iterato ra.
A tak prosto się tą funkcją możemy posłużyć: string o powieść("Byl sobie dziad i baba..."); string rozdzial_4("Juz starożytni Rzymianie... zielony zielony
);
opowie s c .b e g i n () ; rozdział 4.begin();
W id z im y tu d efin icje d w ó c h stringów . T rzecia in stru k cja sp ra w ia , że nas ite ra to r z i e l o n y u s ta w io n y zostaje tak , iż p o k a z u je n a p o c z ą tk o w y (zerow y z n a k u strin g u o p o w i e ś ć . W n astęp n e j in stru k c ji zm ie n ia m y z d a n ie i u s ta w ia m y n a sz ite ra to r na począte in n e g o strin g u . P o d o b n ie jest przecież z e z w y k ły m w sk a ź n ik ie m - ra z m óg p o k a z a ć n a jkkieś m iejsce teg o C -strin g u , a z a ch w ilę m o ż e p o k a z a ć na jakie m iejsce w in n y m C -stiin g u .
M am te ra z d o b rą w iad o m o ść. Otóż...
znaczna większość operacji na iteratorach do złudzenia przypomina nam operacje ze wskaźnikami. S am sobacz: * Jeśli chcesz d o w ie d z ie ć się, jaki z n a k p o k a z y w a n y je st w ła śn ie iteratc ni*;; rem - u ż y w a s z g w ia z d k i *zielonv
♦♦♦ Jeśli ch cesz iterato r p rzesu n ąć ta k , b y p o k a z y w a ł n a n a s tę p n y / p o p r d n i zn ak s trin g u , to m ożesz u ż y ć o p e ra to ra in k re m e n ta c ji/d e k re m e n ti cji, czyli ++ lu b ++ zielony — zielony
28)
ang. begin - początek [czytaj: „begin"].
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
589
Jeśli ch cesz przeskoczyć o określoną liczbę z n a k ó w - m ożesz posłużyć się z w y k łą arytm etyką, z n a n ą Ci z „ary tm ety k i w sk aźn ik ó w ". (§ 8.8.1, str. 267). z i e l o n y = z i e lo n y + 6; zielony += 12;
Pierw sza z p o w y ższy ch instrukcji p rzesu w a iterator o 6 z n a k ó w dalej. D ruga p rzesu w a o d a ls z e 12 znaków , O czyw iście sam i m usim y zad b ać o sensow ność tak ieg o p rzesu w an ia. To zna czy, jeśli strin g m a długość sześciu zn ak ó w , a m y p rzesu n ie m y iterator (pokazu jący do tej p o ry na znak zerow y) - o sto pozycji dalej, w ó w czas pokaże on na ii
•
•
•
* ii
Oto przykład fragmentu programu ilustrującego pracę z iteratorem: string kolę d a ("Gloria in Excelsis Deo"); ' Y i ń l ' 1 ' - . ł '-‘i
ditbsfiłW a r:■)
i n t z a c z n i j = 3; cout « «
"W s t r i n g u [" « ile «
k o lę d a «
"1" «
" \n z a m ie n ie "
" znaków począwszy od "
<< z a c z n i j «
e n d l;
string::iterator it; it = kolęda. beginO + zacznij;
for(int i = 0
i < ile ; i+-+)
*it it++; cout << "Rezultat:
[" «
kolęda «
")\n";
W ykonanie tego fragmentu programu zaowocuje takim wydrukiem na ekranie: W stringu [Gloria in Excelsis Deo]
z a m ie n ię 12 znaków począw szy od 3 Rezultat: [Glo........... sis Deo]
Komentarz
iVd :n :
O Definicja ite ra to ra o nazw ie i t .
O U staw ien ie ite ra to ra i t tak, by p o k azy w ał na trzeci (licząc o d zera) znak stringu y;;t zn ajd u jąceg o się w obiekcie kolęda. T e d w ie in stru k c je ( O i © ) m ożna oczyw iście zapisać w postaci jednej takiej: string::iterator it
kolęda.begin() + zacznij;
590
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu Jest to naw et bardziej zalecany sp o só b , bo od razu w instru k cji definiujące iterator - u staw iam y go na jakąś sen so w n ą w artość. (N ie m a ryzyka, a zapom nim y). . , , . . , • , ,. • Mimo to zdecydowałem się tutaj na dwu: odrębne instrukcje - notauj^ zasadzie, by nie przerażać Cię na wstępie bardziej skomplikowaną formą. © W miejsce p o k azy w an e iteratorem w staw iam y now ą w a rto ść - zn ak : ’ . ' O Przesunięcie iterato ra na następny znak. Instrukcje © i O m ożna napisać w p o staci złożonej i w y g lą d a to tak * it+ + = ' . ' ; Oczyw iście na p ew n o nie jesteś tutaj zaskoczony - b o z tak ą sam ą składni! spotkaliśm y się w p rzy p ad k u posłu g iw an ia się zw ykłym i w sk aźn ik am i poka żującym i na tablice znaków .
Zobaczyliśm y tu n o w e n arzędzie do pracy ze stringam i. N a rz ę d z ie to nazyw! się iterator, a b ard zo przy p o m in a w skaźnik, którym p o k azy w aliśm y na znak C-stringu. ♦♦♦ Czy zw róciłeś u w agę, że w linijce definicji iteratora n ie by ło gw iazdki?
Łatwo to zapamiętać, gdy sobie uświadomimy, że naprawdę definiujemj nie wskaźnik, ale obiekt, który jedynie ma zdolności wskaźnika. ♦♦♦ G w iazd k i nato m iast u ży w am y jako operatora o d n ie sien ia się d o znaki p o k azy w an eg o w łaśnie iteratorem . . W obec iteratora m o żem y stosow ać p o d o b n e operacje, jak w obec w skaźnika - 1< znaczy m ożem y g o p rzesu w ać w p rz ó d /ty ł. Robim y to o p erato ram i ++, - HI albo dodając/odejmując od niego liczbę całkowitą.
11.23.1
Iterator do obiektu stałego Także i ten p a ra g ra f zd ecydow anie ra d z ę opuścić przy p ie rw sz y m czytaniu książki.
M oże się zd arzy ć, że obiekt klasy s t r i n g będzie miał d o d a tk o w o p rzy d o m e c o n s t , czyli b ę d z ie to taki obiekt, k tó reg o nie w olno zm ien iać (m odyfikow ać. Jeśli chcielibyśm y posługiw ać się w obec takiego obiektu iterato rem - to pow i nien on także m ieć cechę c o n s t . P ow inien być iteratorem do obiektu stałego M o żn a takim iteratorem oczyw iście po ru szać, ale pokazyw anego za jego pom ocą obiektu - m odyfikow ać nie w olno. O to, jak w y g ląd a definicja takiego iteratora: string::const_iterator m;
C zy tam y ją:
591
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
const string Ew_Jan("Na początku było słowo");
/ / stały string O
i t J a n = Ew_Jan.begin() ;
//
w h ile (i t J a n != Ew_Jan. end())
H
cout «
«
(*itJan++);
©
// ©
}
Łatwo zauważyć, że ten fragment kodu spowoduje wyprowa dzenie na ekran tekstu, którego poszczególne znaki p o p rzed zo n e są znakam i podkreślenia. _N_a_ _P_o_c_z_a_t_k_u__ b_y_l_o__ s_l_o_w_o Zw róć u w ag ę, że nie zm ieniliśm y tu niczego w obiekcie. To tylko na ekran popłynęły d o d atk o w e znaki podkreślenia.
O Definicja obiektu klasy s t r in g , który jest stały - to znaczy n ad an y mu w czasie inicjalizacji tek st nie m oże być potem (naw et przez nieuw agę) zm ieniany.
© Definicja iteratora mogącego pracować z obiektami stałymi. ammmmmmrnmwiw u — wwiin wwni m r - n i r in i m r t n iii
r
t i
1
"
1 '— ***^**4
Ten iterator sam jest zmienny - może pokazywać na to tu, to tam. Jednak . i zawsze to, na co pokazuje, traktuje jako „świętość” , której nie będzie modyfikował.
^
_
© U staw ienie iterato ra o nazw ie i t J a n tak, by pokazyw ał n a początkow y znak stringu E w _Jan. O Pętla, w której p rzesu w ać będziem y n asz iterator. P rzed k ażd y m obiegiem pętli sp raw d za się, czy aby iterator nie przebiegł już całości stringu. Dzieje się to dzięki p o ró w n an iu z rezultatem w yrażenia: Ew _Jan. e n d () | W yrażenie to w ykorzystuje istnienie funkcji składow ej ite ra to r
e n d ();
k tó ra, jako swój rezultat, zw raca w artość iterato ra pokazującego tu ż za ostatnim znakiem w danym obiekcie klasy s t r i n g .
I
M ów iąc o brazow o - gdyby w obiektach klasy s t r i n g na końcu za tekstem m usiał być z n a k nuli (bajt zerow y) - jak to jest w C -stringach - to om aw iana tu
592
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu funkcja e n d zw raca w łaśnie iterator pokazujący na to miejsce g d z ie , taki nuli b; się znajdow ał. K rótko mówiąc: na miejsce b ezp o śred n io za o statn im znakiem O tej funkcji en d porozm aw iam y bliżej w n astęp n y m parag rafie. W racając do naszej instrukq'i O - jest to spraw dzenie czy nasz iterato r nie d o ta rł ju ż czasem za ostatni m ożliw y znak stringu. © Instrukcja w ypisująca na ekranie p o k azy w an y zn ak - p o łączo n a z p rzesu n ię ciem g o na następną pozycję. Iterator jest z p rzy d o m k iem c o n s t , w ięc m o ż zn ak i odczytyw ać, ale zapisać nowej treści - nie!
W j
’. if ?
IV
- ł *'>
O
l * r" i 'v
ł .* r
v
" * * '* ł
1i
tl-
**"
- ,* ** • * .**>
Zobaczyliśm y tu więc, co zrobić, jeśli p rzy jd zie nam praco w ać ite ra to ra m i; o b iek tam i klasy s t r i n g m ającym i cechę c o n s t . Jak w id ać - ż a d e n p ro b lem jest d o tego o d p o w ied n i iterator.
11.23.2
Funkcje składow e klasy s t r i n g pracujące z iteratorami M ów ić tu będziem y o funkcjach składow ych klasy s t r i n g , w których miejsce w y k o n an ia danej akcji określane jest nie n u m e re m pozycji d a n e g o zn ak u , ale iterato rem pokazującym na w ybrane miejsce. P rzy p ie rw szy m czytaniu te k siążk i rad zę ten p arag raf zdecydow anie opuścić.
Funkcje: begin i end P ierw sze dw ie funkcje składow e pracujące z iteratoram i ju ż poznałeś. To funkcje: iterator
begin();
b e g i n - z w r a c a iterator (jakby: w sk aźn ik ) d o początkow ego z n a ku zap isan eg o w obiekcie klasy s t r i n g . iterator
end();
e n d - zw raca iterator (jakby: w sk aźn ik ) do tajem niczego miejsca znajdującego się bezp o śred n io za o statn im zn ak iem stringu.
I
P o n ie w a ż m asz ju ż d u że do św iad czen ie z C -strin g am i m o g ę uży ć takiego p o ró w n a n ia : w C -stringu za w łaściw ym ciągiem zn ak ó w jest bajt zerow y, czyli ta k z w a n y znak nuli. N a m iejsce gdzie byłby(\) ta k i zn ak nuli - pokazuje iterator z w ra c a n y przez funkcję e n d ( ) . O czy w iście w obiekcie klasy s t r i n g koniec tek stu nie jest sygnalizow any b ajtem zero w y m , ale to nie m a tutaj zn aczen ia - liczy się jakby sam a idea:
593
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
\ Za w ła ściw ym ciągiem znaków w ob ie kcie klasy s t r i n g je s t jakb y m iejsce, w ! 1 ■ ' /m m ożna by postawić ta b liczkę „Koniec W yścigu".____
W p rz y sz ło śc i, g d y poznasz klasy p o je m n ik ó w - docenisz, ż e taki p o m y sł o zn a czan ia k o ń c a je st b ard zo p rzy d atn y .
W 'o d tą d - d o tą d "
P o z n a m y za ch w ilę w iele takich fu n k cji składow ych k lasy s t r i n g , k tó ry ch a rg u m e n ta m i b ę d ą iteratory klasy s t r i n g . Itera to ry te s łu ż y ć będą nie tylko d o ok reślen ia jednego m iejsca w strin g u , ale często s p o tk a m y się z tym , że d w a iterato ry p o k azy w ać b ę d ą p ew ien o b szar „ o d t ą d - d o tą d " . W taki, n a p rz y k ła d , sposób m o ż e m y określić zn ak i, k tó re chcielibyśm y ze strin g u u s u n ą ć . Konkretnie: * Jeśli w d anym s t r i n g u jeden iterator p o k a z y w a ł na znak p ią ty , a d ru g i tak, by jed en asty , a n astęp n ie z a ż ą d a m y u su n ięcia z n a k ó w o b szaru - to znaki od p ią te g o d o dziesiąteg o
.Wf.-if
Tak: ty lk o d o d ziesiątego (a nie jed en asteg o ), bo...
u s ta w ia m y tak , by p o k a z y w a ł na zn ak z tak o k reślo n eg o z o sta n ą u su n ięte. ______________
.przy m etodzie „odtąd - dotąd", je s t taka umowa, że m iejsce pokazane jako „dotąd” je s t tym , przy którym należy się zatrzymać. ____________________
Jeśli żona mówi do męża: masz mi skopać grządki - od tej, aż do płotu, to znaczy, że płotu już kopać nie należy. M atem aty c y zap isalib y ten p rzed z iał jako: [odtąd, dotąd) Jak w id z isz , n aw ias z lewej jest k w a d ra to w y , natom iast z p raw ej o k rąg ły . Z a strz e ż e n ie : Tych w ersji funkcji z iteratorami nie będziem y stosować często.
Służą o n e raczej funkcjom bibliotecznym m ającym p raco w ać z o b ie k tam i k lasy s trin g . O w e funkcje biblioteczne, często p racujące z tak z w a n y m i pojemnikami, (w których iterato ry to b ardzo p rz y d a tn e n a rz ę d z ie ) chciałyby mieć iteratory ta k ż e w klasie s t r i n g , ab y strin g i tra k to w a ć podobnie jak in n e pojem niki.
594
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
P rzeg ląd
fu n k cji s k ł a d o w y c h
k la sy string,
k tó ry c h
a rg u m e n ta m i
sć
ite rato ry
R ozpoczniem y od konstruktora: string(iterator odtąd, iterator d o tą d ); K o nstruktor ten m ożna wykorzystać, g d y tw o rzy m y n ow y obiekt klasy string, a chcielibyśm y od razu inicjalizować go treścią stanow iącą fragm ent innego obiektu klasy string. O to p rzy k ład , w którym druga instrukcja po k azu je tu om aw ian y konstruktor. string stary("Ocalic od zapomnienia”); string nowy (stary. beginO , stary.begin () + 4);
// "Ocal"
Z o b a c z y m y t e r a z d a ls z e fu n k cje s k ła d o w e k la s y string
Id ę o zak ład , że - p atrząc poniżej na te ich deklaracje - sam potrafiłbyś pow ie dzieć, co robią i jakie jest znaczenie poszczególnych argum entów . II w ym azyw anie ---------
iterator iterator
erase (iterator adresjpozycji) ;
era se (iterator
odtąd,
iterator d o tą d ); f
II wstawianie-
iterator insert(iterator wsk_gdzie, const char z n a k ); W void insert (iterator wsk_gdzie, size_type ilej a z y , char znak) ; & void insert (iterator wsk_gdzie, © iterator skądjbrać, iterator d o tą d jira ć ); //zastępowanie -------iterator replace (iterator odtąd, iterator dotąd, const string & c o jo s ta w ić ); iterator replace (iterator odtąd, iterator dotąd, const char * c o jo sta w ić , size_type ile_znaków ); iterator replace (iterator odtąd, iterator dotąd, const char * c o jo s ta w ić ) ; iterator replace (iterator odtąd, iterator dotąd, size_type ile_znaków, const char znak) void replace (iterator odtąd, iterator dotąd, iterator skądjbrać, iterator dotąd_brać) ; //przypisanie ------string & assign(iterator odtąd, iterator d o tą d ); //dopisanie do końca-------string & append (iterator odtąd, iterator dotą d );
Komentarz kf‘ titTZr. : O U su n ięcie jednego znaku realizuje nam funkcja sk ład o w a iterator
erase (iterator k tó ry);
0
595
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu F u n k c ja ta p o w o d u je , ż e z o b ie k tu k la s y
string u s u n ię t y
z o s ta je z n a k p o k a z a
n y i te ra to re m . W r e z u lta c ie f u n k c ja ta z w r a c a i t e r a t o r (ja k b y w s k a ź n ik ) d o z n a k u n a s tę p n e g o z a ty m w y m a z a n y m . . . . . N ie potrzebuję chyba przypom inać, że po w ym azaniu s t r i n g stał się o jeden znak krótszy i znaki nieco się przesunęły.
© U s u w a n ie z n a k ó w z z a d a n e g o p r z e d z ia łu .
iterator erase (iterator
o d tą d ,
iterator
d o tą d );
F u nkcja ta s p r a w i a , ż e z d a n e g o s t r i n g u u s u n ię te są z n a k i z a w a r t e w p e w n y m p r z e d z ia le . ♦♦♦ P ie r w s z y a r g u m e n t (odtąd) s ł u ż y d o w s k a z a n ia , o d k tó r e g o z n a k u n a le ż y r o z p o c z ą ć u s u w a n ie . ♦♦♦ D r u g i a r g u m e n t (dotąd) s łu ż y d o w s k a z a n ia , p r z y k tó r y m z n a k u n a le ż y p r o c e s u s u w a n i a p o p r z e s ta ć . Z n a k p o k a z y w a n y ite r a to r e m dotąd - m a b y ć p ie r w s z y m n ie -u s u w a n y m z n a k ie m . R e z u lta te m p r a c y te j fu n k c ji s k ła d o w e j je s t ite r a to r ( ja k b y w s k a ź n ik ) d o p ie rw s z e g o z n a k u p o ty c h s k a s o w a n y c h . J e ś li k a s o w a lib y ś m y z n a k i a ż d o k o ń ca s tr in g u , w ó w c z a s fu n k c ja z w r a c a w a r to ś ć s t r i n g : : e n d ( ) , czy li ite ra to r w s k a z u ją c y n a m ie js c e b e z p o ś r e d n io z a o s ta tn im z n a k ie m s tr in g u .
© F u n k c ją
iterator insert (iterator
const char
g d z ie ,
zn a k ),
p o s łu g u je m y s ię , g d y c h c ie lib y śm y , w m ie jsc e p o k a z y w a n e ite r a to r e m (gdzie), w s ta w ić je d e n
z n a k .
J a k o r e z u l t a t f u n k c ja z w ra c a ite r a to r d o te g o n o w o w s ta w io n e g o z n a k u . ■
, .
• . • f,i ' . ’ •
© F u n k c ją 1
void
in sert
■ i- •
(iterator
”
g d z ie ,
■
size_type
d e jr a z y
, char
zn a k );
p o s łu g u je m y s ię , g d y c h c ie lib y śm y , w m ie jsc e p o k a z y w a n e ite r a to r e m , w s ta w ić te k s t s k ła d a ją c y s ię z w ie lo k ro tn e g o p o w tó r z e n ia je d n e g o z n a k u . K ro tn o ść teg o p o w tó r z e n i a - o k r e ś la m y z a p o m o c ą d r u g ie g o a r g u m e n tu .
© F u n k c ja :
596
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
void insert (iterator g d z i e , iterator o d tą d jo r a ć , iterator d o t ą d J b r a ć ) ; D zięki niej, m o żem y d o naszego stringu, w m iejsce p o k azy w an e iteratorem , w staw ić zn ak i z (frag m en tu ) jakiegoś innego o b iek tu klasy s t r i n g . A
Y A © Jeśli w obiekcie k lasy string chciałbyś p ew ien frag m en t, p o k azy w an y iteratoram i (m eto d ą "o d tąd -d o tąd "), w ym ienić na strin g , będący treścią in n eg o obiek tu k lasy string, to słu ży do tego funkcja:
iterator replace ( iterator o d t ą d , iterator d o t ą d , const string & c o _ w s t a w i ć ) ; ♦> D w a p ierw sze a rg u m e n ty służą do p o k a z a n ia (iteratoram i), k tóry frag m e n t n aszeg o stringu chcielibyśm y w y m ien ić. ♦> T rzeci a rg u m e n t ( co_wstawić), to obiekt k lasy string zaw ierający ciąg zn ak ó w , k tó re chcielibyśm y um ieścić w n aszy m strin g u (w m iejsce starych).
Przykład:
string decyzja("Otworzyć komory próżniowe"); string zaw("zawory"); hłfiftf: v-f:,r
O*??' .u
•«.•
ojj..’" :
string::iterator odtąd = decyzja.begin () + 9; string::iterator dotąd = odtąd + 6; . decyzja,r e p la c e (odtąd, dotąd, zaw); //" O t w o r z y ć z a w o r y p r ó ż n i o w e "
O Jeśli w obiekcie klasy string chciałbyś p ew ien frag m e n t p o k a z y w a n y iterato ra m i (m eto d ą "o d tąd -d o tąd ") w ym ienić na tek st, b ęd ący frag m en tem jakiegoś C -strin g u - to słu ży d o teg o funkcja
iterator replace ( iterator odtąd, iterator dotąd, const char * co_wstawić, size_type
ilejznakćw) ;
<♦ D w a p ierw sze a rg u m e n ty służą do p o k a z a n ia (iteratoram i), k tóry frag m e n t naszego strin g u chcielibyśm y w y m ien ić. <► T rzeci a rg u m e n t (cojwstawić), to w sk a ź n ik d o C -strin g u zaw ierającego ciąg znaków , k tó re chcielibyśm y u m ieścić w n aszy m strin g u (w miejsce starych). «$♦ C z w a rty - określa ile początkow ych z n a k ó w z C -strin g u m a stanow ić te n „n o w y " fra g m e n t tekstu.
Przykład:
string scyntyl("Pomiar trajektorii przelotu");
597
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
string::iterator odt = scyntyl.begin{) + 7; >V-rJV\ string::iterator dot = odt + 11; s c y n t y l . replace (odt, dot, "czasu martwego", 5) ; //w r e z u l t a c i e o b i e k t scyntyl n u t t e r a z t r e ś ć : " Pomiar czasu przelotu"
i w obiekcie k la sy s t r i n g chciałbyś p e w ie n fra g m e n t p o k a z y w a n y iteratora m i (m eto d ą " o d tą d -d o tą d " ) w y m ie n ić na string, b ęd ąc y całością jakiegoś C -strin g u - to s łu ż y d o tego funkcja
iterator replace ( iterator o d t ą d , iterator const char * c o j w s t a w i ć ) ;
d o tą d ,
Jest to
jakby p ro s ts z a form a funkcji o m ó w io n e j pow yżej. T y m ra z e m z C -stringu b ra n e są w sz y stk ie z n a k i (a nie tylko k ilk a pierw szych). P rzy p o m n ijm y jed n ak in extenso: <♦ D w a p ie rw s z e a rg u m e n ty słu ż ą d o p o k aza n ia (iterato ram i), który frag m e n t n a s z e g o strin g u chcielib y śm y w ym ienić.
i!>ł
jich
Przykład:
♦♦♦ T rzeci a r g u m e n t (cojwstawić ), to w sk a ź n ik d o C -strin g u zaw ierającego ciąg z n a k ó w , k tóre chcielibyśm y um ieścić w n a sz y m s trin g u (w miejsce stary ch ). -
( b fiM y S R
string adc ("Pomiar nachylenia przemiennika"); string::iterator tu = adc.begin() + 7; string::iterator tam * tu + 10; adc.r e p l a c e (tu, tam, "rozdzielczości"); // adc m a t e r a z t r e ś ć : "Pomiar rozdzielczości przemiennika" 4
© Jeśli w obiekcie k la s y s t r i n g chciałbyś p e w ie n frag m en t p o k a z y w a n y iterato ram i (m eto d ą " o d tą d -d o tą d ") w y m ien ić n a string, sk ład ający się z w ielokrot nego p o w tó rz e n ia tego sam eg o z n a k u , to słu ży d o tego funkcja:
iterator replace (iterator o d t ą d , iterator d o t ą d , size_type i l e _ z n a k ó w , const char z n a k ) ; -
- , >
•
| .
.
.
.
f
"
•
.
j
-
.
:
»
’
,
♦♦♦ D w a p ie rw s z e arg u m e n ty słu ż ą d o p o k azan ia (iterato ram i), który frag m e n t n a s z e g o strin g u chcielibyśm y w ym ienić, <♦ ilejznakóu? - ten a rg u m e n t o k re śla m y krotność p o w tó rz e n ia jednego znaku, , ♦♦♦ znak - ty m arg u m e n tem o k re śla m y jaki to zn ak m a b y ć w ielokrotnie p o w tó rz o n y
598
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Iterato ry stringu
Przykład: string tof("sci21 do sci42"); string::iterator string::iterator
itl = tof.begin() + it2 = itl + 2 ;
tof •replace (itl, it2,
10,
6;
'>'); //"sci21 » » » » »
scj42"
© Jeśli w o b iekcie k la sy string chciałbyś p e w ie n fra g m e n t p o k a z y w a n y ite ra to ra m i (m eto d ą " o d tąd -d o tąd ") w y m ien ić na strin g , b ęd ący frag m en tem in n eg o obiektu klasy string, to służy do te g o funkcja: void replace (iterator odtąd, iterator dotąd, iterator skądjbrać, iterator d o tą d jn a ć ); ❖
P ierw sze d w a a rg u m e n ty określają w n aszy m strin g u "stary" frag m en t, (k tó ry chcielibyśm y w ym ienić na "nowy"). A rg u m e n ta m i trzecim i czw artym o k re śla m y w innym ob iek cie k lasy string ten frag m en t, k tóry m a być u ż y ty jako "nowy".
Przykład: string ilocz("Iloczyn bramek jednowymiarowych"); string::iterator string::iterator
itA = ilocz.begin() + itB = itA + 6;
8;
string zest("zestaw warunków koniecznych"); string::iterator string::iterator
itX = zest.begin() + 7; itY = itX + 8;
ilocz.rep la c e (itA, itB, itX, itY); //treścią ilocz je st teraz: "Iloczyn warunków jednowymiarowych"
X4t. rf :
J.ł_.-
O O Jeśli m a s z jakiś in n y strin g i chciałbyś jego fra g m e n t (p o k a z y w a n y iterato ra m i m e to d ą " o d tą d -d o tą d ") p rz y p isa ć do sw ojego o b ie k tu klasy string, to słu ż y --/a d o te g o funkcja string & assign (iterator odtąd, iterator d o tą d ); <♦ A rg u m e n ta m i są tu d w a iteratory, k tó ry m i p o k a z u je m y n a w y b ran y fra g m e n t in n eg o strin g u . T ak o k reślo n y fra g m e n t p rz y p is a n y zo stan ie d o naszeg o obiektu k lasy string.
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pamięć zew nętrzna” program isty
599
s t r i n g a k ty w n e ( " — " ) ; s t r i n g w s z y s t k i e ( "mw21, mw22, mw31 mw41, m w42");
U ---- iteratory mogą pokazywać na fragment innego obiektu klasy string s trin g : : ite r a to r s trin g :: ite ra to r
i t l = w s z y s t k i e . b e g i n () + 12; it2 = i t l + 4 ;
a k ty w n e .a s s i g n ( i t l ,
it2 );
// efekt przypisania: "mw31"
O © D ołączanie d o n aszeg o stringu - fragm entu innego stringu. s t r i n g & ap p en d ( i t e r a t o r odtąd, i t e r a t o r dotąd) ; Jeśli m asz jakiś in n y s t r i n g i chciałbyś jego fragm ent (p o k azy w an y iteratoram i m eto d ą "o d tąd -d o tąd ") dopisać do końca swojego obiektu k lasy string, to m ożesz posłużyć się w łaśnie tą form ą funkcji. P rz y k ła d ...
byłby niem al id en ty czn y , jak pow yżej p rz y funkcji a s s i g n . Jed y n a różnica to efekt końcow y. To znaczy jeśli w po w y ższy m p rzy k ład zie funkcję assign zam ienim y na append, obiekt aktywne zaw ierał będzie ostatecznie taką treść "-- mw31" Jak w idać, tu (zam iast zastąpienia całego te k stu ) nastąpiło d o p isan ie do końca.
W
■
O b ie c a łe m p rz y p o m n ie ć , ż e ...
...jeśli interesuje Cię porów nyw anie stringów w edług p o rząd k u alfabetycznego nieczułe na w ielkość liter - i jeśli chciałbyś zobaczyć, jak m o żn a to robić posłu gując się funkcją-algorytm em o n azw ie trans fo rm , to m ożesz teraz wrócić na stro n ę 568 (to część § 11.17). Teraz, g d y rozum iesz istotę iteratorów , przeczy tan ie tego fragm entu przyjdzie Ci z łatwością.
11.24 Bryk - czyli "pamięć zewnętrzna" programisty N a zak ończenie - tabela, w której zestaw ione są najw ażniejsze form y funkcji składow ych klasy s t r i n g . Taka "ściąga" m oże Ci się p rzy d ać w codziennej p ra ktyce, bo przecież tru d n o pam iętać te w szystkie argum enty. P odejrzew am , że najczęściej tu w łaśnie będziesz zaglądał, a d o p ie ro w razie niejasności, lu b b ard zo w yjątkowego problem u - zajrzysz d o odnośnego parag rafu .
600
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pam ięć zew nętrzna” program isty
P u p c i e s k ła d o w e k lasy s t r i n a O p is
F u n k c ja
Pusty string s t r i n g n ap isA ;
!*
Inicjalizacja C-stringiem s t r i n g n a p i s B l ( " J a k iś t e k s t " ) ; Iniqalizacja tablicą znaków c h a r t a b l i c a [20] = {"N atenczas W o jsk i" }; s tr in g n a p is B 2 (ta b lic a ); Iniqalizacja fragmentem (powyższej) tablicy znaków s t r i n g wiadom ość U t a b l i c a [ 5 ] ) ; // "czas Wojski" ■' ■ Inicjalizacja fragmentem C-stringu s t r i n g o s t r z e ż e n i e ( "Awaria s t u d n i " , ininqalzacja
8 ) ; / / "Awaria s"
konstruktory Inicjalizacja jednym znakiem s t r i n g j e d e n (1, ' m ' ) ; zauważ, że poniższa forma jest niedozwolona: s trin g j j j ( ' m ' ) ; //b łcfd !!!
■ •
Inicjalizacja 25-krotnie powtórzonym tym samym znakiem: s t r i n g g w ia z d k i(25, ' * ' ) ; Inicjalizacja innym obiektem klasy s t r i n g s t r i n g in n y = "ABCDEFGH"; s trin g nnn(inny);
■-
. H Inicjalizacja fragmentem innego obiektu klasy s t r i n g . s t r i n g nowy ( i n n y , 4, 2) ; / / rezultat: "EF" -- - — ---•---- .----------- ■----- -------- r—---- , — -....... - ------ — li ! s i z e ty p e S t i n g : : s i z e ( ) ; s i z e ty p e S t i n g : : l e n g t h ( ) ; Obie te funkcje składowe - robią dokładnie to samo: pozwalają dowiedzieć się z ilu znaków składa się przechowywany w danym obiekcie tekst.
•-
Długość stringu s i z e , le n g th
|
f 1 1i '-.-yQi Vc : ■
d lu g o s c = ś p i e w a k . s i z e <); d lu g o s c = ś p i e w a k .l e n g t h {) ;
Oba powyższe wywołania funkcji zwracają wartość 11
; i - ‘^
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pamięć zew nętrzna” program isty
601
F u n k c j e s k ł a d o w e k l a s y s t r i n g __ O p is
bool string::empty() ; Funkcja zwraca wartość t r u ć jeśli dany obiekt klasy string jest pusty.
empty s t r in g n ic ; i f (nic.empty( ) ) cout << "Ten s tr in g j e s t p u s ty . ;
c a p a c ity
size_type capacity O; Funkcja ta pozwala dowiedzieć się jak duża jest bieżąca rezerwacja miejsca na przechowywanie ewentualnego tekstu w obiekcie klasy string. (Jak duży jest p o j e m n i k ) . __________________________ ’QWERTYU"; s t r in g a s i z e typ e pojemność = a . capacity(); void reserve(size_type ile); Funkcja ta sprawia, że obiekt klasy string może zrobić rezerwację pamięci przeznaczonej na ewentualny przyszły tekst.
r ese rv e
r e s iz e
s t r in g poemat; poem at. reserve(1 0 0 ) ; Pusty jeszcze obiekt klasy s tr in g rezerwuje tu miejsce na spodziewane 100 znaków. void resize (sizetype i l e , char z n a k = ’\0'); Funkcja ta powoduje zmianę długości stringu do długości określonej argumentem ile . W zależności od bieżącej długości stringu oraz wartości argumentu ile - nas tąpi skrócenie lub wydłużenie stringu. Skrócenie polega na drastycznym odcięciu pozostałej części. Wydłużenie polega na dopisaniu odpowiedniej krotności znaku dostarczo nego jako drugi argument. (Wartość domniemana tego znaku to bajt zero wy, czyli znak n u l i ) . _______________________ _
s t r in g p e le t o n ("01234567") ; p e le t o n . resize ( 2 0 , ' k 1 ) ; // r e z u l t a t :
" 0 1 2 3 4 5 6 7 kkkkkkkkkkkk'
void elear(); Kasuje dotychczasową treść obiektu. Od tej pory string jest pusty.
e le a r
s t r in g dyrygent("Willi B oskovsky"); d y r y g e n t. elear (); // r e z u l t a t :
602
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pam ięć zew nętrzna” program isty .......
~
.. -
h
F u n k c je s k ła d o w e k lasy strinq
Funkcja
O p is
___
refercncjn_do_znnku at(size type pozycja); Umożliwia odniesienie się w danym stringu do znaku wybranego argumen tem pozycja. W razie błędnego określenia pozycji - funkcja rzuca wyjątek out of rangę.
at
1-------------
string wyd("Kallimach"); cout << w y d . a t ( l ) << endl; //odczytanie znaku Ca') wyd. a t (5) = ’c ’; //przypisanie nowej zawartości
referencjajiojznaku operator!) (size type pozycja); To samo, co powyżej, ale - w razie nieprawidłowego określenia pozycji nie ma żadnej sygnalizacji błędu. operator []
-
-
-
-
string wyd("Kallimach"); cout << wyd[l] << endl; // odczytanie znaku wyd [ 0] = 1B 1 ; // przypisanie nowej zawartości
1 ►
substr
string substr(size_type p, size type n); Funkcja pozwala z danego stringu wydobyć pod-string zaczynający się od znaku na pozycji p i mający długość n. Wydobyty substring jest rezultatem tej funkcji (jako obiekt typu string zwracany przez wartość). string nazwa_pliku("detektor A12.txt"); string sym bol; symbol = nazwa_pliku. s u b s t r (9 , 3); H efekt: "Al2" size_type find(const string & ss, size typep = 0); size_type find (char zz, size type pp = 0 ) ; size_type rfind(const string & ss, size type pp = npos) ; size_type rfind(char zz, size type pp = npos); Funkcje te służą do poszukiwania zadanago znaku zz (lub stringu ss) w danym stringu. Poszukiwanie rozpoczyna się od pozycji pp. Jeśli funkcje zwrócą wartość string:;npos - to znaczy, że poszukiwanie się nie udało. Jeśli zaś poszukiwanie się udało - funkcje zwracają numer pozycji, na której znaleziony został znak zz (lub początek stringu ss). Funkcje find poszukują począwszy od pozycji pp w stronę końca stringu, a funkcje rfind w stronę początku.
s
find rfind
1
____
____
string liścik("Trzeba wykonać zamach prawa ręka"); string: :size_type pozycja = liścik.find("zamach"); if(pozycja != string::npos) cout << "Nie znalezione !" << endl; else cout << "Znalezione, pozycja= " « pozycja << endl;
603
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pamięć zew nętrzna” program isty
F u n k c je s k ła d o w e k lasy s tr in g L .
.
. ......._
Opis
Funkcja
size type find first offconst string &
w,
....
___ size_type p = 0 );
size type find first not oficonst string &w, size_type
p
= 0 );
size type find last of(const string & w , size_type p = npos); size type find last not offconst string•&w
find_first_of find_first_not_of find_last_of find_last_not_of
,
size_type
p =
npos);
Poszukiwanie w danym stringu dowolnego znaku z (...of...) lub spoza (...not o f ..) załączonego zestawu w (pierwszy argument). Poszukiwanie zaczyna się od pozycji wskazanej drugim argumentem p . Funkcje ...first... szukają od tej pozycji w stronę końca stringu, zaś funkcje ...last... w stronę początku stringu. Rezultatem jest albo numer znalezionej pozycji, lub wartość string: :npo s - jeśli poszukiwanie się nie powiodło.
3tring
zdanie("Wszyscy spia"); string::size type pozycja; pozycja = zdanie.f i n d _ f i r s t _ o f ("aoeiouAOEIOU"); if(pozycja != string::npos) cout << "Łacińska samogłoska na pozycji " << pozycja « endl; else _ cout << "Nie znaleziono samogłosek" « endl; string& erase (size_type p = 0 , size_type ile = npos); Kasowanie znaków w stringu począwszy od pozycji p Kasowanych zostaje ile znaków, lub wszystkie do końca, gdy ile jest większe niż ich liczba. Wartość domniemana drugiego argumentu oznacza: wszystkie do końca . erase string m ( "abcdefgh") ; m . e r a se ( 1 , 4 ) ; // r e z u l t a t : m. e r a se ( ) ; II r e z u l t a t :
" a fg h " ""
604
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pam ięć zew nętrzna” program isty
n
F u n k c je sk ła d o w e k la sv s t r i n a
F u n k c ja -----1----------——■
O pis
s tr in g & in s e r t ( s iz e typ e p l , con st s tr in g suw, s i z e typ e p w , s iz e _ ty p e n w ) ; Wstawienie do środka stringu, począwszy od pozycji p l takiego fragment stringu w w , który zaczyna sie od pozycji p i v i ma długość n w znaków. W rezultacie dotychczasowe znaki rozsuną się, robiąc miejsce tym nowym.
1
1
_
in s e r t
1
s tr in g u r z ą d z e n ie ("detektor en ergia" ); s tr in g segm ent( "6A3") ; urządzenie . in s e r t ( 8 , segment, 1, 2 ); //
replace
" d e te k to r A 3 _ c n c r g ia
string& rep la ce (s iz e type o d k q d j z a s t ę p o w a ć , s iz e type i l c _ z a s t q p i ć , const s tr in g i c o j w s t a w i ć , s iz e type o d k q d _ b r a ć , s iz e type i l e j z n a k ć w ) ; Funkcja ta powoduje zastąpienie fragmentu stringu przez jakiś inny tekst. Fragment "stary" określamy pozycją o d k q d _ z a s t q p i ć oraz liczbą i l e _ z a s t q p i ć . "Nowy" fragment pochodzi z innego stringu (przysłanego jako trzeci argument - c o j w s t a w i ć ) . Fragment "nowy" określa argument o d k q d _ b r a ć oraz liczba il c _ z n a k ó w . r-*- ----------------------------------------s tr in g moj("abcdefgh") ; s tr in g c y f r y ("0123456789"); moj . r ep la ce (3, 2, c y fr y , 4, 5); / / "abc45678fgh' const char* c _ s t r ( ) ; Udostępnia treść obiektu klasy s t r in g w postaci C-stringu. #inclu de using namespace std ;
c s tr
in t grupa = 5; // z a p o m o c ą k l a s y s t r i n g b u d u j e m y n a z w ę s tr in g nazwa p li k u (" m in ib a ll "); nazwa p lik u += char('A ' + grupa); nazwa p lik u += ".parametry"; // " m i n i b a l l _ F . p a r a m e t r y ' ifstr e a m plik(nazw a p lik u .c s t r ( ) } ;// o t w i e r a n i e
|
data
p lik u o t e j n a z w ie
con st char* d a t a ( ) ; Funkcja ta udostępnia adres tablicy, w której obiekt klasy s t r i n g obecnie przechowuje powierzony mu tekst. AJ
codziennej praktyce nie ma powodów, by używać tej funkcji
605
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pamięć zew nętrzna” program isty 7
,. .
11
j
.■- - ----------—— ............... ■ '
'
F u n k c je s k ła d o w e k lasy string O p is
Funkcja
_____
___
_________________ i
int compare (size typcp o z , size_type i l e _ z n , const string& i n n y ); Funkcja ta porównuje (wg. porządku alfabetycznego) fragment danego stringu z i n n y m stringiem. Fragment określany jest przez pozycję p o z i ilość znaków i l e _ z n . Porównanie następuje z całością stringu i n n y , przysłanego jako trzeci argument. Rezultatem porównania jest: - 1 gdy dany string wg. porz. alfabet, następuje wcześniej niż string i n n y 0 gdy dany string jest identyczny jak string i n n y + 1 gdy dany string wg. porz. alfabet, następuje później niż i n n y . Porównywanie to rozróżnia wielkie i małe litery. Uwaga: Jeśli chcemy porównywać całe stringi, lepiej posłużyć się wygodnymi operatorami ==, !=, <, > itd.
compare
string fizyk("Prof. A. Hrynkiewicz"); string inny ( "Mottelson") ; switch( fizyk.compare(9 , string::npos, inny) ) { case -1 : cout << "poprzedza" << endl; break; case 0 : cout « "identyczny" << endl; break; case +1 : cout << "następuję po" << endl; break; 1
size type copy (char * w s k _ t n b l i c y _ z n a k ó w , size_type ile , size type o d _ p o z y c j i = 0 ) ; Kopiuje fragment zawartości obiektu klasy string do zwykłej tablicy zna ków. Argument il e określa ile znaków ma zostać skopiowanych, argument o d _ p o z y c j i określa pozycję w obiekcie klasy string skąd ma zacząć się ich pobieranie.
copy
-
char tablica( 100] = { "habent+sua*fata*libelli" string przepis ( "Olejek bezwonny lej przez lejek, przepis.copy(tablica, 15, 1); / / w rezultacie treść tablicy: "lejek bezwonny *libelli"
};
void swap(string & inny_string); Powoduje, że dwa obiekty klasy string wymieniają się zawartościami. swap
a ssig n
string ofiara ( "Desdemona" ) ; string bestia("Otello") ; ofiara.swap(bestia) ;
Przypisanie nowej treści do obiektu klasy string. Zamiast używać funkcji assign, proponuję posługiwać się operatorem ’ = ' (Gdyby chodziło o przypisanie f r a g m e n t u innego stringu - posłużyć można się dodatkowo funkcją substr).
606
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pam ięć zew nętrzna” program isty
-■............ .............. ...... t--------- -
:-------- "— y—*—■ '
F u n k c je s k ła d o w e k lasy s t r i n a
; F u n kcja
_ _
O pis
Dołączanie jakiejś treści do końca istniejącej zawartości stringu. Zamiast używać funkci append, proponuję posługiwać się operatorem ' +=' (Gdyby chodziło o dołączenie f r a g m e n t u innego stringu - posłużyć można się dodatkowo funkcją substr).
append
|J“
iterator begin(); zwraca iterator do początkowego znaku zapisanego w obiekcie klasy string. iterator end ( ); zwraca iterator do miejsca znajdującego się bezpośrednio za ostatnim znakiem stringu.
begin end
O b i e f u n k c j e u ż y w a n e s q r z a d k o , n a jc z ę ś c ie j p r z y w y w o ł a n i u a l g o r y t m u
transform.
1
F u n k c ie to w a rz y s z ą c e k la sie F unkcja
O pis
i--------- -----
istream sgetline (istream w e j , string s c h o w e k , char o g r a n i c z n i k = '\ n '); Funkcja getline umożliwia nam wczytać (z klawiatury, czy z pliku dyskowego) nie jeden wyraz, ale nawet bardzo długi, wielowyrazowy tekst.
;
getline
j
strinq
Pierwszym argumentem ( w e j ) określamy skąd będą pochodzić wczytywane znaki. Drugi argument to nazwa obiektu klasy string, w którym funkcja ma umieścić nam wczytany tekst. (Może to być obiekt jeszcze pusty). Trzeci argument - to o g r a n i c z n i k - znak, który wybieramy jako oznaczający koniec wczytywania tekstu. Na jego widok funkcja zakończy wczytywanie, umieści dotychczasowe znaki w obiekcie s c h o w e k , a ogranicznik - wyrzuci. Domniemana wartość ogranicznika, to znak nowej linii (klawisz Enter). U w a g a : fu n k c ja ta u ż y t a p o w c z e ś n ie js z y m w c z y t y w a n i u o p e ra to re m w c z y t a z e s tr u m ie n ia z n a k n o w e j lin ii, k tó r e g o o p e r a to r
»
»
n a jp ie r w
z e s tr u m ie n ia n ie
u s u w a ! (P a tr z s tr . 5 8 1 ) .
i
string miasto; cout << "Napisz dowolnie długi tekst, a kon iec t e k s tu " "oznacz znakiem 'v'" « endl; g e t l i n e ( c i n , m ia sto , 'v ') ; j //p o w p i s a n i u n a k l a w i a t u r z e n a p r z y k ł a d : Byl i jest i wieki sławiony v //w o b ie k c i e m i a s t o z n a j d z i e s i ę 'Byl i jest i wieki sławiony "
607
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i B ryk - czyli „pamięć zew nętrzna” program isty
i .|
■ ..
....... F u n k c je to w a r z y s z ą c e k lasie
string
iterator
algorytm transform
transform(iterator s k ą d J a r a ć , iteratord o k ą d _ b r a ć , i terator g d z i e _ w k ł a d n ć , operacja n a z w a _ f u n k c j i _ k o n w e r t u j q c e j ) ; Nie jest to funkcja składowa klasy string. Przydaje się w sytuacji, gdy na każdym elemencie (znaku) chcemy wykonać jakąś operację. Na przykład zamienić litery wielkie na małe - co widzimy poniżej: #include string napis("ABCdefGHI"); transform ( n a p is .b e g in ( ) , n a p is .e n d ( ) , n a p is .b egin () , to lo w er) ;
//
a b c d c fg h i
W O p e r a to r y p r a c u j ą c e z o b ie k ta m i k la s y string. O pis
O p e ra to r
operator »
_
Umożliwia wczytanie z klawiatury (lub pliku) jednego wyrazu. Białe znaki, poprzedzające wyraz, są ze strumienia wyjmowane i wyrzucane, dopiero nie-biale znaki umieszczane są w obiekcie klasy string. Znaki są wczytywane aż do napotkania pierwszego białego znaku. Nie wczytane białe znaki, w tym znak nowej linii (klawisz Enter) pozostają w strumieniu czekając na ewentualną następną operację wyczytywania. string wyraz; c in >> wyraz;
operator <<
Umożliwia wypisanie na ekranie (lub umieszczenie w tekstowym) pliku dyskowym całej zawartości danego obiektu klasy string. Jeśli przez przypadek gdzieś w środku, w tym obiekcie, umieszczony jest bajt zerowy - n u l i (bardzo rzadka praktyka), to operator zatrzyma wypisywanie, gdy się na niego natknie. string hamletCByc albo nie byc"); cout« hamlet; Operator ten pozwala dopisać do końca istniejącej treści stringu jakiś dalszy tekst. string żakiecie("Abra"); żakiecie += "kadabra"; string b ("0123456789"); żakiecie += substr(b, 2, 3);
i+=
—
// "Abrakadabra" // "Abrakadabra234"
608
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia
O p e r a t o r y p r a c u j ą c e z o b i e k t a m i k l a s y stri n g . i --------------
O p e ra to r
O p is
Dodanie do siebie zawartości dwóch stringów |
s tr in g raz ("Dur.can” ); s tr in g dwa("B lanco") ; s tr in g tr z y = raz + " i " + dwa;
// "Duncan i Blanco"
Opisu szukaj w pierwszej tabeli, tuż za opisem funkcji składowej a t
operator [] ■
Pozwala do stringu przypisać treść innego stringu, lub treść jakiegoś C-stringu, lub pojedynczy znak. s tr in g stary(" M ieszk o" ) ; s tr in g nowy; nowy = s ta r y ; nowy = "to j e s t C -string"; nowy = 'x'; nowy = s t a r y .s u b s t r (3, 4);
operator =
~~r •=/
^=/
// "szko"
Operatory te służą do porównywania stringów według porządku alfabetycznego. (Odróżniają wielkie i małe litery). Zwracają wartość true/false (prawda/fałsz) zależnie od tego czy dana relacja zachodzi czy nie. Co najmniej po jednej stronie operatora powinien stać obiekt klasy s t r i n g .
>=
s t r in g s l ("raz "); if("dwa" == s l ) cout << "identyczne" << en d l; e l s e cout << "rożne" << endl;
________________________________________________ ___ ____________
11.25 Ćwiczenia W danym programie C++ włączyłeś nagłówek #include < strin g>
a poniżej zamieściłeś definicje s t r in g n; W trakcie kompilacji kompilator wskazuje na te linie definicji mówiąc, że nie zna nazwy s t r in g . Co należy zrobić? Podaj co najmniej dwa rozwiązania. Czy plik nagłówkowy < str in g > to nowsza wersja pliku < s tr in g . h>? Czy można je stosować wymiennie? Dlaczego? Co to jest C-string? W jakim obiekcie sie go przechowuje?
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia
609
Które z poniższych operatorów NIE można zastosować wobec obiektów klasy strin g ? +, +=, =, ==, != <, >, >= <=, !, / , A, &*, fi, « , » / *# Która z poniższych definicji jest poprawna? s t r in g a ("123"); s t r in g b ( 'X ') ; s t r in g c = "XYZ"; Czy jeśli tekst w obiekcie klasy s tr in g przesyłamy do funkcji v o id f ( s t r in g ) ; to czy pracuje ona na oryginale? Jeśli TAK, to co zmienić w deklaracji, by zabezpieczyć sie przed zniszczeniem treści stringu przez funkcje7 Jeśli NIE, to co zmienić w deklaracji, by funkcja mogła dokonywać zmian w oryginal nym stringu? Użytkownik programu powinien z klawiatury wpisać do programu wyraz. Aby to było możliwe w programie należy przygotować na ten cel obiekt klasy string. Co zrobić jeśli spodziewamy sie, że wyraz bydzie długi? (Nie wiecej jednak niż 50 znaków). W jakimś obiekcie typu char znajduje sie znak. Napisz definicje obiektu klasy s t ring, który od razu inicjalizowany bedzie zawartością tego obiektu char. Które z poniższych definicji są niepoprawne? in t m = 4; in t p = 6 ; s iz e _ t y p e a =m; s iz e _ ty p e b =m + p; s iz e _ ty p e c =m - p; s iz e _ ty p e d =p - m; s iz e _ ty p e e =m * p; Co oznaczają rezultaty funkcji s iz e i funkcji length . Jaka jest miedzy nimi różnica? Jeśli wywołana na rzecz jakiegoś obiektu klasy s t r in g funkcja length zwróciła war tość 25, to jaką wartość zwróciłaby funkcja empty? W danym obiekcie klasy s t r in g jest tekst „Biosynteza białka". Jeśli na rzecz tego obiektu wywołamy funkcje m ax_size - jak należy rozumieć zwracaną przez nią wartość? Jaka jest różnica miedzy funkcjami max_s i ze i capac i t y. Jak należy rozumieć zwraca ne przez nich wartości. Do końca da nego obiektu klasy s tr in g zamierzamy właśnie operatorem += dopisać 500 znaków. Czy należy wcześniej zarezerwować na nie miejsce funkcją reserve? Wyjaśnij, jaka jest różnica miedzy działaniem funkcji reserv e, a funkcji r e s iz e . Co może powiększyć każda z nich? C o zrob ić jeśli w Twojej realizacji biblioteki standardowej niema w klasie s t r in g funkcji składowej e lea r . Jak najprościej można wykonać te samą operacje? (Podaj co najmniej dwa sposoby). Mamy taki fragment programu. s t r in g s t a r ( ”co i jak"); char znak;
610
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia znak = star[25000]; znak = star.at(25000); cout << "dlugosc stringu = " << star.length();
Jak zachowa się program w obu przypadkach przypisywania do obiektu znak:’ Jaką funkcją można wydobyćz danego obiektu klasy s tr in g fragment zaczynający się od znaku N i mający długość Z znaków? Mamy jakiś obiekt klasy string, w którym jest tekst zawierający kilkakrotnie znak . (kropka). Jaką funkcją można najprościej zlokalizować ostatnie wystąpienie tej kropki? Jeśli funkcje z rodziny find odpowiadają wartością string::npos - co to oznacza? Jest kilka funkcji składowych klasy string, których jednym z argumentów jest liczba znaków, na których mają wykonać daną pracę. Jeśli jako ten argument wyślemy im wartość string: : npos - co to oznacza? Jeśli funkcjom składowym z rodziny find wskazujemy gdzieś w środku stringu miejsce skąd należy rozpocząć poszukiwanie - w którą stronę zaczyna się poszukiwanie w przypadku poniższych funkcji? a) b) c) d)
find_first_of find_first_not_of find_last_of find_last_not_of
W funkcjach składowych klasy s tr in g często argumentami są: - pozycja określająca numer znaku, od którego funkcja ma zacząć pracę - liczba znaków, które mają zostać poddane operacji. Możliwe, że programista korzystający z tych funkcji poda niewłaściwe wartości argu mentów. To znaczy albo poda niewłaściwy numer pozycji (gdzieś daleko poza końcem stringu), lub poda za dużą liczbę znaków (string jest krótszy). Jak w tych dwóch przypadkach zachowa się funkcja? Jaka praca zostanie wykonana (lub nie). Jak wpłynie to na dalsze działanie programu? Mamy string składający się z 20 znaków. Jeśli teraz za pomocą funkcji in s e r t wstawimy pięć znaków począwszy od znaku piątego, to co stanie się z dotychczasowym znakiem piątym, szóstym i siódmym? Mamy string składający się z 20 znaków. Jeśli teraz za pomocą funkcji r e p la c e wstawi my pięć nowych znaków w miejsce znaku piątego i szóstego - to co stanie się z dotychczasowym znakiem piątym, szóstym i siódmym? Mamy wywołać jakąś funkcję, której argumentem ma być nazwa pliku w postaci C-stringu. Tymczasem w naszym programie nazwa ta jest treścią obiektu klasy s tr in g . Co robić? KSSSSI Funkcje składowe data i c_str mają ten sam typ rezultatu const char* i obie pozwalają nam poznać bieżącą zawartość obiektu klasy string. Jaka jest między nimi zasadnicza różnica? Dlaczego w typie rezultatu jest przydomek const? HE5SS03 Dla dociekliwych: Wyjaśnij, dlaczego z rezultatów funkcji składowych c_str i data należy skorzystać „od razu", (czyli przed następną ewentualną modyfikacją stringu np. operatorami +=, =, czy funkcjami insert, replace). H E S S ! Klasa string dostarcza nam funkcji składowej compare oraz operatorów >, <, <=, >=, !=, == do porównywania treści stringów. Które z nich przy tych porównaniach są czułe na wielkie i małe litery, a które nie?
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia
XXXII
XXXIII
XXXV
611
Funkcja compare dokonuje porównania treści obiektu (na rzecz którego została wywo łana) z innym stringiem. Jako rezultat swej pracy może zwrócić jedną z trzech wartości. Jakie to wartości i jak należy rozumieć każdą z tych odpowiedzi? Jaki jest typ rezultatu porównania dwóch stringów za pomocą operatorów >, <, <=, >=, !=( == Co oznacza każda z możliwych wartości? Czy za pomocą operatorów >, <, <=, >=, !=, == można dokonać takiego porównania w którym po lewej stronie operatora stoi C-string, a po prawej obiekt klasy s t r in g . Jakie są możliwe, a jakie niemożliwe kombinacje przy takich porównaniach. Napisz definicję funkcji, którą będzie można wywołać z dwoma argumentami klasy s tr in g , a która dokona ich porównania wg. porządku alfabetycznego ae w przeciwieństwie do funkcji compare - tutaj porównanie ma byc nieczułe na wielkość liter. (To znaczy wielka litera 'A' oraz mała litera 'a' - według takiego porównania są według porządku alfabetycznego identyczne). Funkcja powinna zwracac rezultat o znaczeniu podobnym do rezultatu jak funkcji compare. Jaką funkcją możemy treść obiektu klasy string skopiować do tablicy char? Jeśli za pomocą tej funkcji do pustej tablicy typu char skopiujemy całą zawartość obiektu k asy string, to czy w rezultacie powstanie C-string będący kopią zawartości obiektu klasy string ? Co robi funkcja assign i czym można ją najczęściej zastąpić? Co się dzieje z dotychczasową treścią obiektu klasy string w przypadku wywołania funkcji składowej assign, a co w przypadku wywołania funkcji składowej append. Czym można ją najczęściej zastąpić wywołanie funkcji składowej append? Nazwa funkcji getline można przetłumaczyć jako „weź linię" (tekstu). W jaki sposob wobec tego można wczytać za jednym razem kilkanaście linii tekstu? Wytłumacz, co się dzieje w przypadku pracy funkcji getline z tak zwanym ogranicznikiem. Po co potrzebny jest ten znak? Co funkcja getline robi napotkawszy ten znak w strumieniu danych? Co robi z nim ostatecznie? Czy funkcja getline może wczytać białe znaki poprzedzające pierwszy wyraz spodzie wanego długiego tekstu? Czy biały znak może być ogranicznikiem? (W wywołaniu funkcji getline). Operator wczytywania » i funkcja getline inaczej postępują z ogranicznikiem wczy tywanego tekstu. Na czym polega różnica i jakie to może mieć dla nas niespodziewane konsekwencje? Jak można temu zaradzić? Funkcja ge 1 1ine nie jest funkcją składową klasy s t ring. Czy jeśli za pomocą tej funkcji zamierzamy wczytać do obiektu klasy string dłuzszy tekst - czy musimy dla tego obiektu zarezerwować miejsce? Napisz funkcję, która otrzymawszy (jako argument) obiekt klasy string pokaże na ekranie kolejno wszystkie jego znaki. Każdy znak powinien byc u,ęty w nawiasy kwadratowe. Przykładowo: string o treści "Gen” powinien byc pokazany na ekranie jako [G] [e] [n]. Napisz funkcję, która z nazwy pliku przysłanej jako argument klasy s t r in g usunie ewentualne rozszerzenie i rezultat zwróci jako obiekt klasy s tr in g . Przykładowo: funkcja ta otrzymawszy nazwę "rezerwac j e _ l u t y . dane" jako rezultat zwróci naz wę "rezerw ac je _ lu ty " . Uwaga: Rozszerzenie może mieć dowolną długość (tzn. więcej niż tradycyjne 3 znaki).
612
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia
Napisz funkcje, która poleci użytkownikowi wprowadzić (z klawiatury) nazwę jakiegoś pliku tekstowego. Nazwa ta ma zostać zapisana w obiekcie klasy str in g . Funkcja ta ma sprawdzić, czy w nazwie pliku użytkownik podał rozszerzenie " . txt" . Jeśli nie ma żadnego rozszerzenia, w funkcji masz dodać rozszerzenie " .tx t" . Jeśli jesl rozszerzenie, ale inne niż " . t x t ", zamień je na takie. Funkcja (jako rezultat) ma zwrócił obiekt klasy s tr in g zawierający tak zmodyfikowaną nazwę pliku. Napisz funkcję, która w stringu (przysłanym jej jako argument) zamieni znaki w taki sposób, że znaki parzyste będą napisane literami wielkimi, a nieparzyste - małymi, Wynik pracy zapisany w obiekcie klasy string ma być rezultatem zwracanym przez tę funkcję. W pewnym międzynarodowym urządzeniu badawczym jest piętnaście identycznych modułów. Ich nazwy składają się z trzonu np. „Cluster_", za którym następuję jednoliterowy symbol. Oto stosowane litery: ABCDEFGJKLMNPQR
Na pierwszy rzut oka wydaje się, że są to kolejne litery alfabety, ale są tu wyjątki. Nie ma litery 'H', bo Włosi i Francuzi mają trudności z ich wymawianiem. Nie ma litery T, bo mogłaby nastąpić pomyłka z jedynką. Nie ma litery 'O', bo za bardzo przypomina zero. Napisz funkcję, która pełną nazwę modułu przysłaną do niej jako argument - (na przykład "C luster M") rozpozna w taki sposób, że jako rezultat (typu in t ) zwróci numer modułu. Ów numer-to jakby numer litery, według porządku, w jakim widzisz jc napisane powyżej. (Numerujemy od zera). Na przykład nazwa „Cluster_M" - określa moduł numer 1 0 . Napisz funkcję, wywoływaną z jednym argumentem klasy s tr in g , a zwracającą jako rezultat obiekt typu string. Funkcja powinna zanalizować przysłaną do niej nazwę pliku, - Jeśli nazwa ta ma rozszerzenie inne niż ”.p o ly " , to funkcja powinna zwrócić string pusty. - Jeśli nazwa ma rozszerzenie ".poły", to należy w tej nazwie znaleźć ostatnie wystąpienie wyrazu " polygon ". (Jeśli nie ma - funkcja ma zwrócić string pusty). -Jeśli zaś „_polvgon_" w tej nazwie jest, to funkcja powinna jako rezultat zwrócić substring zaczynający się bezpośrednio za tekstem „_polygon_'' i ciągnący się do kropki oznaczającej początek rozszerzenia. Przykład: jeśli przysłana (jako argument) nazwa pliku brzmiałaby: "matrix zet vs_aoq_polygon_cr54. poły",
to funkcja powinna zwrócić jako rezultat "cr54"
Napisz funkcję, do której można wysłać obiekt klasy s t r in g zawierający długi, wielolinijkowy tekst. Funkcja ta powinna ten tekst wydrukować na ekranie - rozpoczynając każdą linijkę tekstem opisującym jej numer. To znaczy, jeśli wysłalibyśmy następujący tekst: Narodowa Galeria, wielka sztuka na serio owionęła nas chłodem swych sal to funkcja w y d ru k u je na ek ran ie
Rozdział. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia
613
Linia 1: Narodowa Galeria, Linia 2: wielka sztuka na serio Linia 3: owionęła nas chłodem swych sal
Obiekty klasy string mogą byc zgrupowane w tablice. Na przykład może być tablica przystanków tramwaju lub metra. Nazwy przystanków oczywiście mogą być wielowyrazowe. („Bronowice Wielkie", „Mały Rynek", „Most Dębnicki ). Napisz funkcję u nazwie przystanek, w której składnikiem statycznym bedzie tablica stringów z kolejnymi przystankami danej linii tramwajowej. Funkcja wywoływana ma bycz argumentem oznaczającym numer najbliższego przystanku. Funkcja ma wówczas zwrócić jako rezultat stringu opisujący następny przystanek. To znaczy na przykład taki string: "Następny przystanek: Teatr Bagatela." Dla bardziej dociekliwych. Napisz funkcję, która wywoływana będzie z jednym argumentem będącym liczbą z zakresu 0-99. Rezultatem zwracanym przez funkcję ma być string opisujący słownie tę liczbę - czy jest parzysta czy nie parzysta. Przykładowo jeśli wywołano ją by dla liczby 55 - powinna wyprodukować string o treści: "Liczba 55 jest nieparzysta". Wskazówka - parzystość łatwo sprawdza się operatorem reszty z dzielenia. Natomiast istotą tego zadania jest zamiana liczby 55 na ciąg znaków „55". Napisz funkcję, która otrzymawszy jako argument liczbę całkowitą z przedziału [-9999 +9 9 9 9 ] w rezultacie zwróci string, ze słownym wyrażeniem tej liczby. To znaczy: gdy wyślemy do niej liczbę 729, w rezultacie otrzymamy string "siedemset dwadzieścia dziewiec". Napisz funkcję string litera_alfabetu(int );
która otrzymawszy jako argument numer litery, odpowie stringiem, wyjaśniającym która to jest litera w alfabecie angielskim. Przykładowo: jeśli funkcję tę wywołamy z wartością 6 , wówczas odpowie ona takim tekstem: "W a lf a b e c ie l i t e r a nr 6 to l i t e r a : F". Napisz krotki program, który wywoływany będzie dwoma argumentami. Pierwszy argument będziemy określać na przykład rodzaj miejsca, skąd program będzie czytał dane. Mogą być takie wartości tego parametru -serwer -plik -strumień
Drugim argumentem będzie jakaś nazwa. Na przykład nazwa serwera, pliku itp. Drugi argument nie ma w tym ćwiczeniu specjalnego znaczenia. Jak pamiętamy z § 8.20, str. 363, jeśli wywołujesz program nie tylko pisząc jego nazwę, ale dodatkowo podając argumenty - to przychodzą one do programu w postaci C-stringów. Przepisz je najpierw do obiektów klasy s tr in g , a następnie rozpoznaj jakiego trybu pracy żąda użytkownik. Zrobisz to analizując cztery pierwsze znaki pierwszego argumentu. Tylko cztery znaki, bo to wystarczy do rozpoznania trybu. (Z drugiej strony użytkownikowi pozwoli to skracać sobie argumenty, które ma wielokrotnie w ciągu dnia wystukiwać na klawiaturze). Program powinien przyjąć i ocenić te argumenty, po czym wypisać na ekranie tekst wyjaśniający. Na przykład, jeśli wywołany został w jeden z poniższych sposobów:
614
Rozdz. 11. Biblioteczna klasa s t d : : s t r i n g do operacji z tekstam i Ćwiczenia program -ser lxgl013 . program -plik europiuml52_cal.lmd program -stru cin
Powinien na ekranie napisać odpowiednio: "Dane przychodzie beda z serwera lxgl013" "Dane przychodzie beda z pliku europiuml52_cal.lmd" "Dane przychodzie beda ze strumienia cin"
Rozdział. 12. D eklaracje przyjaźni
615
Deklaracje przyjaźni u n k c ja z a p rz y jaźn io n a z klasą to funkcja, która (m im o że nie jest s k ła d n i k ie m klasy) ma d o stę p d o w szystkich (n aw et p ry w atn y ch ) sk ład n ik ó w klasy. W y o b ra ź sobie taką sytuację. W Tw oim d o m u jest d u ż o roślin. Rośliny te są p ry w a tn y m sk ład n ik iem ob iek tu klasy dom .
F
P e w n e g o d n ia w y jeżd żasz na w akacje na M ajorkę. C h cesz jednak, by kw iatki Cii nic „ z d e c h ły " . M asz d w a wyjścia: 1) S praw ić, by k w iatk i stały się p u b liczn e, czyli w ystaw ić je na k latk ę schodow ą (k w iatk i globalne). K ażdy w te d y m oże w y k o n ać na nich funkcję „p o d lew a n ie". R y zy k u jesz jednak, że ktoś n ie p ro szo n y w y k o n a na n ich funkcję „m o d y fik acja", czyli p rzero b i je na p o k arm dla sw o jeg o królika czy w ęża boa. Wyjście - jeś.i kochasz sw oje k w iatk i - nie jest dobre. 2) E w en tu aln o ść d ru g a : m asz z au fa n eg o przyjaciela. Dajesz m u kl u cze d o sw ojego m ieszkania i p ro sisz go, by kw iatk i podlew ał. Przyjaciel m a d o stę p d o w szystkich T w o ich p ry w atn y ch s k ła d n i k ó w (np. kolia b ry lan to w a w k o m o d zie), ale p o n iew aż mu ufasz, w ięc nie b o isz się o nic. Przyjaciel p rzy ch o d z i co d ru g i d zień i pod lew a. Jak d o tą d obrazek jest idylliczny. W ścibscy sąsied zi zau w aża ją je d n ak , że ktoś obcy w ch o d zi do Tw ojego dom u i d z w o n ią na policję, k tó ra p e w n e g o p o p o łu d n ia u rz ą d z a zasad zk ę w y k o p u jąc p rz e d d rz w ia m i trzy m etro w y d ó ł i nak ry w ając go gałęziam i. P rzyjaciel w p a d a w zasad zk ę. Policja zaciera ręce, że złapała złodzieja. Co p ra w d a , przyjaciel z d n a dołu k rzy czy coś o p rzy ja źn i, ale nikt go nie słucha. 1 słu sz n ie , p ra w d z iw y zło d ziej te ż to sam o by krzyczał. N a d je ż d ż a specjalnym w o zem n a d in sp ek to r. O cenia sytuację i m ów i: „ -C h w i leczkę: o to m am w ręce definicję k la sy pod ty tu łem D om _C zytelnika i w id zę, że
2
616
Rozdz. 12. D eklaracje przyjaźni
na liście sk ład n ik ó w jest deklaracja, iż p a n a X uznaje się za przyjaciel! D o m u _ C zy teln ik a. Ma on, zatem , praw o zrobić w szystko ze sk ła d n ik a m i te klasy. P ro sz ę go więc w y ciąg n ąć z dołu, bo jeszcze kw iatki zw ięd n ą ". P rz e łó ż m y te n o b r a z e k n a ję z y k p o jęć C ++
tffy ,
K o n stru u jąc klasę ustalam y , ż e pew ne składniki będ ą p ryw atne. M ogą w ięc n.| nich p ra c o w a ć funkcje sk ład o w e tej klasy. Inne nie. W p e w n y c h jed n ak sytuacjach m oże być k o rzy stn e, by jakaś funkcja, spozl z a k re su te] klasy, m iała tak że d o stęp do sk ład n ik ó w pryw atnych. R obi się t< b a rd z o p ro sto . W ew n ątrz definicji klasy w y starczy um ieścić d e k larację te funkcji p o p rz e d z o n ą sło w em friend1. D zięki tem u ta zw y k ła fu n k cja ma p ra w o d o stę p u d o p ry w atn y ch składników klasy. T ak, jakby sk ład n ik i te stały się d la n ie publiczne. W ażne jest, że to nie funkcja ma tw ierd zić, iż jest zap rzy jaźn io n a. To klasa m a zad ek laro w ać, że p rzy jaźn i się z tą funkcją i tym sam y m n ad aje jej p raw o dostępu d o sw oich sk ład n ik ó w p ry w a t nych. Z atem sło w o friend pojaw ia się tylko w ew n ątrz definicji klasy. Funkcja zap rz y jaźn io n a na m ocy tej przyjaźni m a oczyw iście także d o s tę p do sk ła d n ik ó w protected.
P rz y k ła d d e k la r a c ji p rzy ja źn i z fu n k cją
O to k la sa pionek, w której jest deklaracji przy jaźn i z funkcją raport: class pionek { int x, y; // d o t y c h c z a s o w e I I .........
. _ d e k la r a c je
friend void raport(pionek &); }; Sam a funkcja jest gdzieś w p ro g ram ie zd efin io w an a następująco: void raport (pionek & p) * cout « p.kolor << ” pionek jest na pozycji " << p.pozycja << endl; } Funkcja raport w y w o ły w an a jest z arg u m e n tem b ęd ąc y m referencją d o obie ktu ty p u pionek. (M oglibyśm y rów nie d o b rze w y słać obiekt p rzez w artość, w ięc jeśli jeszcze nie osw oiłeś się z referencjam i, to sk reśl sobie zn ak & w p ie rw szej linii definicji funkcji - w te d y będ zie to p rzesła n ie p rz e z w artość). C o jednak jest w ty m w szystkim najw ażniejsze? 1
).
ang. f r i e n d - przyjaciel [czytaj: „frend"]
617
Rozdział. 12. D eklaracje przyjaźni
To m ianow icie, że: w e w n ą trz tej zap rzy jaźn io n ej fu n k q 'i raport o d w o łu je m y się do p ry w atn y ch sk ład n ik ó w obiektu k la sy pionek. Tak, ja k b y te skła dniki były p u b liczn e. To w szystko. Jeśli chcem y, by funkcja w ypisała d an e o jakim ś w y sy łam y go jako a rg u m e n t funkcji.
pionek n ie b ie s k i;
/ / d e f o b ie k tu
raport (n ie b ie s k i) ;
// w y w o ł a n i e f u n k c j i
pionku, to p o prostu
M oże Ci się n asu n ąć p y tan ie: „—Skoro chcem y, b y funkcja praco w ała n a danych sk ład o w y ch klasy, to d la czeg o nie zrobić z niej p o prostu funkcji sk ład o w ej tej klasy ?" P o ch w alam ten pom ysł. T ak w łaśnie p o w in n o b y ć w tym p rz y p a d k u . Lepiej m ieć funkcję jako sk ład n ik , bo łatwiej w ted y p an o w ać na nią. T ak sam o, jak lepiej b y d o m o w n ik p o d le w a ł kw iatki, niż p rz y c h o d z ił w tym celu k to ś obcy. J e d n a k fu n k c je z a p r z y ja ź n i o n e m a ją p e w n e c e c h y , k tó re je w y ró ż n ia ją i c z y n ią z n ich b a r d z o d o b r e n a r z ę d z ie
O to te cechy: ♦♦♦ Funkcja m oże być przyjacielem więcej n iż jednej klasy.(Tak się dzieje, g d y w ięcej niż je d n a klasa zad ek laru je p rzy jaźń z nią). W te d y taka funkcja m oże m ieć d o stęp do p ry w atn y ch sk ład n ik ó w kilku klas. ♦♦♦ F unkcja z a p rz y jaźn io n a m oże na a rg u m e n ta c h jej w y w o łan ia d o k o n y w a ć k o n w ersji zdefiniow anych p rz e z użytkow nika. O tym wspaniałym mechanizmie jeszcze nie mówiliśmy, więc brzmi to tajemniczo. Sprawy zajmiemy się w jednym z następnych rozdziałów. Tu wystarczy przyjąć, że funkcja zaprzyjaźniona jest bardziej uniwersalna niż zwykła funkcja składorwa. _ _ Dla wtajemniczonych: Funkcję składowy można wywołać na rzecz jakiegoś obiektu jej klasy, ale przecież nie na rzecz np. liczby 5. Tymczasem funkcja zaprzyjaźniona może przyjąć jako argument nawet tę liczbę 5. Pod warun kiem, że użytkownik określił, jak się liczbę 5 zamienia na obiekt danej klasy. ♦♦♦ D zięk i deklaracji p rzyjaźni m ożem y n a d a ć d o stęp do p ry w atn y ch s k ła d n ik ó w klasy n aw et takim funkcjom , które nie m o g ły b y być fu n k cjam i sk ła d o w y m i z pow odów zasad n iczy ch . N a p rz y k ła d dlatego, ż e są n ap isan e w zupełnie innym języku p ro g ra m o w a n ia w asem b lerze, P ascalu czy FORTRANie. Z ilu stru jm y p r z y k ł a d e m
p i e r w s z ą c e c h ę : m o ż liw o ś ć p rz y ja ź n i z k ilk o m a
k la sa m i
M am y d w ie klasy. K lasa punkt opisuje w sp ó łrz ę d n e jakiegoś p u n k tu . Klasa kwadrat o p isu je w sp ó łrz ę d n e lewego do ln eg o ro g u prostokąta i d łu g o ść jego boku. O b ie klasy uznają z przyjaciela funkcję g lo b a ln ą o n azw ie s ę d z i a .
4 12
>> bmzbu "id >> qnoo
9X3 eu Azsx ( (5(oq-M5( + A'm >() => A-qd)
55
(A-m >( =< A-qd) 55
( (5(Oq*M>( + X'M>() => X*qd) 55 (X' M}( =< X- qd)
)JX }
Q
(MW 5 ^eapeMi( 'qd 5 qxund) Bxzpas qux (\su\y aiąo dis biuz.vlfizj.ti bhyunjbj z //
//
y ^ ^ ^ ł ; i f ł ł 4 f ł J M i ł ł ł + ł . * » ł ł f J f ł ł ¥ ł # » ł ł ł ł ł ł ł ł ł ł ł ł ł ^ ł J f ł ł J f J M
M
i J f ; ł ł ł ł ł J ł ł J f j f ł /
{ .'Sjdo =
BMZBU
.'pp = 5(oq
.'q = A .' b = x
} JOiyiUJSllOy // (sxdo 6uxxqs 'pp 5ut 'q :jux 'b qux )^b x p b m >(::qBjpBM>( / ł ł » » ) m
ł ł ł ł * ł » » ł ł J H
M
f » ł ł ł ł ł ' M
> ł » « ł ł ł ł ł » ł t ł i H
i » ł ł ł ł ł ł ł » ł » ł ł » ł f ł » ł ł /
{ .'SXdO =
BMZBU
.'q = A B = X lO ty tu is u c ą / /
(sxdo 6ux;rqs 'q ;ux 'b
}
xut
)q>[und::qj(und
/////////////////////////////////////////////////////////////// M q
//
(m 5 XBjpBMj( 'd
5
q>(und) Bxzpes aux puexjj d zd zsd
I
so d
a z o tu — / /
.' (sxdo 6uxx^s 'pp qux 'q qux ' b qux) qB.ipBM)( : oxxqnd .'b m z b u 5uxxqs
•Moq 'A 'x quT } qBUpBM>( SSBXO
/////////////////////////////////////////////////////////////// •'{ 0
//
.'(>f 5
5
BjpBM>) 'd
5
}>fund) Bxzpss qux puexaj ••• D Z D Z S o l SOD D Z O U l ' " H
.'■ju =+ A .'U =+ X
{ )
(ui }ut 'u 3ux)yon:t pxoA .'(sxdo 6uxxqs 'q qux 'b xux)q>{und
: oxxqnd uxxqs .'A 'x qux } q>(und ssbxo /////////////////////////////////////////////////////////////// .'b m z b u
a
i u z i ; ( a z . i (I
a f 3 B . i B [ > |3 Q ' z i ‘z p z o y
6
i x b x p b m >( s s bxo --------------------------------------------- u ,'pxs aoBdsauiBu buxsn <6uxjqs> apnxouxf epnxoux#
UDblupuunoduz vhvuD[>(Dp/I
8T9
619
Rozdział. 12. D eklaracje przyjaźni
<< kw.nazwa << endl; return 1; }else { cout << "AUT! " << pt.nazwa << " jest na zewnątrz " << kw.nazwa << endl; return 0; ) Z*************************************************************/ main () { kwadrat b o (10, 10, 40, "boiska"); punkt p i (20, 20, "piłka"); sędzia(pi, bo ); cout << "kopiemy pilke !\n"; while (sędzia(pi, bo)) { pi.ruch(20,20) ; }
// ©
Po wykonaniu programu na ekranie zobaczymy piłka leży na tle boiska kopiemy pilke ! piłka leży na tle boiska piłka leży na tle boiska AUT! piłka jest na zewnątrz boiska
Przyjrzyjmy się ciekawszym punktom programu © W e w n ą trz definicji klasy punkt w idzim y d ek larację przyjaźni. Klasa punkt s tw ie rd z a tutaj, że ma za u fa n ie do funkcji sędzia. B ard zo w ażn a uw aga: zau w aż, że na liście arg u m e n tó w tej funkcji jest n azw a klasy kwadrat. D o tej pory klasa ta jeszcze nie została zd efin io w an a. P am iętam y je d n ak , że w C++ k a ż d a n azw a, zan im zo stan ie użyta po raz p ierw szy , m usi zostać z d e k la ro w a n a . Jak te n problem rozw iązać? O O to ro zw iąz an ie. Jest to tak zw ana d ek laracja zap o w ia d a ją c a (zw iastująca). M ów i ona: „Jakby co, to n azw a Kwadrat jest n azw ą klasy". To w szy stk o . Nie m a tu nic więcej na te m a t w ew nętrznej s tru k tu ry klasy kwadrat, ale to nie sz k o d zi, bo w m om encie deklaracji przyjaźni te d e ta le nie są jeszcze kom p ilato ro w i p o trzeb n e. © D eklaracja p rzy jaźn i w d ru g iej klasie. Podobnie: klasa kwadrat s tw ie rd z a tutaj, że m a zau fa n ie d o funkcji sędzia. O D efinicja funkcji sędzia. Jest zdefiniow ana jak najzw yklejsza funkcja.
4 12
620
Rozdz. 12. D eklaracje przyjaźni
R óżnica polega tylko n a tym, że funkcja ta pracuje sobie na p ry w atn y ch sk ład n ik ach obu klas - tak, jakby były one publiczne. Czym zajm uje się funkcja sędzia? Łatw o się zorientow ać, że po prostu sp ra w dza czy obiekt klasy punkt leży na tle obiektu klasy kwadrat. Robi to p rz e z poró w n an ie w spółrzędnych p u n k tu z obszarem zajm o w an y m przez obiekt klasy kwadrat. © W funkcji mai n korzystam y z tej funkcji. Zdefiniow aliśm y d w a obiekty i w y w o łu jemy funkcję. Z auw aż, że obiekty te w ysyłam y do funkcji jako zw ykłe argum enty - nie m a tu żad n eg o zapisu w stylu o b ie k t.fu n k c ja f) bow iem funkcja - nie jest funkcją sk ład o w ą żadnej klasy.
W Trzeba pamiętać o kilku jeszcze sprawach: Funkcja jest zaprzyjaźniona z klasą, a nie tylko z jakim ś konkretnym obiektem danej klasy. To znaczy, że funkcja zaprzyjaźniona otrzymuje prawa przyjaciela w stosunku do w s z y s tk ic h obiektów tej klasy. . ...
4
•
. .
. * •• * • '.. • ‘ >' *" "• - - '
' '
Deklaracja p rzy jaźn i tylko d ek laru je przyjaźń - i nic w ięcej. K onkretnie. N a z w a funkcji zap rzy jaźn io n ej nie staje się p rzez to nazw ą z zak re su tej klasy. Po prostu w deklaracji przyjaźni rozmawiamy z kompilatorem tłumaczyć mu, że taka fiinkcja ma dostęp.... Funkcja zap rzy jaźn io n a nie jest sk ład n ik iem klasy, d la te g o w ięc nie ma w sk a źnika t h i s d o obiektów klasy, która obdarzyła ją p rzy jaźn ią. Dla nas oznacza to, że jeśli chcem y w ciele tej funkcji o d n ie ść się d o sk ład n ik a klasy, która u zn aje ją za przyjaciela - posługujem y się o p erato rem V (k ro p k a) lub op erato rem -> Po prostu m ó w im y : chcę odnieść się do obiektu p t , konkretnie do jego składnika x pt.x Albo: clicę odnieść się do wskazywanego obiektu, do jego składnika :<
wskaźnik->x W n aszy m ostatnim p rzy k ła d zie celow o w sp ó łrz ę d n e w obu k la sach n azw ałem id en ty czn ie (x, y), by ani p rz e z m yśl Ci nie p rz e szło, że m ożesz odnieść się do nich b e zp o śred n io . T utaj nie w ia d o m o b y łoby przecież czy chodzi o sk ła d n ik x z obiektu klasy kwadrat czy z obiektu klasy punkt. Jed n ak n aw et g d y b y n azw y sk ła d n ik ó w były z u p e łn ie ró żn e, to nie m o ż n a pom in ąć n azw y o b iektu. N ie m o żn a, bo przecież w n a s z y m p ro g ram ie m o ż e być m ilion o b ie k tó w klasy kwadrat i dw a m iliony o b ie k tó w klasy punkt.
Rozdział. 12. D eklaracje przyjaźni
621
Funkcja m usi p o w ied zieć k o m p ilatorow i, d o k tó reg o z ob iek tó w teraz ch ce sięg nąć, by odczytać p ry w a tn y sk ład n ik x.
Z w y k le w e w n ątrz klasy fu nkcja jest tylko d ek laro w a n a. Jest to jed y n ie d e k la ra cja p rzy jaźn i i tyle. N ie m a znaczenia w k tó ry m miejscu klasy (public, protected, pri v a t e ) tak a deklaracja nastąpiła. Słow a public, protected, private nie m ają na to w p ły w u . Przyjacielem się albo jest, albo nie jest. M oże się tak zd arz y ć, ż e k o m pilator (pracując n a d jakim ś plikiem ) zobaczy d eklaracje jakiejś funkcji po raz pierw szy d o p ie ro w miejscu deklaracji p rzyjaźn i. N ie jest to b łą d , ale u w a g a : w t y m m iejscu k o m pilator u z n a , że chodzi o jak ąś funkcję g lo b a ln ą, d o stęp n ą ogólnie - ta k że z innych p lik ó w tego p ro g ra m u . Jeśli je d n a k zo stan ie p rz e z nas oszukany, to zn ac zy g dzieś dalej zo b aczy , że 1 d efin iu jem y tę funkcję jak o funkcji statyczną (czyli w idzialną tylko d la tego je d n eg o k o n k retn e g o p lik u ) - zasygnalizuje błąd. N ie b y ło b y p ro b lem u g d y b y śm y wcześniej zam ieścili deklarację tej funkcji jako statyczn ej — bo w ted y , p rz y deklaracji przyjaźni, ko m p ilato r już w ied ziałb y , z czym m a d o czyn ien ia i nie m usiałby niczego zak ład ać w ciem no, a potem zm ien ia ć zdanie. Dla w tajem n iczo n y ch :
M o żem y tak zrobić, że w e w n ą trz klasy będzie nie tylko deklaracja fu n k cji za p rzy jaźn io n ej, ale w ręcz jej definicja (czyli całe ciało funkcji). M im o to, ż e jest ona u m ieszczo n a "w ś ro d k u " ciała klasy — fu nkcja jest nadal tylko przyjacielem , a nie sk ład n ik iem . Taka definicja funkcji zaprzyjaźnionej ma następ u jące konsekw encje: •
tak zd efin io w an a funkcja zap rzy jaźn io n a jest ty p u inline
•
tak d efin io w an a funkcja leży w zakresie leksykalnym d e k la racji tej klasy; leksykalnym - czyli oznacza to, że m o żn a w definicji tej zaprzyjaźnionej funkcji: a) sk o rzy stać z w łaśnie obow iązujących (w ew nątrz deklaracji tej klasy) instrukcji t y p e d e f , b) sk o rzy stać ze zdefiniow anych w tej klasie ty p ó w w ylicze n io w y ch enum.
4 12
622
Rozdz. 12. D eklaracje przyjaźni
Nie zaw sze m o żn a takim chw ytem się posłużyć. Aby to by ło m ożliw e: 1. Klasa, w której to się ma z d arzy ć, nie m oże być tak zw an ą klasą lokalną.
❖
Dotychczasowe nasze klasy były nie-lokalnc, czyli zwykłe - więc tutaj nie ma przeszkód. (O klasach lokalnych porozmawiamy na str. 649). 2. Tak definiow ana funkcja zaprzyjaźniona nie m o ż e mieć nazw y Z kw alifikatorem zakresu tw ierdzącej, żc jest o n a (jakoby) funkcja sk ład o w ą jakiejś innej klasy - podczas gdy n a p ra w d ę takiej funkcji składow ej w tamtej klasie nie m a. To by byh/ wręcz podchody! Wyobraź sobie: ktoś zdefiniował klasę ijest Z niej dumny, a m y -za pomocą deklaracji przyjaźni - cichaczem dodefiniowujemy tej cudzej klasie piętnaście dodatkowych funkcji składowych.
W W p rzy p ad k u funkcji przeład o w an y ch , przyjacielem klasy K jest tylko ta wersja funkcji, która o d p o w ia d a liście arg u m e n tó w w idocznej w d ek laracji przyjaźni w definicji d anej klasy class
K
K
{ frien d
vo id
alarm (K
obj ,
in t
k) ;
}; vo id vo id vo id
a l a r m (f l o a t * , K o b ie k t ) ; alarm (v o id ); a l a r m (K o b i e k t , i n t i ) ;
. . . , K // < - t o j es t p r z y j a c ie l k l a s y K
Funkcja z ap rz y jaźn io n a m oże być zw y k łą funkcją, a m o ż e byc tez funkcją składow ą z u p e łn ie innej klasy. O czyw iście m a w tedy d o s tę p d o pry w atn y ch skład n ik ó w sw ojej klasy, a także tej klasy, która n azw ała ją zap rzy jaźn io n ą.
O to ta k z m o d y fik o w a n y p o p rze d n i p rz y k ła d , że fu n k c ja s ę d z i a je s t s k ła d n ikie m k la s y k w a d r a t , a kla sa p u n k t d e k la ru je z fu n k c ją p rz y ja z n # in clu d e < io stream > # in clu d e < s t r in g > u sin g nam espace std ;
f^W m m njninfm ifm Trm Pninililinninninnu class
kwadrat
1 in t
x, y, bok; s tr in g nazwa; p u b lic : k w ad rat(in t a,
in t
H ... m o ż e c o ś j e s z c z e ...
b,
in t
dd,
. strin g
. . o p is),
623
Rozdział. 12. D eklaracje przyjaźni
in t
sęd zia
(punkt
// ©
& p );
///////////////////////////////////////////////;/////////////// c l a s s punkt { i n t x, y; s t r i n g nazwa; p u b lic : p u n k t(in t a, in t v o id ru ch (in t n,
b, s tr in g i n t m)
o p is);
1 x y
+= n ; + = m;
H ... m o ż e c o ś j e s z c z e ...
frien d
punkt:
in t
k w a d ra t: : sęd zia(p u n kt
: punkt (in t
a,
in t
b ,ch ar
i
// ©
p );
*opis)
//k o n s tr u k to r
X 3/ y = b; nazwa
=
o p is;
;** **** .*** **** *** *** **** *•** *** *** *.*** *** **** *** *** **** *** **/ k w a d r a t : : k w a d r a t ( i n t a , i n t b, i n t dd, s t r i n g o p i s ) // k o n s tr u k t o r {
4 12
x = a; y = b; bok = dd; nazwa = o p is ;
J* * * * * * * * * * * * * * * * * * * * * * * * * "* "* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // in t
{
kw adrat: : sęd zia
if(
(pt.
(punkt
x >= X) &&
&& ( p t . y
>=
y)
«■ &
& pt)
(p t.x
<= (X +
(p t.y
<=
+
bok)
)
bok>
>
) cout
<< p t . n a z w a << " l e ż y na << n a z w a < < e n d l ; return 1 ; }else { cout « "AUT! " « pt.n azw a « " j e s t na zew nątrz " << n a z w a << e n d l ; r e tu r n 0;
tle
} )*******************************************************/ m a i n () kwadrat b o ( 10 ,10 , punkt p i ( 20, 20, b o .sę d z ia (p i);
)
40, "b o isk a "p iłk a");
), ''
~ ^
// ©
624
□
Rozdz. 12. D eklaracje przyjaźni
Po wykonaniu programu na ekranie pojawi się p iłk a
leży
na
tle
bo iska
Komentarz @ D eklaracja, zw ykłej funkcji składow ej w klasie k w a d r a t . A rg u m en te m jest obiek t klasy p u n k t , stąd też konieczna była deklaracja zap o w iad ająca tę klasę O . © D eklaracja przyjaźni. Tutaj jednak w ażn a u w ag a. Jeśli przyjacielem ma być funkcja składowa z innej klasy, to ta klasa m usi być już w tym m om encie kom pilatorow i zn an a . D latego najpierw w program ie jest d ek laracja klasy k w a d r a t (z fu n k cją s ę d z i a ) , a p o te m d o p ie ro d ek laracja k la sy p u nkt i niniejsze ogłoszenie przyjaźni. O O to definicja funkcji s ę d z i a . Już na p ew n o p rzy deklaracjach zau w aży łeś, że zm ieniła się lista arg u m e n tó w . Teraz a rg u m e n te m jest ty lk o o b iek t klasy p u n k t . A co z ob iek tem klasy k w a d r a t , funkcja g o przecież tak że potrzebuje?! Z ap o m in asz, że teraz funkcja jest funkcją sk ład o w ą klasy k w a d r a t , a w ięc jest w y w o ły w an a na rzecz obiektu klasy k w a d r a t . Z resztą spójrz poniżej. © T a k w łaśnie w m ai n w y w o łu jem y funkcję sędzia. O biekt klasy p u n k t w ysyłany je st jako arg u m en t, a obiekt klasy k w a d r a t p rzez uk ry ty w sk a źn ik t h i s . © Z faktu, iż funkcja sęd zia jest funkcją sk ład o w ą klasy k w a d r a t w y n ik a, że o d n o sząc się do d an ej składow ej swojej klasy m ożna p o słu g iw ać się zapisem : s k ła d n ik , a nie zap isem : o b ie k t.s k ła d n ik - w id ać to w yraźn ie w tej linijce. W sto su n k u do o b iek tu klasy zaprzyjaźnionej
punkt
stosujem y tu za p is
p t .x
a le w stosunku d o sw ojej w łasnej klasy x, bok
(d a w n ie j b y ł o k o n ie c z n e k w . x ,
T o dlatego, że p rzecież gdy piszem y
k w . bo k.) x,
to jest tam n a p ra w d ę
th is-> x
„ -C o ś kręcisz! " - z a w o ła łe ś z a p e w n e - „ p a r ę stro n w cześniej w m aw iałeś mi, że funk cja zap rzy jaźn io n a z klasą nie zaw iera w sk aźn ik a t h i s ! " P o d trzy m u ję to! Funkcja F z ap rz y jaźn io n a z klasą K nie zaw iera w sk a źn ik a t h i s d o klasy K, k tó ra uznaje ją za p rzy jaciela - bo nie jest sk ład n ik iem tej klasy. Jeśli je d n a k sam a funkcja F jest zw y k łą funkcją s k ła d o w ą jakiejś innej klasy, to w skaźnik t h i s d o obiektu sioojej k lasy zaw iera. Za jego p o m o c ą pracuje przecież na sw oich w łasnych p ry w atn y ch skład n ik ach .
625
Rozdział. 12. D eklaracje przyjaźni
Brzmi to z a w ile , w ię c p o s łu ż m y s ię a n a lo g ią z p o d le w a n ie m k w ia tk ó w Z ałó żm y , że to Ty jesteś osobą podlew ająca k w iatk i i Twoja znajom a P erfidia zad e k la ro w a ła p rzy ja źń z Tobą. M asz jeszcze także in n eg o zn ajo m eg o O n u freg o , k tó ry p ro sił Cię o to sam o. (To sytuacja, kiedy funkcja g lo balna jest za p rz y ja ź n io n a z d w o m a klasam i). G d y ok reślasz sw oje czynności to m ówisz: „Id ę p o d la ć kw iatki O n u freg o albo „id ę p o d lać kw iatki P erfidii". Jak do tej po ry rzeczyw iście nie m a w sk a źn ik a t h i s . M ów isz p rzecież o sk ład n ik ach tych klas O n u fry.k w iatk i P e r f i d i a . k w iatk i
T eraz u w ag a: O k azu je się, C zytelniku, że d o stałeś m ieszkanie i nie m ieszk asz już w ięcej p o d m o stem . N ie jesteś już funkcją g lo b aln ą tylko należy sz już do k lasy p o d n azw ą „m ó j_ d o m ". Z ałóżm y, że O n u fry i Perfidia d ek laru ją Cię n a d a l jako sw ojego przyjaciela. M ó w isz „p o d lew a m k w iatk i O n u freg o " albo „ p o d le w a m kw iatki P erfid ii" P e r f i d i a . k w iatk i O n u fry.k w iatk i
M o żesz je d n ak p o w ied zieć też „p o d lew am k w iatk i" m yśląc o p o d le w a n iu sw o ich w łasn y ch k w iatk ó w , czyli
k w iatk i
th is-> k w iatk i
g d z ie t h i s ozn acza w sk aźn ik d o obiektu „m ó j_ d o m " P o d su m u jm y : funkcja zap rzy jaźn io n a nie zaw iera w sk a źn ik a this d o ob iek tó w klas, które d ek laru ją ją jako przyjaciela. Do swojej w łasn ej m oże. N ie jest się p rzecież przyjacielem sw ojego w łasn eg o d o m u , tylko po prostu d o m o w n ik iem (składnikiem ).
Jeśli p ro jek tu jesz p ro g ram i w id zisz, że p rz y d a ło by się, żeby funkcja m iała d o s tę p d o sk ła d n ik ó w p ry w atn y ch dw u klas, to m asz do w yboru: •
- o b ie klasy d eklarują ją jako funkcję zaprzyjaźnioną
•
- funkcja jest składnikiem jednej klasy, zaś d ru g a klasa d ekla ruje ją jako funkcję zaprzy jaźn io n ą.
K tóry z w a ria n tó w w y b rać, d ecydujesz rozw ażając w sp o m n ian e zalety funkcji z a p rz y ja ź n io n e j z cecham i funkcji składow ej. O p o dejm ow aniu takich w y b o ró w p o ro zm a w iam y jeszcze w jednym z następ n y ch ro zd ziałó w . (O p rz e ła d o w a n iu o p erato ró w - str. 793).
12.0.1
Klasy zaprzyjaźnione K lasa K m o ż e d ek laro w ać p rzy ja źń z więcej niż jed n ą funkcją sk ład o w ą klasy M. M o że n a w e t d ek laro w a ć p rzy ja źń ze w szystkim i funkcjam i tej klasy M. Jest to
2
626
Rozdz. 12. D eklaracje przyjaźni
trochę tak, jakbyś w y trw ale zadeklarow ał przy jaźń klasy K z k a ż d ą funkcją sk ład o w ą klasy M. S p raw a jest prosta, ale d u żo pisania. Z a m ia st tego m o ż em y zad ek laro w ać, że cała klasa M jest u z n a w a n a za przyjaciela klasy K class K { friend class M; // ... ciało k la s y K
}; Jak w idać, chodzi o um ieszczenie takiej deklaracji: friend class
n a z w a _ k la s y ;
w ciele klasy, która p rzy jaźń deklaruje. O d tej p o ry w szystkie fu n k cje składow e klasy M mają d o stęp d o p ryw atnych sk ład n ik ó w klasy K d eklarującej tę przyjaźń. T o już Cię nie d z iw i - przyzw yczaiłeś się d o tego p rzy o k azji funkcji zaprzy jaźn io n y ch . Jest tu jednak coś jeszcze, coś czego p rzy funkcjach być nie m ogło. Z ałóżm y, że m am y zw y k łą klasę KLA, k tó ra deklaruje p rz y ja ź ń z klasą PRZ. O tóż,klasa-przyjaciel PRZ m oże u ż y w a ć sk ładników p ry w atn y ch klasy KLA nie tylko w sw ych funkcjach składow ych, ale tak że przy inicjalizacji sw ych sk ład n ik ó w statycznych. M o że jeszcze p am iętasz, że ich definicje trzeba u m ieszczać o so b n o , jakby na z e w n ą trz definicji k lasy PRZ. N aw et więc tam , jakby w na z e w n ą trz ciała klasy PR Z, przyjaciel m oże sk o rzy stać z w artości p ry w atn eg o sk ład n ik a k la sy KLA. Jeśli klasa KLA ma w sobie jakieś definicje ty p ó w enura lub t y p e d e f , to klasa p rzyjaciel PRZY też m o ż e z nich skorzystać p rz y deklaracji sw oich sk ład n ik ó w . D la w tajem niczonych: Gdyby klasa przyjaciel PRZ miała w sobie zagnieżdżoną inną klasa, to nawet na jej liście dziedziczenia można posłużyć się składnikiem prywatnym klasy K Natomiast ju ż wewnątrz tej zagnieżdżonej klasy niemożna, bo to już inna klasa.
Co p ra w d a , funkcję zap rzy jaźn io n ą m ożna zd efin io w ać w tym s a m y m m iejscu, g d z ie jest d ek laro w an a jako przyjaciel, ale z k lasą-p rzy jacielem tej sztu czk i zro b ić się nie da. Ta k lasa m usi zaistnieć osobno.
Deklaracja przyjaźni jest oczywiście jednostronna W y ra ż a ją klasa KLA w o b ec klasy PRZ, n ato m iast k lasa PRZ nie w y ra ż a niczego szcze g ó ln eg o w sto su n k u do klasy KLA . K o n k retn ie - w cale jej nie u p o w a ż n ia d o g rz e b a n ia w sw oich sk ład n ik ach p ry w atn y ch .
Rozdział. 12. D eklaracje przyjaźni
627
Dwie klasy mogą się przyjaźnić także z wzajemnością. J e d y n y m sp o so b em z a d e k la ro w a n ia takiej p rzy ja źn i jest w łaśn ie deklaracja p rz y ja ź n i z klasą jako całością - sposobem jaki w łaśn ie po k azaliśm y . N ie m a m ożliw ości zd ek laro w an ia w jednej klasie, że p rzy jaźn i się ona z funkcjam i innej klasy, a w tej innej k la s ie - p rzy jaźn i z w ybra n y m i fu n k cjam i klasy pierw szej. To z p o w o d u , o któ ry m ju ż w spom nieliśm y: jeśli d ek laru jem y p rz y ja ź ń z fun kcją, k tó ra jest funkcją sk ła d o w ą innej klasy, to k o m p ilato r życzy sobie ju ż znać d ek larację tej klasy. (Np. żeby sprawdzić czy taka funkcja rzeczywiście tam jest). Ż eb y śm y się nie w iem jak gim nastykow ali, to zaw sze definicja jednej klasy b ęd zie z n a n a w cześniej o d drugiej, bo tak przecież piszem y tekst p ro g ra m u . Siłą rzeczy klasa, k tóra d efin io w an a jest w cześniej, m usiałaby zaw ie rać w sobie d ek laracje p rzy jaźn i z funkcjam i sk ładow ym i klasy drugiej - c h w ilo w o jeszcze n iezn an y m i. (Sama deklaracja zap o w iad ająca klasę nie w ystarcza k o m p ilato ro w i - m usi z n a ć w nętrze klasy). B łędne koło? 1 tak i nie. W sposób, o którym m ó w im y , rzeczyw iście nie da się tego zrobić, ale p roblem ro zw iązu je w łaśnie deklaracja przy jaźn i — nie z konk retn y m i funkcjam i klasy, ale z całą klasą. c l a s s dr uga; H ■*-deki. za p o w ia d a ją ca /////////////////////////////////////////////// c l a s s pierwsza { f r i e n d c l a s s dr uga; H ... re szta ciała k la s y p ie rw sz e j
); /////////////////////////////////////////////// c l a s s druga { frie n d c l a s s pierwsza; //...re szta ciała k la sy d ru g iej
}i
Przyjaźń nie jest przechodnia - przyjaciel m ojego przyjaciela nie jest m oim przyjacielem . Inaczej m ów iąc: jeśli klasa A deklaruje p rzyjaźń z klasą B, n ato m iast klasa B dek laru je p rzyjaźń z klasą C, to wcale nie oznacza, że klasa A u zn aje klasę C za sw ojego przyjaciela. G dyb y o to chodziło - n ależy w klasie A zam ieścić deklarację takiej p rzyjaźni f r i e n d c l a s s C;
P rzechodniość przyjaźni byłaby bardzo niebezpieczna. Z resztą w życiu także się nią nie posługujem y.
(Dla wtajemniczonych) Przyjaźń nie jest dziedziczna - przyjaciel mojej prababki nie jest moim przyjacielem .
4 12
628
Rozdz. 12. D eklaracje przyjaźni
W tajem niczeni w iedzą, że klasa m oże mieć „p o to m stw o " (klasy pochodne). P rzyjaźń nie jest dziedziczna - jeśli jakaś klasa chce m ieć przyjaciela, to p o w in n a to pow iedzieć w yraźnie sam a.
W deklaracji przyjaźni nie m ogą pojawić się p rzy d o m k i (specyfikatory) o k reśla jące sposób, w jaki przyjaciel został (przez kom pilator) um ieszczony w pam ięci. Te n ied ozw olone p rzy d o m k i t o s t a t i c , a u t o , r e g i s t e r , e x t e r n , m u t a b l e .
Wżyciu codziennym jest podobnie. Wypada powiedzieć: "Przyjaźnię się z Tomaszem ", a nie wypada powiedzieć: "Przyjaźnię się z Tomaszem, bonia własną willę", albo "Przyjaźnię się z Tomaszem, bo mieszka za granicą
12.0.2
Słowo o zakresie Z astan ó w m y się jak to jest, g d y deklarujem y p rzy ja źń ze zw ykłą fu n k cją, nie będącą składnikiem żadnej klasy. Jeśli funkcja taka w m om encie deklaracji p rzy jaźn i jest jeszcze k o m p ilato ro w i n iezn an a, w ów czas zak ład a on, że jest to funkcja leżąca w tym sam ym zakresie, w k tó ry m jest definicja klasy. Czyli jeśli definicja k lasy jest globalna, to funkcja z a k ła d a się - jest też globalna. Jeśli w ięc w dalszej części pliku program u okaże się, że funkcja, o której m ow a, zd efin io w an a jest jako staty czn a - czyli znana jest tylko w zakresie w ażn o ści je d n eg o pliku - w ów czas linker zaprotestuje, że funkcji nie znalazł.
Konwencja umieszczania deklaracji przyjaźni w klasie S po ty k a się często konw encje definiow ania klasy w taki sposób, że n ajp ierw w definicji klasy w yszczególnia się w szystkie sk ład n ik i pub liczn e (czyli w id z ia n e z z e w n ą trz klasy). D opiero dalej w ystępują sk ład n ik i p ry w atn e, czyli takie, o który ch zw y k ły u ży tk o w n ik klasy nie m usi ju ż w iedzieć. (U żyw ając p ralk i au to m aty c zn ej u ży tk o w n ik nie m usi w iedzieć o w szystkich jej elem en tach elek tro n iczn y ch i m echanicznych). Funkcje zap rzy jaźn io n e są ty m , co pow inno się o d razu zau w aży ć p a trz ą c na defin icję klasy, więc przyjęło się um ieszczać je na sam y m po czątk u , n a sam ej g ó rz e definicji klasy. Jak m ó w ię - jest to tylko konw encja, która m oże czasem u łatw ić „cz y ta n ie definicji klas.
Rozdział. 13. S truktury, Unie, Pola bitowe S tru k tu ra
629
13 Struktury, Unie, Pola bitowe
struct nazwa { / / lista składników }; o d p o w ia d a definicji class nazwa { public: / / lista składników i;
Uwaga dla programistów klasycznego C Jak wiesz, w klasycznym C mieliśmy również struktury. Struktury w C++ sy tak zrobione (i po to zrobione), żeby ułatwić przejście z C do C++.
630
Rozdz. 13. S truktury, Unie, Pola bitowe Unia
10
Oczyioiście - jak już zdążyłeś się zorientować - struktura (czyli klasa) C++ jest o wiele mądrzejsza niż w klasycznym C, choćby dlatego, że tutaj może ona mieć funkcje składowe. Nic to jednak nie przeszkadza starym strukturom. Mogą być przecież także klasy bez funkcji składourych. Krótko mówiąc, jest to dobra wiadomość: Twoje programy w C posługujące się strukturami są najłatioiejsze do przerobienia na C++.
13.2
Unia P rzy p o m n ij sobie, jak to było w dzieciństwie: M iałeś takie pudełko, w któ ry m trzy m ałeś swój najw iększy skarb. Czasem był to opalizujący kam yk, czasem sam o ch o d zik , czasem zd ech ła żaba. Pudełko to służyło Ci do po m ieszczen ia tylko tego jednego p rzed m io tu . Często zm ieniałeś zd an ie i coraz to inny p rz e d m io t był Twoim najw iększym skarbem . Jed n o było pew ne: p u d e łk o było na tyle d u że, by pom ieścić najw iększy z przedm iotów . Ta analogia ilu stru je pojęcie unii: U n ia w języku C++ to w łaśn ie takie p u d ełk o d o p rzech o w y w an ia jednego p rz e d m io tu . C okolw iek by to nie było. Jeśli m asz w pam ięci operacyjnej jakiś obszar p rzez n aczo n y na trz y m a n ie liczby ty p u double, to nie m ożesz tam norm alnie w p isać zn ak u czy liczby int. Jeśli je d n a k po słu ży sz się u n ią - to możesz: d o p u d e łk a w k ład a się (w to sam o m iejsce) ró w n ie d obrze g u m ę d o żucia, jak i zd ech łą żabę. D zięki u n ii m ożem y sp raw ić, że w tym sam ym m iejscu w pam ięci trzy m an e b ęd ą ob iek ty różnego ty pu. O czyw iście tvlko jed en w d an y m m om encie. U nia w ięc p o zw ala zao szczęd zić pamięć. R ozm iar u n ii w ynika z ro zm iaru n ajw ięk szeg o z obiektów , d o których p rzech o w y w an ia m a służyć. O to p rzy k ła d : union skarbiec { short int m; long 1; char z; }; Z d efin io w aliśm y unię, w której m ożna p rzec h o w y w ać albo liczbę ty p u short int, liczbę ty p u long, albo zn ak . Jedno z trojga. U nia ta m a ro zm iar o d p o w iad ający rozm iarow i n ajw ięk szeg o sk ła d n ik a —czyli sizeof (long). W w iększości k o m p u teró w b ę d z ie to 4 bajty.
631
Rozdział. 13. S truktury, Unie, Pola bitowe Unia
Rys. 13-1. Tak można sobie wyobrazić omawianą tu unię: miejsce w pamięci, na którym czasem znajdzie się znak char, czasem wartość typu long, a czasem wartość short int.
Zdefiniujmy sobie egzemplarz obiektu takiej unii skarbiec sss;
Od tej chw ili w pamięci począwszy od tej samej komórki może zostać zapisana dana typu short int, long albo char. O dnoszenie się do składników unii przypomina odnoszenie się do składników klasy. Robimy to więc za pomocą o p era to ra (k ro p k a ) - który właśnie zapew nia nam odnoszenie się do składników obiektów. sss. i = 5; cout << sss .i;
1 /w pis
//odczyt (np. na ekran)
Tak zapisaliśmy lub odczytaliśmy liczbę typu short int. Następna instrukcja: sss .z — ’m ’;
powoduje zapisanie do unii litery • m’, przy czym dotychczas będąca tam liczba 5 zostaje przez ten zapis zniszczona. Literę tę możemy odczytać na ekran tak: cout << sss.z;
O czyw iście odczytanie w celu wypisania na ekran jest tylko przykładem. Moż na zrobić z tym, co się chce: można przypisać innej zmiennej lub wysłać jako argument wywołania funkcji char c; funkcja(char); c = sss.z; funkcja(sss.z);
Trzeba pamiętać, jaki typ danej zapisuje się do unii - i taki sam typ odczytać To zrozum iałe: jeśli się zapisuje do pudelka daną typu „martwy płaz" to nie należy g o odczytywać jako typu „przedmiot jadalny', bo w rezultacie takiej pom yłki, żuć będziemy zdechłą żabę. Oto ilustracja: sss.z = ’x ’; cout << "liczba long z unii = " << sss.l;
Do unii zapisaliśm y znak. Jak pamiętamy, rozmiar znaku to 1 bajt. Tam właśnie odbył się zapis. W rezultacie mamy tam na poszczególnych bitach tego bajtu taki rozkład zer i jedynek, który odpowiada znakowi x . Nasza unia ma, jak
4 13
632
Rozdz. 13. S truktury, Unie, Pola bitowe Unia
wiemy, 4 bajty. Te pozostałe 3 są teraz nieużywane. Co tam jest? Obecnie są tam śmieci wynikające z historii posługiwania się tą unią. To jest jakiś (przypadkowy) rozkład bitów. Chociażby kawałek danej i n t , a spod niego wystający kawałek jeszcze starszej danej typu lo n g . Jeśli teraz zapomnimy, że wpisaliśmy tam znak i odczytamy to jako liczbę typu lon q , to wszystkie te bity z 4 bajtów zostaną zinterpretowane jako zakodowana liczba typu lo n g . Oczywiście otrzymamy bezsens. Gdybyśmy chcieli odczytać znak - to bity pierwszego bajtu zostaną zinterpretowane jako znak - co da nam wynik poprawny. Do składników unii możemy także odnosić się przez wskaźnik (operatorem ->) tak samo, jak do składników klasy. Unia jest bardzo podobna do struktury, z tym, że o ile składniki struktury umieszczane są w kolejnych miejscach w pamięci - w unii umieszczane są w tym samym miejscu. Jeden na drugim.
Napisałem „podobna do struktury" a nie: „do klasy", bo domniemywa się, że składniki unii są publiczne, o ile nie określimy tego inaczej.
Dla wtajemniczonych: Kilka szczegółów wybiegających w przyszłe zagadnienia:
13.2.1
•
Tak jak w klasach i strukturach, możemy tu używać słów okre ślających dostęp: p r i v a t e , p u b l i c . Słowo p r o t e c t e d jest niedopuszczalne. To dlatego, że służy ono celom dziedzicze nia, a unia nie może mieć dziedzica.
•
Składnikiem unii nie może zostać obiekt klasy, która ma konstruktor lub destruktor.
•
Składnikiem u nii nie może zostać także obiekt klasy, która ma operator przypisania (=) zdefiniowany przez użytkownika.
•
Unia nie m oże mieć funkcji wirtualnej. (Bo po co, skoro i tak nie może mieć potomstwa7)
Inicjalizacja unii Jeśli unia nie ma konstruktora, to można ją inicjalizować, pamiętając jednak o zasad zie, że inicjalizuje się ją tylko daną odpowiadającą typow i p ierw szego składnika z jej listy składników. union sk ryt ka {
ch a r c; d o u b l e p i; s k r y t k a moja = (
' z '
};
633
Rozdział. 13. S truktury, Unie, Pola bitowe Unia skryka twoja = {
):
skrytka jego = { 3.14 };
H<—
jest błędem
O statn ia linijka to błąd, bo p ie rw szy składnik jest p rzecież ty p u c h a r . Dla w tajem n iczo n y ch : . Pokazany tu sposób inicjalizacjipolega na posłużeniu się klamrą, w której jest dana. Rozeznajesz już na pewno, że jest to tzw. inicjalizacja zbiorcza. Możemy ją stosować, bo taka unia jest tzio. agregatem. (Zob. str. 724)
13.2.2
Unia anonimowa U nia a n o n im o w a jest to taka u n ia, która jako „k la sa " nie ma swojej n azw y , a także nie m a n azw y jed y n y jej (anonim ow y) eg zem p larz. Oto p rzy k ład u n io n int double char int
licz; wspol; znak; *wsk;
); Do sk ła d n ik ó w takiej u n ii o d nosim y się po p ro stu podając nazw ę tego sk ład n ik a. N ie trzeba operatora Y (kropka), bo przed tą kropką musiałaby przecież stać nazwa obiektu - a unia anonimowa nie ma "nazwy obiektu". Gdyby miała - przestałaby być
anonimowa. Z atem d o sk ład n ik ó w o d nosim y się tak: licz = 4; cout << licz; wspol = 6.26; Jak w idzisz, po słu g u jem y się w ięc tym i sk ład n ik am i tak, jakby by ły to zw ykłe n azw y zm iennych. (Trzeba jednak zaw sze pam iętać, że m am y d o czynienia z u n ią, zatem m ożem y przech o w y w ać tam tylko jedną d an ą w określonej chwili).
Co na tym zyskujemy ? O szczęd n o ść miejsca (w ynika to z unii) oraz prostotę notacji. O dnosząc się d o sk ład n ik a nie m u sim y pisać
obiekt.składnik tylko p o p ro stu sk ła d n ik
(to w y n ik a z faktu anonim ow ości). Jasne jest, że sk o ro teraz d o n azw y składników tej unii odnosim y się tak sam o, jak do najzw yklejszych nazw zm iennych, to nazw a składnika nie może być id en ty czn a jak nazw a innej znanej w tym zakresie zm iennej.
3
634
Rozdz. 13. S truktury, Unie, Pola bitowe Pola bitowe K rótko m ów iąc: Jeśli istnieje ju ż zm ienna aaa, to sk ład n ik jakiejś anonim ow ej unii (w ty m sam y m zakresie w ażności) nie m oże m ieć ju ż nazw y aaa. in t
aaa;
union 1 double a a a ; char zzz; };
. //błąd - redefinicja nazwy aaa
U nie an o n im o w e zw ykle sto su jem y w jakimś lokalnym zakresie w ażności. Jeśli chcielibyśm y u n ię an o n im o w ą um ieścić w zak resie globalnym , to p o w in ien jej to w arzy szy ć przydom ek static. (Znaczy on, że d a n a u n ia anonim ow a m a zakres tylko tego je d n eg o pliku). Z faktu, iż do odnoszenia się d o sk ład n ik ó w u n ii anonim ow ej nie u ż y w a m y notacji z k ropką w ynika, ż e nie są sp raw d zan e p ra w a dostępu d o d an eg o sk ład n ik a. W szystkie sk ład n ik i mają w ięc dostęp public. D latego nie m o żn a d ek laro w a ć sk ład n ik a takiej u n ii jako p r i v a t e . Z tego sam eg o po w o d u nie m o ż e być w takiej unii funkcji składow ych. Funkcje sk ład o w e w y w o ły w an e są przecież na rzecz jakiegoś obiektu. Tu nie m a ani nazw y o b ie k tu , an i nazw y sam ej unii. Jeśli zd efin iu jem y unię tak: union { int i; double pi ; i egz, *wsk; to u n ia n ie jest ju ż anonim ow a. Co p raw d a typ tej u n ii n ie m a nazw y, ale jest k o n k retn y eg zem p larz obiektu tej unii - i ma on n azw ę egz. D odatkow o zd e fi n io w aliśm y w skaźnik wsk m ogący pokazyw ać na tak o kreśloną unię. U nia, która - m im o że nie ma nazw y ty p u - m a jakiś obiekt, alb o ma jakiś w skaźnik m ogący na mą p o k a z y w a ć - nie jest ju ż u n ią anonim ow ą. Przy o d n o sz e n iu się do takiej u n ii obow iązuje zw y k ła, z n a n a nam sk ład n ia: o p e ra to r
(kropka) - o d n iesien ie się do s k ła d n ik a obiektu
o p e ra to r - > czyli o d n iesien ie się do sk ład n ik a p o k azy w an eg o w s k a ź nikiem .
13.3
Pola bitowe Pola b ito w e to specjalny typ sk ład n ik a klasy polegający n a tym , że inform acja jest p rz e c h o w y w a n a na określonej liczbie bitów. O to p rzy k ła d class port { // ...
635
Rozdział. 13. S truktury, Unie, Pola bitowe Pola bitowe unsigned int odczyt : 3; // ... };
o d c z y t jest sk ład n ik iem klasy port. M ożna w nim zap isać inform ację 3 bitow ą. T yp te g o składnika to unsigned int. Z au w aż y łeś ju ż , że ta sk ład n ia definicji pola bito w eg o jest b ard zo prosta: p o d ajem y typ, n a z w ę i rozm iar. P o le b ito w e m oże być jakiegokolw iek ty p u całk o w iteg o - a w ięc char, short, int, long- w obu w arian tach : signedalbo unsigned, także m o że b y ć boo 1, a n a w e t enum. «$♦ P o tem n astęp u je (lub nie!) n a z w a tego pola, ♦♦♦ a p o te m d w u k ro p e k i liczba określająca na ilu bitach m a być p rzech o w y w a n a w ty m polu inform acja.
Ta liczba - lub stałe w yrażenie- powinny być większe lub równe zero
Oto przykład klasy, w której składnikami są pola bitowe: class portA i // ... unsigned unsigned unsigned 1/
odczyt tryb
: 1; : 2;
gotow dana
: 1, : 4;
...
1; Są tu, jak w id ać, cztery składniki b ęd ące polam i bitow ym i. W su m ie więc całość inform acji, która m a być zapisana w y m ag a 8 bitów (1+2+1+4). To zm ieściło by się a k u ra t w je d n y m bajcie!
Niepewność To, jak k o m p ila to r rozmieści w pam ięci kom putera o w e d an e, zależy od im plem entacji. M o że upakow ać je p o prostu w jednym bajcie, ale nie m usi. Co więcej, jedno p o le bitowe m oże się n a w e t „przełam y w ać" z jednego bajtu na n astęp n y . To z n a c z y np. z czterech b itó w przypadających jed n em u polu: jeden bit jest w je d n y m bajcie, a pozostałe w następnym . T akże za le ż n e od im plem entacji jest to, czy p rzy d zielo n e nam bity zajm ą kolejno m iejsca o d począw szy najbardziej znaczącego b itu w słow ie, czy o d najm niej zn acząceg o . Jeśli n ap isze m y , ż e pole bitow e ma b y ć typu int bez ok reślen ia czy signed / unsigned to - czy otrzym am y signed czy unsigned - rów nież zależy od im plem entacji.
Skoro tyle niepewności, to co za korzyść ? N ajczęściej a u to rz y piszący o polach bitowych p rzekonują, że pozw alają ono gęsto u p a k o w a ć dane, co daje oszczędność pam ięci. Zapom nij o takiej
4 13
636
Rozdz. 13. S truktury, Unie, Pola bitowe Pola bitowe
argumentacji. Rzeczywiście informacja jest upakowana gęsto, ale spróbui wyobrazić sobie, że potrzebujesz odnieść się w naszej klasie do składnika t r y b Kompu ter pobiera więc określony bajt, w którym ten bit tkwi, po czym musi - że tak powiem - „odcedzić" żądane 2 bity od pozostałych 6 bitów. Sprawa nie jest trudna - wykonuje na przykład jedną operację bitowego iloczy nu logicznego (operator &), a wynik przesuwa o pewną liczbę miejsc w prawo. Niby to proste, ale trzeba to zrobić. To trwa, a kod tej akcji „odcedzania może zająć więcej bajtów niż zaoszczędziliśmy pakując informację do pól bitowych
Oszczędności pamięci więc raczej nie ma, zatem gdzie zalety ? Wyobraź sobie, że do twojego komputera podłączony jest układ sprzęgający z jakimś zewnętrznym urządzeniem (ang: interface). Na przykład sterem kierun ku samolotu, który oprogramowujesz. Taki układ sprzęgający jest w pamięci naszego komputera reprezentowany przez kilka komórek. W tych komórkach interfejsowych właśnie tak gęsto upakowana jest informacja. Upakowanie jest takie po to, by zminimalizować komunikację z urządzeniem - (szybciej się to prześle). Poszczególne bity w tych komórkach mogą oznaczać na przykład to, czy ster ma się wychylić w lewo czy w prawo, następne bity - o ile stopni i tak dalej. Przez fakt pracy z takim urządzeniem zewnętrznym jesteśmy skazani na pracę z danymi, które zajmują określone bity. Nie ma problemu: do tego w ła ś n ie przydają się pola bitowe! Zapytasz pewnie: „-N o, ale co mi po narzędziu, które nie wiadomo jak się zachowa, bo w tylu punktach jest zależne od implementacji'.1' Rzeczywiście, ale przecież możesz łatwo ustalić jak postępuje Twój komputer. Wystarczy, jeśli przyglądniesz się temu obszarowi pamięci. „-D a mi to program niemożliwy do przeniesienia na inny typ komputera! — zaw ołasz pewnie. Kochany, jak będziesz chciał pomachać sobie sterem kierunku za pom ocą innego komputera, to najpierw musisz zamówić do niego nowy, w łaściw y układ sprzęgający za parę tysięcy dolarów. Poprawka w programie przy polach bitowych, to przy tym pestka! Piszesz przecież programy do współpracy z jakimiś zewnętrznymi układami elektronicznymi. N ie wymagaj od takiego programu, żeby mógł ruszyć bez zmian na każdym typie komputera. A tak naprawdę, to jest zaw sze odwrotnie: najpierw masz problem „ster kierun ku", a dopiero potem do niego dobierasz komputer i kompilator, który zrobi to najlepiej. Wtedy już piszesz program na ten ster k ie r u n k u , na ten komputer i ten kompilator. Koniec kazania. Rzeczywistość nie jest jednak taka czarna.
Mimo dowolności implementacji zwykle obowiązują jakieś rozsądne reguły A więc:
Rozdział. 13. S truktury, Unie, Pola bitowe Pola bitowe
637
♦> Dane z pól bitowych pakowane są jedna obok drugiej, w kolejności, w jakiej deklarujesz je w klasie. ♦♦♦ To, czy pakowanie zaczyna się od prawego czy lew ego brzegu słowa (najmniej- i najbardziej znaczące bity słowa), rzeczywiście zależy od implementacji. Z mojego doświadczenia wynika, że najczęściej od pra wego brzegu słowa. ♦♦♦ Jeśli jakieś pole bitowe np. 5 bitowe nie mieści się już w całości w słowie, do którego pakowano do tej pory - wówczas nie jest ono przełamywane - lecz raczej jest w całości umieszczane od początku następnego słowa. Ostatnie bity tamtego słowa pozostają niewykorzystane. Jeśli Twój komputer postąpi według takich reguł, to pola bitowe z poniższej klasy: c la s s sp rz ę g { unsigned a b c d e 1;
zostają rozm ieszczone w pamięci w następujący sposób: 4 13
Rys. 13-2. Przykład rozmieszczenia bitów pięciu pól bitowych struktury s p rz ę g
(zakładam, że posługujemy się komputerem 16 bitowym). Do dokładnego rozmieszczania pól bitowych w słowach pamięci posłużyć się można polami bez nazwy. Służą one jako „wypełniacz" nadający odstęp dwu sąsiednim polom bitowym. Natomiast takie nienazwane pole bitowe, które ma szerokość 0 bitów - jest sugestią, by następne pole bitowe znalazło się w następnym słowie. Jeżeli więc w przypadku tego samego komputera chcemy otrzymać następujący rozkład bitów:
638
Rozdz. 13. S truktury, Unie, Pola bitowe Pola bitowe
Pola b ito w e są sk ład n ik iem klasy, w ięc odnosim y się d o nich id en ty czn ie, ak do innych sk ład n ik ó w klasy w zór ob j; o b j . c = 2;
//definicja obiektu tej klasy II wstawienie liczby 2 do pola o nazwie c
// ©
To ty lk o k o m p u ter m usi się gim nastykow ać z ich „o d c e d z a m e m " - n as to me obchodzi. To sło w o p u b l i c O nie b y ło obow iązkow e, ale skoro w 0 chciałem podstaw ić tam coś z z ew n ątrz klasy, to m usiałem uczynić pole c sk ład n ik iem pub liczn y m . O czyw iście składnikiem klasy jest k ażde pole osobno, a nie cała ta litania jako całość. A by to pokazać w p u n k cie © bez p o w o d u tę litan ię p rzerw ałem . To tak, jak b y m w p rzy p ad k u zw ykłych sk ład n ik ó w napisał: in t
n, o, p;
in t
q, r;
Pole jest w ięc zw ykłym sk ład n ik iem klasy, z tą różnicą, że. •$» - nie m oże być sk ład n ik iem typu s t a t i c , _ nie m ożna p o b rać adresu pola b ito w eg o (jed n o arg u m en to w y m ^ op erato rem & - adres). To w ydaje się oczy w iste - w k o m p u terze ad reso w an e są sło w a lub bajty - nie ad resu je się pojedynczych bitów.
Rozdział. 13. S truktury, Unie, Pola bitowe U nia i pola bitowe - upraszczają rozpakowanie słów
639
M ów im y: „bit 4 w słowie o adresie 146728", a nie: „bit numer 8457291663456 w tym komputerze". Z faktu, iż nie m o ż n a określać ad resu p o la bitow ego w y n ik a, ż e nie m ożna też na to pole p o k a z a ć w skaźnikiem . Pole b itow e n ie m o że też mieć referencji (przezw iska) - bo referencja, to jakby rodzaj ad resu .
¥ N a zak o ń c zen ie d o d a m , że czasem rzeczyw iście pola b ito w e pozw alają nam zaoszczędzić pam ięć. M im o tego, że - jak to p o w ied ziałem - k o d odcedzania żąd an y ch b itó w zajm u je d o d atk o w o pam ięć. W yobraź sobie sy tu ację, gdy po trzeb u jem y pracow ać z o g ro m n ą liczbą dan y ch jed n eg o ro d zaju . N a p rzy k ład tysiące p ró b ek - a w każdej z nich jakieś zjaw isko zaszło lub nie zaszło . Inform acje taką m o żem y zap isać w je d n y m bicie. D anych tych m ają być tysiące, czy n a w e t m iliony. O czyw iście w ó w czas sp ry tn ie um ieścim y je w polach bitowych. O g ro m n a ilość d an y ch zo sta n ie w ów czas gęsto u p a k o w a n a . A co ze stratą p am ięci, którą pochłonie kod w y d o b y w ający tak u p ak o w an e inform acje ? Ż a d e n problem , przecież n ap isze m y sobie funkcję, która obsłuży nam to z a d a n ie. P ró b k a nu m er 57281 ? - p ro szę b ard zo , w y w o łu jem y fu n k q ę i m am y o d p o w ie d ź . W tej funkcji k o d w y d o b y w a n ia inform acji zajm ie oczyw iście jak ieś kom órki pam ięci, ale p rzecież ta funkcja b ę d z ie tylko jedna, a danych - tysiące. Z ysk jest w tedy oczyw isty. 3
13.4
U n ia i p o la b ito w e - u p ra s z c z a ją ro z p a k o w a n ie słów Kilka stro n w cześn iej przestrzegałem : „T rzeb a pam iętać, jaki o b iek t się do u n ii w k ład a - i taki s a m wyjąć, bo w p rz y p a d k u pom yłki m oże się zd arzy ć, że żuć będ ziem y zd ec h łą żabę". O tóż w y z n a m te ra z , ż e w łaśnie do takich sztuczek najczęściej służy mi unia. Nie, niczego n ie ro b ię p rzez pom yłkę! T o działanie całkiem św iad o m e. W kła dam d o u n ii o b ie k t jednego typu, a w yjm uję typu innego! —Że co?... że to b ez sensu? A czy w id ziałeś k ied y ś u rządzenie d o krojenia frytek? W k ład asz cały ziem niak, coś p rzy cisk asz, po czym otw ierasz u rz ą d z e n ie i w y jm u jesz kilkanaście pokrojonych fry tek . Co zaszło? N a stą p iła zam iana (inaczej: konw ersja) ziem n iak a na kilka sam o dzielnych frag m en tó w .
Przejdźmy od tej metafory do konkretu W mojej p ra k ty c e p ro g ram o w an ia m am d o czynienia z elek tro n iczn y m i u k ła d a mi p o m iaro w y m i dostarczającym i d a n e w postaci słów ty p u unsigned long.
640
Rozdz. 13. S truktury, Unie, Pola bitowe Unia i pola bitowe - upraszczają rozpakowanie słów W je d n y m takim słow ie (32 bity) zapisany jest nie tylko w y n ik p o m iaru (zmie rzo n a w arto ść), ale są ta m także rozm ieszczone (na określonej liczbie bitów) inform acje o tym , z którego to u rządzenia i z k tó reg o czujnika p o ch o d zi dany w y n ik p o m iaru . (Mamy do 256 urządzeń, a w każdym może być do 64 czujników). In n y m i słow y: u k ła d y elek tro n iczn e dostarczają mi, co p ra w d a , słow a typu u n s i g n e d l c n g ,a l e j a z dok u m en tacji wiem , że w rzeczyw istości w słow ie tym jest z e s ta w kilku inform acji zapisanych na w y d zielo n y ch g ru p ach bitów. Jeśli chcę o d z y sk a ć jakąś inform ację, to po pro stu m u sz ę u m ieć sobie ją ro zszy fro w ać. T eo rety czn ie nie m a p ro b lem u - po to przecież w C++ są o p erato ry bitow e 6 oraz > > , bym m ógł po szczeg ó ln e inform acje w y d o b y ć za pom ocą operacji b ito w y ch .
Można to jednak zrobić dużo sprytniej! N a c z y m p olega ten sp ry tn iejszy sposób? Otóż, p o słu g u ję się u n ią, w której są d w a sk ład n ik i: ♦♦♦ 1) o b iek t ty p u u n s i g n e d l o n g (to ten „ziemniak"), ♦♦♦ 2) o b iek t ty p u zd efin io w an eg o przez p e w n ą stru k tu rę (to te „frytki") Z aczn ijm y od stru k tu ry , czyli tych „frytek". Jest to s tru k tu ra składająca się z kilku p ó l bitow ych. Z ałóżm y, ż e słow a typu u n s i g n e d l o n g , k tó re o trzy m u je należy ro zu m ieć w taki sposób:
^ 1X
i
1 '
1 i X
nr czujnika
i
i
l i
X
nr urządzenia
i
l
' l ł 1 1 l i i dana (wynik pom iaru)
i
i
i^ )
Rys. 13 -4 . R o z m ie s z c z e n ie inform acji w słow ie typu u nsig ned long d o s ta rc z a n y m p rz e z układy e le k tro n ic zn e V X I.
Taki ro z k ła d inform acji m o żn a zapisać w postaci s tru k tu ry z n astęp u jący m i polam i b itow ym i. struct slowo_vxi { unsigned int dana : 16; unsigned int urządzenie : 8; unsigned int czujnik : 6; unsigned int : 2; >; Jest to definicja stru k tu ry (czyli typu), której d a łe m n azw ę s l o w o _ v x i . W ym yśliłem taką nazw ę, b o stru k tu ra ta p raco w ać b ęd zie z inform acją p o ch o d zą cą z u rząd z eń elektro n iczn y ch p racu jący ch w g s ta n d a rd u VXI. U kłady te dostarczają mi sło w a typu u n s i g n e d l o n g , ale ja m am je ro zu m ieć jako zło ż o n ą informację.
Rozdział. 13. S truktury, Unie, Pola bitowe U nia i pola bitowe - upraszczają rozpakowanie słów
641
Jak w idać z tej s tru k tu ry : jest tu w y n ik p o m ia ru zap isan y na 16 b itach , obok tego n u m e r u rz ą d z e n ia zap isa n y na 8 bitach, a o b o k tego n u m e r c zu jn ik a - zap isan y na 6 bajtach. P o n ie w a ż dw a najbardziej znaczące bity w ty m p rzy p ad k u pom ijam y, z a te m to p o le bitow e nie m u si m ieć nazw y. P rzy p o m in am , ż e ta stru k tu ra d efin iu je „frytki jakie m ają pow stać z „ziem n iak a". A oto sam in s tru m e n t d o cięcia: u nia. union i unsigned lony cale; słowo vxi vxi; ); D ziała b ard zo p ro sto . W kładam do niej całe słow o typu unsigned long (to jest ten „ziem n iak ") i n aty ch m iast m ogę w yjąć pocięte na sto so w n ej w ielkości kaw ałki. Co d o tych „ fry te k " - (pól bitow ych) - to k ażd a jest innej w ielkości - no ale przecież w łaśn ie o to n am chodziło! Ta analogia z frytkami jest stosownie dramatyczna i obrazowa, ale rzeczywistość jest jeszcze badziej zadziwiająca. Nie ma tu, bowiem, nieodwracalnego cięcia, tylko jest jakby patrzenie na zawartość unii raz przez różowe, a raz przez błękitne okulary. C zyli coś, co w id z im y jako „frytki", m o żem y zn o w u zo b aczy ć jako „cały ziem n iak ". O to d o w ó d . Jeśli sp o jrzy m y n a zaw arto ść unii p rzez sk ład n ik o n azw ie v x i , to w idzim y tę zaw arto ść p o ciętą n a pola bitow e („frytki"). Tak p o k azan ą inform ację m ożna łatw o p rzep isa ć d o innych obiektów . int d = vxi.dana; int u = vxi.urządzenie; int c = vxi.czujnik;
Jeśli zaś s p o jrz y m y na zaw artość u nii p rz e z składnik u n s i g n e d lo n g w id z im y całe sło w o (nadal cały ziem niak).
to
long orygmalne_slowo = cale; C zyli to cięcie z ie m n ia k a jest odw racalne! M im o to, trz y m a jm y się tej analogii z frytkam i, bo jest o b razo w a.
Pora na przykład prostego programu, ilustrującego takie zastosowanie unii #include using namespace std; const int max_urzadz = 15; const int max czujn = 10;
4 13
642
Rozdz. 13. S truktury, Unie, Pola bitowe Unia i pola bitowe - upraszczają rozpakowanie słów
//tablica
na w y n i k i p o m ia ró w
// O
int detektor[max urzadz][max_czujn]; bool odczytaj_zdarzenie(); void analizuj_zdarzenie(); unsigned long pobierz_slowo(); /y********************
**★ *★ *★ ****★ ********★ ★ *★ ***★ ★ ★ ★ ★ ★ *★ ★ ★ ★ **
int main () { cout << "Program analizujący ciągle dostarczane " "dane pomiarowe" << endl; while (odczyta j_zdarzenie () ) //p o k a za n ie
//odebranie
d a n ych
i ew en tu a ln a a n a liza
analizuj zdarzenie!);
0 // ©
} cout << "Nie ma wiecej danych, kończymy " << endl; } / / ************************************************************* // O bool odczytaj_zdarzenie() { // W //O
tej fu n k c ji lokaln ie d e fin iu je m y so b ie str u k tu rę ora z unię. ty m , ż e ta k w o ln o - p o ro zm a w ia m y n a stro n ie 654.
//S tru k tu ra sk ła d a ją ca się z p ó l b ito w y c h o p isu ją c y c h ro zk ła d b itó w H p r z y c h o d z ą c y c h z u k ła d ó w elek tro n ic zn y ch V X I
struct slowo_vxi { _ unsigned int dana : 16; unsigned int urządzenie : 8; unsigned int czujnik : 6; unsigned int : 2; //u nia
II &
a n o n im o w a , która p o z w o li na k o n w e r sję całego sło w a na " p o k a w a łk o w a n e'
union ( unsigned longcale; slowo_vxi vxi; );
II ©
cout << "Wczytywanie następnego zdarzenia...\n"; //p ętla
w c z y tu ją c a w ie le s łó w n a leżą cych d o je d n e g o "zdarzen ia"
while(1) { cale = pobierz_slowo(); if (!ca 1e) //je ś li je s t tam ze ro , to ... return false; //koniec ca łeg o p o m ia ru
// O // ©
643
Rozdział. 13. S truktury, Unie, Pola bitowe U nia i pola bitowe - upraszczają rozpakowanie słów //poniżej k o rzysta m y ju ż z postaci v x i - c zy li informacji pociętej na pola bitowe i f (vxi.urządzenie == 0xf8)
// ©
//taki w yją tk o w y numer urządzenia oznacza, że tak naprawdę nic jest ^ //to słow o z daną pomiarowa, ale że to tak zw an y "znacznik końca zdarzenia . cout << "Koniec odczytu danych zdarzenia nr " // © << v x i .dana << endi; _ return true; //zakończyły się poprawnie dane tego zdarzenia
} else //Tu jesteśm y, g d y jest to zw ykłe słowo z w yn ikiem pomiaru _ //N ajpierw spraw dzam y czy num ery urządzenia i czujnika maja sens if (vxi.urządzenie > max_urzadz I1 vxi.czujnik >= max_czujn)
//OO
cont inue; //zła dana, taki detektor nie istnieje, pomijamy
) 1/ dobra dana, pakujem y do określonego detektora
detektor[vxi.urządzenie][vxi.czujnik] = vxi.dana;
//O ©
} / z*************.*********************************************** . . / /O © void analizuj zdarzenie () / / w { << endi; cout << "Analiza zdarzenia. Zadziałały: for(int u = 0; u < max_urzadz ; u++) { for(int c = 0 ; c < max_czujn ; c++> { if(detektor[u] [c]) co u t « "\turzadzenie " << u << ", czujnik " << c << ", odczyt = " << detektorfu][c] << endi; //tu udajem y ze analizujemy I I .... )
} //po analizie tego zdarzenia zerujemy przygotowując się na następne. memset (detektor, 0, sizeof (detektor));//wyzerowanie całe] tablicy U W //**************************************'********** **★ ★ ****★ ★ * //O © unsigned long pobierz_slowo() //ta funkcja im ituje odbieranie danych z elektronicznego układu akwizycji danych unsigned long słowo[] = 0x4060658, 0x60201ff, 0x9058c, 0xf80000, 328457, 100729404, 0xf80001, 197R27, 134417127, 84087033, 50927293, 16848207, 17105686, 16847128, 0xf80002, . 0 //ostatnie słowo puste, oznacza koniec danych pomiarowych
4 13
644
Rozdz. 13. S truktury, Unie, Pola bitowe Unia i pola bitowe - upraszczają rozpakowanie słów s ta tic int licznik ; return s ł o w o [liczn ik + + ] ;
Po wykonaniu tego programu na ekranie zobaczymy: Program a n a l i z u j ą c y c i ą g l e d o s t a r c z a n e dane pomiarowe Wczytywanie n a s t ę p n e g o z d a r z e n i a . . .
Koniec odczytu danych zdarzenia nr 0 Analiza zdarzenia. Zadziałały: u r z ą d z e n i e 2, c z u j n i k 6,o d c z y t u r z ą d z e n i e 6, c z u j n i k 4,o d c z y t u r z ą d z e n i e 9, c z u j n i k 0,o d c z y t Wczytywanie n a s t ę p n e g o z d a r z e n i a . . . Koniec odczytu danych zdarzenia nr 1 Analiza zdarzenia. Zadziałały: u r z ą d z e n i e 1, c z u j n i k 6,o d c z y t u r z ą d z e n i e 5, c z u j n i k 0,o d c z y t Wczytywanie n a s t ę p n e g o z d a r z e n i a . . . Koniec odczytu danych zdarzenia nr 2 Analiza zdarzenia. Zadziałały: u r z ą d z e n i e 1, c z u j n i k 1,o d c z y t u r z ą d z e n i e 3, c z u j n i k 0,o d c z y t u r z ą d z e n i e 3, c z u j n i k 5,o d c z y t u r z ą d z e n i e 3, c z u j n i k 8,o d c z y t u r z ą d z e n i e 5, c z u j n i k 1,o d c z y t u r z ą d z e n i e 9, c z u j n i k 3, o d c z y t Wczytywanie n a s t ę p n e g o z d a r z e n i a . . . Nie ma wiecej danych, kończymy
= 511 = 1624 = 1420
= 572 = 777
= = = = = =
4376 1219 4345 2791 790 5821
0 W funkcji m a in w id zim y pętlę p o m iarow ą. Pętlę - bo p o m iary p rzep ro w ad zą będ ziem y w ielokrotnie. Za każd y m raze m odczytujem y g ru p ę liczb zwani
zdarzeniem.
Zdarzenie W trakcie jed n eg o po m iaru zadziałać m o że kilka czujników . Ich w yniki, postaci liczbow ej, opisują w łaśnie je d n o zd arzen ie. Za ch w ilę m o że n astąp i kolejny pom iar, w którym znow u zad ziałają jakieś czujniki. T aka następni g ru p a liczb, to „ n a stę p n e zdarzenie". W fizyce jądroioej "zdarzenie", to często zbiór liczb opisującycl zarejestrowane kioanty gamma z jednej elementarnej reakcji. Takie, zdarzeń może być nawet kilka tysięcy na sekundę. Pętla while 0 słu ży do odebrania od u k ła d ó w elektro n iczn y ch jed n eg o zda rżen ia —a jeśli to się p o w iodło —n atychm iastow ej an alizy o trz y m a n y c h właśnii d an y ch . (W yw ołanie funkcji analizuj_zdarzenie ©).
Rozdział. 13. S truktury, Unie, Pola bitowe U nia i pola bitowe - upraszczają rozpakowanie słów
Z naszego punktu widzenia - interesująca jest tu funkcja
645
odczyta j_zda-
rzenie. O O to definicja fu nkcji ode zytajzdarzenie.To o n a ilustruje o m a w ia n e w tym p a ra g ra fie zag ad n ien ie. W ciele tej funkcji - zauważysz rzecz straszną: Jest tu definicja struktury i unii, które nie są w zakresie globalnym, ale po prostu we wnętrzu ciała funkcji! O tym , że tak można - porozmawiamy już za chwilę, w następnym rozdziale (str. 654). Wtedy doioiesz się też, dlaczego tak jest lepiej. © O to definicja s tru k tu ry słowo vxi (W racając d o naszego o b ra z k a - jest to definicja tego, na jakie „fry tk i" chcem y „zie m n ia k " pociąć). R o zm aw ialiśm y juz o tej s tru k tu rz e p rzed p rzy k ład em . 0 T akże z n a n a Ci już u n ia, czyli in stru m en t do cięcia. Z jednej stro n y w k ła d a sz do niej całe słow o unsigned long („cały z ie m n ia k "), ale m o ż esz je także zo b aczy ć w form ie pociętej - jako zestaw 3 p ó l bitow ych („zestaw frytek").
Zobaczmy, jak prosto można się tym posługiwać w instrukcjach programu O O to w y w o łan ie tajem niczej (na razie) funkcji pobierz_slowo, k tó ra imituje o d e b ra n ie jednego słow a ty p u unsigned long o d u k ład ó w elektronicznych. S łow o to w k ład an e zostaje d o unii, do sk ła d n ik a cale (skrót o d „całe słow o"). P o n iew aż u n ia jest an o n im o w a, więc w p o n iższej instrukcji w y starczy nazw a sk ła d n ik a cale: cale = pobierz_slowo();
// ©
Z a p o m o cą tej instrukcji cały ziem niak z n alazł się w sk ład n ik u unii. M oglib y śm y od razu ciąć - ale najpierw sp ra w d ź m y , co tam jest. Bo jeśli jest zero, to o znacza na p rz y k ła d , że pom iar zo stał zakończony. (Taka um ow a). W ted y kończym y p ro g ram . Funkcja zawraca wartość false, co w funkcji ma i n powoduje zakończenie pętli i zakończenie programu. Z ałó żm y jednak, że nie było zera i w unii tk w i już jakieś sen so w n e słow o. 1tu n astęp u je g w ó źd ź teg o program u: O to p a trz y m y na tę sam ą zaw arto ść unii, ale nie p rzez składnik unsigned long o n azw ie cale, tylko p rzez składnik ty p u slowo_vxi o n azw ie vxi. Interesuje n as ta g ru p a bitów, której nadaliśm y
Jak w id zisz, po prostu n azy w am y interesujący nas fragm ent - i już! N ie trzeba tu ż a d n y c h operacji bitow ych. Dla porządku wyjaśniam: w warunku tej instrukcji i f sprawdzamy czy w składniku u r z ą d z e n i e jest tajemnicza wartość 0 x f8 . Ta tajemnicza wartość (według dokumentacji VXI) oznacza, że oto następuje koniec przysyłania danych dotyczących jednego zdarzenia. © K ażde zd arzen ie ma kolejny num er. Jeśli rzeczyw iście napotkaliśm y słowo oznaczające koniec zd arzen ia - w składniku dana jest num er tego zdarzenia. W ypisu jem y go na ekranie.
4 13
646
Rozdz. 13. S truktury, Unie, Pola bitowe Unia i pola bitowe - upraszczają rozpakowanie słów cout << "Koniec odczytu danych zdarzenia nr " << vxi .dana << endl; //'*!' P rzypom inam jed n ak , że ta cała spraw a „zd arzeń " jest tylko „fo lk lo rem " tęgi p rzy k ład u . P ra w d z iw ą tre ś c ią je s t to , że coś, co p rz y s ła n e z o s ta ło na o k re ś lo n e j liczbio b itó w m o g ę o d c z y ta ć (i np. w y p is a ć na e k ra n ie ) b e z u c ie k a n ia s ię d o o p e ra cji ilo c z y n u b ito w e g o '& ’ i p rz e s u n ię c ia b ito w e g o T u ta j w id z is z , ż e daną m o g ę w y d o b y ć z e s ło w a typ u unsigned iong po p ro s tu n a z y w a ją c
odpow iednie pole bitowe mukt*%*Mwm**w *1*i
I
i M M H M M rm Nm Mm M n N f f M N I ' 4 i M « mamm m u
4 .*w «U *«**««**H l * * * * * * * * * " *
o o O to inna instrukcja, w której d w ukrotnie k o rzy stam y z m ożliw ości odczytani* g ru p y bitów ze słow a dan y ch tkw iącego w unii. Z now u proste p o słu żen ie si( skład n ik iem obiektu vxi, bez konieczności żad n y ch operacji bitow ych! if (vxi .urządzenie > max_urzadz
II
” .
vxi.czujnik >= max_czujn)
W tej instrukcji i f chodzi ak u ra t o sp raw d zen ie, czy aby n u m e ry u rz ą d z e ń ni< przek ro czy ły ro zm iaró w zadek laro w an y ch w program ie. O © Jeśli nie, to dan ą (o d czy t czujnika) u m ieszczam y w tablicy d w u w y m iaro w ej (;< definicję w id zisz w Ó ). W iersze tej tablicy o d p o w iad ają u rz ą d z e n io m w n aszy m uk ład zie p o m iaro w y m , a k o lu m n y - czujnikom w poszczególnych u rząd z en iac h . detektor[vxi.urządzeniej Ivxi.czujnik] = vxi.dana;
W tej instrukcji aż trzy k ro tn ie korzystam y z pociętego na frytki słow a typu unsigned long. W idzisz jakie to w ygodne?
Mniej ważne funkcje O © F u n k c ja analizuj zdarzenie n ie m a sp e c ja ln e g o z n a c z e n ia w tyifl p ro g ram ie. (N ajw ażniejsze ju ż sobie o m ó w iliśm y pow yżej). Ta fu n k cja imituj*' o p raco w an ie d anych, k tó re w łaśnie zostały z a p isa n e w tablicy d e t e k t o r . Tu „o p rac o w an ie" polega u nas w łaściw ie tylko na w y p isan iu d an y ch n a ekranie. O O N a zak o ń czen ie p racy funkcji analizującej o d b y w a się zero w an ie tablicy, aby p rz y g o to w a ć ją na przyjęcie następ n eg o z d a rz e n ia Realizuje to w y w o łań u funkcji bibliotecznej memset, która służy d o w p isan ia zera do w sz y stk ich baj tó w w y b ran eg o frag m en tu pam ięci. memset(detektor, O, 3izeof(detektor) ) ;
W tym p rz y p a d k u ch o d zi o zero w an ie frag m e n tu pam ięci zaczy n ająceg o się nrt p o c z ą tk u tablicy detektor, a m ającego d łu g o ść sizeof ( d e t e k t o r ). VYl re z u lta c ie w szystkie bajty składające się na tę tablicę zo stan ą w y z e ro w a n e . Oczywiście mógłbym zrealizować to samo za pomocą pętli f o r , (podobn i# jak powyższy loypis na ekran), ale funkcja m e m se t działa szybciej.
647
Rozdział. 13. S truktury, Unie, Pola bitowe Ćwiczenia
O © D efinicja funkcji pobierz słowo, która im itu je o dczyt d an y ch z u k ład ó w elek tro n iczn y ch . T akże i ta funkcja n ie m a z n a c zen ia dla zro zu m ien ia treści tego p a ra g ra fu , ale jeśli jesteś ciekaw y, to spójrz na jej ciało. W jej ciele funkcji p rzy g o to w ałem tablicę z d a n y m i, które funkcja kolejno będzie d o starczać. Co prawda, widzisz tu dane (stałe dosłowne) czasem w zapisie szesnastko wym (heksadecymalnym), a czasem w zapisie dziesiątkowym - ale to nie problem - przecież komputer i tak pracuje na liczbach binarnych. W funkcji jest staty czn y licznik, który za k a ż d y m w y w o łan iem funkcji poka zu je na kolejny elem en t tablicy słowo. Ten elem en t jest rezu ltatem pracy funkcji. Przypominam, że ten l i c z n i k za każdym razem podwyższany jest o jeden, a po zakończeniu funkcji nie ginie tylko „zapada w sen zimowy" taka jest przecież natura lokalnych obiektózo statycznych.
W P o d su m u jm y . W p arag rafie tym zobaczyliśm y sp ry tn y sposób, w jaki m ożna ro z p a k o w y w a ć inform ację p rzy sy łan ą na o k reślo n y ch bitach je d n eg o dłu g ieg o słow a. Sposób ten nie w y m ag a w y k o n y w a n ia operacji koniunkcji bitow ej i p rz e s u w a n ia bitów . C ała sztu cz k a polega na ty m , ż e p o k azan a tu u n ia p ozw ala n a m sp o jrzeć na to sam o m iejsce w pam ięci na d w a sposoby: - ra z jak o n a cale sło w o ty p u unsigned long, a innym razem - jak na stru k tu rę , której s k ła d n ik a m i są o d p o w ie d n io p rzy g o to w an e p o la bitow e.
Ć w ic z e n ia Czym różni się struktura od klasy? Można powiedzieć, że w klasie czy strukturze poszczególne składniki umieszczane są obok siebie. A jak umieszczane są w unii? Dokończ poprawnie następujące stwierdzenie. Rozmiar unii wynika: a) - z ilości jej składników, b) - z wielkości największego składnika, c) - z ilości składników publicznych. Mamy unię, której składnikiem jest obiekt typu char i obiekt typu double. Jeśli w obiekcie tej unii przechowywana jest właśnie wartość typu char, to czy można wówczas włożyć do tego obiektu wartość typu double? Tworzymy obiekt jakiejś unii, a chcielibyśmy od razu nadać mu wartość (inicjalizacja). Jakiego typu musi to być wartość? Jaka jest składnia wyrażenia odnoszącego się do składnika unii - w przypadku unii zwykłej, a jaka w przypadku unii anonimowej? Czy w przypadku unii anonimowej są jakieś ograniczenia co do typu, lub co do nazwy składników? Jeśli tak, to jakie? Jak stworzyć obiekt typu unii, która nie ma nazwy?
648
Rozdz. 13. S truktury, Unie, Pol a bitowe Ćwiczenia Czy do tego, by unia była anonimowa wystarczy, by nie miała nazwy? Czy można zdefiniować obiekty typu pola bitowego? Wybierz poprawne warianty poniższego zdania. Pole bitowe może być typu: a) b) c) d) c) f) ę) h)
signed int double long int bool unsigned int enum signed short char
Który składnik w definicji pola bitowego można opuścić? a) typ b) nazwę c) rozmiar (ile bitów) Jeśli jakieś pole bitowe ma rozmiar zero bitów - co to oznacza? Co to znaczy, że rozmieszczenie pól bitowych w słowie zależne jest od implementacji! Wybierz poprawne zakończenia następującego zdania. Pole bitowe: a) może być typu bool b) może być static c) może mieć egzemplarz obiektu zdefiniowany globalnie d) musi być składnikiem klasy/struktury e) nie musi mieć nazwy, bo można na nie pokazać wskaźnikiem Napisz prosty program, który zapyta użytkownika o datę (np. urodzenia) żądają* wprowadzenia jej jako jednej długiej liczby w postaci DDMMRRRR, gdzie DD oznacz! dwucyfrowy numer dnia w miesiącu, MM dwucyfrowy numer miesiąca, a RRRR numef roku. (W tej notacji data bitwy pod Grunwaldem - 15 lipca 1410 - to: 15071410). Program ma przyjąć tę liczbę z klawiatury jako wartość typu long, a następnie wypisa< na ekranie w postaci dziesiątkowej następujące jej fragmenty - fragment o nazwie niebieski, na który składają się bity 0,1, 2,3, 4, 5 - fragment o nazwie zielony, na który składają się bity od 10 do 14 (włącznie) - fragment o nazwie czerwony, na który składają się bity 16, 17,18. W programie nie można ani raz użyć operacji bitowej koniunkcji ani przesunięcil bitowego. Jeśli posłużysz się unią, to niech NIE będzie ona anonimowa.
W
649
Rozdział. 14. K lasa zagnieżdżona lub lokalna Zagnieżdżona definicja klasy
Klasa zagnieżdżona lub lokalna 4.1
Z a g n ie ż d ż o n a d e fin ic ja klasy o p isa n ia tego p arag rafu przystępuję bez p rzek o n a n ia. M am bo w iem o p isać rzecz, która m oim zdaniem w ydaje się m ało p rzy d atn a. Z d ru g iej jednak s tro n y trzeba o tym pow iedzieć, choćby d lateg o , że jest tu w y ra ź n a różnica w sto su n k u d o analogicznej sytuacji w k lasy czn y m języku C. (W analo g ii d la s tru k tu r w C). M ów ić tu b ęd ziem y o rzeczy b ardzo specyficznej, której znajom ość n ie jest konieczn a d o zro zu m ien ia dalszy ch rozdziałów tej książki. W d o d a tk u nie w sz y stk ie k o m p ilato ry resp ek tu ją tu zalecenia s ta n d a rd u . D latego p ro p o n u ję ten cały ro z d z ia ł, p rzy p ie rw szy m czytaniu tej k siążki, zd ecy d o w an ie opuścić.
D
Jeżeli w e w n ą trz definicji klasy A zam ieścisz definicję innej klasy W - to m ó w im y , ze definicja klasy W jest zag n ieżd żo n a w definicji k la sy A. / / | klasa zewnętrzna // 1 //
class A { class W t
// 1 klasa loewnętrzna
ii
!/ »
};
// » // »
// 1 // // // // 1 // // 1
650
Rozdz. 14. K lasa zagnieżdżona lub lokalna Zagnieżdżona definicja klasy
P o d k re ś la m je d n a k : to d e fin ic ja je s t z a g n ie ż d ż o n a , a nie o b iekt. - - w.Mr» »«ornr riirirr Br- r —- f
T ak a definicja klasy w ew n ętrzn ej jest lokalna d la klasy zew nętrznej. C zy li n\ o n a zak res w ażności ograniczony do w n ętrza klasy, w której tkw i. je st znali jest ty lk o w obrębie klasy, w której ją zagnieździliśm y. C o z teg o w ynika? To, ż e jedynie w obrębie w n ętrza klasy A m ożna kreowrt o b iek ty klasy W. S k o ro definicja klasy w ew n ętrzn ej W jest na z e w n ą trz klasy A zu p ełn ie nieznan - to ró w n ież nie m oże być w skaźnika, k tóry p o k azałb y z z ew n ątrz na obieli k la sy W. Tylko w o b ręb ie w nętrza klasy A m o żn a tw orzyć obiekty k la sy W pokazujące n a nie w skaźniki (W*). Pg3
N ie trak tu j tego jako w a d ę , w ręcz przeciw nie. Po to w łaśnie zagnieżdżeni) z o sta ło w ym yślone!
Zagnieżdżenie nie narusza prywatności Jeśli w obrębie klasy A p o w stan ie obiekt klasy zag n ieżd żo n ej W, to o b o w iązu j ) m im o w szystko, zw y k łe zasad y dostępu. Ż ad en z o b iek tó w jednej z tych klas nie m a żad n y ch p rzy w ile jó w vf stosunku d o obiektu tej drugiej klasy. Składniki p ry w atn e o b u klai są dla nich naw zajem n iedostępne.
Jak wygląda definicja klasy zagnieżdżonej? T ak, jak definicja zw ykłej klasy z tym , że zn a jd u je się w środku innej klasy , fi już m ó w iliśm y . A te ra z zag ad k a. G dzie, w obec tego, są definicje funkcji sk ład o w y ch tej klas) zag n ieżd żo n e j? W iem y, ż e norm alnie m ożna to robić na d w a sposoby: alb o vv( w n ę trz u definicji klasy - są one w ted y typu i n 1 i n e , albo na ze w n ą trz defim cj klasy. Jak to jest w p rzy p ad k u definicji klasy, która je st zag n ieżd żo n a w innej? Są te sa m e dw a sposoby: ♦♦♦ p ie rw szy —definicje w e w n ętrzu klasy zag n ieżd żo n e j ( i n l i n e ) . ♦♦♦ d ru g i sposób - na ze w n ą trz definicji zag n ie ż d ż o n e j klasy, ale... N o w ła śn ie , zagadka: G d z ie k o nkretnie są definicje tych funkcji sk ład o w y ch ? • •
N a z e w n ą trz obu klas? C zy też ty lk o na z ew n ątrz tej z a g n ie ż d ż o n e j W,ale w wewnątrz klasy A?
O d p o w ie d ź jest taka: N a z e w n ą trz obu. Jeśli definicje (czyli ciała) funkcji sk ła d o w y c h klasy zag n ieżd żo n e) nie mają być b ezp o śred n io w definicji sw ej klasy (czyli sposób pierw szy),
651
Rozdział. 14. K lasa zagnieżdżona lub lokalna Zagnieżdżona definicja klasy
to w ó w c z a s n ależy je um ieścić na z e w n ą trz obu klas - czyli w tej sam ej p rz e strz e n i nazw , w której u m ieszczan e są definicje funkcji sk ład o w y ch k lasy A. P rzed ich nazw ą p o w ien ien być kw alifikator z a k re su , dzięki k tó rem u k om pi lator ro zp o zn a d la której klasy są p rzezn aczo n e. Ten k w alifik ato r jest jakby p o d w ó jn y , bo m ów i nie tylko d o której klasy n ależy d an a funkcja sk ła d o w a , ale też g d z ie jej klasa jest zag n ieżd żo n a. Jak to w y g ląd a - zo b aczy m y za chw ilę w p rzy k ła d zie. T ym czasem p rzestro g a: N ie p o p ełn ij b łędu i nie p róbuj definiow ać takich funkcji sk ład o w y ch z a ra z za końcem definicji z ag n ieżd żo n e j klasy W, czyli jeszcze w ew n ątrz klasy A. To b o w iem złam ało b y p rzy jętą w języku C++ z asad ę , że: F u n k c je nie mogą b y ć d e fin io w a n e lo k a ln ie w e w n ą trz in n y c h fu n k c ji i in n y c h (o b c y c h ) b lo k ó w .
>.4
JrfclTiUBWMMfc
Oto przykład: //////////////////////////////////////////////////////////////> class zewn C { i n t a; class wewn // © •k ★ ir II { •k float x; // static int składnik; © // •k k public: // ■k k k © void funjeden(); // k k k © void fundwa{); // // k k k k k k k k k k k k ); ★ ★
★ ★ ★ *
// poniżej jest błqd, tutaj funkcji składowej klasy wewn nie wolno definiować v o id wewn: : f u n j e d e n () I }
O
// umy sposób zagnieżdżani klasy c l a s s lu z a k ; // sarna jej deklaracja }; /////////////////////////////////////////////////////////////// // poprawne miejsce definicji funkcji składowej klasy zagnieżdżonej / k k k k k k k k k k k k k k k k k k k k y c k k - $ r k k k k k k k k k k - k k * - k k k k k k k k k k k k k k k k k k k k k k k k
v o id zewn::wewn::fundwa( { n
II &
/************************************************************* i n t zewn::wewn: : składnik = 66; // o ////////////////////////////////////////////////////////////// c la s s zewn::luzak // © { // ciało tej klasy
652
Rozdz. 14. K lasa zagnieżdżona lub lokalna Zagnieżdżona definicja klasy
Komentarz O Z w y k ła klasa "zew nętrzna" o nazw ie zewr.. 0 Definicja klasy wewn, k tóra jest zagnieżdżona w definicji klasy O. O i © deklaracje dw óch funkcji składow ych klasy wewn. © P róba defin io w an ia fu n k q i © jeszcze w ciele klasy zew n — to błąd. (Pam iętasz przestrogę?) © P o p raw n e miejsce definicji fu nkcji składowej © . Z au w aż, że tutaj n azw a funkcji p o p rz e d o n a jest niemal "ścieżką" do funkcji - są tu d w a operatory zak resu void zewn::wewn::fundwa() K om p ilato r odczytuje sobie to tak: - funkcja fundwa jest funkcją składow ą klasy wewn, która ma definicję zagnieżdżoną w zakresie w ażności klasy zewn. © W k lasie zagnieżdżonej m o ż e być składnik statyczny. Przypominam, że jest to składnik, który będzie wspólny dla wszystkich obiektów tej klasy. Przypominam też, że taki składnik deklaruje się w ciele klasy, ale jego definicja musi znaleźć się na poza ciałem klasy. Tu k la sa jest zag n ieżd żo n a w innej. Wobec tego g d z ie ko n k retn ie definicję s k ła d n ik a statycznego trzeb a umieścić? O czyw iście m asz rację - to niem al identyczna sytuacja, jak o m a w ian e przed chw ilą defin io w an ie funkcji składow ej poza ciałem jej klasy. Definicja sk ład n ik a statycznego klasy zag n ieżd żo n ej - też m usi znaleźć się na ze w n ą trz obu tych klas. © To m iejsce definicji składnika statycznego klasy zag n ieżd żo n ej. int zewn::wewn::składnik = 66; Jak w id ać, także i tu p o słu g u jem y się podw ójnym k w alifikatorem z ak re su , d zięk i czem u inform ujem y kom pilator d o której (zagnieżdżonej) k lasy należy ten sk ład n ik .
Inny sposób zagnieżdżania klasy Do tej p o ry zobaczyliśm y, że - aby dokonać zag n ieżd żen ia klasy - n a le ż y p o p ro stu um ieścić jej definicję w definicji innej klasy. N ie zaw sze to w y g o d n e , b 0 ta z a g n ie ż d ż a n a klasa m o że być przecież dość d u ż a i w ted y tekst n a sz e g o p ro g ra m u stanie się bardzo nieczytelny. Jest inny sp o só b , w id zim y g o ta k ż e w n aszy m przy k ład zie. O W klasie, w której chcemy d o k o n ać zagnieżdżenia - u m ieszczam y ty lk o d e k la rację zap o w iad ającą. class luzak; © N a to m ia st całkiem na z e w n ą trz um ieszczam y definicję (ciało) tej k lasy , k tó rą chcem y zagnieździć. Ta definicja w y g ląd a tak jak zw y k ła definicja k lasy —p o z a je d n y m w yjątkiem : jej n azw a p o p rzed z o n a jest kw alifik ato rem zak re su .
Rozdział. 14. K lasa zagnieżdżona lub lokalna Zagnieżdżona definicja klasy
653
c la s s zewn:: luzak f / / ciało }; Tak in fo rm u jem y k o m p ilato r, że jest to klasa, k tó rą zag n ie ż d ż a m y w innej.
Klasa o definicji zagnieżdżonej ma zakres ważności ograniczony do klasy, w której się znajduje Z tego w y n ik a, że: K lasa zag n ie ż d ż o n a (w ew n ętrzn a) m oże u ż y w a ć zdefin io w an y ch w klasie ze w n ę trz n e j •
n azw ty p ó w (instrukcja t y p e d e f ),
•
typów w yliczeniow ych (enum),
•
p u b liczn y ch sk ład n ik ó w staty czn y ch - bo d o takich nie m usi się docierać podając nazw ę k o n k retn e g o obiektu klasy zew n ę trznej.
W klasie zag n ieżd żo n ej do in n y c h składników klasy zew n ętrzn ej o d n o sim y się tak , jak z każdej innej obcej klasy: obiekt.składnik referencja.składnik
wskaźnik->składnik
Przyjaciele Jeśli k lasa zag n ieżd żo n a (czyli ta "w ew nętrzna”) d ek laru je sw ą p rzy jaźń z jakąś d o w o ln ą obcą funkcją, to w cale nie oznacza, że klasa zew n ętrzn a też się z tą funkcją au to m aty c zn ie p rzy jaźn i. (Pam iętasz? "-Jeśli jakaś klasa chce mieć przyjaciela - musi to powiedzieć loyrażnie sama"). Jeśli zd ec y d o w aliśm y , że w klasie zagnieżdżonej jest definicja (czyli ciało) funkcji zap rzy jaźn io n ej, to je st o n o tym sam ym w zak resie leksykalnym klasy, w której n astąp iło zag n ieżd żen ie. C o to o z n a c z a w praktyce? Przyzwyczaiłeś się ju ż chyba, że na dźwięk słowa "leksykalny1' stają Ci przed oczami- t y p e d e f i en um. T ak, sk o ro ciało tej funkcji zaprzyjaźnionej jest w zakresie leksykalnym - to znaczy , ż e w tej funkcji zaprzyjaźnionej m ożesz k o rzy stać z defin io w an y ch w klasie "zew nętrznej" t y p e d e f i enum.
Ale nie odwrotnie... Jeśli w k la sie zagnieżdżonej jest jakaś deklaracja t y p e d e f , to klasa zew n ętrzn a o tym nic nie wie. Ta deklaracja nie rozciąga się na nią - ma ona tylko zak res klasy zag n ieżd żo n ej.
Skoro tyle ograniczeń, to jaka korzyść z zagnieżdżenia definicji klasy? M oim z d a n ie m niew ielka.
654
Rozdz. 14. K lasa zagnieżdżona lub lokalna Lokalna definicja klasy Polega ona na tym, że zagnieżdżona k lasa jest ona nieznana gdzie indziej. N ie m ożna g d zie indziej k reo w ać obiektów tej klasy.
I
U k ry w an ie czegoś przed sobą sam ym w ydaje się m ało p rzy d atn e, jednak z d a rz a się, że piszem y klasę dla innych u ży tk o w n ik ó w . Dla nich - to, jak w śro d k u z ro b io n a jest klasa - nie jest interesujące. O ni chcą tylko tej klasy używ ać, N ic ich nie obch o d zi czy m y po d ro d ze nie defin iu jem y sobie jakiejś „roboczej" klasy, której obiekty mają nam ułatw ić osiągnięcie jed n eg o z celów. Z a g n ie ż d ż e n ie takiej klasy "roboczej" spraw ia, że staje się ona zu p ełn ie niew i d o czn a dla św iata zew nętrznego. N ie ma więc ry zy k a, że nastąpi kolizja nazw , g d y u ż y tk o w n ik p rzy p ad k o w o w ym yśli identyczną n a z w ę jak n asza „robocza" klasa. To tyle. Jeśli m asz jeszcze jakieś pytania, to najlepiej o d razu d o d am , że jeszcze an i raz, w ż a d n y m p o w ażn y m program ie, nie u ż y łe m zag n ieżd żen ia definicji klas.
14.2
L o k a ln a d e fin ic ja klasy Tutaj m ó w ić b ędziem y o zag n ieżd żen iu definicji k la sy , ale nie w innej klasie, tylko w jakiejś funkcji. Jeśli definicję klasy u m ieścim y w funkcji, to m a ona zak res w ażn o ści lokalny, o g raniczony do bloku tej funkcji. P od k reślam : w ew n ątrz funkcji jest definicja klasy , a nie tylko k o n k retn y eg ze m p la rz jej obiektu. N azw a takiej klasy w id zian a jest tylko w zakresie, w k tó ry m jest zd efin io w an a, czyli p o z a zak re sem tej funkcji nie d a się kreow ać o b ie k tó w tej klasy, an i naw et na nich działać. L okalna klasa nie m oże m ieć sw oich sk ład n ik ó w staty czn y ch . ( C z y l i ta k ic h : " w s p ó ln y c h d la w s z y s t k i c h e g z e m p l a r z y o b i e k t ó w t e j k l a s y "
F u n kcje s k ła d o w e k la s y loka ln ej m u szą b y ć z d efin io w an e w e w n ą trz ciała klasy (b ęd ą w ięc inline). N ie m o ż n a definicji tych funkcji um ieścić po za ciałem klasy bo: ♦♦♦ - z a ra z za definicją klasy, (czyli jeszcze w funkcji, w której jest ona lo k aln ą) nie m ożna, bo łam ałoby to w sp o m n ia n ą już w p o p rz e d n im p a ra g ra fie zasad ę C++, że definicje funkcji n ie m o g ą być zag n ie ż d ż a n o w in n y ch funkcjach, ♦♦♦ - definicji funkcji sk ład o w y ch tej lokalnej k la sy nie m o żn a u m ieścić vt. z a k re sie globalnym , bo tam nazw a tej klasy je st zu p e łn ie n ie z n a n a . Z tych o g ran ic zeń w ynika następ u jąca zasad a p ra k ty c zn a: Skoro fu n k cje s k ła d o w e b ę d ą i n l i n e - to p o w in n y być krótkie.
655
Rozdział. 14. K lasa zagnieżdżona lub lokalna Lokalna definicja klasy
K la s a lo k a ln a p rz y d a je s ię w te d y , g d y m a m y d o c z y n ie n ia z k la s ą b a rd z o p ro s tą i g d y je j u ż y c ie o g ra n ic z a s ię ty lk o d o w n ę trz a te j je d n e j je d y n e j fu n k c ji
w p r o g ra m ie . L okalna k la sa m a zakres lek sy k aln y w n ętrza funkcji, w której się znajduje, w ięc m oże u ż y w a ć zdefiniow anych w niej nazw ty p ó w ( t y p e d e f ) , ty p ó w w ylicze niow ych ( e n u m ) , zd efin io w an y ch w niej zm iennych staty czn y ch , a ta k że n azw z a d e k la ro w a n y c h w niej jako extern. S trasznie to zaw iłe, co? N ie przejm u j się, łatw o to z a p a m ię ta ć tak: W klasie tej m o żn a u ży w ać tego w szystkiego, co ju ż istnieje w czasie kom pilacji oraz lin k o w a n ia . • Definicje ty p ó w w yliczeniow ych (enum) w ted y już istnieją? Tak, są przecież n ap isan e czarno n a b iałym . •
Instrukcje t y p e d e f ? - także - k o m p ila to r je poznał.
•
N azw y z d e k la ro w a n e jako ty p u e x t e r n ? T a k ż e -k o m p ila to r się z nim i z a p o z n a ł i w czasie lin k o w a n ia ju ż będzie d o k ład nie w iadom o, które kom órki w pam ięci o n e zajm ą.
S k o ro tak, to c z e g o nie m a w tym z e s ta w ie ? Z m ien n y ch (obiektów ) au to m aty czn y ch , które najczęściej definiujem y sobie w fu n k cjach . O ne będą bow iem leżały na stosie, w ięc ich ad res na etapie kom pilacji i linkow ania jest n a w e t w przybliżeniu n iezn an y . Spójrz na stos książek na T w o im b iurku - jego w y g lą d zależy nie ty lk o o d tego, co robisz teraz, ale ta k że o d tego, co robiłeś w czoraj i przedw czoraj. K o m p ilato r n ie m oże w ięc w y g en ero w ać dla naszej k la sy lokalnej k o d u , k tó ry sp raw i, ż e z m ien n ą z tej kom órki stosu doda się d o tam tej, a rezu ltat złoży się w jeszcze in n ej. Zatem: ! K la s a lo k a ln a w fu n k c ji - n ie m o ż e u ż y w a ć je j z m ie n n y c h a u to m a ty c z n y c h
..
— — ...»
/««■».-v*m
Fakt, że d efin icja klasy jest u m ieszczo n a w lokalnym blo k u funkcji nie nad aje tej funkcji ż a d n y c h przyw ilejów w sto su n k u do tej klasy. K onkretnie - sk ład n ik i p ry w a tn e tej klasy są dla tej funkcji niedostępne.
>okażmy to wszystko na przykładzie #ińclude using namespace std; int xyz = 10; //zmienna globalna O void zwykła (); , Z *************************************************************/ main () { zwykła(); 0 //
lokalik
BBB;
656
Rozdz. 14. K lasa zagnieżdżona lub lokalna Lokalna definicja klasy ^***************************★ ★ ***★ *★ *************************1
void zwykła()
{ int xyz = 15; int lokal_autom; static float lokal_stat = 77;
* ( // (
II /)
/////////////////////////// class lokalik public : I I static int sss;
// W ad - k la sa lo ka ln a n ie m o że // m ieć s k a d n ik ó w s ta ty c z n y c h
©
//void lok_funskl()
{ cout << "Jestem w funkcji inline (lok_funsk) \n" // << "xyz= " << xyz << endl <<" Globalne ::xyz = " << ::xyz // << lokal_autom //b łą d ! « "\n Lokalne statyczne lokal_stat = " << lokal_stat I I O.K. ! << endl;
a // a / / <8)
} }; ////////////////////////// cout << "Jestem w zwykłej funkcji \n"; lokalik A; A . lok funskl();
□
//
88
Spróbuj sam to skompilować... ...a le n ie z d z i w s ię , j e ś l i p o ja iu u f s ię ja k ie ś b ł ę d y k o m p il a c ji . W ie le k o m p il a t o r ó w z a c h o w u je s i ę t u t a j p o s w o je m u n i e r e s p e k tu ją c s t a n d a r d u .
O to, co p o jaw i się na ek ran ie w p rzy p ad k u kom pilacji tego p ro g ra m u za pom ocą (zg o d n eg o ze sta n d a rd e m ) k o m pilatora G N U C++ (w ersja 3.3.3) Jestem w zwykłej funkcji Jestem w funkcji inline (lckfunsk) Globalne ::xyz = 10 Lokalne statyczne lokal stat = 77
Porozmawiajmy o tym, co widzimy w programie O Definicja obiektu globalnego x y z. © W ew n ątrz funkcji zw ykła d efin iu jem y obiekt a u to m a ty c z n y o tej sam ej n a z w ie xyz. © D efinicja in n e g o obiektu au to m aty czn eg o . T ym ra z e m n a z w a tego o b iek tu (lokal autom) jest n ig d zie indziej nie u ży w an a. © Definicja obiektu lokalnego, ale staty czn eg o (lokal stat).
657
Rozdział. 14. K lasa zagnieżdżona lub lokalna Lokalne nazwy typów
@ Definicja k lasy o n azw ie l o k a l i k. Ta definicja jest lo k aln a d la funkcji z w y k ła . Próba z d e fin io w a n ia sk ład n ik a staty czn eg o w lokalnej k lasie jest błędem . Trze ba ująć to w z n a k i k o m en tarza, by kom pilacja się p o w io d ła . O O d n iesien ie się d o obiektu o n azw ie x y z, jest błęd em , bo n a z w a x y z jest nazw ą obiektu automatycznego funkcji z w y k ła . (N azw a ta z asła n ia nazw ę globalną). © Natom iast od n iesien ie do obiektu globalnego o tej n azw ie - jest poprawne Przypominam, że odniesienie się do obiektu globalnego załatwia nam operator zakresu :: Spójrz na ek ran : w arto ść 10 jest w obiekcie g lo b aln y m (w lo k aln y m jest 15). © Próba o d n ie sie n ia się d o ob iek tu au to m aty czn eg o o n a z w ie l o k a l _ a u t o m byłaby b łę d em . D o obiektu au to m aty czn eg o z funkcji z w y k ła odnosić się nam nie w olno. © N ato m iast d o lo k aln eg o staty czn eg o wolno! N a ek ran ie w id zim y p o p raw n ie w y p isan ą w a rto ś ć 77. (Uwaga: kompilator Visual C++ wersja 6.0 - nie jest tu zgodny ze standar dem i odnieś ien ie się do obiektu statycznego uznaje on n iesłuszn ie za błąd). o o Tak d e fin iu je się eg ze m p la rz ob iek tu klasy l o k a l i k . W ew n ątrz funkcji z w y k ła n a m to w olno. (Po to przecież um ieściliśm y tam definicje tej klasy). N ie w o ln o n am natom iast n ig d zie poza tą funkcją - bo ta klasa jest tam nieznana. D lateg o błędem byłaby definicja w funkcji m a in 0 . N o i słusznie, po to w łaśn ie u czy n iliśm y tę klasę lokalną —by w innych ob szarach pro g ram u nie była z n an a . O 0 W yw ołanie fu n k cji składow ej k lasy l o k a l i k dla ob iek tu tej klasy.
W N a koniec jeszcze jedna oczyw ista chyba u w ag a —klasa nie m u si być lokalna z pow o d u fak tu zam ieszczenia jej definicji w bloku funkcji. R ów nie d o b rze może być lo k aln a z p o w o d u zag n ieżd że n ia jej w jakim kolw iek zw y k ły m lokalnym bloku o z n a c z o n y m w p ro g ram ie d w o m a klam ram i { }. Może to być nawet (!) blok należący do instrukcji i f , f o r itd.
14.3
L o k a ln e n a z w y typ ó w Instrukcja t y p e d e f definiująca now ą nazw ę dla jakiegoś istniejącego już typu m oże być u m ieszczo n a w zakresie w ażności lokalnego b loku lub w ew nątrz definicji klasy . W ów czas jej d ziałan ie ogranicza się tylko d o zakresu tej klasy lub bloku. P o za ty m zakresem w ażności jest nieznana. N ato m iast jeśli tak a instrukcja istniała już na zew n ątrz lokalnego bloku, to w ów czas w e w n ą trz bloku m ożem y skorzystać z efektu d ziałan ia tej definicji. void funkcja()
1 typedef int czas;
{
//--------- lokalny blok ----------------
658
Rozdz. 14. K lasa zagnieżdżona lub lokalna Ćwiczenia typedef unsigned char byte; czas bbb; byte aaa; //... z w y k ł e i n s t r u k c j e
}
I I -------koniec lokalnego bloku -
czas ccc; / / by t e d d d ; błąd ! - nieznane tutaj
H ... zwykłe instrukcje W id zim y , że jest tak, ja k b y to było defin io w an ie zw ykłych zm ien n y ch . Pode* b ie ń stw o jest jeszcze w iększe: Otóż, jeśli w e w n ą trz n aszego lo k aln eg o bloku in stru k cją t y p e d e f zd efin io w alib y śm y n azw ę ty p u c z a s , to ta definicja zasło ni w lo k a ln y m bloku p o p rz e d n ią definicję o tej nazw ie. T en c h w y t jed n ak m ożna zasto so w ać tylko w te d y , jeśli w lo k aln y m bloku ar.i raz jeszcze nie sk o rzy staliśm y z dotychczasow ej definicji (tej zew n ętrzn ej). Jeśli ju ż sk o rzy staliśm y - to p rzep a d ło . P o d o b n ie jest z nazw ą z ew n ętrzn ą: jeśli jest raz u ż y ta w ew n ątrz definicji klasy, to nie m o żn a się ju ż n agle rozm yślić i zasto so w ać ją d o instrukcji t y p e d e f .
P o d o b n ie jak i w p o p rz e d n im paragrafie - zw racam uw agę, że i te sp raw y ró żn ie re sp e k to w a n e są p rz e z różne kom pilatory. Tutaj w ięc m ó w iliśm y o tym, jak być p o w in n o (w edług sta n d a rd u ), a nie o tym , jak to w istocie byw a.
14.4
Ć w ic z e n ia W definicji przykładowej klasy lampa, składnikiem jest obiekt klasy żarówka. Czy jest to zagnieżdżenie definicji klasy? Dlaczego? Gdzie może znaleźć się definicja klasy W zagnieżdżonej w klasie A? Podaj dwa sposoby. Które z poniższych stwierdzeń są prawdziwe? Jeśli klasa W jest zagnieżdżona w klasie A, to mimo braku deklaracji przyjaźni: a) funkcje składowe klasy A mogą korzystać z prywatnych składników obiektów klasy W, b) funkcje składowe klasy W mogą korzystać z prywatnych składników klasy A.
■
D
Które z poniższych stwierdzeń są prawdziwe? Jeśli klasa W jest zagnieżdżona w klasie A, to wskaźniki do obiektów klasy W mogą być definiowane: a) w zakresie globalnym b) w klasie W c) w klasie A d) w funkcji m ain Które ze stwierdzeń są poprawne? Jeśli klasa W jest zagnieżdżona w klasie A - gdzie można umieścić definicje funkcji składowych klasy zagnieżdżonej W:
Rozdział. 14. K lasa zagnieżdżona lub lokalna Ćwiczenia
659
a) w ciele zagnieżdżonej klasy W b) w ciele klasy A, w której zagnieżdżenie klasy W nastąpiło c) gdzieś w zakresie, w którym jest definicja klasy A (czyli na zewnątrz obu klas) ■ H I VII
Czy można w funkcji f zdefiniować lokalną funkcję g? Klasa W jest zagnieżdżona w klasie A. Załóżmy, że w klasie W jest deklaracja (tylko deklaracja!) funkcji składowej void funkcja (). Gdzieś zatem musi być definicja (ciało) tej funkcji. Jak może wyglądać pierwsza linia jej definicji?
Czy klasa zagnieżdżona w innej klasie może mieć składniki statyczne? Jeśli tak, to gdzie umieszcza się ich definicje? Czy klasa lokalna (np. dla bloku funkcji) może mieć składniki statyczne? Jeśli tak, to gdzie umieszcza się ich definicje? Co to znaczy, że klasa W zagnieżdżona w klasie A jest w jej zakresie leksykalnym? Z czego może zatem skorzystać? Czy jeśli w klasie W (zagnieżdżonej w klasie A) jest definicja funkcji zaprzyjaźnionej z klasą W, to funkcja ta jest w zakresie leksykalnym klasy A? Czy klasa A, w której jest zagnieżdżona klasa W, może skorzystać ze zdefiniowanego w klasie W jakiegoś typu wyliczeniowego enura? Jeśli w jakimś bloku (np. funkcji) definiujesz lokalną klasę, to jej funkcje składowe można umieścić: a) w ciele tej klasy L b) w bloku w którym lokalna definicja klasy L nastąpiła c) na zewnątrz bloku funkcji Które ze stwierdzeń są poprawne? Klasa lokalna L (zdefiniowana w bloku funkcji f) może korzystać: a) ze zdefiniowanych w tej funkcji obiektów statycznych, b) ze zdefiniowanych w tej funkcji obiektów automatycznych, c) ze zdefiniowanych w tej funkcji typów wyliczeniowych, d) z obowiązujących w tej funkcji ty p ed ef, e) z obowiązujących w tej funkcji deklaracji excern, f) z nazw globalnych . Mamy lokalny blok {} —dla prostoty opisu mówić o nim będziemy „większy". W bloku tym zdefiniowany jest typ jakiś wyliczeniowy enum. We wnętrzu tego bloku tworzymy następny blok { ). (Mówmy o nim: „mniejszy"). W tym „mniejszym" bloku - także są definicje typów enum. Czy w bloku „większym" możemy skorzystać z definicji enum bloku „mniejszego"? A odwrotnie?
660
Rozdz. 15. K onstruktory i D estruktory K onstruktor
15 K onstruktory i D estruktory
A
by klasa, czyli ty p defin io w an y p rz e z u ży tk o w n ik a - p rzy p o m in ała sw o im zac h o w an iem ty p y w b u d o w a n e , w ym yślono trzy sp ecjaln e ro d z a je funkcji sk ład o w y ch : 1) k o n stru k to r i destru k to r, 2) funkcje sk ła d o w e p rzeład o w u jące op erato ry , 3) o p erato ry konw ersji.
P u n k to m 2) i 3) p o św ięco n e są dalsze ro zd ziały . Tutaj zajm iem y się szczegóło w o p u n k te m p ierw szy m . Co p raw d a, o k o n stru k to rac h i d e s tru k to ra c h n ap o m k n ęliśm y w jednym z p o p rzed n ich ro zd ziałó w , jednak teraz czas p rzy jrz eć im się bliżej.
15.1
K o n s tru k to r K o n stru k to r to specjalna funkcja sk ład o w a, k tó ra n azy w a się ta k sam o , jak klasa. W ciele tej funkcji (k onstruktora) m o ż e m y zam ieścić in stru k c je nadające w arto ści p o czątk o w e sk ład n ik o m d efin io w an eg o w łaśn ie o b iektu; B ow iem , w tra k c ie d efin io w an ia obiektu, p rzydziela m u się m iejsce w pam ięci, a n astęp n ie u ru c h a m ia n y jest dla niego konstruktor. Z w ró ć u w a g ę , że: K onstru k to r sam nie p rzy d ziela pam ięci na obiekt. O n m oże ją tylko zainicjalizow ać. Z atem u ż y w a ją c analogii: nie jest to b u d o w niczy obiektu - raczej: d ek o rato r w n ętrz. W sam ej treści k o n stru k to ra nie ma nic n ad zw y czajn eg o : ot, tak a s o b ie funkcja sk ła d o w a . N ajw ażniejszym aspektem k o n stru k to ra jest to, że jeśli klasa m a o d p o w ie d n i kon stru k to r, to jest on a u to m a ty c z n ie u ru c h a m ia n y p r z y d efin io w a n iu k ażd eg o obiektu tej klasy.
Rozdział. 15. K onstruktory i D estruktory K onstruktor
661
Cechy konstruktora K o n stru k to r m oże b y ć p rzeład o w y w an y . Jest to b ard zo częsta praktyka, w definicjach klas w id z i się zw ykle kilka w ersji k o n stru k to ra (ró żn ią się o czyw iś cie listą a rg u m e n tó w ). K o n stru k to r nie m a w y sp ecy fik o w an eg o żad n eg o typu w arto ści zw racanej. N ie zw raca n ic —n a w e t ty p u v o id ! Jeśli w ięc w ciele k o n stru k to ra jest instrukcja r e t u r n , to nie m o ż e p rz y niej stać ż a d n a w artość. Tylko śred n ik . K o n stru k to r m o ż e b y ć w y w oływ any d la tw orzenia o b iek tó w z p rzy d o m k am i c o n s t i v o l a t i l e , ale sam nie m oże być funkcją ty p u c o n s l i v o l a t i l e . (Pamiętamy, że z innymi funkcjami składowymi jest tak, że na rzecz obiektów takiego typu mogą pracować tylko te, które obiecują, że także są odpowiednio: c o n s t lub v o l a t i l e . Juk widać, konstruktora to zastrze żenie nie obowiązuje). K o n stru k to r n i e m o ż e być także typu s t a t i c - m ię d z y in n y m i dlatego, że ma pracow ać na n iestatycznych sk ład n ik ach klasy. Jako s t a t i c - nie m iałby d o tego p raw a. (M usi m ieć przecież w sk a źn ik t h i s ) . Dla w tajem n ic zo n y ch p rzypom nę, że k o n stru k to r nie m o ż e być także typu v irtu a l. N ie m o żn a p o słu ż y ć się adresem k o n stru k to ra.
Wspomnienie o unii Jeśli o b iek t m a być sk ład n ik iem unii, to jego klasa nie m oże m ieć k o nstruktora. Łatw o to zro zu m ieć: jeśli jest k o n stru k to r, to startuje o n d o pracy au to m a tycznie. W p r z y p a d k u , gdyby w unii b y ło kilka obiektów k las z k onstruktoram i, to p rzecież n ie m a sen su , by w szystkie ru szy ły do pracy zw alczając się naw za jem. Skoro z g o d y b y ć nie może, to lepiej niech u n ia k o n stru k to ró w nie ma. Dla w tajem n iczo n y ch : Uwaga: co prawda kompilator, w pewnych sytuacjach, może dla jakiejś1 5 klasy automatycznie wy generować konstruktor, ale taki się tutaj nie liczy (on w unii nie przeszkadza).
15.1.1
Przykład programu zawierającego klasę z konstruktorami Ilustruje o n n ie k tó re z om ów ionych cech. Problem jest taki: m a m y na ekranie narysow ać kilka p rz y rz ą d ó w p o k ład o w y ch . W szystkie m ają w y g ląd w yśw ie tlacza cyfrow ego. O czyw iście od razu n asu w a się, że k a ż d y taki m iernik jest obiektem klasy p rz y rz ą d . Taka w łaśn ie klasa zd efin io w an a jest w naszym przy k ład zie. W pierwszych wydaniach tej książki program, który tutaj następował posługiwał się funkcjamibibliotecznymi kompilatora Borland C++ (wersja 3.1). Funkcje te pozwalały na pisanie tekstów w dowolnym miejscu ekranu alfanumerycznego. Argumentami tych funkcji były na przykład współrzę-
662
Rozdz. 15. K onstruktory i D estruktory K onstruktor
due x, y miejsca na ekranie (czyli która kolumna i który rzyd) oiaz tekst do wypisania. N iestety nie były to funkcje standardow e. Mogli z nich korzystać tylko Ci czytelnicy, którzy posługiwali się tym samym kompilatorem. Dlatego teraz jest to program gruntown ie zmieniony - tak by mogl i go skompilować i uruchomić wszyscy pracujący na z dowolnym (zgodnym ze standardem) kompilatorem. Wydaje mi się, że jest nawet bardziej pouczający.
Trzeba sobie jakoś radzić —powiedział baca, zawiązując buta dżdżownicą W zw ykłych w aru n k ach ek ran alfanum eryczny nie pozw ala na p isa n ie w d o w o ln y m jego miejscu - czyli ra z tu, raz tam . M ożna tylko d o p isy w ać d o ostatn io w y p isy w an ej linii, lu b przejść do następnej. M y jednak w tym p ro g ram ie chcielibyśm y u m ieszczać teksty raz na d o le , ra z na g ó rze ek ran u . Raz z p raw ej, ra z z lewej. A by to było m ożliw e - p o słu ż y m y się p ro sty m sposobem . Otóż: nie b ęd ziem y pisać tekstów w p ro st na ekranie, ale będ ziem y je um ieszczać w obiekcie klasy s t r i ng. Ten string b ę d z ie tak d łu g i (25 linijek), że - g d y go od czasu d o czasu b ędziem y w y p isy w a li na ekranie - b ęd zie pokryje całą jego pow ierzchnię. W y m ien i całą zaw arto ść ek ran u - jak n astęp n a k latk a an im o w an eg o film u. Jak k o n k retn ie to zrobim y? * N ajp ierw d o strin g u załad u jem y 25 linijek sam ych spacji. T o w y p iszem y na ekranie. E k ran zrobi się p u sty - z a p isa n y sam ym i spacjam i. ♦♦♦ Jeśli teraz zap ra g n ie m y , by w linii n r 7, p ocząw szy o d 47 kolum ny, pojaw ił się n a p is - trzy litery "abc", to najpierw o b liczy m y sobie, w którym m iejscu n aszeg o stringu p o w in n iśm y je um ieścić - by w p rzy kolejnym w y p isan iu stringu na ek ra n — te trzy liter p o ja w iły się w żą d a n y m m iejscu. G d y jesteśm y g o to w i - ten cały string (25 linijek), w now ej postaci w y p isu jem y na ekran. Z o b aczy m y teraz z n o w u spacje, ale w śró d nich - w linii 7 - będą litery abc . G d y pojaw i się konieczność, by d o b ieżąceg o w y g ląd u e k ra n u w pisać coś w linijce n r 2 p o cząw szy od k o lu m n y n r 22, p o stęp u jem y p o d o b n ie m o d y fik u jem y n asz strin g w pisując w jego o d p o w ied n ie m iejsce n ow y tekst. (Tego p o p rz e d n ie g o w linii n r 7 - nie ru szam y ). G d y po tej m odyfikacji w y p isz e m y ten cały strin g (25 linijek) na ek ran ie - w linii nr 2 pojaw i się n o w y tekst, a w linii 7 - istniejące ju ż w cześniej litery "abc". P ro ste p ra w d a ? S p ra w a m i, które w łaśn ie om ów iliśm y, w p o n iż sz y m p ro g ram ie zajm ie się klasa o n azw ie ekran_alfanumeryczny. D o d a tk o w o w n aszy m p ro g ram ie jest klasa przyrząd. O biekty tej klasy sam e b ę d ą ro zm aw iały z e k ran e m , czyli sam e b ę d ą d b ały o w y p isy w a n ie się na ek ran ie. (To jest w łaśn ie w sp an iało ść p ro g ram o w an ia o b iektow o o rien to w an eg o ).
Rozdział. 15. K onstruktory i D estruktory K onstruktor
663
Przyrządy rysować będziemy za pomocą liter 'V i znaków podkreślenia. Oczywiście byłoby o wiele ładniej, gdybym posłużył się tak zwanymi znakami semigraficznytni, które pozwalają na narysowanie ładniejszej ramki. Mogłem też przejść do trybu graficznego i narysować piękne instrumenty pokładowe, jak w symulatorach lotu. Byłyby to jednak rzeczy zbyt związane z jakimś konkretnym typem komputera, kompilatora i określoną biblioteką graficzną. N i e o t o c h o d z i ł o m i z v t y m p r z y k ł a d z i e . Ilustruje on tylko różne a sp ekty konstruktorów .
Plik nagłówkowy: p r z y r z ą d , h #include using namespace std; #include #include łfinclude
i, // dla: log, // dla: time
pow
u *>*************************»************************************************************#»******* // Aby możliwe było narysowanie równych ramek, czcionka liter na ekranie //p o w in n a
b y ć p ro p o rc jo n a ln a , (p ro p o rcjo n a ln a je s t na p r z y k ła d czcio n k a "C ouricr").
iiiiiiiiiiiiiiiifiiiliiiiiiiiiliiim // class ekran alfanumeryczny
//
{ string tresc; char znak_tla; public: enum { szerokość ekranu = 63, wysokosc ekranu = 2 4
©
},
//
©
// konstruktor ekran_alfanumeryczr.y(char znak = ’ ')
// ©
{ znak_tla = znak; wyczysc();
)
/ / --------------------------------------------------------void wyczysc ()
{
4 1
// ©
. . , . // wypełnienie całego "ekranu" samymi znakami tła int i l e z n a k o w = wysokosc_ekranu * szerokosc_ekranu; tresc.assign (ile znaków, znak_tla);
// wstawienie znaków przejścia do nowej linii ekranowej po każdej szerokości ekranu for(int i = 0 ; i < wysokosc_ekranu ; i++)
{ tresc[(i+l)
* szerokosc_ekranu - 1] =
'\n';
} void wyświetl ()
// ©
( cout «
tresc «
flush;
} / / ----------------------------------_ . // Funkcja składowa wpisująca w wybranym miejscu zadany tekst do treści ekranu void napisz (int kolumna, int rząd, string tekst)
{
// ©
5
664
Rozdz. 15. K onstruktory i D estruktory K onstruktor tresc.replace(
l ///////////////////////////////////////////////////////////////////////////////////////////////////////////////V////////////// ~ //u // © class przyrząd string nazwa; string jednostki; int pokazuje; int x, y; / / g d z ie je s t n a ekra n ie te n p r z y r z ą d static int ile_nieznanych;
// ©
public :
// k o n s t r u k t o r y
.
przyrząd (int, int, string, string, przyrząd (void);
int —
// z w y k ł e f u n k c j e
_
// ©
),
sk ła d o w e
void zmien_wskazanie (int w) ; void narysuj (void);
-/Z'//////////////
Plik: p r z y r z ą d , cpp #include "przyrząd.h" tinclude
// d e f i n i c j a
//
g lo b a ln e g o o b ie k tu e k ra n
OQ
// O©
ekran_alfanumeryczny ekran('.');
// d e k la r a c ja f u n k c j i p o m o c n i c z e j string liczba_na_napis(int wartość);
przyrząd::przyrząd(int xx, int yy, string nnn, string jedn, int w)
//
{ nazwa = nnn; jednostki = jedn; pokazuje = w; x = xx ;
y = yy;
//O ©
narysuj();
11 . .. przyrząd::przyrzad(void) t
// j e s z c z e
++ile_nieznanych; jednostki = "
// w y p e ł n i e n i e
ta b lic y te k s te m
//O ©
-
je d e n n ie z n a n y w y ś w ie tla c z
W s k a ź n ik n r n
nazwa = "Wskaźnik nr "; nazwa += liczba_na_napis(ile_nieznanych);
//O ©
Rozdział. 15. K onstruktory i D estruktory K onstruktor
665
//w y m y ś le n ie d la p r z y r z ą d u je g o p o z y c ji n a e k ra n ie x = 33; y
=
1+
(ile_nieznanych-l)
pokazuje = n a r y s u j ();
*
O©
4;
//co m a o n p o k a z y w a ć
0;
H n a r y s o w a n ie g o n a " e k ra n ie "
1 1 ******** d a ls z e fu n k c je sk ła d o w e ***************************************************************
im »*»**Ą***»***i»*********t ********************************************************************** void
p r z y r z ą d : : z m i e n _ w s k a z a n i e (i n t
w)
t pokazuje
=
w;
n a r y s u j () ;
/ z************************************************************* //O © przyrząd:: n a r y s u j ()
v" o i d
* //F u n k c ja o d p o w ia d a ją c a za n a ry so w a n ie na e k ra n ie ra m k i z " w y ś w ie tla c z e m c y fr o w y m " e k r a n .n a p i s z ( x , e k r a n . n a p i s z (x,
y, y+1,
"I
ekran.napisz(x, e k r a n .n a p i s z (x,
y+2, y+3,
"I "I_
"); * i") i" ) i" )
//O ©
// R y s o w a n ie lu y p c łn io n c j sp a c ja m i ra m k i o d b y ło się n a jp te rw . //Ta k o le jn o ść f e s t w a ż n a , bo sp a cje ze strin g & w p o w y ż e j - z a m a z a ły b y te k s ty , które II n a p is z e m y p o n iż e j. ekran.napisz(x+4, e k r a n .n a p i s z ( x + 7 , ekran, n a p i s z ( x + 1 4 ,
y+1, y+2,
nazwa); liczba_na_napis(pokazuje));
y+2,
jednostki
);
//© ©
e k r a n .w y ś w i e t l ( ) ;
} int
//© O
p r z y r z ą d : :i l e _ n i eznanych;
// To je st fu n k c ja , k tó ra p o zw a la za m ie n ić lic zb ę c a łk o w itą na s tr u ig //Jest o n a z r e a liz o w a n a n a d w a sp o so b y, a ich w y b o r u d o k o n u je się za p o m o c ą // k o m p ila c ji w a r u n k o w e j.
4 1
H 2 ....je d e n sp o só b j e s t dla ty c h , k tó r z y c zy ta ją k s ią ż k ę po raz //p ie r w s z y i n ie z n a ją je s z c z e k la sy b ib lio teczn ej o strin g stre a m // T u ta j z a m ia n ę lic z b y n a w y ra ża ją c y ją s tr in g p r z e p r o w a d z a m y // że tak p o w ie m "n a p ie c h o tę " sposobem p o z n a n y m w ro zd zia le //o klasie s tr in g , n a s tr o n ie 5 1 7 .
//2. — D r u g i sp o s ó b - to w y k o r z y s ta n ie k la sy o s tr in g s tr e a m , // (jak w id a ć sp o só b te n je s t b a rd zo p ro sty )
*************************w******************************************************* yy*>U**********W ******>****W****************************** / @0 string liczba_na_napis(int wartość) #def ine #if
JESTEM
JESTEM
WTAJEMNICZONY
WTAJEMNICZONY
==
1
/ / O -
nie,
1
-
tak
/ 0©
0
//W e r s j a U la n i e w t a j e m n i c z o n y c h if(wartość
==
0)
return
0©
//
po
prostu
zero
5
666
Rozdz. 15. K onstruktory i D estruktory K onstruktor string rezultat; i f (wartość < 0)
{
//w p is u je m y z n a k m in u s //i o d tą d p r a c u je m y ja k z lic zb a d o d a tn ia
rezultat += ' wartość = -wartość;
int reszta = wartość; int pułap dolny = (int)
powflO.O,
(int)
l o g l O ( (doub.e)
wartość)
);
U p u ła p d o ln y to n a jb liż s z a w a rto ść p o tę g i 1 0 , k tó ra n ic je s t w ię k sza n i z za d a n a w a rto ść. // N p . d la w a rto śc i 8 5 2 p u ła p te n to je s t 1 0 0
for ( t
)ulap dolny > 0 ; pulap_dolny /= 10)
int cyfra = reszta / pulap_dolny; reszta = reszta % pulap_dolny; rezultat += char('0' + cyfra);
J
return rezultat;
^6 1 S0
00
// W ersja dla w ta je m n ic z o n y c h / / D o p r z e tw o r z e n ie in fo r m a c ji lic zb o w e j na s t r i n g n a jle p ie j p o s łu ż y ć się
//s tr u m ie n ie m w y jś c io w y m p ły n ą c y m d o s tr in g u . ostringstream strum; strum << wartość; return strum.str(); #endif }
//d e fin ic ja o b ie k tu s tr u m ie n ia II <— t u n a s tę p u je za m ia n a
//z w r o t r e z u lta tu
Główny plik programu - ilustrujący użycie konstruktorów ******************************** // G łó w n y p lik p ro g ra m u (za w ie ra ją c y fu n k c ję m a in ) #include "przyrząd.h" extern ©Jcran alfanumeryczny ekran; void zwloką (int sekund); //d ekla ra cja f u n k c j i p o m o c n ic ze j ****>■***************************************** ...... ............. 1.+++************************************-******************' u*************************************************** int m a i n ()
{ ekran.wyczysc();
//& &
/I definicje obiektów --------------------------------------- “ przyrząd przyrząd przyrząd przyrząd volatile
Pred(2f 1, "Prędkość", wezlow , U 0 ; V ari(2, 7, "Wznoszenie", "stopy/sec ); A; B; przyrząd C;
const przyrząd
Udzw(2, 11, "Udźwig maksymalny", "ton", 15000 );
//s y m u la c ja n o r m a ln e g o ciągłego w y ś w ie tla n ia — for(int i = 0 ; i < 30 ; i++)
Vari.zmień wskazanie(i);
//© © //© O //© ©
Rozdział. 15. K onstruktory i D estruktory K onstruktor
667
O Tu w łączam y k ilk a p lik ó w nagłów kow ych zaw ierających d ek laracje od p o w ied nich funkcji b ib lio teczn y ch . W k om entarzach um ieszczone są n a z w y funkcji, o których dek laracje n am tu chodzi.
668
Rozdz. 15. K onstruktory i D estruktory K onstruktor
Klasa e k r a n _ a l f a n u m e r y c z n y © Definicja klasy e k r a n _ a l f a n u m e ry c z n y , która pozw oli nam n a um ieszczanie tek stó w różnych miejscach ekranu. Składnikiem tej klasy je st o b iek t klasy s t r i n g o n azw ie t r e s c . To on zaw ierał będ zie w ypisyw aną o d czasu d o czasu całą treść ek ran u . D rug i sk ład n ik jest typu c h a r i m a n azw ę z n a k t l a . Będzie o n n am służył do p rzec h o w y w an ia zn ak u , który u ży tk o w n ik klasy w ybiera na zap e łn ia n ie tła ek ran u . (N ajczęściej będzie to spacja). © Z w y k le ek ran alfan u m ery czn y ma 25 linii tekstu, a w każdej jest 80 zn ak ó w . Aby je d n ak w y d ru k p ro g ram u zm ieścił się na stronie tej książki - ograniczyłem szero k o ść ek ran u d o 63 znaków , a w ysokość do 24 linii. W artości te są w klasie stałym i typu w y liczen io w eg o , bo jest to n ajp ro stszy sposób zd efin io w an ia stałych dla klasy.
O Oto konstruktor klasy e k r a n _ a l f a n u m e r y c z n y . Jak w idać, m a o n jed en arg u m en t ty p u c h a r . Definiując obiekt tej k lasy m ożna będ zie zd ecy d o w ać, jaki znak będzie zap ełn iał cały ekran. W arto ścią d o m n ie m aną tego arg u m e n tu jest znak s p a c ji-b o to w ydaje się n ajbardziej oczyw iste. : W idziałeś ju ż zapewne zoydruk ekranu po w ykonaniu program u - i ju ż wiesz, że z tego domniemania nie skorzystaliśm y, bo znakiem tła jest . (kropka). W ciele k o n stru k to ra, oprócz zap am iętan ia w y b ran eg o zn ak u tła, w id zisz w y w o łan ie funkcji w y c z y s c . Z naczy to, że z k o n stru k to ra w o ln o w y w o łać inną funkcję sk ład o w ą d an ej klasy. © Funkcja o n azw ie w y c z y s c po w o d u je w yczyszczenie do ty ch czaso w ej z a w a rto ści strin g u t r e s c . R obim y to za pom ocą funkcji składow ej a s s i g n . (To funkcja sk ład o w a klasy s t r i n g - poznaliśm y ją na stronie 573). Funkcja ta p o w o d u je, że w stringu t r e s c zostaje u m ieszczo n y ch 63*24 je d n a kow ych zn ak ó w ( z n a k _ t l a ) . Sam e zn ak i tła je d n a k nie w ystarczą, trzeb a w niektórych m iejscach (o d p o w ia dających końcow i linii) um ieścić zn ak i now ej linii. Jeśli linia m a długość 63 zn ak ó w , to zn ak i now ej linii należy u m ieścić w strin g u t r e s c na pozycjach (liczym y od zera!): 6 2 ,1 2 5 ,1 8 8 ,... itd. R obim y to z pom ocą p ę tli f o r . © Funkcja wyświetl - po prostu w ysyła na ek ran treść p rzy g o to w a n e g o stringu y całą n o w ą zaw arto ścią ek ran u . Z a m ia st końcowego e n d l w id z im y tam f l u s h . T e n , tak zw a n y manipulator, h/m się różniod e n d l , że co prawda, także inicjuje w ypis na ekran, ale po w ypisanym tekście nie dodaje znaku nowej linii. © Definicja funkcji sk ład o w ej o nazw ie n a p i s z. Jej a rg u m e n tam i są d w ie w artości i n t (n azw an e r z ą d , k o lu m n a ) opisu jące m iejsce, g d z ie na ek ran ie m a się rozpo cząć string p rzy słan y tu jako trzeci arg u m e n t. K o rzy stam y tu z funkcji r e p l a c e , o której ro zm aw ialiśm y w ro z d z ia le o k l a s i e s t r i n g . ( Z o b .s t r . 547). I tiresc.replace ( (szerokość_ekranu * rząd) + kolumna/ tekst.length(), tekst);
Rozdział. 15. K onstruktory i D estruktory K onstruktor
669
To w y w o łan ie funkcji p o w o d u je, że w strin g u , będącym treścią ek ran u , p o d m ien io n y zo stan ie p e w ie n fragm ent. P ierw szy a rg u m e n t tego w y w o łan ia określa, g d zie ten fra g m e n t m a się zacząć. W w y w o łan iu w id zisz p ro ste w yrażenie, które p rzelicza pozycję o k reślo n ą sp o so b em „ rz ą d i ko lu m n a" na pozycję z n a k u w stringu. (N um erację rz ę d ó w i ko lu m n , zw yczajem C++, za c z y n a m y od zera). Jeśli w ięc na p rz y k ła d chcielibyśm y coś w ypisać na ek ran ie w trzeciej kolum nie p ie rw szeg o w iersza, to znaczy, że chodzi (w tym strin g u ) o pozy cję nu m er (3 z e r o k o s c _ e k r a n u
*
1)
+
3
czyli w n aszy m p rz y p a d k u (63 * 1) + 3 = 66 ♦♦♦ D ru g im a rg u m e n te m w y w ołania funkcji r e p l a c e jest liczba określa jąca ile z n a k ó w m a zostać podm ienionych. W naszym p rz y p a d k u ma być d o k ła d n ie tyle, ile now ych zn ak ó w ma być w staw io n y ch - tę liczbę określa nam w y rażen ie t e k s t . l e n g t h ( ) . ♦♦♦ Trzecim a rg u m e n te m jest tekst, który chcem y u m ieścić gdzieś na ekranie.
Klasa p r z y r z ą d © O to definicja klasy o n azw ie p r z y r z ą d , która rep rezen tu je w yśw ietlacze cy fro w e pojaw iające się na ekranie. S k ład n ik am i tej klasy są m iędzy innym i d w a obiekty klasy s t r i n g - jeden z nich słu ży ć m a d o p rzech o w y w an ia n azw y tego, co w yśw ietlacz p o k azu je (iip. prędkość), a d ru g i do p rzech o w y w an ia stringu jednostek (np. km/h); S k ład n ik o n azw ie p o k a z u j e —słu ży d o przechow yw ania w artości, którą na e k ra n ie d a n y p rz y rz ą d m a bieżąco w yśw ietlać. Składniki x, y o k reślają położe nie p rz y rz ą d u n a ek ran ie (dokładniej: położenie jego lew ego g ó rn e g o rogu). © W id zim y też tu taj d e k la ra c ję statycznej danej składow ej tej klasy. (Statyczna, czyli w sp ó ln a d la w szystkich obiektów tej klasy). P rzy p o m in am , ż e (taka jak tutaj) staty czn a d an a sk ład o w a m u si być zd efin io w an a na z e w n ą trz ciała k la sy . Jej d e fin ic ję w id z im y w m iejscu ze znaczkiem © O Z a u w a ż kwalifi k a to r zak resu z n azw ą klasy, oraz to, że w miejscu definicji nie p o w ta rz a się już sło w a s t a t i c .
© Klasa p r z y r z ą d ma kilka wersji konstruktorów - jest to, jak w iad o m o , p rzeładow anie. W idzim y tu d w a k o n stru k to ry , ale p o n ie w a ż jeden z nich m a (ostatni) a rg u m e n t dom niem any, w ięc to tak, jakbyś m y m ieli trzy n astęp u jące konstruktory: przyrząd (int, int, char *, char *, int); przyrząd (int, int, char *, char *); przyrząd (void);
P rzy p o m in am , ż e a rg u m e n t dom niem any funkcji określa się w deklaracji w ew n ą trz klasy, a jeśli sam a definicja takiej funkcji (tutaj: ko n stru k to ra) jest gdzieś na
4 1
5
670
Rozdz. 15. K onstruktory i D estruktory K onstruktor zew n ątrz definicji klasy, to przy jej definicji (O © ) już się o ty m nie w spom ina po raz drugi.
D aw no tem u rozm aw ialiśm y o tym , jak rozmieścić klasy w plikach. Tuta) w idzisz tego ilustrację. W pliku nagłów kow ym p r z y r z ą d . h zn ajd u ją się defi nicje klas e k r a n _ a l f a n u m e r y c z n y o ra z p r z y r z ą d . Tak się składa, ż e w szystkie funkcje klasy e k r a n _ a l f a n u m e r y c z n y były proste, w ięc zo stały zdefiniow ane w w ew n ątrz definicji ich klasy (czyli są in lin e).
Dla o d m iany, a ta k ż e dlatego, że fuhkcje składow e klasy p r z y r z ą d są bardzie) ro zb u d o w an e - zd efiniujem y je na ze w n ą trz ciała klasy. Jak p am iętam y , w tym celu tw orzy się o so b n y plik (już nie nagłów kow y).
Zatem od tej pory omawiamy plik o nazwie p r z y r z ą d . c p p O czyw iście w ty m pliku definicje klas e k r a n _ a l f a n u m e r y c z n y o raz p r z y - * r z ą d m uszą być z n an e , dlatego też na początku pliku w id z im y w łączenie ich definicji d y rek ty w ą i n c l u d e . O © W naszym p ro g ra m ie m am y zam iar tw o rzy ć w iele obiektów k la sy p r z y r z ą d , ale w szystkie b ęd ą pojaw iały się na jednym ekranie. Z atem w p rogram ie będzie jed en obiekt klasy
I
e k r an _ a 1 f anum e
ryczny.
Jeśli funkcja sk ła d o w a jakiegoś p r z y r z ą d u będzie go ry so w a ła na ekranie, to n ie na jakim ś nieo k reślo n y m obiekcie k lasy e k r a n _ a l f a n u m e r y c z n y , ale na ty m jednym , k o n k retn y m . Tu ( O 0 ) w id zisz jego definicję. N az y w a się po p ro stu : e k r a n . Definicja obiektu połączona jest oczyw iście z w yw ołaniem k o n stru k to ra . W O w idzieliśm y , że k o n stru k to r ma a r g u m e n t- który rep rezen tu je zn a k m ający być ry so w a n y jako tło ek ran u . P ierw otnie jak o znak tła najczęściej w ybierałem spację, ale k ropki b ęd ą bardziej pouczające w naszym p rzy k ła d zie. O © W ew n ątrz definicji k o n stru k to ra może n astąp ić w y w ołanie in n ej funkcji sk ład o w ej tej klasy. O © O to definicja k o n stru k to ra p r z y r z ą d : .-p rz y rz ą d (void) ;
w y w oły w an eg o , jak w idać, bez żad n y ch arg u m e n tó w . Taki k o n s tru k to r n azy w a się także k o n s tru k to re m d o m n ie m a n y m . (N ie m a to n ic w sp ó ln eg o z arg u m e n tem d o m n iem an y m w sp o m n ian y m p rzed chw ilą). W n astęp n y ch linijkach tego k o n stru k to ra w id zim y , jak w p isu je się d a n e d o określonych sk ład n ik ó w . Jeśli k to ś definiuje p r z y r z ą d za pom ocą ta k ieg o k o n stru k to ra d o m n iem an e g o , to znaczy, że n ie p o d a n azw y tego w y św ietlacza.
I
Rozdział. 15. K onstruktory i D estruktory K onstruktor
671
Sam już pewnie zauważyłeś, co wtedy robi nasz konstruktor K o n stru k to r ten , nie m ając opisu m ów iącego, co p o k azu je ten p rz y rz ą d , w pisuje tam napis: „W sk a ź n ik n r ... ", gdzie numer jest kolejnym n u m e re m p rzy rząd u . N ie n u m e ru je je d n a k w szystkich p rz y rz ą d ó w - tylko te, k tó re kreujem y tym k o n stru k to re m domniemanym. Inform acja o tym , ile ju ż takich n ien azw an y ch p rzy rząd ó w p o w sta ło , przecho w y w a n a jest klasie p r z y r z ą d w sk ład n ik u staty czn y m (© ). Jak pam iętam y, sk ła d n ik staty czn y je st w spólny dla w szy stk ich obiektów d a n e j klasy. Tutaj k a ż d e w y k o n a n ie te g o w łaśnie k o n stru k to ra - p o w o d u je zw ię k sz e n ie ow ego sk ład n ik a o 1 (z a u w a ż inkrem entację). O 0 P oniew aż chcę liczb ę nieznanych p rz y rz ą d ó w zam ienić na s trin g będący jej w iz e ru n k ie m alfan u m ery czn y m , to m u szę się p o słu ży ć taką funkcją s t r i n g l i c z b a _ n a _ n a p i s (i n t w a r t o ś ć ) ; © 0 O to definicja funkcji l i c z b a _ n a _ n a p i s . Jest ona z re a liz o w a n a w dw óch w arian tach . W a ria n t m ożesz w ybrać za pom ocą kom pilacji w arunkow ej. W y starczy , że w dy rek ty w ie d c f i n e 0 © w y b ierze sz 0 lub 1 •
0 —o znaczać będzie, że w y b ierasz w arian t d la początkujących
•
1 - oznaczać będzie, że w y b ierasz w arian t d la zaaw an so w a nych
©O Wariant 0 (trudniejszy!) jest dla czytelników, którzy czytają tę książkę 1 po raz pierwszy... i niestety jeszcze nie zn ają rozdziału o stru m ien iach w ejściow o-w yjściow ych. W tej sytuacji z a m ia n ę liczby na w artość trzeb a zrobić sam em u . Robim y to w sposó b , k tó ry p o zn aliśm y d aw n o tem u, w ro zd ziale o klasie s t r i n g . (Zob. str. 517).
Dla niewtajemniczonych, ale za to dociekliwych —krótka uwaga.. Zaskoczyć Cię może tylko ta instrukcja: int pułap dolny = p o w (10.0, (int)
logio((double) wartość));
K o rzy sta m y tu ze standardow ych fu n k c ji bibliotecznych pow (podnoszenie do potęgi) oraz l o g i 0 - logarytm dziesiętny. To wyrażenie pozwala nam się dowiedzieć ilu-miejscowa jest nasza liczba. Potrzebujemy tego by wiedzieć, gdzie zacząć procedurę uzyskiwania kolejnych cyfr. Jeśli w ż a d e n sp o só b nie m ożesz zro zu m ieć całej tej funkcji - z u p e łn ie się nie p rzejm u j —poczekaj cierpliw ie d o rozdziału o form atow aniu w e w n ę trz n y m (str. 1150). T am p o ro z m a w ia m y o bardzo łatw y ch sposobach na w y k o n a n ie takiego z a d a n ia . Jak to m o że być łatwe, rzuć o k iem poniżej - d o w arian tu drugiego.
5
672
Rozdz. 15. K onstruktory i D estruktory K onstruktor
Wariant łatwiejszy - dla zaawansowanych 0©
P ostan o w iłem jed n ak zam ieścić tu ta k że sposób, w k tó ry m w przyszłości p o w in ie n eś się w takich sytuacjach p o sługiw ać. N azw an y jest on w ariantem „ d la czy teln ik ó w w tajem niczonych". Jest on dla tych, k tó rzy znają już klasę b ib lio teczn ą ostringstream. W ta k im p rz y p a d k u s p ra w a jest zupełnie p ro sta. Z am ian ę liczby na rep rezen tu jący ją s trin g -w y k o n u je jedna instrukcja. Nie potrzebuję chyba przypominać, że aby posłużyć się klasy biblioteczny musimy zamieścić w programie jej deklarację. Robimy to dyrektywy i n d ude o o A te ra z n a p o m n ie n ie : N ie d a j się z w a rio w a ć - to w s z y s tk o , co m ó w iliś m y
i
te ra z na te m a t z a m ia n y lic z b y na s trin g - to b y ła ty lk o d u ż a d y g re s ja ...
W tym paragrafie rozmawiamy przecież o konstruktorach! OO N a w e t bliżej nieo k reślo n y p rzy rząd m u si mieć na ekranie o k reślo n e miejsce. T rzeb a je jakoś w y b ra ć i ko n stru k to r d o m n ie m a n y robi to tu taj, na p rzy k ła d , w ta k i sposób. Jak w id a ć , te „nieokreślone p rz y rz ą d y " będą raczej z praw ej strony e k ra n u . Przypominam, że tego wyboru miejsca musimy dokonać, dlatego że ten konstruktor domniemany nie ma argumentów - czyli nie przysłano mu żadnych życzeń, co do pozycji przyrządu na ekranie. O © P rz y rz ą d m oże d o s ta ć polecenie n ary so w an ia się na ekranie. T akie polecenie p o le g a na tym , ż e k to ś w y w o ła tę funkcję n a r y s u j . O © W funkcji tej w id z isz w iele w yw o łań funkcji napisz na rzecz obiektu ekran (k tó ry jest (jedynym !) obiektem klasy e k ran alfanumeryczny). K olejne w y w o łan ia rysują w su m ie b ard zo p ry m ity w n ą ram k ę — w m iejscu ekranu o k reślo n y m p rz e z w sp ó łrzęd n e x o raz y. W ew n ątrz w p isy w a n y jest stosow ny tekst. Z asad ę , w e d łu g k tó rej odbyw a się w y p isy w a n ie tekstów w ró żn y ch miejscach na ekranie, o m a w ialiśm y tuż p rzed p rzy k ła d em . 0 © N a końcu funkcji n astęp u je w y w o łan ie n a rzecz obiektu e k r a n jego funkcji sk ład o w ej wyświetl, która pow oduje, ż e n a ek ran ie po jaw ia się bieżący obraz p rz y rz ą d ó w . N a tym kończy się p lik przyrząd, cpp zaw ierający definicje funkcji sk ła d o w y ch klasy przyrząd.
P rzech o d zim y d o w łaściw ego pliku p ro g ra m u , g d z ie o czy w iście też m u si się z n aleźć d y rek ty w a include w łączająca definicje o m aw ian y ch p rz e d chw ilą d w ó ch klas. 0 © W main, ro zp o czy n ając pracę p ro g ram u , w y w o łu jem y d la e k r a n u funkcję w y c z y s c sp raw iającą, że ew en tu aln a p o p rz e d n ia za w a rto ść ek ra n u zostaje w yczyszczona. ( W n aszy m p rzy p ad k u - z a p e łn io n a sam y m i k ro p k am i).
Rozdział. 15. K onstruktory i D estruktory K onstruktor
673
Oto, jak posługujemy się konstruktorami © O P rzy stęp u jem y d o definicji o b iek tó w klasy p r z y r z ą d . O to p ie rw sza z nich. Definicja z w y w o ła n ie m k o n stru k to ra z arg u m e n tam i. 0 © Tutaj ro b im y to s a m o dla innego o b iek tu , ale ostatni a rg u m e n t jest pom inięty. P o n iew aż ten a rg u m e n t jest o k reślo n y jako d o m n iem an y , w ięc kom pilator m niem a, że p ro g ram iśc ie chodzi o w arto ść = 0. 0 © T utaj w definicji n ie m a żadnej listy arg u m e n tó w . U ru ch am ia to z a te m k o n stru k to r (d o m n iem an y ) przyrząd::przyrząd(void) ; 0 © Definicja obiektu z p rzy d o m k iem v o l a t i l e . Jak pam iętam y , na rzecz takiego obiektu m ogą b y ć u ru ch o m io n e tylko te funkcje sk ład o w e, k tó re także są v o l a t i l e . N ie d o ty c zy to jed n ak k o n stru k to ró w . K o n stru k to r zresztą nie m oże m ieć p rz y d o m k a v o l a t i l e . © O Definicja obiektu z p rzy d o m k iem c o n s t . W szystkie p o w y ższe u w ag i p o w ta rzają się w p rz y p a d k u p rzy d o m k a c o n s t . M im o że k o n stru k to r nie jest (bo być nie m oże) c o n s t , to jednak m oże p raco w ać na rzecz takiego o b iek tu . © 0 M am y tu pętlę, w którei sym uluje się zm ian y w skazań poszczególnych p rzy rząd ó w . N a d o le p ętli (© © ) w id zim y funkcję z w lo k ą , k tó ra sp ra w ia , że w sk a zan ia na p rz y rz ą d a c h aktualizuje się co 1 sek u n d ę. © © Oto definicja funkcji z w lo k ą . Wykorzystuje on biblioteczną funkcję t i we. Funkcja najpierw pyta o bieżący czas i zapamiętuje go jako „począt kowy". Następnie w pętli wielokrotnie pyta o czas, dopóki nie minie zadana liczba sekund. Wtedy funkcja kończy pracę. © © B łędem byłoby w y w o łan ie funkcji z m ie n _ w s k a z a n ie na rzecz obiektu z p rz y d o m k ie m v o l a t i l e . To d latego, iż funkcja ta, nie m ając sam a przy d o m k a v o l a t i l e , nie g w aran tu je tym sam y m , że z naszym ob iek tem będzie się o b ch o d ziła ze specjalną troską (należną obiektom tego typu). 4 1
© O B łędem byłoby w y w o łan ie funkcji z m i e n _ w s k a z a n i e na rzecz obiektu typu c o n s t , p o n ie w aż funkcja z m i e n _ w s k a z a n i e , nie m ając p rzy d o m k a c o n s t , nie g w aran tu je, ż e nie będzie zm ieniać sk ład n ik ó w o b ie k tu . (M y zresztą w iem y, że o n a w łaśn ie chciałaby je zm ieniać!).
W W n aszy m p ro g ram ie, w funkcji m a i n w id zieliśm y m ięd zy in n y m i takie defini cje obiektów : przyrząd Vari (2, 6, "Wznoszenie", "stopy/sec"); przyrząd A; M am nadzieje, że przyzw yczaiłeś się już d o tego zw ięzłego zap isu . Jeśli nie, to p rz y p o m n ę , że te d w ie linijki m ożna zap isać rów nież tak: przyrząd Vari= przyrząd(2,6, "Wznoszenie", "stopy/sec"); przyrząd A = przyrzadt);
5
674
Rozdz. 15. K onstruktory i D estruktory K onstruktor
Pułapka U w aga: P oczątkujący program iści często się m ylą i tę ostatnią definicję (korzys* tającą z k o n stru k to ra dom niem anego) p iszą tak: przyrząd A ();
Jest to błąd - ale k o m p ilato r tu nie zapro testu je, bo nie m a tu b łęd u składni. Błąd polega na tym , ż e ta linijka wcale nie jest definicją obiektu. Z a m ia s t definicji obiektu m a m y tu deklarację... czego ? Przeczytajm y tę deklarację: A - jest funkcją w yw oływ aną bez żadnych arg u m e n tó w , a zw racającą w rezu ltacie obiekt klasy p r z y r z ą d . Z a te m w te j in s tru k c ji nie p o w sta je o b ie k t o n a z w ie A. J e s t to ty lk o w y ja ś n ie n ie k o m p ila to ro w i, ż e "ja k b y co ", to n a z w a A je s t n a z w ą ja k ie jś fu n k c ji. * jrmimnm;m -rmm»n»g.uwww«m«
K rótko m ów iąc - z u p ełn ie nie to, o co n am chodziło. P rzy p o m n ijm y dw ie form y p op raw n ej definicji. przyrząd A; przyrząd A = przyrzadO;
P u łap k a ta g ro zi n am w tedy, g d y chcem y użyć k o n stru k to ra bez żadnych arg u m en tó w . Jeśli k o n stru k to r ma a rg u m e n ty , to oczyw iście zap is: przyrząd A(2,
6, "Wznoszenie",
"stopy/sec");
jest p o p raw n y . N ie m a tu m ow y by k o m p ilato r pom yślał, że d ek laru jem y tu jakąś funkcję. W n aw iasie są przecież nie typy, a konkretne wartości. G dyby nic nie było (jak p rz e d te m ) - to w ted y p o ja w ia się problem . C o p raw d a, k o m p ilato r w p rzy p ad k u tak ieg o błędu, nie zap ro testu je w tej linijce kodu (bo w o ln o nam przecież d ek laro w a ć dow olną funkcję) - ale błąd się objaw i n ajp raw d o p o d o b n iej już w następ n y ch linijkach. Przecież, jeśli definiujem y obiekt, to znaczy, że z a p e w n e już w następnych linijkach będziem y chcieli g o użyć. W ó w czas p rzy instrukq'i, w której chcielibyśm y p rzep ro w ad zić jakąś operację na o b iek cie A, k o m pilator zap ro testu je , że takiej operacji nie w o ln o p rz e p ro w a d z a ć na fu n k c ji A.
Przecież omyłkowo nazwę A zdeklarowaliśmy jako nazwę funkcji!
W Refleksja o sztuce programowania Z obaczyliśm y tu taj nieco ro zb u d o w an y , ale p ro sty w d ziałan iu p ro g ram . W ys tęp o w ały w nim d w ie klasy i dw ie funkcje globalne (nie licząc funkcji m ain). Funkcja zam ien iająca liczbę na string w z a sa d z ie m ogła by być tak że funkcją sk ład o w ą jednej z klas.
Rozdział. 15. K onstruktory i D estruktory Kiedy i ja k wywoływany jest konstruktor
675
M am te ra z taką propozycję: Jeśli chcesz p ręd zej osw oić się z techniką ob iek to w o o rie n to w a n ą spróbuj w y m y ślać p ro g ra m y , w których (poza funkcją m a in ) nie b ęd z ie żad n y ch fu n k c ji g lo b a ln y ch . To znaczy: w szystkie funkcje będą funkcjam i sk ład o w y m i jak ich ś Tw oich klas. Oczywiście w późniejszej praktyce byłaby to duża przesada - funkcje nie-składowe bardzo się przydają, czasem wręcz jakaś funkcja nie powinna być funkcją składową żadnej z klasy. (Porozmawiamy i o tym). T y m czasem tutaj, ch o d zi m i tylko o rodzaj ćw iczenia. O to, byś zm ie n ił sposób m y śle n ia z o rien to w a n eg o funkcyjnie na o rien to w a n y obiektow o. C h w ilo w e n a ło ż e n ie s o b ie ta k ie g o ry g o ru -
"ż a d n y c h fu n k c ji g lo b a ln y c h
(n ie - s k ła d o w y c h ) ” m o ż e C i w p o c z ą tk o w e j fa z ie n auki b a rd z o p o m ó c ro z w i L
15.2
n~ ą ć w yJ o b ra ź n ię o b ie k to w ą . m
p M
W ĘtfKĘKfm
MMMffPM
^
S p e c y fik a to r (p rz y d o m e k )
. __
......
,s. - ,
. •-.... •.i ''
explicit
K o n stru k to r m oże p rz y swojej deklaracji w klasie m ieć specyfikator e y p l i c i t 1. N ależy to ro zum ieć jako: "do u ż y tk u w y łączn ie ja w n e g o , Jest to o zn ajm ien ie k o m p ilatorow i, że nie m oże u ż y ć tego k o n stru k to ra niejaw n ie - czyli d o tak z w an y ch konw ersji, o czym p o ro zm a w iam y bliżej niebaw em , n a s tro n ie 763, (§ 18.2). Gdy już przeczytasz tamten paragraf to zgodzisz się zapewne, że przydo mek e x p l i c i t przy d e k la r a c ji konstruktora jest jakby taką wypowiedzią: "K om pilatorze! Ten konstruktor nie nadaje się do konw ersji" .
15.3
K ie d y i ja k w y w o ły w a n y jest k o n s tru k to r 5
P o d o b n ie, jak w p rz y p a d k u obiektów ty p ó w w b u d o w a n y ch , tak i w p rzy p ad k u o b ie k tó w ty p ó w zd efin io w an y ch p rzez u ży tk o w n ik a, m ożem y m ieć kilka ro d z a jó w obiektów - zależn ie od tego, jak i g d zie je definiujem y. P rzyjrzyjm y się jak w takich ró żn y ch sytuacjach pracuje k o n stru k to r.
15.3.1
Konstruowanie obiektów lokalnych
Obiekty lokalne automatyczne (czyli tw o rzo n e na stosie w trakcie w y k o n y w a n ia p ro g ram u ) - p o w stają w mo m e n cie, g d y p ro g ram n ap o ty k a ich definicję, a przestają istnieć, g d y program w y c h o d z i p o za blok, w k tó ry m zostały p o w o łan e do życia. 1)
ang. explicit -wyraźny, jasny, sprecyzowany, jasno postawiony, jasno sprecyzowany (czytaj: "eksplisyt"]
676
Rozdz. 15. K onstruktory i D estruktory Kiedy i ja k wywoływany jest konstruktor I
<------- o tw a rc ie lo kalnego b lo k u
przyrząd M; j
/ / definicja obiektu M
<------- za n ik n ię c ie bloku, o b ie k t M je s t lik w id o w a n i/
... tu o b ie k tu M j u ż n ie m a
K onstru k to r ta k ie g o obiektu u ru ch am ian y jest w m om encie, g d y program n ap o ty k a definicję tego obiektu. Tak zd efin io w an y o b iek t jest obiektem lo k aln y m au to m aty czn y m .
Obiekty lokalne statyczne Jeśli p rzy definicji stało b y słow o static, to obiekt byłby o b ie k te m lo k aln y m staty czn y m . Z n a c z y to, że istniałby od sam eg o początku p ro g ra m u , a ż do m om entu zak o ń c zen ia p ro g ram u , ale d o stęp n y byłby ty lk o w zakresie w ażności tego b lo k u . (W szystko jest tak sam o , jakby to by ło w p rzy p ad k u statycznego o b iek tu typu: int. Różnica jest tylko ta, że obiekt ty p u int nie ma ko nstruktora). Z agadka:
Skoro nasz obiekt statyczny klasy p r z y r z ą d istnieje przez cały czas wykonywania programu, to kiedy startuje do pracy jego konstruktor ? Z askoczę Cię! K o n stru k to r ru szy d o pracy jeszcze p rz e d rozpoczęciem w y k o n y w an ia main. P oniew aż to Ty sa m piszesz k o n stru k to r, w ięc m ożesz w nim w ykonać ja k ąś akcję jeszcze p rzed tym , jak main z a c z n ie się w ogóle w y k o n y w ać
15.3.2
K onstruowanie obiektów globalnych Jeśli jakieś ob iek ty klasy przyrząd zd efin io w an e będą p o za w szy stk im i fu n k cjami, to będą o b iek tam i globalnym i. przyrząd GGG;
//*************************** int main() { II... )
Z a k re s w ażn o ści ta k ieg o obiektu to plik (zak res pliku). Jeśli z in n y ch plików chcem y się o d n ieść d o takiego obiektu, to jego nazw a m u si być w tych plikach zad e k laro w an a (deklaracja sxtern). C z a s życia: cały czas w y k o n y w an ia p ro g ram u . K o n stru k to r ru sz a d o p racy jeszcze p rz e d rozpoczęciem w y k o n y w an ia funkcji main.
Rozdział. 15. K onstruktory i D estruktory Kiedy i ja k wywoływany jest konstruktor
15.3.3
677
Konstrukcja o b iek tó w tworzonych operatorem new O b iek t m o ż e zostać z d e fin io w a n y (pow ołany d o życia) przez użycie o p erato ra new. Je s t to b ard zo p o ży teczn a rzecz, bo u m o ż liw ia tw o rzen ie w trak c ie w y k o n y w a n ia p ro g ram u takiej liczb y obiektów , o k tó rej się nam n aw et n ie śniło w m o m en cie , g d y p ro g ra m pisaliśm y. O to p rz y k ła d takiej definicji - d okonanej na p rz y k ła d w śro d k u jakiejś funkcji: void f () przyrząd *wsk_przyrz; // n a jp ie r w m u s i m y m ie ć w s k a ź n ik wsk przyrz = new przyrzad(l, 1, "Waga", "kg");
//
...
} «$♦ N ajp ierw d efin iu jem y sobie w sk aźn ik m o g ący p o k azy w ać na jakiś o b ie k t klasy przyrząd. W skaźnik ten n a z y w a m y wsk_przyrz. ♦♦♦ N astęp n ie , za pom ocą operato ra new, k reu je m y obiekt klasy p r z y r z ą d - d zieje się to w z a p a sie dostępnej pam ięci. R ezerw uje się tam d la niego p am ięć i n aty ch m iast ru sz a do pracy k o n stru k to r w pisując ta m m ięd zy in n y m i słow a "Waga” i "kg". ♦♦♦ K ied y obiekt jest g o to w y , w skaźnikow i w s k _ p r z y r z p rzek a zu je się je g o adres. O d tej p o ry m ożem y się ty m obiektem p o słu g iw ać. N ie m a on, co p ra w d a , n azw y , ale m a w sk aźn ik , k tó ry na niego po k azu je. To w ystarcza. C zas ży c ia o b ie k tu : od chw ili, k ied y pro g ram w y k o n a ł linijkę z tą instrukcją z aw ie rając ą o p erato r new - a ż d o chwili, g d y sa m i nie zlik w id u jem y obiektu o p e ra to re m d e l e t e . delete wsk_przyrz; 5
Z ak res w a ż n o śc i: Jeśli tylko jest w danej chw ili d o stę p n y choć jeden w sk aźn ik , k tó ry p o k a z u je na ten obiekt, to obiekt jest d o stęp n y . Jest tu je d n a k niebezpieczeństw o: Jeśli p rzez n ie u w a g ę stracim y w sk a źn ik , który p o k azu je na ten obiekt (zm ienim y, p rzestaw im y go n a coś innego, albo p o prostu przestanie on istnieć) w ów czas stracim y w szelki k o n ta k t z tak u tw o rzo n y m obiektem . O biekt b ęd zie istniał nadal, ale ju ż nie o d szu k am y go. P rzy p o m n ij sobie scenkę z chłopczykiem k u p u jący m balonik w parku (str. 308). W szy stk o to obow iązuje tak że i w stosunku do o b iek tó w klas zdefin io w an y ch p rz e z u ży tk o w n ik a. Co jest n o w e - to oczyw iście zach o w an ie k o n stru k to ra. Jeśli op erato r new nie p o trafi w y k reo w ać obiektu (bo na p rzykład n ie m a już więcej m iejsca w p am ięci) w ów czas k o n stru k to r tego obiektu nie jest urucham iany. T o chyba z ro zu m iałe: jeśli nie d o staliśm y p rzy d ziału na m ieszkanie, to nie w zy w a m y d e k o ra to ra w nętrz.
678
Rozdz. 15. K onstruktory i D estruktory Kiedy i ja k wywoływany jest konstruktor
15.3.4
Jawne wywołanie konstruktora O biekt m oże być też stw orzony p rzez jaw n e w y w ołanie k o n stru k to ra. W efekcie o trzy m u jem y obiekt, który nie m a nazw y, a czas jego życia ogranicza si
Z au w aż , że w y w o łu jem y konstru k to r - czyli funkcję sk ład o w ą, a nie stosujem y notacji obiekt. funkcja_składowa(argumenty)
K o n stru k to r jest, co p raw d a, funkcją sk ład o w ą - ale specjalną. N ie jest w yw oły w a n y na rzecz jakiegoś obiektu, bo ten obiekt jeszcze nie istnieje. Z adaniem k o n stru k to ra jest go utw orzyć. Stąd w w y w o łan iu nie ma jeszcze zap isu z krop ką. Jeśli takie zasto so w an ie nie jest jasne, to p o słu żm y się p rz y k ła d e m . W yobraź sobie, że m am y jakąś p ro stą klasę kl o raz m am y jakąś funkcję, taką zw ykłą, nienależącą d o żad n e j klasy. Jest to je d n ak fu n k q a, której arg u m en tern jest jakiś obiekt k lasy kl. łinclude using namespace std; ////////////////////////////////////////////////////////////// class kl public : int a; float b; char c;
/ / ------------------------------ konstruktor kl(int i, float x, char z ) { a = i ; b = x ; c = z;}
}; ////////////////////////////////////////////////////////////// void wypis(kl );
//************n*******+*******************+*«**+**************
int main()
{ klobiektA(l, 3.14, obiektB (2, 1.41,
'x'), 's');
wypis(obiektA); wypis(obiektB); w y p i s ( kl (3, 7. 77,
'1') );
) void wypis(kl sk)
1 cout << " a= " << sk.a << " b= " << sk.b << " c= " << sk.c « endl;
}
// O
Rozdział. 15. K onstruktory i D estruktory Kiedy i ja k wywoływany jest konstruktor
□
679
Spójrzmy na ekran a=
1
b=
3.14
c=
x
a= a=
2 3
b= b=
1.41 7.77
c= c=
s 1
Komentarz Z a u w a ż w m a in d w a w y w o łan ia funkcji wypis - zro b io n e w zw y k ły sposób. Nic ciek aw eg o . W v w o łu iem y jednak tę funkcję tak że w inny sposób O . W id zim y , że a rg u m e n tem w yw ołania funkcji jest tu w y rażen ie będ ące jaw nym w y w o łan iem k o n stru k to ra.
I
N a u ż y te k te g o w łaśnie w y rażen ia zostaje ch w ilo w o w y tw o rz o n y n ie n a z w a n y o b ie k t k la s y k l i w y słan y d o funkcji jako arg u m en t. O b iek t istnieje na p o trzeb y tej in stru k c ji - p o przejściu do następnej linijki już g o m oże nie być! Tak n a p r a w d ę , to z tym sp o so b em tw orzenia o b iek tó w (m etodą jaw n eg o w y w o łan ia k o n stru k to ra ) - już się spotkaliśm y. P o ró w n aj d w ie w ersje tej sam ej definicji o b ie k tu P: przyrząd P(5, 6, "Prędkość", "km/h" ); przyrząd P = przyrzad(5, 6, "Prędkość", "km/h" );
8
W d ru g ie j w ersji (© ), po praw ej stronie znaku '= ' w y w o ły w a n y jest w łaśn ie jaw n ie k o n stru k to r. Tw orzy się n ien azw an y obiekt k la sy przyrząd. Po lewej stro n ie z n a k u przy p isan ia jest u tw o rzen ie d ru g ieg o o b iek tu klasy przyrząd. Ten o b ie k t n o si nazw ę P. C h w ilo w o w ięc istnieją te d w a obiekty. Z nak p rz y p isa nia o z n a c z a , ż e obiekt P m a stać się kopią, czyli m ieć d o k ła d n ie tę sam ą treść (sk ła d n ik ó w danych), co n ie n azw an y obiekt chw ilow y. W re z u lta c ie p o w ykonaniu tego kopiow ania 2 obiekt P m a także w p isan e słow a " P r ę d k o ś ć ” czy "km/h" itd. P ro g ram p o skończeniu pracy n a d tą definicją p rzec h o d zi d o następnej linijki i w ty m m o m en cie m oże być zlik w id o w an y n ie p o trzeb n y ju ż obiekt chw ilow y. W re z u lta c ie pozostaje tylko o b iek t P o wyżej w sp o m n ian ej zaw artości. W su m ie re z u lta t jest więc taki sam , jak w p rzy p ad k u pierw szej wersji definicji (O ).
Uwaga dla dociekliwych, a nie lubiących marnotrawstwa C zy n ie w y d a je Ci się to trochę m arn o traw stw em ? To znaczy w tej sam ej instrukcji k o m p ilato r najpierw tw o rz y jeden obiekt (chw ilow y), potem kopiuje się g o (w ó w cz as są dw a), po czym ten pierw szy (chw ilow y) się likw iduje.
2)
O tym, jak dokonuje się kopiowania, będziemy mówić w paragrafie o konstruktorze kopiu jącym.
5
680
Rozdz. 15. K onstruktory i D estruktory Kiedy i ja k wywoływany jest konstruktor O czyw iście tak p o w in n o być w e d łu g zapisu naszej definicji - ale standard d o p uszcza, by k o m pilator dokonał sobie uproszczenia, czyli w takiej sytuacjj stw orzył w p am ięci tylko jeden obiekt. K om pilator m o że więc zrobić tak, ż e ten z obiekt lewej stro n y znaku =, i tefl obiekt z p raw ej - będą tak n ap raw d ę tym sam ym obiektem . Zaraz, zaraz... - pomyślałeś peronie - A co będzie, jeśli taka klasa mi destruktor, w którym taki obiekt robi coś poważnego, na przykład zoysadzĄ się w powietrze. Przecież ten destruktor "obiektu z prawej" - w rezultacit zniszczy nam coś w "obiekcie z lewej"? Jeśli m asz tak ie w ątpliw ości, to nie jesteś pierw szy. T w órcy stan d ard u i o tyrfl pom yśleli, to z n a c z y - kom pilator, k tó ry ma sobie tak u p ra sz c z a ć spraw ę, jesj zo b o w iązan y zro b ić to "inteligentnie". To znaczy tak, żeb y ostatecznie efekt by| taki (dobry) jak b y rzeczyw iście istn iały dw a obiekty - n ato m iast optym alizacja (która jest p rzec ież p ryw atną sp raw ą kom pilatora) była ta k rozw iązana, żeby n iep o trzeb n eg o kopiow ania i niszczenia obiektu nie było. W zasad z ie ta s p ra w a m ogłaby być zu p ełn ie n iew id o czn a dla przeciętnego p ro g ram isty , ale przecież n aw et p rzeciętn y pro g ram ista m o ż e sobie w destru k* torze um ieścić ja k ąś instrukcję w y p isu jącą coś na ek ran . W ted y zd ziw i się, dlaczego w p rz y p a d k u takiej in stru k cji definicji o b iek tu , ż a d e n w y d ru k z d estru k to ra na ek ran ie się nie pojaw i. A no w łaśnie dlateg o : kom pilator cichaczem z a m ie n ił obiekt chw ilow y na obiekt p ra w d z iw y —nic nie m usi byt lik w id o w an e, w ię c nie ma p o w o d u , by pracow ał tu d estru k to r.
Obiekt chwilowy przy rezultacie zwracanym przez funkcję Z obaczyliśm y tu ta j sytuację, g d y o b ie k t chw ilow y tw o rz o n y był w celu użycia go jako a rg u m e n t w y w ołania fu n k c ji. Ł atw o się dom yślić, że p o d o b n a sytuacja m o ż e w ystąpić, jeśli o b ie k t danej klasy jest rezu ltatem funkcji (w arto ścią zw racaną p rz e z funkcję). C h o d zi zn o w u o sytuację p rzesłan ia p rzez w artość, bo w ó w c z a s d ochodzi do tw o rzen ia kopii. ( Wrócimy do tej sprawy przy rozmowie o konstruktorze kopiujcfcym),
15.3.5
D alsze sytuacje, gdy pracuje konstruktor W ym ienim y je ty lk o w p u n k tach , bo d o k ład n iej zajm iem y się n im i w stosow* nym czasie. • K o n stru k to r w y w o ły w a n y p rzy tw o rzen iu o b iek tó w chwilo* w y ch (tej klasy). • K o n stru k to r jest w y w o ły w a n y , jeśli p o w sta je o b iek t jakiej! k lasy , który zaw iera w sobie obiekt innej klasy. U ru ch am ian y je st w ted y k o n stru k to r tego składnika. (O tym za chwilę). Dla w tajem n iczo n y ch :
Rozdział. 15. K onstruktory i D estruktory D estruktor
15.4
681
D e s tru k to r O d e s tru k to rz e ju ż n ap o m k n ęliśm y w ro zd ziale o klasach (str. 462). Tutaj zaj m iem y się n im jeszcze raz - bliżej. D estru k to rem klasy K jest jej funkcja sk ła d o w a o n azw ie ~K (w ę ż y k i nazw a klasy). Funkcja ta jest w y w o ły w a n a au to m aty cz n ie - zaw sze, g d y o b ie k t jest lik w idow any. Funkcja jak fu nkcja. N ajw ażniejsze jest tu w łaśnie to automatyczne u ru ch am ia nie. C o do treści d estru k to ra - to ju ż zależy od nas sam y ch . Klasa nie m u si m ieć obow iązkow o d estru k to ra . D estru k to r nie likw iduje obiek tu, ani nie z w a ln ia obszaru pam ięci, k tó ry obiekt zajm ow ał. D estruktor p rz y d a je się w te d y , g d y przed zn iszczen iem obiektu trzeba jeszcze dokonać jakichś działań . Po p ro s tu trzeba posprzątać. ♦♦♦ Jeśli na p rzy k ła d obiekt rep reze n to w ał ok ien k o n a ekranie, to m ożem y chcieć, b y w m om encie lik w id acji tego obiektu o k ie n k o zostało zam k n ię te, a e k ra n w y g ląd ał jak d aw n iej. ♦$* D e s tru k to r jest potrzebny, g d y k o n stru k to r d anej k lasy do k o n ał na swój u ż y te k rezerw acji d o d atk o w ej pam ięci (o p erato rem new ) - na p rzykład z a re z e rw o w a ł sobie m iejsce na d u żą tablicę. W te d y w d estru k to rze o b sz a r pam ięci. ♦♦♦ D e s tru k to r m oże się też p rz y d a ć , gdy liczym y o b iek ty danej klasy. W k o n s tru k to rz e p o d w y ższam y taki licznik o jed en , a w d estru k to rze 5
nów . <♦ Skoro ju ż ta analogia - to d e stru k to r m oże się p rz y d a ć po to, by obiekt m ógł sp isa ć na d y sku (lub ekranie) swój testam en t. Pow ażnie! O biekt ma b y ć zlik w id o w an y , w ięc p isz e na d y sk u w sp ecjaln y m pliku: „Byłem o b ie k tem klasy o nazw ie B oeing 747 o n u m e rz e identyfikacyjnym .... Z o stałem zlikw idow any (d ata) p o dokonaniu n n a p ra w , przeleceniu m tysięcy k ilom etrów .." i tak dalej. D estruktor ja k o funkcja nie m oże zw racać żadnej w artości (n aw et typu v o id ) . D estruktor n ie je st w yw oływ any z ż a d n y m i arg u m en tam i. W zw iązku z tym nie m oże być ta k ż e p rzeładow any. D estruktor je st au tom atycznie w y w o ły w an y , g d y o b iek t au to m aty czn y lub chw ilow y w y c h o d z i ze swojego z a k re su w ażności (bo p rzecież w ted y obiekt ginie). Jeśli obiekt lo k a ln y jest statyczny, to m im o że kończy się jego zakres w ażności nie jest lik w id o w a n y ("zapada w sen zim ow y") - w ięc tak że nie u rucham ia się
682
Rozdz. 15. K onstruktory i D estruktory D estruktor jego d e stru k to ra . Likwidacja następuje dopiero przy zak ończeniu program in I w ted y też ru sz a d o pracy jego destru k to r. Bardziej ogólnie: p rz y kończeniu p racy program u w szystkie istniejące w ów czas obiekty są lik w id o w an e, więc w ted y też autom atycznie u ru ch am ian e są ich destru k to ry . Do obiektu, k tó ry kreow aliśm y operato rem new, d estru k to r w yw oływ any jes-t, g d y zasto su jem y op erato r delete w obec w skaźnika pokazującego na ten obiekt. Jeśli w sk a źn ik taki ma w arto ść 0 (tzw. adres N ULL), to destruktoi (m im o życzenia) n ie jest urucham iany. Jeśli kończy się zak res w ażności referencji (przezw iska) ob iek tu — destruktoi nie jest w y w o ły w an y . Bo: jeśli na Tomka przez pewien czas móioiono „Łysy", to z faktu, żf przezwisko zapomniano - (przezwisko umarło) -n ie wynika, że umarł sam Tomek. A nalogicznie: d e s tru k to r nie jest au to m aty czn ie w y w o ły w an y , g d y w skaźnik d o jakiegoś o b ie k tu w ychodzi ze sw ojego zakresu. To, że w sk a źn ik przestaje istnieć, nie o zn ac za, że obiekt (na k tó ry do tej pory p o k azy w ał) rów nież ma przestać istnieć. O bie zasad y w y d a ją się oczywiste. P odobnie jak k o n stru k to r - d estru k to r tak że m oże w y w o łać jakąś funkcję s k ła dow ą swojej klasy. N iem ożliw e jest p o b ran ie adresu d estru k to ra . O biekt klasy m ającej d estru k to r nie m o że być sk ładnikiem un ii. Powody wydają mi się te same, jak w przypadku konstruktora: gdyby w unii byh/ dwa obiekty klas z destruktorami - to któn/ destruktor przi/ likwidacji należałoby uruchomić. Dwa - to bez sensu. Jeden? A który? Według przebywającego właśnie w uniiobiektu? Zapominasz, że unia nie wie, jaki obiekt w danej chwili mieści. (To my mamy pamiętać czy jest tam guma do żucia, czy zdechła żaba). D estru k to r nie m o ż e być ani const a n i volatile, ale m o ż e p raco w ać na obiektach swej k la sy z takim p rzy d o m k iem . W yjątkow o, bo o czy w iście zw ykła funkcja sk ład o w a nie m ogłaby. M u siałab y sam a być ta k ż e c o n s t lub volatile.
W parag rafie „ D e stru k to r - pierw sza w z m ia n k a " w id zieliśm y ju ż p rzy k ład p ro g ram u zaw ierająceg o prosty d e stru k to r. O b serw o w aliśm y jak zostaje autom aty czn ie u ru c h a m ia n y .
Jawne wywołanie destruktora D estru k to r m ożna w y w o łać jaw nie. N a le ż y w ó w czas p o d ać całą jeg o nazw ę. To dlatego, żeb y nie b y ło n iep o ro zu m ien ia w interpretacji w ęży k a k tó ry jest, jak p am iętam y , tak że je d n o a rg u m e n to w y m o p erato rem bitow ej negacji (p atrz str. 105). Z apam iętaj sobie ta k ą zasadę:
Rozdział. 15. K onstruktory i D estruktory K onstruktor domniemany
683
obiekt.~ k l a s a (); wskaznik->~klasa();
Z ap y tasz m o że: A co zrobić, jeśli d e stru k to r u ru c h a m ia m y z w n ętrza klasy? Przecież w ó w c z a s odnosząc się d o funkcji sk ład o w y ch nie określa się, o który obiekt ch o d zi. W iadom o, że d e s tru k to r w y w o łu jem y w ó w czas na rzecz tego w łaśn ie o b ie k tu . Z atem nic nie stoi p rzed nazw ą u ru c h a m ia n e j funkcji sk ład o wej (tutaj: d e stru k to ra ). Czy zatem w tym p rz y p a d k u w o ln o w y w o łan ie d e s tru ktora za p isa ć tak: -klasa();
Nie. W ó w czas m u sim y napisać tam to, co tam n a p ra w d ę jest, ale jest niew id o cz ne: w sk a źn ik t h i s th is -> ~ k la s a ( ) ; P rz y p o m in a m , ż e jaw n e w y w o łan ie d estru k to ra nie lik w id u je obiektu. To tylko jakby w e z w a n ie sprzątaczki. C o ona zrobi, to już n asza sp ra w a . Po jej wyjściu sam o b iek t n a d a l istnieje. M oże trochę p o sp rząta n y , zm o d y fik o w an y , ale istnieje.
Wywołanie nieistniejącego destruktora M oże się z d a rz y ć , że klasa, nie m a zd efin io w an eg o d estru k to ra. W ów czas i k om p ilato r s a m w y g en eru je dla niej d estru k to r. B ędzie on w y g ląd ał tak, jakbyś m y go z d efin io w ali, ale jego ciało było p u ste (żadnej instrukcji). Także i takie w y w o łan ie jest d o p u szcza ln e, ale ig n o ro w an e. O to w y w o ła n ie d estru k to ra na rzecz obiektu i n t . int a; a .-int () ;
Uwaga: Niektóre kompilatory (np. M S Visual C++ wersja 6.0), wbreio standardowi, uznają ten zapis za błędny. D estru k to r m a jeszcze inne ciekaw e w łaściw ości - p o z n a m y je w następnych rozd ziałach .
5.5
K o n s tru k to r d o m n ie m a n y K o n stru k to r d o m n ie m a n y to taki k o n stru k to r, który m o żn a w yw ołać bez ż a d nego a rg u m e n tu . O to p rzy k ła d k lasy z kon stru k to rem dom niem anym :
684
Rozdz. 15. K onstruktory i D estruktory Lista inicjalizacyjna konstruktora class boss
{
II . . . public :
// konstruktory boss(int); boss (void) ; boss(float*) ;
, //4— d o m n ie m a n y
II... }; Z au w aż, że nie m ó w im y I „k o n stru k to r bez arg u m e n tó w " tylko
I „konstruktor, który można wywołać bez żadnych argumentów. K onstruktorem , który m ożna w yw ołać bez żadnych a rg u m e n tó w jest na p rzyk ład k o n stru k to r ze w szystkim i arg u m e n tam i dom niem anym i. class don
II ... public: // konstruktory don(int) ; d o n (float) ; dontchar *s = 0, int a =4, float pp = 6.66);
}; K onstruktorem d o m n iem an y m jest ten o statn i, najdłuższy. T o d lateg o , że m oż na go w yw ołać b ez żadnych argumentów. Klasa m oże m ieć oczyw iście tylko jed en k o n stru k to r d o m n ie m a n y . W ynika to z istoty p rzeład o w an ia funkcji. Jeśli klasa nie m a w ogóle żad n eg o k o n stru k to ra, w ó w czas sam kom pilator w ygeneruje so b ie dla tej klasy k o n stru k to r d o m niem any. B ędzie on p u b l i c oraz inline. Podkreślam : to au to m aty c zn e g en e ro w a n ie k o n stru k to ra do m n iem an eg o zajd zie tylko w ted y , g d y klasa nie m a ani jednego konstruktora.
15.6
L is ta in ic ja liz a c y jn a k o n s tru k to ra P am iętasz z a p e w n e takie definicje const double stal = 44.7;
W taki sposób w p ro g ram ie tw orzym y o b iek t stały o n azw ie s t a l i w arto ści 44.7 P am iętasz też z a p e w n e zasad ę, że o b iek ty sta łe m ogą być ty lk o in icjalizo w an epotem nie w o ln o d o nich nic przy p isy w ać. # Przypominam, że inicjalizacja to n a d a n i e zoartości w momencie narodzin. Z atem , g d y b y śm y w pow yższej instrukcji nie nadali od ra z u o b iek to w i .Dla] w artości 44.7, to p o te m ju ż nie m ożna b y teg o zrobić.
Rozdział. 15. K onstruktory i De struktory Lista inicjalizacyjna konstruktora
685
T o były s ta re sp ra w y . A teraz w y o b raź sobie taką klasę:
class abc { const double stal;
//
...
); W idzim y w niej sk ła d n ik stal z p rz y d o m k ie m const. P ytan ie: Jak n ad ać tem u sk ła d n ik o w i w a rto ść początkow ą? Jak w iem y, w ciele k la sy nie w olno nam nap isać inicjalizacji w taki sposób class abc
1 const double stal = 4 4 ;
II
H i-ź le !!!
...
}; O d p o w ied ź: D o tego, by zainicjalizow ać sk ład n ik stały , słu ży w łaśnie k o n stru k to r. K onkretnie: lista inicjalizacyjna k o n stru k to ra. Tym teraz się zajm iem y. O to sch em a ty czn ie p okazany k o n stru k to r. Z au w aż d w u k ro p e k oddzielający listę inicjalizacyjną kla sa ::kla sa (a rg u m e n ty) : listainicjalizacyjna
// ciało konstruktora }
Pokażmy listę inicjalizacyjną na przykładzie naszej klasy a b c , którą te ra z nieco rozbudujem y. D o d am y tam jeszcze inne sk ład n ik i - tym razem już nie const - o ra z rzeczony k o n stru k to r z listą inicjalizacyjną class abc
f const double stal; float x; cha r c ;
4 1
//---------- deklaracja konstruktora abc(float pp, double dd, char znak);
}; lllllllllllllll/lllllllllllllllllllllllllllllim illlllllllllll I / ------------ definicja konstruktora abc::abc(float pp, double dd, char znak)
: stal(dd), c(znak)
( x = pp;
1 P rzyjrzyjm y się pierw szej linijce definicji konstruktora. Z auw aż, ż e po zam knięciu naw iasu z arg u m e n tam i fo rm aln y m i zam iast zw y kłego otw arcia k la m ry ' {' rozpoczynającej ciało fu nkcji - w id z im y dw ukropek. K lam ra, o której m yślim y, jest za to niżej. To, co n astęp u je p o dw u k ro p k u , n azyw a się w ła śn ie listą inicjalizacyjną. N a niej jest kilka pozycji oddzielonych przecinkam i. Z a u w a ż , że lista inicjalizacyjna pojaw ia się ty lk o przy definicji k o n stru k to ra - p rz y deklaracji jej nie było.
5
686
Rozdz. 15. K onstruktory i D estruktory Lista inicjalizacyjna konstruktora
Łatwo to zapamiętać tak: Lista micjalizacyjna to nie „cecha konstruktora ale lista konkretnych prac, które ma on wykonać. Lista inicjalizacyjna specyfikuje, jak należy zainicjalizow ać niestatyczne składniki klasy.
■
.....
.......— - ___ _
W y k o n an ie k o n stru k to ra sk ład a się bow iem z d w ó ch o d d zieln y ch etap ó w . Etap 1: in icjalizacja składników . - w y k o n y w an y jest w łaśnie dzięki liście inicjalizacyjnej. ♦♦♦ Etap 2: p rz y p is a n ia i inne akcje - to instrukcje w y k o n y w an e w ciele konstruktora. T am m o żem y dać instrukcje p rzy p isan ia (a także inne, k tóre w y d ają się n a m p rz y d a tn e w konstruktorze). T en etap d ru g i jest n am zn an y - robiliśm y to już w ielokrotnie. P r z y jr z y jm y s i ę inicjalizacji a n a l i z u j ą c p o z y c j e n a liście.
P ozycję s t a l (dd) k o m p ilato r rozum ie jako: • sk ład n ik s t a l należy zainicjalizow ać w arto ścią w y ra ż e n ia vj n aw iasie (czyli u nas w artością a rg u m e n tu d d ). P o tem n astęp u je kolejna pozycja na liście: • c ( z n a k ) - oczyw iście już w iesz, że chodzi o inicjalizacji, sk ła d n ik a c w artością w y rażen ia ( z n a k ) T a d ru g a pozycja na liście nie była konieczna. S kład n ik c me m a p rzy d o m k a c o n s t , d la teg o nie m u si być koniecznie inicjalizow any. C hcąc n a d a ć mu w arto ść, m o żn a to u czy n ić w ciele k o n stru k to ra - tak, jak to zro b io n e jest w p rz y p a d k u sk ład n ik a x.
x = pp; D la sk ład n ik a n ie - c o n s t obie form y są d o p u szcza ln e. P rz y z n a sz je d n ak , ze ten sp o só b zap isu w liście inicjalizacyjnej jest krótszy. P o d su m u jm y :
■ ** •
S k ła d n ik o w i nie-const m o ż e m y w k o n s tru k to rz e n a d a ć w a r t o ś ć n a d w a sp o so b y:
- przez listę inicjalizacyjna konstruktora, — przez zw ykłe podstawienie w ciele konstruktora, S kładnikow i c o n s t m ożna nadać w artość początkow ą tylko za pom ocą listy inicjalizacyjnej.
Ważne: Lista inicjalizacyjna nie może inicjalizować składnika S mmmmwmmmmtmmm* ********•**»»m m m 'ł**^******1* ** *^ * ** ^ ^ * *^
static
Rozdział. 15. K onstruktory i De struktory Lista inicjalizacyjna konstruktora
687
(P rzy p o m in am , że sk ład n ik staty czn y klasy to składnik, k tó ry nie należy do konk retn eg o ob iek tu - lecz jest w sp ó ln y dla w szystkich obiektów tej klasy). C iekaw ostką jest to, że nie m u sim y w cale postępow ać tak grzecznie, jak to zrobiliśm y teraz. Inicjalizow ać m o żem y nie tylko arg u m en tem k o n stru k to ra, ale n aw et jakim ś w yrażen iem , w którym m ożem y w y w o łać jakąś fu n k c ę sk ład o w ą lu b zw y k łą. O to p rzy k ła d y w yrażeń , które m ogłyby się znalezc na liście. s t a l (dd) stal(dd +4) s t a l ( funkcja(dd) ) stali x + funkcja(dd*dd)
+2)
Jeśli tak zamierzasz robić, koniecznie przeczytaj dalsze uwagi. K o le jn o ś ć n a liście inicjalizacyjnej - n ie m a z n a c z e n i a
N a liście inicjalizacyjnej w y rażen ia stoją w pewnej, w ybranej p rzez nas, kolejności. N a p rz y k ła d w n aszy m ostatnim p rzy k ład zie najp ierw na liście w id zieliśm y inicjalizację sk ładn ik a s t a l , a potem im cjahzację składnika c. Ta w ybrana przez nas kolejność nie ma dla kompilatora znaczenia. I tak zrobi on to w ed łu g przyjętej zasady, że inicjalizacja odbywa się w kolejności, w jakiej dane składniki są deklarowane w ciele klasy.
U nas akurat, w klasie abc - kolejność w jakiej odpowiednie wyrażenia inicjalizacyjne stoją na liście inicjalizacyjnej, zgadza się kolejnością deklaracji składników s t a l i c w klasie abc, ale przecież nie zawsze tak być musi. N iek tó re k o m p ila to ry nieśm iało zachęcają do um ieszczania na liście lmcjalizacyjnej w y ra ż e ń w takiej sam ej kolejności, jak kolejność deklaracji od p o w ied n ich sk ład n ik ó w w klasie. Inne, w idząc o d m ien n ą kolejność, ostrzegają, ze od b ęd zie się to i tak w e d łu g - stosow anej p rz e z nie - standardow ej zasady. W y w o ł a n i e f u n k c j i s k ł a d o w e j z listy i n i c j a l i z a c y j n e j ?
D o w ied zieliśm y się p rz e d chw ilą, że w y rażen ie na liście inicjalizacyjnej m oże zaw ierać w y w o ła n ie funkcji składow ej. Pam iętać jednak należy, ze w ted y , w czasie e d y p ro g ra m pracuje n ad listą inicjalizacyjną, m am y jeszcze d o czy nienia z nie całk iem z b u d o w a n y m obiektem . W yobraź sobie co się m oże zd arz y ć, jeśli w y w o ła sz funkcję sk ład o w ą, która korzysta z jakiegoś innego sk ła d n ik a -d a n e j, a te n sk ładnik b ę d z ie inicjalizow any d o p iero za chwilę... jeśli z a te m ch cesz w y k o n y w ać tak ie akrobacje, to m usisz d o b rze zastanow ić się kiedy b ę d z ie to m iało sens. N ie jest to tru d n e, gdy się pam ięta, w jakiej kolejności o b y w a się inicjalizacja sk ład n ik ó w .
688
Rozdz. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy
15.7 Konstrukcja obiektu, którego składnikiem jest obiekt innej klasy W sp o m in aliśm y już, ż e d an ą składow ą (sk ła d n ik ie m -d a n ą ) jakiejś klasy m o ż | być nie tylko obiekt ty p u int, czy float, ale n a w e t o b ie k t innej klasy. Jest to sytuacja b ard zo często sp o ty k an a w życiu co d zien n y m : Składnikiem obiektu klasy o d b io rn ik ra d io w y są o b iek ty klasy tranzystor, obiekty klasy k o n d e n sa to r itd. O czyw iście składnikiem takiej klasy są także zw ykłe liczby n p . cena, w ag a, ro k produkcji, j Z astan ó w m y się n ad tym , jak konstruow any je st taki obiekt. Z an im zb u d u je sitf ten głó w n y obiekt (radio) trzeba najpierw sk o n stru o w a ć o b iek ty składów* (tranzystory). N ie m o żn a postąpić odw rotnie i n a jp ie rw zb u d o w a ć o b iek t głów* ny , a potem zabrać się d o b u d o w y obiektów sk ła d o w y c h . C hociażby dlatego, ż» nie w iem y w ów czas, jak d u ż o zarezerw ow ać m iejsca n a g łó w n y obiekt. (Jakn d u ż a m a być o b u d o w a tego radia). C ała ta praca w y k o n y w a n a jest au tom atycznie, a jeśli o niej m ó w im y , to tylko p
Z ałó żm y , że chcem y tu zb u d o w ać klasę, k tó ra re p re z e n to w a ć b ę d z ie panel, ną k tó ry m znajduje się kilka przy rząd ó w . P rz y rz ą d y te już kied y ś p rzerabialiśm y i m a m y k la sę słu żącą do ich b u d o w an ia Z ałó żm y , że jej definicja jest w pliku przyrząd, h N ie ch o d zi mi tu o to, byś od razu zaczął so b ie p rzy p o m in ać, jak w yglądała realizacja klasy przyrząd. Zrobiliśm y to raz, d z ia ła ło —i od tej p o ry więcej naa to już nie interesuje. A by z klasy korzystać, in teresu je nas tylko deklaracja p u b liczn y ch sk ład n ik ó w klasy pr zyr zad. P rz y p o m n ę w ięc tylko te n fragm ent
// pp u b l i----c z n eJf....... u n k cJj e s k ł a d o w e II public •: przyrząd (int, int, char *, char *,, int = 0) ; przyrząd (void); void zmień wskazanie(int w ); void narysuj(void); O d tej p o ry cała nasza p raca z tą klasą - p o leg a n a w y w o ły w an iu tych funkcji i już. D latego nie m a p o w o d u p rzy p o m in an ia so b ie b u d o w y tej klasy. Tym bard ziej, że w tym p arag rafie sk u p im y się na z u p e łn ie innej klasie. Ki lka s ł ó w o k l a s i e
panel
P an el jest jakby tablicą p rz y rz ą d ó w p o k ła d o w y ch . N a tym n aszy m panelu zn ajd u ją się dw a p rz y rz ą d y . Są one w ięc d w o m a sk ład n ik am i obiektu klasy panel.
Jeśli zdefiniuję obiekt klasy panel, to na e k ra n ie pojaw i mi się tablica rozd zielcza składająca się z d w ó ch p rz y rz ą d ó w . Jeśli zdefiniuję n astę p n y obiekt
Rozdział. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy
689
klasy p an el, to w ż ąd a n y m m iejscu ekranu p o jaw i mi się następna tablica z d w o m a p rz y rz ą d a m i. O to p ro g ra m , w którym definiujem y klasę panel w y k o rzy stu jąc już istniejącą klasę przyrząd. Z ak ład am , że definicja klasy p rz y rz ą d zn ajd u je się w pliku n ag łó w k o w y m przyrząd.h #include "przyrząd.h"
//O
extern ekran_alfanumeryczny ekran; void zwloką(int sekund);
...
I lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllt llllllllllll! 1
class panel {
''
”
int poz_x; const int poz_y;
przyrząd
prędkościomierz;
int *wskl; przyrząd drugi; int *wsk2; public : // k o n s t r u k t o r --------------------panel ( string nazw, int x, int y, int * zrodlo_sygnalul, int * zrodlo_sygnalu2, string opis2, string jedn2) // d c s t r u k t o r ~panel();
// ©
l f z w y k ł a f u n k c ja sk ła d o w a
void aktualizuj();
}; ////////////////////////////////////////////k o n icc def k,asy Pattel i i l l l i i i i i i i i i i i i t i i i l l l i l l i l i i i i l i i l l l i n i l i l l l l i i
---- d e f i n i c j a k o n s t r u k t o r a // O panel::panel (string nazw, int x, int y, int * zrodlo_sygnalul, int * zrodlo_sygnalu2, string opis2, string jedn2) : poz_x (x) , //ir-lista incjalizacyjna poz_y(y), prędkościomierz(x, y+3, "Prędkość", "km/h"), drugi(x, y+7, opis2, jedn2) ( // © wskl = zrodlo_sygnalul, wsk2 = zrodlo_sygnalu2; ekran.napisz(poz_x, poz_y, nazw);
//
Z*********************************************************** * £ / // panel::~panel ()
ekran.napisz (poz_x, poz_y,
- ZŁOMOWANY -
");
Z *************************************************************/ void panel:: aktualizuj() (
4 1
5
690
Rozdz. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy // Q
prędkościomierz.zmien_wskazanie(*wskl); drugi.zmień wskazanie(*wsk2);
)
/************+************************************************y int main() int
prędkość = 0, azymut = 270; ekran.wyczysc();
//s k a s o w a n ie d o ty c h c z a s o w e j tre śc i e k ra n u
//d e fin ic ja o b iektu k la s y p a n e l panel kabina("Panel pilota", 1,2,
// O Spredkosc, sazymut, "kurs ", "stopni");
for(int i = 0 ; i < 50 ; i++)
f // im itu je m y z m ia n ę w czasie lo tu prędkość = 60 + i; azymut = 270 + i%3;
//p a n e l te z m ie n n e w ła ś n ie p o k a zu je
II ©
kabina.aktualizuj() ; zwloką(1) ;
} // — b a w im y się d a l e j -----------------------------------int paliwo = 500; //d e fin iu je m y d r u g i p a n e l // panel maszynownia("Panel mechanika", 35,2, &predkosc, spaliwo, "Zużycie paliwa", "litrów");
©
for(int m ; m < 100 ; m++)
{ // im itu je m y z m ia n ę w czasie lo tu prędkość = 60 + m; azymut = 270 + m%3; paliwo--; kabina.aktualizuj(); maszynownia.aktualizuj(); zwloką(1);
) )
n ****»***»***»*»»*»»»»»*****»*»**»♦*»»»»*»»»»»*»»**»»*»*>,**»**»*»»*********»*»»***»*»*»*»#»*»»*»»
// F u n k c ję zw lo k ą , któ ra p o w o d u je w p ro g ra m ie //z w ło k ę cza so w a o d łu g o ś c i za d a n e j lic z b y s e k u n d
u ******************************************************************************+»**t***t»**»»»t»* void zwloką(int sekund)
{ time_t poczatkowy_czas = time(NULL); // ta p ę tla w y k o n u je się d o p ó k i n ie m in ie za d a n a lic z b a s e k u n d while (time (NULL) - poczatkowy_czas < sekund) { }
□
//c ia ło p u s lc
Ekran pod koniec wykonywania programu będzie wyglądał mniejwięcejtak
.Panel pilota....................... Panel mechanika.
Rozdział. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy
.1 .1
Prędkość 159
km/h
I t,.., ,.,I I ..... .. .1 T ....___ I
Prędkość 159
stopni
I ..... .. .1 I .a.. .. .I I _______ I
Zużycie paliwa 450 litrów
.1 .I .I
kurs 270
.I
691
_______________
km/h
Ciekawsze punkty programu O Włączenie pliku nagłówkowego z definiq'ą klasy p r z y r z ą d znajdującego się w bieżącym katalogu. (Bieżącym, bo nazwa ujęta w cudzysłów). © Definicja klasy panel. Składnikami jej są: * Dwa obiekty typu int. Drugi jest typu const, bo będę chciał coś ciekawego pokazać. W tych dwóch składnikach przechowujemy pozy *
cję panelu na ekranie. Obiekty klasy przyrząd. Jeden egzem plarz obiektu nazywamy p r ę d k o ś c i o m i e r z e m , a drugi nazywa się po prostu drugi. Na razie druga nazwa nie ma znaczenia. Oba przyrządy równie dobrze mogłyby ^
❖
się nazywać A oraz B. Dwa wskaźniki int*. Posłużą mi one do trzymania w nich adresów tych zmiennych, które mają być w yśw ietlane przez każdy z
przyrządów. ♦♦♦ Funkcje składowe - te jednak omówimy niżej. © Deklaracja konstruktora. Nie ma w niej nic nadzwyczajnego, ale wytłumaczę się ze znaczenia poszczególnych argumentów: - tak przysyłamy string z tytułem p a n e lu , nazw • , y - to współrzędne panelu na ekranie-konkretnie to współrzędne lewego górnego rogu tego panelu, zro d l o sygnalul, zrodlo_syijnalu2, to dwa wskaźniki do po kazywania na te zmienne typu int, które mają byc wysw.etlane
przez przyrządy,
692
Rozdz. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy o p i s 2, j e d n 2 - poniew aż nie byłem zd ecy d o w an y jaką tabliczkę m< m ieć w ym alow any d r u g i p rzy rząd , dlatego w o statniej chwili, M m o m en cie konstrukcji p an elu p rzyślę żąd an e napisy. O To je st n a jw a ż n ie jsz a lin ijk a w tym p rz y k ła d z ie . W idzim y tu p ierw szą linijką definicji k o n stru k to ra. N ie jest to byle jaki konstruktor, ale ko n stru k to r klasy, k tó ra zawie< ra w sobie obiekty innej klasy.
I
Z au w aż , że k o n stru k to r ma bardzo ro z b u d o w an ą listę inicjalizacyjną.
Przyjrzyjmy się inicjalizacji analizując pozycje na tej liście poz_x(x)
O zn acza to, że s k ła d n ik p o z _ x należy zainicjalizow ać w artością arg u m e n tu x S kładnikow i tem u m oglibyśm y rów nie d o b rz e p rzypisać w arto ść początkowy ju ż w c ie le k o n s tru k to ra .T o d la te g o ,ż e te n s k ła d n ik n iem a p rz y d o m k a const. D alej na liście w id z im y poz_y(y)
T utaj zajm ujem y się składnikiem , p rz y któ ry m stoi p rz y d o m e k c o n s d T akiem u obiektow i m ożna nadać w arto ść ty lk o w m om encie jego narodzin P otem już p rzep ad ło . C zyli: w p rz y p a d k u sk ład n ik a p o z _ y nie m oglibyśm y p rz y p isa ć m u w arto ści W ciele k o n stru k to ra. T o ju ż byłoby za p ó źn o . Inicjalizacja o d b y ć się m u s i tutaj, zfl po m o cą listy inicjalizacyjnej. D alej na liście inicjalizacyjnej w idzim y n astęp u jące w y rażen ie prędkościomierz (x ,
y+3, "Prędkość", "km/h")
M am nadzieję, ż e ro zp o zn ajesz. Jest to w y w o łan ie k o n stru k to ra d la obiekttl p rę d k o ś c io m ie rz będącego sk ład n ik iem pan elu . To w y w o ła n i! k o n stru k to ra m u sim y um ieścić na liście inicjalizacyjnej. Z ap a m ię taj O biekt jakiejś klasy - b ęd ący sk ład n ik iem innej k lasy m o ż e b y t in icjalizo w an y jedynie za p o m o cą listy inicjalizacyjnej (czyli w eta* pie 1). N ie m o ż n a próbow ać u ru ch o m ić k o n stru k to ra ta k ie g o o b iek tu sk ła d o w e g o później, czyli z w n ę trz a (z ciała) k o n stru k tó ra k lasy , W której o b ie k t ten się zaw iera (E tap 2). W tedy ju ż za p ó źn o .
Rozdział. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy
693
Co zrobić, jeśli obiekt składowy nie ma konstruktora? R zeczyw iście - m o ż e być ta k a sytuacja, p o sia d a n ie konstruktorai m e jest przeciez obo w iązk o w e. W ó w czas p o prostu na liście m icjalizacyjnej m e u m ieszcza się go, . .. Jeśli klasa m a k o n stru k to r w y w o ły w an y bez ż a d n y c h a rg u m e n tó w - czyi, tzw. k o n stru k to r d o m n ie m a n y - i to ten k o n stru k to r ch cem y u zy c. w ó w czas m ożna te pozycję na liście inicjalizacyjnej pom inąć. Zostanie on przecież użyty - jak sama nazwa zoskazuje -
przez
domniemanie". . . . Jeśli je d n ak klasa ob iek tu sk ład o w eg o m a k o n stru k to ry (a w szy stk ie z ja k im ^ a rg u m e n tam i), to p o m in ięcie w yw ołania na liście sp o w o uje ą P 1 K o m p ilato r b ęd zie się d o m a g a ł określenia, k tó rą w obec tego w ersję k tó ra m a u ru ch am iać. N a naszej liście inicjalizacyjnej ostatnią pozycją jest d r u g i (x, y+7, opis2,
jedn2)
T u są a rg u m e n ty w y w o ła n ia ko n stru k to ra d ru g ie g o obiektu sk ład o w eg o Jak w id ać, a rg u m e n ty w y sy ła n e k o nstruktorow i - to o p isy do starczo n e p rz e z tego, k to w y w o łał k o n stru k to r k lasy p a n e l . . . ,, Zobaczymy to dokładniej w instrukcji © , która jest definicją obiektu klasy p a n e l Argumentami są tam, między innymi, opisy przeznaczone dla
przyrządu d r u g i.
©
K iedy w y k o n a n e z o sta n ą w szy stk ie inicjalizacje z listy, w ted y d o p ie ro zaczyna się w y k o n y w a n ie ciała k o n stru k to ra klasy „zaw ierającej czyli k lasy p a n e l . W e w n ą tr z te g o k o n s tr u k to r a w id z im y d w a p r z y p is a n ia w s k a ź n ik ó w . O czy w iście i to m o g łem um ieścić na liście inicjalizacyjnej, ale m e chciałem C ę W c te ^ j^ s t też w y w o ła n ie funkcji wypisującej w o k reślo n y m miejscu na ekranie
©
d e s tru k to ra polega na tym, że w m iejscu, g d zie był ty tu ł panelu, pojawia się informacja, że p a n e l jest zezło m o w an y . C ie k aw sza jest tu taj kolejność zdarzeń. O ile w p rzy p ad k u i tworzenia obiektów n ajp ie rw ru szy ły d o p racy k o n stru k to ry o b ie k tó w składow ych a dopiero p o te m k o n s tru k to r klasy zaw ierającej, to w p rz y p a d k u d estru k to ró w ,est o d w ro tn ie : Z ap am iętaj: N ajpierw rusza do pracy destruktor klasy zaw ierającej obiekty a dopiero potem w ykonyw ane są destruktory obiektów składow ych.
T o ta k że ła tw o zap am iętać: Jeśli chcesz d o k o n ać destrukcji obiektu k la sy radio to n a jp ie rw ro z w a la sz m ło tk iem obu d o w ę, a d o p ie ro w ted y m ozesz zab rać s «, z a d estru k c ję tran z y sto ró w .
5
694
Rozdz. 15. K onstruktory i D estruktory K onstrukcja obiektu, którego składnikiem jest obiekt innej klasy W naszym p rz y p a d k u klasa przyrząd nie m iała d estru k to ra . Nic nie jest w obec tego ak ty w o w an e. O Tutaj funkcja sk ład o w a aktualizuj w y w o łu je funkcję s k ła d o w ą przyrząd::zmien_wskazanie(int i ) ;
na rzecz obu o b iek tó w składow ych. Z au w aż , co k o n k retn ie panel wysyła sw oim p rz y rz ą d o m jako argum ent - czyli co poleca im p o k azać n a ekranie. Jest to na p rzy k ła d w y rażen ie: *wskl
Jak się do m y ślasz, jest to w artość takiej zm iennej ty p u int, na którą teraz pok azu je w sk aźn ik wskl. Czyli jest to p o p ro stu jakaś liczba całkow ita. © W fu n k cji main w id z im y d efin icję e g z e m p la rz a o b ie k tu k la sy panel. E gzem plarz ten n azy w a się kabina. G d y przy jrzy m y się arg u m e n to m tej definicji (arg u m e n to m w yw ołania k o n stru k to ra), to w id zim y , że: N a d efin io w a n y m tutaj p anelu m a być n ap isan y ty tu ł Panel pilota . Lewy g ó rn y róg tego panelu m a być w k olum nie 1 i rz ę d z ie 2. P rzy rząd p ie rw szy ma p o k azy w ać stan zm iennej prędkość, P rzy rząd d ru g i stan zm iennej azymut. W ysyłam y te ż napisy, które m ają się pojaw ić na d ru g im p rzy rząd zie. W dalszej części p ro g ra m u w idzim y p ętlę for, która m a im ito w ać nam ciągle zm ian y zm ien n y ch prędkość i azymut. Na te zmienne ( p r ę d k o ś ć i a z y m u t ) spojrzą oba przyrządy z panelu k a b i n a w cluoili, gdy wywołam funkcję składową a k t u a l i z u j © © Dalej w p ro g ram ie w id zim y definicję in n e g o obiektu klasy panel. O biekt ten n azy w a się maszynownia. Z a rg u m e n tó w k o n stru k to ra w id z im y , że N a d efin io w a n y m panelu m a być n ap isan y ty tu ł 'P an el M echanika . «$♦ Lewy g ó rn y róg tego panelu m a b y ć w kolum nie 40 i rz ę d z ie 2. P ierw szy jeg o p rzy rząd m a p o k a z y w a ć stan zm ien n ej prędkość, P rzy rząd d r u g i sta n zm iennej paliwo. Jak w idać, p an el ten p o jaw i on się na p raw ej stro n ie e k ra n u . Ż e ta k było w istocie - m o żesz się p rz e k o n a ć p atrz ąc na zam ieszcz o n y w y d ru k e k ra n u .
Rozdział. 15. K onstruktory i D estruktory K onstruktory nie-publiczne ?
695
Z atem - m im o iż lista inicjalizacyjna klasy p a n e l n ap isan a b y ła w takiej kolejności poz_x (x) , // <— lista m c ja liz a c y jn a . poz_y(y), , prędkościomierz(x, y+3, Prędkość drugi (x, y+7, opis2, jedn2)
to i tak kolejność w y d a rz e ń b ęd zie - w m yśl o p isan ej zasad y
"km/h"),
inna!
O to, jak to się odbędzie: 1
* *
wywołanie konstruktora prędkościomierza - bo to gość p ierw szy
«$► w y w o łan ie k o n stru k to ra obiektu d r u g i - bo to gość d ru g i ♦♦♦ inicjalizacja sk ład n ik a poz_x - bo to dom ow nik p ie rw szy w klasie panel
M ów iąc tu taj o b razo w o np. domownik pierwszy - m am na m yśli, że jest to składnik, k tó reg o d eklaracja w klasie panel jest w cześniej (czyli wyżej) ♦$» inicjalizacja sk ład n ik a p o z y - b o to d o m o w n ik d ru g i w k la s ie p a n e l •
i sk o ro ju ż w szy stk ie inicjalizacje zostały w y k o n an e, n astąp i teraz w y k o n a n ie ciała k o n stru k to ra obiektu k la sy panel. u n in i- j a i— l i i i — m
15.8 Konstruktory nie-publiczne ? K o n stru k to r jest zw y k le d ek la ro w a n y jako p u b liczn y . W ydaje się to oczyw iste, b o p rzec ież obiekty p o w o łu je się do życia b ęd ąc w „św iecie zew n ętrzn y m w sto s u n k u d o klasy. K o n stru k to r jest sk ład n ik iem klasy i jako ta k ieg o - obow iązują go rów nież zw y k łe re g u ły d o stęp u u sta la n e za pom ocą słó w p u b l i c / p r o t e c t e d / private.
K lasa, k tó ra nie m a p u blicznych k o n stru k to ró w n a z y w a n a jest k la są p ry w atn ą. N a s u w a się pytanie:
Jak są tworzone obiekty takiej prywatnej klasy, skoro nie można sięgnąć do konstruktora, bo jest niedostępny? B ardzo p ro sto . K on stru k to r jest n ied o stęp n y d la tzw . szerokiej publiczności, ale je st d o s tę p n y dla obiektów tej klasy. K o n stru k to r je st funkcją sk ład o w ą tej klasy, co p ra w d a p ry w atn ą funkcją sk ład o w ą, ale o b ie k ty tej klasy m aja d o stęp do p ry w a tn y c h składników . S łow em : obiekty tej klasy m o g ą być tw o rzo n e p rz e z in n e obiekty tej klasy.
„-M am cię!" - zaw o łasz - ,,/4 skąd się weźmie pierwszy obiekt tej klasy?"
696
Rozdz. 15. K onstruktory i D estruktory K onstruktory nie-publiczne ? D obre pytanie. Pierw szego obiektu rzeczyw iście w ten sp o só b nie d a się powo> łać do życia. Z astanów się jednak: kto jeszcze m a d o s tę p do prywatnych sk ładników klasy, w ięc mógłby u ruchom ić p ry w atn y k o n s tru k to r ? O czyw iście — funkcja zaprzyjaźniona, albo klasa zap rzy jaźn io n a. OUi przykład: class star { friend vcid kreator(char z); private : char znak; star (int i) ; //<— p r y w a tn i/k o n s tr u k to r
Definicja sam eg o konstruktora jest teraz nieistotna. W ażn a jest n ato m iast defini cja tej funkcji zaprzyjaźnionej. void kreator(char z)
{ star a ('* '); static star b ( '*') ;
/*
. . .
*/
wskaźnik = new start’?');
]
//o b iekt a u t o m a t y c z n y //o b iekt s t a t y c z n y //ja k a ś p ra ca z n u n //o b iekt w „ za p a sie p a m ię c i"
Jak w idzim y, m im o "prywatności" k o n stru k to ra - w bloku funkcji k r e a t o r m ożna zdefin io w ać obiekt klasy s t a r . C o to znaczy, że w obrębie bloku funkcji? To, że obiekty tam tw orzone m ogą być: ♦J* -a u to m a ty c z n e lu b s ta ty c z n e -c z y li ich n a z w y z n a n e s ą tylko w ew nątrz tej funkcji. (Statyczne są d o d a tk o w o nieśm iertelne). ♦> - tw o rzo n e operatorem new. W ted y z takim o b iek tem m o żn a pracow ać n aw et p o za funkcją k r e a t o r i w d o w o ln y m m o m en cie go skasow ać
Oczywiście można pracować poza funkcją pod warunkiem, że funkcja przekaże komuś szn urek tego balon ika (przepraszam: adres, pod którym jest utioorzony przez nią nowy obiekt).
Skoro k o n stru k to ry byw ają p rzeład o w an e, a są o n e p rzecież zw y k ły m i funkcja m i składow ym i, to za pom ocą słów p r i v a t e / p r o t e c t e d / p u b l i c m ożem y różnie określić d o stęp d o różnych w ersji k o n stru k to ra. In n y m i słow y jeden m oże być p ry w a tn y , in n y publiczny itd. Co to oznacza w p rak ty ce? To, że jeden ze sp o so b ó w k o n stru o w an ia obiektów jest d o stęp n y dla szerokiej publiczności, a in n y tylko d la „sw o ich ". P róba u tw o rzen ia obiektu za pom ocą k o n stru k to ra, d o k tó reg o nie m a m y p raw a, z o s tanie o d rzu co n a p rz e z kom pilator. (Tak sam o , jak p ró b a w y w o łan ia inne) pryw atnej funkcji). Jeśli chcem y - to m o ż em y k reo w ać o b iek t, ale tylko w sposób, k tó ry jest n am dostępny.
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
697
class club { . . . friend void przyjaciel(); int x; char tekst[20]; club (int n, char *s) ; PUt>club(int n);
/ / p r y w a t n y k o n s tr u k to r
/ / p u b lic z n y k o n s tr u k to r
); Jeśli w p ro g ram ie, w e w n ą trz funkcji p r z y j a c i e l , chcielibyśm y zd efin io w ać ob iek t k lasy c l u b , to m o żem y posłużyć się tak im i definicjam i: void przyjaciel()
{ club jacek(5); club mirek(7, "Kangur");
//
...
Natom iast w innych miejscach programu pierwsza z tych definicji jest akcepto walna, ale druga błędna: club maciek (58); club zosia (3, "Ruda");
/ / o .k . //ź le !!
Konstruktor domniemany prywatny? M o żn a sobie w yobrazić, ż e k toś definiuje klasę, a w p rzy p ły w ie paranoi (m ania p rześla d o w cz a) decyduje, b y k o n stru k to r d o m n ie m a n y był p r i v a t e . (Czyli n ie d o stę p n y dla nikogo sp o z a klasy i przyjaciół). W zasadzie mu oczywiście w olno - ale trzeba pamiętać, że kompilator czasem potrzebuje użyć konstru która domniemanego niejawnie. Na przykład potrzebuje go w przypadku definiowania operatorem new tablicy kilku obiektów danej klasy. . Co w te d y ? Jeśli p ro g ram ista d o p ro w ad zi d o takiej sytuacji, ż e kom pilator b ęd zie te g o k o n stru k to ra d o m n iem an eg o p o trze b o w a ł - czy k o m p ilato r użyje tego k o n s tru k to ra bez p o zw o len ia? Nie. Kompilator odmówi w ów czas współpracy z takim programistą, sygnalizu
jąc po prostu błąd kompilacji.
Uwaga dla wtajemniczonych: Jeśli k o n stru k to r jest oznaczony d o stęp em p r o t e c t e d , to znaczy, że m oże być w y w o ła n y w sytuacjach identycznych j a k p r i v a t e , a w d o d atk u m o ż e być w yw ołany z k lasy pochodnej od danej klasy.
Piiiiiinin —*1"—....................... .. ........ .......................... ................ ..
■ ■ ■ 1
15.9 K o n s tru k to r k o p iu ją c y (albo in ic ja liz a to r k o p iu ją c y ) Konstruktorem kopiującym w danej klasie k la s a nazywamy konstruktor, któ ry m ożna wywołać z jednym argumentem poniższego typu
5
698
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący) klasa::klasa ( klasa &);
A rg u m en tem jest, jak w idać, referencja (p rzezw isk o ) o b ie k tu d an ej klasy. Inne w arian ty k o n stru k to ra kopiującego to: klasa::klasa(const klasa &); klasa::klasa(volatile klasa &) ; klasa::klasa(const volatile klasa &);
Dawno temu, w rozdziale o przeładowaniu nazio funkcji rozmawialiśmy, że wszystkie warianty tak przeładowanej funkcji mogą istnieć równo cześnie. (Czy pamiętasz jeszcze scenkę o befsztykach normalnych, spalo nych na węgiel i ociekających knoią?).
z a m ia s t w „zw ykłym " k o n stru k to rz e w y sz czeg ó ln iać argum enty inicjalizacji - m ów im y: chcę, by n o w y o b ie k t był taki sam jak tam ten , który posyłam na w zór. Z au w aż, że w definicji pow iedzieliśm y: k o n stru k to r, k tó ry m o ż n a w yw ołać z jed n y m arg u m e n te m . To dlatego, że d o p u sz c z a się, by b y ły jeszcze inne arg u m enty , ale d o m n iem an e. K o n stru k to rem kopiującym klasy K je st więc: K::K(K &)
albo K::K(K&,
float = 51.45,
int * = NULL)
O czyw iście „alb o -alb o ". Deklaracja p ie rw sza jest b o w iem jakby szczególnym p rzy p ad k iem tej d ru g iej, w ięc w definicji tej klasy nie m o g ą się o n e pojawić rów nocześnie. K onstru k to r k o p iu jący nie jest o b o w iązk o w y . Jeśli go n ie zd efiniujem y, w ó w czas ko m p ilato r w y g en eru je go sobie sam . Konstruktor kopiujący inaczej można by nazwać
inicjaiizatorem kopiującym. r ( M I I I ftU .H.POW W W HiW SIt W I I I 1H W ił UW MII m i »
WOtI t fftfMMHMKWJMMMM Ji 'CJłU 'KU M f F J N M W M M M M M M M H n M I
To dlatego, że d z ia ła on na praw ach inicjalizacji, a nie p rz y p isa n ia (p o d staw ie nia). Co to o zn acza? O znacza to, że p racu je w ted y , gdy o d b y w a się inicjalizacja nowego o b iektu, a n ie zw y k łe p rzy p isan ie.
Kiedy wywoływany jest konstruktor kopiujący ? W kilku sytuacjach, k tó re m ożna najogólniej podzielić na: - g d y tego jaw n ie zażąd am y , ♦♦♦ - bez n aszej w ied zy (niejaw nie).
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
699
W yw ołanie k o n stru k to ra k o p iu ją c eg o n a n a s z e ż y c z e n ie
n a stę p u je w ted y , g d y teg o jaw n ie z a ż ąd am y i d efin iu jem y n o w y obiekt w n as tęp u jący sposób: K obiekt wzór;
/ / w cześniej zd efin io w a n y obiekt klasy K
£ /obiilct_nowy - ebiektj.'zor;
lak w id ać, d efin iu jem y tu n o w y obiekt, a d o k o n stru k to ra w y sy łam y inny istniejący o b iek t jako w zór. Z ałożyłem tu oczyw iście, z e klasa K m a k o n stru k to r k o p iu jący , i że ten sta ry o b iek t jest g o d n y tego, by g o rzeczywiście kopiow ać. N i e j a w n e w y w o ł a n i e k o n s t r u k t o r a k o p i u j ą c e g o k l a s y K n a s t ę p u j e w k i lk u sytuacjach
...
przestania argumentów do funkcji - jeśli argumentem funkcji jest obiekt klasy K, (a przesłanie odbyw a się przez wartość).
a) Podczas
lak w iem y - n a w e t z ty p ó w w b u d o w a n y c h - w obrębie funkcji a rg u m e n t jest k o p io w an y 3 i funkcja p racu je na kopii. Jeśli w ięc arg u m e n tem jest obiekt klasy K, to m u si istnieć n a rz ę d z ie do zrobienia kopii obiektu tej klasy K. S łow em - m usi zostać u ży ty k o n stru k to r kopiujący klasy K. W y w o łan ie takiego k o n stru k to ra o d b y w a się bez n aszeg o u d ziału . S łużby o d p o w iad ające za p rz e syłanie arg u m e n tó w d o funkcji - załatw iają to sam e.
*
b) Podczas, gdy funkcja, jako swój rezultat, zwraca (przez wartość) obiekt klasy K. W y tłu m aczen ie jest identyczne jak w yżej. T ak że słu żb y specjalne, czyli m echanizm zw ro tu rezu ltatu funkcji przez w artość, p o słu ży się tym konstruktorem. ........... ^
Cii obiektu ch w ilo w eg o będącego w arto ścią tej funkcji. Ten chw ilo w y obiekt nie jest ju ż lokalny - jest w id z ia ln y z zew nątrz, z zak resu z którego funkcję w yw ołaliśm y.
15.9.1
Przykład klasy z konstruktorem kopiującym
N ajpierw w y j a ś n i e n i e d o c z e g o s łu ż y n a s z a k la s a
P rzy p o s łu g iw a n iu się a p a ra tu rą p om iarow ą zach o d zi konieczność o d m o m u rz ą d z e n ia M ów i się na to także: kalibracja. Polega to na tym , ze należy sfo rm u ło w ać z a s a d ę zam iany jednej w ielkości na inną. P rz y k ład o w o : 3)
casus: fotografia babci
700
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
Cechowanie wagi (takiej jak daxvniej w sklepie spożywczym: z dwom® szalkami i wskazówką) to poznanie zależności: jak wychylenie w s k a z ó io k .i (zmierzone w milimetrach lub stopniach) zamienić na kilogramy. Po ustaleniu takiej zależności wychylenia od ciężaru - maluje się na w a d zie skalę. W a p ara tu rze naukow ej często posługujem y się tzw . an a liz a to ra m i am plitudy,, które m ierzoną w ielkość fizyczną - na p rzy k ład energię k w a n tu prom ieniow a nia - zam ieniają na liczbę od 1 d o 8192. Ta liczba p rz y c h o d z i z u rząd zen ia pom iarow ego d o kom putera. Liczba taka, to jakby p o d a n a w m ilim etrach w artość w y chylenia w skazów ki. Taką liczbę trzeba zam ien ić na w ielkość, którą) ona sym bolizuje - czyli ciężar p ro d u k tu albo energię k w a n tu prom ieniow ania. Często w y starczy proste rów nanie waga
= (a * wychylenie) + b
energia
= (a *liczba) + b
Jeśli nic z tego nie zrozumiałeś - nie szkodzi, rozm aw iam y przecież o k o nstruktorze kopiującym . W y starczy w iedzieć, że nasza klasa sk ła d a się z tych dw óch liczb: a , b o raz z fu n k cji składow ej, która oblicza p o w y ższą zależność. D odatkow o, jest - oczyw iście —nasz konstruktor kopiujący. #include #include using namespace std;
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll class kalibracja
1 double a, b; string nazwa;
//w s p ó łc z y n n ik i ka lib ra cji //n a z w a d la n a sze j k o n tr o li
O
public : U ---------- k o n s tr u k to r kalibracja(double wsp_a, double wsp b, string txt) ; //--------- k o n s tr u k to r k o p iu ją c y kalibracja! kalibracja & wzór);
//
0
// -------- in n e fu n k c je sk ła d o w e double energia (int kanał)
1 return( (a * kanał) + b ); string opis()
{ return( nazwa);
)
// ©
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllH I I I I I I I I I I I I I I
kalibracja::kalibracja(double wsp_a, double wsp_b, string txt ) : a(wsp a), b(wsp b)
~
{
~
// O
nazwa = txt;
} /★ •A r*********************************************************** ^ kalibracja::kalibracja(kalibracja i wzorzec)
// ©
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
a
=
wzorzec.
b
=
wzorzec.b;
a;
nazwa = w z o r z e c .nazwa;
//za m ia st: nazwa
701
=
To
ja,
konstruktor
kopiunacy
// ©
!!!
;********** Z /**************************************************’ void
fun
pierwsza ( kalibracja
kalibracja
fun
odebrana);
^^^*************************/
druga (void);
z*******;*****-******************************
int {
main()
kalibracja
k o b a l t (1.07,
2.4,
"ORYGINALNA
KOBALTOWA
");
II Różne warianty tego samego
// O
kalibracja e u r o p (kobalt) ; //kalibracja e u r o p = k a l i b r a c j a (k o b a l t ) ; //kalibracja cout int cin
<<
"O
europ
który
=
kobalt,-
kanał
widma
chodzi
?
:
»
kanał; » kanał;
cout
cout
// ©
«
"\nWedlug
kalibracji
«
k o b a l t . o p i s ()
<< «
" odpowiada energia " kobalt.energia(kanał)
«
"\nWedlug
e u r o p . o p i s () " \ n k a n a l o w i nr
<<
"
kalibracji
odpowiada
\nopisanej
" \n k a n a lo w i
«
« «
kobalt,
"
<<
energia
«
nr
"
«
jako
// ©
kanał
endl;
europ,
\nopisanej
jako
"
// ©
kanał "
<< e u r o p .energia(kanał) << endl; cout
« " \ n D o <<
funkcji
pierwszej
k o b a l t .o p i s ()
<<
wysyłam
kalibracje
endl; 4 1
h OO
fun pierwsza(kobalt) ; cout «
"\nTeraz wywołam funkcje druga, a jej" " rezultat\n" " p o d s t a w i e do
innej
.. kalibracji
tt \n
,
cout << "Obiekt chwilowy zwrócony jaJc° " "rezultat funkcji \nma opis " << ( fun_druga() ).opis() << endl; /****************************************** void
fun
pierwsza(
kalibracja
//O0
*** * * * * * * * * * * * * * * * * /
odebrana)
cout «
"Natomiast w funkcji pierwszej " „ "odebrałem te kalibracje\n\topisana ]ako << odebrana.opis() << endl;
//O ©
5
702
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
**/ o es
z********************************************************** kalibracja fun_druga (void) //Ut { kalibracja w e w n (2, 1, "WEWNĘTRZNA"); //O © cout << "W funkcji fun_druga definiuje kalibracje" ” i ma \nona cpis : " << wewn.opis() « endl; return wewn;
□
//O©
Na ekranie, po wykonaniu tego programu, może pojawić się następujący tekst 0 który kanał widma chodzi ? : 100 Według kalibracji kobalt, opisanej jako ORYGINALNA KOBALTOWA kanałowi nr 100 odpowiada energia 109.4
©
Według kalibracji europ, opisanej jako — To ja, konstruktor kopiujący !!* — kanałowi nr 100 odpowiada energia 109.4
©
Do funkcji pierwszej wysyłam kalibracje ORYGINALNA KOBALTOWA Natomiast w funkcji pierwszej odebrałem te kalibracje opisana jako — To ja, konstruktor kopiujący !!! — Teraz wywołam funkcje druga, a jej rezultat podstawie do innej kalibracji W funkcji fundruga definiuje kalibracje i ma ona opis : WEWNĘTRZNA Obiekt chwilowy zwrócony jako rezultat funkcji i — ma opis — To ja, konstruktor kopiujący
□
Jeśli zaś masz sprytniejszy kompilator, to wydruk treści ekra nu różnić się będzie w ostatniej linijcje O który kanał widma chodzi ? : 100 Według kalibracji kobalt, opisanej jako ORYGINALNA KOBALTOWA kanałowi nr 100 odpowiada energia 109.4 Według kalibracji europ, opisanej jako -- To ja, konstruktor kopiujący !!! - kanałowi nr 100 odpowiada energia 109.4 Do funkcji pierwszej wysyłam kalibracje ORYGINALNA KOBALTOWA Natomiast w funkcji pierwszej odebrałem te kalibracje opisana jako — To ja, konstruktor kopiujący !!! - Teraz wywołam funkcje druga, a jej rezultat podstawie do innej kalibracji W funkcji fun_druga definiuje kalibracje i ma ona opis : WEWNĘTRZNA Obiekt chwilowy zwrócony jako rezultat funkcji
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
703
ma opis WEWNĘTRZNA
£
Spójrzmy na ciekawsze miejsca programu O O prócz w sp ó łc z y n n ik ó w ró w n an ia - w p ro w a d z a m y też o b iek t k lasy s t r i n g , w którym b ęd ziem y p rzech o w y w ać opis słow ny. 0 K on stru k to r k o p iu jący (tylko deklaracja). © Jedną z funkcji sk ład o w y ch jest funkcja, która podaje n am o p is słow ny tego obiektu. © D efin ic ja k o n s tr u k to r a (ta k ie g o z w y k łe g o ). W id z im y , ż e ln ic ja liz a c ję sk ład n ik ó w a i b załatw iłem w liście inicjalizacyjnej. T en za p is jest krótszy m z n ap isan ie w ciele k o n stru k to ra a = wsp_a; b = wsp_b;
W ciele jest natomiast przepisanie (p rzy słan eg o jako a rg u m e n t) tekstu do obiek tu sk ład o w eg o n a z w a . . . To też mogłem umieścić w liście inicjalizacyjnej. Stosowne wyrażenie
miałoby wtedy postać: naz wa ( t x t )
© Definicja konstruktora kopiującego. O tym , że to w ła śn ie on - m ów i n am arg u m en t: jest to referencja do obiektu swojej w łasnej klasy . W ciele k o n stru k to ra w id zim y sk o p io w an ie sk ład n ik ó w o b iek tu w zorcow ego d o tego o b iek tu , na rzecz którego w y w o łan o konstruktor. Słyszałeś co p o w ied ziałe m ? Do: tego - a w ięc t h i s . Z atem p ie rw sze dw ie linijki m o żn a inaczej z a p isa ć jako this->a this->b
15
wzorzec.a; wzorzec.b;
Dalej też by m o ż n a p rzekopiow ać treść strin g u n azw a, ale w te d y kopia niczym nie ró żn iłab y się o d w zorca, dlatego w p isu jem y tam tekst, k tó ry będzie nam u d o w a d n ia ł, że ru sz y ł do pracy ten w łaśn ie k o n stru k to r kopiujący. © Jest to jawne u ru c h o m ie n ie k o nstruktora kopiującego. N o, m o że m e tak całkiem jaw n e, ale ju ż p rzy zw y czailiśm y się, że p o n iższe zapisy są jakby tym sam ym . kalibracja europ = kalibracja(kobalt) , kalibracja europ = kobalt;
A jeśli się nie p rzy zw y czailiśm y , to p rzy p o m in am jakby to w y g ląd ało dla typów w budow anych float wzór = 6.66; float kopia2 = float (wzór); float kopial = wzór;
// obiekt w zo rc o w y / / ca łkiem ja w n ie //p ra w ic -ja w n ic
704
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
Jeśli linijka z k o p ia 2 C ię dziwi, to przypominam, że operacja rzutowania może mieć dwie formy: ( f l o a t ) w zór, oraz f l o a t f w z ó r ) . Tę drugĄ formę stosujemy tutaj. Tym sposobem użyliśm y jaw nie (a przynajm niej ś w ia d o m ie ) ko n stru k to ra kopiującego. © Cała kalibracja jest p o to, by - na ż ą d a n ie - przeliczyć m ilim e try na kilogram y (lub kanały na energie). Tutaj py tam y u ży tk o w n ik a o w a rto ść d o przeliczenia. © To, co p ro g ram o d p o w iad a, nie jest tak ie istotne w tym m o m en cie. Przyjrzyjm y się zaw artości strin g u nazwa - bo tu taj zorientujem y się k ie d y m ieliśm y d o czynienia z kopią. Patrząc na ekran w id zim y , że w tym m iejscu m am y oryginał. P rzypom in am , ż e w yw ołanie funkcji składow ej opis n a rz e c z obiektu klasy kalibracja ow ocuje stringiem nazwa. Czyli p o n iższe in stru k c je są rów no w ażne: cout << kobalt.nazwa;
/ / to s a m o c o : cout << kobalt.opis ();
Z tym , że nie m o żem y tak tego zro b ić (składnik nazwa je st p ry w atn y ), w ięc posługujem y się funkcją składow ą. © W yw ołanie tejże funkcji składow ej na rzecz obiektu europ p o k azu je tkw iącą tam treść - u d o w ad n iającą, że obiekt p o w stał p rzy uży ciu n a s z e g o ko n stru k to ra kopiującego. O O W yw ołanie funkcji, która, jak łatw o sp ra w d z ić w definicji, p rzy jm u je arg u m en t będący obiektem k lasy k a l i b r a c j a . O b iek t m a być p rz e s ła n y p rzez w artość. Linijkę w cześniej w ypisaliśm y na ek ra n ie opis tego o b ie k tu , k tó ry teraz służy jako argum en t. O © W tym miejscu w e w n ą trz funkcji w y p isu jem y opis z o b ie k tu , który o trzy m a liśm y. Co w id z im y na ekranie? O czy w iście - u ży ty z o s ta ł n asz k o n stru k to r kopiujący. To chciałem pokazać. N ajw ażniejsze: u ży ty z o s ta ł niejaw nie. My go n ie uru ch am ialiśm y . P rzy p o m in am więc: K o n stru k to r kopiujący k lasy K jest u ru c h a m ia n y n iejaw nie w sytu- j acji, g d y d o jakiejś funkcji w y sy łam y obiekt k la sy K p rzez w artość. U ży ty on zostaje, jako n a rz ę d z ie d o zro b ien ia lokalnej kopii p rzy słan eg o obiektu. O © Z tej linijki m u sz ę się gęsto tłum aczyć. N ap iszm y to prościej. C o to jest? ( fun_druga() ).opis() N ajpierw o d p o w ie m , a potem u d o w o d n ię . Jest to w y w o łan ie funkcji sk ład o w ej opis na rzecz o b iek tu ch w ilow ego p rzy słan e g o jako re z u lta t w y w o łan ia funkcji sk ład o w ej fun_druga. Inaczej m ó w iąc coś takiego: ( obiekt chwilowy ).opis() T eraz p o k ażę s k ą d się bierze o b iek t chw ilow y. S pójrzm y w ięc na definicję funkcji fun_druga G O . D eklarację tej funkcji czytam y: f u n _ d r u g a jest fu n k cją w y w o ły w a n ą b ez ż ad n y c h a rg u m e n tó w , a k tóra jako re z u lta t zw raca (pr/.ez w artość) ob iek t k lasy (ty p u ) kalibracja.
j
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
705
O © W ew n ątrz d a t a o m aw ian ej funkcji - definiujem y o b iek t k la sy kalibracja. Ten e g ze m p la rz n azy w a się wewn. ja k nam w ia d o m o , tak d efin io w an y o b iek t lokalny au to m a ty c z n y czy i, jego n azw a jest z n a n a tylko w tej funkcji (zakres w a ż n o ść ), a u m r z e o n w chw .lt, gdy opuścim y tę fu n k cję (czas życia). Coś sobie w tej funkcji na tym obiekcie pracujemy - w naszym przypadku jest to wypisanie na ekranie jego opisu. Już nawet nie obliczamy energii promieniowania, bo to jest nudne w porównaniu z naszymi konstrukto
rami kopiującymi. O © N ad ch o d zi m o m e n t opuszczenia funkcji. N a m om ent p rz e d s w ą zag ład ą obiekt lokalny s ta w ia n y jest obok słow a return. To sp ra w ia ze słu żb y sp eq aln e o d p o w iad ające z a m echanizm zw rotu rezu ltatu funkcji - tw o rz ą obtekt chw ilo w y klasy k a l i b r a c j a - i d o niego kopiują obiekt wewn. N ie m ogą m u u ra to w a ć życia, bo został o n założony na stosie - czyli jakby na piasku p la ży , d o której zbliża się p rzy p ły w . K opiują go jed n ak za pom ocą naszego k o n stru k to ra kopiującego. O biekt ginie, a le funkcja jako w artość zw raca obiekt ch w ilo w y będący jego kopią. K opia n ie jest oczyw iście w iern a - o to już się p o staraliśm y w linijce & podm ien iając te k s t opisu. Teraz ch y b a ro z u m ie sz dlaczego w y rażen ie ( fun_druga () ) m a w arto ść b ę d ą c ą chw ilow ym obiektem klasy kalibracja. N a rzecz takiego obiektu w y w o łu je m y funkcję składow ą opis, po czym re z u lta t - strin g o p su piszem y na e k ra n ie . Spójrz na ekran. W idzisz, że jest tam opis u d ow adniający, , że p raco w ał n a s z w łasny k o nstruktor kopiujący. U ru ch o m io n o go bez naszego u d ziału . . O biekt jest ch w ilo w y . Po linijce pro g ram u , w której pow stał, ginie. Żałuję, ze w tej chw ili je szc ze nie um iem y się (sw obodnie) p o słu g iw ać operacjam i z ekranem , b o w te d y zobaczyłbyś, że ten obiekt c h w ilo w y m a w sobie w s pó łczy n n ik i 2, 1 - te które skopiow ał z obiektu wewn. Z apam iętaj: K o n stru k to r kopiujący klasy K m oże być u ru ch o m io n y niejaw nie w sytuacji, gdy funkcja zw raca przez wartość o b iek t klasy K U ż y ty on zostaje jako n arzęd zie do zrobienia ob iek tu chw ilow ego b ęd ą c e g o rezultatem funkcji.
m
Jeśli masz sprytniejszy kompilator... to w y d ru k n a e k ra n ie m oże się różnić. Spójrz na zam ieszczo n y d ru g i w ariant w y d ru k u e k ra n u . Różnica jest w ostatniej linijce, a oznacza to, ze
5
706
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący) w funkcji f u n _ d r u g a O O , która ma zw ró c ić (p rzez wartość!) o b iek t klasy k a l i b a r c j a (instrukcja © 0 ) - nie ru s z a d o p racy k o n stru k to r kopiujący. Jeśli nie b ard zo rozum iesz, co m am na m yśli, p rzy w o łajm y n aszą d ram aty czn y scenkę: Nadchodzi moment opuszczenia funkcji. Na moment przed swą zagładą obiekt lokalny stawiany jest obok słowa r e t u r n . To sprawia, że służby specjalne odpowiadające za mechanizm zwrotu rezultatu funkcj i - tworzul obiekt chwilowy klasy k a l i b r a c j a - i do niego kopiują obiekt wewn. J Nie mogą mu uratmuać życia, bo został on założony jakby na piasku plaży, do której zbliża się przypływ. Kopiują go jednak za pomocą naszego konstruktora kopiującego. Tu jest w łaśnie różnica. Służby sp ecjaln e ratują mu życie. O czyw iście nie m a m ow y o żad n ej litości. Służby sp ecjaln e (czytaj: k o m p ilato r) u zn ają, że jest m a rn o traw stw e m takie p o stęp o w an ie, g d y niszczy się ja k iś o b iek t (lokalny) i natychm iast tw o rz y się identyczny ch w ilo w y . Zatem z d a n ie : "Obiekt ginie, ale funkcja jako loartość zwraca obiekt chwilowy będący jego kopią" nie zaw sze m u si być p raw d ą. S prytny k o m p ilato r m oże p rz e m ia n o w a ć obiekt lokalny na o b iek t chw ilow y, mający b y ć rezu ltatem o m aw ian ej funkcji. I W rezu ltacie nie jest p o trz e b n e u ru ch o m ien ie k o n stru k to ra kopiuI jącego. To w łaśnie z a u w a ż y liśm y na w y d ru k u ek ran u . N ie tylko z re sz tą nie ruszy k o n stru k to r kopiujący. S k o ro nie o d b ęd zie się niszczenie o b iek tu lokalnego - nie ru s z y d o pracy jego d e s tru k to r. M ów iliśm y o tym już n ie d a w n o (str. 679). Przy tam tej okazji p o w ied ziałe m też, że takie p o stęp o w an ie k o m p ilato ra jest nie ty lk o d o zw o lo n e, ale n a w e t usan k cjo n o w a ne p rzez s ta n d a rd języka C++.
W 15.9.2
D laczego przez referencję? M ożna by p o sta w ić pytanie:
D la c z e g o k o n stru k to r kopiujący p rac u je n a a rg u m e n c ie p r z e s ła n y m przez refe re n c ję , a nie n a a r g u m e n c ie p rz y s ła n y m p rz e z w a r to ś ć ?
K o n stru k to r k o p iu jący klasy K m a, jak w iem y , formę K: :K (K &) ;
C zyli jako a rg u m e n t przyjm uje referencję obiektu klasy K. R eferencja, jak w ie m y, daje m u p ra w o pracow ania na o ry g in a le p rzy słan eg o arg u m e n tu . G dyby p rz y sła n o m u o b ie k t przez wartość, to te g o p raw a by nie m iał. Referencja mu je daje. M oże on w ięc n aw et z m o d y fik o w a ć (zepsuć!) o ry g in ał. Z u p e łn ie nie te g o oczekujem y od k o n stru k to ra kopiującego. K opiow ać p o w i n ien nie zm ien ia jąc o ry g in ału , to ch y b a oczyw iste.
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący) Zapytasz:
707
. . N o to - skoro je st tak ie ry zy k o - d laczeg o nie każem y tem u k o n stru k to ro w i o d b ie ra ć o b iek t przez w arto ść?"
M asz św iętą rację! - Tak by b y ło najlepiej, niestety nie d a się. W yobraź so b ie, że d o naszej funkcji f u n _ p i e r w s z a w y sy ła m y obiekt klasy k a l i b r a c j a P oniew aż p rzesłan ie o d b y w a się p rzez w arto ść, d lateg o m usi zostać z ro b io n a kopia, w ięc ru sz a d o p racy k o n stru k to r kopiujący. K o n stru k to r jak o a rg u m e n t przyjm uje: - w iad o m o - obiekt k lasy ka 1 i b r a c o a . Z tym że teraz przesyła go nie p rz e z referencję, ale - w m y si naszeg o najnow szego p o m y słu - p rz e z w artość. Skoro w iec przez w artość, to znaczy, ze w tym k o n stru k to rz e kopiującym trzeb a zro b ić kopię obiektu. W p o rz ą d k u , ab y zrobić kopię w d la k o n stru k to ra k o p iu jąceg o uru ch am iam y k o n stru k to r kopiujący - tylko, że ten znow u w y m ag a kopii, w ięc znow u k o n stru k to r kopiujący, czyli robienie kopii... 1 tak dalej - b łę d n e koło niesk o ń czo n y ch w yw ołań k o n stru k to ra kopiującego. P om ysł nie b y ł w ięc dobry. Z w ró ć też u w ag ę, że to całe nieszczęście nie m usiało być w y n ik ie m naszej w ynalazczości. Poniew aż p rzesła n ie przez referencję różni się o d p rz e sła n ia przez w arto ść ty lk o jednym m ałym zn aczk iem * (am persan d ) w d ek laracji funkcji, to jeśli p rz e z nieuw agę o tym zn ac zk u zapom nisz, N ie, nie o b aw iaj się — N asz przyjaciel kom pilator d o tego ju z me dopuści.
15.9.3
Jak dostać piątkę z C++ ? P o n iższy te k s t jest p rzezn aczo n y ty lk o d la dociekliw ych. Jeśli jesteś choć trochę z m ęczo n y o sta tn im p rzy k ład em - b ez w ahania op u sc ten parag raf.
15
P ew ien p ro fe so r jednej z polskich p o litechnik zw ierzył m i się kiedyś ze gdy o m a w ia ze stu d e n ta m i ten frag m en t „Sym fonii C++ - a ko n k retn ie ten nasz o s ta tn i p rz y k ła d - zadaje s tu d e n to m pytanie, obiecując ze za p o p r a w y odpowiedź natychmiast w pisuje do in d ek su piątkę, jeśli chciałbyś łatw o zali czyć C + + n a p iątk ę — przeczytaj p o niższych kilka zdań. O tó ż w n a s z y m ostałnim p rzy k ład zie, p o d koniec funkcji r a a in w y stę p u ,, tak.e ro zk az y : jej rezultat\n" cout << "\nTeraz wywołam runkcje druga, "podstawie do innej kalibracji \n"; cout << "Obiekt chwilowy zwrócony jako "rezultat funkcji \nma opis ( fun_druga() ) .opi s () « << endl;
//
o©
ja k w id a ć , s ą to dw ie instrukcje w ypisujące na ekranie t e k s t W drugiej jest d o d a tk o w o w y w o łan ie funkcji sk ład o w ej o p i s na rzecz o b iek tu chw ilow ego
708
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
(zw racanego p rz e z f undruga). W y k o n an ie tych instrukcji p o w o d u je w ypisa • nie na ekranie następ u jąceg o tekstu: Teraz wywołam funkcje druga, a jej rezultat podstawie do innej kalibracji W funkcji fun druga definiuje kalibracje i ma ona opis : WEWNĘTRZNA Obiekt chwilowy zwrócony jako rezultat funkcji ma opis — To ja, konstruktor kopiujący !!! —
Profesor K. p y ta stu d e n tó w dlaczego m ię d z y tekstem 1 "Teraz wywołam..."
a tekstem 2: "Obiekt chwilowy zwrócony..."
- pojaw ił się tekst: "W
funkcji fun..."
O czyw iście jest to efek t d ziałan ia funkcji o p i s , ale dlaczego te k st ten pojaw ił się m iędzy w sp o m n ian y m i tekstam i, a nie poniżej - czyli tak Teraz wywołam funkcje druga, a jej rezultat podstawie do innej kalibracji Obiekt chwilowy zwrócony jako rezultat funkcji ma opis — To ja, konstruktor kopiujący !!! — W funkcji fun druga definiuje kalibracje i ma ona opis : WEWNĘTRZNA
O to dlaczego. W trakcie w y k o n y w an ia instrukcji cout << "Obiekt chwilowy zwrócony jako " "rezultat funkcji \nma opis " « ( fun_druga() ).opis() << endl;
00
było tak: •
K o m p u ter najpierw z ac zął p rzy g o to w y w ać w szy stk o to, czy rr ta instrukcja ma się zająć. M ów iąc "w szy stk o to" - m am na m y śli w szystkie strin g i i w y rażen ia, k tó re w tej instrukcji o d d zielają trzy k ro tn ie u ż y te znaczki « .
•
Jed n y m z tych w y rażeń było w y w o łan ie funkcji o p i s . Jak w iem y - jest w niej in n a instrukcja w y p isy w a n ia na ek ran - i tc o n a posłała na ekran te k st "W f u n k c j i f u n . . . Po w y k o n an iu tej funkcji o p i s - p rzy g o to w an ia były dalej k o n ty n u o w an e. (Czyli k o m p u te r zajął się m a n ip u la to re m e n d l ).
•
G d y w szy stk o było ju ż p rzy g o to w an e i czek ało zap e w n e nn sto sie - o d b y ło się w y p isan ie tego p rzy g o to w a n e g o tekstu. W n aszy m p rzy p ad k u z ac zy n ał się on od słó w "Ob i o kt chw i Io w y .. . " .
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
709
Stąd ta k i w y g lą d ekranu. G d y b y Ci ta kolejność nie o d p o w iad ała - w y starczy ten fra g m e n t p ro g ram u zap isać tak: cout
<<
"\nTeraz
wywołam
" rezultat\n" "podstawie do cout
<<
funkcje
innej
druga,
kalibracji
a
jej"
\n";
"Obiekt chwilowy zwrócony jako " "rezultat funkcji \nma opis " << endl;
// osobno p o n iżej cout << ( f u n _ d r u g a ( ) ) .o p i s ( ) ;
C zyli w y w o ła n ie funkcji o p i s u m ieść w osobnej instrukcji.
15.9.4
Konstruktor kopiujący gwarantujący nietykalność W iem y ju ż , ż e niem ożliw e jest p o zb aw ien ie k o n stru k to ra kopiującego p ra w a m o d y fik o w an ia oryginału. Czyli te m u k o n stru k to ro w i nie m ożna p o p ro stu ufać. N ie ufa m u też kom pilator, p rzek o n ajm y się. Z ałó żm y , że ch cem y tego k o n stru k to ra użyć d o kreacji o b ie k tu na w z ó r obiektu z p rz y d o m k ie m const. Niech tym cen n y m w zo rco w y m obiektem b ęd zie w z o rzec m etra ze sk arb ca w paryskiej d zieln icy Sevres. K o m p ila to r-z a p ro te s tu je . d a s s
metr;
// O to definicja const metr metr
// //
klasa z kon stru ktorem kopiującym m e t r ::metr (metr
obiektu const wzorzec_metra;
krawiecki
=
wzorzec_metra;
4
wz) ;
.
I Ize skarbca w S eores (P aryż) / / 4— błąd —bo ten konstruktor kopiu jący g w a ra n tu je, ż e nie u szk o d zi w zorca.
// nie
S koro k o n stru k to r ma w arunki na to, by uszk o d zić cen n y w zorzec, w ięc k o m p i la to r nie d o p u ści, by m etr kraw iecki robiono w ed łu g w zo rca tym k o n s tru k to rem kopiującym . To ju ż p o w ażn a rzecz: w rezultacie obiekty const nie m o g ą być u ż y w a n e d o rob ien ia z nich kopii. Ani do w y sy łan ia ich do funkcji, a n i d o zw racan ia ich itd . Im p as? Skoro nie m ożem y zab ro n ić ko n stru k to ro w i m odyfikacji - to p o z o staje wyjście dyplom atyczne: pertraktow ać. T o znaczy s a m k o n s tru k to r pow inien obiecać, że oryginału nie z m ien i. To ju ż z n a m y k o n stru k to r pow inien o d b ierać arg u m e n t ja k o referencję (jak do tej pory), ale pow inien obiecać, że obiekt trak to w a ć będzie ja k o o b iek t stały. Jak się definiuje taki konstruktor? A co tu definiow ać? D o p isu je się w d o ty c h c z a so w y m k o n stru k to rze kopiującym je d n o słów ko const i ju ż gotow e! k a l i b r a c j a : : k a l i b r a c j a (const
kalibracja
&
wzorzec)
To nie znaczy, że sam obiekt w y sy łan y m usi być k o n ieczn ie stały - (w y sy łać będ ziem y p rzecież różne obiekty: czasem stałe, czasem nie) - jednak c o k o lw iek b y śm y nie w ysłali, to konstruktor g w aran tu je n iety k aln o ść tego, co d o stał.
5
710
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
15.9.5
W spółodpow iedzialność N iby p ro b lem rozw iązany, niestety nie zaw sze da się tak zrobić- W yobraź sobita że to ty jesteś konstru ktorem kopiującym . K iedy m o żesz g w a ra n to w a ć nietykaT ność śred n io w ieczn eg o staro d ru k u , który p o w ierzo n o Ci d o skopiow ania w d o m u? W tedy, g d y od p o w iad asz za siebie. N iestety - jeśli sk ład n ik i Twojej klas y (Twoi do m o w n icy ) nie zag w aran tu ją także n ien aru szaln o ści staro d ru k u , ko Twoje gw aran cje nie wystarczą. P rzekładając to na język kom puterow y: jeśli klasa m a s k ła d n ik i b ędące obiekt* mi innych klas, to sposób nasz d a się zastosow ać tylko w te d y , g d y konstru który w ew n ętrzn y ch obiektów m ożna w y w o łać dla o b ie k tó w ty p u const. C zyli w ted y , g d y te konstruktory tak że zag w aran tu ją n ie ty k aln o ść analogicznym i sk ładnikom w zorca. O czywiście oznacza to, że i w tych k o n stru k to rac h k o p iu jących a rg u m e n ta m i form alnym i m uszą być referencje d o o biektów stałych const.
P ew nie
p om yślałeś, że
o d p ow iednich przydom ków
m iejsc w
nie m a p r o b le m u program ie
w razie c z e g o w rócisz d o
i uzupełnisz
te
kilka
brakujących
const
W ierz mi - z w y k le to ogrom na praca, bo k ażd y jeden d o p is a n y const pociąga za sobą k o n ieczn o ść um ieszczenia kilku dalszych w in n y c h m iejscach. To jest jak reakcja law in o w a. N aw et, jeśli się u p rzesz i p o stan o w isz m ozolnie u zu p ełn ić w sz y stk o co trzeba, to m oże się okazać, ż e p o p aru g o d zin ach dojdziesz d o funkcji, p rzy której p o w in ien eś p o staw ić const, a w iesz, że o n a być const nie m oże. Jakie jest w yjście? Tylko jedno: n ig d y n iero b ie tego od ty łu . Jeśli tylko zaczy n asz pisać p o w a ż n y program , to od razu staw iaj const w takich m iejscach, jak k o n stru k to ry kopiujące.
15.9.6
Konstruktor kopiujący generow any autom atycznie W linijce @ o statn ieg o przy k ład u - w ew n ątrz k o n stru k to ra kopiującego (w trakcie k o p io w an ia) p o d m ien ialiśm y tekst. O czyw iście m o g lib y śm y zam ien iać cokolw iek in n eg o , te k st był tutaj p rzy k ła d em . Co by było, g d y b y śm y nie po d m ien iali nic, czyli g d y b y ś m y chcieli robić n a p ra w d ę w iern e kopie, a nie w ariacje na te m at o ry g in ału ? ♦> - Po p ie rw sze, nasz p rzy k ła d nie byłby tak pouczający. «$♦ - Po d ru g ie, niepotrzebna b y łab y ta cała praca. Bow iem , jeśli sam i nie zd efin iu jem y ko n stru k to ra kopiującego, to k o m p ila to r p ostara się g o w y g en ero w ać au to m aty czn ie. K o piow anie o d b y w a się w ów czas w e d łu g z a s a d y „sk ład n ik p o s k ła d n ik u " (ang. memberwise copy) - czy li o trzy m alib y śm y obiekt, k tó ry b y łby idealną k o p ią n aszeg o w zorca. W takiej sy tu acji d efiniow anie k o n stru k to ra nie b y ło b y w ięc konieczne.
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
711
W niosek: d o id en ty czn y ch kopii w y starczy k o n s tru k to r kopiujący g en ero w an y au to m aty czn ie.
Jaki to będzie konstruktor kopiujący? Jeśli k o m p ila to r m a w y g en ero w ać dla danej klasy K k o n stru k to r kopiujący - to najpierw sp ó b u je w y g en ero w ać ten bezpieczniejszy: K: :K(const K &) ;
Jeśli jed n ak o k a ż e się to niem o żliw e, bo jakiś o b iek t b ęd ąc y sk ładnikiem klasy K m a swój k o n s tru k to r kopiujący bez p rzy d o m k a c o n s t (n ieo d p o w ied zialn y gość w d o m u ) - to w ted y k o m p ilato r m oże w y g en ero w ać dla klasy K tylko tę m niej b e z p ieczn ą w ersję k o n stru k to ra k o piującego - z arg u m en tem bez p rz y d o m k a c o n s t . K::K(K &) ;
Dla wtajem n iczonych: konstruktor kopiujący wygenerowany przez kompi lator nie jest zastrzeżony przydomkiem e x p l i c i t (wyłącznie jaionego użytku).
Kto by pomyślał... Jeśli m am y n ie zn an ą klasę, o której nie w iem y, czy m a k o n stru k to r kopiujący (lub d e s tru k to r) - to i tak m o żem y dokonać jaw n eg o w y w o łan ia ich. Przecież jeśli n a w e t tw órca klasy nie zdefiniow ał k o n stru która kopiującego (czy d e stru k to ra ), to zaistnieją o n e jako w y g en ero w an e p rz e z kom pilator.
15.9.7
K iedy konstruktor kopiujący jest niezbędny? W iem y ju ż, ż e za pom ocą g en ero w an eg o au to m aty c zn ie k o n struktora k o p iu jącego, p rz y inicjalizacji now ego obiektu obiektem w zorcow ym , pow staje d o k ład n a k o p ia obiektu w zorcow ego.
klasa
obiekt_nowy = obiekt_istniejący;
Są jed n ak sy tu acje, kiedy taka d o sło w n a kopia byłaby w ręcz niepożądana. D o tego sto p n ia , ż e m ogłoby to m ieć skutki fatalne.
Oto przykład (negatywny): #include using namespace std;
class wizytówka string *wsk_nazw; string *wsk_imie;
p u b lic :
// O
712
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący) //k o n s tr u k to r wizytówka(string na = //d e s tr u k to r ~wizytowka() ;
string im
void personalia ()
{ cout << *wsk_imie << " " « *wsk nazw « endl;
//----------
.
void zmiana nazwiska(string nowe) *wsk nazw = nowe;
/W ////////////////////////////////////////////M //d efin icja k o n s tr u k to r a wizytówka::wizytówka(string im, string na) wsk_nazw = new string; *wsk_nazw = na;
// © // ©
wsk_imie = new string; *wsk_imie = im;
)
y ' * * * * * * * * ł
+ + * * * *
+ i * * * i H r * * " * * J r t r + T t * i l r * * * * + + ł * i
★
★
* ★
* * • * * * * * * + * * * ★
+ * *
j
//------- d e fin ic ja d e stru k to ra — wizytówka::~wizytowka()
i
delete delete
~
wsk_nazw; wsk_imie;
// tz
}
int main() wizytówka fizyk( "Albert", wizytówka kolega = fizyk;
"Einstein");
// © // ©
cout << "Po utworzeniu bliźniaczego obiektu oba " "zwieraja nazwiska\n";
// ©
fizyk.personalia(); kolega.personalia(); // m ó j k o leg a n a z y w a się n a p ra w d ę A lb e r t M e tz kolega.zmiana_nazwiska{"Metz") ; cout << "\nPo zmianie nazwiska kolegi brzmi kolega.personalia(); cout << "Tymczasem niemodyfikowany fizyk" " nazywa sie : "; fizyk.personalia();
// O
ono
:
// ©
// ©
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
□
713
Po wykonaniu programu komputer najprawdopodobniej zgło si błąd naruszenia pamięci. Mimo to, na ekranie zobaczymy Po utworzeniu bliźniaczego obiektu oba zawieraja nazwiska Albert Einstein Albert Einstein Po zmianie nazwiska kolegi brzmi ono : Albert Metz Tymczasem niemodyfikowany fizyk nazywa sie . A lb e r t H e.
Gdzie jest przyczyna tej błędnej pracy? Porozmawiajmy O Klasa w i z y t ó w k a słu ży d o p rzech o w y w an ia n a z w isk a i im ienia osoby. Z w ro c u w a g ę , ż e jej sk ład n ik am i nie są obiekty klasy s t r i n g , lecz w skaźniki d o takich ob iek tó w . , . F u n k cjam i sk ład o w y m i tej klasy (oprócz k o n stru k to ra i d estru k to ra) są.
0
*
funkcja p e r s o n a l i a , k tó ra d ru k u je na e k ran ie im ię i nazw isko p rz y p i
*
sa n e d a n e m u obiektow i, funkcja zmiana nazwiska, która zm ien ia n azw isk o w ed le życzenia.
D e fin ic ja k o n s tr u k to r a . W id z im y tu ta j, ż e za p o m o c ą o p e r a to r a n e w re z e rw u je m y w d o stę p n y m zap a sie pam ięci (bezim ien n y ) obiekt klasy s t r m g . W sk a ź n ik w sk _ n a z w u sta w ia m y tak, by p o k azy w ał na ten obiekt.
© D o tak pokazywanego ob iek tu (styd gwiazdka) - k o p iu jem y , p rzy słan y nam jako a rg u m e n t, strin g z n azw isk iem . Poniżej robim y p o d o b n ie z im ieniem . O D z ia ła n ie d e stru k to ra polega n a zw olnieniu rezerw acji pam ięci. D ostępnem u z a p a s o w i pam ięci o d d ajem y z po w ro tem te obszary, n a które pokazują w sk a ź niki w s k _ n a z w i w s k _ im ie .
Funkcja main, w której korzystamy z tej klasy wizytówka
i15
© D efinicja obiektu klasy wizytówka. Ta k o n k retn a w izy tó w k a n azy w a s ię I f i z y k i k o n stru o w a n a jest za pom ocą zw ykłego k o n stru k to ra. © D efinicja in n eg o obiektu klasy w i z ytówka p o łączo n a z e skopiow aniem w izy tó w k i fizy k a. ' Ten zn ak rów ności w definicji m ów i n am , że o d b y w a się to za pom ocą k o n stru k to ra kopiującego. T ak ieg o ko n stru k to ra m e d efi niow aliśm y, w iec kom pilator w y g en ero w ał go sam. D ajeonw 'rezultacie absolutnie wierną kopię obiektu fizyk.
O N a d o w ó d , że tak jest w istocie - w ypisujem y na e k ra n tresc obu w izytow e .
Teraz zrobimy coś, co pokaże nam słabość naszej klasy w ^ y t ówka © U ru c h a m ia m y funkcję zmiana_nazwiska na rzecz obiektu kolega, po czym w y p is u je m y na ekranie personalia z tego obiektu © . W idzim y w yraźnie, ze z m ia n a n astąp iła. © " P rz y p a d k ie m " w y p isu jem y też personalia fizyka.
714
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący) I tu niespodzianka - nic z tym obiektem nie ro b iliśm y , a zm ienił om ta k że nazwisko. Z am iast "Einstein” n azy w a się ta k że "Metz". Dla czego? To dlatego, ż e składnikam i klasy b y ły nie obiekty, ale w sk a ź n ik i d o obiektów , K opiując „sk ła d n ik po sk ład n ik u " m ieliśm y w ierną k o p ię w sk aźn ik ó w , k tó rr p o k azy w ały n a to sam o miejsce w pam ięci. S chem atyczn ie m ożna to pokazać tak, jak na zam ieszczo n y ch poniżej dw óch rysunkach.
Einstein Albert kolega
Metz Albert
Rys. 15-1 Tak powinno być - według naszego zamierzenia. Każdy z obiektów klasy wizytówka powinien mieć swoje własne obiekty klasy string na pomieszczenie nazwisk i imion
)))
Rys. 15-2. Tak stało się w rzeczywistości. Obiekt kolega "pasożytuje" na obiekcie fizyk. Stało się tak dlatego, że wygenerowany przez kompilator konstruktor kopiujący nie uwzględnia wyjątkowości naszej klasy wizytówka.
Jeśli z ry s u n k ó w ro zu m iesz, co się stało , to nie m u sisz c z y ta ć d o k ła d n ie w y tłu m aczenia.
Rozdział. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący)
715
Wytłumaczenie O biekt k o l e g a zo stał u tw o rz o n y m eto d ą k o p io w an ia składnik po składniku. Do s k ła d n ik ó w obiektu k o l e g a z o sta ły p rzep isa n e w ięc a d re sy ob iek tó w klasy s t r i n g , k tó re obiekt f i z y k z are zerw o w ał so b ie, n a sw ó j u ży tek . N ie była k o p io w an a ż a d n a treść strin g ó w . D ziw i Cię to? Przecież za w arto ść o b iek tu klasy w iz y tó w k a jest k o piow ana skła d n ik p o sk ład n ik u - a te obiekty klasy s t r i n g nie b y ły tu ta j składnikam i. S k ład n ik am i są tylko d w a w sk a ź n ik i d o nich. O b iek ty klasy s t r i n g były tylko p rzez w iz y tó w k ę d o d atk o w o w y d z ie rż a w io n e od z a p a su pam ięci. N a s k u te k te g o m am y d w ie w izy tó w k i, ale tylko jed en z esta w s t r i n g ó w . • •
w izy tó w k a f i z y k m a s w o je w ła s n e s t r i n g i , b o je so b ie s a m a w y tw o rzy ła o p erato ram i new w izy tó w k a k o l e g a ma d w a w sk a ź n ik i pokazujące na te s t r i n g i , k tó re f i z y k założył d la siebie!
Błąd w ięc p o le g a ł na tym , ż e d la k o l e g i nie zało żo n o żad n y ch s t r i n g ó w . K olega p a s o ż y tu je na f i z y k u . Z atem zm iana n azw isk a k o l e g i nastąpiła p rzez w p is a n ie now ego n azw isk a d o s t r i n g ó w f i z y k a ! To jeszcze n ie w szystko. Spójrz n a d estru k to r w izy tó w k i. Jeśli jedna z tych w iz y tó w e k b ę d z ie lik w id o w an a (w szystko jed n o k tó ra), to s t r i n g i na jej n azw isk o i im ię zo stan ą z lik w id o w an e o p erato rem d e l e t e . D rugi o b ie k t o ty m nic nie b ęd zie w ied ział i m oże się z d a rz y ć , że w p isze c o ś d o tego o b s z a ru , k tó ry już jest o d d a n y (m oże p rz y d zielo n y kom u innem u). C os
wtedy zniszczym y. Tragedia. D o d atk o w o , jeśli d ru g i obiekt b ęd zie lik w id o w an y , to ta k ż e uru ch o m i °P ^ra| ° r d e 1 e t e w sto su n k u do o b szaru , k tó ry już d o niego nie n ależy . P am iętasz chyba, że d w u k r o tn e u ży cie delete w sto su n k u do tego sam eg o w sk aźn ik a ma skutek fatalny. C o się zd a rz y —zależy to o d im plem entacji Moje kompilatory są w tym miejscu pobłażliwe: wypisują mi informację o dokonanej zbrodni, ale komputer nie wybucha.
Kto jest winny? My sami P am iętajm y , ż e g en ero w an y auto m aty czn ie k o n stru k to r kopiujący w ykonuje kopię „ s k ła d n ik po skład n ik u ". P ow inniśm y sobie z a d a ć pytanie, czy to nam o d p o w ia d a . Jeśli nie o d p o w iad a - to definiujem y w łasn y k o n stru k to r k o p iu jący, w k tó ry m kopiow ać b ęd ziem y tak, by u w zg lę d n ić specyfikę obiektów naszej k lasy . W naszym p rz y p a d k u - zakładając o so b n y zestaw tablic. O to realizacja takiego k o nstruktora: wizytówka::wizytówka(wizytówka Łwzor) // nazwisko-------------------------------wsk_nazw = new string; *wsk nazw = * (wzór.wsk_nazw);
// rezerwacja nowego stringu // przepisanie zawartości
4 1
5
716
Rozdz. 15. K onstruktory i D estruktory K onstruktor kopiujący (albo inicjalizator kopiujący) // mnę------------------------------wsk_imie = new string; *wsk imię = * (wzór.wsk_imie);
)
“
Jak w idać, jest tu dw u k ro tn ie w y k o n a n ie podobnej p r o c e d u ry (dla nazw iska. I dla im ienia). O N ajpierw o p erato rem new reze rw o w an y jest w z a p a sie p am iętl n ow y obiekt klasy s t r i n g , a ad res te g o o b ie k tu zapam iętujem y w e w skaźniku w sk nazw . 0 N astęp n ie do tego n o w eg o obiektu pokazywanego (stąd g w iazd k a) w skaźnikiem wsk nazw, p rzy p isu jem y treść strin g u pokazywane go (stąd d ru g a g w ia z d k a ) an alo g iczn y m w sk a ź n ik ie m z obiektu w zó r. Z atem nie k o p iu jem y tutaj ślepo treści w sk a źn ik ó w (czyli ad resó w ), lecz p re c y zyjnie b u d u je m y dla nich p o d o b n e ob iek ty klasy s t r i n g i kopiujem y d o o d p o w ied n ią nich treść. N asz ostatni p ro g ram w y p o sażo n y w taki k o n stru k to r k o p iu jący zachow a si^ już p o p raw n ie.
Spójrz zresztą na ekran, teraz Einstein ocalał od zapomnienia Po utworzeniu bliźniaczego obiektu oba zawierają nazwiska Albert Einstein Albert Einstein Po zmianie nazwiska kolegi brzmi ono : Albert Metz Tymczasem niemodyfikowany fizyk nazywa sie : Albert Einstein
W iem , co p o m y śla łe ś:
- Jurek, a po co tak cudujesz? Przecież do tej pory zawsze takie s t r i n g i z nazioami - robiliśmy po prostu składnikatn i klasy. Po co te wskaźn iki? N /<• można było zrobić tego tak samo? Wtedy byśmy uniknęli całego problemu ! Racja, w p rz y p a d k u stringów k a ż d y by tak w łaśnie zrobił. W eź je d n ak p o d u w ag ę, że na to tylko na potrzeby teg o p arag rafu w ybrałem strin g i (aby p ro b lem jest bardziej ob razo w y ) W p rak ty c e b ęd ziem y mieli naj częściej d o c z y n ie n ia z (na p rzy k ła d ) olbrzym im i tablicam i obiektów ty p u d o u - : b l e . Takich tab lic nie robi się z w y k le sk ładnikiem klasy. O b iek ty tw orzą je sobie (op erato rem new ) d o p iero w trak cie pracy p ro g ram u , a ich ad resy pam iętają w e w sk aźn ik ach . W ów czas a u to m a ty c z n ie g en ero w an y k o n stru k to r k o p iu jący m oże d o p ro w a dzić d o o p isy w an ej katastrofy. Z resztą nie ty lk o on. Już n ie d łu g o zobaczysz, że w ta k im p rz y p a d k u zaw inić m oże ró w n ie ż ta k zw a n y przeładowany operator przypisania.
717
*****?««■£
Rozdział. 15. K onstruktory i D estruktory Ćwiczenia
718
Rozdz. 15. K onstruktory i D estruktory Ćwiczenia
b) specyfikator con st nie jest konstruktorowi potrzebny, c) dany konstruktor może być użyty przy tworzeniu obiektu c o n s t wtedy, gd;y wszystkie składniki klasy inicjalizuje za pomocą listy inicjalizacyjnej, (a nie wy ciele konstruktora). W programie mamy klasę K, która ma konstruktor wywoływany z jednym argumentem typu double oraz konstruktor domniemany. Która z poniższych instrukcji nie jesl definicją obiektu klasy K? Czym zatem jest w istocie? a) K obiektA; b) K obiektB = K(); c) K obicktCO; d) K obiektD (3.14); e) K obiektE = K(3.14); W programie mamy definicje dwóch obiektów klasy K. Jedna z nich definiuje obiekt globalny, druga (w ciele funkcji main) obiekt lokalny. Kiedy wystartują do pracy konstruktory tych obiektów? W programie składającym się kilku plików jest definicja globalnego obiektu klasy K. Czy taki obiekt jest tym samym dostępny z innych plików? Jeśli „nie", to co trzeba zrobić, żeby był? Jeśli „tak", to jak ograniczyć jego dostępność do tylko tego pliku, w którym jest zdefiniowany? W programie mamy cztery obiekty: globalny, lokalny automatyczny, lokalny statyczny, tworzony operatorem new. Jaki jest czas życia tych obiektów? Jaki jest zakres ważności nazw tych obiektów? Czy destruktor może mieć argumenty domniemane? Lokalnie, w jakiejś funkcji operatorem new stworzyliśmy obiekt, na który pokazuje lokalny wskaźnik. Czy destruktor tego obiektu zostanie uruchomiony automatycznie, gdy skończy się czas życia tego wskaźnika? Konstruktor jest funkcją, wywoływaną przez kompilator w chwili, gdy kończy się czas życia danego obiektu. Czy kompilator zezwoli nam jawnie dwukrotnie wywołać destruktor dla jakiegoś obiektu? Czy wolno wywołać destruktor dla obiektu typu i n t ? Czy konstruktor domniemany może mieć argumenty domniemane?
|^ k !1 3
Klasa K ma trzy konstruktory, ale żaden z nich nie jest domniemany. Czy kompilator wygeneruje automatycznie konstruktor domniemany dla tej klasy. Dokończ poprawnie następujące zdanie. Konstruktor domniemany generowany przez kompilator ma przydomki: a) s t a t i c b) p u b l i c c) const d )v o l a t i l e e) in lin e Czy na liście inicjalizacyjnej konstruktora mogą znaleźć się wyrażenia inicjalizujące to niestatyczne składniki tej klasy, które mają przydomek con st?
Rozdział. 15. K onstruktory i D estruktory Ćwiczenia
719
Czy na liście micjalizacyjnej konstruktora mogą znaleźć sie wyrażenia inicjalizujące te składniki tej klasy, które mają przydomek static? Wybierze poprawne dokończenie następującego zdania. Jeśli konstruktor definiowany jest na zewnątrz ciała swojej klasy, to lista inicjalizacja konstruktora towarzyszy. a) deklaracji konstruktora,
b) definicji konstruktora, c) jest w obu tych miejscach. Na liście inicjalizacyjnej wyrażenia inicjalizujące poszczególne składniki klasy powinny znaleźć się w następującym porządku: a) w porządku w jakim składniki te są deklarowane w klasie, b) najpierw inicjalizacje obiektów będących obiektami innych klas („goście"), w porządku, w akim są te składniki deklarowane w klasie, a dopiero po em zwykłe składniki („domownicy") w porządku, w jakim deklarowane są w klasie, c) porządek nie ma żadnego znaczenia. W jąkim porządku odbywa sie inicjalizacja obiektów składowych danej klasy, jeśli w klasie te f składnikami są obiekty innych klasy. Kiedy wykonywane jest cało konstruktora? Obiekt klasy z poprzedniego zadania zostaje likwidowany. W jakiej kolejności ruszą do pracv destruktory? W klasie K jest składnik typu c o n s t. Czy można go inicjalizować w ciele jej konstruktora? Czy na liścieinicjalizacyjnej można wywołać jakąś funkcję? Czy można wywolaćfunkcje składową tej klasy? Czy na liście inicjalizacyjnej można do inicjalizacji jednego składnika posłużyć się wyrażeniem korzystającym z innego składnika? Czy może być klasa, w której wszystkie konstruktory są p n v a t e ? W klasie K chcemy stworzyć konstruktor kopiujący. Napisz co najmniej dwie formy jego deklaracji. (Zakładamy, że nie występują argumenty domniemane) Podaj okoliczności, kiedy użyty zostanie konstruktor kopiujący a) przesłanie do funkcji obiektu klasy K - przez referencję b) przesłanie do funkcji obiektu klasy K - przez wartość c) zwrot rezultatu funkcji będącego obiektem klasy K - przez referencję d) zwrot rezultatu funkcji będącego obiektem klasy K - przez wartość Mamy klasę K i tworzymy obiekt tej klasy - z przydomkiem const Jaki warunek musi spełniać konstruktor kopiujący klasy K, by można było mm zrobić kopię tego obiektu? Danymi składowymi klasy K są obiekty klasy M. W programie tworzymy obiekty klasy K z przydomkami const. Chcielibyśmy zbudować w klasie K konstruktor kopiujący do kopiowania takich obiektów. Jaki wpływ na to ma konstruktor klasy M? n ^ a —
720
Rozdz. 16. Tablice obiektów
16
Tablice ob iek tó w
J
ak wiemy, w języku C++ można tworzyć tablice z obiektów typów w budo wanych (np. int, double), a nawet ze wskaźników d o tych obiektów. int double char
tablica [30]; nuran[1 2 ]; * tablwsk [5 ];
//3 0 - d e m e n lo w a tablica o b ie k tó w in t / / 1 2 - d c m c n to w a tablica o b ie k tó w d o u b le //5 - d c m e n to w n tablica w s k a ź n i k ó w d o ch a r
Analogicznie da się tworzyć tablice obiektów jakiejś klasy (czyli tablice obiek tów typu zdefiniowanego przez użytkownika). Oto przykład. Najpierw wymyślmy sobie klasę. class stacje_metra ( public: _ double km; // n a k tó r y m k ilo m e trze tr a s y int głębokość; char nazwa[40]; char przesiadki[80];
);
Dla prostoty nie ma w niej funkcji składowych, a składniki-dane są publiczne. Tablicę obiektów tej klasy definiujemy stacje_metra
stacyjka[15];
Definiqę tę czytamy tak: stacyj ka jest 15 elementową tablicą obiektów klasy stacje_metra. Poszczególne obiekty tej tablicy to oczy wiście: stacyj k a [0 ] stacyj k a [1 ] ...itd ...
stacyj ka [14 ]
721
Rozdział. 16. Tablice obiektów
P o n iew aż w sz y stk ie sk ład n ik i tej k lasy są tu aku ra t p u b liczn i do nich odnosić b e z p o śre d n io (bez p o śred n ictw a funkq> sk ład o w y ch ) sto su jąc zn an ą ju ż notację obiekt.skła d n ik czyli: s t a c y j k a [4 ] . g łę b o k o ś ć s t a c y j k a [ 9] .km Z d efin iu jm y sobie w sk aźn ik , k tó ry m oże p o k azy w ać n a tak ie elem enty. s t a ć j e _ m e t r a * wsk; C zytam y : w sk - jest w sk aźn ik iem m ogącym p o k azy w ać n a obiekty ty p u (klasy)
stacj e_metra. W sk aźn ik ten m o żn a u staw ić tak, by p o k azy w ał na k tó rą ś k o n k retn ą stację. wsk = & s t a c y j k a [9] ; W y k o n a n ie operacji: w sk++; sp raw i, ż e w sk a źn ik będzie p o k azy w ał na stację n a stę p n ą . A oto, jak za pom ocą tego w sk a ź n ik a o d n o sim y się d o sk ład n ik a klasy: wsk->km Jeśli w sk a ź n ik p o k azu je w łaśn ie na obiekt stacyj ka [ 9], to w y rażen ie p o w y ż sze o z n a c z a o d n iesien ie się d o sk ład n ik a km w o b iek cie s t a c y j k a [9],
Posługiwanie się tablicami obiektów bardzo upraszcza programowanie. S a m a n o ta c ja z a ś - d la n o w ic ju s z a - m o ż e w y d a ć się tro c h ę s k o m p lik o w a n a . W y T .a rc z y je d n a k u ś w ia d o m ić s o b ie , ż e o b ie k t te ra z w n a żw ,e m a sw o , n u m e r.
Dla p o ró w n a n ia u tw ó rzm y parę obiektów klasy s t a ć j e _ m e tr a ,a le m e róbm y 6
z nich tab licy s ta c je _ m e tra a, b, c; P o ró w n aj te ra z zapis odn iesien ia się d o sk ład n ik ó w w p rzy p ad k u obiektów z tablicy i - ż e tak p ow iem - w olnostojących. O b ie k ty " w o ln o sto ją c e " a . b . km
głęb ok ość
c. nazwa a .przesiadki
O b ie k ty u m ieszczo n e w ta b lic y
stacyjka[0].głębokość stacyjka[1].km stacyjka[2].nazwa stacyjka[0].przesiadki
Kiedy nasza tablica obiektów może się przydać? O to p rz y k ła d . W w agonie m etra, w czasie jazdy, w y św ie tla n e są d a n e o n azw ie n astęp n ej stacji i ew entualnych m ożliw ych p rzesiad k ach . N a trasie danej hm r . r a P M 15 stacji. W trakcie jazdy ich o pisy m u szą s .ę kolejno pojaw ić na
722
Rozdz. 16. Tablice obiektów Tablica obiektów definiowana operatorem new w y św ie tlac zu . Dzięki tem u, że kolejne stacje ułożone są w tab licę - m ożna się p>( prostu p o słu ży ć pętlą. for(int i = 0; i < 15; i++)
(
cout «
"Stacja : " << stacyjka[i] .nazwa «
endl;
II je ś li fa sta c ja je st w ę zło w a , to m o ż liw e są ja k ie ś p r z e s ia d k i if (stacyjka [i] .przesiadki [0 ] ) II c z y w ta b lic y p r z e s ia d k i coś je s t ?
{ cout
<<
"Przesiadki
:"
<< stacyjka[i].przesiadki <<
endl;
N ie pytaj m n ie sk ąd w elem entach tablicy w zięły się d a n e . O ty m po m ó w im y za chwilę. N ajp ie rw jednak inny sp o só b definiow ania tablic.
16.1
T a b lic a o b ie k tó w d e fin io w a n a o p e ra to re m new P odobnie jak zw y k łe tablice, tak i tablice obiektów d a n e j k lasy m o żn a zd efin io w ać w d o stę p n y m zap asie p am ięci (free storę). Oto p o ró w n a n ie : D efiniujem y w zap asie pam ięci tablicę elem en tó w typu int o raz tablicę elem en tó w klasy stacj e metra. Jak pam iętam y, ta k a tablica jest z a w s z e b ezim ien n a, ale to nie szkodzi - i tak w odnoszeniu się d o niej p o słu g u jem y się w sk aźn ik am i. int *wskint; wskint = new int[ 1 0 0 ];
/ / d e fin ic ja w s k a ź n ik a / / s tw o r z e n ie ta b lic y o b ie k tó w t y p u in t
stacje metra *wsk_sta; wsk sta = new stać je_metra [15 ];
//d e f.w s k . d o o b ie k t, ty p u s t a c j c j n e t r a //4- s tw o r z e n ie ta b lic y
D alsza p raca na tak zd efiniow anej tablicy obiektów k lasy s t a c j e m e tr a j w y g ląd a tak sam o , jakbyśm y m ieli w sk aźn ik d o zw ykłej tablicy obiektów typu int. Z resztą, sam porów naj. Jak p am ięta m y , posługując się w sk aźn ik iem p o k azu jący m na tablicę m ożem y stosow ać w ta k im p rzy p ad k u d w a typy zapisów : w sk a ź n ik o w y i tablicow y. (Ten tablicow y w y g ląd a przystępniej). O dn iesien ie się elem entów tablicy o indeksie 8 m ożna w ię c w y razić jako: //w s k a ź n ik p o k a z u ją c y n a tablicę in t * (wskint + 8 ) // n o ta c ja „ w s k a ź n ik o w a " ws kint [8 ] // 4- n o ta cja „ ta b lic o w a " // w s k a ź n ik p o k a z u ją c y n a tablicę o b ie k tó w ty p u s t a c jc jn e tr a * (wsk_sta + 8 ) // n o ta c ja „ w s k a ź n ik o w a " ws k sta [8 ] // i — n o ta cja „ ta b lico w a " W i e m , j e s t e m n u d n y , a le j e s z c z e r a z p r z y p o m n ę , ż e e l e m e n t o i n d e k s i e 8 te d z i e w i ą t y e le tn e n t ta b lic y ( n u m e r u j e m y p r z e c ie ż o d 0 )
723
Rozdział. 16. Tablice obiektów Tablica obiektów definiowana operatorem new Jeśli chcielibyśm y sięg n ąć d o sk ład n ika p r z e s i a d k i w tym elem encie, to stosujem y jeden z p o n iższy ch zap isó w H ja k b y : o b ie k t.sk ła d n ik II ja k b y : w s k a żn ik -> sk ła d n ik II ja k b y : w s k a ź n ik i8]..sk ła d n ik
(*(wsk_sta + 8)) .przesiadki (wsk_sta + 8)->przesiadki w s k _ s t a [8 ] . p r z e s i a d k i
P rzy p o m in am , że w y rażen ie ( w s k _ s t a + 8 ) , jako całość, jest ad resem czegoś, co jest o o siem e lem en tó w dalej n iż to, na co p o k a z u je w sk aźn ik ws k s t a . Jako całość w ięc jest też czy m ś w ro d z a ju w skaźnika. S tą d w y rażen ie (* ( w s k _ s t a + 8 )1 jest odn iesien iem się d o obiektu tak w łaśn ie p o k azy w an eg o . Trzeci z a p is jest, jak p o w ied ziałe m , m ożliw y, m im o z e w skaźnik nie jest n azw ą tablicy W y nika to z p o d o b n eg o trak to w an ia w ję zy k u C ++ w sk aźn ik ó w l tablic. P rzy p o m in am : n azw a tablicy, to jakby stały w s k a ź n ik pokazujący na zero w y elem ent tablicy. Tej k la u z u li o stałości n ie m a n a sz w skaźnik, z a te m jeśli go p rzesu n ie m y tak, żeby p o k a z y w a ł na elem en t o in d ek sie 4, w ó w czas z a p is w sk s t a ( ] b ęd zie odniesieniem się do elementu o indeksie 12. (4+8 —12) O strzeg am je d n ak p rz e d zm ien ian iem w sk aźn ik a p o k azu jąceg o na tablicę w z a p a sie pamięci. Pam iętaj, ż e w m om encie, g d y bę dziesz chciał tablicę kasow ać o p e ra to re m d e l e t e - m usisz mieć w sk aźn ik p o k azu jący na początek tej tablicy. W d o d atk u przy żo n g lo w an iu w sk aźn ik iem m ożem y stracić ko n tak t z tablicą na zaw sze. ( V i d e histo ry jk a o baloniku z e rw a n y m z nitki). Aby się p rz e d takim i ew en tu aln o ściam i uchronić, m a m pew ien sposob. Polega on na ty m , że w takiej sytuacji m a m co najmniej d w a w sk a źn ik i pok azu jące n ę tablicę Jed n eg o z nich n ig d y n ie zm ieniam i p o k a z u je on zaw sze na p o czątek tablicy. Ż eby się u p ew n ić - d efin iu ję go po p ro stu ja k o c o n s L. Oto ta k i w sk aźn ik 4 1
stacje_meUa * const stać;
D efinicję czy tam y - s c a c jest stały m ( c o n s t ) w sk a źn ik ie m * do o b iek tó w klasy s t a c j e _ m e t r a . K aso w an ie tablicy, którą zarezerw o w aliśm y w z a p a s ie pam ięci, jest ró w n ież p o d o b n e jak w p rz y p a d k u tablic typów w b u d o w a n y c h . delete delete
[] (]
wskint; wsk_sta;
P r z y p o m i n a m , ż e j e ś l i r e z e r w a c ji d o k o n y w a l i ś m y o p e r a to r e m n e w [ ] to
d e l e t e []. Jeśli, m im o m oich ostrzeżeń, przesu n ąłeś w sk a źn ik i nie pokazuje on ju ż na początek tablicy - to sk u tek u ży cia takiego w sk a ź n ik a dla operacji d e l e _ z w o ln ie n ie r e z e r w a c ji m u s i
m oże b y ć fatalny.
się o d b y ć
o p e r a to r e m
6
724
Rozdz. 16. Tablice obiektów Inicjalizacja tablic obiektów
16.2
Inicjalizacja tablic o b i e k t ó w Z w racam u w a g ę , że m ówić tu b ęd ziem y o inicjalizacji czyli o n ad aw an iu w artości p o czątk o w ej w m om encie definicji obiektu (n aro d zin ).^ W p rz y p a d k u inicjalizacji tablic o b iek tó w danej klasy, m o g ą nastąp ić takio* sytuacje: ♦♦♦ - in icjalizow ana jest tablica, która jest agregatem,
*1* - inicjalizow ana jest tablica, k tóra nie jest agregatem, ♦♦♦ - inicjalizow ana jest tablica zd efin io w an a w z a p a s ie pam ięci. P rzy jrzy jm y się poszczególnym sytuacjom .
16.2.1
Inicjalizacja tablic obiektów będących agregatami N ajp ierw m u sim y osw oić się z ty m słow em .
P y t a n i e : c o m a w s p ó l n e g o im ię G r z e g o r z z a g r e g a t e m l o d ó w k i ?
W b rew p o z o ro m b ard zo wiele. Im ię Grzegorz p o ch o d zi o d łacińskiego im ienia Gregor, a to o d łacińskiego słow a grex,gregis, które o zn ac za g ro m ad ę , trzo d ę lu b stado. Gregor - to p o prostu czło w iek opiekujący się s ta d e m . Z au w aż , ż e sło w o to pojaw ia się często jako trzo n innych: Kongres, Kongregacja.
Z aw sze c h o d z i o jakieś zg ru p o w an ie. ♦♦♦ A g re g a t lodów ki jest z g ru p o w a n ie m silnika elek try czn e g o i sp rężark i. ♦$» A g re g a t p rąd o tw ó rczy to z g ru p o w a n ie silnika sp a lin o w e g o i p rądnicy. To sta ro ż y tn e słow o brzm i b a rd z o d u m n ie, ale w C++ o z n a c z a g o rszy rodzaj z g ru p o w a n ia . - bo lepszym zgrupowaniem jest skupienie różnych składników w defin icję obiektu klasy i wyposażenie tej klasy w dodatkowe cechy, które nadadzą tej grupie składników jakąś wyższą inteligencję. Z resztą p rz e jd ź m y d o definicji. A g r e g a t e m - (czy li p r o s t y m z g r u p o w a n i e m d a n y c h ) j e s t
tablica o b ie k tó w klasy K lub o b iek t klasy K, g d y ow a k lasa K: •
nie m a sk ład n ik ó w dan y ch p ry w atn y ch lu b zastrzeżo n y ch (czyli private lu b protected),
•
nie m a k o n stru k to ró w ,
ani: (o czy m pom ów im y w n astęp n y c h rozdziałach) 1)
Natomiast nadawanie wartości obiektowi istniejącemu jużjakiśczas — nazywam y przypi saniem (czyli inaczej: podstawieniem).
725
Rozdział. 16. Tablice obiektów Inicjalizacja tablic obiektów •
nie ma k las p o d staw o w y ch ,
•
nie m a fu n k cji w irtualnych.
P rzy p o m n ij sobie n aszą o s ta tn ią klasę class staćje_metra
{ public: double int char char
km; głębokość; n a z w a [40]; przesiadki[80];
}; Jak w id z im y , klasa la n ie m a sk ład n ik ó w d a n y c h ch ro n io n y ch sło w am i private lu b protected. N ie m a też k o n stru k to ra. M u sisz m i teraz u w ierzy ć, ż e nie m a tak że klas p o d sta w o w y c h (bo ic h ira z w y musiałyby wystąpić przy samej nazwie klasy), oraz ze me J nych (p rzy jakiejś funkcji sk ład o w ej stałoby w ó w c zas słow o v i r t u a l ) . O b iek t takiej klasy lu b tablica obiektów naszej k la sy - będ zie w ięc ag reg atem (czyli inaczej m ów iąc: p ro s ty m zg ru p o w an iem d an y ch ). A gregat m o żn a inicjalizow ać za p o m o c ą listy in k ^ liz a to ró w 2 ograniczonej z n a k a m i {}. P oszczególne elem en ty te] listy o d d zielo ne są od siebie p rzecin k am i. S potkaliśm y się ju ż z takim spo so b em p rzy o m aw ian iu zw ykłych tablic - Sposób te n n azy w a się inicjalizacją zbiorczą. P o k a ż m y n a p r z y k ła d z ie . N a jp ie r w in ic ja liz a c ja j e d n e g o o b ie k tu 14.3, - 6 ,
stacje_metra moja_stacja =
" Y o rc k s tra s s e '
Widzimy tu definicję obiektu o nazwie m
o j a _ s t a c j
SI Wansee
a będącego obiektem klasy
stacje_rnetra . S kładniki te g o obiektu są inicjalizow ane za pom ocą listy mi-
cjaliz ato ró w w artościam i: ♦♦♦ sk ła d n ik km w arto ścią
14.3
♦♦♦ sk ład n ik g ł ę b o k o ś ć w artością -6 •> sk ła d n ik n a z w a C -strin g iem "Y orckstrasse” (to nazw a ulicy) * sk ła d n ik przesiadki C -stringiem "SI W ansee" (n azw a kolejki, na k tó rą m ożna się przesiąść). O czy w iście n ad an ie tej sam ej treści ow em u obiektow i m ożna by w y k o n ać ta im zap ise m :
__ _______________ _
Nie myl nazw y l i s , a m k j s I i M o r ó w (...) z lis,, konstruktora (po dw ukropku). Co praw da, nazwy brzm ią podobnie, ,ednak me sądzę, by językowo mocniejsze odróżnienie tych nazw było godne zachodu.
726
Rozdz. 16. Tablice obiektów Inicjalizacja tablic obiektów staćje_metra moja_stacja; / / d e fin ic ja moja_stacja.km = 14.3; moja stacja.głębokość = -6; strcpy ( m o j a s t a c j a . n a z w a , Yorckstrasse ) ^ s t r c p y ( m o j a ~ s t a c j a .p r z e s i a d k i ,
"SI
o b ie k tu
Wansee");
Byłoby to ju ż jed n ak nie inicjalizacja, ale p rzy p isan ie. P rzy zn asz jednak, że ten sposób z listą inicjalizatorów je st k ró tszy i w y g o d n iej szy w zap isie. N ie zaw sze jed n ak m o żn a użyć obu form . Jeśli d efin iu jem y so b ie obiekt jako stały const stacje metra centralna
{ ...
} :
albo g d y b y choć jeden ze skład n ik ó w -d a n y c h w k lasie był ty p u c o nst 1/
...
const int głębokość; 1/
...
to n ad ać w arto ść początkow ą tak iem u obiektow i m o ż n a ty lk o w m om encie je g o n aro d zin , czyli podczas definicji ob iek tu - zatem je d y n ie listą inicjalizatorów .
W M ieliśm y je d n ak m ów ić o tablicach.
J a k in ic ja liz o w a ć ta b lic ę , której e le m e n ty są p ro s ty m i a g re g a ta m i? (a w ięc sam a tablica też jest ag reg atem ). O czyw iście ju ż n a p ew n o się d o m y śli łeś. stacje_metra
{
stacyjka[15]
=
//dane dla stacyjkilO] dane dla stacyjkill] // dane dla slacyjkilZ] // dalej nic nie ma, więc...
10 4, "ZOO", "S3, Ul, U 9" }, { 1 7 , 4, " T i e r g a r t e n " , { 3 , 3 , " B e l l e v u e " , "" 1
/ /
K lam ry o d d zielające w y rażen ia inicjalizujące p o szcze g ó ln e elem enty tablicy nie są konieczne. Tylko całość w y m ag a klam ry. Z a te m rów nie d o b rz e m oglib y śm y n ap isać tak: stacje metra
{
stacyjka[15]
=
0, 4, "ZOO", "S3, Ul, U9", 17 4 "T iergarten ", 3 ' 3, " B e lle v u e " , ""
U d a n e d la s ta c y M O ] / / d a n e d la s t a c y / k i l l l //d a n e d la s la c y jk il2 ]
H dalej nic nic ma, więc...
}; Z achęcam je d n ak d o stosow ania tych klam er, bo b a rd z o p o lep szają czy teln o ść zap isu . P rzyjrzyjm y się treści tej listy inicjalizatorów . Jak w id zim y . ♦J» D an e dla poszczególnych elem en tó w tablicy są p o d a w a n e kolejno.
727
Rozdział. 16. Tablice obiektów Inicjalizacja tablic obiektów
Najpierw element zerowy, potem element pierwszy, drugi, itd. ♦> D ane dla k aż d e g o p o jed y n czeg o elem en tu tej tablicy - są w kolejności w jakiej o d p o w ie d n ie sk ład n ik i-d an e d e k la ro w a n e są w jego klasie.
Zatem u nas u> kolejności: km, głębokość, nazwa, przesiadki U w aga: Jeśli w k lasie jest d a n a sk ład o w a staty czn a (czyli w sp ó ln a dla wszystkich o b iek tó w tej klasy), to tej d anej nie inicjalizuje się za pom ocą listy inicjalizatorów .
Jak to zapamiętać? A czy pamiętasz listę inicjalizacyjną konstruktora? Na niej też nie umieszcza się inicjalizacji składnika statycznego klasy.
W Na liście inicjalizatorów n ie m oże być więcej d an y c h niż elem en tó w tablicy. M oże być jednak mniej. Tak je s t w łaśnie w n a sz y m p rz y p a d k u : zd efin io w aliśm y tablicę 15 elem en tó w , a d a n y m i w y p ełn iam y ty lk o p ierw sze 3. • Jr _______ ..................................................................j Zapamiętaj: reszta wypełniana jest w takim przypadku zerami stosownego typu.
Co to znaczy dla składnika int, double-w iadom o. Dla składnika, który ma przechowywać C-string oznacza bajt zerowy (nu ) - 1 czyli, że C-string jest pusty i nie zawiera żadnego tekstu.
Jeśli obficie p o słu g iw aliśm y się klam ram i, to w n a g ro d ę m ożem y inicjalizow ać - ż e ta k p o w iem - "w ybiórczo . stacje_metra stacyjka { 0, 4 ), { 1.7, 4, { 3 ], i 5 }'6
[15] = . »l
"Tiergarten",
"Bronowice",
t» »
//d a n e U dane // dane //d a n e
dla dJa dla d la
sta cyjkilO ] s ta c y jk il 1 ] s ta c y jk ill] s ta c y jk il3 ]
"Wesel e " ) ' / ' / dane dla stacyjkH4] / / dalej n ic n ic m a , w ię c ...
R ezultat? Tak jak się do m y ślasz! ♦> S tosow n eg o ro d zaju zera zostaną z a p isa n e ju z w elem encie stacyj ka [0] d o jego sk ła d n ik ó w na zwa i pr zes iadki -bo w o d p o w ied n im bloku (klam rach) nie było dla nich dan y ch . *
N a stę p n y elem en t ( s t a c y j k a U l ) m a w swojej klam rze w szystkie
*
p o trze b n e dane. N astę p n y zaś (stacyj ka [ 2 ] ), m a tylko d a n ą dla składnika km, zatem głębokość, nazwa i przesiadki będ ą inicjalizow ane zeram i.
4 1
6
728
Rozdz. 16. Tablice obiektów Inicjalizacja tablic obiektów ♦> K olejny elem ent (stacyj ka [ 3 ] ), m a pustą k la m rę ze sw o im i inicjaliizato ram i, więc w szystkie skład n ik i-d an e tego o b ie k tu b ęd ą inicjalizo w an e sto so w n eg o rodzaju zeram i. ♦> s t a c y j k a [4 ] ma w "swojej" klam rze w sz y stk ie p o trz e b n e inicjalizatory. ♦♦♦ P otem lista się kończy, w ięc w szystkie n a stę p n e d a n e p ozostałych ele m e n tó w tablic (od elementu 5 do elementu 14) b ę d ą oczyw iście inicjalizo w an e sto so w n eg o rodzaju zeram i.
Uwaga dla programistów C Z n an e Ci z klasycznego C stru k tu ry struct są o czy w iście tak że ro zu m ian e w C++ jako szczególny rodzaj klasy. W języku C s tru k tu ry m ogły być inicjalizo w an e p o d o b n ą listą inicjalizatorów . Z ap y tasz p e w n ie - Jeśli m am p ro g ram w języku C, g d z ie k o rzy stam ze s tru k tu r inicjalizow anych listą inicjalizatorów , to czy taki p ro g ra m d a się b ezb łęd n ie sk o m p ilo w ać w C++ ? Tak. A lb o w iem stru k tu ra z klasycznego C jest a g reg a tem w ro zu m ien iu C++ •
•
16.2.2
nie m a przecież sk ład n ik ó w private, p r o t e c t e d - bow iem stru k tu ra jest to klasa, która przez d o m n ie m a n ie m a w szy stk ie sk ład n ik i public, nie m a ani k o n stru k to ró w , ani klas p o d sta w o w y c h , an i funkcji w irtu aln y ch - p o p ro stu dlatego, że te rz e c z y w klasycznym C nie istnieją.
Inicjalizacja tablic nie będących agregatami Jeśli o b iek t, albo tablica obiektów nie jest prostym a g re g a te m , to inicjalizow ać ich za pom ocą zw ykłej listy inicjalizatorów nie m o żn a. C hociażby d lateg o , że jeśli jakiś sk ład n ik jest p r i v a t e - to tym sam ym jest n ie d o stę p n y spoza klasy. Lista in icjalizato ró w nie leży p rzecież w zakresie w a ż n o śc i klasy. W yjściem jest w te d y p ostaw ienie na tej liśc ie - k o n stru k to ra. Jeśli m am y tablicę składającą się z takich obiektów , to na liście in icjalizato ró w u m ieszczam y w y w o łan ia k o n stru k to ró w dla poszczególnych ele m e n tó w tablicy. O to p rzy k ła d , w którym inicjalizujem y pojedynczy o b ie k t, a potem także tablicę obiektów . Jest tu definicja klasy, k tóra na p ew n o n ie jest agregatem - m a skład n ik i p ry w a tn e i k o n stru k tó ry . # in c lu d e < io s tre a m > u s in g n am esp ace s t d ;
/W //M //////M c la ss s ta c je _ k o le jk i d o u b le km; i n t g łę b o k o ś ć ;
// na którym kilometrze trasy // ile metrów pod ziemią
729
Rozdział. 16. Tablice obiektów Inicjalizacja tablic obiektów string nazwa; string przesiadki; public:
H nazwa tej stacji II na co można się przesiąść
// O / / --------- konstruktor , s t r i n g pp = "") • stacje_kolejki(double kk, i n t gg, s t r i n g nn / / ------ .7. konstruktor domniemany--------------stać j e _ k o l e jki(); / / --------- zwykła funkcja składowa---------------void gdzie_je s t e s m y ();
////////////////////////////////^ //////////^ ////^ ^ ^ ^ ^ /////////////////////////////////////////////////////^/^ s t a c j e k o l e j k i : : s t a ć j e k o le j k i ( d o u b le kk, i n t -9 9 * s t r i n g n n , s t r i n g pp)
7/ ®
: k m (kk), głębokość(gg) , nazwa(nn), przesiadki(pp)
} * „ * * * .......... **********.*************** — *******************/ sstacj t a c j ee_Koie k o l ejjkk.i : : s t a c jje _ k o l e j k i 0 : //km( 0 ) , g b o k o ś ć (0) konstruktorłędomniemany &
i
. . „. nazwa = "N ie nazw ana je s z c z e , p rz e s ia d k i = } , , . . . ...4.* ±-J, J,J,■ ),* * * * * * * * * * * * * * * * * +/ j* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * .. Q v o id s t a ć j e _ k o l e j k i : : g d z i e _ je s te s m y () cout << "Stacja : " << nazwa << endl, if(przesiadki.s i z e ())
cout << "\tPrzesiadki : " << przesiadki << endl, z****************************** *******★ ***********************/
i n t m a in () { s ta c je k o le jk i o s ta tn ia “ s t a ć j e _ k o l e j k i (22, 0, "W ansee",
118 Bus
),
// © //W 4 1
ostatnia.gdzie_jestesmy (); cout << "********************\n";
const in t ile _ s ta c ji = 7 ; . _ s t a ć j e _ k o l e j k i p r z y s t a n e k [i l e _ s t a c ; j i J 1
stacje_kclejki stacje_kolejki stacje kolejki stacje kolejki stacje_kolejki
// ©
(0 , 5, "Fredrichstrasse", " L in ia U 6 "), (), O, ,,> (5.7, 4, "Tierąarten )» (8 , 4, "ZOO", Linie
); f o r ( i n t i = 0 ; i < i l e _ s t a c j i , i++) przystanek[i].gdzie_jestesmy();
6
730
Rozdz. 16. Tablice obiektów Inicjalizacja tablic obiektów
Na ekranie po wykonaniu tego programu zobaczymy Stacja : Wansee Przesiadki : 118 Bus ******************** Stacja : Fredrichstrasse Przesiadki : Linia U 6 Stacja : Nie nazwana jeszcze Stacja : Nie nazwana jeszcze Stacja : Tiergarten Stacja : ZOO Przesiadki : Linie Ul i U9 Stacja : Nie nazwana jeszcze Stacja : Nie nazwana jeszcze
Komentarze O D eklaracja k o n stru k to ra. Z au w aż, że ostatni a rg u m e n t jest d o m n ie m a n y -je s t to p u sty strin g . © To definicja tego konstruktora. W liście inicjalizacyjnej - tej za d w u k ro p k iem w id zim y inicjalizację w szystkich składników ' km, głębokość, nazwa o ra z przesiadki. Poniew aż ża d e n z nich nie jest const, w ięc ró w n ie d o b rze m ożna by ło n ad ać im w artość p o czątk o w ą przez p rz y p is a n ie - d o p ie ro w ciele k o n stru k to ra . Jednak p o staw ien ie ich na liście inicjalizacyjnej jest bardziej zalecane!
I
W zw iąz k u z tym , że w szystko załatw iliśm y za p o m o cą listy inicjalizacyjnej ciało k o n stru k to ra jest puste. © Klasa m a też k o n stru k to r d o m n iem an y — czyli w y w o ły w a n y bez żad n y ch a rg u m entów . T u taj, dla odm iany, d a n e do sk ład n ik ó w n a z w a o raz p r z e s i a d k i w p isy w a n e są w ciele k o n stru k to ra. P oniew aż d o k o n stru k to ra d o m n iem an e g o nic p rzy sy ła się a rg u m e n tó w , zatem trzeba jakoś w ym yślić ich w arto ści początkow e. Jak w id zisz, tutaj z am iast n a z w y s ta c ji w p is u je m y te k s t "N ie n a z w a n a je s z c z e ”, a in fo rm a c ja o p rzesiad k ach jest pu sty m strin g iem . O Funkcja s k ła d o w a gdzie j e s t e sm y . W zasadzie tak a fu nkcja m ogła być ju ż w p o p rzed n iej klasie. Jej obecność lu b nieobecność nie m a w p ły w u na fakt czy klasa jest ag reg a tem , czy nie. O ile jednak, łam mogłem obejść się bez funkcji składowych, bo wszystkie składniki-dane były publiczne, o tyle tutaj - funkcje składowe sq konieczne. Pracować na składnikach prywatnych można tylko za fwntocq funkcji składoiuych (oraz ewentualnie zaprzyjaźnionych). © Definicja p o jed y n czeg o obiektu k la sy stać je_kole j k i . W idzim y, że po d r u giej stronie z n a k u stoi w y w o ła n ie ko n stru k to ra z arg u m e n ta m i. © Definicja sied m io elem en to w ej tablicy obiektów klasy s t a c j e w aż listę inicjalizatorów { . . . }
k o lo j k i . Z a u
731
Rozdział. 16. Tablice obiektów Inicjalizacja tablic obiektów
Jak w id a ć - teraz za m ia st g ru p y 4 danych (km, g ł ę b o k o ś ć , naz w a, p r z e s i a d k i ) - dla p oszczególnych stacji m am y w y w o ła n ia k o n stru k to ró w . N a liście inicjalizatorów te w y w o łan ia k o n stru k to ró w są o d d zielo ne p rzecin k am i. Są to w y w o łan ia k o n stru k to ró w d la p o szczeg o nych e lem en tó w tablicy.
Jak należyto rozumieć? O tóż - p am ięta sz chyba, jak m ów iliśm y o sytuacji, kiedy k o n stru k to r w y o y w a n y jest jaw nie. W rezu ltacie takiego w y w o łan ia m am y o b iek t ch w ilo J ' k tóry m a p o słu ży ć d o inicjalizacji dan eg o ob iek tu tablicy. In iq alizacja o y się, jak w iad o m o , k o n stru k to rem kopiującym . P og u b iłeś się? To p rzea n aliz u jm y sytuację pow oli. *
❖
N a liście w id z im y najpierw k o n stru k to r w y w o łan y d la stacji F riedrichstrasse. W jego w yn ik u tw o rzy się obiekt ch w ilo w y z a d a jący d an e o tej stacji. T akim obiektem in icjalizow any jest e lem en t tablicy p rz y s ta n e k [0 ]. N astęp n ie z listy inicjalizatorów b ran y jest kolejny k o n stru k to r i w y n ik u jego d ziałan ia - pow staje k o lejn y obiekt ch w ilow y. To mm in icjalizow any jest p r z y s t a n ę k [ 1 ].
Przypominam, że inicjalizacja jakiegoś obiektu innym obiektem odbywa się: • .
1) albo p rz e z g en ero w an y au to m a ty c z n ie k o n stru k to r k o piu jący, czyli m eto d ą „sk ład n ik p o sk ład n ik u , 2) albo za pom ocą k o n stru k to ra kopiującego, k tóry d o starczy m y m y sam i.
Tutaj -ponieioaż nie dostarczyliśmy swojego - odbędzie się to P° składniku". Nie ma tu żadnego ryzyka w stosunku do tej metody, bo w klasie nie ma składników będących wskaźnikami. Z a p y ta s z pew nie: „ -W n aszy m przykładzie tablica m a 7 na liście inicjalizatorów u m ieściliśm y w y w o łan ia tylko pięciu
ons ru
■
C o z re sz tą ? " i£ 3 P
D la p o zo stały ch elem en tó w tablicy n ie ja w n ie w y w o ły w a n y jest ich k o n st™k' to r d o m n ie m a n y . Jest w ię c b ard zo w ażne, ż e b y - jeś i p rzew i u jem y sy tu ację - w y p o sa ży ć klasę w k o n stru k to r d o m n iem an y .
Co by było, gdybyśmy nie mieli konstruktora domniemanego? K o m p ilato r sy g n alizo w ałb y błąd. P rzy b ra k u k o n stru k to ra d o m n iem an eg o - lista m usi byc k o m p letn a, czyli m u sim y zam ieścić n a liście inicjalizatorów (...) w y w o łan ia k o n stru k to ró w dla w sz y stk ich elem en tó w tablicy.
6
732
16.2.3
Rozdz. 16. Tablice obiektów Inicjalizacja tablic obiektów
Inicjalizacja tablic tworzonych w zapasie pamięci Tablice, k tó re tw orzone są w zap asie pam ięci z a p o m o cą o p erato ra new, nk> m ogą m ieć jaw nie w ypisanej inicjalizacji. Takie tablice są m o żliw e d o w y k re o w ania ty lk o w tedy, gdy: •
klasa nie m a żad n e g o k o n stru k to ra,
•
klasa w śró d sw oich k o n stru k to ró w m a k o n stru k to r d o m n ie m any.
W łaśnie k o n stru k to r d o m n iem an y zajm uje się inicjalizacja takiej tablicy. W naszej klasie stacja k o l e j k i m ieliśm y k o n s tru k to r d o m n iem an y , w ięc m ożem y zrobić w zapasie pam ięci tablicę: stacja_kolejki * wsk; wsk = new stacja_kolejki[8];
Pow staje 8 elem entow a tablica, a do inicjalizacji w sz y stk ich 8 elem en tó w zo stał uży ty k o n s tru k to r d o m n iem an y staćja_kolejki::stacja_kolejk i (void) ;
Co by było, gdybyśmy konstruktora domniemanego dla tej klasy nie zdefiniowali? Byłby b łą d kom pilacji, bo w klasie są inne k o n stru k to ry , a w śró d nich nie m a d o m n iem an eg o . G dybyśm y jed n ak z klasy u su n ę li w sz y stk ie k o n stru k to ry w ted y b łę d u nie ma. W szystkie 8 elem entów tej tablicy inicjalizo w an e b y łyby k o n stru k to re m d o m n iem an y m au to m aty czn ie w y g e n e ro w a n y m p rzez kom pilator. (P am iętasz, m ów iliśm y kiedyś, że w razie p o trzeb y k o n s tru k to r d o m n iem an y , j jak i k o n s tru k to r kopiujący - m ogą być g en ero w an e au to m aty czn e). Co w ó w c zas zostanie w p isan e d o elem entów tej tablicy? O czy w iście sto so w n e go ro d zaju zera.
Rozdział. 16. Tablice obiektów Ćwiczenia
733
Problem się zaczy n a, g d y o p erato rem tw o rzy m y tablicę (wielu) o b iek tó w . W te dy nie m a jak w y słać im arg u m en tó w . P o trzeb n y jest w ięc k o n stru k to r
domniemany.
16.3
Ć w ic z e n ia W klasieK jestskładnik publiczny będący 70 elementową tablicąW double. Ma on o nazwę kondensator. Zdefiniowano 20 elementową tablicę obiek ow J J ■ Tablica ta nazywa sięp ł y t k a (płytka drukowana). Napisz wyrażenie odnoszące siędo kondensatora o indeksie 66 będącego w płytce o indeksie 4 Klasa K ma składniki-dane public oraz private. Z elementów takiej klasy budujemy tablice. Czy jestona tzw. agregatem, (czyli prostym zgrupowaniem danych). Klasa K ma zdefiniowany tylko konstruktor domniemany. Czy jeśli zbudujemy z elementów takiej klasy tablicę, to czy będzie ona agregatem? Czy obiekty typu const będące agregatami można inicjalizować za pomocą listy inicjalizatorów? Lista inicjalizatorów ograniczona jest klamrami { ). Wewnątrz są inicjalizatory oddzie lone przecinkami. Inicjalizatory mogą być także grupowane dodatkowymi klamrami i ) . Czy te wewnętrzne klamry są obowiązkowe? Jakie mają one znaczenie. Co będzie, jeśli lista inicjalizatorów będzie dłuższa lub krótsza niż to wynika z liczby inicjalizowanych elementów? W klasie K są trzy składniki dane - wszystkie typu int iwszystkie publiczne. Drugi z tych składników jest dodatkowo statyczny (czyli wspólny dla każdego elemen u j klasy). W programie jestdefinicja tablicyelementów klasy elementów. Ponieważ tablica ta jest agregatem, więc można posłużyć się listą inicjalizatorów. Napisz przykładową inicjalizację tejtablicy wypełniając poniższą listęw miejscu wie o rop ow. K tablica[2] = { Tworzymy klasę, o której wiemy, że jej elementy będą służyły do tworzenia tablic operatorem new. Jaki powinna mieć zestaw konstruktorów?
734
Rozdz. 17. W skaźnik do składników klasy W skaźniki zwykłe - repetytorium
17 W skaźnik do sk ład n ik ów klasy ro zd ziale tym m ów ić b ęd ziem y o szczeg ó ln eg o ro d zaju w sk aźn ik ach . Takich, które służą d o p o k azy w an ia na sk ła d n ik i w e w n ą trz d an ej klasy. R o zd ział ten dotyczy rzeczy b ard zo specyficznej, d lateg o jeśli ty lk o u zn asz, ż e jest za tru d n y , lub że Cię n u d zi - p rzesk o cz go i p rz e jd ź d o n astęp n e g o (o konw ersjach). W rócisz tu kiedy indziej. P o d a n e tu inform acje nie są n ie zb ęd n e d o zro zu m ien ia n astęp n y ch ro zd ziałó w . D lateg o p rzy p ie rw szy m czytaniu k siążk i proponuję ten ro zd ział z d ec y d o w an ie opuścić.
W
P ierw o tn e w ersje języka C++ nie po zw alały na d efin io w a n ie w sk a źn ik ó w d o sk ła d n ik ó w klasy. O becnie jest to ju ż m ożliw e. Z an im je d n a k zaczniem y m ó w ić o tych w sk a źn ik ac h , p rzy p o m n ijm y sobie co w iem y o w sk a źn ik ac h zw y k ły ch .
17.1 Wskaźniki zwykłe - repetytorium Z p o p rzed n ich ro zd ziałó w w iem y, że na różne o b iek ty tej sam ej klasy (lu b te g o sam ego ty p u ) m o żn a p o k azy w ać w skaźnikiem . P rzy p o m n ijm y sobie. M am y klasę K class K
{ //
. . .
public: int skladniczek;
// };
. . .
735
Rozdział. 17. W skaźniki do składników klasy W skaźniki zwykłe - repetytorium
O to d w ie definicje w sk a ź n ik ó w . Jed en n ad aje się d o p o k azy w an ia na obiekty ty p u int, a d ru g i na o b ie k ty klasy K int K
*wskazint; *wskazobiekt;
II d o t y p u w b u d o w a n e g o int //do ty p u z d e fin io w a n e g o K
Tę d ru g ą definicję c zy tam y : w s k a z o b i e k t jest w sk aźn ik iem m o g ący m poka z y w a ć na obiekty k la sy K O to definicja kilku o b ie k tó w klasy K K
obiekt_duzy,
obiekt_zielony;
A tak u staw ia się w sk a ź n ik , by p o k azy w ał na k tó ry ś z nich: wskazobiekt = &obiekt_zielony;
Jeśli chcem y o d n ieść się d o publicznego sk ład n ik a tego o b iektu, n a który p o k azu je w sk aźn ik , to piszem y: wskazobiekt -> skladniczek;
D o sk ład n ik a n ie p u b licz n eg o nie m ożna się tak, z zew n ątrz klasy, odnieść. D o d atk o w o zw racam u w a g ę , że sam w sk aźn ik p o k azu je tu na obiekt, a nie na sk ła d n ik w ew n ątrz o b iek tu . W sk aźn ik am i często p o słu g u jem y się p rz y p o k azy w an iu na ró żn e elem enty z g ru p o w a n e w tablicę. O to definicja tablicy obiektów klasy K K
t a b [10];
D efinicję tę czytam y: tab jest 10 elem entow ą tablicą obiektów klasy K. U staw ie nie n aszeg o w sk aźn ik a na elem ent takiej tablicy m oże w y g ląd ać następująco: wskazobiekt =
&tab[ 6 ];
Czy można zwykłym wskaźnikiem pokazać na coś, co jest we wnętrzu obiektu? Tak. W skaźnikiem zw y k ły m m ożem y pokazać też na coś, co jest w e w nętrzu o biektu - pod w aru n k iem , że będzie to sk ładnik publiczny. N a p rzy k ła d - jeśli w e w n ą trz klasy jest p u b liczn y składnik ty p u i n t , to do pokazania na niego w y starcza zw ykły w sk aźn ik typu i n t . U staw iając taki w skaźnik w ystarczy pam iętać, że w yrażenie: (obiekt_zielony.skladniczek)
jako całość jest typu int i dlatego m ożna ustalić jego adres. int *wskint; wskint = & (obiekt_zielony.skladniczek);
W pisan ie liczby 55 d o tego składnika m ożna teraz w ykonać dw ojako: obiekt_zielony.skladniczek = 55; *wskint = 55;
736
Rozdz. 17. W skaźnik do składników klasy W skaźnik do pokazyw ania n a składnik-daną W sk aźn ik ty p u int* pokazuje na d an ą sk ład o w ą ty p u int, a le ró w n ie d o b rz e m oże z a chw ilę pokazać na zw y k ły obiekty ty p u int. int m; wskint = &m;
A co by było, g d y b y sk ład n ik , na k tó ry chcem y p o k a z a ć był p ry w a tn y ? W ów czas nie d ałoby się u staw ić w sk aźn ik a na nim . T o d la teg o , że p rzecież w ted y w y rażen ie: obiekt_zielony.skladniczek
jest n ie d o p u szc zaln e . K om pilator nie p ozw oli na to, by o d n o sić się do s k ła d n ik a , który jest p ry w atn y .
Skoro przypom nieliśm y sobie w iadom ości o zw y k ły ch w sk a źn ik ac h - p rzejd ź m y d o meritum.
17.2
W s k a ź n ik d o p o k a z y w a n ia na s k ła d n ik -d a n ą Z ap y tasz p e w n ie - Po co ten pa ra g ra f skoro s p ra w a jest ju ż ro zw iązan a ? W iem y już jak u s ta w ić w skaźnik na p u b liczn y m sk ład n ik u klasy . Rzeczyw iście. Tu jednak p o m ó w im y o nieco inteligentniejszych w sk a ź n ik a c h . Tego ro d z a ju w skaźnik n a z y w a się w sk a źn ik ie m d o sk ład n ik a klasy. K onkretniej: w skaźnikiem d o p o k azy w an ia na n ie sta ty c z n e (czyli zw y k łe) sk ład n ik i klasy.
Aby zrozumieć czym szczególnym jest ten wskaźnik posłużymy się taką scenką: M am y d w ó c h kolegów Piotra i T om asza. Obaj są sp ecjalistam i od w y trzy m ało ś ci m etali. Piotra b ie rz em y na lotnisko T em pelhof, p o k azu jem y m u n a coś palcem (w sk a ź nik) i m ó w im y : „W idzisz ten kaw ałek m etalu? Z a te m s p ra w d ź , czy jest o n w p o rz ą d k u " . P iotr jest fachow cem , w ięc s p ra w d z a to, co m u kazaliśm y. Z resztą zaw sze s p ra w d z a kaw ałki m etali, które m u p o d s u w a m y d o sp raw d zen ia. To, że jest to a k u ra t cięgło steru k ie ru n k u z sam o lo tu , n a w e t g o nie interesuje. N iby jest to fachow iec, ale problem w tym , że za k ażd y m ra z e m m usim y p o d ejść z nim d o te g o k aw ałka m etalu i p o k azać m u go p alcem (u staw ić w sk aźn ik na k o n k retn y sk ład n ik obiektu). M am y też d ru g ie g o kolegę: T o m asza. Ten jest in telig en tn iejszy . Jemu m ó w im y tak: „S pójrz tu na ścianę. O to w isi tu ry su n ek te ch n iczn y sam o lo tu »C oncorde« (czyli definicja klasy sam o lo tó w »Concorde«). A te ra z uw ażaj: na ry su n k u pok azu ję ci sk ład n ik tej klasy sam olotów : cięgło steru k ieru n k u . Z teg o , co pokazuję, w id zisz jak to cięgło steru kieru n k u jest u m ie sz c z o n e w sto su n k u d o
Rozdział. 17. W skaźniki do składników klasy W skaźnik do pokazywania n a składnik-daną
737
in n y c h sk ład n ik ó w sam o lo tu tej klasy. P o k azu ję ci jed n ak nie na rzeczy w isty o b ie k t, ale na ry su n e k tego cięgła." (W ten sp o só b zd efin io w aliśm y w sk aźn ik ). N a stę p n ie m ó w im y d o kolegi: „—W tym eg zem p larzu sam olotu »C oncorde«, k tó ry przyleciał d z iś ran o z P aryża (w sk aźn ik d o obiektu) m asz sp raw d zić cięgło steru k ie ru n k u . Tylko nie m ów m i, że nie w iesz g d zie takie cięgło jest: p o k a z a łe m ci je w ła śn ie na ry su n k u tech n iczn y m ." K olega id zie i w y k o n u je sw oją robotę. Jeśli in n y m razem m am y zn o w u p o d o b n e zad an ie, to m am y d w a w yjścia: «$♦ 1) W ziąć za ręk ę ko leg ę Piotra i czołgając się p o śró d żelastw a w e w n ęt rzu sam o lo tu w reszcie pokazać m u p alcem i pow iedzieć: „ S p ra w d ź ten k aw ałek m e talu ." ♦♦♦ 2) N a p o tk a n e g o w b a rz e kolegę T om asza p o d p ro w a d z ić d o o k n a i po w iedzieć: „W id zisz ten sam olot, k tó ry w łaśn ie ląduje? S p ra w d ź w nim proszę, to cięgło, k tó re ci pokazałem o statn io na p lan ie . Jaka jest różnica m ięd zy ty m i dw om a sytuacjam i? O tó ż taka, że by p o prosić o p rz y s łu g ę Piotra p o trz e b n y n am był tylko jeden palec. M ów iliśm y: N a p ra w TO. C zy li tylko jeden w sk aźn ik . W k o n ta k tach z T o m aszem po słu g iw aliśm y się tak n a p ra w d ę d w o m a w sk aźn i kam i. Raz p o k azy w aliśm y coś specjalnym w sk aźn ik iem na ry su n e k techniczny, a p o te m p o k azy w aliśm y p alcem na sam olot. (M ogliśm y palcem na sam o lo t nie p o k azy w ać. Z a m ia st tego m ożna użyć te ż n azw y tego k o n k retn eg o eg zem p la rz a sam olotu). O ile palec d o b rze się n ad aje d o po k azan ia na sam oloty, o tyle d o p o k azan ia d ro b n e g o szczegółu na ry su n k u technicznym nie stosuje się palca. P okazujem y tu nic na rzeczy w istą rzecz, a raczej na ro ś abstrakcyjnego (jej w izeru n ek ). Do p o k a z y w a n ia na ry su n k u technicznym na te nieszczęsne cięgła słu ż y więc sp ecjaln y w sk aźn ik . Te w sk aźn ik i będą w łaśn ie p rzed m io tem tego ro zd ziału . T en specjalny w sk a źn ik p o k azu je nie ty le na k o n k retn y sk ład n ik , co na jego m iejsce w deklaracji klasy. O to, jak w y g ląd a definicja w skaźn ik a m ogącego p o k azy w ać w e w n ę trz u obiek tó w d an ej klasy K na o b iek ty np. typu int. int K : :*wsk;
D efinicję tę czytam y: ws k
* K: :
-
jest w sk aźn ik iem
-
d o p o k azy w an ia w e w n ętrzu klasy K
int
-
na o b iek ty ty p u int
Czym naprawdę różni się ten wskaźnik od zwykłego wskaźnika? T ym , ż e zw y k ły w sk a źn ik pokazuje na k o n k retn e miejsce w pam ięci o jakimś a b so lu tn y m ad resie. N p. na kom órkę 7183492614. T am m a być d o k ła d n ie szu k an a zm ien n a i n t , albo funkcja, albo - ogólnie m ów iąc - obiekt.
738
Rozdz. 17. W skaźnik do składników klasy W skaźnik do pokazyw ania n a składnik-daną
N a to m ia s t w s k a ź n ik do n ie s ta ty c z n e g o , p u b lic z n e g o s k ła d n ik a k la s y m e p o k a z u je n a ż a d n e k o n k re tn e m ie js c e p a m ię c i, a le ra c z e j m ó w i n a m o ile k o m ó r e k d a le j o d p o czą tku o b ie k tu d a n e j k la s y z n a jd u je s ię z a w s z e ż ą d a n y s k ła d n ik .
M ówi na p rzy k ła d : „23 kom órki dalej". A by w ięc się d o sta ć d o tego sk ład n ik a m usim y złoży ć dw ie informacje: • to, g dzie w pam ięci zaczyna się d a n y o b iek t, •
to, o ile dalej jest ż ą d a n y sk ład n ik o b ie k tu .
Z a złożenie tych informacji jesteśm y o d p o w ied zialn i m y sam i. P o słu żen ie się? tym w sk aźn ik iem ma sens tylko w te d y , g d y jeszcze p o w ie m y o jaki o b iek t n am chodzi; (albo p o d am y nazw ę tego obiektu, albo p o k a ż e m y n a niego in n y m w skaźnikiem ). M ów iąc po prostu: ab y odnieść się do d a n e g o s k ła d n ik a w obiekcie p o trzeb n a jest składnia: obiekt.'wskaźnik
g d z ie w skaźnik jest ty m w skaźnikiem d o p o k a z y w a n ia na ry su n ek techniczny (deklarację klasy). Sam o w y ra ż e n ie 'wskaźnik nie oznacza nic sensow nego. To tak, jakbyśm y T o m aszo w i p o k azali tylko część na ry su n k u technicznym , ale nie p o w ied zieli, w k tó ry m eg ze m p la rz u sam olotu m a tę część p rzeb ad ać. O czyw iście, ab y w ten sposób na coś sen so w n eg o p o k azać, n ajp ierw m usim y ten w sk aźn ik na coś sensow nego ustaw ić. O to klasa: class concorde {
//
...
public: int cieglo_steru; int cieglo_klap // ...
}; int concorde: :*wskaz; wskaż = &concorde : : cieglo_steru;
t/def. wskaźnika //« —ustawienie go
W idzim y, że w klasie jest sk ład n ik ty p u i n t o n azw ie c i ę g i o _ s t e r u . D efiniu jem y w ięc w sk a źn ik m ogący p o k azy w ać na sk ład n ik i ty p u i n t w tej klasie. N astęp n ie u sta w ia m y składnik ws k a z tak, by p o k a z y w a ł na cięg ło .steru . W ol no nam , bo je st to sk ładnik p u b liczn y i niestatyczny. W skaźnik w s k a ż pokazuje o d tą d na cięgło steru. P rzy p o m in am , że nie na ż a d n e k o n k re tn e cięgło steru, tylko na jego u m iejsco w ien ie w klasie (na rysunku technicznym ). N ajlepszy do w ó d , ż e w pow yższej instrukcji nie w y stąp iła,
739
Rozdział. 17. W skaźniki do składników klasy W skaźnik do pokazywania n a składnik-daną
n azw a żad n eg o k o n k re tn e g o obiektu klasy c o n c o r d e , ale tylko n a z w a sam ego
typu sam olotów . . ' Powyższa definicje i ustawienie wskaźnika można zrób* w te, same, linijce, ale zapis wygląda trochę zawile, ioięc staram się go unikać int
concorde::*wskaz
=
sconcorde::cieglo_steru;
A teraz załó żm y , że c h c e m y odnieść się do sk ła d n ik a k o n k retn eg o eg zem p larza obiektu klasy c o n c o r d e . N iech ten k o n k retn y e g z e m c o n c o rd e h u g o ;
p la rz
sam olotu n a z y w a się h u g o (od V ictora H ugo)
IIdefinicja tego samolotu
O dniesien ie się w ty m k o n k retn y m eg zem p larzu d o sk ład n ik a p o k azy w an eg o p rzez n asz w sk aźn ik w y g lą d a następująco hugo.*wskaz
W s k a ź n ik - jak w ia d o m o - m ożna przestaw iać. Tu m ożna g o p rz e sta w ić tak, by p o k azy w ał w e w n ę trz u tej klasy na inny o b iek t tego sam eg o ty p u (tu: inny sk ład n ik i n t w tej klasie). Z a u w a ż p o d o b ie ń stw o d o zapisu: obiekt.składnik T eraz sk ład n ik zo stał z astą p io n y przez *ws k a z A teraz sytuacja, g d y na sam olot o nazw ie h u g o nie m ów im y p o nazw isku, tylko p o k azu jem y n a n ie g o innym , zw ykłym w skaźnikiem concorde palec
=
*palec; &hugo;
//^ -d e fin ic ja w s k a ź n ik a d o p o k a zy w a n ia n a te sa m o lo ty j j ^-sk ie ro w a n ie poleci tui k o n k re tn i /sa m o lo t
P o k azan ie na s k ła d n ik w sam olocie, który w łaśn ie pokazujem y palcem : palec->
*wskaz;
Są tu d w a w sk aźn ik i, ale oczyw iście tylko ten d ru g i jest p rzed m io te m naszego ro z d z ia łu . M yślę, że zdajesz so b ie ju ż sp raw ę z faktu, iż w sk aźn ik d o sk ład n ik a klasy jest na tyle ró ż n y o d z w y k łe g o w skaźnika, ze m e nadaje się d o ty ch celów, co zw y k ły w skaźnik: P róba u staw ien ia w skaźnika do sk ład n ik a klasy na cos, co m e jest sk ład n ik iem k la sy - spow oduje p rotest kom pilatora. *
T ak że o d w ro tn ie: jeśli w skaźnik d o p o k azy w an ia na zwykłe obiekty zechcesz u s ta w ić n a składnik w definicji klasy (rysunek techniczny) sp o w o d u je to p ro te s t kompilatora.
Czy na wszystkie składniki klasy można pokazywać takim wskaźnikiem ? Nie:
740
Rozdz. 17. W skaźnik do składników klasy W skaźnik do pokazyw ania n a składnik-daną Jeśli sk ład n ik jest niepubliczny, to nie m ożna u zy sk ać in fo rm acji o jego umie?jj scow ieniu w klasie. (Detal, który jest ściśle tajny, nie je st ry s o w a n y na rysu n k u i technicznym d la w szystkich). . , „ j eśH w klasie eon c o r d e jest tajne cięgło do otwieran ta luków bombowycii, to nie da się na nie pokazać wskaźnikiem. wskaż = &concorde::cieglo_bomb;
/ / p ro te st kom pilatora !
Po prostu kompilator strzeże prywatności klasy. N ie da się te ż ustaw ić w skaźnika w klasie na coś, co nie m a sw ojej w ła sn e j nazw y. N a p rzy k ła d jeśli składnikiem k lasy jest tablica liczb i n t , to m o żn a pokazać n-a tę tablicę (o n a m a swoją nazw ę), ale nie m ożna p o k aza ć ty m w sk a źn ik ie m na je j piąty elem en t, bo on swojej w ła s n e j n azw y nie ma.
17 2 1
Przykład zastosowania wskaźników do składników klasy W iem jak to jest: w zasadzie zro zu m iałeś ten (prosty w k o ń cu ) sp o só b p o s łu g i w ania się w sk a źn ik am i do sk ład n ik ó w klasy, ale n ie b a rd z o w iesz, kiedy te n sposób m ó g łb y Ci się przydać. Posłuchaj w ięc o p o w ieści, jak - w jednym z moich o statn ich du ży ch p ro g ram ó w - zasto so w ałem ten ro d zaj w skaźników .
K om ora d ru to w a to u rząd zen ie, k tó re słu ży do o cen ian ia pozycji (x, y) przelotu jonu w m iejscu, g d zie ją zainstalow ano. To jest coś, jak m o st z b u d o w a n y na 50 kilom etrze rzek i. Stojąc na tym m oście bystry o b se rw a to r m oże ro zp o zn ać głów ny n u r t rzeki, a także m oże stw ierd zić, że d a n a k ro p la w o d y przep ły n ęła po d m ostem ♦> o 130 cen ty m etró w bliżej lew ego brzegu - w sto su n k u d o głów nego n u rtu ; (czyli w sp ó łrzęd n a X= -1 3 0 cm), ♦$» o raz n a głębokości 14 cen ty m etró w (czyli w sp ó łrz ę d n a Y = -1 4 cm) A nalogicznie: G d y d an a kom ora d ru to w a zain stalo w an a jest na 50 m etrze lotu w iązki jo n ó w , to jeśli przeleci p rz e z nią jakiś jon - o trz y m a m y od niej sygnały, z których ła tw o obliczyć, że ów jon p rzeleciał na p rz y k ła d 2 m ilim etry w lewo i 1 m ilim etr p o w y ż e j geom etrycznej osi w iązki.
741
Rozdział. 17. W skaźniki do składników klasy W skaźnik do pokazywania n a składnik-daną Z obacz na p o n iż szy m ry su n k u . Dzięki d w ó m , sym bolicznie n ary so w an y m , kom orom d ru to w y m o n azw ach mw41 i m w 42 - ostatecznie m o ż em y w y zn a czyć tor przelotu jo nu, k tó ry u d erzy ł w tarczę.
oś g e o m e try c z n a
S&: .
* komora drutowa
Rys. 17-1. Jedna komora drutowa pozwala określić miejsce przelotu jonu. Dwie komory drutowe pozwalają na wykreślenie trajektorii jego lotu.
Jeśli nic z tego nie zro zu m iałeś - nie szkodzi. W ystarczy w iedzieć, że: ♦♦♦ 1. M am y d o czy n ien ia z urządzeniem , które dostarczać b ęd zie sygnały z czterech elektrod: lewej, praw ej, górnej i dolnej. ♦♦♦ 2. W n aszy m p ro g ram ie obiekt klasy k o m o ra _ d ru to w a przeliczać bę d zie d w a sy g n ały (lew y i praw y) na w artość w spółrzędnej x, a d w a inne sygnały (góra i d ó ł), na w artość y. (Prosta arytm etyka). ♦♦♦ 3. Takich o b iek tó w m a być w program ie kilkanaście. D ane z tych elektro n iczn y ch urządzeń (setki liczb!) przychodzą d o pro g ram u w d u ży ch blokach d an y ch . T ak i blok danych jest obiektem klasy z d a r z e n i e . Jest to ja k b y m agazy n z w ielom a regałami. N a pólkach tych regałów stoją, we w łaściw ych sobie m iejscach, liczby-dane pochodzące od najróżniejszych miejsc uk ład u pom iarow ego. Aby k a ż d y z ob iek tó w k la sy kom ora d r u to w a mógł w ykonać obliczenia m usi najpierw w iedzieć, n a której półce będą dla niego dane. W łaściw ie nie m a pro b lem u : wystarczy tutaj zw ykły w skaźnik w skazujący (danej kom orze) n a k o n k retn e miejsce w „m agazynie". Spraw a rozw iązana! K rótko m ów iąc - k o n k retn a komora drutow a (o nazw ie np. mw41) m oże się d o w ied zieć w chw ili sw oich narodzin (argum ent konstruktora), że d an e dla jej lewej elektrody b ęd ą z a w sz e dostępne „TAM!" (tu następuje adres).
742
Rozdz. 17. W skaźnik do składników klasy W skaźnik do pokazyw ania n a składnik-daną
N iestety, w p rz y p a d k u tego p ro g ram u , o którym ro z m a w ia m y - tak się nie d a ło . P ow ód b y ł prosty: W chwili, g d y m ożna było p o in fo rm o w a ć obiekty klas;y komora drutowa, gdzie w zd a rz e n iu będą dla nich d a n e - jeszcze żade: n obiek t k la sy zdarzenie (m agazyn) nie istnieje! N ie m o ż n a w ięc pokazać t>:o kom o rze p alcem i pow iedzieć: „TA M !".
Jak to rozwiązać? Z a sta n ó w m y się: co p raw d a, żad e n obiekt tej klasy z d a r z e n i e (m agazym ) jeszcze nie istnieje, ale istnieje ju ż ry su n ek techniczny te g o obiektu. (Ten r y s u nek tech n iczn y , to oczyw iście definicja klasy zdarzenie). P oszczeg ó ln y m obiektom klasy komora drutowa p o k a z u je m y w ięc na r y s u n ku technicznym , g d zie mają sięgnąć ręką, jeśli w k o ń c u taki m agazyn ( n a p rzy k ład w form ie objazdow ego ko n ten era) d o ich m iastecz k a przyjedzie.
Już się zapewne domyśliłeś, jak to zrealizowałem. Oto programik. #include #include using namespace std; I lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllilllllllllllt lllllllllllllllllllllllllllllllllW
class
zdarzenie
I I I I I I I I I I f
//
{ public: int sygnalXl; int sygnalX44; int sygnalX 8 ; int sygnalX2; int polka_zielona; int polka_niebieska; int polka_rozowa; int polka_biala; int polka_fioletowa; int polka_turkusowa; int polka beżowa;
string nazwa; int zdarzenie::*wL;
int zdarzenie::*wP; int zdarzenie::*wG; int zdarzenie::*wD; public: komora_drutowa(string nnn,
int zdarzenie::*wsk_lewy, int zdarzenie::*wsk_prawy,
f i //
743
Rozdział. 17. W skaźniki do składników klasy W skaźnik do pokazywania n a składnik-daną int zdarzenie::*wsk_gora, int zdarzenie::*wsk_dol) : nazwa(nnn), wL(wsk_lewy), wP(wsk_prawy) wG = wsk_gora; wD = wsk doi;
// ©
void analizuj()
{ cout << "Dla " << nazwa << " elektroda lewa
// @
<< biezace_zdarzenie->*wL << endl;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// y/***************************************************** * ** ***** komora_drutowa
m w 4 1 ( ”m w 4 1 " ,
/ / W
(zdarzenie::sygnalXl, (zdarzenie::polka_zielona, (zdarzenie::polka_niebieska, (zdarzenie::polka_biala); komora_drutowa
mw42("mw42",
// ©
(zdarzenie::polka_rozowa, (zdarzenie::polka_fioletowa, (zdarzenie::polka_bezowa,
(zdarzenie::polka_turkusowa); //************************************************************* void dokonaj p o m i a r u (); yy*************************************************************
main ()
{ for(int i = 0 ; i < 5
i++)
cout << "Pomiar nr " << i << endl; dokonaj_pomiaru(); mw41.analizuj 0 ; m w 4 2 .a na l i z u j (); delete bieżące zdarzenie;
f / ********■*
// © // // o%
//O ©
********************************************
void dokonaj p o m i a r u () if(biezace_zdarzenie)
//O © //O ©
{ delete biezace_zdarzenie;
} b i e z a c e z d arzenie = new zdarzenie;
//O ©
// D a le j j u ż w ie lk ie o s z u s tw o . //Im ita c ja o d c z y tu w y n ik ó w z u k ła d ó w e le k tr o n ic z n y c h / / i " u s ta w ie n ie ty c h w y n ik ó w na o d p o w ie d n ic h p ó łk a c h '
static int m; m += 1 0 0 ;
//O ©
744
Rozdz. 17. W skaźnik do składników klasy W skaźnik do pokazyw ania n a składnik-daną m + 1; = m + 2; 'zdarzenie ->sygnalX44 ni + 3; "zdarzenie ->sygnalX8 = m + 4; zdarzenie ->sygnalX2 = m + 5 zdarzenie ->polka_zielona = m + 6 zdarzenie- ->polka_niebieska = m + 7 "zdarzenie- ->polka_rozowa = m + 8 •>polka_biala zdarzenie= m + 9 zdarzenie- ->polka_fioletowa - m + 10; zdarzenie- ■>polka_turkusowa = m -i- 1 1 ; zdarzenie- ■>polka_bezowa
bieżące zdarzenie ->sygnalXl bieżące bieżące bieżące bieżące bieżące bieżące bieżące bieżące bieżące bieżące
□
//OQ
Wykonanie programu tak się objawi na ekranie Pomiar nr 0 Dla mw41 elektroda Dla mw42 elektroda Pomiar nr 1 Dla mw41 elektroda Dla mw42 elektroda Pomiar nr 2 Dla mw41 elektroda Dla mw42 elektroda Pomiar nr 3 Dla mw41 elektroda Dla mw42 elektroda Pomiar nr 4 Dla mw41 elektroda Dla mw42 elektroda
lewa = 101 lewa = 107 lewa = 201 lewa = 207 lewa = 301 lewa = 307 lewa = 401 lewa = 407 lewa = 501 lewa = 507
O O to klasa zdarzenie, czyli ry su n e k techniczny tego „ m a g a z y n u " . Są w tym m ag azy n ie ró ż n e półki. © W p ro g ram ie d efiniujem y w sk aźn ik d o obiektów takiej k lasy . W skaźnik tak zd efin io w an y je st oczyw iście glo b aln y . To dlatego, że w ie le o b iek tó w z naszeg o p ro g ram u b ę d z ie go potrzebow ało. © O to definicja k lasy komora_drutowa. Jej sk ład n ik am i są: ♦♦♦ strin g p rzechow ujący n azw ę konkretnej kom ory d ru to w e j, cztery w sk aźn ik i d o ro zm aity ch miejsc (półek) w k lasie z d a r z e n i e . © O to k o n stru k to r klasy komora_drutowa. Spójrz na listę a rg u m e n tó w . O prócz n a z w y — są tam jeszcze 4 a rg u m e n ty , które o d b io rą 4 w sk aźn ik i do jakichś (b ęd ący ch w klasie zdarzenie) składników ty p u int. Są to w sk a źn ik i pokazujące na cztery ró żn e „półki w m a g a z y n ie " , g d zie docie rać będą d a n e o d czterech elektrod jakiejś elektronicznej k o m o ry drutow ej. K tóre to p ó łk i (składniki) b ęd ą k o n k retn ie w p rz y p a d k u w ybranej kom ory dru to w ej? Jej o b iek t d o w ie się tego w chw ili sw o ich n a ro d z in — czytając a rg u m e n t ak tu aln y sw o jeg o k o nstruktora. K o n stru k to r o d b ie ra tak p rzy słan e a rg u m e n ty (adresy p ó łe k ) i zap am iętu je je.
Rozdział. 17. W skaźniki do składników klasy W skaźnik do pokazywania n a składnik-daną
745
Zapamiętanie dwóch wskaźników odbywa się na liście inicjalizacyjnej. Dwa pozostałe też powinny się na tej liście znaleźć, ale by pokazać też inny (gorszą) możliwość ich zapamiętania-umieściłem to w ciele konstruktora. © Funkcja a n alizuj w ty m p ro g ram ie oczyw iście niczego nie analizuje. Je d y n ie w y p isu je na ek ran ie w artość, k tórą (na naszy ch oczach, czyli w instrukcji © ) p o b ierze p rzy d zielo n ej jej pó łk i.
Czas teraz na definicję pierwszej komory drutowej. O Ten d łu g i zap is, to definicja obiektu o n azw ie mw41, k tó ry jest obiektem klasy komora drutowa. W id zim y tu oczyw iście pięć a rg u m e n tó w p rzezn aczo n y ch dla k o n stru k to ra. Z a pom ocą tych a rg u m e n tó w k o n stru k to ra inform ujem y d a n y o b ie k t, ż e b ę d z ie on p o b ie ra ć d a n e p o m ia ro w e z n a s tę p u ją c y c s k ła d n ik ó w zdarzenia (czyli z następujących półek w m ag azy n ie) Na przykład zoartość o sygnału z lewej elektrody komory mw41 - będzie dostępna na półce o nazwie s y g n a l X l . © O to definicja n a s tę p n e g o obiektu klasy komora_drutowa. Ma o n nazwęmw4 2. Tu oczywiście „półki" będą inne.
Tak oto nasze obiekty dowiedziały się, gdzie mają szukać swoich danych... ...w sytuacji, g d y w m iasteczku pojaw i się o b jazdow y k o n ten er-m ag azy n z d a n y m i p o m iaro w y m i. Jeśli się pojaw i, jego a d re s um ieszczony b ę d z ie w ogólnie z n a n y m (bo global nym ) w sk a źn ik u o p rzy k ład o w ej n azw ie b i ezace_zdarzenie. ow czas w szy stk ie z a in te re s o w a n e obiekty rzucą się d o o d czy ty w an ia sw o ic an y . K ażd y obiekt b ę d z ie c zy tał z p rzezn aczo n y ch dla siebie czterech półek. C o ciekaw e, jeśli ten o b jazd o w y m ag azy n zajedzie d o m iasteczk a następnym razem -zapew ne zaparkuje w zupełnie innym miejscu. Mimo to -w y s ta rc z y , ze k o m o ry d ru to w e p o zn ają JEGO ad res, a dalej sam e o d n ajd ą w łaściw e półki, a na nich p rzez n aczo n e dla siebie dane.
Zobaczmy do funkcji main. © Jest tu pętla f o r , k tó ra p o zw ala na w ielo k ro tn e od czy ty w an ie i an alizę pom ia rów . © W p ętli tej w y w o łu jem y funkcję dok o n a j_pomiaru. Funkcja ta im itu je zw róce nie się do u k ła d ó w elektronicznych z poleceniem , by u ru ch o m iły pom iar. N astęp n ie w y n ik i p o w in n y zostać u m ieszczo n e w o d p o w ied n ich składnikach obiektu klasy zdarzenie. Zajrzyjm y do ciała tej funkcji. O © O czy w iście n a s z p rz y k ła d o w y p ro g ra m nie k o m u n ik u je się z żad n y m i u k ła d a m i elek tro n iczn y m i. M arkujem y tylko takie jego działan ie. O © Jak p o w ied ziałe m , d a n e będ ą one w o b jazd o w y m m ag azy n ie pojaw iającym się raz tu , raz tam . .. Z realizujem y g o za p o m o cą tw orzenia g o operatorem new na okoliczność je d n eg o po m iaru (p rzy jazd d o m iasteczka), a n astęp n ie k aso w an ie go (wyjazd z m iasteczka)
746
Rozdz. 17. W skaźnik do składników klasy W skaźnik do pokazyw ania n a składnik-daną P rzy p ie rw s z y m w yw ołaniu tej funkcji oczyw iście ten m a g a z y n (obiekt klas y zdarzenie) jeszcze nie istnieje. W skaźnik b i e z a c e _ z d a r z e n i e jest o b ie k tem g lo b a ln y m , a w ięc od u ro d z e n ia jest w nim zero. Z a te m ta instrukcja if ( O O ) sp raw d zając a czy jest jakiś m ag azy n do w y sa d z e n ia w pow ietrze (delete) - za p ie rw szy m razem o m in ie kasow anie m a g a z y n u (skoro go nie ma;), O © Tu w id z im y tw orzenie operato rem new now ego o b iek tu klasy z d a rzenia (now y m a g a z y n ) na okoliczność n o w eg o pom iaru. A d res te g o obiektu z ap isa n y zostaje w e w sk a źn ik u biezac e _ z d a r zenie. (Przed zakończeniem programu skasujemy ostatni magazyn instrukcja
OO). O © Ta zm ie n n a m p rzy d a nam się p rz y im itow aniu w y p e łn ia n ia m ag azy n u se n so w ną treścią. C o k ażd y pom iar - w arto ść tej zm iennej p rz y ra s ta ć będ zie o 100.
OO Przykład
p rz y p isa n ia jakiejś w arto ści sk ład n ik o w i sygnalXl. Innym i słow y: tutaj u k ła d p o m iaro w y kładzie na półce w ynik p o m ia ru , k tó ry jakaś k om ora d ru to w a p o w in n a sobie odebrać. P o zakończeniu p racy tej funkcji - m ag azy n
OO
jest ju ż w y p ełn io n y . W róćm y d o funkcji main. W id zisz tutaj w y w ołanie d la obiektu ma41 jego funkcji sk ład o w ej analizuj. W funkcji tej, jak w id z ie liśm y pow yżej ( 0 ) , obiekt ten zd ejm u je z właściwej so b ie półki p rzez n aczo n ą d la siebie daną. To znaczy: komora drutowa mw41 sięgnie tym wyrażeniem (© ) na pełkę s y g n a l X l , a komora drutowa mw42 sięgnie tym wyrażeniem na półkę
polka_rózowa. To w szy stk o . W ten oto sposób obiekty odbierały b ęd ą p rz e z n a c z o n e d la nich d an e p o m iaro w e .
Na koniec drobna uwaga: P am iętasz oczyw iście, że przestrzeg ałem , by raczej nie (n a d )u ż y w a ć obiektów globalnych. T ym czasem tutaj w sk aźn ik d o bieżącego z d a rz e n ia jest globalny? K ażdy g o m o ż e zn iszczy ć Ł adnie to tak? N ie, nie ła d n ie. U spraw iedliw ię się jednak: zrobiłem tak ty lk o na u ży tek tego p rzy k ła d u , by nie zaciem niać sp raw y . W p ra w d z iw y m p ro g ram ie najlepiej zrobić g o staty czn y m składnikiem klasy komora_drutowa. Dla wtajemniczonych: lub jakiejś z jej klas podstawowych. Taki sk ład n ik staty czn y b i e z a c e _ z d a r zenie będ zie w sp ó ln y dla w szystkich obiektów k la sy komora dutowa, n ato m iast obiekty in n y c h , obcych klas, np. klasy o r k i e s t r a deta nie będą g o m ogły p rzez n ie u w a g ę zepsuć.
Podsumowanie: Dzięki p o słu żen iu się w sk aźn ik am i d o sk ład n ik ó w k lasy - m ogliśm y p o k azać obiektow i jak ieś miejsce w obiektach zu p ełn ie innego ty p u n aw et w ted y , g c y żad en o b iek t tego innego typu jeszcze nie zaistniał. M o żliw e to było d lateg o , ze w ybraliśm y sp o só b pokazyw ania „ n a definicji klasy" (na rysunku technicznym), a nie na k o n k retn y m obiekcie.
Rozdział. 17. W skaźniki do składników klasy W skaźnik do funkcji składowej
17.3
747
W s k a ź n ik d o fu n k c ji s k ła d o w e j S k ład n ik iem klasy, jak w iem y , m oże być ta k ż e funkcja. R ów nież na taką funkcję sk ład o w ą m o żn a p o k aza ć w ew n ątrz klasy w sk aźn ik iem . O czyw iście an i p rz e z chw ilę nie łu d z iłe ś się chyba, z e tym sam ym w skaźnikiem ! P rzy w y k łeś już przecież p rz y zw ykłych w sk aźn ik ach , ze w sk a źn ik , k tó ry słu ży d o p o k azy w an ia n a obiekty ty p u double, nie m oże słu ży ć d o p o k aza n ia na ob iek ty typu char. A ni tym b ardziej d o p o azam a na funkcję w y w o ły w a n ą z czterem a a rg u m e n ta m i ty p u int, a zw racającą double.
To sam o z ty m i w sk a źn ik am i d o s k ła d n ik ó w klas. Jeden p o k azu je tylko na sk ład n ik i ty p u int, a in n y typu char. Jeśli chcem y zaś p o k aza ć na fun cję sk ład o w ą, to też m u sim y przy g o to w ać o d d z ie ln y w sk aźn ik . O tym , ja o zrobić, p o m ó w im y teraz. O to przykład: class concorde
1 public: int ster; int podwozie;
/ / ---------- funkcje składowe int tankowanie(double) ; int start (double); int załadunek(double);
); M o żem y sobie z d efin io w ać w skaźnik d o p o k azy w an ia na o k reślo n e funkcje sk ład o w e int
(concorde::*wskfun)(double);
D efinicję tę czy tam y zaczy nając od nazw y: wskfun
*
- jest w sk aźn ik iem
concorde : :
- do p o k azy w an ia na sk ład n ik i klasy concorde
{d o u b l e )
- będ ące funkcjam i w y w o ły w a n y m i z arg. ty p u double
int - a zw racający m i rezu ltat ty p u int Z a u w a ż , że w y ra ż e n ie (concorde :: *wskfun) zostało ujęte w naw ias^ o o c z y w i ś c i e d lateg o , że o p e ra to r () - w y w o łan ia funkcji znajd u jący c się ez p o śre d n io za ty m w y ra ż e n ie m jest o w iele m ocniejszy od o p erato ra por.
tab ela na str. 123). G d y b y śm y w ięc zap o m n ieli ująć w naw ias n azw ę inapisali tak. int concorde::*wskfun(double) ;
to o trz y m a m y coś z u p e łn ie odm iennego. Przeczytajm y co wskfun (double)
- jest fu n k cją w y w o ły w an ą z 1 arg u m en tem ty p u double
748
Rozdz. 17. W skaźnik do składników klasy W skaźnik do funkcji składowej
*
- która jako rezu ltat zw raca w sk aźn ik
concorde: :
- pokazujący w e w n ętrzu klasy c o n c o r d e
int - na obiekty ty p u int czyli coś z u p e łn ie in n eg o niż zam ierzaliśm y. C hcieliśm y m ieć definicję w sk a ź nika, a z ro b iliśm y deklarację funkcji. P am iętajm y w ięc o n aw iasach .
Sposób dla leniwych N a stro n ie 342 p o k azałem sposób jak, nie w ysilając się z b y tn io , n ap isać definicja w sk aźn ik a m o g ą ceg o pokazyw ać na w y b ran ą funkcję. Tu chciałbym uzu p ełn ić i p o w ied zieć jak łatw o napisać definicję w sk aźn ik a m o g ą ceg o pokazać na w y b ran e fu n k cje sk ład o w e klasy. Sposób jest ta k ż e b ard zo prosty. B ierzem y deklarację jed n ej z fu nkcji - na którą ma p o k a z y w a ć w sk aźn ik - i jej n azw ę zastęp u jem y n a z w ą w sk a ź n ik a z g w iazd ką. Czyli czy m ś takim * n a z w a _ w s k a ź n ik a
Z atem jeśli ch cem y m ieć w skaźnik m o g ący pokazać na funkcję int concorde::załadunek(double);
to n azw ę z a ł a d u n e k zam ieniam y na n azw ę w sk aźn ik a z g w ia z d k ą , np. * wf s . A le u w ag a: p o zo staje teraz ujęcie w n aw ias - i tu jest ró żn ic a w stosunku d o zw ykłych funkcji. W n aw ias ujm ujem y nazw ę w sk aźn ik a, g w ia z d k ę , ale także i n azw ę klasy. C zyli w su m ie d o k o n u je m y ta k iej z a m ia n y n a z w a _ k la s y ::n a z w a _ fu n k c ji
--------------> (n a z w a _ k la s y ::* w s k a ź n ik )
W rezultacie p o w staje po szu k iw an a definicja w sk aźn ik a int
(concorde::*wfs)(double);
G dy taką d ek larację czytam y, to na w id o k operato ra z a k re su :: m ów im y: „w e w n ętrzu klasy...". Pow yższa d ek laracja (będąca też definicją) p rzeczy tan a n a głos daje nam taką w ypow iedź: wfs - jest w skaźnikiem m ogącym p o k azy w ać w e w n ętrzu klasy c o n c o r d e na funkcje w y w o ły w a n e z je d n y m a rg u m e n te m (typu double), a zw racające w arto ść ty p u int
Skoro mamy już wskaźnik, to teraz można go na coś ustawić W skaźnik z d e fin io w an y tak: int
(K::*wskfun)(double);
nadaje się d o p o k azy w an ia w tej klasie na k ażd ą funkcję sk ła d o w ą , która jest w yw o ły w an a z a rg u m e n tem double, a zw raca re z u lta t i n t . C zyli np. funkcje składo w e int tankowanie(double); int załadunek(double);
749
Rozdział. 17. W skaźniki do składników klasy W skaźnik do funkcji składowej
Jeśli w sk aźn ik d o fu n k cji u staw iam y na ja k ąś funkcję, to zw y k le p o to, by za ch w ilę u ru ch o m ić tę funkcję. O czyw iście funkcję sk ład o w ą u ru c h a m ia się na rzecz k o n k retn eg o o b ie k tu jej klasy: concorde biały; (biały.*wskfun)(900.33);
Z n o w u za u w a ż naw ias! P o n iew aż n asz w sk a ź n ik p o k azy w ał na funkcję ta n k o w a n i e , to pow yższa instrukcja o d p o w ia d a tak iem u zapisow i: biały.tankowanie(900.33);
17.3.1
Zastosowanie wskaźników do funkcji składowych N ie d a w n o ro zm a w ialiśm y o sytuacji, g d z ie ż to w p ro g ram ie m o że się przydać w sk a źn ik d o danej sk ład o w ej jakiejś klasy. T eraz z o b a c z m y sytuację, g d z ie w trakcie pisania p ro g ra m u może n am się p rz y d a ć w skaźnik d o funkcji składow ej jakiejś klasy.
I
Znowu na początku będzie trochę beletrystyki, której wcale nie masz obowiązku ani podziwiać, ani rozumieć. Jeśli na ulicy m ijała C ię k ied y ś karetka p o g o to w ia z w łączoną sy re n ą (na tzw. sy g n a le ciągłym ) to m o ż e zauw ażyłeś, że d ź w ię k syreny karetki zbliżającej się zm ie n ia się na inny, w ch w ili, g d y karetka m ija Cię i zaczyna się o d d alać. Ściślej: g d y k aretk a jedzie w n a sz y m kierunku - d ź w ię k syreny w y d aje się w yższy, a i g d y się o d d a la - w y d a je się w yraźnie niższy. Ta p o zo rn a zm ian a w y so k o ści tonu - to tak z w a n e zjaioisko Dopplera. Z jaw isk o to w y stęp u je także w m ikrośw iecie. Jeśli nadlatu jący jon wysyła i17 p ro m ien io w an ie , to p o m ia r energii tego p ro m ien io w an ia wy*.az.e wm iusi. wyc szą, niż g d y b y to sam o prom ien io w an ie p o ch o d ziło z jonu nieruchom ego. Jak b a rd z o różnią się tak m ierzone w artości energii - zależy o d p ręd k o ści jo n u (lub karetki), ♦♦♦ od kąta m ię d z y (o b serw ato rem ).
kierunkiem
lotu
jonu
(karetki), a
czujnikiem
Jeśli nic nie zro zu m iałeś —nie szkodzi. C h o d zi o to , że ktoś nam zlecił napisanie klasy, k tóra dokona p o p ra w k i zm ierzo n ej energii (lu b w ysokości tonu). T en ktoś chcia łby w ied zieć, jaka byłaby zm ierzo n a w artość energii, gd y b y jon (lub k aretk a) nie poruszały się.
750
Rozdz. 17. W skaźnik do składników klasy W skaźnik do funkcji składowej W zó r na ta k ą p o p ra w k ę jest znany o d d aw n a i b ard zo p ro s ty . Problem em jest coś innego. A b y go zastosow ać, trzeb a obliczyć d o k ła d n ie to r ruchu karetki (p rz e p ra sz a m : jonu). Trzeba też zn ać prędkość. Z lecen io d aw ca m ó w i Ci, że: ♦$* M oże być w iele sposobów w y zn aczan ia trajektorii lo tu jonu.
Jednym z nich może być zastosowanie komór drutowych ustawionych nut drodze jonu. Widziałeś to niedawno na rysunku (str. 741). M oże też być kilka sp o so b ó w szacow ania p rę d k o ś c i lecącej karetki (p ard o n : jonu!).
No i wreszcie dotarliśmy do programowania M asz z b u d o w a ć klasę, która na d o w o ln ej zm ierzonej w a rto śc i pozw oli dokonać p o p ra w k i u w zg lęd n iającej efekt D o p p lera. W k lasie tej będzie kilka funkcji składow ych o d p o w ied zialn y c h za ró ż n e sposoby szacow ania prędkości oraz k ilk a funkcji składo w y ch odp o w ied zialn y ch za różne sposoby sz aco w an ia toru lotu jo n u . Jak to m a d ziałać?
C z y m prędzej z o b a c z m y s a m program tin clu d e
// cla ss
K
//
K orektor d o p p le ro w sk i
O // 0
public:
// D w a
s p o s o b y o sza c o w a n ia p rędk o ści jo n u d o u b l e t r y b _ p r z y b l i z o n y ( i n t x)
// ©
1 c o u t << " p r z y b l i ż o n e " re tu rn 0 .4 / x; d o u b le
tryb _d ok lad n y
c o u t < < " d o k ł a d n e " << re tu rn 0 .4 56 777 / x;
<<
(in t
e n d l;
// O
x)
en d l;
//
T r z y sp o so b y o sza c o w a n ia tra jek to rii lo tu -----------------d o u b l e s p o s o b _ S a m i t a ( d o u b l e a , d o u b l e b)
{ c o u t << "sp o s o b e m S a m it a " r e tu r n 0 .3 1 4 + a + b;
<<
e n d l;
II ©
751
Rozdział. 17. W skaźniki do składników klasy W skaźnik do funkcji składowej
// ©
double sposobJuergena(double a, double b) c o u t << " s p o s o b e m retu rn 0 .4 14 + a
Juregena" + b;
<<
e n d l,
) // o
double sposob_Pietera(double a, double b) cout << "sposobem Pietera" << endl* return 0 . 5 1 4 + a + b;
} // © d o u b le l i c z poprawkę (double energia, double (K: :*wskf_predkosc)(int), double (K::*wskf_tor)(double, double)); /;//////////////////////////////////////////////////////////w /^//^/
^ ////////////////////////////////////// . // 0 K ::licz poprawkę (double energia, d o u b le ( K : : * w s k f _ p r e d k o s c ) ( i n t ) , d o u b le (K :: * w s k f _ to r ) ( d o u b le , d o u b le ) )
H fu n k c ja sk ła d o w a
d o u b le
(
cout << "Procedura obliczania poprawki...\n" « " Szacowanie prędkości : "; // p~r( d k o id a , pom ocą funkcji. M m « * * £ int zmierzona_predkosc — u* // double beta = , .
;
// ©
a r _.
( th i s - > * w s k f _ p r e d k o s c ) ( z m ie r z o n a _ p r e d k o s c ) ,
cout
<< "
Szacowanie
//OO
trajektorii:
//
Ś le d ze n ie tra je k to rii jo n u w y la tu ją c e g o w e d łu g in n eg o / / s p o so b u , w y oran ego p r z e z u ż y tk o w n ik a . . . / -> d o u b l e w spx = o fl2 ; H n p. d a n e z kornor d r u to w y c h ? d o u b le
//
wspy
=
//O ©
0 .6 4 ;
D la u p ro szc ze n ia ty lk o d w a a rg u m e n ty
double ccsinus_kata =
//O ©
( th is - > * w s k f _ to r ) ( w s p x , w s p y );
// im itacja w łaściw ych obliczeń double eneria skorygowana = .,. energia * (1- (beta * cosinus_kata)), return eneria_skorygowana;
// //
- * * * * * * * * * * * * * * * * * * * * * * * * * * *
8!
* *
*
/ / i********* * * * * * * * * * * * * * * * * * * * * ’ //O © K doppler; ^j.j.^j.j.i.j.*************************** Z /************************************* in t
m ain (in t
{ cout « cout
<<
d o u b le cin »
arge,
char*
argv[])
"Obliczanie poprawki dopplerowskiej" « "Podaj Em = Em;
0;
w artość
en ergii
zm ierzo n ej?
:
endl;
752
Rozdz. 17. W skaźnik do składników klasy W skaźnik do funkcji składowej double
Ep
=
0;
//
zmienna
na
energię
poprawiona
H je d en w a r ia n t -----------------------------Ep=
d o p p l e r .1 i c z _ p o p r a w k e ( E m , & K : :t r y b _ p r z y b l i z o n y , & K : :s p o s o b S a m i t a ) ;
cout
« <<
"A) Po poprawce, E p << endl;
prawdziwa
wartość
=
//O O
"
// d r u g i w a r ia n t -----------------------------Ep=
d o p p l e r .l i c z _ p o p r a w k e ( E m , & K : :t r y b _ d o k l a d n y , &K::sposob_Pietera);
cout
<<
” B)
<<
Ep
Po <<
poprawce,
prawdziwa
wartość
=
endl;
// tr z e c i w a r ia n t -----------------------------Ep= doppler. l i c z _ p o p r a w k e ( E m , &K::tryb_dokladny, &K::sposob cout
<<
"C)
«
Ep
Po <<
poprawce,
prawdziwa
Juergena) ;
wartość
//O ©
=
endl;
Po wykonaniu programu na ekranie zobaczymy Obliczanie Podaj
poprawki
wartość
dopplerowskiej
energii
zmierzonej?
:
1000
Procedura obliczania poprawki... Szacowanie prędkości : przybliżone Szacowanie
trajektorii:
sposobem
Samita
A) P o p o p r a w c e , prawdziwa w a r t o ś ć = 928.4 Procedura obliczania poprawki... Szacowanie prędkości : dokładne Szacowanie trajektorii: sposobem Pietera B)
Po
poprawce,
prawdziwa
wartość
=
Procedura obliczania poprawki... Szacowanie prędkości : dokładne Szacowanie trajektorii: sposobem C)
•O *
Po
poprawce,
prawdziwa
wartość
=
903.011
Juregena 910.624
Przyjrzyjmy się ciekawszym miejscom tego programu O Definicja klasy o d p o w ied zialn ej za korekcję d o p p le ro w sk ą . O czyw iście aż się prosi, żeby n azy w ała się D o p p l e r K o r e k t o r , ale je d n a k w y b rałem n a z w ę jednoliterow ą, by zap isy były krótsze. (Tylko ze w zg lęd u n a szerokość stro n y w tej książce, bo w p rak ty ce stosuję d łu g ie nazw y).
Funkcje do oszacowania prędkości © i©
Oto d w ie funkcje sk ład o w e m ogące d o k o n ać szaco w an ia prędkości. W ed łu g ich n azw m o żn a się dom yślić, że jed n a b ęd zie to robić s p o so b e m p rzy b liżo n y m , a d ru g a d o k ład n iejszy m .
753
Rozdział. 17. W skaźniki do składników klasy W skaźnik do funkcji składowej
Nie ma tu dla nas znaczettia j ak one to będą robić - nie o tym jest ten paragraf. Patrząc na te definicje widzisz, że udajemy tu tylko, ze cokolwiek robią. W ażne jes tu coś innego: obie są funkcjam i sk ład o w y m i klasy K i p rzy jm u ją jeden a rg u m e n t ty p u i n t , a w rezultacie swojej pracy zw racają w arto ść ty p u d o u b le . W o d p o w ied n iej chw ili - u ż y tk o w n ik naszej klasy p o w ie. „C hcę żeby teraz - p rzy obliczaniu p o p raw k i - d o szaco w an ia pręd k o ści u ż y ta została TA funkcja (czyli alb o © albo © ).
Funkcje do oszacowania trajektorii © i 0 i © . D o szaco w an ia to ru lotu jonu w d a n y m lab o rato riu m stosuje się trzy o d m ie n n e sposoby. (N ie w a ż n e dlaczego). Z atem defin iu jem y w klasie rzy funkcje realizu jące te sp o so b y . N azyw ają się (od im ion pom ys ° aw co w s p o s ó b S a m i t a , s p o s ó b , J u e r g e n a i s p o s ó b _ P i e t e r a . W szystkie w y w o ły w an e są z d w o m a a rg u m e n ta m i typu d o u b l e i p o d o k o n an iu obliczeń zw racają w arto ść ty p u d o u b l e . .. . ., Znowu - w tym paragrafie - nie jest dla nas ważne jak one to robią (w ich ciałach tylko udajemy, że coś liczymy).
Funkcja, która je uruchomi © W ciele tej klasy jest d ek laracja najw ażniejszej funkcja składow ej o n azw ie l i c z _ p o p r a w k e . To ją u ży tk o w n ik klasy b ęd zie w y w o ły w ał, ab y d o k o n ać korekcji d o p p lero w sk iej. P rzy p o m n ijm y sobie jej d ek larację w ciele klasy. d o u b le l i c z popraw kę ( d o u b le e n e r g ia , . .... d o u b le ( K: : * w sk f_ p re d k o sc ) ( i n t ) , d o u b le (K :: * w s k f _ to r ) ( d o u b le , d o u b l e ) ) ; P ierw szy m a rg u m e n tem jest w artość energii, k tó ra ma byc p o p raw io n a. D ru g im a rg u m e n te m jest... No? P rzeczytaj sam ... ❖ O czyw iście! B rzm i to tak: w s k f p r ę d k o ś ć je st w skaźnikiem p o k azu jący m w e w n ętrzu k la sy K n a funkcje w y w o ły w an e z jed n y m a rg u m e n te m ty p u i n t , a zw racające re z u lta t ty p u d o u b le , Funkcji sk ad o w y ch o tak im ty p ie arg u m en tó w m a m y w klasie d w ie. Są to d o u b le K : : t r y b _ p r z y b l i z o n y ( i n t x) ; d o u b le K : : tr y b _ d o k la d n y ( i n t x) ; O "
// u
Czyli u ż y w a ją c teg o w sk a źn ik a u ży tk o w n ik klasy b ę d z ie do k o n y w ał w y b o ru m ięd zy je d n ą , a d ru g ą funkcją. * T rzeci a rgu m en t f u n k c ji li c z _ p o p r a w k e n a z y w a s i ę w s k f t o r . S p r o b u j p rzeczy tać jego deklarację... A zatem :
754
Rozdz. 17. W skaźnik do składników klasy W skaźnik do funkcji składowej ws kf_tor jest w sk aźn ik iem d o p o k azy w an ia w e w n ętrzu klasy K n |
funkcje sk ład o w e w y w o ły w a n e z d w o m a arg u m e n tam i typu (double, double), a zw racające re z u lta t ty p u double.
Jak w iem y , w naszej klasie są trz y tak ie funkcje: sposób_Samita, spo* sób J u e r g e n a isposób_Pietera. Z atem ten a rg u m e n t posłuży u ż y tk o w n ik o w i d o p o in fo rm o w a n ia funkcji licz poprawkę, którego to sp o so b u określania trajek to rii lotu m a u ży ć (przy obliczaniu bieżącej popraw ki). © Tak w y g lą d a definicja tej funkcji - ju ż po za ciałem k lasy . W funkcji tej zn o w u jesl trochę o sz u stw a , bo przecież nie o zjaw isku D o p p lera je st ta książka. © To jest imitacja jakiejś loartości, która ma pozwolić na oszacowan a1 prędkości lotu jonu.
OO Oto chwila historyczna: w y w o łan ie funkcji, którą w sk a z a ł u ży tk o w n ik k la sy jak o tę, która teraz m.t (najlepiej) d o k o n a ć oszacow ania pręd k o ści. (this->*wskf_predkosc)(zmierzona_predkosc);
co odpowiada konstrukcji: wybrana_funkcja_składowa(argument); Jest to w y w o ła n ie tej funkcji sk ład o w ej, na którą p o k a z u je w sk aźn ik i w ysłanie jej jako a rg u m e n tu - pew nej w arto ści ty p u int. S p raw a załatw io n a . P rzech o d z im y dalej i teraz trzeb a b ęd zie oszacow ać to r lotu jonu. O © P rzy szaco w an iu toru lotu jonu - zw y k le m am y ja k ie ś d a n e - na p rzy k ład po ch o d zące z k o m ó r dru to w y ch . T utaj im itują je d w ie z m ie n n e typu double, I
O© Druga chwila historyczna: O to (w celu o szaco w an ia toru lotu jonu) funkcja licz_poprawke w y w o łu je tę funkcję, k tó rą w y zn aczy ł na ten cel u ży tk o w n ik klasy . (this->*wskf_tor)(wspx,
wspy);
Odpowiada to takiej konstrukcji: wybrana_funkcja_składowa(dwa argumenty); Innym i słow y: jest to w yw ołanie tej funkcji składow ej, n a k tó rą pokazuje w sk a ź nik i w y słan ie jej arg u m en tó w - jakichś dw óch w arto ści ty p u double. i Te instrukcje nie mają specjalnie znaczenia. Tutaj mając już jakoś-tam oszacowaną prędkość i jakoś-tam obliczony tor lotu jonu - może m y dokonać poprawki. Nie o tym jednak jest ten rozdział. O © N iniejszym w ięc - zakończyliśm y o m a w ian ie klasy K, p rzezn aczo n ej d o p rz e p ro w ad zan ia p o p raw ek d o p p lerow skich. N a razie jest sa m a klasa - i nic w ięcej M usim y p o w o łać do życia obiekt takiej klasy. O b iek t ten, to jakby w a rs z ta t w y k o n u jący u słu g i dla lu d n o ści. U słu g a nazyw a się "poprawianie dopplerowskie na poczekaniu".
OO o©
I
Rozdział. 17. W skaźniki do składników klasy W skaźnik do funkcji składowej
755
Tu w łaśn ie ( O 0 ) w id z im y definicję tego p u n k tu u słu g o w eg o . N azy w ał się będzie: doppler. W funkcji m a i n - u ż y tk o w n ik tej k lasy będzie k o rzy stał z obiektu doppler, ale najpierw trze b a się d o w iedzieć, jaka energia m a być p o d d a n a korekcji. Tu -je s t to prośba o wystukanie jej wartości na klawiaturze, ale rozumiesz c h y b a , ż c w r z e c z y w i s t o ś c i ta d a n a p r z y j d z i e z c z u j n i k a m ie r z ą c e g o e n e r g ie
kwantu promieniowania.
O© Dalej widzisz trzecią dziejową chwilę w tym programie. O to u ż y tk o w n ik klasy K, czyli osoba pisząca funkcję main, w y w ołuje z obiektu doppler jeg o funkcję sk ład o w ą licz poprawke. W w y w o łan iu tym a rg u m en tam i są: .
•I* w arto ść zm ierzo n a, która m a zostać p o d d a n a korekcji (Em), *1* a d re s funkcji składow ej, k tó ra p o w inna zostać u ż y ta do szacow ania p ręd k o ści jonu, widzimy, że użijtkownik wybiera tutaj funkcję składową t. r y h _ p r z y b l i z o n y ,
<♦ a d re s funkcji składow ej, która pow inna zostać u ż y ta d o szaco w an ia trajek to rii lotu jonu,
widzimy, że użytkownik wybiera tutaj funkcję składową s p o s ó b _ S a ir tita .
© N ie p o trz e b u je d o d aw ać, ż e - a b y n a z w y tych funkcji „szacujących" m o żn a było um ieścić w w y w o łan iu - funkcje te p o w in n y być w k la sie d ek laro w an e jako public. O © To jest in n e , p o w tó rn e w y w o łan ie tej samej funkcji licz_poprawke. W tym przypadku użytkownik klasy chce, by prędkość oszacowała funkcja składowa o nazw ie tryb_dokladny, a tor lotu był o szacow any sposobem_Pietera O © Jeszcze in n e w y w o łan ie tej sam ej funkcji. W tym p rz y p a d k u uży tk o w n ik klasy chce, by p rę d k o ś ć szacow ała funkcja sk ład o w a o n a z w ie tryb_dokladny, a tor lotu b y ł szaco w an y sposobem_Juergena.
Z obaczy liśm y tutaj, jak tw órca klasy m oże dać u ż y tk o w n ik o w i tej klasy sw o b o d ę w y b o ru , co d o „opcji" p rzep ro w ad ze n ia jakichś-tam obliczeń. W tym celu p o słu ży liśm y się w sk aźn ik am i d o funkcji składow ych.
Mógłbyś zapytać: "-D laczeg o nie zrobiliśm y tego tak, ż e d o funkcji licz_poprawke przesyła się p o pro stu u m o w n y n u m e r funkcji?". N a przykład:
756
Rozdz. 17. W skaźnik do składników klasy Tablica wskaźników do danych składowych klasy J e śli p r z y s ła n e b ę d z ie to 2 - w ó w c z a s z n a c z y to , ż e c h o d z i o f u n k c ję tr y b _ d o k la d n y .
W
ciele f u n k c ji l i c z
_popra w k e
b y ła b y p o p r o s t u
in s tr u k c ja s w i t c h , w k tó re j - w z a le ż n o ś c i o d m n o żo n e g o n u m e ru
-!
u ru c h a m ia ło b y s ię tę , lu b ta m tą fu n k c ję .
R zeczyw iście tak też by m ożna. <♦ Jeśli te słow a czytasz w te d y , g d y już jesteś „ w ta je m n ic z o n y ", to nieśmia< ło p rz y p o m n ę Ci, iż w ro zd ziale o funkcjach w irtu a ln y c h był frag m en t n ty m , że instrukcja s w i t c h p rzeszk ad za ro z w ią z a n iu zag ad n ien ia eleg an ck i, orien to w an y obiektow o, sposób. ♦> Jeśli nie jesteś "w tajem niczony" - to w y tłu m aczę to inaczej. O tóż p rz y n a sz y m rozw iązaniu - jeśli jeszcze jeden k o leg a z la b o ra to riu m w y m y ś II ja k iś niezły sposób szaco w an ia toru jonu - p o p ro stu d o p isze jeszcz* je d n ą funkcję składow ą d o klasy K. To w szystko. N ato m iast, gdyby g d z ie ś tam była instrukcja s w i t c h - to sam o zdefi* n io w a n ie now ej funkcji n ie w ystarczy. T rzeb a b ę d z ie jeszcze ż m u d n ie o d s z u k a ć te instrukcje s w i t c h , g d zie d o k o n u je się w y b o ró w - i p o u z u p e łn ią c na okoliczność now ych wartości. R o zw iązan ie, które po zn aliśm y - zu p ełn ie u n ie zale żn ia n a s reszty ko d u . Ż a d nych in stru k c ji s w i t c h nie trzeb a pisać, a potem e w e n tu a ln ie m od y fik o w ać. M niejsze ry z y k o popełnienia b łęd u . O soba, która m a p o m y sł na n o w y sp o só b szaco w an ia toru lotu - nic nie m u si w iedzieć, jak funkcja l i c z _ p o p r a w k e jesl z b u d o w a n a . N ie trzeba w ięc fachow ca od w szy stk ieg o , a to o zn acza, że tak n ap isan y k o d jest tańszy w obsłudze! „N iech ży ją w sk aźn ik i do funkcji składow ej!" - z aw o ła k iero w n ik projektu.
1 7 .4
T a b lic a w s k a ź n ik ó w d o d a n y c h s k ła d o w y c h k la s y Skoro w ję zy k u C++ m ożna o b iek ty g ru p o w ać w tablice - m o ż n a też u czy n ić to z takim i o b ie k tam i jak w skaźniki. W tablicy oczyw iście m o g ą być w sk aźn ik i tylko jednego ty p u . (Nic d ziw n eg o - zw y k łe tablice też z a w ie rają elem enty je d n eg o typu. T ablica i n t zaw iera sam e elem en ty typu i n t ) . O to definicja tablicy w sk a źn ik ó w do sk ład n ik ó w k lasy K int
K : :* t a b w s k [10];
definiq’ę tę czytam y: tabw sk
[1 0 ]
- jest 10 elem en to w ą tablicą
*
- w sk aźn ik ó w d o p o k azy w an ia
K: :
- w e w n ętrzu k la sy K
in t - na sk ład n ik i ty p u i n t U staw ienie je d n eg o ze w sk aźn ik ó w będących w tej tablicy tak , by p o k a z y w a ł na skład n ik s t e r to instrukcja: t a b w s k [5 ]
=
&K::ster;
757
Rozdział. 17. W skaźniki do składników klasy Tablica wskaźników do funkcji składowych klasy
a o d n iesien ie się d o sk ład n ik a w jakim ś k o n k retn y m obiekcie (o n a z w ie p o m a ra ń c z o w y ) to w yrażenie: p o m a r a ń c z o w y . * t a b w s k [5]
C o to o zn acza? W tablicy ch o w aliśm y sobie ró żn e w sk a ź n ik i d o p o k azy w an ia w e w n ętrzu k lasy K na sk ład n ik i p u b liczn e (n iestaty czn e) ty p u i n t . W p o szcze gólnych elem entach tej tablicy są w skaźniki. Jeden p o k a z u je na ten s k ła d n ik i n t , a d ru g i n a in n y sk ład n ik i n t . P ow yższe w y ra ż e n ie oznacza w ięc jakby w y p o w ied ź: bierzem y ten s k ła d n ik i n t klasy K, n a k tóry p o k azu je n a m szósty w sk a źn ik z tej tablicy...
17.5 Tablica wskaźników do funkcji składowych klasy Jest to nieco tru d n iejsze w zap isie, ale m oim zd an iem p rzy d aje się częściej. To jest jak b y p o w ie d z e n ie czegoś takiego: „M am p o n u m e ro w a n e funkcje sk ła d o we. K tóry n u m e r m am w y k o n ać?" Już z a p e w n e się dom yśliłeś, że p rz y d a się to p rzy tw o rz e n iu m enu. O to p rz y k ła d definicji tablicy w sk a źn ik ó w d o funkcji sk ład o w y ch . O czyw iście zn o w u nie w szy stk ich m o żliw y ch w tej klasie, ale o w y b ran ej liczbie i typie a rg u m e n tó w o ra z typie rezu ltatu . int
( K : : * t a b f u n s k l [8])
(double);
c zy tam y to: tabfunskl
[8 ]
- jest 8 elem en to w ą tablicą
*
- w sk aźn ik ó w pokazujących na
K: :
- będące sk ład n ik am i klasy K - funkcje w y w o ły w a n e z 1 a rg u m e n te m ty p u
(double)
in t
I double
- które w rezu ltacie zw racają w artość ty p u i n t
T rochę to w y g lą d a sko m p lik o w an ie. U staw ien ie ta k ich w sk aźn ik ó w zeb ran y ch w tablicę w y k o n u je się p ro sty m i instrukcjam i: Labwskfun(O)
=
& K ::tankowanie;
tabwskfunflj
=
& K : :z a ł a d u n e k ;
P rz y p o m in a m zasad ę: Jeśli tylko o funkcji mówimy, a nie chcemy jej w tym momencie akurat wywoływać, to używamy tylko jej nazwy. Nawiasy (z argumentami) oznaczają wywołanie jej. O to, jak w y w o łu je m y funkcję p o k azy w an ą takim w sk a źn ik ie m z tablicy. O czy w iście funkcję sk ład o w ą w y w o łu je się na rzecz k o n k retn e g o egzem plarza obiek tu .
17
758
Rozdz. 17. W skaźnik do składników klasy W skaźniki do składników statycznych K
egzemplarz;
(egzemplarz.
/ / definicja obiektu
* t a b f u n s k l [0])
(7.43);
P o n iew aż w cześniej ten elem ent tablicy w sk aźn ik ó w u sta w iliś m y (na ry su n k u tech n iczn y m ) na funkcję ta n k o w a n i e , w ięc zap is p o w y ż s z y o d p o w iad a zap iso w i e g z e m p l a r z .t a n k o w a n i e ( 7 . 4 3 ) ;
1 7 .6
W s k a ź n ik i d o s k ła d n ik ó w s ta ty c z n y c h W p o p rz e d n im parag rafie m ów iliśm y, że do p o k a z y w a n ia na sk ład n ik i w klasie p o trzeb n y jest specjalny w sk aźn ik , który m ów i nam , w k tó ry m miejscu w sto su n k u d o początku obiektu zn ajd u je się ż ą d a n y sk ła d n ik . N ie w sz y stk ie jed n ak składniki klasy w ch o d zą w sk ład p o szczeg ó ln y ch eg zem plarzy o b iek tó w . Jak pam iętam y - sk ład n ik staty czn y je st w sp ó ln y dla w sz y st kich eg z e m p la rz y obiektów , ale nie zn ajd u je się w ż a d n y m z nich. Jest zd efin io w an y o so b n o . (I to m y sam i m u sim y g o zd efin io w ać osobno). W zw iązku z tym , ab y na ten sk ład n ik s ta ty c z n y p o k azać w sk a ź n i kiem - uży w a się zw y k łeg o w skaźnika. T ak, jak b y śm y m ieli p o k a zy w ać na zw ykły obiekt, nie będący s k ła d n ik ie m żad n ej klasy. To jest ch y b a zro zu m iałe - taki sk ład n ik staty czn y m a k o n k re tn y ad res w p a mięci, n ato m iast nie ma sensu p o w ied ze n ie o nim , że le ży ileś kom órek dalej od początku o b iek tu . O n po prostu nie leży w obiekcie. Jeśli w ięc ch cem y pokazać na obiekt ty p u i n t b ędący sk ła d n ik ie m staty czn y m jakiejś klasy, to m ożem y się p o p ro stu posłużyć w sk a źn ik ie m : int
*wsk;
P o d su m u jm y d laczeg o dla sk ła d n ik ó w statycznych u ż y w a się zw ykłych w sk a źników , a d la sk ład n ik ó w n iestatycznych - w sk a ź n ik ó w specjalnych : ♦♦♦ S k ład n ik niestatyczny (zw ykły) zn ajd u je się w k a ż d y m obiekcie. A by na n iego po k azać trzeba obliczyć ile ko m ó rek w pam ięci od p o czątk u o b iek tu zn ajd u je się ż ą d a n y sk ład n ik . To w ła ś n ie robi ten sp ecjaln y w sk a źn ik d o p o k azy w an ia na składniki. ♦♦♦ S k ład n ik staty czn y nie leży w e w n ą trz ż a d n e g o eg ze m p la rz a o b iek tu . D lateg o nie m a sensu u ż y w a n ie specjalnego w sk a ź n ik a , który p rzec ież p en etru je w n ętrze obiektu. P y tan ie o to, jak d a le k o o d początku o b ie k tu sk ła d n ik ten się znajduje, nie m a sensu. S k ład n ik ten jest zu p ełn ie g d z ie indziej. Tutaj p o w ied zm y sobie, że np. tam g d z ie z m ien n e globalne. W takie m iejsce pokazuje się w sk a źn ik am i zw y k ły m i.
759
Rozdział. 17. W skaźniki do składników klasy Ćwiczenia
17.7
Ć w ic ze n ia Mamy klasę K, w której publicznym składnikiem typu i n t jest s k l. Zdefiniowano obiekt tejklasy o nazwie obiekt. Czy zwykłym wskaźnikiem i n t a siępo azac n składnik o b i e k t . s k l ? Napisz definicjęwskaźnika o nazwie p do pokazywania we wnętrzu klasyM na składniki będące wskaźnikami do obiektów typu double. Czy zawartość wskaźnika typu in t* można przypisać wskaźnikowi do pokazywania we wnętrzu klasy na składniki typu in t? A czy można odwrotnie? Mamy Klasę K, która ma publiczne składniki typu int. Nazywają się a, b, c. Dla uproszczenia załóżmy, że klasa ma konstruktor domniemany. . . 1. Operatorem new wytwórz obiekt klasy Ki jego adres przypisz wskażni owi o naz wskobj. _ 2. Napisz definicję wskaźnika o nazwie wskskl mogącego pokazywać w tej k składniki in t . _ 3.Napisz wyrażenie ustawiające wskaźnik wskskl na tak, by pokazał na skła 4.Załóżmy, że potem jeszcze ustawienie wskaźnika wskskl sięzmieniło. Napisz instru keję, która wypisze na ekran treść tego składnika obiektu (pokazywanego obecnie wskaźnikiem wskobj), na który to składnik pokazuje wskaźnik wskskl. Mamy wskaźnikint K: :*w.Jak widać, może on pokazać na składniki in t w klasie Jeśli klasa Mdeklaruje przyjaźń z klasą K, to czy wskaźnik ten może po azac n j składnik int w klasie M? Jeśli mamy wskaźnik int K: :*wsk, a w klasie składnikiem jest 10 elementowa tablica typu int. Czy można wskaźnik ten ustawić tak, by pokazywał na piąty e emen a iey. W klasie Kjest publiczny składnik statyczny typu double. a) czy można na niego pokazać wskaźnikiem double * b) czy m ożna na niego pokazać wskaźnikiem double K. . W klasie o nazwie sy n teza jestkilka publicznych funkcji składowych, które wy woły wane są z dwoma argumentami: jeden jest double, drugi jest stn n g iem . un ej zwraca wartość typu s t r in g . Napisz definicjęwskaźnika do pokazywania we wnętrzu tejklasy s y n t e z a na jedną z tych funkcji. W klasie K jest kilka składników typu int. Jest też funkcja składowa o nazwie f.j Załóżmy, że jesteśmy w ciele tejfunkcji składowej. 1. Napisz tu definicję wskaźnika (wskskl) do składników i n t w te] klasie, s aw ei wskaźnik tak, by pokazał na jakiś składnik int, (np. o nazwie sint). 2. Napisz wyrażenie odnoszące się do składnika pokazywanego właśnie zdefiniowanym wskaźnikiem. (Na przykład mech to będzie instrukcja wypisująca treść tego składnika na ekranie). Napisz definicję 16 elementowej tablicy mogącej przechowywać wskaźniki do składników klasy K, które są typu double Napisz definicję 7 elementowej tablicy mogącej przechowywać wskaźniki do takich funkcji składowych klasy K, które są wywoływane z trzema argumentami (char, string, char), a zwracają wartość typu string.
W
760
Rozdz. 18. Konwersje Sformułowanie problem u
18
Konwersje ro z d z ia le tym m ów ić b ęd ziem y o tym , jak so b ie u łatw ić p ro g ram o w a nie. Z ap o zn am y się b o w iem ze sposobam i d efin io w a n ia konw ersji (p rzek ształceń ) obiektów jed n eg o typu (klasy) na in n y . M oże n am to w p rz y szłości zao szczęd zić w iele pracy.
W
18.1
S fo rm u ło w a n ie p ro b le m u R ozw ażm y klasę, która opisuje liczby zespolone. class
zespolona
double
rzeczyw;
double
urojon;
public
:
/ / ---------- k o n s tr u k to r -------------------zespolona(double
r,
double
i)
:
rzeczyw(r),
urojon(i)
{)
/ / . .. }; Klasa jest p ro sta: zaw iera d w a sk ład n ik i-d an e. (Jeden o p isu je część rzeczyw istą, drugi część urojoną). Do tego d o c h o d z i konstruktor. In n y ch funkcji sk ład o w y ch dla p ro sto ty nie w yszczególniam y. Z ałó żm y teraz, że chcem y napisać fu nkcję, k tóra do d aje d w ie liczby zespolone.
I
Taka funkcja m o że być funkcją sk ład o w ą tej klasy, a m o że być też fu n k cją globalną, z k tó rą z klasą z e s p o l o n a deklaruje p rz y ja ź ń - (po to, by m ia ła dostęp d o jej p ry w atn y ch sk ład n ik ó w ). Wybieramy ten wariant "globalny" i wyobraź sobie, że w klasie ze sp olona, (w miejscu wielokropka) -je s t deklaracja przyjaźni. Oto definicja takiej funkcji dodającej:
761
Rozdział. 18. Konwersje Sformułowanie problem u zespolona
dodaj(zespolona
a,
zespolona
z e s p o l o n a suma(0, 0); s u m a .r z e c z y w = a . r z e c z y w
+
b . rzeczyw;
suma.urojon
b.urojon;
b)
{
return
=
a.urojon
+
suma;
} Funkcja ta p o p ro stu d o d aje d o siebie o d p o w ied n ie składniki. R ezultat jest w p isy w a n y d o lokalnego ob iek tu o n azw ie suma. Ten o b ie k t stoi p rzy s ow ie return.
P rzy p o m in am , co to znaczy. O b iek t suma jest lo k aln y i au to m aty czn y , w ięc za chw ilę p rzesta nie istnieć. P o n iew aż funkcja ma, jako re z u lta t, zw rocie obiekt klasy z e s p o l o n a , d lateg o na ten cel zostaje p rz y g o to w an y obiekt ch w ilo w y Na m o m en t p rz e d likw idacją o b ie k tu suma tresc tego ob iek tu jest k o p io w an a d o obiektu ch w ilo w eg o . Ten w łaśnie o b iek t ch w ilow y u d o stęp n ian y jest n am p rz e z m echanizm re turn.
M ając tę fu n k cję m ożem y ju ż p rzep ro w ad zić d o d aw an ie . // s k ła d n ik i d o d a w a n e zespolona p i e r w s z a (1,1) , wynik
=
d r u g a (6, - 3 ) , w y n i k (0,0) ; d o d a j ( pierwsza,
druga);
// obiekt n a w y n ik // akcja d o d a w a n ia
To było proste. Utrudnijmy to trochę. Jak w iem y, ta k że liczba 0.5 jest liczbą zespoloną. Tyle, że ta k ą szczególną, g dzie część u ro jo n a jest rów na zero . Czy m ożna taką liczbę też uzyc w naszym d o d a w a n iu ? wynik
=
dodaj(pierwsza,
0.5
);
Nie, nie m o ż n a. Po prostu nie zg ad z a się typ a rg u m e n tu . Funkcja dodaj oczekuje p rz e c ie ż obiektu k lasy z e s p o l o n a , a nie liczby d o u b l e , u sim y w ięc zd efin io w ać in n ą funkcję n a tę okoliczność. Nie będę już przypominał, że także i tęfunkcję klasa z e s p o lo n a powinna obdarzyć przyjaźnią. . Funkcję tę m o ż em y także n azw ać dodaj, bo skoro a rg u m e n ty ę ą inne, to 4 1
nazw a z o sta n ie p rz eład o w an a d o d a j(zespolona
zespolona
z,
double
f)
1 z e s p o l o n a w y n i k (0, 0 ) ; w y n i k .r z e c z y w = z . r z e c z y w wynik.urojon return
=
+
f;
z.urojon;
wynik;
} Mając ta k ą funkcję - m ożem y już w ykonać to kłopotliw e działanie. Z kolei g d y b y ś m y chcieli ta k że mieć m ożliwość zapisu d o d a j (66.1,
druga);
8
762
Rozdz. 18. Konwersje Sformułowanie problem u to p o trz e b n a jest nam funkcja zespolona
dodaj(double,
zespolona);
| M usim y więc trzy ra z y zdefiniow ać p r a w ie id e n ty czn e funkcje. P om yślisz z a p e w n e - „Czy k om p ilato r nie wie, że liczb ę d o u b l e m ożna p rzed staw ić jak o szczególnego ro d zaju liczbę zesp o lo n ą?" O czyw iście, że nie wie. T rzeba m u to najpierw pow iedzieć. W tym ro z d z ia le z a jm ie m y się w ła ś n ie tym , ja k m u ta k ie rzeczy m ów ić. G d y k o m p ila to r będzie już to w ied ział - zao szczęd zim y sobie pracy. W ystarczy w ów czas je d n a definicja zespolona
dodaj(zespolona,
zespolona);
która za ła tw i nam w szystkie trz y o m aw ian e sytuacje.
Z am ian a ty p u d o u b l e na typ na ty p z e s p o l o n a .
zesp o lo n a,
to inaczej konwersja typu
d o u b le
Konwersje obiektu typu A na typ Z mogą być zdefiniowane przez użytkownika Służą d o tego: <♦ albo k o n stru k to r klasy Z - przyjm ujący jako je d y n y arg u m e n t obiekt ty p u A <♦ albo specjalna funkcja sk ład o w a klasy A z w a n a funkcją konwertującą (inaczej - operatorem konw ersji) C ala w spaniałość konw ersji definiow anej p rz e z u ży tk o w n ik a po lega na tym , że ko n w ersje m ogą być p rz e p ro w a d z a n e niejaw nie, czyli sam oczynnie. P o p ro stu w ted y , g d y z a c h o d z i n ied o p aso w a n ie ty p u - k o m p ilato r sp ra w d z a czy za p o m o cą jakiejś konw ersji n ie d a się d an eg o ty p u zam ienić na in ny. Taki, który pasu je do d a n e j sytuacji. O czyw iście ko n w ersje te m ogą b y ć też w y w o ły w an e ja w n ie - tak, jak zw y k łe funkcje, ale to ju ż przecież nic no w eg o . -Jeśli n a p isa liśm y jakąś funkcję, to >ą przecież m o ż em y sobie jaw nie w y w ołać.
763
Rozdział. 18. Konwersje K onstruktory konw ertujące
18.2 Konstruktory konwertujące K o n s tru k to r w k la s ie
K, p rz y jm u ją c y je d e n a rg u m e n t (np. ty p u T),
K: : K(T) ; (i nie mający przydomka e x p l i c i t) o k re ś la k o n w e rs ję : o d ty p u te g o a rg u m e n tu , do ty p u s w o je j k la s y . T *4
K
f — w i i i iw w iu w ik miymnmnwriiTrni u m r
i
m
-
To b ard zo pro ste: jeśli w naszej klasie d la liczb zespolonych zd efin iu jem y sobie taki k o n stru k to r: z e s p o lo n a : : z e s p o lo n a ( d o u b le r) 1 rzec zy w = r ; u r ó jo n = 0; } to tym sam y m o k re śliliśm y , ja k zro b ić k o n w ersję ty p u d o u b l e na typ z e s p o lo n a . Taki k o n stru k to r n a z y w a się k o n stru k to rem konw ertującym .
Dlaczego wyłącznie bez explicit ? Pow yżej z astrz eg łem , że aby k o n stru k to r m ógł być k o n stru k to re m k onw ertu jącym - nie m o że m ieć p rzy d o m k a e x p l i c i t . Co to w łaściw ie znaczy? Jeśli p rz y deklaracji k o n stru k to ra po staw iliśm y specyfikator e x p l i c i t ("w yłącznie jaw ny"), to oznacza, że zab ra n iam y kom p ila to ro w i uży ć tego k o n stru k to ra niejawnie. A przecież k o n w ersje - k o m pilator robi w łaśn ie niejaw nie ! IW M W M M M l
Z a te m p r z y d o m e k e x p l i c i t p rzy k o n s tru k to rz e m o ż e m y ro z u m ie ć ja k o .
"Kompilatorze! —nie używaj tego konstruktora do konwersji . K onstru k to r z p rz y d o m k ie m e x p l i c i t n ie jest więc k o n stru k to rem konw ertu jącym. (Bliżej o tym w następ n y m paragrafie). 18
W naszej klasie zespolona konstruktor nie był explicit, więc... ...jest on p ra w d z iw y m kon stru k to rem konw ertującym z ty p u d o u b le na typ z e s p o lo n a . Z w racam u w a g ę , że ten sam efekt (k o n stru k to ra konw ertującego) m ogliśm y w naszej klasie z e s p o l o n a uzyskać jeszcze prościej d ek laru jąc m ianowicie d ru g i a rg u m e n t istniejącego już k o n stru k to ra jako d o m n iem an y . z e s p o lo n a ( d o u b le r , d o u b le i = 0 ) ; W obu sytuacjach e fe k tje stte n sa m : m am y konstruktor, który m o żn a w yw ołaćz jednym a rg u m e n te m ty p u d o u b le .
764
Rozdz. 18. Konwersje K onstruktory konw ertujące C o ciekaw sze - taki konstruktor będ zie z a w sz e n iejaw n ie (czyli au to m aty czn ie) w y w oły w an y , g d y tylko funkcja oczekująca a rg u m e n tu ty p u z e s p o l o n a , dosstanie a rg u m e n t ty p u d o u b le . To w sp a n ia łe n a rz ę d z ie s p ra w i, że od tej pory zam iast funkcji: z e s p o lo n a z e s p o lo n a z e s p o lo n a z e s p o lo n a
d o d a j( z e s p o lo n a , z e s p o lo n a ) ; d o d a j ( z e s p o lo n a , d o u b le ) ; d o d a j(d o u b le , z e s p o l o n a ) ; d o d a j(d o u b le , d o u b l e ) ;
w ystarczy tylko jedna z e s p o lo n a
d o d a j( z e s p o lo n a , z e s p o lo n a ) ;
Jak się to dzieje ? O tóż teraz, w p rz y p a d k u , gdy k o m p ilato r zobaczy w y ra ż e n ie wynik = d o d a j( p ie r w s z a , 7 . 5 ) ; zro zu m ie je jako wynik = d o d a j (p ie rw s z a ,
zespolona
( 7 . 5) ) ;
Jak w idzim y, zostało tam um ieszczone w y w o łan ie k o n stru k to ra , które zam ieni liczbę d o u b l e == 7.5 na obiekt ch w ilow y k lasy z e s p o l o n a i te n obiekt zostanie przesłan y d o funkcji d o d a j . M ów im y, ż e nastąp iła k o n w ersja ty p u d o u b l e d o typu z e s p o l o n a . Po wykonaniu wyrażenia, w którym wystąpiła ta funkcja, obiekt chwilowy zostanie zlikwidowany. Kiedy dokładnie - to zależy od implementacji. W ażne jest to, że k o n w ersja n a s tą p iła n ie ja w n ie . N ie m u sie liśm y niczego spe cjalnie w yszczególniać. K om pilator sp o d z ie w a ł się ty p u z e s p o l o n a , a zoba czył ty p d o u b l e . W obec tego szukał: czy jest jakiś sp o só b na zam ian ę typu d o u b l e na ty p z e s p o l o n a ? Jest! - to d o starczo n y p rz e z p ro g ram istę konstru ktor obiektu k lasy z e s p o l o n a przyjm ujący jed en a r g u m e n t- w ła ś n ie typu d o u b l e . S praw a ro zw iązan a. K onstru k to r tak i m o że być też w y w o łan y jaw nie. O to definicja obiektu klasy z e s p o l o n a p rzy użyciu tego k o n stru k to ra z e s p o lo n a czw órka = z e s p o l o n a ( 4 . 0 ) ; Jaw ne w y w o łan ie k o n stru k to ra m oże słu ży ć rozw ianiu w ątp liw o ści jaki sp o só b konw ersji k o m p ilato r m a w ybrać (g d y b y by ło kilka) W idzim y ju ż w ięc, że d zięk i zasto so w an iu konw ersji zap o b ieg am y koniecznoś ci w ielo k ro tn eg o p rzeład o w an ia funkcji - czyli oszczęd zam y sobie pracy.
W
765
Rozdział. 18. Konwersje K onstruktory konw ertujące
18.2.1
Kiedy jawnie, kiedy niejawnie Kilka ra z y w sp o m in aliśm y o ja w n y m i niejaw nym u ży ciu k o n stru k to ra. Z o b a czm y jeszcze kilka definicji ob iek tó w , w których cza se m jaw nie, czasem niejaw n ie u ru ch am ian y jest k o n stru k to r. O to ilustracja te g o zag ad n ien ia #include
using
namespace
iS
s /b m
class
zespolona
std;
faimmiiiiMiimimimiiMMmimimmimmiimmmiiiiimi
{ double
rzeczyw;
double
u r ó j on;
public
:
// d w a k o n stru k to ry konw ertujące explicit zespolona(double
. r = 0, d o u b l e i r z e c z y w ( r) , u r o j o
:
n -■
//
O
)
n (i )
i ) friend
void
void
pokaz(const
pokaz(const
cout
zespolona
zespolona
z)
«
"Liczba
zespolona:
<<
",
z.urojon
"
«
// @
z ),
["
<<
«
"1
z.rzeczyw \n";
;***************.*******************************************W int
m a i n ()
{ zespolona //
// § // O
a; c ^ ^ . S ;
d = z e s p o l o n a (2 . 6 ) ; *wsk_e = new z e sp o lo
zespolona zespolona
f g
= =
//
^ -niejaw ne w yw o ła n ie kon struktora ©
zespolona zespolona zespolona
n a (2.
7 ),
( z e s p o l o n a ) 2.8; s t a t i c _ c a s t < z e s p o l o n a > (2.9) ;
// // // //
© 8 © ©
//pokaz(a); p o k a z (b); // p o k a z ( c ) ;
// (to
jest konsekwencja w cześniejszej " n iem o żliw o ści')
©
pokaz(d); p o k a z (* w s k _ e ) ; p o k a z (f ) ; pokaz(g); // p o k a z ( 3 . 0 ) ;
1
// < - p r z y
explicit niem ożliw e
©
18
766
Rozdz. 18. Konwersje K onstruktory konw ertujące
Po wykonaniu tego programu, w takiej postaci - na ekranie zobaczymy Liczba Liczba Liczba Liczba Liczba
zespolona: zespolona: zespolona: zespolona: zespolona:
[0,
0]
[2.6, [2.7, [2.8,
0] 0] 0] 0]
Liczba
zespolona:
[2.9,
0]
[2.4,
Jeśli zaś usuniemy przydomek expiicit przy deklaracji konstruktora, możliwe będą instrukcje dotychczas ujęte w komentarz i wówczas ekran będzie wyglądał tak:
•O
Liczba Liczba Liczba
zespolona: zespolona: zespolona:
[0, 0] [ 2 . 4 , 0] [ 2 . 5 , 0]
Liczba Liczba Liczba Liczba Liczba
zespolona: zespolona: zespolona: zespolona: zespolona:
[2.6, [2.7, [2.8, [2.9, [3, 0]
0] 0] 0] 0]
Krótki przegląd tego przykładu O K lasę z e s p o l o n a zn am y od d aw n a. T eraz jednak jej k o n s tru k to r m a d o d atk o w y p rzy d o m e k (specyfikator) e x p l i c i t . O znacza to, ż e z a b ra n ia m y kom pila torow i u ż y w a ć tego k o n struktora niejaw nie. D zięki te m u - p rz y każdym e w en tu a ln y m n iejaw nym w yw ołaniu - k o m p ilato r z g ło si b łą d kompilacji. W ten sposób p rz e k o n a m y się, że w d an y m m iejscu, d o ta k ieg o niejaw nego użycia m iałoby dojść. © W klasie jest też deklaracja przyjaźni z funkcją p o k a z . O czy w iście ta „p rzy jaźń " nie jest istotą naszej rozm ow y, sk u p m y się raczej na sam ej deklaracji funkcji. Jak w idzisz, d o tej funkcji p o k a z w ysyła się (jako jed en a rg u m e n t) obiekt klasy z e s p o l o n a . P rzesłan ie odbyw a się p rzez w artość. Przyjaźń jest potrzebna tu tylko po to, by ta funkcja mogła wyświetlić na ekranie prywatne składniki obiektu.
Teraz zobaczymy różne sposoby definicji obiektów © Definicja o b iek tu o n azw ie a . W takiej sytuacji w y w o ły w a n y zostaje konstruktor. Pytanie: Jaw n ie czy nie jaw nie? O czyw iście, że jaw nie. Ł atw o to za p a m ię ta ć tak: Jeśli jest tu n a p isa n a n azw a k la sy ( z e s p o l o n a ) , to znaczy że k o n s tru k to r w y w o łan y b ęd zie ja w n ie.
I
K tóry k o n stru k to r zo stan ie w yw ołany? N o , m am y tylko ten jeden, ale za to m a on dw a d o m n ie m a n e arg u m en ty . Tu w ięc k o m p ilato r sk o rzy sta z d o m n iem a nych w artości o b u arg u m en tó w .
767
Rozdział. 18. Konwersje K onstruktory konw ertujące O Definicja o b iek tu o n azw ie b. K o n stru k to r jaw nie czy niejaw nie? M am y tu n apisaną n a z w ę klasy, a w ięc jaw nie. To jest taka sam a sy tu acja, jak p o p rzed n io , z tą różnicą, że teraz k o rzy stam y z w artości d o m n iem an ej tylko d ru g ieg o arg u m e n tu . © Definicja o b ie k tu c. C o stoi po lew ej stronie zn ak u '= ' ? Stoi tam n a z w a klasy i nazw a obiektu - zatem z lew ą stro n ą nie m a nigdy p roblem u. Co stoi p o p ra w e j stronie zn ak u '= ' ? Stoi tam sta ła d o sło w n a 2.5 ( d o u b le ) . K o m p ilato r w id zi, że p o lewej i praw ej stro n ie stoją ró żn e typy. W łaściw e to od razu m ógłby sy g n alizo w ać b łąd , ale p o n ie w aż jest n a sz y m przyjacielem , sp róbuje jakoś ten p ro b lem rozw iązać. Z atem , k o m p ila to r - ab y m ieć po praw ej stro n ie zn ak u '= ' także w arto ść typu z e s p o l o n a - m u siałb y tę stojącą tam obecnie w arto ść d o u b l e (2.5) niejaw nie zam ienić n a w arto ść ty p u z e s p o l o n a . K o m pilator sz u k a w ięc, czy jest d o takiej zam ian y o d p o w ie d n i k o n stru k to r. Jest k o n stru k to r, k tó ry by się d o tego d o sk o n ale n a d aw ał - to n asz k o n stru k to r O . Ale n ie stety - je st p rzy nim p rz y d o m e k e x p l i c i t , k tó ry m zab ra n iam y k o m p i lato ro w i u ż y ć tego k o n stru k to ra niejaw nie. P oniew aż k o m p ila to r nie zn alazł sp o so b u niejaw nej z a m ia n y (konw ersji) w ar tości ty p u d o u b l e na ty p z e s p o l o n a , zatem pracu jąc n a d tą linijką —zgłosi błąd kom pilacji. To dlatego w programie musiałem ująć tę instrukcję w komentarz. W ten d ra s ty c z n y sposób d o w ied zieliśm y się od k o m p ilato ra, że tu nastąp iło b y niejaw ne u ż y c ie k o n stru k to ra. © Definicja o b ie k tu o n azw ie d. Tu sp ra w a jest inna. z e s p o lo n a d
= z e s p o lo n a ( 2 . 6 ) ;
Po p ra w e j s tro n ie napisaliśm y jaw n ie (w idzisz to sło w o z e s p o l o n a ? ) , że chcem y, b y k o m p ilato r w y g en ero w ał tutaj chw ilow y o b ie k t klasy z e s p o l o n a . Skoro tak, to p o praw ej i po lew ej są obiekty tego sam eg o ty p u . Ż ad n a jaw n a lub n iejaw na k o n w ersja nie m usi zachodzić. © S tw orzenie o p e ra to re m new ob iek tu klasy z e s p o l o n a . P rz y o p erato rze new jest (jaw nie) n a z w a klasy. O biekt tw o rz o n y jest zatem p rz y ja w n y m użyciu kons tru k to ra. Adres tego obiektu (zwracany przez operator new) przypisany jesł do wskaźnika. © Ta in stru k cja je st zaskakująca tylko na p ierw szy rz u t oka: z e s p o lo n a f
= ( z e s p o l o n a ) 2 . 8;
Czy p a m ię ta sz , że b ard zo d a w n o tem u m ów iliśm y o rzu to w a n iu ? (Str. 115 § 4.8.1). C h o d z i m i o tak z w a n e tradycyjne sposoby rz u to w a n ia , po ch o d zące jeszcze z ję zy k a C. O gólnie m ćrw iąc ch o d zi o takie w yrażenie:
< 1
8
768
Rozdz. 18. Konwersje K onstruktory konw ertujące (n a z w a _ ty p u ) wyrażeń ie_in negojypu N a p rzy k ład : jeśli m am y obiekt ty p u d o u b l e o n aw ie p i , to w y rażen ie (int)
pi
jest rzu to w a n ie m (czyli jakby konw ersją) w artości o b iek tu p i n a w artość ty p u i n t . R zu to w an ie jest to przecież ja w n e w yw ołanie k o n s tru k to ra w celu w y k o nania konw ersji. Rozmawiając (w tamtym rozdziale o operatorach) na temat rzutowania, mówiliśmy, że istnieją dwie formy (tzw. tradycyjnego) rzutowania. Zauioaż, że ta druga forma n a zw a _ typufwyrażen ie_innego_typu) to oczywiście prawa strona instrukcji 0 .
Dla wtajemniczonych: © A oto instrukcja definicji, w której p o praw ej stronie u m ie sz c z o n e jest in n e rzutowanie - w no w y m stylu, czyli posługującym się o p e ra to re m rzu to w an ia s t a t i c _ c a s t . (Str. 117,§4.8.3). zespolona
g
=
s t a t i c _ c a s t < z e s p o l o n a > ( 2 .9);
M ogłeś tego w ó w czas nie czytać, bo p arag raf o s t a t i c _ c a s t był raczej d la zaa w an so w an y ch . Spraw a jest je d n a k łatw a: p o praw ej s tro n ie zn a k u '= ' w id z i m y n azw ę k lasy - a to dow ód jaw ności. W iem y też, że chodzi tu o rzu to w an ie, czy li o JA W N Ą z am ian ę w arto ści 2.6 na ch w ilo w y obiekt klasy z e s p o l o n a .
I
D alej w p r o g r a m i e w i d z i s z w y w o ła n i a funkcji o n a z w i e p o k a z
N ic w tym nadzw y czajn eg o , g d y a rg u m e n te m jest o b ie k t klasy z e s p o l o n a . Funkcja ta o czek u je takiego obiektu - i w łaśn ie taki o trz y m u je w w y w o łan iu . Pełna h arm o n ia. : Instrukcje © musiałem ująć w komentarz tylko dlatego, że obiekt o nazwie c nie zaistniał - przypominam - jego definicja © musiała zostać ujęta w komentarz (wiązała się z niejaiunym użyciem konstruktora). © W yw ołanie fu n k cji p o k a z , p o k a z ( 3 .0 ) ; Jak w iem y, funkcja ta oczekuje a rg u m e n tu ty p u z e s p o l o n a . Tym czasem tu taj arg u m e n tem u czy n iliśm y w artość ty p u d o u b l e . K o m p ilato r m ógłby od razu zap ro testo w ać, że ty p się nie z g a d z a , ale próbuje nam p o m ó c , czyli (niejaw nie) dokonać z a m ia n y w artości typu d o u b l e na typ zespolona. Jest na to tylko jed en sposób - u ży cie k o n stru k to ra O . N iestety: p rzy nim sto i słow o e z p l i c i t , którym zab ro n iliśm y kom p ilato ro w i u ży w ać tego k o n stru k to ra niejaw nie. „-N ie? To nie!" - m y śli k o m pilator i sy g n alizu je błąd. W ten sposób z n o w u d o w ied zieliśm y się, g d zie niejaw nie d o szło b y d o w y w o ła nia ko n stru k to ra.
769
Rozdział. 18. Konwersje K onstruktory konw ertujące
G d y b y w n a sz y m p ro g ram ie u s u n ą ć z k o n stru k to ra p rz y d o m e k explicit, to w szystk ie u jęte obecnie w k o m e n ta rz instrukcje b y ły b y m ożliw e. N a d o w ó d zam ieściłem ró w n ież w y d ru k ek ra n u pochodzący z takiej wersji p ro g ram u .
Jak na razie, to m ogłeś o d n ieść w rażen ie, że p rz y d o m e k e x p l i c i t jest jakby kijem w ło żo n y m w szp ry ch y kom pilatora. C hciałby o n d la nas coś p o ży tecz nego zrobić, a m y m u zab ra n iam y . R zeczyw iście - w p rz y p a d k u teg o konstruktora n asza decyzja o p rzy d o m k u explicit b y ła w b rew ro zsąd k o w i. Przecież ro zsąd ek n ak azu je, żeby p o zw a lać k o m p ilato ro w i zam ien iać liczbę 2.3 na liczbę zesp o lo n ą.
Kiedy zatem jest przesłanka, by posłużyć się przydomkiem expiicit? O to moja z a sa d a . (Ty m oże zd ec y d u jesz się na inną). Z ałóżm y, ż e w p ro g ram ie są funkcje przyjm ujące jako a rg u m e n t obiekt mojej klasy. Dla zo b raz o w an ia sp ra w y załóżm y, że chodzi o k lasę ekran alfanu meryczny, k tó rą to klasą b aw iliśm y się d aw n o tem u. P rzy p o m n ę frag m en t jej definicji: class
ekran
alfanumeryczny
string tresc; char znak_tla; public:
/ / . .. // konstruktor ekran alfanumeryczny(char
~
{
znak =
1 ')
'
znak_tla
=
znak;
w y c z y s c ();
) I I ... 1; Jak w id zisz, jest tu k o n stru k to r, który ma jeden a rg u m e n t - a zatem ten k o n stru k to r m oże być k a n d y d a te m na konstruktor konw ertujący. Pozw ala on k o m p ilato ro w i niejaw nie zam ien iać w artość typu char na o b iek t klasy ekran_alf anumeryczny. C o to zn ac zy w praktyce? To zn aczy , że jeśli w n a sz y m program ie będzie funkcja, k tó ra jako a rg u m e n t przyjm uje obiekt klasy ekran_al .anumerycz ny, a Ty p rz e z p rzy p ad e k w yślesz tam w artość typu char, w ów czas k o m pilator nie zap ro testu je , nie poinform uje C ię o praw d o p o d o b n ej pom yłce, lecz ten znak char za m ie n i na ekran alfanum eryczny? C hciałbyś tego? Jeśli nie - to czym prędzej postaw przy tym k o n stru k to rze spccyfik ato r explicit. P o w tarzam jeszcze raz:
4 1
8
770
Rozdz. 18. Konwersje K onstruktory konw ertujące rrg P
G dy d efin io w aliśm y w klasie e k r a n _ a l f a n u m e r y c z n y ten kon stru k to r, to nie w celu z a m ia n y byle litery na ekran. Ta m ało w a ż n a litera p rz y defiriicj ek ran u , słu ży jed y n ie do p oinform ow ania go, co w s tę p n ie m a być jego tłem. .. _ . ii nwmimn—~mimwmwm ^w^^rr^łr^tflTr-trrnirrm-Tffinrthinnwwii—inr S k o ro w ię c u ż y c ie a rg u m e n tu T E G O k o n s tru k to ra je s t d a le k ie o d słowa „k o n w e rs ja ” - trz e b a to p o w ie d z ie ć k o m p ila to ro w i. R o b im y to w ła ś n ie sta w ia ją c p rz y d e k la ra c ji k o n s tru k to ra p rz y d o m e k e x p l i c i t - m ó w ią c y : „K o m p ila to rz e ! T e n k o n s tru k to r n ie robi s e n s o w n e j k o n w e r s ji” .
18.2.2
I
Przykład konwersji konstruktorem W p o p rzed n ich przykładach w id zieliśm y konw ersję z ty p u typem w b u d o w a n y m .
d o u b le ,
który jes*
Typem, z którego dokonuje się przekształcenia nie musi być koniecznie typ wbudowany. Może to być także inna klasa. K o nstruktor w ó w czas m usi p rzy jrzeć się obiektow i tam tej d ru g iej klasy i na tej p o d staw ie sk o n stru o w ać obiekt sw ojej klasy. Aby to „p rz y jrz e n ie się" było m ożliw e, ta d r u g a klasa pow inna z a d b a ć o to, by n asz k o n s tru k to r m iał dostęp do w ażnych d la niego składników . D ostęp ten m oże m u nadać: ♦> przez u czy n ien ie interesujących sk ład n ik ó w p u b lic z n y m i (co by było b ard zo nierozsądne), ❖
przez deklarację przyjaźni z tym (obcym ) k o n stru k to re m , k tóry m a z nich k o rzy stać,
❖
przez pub liczn e funkcje sk ład o w e, które p o z w o lą u zy sk ać w szystkie interesujące informacje. Te funkcje m oże u ru c h o m ić k ażd y , w ięc także nasz k o n stru k to r.
N ajlepiej zilu stru je to przykład. Z o b aczy m y w nim d w ie klasy. Jed n a z nich to klasa z e s p o l o n a . Z auw aż, że te ra z d an e sk ład o w e są ju ż p ry w atn e. O to definicje tych klas:
c l a s s numer;
//
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiim class
zespolona
{
private : double rzeczyw; double public
urójon;
:
/ / -------- k o n s tr u k to r y ----------zespolon a(dou ble r = 0, rzeczyw(r) zespolona(numer);
double
urójon(i)
i
=
0)
// //
{)
I; illllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllMIIIIIIIIIIIIIIIIIIIIII class
numer
{ double
n;
string
opis;
// deklaracja p r z y j a ź n i z
.
ta k im k o n stru k to re m z m n e] klasy
771
Rozdział. 18. Konwersje K onstruktory konw ertujące
friend
//
z e s p o l o n a ::z e s p olona(numer);
''
v
. . .
public
:
/ / ------ zwykły, własny konstruktor n u m e r (i n t
{
k,
string
t
=
"bez
opisu"):
n(k),
opis(t)
)
};
Przyjrzyjmy się tym klasom O D eklaracja k o n stru k to ra k o n w ertu jąceg o z ty p u d o u b l e na typ w łasnej klasy — czyli z e s p o l o n a . Ma on a rg u m e n t d o m n iem an y i , w ię c m ożna go w y w o łać także jako z e s p o l o n a ( d o u b l e ) —a o to nam chodzi. T y m sposobem b ęd zie m y m ieli ko n w ersję typu d o u b l e n a typ z e s p o l o n a . Zauważ, ż e jesteśm y jeszcze sprytniejsi. Także p ierw szy a rg u m e n t jest d o m n ie m an y (p rzez d o m n iem an ie = 0 ). D zięki tem u w rezu ltacie takiej definicji. zespolona
xxx;
p o w stan ie obiekt, w k tórym część u rojona i część rzeczy w ista jest zerow a. © D eklaracja k o n stru k to ra konw ertującego, k tóry za m ie n i typ n u m e r na ty p swojej klasy. Tym sposobem b ęd ziem y mieli k o n w ersję typu n u m e r na ty p z e s p o l o n a . P o n iew aż n u m e r nie jest typem w b u d o w a n y m , w ięc k o m p ilato r nie zn a tej n azw y . To d lateg o po w y żej © m usieliśm y p o w ied zieć, że n u m e r jest nazw ą jakiejś klasy. (D eklaracja zapow iadająca). © N ato m iast w k lasie n u m e r (taka so b ie nieciekaw a klasa) jest deklaracja p rz y ja ź ni. D eklaracja ta m ów i, że klasa n u m e r zg a d z a się, b y w yspecyfikow any k o n stru k to r z obcej klasy m iał d o stę p do jej sk ła d n ik ó w p ry w atn y ch . ^ Pamiętasz oczywiście, że fakt iż deklaracja przyjaźni jest tu akurat w części p r i v a t e nie ma żadnego znaczenia. Deklaracja przyjaźni może być w dowolnej części definicji klasy.
A oto realizacja tego © konstruktora klasy zespolona: z e s p o l o n a ::z e s p o l o n a ( n u m e r
ob)
( rzeczyw urójon
= =
ob.n; 0; 18
Co tu jest interesującego? To, że m o ż e on odnieść się d o p ry w atn eg o sk ład n ik a obiektu k lasy n u m e r tak, jakby b y ł o n publiczny. Liczba w yjęta ze sk ład n ik a o n azw ie n jest u ż y ta d o p rzy p isan ia d o części rzeczy w istej now o k o n stru o w a nego o b iek tu . Definicji funkcji d o d a j (przy której w y w ołaniu zajdą konw ersje) nie zam iesz czam , b o jest id en ty czn a jak n a stro n ie 761. O to p rz y k ła d o w e użycie konw ersji w e frag m en cie p ro g ram u :
II .. . numer
num(4,
zespolona
"czwórka");
z (10,
9) ;
772
Rozdz. 18. Konwersje Funkcja konw ertująca - operator konwersji
© Tu zo stan ie w y w o łan a konw ersja z ty p u d o u b l e na ty p z e s p o l o n a . O tyJ*j m ó w iliśm y pow yżej. © Tu n ato m iast n astąp i konw ersja z ty p u n u m e r na ty p z e s p o l o n a . O czywiści* o d b ęd zie się to p rzez niejaw ne w y w o łan ie naszego k o n s tru k to ra k o n w ertu jącego: zespolona
i i z e s p o l o n a (nurner)
o tym , ze tak jest w istocie, m o żn a się zaw sze p rzek o n a ć p ro s ty m
sposobem .
W takiej n iejaw n ie w yw oływ anej funkcji w staw iam y in stru k c ję cout
«
" T o ja , "z t y p u
twój k o n s t r u k t o r k o n w e r t u j ą c y n umer n a typ z e s p o l o n a \n";
\n"
T eraz - ile ra z y konstruktor b ęd zie pracow ał - z a w sz e s ię o tym dow iesz. C zasem tak ie z ab a w y bywają pouczające.
18.3
F u n k c ja k o n w e rtu ją c a - o p e ra to r k o n w e rs ji W yobraźm y sobie sytuację o d w ro tn ą d o poprzedniej. M a m y funkcję, która przyjm uje jako arg u m e n t typ w b u d o w a n y (np. d o u b l e ) void
funkcja
(double
x
);
a chcielibyśm y m óc w yw ołać ją tak że z arg u m en tem b ę d ą c y m obiektem jakiejś klasy. zespolona obiekt; funkcja(obiekt);
P o p rzed n ie ro zw iąz an ie się nie nadaje. Polegało ono na zasto so w a n iu k o n stru ktora, k tóry stw o rzy obiekt ż ą d a n e g o (w ynikow ego) ty p u . Tym żąd an y m typem jest teraz double - czyli ty p w b u d o w a n y . N ie m o ż e m y przecież napisać żad n eg o k o n stru k to ra w „k lasie" d o u b l e . Co robić? Jest wyjście: m usim y n aszą klasę z e s p o l o n a w y p o sa ż y ć w funkcję składow ą, k tó ra zajm ie się przek ształcen iem obiektu tej k la sy na ty p d o u b l e . | rp 5 P
Funkcja taka z w an a jest fu n k cją k o n w e rtu ją c ą albo inaczej (choć niezbyt precyzvinie) o p e ra to re m k o n w ersji. Funkcją konw ertującą jest funkcja sk ła d o w a klasy K, która się
I
n azy w a K : :o p e r a t o r
T ()
g d z ie T jest nazw ą ty p u , n a który chcem y d o k o n ać konw ersji (T m oże być naw et n a z w ą ty p u w b u d o w a n eg o ).
773
Rozdział. 18. Konwersje F unkcja konw ertująca - operator konwersji
Tym sp o so b em w y p o saży liśm y k o m p ilato r w n a rz ę d z ie d o d o k o n y w an ia k o n w ersji z ty p u K na ty p T.
Zdefiniujmy sobie taką funkcję na przykładzie O czyw iście m u sim y być n ajp ierw św iad o m i tego, co chcem y o trzy m ać p o konw ersji. Z ałóżm y, że m a m y naszą klasę liczb zespolonych i w y m y śliliśm y , iż ko n w ersja na ty p i n t p o le g a na w zięciu części całkow itej sk ład n ik a rzeczyw istego. (Urojo ny zan ied b u je m y ). Definicja tak ieg o o p erato ra k o n w ersji jest b ard zo pro sta: c l a s s z e s p o lo n a { // ... p u b lic : operator intO ^ }
re tu rn
(in t)
rz e c z y w ;
/ / prościej: r e t u r n rzeczy w ;
); To w sz y stk o . O d tej p o ry m ożem y n a sz ą liczbę zesp o lo n ą w y słać d o w szystkich funkcji, k tó re jak o a rg u m e n t sp o d ziew ają się ty p u i n t . I to nie tylko do naszych funkcji w p ro g ra m ie , ale n aw et d o funkcji bibliotecznych. K om pilator b ow iem za po m o cą te g o o p erato ra d o k o n a potrzebnej z am ian y i w yśle tam ju z liczbę typu i n t . Jeśli w ięc jest g d z ie ś funkcja v o id k u k u ł k a ( i n t i l e _ r a z y ) ; to m o ż em y te ra z n ap isać takie jej w y w o łan ie z e s p o lo n a z z z ( 5 . 1 , 4 4 4 . 5 ) ; / / dęf. przykładowego obiektu U------------- wywołanie w którym nastąpi niejawna konwersja kukułka(zzz); W ra z ie p o trz e b y , ko m p ilato r w y w o łu je funkcję k o n w ertu jącą sam odzielnie (niejaw nie). O czy w iście m ożem y też sam i w y w o ły w ać ten o p erato r jaw nie. in t i ; i = (int)z z z ; i = int( z z z ) ; Jak w id a ć , d o p u sz c z a ln e są tu d w a zapisy. Jeden, k tó ry p rzy p o m in a rz u to w a nie, a d ru g i, p rzy p o m in ają cy w y w o łan ie funkcji. W róćm y je d n a k d o sam ej definicji funkcji konw ertującej.
Należy tu zapamiętać parę ważnych rzeczy: ♦♦♦ 1) F u n k cja konw ertująca (o p erato r konw ersji) m u s i b y ć funkcją s k ła d o w ą k lasy . Jest w ięc w niej w sk aźn ik t h i s d o o b iek tu , który ma p o d d ać k o n w ersji.
4 1
8
774
Rozdz. 18. Konwersje Funkcja konw ertująca - operator konwersji A *
* *
2) Funkcja ta nie ma o k re ś le n ia ty p u r e z u lta tu z w ra c a n e g o . To ch y b a oczy w iste - funkcja ta p rzecież zaw sze z w ra c a ta k i ty p , jak się sa m a n a zy w a, w ięc d o d atk o w e ok reślan ie ty p u re z u lta tu b y ło b y p o w ta rz a niem się. 3) Funkcja ta ma p u stą listę a rg u m e n tó w . S k o ro tak , to au to m a ty c z n ie w y k lu czo n a jest m ożliw ość p rzeład o w an ia jej.
N astęp n e p u n k ty są już dla w tajem niczonych: ♦♦♦ 4) Funkcja konw ertująca je st d z ie d z ic z o n a . C zy li k la sy p o ch o d n e m ają * ją au to m aty czn ie po sw oich p rzo d k ac h - ch yba, że ją za sło n ią d efin iu jąc sw oją w łasn ą wersję. 5) Funkcja konw ertująca m oże b y ć fu n k c ją w ir tu a ln ą .
❖ D otychczas p o k azaliśm y , że typ, na k tó ry o d b y w ała się k o n w ersja był ty p em w b u d o w a n y m (u n as i n t ) . To d o b ry p rzy k ła d , bo w y k lu c z a m y si o defin io w a niu k o n stru k to ra konw ertującego w klasie i n t - takiej k la sy m e ma. Teraz jed n ak w y p ad a w spom nieć, że funkcja k o n w e rtu ją c a m oże d o k o n ać ' • d o b rz e konw ersji na ty p zd efin io w an y p rz e z u ż y tk o w n ik a (czyli na rów nie obiekt innej klasy).
Oto przykład C hcem y zd efin io w ać funkcję kon w ertu jącą z ty p u z e s p o l o n a na typ n u m e r czyli w o d w ro tn ą niż p o p rzed n io stronę. A by to u c z y n ić w k lasie z e s p o l o n a um ieszczam y deklarację funkcji konw ertującej
Zobaczmy teraz ten program całościowo #include using
namespace
#include
std;
// © class
1
•
zespolona
double double
rzeczyw; urójon;
//O
775
Rozdział. 18. Konwersje F unkcja konw ertująca - operator konwersji
p u b lic : // d w a k o n s tr u k to r y k o n w e rtu ją c e zesp o lo n a (d o u b le r = 0/ double i = 0 ) : r z
e c z y w
, (r ) / uroj o n ( i )
< > zespolona(numer ob) ;
// © w
' '
// O
operator d o u b l e () { return rzeczyw; } operator numer( }; v o id p o k a z() cout << " \tL ic z b a zespolona: (" « << ", " << urojon << ") \n"; fr ie n d
z esp o lo n a
rzeczyw
d o d a j(zespolona a, zespolon a b) ;
/W //////////////////////////M c l a s s numer ( double n; s t r i n g o p is ; fr ie n d z e s p o lo n a : : zespolona(numer) ; fr ie n d v o id plak ietk a(n u m er) ; p u b lic : _ . numer(double k, s t r in g t = "opis domniemany ) : n (k ), o p is(t) { ) o p era to r d o u b le () { return n; }
// ©
// © // ©
z e s p o lo n a :: zesp o lo n a (numer ob) : r z e c zy w (o b .n )* urójon(U) { /* p u s te ciało, bo w s z y s t k o zro b io n e z a p o m o c ą l i s ty m ic ja liza c y jn e j / } Z*************************************************************/ z e s p o lo n a : : o p e r a to r numer() // p o m a g a m y s o b ie // bo w y w o ł y w a n y
w y w o łu ją c k o n stru k to r (ale j u ż n ie k o n w e r tu ją c y , z 2 a rg u m e n ta m i).
return numer(rzeczyw, "powstał z zespolon ej ); /*************************************************************/ ___________________________________________ deklaracje f u n k c ji g lo b a ln ych void p o le kwadratu(double bok); void plak ietka(n um er nnn); zespolon a dodaj (zespolona a, zespolona b) ; j.^^^****/ /************* i * * * A ******************************************/ i n t main() { . . . // d efin ic je tr z e c h o b ie k tó w double x = 3.2 1 ; numer nr (4 4, "a imię je g o " ); zespolon a z (6, - 2 ) ; // w y w o ła n ia
f u n k c ji p o le jc w a d r a tu (d o u b le ) -----------------------------------
pole_kwadratu ( x ) ;
// n iep o trzeb n a
ż a d n a ko n w ersja
// p o n iż s z e w y w o ła n ia n ie są d o p asow an e, a le m im o to tn o żliw e, bo k o m p ila to r / / s a m o c z y n n ie z a s to s u je n a sze k o n w ersje p o le kwadratu (nr) ; // op era to r k o n w e r sji n u m e r d o u b le
©
4 1
8
776
Rozdz. 18. Konwersje Funkcja konw ertująca - operator konwersji
/ / operator konwersji z e s p o lo n a
pole_kwadratu(z);
//------zespolona
double
// def 2 roboczych obiektów
z2(4,5), wynik;
©
H wywołania funkcji dodaj(zespolonn, zespolona)wynik
=
dodaj(z,
// niepotrzebna żadna konwersja
z2);
w y n i k . p o k a z ();
// poniższe wywołania nie są dopasowane, ale mimo to możliwe, bo kompilator // samoczynnie zastosuje nasze konwersje wynik
=
d o d a j (z,
w y n i k .p o k a z ( ) ; w y n i k = d o d a j (z,
// konstr.
x);
konwertujący d o u b le
zesp o lo n a
_ nr);
//
konstr. konwertujący num er
zesp o lo n a
w y n i k .p o k a z () ;
©
// wywołania funkcjiplakietka(numer); plakietka(nr);
// poniższe wywołania nie są dopasowane, ale mimo to możliwe, bo kompilator // samoczynnie zastosuje nasze konwersje p l a k i e t k a (x) ; // konstr. konwertujący d o u b le numer p l a k i e t k a (z) ; // operator konwersji z e s p o lo n a numer /*************************************************************/ zespolona
dodaj(zespolona
a,
zespolona
b)
( zespolona return
chwilowy(
a.rzeczyw
+
b.rzeczyw,
a . urój on
+
b.urój on);
chwilowy;
^*************************************************************/ void
plakietka(numer c ut t co ou cout
<< << «
cout
<<
c ou ut t co
« << < <
nnn)
"*****************************" "***
<<
endl;
* * * \ T '*
■★ ★ "
<<
nnn.opis
<<
endl;
»»***
"*** ” << nnn.n << endl; *•*♦#★★♦★★★★★•*■★★★ ^ * * * * * * * * * * * * * M < < e n d l ;
}*************************************^*^^^^^^**^****V void
pole
kwadratu(double
cout
<< «
bok)
" P o l e k w a d r a t u o b o k u " << b o k " wynosi " « (bok * bok) « endl;
^ * n m, * K* * * i ***************************************************/ D
W rezultacie na ekranie pojawi się Pole
kwadratu
o
boku
3.21
Pole
kwadratu
o
boku
44
wynosi
Pole
kwadratu
o
boku
6 wynosi
wynosi
10.3041
1936 36
777
Rozdział. 18. Konwersje F unkcja konw ertująca - operator konwersji
Ciekawsze m iejsca programu O D eklaracja k lasy z e s p o l o n a (liczba zespolona). S p o tk aliśm y się juz z tą klasą. S k ład n ik am i tej klasy są d w ie liczby ty p u d o u b l e n a z y w a n e u m o w n ie częścią
rzeczywistą i częścią urojoną. © D w a k o n stra k tó ry k o n w ertu jące. T en p ierw szy p o trafi z b u d o w a ć obiekt klasy z e s p o l o n a z o b iek tu ty p u d o u b l e . Drugi k o n stru k to r ten przyjmujący'jeden a rg u m e n t ty p u n u m e r , jest to k o n stru k to r, który p otrafi zam ie n ić obiekt klasy n u m e r na o b ie k t klasy z e s p o l o n a . T y m sam ym w y p o sa ż a m y kom pilator w n arzę d zia d o niejaw nej, sam oczynnej konw ersji d o u b le numer
zesp olon a zespolona
W deklaracji teg o d ru g ie g o k o n stru k to ra k o m pilator n ap o ty k a nazw ę n u m e r . A by się nie z d z iw ił i nie p ro testo w ał, ż e nie wie, co to jest, p o w ied zieliśm y m u to © D eklaracja z ap o w ia d ając a. Jest to p o in fo rm o w an ie k o m p ilato ra, że: "jakby co, to jest n a z w ą jakiejś klasy". O O p e ra to r k o n w e r s ji (fu n k cja k o n w e rtu ją c a ) z ty p u num er
zesp o lo n a
na ty p
dou
b le.
© W łaściw a d ek laracja klasy n u m e r . Jak w idzim y, obiekt klasy n u m e r zaw iera liczbę ty p u d o u b l e o raz s t r i n g d o p rzec h o w y w an ia tekstu opisu tej licz y. 18 Poniżej w id z im y też, że klasa d ek laru je przyjaźń z k o n stru k to re m k o n w ertu jącym z k la sy z e s p o l o n a - czyli u d o stęp n ia m u inform ację tak, by m ogl z łatw ością zam ie n ia ć o b iek t klasy n u m e r na obiekt swojej klasy. 0 K o n stru k to r k o n w ertu ją cy o b iek t ty p u d o u b l e na ty p n u m e r . (D zięki tem u, ze d ru g i a rg u m e n t jest d o m n iem an y ). © F un k cja k o n w e rtu ją c a obiekt k lasy
num er
na typ
d o u b le.
0 W yw o łan ia funkcji p o l e _ k w a d r a t u na rzecz ob iek tó w trzech różnych typów . Pierw sze w y w o ła n ie jest oczyw iste - funkcja w y w o łan a jest z takim arg u m en tem jakiego oczekuje. C iek aw sze są d w a pozostałe w y w o łan ia, bo tu o b jaw ia się po raz p ie rw s z y w tym p rz y k ła d z ie w sp a n ia ło ść k o n w e rs ji. O to funkcję,
778
Rozdz. 18. Konwersje Funkcja konw ertująca - operator konwersji która się sp o d z ie w a arg u m en tu ty p u d o u b l e , m o żn a w y w o ła ć dla arg u m e n tu ty p u n u m e r lu b argum entu ty p u z e s p o l o n a . K o m p ilato r niejaw nie, sam oczynnie, posługując się d o sta rc z o n y m i operatora mi k o n w ersji, dokonuje konw ersji i jej r e z u l ta t - ju ż liczbę d o u b l e w ysyła d o funkcji p o l e _ k w a d r a t u . © P o d o b n ie tu taj. Teraz dzięki sam o czy n n y m k o n w ersjo m m o ż n a w yw ołać funk cję d o d a j ( z e s p o l o n a , z e s p o l o n a ) naw et dla a rg u m e n tó w typu d o u b l e i n u m e r . K tórym n arzędziem p o s łu ż y się k o m pilator — z az n aczy łem w kom en tarzach. © W y w o łan ia funkcji p l a k i e t k a ( n u m e r ) . Dzięki w sp a n ia ło śc i k o n stru k to ró w k o n w ertu jący ch i operatorów k onw ersji m ożliw e są n a w e t ta k ie w yw ołania.
W 18.3.1
Na co konwertować nie można To b ęd zie d ro b n a uw aga, dla dociekliw ych. D robna, b o nie s ą d z ę byś o d czu w ał p o k u sy o p sa n ie poniżej. Jeśli w k lasie K chcem y zd efin io w ać funkcję k o n w ertu jącą n a ty p T, to ó w typ T nie p o w in ie n być: ty m sam y m co typ K, ♦J* an i ty p e m K& a n i ty p e m
vo id
♦♦♦ an i ty p e m jakiejś funkcji ♦♦♦ a n i ty p e m rep rezen tu jący m tablicę ♦♦♦ (ani - dla wtajemniczonych: typem klasy podstawowej dla K) M ów iąc o b razo w o : O biektu k lasy F i a t l 2 6 p nie p o w in n iśm y k o n w erto w ać •
ani obiekt F i a t 12 6p, - (czyli na o b ie k t ta k ie g o sam ego typu),
•
ani na obiekt "maluch" - (bo to p rz e z w isk o F i a t a l 2 6p),
•
ani na NIC ( v o i d ) - (byłaby to m a sz y n k a do "znikania obiektów"),
•
ani na obiekt ty p u "śpiewam _piosenkę_tyrolską()"
•
ani na koszyk o b ie k tó w jednego ty p u
•
ani - uwaga koledzy "wtajemniczeni" - na s a m o c h ó d - (gdy to klasa podstawowa dla F ia ta l2 6 p ).
Inform ację tę p o d aję raczej dla o b o w iązk u , bo m yślę, ż e n ig d y nie p rzy szło b y Ci d o g ło w y , by definiow ać o p e ra to r d o takiej konw ersji.
Rozdział. 18. Konwersje Sytuacje, w których zachodzi konw ersja
779
780
Rozdz. 18. Konwersje K tóry w arian t konwersji w ybrać? b e z ra d n y i zaprotestuje. N ie m o ż e być bow iem żad n e j w ieloznaczności. O ba sposoby konw ersji są dla n iego je d n ak o w o dobre. J No, chyba że to my -ja w n ie wywołamy konwersję (konstruktor lub funkcji:konwertującą). Wówczas kompilator nie musi niczego decydować - zrób i
to, o co go prosimy. 1 Jednak nie polecam tego. Jak ju ż mówiłem - wspaniałość konwersji polega na tym, że zachodzą niejawnie. W niosek w ięc taki: m usim y w y b ra ć jeden ze sp o so b ó w konw ersji. Albo-albo. W obec teg o , co w ybrać? Spraw a n ie jest tru d n a. To d la teg o , że te dw a w aria n ty n ieco się różnią. Oto te różnice: K onstruktor m a p e w n e w ady
* *
N ie m ożna zdefiniow ać k o n stru k to ra dla ty p u w b u d o w a n e g o . N ie m o żn a napisać k o n stru k to ra dla klasy, k tó ra nie jest naszą w łasnoś cią - np. będącą sk ład n ik iem biblioteki. M o d y fik acje takiej klasy są z w y k le niepożądane. N a w e t jeśli klasa jest n a sz ą w łasnością, to k o n s tru k to r, k tóry chcem y n ap isać, m usi oprzeć się na inform acjach z tej obcej klasy. T am ta obca k la sa m usi zapew nić sp o so b y dotarcia do tych inform acji. j (Robi to: przez publiczne dane składowe lub przez odpowiednie funkcje składowe, ewentualnie przez deklarację przyjaźni). _ J Jeśli ta obca klasa nie z a p e w n ia nam tych inform acji, to m u sim y ją
zm od y fik o w ać. ♦♦♦ P rz y k o n stru k to rze k o n w ertu ją cy m a rg u m e n t m u si p aso w ać d o k ład n ie d o typu arg u m en tu d ek laro w a n eg o w k o n stru k to rz e . N ie m ożem y p o le g ać na żadnych —tak zw an y ch konw ersjach sta n d a rd o w y c h . (dla w tajem niczonych) K o n stru k to ra służącego d o konw ersji nie d zie d z ic z y się (bo nie d z ie d z i * czy się przecież żad n y ch kon stru k to ró w ). N a to m ia s t funkcja k o n w e rtu jąca jest funkcją sk ład o w ą, w ięc m oże być d z ie d z ic z o n a . I to w sposób „ m ą d ry " - m oże być b o w iem w irtu aln a.
Jakie wynikają z tego wskazówki 1) Aby z a p e w n ić konw ersję o b iek tu klasy A na ty p w b u d o w a n y - m u sisz posłużyć się o p erato rem konw ersji. Innej drogi nie m a. 2) Jeśli m a sz zap ew n ić ko n w ersję obiektu klasy A na o b ie k t klasy B to: •
1)
a) Staraj się n ap isać funkcję k o n w ertu ją cą w klasie A. N ie w y m ag a to ż a d n y c h ingerencji w k lasę B.
O tym niebawem - paragraf o niepasujących argumentach.
781
Rozdział. 18. Konwersje Sytuacje, w których zachodzi konw ersja
b) Jeśli zaś klasa A jest C i n ied o stęp n a, (bo n a p rzy k ła d jest z bi blioteki) to spró b u j w k lasie B napisać k o n stru k to r k o n w ertu jący obiekt klasy A na o b iek t klasy B, czyli k onstruktor: B: : B( A) U d a C i się to p o d w aru n k iem , że interesująca C ię część infor m acji o obiekcie klasy A jest Ci dostępna. Jeśli n ie jest, to nic nie z ro b isz - nie m o żesz p rzecież m o dyfikow ać klasy A (gdybyś m ó g ł - w ybralibyśm y w a ria n t a).
18.5
S y tu a c je , w k tó ry c h z a c h o d z i k o n w e rs ja Siłą tego n a rz ę d z ia , jak im są konw ersje d efin io w an e p rzez u ży tk o w n ik a, jest to, że zac h o d zą one niejaw nie. W ty m p a rag rafie zbierzem y sy tu acje, kiedy ko m p i la to r sięga p o te konw ersje. A zatem
Konwersje są wykonywane niejawnie, gdy: 1) K o m p ilato r, w w y w o ła n iu fu n k c ji, w idzi n ie zg o d n o ść arg u m en tó w ak tu aln y ch (w y w o łan ia) z a rg u m e n tam i form alnym i, a ró w n o cześn ie jest jed n o zn aczn a m o żliw o ść by za pom ocą ko n w ersji to n ie d o p a so w a n ie usunąć. 2) G d y , p rz y z w ra c a n iu re z u lta tu fu n k c ji, obiekt stojący o b o k słow a r e t u r n jest in n eg o ty p u n iż d ek laro w a n y dla funkcji typ rezu ltatu . Jeśli funkcja m a zw ró cić typ B, a obok sło w a r e t u r n stoi o b ie k t ty p u A - to jeśli tylk o jest z d e fin io w a n y jednoznaczny sp o só b , jak zam ien ić o b ie k t klasy A na obiek t k lasy B - w ó w c z a s konw ersja ta jest niejaw nie p rz e p ro w a d z a n a . 3) W o b e c n o śc i o p erato ró w . O ty m b ęd zie n astęp n y r o z d z ia ł- w ięc krótko: Jeśli m am y o p e ra to r d o d a w a n ia (inaczej m ó w iąc znaczek ' +' ) , to sp o d ziew a się on (zw ykle), ż e obok b ę d ą stały jakieś o b iek ty typu d o u b l e lu b i n t - ogólnie m ó w iąc ty p u B 3.4 + 7 Jeśli zaś p o sta w im y ta m obiekt klasy A
8
A ob j ; 3 .4 + o b j ;
to jeśli tylko zdefiniowaliśm y jak z obiektu klasy A zrobić obiekt typu B w ów czas zajdzie taka niejawna konwersja z typu A na typ B. W n aszy m p rz y k ła d z ie z liczbam i zesp o lo n y m i zd efin io w aliśm y konw ersję z obiek tu z e s p o l o n a na i n t - w ięc tu w łaśn ie by ona zaszła. zespolona z z z (4 .4 , i n t m; m
1 + zzz;
10.0);
// c z y li m = 1 + 4;
782
Rozdz. 18. Konwersje Zapis jawnego wywołania konwersji typów
4)
N a s tę p n ą sy tu acją, g d y k o n w e rsja za jd z ie n ie ja w n ie s ą w y r a ż e n ia :
// o b j - je s t
obiektem k la sy A
if(obj) ..■ switch(obj) ... while (obj ) ... for (. .. ; obj ;
. . .
)
/ / <- w a r u n e k
w in s tr u k c ji fo r
W k a ż d y m p rz y p a d k u k o m p ila to r b ę d z ie chciał o k re ś lić w a rto ś ć (int)obiekt
Jeśli te g o n ie z d e fin iu je m y , to k o m p ila to r z a p ro te s tu je p r z y ta k z a p is a n y c h in stru k cja ch . Jeśli funkcję k o n w e rtu ją c ą A : : operator i n t ( ) ; z d e fin iu je m y , to z o sta n ie o n a n ie ja w n ie u ży ta. P o n ie w a ż w n aszej klasie z e s p o l o n a m am y te n o p e r a to r , w ię c m o ż e m y s to s o w ać w s p o m n ia n e w yżej z a p isy zespolona zzz(10, 0.3)
if(zzz)...
5) N a s tę p n y m p rz y p a d k ie m , g d y z a c h o d z i n ie ja w n a k o n w e rs ja ty p u , to w y r a ż e n ia in ic ja liz u ją c e . O to p rz y k ła d : zespolona zzz (10.5, 3); ^ = Zzz; // + - t u
_
...
..
.
.
n ie ja w n e w y w o ł a n ie k o n w e r s ji: z e s p o lo n a n a in t
P o d c z a s inicjalizacji obiektu ty p u int o b iek t k la sy zespolona z o sta n ie p o d d a n y k o n w e rsji n a ty p int. W re z u lta c ie , jak w ie m y , z a m ie n ia m y g o na lic z b ę 10. Tą w a rto ś c ią jest in ic ja liz o w an y o b ie k t o n a z w ie k.
18.6
Z a p is ja w n e g o w y w o ła n ia k o n w e rs ji ty p ó w D o tej p o r y z a c h w y c a liśm y się ty m , ja k to w s p a n ia le , ż e k o n w e rs je z a c h o d z ą n ie ja w n ie (czyli sa m o c z y n n ie ). T e ra z p o ro z m a w ia jm y o sy tu a c ji, k ied y s a m i c h cem y s p o w o d o w a ć k o n w e rsję - c z y li g d y ja w n ie ją w y w o łu je m y . K o n w ersja jaw n a z a p o m o c ą funkcji k o n w e rtu ją c e j m o że m ie ć d w ie fo rm y z a p isu - fo rm ę rz u tu lu b fo rm ę w y w o ła n ia fu n k cji. P rz y k ła d o w o : jeśli m a m y k lasę zespolona, a jest o k r e ś lo n a ko n w ersja z ty p u zespolona na ty p inny, to d o p u s z c z a ln e są 2 fo rm y z a p is u takiej w y m u sz o n e ] (jaw nej) k o n w e rs ji zespolona zzz(l, 1 ); inny n; n = inny (zzz); n = (inny) zzz;
// f o r m a // f o r m a
, ... fu n k c ji r z u to w a n ia
783
Rozdział. 18. Konwersje Zapis jawnego wywołania konwersji typów
Z tym i formami spotkaliśmy się już przy typach zabudowanych (str. 115). O bie te form y robią to sam o, jed n ak nie są całkow icie id en ty czn e. W w iększości p rz y p a d k ó w m o żn a p o słu ży ć się o b o m a zap isam i. Są je d n ak p rzy p ad k i, gdy jed en z z ap isó w jest p refero w an y .
18.6.1
A dvocatus zapisu przypom inającego: „w yw ołan ie funkcji" Z ałó żm y , ż e k o n w ersja jest d o k o n y w an a w klasie i n n y za pom ocą k o n stru k tora k o n w ertu jąceg o o takiej deklaracji i n n y : : i n n y (z e s p o l o n a
z,
int
i
—
16),
(Jest to k o n stru k to r k o nw ertujący, bo sk o ro d ru g i a rg u m e n t jest d o m n iem an y to
da się ten k o n s tru k to r w y w ołać z jed n y m tylko a rg u m e n te m ty p u z e s p o lo n a ) . M am y w ięc ten k o n stru k to r i, jak na razie, obie form y zap isu konw ersji są d o p u szcza ln e. A le jeśli zap rag n iem y ta k ieg o jaw n eg o w y w o ła n ia , w którym d o d a tk o w o jest o b ecn y d ru g i arg u m e n t k o n stru k to ra n
=
inny(zzz,
333);
T o oczyw iście m o żliw a jest tylko ta fo rm a (w y w o łan ie funkcji). W d ru g ie j form ie - tej w sty lu rzu to w a n ia - nie d a się p rzesłać tego d ru g ie g o arg u m en tu . Z atem w tej sy tu acji jest tylko jedna m ożliw ość. Z w racam je d n a k u w ag ę: to tylko w ja w n y m w y w o łan iu tego ko n stru k to ra m ogliśm y sobie d o d a tk o w o określić ten d ru g i - d o ty ch czas d o m n iem an y - ar g u m e n t. W w y w o ła n iu niejaw nym - czyli konw ersji „sam o czy n n ej jest to niem ożliw e.
18.6.2
A dvocatus zapisu: „rzutowanie' C zasem jed n ak p refero w an a jest form a "rzutow anie", szczeg ó ln ie tam , g d zie ty p , na k tóry zam ien iam y , m a zb y t sk o m p lik o w an ą sk ład n ię. Z ałó żm y , ż e m am y klasę A, która m a funkcję k o n w ertu jącą na ty p c h a r * , j D eklaracja tej funkcji konw ertującej w y g lą d a następująco:
A:: operator char* O;
I I -■
Jaw n a ko n w ersja w form ie rzu to w an ia w y g ląd a tak:
A obj; char
*napis;
napis = (char *)obj; N ato m ia st form a fu n k cji byłaby n ied o p u szczaln a.
napis = char*(obj);
urn
784
Rozdz. 18. Konwersje Niecałkiem pasujące argum enty, czyli konwersje przy dopasowaniu Ł atw o się zo rien to w ać dlaczego - form a jest tak d z iw n a , iż k o m p ilato r raczc*| p o d ejrzew a, że się pom yliliśm y. Jestjcd n ak wyjście: jeśli instrukcją inną n azw ę
typedef dla ty p u c h a r * zd efin iu jem y sobiio
typedef char* tekst;
to zap is funkcyjny, z u życiem tej now ej n a z w y (ty p u c h a r * ) , jest nadal m ożliw y n a p is = t e k s t ( o b j ) ;
W W tajem niczeni w iedza, że jest jeszcze (w p ro w ad z o n y n ie d a w n o d o s ta n d a r d u ) o p erato r rzu to w an ia staticcast.Tutaj n ie m a p ro b lem u n aw et ze sk o m p li kow aną n azw ą ty p u , bo jest ona w y ra ź n ie w y d zielo n a o stry m i n aw iasam i < > n a p i s = static_cast ( o b j ) ;
1 8 .7
N ie c a łk ie m p a s u ją c e a rg u m e n ty , c z y li k o n w e rs je p rzy d o p a s o w a n iu Do tej p o ry m ów iliśm y o sytuacji idealnej: •
M am y ty p Y tam , g d z ie p o trzeb n y jest ty p X.Skoro istnieje o p e rator zam ien iający obiekt klasy Y na o b iek t klasy X - k o m p i lator stosuje (niejaw nie) ten o p e ra to r i sp ra w a załatw io n a.
N ie zaw sze je d n ak jest tak je d n o zn aczn ie pięknie. M im o to, n a w e t w sytuacjach n ie zu p ełn eg o d o p aso w an ia, k o m p ila to r m oże d o k o n a ć konw ersji. A by m ieć w yczucie, w ed łu g jak ich zasad k o m p ilato r postępuje, ro z w a ż m y kilka sytuacji:
Problem 1: ("Gdy bez konwersji, to lepiej") Są d w ie p rz y k ła d o w e funkcje - o p rzeład o w an ej n a z w ie fun fun(int); fun (X) ;
istnieje też o p e ra to r zam ieniający o b ie k t klasy X na ty p i n t . X : :o p e r a t o r int(); P y ta n ie : jeśli k o m p ilato r n ap o tk a ta k ie w y w o łan ie funkcji
X obj; fun ( o b j )
to k tó ra z p o w y ższy ch w ersji fu n k cji fun zo stan ie w y w o łan a? O d p o w ie d ź b rzm i: w y w o łan a z o s ta n ie funkcja
785
Rozdział. 18. Konwersje Niecałkiem pasujące argum enty, czyli konwersje przy dopasowaniu
fun(X)
;
dlatego, ż e p aso w ała o n a d o k ład n ie d o w y w o łan ia - bez konieczności stosow a nia jakiejkolw iek konw ersji. D ruga w ersja fu n (X ) w y m ag ałab y użycia niejaw nej konw ersji.
Problem 2: ("Konwersja użytkownika łącznie z konwersją standardową") M am y funkcję
fun( d o u b l e ) ; oraz istnieje o p e ra to r zam ieniający o b ie k t klasy
X na liczbę ty p u int.
X::operator i n t ( ) ;
X "■* int P ytanie: czy p o n iż sz e w y w o łan ie funkcji jest p o p raw n e? X ob j ; fun(obj) ;
Tak, bo n ajp ierw zo sta n ie zasto so w an y o p e ra to r konw ersji zam ieniający obj nna liczbę ty p u int, a n astęp n ie - za pom ocą konw ersji sta n d a rd o w y c h - zostanie to zam ien io n e n a ty p double. Jak w id zim y - n astą p iło tu jakby „ k a sk a d o w e " (2-krotne) zasto so w an ie k o n wersji. Ale u w ag a: W takiej kaskadzie (niejawna) konwersja zdefiniowana przez użytkownika może się pojaw ić tylko jednokrotnie.
Innym i słow y n ie jest m ożliw a taka sytuacja: M am y trz y klasy X, Y, Z. Z d efiniow aliśm y konw ersję z ty p u X n a ty p Y, a tak że z typu Y na typ Z .
X
V
z
T o w c a le n ie o z n a c z a , że z d e fin io w a liśm y ko n w ersję z ty p u
Zatem jeśli jest fu n k cja f u n ( Z) ; to nie m o żn a jej w y w o łać tak: X objx; fun(objx);
X na
ty p
Z.
786
Rozdz. 18. Konwersje Niecałkiem pasujące argum enty, czyli konwersje przy dopasowaniu dlateg o , że w y m ag ało b y to d w u k r o tn e g o zasto so w an ia k onw ersji zdefiniow a nej p rz e z u ży tk o w n ik a:
1).X 2). Y
Y Z
Jak p o w ied zieliśm y jest to n ie d o p u szc zaln e . Zasada ta jest dla naszego dobra. Chodzi o to, by przy luiększej ilości zdefiniowanych komoersji po prostu nie pogubić się . Wyobraź sobie, co by było, gdyby konwersje wykonywały się niejawnie jak rząd padających kostek domina. Czy z a te m w yw ołanie fu n k cji fun jest d la o b iek tu X absolutnie.’ n ied o p u szczaln e? Da się to zro b ić specyfikując ja w n ie pierw szą ko n w ersję. W ted y niejaw nie zajdzie ju ż ty lk o jedna - a to jest p o p ra w n e
fun( (Y)objx ); Nic w tym d ziw n eg o - w y ra ż e n ie b ęd ące a rg u m e n te m funkcji jest w ów czas typu Y. D o p iero to podlega n iejaw n ej konw ersji. W niejaw nej części tego procesu w y stęp u je już tylko je d n a k o n w ersja zd efin io w an a p rz e z u ży tkow nika. Z apam iętaj - zasad a ta do ty czy k o n w ersji niejaw nych. Jeśli n ato m iast robisz konw ersję jaw n ą - to m ożesz so b ie łączyć do w o ln ie d łu g ie kaskady.
Problem 3 ("Standardowa jest lepsza") W p ro g ra m ie nazw a funkcji fun jest p rzeład o w an a, istnieją b ow iem takie dw ie funkcje:
fun(double); fun(X) ; Jest też zd efin io w an y sposób k o n w ersji
int
X
P ytanie: K tóra z pow yższych fu n k cji zo stan ie wywro ła n a p rz y takim zapisie
fun(1) ; O dpo w ied ź: Z adziała funkcja dow a konw ersja
fun (double) d latego, ż e w y starczy ła s ta n d a r
int '»* double Konwersje zdefiniowane przez użytkownika uru cha m ian e zostają tylko wtedy, gdy w żaden inny sposób nie da się dopasow ać w yw ołania do funkcji.
mw»mturr>nmrniim ran-m nr>
--*•1
Inaczej m ów iąc: K onw ersje zd efin io w an e przez u ży tk o w n ik a są u ż y w a n e niejaw nie w u z u p e łn ie n iu k onw ersji stan d ard o w y ch .
Rozdział. 18. Konwersje Niecałkiem pasujące argum enty, czyli konwersje przy dopasowaniu
787
P r o b l e m 4: ( " N ie m o ż e b y ć w i e l o z n a c z n o ś c i " )
W iemy, że k o n w ersja zajd zie n iejaw n ie tylko w ted y , g d y n ie m a w ieloznaczno ści. Z ałó żm y w ięc, że m am y d w ie p rz e ła d o w a n e funkcje fun(int) fun(double);
oraz sposób k o n w ersji z ty p u X na ty p i n t , a także sp o só b konw ersji zam ienia jący typ X n a d o u b l e
int
Rys. 18-3.
X Pytanie: P rz y tak im zapisie X objx; fun(objx);
która w ersja funkcji f u n zo stan ie w y w ołana? O d p o w ied ź: Ż a d n a . Jest to klasyczny p rzy p ad ek d w u zn aczn o ści. O bie kon w ersje zam ien iają ty p obiektu na ty p pasujący d o k ła d n ie d o jednej z funkcji. K om pilator jest w rozterce, bo nie w ie, którą z konw ersji w ybrać. Z tej rozterki w yn ik a k o m u n ik a t o błędzie.
P r o b l e m 5 : ( " G d y j e d n a d r o g a j e s t k r ó t s z a , n i e m a w ą t p l i w o ś c i k t ó r ą iś ć " )
P rzy p ad ek p o d o b n y d o p o p rzed n ieg o , z tym , że teraz funkcje są takie fun(long); fun(double);
T eraz d w u z n a c z n o śc i nie ma. Jest bow iem jeden o p e ra to r konw ersji, który zam ieni o b ie k t k lasy X na obiekt typu
X
double
dokładnie pasującego do funkcji
fun(double),
N ato m iast d ru g i o p e ra to r zam ienia o b iek t klasy X na o b iek t i n t , po czym m usi zostać jeszcze w y k o n an a stan d ard o w a konw ersja i n t na l o n g .
X
"•+
int
"■*
long
co pasowałoby do funkcji fun (long); T a d ru g a d roga jest w y raźn ie dłuższa, w ięc kom pilator nie ma w ątp liw o ści (w ieloznaczności), którą należy w ybrać.
788
Rozdz. 18. Konwersje Niecałkiem pasujące argum enty, czyli konwersje przy dopasowaniu
P r o b l e m 6:
private
— n ie s t r z e ż e p r z e d d w u z n a c z n o ś c i ą
Klasa X m a d w a operatory konw ersji // //
X: : o p e r a t o r i n t ( ) ; X : : o p e r a t o r d o u b le ( ) ; R ys. 18-4.
(p riv a te ) ( p u b lic )
p r iv a te
X
/>ob //c
int double
P ierw szy z nich mieści się w części pryw atnej, a d ru g i w części publicznej klasy. M am y też d w ie funkcje fu n (in t); fu n (d o u b le ); C zy następ u jące w yw ołanie jest d o p u szczaln e? X o b jx ; fu n (o b jx ); O d p o w ied ź: N ie, nie jest d o p u szcza ln e. W ystępuje d w u zn aczn o ść. M im o że o p erato r i n t () znajduje się w części pry w atn ej klasy. To d lateg o , że:
K o m p ila to r z a w s z e n a jp ie rw ro z s ą d z a s p ra w ę d w u z n a c z n o ś c i, a d o p ie ro p o te m s p ra w d z a d o s tę p .
Z asad ę tę ła tw o zrozum ieć, jeśli się p am ięta, że n a z w a sk ła d n ik a pr i vat e jest z ze w n ą trz w id zialn a tyle, że n ied o stęp n a. P r o b le m 7: ( " S a m o is tn ie n ie n ie j e s t b ł ę d e m . D o p ie r o u ż y c ie ..." )
M am y d w ie klasy X oraz Y. K onw ersja z typu X na Y jest zrealizo w an a przez: a) funkcję k o nw ertującą w klasie X, b) k o n stru k to r k o n w ertu jący w klasie Y. Czyli są d w a je d n ak o w o d o b re sp o so b y zam ian y ty p u X n a Y. Pytanie: czy to ju ż jest błęd em ? . O d p o w ied ź: Jeszcze nie. Błąd n a stą p i w ted y , g d y g d z ie ś p o trze b n a będ zie n ie jaw na k o n w ersja z ty p u X na Y. (O p rz y p a d k a c h , k ied y tak a niejaw na k o n w ersja następ u je - m ó w iliśm y n ied aw n o ). To d o p ie ro w te d y o każe się, że je st d w u zn aczn o ść. Błąd n astąp i też w p rzy p ad k u jaw nej konw ersji. A lb o w iem oba zapisy X o b jx ; Y o b jy ; o b jy = (Y )o b jx ; o b jy = Y ( o b jx ) ; nie p recy zu ją, którą k o n k retn ie d ro g ą m a zostać z re a liz o w a n a konw ersja. J e d n ak że zap is
789
Rozdział. 18. Konwersje K ilka rad dotyczących konwersji o b jy = o b j x . o p e r a t o r Y ( ) ;
już nie w y w o ła b łę d u , bo tu jest jasn e, że chodzi o w y b ra n ie sposobu z funkcją ko n w ertu jącą. P r o b le m 8: ("T o p r z e c i e ż n ie je s t w ie lo z n a c z n o ś ć " )
M am y d w ie k lasy X i Y, a mają o n e w zajem nie z d e fin io w a n e konw ersje tak, że obiekt jed n ej k lasy m o żn a zam ien ić n a drugi.
X Y
Y X
Rys. 18-5.
Czy to b łąd ? N ie, nie jest to błąd, bo nie ma d w u zn aczn o ści Sam się p rzek o n a j. O to d w ie funkcje: f u n _ d la X ( X ) ; fu n d la Y ( Y ) ;
K onw ersje są p o to, by życie u łatw iać, a nie u tru d n iać. N iestety m ożna i tutaj sp ra w ę zag m atw ać. Co robić by ko n w ersje nie p rzy sp o rzy ły kłopotu? T rzeba trzym ać się kilku zasad. K orzystając z ry su n k o w eg o zap isu , p o p raw n y sch em at konw ersji m ożna p rzed sta w ić o g ó ln ie tak:
790
Rozdz. 18. Konwersje K ilka rad dotyczących konwersji
Rys. 18-7.
D □ Q
■*--—**- n
a n ie p o p ra w n y tak: V
Rys. 18-6.
jg ? ' IM
N
% w—— •■ni— ą-f
A k I b r ' v*' *•'?)**zr
0
P _J P ierw szy schem at n azyw am sobie „d o rzecze", bo p rz y p o m in a zasad ę , w edług której pły n ą zw ykle rzeki. D rugi sch em a t m ożna by n azw ać „ek sp lo zją" - sam ch y b a czujesz dlaczego. T e r a z o m ó w m y to w s z c z e g ó ł a c h
N ie p o w in n o sie m nożyć konw ersji p o n ad potrzebę. P rzy sch em acie „eksplozja" — jeśli są trzy p rz e ła d o w a n e funkcje fu n (N ); fun (O) ; fun(P) ;
to m im o tych istniejących konw ersji nie m ożem y w y w o ła ć funkcji f u n dla obiektu klasy M. To dlatego, że w szy stk ie trzy k o n w ersje są jed n ak o w o d o b re do z rea lizo w an ia tego celu. Je d n ak o w o d o b r e - czyli z a c h o d z i w ieloznaczność. N ato m iast p rz y schem acie „d o rz e c z e " żad n e g o ta k ie g o ry zy k a nie m a. O biekt m oże zo stać zam ien io n y na inny tylko w jeden sp o só b .
Jeśli p o trz e b n e będą Ci jeszcze in n e konw ersje dla tej klasy, to n ap isz so b ie funkcję sk ład o w ą zajm ującą się ty m , ale nie daw aj jej n a z w y operator typ{)
Tylko w y b ierz jakąś zw ykłą n a z w ę d la tej fu nkcji. Z w y k le n azw ę przypom inającd na jaki ty p zam ien iam y . D zięki tem u b ęd zie to zw y k ła funkcja sk ład o w a, a n ie o p e ra to r konw ersji. Ty sam b ęd ziesz m ógł p o słu g iw ać się nią d o w oli, n a to m ia s t nie b ęd zie ona b ran a pod u w a g ę w niejaw nych k o n w ersjach robionych a u to m a tycznie p rzez kom pilator. O p erato r konw ersji m ięd zy jed n ą klasą, a d r u g ą pow inien z a m ie niać w stro n ę obiektu o prostszej stru k tu rz e .
791
Rozdział. 18. Konwersje Ćwiczenia
To d la te g o , ż e o p erato r •ten nie- m a sam niczego w ym yślać. Z n_ o w u spójrz na 1 U U W IC L U , 'w * . m . % 11 r~\ i • i « nasz schem at „dorzecze": Konwersja następuje z obiektów klas A, B, C na obiekt k lasy D, k tó ry jest zap ew n e tym , co klasy A, B, C m ają ze sobą w sp ó ln eg o . 0
N a to m ia s t jeśli konw ersja m iałaby następow ać w stro n ę obiektu b ard ziej skom . 9 i. ___ j ^ A c MnH n ik ar n na o b i e k t O / / 4 p lik ó w ,n e g o - czyn n , p rzy k ła d z o b i e k t o s k ła d n ik a c h , to kto m a w ym yślić treść tych n o w y ch 770 sk ład n ik ó w ? O p erato r konwersji? Oczywiście mógłby, ale czy to mądre?
Nie każda konwersja ma sens P a m ię ta sz n aszą klasę o s o b a d o spisyw ania d an y' ch o ludziach? C zy w p ,ad łb y ś ramiętcisz ~ n . . -klasy .b a zam ieniać • i ___ 'roonA lnnaM irzm 7 .PSna to bv obiekt o s o na obiekt k lasy z e s p o l o n a , (liczba zesIId UJ. ------- -' . ___ :_____fo n ie należy ró w n ież na . ' r , p o lo n a). S koro taka zam iana nie m a specjalnego se n su , to me należy ró w n ież na siłę d e fin io w a ć operato ra takiej konw ersji.
*
18.9 Ćwiczenia Miedzy typem A, a typem Z konwersja może być przeprowadzona za pomocą dwóch narzędzi. Jakie to narzędzia i w jakich klasach mają być zdefiniowane? Czemu zapobiega przydomek explicit? Czy konstruktor mający 4 argumenty może być konstruktorem konwertującym? Kiedy jest to możliwe? W klasie Z jest konstruktor domniemany i konstruktor Z: :Z (double). W programie jest też funkcja void f (Z) ; Która z poniższych instrukcji spowoduje niejawne wywo łanie konstruktora konwertującego klasy Z. a)
b) c)
d) e)
fiii
7.
nh-il o b j l = static s t a t i c _ cast(6.6)
obj obj obj obj obj
2 ( 6 .6 ) ; 3 = Z ( 6 .6 ) ; 4 = 6 .6 ; 5 = (Z )6 .6 ; 6;
f(static_cast(6 .6 )) f ( (Z) (6 . 6 + 3)) ; f (6 .6 ) ;
Mamy realizować konwersję z typu zdefiniowanego przez użytkownika do typu w bud owanego. Czy lepiej wybrać sposób z operatorem konwersji, czy konstruktorem kon wertującym? Mamy klasę K. W co należy ją wyposażyć, by możliwa była następująca instrukcja i f ? K obj; if(obj)
cout «
"Jest " «
endl;
Dlaczego można (lub nie) przeładować funkcję konwertującą? Mamy obiekt klasy K, a w niej chcemy zdefiniować operator konwersji. Na które z poniższych typów konwersja będzie niemożliwa.
792
Rozdz. 18. Konwersje Ćwiczenia a)
W
c) d) e)
f>
g) Ti) i)
int int* i n t [5] void* void K K& K* i n t m(K)
Które rozwiązanie konwersji (konstruktor konwertujący czy operator konwersji) wydaje się mieć więcej wad? Potrafisz wymienić kilka tych wad? X
Kiedy przy wywoływaniu funkcji może nastąpić niejawna konwersja? W jakich warunkach przy zwracaniu rezultatu przez funkcję może nastąpić niejawna konwersja? Istnieje sposób zamiany obiektów klasy K na obiekty klasy M. Napisz dw a wyrażenia reprezentujące dwie formy jawnego wykonania takiej konwersji — sposobami „tradycyjnymi". (Jeśli czytałeś o operatorze s t a t i c _ c a s t - napisz też formę z tym operatorem). Mamy dwa warianty zapisu jawnej konwersji (styl: „rzutowanie", styl: „wywołanie funkcji"). Który z nich musimy wybrać, gdy chcemy do konstrukcji obiektu chwilowego użyć więcej niż jednego argumentu? Obiekt typu K chcemy jawnie konwertować na obiekt typu wskaźnik do M (czyli M*). Który rodzaj zapisu jest wtedy niemożliwy?
n) b) c)
(M*)objK M*(objK) s t a t i c cast(objK)
W programie jest klasa X mająca zdefiniowany operator konwersji na typ lon g. W programie jest też funkcja v o i d f f f ( d o u b l e ) . Czy możliwe jest wywołanie tej funkcji z argumentem będącym obiektem typu X ? W klasie Kjest operator konwersji na typ M. W klasie E jest operator konwersji na typ K. Czy można wywołać funkcję v o id f (M) dla obiektu klasy E? W programie jest kilka funkcji o nazwie f (to przeładowanie). Jest też wywołanie funkcji f z argumentem wywołania niepasującym do typu argum entu formalnego. Istnieje jednak konwersja standardowa zamieniająca argum ent wywołania - na typ argum entu formalnego jednej z funkcji o nazwie f. Jest też konwersja zdefiniowana przez użytkownika zamieniająca argument wywo łania funkcji f - na typ argumentu innej funkcji z zestawu przeładowanych funkcji f. Co zrobi kompilator? Czy jest to sytuacja wieloznaczności? Przez nieuwagę w programie są dwie, jednakowe dobre, możliwości zamiany obiektu klasy K na jakiś typ M. Co na to powie kompilator? Wyjaśnij jak zasada „Staraj się by klasa miała, co najwyżej, jeden operator konwersji" est urzeczywistniana w schemacie typu „dorzecze", a jak w schemacie typu „eksplozja .
W
Rozdział. 19. Przeładowanie operatorów
19
793
Przeładowanie operatorów
M
ó w iąc najkrócej w ro zd ziale tym zajm iem y się tym, jak sp raw ić by zn aczk i takie jak +, - itd. pracow ały dla n as i robiły to, co m y im
każem y .
W ielo k ro tn ie próbow ałem Cię przekonać, że ty p y zd efin io w an e p rzez u ż y tk o w n ik a są tak sam o w ła d n e jak ty p y w bu d o w an e. N iestety tak d o tą d nie było. Z a u w a ż p ro sto tę zapisu: int
a = 5, b = 7,
c; c = a + b;
M am y tu trzy obiekty ty p u w b u d o w an eg o i n t . A by dw a z tych ob iek tó w d o d ać, p o słu g u jem y się p o prostu znaczkiem + . Inaczej mów iąc: operatorem +. N iestety , g d y n iedaw no m ieliśm y d o czynienia z ty p em zdefiniow anym p rzez nas, a rep rezen tu jący m liczby zespolone, to d o d a w a n ie takich dw óch obiektów zap isy w a liśm y następująco: zespolona
z (10,
5),
x (7, 0) , w; w = dodaj(z, x) ;
C óż, jeśli p o ró w n ać to z zap isem c= a+ b , to p o ró w n an ie nie w y p ad a na korzyść klas. Z a sta n ó w m y się jednak, jak to jest: przecież sa m zn aczek + nie dodaje d w ó ch liczb. K ied y o n w ystępuje w tekście program u, k o m p ilato r w yw ołuje specjalną
19
794
Rozdz. 19. Przeładowanie operatorów
funkcję, która zajm uje się d o d aw an iem liczb. N az w ijm y tę funkcję operatorem d o d a w a n ia . Jeśli ko m p ilato r p o obu stro n ach zn a c z k a + w id zi liczby ty p u int„ to u ru ch am ia taką funkcję. A rgum entam i są tu te d w ie liczby ty p u int stojące p o o b u stronach znaku +. Ł atw o się też dom yślić, ż e w p rz y p a d k u , g d y obok z n a c z k a + stoją liczby typui
double - kom pilator u ru ch am ia inną w ersję tego o p e ra to ra d o d a w a n ia . To oczyw iste, przecież w m aszy n ie takie liczby są inaczej k o d o w an e, w ięc i dodaje się je inaczej. Z atem istnieje inna w ersja fu n k cji„o p erato r d o d a w a n ia " - d la in n y ch arg u m en tów . Z araz, zaraz - to przecież już zn am y - przecież to p rz e ła d o w a n ie funkcji: m o że być kilka wersji funkcji o tej sam ej n azw ie - byle ró żn iły się arg u m en tam i. A te ra z uw aga - będzie najw ażniejsze: M ożesz sam n ap isać swoją funkcję „ o p e ra to r d o d a w a n ia ", która będzie w y w o ły w an a w tedy, g d y koło z n a c z k a + pojaw ią się arg u m enty w y b ran eg o przez C iebie ty p u . P o p ro stu p o raz kolejny zostanie p rzeład o w an y o p e ra to r + Już w iem , co sobie teraz pom yślałeś: „-Tak, tak,słyszałem, że istnieją podobno ludzie, którzy rozkręcają kompu ter, przelutowują mu tranzystory albo piszą fragmenty systemu operacyj nego. Ja jednak nie jestem jeszcze na tym etapie!" M ylisz się. N ie potrzeba tu żad n ej specjalnej w ied zy . Jest to taka sobie zw ykła funkcja. Co jest w obec tego w niej specjalnego? N ic, p o za nazw ą. Funkcja m usi się n a z y w a ć operator+ i jako jeden z a rg u m e n tó w p rzy jm o w ać obiekt w y b ra nej klasy. operator+(X, Y);
P oza ty m jest to najzw yklejsza w św iecie funkcja, k tó ra n aw et w cale nie m usi d o d a w a ć - m oże po p ro stu tylko zagw izdać. N ajw ażn iejsze jest jednak to, że gdy koło ob iek tu naszej klasy w y stąp i zn a k + , to funkcja zostanie u ru c h o m ia n a au to m aty czn ie argl + arg 2
N ie n ap isałem „u ru ch o m io n a niejaw nie", bo w końcu jest tu w zapisie ten zn ak +, w ięc sp raw a jest jaw na. Funkcję tę m ożna oczyw iście w yw ołać też jak zw y k łą funkcję: operator+(argl, arg 2 )
no ale p o co? Do tego n ie potrzeba n az w y operator+ . To ju ż p rzec ież p rzerab ialiśm y pisząc n ie d a w n o funkcję dodaj.
Pokażmy teraz przykład S koro narzek aliśm y na fu n k cję dodaj p racującą na liczbach zesp o lo n y ch (klasa zespolona) , to p o k a ż e m y teraz, jak d o d a w a ć je za pom ocą o p e ra to ra d o d a w a n ia .
795
Rozdział. 19. Przeładowanie operatorów
N a sz o p e ra to r d o d a w a n ia liczb zespolonych m a ta k ą definicję: zespolona
operator+(zespolona a, zespolona b)
{ zespolona suma; suma.rzeczyw = a.rzeczyw + b.rzeczyw; suma.urojon = a.urojon + b.urojon; return suma;
1 D zięki tak z d efin io w an em u o p erato ro w i d o d a w a n ia , m ożem y teraz w y k o n y w ać d zia ła n ia zespolona
a(l, 0 ), b (6 .3, 7.8),
c; c = a + b;
// «- a u to m a ty c z n ie p r a c u je n a s z o p era to r
T eraz p o z w ó l, że p rzy p o m n ę , jak w yglądała funkcja d o d a j z p o p rz e d n ie g o ro zd ziału zespolona dodaj(zespolona a, zespolona b)
1
zespolona suma; suma.rzeczyw = a.rzeczyw + b.rzeczyw; suma.urojon = a.urojon + b.urojon; return suma;
} C o w id zim y ? - Tak! Te d w ie funkcje są identyczne! Jedyne, co je o d ró żn ia, to nazw a. T e ra z już mi chyba w ierzy sz, że aby p rz e ła d o w a ć o p erato r-t n ie trzeb a rozkręcać k o m p u tera, an i w g ry zać się w jego system operacyjny. Jak już p o w ied ziałem , o p e r a t o r + () w cale nie m u s i służyć d o d o d a w a n ia . R ów nie d o b rz e m ógłby z a g w iz d a ć głośniczkiem k o m p u tera. Tyle, że ten g w iz d rozlegnie się w ted y , gdy w p ro g ram ie obok znaku + z n a jd ą się a rg u m e n ty klasy z e s p o lo n a .
Zatem wiemy już, że można przeładowywać operator-!. Kiedy to zrobić? O czyw iście w sytuacji, g d y w obec obiektu danej klasy bardziej n a tu ra ln e w ydaje się u ży cie znaku + niż w yw ołanie funkcji. Jest to przesłanka, by ro z w a żyć m ożliw ość p rzeład o w an ia. Najczęściej tak się dzieje, wobec o b iek tó w , których k la sy mają jakieś pow inow actw o z m a tem aty k ą. Ale niekoniecznie m ożna p rzecież zastosow ać zap is ekran += okienko
po to, by na ek ran ie pojaw iło się to okienko. P rzeła d o w a n ie o p erato ra nie jest obow iązkow e. M o żn a sobie dobrze ra d z ić nie używ ając go. Jednakże d o b rze obm yślony system p rzeład o w an ia o p erato ró w dla jakiejś klasy spraw ia, że posługiw anie się nią w y d aje się tak n a tu ra ln e ,
4 1
9
796
Rozdz. 19. Przeładowanie operatorów Przeładowanie operatorów - definicja i trochę teorii
jak b y śm y mieli do czynienia z typem w b u d o w a n y m . P rogram jest czytelniejszy •MIMMHinNMMMI i k rótszy.
19.1
Przeładowanie operatorów - definicja i trochę teorii W p o p rzed n im p arag rafie zd o b y liśm y ogólne pojęcie o p rz e ła d o w a n iu . Teraz p o d e jd ź m y do tego precyzyjniej. P rojektując klasę m o żem y zad b ać o to, by p e w n e operacje na o b iek tach tej klasy w y k o n y w a n e były p rz y pom ocy p rzeład o w an y ch o p eratorów .
Przeładowania operatora dokonuje się definiując swoją własną funkcję która: ❖
nazyw a się o p e r a t o r ® - tułaj znaczkiem @oznaczmy sobie symbol operatora, o którego przeładowanie nam chodzi (np. +, *, &, itd.)
❖
jako co najm niej jeden z arg u m e n tó w , p rzy jm u je obiekt d an ej klasy lub referencję do tak ieg o obiektu. (Natomiast nie może być wskaźnik do obiektu).
W su m ie w ięc definicja takiego o p erato ra w y g lą d a tak:
typjaoracany
o p e r a to r @ ( argumenty )
/ / ciało funkcji D o p rzeład o w y w an ia m am y d o dyspozycji b a rd z o w iele o p erato ró w — w ybie ra m y z nich te, które rzeczy w iście będą nam p o trze b n e. N ie m a sensu p rzeład o w y w a ć w szystkich. O to lista operatorów , k tó re m o g ą być p rzeład o w an e: *
/
i
<
>
+=
<< 1
[)
new
1
new []
+—
/=
%=
>>
»=
<< =
==
!»
+4
—
r
delete
I
&&
-=
V
>=
=
-
+
<=
l
i
V
&=
&
d e l e t e [i
Te p ie rw sze d w a o p e ra to ry w ostatniej linijce zesta w ien ia to: •
o p erato r (i - "wywołanie funkcji",
•
o p erato r [ ] - "odniesienie się do elementu tablicy".
O p erato ry m ogą być p rz e ła d o w y w a n e zaró w n o w sw ojej w ersji jed n o - jak i d w u arg u m en to w ej. To znaczy można przeładować zarówno jednoargumentouny operator & ("pobranie adresu"), jak i dwuargumentowy &("iloczyn bitowy").
Natomiast nie mogą być przeładowane operatory... k tórym i w y d ajem y p o lecenia p rep ro ceso ro w i, czyli znaczki # o raz # #,
797
Rozdział. 19. Przeładowanie operatorów Przeładowanie operatorów - definicja i trochę teorii
♦♦♦ n ie m o ż n a t e ż p rz e ła d o w y w a ć operatora siz e o f an i o p erato ró w o d p o w ia d a ją c y c h za rzu to w a n ie w "now ym stylu", czyli operatorów : static_cast,
dynamic_cast,
reinterpret_cast,
const_ca.st;
♦♦♦ n ie m o ż n a też p rz e ła d o w y w a ć m iędzy innym i następujących o p erato ró w
To d lateg o , ż e p rz e ła d o w a n ie polega na nadaniu sy m b o lo w i (np. +) specjalnego znaczen ia, k tó re ma on w m om encie, gdy stoi on o b o k obiektu jakiejś klasy. T ym czasem te ostatn w y m ien io n e operatory już m ają b a rd z o w ażn e zn acze nie w obec o b ie k tó w każdej klasy. N a przykład: - tak się odnosimy do składnika klasy - tak wybieramy składnik wskaźnikiem - t a k określamy zakres
. .+ ::
N ie m o ż em y ty ch b ardzo w ażn y ch znaczeń niszczyć. O statn ieg o o p e ra to ra ? : nie p rzeład o w u jem y , bo - jak tw ierd zą Bjarne S. i M arg aret E. - p o prostu szkoda so b ie tym zaw racać głow y.
Domyślam się, co sobie pomyślałeś patrząc na listę operatorów: No dobrze, możliwe, że przeładuję kiedyś operator + , - , * , / ale bez p r z e s a d y ! N i e b ę d ę p r z e c i e ż p r z e ł a d o w y w a ł o p e r a to r a
( ) lu b [ ] .
Id en ty czn ie p o m y ślałem sobie, g d y uczyłem się języka C++. Tym czasem w łaś nie p ie rw sz e m o je p rzeład o w an ie zm uszony byłem zro b ić w sto su n k u d o op erato ra [ ] Problem b y ł tak i. M iałem d o czynienia z d u żą tablicą. ir.t
t a b l i c a [4096] [4096];
Taka tab lica n ie mieściła m i się w pam ięci operacyjnej. (W kom puterze, w którym ty p int m a rozm iar 4 bajtów , w ym aga ona 64 m egabajty pam ięci). I W obec te g o ta b licę m usiałem trzy m ać zapisaną na d y sk u w postaci jednego lub w ielu p lik ó w . Jeśli potrzebujem y inform acji z d o w o ln eg o elem entu tej tablicy, to trzeba g o o d c z y ta ć z d y sku. To w łaśn ie zrobiłem . N ap isałe m funkcję o p e r a tor [ ) , k tó ra sięg ała na dy sk , szu k ała tam żąd an eg o elem en tu , po czym sp ro w ad zała m i g o d o pamięci. N ie p o k a ż ę tu ta j realizacji tego p rzy k ład u , bo o operacjach z dyskiem ro zm a w iać b ę d z ie m y d o p iero w ro zd ziale o operacjach w ejścia/w y jścia. Tam zilu s truję to p rz y k ła d e m (str. 1136). T utaj w ażna jest tylko idea: oto p rzeład o w an ie op erato ra [ ] po zw o liło mi u ży w ać zapisu duża tablica t; t [7] [4] = t [33] [4] + 5;
< 1
9
798
Rozdz. 19. Przeładowanie operatorów Przeładowanie operatorów - definicja i trochę teorii W ierz m i, to n ap raw d ę rew elacyjne. W tych k la m rach u d ało m i się ukryć w sz y stk ie operacje d y sk o w e o tw ieran ia p lik u , czy tan ia go, zap isy w a n ia do niego. To b a rd z o ułatw iło sp raw ę.
W ró ćm y je d n ak do naszej listy o p erato ró w . O to k ilk a uw ag: P rz e ła d o w a ć m ożna te o p erato ry i tylko te. N ie m o ż n a w ym yślać sw oich. P rz e ła d o w a n ie m oże n ad ać op erato ro w i d o w o ln e znaczenie, nie ma też ograni czeń, co d o w artości zw racan ej p rzez o p erato r (w yjątkiem są o p e ra to ry n e w , n e w [ ] , d e l e t e , d e l e t e [ ] o raz o p erato r ->). N ie m o żn a zm ienić prio ry tetu w y k o n y w a n ia tych op erato ró w . W yrażen ie: a
+
b
*
c
z a w s z e będ zie w y k o n y w an e jako: a
+
(b
*
c)
a n ie jak o : (a
+
b)
*
c
i to niezależn ie od tego, czy o p erato r * za jm u je się m n o żen iem , czy g w izd an iem . N ie m ożna też zm ienić „argumentowości" o p e ra to ró w , czyli tego, czy o p e ra to r jest je d n o -, czy d w u arg u m en to w y . P rzy k ład o w o : o p erato r / (dzielenie) m usi być z a w s z e d w u a rg u m e n to w y , nie z a leżn ie od tego, czym się zajm uje. O bok n ieg o m u szą stać z a w sz e d w a a rg u m e n ty obiektA
/obiektB
n ato m iast n ie są p o p raw n e tak ie w y rażen ia / obiektA obiektA /
P o d o b n ie o p erato r ! (negacja) jest zaw sze je d n o arg u m en to w y . N ie m o żn a w ięc n ap isać w y rażen ia obiektA
! obiektB
d o p u sz c z a ln e jest tylko w y rażen ie typu: ! obiektA
N ie m ożna też zm ienić łączności o p e ra to ró w - czyli teg o , czy o p erato r łączy się z a rg u m e n te m z lewej, czy z p raw ej strony.
799
Rozdział. 19. Przeładowanie operatorów Przeładowanie operatorów - definicja i trochę teorii
D zięki te m u , ż e o p e ra to r = (p rzy p isan ie) jest p ra w o stro n n ie łączny, w yrażenie: a
=
b
=
c
=
d;
o d p o w ia d a w y ra ż e n iu : a
=
(b
=
(c
=
d ) ));
i n iezależn ie jak ten o p erato r p rz eład u jem y taka p ra w o stro n n a łączność n adal b ędzie z ac h o w an a . Jeśli funkcja o p e ra to ro w a jest zd efin io w an a jako zw y k ła (nie-składow a) fu n k cja, to p rzy jm u je tyle a rg u m e n tó w na ilu pracuje o p erato r. Skoro d zielen ie pracuje na 2 a rg u m e n tach klasaA klasaB argl
arql; arg2; /
arg2
to sto so w n a funkcja o p e r a t o r / m a m ieć d w a arg u m en ty ; •
p ierw szy a rg u m e n t jest typu takiego, jak obiekt stojący po lewej stronie o p erato ra /
•
d ru g i a rg u m e n t - ty p u takiego, jak o b iek t stojący po praw ej stronie
operator/(klasaA,
klasaB);
// deklaracja
funkcji: o p e r a t o r /
P rzynajm niej je d en z tych a rg u m e n tó w - nie ma znaczen ia który - m u si być typu zd efin io w an eg o p rzez u ży tk o w n ik a lub być referencją d o takiego typu. Przypominam , że typem zdefiniowanym przez użytkownika jest także typ wyliczeniowy (enum). O czyw iście jest jasne, że arg u m e n ty nie m ogą być do m n iem an e: K iedy u ż y w a sz zapisu argl
/
arg2
to m usisz po o b u stronach sym bolu / te oba arg u m en ty n ap isać - nie m ożesz kazać k o m p ilato ro w i się ich dom yślać. W yjątkiem jest tylko o p e r a t o r () czyli "wywołanie funkcji"-tuta], z oczyw istych p o w o d ó w , kom pilator nam p o zw ala na arg u m en ty d o m n iem an e.
Ten sam o p erato r m ożna p rzeładow ać w ielokrotnie, z tym , że na okoliczność pracy z innym zestaw em arg u m en tó w . To też już znam y: m ożna przecież tw orzyć b ard zo w iele wersji funkcji p rz e ła d o w a n e j-p o d w aru n k iem , że w ersje te będ ą się ró żn iły kolejnością lub ty p em argum entów .
< 1
9
800
Rozdz. 19. Przeładowanie operatorów M oje zabawki W definicji operatora funkcji jest zastrzeżenie o ty m , że p rz y n a jm n ie j jeden z arg u m e n tó w operatora m usi być typu zd efin io w an eg o p rz e z u ż y tk o w n ik a. Z ap y tasz pew nie: ,,-Czu oznacza to, że nie można napisać funkcji operator+, która będzie przyjmowała oba argumenty będące typami wbudowanymi?" operator+(int, double) ;
N ie, nie m o żn a tak p rzeładow ać. P rzeładow anie słu ż y ty lk o do u sp ra w n ie n ia pracy z k lasam i. N ie m o żn a zdefiniow ać funkcji o p erato ro w ej pracującej na sam ych typach w b u d o w an y ch . Jeśli chcesz zapam iętać dlaczego, to pomyśl: sk o ro operacja 2 + 3
jest w y k o n y w a n a p rzez k o m p u ter, to znaczy, że g d zieś ju ż jest o p e ra to r int operator+(int,
int)
N ie m o żesz więc tak p rzeład o w y w ać, bo ten z e sta w a rg u m e n tó w jest ju ż zajęty1. P rzeład o w y w ać więc tylko dla typów w b u d o w a n y ch n ie m ożna. Z drugiej stro n y nie m a czego żałow ać. N ie zam ierzasz ch y b a sam pisać o p erato ró w na d o d a w a n ie liczb całkow itych i n t .
19.2
M oje z a b a w k i P rz ed staw ię teraz klasę, z k tórą w mojej p rak ty c e p rz y c h o d z i m i praco w ać najczęściej. W lab o rato ry jn y ch zasto so w an iach k o m p u teró w b a rd z o często m am y tak ą sytuację, ż e k o m p u ter steru je pom iaram i. R ezu ltatem tak ieg o p om iaru jest zw y k le tablica liczb. W elem en tach tej tablicy liczb m ogą być zan o to w a n e d a n e o ilości w y p ro d u k o w an y ch w jed n o stce czasu now ych kom órek tk a n k i, m o że b y ć to tablica, w k tó rej są z a p isa n e tem p eratu ry poszczególnych d n i w roku. W m oim p rz y p a d k u jest to najczęściej tablica, w k tó rej po szczeg ó ln y ch elem en tach o d n o to w a n e jest w y stąp ien ie k w an tó w p ro m ie n io w a n ia o o kreślonych energiach. (Te energie mierzymy w kiloelektronowoltacli [keV], ale to jest teraz nieistotne).
1)
Trochę to moje tłumaczenie jest naciągane, przepraszam.
801
Rozdział. 19. Przeładowanie operatorów Funkcja operatorow a jako funkcja składowa
Z a sa d a p o m ia ru jest taka: jeśli u k ła d p o m iarow y za re je stro w a ł k w an t p ro m ie n io w an ia o en erg ii 511, to w tablicy elem ent nr 511 z w ię k sz a się o jeden. C zyli jeśli d o tej p o ry była tam w artość 6, to teraz będzie 7. Jeśli w n a sz y m p o m iarze zarejestrow aliśm y 1000 takich w łaśn ie k w an tó w , to w rezu ltacie w tablicy będą zap isan e sa m e zera, a tylko w elem en cie o n u m e rz e 511 zap isa n a b ę d z ie liczba 1000. Te elem enty tablicy zw y czajo w o n azy w a się kanałami Rys. 19-1 ,c
8
Tj
30 000
Rzeczywiste widmo (jądro wanadu)
25 000
O a>> co to (U C O N 'O *(/> .9
a?
15000 10000 5000
—r- -i--i v i. 100
200
300
400
500
600
700
800
900
1000
Energia kwantów promieniowania (kanały)
Jeśli w ięc zarejestrow aliśm y 100 k w an tó w o energii 511 o ra z 200 k w an tó w o energii 350, to zera będą w e w szystkich kanałach z w y jątk iem kanału 511, g d zie będ zie 100 i k an ału 350 - gdzie b ęd zie liczba 200 k a n a ł [511] k a n a ł [350]
200 100
Ten ty p d an y ch pom iarow ych n azy w a się w idm em . Z au w aży łeś, co p o w ie działem ? ... te n ty p .... N ic w ięc d ziw n eg o , że definiuję g o jako klasę class
widmo
( public: int k a n a ł [1024];
> / . .. }; 9
Klasa ta, o p ró cz tego, że zaw iera tablicę, zaw iera też funkcje składow e. N iektórym i z tych funkcji m ogą być p rzeład o w an e o p erato ry . To d lateg o klasą tą posłużę się w przykładach d o tego rozdziału.
19.3
F u n kcja o p e ra to ro w a ja k o fu n k c ja s k ła d o w a Funkcja operato ro w a może być:
802
Rozdz. 19. Przeładowanie operatorów Funkcja operatorow a jako funkcja składowa <♦ zw y k łą funkcją (nie-składow ą), z jakiejś p rzestrz en i n a z w , n a przykład globalnej dla u p r o s z c z e n i a dalszych rozważań mówić będziem y "funkcja nie-składowa " ♦♦♦ a m oże być ta k ż e funkcją sk ład o w ą klasy. Z ałó żm y , że w p rz y p a d k u klasy widmo zac h o d z i konieczność d o d a w a n ia do w sz y stk ich elem en tó w tablicy widmo jakiejś liczby int.
Oto definicja takiej funkcji jako funkcji nie-składowej, (globalnej): widmo
operator+(widmo
dane,
int
liczba)
1 widmo
rezultat;
for(int i = 0; i < 1024 ; i++) rezultat.kanał[i]
=
d a n e .k a n a ł [ i ]
+
liczba;
} return
rezultat;
1 D ziałan ie funkcji jest p ro ste - w jej ciele d efin iu jem y obiekt k lasy w idm o, który p o słu ż y n am do p rzech o w an ia rezultatu operacji. N astęp n ie d o poszczeg ó l n y ch kan ałó w w id m a d a n e dodajem y l i c z b ę . W y nikow e w id m o r e z u l t a t z w ra c a n e jest jako re z u lta t działania funkcji. T e ra z zobaczm y, jak w y g ląd a realizacja teg o sam eg o o p erato ra d o d aw an ia,...
...jeśli zostanie on zdefiniowany jako funkcja składowa klasy widmo
widmo::operator+(int
widmo.
liczba)
{ widmo rezultat; for(int i = 0 ;
i
<
r e z u l L a t .k a n a l [ i ]
1024 “
;
i++)
kanał
[i]
+
liczba;
) return
rezultat;
}
Porównajmy obie te wersje Jak w id zim y , teraz funkcja przyjm uje ty lk o d ru g i arg u m en t. Co się stało z p ie rw szy m ? Skoro funkcja jest teraz funkcją sk ład o w ą klasy widmo, to znaczy, ż e jest w y w o ły w an a n a rzecz jakiegoś obiektu klasy w i d m o - d ostaje zatem w sk a źn ik do tego o b iek tu . Jest to w sk aźn ik this. Pierw szy a rg u m e n t p rzesy łan y jest w ięc d o funkcji dzięki w sk aźn ik o w i this. N ajlepiej to z a u w aży ć w zapisie jaw nego w y w o łan ia tej funkcji. Jeśli m am y o b iek t klasy w idm o: widmo
kobalt;
to w yrażenie: kobalt
+
5
803
Rozdział. 19. Przeładowanie operatorów Funkcja operatorow a jako funkcja składowa jest ró w n o w a ż n e jaw n em u w y w o łan iu operatora d o d aw an ia: kobalt.operatora-(5)
Skoro p rz y p rz e k a z y w a n iu a rg u m e n tó w bierze u d ział w sk a źn ik t h i s , stąd jasne jest, ż e nie m o ż e być to funkcja sk ład o w a typu s t a t i c , bo taka w skaźnika t h i s nie m a. Jak p am iętam y - funkcje s t a t i c pracują nie d la konkretnych eg ze m p la rz y o b iek tó w , lecz dla klasy „w ogólności". P o n iew aż nam chodzi o d o d a n ie liczby d o k o n k retn eg o eg zem p larza obiektu klasy widmo, zatem o funkcji sk ład o w ej ty p u s t a t i c nie m o że być mowy. Dana funkcja operatorowa może być więc albo funkcją nie-składową, albo niestatyczną funkcją składową klasy, dla której pracuje.
O czyw iście: albo—albo! N ie m ożem y zdefiniow ać obu form tej funkcji. Obie form y b o w iem p racu ją na tych sam ych argum entach (widmo,
int)
chociaż w d ru g im p rz y p a d k u w y d aje się to ukryte. To znaczy obie te formy są jednakozoo dobre w sytuacji, gdy w programie po lewej strony znaczka + stoi wyrażenie typu wi dmo, a po prawej typu i n t . (Zatem: dwuznaczność!). Z au w aż, że: Jeśli operator definiujemy jako funkcję składową, to ma ona zawsze o jeden argument mniej niż ta sama funkcja napisana w postaci funkcji nie-składowej. Ten „niewidzialny11, „brakujący" argument —w przypadku funkcji składowej — spowodowany jest oczywiście istnieniem wskaźnika t h i s .
Czas chyba na przykład działającego programu #include using namespace std;
/ »
/ / / »
»
«
»
class widmo public: int k a n a ł [rozmiar];
7 O
/ / -------------------- — ----- konstruktor widmo(int wart = 0 );
/ / --------------------------- — przeładowany operator widmo operatora-(int );
1; + * *J / ********************************************************** // © w i dmo:: widmo (int wart) { for(int i = 0 ; i < rozmiar ; i++) kanał[i] = wart;
)
9
804
Rozdz. 19. Przeładowanie operatorów Funkcja operatorow a jako funkcja składowa r* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /*************<
widmo
w i d m o : : o p e r a t o r + (i n t
liczba)
{ widmo rezultat; f o r ( i n t i = 0 ; i < r o z m i a r ; i++) r e z u l t a t . k a n a ł [i ] = k a n a ł [ i ] + liczba; return
rezultat;
/****************************** int
// Q
★ A -*****************************/
m a i n ()
{ widmo
kobalt(5);
w i d m o nowe; n o w e = k o b a l t + 100; cout << "Przykładowo
nowe cout
, patrzymy
na
na
kanał
<< <<
"Widmo 'kobalt' ma tam : " k o b a l t .k a n a ł [ 4 4 ] "\na w w i d m i e 'nowe' j e s t t a m
«
n o w e . k a n a ł [44]
«
n o w e .k a n a ł [ 4 4 ]
\n
"
endl;
= nowe + 7 0 0 ; <<"A teraz w kanale «
:
44.
// // //
44
<<
obiektu
'nowe'
. jest
// 0 :
endl;
Po wykonaniu go na ekranie zobaczymy Przykładowo
patrzymy
na
na
kanał
44.
W i d m o 'kobalt' ma t a m : 5 a w widmie 'nowe' je s t t a m : 105 A t e r a z w k a n a l e 44 o b i e k t u 'nowe'
jest
805
Komentarz O P rzy p o m in am , że ro zm iar tak definiow anej tablicy m usi być stałą z n a n ą kom pi lato ro w i w trakcie kom pilacji. To dlatego w definicji © jest o b o w iązk o w e słow< const.
© Definicja k o n stru k to ra. Ciało tego k o n stru k to ra to oczyw iście a b s u rd , bo d am b ęd ąc e treścią w id m a p o ch o d zą zw ykle z u k ła d u p o m iaro w eg o . T utaj sy m u lu jem y je w ypełniając całe w id m o tą sam ą w arto ścią. Z au w aż w d eklaracji tcgd k o n stru k to ra, że a rg u m e n t w a r t m oże być d o m n iem an y . O Definicja operatora+ b ęd ąceg o funkcją sk ła d o w ą klasy widmo. Z au w aż , żc z a p is kanał [i] o d p o w ia d a zap iso w i t h i s - > k a n a l [i ] © Definicja obiektu klasy
widmo.
Ten obiekt n a z y w a się
kobalt
i p o stan aw iam y
w y p ełn ić go w artością 5. 0 Definicja in n eg o ob iek tu tej sam ej klasy. Z a u w a ż , ż e teraz z a d z ia ła argum ent d o m n iem an y k o n stru k to ra. O b iek t n o w e zo sta n ie w y p ełn io n y w arto ścią 0. © To je s t n a jp ię k n ie js z e m iejsce tego p ro g ra m u . K o rzy stam y tu b o w ie m 7 w ła ś n ie z d e fin io w a n e g o o p erato ra+ nowe
=
kobalt
+
100;
805
Rozdział. 19. Przeładowanie operatorów Funkcja operatorow a nie musi być przyjacielem klasy
Z ap is ten sp ra w ia , ż e ru sza do pracy n asz op erato r d o d a w a n ia . P iękna jest tu oczyw iście p ro sto ta zap isu . Tę sam ą linijkę m ożna by zap isa ć ró w n ież tak nowe = kobalt.operator+(1 0 0 ) ;
© Ta linijka jest jak b y p o w tó rzen iem p o w y ższeg o . Inny sposób z a p isa n ia tej samej operacji to: nowe = n o w e .operator+(700) ;
M am n ad zieję, że p rzy k ła d ten p rz e k o n a ł Cię, jak łatw o p rzeład o w u je się o p erato r. 1
............. . ii. ii n i - i .................
'- i
..................................... ..................
■■■ ■11
-------- — *------ — —........................ ............................... ..................
19.4
.....................................
F u n k c ja o p e ra to ro w a n ie m u si być p rz y ja c ie le m klasy W iem y już, ż e m am y d w a sposoby definicji tego sam eg o o p e ra to ra . M oże być on •
funkcją składow ą klasy,
•
zw y k łą funkcją n ie-sk ład o w ą (np. globalną).
Funkcja zw y k ła - jest n ap raw d ę zw y k ła; tylko w n azw ie w y stęp u je słow o o p e ra to r. Tu chciałbym zw ró cić uw agę, że ta zw y k ła funkcja nie m usi m ieć też specjal nych u p ra w n ie ń w sto su n k u do klasy. P o d k reślam to d lateg o , ż e niektórzy uzn ają za oczy w isto ść, iż funkcja m usi b y ć zaprzyjaźniona z klasą, na której pracuje. N ie jest tak na szczęście. G dyby tak było - nie m oglibyśm y d o p isa ć operatorów d o klas, k tó re ktoś in n y daw niej napisał (i są na p rzy k ład w bibliotece). Skoro ten ktoś, pisząc klasę, n ie um ieścił tam deklaracji przyjaźni z n aszą funkcją - to p rzep ad ło . Jak m ów ię - n ie jest tak , na szczęście. M o żn a zdefiniow ać o p e ra to ry dla klas już w cześniej n ap isan y c h . (D laczego to takie w a ż n e - w rócim y d o tego p rzy om a w ianiu p rz e ła d o w a n ia operato ra « ) .
Czy przyjaźń jest zatem niepotrzebna ? 9
Jeśli funkcja o p e ra to ro w a - w skrócie m ów ić będziem y: o p erato r - m a pracow ać tylko na p u b liczn y ch składnikach klasy, to m oże być ona zw y k łą funkcją globalną, b ez ż a d n y c h specjalnych u p ra w n ie ń . T ak jestz naszą klasą w idm o. C elow o tablicę k a n a ł uczy n iliśm y ta m publiczną. Jeśli jednak chcem y, by op erato r m ógł p racow ać także na niepublicznych skład n ik ach klasy — w ów czas klasa m u si d ać operatorow i votum zaufania m u si zad e k laro w ać tę funkcję o p erato ro w ą jako zap rzy jaźn io n ą. T ak też d zieje się w w iększości p rz y p a d k ó w . O peratory są:
806
Rozdz. 19. Przeładowanie operatorów O peratory predefiniowane •
- albo funkcjam i składowymi klasy; m ają w ted y d o stęp da sk ład n ik ó w pry w atn y ch swojej klasy,
•
- albo są funkcjam i zaprzyjaźnionymi z klasą; p rz y ja ź ń ta daje im d o stęp d o sk ład n ik ó w p ry w atn y ch .
C h cę jed n ak , abyś w ied ział, że przyjaźń nie je st o b o w iązk o w a. T o przecież o c z y w is te -je ś li o p erato r m a np. zag w izd ać na cześć obiektu danej k lasy , to da te g o nie jest m u p o trze b n y d o stęp do jej p ry w a tn y c h sk ład n ik ó w .
19.5
O p e ra to ry p re d e fin io w a n e Jeśli w obec obiektu d an ej klasy użyjem y o p e ra to ra , k tó reg o nie zdefin io w aliś m y , w ó w czas k o m p ilato r zasygnalizuje błąd. Po p ro stu d la teg o , żc n ie w ie, jak m a w te d y postąpić. To tak, jakbyśm y w y w o ły w a li funkcję, której nic zd efin io w aliśm y .
Jest jednak kilka operatorów, których znaczenie jest tak oczywiste, że zostają one automatycznie generowane dla każdej klasy Te o p e ra to ry to; = p rzy p isan ie [podstaw ienie] d o o b ie k tu danej klasy, &
(jednoargumentowy &) - u zy sk an ie a d re su o b iek tu d an ej klasy,
,
(przecinek) - znaczen ie iden ty czn e jak dla ty p ó w w b u d o w a n y ch ,
new, n e w [ ] , d e l e t e ,d e l e t e [ ] - kreacja i likw idacja o b iek tó w w
zap asie pam ięci. O p e ra to re m =, w jego p red efin io w an ej w ersji, ju ż się p o słu g iw a liśm y w n a szy ch p rzykładach. Instrukcja nowy = kobalt + 5;
była m ożliw a dzięki o p erato ro w i+ , ale tak że d zię k i o p erato ro w i= (p rzy p isa nie). P rzy p isan ie o d b y w a się m etodą „sk ład n ik p o sk ład n ik u . O ty m , że nie z a w sz e n am to m usi o d p o w iad ać , p o ro zm a w iam y n iebaw em . Jeśli chcem y, by o p erato ry w y k o n ały dla nas in n ą akcję n iż p red efin io w an a, w ó w czas m u sim y zd efin io w ać swoją w ersję tego o p e ra to ra , a w ó w czas kom pi la to r u z n a tam tą p red efin io w an ą w ersję za n iebyłą. N ato m ia st nie m a elegan ck ieg o sp o so b u , by zrez y g n o w ać z p re d e fin io w a n e g o zn ac zen ia tych o p e ra to ró w . I Słowem — nie można ich „rozdefiniować tak, by nie znaczyły nic i by kompilator widząc taki operator zastosowany wobec obiektu danej klasy zaprotestował mówiąc, że nie wie, jak się zachować, jedynym sposobem jest napisanie swoich wersji, które albo będą robihj coś sensownego, albo nie będą robiły nic (puste ciało funkcji operatorowej). O czyw iście te au to m aty c zn ie g en ero w an e o p e ra to ry rz a d k o nam p rz e s z k a d z a ją. N ajczęściej sk w ap liw ie się g o d zim y na nie —cza se m tylko zm ieniając zn ac ze nie o p erato ra p rz y p isa n ia = . A le o tym w sw o im czasie.
807
Rozdział. 19. Przeładowanie operatorów Argumentowość operatorów
19.6
A rg u m e n to w o ś ć o p e ra to ró w O p erato ry d ziałają albo na je d n y m argum encie (jak np. negacja), albo na dw óch arg u m en tach (jak np. dzielenie). P o za jednym w yjątkiem n ie m a operatorów , które pracują na więcej niż d w ó ch arg u m en tach . To znaczy: o p e ra to r m nożenia * m a m ieć d w a a rg u m en ty . Jeśli sp róbow alibyśm y go zd efin io w ać jako funkcję z w iększą ilością arg u m en tó w , to kom pilator zaprotestuje. C zyli niem ożliw a jest np. taka d eklaracja widmo
o p e r a t o r * ( w i d m o ,i n t , d o u b l e , c h a r , i n t ,c h a r ) ;
//••
Jednym je d y n y m w yjątkiem , kiedy d o operatora m ożna p rz e sła ć w iększą liczbę arg u m e n tó w jest o p e r a t o r () - [d w a naw iasy okrągłe]. N ic w tym dziw nego, op erato r ten n a z y w a się o p erato rem : „w yw ołanie funkcji". O ile ze znakiem + kojarzą się dwa (i tylko dwa) argumenty stojące po jego obu stronach, o tyle ze znakiem () kojarzy się większa liczba argumentów, które wysyłane są do funkcji. Argumenty te oddzielone są od siebie przecin kami, ale wszystkie są argumentami wywołania funkcji. Dokładniej: „... są argumentami operatora wywołania funkcji". Temu ciekawemu opera torowi poświęcany specjalny paragraf. Teraz p rzy jrz y jm y się tym p o d sta w o w y m grupom o p erató w .
19.7
O p e ra to ry je d n o a rg u m e n to w e O p erato ry te w y stęp u ją zw y k le ja k o przedrostek (prefioc), czyli stoją przed obiektem danej klasy. O to p rz y k ła d y - dla oswojenia p o k azu ję też ich o d p o w ie d nik i za sto so w a n e dla obiektu ty p u i n t : widmo int
wid;
i;
- w id ! w id ++wid ~ w id & w id
- i ! i ++ i ~ i & i
M ogą być też jed n o arg u m en to w e^ o p eratory p rzy ro stk o w e (końców ka) (postfix). Stoją one za obiektem . wid++
i++
Jeśli m am y d a n ą klasę K, to o p e ra to ry jednoargum entow e ty p u p rzed ro stk o w e go pracujące na obiektach klasy K m o żn a zdefiniow ać jako:
2)
Zobaczymy niebawem, że tak naprawdę to te operatory przyrostkowe są zrealizowane jako dwuargumentowe.
808
Rozdz. 19. Przeładowanie operatorów O peratory jednoargum entow e nieskładow ą (zw ykłą) funkcję w y w o ły w a n ą z je d n y m argu m entem (obiektem tej klasy K) o p e r a t o r @ (K) ;
albo •
jako funkcję sk ład o w ą klasy K w y w o ły w a n ą bez żadnycl arg u m e n tó w K : :o p e r a t o r @ ( v o i d ) ;
Oto przykład: D otyczy o n operatora jed n o arg u m en to w eg o . O p e ra to r ten w sto su n k u d( ty p u int oznacza liczbę p rzeciw n ą d o danej. (N ie jest to o d ejm o w an ie - bo t( jest d w u arg u m en to w e ). P rzy p o m n ijm y sobie: int a = 2, b = -15 cout << (-a) ; cout << (-b) ; cout << a;
H U
-2
//
2
15
N a s k u te k d ziałan ia tego o p erato ra w p ierw szy m p rz y p a d k u na ek ran ie zosta' nie w y p is a n a liczba —2, a w d ru g im p rzy p ad k u liczba +15, czyli liczby p rz e c i w n e d o o ry g in aln y ch a i b. W trzecim p rz y p a d k u n a ek ran zo stan ie w ypisana będąca w obiekcie a liczba 2 - na d o w ó d , że treść sam eg o obiektu nie uległa zm ianie. Pytanie: C o taki o p erato r zn aczy w obec naszej k lasy widmo? Jeszcze nic, w szystko zależy od nas —będzie z n ac zy ł to, co so b ie postanow im y, P ro p o n u ję, żeby operator ten w rezultacie zasto so w an ia w o b ec obiektu klasy w i d mo d a w a ł taki obiekt klasy widmo, w którym w e w szy stk ich k an ałach będą liczby p rzeciw n e. D obrze jest też zachow ać taką k o n sek w en tn ą logikę: sk o ro w p rz y p a d k u obiek* tów ty p u int op erato r nie zm ien iał treści sam ego ob iek tu (po operacji zm ien n a a m iała n a d a l w artość 2), to ten sam o p erato r zasto so w a n y w obec w id m a niech także n iczeg o nie zm ienia w sam y m w idm ie. N iech tylko zw ró ci jako w artość inne w id m o , które będzie m iało w artości w e w sz y stk ich k an ałach zam ien io n e na liczby przeciw ne. Jest to d o b ra rad a na przyszłość: Staraj się by operator, który je st nieszkodliwy dla obiektów typu w budow ane go - był także nieszkodliwy dla obiektów Twojej klasy. Siłą rzeczy przenosi się przyzwyczajenia z typów w budowanych na klasy. Łatw iej w tedy zresztą „czytać" takie wyrażenia.
N a p isz m y teraz definicję tego o peratora. M am y d w ie m ożliw ości: m o że być on funkcją sk ła d o w ą klasy w i dmo lub m oże być funkcją sp o za tej klasy.
809
Rozdział. 19. Przeładowanie operatorów O peratory jednoargum entowe
Zróbmy go jako funkcję nieskładową widmo o p e r a to r - ( w id m o z ro d lo ) { widmo r e z u l t a t ; f o r ( i n t i = 0 ; i < ro z m ia r ; i++) { r e z u l t a t . k an ał fi] = - z r o d lo .k a n a ł[ i] ; } re tu rn r e z u lta t; > P rzyjrzyjm y się tej funkcji. Przyjm uje ona jako arg u m en t obiekt klasy widmo. Przesłanie n astę p u je p rzez w artość. W rezultacie w obrębie funkcji pow staje kopia o n a z w ie z r o d l o . W idzim y też, że w funkcji d efin io w an y jest obiekt lokalny o n a z w ie r e z u l t a t . N astęp n e linijki to p rzep isy w an ie jednego widm a do drugiego. Przy okazji zm ienia się z n a k w y rażen iu ( z r o d l o . k a n a ł [ i ] ). W yrażen ie to je s t- ja k w ie m y - typu in t,z a te m stojący przed nim operator (m inus) jest ju ż zw y k ły m operatorem liczby przeciwnej dla ty p ó w w b u d o w a nych. („ N a sz " p racu je tylko w tedy, gdy znak stoi p rzed obiektem klasy widmo). O statnia in stru k cja r e t u r n pow oduje przesłanie (przez w artość) w id m a będąceg o rez u lta te m . Sam obiekt o nazwie r e z u l t a t (p o n iew aż jest a u to m aty czny) p rzesta je istnieć. Funkcję można by napisać o wiele sprytniej, jednak stracilibyśmy na czytelności zapisu, co wydaje się tu istotniejsze. A oto, jak u ż y w a m y tego operatora w program ie (zak ład am , że m am y tu obiekty z n a sz e g o ostatniego przykładu): // . . . no wy = ( - k o b a l t ) ;
H ten nawias nicjest kon icczny
W rezu ltacie w w id m ie nowy znajdzie się widmo, którego w szy stk ie kan ały są liczbam i p rzec iw n y m i do treści kanałów w idm a k o b a l t .
Oto ten sam operator w wersji jako funkcja składowa klasy:
widmo
widmo w idm o: : o p e r a t o r - () { widmo r e z u l t a t ; f o r ( i n t i = 0 ; i < ro zm iar ; i++) { . r e z u l t a t . k a n a ł[il = -k a n a l(i); } return
rezultat;
Porównajmy oba warianty funkcji operatorowej O czyw iście p o p ierw sze - różnica jest w liczbie argum entów . W drugiej w ersji nie m a p rz y s ła n ia żadnych argumentów. Funkcja - jak o sk ład o w a w y w o ły w a n a jest na rzecz obiektu swej klasy widmo, zatem zapis:
4 1
9
810
Rozdz. 19. Przeładowanie operatorów O peratory dwuargum entowe -k o b a lt; oznacza to sam o co k o b a lt.o p e r a to r - ( ) ; W sk aźn ik d o obiektu k o b a l t został przesłany d o w n ętrza funkcji i nazyw a się tam t h i s . To, że ta funkcja sk ład o w a jest funkcją o p erato ro w ą, to nic szczegól nego. Jeśli jest tylko niestatyczna, to m a w skaźnik t h i s . To tłum aczy dlaczego w m iejscu, g d zie p rzedtem b y ł zapis r e z u lta t.k a n a ł[ i] =
-z r o d lo .k a n a ł[ i ] ;
jest teraz r e z u l t a t . k a n a ł [iJ =
-k a n a lii];
N a p ra w d ę jest tam przecież rezultat.kanał[i] =
- (this->kanal[i]);
P o k azaliśm y p rzeład o w an ie o p erato ra je d n o arg u m en to w eg o W szystkie inne je d n o arg u m en to w e o p erato ry p rzed ro stk o w e (stojące p rz e d n a z w ą obiek tu) p rze ła d o w u je się podobnie. Są jed n ak jeszcze je d n o arg u m en to w e o p eratory stojące za n azw ą obiektu (że tak pow iem : o p erato ry "końców kow e", postfix). Są to o p erato ry : postinkrem entacji i p o std ek rem en tacji. Z nimi s p ra w a jest szczególna. O m ó w im y je w stosow nym miejscu (str. 871).
1 9 .8
O p e ra to ry d w u a rg u m e n to w e O p erato ry d w u a rg u m e n to w e m o żem y także p rz e ła d o w y w a ć na d w a sposoby •
albo jako funkcję składow ą n ie staty cz n ą w y w o ły w an ą z jed n y m a rg u m e n te m x .o p e r a t o r @ (y)
•
albo jako fu nkcję nie-składow ą (czyli zw y k łą), w y w o ły w aną z d w o m a a rg u m e n tam i. o p e r a to r @ ( x , y)
Taka funkcja o p erato ro w a zo staje au to m aty czn ie w y w o ła n a , g d y obok zn aczk a d an eg o o p erato ra zn ajd ą się d w a arg u m e n ty o k reślo n eg o p rz e z nas ty p u x @ y Z now u jest: alb o -alb o . N ie m o ż n a dla tego sam eg o z e sta w u a rg u m e n tó w d efi niow ać i tak , i tak.
Rozdział. 19. Przeładowanie operatorów O peratory dwuargumentowe
19.8.1
811
Przykład na przeładow anie operatora dw uargum entow ego Z ałóżm y, że ch cem y p rzeład o w ać o p erato r m nożenia *. D la o d m ian y w eźm y klasę rep reze n tu ją cą tró jw y m iaro w y w ektor. Trzeba się zasta n o w ić, co chcem y, by ten o p e r a t o r * z obiektem klasy we ktorek dla nas robił. Miech, dajm y na to, służy d o m n o ż e n ia w ek to ra p rzez liczbę rzeczyw istą. Jeśli jakiś w ektor m n o ży m y p rz e z 2, to w szystkie jego w sp ó łrzęd n e m ają zo stać podw ojone.
Oto realizacja tego operatora jako zwykłej funkcji (nie-składowej): iinclude
M //7 M 7 ////M ^
class wektorek
public : double x, y, z;
/ / ---------konstruktor
wektorek(double xp = 0 , double yp = 0 , double zp = 0 ) : x (xp) , y (yp) , z (zp)
na
{ I* ciało puste 7 } ; //... inne funkcje składowe
/M //////////////////////////////M
wektorek operator*(wektorek kopia, double liczba ; operat wektorek rezultat; rezultat.x = kopia.x * liczba; rezultat.y = kopia.y * liczba; rezultat.z = kopia.z * liczba; return rezultat;
z*************************************** **********************/
// deklaracja void p o k a z (wektorek www) ; /**************************** ****************** * **************/ int m a i n () { wektorek
a (1 , 1 , 1 ), b (-15, -100, +1),
c; c = a * 6 .6 6 ; p o k a z (c );
// ©
c = b * - 1 .0 ; pokaz(c);
z********************************** ****★ **********************/ void pokaz(wektorek www)
( cout
}
<< " X << " y << ” z
= " << www.x = " << WWW.y = " << www.z << endl;
8
812
Rozdz. 19. Przeładowanie operatorów O peratory dwuargum entowe
Po wykonaniu tego programu na ekranie pojawi się x = 6 .6 6 y = 6.66 z = 6.66 x = 15 y = 100 z = -1
Komentarz O D la skró cen ia zap isu k o n stru k to r nad aje sk ładnikom w a rto śc i początkow e w liście inicjalizacyjnej. © Definicja o p e r a t o r a * , gdy jest on funkcją nie-sk ład o w ą. Jak w id ać, w y w o ły w any jest o n z d w o m a argu m en tam i. © P rzy k ład o w e użycie tego o p erato ra. O p erato r ten (jak m o ż n a zobaczyć z jego definicji) w y w o ły w a n y jest w ted y , g d y obok znaczka * p o lew ej stoi obiekt klasy w e k t o r e k , a p o praw ej obiekt ty p u double.
A oto realizacja tej samej funkcji operatorowej jako funkcji składowej klasy wektorek: wektorek w e k t o r e k ::operator*(double liczba
{
)
wekto r e k rezultat; rezultat.x = x * liczba; rezultat.y = y * liczba; rezultat.z = z * liczba; return rezultat;
1 P o d staw o w ą różnicą jest to, że te ra z ta funkcja o p e ra to ro w a w y w o ły w an a jest z jednym a rg u m e n te m typu double. Jak w iad o m o - in fo rm acja o obiekcie k lasy w e k t o r e k p rzy ch o d z i dzięki w sk aźn ik o w i this. Użycie w p ro g ra m ie operatora w tej w ersji jest id e n ty c z n e , jak p o p rzed n io . Z apis jest te n sam .
19.8.2
Przem ienność Z au w aż, ż e d z ię k i p rzeład o w an iu , m ożem y teraz tw o rz y ć w yrażenia: wektorekA = wektorekB * 11.1;
natom iast o d w ró c e n ie kolejności czy n n ik ó w iloczynu n ie w ch o d zi w grę, czyli zapis: wektorekA = 11.1 * wektorekB;
zostanie p rz e z k o m pilator o d rzu co n y jako błędny. D laczego? D latego, ż e p rzecież zd efin io w aliśm y o p erato r p racu jący n a argum entach (wektorek, double) ale nie z d efin io w aliśm y dla a rg u m e n tó w (double, wektorek)
813
Rozdział. 19. Przeładowanie operatorów O peratory dwuargumentowe To oczyw iście n ie jest to sam o. Jeśli ch cem y , by sto so w an ie z a p isu 6.26
*
wektorekB
było m ożliw e, to m u sim y d o d atk o w o zd efin io w ać o p erato r n a taką okoliczność.
Oto, jak wygląda realizacja tego operatora jako funkcji nie-składowej: wektorek operator*(double liczba, wektorek kopia)
{ wektorek rezultat; rezultat.x = kopia.x * liczba; rezultat.y = kopia.y * liczba; rezultat.z = kopia.z * liczba; return rezultat;
1 A rg u m en ty fo rm aln e są oczyw iście w od w ro tn ej kolejności nato m iast ciało funkcji o p erato ro w ej jest takie sam o. T o zro zu m iałe - ch o d zi n am przecież o to, by ten o p erato r ro b ił to sam o, co ta m ten . Bardziej fachow o: chcem y, b y nasze m n o żen ie by ło p rzem ien n e. A teraz zag ad k a: Jak w y g ląd ałb y ten o p e ra to r jako funkcja sk ład o w a klasy? W łaśnie, to jest p ro b lem . Z au w aży łeś ju ż m oże, że jeśli funkcja o p erato ro w a była funkcją sk ła d o w ą klasy, to n a leżała do tej klasy, d o której należał jej p ierw szy a rg u m e n t. P rzed te m p ie rw sz y m arg u m en tem był wektorek, d ru g im d o u b l e - w ięc ope rato r był funkcją sk ład o w ą klasy wektorek. T eraz p ie rw szy m a rg u m e n tem jest double, a we ktorek jest d ru g im . O p erato r nie m oże być funkcją sk ład o w ą klasy d o u b l e - bo takiej k lasy nie ma. double to ty p w b u d o w a n y . Jeśli jeszcze nie jest to d la Ciebie jasne, to zau w aż, jak w y w o ły w alib y śm y jaw nie funkcję o p erato ro w ą w obu sytuacjach. wektorek krotki(1,1,1), wynik; wynik = krotki.operator*(3.1 6 ) ; < 1
to jest w p o rz ą d k u , ale zapis wynik - 3 .1 6 .operator* (krotki ) ;
I I b łą d !
nie m a sensu. N ie m o żn a w yw ołać funkcji składow ej o p e r a t o r * () na rzecz liczby 3.16 W idzisz w ięc, że w y b ó r jednej z d w ó ch m ożliw ości realizacji funkcji op erato row ej m oże być istotny.
9
814
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny
Funkcja operatorow a, która je st fu n k c ją skład o w ą k l a s y - w ym aga, aby po lewej stro n ie (znaczka) operatora stał obiekt jej klasy, lub referencja do takiego obiektu. O p e ra to r, któ ry je s t z w y k łą fu n k c ją n ie - s k ła d o w ą - nie ma tego ograniczenia. t
ii* rn-ri M t i r r '<
u jm
i'.tB rfiw rLm nwu m m - t M e m m a u n k t c * - * atW rtattM ttM wrw a r **£■*-*»*■ «*<
iwi i wn
i
N ie ch ciałb y m jed n ak , byś z teg o w yciągał zbyt p o c h o p n e w n io sk i - iż wobec tego od d z iś w szystkie p rzeład o w an ia op erato ró w ro b isz za pom ocą funkcji niesk ład o w y ch . I jedna i d r u g a form a ma swoje w a d y i zalety. O tym je d n a k , k tórą kiedy wybrać, p o ro z m a w ia m y w ted y , gdy już p o zn am y w szystkie asp e k ty p rzeład o w y w an ia o p erato ró w .
19.8.3
Choć operatory inne, to nazw ę mają tę samą Ten k ró tk i p a ra g ra f jest dla wtajemniczonych w sp ra w y d zied ziczen ia.
Co p ra w d a , jednoargumentowy operator- (liczba p rzec iw n a) to co innego, niż dwuargumentowy operator- (odejm ow anie), ale jest coś, co je łączy: oczyw iś cie n azw a. O ba n azyw ają się tak sam o: operator-. M ogą z te g o w y n ik n ąć d robne kłopoty. Z ałóżm y, ż e w klasie p o d staw o w ej m asz p rz eład o w an y te n operator- w obu w ersjach: jed n o arg u m en to w ej (liczba przeciw na), o ra z d w u arg u m en to w e j (odejm ow anie). W klasie pochodnej m o żesz oczyw iście z obu tych p rzeład o w ań korzystać - b o o p erato ry się dzied ziczy . Jeśli jed n ak w klasie pochodnej p o stan o w iłeś na n o w o zd e fin io w a ć na p rzy k ład d w u a rg u m e n to w ą w ersję tego o p e ra to ra - to pam iętaj, że: ta n ow a definicja zasło n i nie tylko d w u a rg u m e n to w ą w ersję (odejm ow anie) z k lasy pod staw o w ej. Z asło n i tak że tę je d n o arg u m entow ą! Przecież zasłania się n a z w ę - a s k o r o n a z w a operator- je st ta sam a dla jedno- i d w u a rg u m e n to w e j wersji, w ięc obie zo stan ą zasłonięte. N iesp o d zia n k a taka m oże Cię sp o tk ać w p rz y p a d k u o p e ra to ró w &, *, - , +, bo one m ogą m ieć zaró w n o w ersje je d n o arg u m en to w ą, jak i d w u a rg u m e n to w ą .
19.9
P rzykład z u p e łn ie nie m a te m a ty c z n y Z tego, co d o ty ch czas m ów iliśm y, m o żesz odnosić w ra ż e n ie , ż e p rz e ła d o w a n ie o perato ró w , to coś zw iązan e z obliczen iam i m atem aty czn y m i. M yśląc tak - c z y telnicy, k tó rz y uży w ają k o m p u tera raczej do stero w an ia n iż d o obliczeń - m o g ą
Rozdział. 19. Przeładowanie operatorów Przykład zupełnie nie matem atycz ny
815
w tym m iejscu stracić zain tereso w an ie tym tem atem , jako m ało p rzy d atn y m . D o d atk o w o czytelnicy, którzy z p ew n y ch b a rd z o w ażn y ch p o w o d ó w życio w ych m ają w strę t d o m atem atyki - też m ogliby się w ty m m iejscu zniechęcić. Dla W as to, m oi d ro d z y , p rzy g o to w ałem ten parag raf. Z o b aczy m y tu zastoso w an ie p rz e ła d o w a n ia o p erato ró w d o p racy z ek ran em i pojaw iającym i się na nim okienkam i. C zy li coś, co nie m a n ic w sp ó ln eg o z m a tem a ty k ą.
Najpierw jednak wprowadzenie Jak w iesz, d a w n o m in ęły czasy, g d y p raca k o m p u terem w y g ląd ała tak, jak rozm o w a z kim ś p rz e z dalekopis. O becnie m am y d o czy n ien ia z m onitorem ek ran o w y m , na k tó ry m m oże być ró w n o cześn ie kilka działający ch program ów . K ażdy z nich m a d la siebie p ew n ą część ek ran u - zw an ą o k n em . To tak, jakby p ro g ram m iał sw ój w łasn y m onitor, n ary so w a n y na ek ran ie. N a ek ran ie m oże m y mieć kilka tak ich okien - a jeśli są o n e d u że, to m o g ą na siebie zachodzić zasłaniając się nieco. P raca z tak o p ro g ra m o w a n y m k o m p u terem polega na tym , że najp ierw u ru c h a m iam y w y b ra n e p ro g ram y . K ażdy z n ich pojaw ia się na e k ra n ie w p rzy d zielo nym m u oknie. T ak, jak przyniesione i p o ło ż o n e na b iu rk u ry su n k i. O czyw iście ostatn io p o ło ż o n y ry su n ek m oże zasła n iać p o p rzed n ie. G d y chcem y zw ró cić się d o jed n eg o z p ro g ram ó w , to w sk a zu jem y k o m p u tero w i, że chcem y ro zm a w iać z d an y m o k n em . (M ożem y to zrobić za pom ocą m yszki lu b w ciskając kom binację k law iszy). W ybrane o d p o w ie d n ie okienko m ożliw e, ż e frag m e n tam i p rzy sło n ięte p rzez inne okna - k o m p u te r w yjm uje n am na sa m w ierzch . Tak jak rysunek n a b iu rk u , częściow o p rzy słan ian y p rzez d w a inne. W y jm u jem y go na w ierzch i od tej p o ry to o n p rzy słan ia inne. M ożem y w ó w c zas ro zm aw iać z d a n y m p ro g ram em . Jeśli p rzy jd z ie czas w y d a nia ko m en d in n em u p ro g ram o w i, to p o stęp u jem y p o d o b n ie. M ożem y też zd e c y d o w a ć się u ru ch o m ić jeszcze jakiś n o w y p ro g ra m i w ów czas k o m p u te r d o łączy d o d a tk o w e o k n o d o istniejących na n aszy m ekranie. M ożem y też jakiś z działających p ro g ra m ó w zakończyć, w ó w czas jego okno zniknie. P ro g ram też m oże zak o ń czy ć się sam i w ó w czas o k n o też zniknie (choćby n a w e t b y ło g d z ie ś p o d sp o d em ). Jeśli chcielibyśm y n ap isać taki p ro g ra m o bsługujący ok ien k a na ekranie, to m ożna operacje, o k tó ry ch m ów iłem , zrealizo w ać za pom ocą p rzeład o w an ia o p erato ró w . < 1
O czyw iście, tak czy o w ak , m u sim y m ieć w n aszy m program ie jakieś funkcje dodające o k ien k a, usu w ające je, czy sprow adzające na p ie rw s z y plan. Jednak z a m ia s t w y w o ły w ać je w zw y k ły sposób —m o ż em y posłużyć się sy m b o lam i. Będzie to m ożliw e, jeśli tylko funkcje te będą z re a liz o w a n e jako p rz e ła d o w a n e op eratory. Ł atw o się do m y ślić, ż e w p ro g ram ie w y stą p ią takie obiekty, jak ekran czy okienka. To d o p ra c y z nimi p rz e ła d u je m y o peratory. To zn aczy , że to one będą a rg u m e n ta m i o p erato ró w .
9
816
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny
Oto moje propozycje przeładowania: ekran += oknol
♦> czyli: do ekranu, na którym coś już może jest, dodaj oktiol ekran -= okno 2
«$♦ czyli: usuń z ekranu okno2 ekran != okno3
«$» czyli: wyjmij na pienoszy plan (na sam wierzch) okno3 P o w y ższe o p e ra to ry zaspokajają n asze w szystkie p o trzeb y . Jeśli je d n ak chcielibyśm y, aby m ożliw e było takie sk ła d a n ie operacji ekran = oknol + okno2 + okno3
«$♦ czyli: na pustym ekranie umieść trzy okna l,2f3 co m o żn a zap isać inaczej ekran =
(oknol + okno2) + okno3
to m u sim y m ieć p o pierw sze o p erato r: oknol + okno 2
«$* czyli: stwórz chwilowy pusty ekran i na nim umieść oktia 1 i 2 a po d ru g ie o p erato r: ekran = ekran_chwilowy + okno3
«J» czyli: do istniejącej treści ekranu chwilowego dodaj okno3. Jak w id ać d w a ra z y w ystępuje o p erato r +, ale za p ie rw sz y m raze m a rg u m e n tam i są (o k n o , okno)
a za d ru g im razem ( e k r a n , okno)
Nasz program rysował będzie treść ekranu w trybie alfanumerycznym O kna b ęd ą raczej schem atyczne. To d lateg o , by k ażd y C zy teln ik , niezależnie o d tego z jak im p racu je kom p ilato rem , m ógł ten p ro g ra m sk o m p ilo w ać I uruchom ić. W szystkie w y stęp u jące tu funkcje b ib lito teczn e są z biblioteki stan d ard o w ej. Jak już kilkakrotnie wspomniałem praca w trybie graficznym nie jest objęta standardem, zatem mamy bardzo wiele bardzo różniących się bibliotek graficznych. Nie dałoby się więc napisać graficznego programu dla wszystkich. E krany n a ry so w a n y w trybie alfa n u m ery cz n y m b ęd zie o czy w iście b rzy d szy o d takiego n a ry so w an eg o w trybie graficzn y m . N ie n ależy je d n a k zap o m in ać, że to całe ry so w a n ie jest tylko p retek stem d o w łaściw ego p rz e d m io tu tego p a ra g ra fu
817
Rozdział. 19. Przeładowanie operatorów Przykład zupełnie nie matem atycz ny A co jest p rz e d m io te m tego p arag rafu ? P rzeła d o w a n ie o p e ra to ró w do operacji innych niż m atem aty czn e. #include using namespace std;
,
#include #include __ void zwłoka(int sekund);
//d ekla ra cja f u n k c j i time //s a m a d ekla ra cja
iiiiiiM iiiliiiiiM iW class ekran_alfanum
//V
{ string tresc; char tlo_ekranu; enum { wys_ekranu = 24, szer_ekranu = 6 3 public: ekran alfanum()
{
};
: tło ekranu(' ')
tresc.resize(wys_ekranu * szer_ekranu,
tlo_ekranu);
} //void zamaluj_obszar(int lewy,
int góra, int prawy, int doi, char tlo_okna) ;
/ / ----------------------------------void wyświetl()
{ cout «
tresc «
flush;
) / / ---------------------------------void wymaz_wszystko()
{
zamaluj o b s z a r (0 ,0 , szer_ekranu,
wys_ekranu,
tlo_ekranu) ;
li ii
fu n k c ja sk ła d o w a w p isu ją c a w m iejscu x , y z a d a n y te k st
void napisz(int kolumna, int rząd, string tekst)
{
tresc.replace(szer_ekranu * rząd + kolumna, tekst.length(), tekst);
/W///////////////////////////////////////////M
void ekran_alfanum: :zamaluj_obszar(int lewy, int góra, int prawy, int doi, char tlo_okna) 1
// w y p e łn ie n ie z a d a n e g o fr a g m e n tu ek ra n u "k o lo r e m " tła o kn a int dlugosc = prawy - lewy; for(int y = góra ; y < doi ; y++) tresc.replace (y * szer_ekranu + lewy, dlugosc, dlugosc, tlo_okna);
) / / d la p e w n o ś c i w s ta w ie n ie zn a k ó w przejścia d o n o w e j lin ii e k ra n o w e j II
w o d p o w ie d n ic h m iejsca ch strin g u
for(int i = 0
; i < wys_ekranu ; i++)
{ tresc[(i+ 1 ) * szerekranu - 1 ] =
'\n';
4 ]
9
818
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny
//************+*********************************************^. ekran alfanum monitor; IId e fin ic ja g lo b a ln e g o o b ie k tu 0 / / **-******** ************************************* ************ class okno; H d e k la ra c ja za p o w ia d a ją c a
///////////////////////////////////////////////////////////////////////////^^^
//////////////////////////////////////
//d efin icja k la s y o d p o w ia d a ją c e j za u s ta w ia n ie okien n a ekranie //w z g lę d e m sieb ie , w y jm o w a n ie n ie k tó ry c h n a w ie rzc h Ud.
class wizerunek_ekranu
// ta b lic a w s k a ź n ik ó w
okno * tab_okn( 2 0 ] int ile_okien; public: // konstruktor wizerunek_ekranu() {) void o d ś w i e ż (); void m a z a n i e O ;
fJ
--------------- ----- Up/ rze U I Mła Mdl /oWwI IaOnb e
"I
''
{
ile okien(O) //n a r y s o w a n ie obecnego s ta n u e k r a n u // m a za n ie tre śc i całego ek ra n u o p era toyry
,
| •
I
void operator+= (okno & ref_ok) ; / / d o d a w a n ie okienka wizerunek ekranus operator+(okno& ref_ok) ; //d o d a w a n ie o kien ek void operator-= (okno & ref_ok) ; // u s u w a n ie o kien ka void operator != (okno & ref ok) ; / / w y jm o w a n ie na ~~
H
sa m w ie rzc h
•1 a c e zalznA •• class okno { //g e o m e tria o kn a int x, y, wys, szer; // ko lo r o kien ka char kolor; // o p is o kien ka string tytuł; public: . okno(int xx, int yy, int ss, int ww, char kk, string tt) : x(xx), y(yy), wys(ww), szer(ss), kolor(kk), tytuł(tt) //d o d a n ie o k ie n k a w o p era cji p u lp it = p u lp U + o k ien ko wizerunek_ekranu operator +(okno & m 2 ); void narysuj_sie();
« /////////////////////////»
//d e fin ic je f u n k c j i s k ła d o w y c h k la s y o kn o
Z*********************************************************
void okno::narysuj_sie()
^
( //z a m a lu j k o lo r e m tła monitor.zamaluj_obszar(x,y, x + szer, y + wys, // n a r y s o w a n ie r a m k i - d w ie lin ie p o z i o m e ------string linijk a (szer, '-'); monitor.n a p i s z (x, y, linijka); monitor.napis z (x, y + wys, linijka); //c. d. r y s o w a n ia r a m k i - d w ie lin ie p io n o w e -----for(int i = 0 ; i < wys+ 1 ; i++)
kolor);
®
819
Rozdział. 19. Przeładowanie operatorów Przykład zupełnie nie matem atycz ny monitor.napisz(x, y + i, I") ; monitor.napisz(x + szer, y+i,
r >;
} H n a p is a n ie t y t u ł u o k n a n a śro d k u p ie rw sze j lin ijk i
monitor.napisz(x + (szer - (tytuł.s i z e ())) / 2 , y, tytuł); monitor.wyświetl();
*********************************************************** wizerunek_ekranu okno::operator +(okno & m 2 )
// ©
//d o d a n ie o k ie n e k d o c h w ilo w e g o p u lp itu roboczego wizerunek_ekranu roboczy; roboczy += *this; //d o d a je m y p ie r w s z e (p u lp it += o k n o ) roboczy += m 2 ; //d o d a je m y d r u g ie return roboczy; //z w r a c a m y p u l p i t (p r z e z w a rto ść ) /***********************♦*************************************/ / j d e fin ic je fu n k c ji s k ła d o w y c h k la s y w iz c r u n c k je k r a n u
/*************************************************************/ // d o d a w a n ie o b ta void wizerunek ekranu::operator +=(okno & ref_ok)
u o
( tab_okn[ile_okien++] odśwież();
= &ref_ok;
/************************ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / // u s u w a n ie okna void wizerunek_ekranu::operator -=(okno & ref_okna)
// w
//o d s z u k a n ie w ta b lic y w s k a ź n ik a d o tego okna int i= 0 ; for( ; i < ile_okien ; i++)
{ if(tab_okn[i] == &ref_okna) break; //s p r a w d z a m y ja k z a k o ń c z y ło się p o s z u k iw a n ie if(i == ile_okien) // tz n . ta k ie g o okna n ic m a o b e c n ie n a ekranie H w ie c p o p ro stu n ic n ie r o b im y
else //o b w o d s z u k a n e , to j e u s u w a m y zla b lic y for (int k = i ; k < ile okien ; k++) . ~ //p r z e s u w a n ie p o z o s ta ły c h tab o k n [k] = tab okn[k+l]; ł ile_okien— ;
1 m a zanie(); odśwież();
// n a jp ie r w m u s im y z m a z a ć c a ły p u lp it
z************************************ *w ***********************/ / * w y d o b y c ie na s a m w ie r z c h za d a n e g o okienka */ void wizerunek ekranu::operator != (okno & ref_okna)
// ©
//polega na p o sta w ie n iu g o n a s a m y m k o ń c u ta b lic y //w ty m celu n a jprościej u s u n ą ć j e z lis ty i n a ty c h m ia s t dodać
4 ]
9
820
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny
// c z y l i p u l p i t - = o k n o // c z y l i p u l p i t += o k n o
*this -= ref_okna; *this += ref_okna; odśwież();
/*★*★■****■★★**************’t*•*★★★★** **■*■**••**** void wizerunek_ekranu::odśwież()
//
for(int i = 0 ; i < ile_okien ; i++) { tab okn [i]—> narysuj_sie(); } z****************************************
* * * * * * * * * ************/
void wizerunek_ekranu:zmazanie() ( monitor.wymaz_wszystko(); /*************************** ****+********+ ******+*************/ wizerunek ekranu & , , . // 0 0 wizerunek_ekranu: :operator +(okr.o & ref_okna)
* // d o d a n i e o k n a d o p u l p i t u *this +— rcf_okna; return *this;
// c z y l i p u l p i t // c z y l i r e t u r n
+ = okno p u lp it
/*************************************************************-j
int main() '
w izeru n ek_ekran u
/ / dejinicja obkktu pulpit
p u lp it;
// definicja kilku okienek okno okno
g r a ( 1 5 , 7, 2 1 . 6, k a l k u l a t o r (2, 3, 14,
okno edytor(7,5, 18, 6, •
"Gra 6,
w Chińczyka "Kalkulator
, >;
"Edytor");
// u m i e s z c z e n i e o k ie n n a p u l p i c i e pulpit = gra + kalkulator + edytor; zwloką(1);
//O 0
I/ w y m y ś la m y je s z c z e je d n o okno
okno zegar(4,9,18,6,
pulpit += zegar; zwloką(1); pulpit != kalkulator; zwloką( 1 ) ; pulpit != gra; zwloką( 1 ) ; pulpit -= kalkulator;
"Zegar");
// d o d a n i e // w y j e c i e
g o d o o b e c n e g o p u lp itu
O©
k a lk u la to r a n a s a m w ie r z c h
O Oj
// t e r a z
w y je c ie g r y
// u s u n i e c i e j e d n e g o
z o k ie n
O©
zwloką(1); t****************************************** II D o d a t k o w o z d e f i n i o w a ł e m f u n k c j e z w ł o k a , k t ó r a p o w o d u j e w p r o g r a m i e
s & ± 2 Ł ----------- « ----------------------void zwloką(int sekund)
821
Rozdział. 19. Przeładowanie operatorów Przykład zupełnie nie matem atycz ny
s t a t i c time_t poprzedni_czas = time(NULL); while (time (NCJLL) - poprzedni_czas < sekund);
//c ia ło
p u ste
poprzedni czas = time(NULL);
Oto, co po zakończeniu programu pozostanie na ekranie Edytor I—
Gra
w
C h i ń c z y k a ----
Z e g a r |$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ I
I %%%%%%%%%%I $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ I %%%%%%%%%%I $$$$$$$$$$$$$$$$$$$$I
| %%%«%«%%%%I$$$$$$$$$$$$$$$$$$$$ I %%%%%%%%%%|
Ekran w trakcie pracy będzie się zmieniał, więc zamieszczam kilka schematycznych rysunków z różnych faz Po w y k o n an iu linijki © i p o w ykonaniu linijki O O Rys. 19-2.
Edytor
Edytor
Zegar ■
_-_—.
Po w y k o n an iu linijki O © i po w y k o n an iu 2 następ n y ch tor
Rys. 19-3.
or
or
Gra «
Po w y k o n an iu linijki O © - (patrz ry su n ek niżej) Rys. 19-4.
tor
________
822
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny
Przyjrzyjmy się ciekawszym miejscom tego programu O Abym mógł pisać teksty w wybranych miejscach ekranu, posłużę się klasą ekran alf anum. Podobnie postąpiłem w rozdziale o konstruktorach. Czy pamiętasz jeszcze ten przykład z rysowaniem przyrządów pokłada* wych na panelu pilota? (Zob. str. 662). Tylko więc dla porządku przypomnę, że chwyt polega na tym, że nie piszemy bezpośrednio na ekranie, ale przygotowujemy obiekt klasy string, który swoją treścią pokryje całą powierzchnię ekranu. O dpowiednie teksty umiesz czamy w odpowiednich częściach tego stringu tak, że - po wypisaniu go nn ekranie - teksty znajdą się we właściwych rzędach i kolumnach. Jest tu kilka prostych funkcji składowych. <♦
Konstruktor - przygotowuje obiekt klasy string i wpisuje do niego odpowiednią ilość znaków, które stanowić mają tło ekranu.
♦♦♦ Funkcja składowa zamaluj_ofcszar sprawia, że na prostokątnym fragmencie ekranu (określonym przez współrzędne lewego górnego wierzchołka oraz szerokość i wysokość prostokąta) maluje się odpowie dnie dla danego okienka tło. (Kolorem - lub, jak w naszym przypadku, symbolem znakowym). Funkcja składowa wyświetl - wypisuje na ekranie wcześniej przygo towany string (zawierający obecną treść ekranu). ♦♦♦ Funkcja składowa wymaz wszystko - powoduje, że dotychczasowa treść ekranu zostaje usunięta, bo ekran znowu jest zapełniony tylko „kolorem" swojego tła. Funkcja składowa napisz pozwala wpisać tekst w odpowiednie miejs ce stringu, będącego treścią ekranu. Funkcja ta oblicza, w którym mie scu stringu ten nasz tekst należy umieścić, aby potem na ekranie znalazł się dokładnie w wybranym miejscu.
0 Mając zdefiniowaną klasę ekran alf anum, definiujemy taki obiekt i nazywa my go monitor. O Deklaracja klasy okno. Jak widzimy, składnikami są liczby określające położe nie lewego górnego rogu tego okna na ekranie oraz jego szerokość i wysokość, Okno ma jakiś kolor i tytuł, który je opisze. Spójrz na konstruktor. Umieszcza o n a r g u m e n t y w odpowiednich składnikach i nie robi nic więcej - w szczególności nie rysuje okna na ekranie. Okno się narysuje na ekranie dopiero wtedy, gdy dostanie od kogoś polecenie. Jak? © Składnikiem tej klasy jest, jak widzimy, funkcja składowa n a r y s u j_sie. Jest tn publiczna funkcja składowa, bo wywoła ją ktoś inny. Ciało tej funkcji zawiera wywołania innych funkcji, które sprawiają, że: <♦ fragment ekranu, na którym ma się pojawić to okno, zostaje zamalo wany odpowiednim kolorem (u nas kolor zastąpi jakiś znak alfanume ryczny),
Rozdział. 19. Przeładowanie operatorów Przykład zupełnie nie matem atycz ny
823
♦♦♦ n ary so w an e zo staje ob ram o w an ie o k ie n k a d w o m a k resk am i pozio m ym i o raz z n a k a m i ' I' z których p o w sta n ie o b ram o w an ie z lewej i praw ej, ♦♦♦ ładnie zo stan ie n a p isa n y ty tu ł tego o k ie n k a; (ładnie - b o sym etrycznie na sam ej g ó rze o k n a). A le u w ag a: ♦♦♦ p o n iew aż w y m ie n io n e działania nie o d b y ły się na p ra w d z iw y m ekra nie, tylko d z ia ła ły na stringu p rz e c h o w y w a n y m w obiekcie m o n i t o r teraz w y d a n e jest monitorowi polecenie, żeby ten p rz y g o to w a n y w aśnie m a lu n ek w y sła ł na p raw d ziw y e k ran . © D eklaracja klasy w i z e r u n e k e k r a n u . K lasa ta d b ać będzie, b y o d p o w ied n ie o k n a pojaw iały się na e k ra n ie w o d p o w ied n iej kolejności, to zn aczy n iektóre na w ierzc h u , inne p o d sp o d e m . Ta klasa, to po p ro stu ja k b y lista, na którą w p isu ją się okienka chcące się pokazać na ek ran ie. W id zim y , ż c sk ładnikiem jest 20 elem en to w a tablica w sk aźn ik ó w d o okien. D o d atk o w o jest składnik m ów iący ile okien jest a k tu a ln ie na liście. K lasa ta, aby um ieścić w szy stk ie (zapisane na liście) okna na ek ran ie, p rz e lega p o p ro stu tę tablicę i w y d a je polecenia n a p o tk a n y m oknom , by się narysow ały. W k lasie w id zim y fu n k cję składow ą m a z a n i e - która zleca ch w ilo w e w ym aza nie całej treści e k ra n u . C ie k aw sza jest funkcja o d ś w i e ż - która o d p o w ia d a za o d św ieżen ie w izeru n k u e k ra n u . To o n a n a ry su je w szystko na ek ra n ie w sytuacji, g d y zajd zie jakaś z m ian a. © P a trz y m y więc d o definicji tej funkcji i co w id z im y ? Funkcja sam a niczego nie rysuje. Jej praca po leg a n a tym , że bierze tablicę w sk aźn ik ó w d o okien, patrzy, na co p o k azu je p ie rw sz y w skaźnik, i zleca: „Hej ty, okno, które pokazuje pierwszym wskaźnikiem - proszę się teraz narysować na ekranie!". F o tem to sam o z w sz y stk im i w skaźnikam i zap isa n y m i obecnie w tej tablicy. Z ajm ijm y s i ę te r a z p r z e ła d o w a n y m i o p e ra to ra m i
Operator += © A by m o żliw e było d o d a n ie okienka do bieżącego w izeru n k u ek ran u (w szystko je d n o p u ste g o czy ju ż nie) m am y o p e r a t o r + = . Tutaj w id zim y definicję tego o p e ra to ra . Jego p raca p o leg a na tym, że na k o ń cu tablicy w sk a źn ik ó w dopisuje on w sk a ź n ik (adres) n o w e g o okna. Przy okazji inkrem entuje też licznik. Potem n a stę p u je w y w o łan ie funkcji rysującej na ek ra n ie jeszcze raz w szy stk ie okienka. Jak w id ać, jest to b a rd z o p ro sta funkcja, a m im o to obsługuje zap is.
wizerunek_ekranu += oknol; Jej w y k o rz y sta n ie w id z im y w program ie w m iejscu O ® .
<19
824
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny
Operator-= © N ieco bardziej sk o m p lik o w an a jest realizacja o p e r a t o r a - = zdejm ującego d a n e okienko z e k ra n u . Z asada jednak jest dość czytelna. N a jp ie rw należy o d szu k ać w tablicy w sk aźn ik ó w adres w y b ran eg o okienka i te n ad res stam tąd usun ąć. N ie jest to tru d n e: D o operatora, jako arg u m en t, p rz y s y ła n e jest przecież okienko (przez referencję). Pozw ala n am to poznać a d re s te g o okienka w pam ięci. Ten ad res p o ró w n u jem y z kolejnym i ad resam i z a p isa m i w tablicy. ♦♦♦ Jeśli nie zn ajd ziem y , to znaczy, że tego okienka nie m a te ra z na ekranie wcale. M o żem y w ów czas nic nie robić.
•I* Jeśli n ato m iast w tablicy jest już te n adres, to u s u w a m y go. U su w an ie polega p o p ro stu na tym , że w szy stk ie ad resy na p o zy cjach dalszych przep isu ją się o je d n ą pozycję tablicy w cześniej. Tak, jakby stojący w kolejce lu d z ie nagle z a d e p ta li tego, kto stał jako piąty. Kolejka się zm n iejszy ła, a po piąty m elem encie zo staje tylko m okra p lam a. Jeśli byśm y teraz z aw o łali funkcję odśw ieżającą w i z e r u n e k _ e kranu, to nary suje o n a już o to je d n o o k n o mniej. D latego, że to o k n o -ja k o n ieo b ecn e na liście— nie do stan ie już w ięcej rozkazu, by się n ary so w ało . Z d ru g ie j jed n ak strony o b raz tego okna na p raw d ziw y m ekranie ju ż od d aw n a w id n ieje. Trzeba więc najpierw w y m azać całą treść ekranu i d o p ie ro potem o d św ieży ć, rysując bieżącą sytuację. Jak p ro sto w y k o n u je się to w program ie, w id zim y chociażby w miejscu O © .
Operator != © W yjęcie d o w o ln eg o o k n a „na sam w ierzch" polega na tym , by p r z y odśw ieżaniu zostało o n o n a ry so w a n e jako ostatnie. A by tak się stało, w sk a ź n ik d o tego okna p o w in ien być jako o sta tn i w naszej tablicy. Jak zrobić takie p rzem ieszczen ie? B ardzo p ro sto - w y starczy u su n ąć okno z listy i n aty ch m iast d o d a ć . Poniew aż d o d a w a n ie okien d o p u lp itu odbyw a się zaw sze p rzez d o p is a n ie d o końca tablicy, w ięc tym p ro sty m sposobem zała tw ia m y cały p ro b lem . Z atem ta funkcja o p e r a t o r != (w y w o łan a na rzecz ob iek tu k lasy wizerune k_e kranu) p o w in n a teraz (na rzecz tego sam eg o obiektu, d la którego p racu je) w y w o łać kolejno d w a b ratnie sobie o p erato ry : o p e r a t o r - = i o p e r a t o r + = .j Jak to zrobić? Z au w aż , że funkcja sk ła d o w a (nasz o p e r a t o r ! =) p racuje z a w s z e dla obiektu, na k tó ry p o k azu je jej w sk aźn ik this. (C zyli obiektu: *this). W y starczy więc, że funkcja ta w y w o ła teraz te b ratnie o p e ra to ry ró w n ież dla o b ie k tu * t h i s . Z atem u su n ięcie zrea lizu je ona w y w o łan iem *this -= ref_okno;
a p o w tó rn e d o d a n ie *this += ref okno;
825
Rozdział. 19. Przeładowanie operatorów Przykład zupełnie nie matem atycz ny
To, że zamiast nazwą usuwanego/dodawanego okienka, posługujemy się przezwiskiem (referencją), nie przeszkadza - i tak chodzi o właściwy, oryginalny obiekt. M ając tak zd efin io w an y o p e r a t o r ! = m o żn a b a rd z o elegancko p isać in stru k q e żo n g lu ją c e okienkam i. T aką operację w y jm o w an ia okienka na p ie rw sz y plan w y k o n u je m y w n aszy m p ro g ram ie w m iejscu O © , a po raz d ru g i w linijkach n a stę p n y c h .
Operator + O 0 Z o b ac z na ekranie tę instrukcję. C hodzi tu o to, by w izeru n ek ek ran u stanow iły trz y o k n a w iz e ru n e k _ e k ra n u = o k n o l + o k n o 2 + o k n o 3 ;
P o d o b n y zapis zm ien n y ch p rzy operacjach m atem atycznych nie po zo staw ia z łu d z e ń , ż e d o ty ch czaso w a treść zm iennej w i z e r u n e k e kranu jest niszczona, a w p isu je się d o niej su m a w yszczególnionych trzech składników . Z ró b m y więc tak i u nas: niezależnie, co było na ekranie d o tej p o ry - o d tą d m ają być te trzy o k n a. P ro b lem jest jednak w ty m jak rozw iązać tak ie w ielo k ro tn e su m o w an ie. N ie jest to tru d n e - inaczej m o ż n a to zapisać jako: w ize ru n e k _ _ e k ra n u = ( o k n o l + o k n o 2 ) + o k n o 3 ;
a to p ro p o n u ję rozd zielić na d w ie poniższe instrukcje w iz e ru n e k _ e k ra n u = o k n o 1 + o k n o 2 ;
// operator+ dla arg: (okno, oktw) w iz e ru n e k _ e k ra n u = w iz e ru n e k _ e k ra n u + o k n o 3 ;
// operatora dla arg: (wizerunek_ekranu, ohio) O bok zaznaczyłem , jakie d o d aw an ie tu w y stępuje. C h o d zi tu w ięc, by zrealizo w ać p rzeład o w an ia w ła śn ie takich operatorów . Jest to p ro stsze niż się w y d aje, bo praw ie całą p racę m a m y już zrobioną. W eźm y p ie rw sz y z tych o p erato ró w .
Operator: okno + okno 0 O to je g o realizacja. Jak w id z isz , w ew nątrz tej funkcji operatorow ej d efin iu jemy sobie roboczy obiekt k lasy wizerunek ekranu. (Nie ma żad n ej kolizji z istniejącym już innym takim obiektem klasy wizerunek_ekranu). D o tego ro b o czeg o ekranu zn a n y m już operatorem += do d ajem y najpierw je d n o okno, p o te m drugie. P ierw sze okno p rzesłan e je s tto tej funkcji o p erato ro w ej (składow ej klasy o k n o ) za p o m o cą w skaźnika thi s , w ięc sam o okno, to p o prostu * this. D o d an ie go d o rob o czeg o w izeru n k u ek ran u to:
< 1
9
826
Rozdz. 19. Przeładowanie operatorów Przykład zupełnie nie matematyczny roboczy += *this;
D rugie okno jest p rzy słan e przez p rzezw isk o (referencję), w ięc po prostu tej referencji u ży w am y , a oznacza ona o ry g in aln y obiekt. Stąd w z ią ł się zapis: / / m2 to przezwisko
roboczy += m2;
P oniew aż rezu ltatem działania tego o p erato ra ma być jakiś o b ie k t k lasy wize runek e kranu, w ięc ten w łaśnie roboc z y obiekt klasy wi ze runę k_e kranu zw racam y p rzez w artość.
Operator: wizerunek_ekranu + okno 0O
P rzyjrzyjm y się te ra z realizacji tego o p erato ra+ . Ma on d o d a w a ć wizerunek_ekranu i ok ien k o , a w rezultacie d o stać m am y now ą po stać w izerunku ek ran u - w zbogaconą o to okienko. C zyli ch o d zi o coś takiego: w iz e r u n e k e k r a n u = w iz e ru n e k _ e k ra n u + o k ie n k o
a to p rzecież (tylko logicznie!) to sam o, co w iz e ru n e k e k ra n u += okienko Skoro taką operację + = już potrafim y zrealizo w ać, w ięc o czyw iście to w ykorzy stujem y i w rezu ltacie w funkcji w idzim y: *this += ref okna;
H czyli tuizerunek_ekrmm += przezwisko_oknn
Skoro w szystko w funkcji operator + zro b iliśm y , z a sta n ó w m y się, co m a ona zw rócić jako rezu ltat. Ma to być obiekt k la sy wizerunek ekranu . Który konkretnie? P rzypo m in am , że o p erato rem tym w y k o n u jem y taką operację: w iz e ru n e k _ e k ra n u = w izerunekekranu + okienko Z atem rezu ltatem o p e ra to ra + pow inien b y ć ten w izeru n e k e k ra n u , k tó ry stał p o lew ej stronie zn ac zk a +. Taki o p eran d w naszej funkcji operator+ jest d o stęp n y jako *this. Z atem spraw a jest p ro sta: koło instrukcji return staw ia m y w łaśn ie * t h i s . I tak u p o raliśm y się z dw o m a definicjam i p rzeład o w an eg o o p e ra to ra + na okoliczność d o d aw an ia :
*X» o k n a i okna w izeru n k u ek ra n u i okna. M ając te d w a o p e ra to ry m ożem y stosow ać ju ż ten zap is z w ielo k ro tn y m d o d a w an iem okienek w jed n ej instrukcji.
Po co były n am te w sz y stk ie p rzeład o w an ia o p e ra to ró w ? Po to b y funkcja ma in m ogła w y g ląd ać tak p ro sto , czytelnie i o b razo w o , jak to w id z isz w tekście. N ic więcej, ale czasem to w z g lą d ogrom nie w a ż n y - szczególnie w p rz y p a d k u , g d y p iszem y taką klasę d la in n y ch u ży tk o w n ik ó w . C iu ż y tk o w n ic y łatw iej z a p a m ię tają u ż y w a n ie o p e ra to ró w +=, - = , ! =, +, n iż n a z w y jakichś funkcji.
Rozdział. 19. Przeładowanie operatorów Cztery operatory, które muszą być niestatycznymi funkcjam i składowymi
827
Głowa do góry! W iem , że ten ro zd ział o p rzeład o w an iu o p erato ró w w p ew n y m m om encie m o że C i się w y d ać tru d n y . C hciałbym w obec tego p o w ied zieć cos na p ociesze nie d la tych, którzy nie zro zu m ieją go od razu: ......... ........ ■■ " "~ \ Przeładow anie operatorów je st niczym więcej jak tylko efektownym sposo bem ułatwiającym notację wyrażeń, w których w ystępują obiekty danych Kiss Przeładow anie operatorów nie daje niczego takiego, co me było m ożliwe do tej pory. To tylko notacja się upraszcza.
N ie p rzejm u j się w ięc jeśli sp raw ią Ci jakieś tru d n o śc i najbliższe p arag rafy . Je d n a k z d ru g iej stro n y - często w bibliotekach, k tó ry m i b ęd ziesz się p o słu g i w ał, n a p o tk a sz operacje w y k o n y w a n e w łaśn ie za pom ocą p rz e a o w a n Y^ o p e ra to ró w . D obrze jest w ó w czas rozum ieć ten m echanizm . D latego czyta] dalej ten ro zd ział.
19.10 Cztery operatory, które muszą być niestatycznymi funkcjami składowymi P o w ied zieliśm y , że w p rz y p a d k u funkcji o p erato ro w ej m am y w ybór: a lb o reali zu je m y ją jako funkcję sk ład o w ą danej klasy, alb o jako zw y k łą fu n k q ę me s k ła d o w ą (np. globalną). Je d n a k ż e w p rz y p a d k u czterech o p erato ró w O w y b o ru nie m a. O p erato ry te m u sz ą być n iestaty czn y m i funkcjam i sk ład o w y m i klasy.
. . . . . Dzięki temu przymusowi, kompilator ma gwarancję, że ic i pierwszy ope rand - czyli to, co postawimy po lewej stronie każdego z tych operatorów będzie na pewno l-wartością. O m ó w m y sobie kolejno te o p erato ry .
19.11 O p e ra to r p rz y p is a n ia = D w u a rg u m e n to w y o p e ra to r przy p isan ia K&
K: : o p e r a t o r = (K &);
słu ż y d o p rzy p isan ia je d n e m u obiektow i klasy K, treści d ru g ieg o o b ie k tu tej klasy. C zęsto m ów i się n a tę operację: „podstawienie . Jeśli n ie zdefiniujem y so b ie teg o o p erato ra - k o m p ilato r a u to m aty c zn ie w yge n e ru je swoją w ersję te g o o p erato ra - p olegającą na tym , ze p rzy p isan ie
9
828
Rozdz. 19. Przeładowanie operatorów O perator przypisania = o d b ęd zie się m eto d ą „sk ład n ik po sk ład n ik u "3. W rezultacie ta k ie g o p rzy p isa n ia b ęd ziem y m ieli d w a o b ie k ty o b liź n ia c z o iden ty czn ej treści. Najczęściej rzeczyw iście o to chodzi, ale czasem m oże nam to n ie o d p o w iad ać. Jeśli w k lasie składnikam i są jakieś w skaźniki, lub jeśli k la sa używ a o p erato ra new do rezerw acji m iejsca w zapasie p am ięci, w ów czas m ogą w y n ik n ąć kłopoty. . M ów iliśm y już o analogicznej sytuacji p rz y okazji o m a w ian ia k o n stru k to ra kopiującego, w ięc jeśli nie pam iętasz - ra d z ę zajrzeć do tego ro z d z ia łu (str.697).
Skoro więc mamy akurat do czynienia z klasą, dla której przypisanie „składnik po składniku" nie jest dobre, to co robić? O czyw iście: zad ać so b ie pytanie czy obiekty tej klasy będą p o d d a w a n e przy p i san iu . Jeśli tak - to trze b a napisać swoją w ersję operatora p rz y p isa n ia . W p rzy p ad k u klasy wektorek, którą p o słu g iw aliśm y się n ie d a w n o , przy p i san ie "składnik p o s k ła d n ik u ” jest tym, o co chodzi. Po p ro stu ch cem y tu obiekt ab so lu tn ie id en ty czn y . N ie m usim y w ięc pisać tu swojej w ersji operatora=. N iestety - nie z a w sz e jest tak prosto. W ted y o p erato r p rzy p isan ia zdefiniow ać trzeb a sam em u.
Kiedy to robić? Jeśli sk ład n ik am i klasy są w sk a źn ik i d o tablic lu b o b ie k tó w - to jest p rzesłan k a, że n ajp raw d o p o d o b n iej nie m o ż em y p o leg ać na o p erato rze przy p isan ia g e n e ro w a n y m au to m aty czn ie. T o dało by nam p rz y p isa n ie "składnik p o sk ład n ik u " - czyli a b so lu tn ą id en ty czność - a na to nie m ożem y się zgodzić. O to pow ód: Jeśli obiekt reze rw o w ał sobie operatorem n e w jakąś tablicę, to sk ład n ik iem tego obiektu jest jedynie w sk a źn ik d o tej tablicy. Sam a tablica - nie! Z atem podczas p rz y p isa n ia (m etodą "składnik p o składniku ) d w ó c h obiektów tej klasy, np.: obiekt_a = obiekt_b;
sk o p io w an a zostaje je d y n ie treść tego wskaźnika (czyli ad res tablicy), a nie sam a tablica. W rezultacie, p o takim p rzy p isan iu , są d w a obiekty k o rzy stające z jednej i tej sam ej tablicy. T en obiekt z lewej stro n y o p erato ra= (obiekt_a) o d tą d nie m a swojej tablicy, lecz "pasożytuje" na tablicy tego obiektu, k tó ry stoi po praw ej (obiekt b).
3)
Trzeba by raczej powiedzieć: kompilator będzie próbował wygenerować swoją wersję. Nie zawsze jest to możliwe.
829
Rozdział. 19. Przeładowanie operatorów O perator przypisania = A by nie d o p u ścić d o takiej sytuacji, trz e b a zd efin io w ać o p e ra tor p rzy p isan ia, k tó ry nie przep isu je "na ślepo" treści w sk a ź n i ka, lecz zach o w a się inteligentniej - sk o p iu je treść tablicy.
O co powinien zadbać dobrze napisany operator przypisania w przy padku, gdy w klasie jest wskaźnik do tablicy? ♦♦♦ O czyw iście d o b re obyczaje nakazują, by n ic zeg o nie ru szał w obiek cie, k tó ry został m u d a n y tylko na w zó r d o k o p io w an ia. ♦♦♦ O p e ra to r p o w in ie n się u p ew n ić, czy aby o p e ra to r= nie zo stał u ży ty d o p rzy p isan ia tego sa m e g o obiektu - te m u sam em u . obiekt
=
obiekt;
Takie przypisanie jest oczywiście poprawne, ale operator powinien się odpowiednio w takiej sytuacji zachować. Jeśli zaś, nie m a takiej sytuacji, to... ♦♦♦ O p e ra to r p o w in ie n zlik w id o w ać d o ty c h czaso w ą tablicę. (M oże być przecież n iew łaściw eg o rozm iaru).
♦ O p e ra to r p o w in ie n u tw o rz y ć n ow ą tablicę o d p o w ied n ieg o ro zm ia ru , a n astęp n ie - p a trz ą c na obiekt w zo rco w y - p rzep isać z n ieg o inform acje do tej now ej tablicy.
O czy w iście w obiekcie d an ej k lasy m oże być kilka w sk aźn ik ó w . M ogą o n e p o k a z y w a ć n a tablice w y tw o rz o n e operatorem new , a m ogą p o k azy w ać na o b ie k ty (zu p ełn ie innej klasy) w y tw o rzo n e d la nich o p erato rem new. Z a s a d a p o stę p o w a n ia w trakcie przy p isan ia jest je d n a k p o d obna. M ożna ją u o g ó ln ić tak: W o p e ra to rz e p rzy p isan ia w y ra ź n ie ro zró żn iam y trz y zasad n icze części. •
tę, w której s p ra w d z a się czy to n ie ko p io w an ie na siebie sam ego;
•
tę, w której dotychczasowe wnętrze obiektu likwidujemy,
•
i tę, w której obiekt bud u jem y jeszcze raz, na o b raz i p o d o b ie ń stw o o b iek tu w zorcow ego.
S ta n ie się to w szy stk o jasne, g d y zobaczym y to w p rzy k ła d zie. T ym czasem je d n a p rzestro g a.
Umieć rozróżnić Z a p e w n e zau w aży łeś, ż e sp ra w y , o których m ó w im y , p rzy p o m in ają to, o c z y m m ó w iliśm y p rzy k o n stru k to rze kopiującym . Tak, m asz rację. To d la te g o , ż e w obu p rz y p a d k a c h chodzi o ten sam zn aczek =. N ie jest to jed n ak ta s a m a sytuacja.
9
830
Rozdz. 19. Przeładowanie operatorów O perator przypisania =
Jeśli znaczek ’= ’ występuje w linijce definicji obiektu (inicjalizacja), to do pracy rusza k o n s tru k to r k o p iu ją c y . W każdej innej sytuacji rusza do pracy o p e ra to r p rz y p is a n ia .
R ad zę tę zasad ę zapam iętać. Jeśli zapom nisz, to m oże się z d a rz y ć , że pracując n a d tym frag m en tem p ro g ram u , gdzie zn aczek = m a w ykonać d la C iebie jakieś u słu g i, robić będ ziesz om yłkow o m odyfikacje w o peratorze p rz y p is a n ia , p o d czas g d y w ted y p racu je w łaśnie konstru k to r kopiujący. Lub o d w ro tn ie - zm ie niać b ędziesz k o n stru k to r kopiujący, p o d czas g d y tam pracuje w ła śn ie operator p rzy p isan ia.
J& D
Z apam iętaj: I Jedyną sytuacją, gdy na widok znaczka = rusza do pracy konstruktor kopiujący, jest w ystąpienie tego znaku w linijce definicji obiektu.
Z naczek ten w ted y oznacza inicjalizację, a nie przypisanie. f — Inicjalizacją zajm uje się konstruktor kopiujący, a przypisaniem - operator przypisania.
19.11.1
Przykład na przeładow anie operatora przypisania Te w szystkie sp raw y ilustruje nasz p rzy k ład . #inciude using namespace std; #include
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllilllllllllllllllllllllllllllll class persona
// O
i string * wsk nazwiska; double * tabTica_prob; int ile_prob; enum { max_elem = 1 0 ); public: persona(string n ) ; persona(const persona &wzor) ; -persona(); void info(const string); void dodaj_czas próby(double wart)
// konstruktor // konstruktor kopiujący // destruktor
// operator przypisania persona &
operator=(const persona &wzór);
IllllllllllllllllllllllllllllllllllllllllllillllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllUIIIIIIIIUJJ persona: :persona (: persona:ipersona(string miano)
{ wsk_nazwiska = new string(miano); tablica_prob = new double[max_elem]; ile prób = 0 ;
//
831
Rozdział. 19. Przeładowanie operatorów O perator przypisania =
cout << " (Pracuje konstruktor zwykły dla: << endl;
" << miano << ")"
}
/*************************************************************/ persona::persona(const persona &wzor) //O
{
// proste przepisanie wartości ile_prob = wzór.ile_prob;
// stworzenie obiektu klasy string, wsk_nazwiska = new string;
Ha potem przepisanie zawartości ze wzorca *wsk_nazwiska = * (wzór.wsk_nazwiska) ;
// stworzenie tablicy tablica_prob = new double[max_elem] ;
// przepisanie zawartości z obiektu wzorcowego for(int i = 0 ; i < max_elem ; i++)
{ tablica prob[i] = wzór.tablica probfi];
//------------------------------------------------------------------------cout << " (Pracuje konstruktor kopiujący, " "kopiowanie z obiektu " << *wsk_nazwiska « ")" « endl;
}
/************************************************************ persona::~persona() // © delete (] tablica_prob; delete wsk_nazwiska;
}
/******************************************************* * * * * * * / // © void p e r s o n a ::info (const string txt)
{ cout << << << forfint
" " « txt « " ” *wsk_nazwiska ", czas prób: "; i = 0 ; i < ile_prob ; i++)
{ cout «
tablica_prob(i ] «
" h, ";
} cout << endl;
} z******************************************************* void persona ::dodaj_czas_proby (double wart)
// ©
{
9
if(ile_prob < max_elem)
( tablica_prob[ile_prob++] = wart;
} )
/****★★**★*★★***★*★**★**★*★★*****★★****★★*★★**★★★**★★★★★★***■★ w/ persona & persona::operator=(const persona &wzor) // 00
// ## Część 1) Sprawdzenie czy to nie kopiowanie siebie samego if(&wzor == this) return *this;
//O ©
832
Rozdz. 19. Przeładowanie operatorów O perator przypisania = //O ©
// ## C z ę ś ć 2 ) " D c s tr u k lo r o w a ' delete [] tablica_prob; delete wsk_nazwiska;
// ## C zę ść 3 ) " K o n s tr u k to r o w a (ko n st. k o p iu ją c y )" II IM liii IIII ##il II il li IIIIIIHWt### / /O © //p ro ste p rze p isa n ie w a r to ś c i ile prób = w z ó r .ile_prob; //s tw o r z e n ie o b iektu k la s y s t r m g , , . , ,.
wsk nazwiska = new string; — H a p o te m p rze p isa n te z a w a r to ś c i z e w zo rc a * w s k nazwiska = * (wzór.wsk_nazwiska); //s tw o r z e n ie ta b licy tablica prób = new double[max elem] ; JJ p r z e p is a n ie za w a rto śc i z o b ie k tu w zo rc o w e g o for(int i = 0 ; i < max_elem ; i++) tablica prob[i] = wzór.tablica_prob[i];
)
"
______________ _____________________
cout « " (Pracuje operator= czyli operator przypisania) return *this; U -druga p ie c ze ń n a t y m s a m y m o g n iu / / W ^/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
**********************/
int m a i n () ^
cout << "Definicje 'pianista', i 'skrzypek' \n";
//O 0
persona pianista("Dino" ); persona skrzypek("Xavier");
II kolejne dni prób-------------pianista.dodaj_czas_proby(5.5); skrzypek.dodaj_czas_proby(6 .0 ) ; pianista, doda j_czas_proby (4) ; skrzypek.dodaj_czas_proby(5) ; II k o n ie c p r ó b ----------cout «
"\nDefinicja 'dyrygent':
persona dyrygent = pianista;
\n";
// ( k o n s tr u k to r k o p iu ją c y !)
//O ©
cout << "\nOto tresc w obiektach\n"; pianista.info("Pianista:"); skrzypek.info("Skrzypek:"); dyrygent.info("Dyrygent to:");
//O ©
cout << "\nZabawy z przypisywaniem ---\n"; dyrygent = skrzypek; dyrygent.i nfo("Dyrygent:"); cout << "Zmiana dyrygenta\n"; dyrygent = pianista;
//O ©
833
Rozdział. 19. Przeładowanie operatorów O perator przypisania = dyrygent.info("Dyrygent: " ); cout « "'Kaskadowe' przypisanie dyrygent = skrzypek = pianista;
—
-
\n";
//© ©
// sprawdzamy, co to dało p i a n i s t a . i n f o ( " P i a n i s t a : "); s k r z y p e k . i n f o (" S k r z y p e k : " ) ; d y r y g e n t . i n f o (" D y r y g e n t : " ) ;
Na ekranie, po wykonaniu tego programu, zobaczymy: Definicje (Pracuje (Pracuje
'pianista', konstruktor konstruktor
i 'skrzypek' z w y k ł y dla: Dino) z w y k ł y dla: Xavier)
Definicja (Pracuje
'dyrygent': konstruktor
kopiujący,
Oto tresc w obiektach Pianista: Dino, czas prób:
Skrzypek: Dyrygent
Xavier, to:
Dino,
5.5
czas prób: czas
kopiowanie
h, 4
z
obiektu
h,
6 h, 5 h,
prób:
5.5
h,
4
h,
Z a b a w y z p r z y p i s y w a n i e m ---(Pracuje operator= czyli operator Dyrygent: Xavier, czas prób: 6 h,
przypisania) 5 h,
Zmiana dyrygenta (Pracuje operator=
_ przypisania)
czyli
Dino)
operator
Dyrygent: Dino, czas prób: 5 . 5 h, 4 h, 'Kaskadowe' p r z y p i s a n i e -------------(Pracuje operator= czyli operator przypisania) (Pracuje operator= czyli operator przypisania) Pianista: Skrzypek:
Dino, Dino,
czas prób: czas prób:
5.5 5.5
h, 4 h, 4
h, h,
Dyrygent:
Dino,
czas prób:
5.5
h, 4
h,
Kom entarz O O to definicja klasy p e r s o n a . M a o n a kilka sk ład n ik ó w celow o tak w ybranych, żeby k o n s tru k to r kopiujący g e n e ro w a n y au to m aty czn ie p rz e z kom pilator - nie sp ełn iał o czek iw ań . P am iętasz ?
9
W p rz y p ad k u , g d y sk ład n ik am i klasy są w sk aźn ik i —najczęściej p o trze b n y jest o p e ra to r p rzypisania, k tó ry zdefiniujem y sam i. S pójrzm y n a sk ład n ik i tej klasy p e r s o n a . 0 W sk aźn ik d o obiektu klasy s t r i n g . N azw isk o danej persony, trzy m ać b ęd ziem y bow iem w osobnym obiekcie klasy s t r i n g , stw o rzo n y m o p erato rem n e w , n ato m iast w tym sk ład n ik u w s k _ n a z w i s k a z a p a m ię ta m y ad res tego obiektu klasy s t r i n g .
834
Rozdz. 19. Przeładowanie operatorów O perator przypisania = ❖
© W skaźnik d o tablicy elem entów ty p u double. T ak że i ta tablic a stw o rzo n a zo stan ie w zapasie p am ięci operato rem new, a jej adre< zostanie zap a m ię tan y tutaj, w tym sk ład n ik u tablica_prob. © Składnik ty p u int. Będzie nam słu ż y ł do p am iętan ia ile początko* w ych elem en tó w pow yższej tablicy za w ie ra obecnie se n s o w n ą treść.
«$► © Tak m ożna w y g o d n ie zdefiniow ać stałą (typu enum) o znaczającą iltf w stęp n ie elem en tó w ma m ieć tablica.
Teraz porozmawiajmy o funkcjach składowych © D efinicja ko n stru k to ra k lasy persona. O to lista zad ań , k tóre on w ykonuje: wsk_nazwiska = new string(miano) ;
Jest to stw o rzen ie op erato rem new o b iek tu klasy string. A rg u m en tem k o n stru k to ra tego stringu jest miano. Z atem nie ty lk o tw orzym y string, ale od ra z u nadajem y mu w a rto ść p o czątk o w ą. W stringu tym przechowywać będziemy nazzuisko (imię) osoby. Jeśli przyjrzałeś się funkcji main, to ju ż wiesz, że osoby te będą muzykami, spędzającymi codziennie długie godziny na próbach. tablica_prob = new double[max_elem] ;
<♦ T o instruktcja tw o rząca (operatorem new) tablicę e lem en tó w typu dou ble. Tablica ta m a m ieć max elem elem en tó w .
Tak tworzymy tablicę t a b l i ca_ p ro b ,w k tó re j przechowywać będziemy informację o tym , ile godzin dziennie tnoały kolejne próby odbyte przei danego muzyka. ileprob = 0 ;
<♦ N a d a n ie zw y k łem u składnikow i i l e _ p r o b w artości 0. D o d atk o w o , w ciele k o n stru k to ra w idzisz też in strukcję w y p isu jącą na ekranie inform ację o tym , że p racu je w łaśnie ten k o n stru k to r. To, o czyw iście, w celach p o g lą d o w y ch . D la b a rd z o am bitnych:
Jeśli wziąłeś sobie do serca radę, by —co się da —inicjalizować za pomocy listy inicjalizacyjnej, to poniżej zamieszczam definicję tego konstruktora vi takim przypadku. persona::persona(string nnn)
:
wsk nazwiska(new string(nnn)), tablica prób(new double[max_elem]), ile prob(O)
{
cout << " (Pracuje konstruktor zwykły dla: << n « ")" « endl;
Rozdział. 19. Przeładowanie operatorów O perator przypisania =
835
Jednak w tekście p ro g ram u p o stan o w iłem um ieścić te in stru k cje m im o w szy s tko w ciele k o n stru k to ra, b o d zięk i tem u łatw iej b ę d z ie ilu stro w ać d alsze za g a d n ie n ia . O N aszą k la sę w y p o sa żam y d o d a tk o w o w k o n stru k to r ko p iu jący . Taki k o n stru ktor - jak p am ięta m y - m u si jako jed y n y a rg u m e n t p rzy jm o w a ć referencję obiektu sw ojej klasy. P o n iew aż o b iek t do sk o p io w an ia d o stajem y jako w zó r, w ięc s k ła d a m y obietnicę, że w zo rco w eg o obiektu nie b ęd ziem y zm ieniać. Tą obietnicą je st słow o const p rz y arg u m en cie fo rm aln y m wzór. Treść (ciało) tego k o n stru k to ra b a rd z o p rzy p o m in a treść k o n stru k to ra, k tóry o m ó w iliśm y pow yżej. G dzie trzeb a - tw o rzy się o p e ra to re m new obiekt ty p u string, a tak że tablicę double. P o d staw o w a ró żn icą jest to, że d an e, k tó re m ają się znaleźć w sk ład n ik ach n o w eg o obiektu, o d czy ty w a n e są teraz z w n ętrza ob iek tu p rzy słan e g o nam jako w z ó r d o sk o p io w an ia.
Nie ma problemu z tym, że kopiowane składniki w zo r u sy p r i va te , bo tworzymy przecież obiekt tej samej klasy. © P oniew aż k lasa p e r s o n a d w u k ro tn ie operato rem new reze rw u je o b szary w zapasie p am ięci, (na p o jed y n czy o b iek t klasy string, a tak że na tablicę ty p u double) d la te g o w y p o sa żam y klasę p ersona w d e stru k to r, k tóry obie te rezerw acje o d w o ła. Przypominam, że odwołań ie rezerwacji jednego obiektu robimy operatorem
a odwołanie rezerwacji tablicy obiektów —operatorem d e l e t e []. © Funkcja sk ła d o w a o n azw ie info w y p isu je na ek ran ie inform ację o bieżącej zaw arto ści d a n e g o obiektu k lasy persona. © Funkcja o n a z w ie doda j_czas_proby. Służy po p ro stu d o zap am iętan ia w a r tości (p rzy słan ej jako a rg u m e n t) - w p ierw szy m w o ln y m elem encie tablicy.
Operator przypisania O O Oto, b ęd ąc y p rzed m io tem tego p arag rafu , o p erato r p rzy p isan ia. C zy jest konie czny? Tak! P rzy p o m in am zasad ę: Klasa m ająca konstruktor kopiujący, destruktor, albo operator przypisania = najczęściej w ym aga istnienia ich wszystkich trzech.
Z atem p rzy jrz y jm y się tem u o p erato ro w i przypisania. Po p ie rw sz e jest o n zrealizo w an y jako funkcja sk ład o w a k la sy persona. Jest to, jak w iem y , o bow iązkow e: o p erato r p rzy p isan ia m usi być funkcją składow ą. D ruga b a rd z o w ażn a rzecz: (a jest o n a w ażna, bo d aje rece p tę na p isanie takich o p erato ró w )
< 1
9
836
Rozdz. 19. Przeładowanie operatorów O perator przypisania = Pam iętasz, że przed p rzy k ład em m ów iłem , iż o p e ra to r przypisać nia sk ład a się z trzech zasadniczych części? M ożem y je tu rozpoznać: •
00 - część I, w której sp ra w d z a m y czy to nie kopiowani.tego sam eg o na to sam o ( a = a ) ,
•
O 0 - część II, w której lik w id u jem y stare w n ę trz e obiektu,
•
OO - część III, w której k reu jem y go jeszcze ra z o d now a - tak, by sp ełn ił nasze now e oczekiw ania.
To w łaśn ie w id zim y w n aszy m operatorze.
N a ty m , w łaściw ie m oglibyśm y zakończyć tę rozm ow ę. Jest je d n a k jeszcze coś w yjątkow ego.
Przypisanie „kaskadowe" O ile an i d estru k to r, an i k o n stru k to r nie m o g ły specyfikow ać ty p u rezultatu zw racan eg o , o tyle o p e ra to r przy p isan ia zw raca jakiś rezu ltat. Referencję o biek tu sw ojej klasy. W d ek laracji tego o peratora w id z im y p rzecież persona &
operator=(const persona Swzor);
C h o d zi oczyw iście o ten zap is p e r s o n a &, k tó ry sto i po lew ej stro n ie słow a o p e r a t o r = . Z atem z w raca on referencję o b iek tu klasy p e r s o n a . A k tó reg o to k o n k retn ie obiektu? Tego ju ż z deklaracji n ie w y czytam y, sp ó jrzm y d o ciała funkcji i zo b aczm y , cOj stoi ko ło słow a r e t u r n - tam jest o d p o w ied ź (O © ). return *this;
jest, jak w iem y, w sk a źn ik ie m po k azu jący m na o b iek t k lasy p e r s o n a , na ten k o n k retn y , na k tó reg o rzecz w yw o łan o o p erato r. C zyli ten, k tó ry p o staw ic-j no p o lewej stro n ie z n a k u przypisania: obiekt_a = obiektJb; th is
Rozdział. 19. Przeładowanie operatorów O perator przypisania =
837
To na n ieg o p o k azu je w sk aźn ik t h i s . N asza funkcja o p erato ro w a, instrukcją return, z w ra c a w ięc zaw sze referencję tego o b iek tu , k tó ry stoi na lew o od znaku = Z ap y tasz p ew n ie: . . . - A po co? M usi to zwracać? Przecież całe przypisanie juz odbyło su; w poprzednich instrukcjach o p e r a to r a = i ten obiekt "z lewej ma się całkiem dobrze. Po co zatem, w tej końcowej instrukcji r e t urn, zwracać dodatkowo tę referencję? M asz rację, w sz y stk o jest już zrobione. M ożna by tej referencji w cale nie zw racać. Sw oją pieczeń ju ż upiekliśm y. Czy p am ięta sz jed n ak p o rzek a d ło o pieczeniu d w ó c h pieczeni na jed n y m ogniu? To w łaśn ie tu robim y. O co ch o d zi d o k ład n ie, w y tłu m aczy m y za chw ilę.
Teraz przyjrzyjmy się funkcji m a i n . O © W funkcji m a i n w id zim y definicję dw óch o b iek tó w k lasy persona. Jest to najzw yklejsza definicja, zatem d o pracy p rzy stąp i zw y k ły k o n stru k to r © . By się przek o n ać, sp ó jrz na w y d ru k ek ran u - k o n stru k to ry są w n aszy m p rzy k ła d zie „ g a d a tliw e ", w ięc łatw o zobaczyć, który kiedy pracuje. D alsze, m a ło w a ż n e instrukcje, to w yw ołania funkcji doda j_czas_proby. Są po to, by w tablicach znalazła się jakaś treść. O © Definicja o b iek tu o nazw ie dyr y g e n t na w zó r in n eg o obiektu. N aw et się nie zastanaw iaj: je st to linijka definicji i w niej w y stęp u je z n a k O zn acza to, ze działa tu k o n stru k to r kopiujący (© ). O © Po tych trzech definicjach - w y p isu jem y na ekranie z aw arto ść poszczególnych obiektów .
Korzystamy z naszego operatora przypisania O © W szystkie p o w y ż sz e operacje w funkcji ma i n - to było tylko p rzy p o m n ien ie. W tej linijce w id z im y w spaniałość o p erato ra przypisania. P rzy p o m in am , że p o n iż sze linijki so b ie odpow iadają: dyrygent = skrzypek; d y r y g e n t . operator=(skrzypek) ;
W rezu ltacie tych zapisów , w obiekcie dyrygent o d tej p o ry jest zap isa n e to sam o, co w obiekcie skrzypek, (im ię, ilość prób i ich czas). N a d ow ód - w y p is u jem y to n a ekranie. W róćm y d o tych dw óch form w yw ołania operatora p rzy p isan ia. Szczególnie z drugiej fo rm y zapisu (w yw ołanie jawne) w id zim y w y raźn ie , że o p e ratorzostal tu w y w o ła n y na rzecz obiektu dyrygent. N a d y r ygenta więc po k azu je w e w n ą trz te g o operatora w sk aźn ik this. P am iętasz, ż e m ów iłem , iż funkcja operator= zw raca jak o rezu ltat referencję d o obiektu p o k azy w an eg o p rzez w skaźnik this? Pytanie: C o tu taj robim y z tą referencją? O d p o w ied ź: Nic. N aw et się nią nie zainteresow aliśm y.
< 1
9
838
Rozdz. 19. Przeładowanie operatorów O perator przypisania = W następnych linijkach w idzim y dalsze p rzy p isy w an ia w p o d o b n y m stylu. © O T o jest b ard zo ciekaw a konstrukcja.
Przypomnienie ze świata typów wbudowanych P am iętasz zap ew n e, że w p rzypadku ty p ó w w b u d o w an y ch d o p u sz c z a ln y jes| ta k i „k ask ad o w y " zapis: int a,b; a = b = 7;
W rezu ltacie obie te z m ien n e mają tę sam ą w arto ść - liczbę 7. To s a m o możn# z a p isa ć tak: a = (b = 7) ;
Z a p is ten jest m ożliw y dlatego, że n ajpierw w y k o n y w an e je st p rz y p isa n ie w naw iasie... Pamiętasz zapewne, że wyrażenie przypisania, samo w sobie, ma takżĄ wartość. Jest n ią właśnie wartość będąca przedmiotem przypisania. W naszym przypadku wyrażenie (b=7) jako całość ma także wartość 7. ...a p o tem rezu ltat tego, co w naw iasie staje się p rzed m io te m te g o drugiego p rz y p isa n ia . Z atem d ru g im etapem pracy n a d tą „k ask ad o w ą" in stru k cją jest a = (7) ;
W y g o d n y ten "kaskadow y" zap is p raw d a? S koro więc m o żn a było ta k z typern w b u d o w a n y m ... Z n a sz moją obsesję. T eraz b ęd ę Cię p rz e k o n y w a ł, że Twój w ła sn y ty p nie b ęd zie w cale gorszy.
Jak to zrobić dla obiektów swojej klasy? O tó ż, jeśli chcem y by instrukcja: dyrygent - skrzypek = pianista;
b y ła m ożliw a, to trzeb a b y w yrażenie: (skrzypek = pianista)
czyli skrzypek.operator=(pianista)
n ie tylko w y k o n y w a ło p rzy p isan ie, ale jeszcze d o d a tk o w o m iało s a m o w sobio w arto ść. W artość b ęd ąc ą p rzed m io tem tego p rzy p isan ia. Jak to zrobić? To proste! - ta funkcja opera t o r = m u si d o d a tk o w o zwracać jakił rezu ltat. Taki m ian o w icie, który nadaje się d o p rzy p isan ia następnem u obiekto w i. dyrygent = (skrzypek.operator= (pianista)
);
S k o ro po praw ej stro n ie zn a k u = m a u nas stać o b iek t klasy p e r s o n a (ew en tu aln ie referencja tak ieg o ob iek tu ) więc m u sim y to w łaśn ie u czy n ić rezultatem
839
Rozdział. 19. Przeładowanie operatorów O perator przypisania =
funkcji o p e r a t o r = . Dla o szczę d n o ści czasu d e c y d u je m y się zw racać referen cję. T en z w ro t obiektu, lu b jego referencji, jest w łaśn ie tajem nicą instrukcji return *this;
// O ©
w o p erato z e p rzy p isan ia. To za ła tw ia spraw ę. N ie tylko d o k o n a liśm y przypisania, ale d o d a tk o w o to w y rażen ie p rzy p isu jące m a w arto ść, którą m ożem y p rz e d sta w ić n astęp n em u członow i „ k a sk a d y 7'. Jak p o w ie d z ia łe m - nie jest to ko n ieczn e - bo p ro ste (jed n o k ro tn e) p rzy p isan ia m ożliw e są i b e z tego. Z d ru g iej stro n y , jeśli tak m ały m kosztem m o żem y u p iec tę d ru g ą p ieczeń , to czem u z teg o rezygnow ać? Jeśli je d n a k n a początek w y d a je Ci się to zaw iłe, w ó w czas sw ój o p erato r p rz y p isa n ia zd efin iu j jako: void
persona::operator=(const persona swzor);
- czyli nie zw racający niczego ( v o id ) . O czyw iście w ciele o p erato ra u su w a sz w ów czas instrukcję: return *this;
P ro g ram w ó w c zas b ęd zie d ziałał tak sam o dobrze. To ty lk o linijka 0 © b ęd zie niem o żliw a. M ożesz ją jed n ak z a stą p ić złożeniem tak ich dw óch: skrzypek = pianista; dyrygent = skrzypek;
Po co i jak zabezpieczamy się przed przypisaniem a = a M oże się z d a rz y ć , że ktoś, p o słu g u ją c się operato rem p rz y p isa n ia , n ap isze taką instrukcję skrzypek = skrzypek;
Jest to p rz y p isa n ie , k tóre logicznie nie ma specjalnego sen su , ale g ram aty czn ie takie w y w o ła n ie o p erato ra= jest p o p raw n e. M oże je d n a k sp raw ić kło p o ty - b o przecież string z n azw isk iem i tablica d o u b l e zo stają na chw ilę sk aso w a n e i stw o rzo n e od n o w a p o to, by d o nich w p isać inform acje z ich o d p o w ie d n ik ó w w obiekcie w zo rco w y m . Skoro jed n ak teraz w zo rco w y o b iek t jest tym sam y m , co p rzypisyw any ( s k r z y p e k = s k r z y p e k ) to znaczy , ż e chcielibyśm y o d czy ty w a ć informację z w łaśn ie skasow anej tablicy (lub właśnie sk aso w a n eg o s t r i n g u ) . Informacji ju ż nie m a. P rzepadła. Jak się p rz e d tym zabezpieczyć? W ystarczy, jeśli n a sz o p erato r p rzy p isan ia zaczn ie się o d sp raw d zen ia, czy ab y nie p rzy p isu jem y tego sam ego, tem u sam em u . Jeśli tak, to niech nic n ie robi, jedynie zw ró ci referencję do obiektu z lewej s tro n y persona s persona::operator=(const persona swzor) if (this == swzor)return *this // p o z o s ta łe in stru k c je , ja k p o p r z e d n io
840
Rozdz. 19. Przeładowanie operatorów O perator przypisania =
II... ) Po p ro stu w takiej sytuaq'i - operator ten nie robi nic. N a w sz e lk i w ypadek zw raca jed n ak jak ąś w arto ść - bo przecież ktoś m oże uży ć takiej konstrukcji vv "kaskadzie". d y ry g e n t =
(skrzypek
=
skrzypek);
N a koniec jeszcze je d n a uw aga. P rzeład o w an y o p erato r p rz y p is a n ia jest - jak w iem y - zaw sze funkcją składow ą jakiejś klasy. O znacza to, ż e p o jego lewe| stro n ie m u si stać o b ie k t jakiejś klasy - bo to on zo stan ie p rz e s ła n y za pomocą w sk aźn ik a t h i s . T o na jego rzecz w y w o ły w a n y jest ten o p erato r. W ynika z tego w a ż n a konsekw encja: niem ożliw y jest za p is instrukcji w stylu 6 = o b ie k t; Tylko mi nie m ów , ż e Ci żal.
19.11.2
Jak konieczność istnienia operatora przypisania opow ied zieć potocznie? N iniejszy p arag raf p rzez n aczo n y jest dla najm ło d szy ch czy teln ik ó w . Jeśli jesteś starszy , to z d ec y d o w an ie opuść go, bo w y d a Ci się in fan ty ln y .
B ardzo d u ż o p o w ied ziałe m na tem at konieczności istnienia o p e ra to ra p rzy p isa nia. Tak d u ż o i trochę form alnie, że boję się czy n ajm ło d szy ch czy teln ik ó w to nic zn u d ziło . Jeśli takie w łaśn ie ogarnęło Cię, d ro g i czy teln ik u , u czu cie, to zatrzy m ajm y się na chw ilę i sp ó jrzm y z d y stan sem n a tę sp raw ę. O tóż d o w ied zieliśm y się w łaśnie, że k o m p ilato r w stosunku d o o b iek tó w k ażd ej naszej klasy - stara się nam zrobić p rezen t. C hce m ianow icie p o d a ro w a ć n am o p e ra to r p rzy p isan ia pozw alający na p rz y p isy w a n ie obiektów naszej klasy. P rzy p isy w an ie - m ó w iąc inaczej - polega na tym , że m o ż em y o d w ó ch obiek tach naszej klasy zad ecy d o w ać: „N iech ten o b iek t b ę d z ie o d tej p o ry tak i, jak tam ten ". W rezu ltacie tak w y p o w ied zia n eg o ży czen ia m a m y teraz dw a ab so lu tn ie id en ty czn e obiekty. Najczęściej jest to w ła śn ie to, o co nam ch o d zi - czyli b a rd z o cieszy m y się z tego o p erato ra, bo m o żem y w sto su n k u d o o b ie k tó w naszej k lasy u ż y w a ć zn aczk a =»j bez najm niejszego w y siłk u definiow ania go. Bez żad n e j p ra c y m o żem y łatw o spraw ić, że d o w o ln e o b iek ty danej klasy stają się a b so lu tn ie id en ty czn e. Jeśli jesteś p u ry stą języ k o w y m , to p ew n ie się teraz z d e n e rw o w a łe ś - co to znaczy „ab so lu tn ie id en ty czn e"? Po co to sło w o „ a b so lu tn ie"? Id en ty c zn e coś albo jest, albo nie jest - i nie potrzeba d o d a w a ć żad n y c h p rzy m io tn ik ó w . M asz
Rozdział. 19. Przeładowanie operatorów O perator przypisania =
841
rację. D o d ając słow o „ a b so lu tn ie " chciałem byś o d czu ł, że chodzi o id e n ty c z ność aż d o bólu! N a czy m m a polegać ó w „b ó l"? N a pew nych s k u tk a c h ubocznych, k tó re m ogą n as zask o czy ć w p rz y p a d k u p racy z niektórym i k lasam i. O czyw iście m ó w iliś m y już o ty m , w ięc teraz p o k a ż ę Ci jak takie sk u tk i zask o czy ły by Cię w życiu n ie -c o d z ie n n y m .
Historia jeszcze gorsza niż króla Midasa W y o b raź sobie, że p ew n eg o d n ia w zdychasz: „Ech, chciałbym być taki jak... ". T u, w m iejsce kropek w sta w sobie nazw isko T w ojego ak tu aln eg o idola m u z y k i ro zry w k o w ej. W estchnąłeś tak sobie, a złe m oce p o d słu ch ały i spełniły n aty ch m ia st T w o je życzenie. Ż y czen ie, k tóre m iało Cię u szczęśliw ić, ale n ap raw d ę... P a trz y sz d o lustra i oczom nie w ierzy sz - z a m ia st sw oich piegów w id zisz szlac h etn ie piękną tw arz, id e n ty czn ą jak u T w ojego idola. Taka jest teraz n a p ra w d ę T w oja tw arz. K rzy k n ąłeś z e zd u m ien ia - i u sły szałeś głos, k tó ry jest id e n ty c z n y jak jego głos. N ie m o żesz uw ierzyć, za c z y n a sz śpiew ać - śp ie w a sz głosem id e n ty czn y m jak on. I tak sam o św ietnie.
„To fantastyczne" - myślisz sobie - „jakiś kompilator wygenerował mi operator przypisania dla klasy człowiek. Mogę teraz po obu stronach znaku równości napisać ja = moj_idol;
- wreszcie koniec z bezbarwną egzystencją. Świat leży u moich stóp!" N ie p rzew id z iałe ś jed n ak efek tó w ubocznych. Id zie sz d o d ru g ieg o p o k o ju , a ta m sied zi tw ój idol. W p a d a sz w bezgraniczny za c h w y t, a on bezcerem o n ialn ie pyta, co ro b isz w jego d o m u . Ty jednak tw ierd zisz, ż e to Twój d o m - zresztą jed y n y jaki m asz. W y w iąz u je się sp rzeczk a w trakcie, której on w y ciąg a z kieszeni... (nie, nie rew o lw er - skończ z o g lą d a n ie m tych głupich k aset) - w ięc on w yciąga z k iesze n i ak t w łasn o ści tego d o m u , ab y Ci u d o w o d n ić , ż e to jego dom . T y tak że w y ciąg a sz z kieszeni a k t w łasn o ści loskazujący, że jesteś w łaścicielem tego sam e go dom u. P o stan aw iac ie jechać sam o ch o d e m do n o tariu sza. O n w yciąga z kieszen i d o w ó d rejestracyjny wskazujący, ż e sam ochód stojący w g a ra ż u , to jego sam o ch ó d ; Ty n a to m ia st (choć n ig d y d o tą d nie m iałeś g a ra ż u ) m asz taki sam d o w ó d wskazujący, że jesteś w łaścicielem tego sam o ch o d u (i g a ra ż u też). C zyje p a p ie ry są p raw d ziw e? Je d n e i drugie, alb o w iem ten, kto nas w te ta ra p a ty w p ęd ził, p o sta ra ł się, ab y d o k u m e n ty były ab so lu tn ie identyczne. Jadąc na policję m yślim y, że historyjka ta p rz y p o m in a nam starogrecki m it o królu M idasie, który g o rzk o po żało w ał sw ego ży czen ia, by w szystko, czego dotkn ie, zam ien iało się w złoto.
< 1
9
842
Rozdz. 19. Przeładowanie operatorów O perator przypisania =
Zastanówmy się, kiedy popełniony został błąd, który w rezultacie wprowa dził nas w tarapaty P ow iedzieliśm y, ż e chcem y być id en ty czn i jak n asz idol - i sta liśm y się tacy,: M am y taką sam ą tw a rz - ale jest to n asza tw arz, on ma sw o ją. W ielokrotnie po d ziw ialiśm y w telew izji jego harm onijny m u sk u larn y tors - i oto m a m y takie sa m e w sp an iałe m u sk u ły i obaj w y g ląd am y jak greccy b o g o w ie. Z tym, że k a ż d y z nas m a sw ój tors, swoje dłonie - i żad n e g o konfliktu nie m a . D laczego zatem każdy z nas nie m a sw ojego (ch o ć identycznego) d o m u i sw ojego (choć iden ty czn eg o ) sam o ch o d u ? D latego, że ich nie nosi się przy sobie, w ięc o n e nie są sk ład n ik iem o b iek tu klasy człow iek. (M yślę o człow ieku w sensie nie tylko an ato m iczn y m , ale ta k ż e p raw nym ). Takim sk ład n ik iem jest jednak d o w ó d rejestracyjny - czy li w sk aźn ik , że d a n y sam ochód jest jego własnością. W szystkie takie w sk aźn ik i zostały sk o p io w an e - stąd m a m y też identyczne d o k u m e n ty w łasności dom u. To w tym m iejscu zaczęły się kło p o ty : zły duch z am iast sk o p io w ać Ci sam ochód sk o p io w ał d o w ó d rejestracyjny. Bo tylko ten d o w ó d człow iek m o że nosić p rzy sobie.^ P o d o b n e kłopoty p o w stały przy p raw ie w łasności jego d o m u , k tó ry m odtąd m usicie się dzielić. N ie będę ciągnął dalej tej historii, bo w o lę n ie myśleć o kłopotach zw iązan y ch ze skopiow aniem a k tu zaw arcia m a łżeń stw a z p an ią X. j
W W idzisz więc, że są sytuacje, kiedy o trz y m a n y od k o m p ilato ra o p e ra to r przy p i san ia m oże nam p rz y sp o rzy ć kłopotów . T ak się m oże zd arzy ć, gdy sk ład n ik am i obiektu danej k lasy są w skaźniki. Szczególnie, g d y p o k azu ją o n e n a coś, co ma być w yłączną w łasn o ścią dan eg o obiektu. G dy p rz e w id u je m y , ż e będziem y p rzy p isy w ać o b iek ty takiej klasy, to lepiej o trzy m an eg o w p re z e n c ie operatora p rzy p isan ia nie p rzy jąć i zdefiniow ać sw o ją w ersję. O ty m , jak to się robi, trak to w ał p o p rz e d n i paragraf.
19.11.3
K iedy operator przypisania n ie jest generow any autom atycznie O p erato r p rz y p isa n ia m oże, jako arg u m e n t, p rzyjm ow ać o b ie k t d anej klasy K p rzy słan y p rzez w a rto ść (K) lub p rzez referencję (K&). W iem y już, że jeżeli tak ieg o o p erato ra nie zdefiniujem y, w ó w czas k o m p ilato r p o s ta ra się o w y g en e ro w an ie sw ojego - p rzy p isu jąceg o na z a sa d z ie „sk ład n ik p o s k ła d n ik u ". Słow a „p o stara się" d o b rz e oddają tu sytuację. M ianow icie czasem to g en ero w a n ie m oże się o k a z a ć niem ożliw e.
4)
Przypomina się łacińska maksyma:
O tm tia m en m e c u m p o r to .
Rozdział. 19. Przeładowanie operatorów O perator [] *
843
Jeżeli klasa m a sk ła d n ik c o n s t , to o p erato r nie b ęd zie w y g en ero w an y . e s t to oczyw iste, b o sk o ro w klasie jest jakiś sk ła d n ik u stan o w io n y jako c o n s t to znaczy , że d o p u szczam y jego micjalizację, ale potem n ie w o ln o już go zm ien iać, czyli nic do n ieg o p rzy p isy w ać.
♦♦♦ P o d o b n ie w p rz y p a d k u obecności sk ład n ik a b ęd ąceg o referencją. Jak w iem y , referencję (p rzezw isk o ) tylko się inicjalizuje, a p o tem juz p rz e p a d ł o - nie m o żn a ro zm y ślićsię i przerzucić p rzez w isk o na in n y obiekt. *
Jeśli klasa (np. r a d i o ) m a składnik b ęd ący o b iek tem innej klasy (np. t r a n z y s t o r ) i w tej innej klasie ( t r a n z y s t o r ) o p erato r p rzy p isan ia o k reślo n y jest jako p r i v a t e - to w ów czas nie b ę d z ie g en ero w an y o p e r a to r p rzy p isan ia d la klasy go zaw ierającej (klasa r a d i o ) .
To zrozumiałe - jeśli ktoś określa o p e r a tor= jako p n v a t e , to znaczy, zenie chce, aby przypisywanie do obiektu odbywało się spoza tej klasy. Klasa zawierająca nie może go użyć. Dla w tajem niczonych: <♦ an alo g icz n ie jest w p rz y p a d k u , gdy klasa m a k lasę p o d staw o w ą, w k tó rej o p e r a t o r = jest ty p u p r i v a t e .
Dla wtajemniczonych: przypomnienie o dziedziczeniu operatorów Jeżeli o p e ra to r jest funkcją sk ład o w ą klasy, to m oże być d zied ziczo n y . D otyczy to w sz y stk ich o p e ra to ró w oprócz operatora p rz y p isa n ia . ( • nas . rozd ział, str. 904).
19.12 O p e ra to r [ ] Kontynuujemy naszą opowieść o czterech operatorach, które mogą hyc tylko niestatycznymi funkcjami składowymi odpowiedniej klasy. O p erato r [ ] - "o d w ołanie się do elem entu tablicy" - jest o p erato rem d w u a rg u m e n to w y m . Jeśli chcem y p rzeład o w ać ten o p erato r, to funkcja o p erato ro w a m usi b y ć n iestaty czn ą (czyli zw ykłą) funkcją sk ład o w ą klasy. Jeśli m a m y obiekt klasy K i d o k o n am y p rzeład o w an ia tego o p erato ra d la tej klasy, to w ó w c zas w y rażen ie K obiekt; obiekt [ a r g u m e n t ]
o d p o w ia d a w y rażen iu obiekt.operator [] (a r g u m e n t )
P rz y p o m n ę po raz kolejny, że - tak że i tutaj - treść o p erato ra nie m u si mieć nic w sp ó ln e g o ze znaczeniem , jakie m a on dla ty p ó w w b u d o w a n y ch . O czyw iście lepiej je d n a k , jeśli słu ży n am do podobnych celów .
9
844
Rozdz. 19. Przeładowanie operatorów O p erato r []
G o d n y z w r ó c e n i a u w a g i j e s t fakt, ż e a r g u m e n t n ie m u s i b y ć w c a l e t y p u
int
Było to nie do po m y ślen ia dla ty p ó w w b u d o w an y ch . P isa liśm y przecież w yrażen ia: double tablica[30]; tablica[7] tablica[0 ]
n ie m ożna jednak b y ło napisać: tablica[0.5]
bo elem en t nr 0.5 p o prostu nie istnieje. Jest: albo 0 albo 1. W p rzy p ad k u p rzeład o w an ia operatoraf ] a rg u m e n t w cale n ie m u si oznaczać n u m e ru elem entu tablicy. Przecież o b i e k t w cale nie m u si b y ć tablicą. P rzy k ład o w o w y o b raź sobie klasę m ie j s c e , k tóra oznacza m iejsce na ekranie, a o p erato r [ ] zasto so w an y wobec obiektu tej klasy m oże o z n ac zać w y p isan ie w tym miejscu jakiegoś tekstu w ramce. T ylko dlatego, że sy m b o l [ ] p rzy p o m i na ram kę. Co jest w ó w czas argum entem ? A ni liczba i n t , a n i n a w e t d o u b le tylko s t r i n g . Tekst, k tó ry chcemy w ypisać void miejsce::operator[](string napis)
{
II ...
} H ip o tety czn e u ży cie takiego operatora: miejsce info( 6 , 4); info["Wszystko w normie"];
co o d p o w iad a zap iso w i info.operator!] ("Wszystko w normie");
Z asto so w an ia m ogą być w ręcz n iep raw d o p o d o b n e, ale z w y k le o p rz e ła d o w a niu tego operato ra m y ślim y, g d y chcem y, by o d d a ł n am p o d o b n e u słu g i, jak w p rz y p a d k u ty p ó w w b u d o w an y ch .
Ten operator [ ] , w stosunku do typów wbudowanych, ma pewną szczegól ną cechę: M oże on stać p o obu stro n ach w yrażenia p rzy p isan ia. Z a u w a ż to w sto su n k u do tablicy i n t int tablica[1 0 ]; int x; x = tablica [5 ]; tablica [7] = x; tablica [0] = tablica [4];
// po p r a w e j s tr o n ie // p o le w e j / / poobu
845
Rozdział. 19. Przeładowanie operatorów O perator []
NJic w tym n ad zw y czajn eg o , zn am y to już o d d aw n a. Jak je d n a k zrobić, a b y po n aszy m p rzeład o w an iu tego operatora, m ógł o n rów nież stać p o obu stro n ach p rzy p isan ia? N ie jest to tak ie tryw ialne. Dla p rz y k ła d u zró b m y eksperym ent: u d ajem y , że nic nie w ie m y o istnieniu tablic ty p u i n t . Z d efin iu jem y sobie klasę, w której sk ład n ik iem b ęd zie, co p ra w d a , taka tablica, ale z zew n ątrz niew idoczna: class tab_calkow
1 int a [100] ; public :
_
// deklaracja funkcji operatorowej II >; To w szy stk o . O to definicja obiektu naszej klasy: tab_calkow t;
Problem: Jak powinien wyglądać operator [ ] , aby możliwa była instrukcja int x = t [5);
polegająca na u zy sk an iu w arto ści z w y b ran eg o elem entu tablicy? O to p rz y k ła d o w a definicja takiej funkcji operatorow ej: int
tab c a l k o w : :operator[](unsigned int który)
“
{
return a[który];
} Po p ro stu o d czytuje się ele m e n t o ż ąd a n y m n u m e rz e i zw raca się jego w artość instrukcją r e t u r n . . . Podkreślam jednak, że operator [), który widzisz wewnątrz funkcji, to juz nie żadne przeładowanie, gdyż stoi tu koło składnika i n t . W tym przypad
ku więc, zadziała standardowa wersja tego operatora. Z atem o p erato r ten, ja k o rezu ltat, zw róci liczbę i n t sch o w an ą w ż ą d a n y m elem encie w ew n ętrzn ej tablicy. Inaczej m ó w iąc rezu ltatem w y rażen ia. ( t [5] )
czyli ( t .o p e r a t o r [ ] (5)
)
jest liczba ty p u i n t . (N p . ró w n a 22). W instrukcji: x = t [5 ];
ch o d ziło nam w łaśnie o to . Inaczej m ów iąc, p o obliczeniu w arto ści w y rażen ia, instrukcja ta zam ienia się na x -
( 22) ;
< 1
9
846
Rozdz. 19. Przeładowanie operatorów O p erato r []
No dobrze, teraz dalsza część problemu: zastanówmy się, co by było w przypadku instrukcji t[5] = 8; C zy w w yniku teg o , do elem entu naszej tablicy zo stan ie rzeczy w iście w pisana liczba 8? O czyw iście, że nie! Lewa stro n a p rzy p isan ia m a p rzec ież wartość 22 C zy m ożliw y je st z ap is // bezsens!
(22) = 8 ;
A teraz bardziej form alnie: I Po lew ej stronie przy p isan ia m ogą się zn aleźć ty lk o tak zw ane I 1-wartości (1- jak lewa strona). (Zob. str. 260). Z atem po lewej stro n ie m oże być tylko w y rażen ie o zn aczające obiekt. •
M o że być to sam obiekt,
•
m o że być to obiekt p o d p rzezw isk iem (referencja),
•
a m oże to być obiekt p o k azy w an y p rzez w sk a źn ik .
T ylko do tego m o ż n a w staw ić (przypisać), w n aszy m p rz y p a d k u , liczbę 8. T o jest już p raw ie ro zw iązan ie naszego p ro b lem u . C o ma z a te m zw rócić jako re z u lta t funkcja operator [ ] ? O d pow iedź: I R eferencję do tego, co m a być p o d d a n e p o d s ta w ie n iu . C o jest tym w n aszy m p rzy p ad k u ? O b iek t klasy
tab_calkow? Nie!
C o p raw d a, to n a rzecz tego obiektu w y w o łu je się o p erato r [ ] , ale przecież liczbę w staw ia się ju ż do jednego k o n k retn eg o elem entu tablicy, k tó ra jest w śro d k u naszego o b iek tu . Z atem o p e ra to r ten p o w in ie n zw racać re feren c ję do w y b ra n e g o e le m e n tu tablicy.
T eraz zobaczm y realizację tego o p erato ra. To aż śm ieszne, ż e ty le było tłu m a czenia, a sp raw ę załatw ia jeden jedyny zn aczek & w p ierw szej linijce definicji funkcji. Dzięki n ie m u op erato r zw raca elem en t nie p rz e z w arto ść, tylko przez referencję # in c lu d e < io s tre a m > u s i n ą nam espace s t d ;
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHIIIIIIIIHl\ c l a s s t a b calk o w
i n t a [1 0 0 ]; p u b lic : // deklaracja funkcji operatorowej i n t & o p e r a t o r [] (u n s ig n e d i n t k tó r y ) { re tu rn a [ k tó r y ] ; )
// <-tutaj cała tajemnica
]i;illlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll 1 i n t m a in ()
847
Rozdział. 19. Przeładowanie operatorów O perator []
tab calkow t; for(int i = 0 ; i < 100 ; i++) t[i] = 100 + i; t[1] = t [2] + 50 + t[3];
// załadowanie tablicy // pracujemy tak jak na zwykłej tablicy !
cout << "Mamy kolejno " «
t[0 ]
<< " ,
" «
t [ 1] «
",
"
« t [2] « ", " « t[3] « " itd... Na ekranie zobaczymy Mamy kolejno 100, 255, 102, 103 itd... Komentarz To raczej p o d su m o w a n ie : C h c ą c n a p is a ć fu n k c ję o p e ra to ro w ą [ ] ta k , by o p e ra to r m ó g ł s ta ć p o o bu s tro n a c h z n a k u p rz y p is a n ia - m u s im y z d e k la ro w a ć , ż e fu n k c ja z w ra c a re fe re n c ję d o te g o , c z e m u m am y p rz y p is y w a ć .
N ie jest to je d n a k obow iązek. Funkcja o p e r a t o r [ ] w cale nie m u si nam służyć d o pracy z tablicam i.
Zastosowanie przeładowania operatora [ ] K iedy p rz e ła d o w a n ie tego operatora m oże się nam przydać? W sp o m in ałem już nieco o tym , ż e był to jeden z p ierw szy ch o p eratorów , k tó ry m usiałem p rzeład o w y w ać. S praw a p o leg ała na tym , że m iałem po słu g iw ać się o lb rzy m ią tablicą. Taka tablica nie m ieściła m i się w pam ięci, w ięc trzym ałem ją n a d y sk u . Ile razy p o trze b o w a łem skorzystać z określonego fragm entu tablicy —u ru c h a m iałem funkcję, która czytała żąd a n y frag m e n t tablicy do pam ięci. N astęp n ie m ogłem d o trz e ć do żąd an eg o elem entu i w ykonać na nim jakąś operację: odczytać z n ieg o w arto ść lub coś tam przypisać. Jeśli p o trzeb o w ałem innego M elem entu — sta ry od sy łałem specjalną funkcją na dysk i w czy ty w ałem now y. ™ N ie m u szę ch y b a do d aw ać, że w ykonanie instrukcji
tab[10][5] = tab[511][5] + tab[77][5]; było w ręcz kark o ło m n e. Tym czasem p rzeład o w an ie o p erato ra f ] doskonale rozw iązuje tu sp raw ę. N ie chodzi tu tyle o prędkość, co o łatw o ść zapisu. P oniew aż nie m ó w iliśm y jeszcze o operacjach z dyskiem , d lateg o z p rzy k ład em realizacji tego p rzeład o w an ia m usim y zaczekać (str. 1136).
848
Rozdz. 19. Przeładowanie operatorów O p erato r ()
Innym , pro stszy m p rzy k ład em m oże być chociażby p rz e ła d o w a n ie , które sp ra w d z a czy nie o d n o sim y się do takiego elem en tu tablicy, k tó ry n ie istnieje (bo tablica jest krótsza). W naszej klasie t a b _ c a l k o w tak z re a liz o w a n y operator m iałby następującą definicję i n t & o p e r a t o r [ ] (u n sig n e d i n t k tó r y ) { i f (i <= 99) r e t u r n a [ k t ó r y ] ; e ls e b la d ( ) ; } g d zie b l a d jest n azw ą jakiejś funkcji, k tó ra ostrzega n a s, że oto w łaśnie chcieliśm y p o p ełn ić sam obójstw o.
Podsumjjmy ty parag rafie tym zobaczyliśm y, jak p ro s to p rzeład o w u je się o p e ra to r [ ] , O czyw iście m o żn a uży ć go do d o w o ln eg o celu. Jeśli jed n ak chcielibyśm y, by - w sto su n k u do o b ie k tu naszej klasy - m iał p o d o b n e działanie, jak w sto su n k u d o ty p ó w w b u d o w an y ch (czyli słu ży ł d o pracy z ob iek tam i u staw ian y m i w tablicę), to trzeba p am iętać o jednej „h arcersk iej" zasadzie. Po p ro s tu ten nasz prze ła d o w an y o p erato r p o w in ien jako rezu ltat z w racać referencję do obiektu będącego pojed y n czy m , w y b ran y m elem en tem tablicy. K rótko m ów iąc jego deklaracja p o w in n a w y g ląd ać tak: n d s z a _ k la sa &
n a3 z a _ k la sa : : o p e r a t o r ! ] ( i n t n r_ e le m e n tu ) ;
7 o w szystko.
19.13 O p e rator () Jest to trzeci z g ru p y o peratorów , które m u sz ą być n ie staty cz n y m i (czyli zw yk łym i) funkcjam i sk ład o w y m i klasy. O ile p rzy p rz eład o w an iu operatorów = i [ ] m ów iłem , co zro b ić, aby operatory te sen so w n ie i bezp ieczn ie im itow ały d z ia ła n ie sw ych o d p o w ie d n ik ó w wobec ty p ó w w b u d o w a n y ch - o tyle tutaj ten p ro b le m nie istnieje. T y m operatorem robim y n ap raw d ę, co chcem y, a jego u ży cie nie m a nic w sp ó ln e g o z im itow a niem w y w o ły w a n ia funkcji. D latego p rz e ła d o w a n ie tego o p e ra to ra jest najłat w iejsze z o m aw ianej czw órki. P ew n ie pom yślałeś: „No to po co poświęcać mu cały paragraf?!" O d p o w iem tak: I W yjątkow ość tego o p erato ra polega na ty m , że: ♦♦♦ Jako jed y n y m oże przyjąć więcej n iż d w a arg u m e n ty .
Wszystkie inne operatory s(f albo jedno-, albo dwuargumentowe. Ten przyjmuje więcej, czyli dzięki niemu do funkcji operatorowej może zostaf przesłana większa liczba argumentów.
Rozdział. 19. Przeładowanie operatorów O perator ()
849
♦> A rg u m en ty te m ogą być n a w e t d o m n iem an e,
co jest przecież niemożliwe w przypadku pozostałych operatorów
Jak to działa? Jeśli o b i e k t jest o b iek tem klasy, dla k tó rej ten o p erato r z o sta ł zdefin io w an y , to op erato rem tym m o ż n a p rzy k ład o w o p o słu ży ć się w taki sp o só b : obiekt (argl, arg,2 arg3, arg4, arg5)
lub w y w o łu jąc go jawnie na rzecz tego o b iek tu . obiekt .operator () (argl, arg,2 arg3, arg4, argU)
Z au w aż, że d o funkcji o p eratorow ej p rzesy ła m y więcej niż 2 a rg u m e n ty (tutaj 5, p lu s jeden u k ry ty - w sk aźn ik d o o b iek tu , na rzecz którego z o s ta ł ten operator w yw ołany). O czyw iście m o g liśm y zdefiniow ać funkcję przyjm ującą in n ą liczbę arg u m en tów . Funkcje takie m o g ą istnieć ró w n o cześn ie, g d y ż p rze ła d o w u ją się naw za jem. S pełnione są p rzec ież w aru n k i p rzeład o w an ia: •
funkcje mają ten sam z a k re s w ażności n a z w y (zakres klasy),
•
ró ż n ią się rodzajem , liczb ą lub kolejnością arg u m e n tó w .
Kiedy takim przeładowaniem można się posłużyć? O czyw iście w te d y , g d y chcielibyśm y sk o rzy stać z m ożliw ości przesłania do o p erato ra w iększej liczby arg u m en tó w . Jest to najw ażniejsza i najczęstsza prze słanka d o sto so w a n ia tego operatora. W iele a rg u m e n tó w m o ż e się nam p rz y d a ć na p rzy k ład w ted y , g d y klasa opisuje tablicę w ielo w y m iaro w ą. Zw ykle, aby o d n ie ść się d o tablicy, p rzeład o w u je się o p erato r [ ] . Jed n ak ten o p erato r m oże n am „obsłużyć" ty lk o jeden w ym iar tablicy - jest b o w iem tylko d w u a rg u m e n to w y . Jeśli n ato m iast tablica jest 3 w y m iaro w a, to w szy stk ie trzy jej in d ek sy m ożna w ysłać do o p erato ra um iesz czając je w naw iasie. N p. t a b l (1 , 1 , 6 );
Innym p o w o d em , d la którego w y b ó r m o ż e paść w łaśn ie na ten operator, jest jego sk o jarzen ie z w y k o n y w a n iem jakiejś czynności. Klasa b ow iem m o że o pisyw ać nie tylko o b iek t złożony z jakichś liczb (pralka). M oże na p rzy k ła d o p isy w ać proces (wytop stali, wyiooływanie filmu). Jeśli obiekt takiej k la sy reprezentuje p ro ces zbierania d anych, to zapis: z b i e r a n i e D a n y c h ();
od razu kojarzy się z w y k o n y w an iem jakiejś czynności. Jest to argum entacja czysto sk o jarzen io w a, ale w łaściw ie czem u nie?
< 1
9
850
Rozdz. 19. Przeładowanie operatorów O p erato r - >
0 p rzeład o w an iu ta k ieg o operatora p o m y ślim y także w ted y , g d y w klasie jest ty lk o jedna jedyna funkcja składow a, k tó rą co chw ilę w y w o łu je m y na rzecz ob iek tu Jeśli m am y klasę s y g n a l i z a t o r opisującą różne u rz ą d z e n ia ala rm o w e sygnalizator
j/defuiicja obiektu
syrena;
1 co chw ilę w y w o łu jem y funkcję s y r e n a . r y c z e c () ; to, p o n ie w aż robi się to ciągle, m ożna sobie oszczędzić n azw y tej funkcji i pisać p o p ro stu sy re n a ( );
II czyli: s y r e n a . o p e r a t o r () ( ) ;
O szczęd zam y p isan ia, a zapis i tak jest jasn y . Funkcja nie m u si być n aw et tą je d n ą, jedyną. M ogą b y ć inne, ale w yraźn ie m niej w ażne. P o d sta w o w ą funkcją sy re n y jest jednak: ryczeć. M ożliw esą dla niej jakieś inne fu n k c je - np. regulacja w ysokości tonu. Tę funkcję w yw ołuje się je d n a k rzadziej. In n y p rzy k ład z życia codziennego. Jeśli w teatrze inspicjent w o ła: „K urtyna!" to n ie chodzi przecież o nazw an ie czegoś, ty lk o o w y w o łan ie na ty m w iadom e) akcji. W szystko załatw ia obecność w klasie takiej funkcji: v o id k u r t y n a : : o p e r a t o r ( ) ( ) // składnik b o o l k ie r u n e k jest true, gdy podniesiona if ( k ie r u n e k ) w _ d o l( ) ; e ls e w g o re ( ) ; 1 P o n iew aż nie p rzew id u jem y , żeby ten o p e ra to r m iał się z n aleźć kiedykolw iek po lew ej stronie zn ak u p rzy p isan ia, więc o p e ra to r zw raca ty p v o i d . N ie m u si jednak być ta k zaw sze. Jeśli tylko chcesz, by o p e ra to r ten m ógł stać po lewej stronie zn ak u p rz y p isa n ia , to p o w in ien eś po starać się, by z w racał referen cję d o tego, czem u m a się przypisyw ać. O ty m już m ó w iliśm y w po p rzed n im paragrafie.
19.14 O p e rator - > Jeśli nie zro zu m iesz te g o p arag rafu od razu , n ie przejm uj się. P rzeład o w y w an ie tego o p erato ra to ju ż n a p ra w d ę h iszp ań sk a szk o ła jazdy. N ie, żeb y to by ło takie straszn ie tru d n e, ale p o prostu rzad k o się to robi. M im o w sz y stk o p ro szę oj p rzeczy tan ie tego p a ra g ra fu .
W
Rozdział. 19. Przeładowanie operatorów O perator - >
851
O p erato r - > o d n ie sie n ia się d o sk ła d n ik a klasy m u si być zd efin io w an y jako niestaty czn a (czyli zw y k ła) funkcja sk ła d o w a klasy. O p erato r ten jest je d n o arg u m en tn w y , a d ziała on na arg u m e n c ie stojącym po jego lewej stronie. Jest to trochę zaskakujące. Operatora tego nie interesuje zupełnie co stoi po prawej stronie. Później zobaczymy dlaczego. Coś jednak po drugiej stronie tej strzałki musi stać. Kompilator oczekuje tu na coś. obiekt-> d A rg u m en te m jest tu w łaściw y obiekt, a nie - jak d o tego jesteśm y p rzy zw y czajeni - jed y n ie w sk a ź n ik do o b ie k tu . Jeśli dla d anej k la sy zd efin io w an y z o sta ł ten o p erato r, to jego w ystąpienie w o b ec obiektu tej k la sy
ob\e\ó.->składnik je st p rzez k o m p ila to r u zn aw an e za w y rażen ie: (obiekt.operator->()) -> składnik
Z a u w a ż , że teraz, w ty m w yrażeniu, m a m y d w a sy m b o le-> . Tylko ten lewy jest tu ta j p rzeład o w an iem . Ten p raw y to zw y k ły operator. A rg u m e n te m tego "na szego" p rz e ła d o w a n e g o operatora jest o b iek t (albo jego referencja). N atom iast te n d ru g i, zw y k ły o p e ra to r -> , po starem u w y m ag a p o sw ojej lew ej stronie a d re su . Z a p y ta sz pew nie:
Dlaczego nasz przeładowany operator jest jednoargumentowy ? Z o b acz sam . N a a rg u m e n cie stojącym ze swojej lewej stro n y w y k o n u je on jakąś akcję, p o czy m z w ra c a rezultat. (obiekt.operator->() ) -> składnik ( rezultat) -> składnik T o w szystko. Ó w składnik go nie interesuje. T eraz na ten re z u lta t o ra z na składnik p a trz y już z w y k ły o p e ra to r - > , on jest ju ż d w u arg u m en to w y .
Uwaga, ten operator ma zalecenie, co do typu wartości rezultatu I O p erato r-> jako rezu ltat sw ej pracy pow inien zw racać •
1) a lb o w skaźnik
•
2) a lb o obiekt, (lub referencje do obiektu) takiej klasy, która ta k ż e m a p rzeład o w an y operator->
Ju ż to w yjaśniam :
Ad 1. Jeśli wskaźnik, to dlaczego? D latego, że p o w y k o n an iu naszej funkcji operatorow ej - > m u sim y otrzym ać w y rażen ie (wskaźnik) - > składnik
< 1
9
852
Rozdz. 19. Przeładowanie operatorów O p erato r - > g d y ż to m oże stać po lewej stronie zw ykłego o p erato ra - >
Ad 2. Jeśli obiekt, lub referencję obiektu innej klasy mającej operator -> , dlaczego?
to
Wyjaśnię to, ale to rzadko spotykana sytuacja, więc nie przejmij się tym u bardzo, przy pierwszym czytaniu możesz to opuścić
D latego, że w ted y sp ra w a się tylko chw ilow o odw lecze. O b ecn y obiekt zam ieni się w tym w yrażen iu na obiekt innej klasy, czyli w ó w czas w y ra ż e n ie przybierz# ta k ą formę: (obiekt_innej_klasy)->składnik o p e r a t o r - > z tej pierw szej klasy, zep ch n ął p racę na o p e r a t o r - > z tej innej klasy . Z n ac zy to, że teraz o p e r a t o r - > z tej innej k lasy p o w in ie n się w ykazać, Z w ró cić w skaźnik, a w te d y to obecne w y rażen ie zam ien i się na: (wskaźn ik)->składn ik N ie m u szę chyba pisać, co będzie, g d y ten d ru g i o p e ra to r też nie zwróci w sk a źn ik a, lecz obiekt (referencję) obiektu jeszcze innej klasy. Po p ro stu w tedy s p ra w a się znow u odw lecze. O statecznie jednak, na sam y m k o ń cu tej "syiyekologii', jakiś o p e r a t o r - > m usi zw rócić w sk aźn ik . G d y b y zaś w tej innej klasie nie było o p e r a t o r a - > , k o m p ilato r o znajm i błąd.
W Co naprawdę może dla nas zrobić taki operator? To sam o, co zw y k ły o p erato r - > , ale: • Z w ykły o p erato r nie m oże być z a sto so w a n y d o obiektti dane| klasy. Stosuje się go tylko w obec wskaźnika do obiektu tej klasy. To jest ogro m n a różnica. •
19.14.1
O p erato r ten p ozw ala n am w y k o n ać jak ąś d o d a tk o w ą akcj# przy okazji sięgania do sk ład n ik a klasy. Tę d o d a tk o w ą akcjy m ożem y zap ro g ram o w ać w ciele funkcji o p erato ro w ej.
„Zręczny wskaźnik" - wykorzystuje przeładowanie właśnie tego operatora S ta n d a rd o w y m p rzy k ła d em cytow anym w takim p rz y p a d k u jest - tak zw any zręc zn y w sk a ź n ik . M im o tej sugestyw nej n azw y , n ie jest to w skaźnik.
853
Rozdział. 19. Przeładowanie operatorów O perator - >
Jest to k lasa obiektów , k tó re zach o w u ją się, jak z w y k le w skaźniki, ale m ają jeszcze p ew n ą d o d a tk o w ą inteligencję. Z w y k ły m w sk a ź n ik ie m sięgam y d o w n ę trz a klasy w ten sp o só b
zwykłyjwskażnik -> składnik w sk aźn ik iem z ręc zn y m sięgam y tak:
zręcznyjwskażnik —> składnik czyli id en ty czn ie. N o to g d zie tu sp ry t? Jest, jest! N ie zapom inaj, że p o d o p erato rem - > k ry je się w d ru g im p rz y p a d k u funkcja, k tóra m o że zrobić p a rę sp ry tn y c h rzeczy. M o że to być liczenie ile razy d o k o n y w an e jest o d n o szen ie się do sk ła d n ik ó w klasy tym w łaśn ie sposobem , m oże to być w yśw ietla nie n a ek ran ie inform acji z o strzeżen iem itd.
Umieć rozróżnić P ow ied ziałem , ż e o p e ra to r m ożna zd efin io w ać - z p o p rz e d n ic h p a ra g ra fó w w iesz, że nie je st on au to m aty czn ie g en ero w an y . P ew nie m yślisz teraz: „W obec tego jeśli n ie zdefiniujem y o p e ra to ra - > , to jakim p ra w e m m ożliw a jest operacja żurykły_wskaźtiik - > składnik skoro ten o p e ra to r nie został (jeszcze) zd efin io w an y ?" Prosta sp raw a: p o lew ej stronie o p erato ra - > stoi nie obiekt d an ej klasy, ale w sk aźn ik d o tak ieg o obiektu. W skaźnik d o obiektu, to nie to sam o , co obiekt. ♦$* W sk aźn ik (d o czegokolw iek), to ty p w b u d o w an y . Z atem w tym p rz y p a d k u d ziała w ersja o peratora - > d la w skaźników . Ta z a w s z e istnieje! ♦> Za to nie m a m y p rzeład o w an ia na okoliczność w y stąp ien ia teg o sy m b o lu obok obiektu danej klasy. D op ó k i nie zdefiniujem y fu n k cji o peratorow ej - > d la danej klasy K, p o lew ej stronie takiego z n a k u - > nie m oże się z n a le ź ć obiekt tej klasy K (ani jego p rzezw isk o czyli referencja) obiekt - > składnik
// kompilator wówczas zaprotestuje !
Zręcznych wskaźników może być wiele rodzajów, pokażemy więc jakieś przykładowe zastosowanie O to program , w k tó ry m m am y do czyn ien ia z dw om a klasam i obiektów . ♦$» K lasa w e k t rep rezen tu je w ek to r trójw ym iarow y. P o d o b n ą klasą ju ż baw iliśm y się w ielokrotnie.
c
854
Rozdz. 19. Przeładowanie operatorów O p erato r - > ♦> K lasa s p r y c i a r z , która reprezentuje w sk a ź n ik d o o b iek tó w klasy wekt.
W łaściw ie klasa ta pow inna nazyw ać się nie s p r y c i a r z , ale s z p i c e l . Klasa ta b o w iem notuje sobie w pam iętniku do jakich celó w i ile ra z y u ży liśm y tego w sk a źn ik a. M ożliw e jest to dzięki p rzeład o w an iu o p erato ra - > . Ilekroć p osłu ż y m y się tym operatorem w obec o b ie k tu k la sy sp ry ciarz , za k ażd y m razem z o sta n ie to w p isan e do akt. #include
ImMmmmmmmmmiimimmmmnmimiiimmnmmumnmnmm class wekt
1 public : double x, y, z;
//-------- k o n stru k to r wekt(double a, double b, double c) : x (a), y(b), z(c)
//—----- kilka
// O
zw y k ły c h fu n k c ji sk ła d o w ych
void podwojenie!)
{
x *= 2 , y *= 2 z *= 2,
} / / --------void pokaz() { cout « "x— " « X « ”, y = " « « " , z= "<< z << endl;
y
}; class spryciarz wekt *wsk; wekt * (pamiętnik[10]); int użycie; public : U
// // .
.
----------------------o p era to r p r zy p isa n ia -------------
// o
void operator=(wekt* w)
( wsk = w; U
______________
k o n stru k to r (ta k że d o m n ie m a n y !)
spryciarz(wekt * adr— 0) ; H
---------------------- p r ze ła d o w a n ie
// 0
// ©
c z y li N U L L
operatora —>
wekt * operator->(); void statystyka(void);
z* * * * * * **************************************************** * * ^ spryciarz::spryciarz(wekt * adr) : wsk(adr), użycie (0) // * 1 for (int i = 0 ; i < 10 ; i++) pamiętnik [i ] = 0;
//c z y l i
NULL
855
Rozdział. 19. Przeładowanie operatorów O perator - > Z************************************************************ *1 wekt
*
//
s p r y c i a r z : : o p e r a t o r - > ()
.
{
//------------ akcja sprytna: wpisujemy do a k t! pamiętnik[użycie] użycie
=
=
(++uzycie)
wsk; %
// ©
10;
/ / ------------ zwykła akcja-----------------------return
wsk;
Z *************************************************************/ void
s p r y c i a r z ::statystyka(void)
cout
<<
"Ostatnie "dla
for(int
10
przypadków
obiektów
i=0
;
i
<
10
\no ;
użycia
adresach
odbyło
sie
"
:\n";
i++)
£
cout
<< «
pamietnik[ ( (użycie) + ( ( i = = 4 ) ? " \ n " : ", ");
i)
%
10
//©
]
} cout
<<
endl;
Z ********************** * * * * * * * * ** * '* * * * * * * * * * * * * * * * * * * * * * * * * * * / int
main()
{ d o u b l e m; w e k t w w w (1,
1,
1) ;
wekt
*zwykly_wsk;
spryciarz
zreczny_wsk;
// ©
//OO
//-----------------------------------------zwykły
wsk
zreczny_wsk cout
<<
=
Swww;
=
&www;
"Operacja
za
m = zwykly_wsk->x; cout « "m = " << m cout
<<
"Operacja
zreczny_wsk->x;
wekt
w 2 (2, 2 , 2 ) , w 3 (3, 3 , 3), w 4 (44, 10, 1);
"m
=
"
<<
pomocą «
za
m = cout
<<
/O ©
m
wskaźnika
\n";
endl; pomocą
«
zwykłego
zręcznego
wskaźnika
\n";
//O ©
endl;
z r e c z n y _ w s k = &w2; z r e c z n y _ w s k — > p o d w o j e n i e () ;
//OO
z r e c z n y _ w s k - > p o k a z () ;
4 ]
z r e c z n y _ w s k = &w3; z r e c z n y _ w s k - > p o d w o j e n i e () ; zręczny
w s k — > p o k a z () ;
z r e c z n y _ w s k = &w4; z r e c z n y _ w s k - > p o d w o j e n i e () ; z r e c z n y _ w s k - > p o k a z () ; z r e c z n y _ w s k . s t a t y s t y k a () ; zreczny_wsk
=
Swww;
//O ©
9
856
Rozdz. 19. Przeładowanie operatorów O p erato r - > z r e c z n y _ w s k - > p o k a z () ; z r e c z n y _ w s k . s t a t y s t y k a ();
}
Na ekranie po wykonaniu tego programu pojawi się Operacja
za
pomocą
zwykłego
pomocą
zręcznego
wskaźnika
m = 1 Operacja
za
m = 1 x= 4 ,
y=
4,
z=
4
x=
y=
6,
z=
6
6,
x = 88, y = 20, z= 2 O s t a t n i e 10 p r z y p a d k ó w o
adresach
użycia
wskaźnika
odbyło
sie
dla
obiektów
:
00000000,
00000000,
00000000,
0012FF70,
0012FF30
0012FF30,
0012FF24,
0012FF24,
0012FF18,
0012FF18,
x = 1, y = Ostatnie
1, 10
z= 1 przypadków
o adresach : 00000000, 00000000, 0 0 1 2 F F 2 4 , 0012FF24,
użycia
0012FF70, 0012FF18,
odbyło
sie
0012FF30, 0012FF18,
dla
obiektów
0012FF30 0012FF70,
Komentarz O K lasa w e k t nie jest niczym szczególnym . Jej k o n stru k to r, jak w id zim y , inicjali zu je sk ład n ik i za pom ocą listy inicjalizacyjnej. Tak jest zalecam - co się tylko da - inicjalizuj za pomocą listy. In n e funkcje sk ład o w e to •
podwo j enie
•
oraz funkcja
- (podw ajające k a ż d ą ze w sp ó łrzęd n y ch )
pokaz
(w ypisująca na ekran).
0 Klasa s p r y c i a r z . P o n iew aż jest ona klasą ob iek tó w zach o w u jący ch się jak w sk a źn ik i d o obiektów klasy w e k t , dlatego jakim ś sk ład n ik iem tej k la sy p o w i n ie n być w łaśnie wskaźnik do w e k t. M am y go d o k ła d n ie w miejscu 0 . N ato m ia st reszta sk ład n ik ó w jest już jakby tylko d la n a d a n ia tem u zw y k łem u w sk a źn ik o w i d o d atk o w ej „inteligencji" i „zręczności". © T ablica w sk aźn ik ó w d o o b iek tó w klasy w e k t . wekt
*
(pamiętnik[10]);
Czytamy: pamiętnik jest 10 elementową tablicą wskaźnikóio do obiektów klasy w ek t. To tu taj b ęd ziem y sobie potajem nie notow ać ad resy , na które p o k azy w ał zrę c z n y w sk aźn ik w chw ili, g d y u ży to o p erato ra - > W id zim y też sk ład n ik u ż y c i e . P oniew aż p am iętn ik nie jest niesk o ń czo n y m o żem y w nim zapisać tylko 10 ostatnich p rz y p a d k ó w - d la teg o p o słu ży m y się sk ład n ik iem u ż y c i e . D zięki niem u, najstarsze d a n e w pam iętn ik u b ę d z ie m y k aso w ać i w p isy w ać w to miejsce - najnow sze.
857
Rozdział. 19. Przeładowanie operatorów O perator - >
O Definicja o p e ra to ra p rzy p isan ia d la tej klasy. W ydaje się o czy w iste, że w skaźnik często u s ta w ia się tak, by p o k a z y w a ł na coraz to in n e obiekty. Tak, jak w p rz y p a d k u ty p ó w w b u d o w an y ch : in t a, b; i n t * w sk i; w ski = &a; w ski = &b; Skoro w y stę p u je tu zn ak ' = ' , a n ie jest to definicja o b ie k tu , znaczy to, ż e tu zad z iałać ma o p e ra to r p rzy p isan ia (a nie k o n stru k to r kopiujący). Realizacja tego o p erato ra jest p ry m ity w n a —p rzy słan y a d re s obiektu klasy w e k t w p isu jem y d o sk ład n ik a wsk. void
t
operator=(wekt*
w)
//
©
wsk = w;
P rzeła d o w a n ie teg o operato ra n ie jest przed m io tem te g o p arag rafu . Jednak, gdv go m am y , operacje zm ian y w sk a źn ik a będą się o d b y w a ły w zapisie b ard zo n atu raln y m . W id ać to choćby w O © . © Klasa s p r y c i a r z m a kon stru k to r. P oniew aż w deklaracji w id zim y , że m o żn a go także w y w o łać bez żad n y ch arg u m e n tó w , d lateg o je st to także k o astru k to r do m n iem an y . Wartość domniemana tego argumentu to adres zerowy (tzw. N U L L ). 0 Realizacja tego k o n stru k to ra. Co się d a - inicjalizuję listą inicjalizacyjną. W ciele funkcji jest w p isa n ie ad resó w zero w y ch do elem entów p a m i ę t n i k a .
O Oto funkcja będąca przedmiotem tego paragrafu: P rzeła d o w a n ie o p erato ra -> . O p e ra to r ten —w edle z a s a d y —jest funkcją sk ła dow ą k lasy s p r y c i a r z . Jest on jed n o arg u m en to w y , a ten jedyny a rg u m e n t p rzy ch o d z i d o n iego za p o śred n ictw e m w skaźnika t h i s . D latego w liście arg u m e n tó w form alnych nie ma nic. P oniew aż jesteśm y zd ecydow ani, ż e operator ten m a n am oddaw ać u słu g i i zw iązan e z pokazywaniem - d la te g o p o w in n iśm y zw rócić, jako jego rezultat, adres o b iek tu , na który n asz s p r y c i a r z w łaśnie po k azu je. Ten adres m am y w p isan y w sk ład n ik u w sk. To w łaśn ie zw racam y jak o rezu ltat działania o perato ra. r e t u r n w sk; © Z anim jed n ak to zrobim y - zap isu jem y sobie bieżącą w arto ść sk ład n ik a ws k do tablicy p am iętn ik . p a m ię tn ik [ u ż y c ie ] = wsk; W m iejscu © w id a ć chw yt, jaki zastosow ałem , by in d ek s u ż y c i e - mimo że za każdym razem in k rem en to w an y —p o osiągnięciu w artości 9 zm ienił się na 0. Dzięki tem u starsze zapisy w p am iętn ik u będą k aso w an e ("nadpisywane ).
(i9
858
Rozdz. 19. Przeładowanie operatorów O p erato r - >
Gwoli informacji © W funkcji statystyka w y p isu jem y na ek ran a d re s y sch o w an e w tablicy p a m iętn ik . W y p isy w an ie nie o d b y w a się od elem en tu 0 d o elem en tu 9, lecz od n ajstarsz eg o d o najnow szego. Jeśli więc p rzed ch w ilą w elem en cie n r 7 zan o to w aliśm y jak iś ad res, to w y p isy w a n ie o d b ęd zie się w e d łu g p o rz ą d k u : 8 , 9,C, 1,2, 3, 4, 5, 6, 7.
W funkcji m a i n © D efin iu jem y zw y k ły w sk aźn ik d o po k azy w an ia n a obiekty klasy wekt. O O D efiniujem y obiekt „zręczn y w sk aźn ik ". (Z au w aż, że w definicji nie m a g w iaz d k i, bo n asz „w sk aźn ik " tak n a p ra w d ę nie jest w sk a źn ik ie m , lecz obiektem ).
O © U staw ien ie zręcznego tak, b y pok azy w ał na jakiś obiekt, o d b y w a się p o d o b n ie, jak w p rz y p a d k u zw ykłego w skaźnika. Tu o czyw iście d ziała n asz p rz e ła d o w a n y o p e ra to r p rz y p isa n ia . O płaciło się tutaj, a p o tem jeszcze w iele razy poniżej. O © P o słu g iw an ie się zręcznym w sk aźn ik iem także je st id en ty czn e, jak w p rz y p a d k u w sk a ź n ik a zw y k łeg o (por. 3 instrukcje w yżej). Jed y n a różnica to to, że w linijce O © p o cichu zostało zan o to w an e, iż sk o rzy staliśm y z tego w sk aźn ik a. A d res o b iek tu www p o szed ł d o pamiętnika. O © W id zim y tu całą serię sytuacji, g d y p o sługujem y się o p erato rem Za k a ż d y m razem jest to n o to w an e w p am iętn ik u .
.
O © O d czasu d o czasu m ożem y z aż ąd ać raportu. F unkcja statystyka p o k azu je n am inform ację, które w p am iętn ik u złożył p rz e ła d o w a n y operator-> .
Jak w id ać, operator-> p rz y d a je się, g d y p iszem y klasę, której obiekty mają pełnić rolę p o d o b n ą w sk aźn ik o m . Na zak o ń c zen ie w y p ad a d o d ać, że je d n o arg u m en to w y o p e ra to r-> nie m a nic w sp ó ln eg o z o p erato rem - > * . Szczególnie ten d ru g i w cale nie m usi być funkcją skład o w ą.
W ten sposób zakończyliśm y omawianie czterech w yjątkow ych operatorów = [ ] ( ) -> W yjątkow ych p rzez to, że jeśli chcem y je p rzeład o w ać, to m u sim y je zd efin io w ać jako n iestaty czn e funkcje sk ład o w e klasy. Są jed n ak in n e o p erato ry o d różniające się od p o zo stały ch tym , że jest ściśle określone, co m ają zw racać jak o rezu ltat sw ojego w y k o n an ia. Te o p e ra to ry to
new i delete. N im i zajm iem y się w n astęp n y ch parag rafach .
P rzy p o m in am , że w p rz y p a d k u innych o p erato ró w (poza o p erato rem -> ) - nie obow iązu ją ż a d n e restrykcje, jeśli chodzi o ty p rez u lta tu . Funkcje m ogą n aw et
859
Rozdział. 19. Przeładowanie operatorów O peratory n e w , new [ ]
nic nie zw racać. W szystko, co d o tej pory napisałem , to b y ły rad y , co „o p łaca się' zw racać p o to, by mieć taki lu b in n y efekt.
19.15
Operatory
new, new[]
O p erato ry n e w o raz n e w [ ] - słu ż ą c e do p rzep ro w ad za n ia rezerw acji o b szaró w w z ap a sie p am ięci (free storę) - m ają swoje w ersje g lo b a ln e i tym i w ersjam i do tej p o ry się posługiw aliśm y. ♦> O p e ra to r n e w służył n am d o tw orzenia p o jed y n czy ch obiektów . O p erato rem
new
[ ] tw o rz y ć m ogliśm y tablice obiektów .
O p erato ram i ty m i m ożna p o słu g iw ać się nie tylko p rz y tw orzeniu obiektów ty p ó w w b u d o w a n y c h , ale w obec klas. Jeśli jed n ak , z jakichś po w o dó w , d ziałan ie tych o p e ra to ró w n am nie o d p o w ia d a (na p rz y k ła d chcielibyśm y je u sp raw n ić) - m ożem y, na u ż y te k obiektów w y b ra nej klasy o w e o p erato ry przeład o w ać. W ów czas to, o b o k w ersji globalnej new lub new U , zaistn ieje wersja lo k aln a, zdefiniow ana na u ż y te k danej klasy.
Tyle tytułem wstępu Z ag a d n ien ie, o którym teraz p o ro zm aw iam y , nie jest specjalnie tru d n e , ale takich p rz e ła d o w a li na pew no m e będziesz robił w p ierw szej fazie n auki. W ydaje m i się w ięc, że - p rzy p ierw szy m czytaniu tej książki - m ożesz te sp ra w y o p u śc ić i przejść od razu d o czytania § 19.19, n a stronie 871, czyli do p arag rafu o p rz eład o w an iu o p erato ró w postinkrem entacji i postdekrem entacji.
K om pilator p rz y tw orzeniu ob iek tó w dow olnej klasy n ajp ierw sp raw d za, czy na tę okazję nie zdefiniow aliśm y operatora n e w (lub, w p rzy p ad k u tablicy takich o b iek tó w , operatora n e w [ ]). Jeśli nie zd efiniow aliśm y, to w ted y u ru c h a m ian a jest g lo b aln a w ersja d a n e g o operatora.
Gdy chcemy zdefiniować operator new (lub new [ ] ) dla jakiejś klasy, to musimy pamiętać, że jest p a r ę wymogów: ❖
O p e ra to r n e w jest funkcją składow ą statyczną. O zn acza to, że jest wyw oły w a n y nie na rzecz kon k retn eg o obiektu (bo ten jeszcze nie istnieje), g ale n a rzecz klasy. Jeśli w deklaracji tego o p erato ra zap o m n im y o p rz y d o m k u s ta t i c ,k o m p i l a t o r z r o b i t o i t a k w ten sposób. Bez u p o m i n ania n as o błędzie. N o w a funkcja o p e r a t o r new () lub o p e r a t o r new [ ] ( ) m u si zw ra cać w s k a ź n ik typ v o i d * , który w skazuje na o b szar pam ięci, g d zie k o m p ilato r m a dokończyć budow y obiektu tej klasy. P ierw szy arg u m en t funkcji o p e r a t o r ma b y ć typu s i z e _ t .
new
( ) lu b o p e r a t o r
new
[ ] ()
860
Rozdz. 19. Przeładowanie operatorów O peratory n e w , new [ ] A rg u m e n t ten posłuży w ciele o peratora do o k reślen ia ile bajtów p am ię ci zare zerw o w ać .
Co to jest typ s i z e _ t - to zależy od implementacji. W pierwszym przybliżeniu traktuj go jako typ całkowity bez znaku. (Definicja tego typu jest w pliku nagłówkowym s t d d e f . h). O szcze g ó ły w ysłania do funkcji operatorow ej o d p o w ie d n ie j w artości tego a rg u m e n tu n ie m usim y się m a rtw ić - kom pilator sa m p rzesy ła tam w artość, określającą: ♦J* w p rz y p a d k u o peratora new - ro zm iar obiektu d an ej klasy, w p rz y p a d k u operato ra new [ ] - rozm iar ja k i zajm ie żą d a n a liczba o b ie k tó w danej klasy. P rz y p o m in a m , że skoro funkcja jest statyczna, to nie m a u k ry teg o arg u m en tu t h i s . Z a te m p ierw szy arg u m e n t, to n ap raw d ę p ie rw sz y a rg u m e n t z listy arg u m e n tó w .
Co to znaczy, że operatory new, new [ ] są funkcjami statycznymi? To m ian o w icie, że operatory te n ie są w łaściw ością, czy zach o w an iem , poszcze gólnego o b ie k tu tej klasy. D efiniują one raczej zdo ln o ść całej klasy d o tw orzenia now ych o b iektów .
Dla wtajemniczonych, znających funkcje wirtualne Skoro funkcje operatorowe new i delete są funkcjami z przydomkiem static (czyli wywoływane są bez wskaźnika this) - dlatego nie mogą być one funkcjami wirtualnymi klasy. To dlatego, że istotą funkcji wirtualnej jest rozpoznawanie (na podstawie wskaźnika this), na rzecz obiektu której to klasy (podstaiuoiuej czy pochodnej) została wywołana ta funkcja. Funkcje statyczne nie mają wskaźnika this, nie mogą być więc wirtualne. Czyli nasze operatory new i delete także nie.
19.15.1
Przykład przeładowania operatora new O to p rz y k ła d o w a definicja o p e ra to ra new dla klasy w e k t, k tórą o statn io się zajm ow aliśm y. Jeśli w deklaracji teg o operatora w k la sie w e k t - nie m a słow a s t a t i c , k o m p ilato r i tak zrealizu je tę funkcję jako staty czn ą. v o id * w e k t: : o p e r a t o r n e w ( s iz e _ t ro z m ia r) f c o u t << "K re u je o b ie k t ! \ n " ; r e t u r n (new c h a r [ r o z m ia r ] ) ; } Funkcja ta (w ty m p rzy p ad k u ) p o p ro stu u ży w a g lo b a ln eg o new , by zare zerw o w ać o d p o w ie d n i obszar. W skaźnik d o tego obszaru z w ra c a jak o rezu ltat sw ej pracy.
861
Rozdział. 19. Przeładowanie operatorów O peratory n e w , new [ ] Jeśli zech cem y z a pom ocą tego o p e ra to ra kreow ać o b iek t k la sy
wekt,
to o d p o
w ied n i zapis w y g lą d a następująco: wekt wsk
*wsk; new
=
wekt( 1 , 2 , 3 ) ;
Te liczby w n a w ia sie - to oczyw iście arg u m e n ty dla k o n stru k to ra klasy w e k t. ' Oczywiście tak przeładowany operator new wymaga stosownego dla siebie operatora d e l e t e . Zobaczymy to niebawem. C o d o treści (cia ła)sam e g o o p e r a t o r a new ( ) , to - j a k w id z is z - m e m a tam mc o d k ry w czeg o . N a sz o p erato r o prócz tego, że reze rw u je p am ięć za pom ocą globalnego o p e ra to ra new - w y p isu je n a ekranie k ró tk ą inform ację. O czyw iście m oglibyśm y b y ć przebieglejsi.
Kiedy zatem przeładowanie tego operatora może się naprawdę przydać? M yślę, że np. w te d y , g d y w p ro g ra m ie zm uszeni jesteśm y po słu g iw ać się tym o p erato rem (p rz y tw o rzen iu o b iek tó w danej klasy) w ielo k ro tn ie - a, znając n a tu rę d anej klasy , czujem y, że m o żn a by w jej p rz y p a d k u jakoś to rezerw o w an ie u s p ra w n ić , przyspieszyć. N a p rzy k ła d zrobić to h u rte m , co ileś now ych obiektów . M ożna też p rz e ła d o w a n y o p e r a t o r n e w () zasto so w ać d o m onitorow ania zap a su p am ięci. M ożem y p ro w ad zić statystykę, jaki p ro c e n t zap asu pam ięci zajm ują o b ie k ty tej w łaśn ie klasy.
W Gdyby zabrakło pamięci... W naszym p rz y k ła d o w y m p rz e ła d o w a n iu - sam a rezerw acja pam ięci w tym przeładowanym o p erato rze new o d b y w ała się p rz e z w y w o łan ie g o >a nego o p erato ra new . W takim p r z y p a d k u nie ma p ro b lem u , g d y zapas p am ięci się w yczerpie. Po pro stu g lo b a ln y o p erato r new m a taki zw yczaj, że w ó w czas albo rzuci w yjątek b a d a l l o c , alb o zw róci w sk aźn ik o w artości zerow ej - i to otrzym a ktoś, kto w yw o łał iytsz o p erato r. Jeśli jednak w ciele n aszeg o p rzeład o w an eg o o p erato ra new nie k o rz y sta m y w tak p ro sty sposób z u słu g g lo b a ln eg o operatora new - w ó w czas sam i m u sim y zad b ać o p o in fo rm o w an ie tego, kto w y w o ła ł nasz o p erato r, że rezerw acja się nie p o w iodła. Innym i słow y: Jeśli rezerw ację robim y jakoś inaczej - na p rz y k ła d h u rto w o , lub z pom ocą sta ro ż y tn y c h funkcji bibliotecznych m a l l o c (ang. memory allocation alokacja, reze rw ac ja pam ięci), w ó w czas w ciele p rz e ła d o w a n e g o o peratora new p o w in n iśm y tę sytuację n iem ożności rezerw acji o b szaru pam ięci jakoś u w zg lęd n ić. Co robić w takiej sytuacji? O p o stęp o w an iu w p rz y p a d k u , gdy zab rak n ie pam ięci - ro zm a w ialiśm y d a w n o tem u , na stronie 309 (w § 8.11.6). Tu, zatem , p rz y p o m n ę tylko, że w ó w czas zasadniczo m ożem y.
< 1
9
862
Rozdz. 19. Przeładowanie operatorów O peratory n e w , new [ ] ♦> rz u c ić w yjątek typu b a d _ a l l o c , ja k o rezu 1ta t pracy o p era tora zw rócić zero, (to sp ra w i, że kons tru k to r nie zo sta n ie uru ch o m io n y ) - "jeśli nie udało się kupić działki, nie będzie budowy domu M o żem y też w yw ołać z w n ętrza operatora new jak ąś "alarm o w ą” fu n k cję, k tórą użytk o w n ik na taką sytuację specjalnie p rz y g o to w a ł (i w y z n a czy ł w yw ołaniem : s e t _ n e w _ h a n d l e r ) . N ajb ard ziej eleganckim ro zw iązan iem jest to, z rz u c e n ie m w yjątku.
Jak sięgnąć do zasłoniętego? jeśli p rz e ła d o w u je m y na u ży tek klasy (w e k t) o p erato r new , to w ersja globalna tego o p e ra to ra zostaje zasłonięta (tylko dla tw orzenia o b ie k tó w tej klasy w ek t). M im o to, m o żn a jednak p o słu ży ć się tą zasłoniętą g lo b a ln ą w ersją o p erato ra new. W y g lą d a to tak: wekt
*wskl,
wskl wsk2
= =
*wsk2;
new wekt(l, 2, : : n e w w e k t (10,
3); 20,
30);
// w e rsja // w e rsja
d la k la sy w e k t g lo b a ln a
W id zim y tu d w a w arianty w y w o łan ia. P ierw sze u ru c h a m ia o p erato r new p rz e ła d o w a n y na uży tek klasy w e k t. O p erato r ten z a sła n ia glo b aln y (tradycyj ny) o p e ra to r new. Jeśli z jak ich ś p o w o d ó w chcem y, by rezerw acja o b iek tu tej klasy o d b y ła się globalną w ersją o p erato ra new , to p rzed o p erato rem sta w ia m y zn ak : : - co oznacza, ż e ch o d zi o wersję globalną. (Jest to, jak p a m ię ta m y , p o w szech n a prak ty k a d o cieran ia do zasłoniętych globalnych n azw ).
19.15.2
Przykład przeładowania operatora new [ ] N ajpierw p y tan ie: Jeśli o p erato rem new zap ra g n iem y stw o rzy ć tablicę obiek tów klasy w e k t, to czy k o m p ilato r użyje p o k azan eg o p rz e d chw ilą o p erato ra new (p rzeład o w a n eg o dla k lasy w e k t) ? O d p o w ied ź: N ie. T am ten o p erato r new służy k o m p ilato ro w i tylko dla tw o rze nia pojedynczego obiektu klasy w e k t. Do tw o rzen ia tablicy takich obiektów , ko m p ilato r u ż y w a tylko o p erato ra new [ ]. P ytanie jeszcze bardziej podchw ytliw e: C zy zatem k o m p ilato r użyje w tej sytuacji g lo b aln eg o o p erato ra n e w [] ? O d p o w ied ź: T ak b y się rzeczyw iście stało, gd y b y nie n a sz e m ałe zaniedbanie. M oże p am ięta sz , że jeśli klasa m a m ieć obiekty tw o rz o n e w zap asie pam ięci op erato rem n e w [ ] , to p o w in n a m ieć k o n stru k to r d o m n ie m a n y - czyli taki, który m o żn a w yw ołać bez żad n y c h arg u m en tó w . M y w k lasie w e k t takiego nie zdefiniow aliśm y... To nic - z a ra z będzie. W ystarczy, że deklarację k o n stru k to ra
863
Rozdział. 19. Przeładowanie operatorów O peratory d e l e t e , d e l e t e [ ] w e k t ::w e k t ( d o u b l e
a,
double
b,
double
c);
zam ien isz na w e k t ::w e k t ( d o u b l e
a
=
0,
double
b
=
0,
double
c
=
0);
O d tej p o ry k o n s tru k to r ten m ożna w y w o łać n aw et bez żad n y c h arg u m en tó w .
Zwracam uwagę, że obecność konstruktora domniemanego jest wymagana - niezależnie od tego czy operator new[] przeładowujemy czy nie. Do tworzenia tablicy obiektów jakimkolwiek operatorem new [] konstruktor domniemany jest wymagany i już. Skoro m am y ju ż w klasie w e k t k o n stru k to r d o m n iem an y , a d la tejże klasy nie p rz eład o w aliśm y o p erato ra new [ ] (jest je d y n ie new), to, w p rz y p a d k u tw orzenia (w zap asie pam ięci) tablicy obiektów tej k la s y -k o m p ila to r sięgnie p o globalną w ersję o p e ra to ra new [ ].
I
Jeśli, z jakichś p o w o d ó w , działanie tego g lo b aln eg o new [ ] n am nie o d p o w iad a - m o żem y w k lasie w e k t stw orzyć sw ój w ła sn y o p erato r new ( ] . O to, jak prosto w y g ląd a p rz y k ła d o w a definicja: void
*
w e k t ::o p e r a t o r
new[](size_t
rozmiar)
{ cout
<< "Kreuje tablice obiektów << r o z m i a r << endl; return ( n e w c h a r [r o z m i a r ] ) ;
klasy
wekt,
rozmiar=
"
)
Tu też może zabraknąć pamięci P rzy p o m in am , ż e jeśli chcielibyśm y robić tę rezerw ację w jak iś sprytniejszy sposób, to p o w in n iśm y u w zg lęd n ić sytuację, g d y zab rak ło p am ięci. Podobnie jak w p o p rz e d n im parag rafie - i tutaj su g ero w ałb y m rz u c e n ie w yjątku b a d a l l o c , lu b z w ro t ad resu zero.
G lobalne o p erato ry d e l e t e , o raz d e l e t e [ ]
-s łu ż y do oddawania obszarów pamięci rezerwowanych operatorami new, I oraz new [] Tych globalnych o p erato ró w d e l e t e , d e l e t e [ ] u ż y w a m y w sto su n k u d o ty p ó w w budow anych^ ale także m o żem y ich użyć w s to su n k u do klas nie m ających swojej, p rzeład o w an ej w ersji tych operato ró w . Jeśli - z jakichś p o w o d ó w - te globalne w ersje nam nie o d p o w ia d a ją , m ożem y (na uży tek w y b ran ej klasy) zdefiniow ać p rz e ła d o w a n e w ersje d o w o ln eg o z nich.
9
864
Rozdz. 19. Przeładowanie operatorów O peratory d e l e t e , d e l e t e [ ]
Jeśli chcemy dla danej klasy zdefiniować operatory delete, lub de-ł lete [ ], to musimy pamiętać o tym, że: Funkcje o p e r a t o r d e l e t e oraz o p e r a t o r d e l e t e [] p o w in n y być| staty czn y m i funkcjam i składow ym i klasy. N aw et jeśli w ich deklaracjach zapom nim y napisać ten przydom ek s t a t i c , to kom pilator i tak funkcje te uczyni ty p u s t a t i c . ♦{♦ P ierw szy arg u m en t tych funkcji operatorow ych m a być ty p u v o id * .
(czyli wskaźnik do czegoś nieokreślonego, po prostu: "adres i już") Funkcje te mają zw racać m a typ v o i d (czyli nic).
19.16.1
Prosty przykład przeładowania d e l e t e Oto realizacja przeład o w an ej w ersji operatora de 1 e t e d la naszej klasy w e k t : v o id w e k t : : o p e r a t o r d e l e t e ( v o i d * wsk) 1 c o u t << "K asu je o b i e k t ! \ n " ; d e l e t e wsk; 1 W ciele o p e ra to ra w idzim y jeszcze raz op erato r d e l e t e . T eraz stoi p rz y nim nic w skaźnik d o obiektu klasy w e k t, ale w skaźnik d o v o i d , zatem teraz ru sz y do pracy g lo b aln a w ersja tego o p erato ra. To w szystko.
Sięgamy po zasłonięty Jeśli, p rz y k aso w an iu jakiegoś z obiektów tej klasy w e k t , chcielibyśm y, m im o w szystko, sk o rzy stać nie z tej, ale raczej z globalnej w e rsji o p erato ra d e l e t e w ystarczy zastosow ać o p erato r zak resu ': Załóżm y, że kasujem y te o b iekty, które w y k reo w a liśm y w p o p rzed n im paragrafie. delete
wskl;
:: delete w sk2;
// wersja przeładowana // zasłonięta wersja globalna
Z apytasz p ew nie: C zy obow iązuje n as konsekw encja - to zn aczy : jeśli obiekt k reo w a liśm y p rzeład o w an y m o p erato rem n e w d la danej klasy - p o w in - j niśm y także zlik w id o w ać tym p rz e ła d o w a n y m o p erato rem d e - I le te ? Raczej tak. To oczyw iście zależy od tego, co w funkcji o p e ra to ro w e j robione jest dodatkow o, jeśli w wersji p rz e ła d o w a n e j o p erato ra n ew - ro b isz coś d o d a t kowo, choćby staty sty k ę n aro d z in i istniejących o b ie k tó w - to likw idacja o b ie ktu o p erato rem globalnym , nie u a k tu a ln i Twojej sta ty sty k i o d n o to w u jąc zg o n . I
865
Rozdział. 19. Przeładowanie operatorów Program przykładowy na zastosowanie operatorów new , d e l e t e
19.16.2
Prosty przykład przeładowania d e l e t e [ ] Jeśli tw o rzy liśm y tab licę obiektów przeładow anym dla d an ej k lasy operatorem new [ ] , to lik w id o w a ć tę tablicę powinniśmy o peratorem d e l e t e [] rów nież p rz eład o w an y m d la tej klasy. P o w in n iśm y d la teg o , że jeśli "n a sz ' operator new [ ] robi co ś d o d atk o w o , to "nasz" d e l e t e [ ] n ajp raw do p o d o b n iej um ie na to o d p o w ie d n io zareagow ać. O to p rak ty c zn a realizacja operatora d e l e t e [] p rz e ła d o w a n e g o dla klasy w e k t. v o id w e k t: : o p e r a t o r d e l e t e ( ] cout
<<
d e l e t e []
"Kasuje
tablice
(v o id * wsk)
obiektów
klasy
wekt\n";
wsk;
} Jak w idać, tu taj o p e ra to r ten jedynie w ypisuje na ek ran ie inform ację o likwidacji tablicy, a p o te m w y w o łu je już globalną wersję op erato ra d e l e t e [ ] . Globalną, bo przecież w sk a ź n ik w sk jest typu v o id *
19.17 P r o g r a m p r z y k ł a d o w y n a z a s t o sowanie o p e r a t o r ó w
new, d e l e t e Z obaczym y tu z n a n ą nam klasę w e k t, dla której p rz e ła d o w a n e zostają opera tory new, new [ ] , d e l e t e , d e l e t e [ ] . # in c lu d e < io s tr e a m > u s in g n am esp ace s t d ;
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII/IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIMMMMMIMMMMM c l a s s w ekt { p u b li c f l o a t x, y, z; //—
k o ns t ru kt or ----
w e k t ( f l o a t a= 0 , ( l //—
«
f l o a t b=0, f l o a t c=0) : x ( a ) , y ( b ) ,
kilka z w y k ł y c h funkcji składowych
v o id p o d w o je n ie () { x *= 2; y *= 2; z *= 2; l II------------------------------------------v o id p o k a z ( ) < c o u t << " \ t x = "<< x « " , y= " « y << ", z= ”« z << e n d l; ) , , v o id * o p e r a t o r n e w ( s iz e _ t ro zm iar) ; v o id o p e r a t o r d e l e t e ( v o i d * w sk);
z (c ) / / V
866
Rozdz. 19. Przeładowanie operatorów Program przykładowy na zastosowanie operatorów new , d e l e t e static static
void void
*
operator operator
new[](size_t rozmiar); d e l e t e [ ] (void * w s k ) ;
// //
///////////////////////////////////^ //********■*"************************************************** void
*
w e k t ::o p e r a t o r
new(size_t
//
rozmiar)
®
.
{ cout
<<
"Kreuje
pojedynczy
obiekt,
rozmiar=
"
<<
rozmiar
<< endl; return (new c h a r [ r o z m i a r ] ) ;
Z /***********************************************************1^* void
w e k t ::operator
cout
<<
delete
"Kasuje
delete(void pojedynczy
*
wsk)
//
V
obiektNn";
wsk;
//************************************************************* void
*
w e k t ::o p e r a t o r
cout
<<
"Kreuje
n e w [] (size_t
tablice
//
rozmiar)
obiektów
klasy
wekt,
rozmiar=
&
"
<< r o z m i a r << e n d l ; return (new c h a r [ r o z m i a r ] ) ;
void
w e k t :: o p e r a t o r
d e l e t e []
(void
*
wsk)
/ / O
I cout
<<
delete
"Kasuje []
tablice
obiektów
klasy
wekt\n";
wsk;
//************************************************************* int
main()
( cout
<<
"Operatorem
wekt
*wl;
wl
new wekt(0,
=
new
1, 3)
z
klasy
wektor..."
«
endl;
//©
;
w l - > p o k a z (); cout
<<
wekt
*
"\nOperatorem w2
=
: :new
new
w e k t (2,
globalnym..." 2,
<<
endl;
/ / W
2);
w 2 - > p o k a z (); cout
<<
wekt
*
"\nOperatorem w3
new[]
z
klasy
wektor..."
<<
endl;
//O ©
new wekt[10];
=
w 3 - > p o k a z (); cout
<<
" ----------- P r z y s t ę p u j e m y
do
k a s o w a n i a -------- "
delete wl; cout
<<
<<
delete cout
<<
"Po
skasowaniu
wl
(pojedynczego)\n"
<<
skasowaniu
w2
(globalnego)\n"
<<
w3
(tablicy)\n"
endl;
endl;
//O © endl;
//O ©
[] w3; "Po
endl;
//O©
"Po
::delete w 2 ; cout
<<
skasowaniu
<<
867
Rozdział. 19. Przeładowanie operatorów Program przykładowy na zastosowanie operatorów new , d e l e t e
oto wydruk ekranu po zakończeniu tego programu Operatorem new z klasy wektor... ^ Kreuje
pojedynczy
x=
0,
y=
obiekt, 1/
z=
rozmiar-
lz
3
Operatorem new g l o b a l n y m . .. x= 2, y= 2, z= 2 Operatorem n e w [] z klasy wektor... . Kreuje tablice obiektów klasy wekt, rozmiar x= 0, y= 0, z= 0 ________ Przystępujemy do kasowania -
12n
Kasuje pojedynczy obiekt Po s k a s o w a n i u wl (pojedynczego) Po
skasowaniu
Kasuje Po
tablice
skasowaniu
w2
(globalnego)
obiektów w3
klasy
wekt
(tablicy)
Kilka zdań komentarza O Oto konstruktor w klasie w e k t . Jak widać, teraz w sz wartości domniemane. Zatem jest to konstruktor d o m m e m a n ^ c z ^ fa k a to r y można w yw ołać bez żadnych argumentów. Będzie nam t c ^ , tb U c rtttektakiego konstruktora nie można by tworzyć (operatorem - ' I ) tów klasy w e k t . © Definicja przeładowanego (na użytek klasy w e k t ) operatora n e w . Deklaracja jest w © i widzimy, zenie ma tam słowa s t a t i c. Mozę przez zapomnLnie, może przez lenistwo, a może w celach ma znaczenia - i tak kompilator uczyni ten operator new statyczną funkcją składową tej klasy. Operator ten służy do tworzenia pojedynczych obiektów klasy w e k t . Nte zostanie użyty przy tworzeniu tablicy. (Do stworzenia tablicy w zapasie pamteci m oże zostać tylko użyty operator n e w [ ] ). Patrząc na ciało tego operatora, widzimy, że - w naszym przypadku - operator ten po prostu wypisuje na ekranie informację o tym, ze pracuje, a następnie globalnym operatorem n e w rezerwuje pamięć, n sp ^
lako rezultat swej pracy zwraca adres tak zarezerwowanej pamięci. To jest „ S c e s e n d a tego, co powinien zrobić - zwrócić wskazmk do mte,sca, gdzte m oże zostać założony nowy obiekt. Reszta, to już nasza własna pomyslowość. Dveresia I Na przykład można sobie wyobrazić, ze operator ten przy pierwszym swoim uruchomieniu rezerwuje obszar pamięci zdolny pomieścić wiele przyszłych obiektów - „Zakup dużego obszaru gruntu pod budowę osiedla .
19
868
Rozdz. 19. Przeładowanie operatorów Program przykładowy na zastosowanie operatorów new , d e l e t e Potem , kolejne uruchom ienia zw racały b y p o p ro stu ad resy róż nych miejsc tego obszaru - „ Wskazanie działki na której ma stanąć właśnie budowany nowy dom", jj O p erato r ten oczyw iście p ro w ad ziłb y księgę, w której notow ałby sobie, które to fragm enty (działki) są obecn ie ju ż zajęte, a które m ożna udostępniać. O Jeśli w k lasie w e k t przeładow aliśm y operator new, to najczęściej p o trzeb n y jesj też p rz e ła d o w a n y operator d e l e t e . O to jego definicja. (D eklarację - rów nież z z a p o m n ia n y m /p o m in ię ty m p rzydom kiem s t a t i c w id z im y w m iejscu 0 ) . Praca te g o o p erato ra polega u nas po prostu na w y p isan iu na ek ran ie informacji o lik w id acji obiektu, a następ n ie w yw ołaniu g lo b aln eg o o p e ra to ra d e l e t e , k tóry o d d a zarezerw o w an y obszar pamięci. D ygresja II. G d y b y śm y przeład o w an ia o p e ra to ra new u ż y li tak, jak sugerow ałem w poprzedniej dygresji I, to tu taj o p e ra to r d e l e t e ' po w in ien jedynie zanotow ać sobie, że o b szar p o tym , lik w id o w a nym w łaśnie obiekcie jest od tej p o ry w o ln y , czyli jeśli za chw ilę ktoś w yw oła zn o w u operator new dla klasy w e k t, m o żn a mu tę, zw olnioną w łaśn ie działkę, przydzielić. G dyby zaś o p erato r zobaczył, że oto lik w id u je się już o statn i obiekt tego typu - „ Wszystkie domy na danym osiedlu zostały zburzone", to m oże w yw ołać globalny operator d e l e t e i zlik w id o w ać rezer wację całego o b szaru . - "Sprzedać grunt, na którym kiedyś stało osiedle domków, jako już nie potrzebny".
Dla tablic są specjalne operatory 0 Jeśli chcielibyśm y mieć jakiś szczególny sposób tw o rzen ia tablic obiektów ty p u w e k t w za p a sie pam ięci, to pow inniśm y w tej k lasie w e k t d o k o n ać p rzeład o w an ia o p e ra to ra new [ ]. rtS P U W AGA: Jeśli tego nie zrobim y, to nie licz na to, że funkcję tę w ykona p rz e ła d o w a n y d la tej klasy operator new. W ted y k o m p ilato r p ow ierzy to zad an ie g lo b aln em u op erato ro w i new [ ] . D eklarację tego p rzeład o w an eg o operatora new [ ] w id z isz w O . Tym razem nie zap o m n iałem o p rzy d o m k u s t a t i c . W ciele te g o k o nstruktora w ypisujem y na ek ran ie inform ację o tym , pracuje w łaśnie te n operator. W ypisana zostanie też w arto ść s i z e _t r o z m i a r . Jest tej w artość określająca ile bajtów m a zostać zarezerw o w an y ch d la w y konania tego zadan ia.
. . . . , . 1 To znaczy, że jeśli rezerwujemy tablicę dziesięciu obiektów, z których każdy, ma rozmiar 5 bajtów, to wartość r o z m i a r zoyniesie 10 5 = 50. © Definicja p rzeład o w an eg o dla k lasy w ek t o p erato ra d e l e t e [ ] . (D ek laraca je sti w © ). O p e ra to r ten w ypisuje informację o tym , że w łaśn ie pracuje, po czym: uży w a g lo b aln eg o operato ra d e l e t e [ ] .
Rozdział. 19. Przeładowanie operatorów Przeładowanie globalnych operatorów new , n e w [ ] ,
869 d e le te ,
d e le te []
Zajrzyjmy do funkcji m ain, w której korzystamy z właśnie zdefiniowanych operatorów. © Definicja w zap asie pam ięci pojedynczego ob iek tu klasy w e k t. W id zisz tez a rg u m e n ty k o n stru k to ra. K om pilator posłuży się tu w ięc n aszy m p rz e ła d o w a nym o p erato rem new ( 0 ) , by zarezerw ow ać pam ięć, a n astęp n ie z b u d u je tam obiek t (w yw ołując k o n stru k to r z tym i w łaśnie arg u m en tam i). O tym , z e pracuje n a p ra w d ę „nasz" p rz eład o w an y operator new p rzek o n ać m ożem y się p atrząc na w y d ru k ek ran u . Jest tam tekst w ypisany p rz e z n asz o p erato r & W n astęp n ej instrukcji w y w o łu jem y funkcję p o k a z na r z e c z w łaśn ie z b u d o w a nego o b iektu. N a ek ran ie w id zim y po tw ierd zen ie, ze obiekt isto tn ie k ryje sobie w artości, k tó re w ysłaliśm y konstruktorow i. O O P rzeła d o w a n y na rzecz k lasy operator new zasłan ia globalną w ersję o p erato ra r.e w. T u w id zisz jak, m im o tego zasłonięcia, m o żn a m im o w szy stk o skorzystać z w ersji globalnej. Spójrz na w y d ru k ekranu. N ie m a tu żad n e g o tekstu pc cho d ząc eg o od o p erato ra new z klasy w e k t. To oczyw iste, globalny o p e ra to r new
O0
pracu je przecież w m ilczeniu. S tw o rzen ie (w za p a sie pam ięci) tablicy dziesięciu obiektów klasy w e k t. W tej sytu acji ru sza d o p racy n a sz p rzeład o w an y o p e ra to r new [ ] (W Przypominam: gdybyśmy go nie zdefiniowali - kompilator nie użyłby zamiast tego operatora new 0 , lecz posłużyłby się globalnym operatorem A by m o ż n a b y k /tw o rz y ć w zapasie pam ięci tablice obiektów danej klasy , klasa ta m u s i m ieć k o n stru k to r d o m n iem an y . Tu nie m a problem u - taki jest w łaśnie k o n stru k to r O .
Kasowanie obiektów O © K aso w an ie p o jed y n czeg o obiektu. Tutaj ru sza d o pracy nasz p rzeład o w an y o p e ra to r d e l e t e (© ) O O O to , jak m ożem y sk aso w a ć obiekt klasy w e k t za pom ocą globalnego ° P e ™‘0™ d e l e t e , m im o że jest o n zasłonięty. W ystarczy posłużyć Sie o p erato rem z ak re su : : O © K aso w an ie tablicy o b ie k tó w klasy w e k t o d b y w a się za pom ocą naszego opera tora d e l e t e [ ] (© ), o c z y m m ożna się przek o n ać p atrząc na w y d ru k ek a u.
W 19.18 P r z e ł a d o w a n i e globalnych o p e r a t o r ó w
9
new, n ew []
d e l e t e , d e l e t e [] T ak że i ten p a ra g ra f p ro p o n u ję opuścić p rzy p ierw szym czytaniu.
870
Rozdz. 19. Przeładowanie operatorów Przeładowanie globalnych operatorów new , n e w [ ] , d e l e t e , d e l e t e [ ] W p rz y p a d k u w szystkich o m aw ian y ch w tym ro zd ziale o p erato ró w - p rz e ła d o w y w ać m ogliśm y je tylko na u ży tek obiektów d an ej klasy. O p erato ry new , new [ ] , d e l e t e , d e l e t e [] są w yjątkow e - m o żn a p rz eład o w ać także ich w ersje g lo balne. Tym sam ym przejm u je się kontrolę n a d g o sp o d a rk ą pam ięci w całym sw y m program ie.
„Obłędna! Stamtąd nikt nie wraca!" Jeśli p rze ła d o w u je sz globalny o p e ra to r new, to n in iejszy m p o p rz e d n ia , „oficjal na" w ersja przestaje istnieć. N ie jest zasłonięta, ty lk o n a p ra w d ę p rzestaje istnieć. M u sisz w ięc znać jakiś in n y sposób n ak łan ian ia Tw ojego sy stem u operacyjnego, by u d o stęp n iał Ci jakieś obszary pam ięci. Jednutn ze sposobów na to, jest wywołanie standardowej bibliotecznej funkcji malloc. Zwrot tak uzyskanego obszaru pamięci robi się funkcją
free. Jeśli się zd ec y d u jem y na p rz e ła d o w a n ie globalnego o p e ra to ra new, trzeb a pam iętać, ż e jest to p rzeład o w an ie „aż d o bólu" i o b o w iązu je od sam eg o początku p ra c y program u. Ma to b a rd z o p o w ażn e n astęp stw a. K iedyś w sp o m in ałem , że te, d o b rze n a m znane, s tru m ie n ie c o u t oraz c i n są obiektam i p ew n y ch klas. O tóż, jeśli p rz e ła dujem y g lo b a ln ą w ersję o p erato ra new , w ów czas o b ie k ty c i n , c o u t b ę d ą p o w staw ały p rzy użyciu w łaśn ie tego now ego g lo b a ln eg o o p erato ra new . Zatem p ie rw sz y raz do pracy ru szy on w ted y , g d y jeszcze „n a św iecie" n ie będzie ż a d n e g o c o u t (c in ) . Co to o z n a c z a w praktyce? Tu, iż w ciele tego o p e ra to ra nie m ożna um ieścić instrukcji korzystających z tych stru m ien i. N a p rzy k ła d takiej prostej instrukcji: cout
<<
"To
ja,
globalny
operator
new"
<<
endl;
K om pilator nie zabroni nam tej instrukcji, (bo n azw a c o u t jest n a jp ra w d o p o dobniej p o p ra w n ie zd ek laro w an a), ale w trakcie w y k o n a n ia w stępnej fazy p ro g ram u (p rz y tw orzeniu p rzy szłe g o stru m ien ia c o u t ) - n astąp i błąd. Tak, jakby cout był wskaźnikiem pokazującym na razie byle gdzie, a m y próbowalibyśmy korzystać z - na razie jeszcze nonsensownego - adresu. ; (Pamiętasz paragraf „Strzał na oślep"?)
C z y zatem nigdy nie możemy dokonać żadnego wypisu na ekran z wnętrza przeładowanego globalnego operatora new? O biektem c o u t rzeczyw iście nie m ożem y , ale są p rzec ież in n e sposoby. Znają je zap ew n e p ro g ram iśc i klasycznego C, g d z ie w y p isy w a n ie na ek ran o d b y w ało się za p o m o c ą w y w o ły w an ia s ta n d a rd o w y c h funkcji p u t s , p r i n t f (biblioteka
871
Rozdział. 19. Przeładowanie operatorów O peratory postinkrem entacji i postdekrem entacji, czyli koniec z niesprawiedliwością
s t d i o ) . Tym i sp o so b am i m o żn a się p o słu ży ć. Są to p rzecież funkcje, a me obiekty. p u t s (" T o
ja
globalny
i n t m = 7; p r i n t f (" W a r t o ś ć
m
=
operator %d
new");
została
, „ . wypisana , nu,
Jak to wygląda w praktyce? P o n iżej zo b aczy m y sk ład n ię prostego p rz e ła d o w a n ia o p erato ró w new , new [ ], d e l e t e d e l e t e [ ] , które d o rezerw acji o b s z a ró w pam ięci u ż y w a ją funkcji bibliotecznej m a l l o c , a d o zw rócenia n ie p o trz e b n e g o juz o b szaru pam ięci u ż y w a ją funkcji f r e e . Jeszcze raz zw racam u w ag ę: O bejrzyj te definicje, ale n aty ch m iast o nich zap o m n ij. #include
/ / +* +****•**************************** ******** ***************** void
*
operator
new(size_t
{
rozmiar) .
.
v o i d *wsk = m a l l o c (rozmiar) ; printf("GLOBALNYM Kreuje pojedynczy return
obiekt
„ ux \ n
w
<.m
,
. ,,
wsk;
} .j.^a.a.a.a.******************************* //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * void
operator
d e l e t e (void
puts("GLOBALNYM
Kasuje
*
wsk)
pojedynczy
obiekt");
free(wsk); //******************************************* void {
*
operator
n e w [ } (size_t
puts("GLOBALNYM return
Kreuje
rozmiar)
tablice
{
obiektów");
( m a l l o c (r o z m i a r ) ) ;
I /* ******************************* void
* ** * * ** * * ** * * ** * * *
operator
d e l e t e [ ] (void
puts("GLOBALNYM
Kasuje
:***************’
* wsk)
tablice
obiektów");
free(wsk);
.............................................mmmmmm* u u r f - i i i i i im i
19.19 O p e r a t o r y postinkrementacji i postdekrementacji, czyli koniec z niesprawiedliwością G d y m ó w iliśm y o p rz e ła d o w a n iu jed n o arg u m en to w y ch o p erato ró w inkrem entacji i d ek rem en tac ji zazn aczy łem , że ch o d zi o operatory p rzed ro stk o w e, czy li takie, k tó re w y stę p u ją p rz e d n azw ą o b iek tu . Innym i słow y chodzi o p re -in k re m e n ta c ję i p re-d e k re m e n ta c ję . ++i
--i
19
872
Rozdz. 19. Przeładowanie operatorów O peratory postinkrem entacji i postdekrem entacji, czyli koniec z niesprawie dliwością T y m czasem d la typów w b u d o w a n y ch m am y jeszcze „k o ń có w k o w ą" wersję tych o p e ra to ró w .
O p e ra to ry te w C ++ przez d łu g i czas były d y sk ry m in o w a n e w kw estii możliwo* ści p rz e ła d o w a n ia . Po p ro stu o p e r a t o r + + m ó g ł m ieć ty lk o jedną fo rm ę - tę p rz e d ro stk o w ą (preinkrementacja). N ie m ożna było z a te m w o b ec obiektu danej klasy w y k o n a ć tego, co łatw o w y k o n y w ało się d la ty p ó w w b u d o w a n y c h . P ew n ie pom y ślałeś: „-B ez p rzesad y , przecież m o żn a b ez teg o żyć. N ie jest to aż tak w a ż n y o p e ra to r." Cóż, k w e stia p rzyzw yczajenia. P am iętasz, jak d la ty p ó w w b u d o w a n y c h , za pom ocą w sk a ź n ik a , odczy ty w aliśm y elem enty tablicy? W y starczy ło w y rażen ie * (wsk++) i za je d n y m zam ach em o dnosiliśm y się do elem en tu tablicy o ra z p rzesk ak iw a liśm y na n a stę p n y element! P om yślałeś: „ - N o tak, w p rz y p a d k u w sk aźn ik a je st to b a rd z o p o trzeb n e, czy jednak je st to tak potrzebne w obec obiektu jakiejś k lasy ?" Jak to nie? P rzy p o m n ij sobie n asz zręczny wskaźnik —klasę, k tó rą zajm ow aliśm y się n ie d aw n o . K lasa ta rep rezen to w ała obiekty b ę d ą c e jak b y w skaźnikam i. T akim i jak zw y k łe, tylko m ąd rzejszy m i. I nagle tu się o k azu je, że do m ą d rz ej szego w sk a ź n ik a n iem o żn a zasto so w ać postin k rem en tacji, a d o g łu p szeg o tak! Te p o w o d y spraw iły, że d o C++ w p ro w a d z o n o m o żliw o ść p rzeład o w an ia także i o p e ra to ró w 'k o ń có w k o w y ch ' czyli występujących za nazwą obiektu.
Jak to jest zrobione ? O tó ż - p o n ie w a ż o p e r a t o r+ + p rz e d ro stk o w y jest ju ż z a ję ty - a b y zrobić coś, co pozw oli n a definicję d ru g ie g o o p erato ra+ + , d o p u s z c z a m y się p ew n eg o oszustw a: M im o ż e je st to p rzecież o p erato r pracujący na je d n y m a rg u m e n c ie , d efiniujem y go jako o p e ra to r d w u arg u m en to w y . N ic w tym d z iw n e g o - są przecież o p e ra tory, k tó re m ają form ę i je d n o - i d w u a rg u m e n to w ą . Jest przecież o p erato r & je d n o arg u m en to w y - czyli "pobranie a d re s u " & d w u a rg u m e n to w y - czyli "iloczyn bitow y" N ato m iast o p erato r+ + jest tylko je d n o a rg u m e n to w y . N o rm a ln ie dla innych o p erato ró w p ró b a definicji o p erato ra w yłącznie je d n o a rg u m e n to w e g o (np. o p e r a t o r ! ) jako d w u a rg u m e n to w e g o byłaby b łę d em . K om pilator o d razu protestuje. Tu jed n ak , w p rz y p a d k u o p e ra to ra + +, d zieje się to za bło g o sław ień stw em Bjarne S., który presją śro d o w isk a z m u sz o n y b y ł w ym yślić o p e ra to r postinkrem entacji. O to p rz y k ła d definicji tego o p erato ra dla naszej k lasy w e k t: w ekt w e k t : : o p e r a t o r + + ( i n t ) {
Rozdział. 19. Przeładowanie operatorów O peratory postinkrem entacji i postdekrem entacji, czyli koniec z niesprawiedliwością x
=
x
+
y
=
y
+
873
l; 1;
Z =
z + 1; r e t u r n *this;
} _ Jest to funkcja sk ład o w a klasy w e k t, a w ięc p ie rw sz y a r s u m e n ^ r z y c h o t ó j a k o wskaźnik this.Drugiargumentjest, jak w id zim y , typu i n t . O ' y liczby d o funkcji op erato ro w ej nie posyłam y - jest o n a g en ero ■ i oczyw iście nieużyw ani w definicji tej funkcji. W łjs c e argumen o „ form aln y ch w id zim y tylko nazw y ty p u . Tak ro b tltim y , k o rzy stać z jakiegoś w y sian eg o d o funkcji a rg u m e n tu (Zob. § 5.8 pt. N .e n a z w a
ńte
n y arg u m en t", str. 156). M ając tak ą funkcję o p erato ro w ą m ożem y zasto so w ać n asz o p e ra to r w w yrażen iach : we kt
w (3,
5,
6) ;
prc -inkrementacja
+ +W
p o si-in k rc m c n ta c ja
W++
Z apis ten ró w n o w aż n y jest następującem u: p re -in k rc m e n ta c ja p o s t- u ikrcm cntn cja
w . o p e r a t o r + + () w . o p e r a t o r + + (0)
O sz u stw o polega w ięc na ty m , że gdy k o m p ilato r z a u w a ż y zapis W++
u zn aje g o za zapis II za p is n o rm a ln ie n ie d o p u s z c z a ln y ! w++
0
CO w n o rm aln y ch w a ru n k ach jest przecież błędem . Jednak u m ieszczo n e po obiekcie, k o m p ilato r w yw ołuje d w u a rg u m e
o y p
•
D zięki te m u , m am y d w a ró ż n e o p e r a t o r y + + • G d y sym bol ++ stoi p rzed n a z w ą obiektu - to k o m pilator w ybiera w ersję je d n o arg u m en to w ą tego operatora. .
G d y sym bol ++ stoi za nazw ą o b iek tu - kom pilator w yw ołuje w ersję d w u arg u m en to w ą .
W aż n e jest tu sform ułow anie: dw a różne. B ow iem ciało jednej i d ru g iej wersji m oże być całkow icie o d m ien n e.
9
W Wszystko, co powiedzieliśmy o operatorze postinkrementacji, dotyczy analogicznie operatora postdekrementacji Jego p rz y k ła d o w a definicja d la klasy we k t
w e k t ::o p e r a t o r —
(int)
wekt
w y g lą d a tak.
874
Rozdz. 19. Przeładowanie operatorów Rady praktyczne dotyczące przeładow ania x = x - 1;
y = y - i;
z = z - 1; return
*this;
}
19.20 R a d y praktyczne d o t y c z ą c e prze ł a d o w a n i a Jako się rzek ło , m ech an izm p rzeład o w an ia jest m o żliw o ścią, z której m ożesz sk o rzy stać lu b nie. C h o d zi teraz o to, by - jeśli już z d e c y d u je m y się na p rz e ła d o w an ie je d n e g o lub kilku o p e ra to ró w - by zrobić to m ą d rz e , p o słu g u jąc się jakąś logiką. O to kilk a rad: N ie m a sen su p rzeład o w y w ać w szystkich o p erato ró w dla d an ej klasy. M o żesię b o w iem okazać, że w y k o n ałeś k aw ał dobrej, so lid n ej, nik o m u n iepotrzebnej roboty. K tóre o p e ra to ry zatem p rzeład o w y w ać? P rzed p rz y stą p ie n ie m d o p racy trzeba się zastan o w ić, jak tak a klasa w y g ląd a z z e w n ą trz - to zn aczy jakie w y k o n u je się operacje n a o b iek tach danej klasy. C zyli jakie m u si o n a mieć: pub liczn e funkcje składow e. K ied y ju ż to jest jasne, m ożna się zasta n o w ić, które z tych funkcji w ygodniej b y ło b y p rz e p ro w a d z a ć za pom o cą o p e ra to ró w . W ybór jest p rosty: chodzi o to, z ja k im sy m b o lem o p e ra to ra ta akcja się kojarzy. P ew ne o p e ra to ry od razu n arzu cą się sam e, (np. w p rz y p a d k u klasy w e k t o r e k p o ró w n an ie długości dw óch w e k to ró w z a pom ocą o p e ra to rów = = , < , > ) inne zaś o p e ra to ry w ym usi na n a s „w y g o d n ictw o " ( - jak w p rz y p a d k u p rzeład o w an ia o p e ra to ra [ ] dla w ielkiej tablicy złożonej na dy sk u ). N ie staraj się p rzeład o w y w ać na siłę. Jeśli n a z w a funkcji składow ej lepiej opisuje d z ia ła n ie tej funkcji, niż robi to w y g ląd o p e ra to ra , to lepiej pozostać p rz y funkcji. O c z y w iśc ie -je śli chodzi o d o d aw an ie , to od razu n a rz u c a się o p e r a t o r +, ale w p rz y p a d k u z a g w iz d a n ia - o p e r a t o r ! jest już b a rd z o o d leg ły m skojarzeniem . N ie cu d u j i n ie p rzeład o w u j bez sensu. Jeśli w p a d n ie s z na w eso ły pom ysł, żeby w Twojej klasie o p e r a t o r * ro b ił odejm ow anie, o p e r a t o r - d o d a w a n ie , a o p e r a t o r + m no żen ie, Tw oje poczucie h u m o ru s z y b k o się w yczerpie, g d y zo baczysz p o ty g o d n iu swój w łasn y zapis a
+
b * ( c - d * a )
P rzeła d o w a n ie p o w in n o słu ży ć raczej u p ro szczen iu czy tan ia, a nie p ro d u k cji łam igłów ek. C ała w spaniałość p rzeład o w an ia p o leg a n a zb liżen iu zap isu o p e racji na klasach, do pro sto ty zap isu operacji na ty p a ch w b u d o w an y ch . P o w ta rzam : p ro sto ty !
Rozdział. 19. Przeładowanie operatorów Rady praktyczne dotyczące przeładow ania
875
Jeśli p rz e ła d o w a łe ś o p e ra to r + oraz o p erato r = to nie sądź, ze tym sam y m masz au to m aty c zn ie o p e r a t o r + = albo o p e r a t o r ++. Są to zu p e łn ie inne funkcje o p erato ro w e i - jeśli chcesz się nimi p o słu g iw ać w obec obiektów danej klasy, to m usisz je także p rzeład o w ać. P rzy k ład e m na g ran icy ż a rtu jest p rz e ła d o w a n ie o p erato ra - o raz > i sp o d zie w an ie sie, że tym sam y m m am y o p erato r - > . M im o całej dow olności treści p rzeład o w an y ch funkcji o p eratorow ych - staraj się zac h o w ać logikę p ew n y ch zależności m ię d z y o peratoram i. Jeśli d la ty p ó w w b u d o w a n y c h poniższe w y ra ż e n ia są ró w now ażne: a = a + 1 a += 1 a++ to d o b rą p rak ty k ą jest trzy m an ie się tej k o nsekw encji dla klasy, która m a takim i o p e ra to ra m i się p o słu g iw ać. C hodzi tu o nic w ięcej, jak o siłę przyzw yczajenia, ale jest to w zg ląd b a rd z o w ażny. P o d o b n ie z zależnością o p erato ró w dostępu d o sk ład n ik ó w klasy: w sk a ź n ik -> s k ł a d n i k ( ‘ w sk a ź n ik ) . s k ł a d n i k w s k a z n ik [0 ] . s k ła d n i k Jeśli p rz e ła d u je sz te o p e ra to ry - zrób to tak, b y p o w y ższe w y rażen ia były sobie ró w n o w aż n e. D 5P
Jeśli o p e ra to r jest „n ieszk o d liw y " dla ty p u w b u d o w a n eg o - to znaczy nie z m ie n ia w arto ści zm iennej, na której pracuje - to staraj się, by jego odpow iednik dla k la sy ró w n ież niczego nie zm ieniał w e w n ą trz obiektu, p rzy którym stoi. N a p rz y k ła d je d n o arg u m en to w y o p e r a t o r - zasto so w an y w obec obiektu: i n t i = 4;
. . . . <- nic zmienia samego i
nie z m ie n ia w artości zm iennej i. Jest ona n ad al ta sam a (4). To tylko w yrażenie ( - i ) jako całość, m a w arto ść -4.
876
Rozdz. 19. Przeładowanie operatorów Pojedynek: O p erato r jako funkcja składowa, czy globalna P raw ie w sz y stk ie operatory m ogą zostać p rzeład o w an e na d w a sposoby - jako funkcja n ie-sk ład o w a (np. globalna) lub funkcja sk ład o w a. K tóry z tych sposo bów w y b rać? P orozm aw iajm y o tym .
19.21 Pojedynek: Operator jako funkcja skład o w a , czy globalna Skoro ten sa m o p erato r m ożna zd efin io w ać jako funkcję s k ła d o w ą albo funkcję nie-składow ą, to nasu w a się pytan ie: jak lepiej zrobić? Jed n o zn aczn ej o d p o w ied zi nie m a. Zależy to od tego, czeg o oczekujem y od operatora. O g ó ln ie m ożna po w iedzieć, że:
J e ś li o p e r a to r z m ie n ia w ja k iś s p o s ó b o b ie kt, na k tó ry m p ra c u je , to p o w in ie n b y ć z d e fin io w a n y ja k o fu n k c ja s k ła d o w a je g o kla sy. O p e ra to ry te w te d y z w ra c a ją l- w a r to ś ć . m jB M Tiir—IBM
Przykładem są tu takie operatory jak = czy też wszystkie operato ry w stylu + = , *=, itd. Operatory te (przynajmniej w stosunku do typów wbudowanych) modyfi kują obiekt stojący po ich lewej stronie. J e ś li n a to m ia s t o p e ra to r s ię g a p o o b ie k t p o to, b y p r a c o w a ć z nim bez m o d y fik o w a n ia g o to w ó w c z a s ra c z e j s to s u je się o p e ra to r w p o s ta c i fu n k c ji n ie -s k ła d o w e j.
Przykładem takich operatorów są choćby + &, ! Nie modyfikują one obiektu (obiektów), koło których stoją. Argument biorący udział w sumo waniu - sam nie ulega przecież zmianie. Skąd jest taka z a sa d a ? W ynika ona p o prostu z naszych p rz y z w y c z a je ń d o tego, jak te o p e ra to ry zachow ują się w sto su n k u d o ty p ó w w b u d o w a n y c h .
J e ś li o p e ra to r m a d o p u s z c z a ć , b y p o je g o le w e j s tro n ie s ta ł ty p w b u d o w a n y , to nie m o ż e b y ć fu n k c ją s k ła d o w ą . M u s i b y ć n ie -s k ła d o w ą .
C ho d zi o zap is x 2
+ +
2 x
g d z ie x jest o b iek tem jakiejś klasy. T eg o d ru g ieg o w y ra ż e n ia n ie d a się sto so w ać, gdy o p e r a t o r + jest funkcją sk ład o w ą. A by p rz e k o n a ć się dlaczego — w ystarczy tę lin ijk ę zap isać sobie w p o staci jaw n eg o w y w o ła n ia o p erato ra 2 .o p e r a t o r + ( x )
// U!
877
Rozdział. 19. Przeładowanie operatorów Pojedynek: O perato r ja k o funkcja składowa, czy globalna
To oczyw iście bezsens, d la te g o że nie m ożna zd efin io w ać o p erato ra jako funkcji sk ład o w ej klasy i n t . K lasy i n t po prostu nie m a - jest to ty p w b u d o w a n y . Jeśli jed n ak funkcja o p erato ro w a jest zrealizo w an a jako funkcja g lo b a ln a, to z a p is jaw n eg o w y w o łan ia o p erato ra w y g ląd a następująco. o p e r a t o r * (2,
x)
Tu T w oje oko p ro g ram isty już się nie buntuje, p raw d a? Jak w iesz, o p erato ry p rzeład o w u jem y po to, by uprościć sobie p racę z typami definiowanymi przez użytkownika. N ie zap o m in aj jednak, że ty p am i defin io w an y m i p rzez u ży tk o w nika są nie ty lk o klasy, lecz tak że ty p y w y liczeniow e ( e n u m ) . Tu jest jednak kłopot, b o ty p w yliczeniow y nie m o że mieć funkcji składow ych. Z a te m w sto su n k u d o enum - p o d o b n ie jak w p rzy p ad k u ty p ó w w b u d o w a n y ch - n ie m a w y b o ru . Jeśli p ierw szy o p eran d d an eg o o peratora m a byc typu w y liczen io w eg o (enum ), to ten o p erato r m oże być zrealizo w an y tylko jako funkcja nie-składow a. G d y chcem y do p u ścić d w a sposoby u ży w an ia o p erato ra, KlasaX
objx;
KlasaY
objy;
objx
+
objy
objy
+
objx
to d efin iu jem y ten o p e ra to r jako funkcję nie-składow ą. D okładniej: jako dwie o p erato ro w e funkcje nie-składow e: operator+(KlasaX, operator*(KlasaY,
KlasaY); KlasaX);
To czy obie b ęd ą realizo w ać tę sam ą akcję, zależy już od tego, co n ap iszem y w ciele tych funkcji o p erato ro w y ch . Jeśli napiszem y to sam o w obu - to m atem aty cy p o w ied zą , że d o d a w a n ie takich obiektów jest przem ienne. D o k o n u jąc w y b o ru sp o so b u realizacji operatora należy także p am iętać, że: Jeśli u ż y w a m y operatora, k tóry zd efin io w an y jest jako funkcja sk ład o w a, w ów czas w stosunku d o jego pierw szego arg u m en tu nie m o ż e zajść żad n a niejaw na konw ersja. M o w a o ty m arg u m encie, k tó ry zostaje przęsła ny za pom ocą w sk a źn ik a
tn is.
C zasem to d o b rze, czasem źle. Jeśli chcem y, by na obu arg u m en tach
a
i
b,
w
w y ra ż e n iu a
+
b
m o e ły zajść w razie p o trzeb y niejaw ne konw ersje, to w ów czas należy zdefinio w ać o p e ra to r jako funkcję nie-składow ą. Jeśli nie życzym y sobie, by na
< 1
9
878
Rozdz. 19. Przeładowanie operatorów Zasłona spada, czyli tajem nica operatora < < p ierw szy m arg u m e n cie zaszła jakakolw iek konw ersja, to d e fin iu je m y funkcje o p erato ro w ą ja k o fu n k c jęsk ła d o w ą. (D efiniujem y ją w klasie te g o argum entu). Łatw o to so b ie uzm ysłow ić i za p a m ię ta ć tak. O to z a p is w y ra ż e n ia (a+ b ) w form ie ja w n e g o w yw ołania funkcji operatorow ej:
<♦ a .operator+ (b) ♦> operator+( a , b )
/ / gdy operator jest f. składową //jeśli operatorjest f.nie-składową(np. globalną)
K onw ersje n iejaw n e m ogą zostać w y k o n an e tylko d la tych o b iek tó w , które są w e w n ą trz n a w ia su w yw ołania funkcji operator+. W pierw szej w ersji obiekt a nie jest w naw iasie, w ięc na nim n ie ja w n a konw ersja nie m oże się odbyć.
1 9 .2 2 Z a s ło n a s p a d a , czyli ta je m n ic a o p e ra to ra « W łaściw ie ju ż o d pierw szych stro n tej książki p o słu g u jem y się zap isem , w któ rym w y stę p u je o p erato r « cout << "Witamy na pokładzie \n";
S kądinąd w iem y , że d w u a rg u m e n to w y o p e r a t o r < < jest, w sto su n k u d o typu w b u d o w a n e g o ir.t, operato rem p o w o d u jący m p rz esu n ię cie b itó w o żąd an ą liczbę pozycji. Jak to w ięc m ożliw e, że tak i zapis: in t m = 2; c o u t « m; po w oduje w y p ro w a d z e n ie na e k ra n liczby zap isan ej w zm ien n ej ty p u int? O d p o w ied ź je st prosta. M amy tu d o czynienia z n ajzw y k lejszy m p rzeład o w a niem o p e ra to ra << . Z apis ten je st inaczej ro zu m ian y tak: c o u t . o p e r a to r < < ( m ) ;
cout jest eg zem p larzem obiektu klasy, która się n azy w a ostream. [skrót od: O u tp u t STREAM - stru m ień w yjściow y]. To dla tej k la sy d o k o n a n o p rzeład o w ania o p erato ra. P rzeładow anie jest m ożliw e, g d y je d n y m z a rg u m e n tó w jest obiekt typu zd efin io w an eg o p rzez u ży tk o w n ik a. T akim ty p e m zd efin io w an y m - choć bibliotecznym - jest w łaśnie klasa ostream. W n aszy m zap isie, po lewej stronie o p erato ra << stoi o b ie k t k la sy o s t r e a m , a po praw ej ty p w b u d o w a n y i n t . W y w o ły w an a jest w ó w czas fu n k cja operato ro w a zajm ująca się w y p isan iem na ek ran liczby i n t . Pytanie: Kto n ap isał tę funkcję o p erato ro w ą? O dpow iedź: T w órcy klasy o s t r e a m . N ie jest ona częścią sa m e g o języka C++, ale zaw iera ją biblioteka sta n d a rd o w a . Jeśli jakaś firm a p ro d u k u je ko m p ilato r C++, to nie d o po m y ślen ia jest, by d o niego nie d o łą czy ła biblioteki, w której znajdzie się definicja tej klasy. To, jak je st z b u d o w a n a ta k la sa , jest dla nas nieistotne. M y chcem y tylko z niej ła tw o k o rzy stać i m ieć m o żliw o ść w y p isy w ania na ek ran ie liczby ty p u i n t . P rzeła d o w a n ie o p e ra to ra « w łaśn ie d aje tę łatwość.
l ]
\ ]
879
Rozdział. 19. Przeładowanie operatorów Zasłona spada, czyli tajem nica operatora < <
O czyw iście jest kilka w ersji p rzeład o w an ia o p erato ra « na ok o liczn o ść pracy z ró żn y m i typam i a rg u m e n tó w np. double, char* . P o słu g iw aliśm y się już tym w ielo k ro tn ie, w y p isu jąc na ekranie liczby ty p u double, czy C -stringi. Je d n a k ta, n ap isan a kilka lat temu w USA, klasa biblioteczna ostream i d o sta rc z o n a nam w w ersji binarnej - nic nie w ie o naszej klasie we kt or e k, którą n ap isaliśm y sobie w czo raj po kolacji. D latego nie m ożem y sobie o b iek tu klasy wektorek po staw ić obok operatora « w instrukcji
wektorek w; cout «
w;
// je s z c z e
n ie p o p ra w n e
Tu z p o m o c ą m o że nam p rz y jś ć z ro b ie n ie p rz e ła d o w a n ia o p e ra to ra «
.
M o żem y p o raz kolejny p rzeład o w ać o p erato r « . Jest on d w u a rg u m e n to w y , w ięc a rg u m e n ty b ę d ą ty p u ostream i wektorek. Ju ż sły szę jak p ro testu jesz: „-Jak to ?-T ak bez w ie d z y i zg o d y klasy ostream?" Tak! W łaśnie bez w ie d z y i zg o d y klasy ostream. P am iętasz? -M ó w iłe m , że funkcja o p erato ro w a m oże być zw y k łą globalną funkcją. N ie m u si b y ć n a w e t zaprzyjaźniona z klasą. Tu właśn ie okazuje się, że gdyby musiała być zaprzyjażn iona, to byłoby źle. Dlatego, że w niej musiałaby być deklaracja przyjaźni z naszą klasą w ek t o r e k . Rozumiesz co to znaczy? To znaczy, że abyśmy mogli skorzy stać z przeładowania operatora « na rzecz tej klasy o s tr e a m i klasy w e k t o r e k - programista, piszący te kilka lat temu w USA klasę o s tr e a m , musiałby umieścić w swojej klasie deklarację przyjaźni z naszą
klasą w e k to r e k . D zięk i tem u , że p rzy ja źń nie jest w ym agana d o p rzeład o w an ia p isz e m y sobie funkcję o p e r a t o r « taką, w której arg u m e n t ty p u o stre am stoi na p ierw szym m iejscu, a n asz a rg u m e n t ty p u wektorek na d ru g im . C o tracim y p rz e z to, że klasa ostream nie d ek laru je z nam i p rzy jaźn i? T racim y d o stę p d o niep ublicznych sk ład n ik ó w klasy os t ream. Tyle, ż e nam na ty m d o stęp ie w cale nie zależy. Po prostu nie interesują nas w szy stk ie trybiki i kółka tej klasy. M y ch cem y tylko jej używ ać. Jest w ięc oczyw iste, że n asza funkcja nie m oże być funkcją sk ła d o w ą klasy ostream. Po pro stu nie m o żem y m odyfikow ać w n ętrza tej klasy bibliotecznej. Z atem ta m ożliw ość o d p a d a . Są je d n a k jeszcze 2 in n e m ożliw ości realizacji tej funkcji o p e r a t o r « . M oże to być: •
- funkcja nie-składow a (np. globalna),
•
- funkcja składow a klasy wektorek.
Ta o sta tn ia ew en tu a ln o ść o d p a d a z prostego p o w o d u : pierw szym arg u m en tem n a sz e g o o p erato ra << m u si być argum ent klasy ostream. 1ym czasem , gdybyś m y ó w o p erato r u czy n ili funkcją składow ą k la sy wektorek, to pierw szym a rg u m e n te m byłby u k ry ty w skaźnik this do obiektu klasy wektorek.
880
Rozdz. 19. Przeładowanie operatorów Zasłona spada, czyli tajem nica operatora < < N ic w ty m straszn eg o , w k o ń cu kolejność a rg u m e n tó w m o żn a by przestaw ić, ale sa m p o p a trz , jak w ó w czas w y g ląd ałb y zapis: wektorek w; w
«
cout;
/////
Jest to z a p is d ziw aczn y . N ie chcem y tak, bo już p rz y zw y cza iliśm y się do o d w ro tn e g o Z ostaje w ię c ew en tu aln o ść d ru g a : realizacja funkcji o p erato ro w ej jako funkcji n ie -sk ład o w e j (np. globalnej). W tym p rzy p ad k u nie m a w y m o g ó w , co do kolej* ności a rg u m e n tó w . M ożem y w ybrać, jak chcem y - oczyw iście w yb ieram y kolejność:
(ostream, wektorek) A z atem to ju ż u stalo n e. B ędzie to funkcja n ie-sk ład o w a (np. globalna). W łaściw ie ju ż b y m ożna n ap isać funkcję operator<< , ale w strzy m ajm y się se k u n d ę . Z a sta n ó w m y się, co z klasy wektorek b ę d z ie w y p isy w a n e na ekran: tylko sk ła d n ik i p u bliczne, czy też ta k że niepubliczne. ♦♦♦ Jeśli b ow iem chcem y korzy stać ze sk ła d n ik ó w niepublicznych klasy wektorek, to klasa wektorek p o w in n a n a m na to pozw olić, czyli p o w in n a zad e k laro w ać p rzyjaźń z naszą fu n k cją o p erato ro w ą. ♦♦♦ Jeśli b ęd ziem y k o rzy stać tylko z publicznych sk ła d n ik ó w klasy wektorek, to p rzy jaźń nie jest potrzebna. K a ż d y m o że o d czy tać p u b li c z n e sk ład n ik i, zatem m o że to też globalna fu n k cja operator<<.
O to kró tki p ro g ra m : #include usiną namespace std;
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH class wektorek ( public : double x, y, z; //--- konstruktor wektorek(double a=0, double b=0, double c=0) : x (a), y(b), z(c)
{ 1 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////^ /**★ ********★ ***★ *■*■*■*'*' + + ■**• +++ + * + +•* +++ +■*■+ *■ *********************/ | // globalna funkcja operatorowa // realizujące} przeładowania « dla naszej klasy wektorek ^★ ★ ★ ★ ★ ★ ★ *-*'************-* ***********'******''‘r,*'*',‘r*'*r*''*',lłr-*''****'*****'*'* * / i ostream
fi o p e r a t o r « ( o s t r e a m
&
ekran,
wektorek
fi w )
/ / U
ekran << "współrzędne wektora : ekran « ekran << w.x;
// O
ekran
// ©
<< ", « ", return ekran;
«
w .y
« w .z «
")"; //
©
881
Rozdział. 19. Przeładowanie operatorów Zasłona spada, czyli tajem nica operatora < <
/*************************************************************/ int main() we ktorek
w (1, 2, 3) , v, k(-10, -20, 100);
cout << "Oto nasze wektory \nwektor w —' ; cout << w; cout
cout
<< <<
" \ n w e k t o r v — " << v " \ n w e k t o r k — " << k
<<
endl;
<<
"Wywołanie
o p e r a t o r < < ( cout,
jawne
\n";
// 0
// ©
w);
Po wykonaniu tego programu na ekranie pojawi się Oto nasze wektory wektor w -współrzędne wektor v —współrzędne wektor k -współrzędne Wywołanie jawne współrzędne wektora :
wektora wektora wektora
(1,
2,
3)
(0 , 0 , 0 ) (-1 0 , - 2 0 , 1 0 0 )
(1, 2,
Ciekawsze punkty programu O Z w ró ć u w a g ę na a rg u m e n ty przesyłane do funkcji operatorow ej. P orów najm y to z w y w o łan iem tej funkcji operatorow ej cout
<<
w;
czy li operator«(
cout,
w);
O ba te ty p y w y w o łań funkcji operatorow ej w ystępują w m a in w miejscach o zn aczo n y ch jako 0 i ©• Z atem w definicji funkcji operatorow ej O w idzim y, że a rg u m e n t c o u t o d e b ra n y zostaje p rzez referencję. To przezw isko obiektu c o u t w naszej funkcji brzm i: e k r a n . T akże i obiekt klasy w e k t o r e k przesyłam y p rzez referencję, j l Taki sp o só b przy sp iesza przesłanie dużych obiektów . W ektorek m e jest duży, V w ięc ró w n ie d o b rze m ożna by przesłać go przez wartość. O Sym bole « , k tó re w id zisz w ew nątrz funkcji operatorow ej, to ju ż nie jest w y w o łan ie w ersji p rzeładow anej. Z auw aż, że po ich lewej stro n ie stoi obiekt klasy o s t ream , ale p o praw ej stronie czasem typ c h a r * (C -stringi), czasem typ
double (sk ład n ik i x, y, z). Co p ra w d a , p rzeład o w an ia na okoliczność argum entów typu (ostream,
double)
882
Rozdz. 19. Przeładowanie operatorów Zasłona spada, czyli tajem nica operatora < < nie ro biliśm y, ale zrobił to za nas p rogram ista, k tó ry pisał tę klasę o s t r e a m — bow iem c h a r * i d o u b l e są ty p am i w b u d o w a n y m i, a w ięc p o w szech n ie zn a n y m i już w czasie pisania tej klasy bibliotecznej. 0 Z a sta n ó w m y się, co p o w in ien zw racać jako re z u lta t taki o p e r a t o r < < . Sprawa jest p ro sta, nie m usi nic zw racać. Jego rolą jest ty lk o w y p isy w an ie n a ekran. Jed n ak jeśli zdecydujem y się zw racać rezu ltat b ęd ący referencją d o obiektu klasy o s t r e a m , to dzięki te m u oprócz zapisu: cout cout
<< <<
w; v;
cout
<<
k;
m ożem y tak że stosow ać 'k askadow y" zapis: cout
<<
w
<<
v
<<
k;
C Jak to m ożliw e? Otóż, o statn i zap is m ożem y zro zu m ieć jako: (
(cout
«
w)
«
v)
<<
k;
Już w id a ć o dpow iedź. W yrażenie: (cout
<<
w)
jako całość m u si m ieć re z u lta t b ędący o d p o w ied n ik iem
cout.
P o w y ższą linijkę m ożna za p isa ć też jako (operator<<(cout,
w)
)
Z atem , ab y śm y osiągnęli n asz cel, funkcja o p erato ro w a p o w in n a zw ró cić jako re z u lta t - referencję do p ie rw szeg o przysłanego d o niej arg u m e n tu . Tak ro b im y w naszym p rz y p a d k u . Z w róć u w a g ę na linijkę 0, a także na deklarację ty p u rezu ltatu zw racan eg o p rzez funkcję o p e r a t o r < < O . Z asto so w a n y przez nas sp o só b jest zg o d n y z tym , co zasto so w ał p rojektujący s ta n d a rd o w ą klasę o s t r e a m , d zięk i tej zgodności m o żem y w jednej k ask ad zie m ieszać je g o i nasze p rzeład o w an ie. cout
<<
"tekst"
<<
w
<<
5;
W W naszej k la sie w e k t ó r e k sk ła d n ik i x, y, z są p u b liczn e. C o by było, g d y b y były p ry w atn e? Jeśli klasa w e k t ó r e k chce, b y o p e ra to r « m ógł d o tych sk ła d n ik ó w b e z p o śre d n io zajrzeć, m usi je o p erato ro w i u d o stęp n ić za p o m o c ą deklaracji p rzy jaźn i. Słow em : w takim p rz y p a d k u w definicji klasy p o w in n a się zn aleźć d ek laracja p rzy jaźn i z op erato rem ostream
5)
&
operator<<(ostream
&ekran,
wektorek
Przypominam, że operator « jest zawsze lewostronnie łączny.
&w);
883
Rozdział. 19. Przeładowanie operatorów Zasłona spada, czyli tajem nica operatora < < To w szy stk o .
¥ M ożna b y zapytać: no to w łaściw ie na rzecz k tó rej klasy p rz e ła d o w a liśm y ope r a t o r - n a rzecz klasy o s t r e a m , czy klasy w e k t o r e k ? T ak s p ra w y staw iać nie m o ż n a. Tak sam o, jakbyś p rz y w y rażen iu . 2
+
3.14
zap y tał: d la jakiego ty p u p racu je w łaściw ie zn a c z e k +. Dla typu
int
czy dla
ty p u d o u b l e ? O p e ra to r p rz e ła d o w a n y jest na okoliczność, g d y p o jego lewej stro n ie zn ajdzie się ob iek t klasy o s t r e a m , a p o jego praw ej o b iek t k lasy w e k t o r e k . W y m ag a to takiej funkcji o p erato ro w ej, w której p ie rw szy arg u m e n tem b ęd zie typu o s t r e a m , d ru g i ty p u w e k t o r e k .
Przypomnijmy, jak podjęliśmy decyzję Funkcję o p erato ro w ą, która p racuje na dw óch arg u m e n ta c h klasy z re a liz o w a ć na trzy sposoby:
A iB
m ożna
1) albo jako funkcję sk ład o w ą klasy A, 2) albo jako funkcję nie-składow ą (np. g lobalną), 3) albo jako funkcję składow ą klasy B. W n a sz y m p rzy p ad k u p ie rw sz y sposób o d p ad ł, b o n ie chcieliśm y g rzeb ać w bi bliotecznej klasie o s t r e a m , k tó rą dostaliśm y w w ersji binarnej . T rzeci sp o só b o d p ad ł, bo u p arliśm y się, że a rg u m e n te m z lewej stro n y zn ak u « m a być ty p o s t r e a m , a nie ty p w e k t o r e k . Jak w ia d o m o , funkcja sk ład o w a ma z a w sz e ja k o pierw szy a rg u m e n t - obiekt swojej w łasn ej klasy. Z o stał d r u g i sposób - jako nie-składow a funkcja (globalna, lub z jakiejś p rz e s trze n i n azw ). Tak też postąpiliśm y.
W M yślę, ż e rozum iesz, iż sp ra w a przeład o w an ia o p e ra to ra »
(w czytyw ania):
w e k t o r e k w; cin » wekt;
w y g lą d a analogicznie. Z tą tylko różnicą, że m a m y tu d o czy n ien ia z klasą i s t r e a m (ang. input stream - strum ień w ejściow y), a jej k o n k retn y m obiektem je st w ła śn ie c i n . Oto realizacja tej funkcji operatorow ej: istream
&
operator>>(istream
&
klawiatura,
wektorek
&w)
< cout
6)
<<
"Współrzędna
x
:
Dla wtajemniczonych: ostatecznie byłyby na to bezpieczne sposoby - przez dziedziczenie.
884
Rozdz. 19. Przeładowanie operatorów R zut oka wstecz klawiatura
>>
w.x;
c o u t << "Współrzędna klawiatura » w.y; cout « "Współrzędna klawiatura return
»
y
:
z
:
w . z;
klawiatura;
D zięki p rzeład o w an iu m o żem y w pisyw ać ręczn ie d a n e o w sp ó łrzęd n y ch . Jeśli w p ro g ra m ie znajdzie się sekw encja wektorek
s; dane
dla
wektora
c i n > > s; cout << "Wypisujemy
cout
<<
"Podaj
to,
co
s
\n";
dostaliśmy
\n"
<<
s;
W ów czas na ekranie zob aczy m y Podaj dane dla wektora s Współrzędna x : 11 Współrzędna y : 2 2 Współrzędna z : 3 3 W y p i s u j e m y to, c o d o s t a l i ś m y współrzędne wektora : ( 11, 2 2 ,
33 )
O tych o p erato rach m ów ić będziem y d o k ład n iej w ro zd ziale p o św ięco n y m o p eracjo m w ejścia/w y jścia. Tu taj ty lk o nadm ienię, ż e o w iele ładniej by było, g d y b y oba zd efin io w an e p rz e z nas o p e ra to ry « i » by ły „sy m etry czn e". To z n a c z y g d y b y tek st w y p isy w a n y na e k ra n , był id entyczny z tekstem , którego s p o d z ie w a m y się p rz y w czy ty w an iu z k law iatu ry . Słow em : na klaw iatu rze p o w in n iś m y w y stu k ać tylko (11,
22,
33 )
a nie o czek iw ać d o d atk o w y ch p y ta ń o każdą ze w sp ó łrzęd n y ch . D laczego tak jest lepiej? W y o b raź sobie, że o p e ra to r « nie w y p isu je na ekranie, ale za p isu je d o pliku d y sk o w eg o . N ato m iast o p e ra to r » nie w czy tu je z k la w ia tury, ale w czy tu je z pliku d y sk o w eg o . W ó w czas, jeśli d o ch o w asz tej "symetrii", to o p e ra to r » to, co z a p isa ł o p e r a t o r « .
b ęd zie po trafił w czytać
Rozdział. 19. Przeładowanie operatorów R zut oka wstecz
885
S p ró b u jm y u p o rząd k o w ać so b ie w pam ięci z a g a d n ie n ia z tego ro zd ziału . *
R ozm aw ialiśm y o p rz eład o w y w an iu o p e ra to ró w je d n o - i d w u a rg u m e n to w y ch . je st to tech n ik a b ard zo p ro sta i nie niosąca żad n y c h n ieb ez pieczeństw '.
♦♦♦ P o zn aliśm y o p erato r p rzy p isan ia o fero w an y n am w p rezencie p rz e z k o m p ilato r i zobaczyliśm y, że w sto su n k u d o klas, których sk ład n ik am i są w sk a źn ik i - taki o p e ra to r m oże być n iezad o w alający . ♦> P o zn aliśm y p rz e ła d o w a n ie o peratora [ ], k tó re nie jest niczym tru d n y m , ale jeśli chcielibyśm y, b y w y rażen ie z ty m o p erato rem m ogło czasem stać p o lewej stronie z n a k u = a [4] = 1 0 ; to m u sim y spełnić w a ru n e k , by w definicji te g o operatora p am ięta ć o zn a c z k u &. ♦♦♦ P o zn aliśm y p rz e ła d o w a n ie operatora ( ), k tó re jest najłatw iejszym z m ożliw y ch . D o w ied zieliśm y się, że tylko ten o p e ra to r p o zw oli w ysłać d o funkcji o p erato ro w ej więcej niż d w a a rg u m e n ty . P o zn aliśm y p rz e ła d o w a n ie o peratora - > , k tó ry m oże nam się p rzy d ać w te d y , g d y b y śm y chcieli zdefiniow ać klasę ob iek tó w zachow ujących się jak w skaźniki. ♦♦♦ P o zn aliśm y p rz e ła d o w a n ie o peratorów n e w i d e l e t e , o których z w y k le w pierw szej chw ili m yśli się, jak o czym ś tru d n y m - jed n ak jest to sto su n k o w o proste. *
D o w ied zieliśm y się jaką sztuczkę trzeba zasto so w ać, by mieć o p erato ry po stin k rem en tacji i postdekrem entacji. W g ru n c ie rzeczy ch o d zi ty lk o o to, by w ew n ątrz n aw iasu napisać i n t i nic więcej!
♦♦♦ Z o b aczy liśm y jak p rzeład o w an o o p erato ry » i « , by słu ży ły do w y p isy w a n ia na ek ra n i w czytyw ania z k law iatu ry . N ie jest to nic tru d n e g o - i dość p ro sto m ożna to robić w stosunku do obiektów d o w o ln ej klasy. D lateg o rad zę pobaw ić się tak im p rzeład o w an iem w ( sto s u n k u d o jakiejś, w y m yślonej przez siebie, prostej klasy.
< 1
Już to k ie d y ś m ów iłem , ale te ra z przypom nę: p rz e ła d o w a n ie o p erato ró w nie w p ro w a d z a „now ej jakości" d o języka C++. To ty lk o in n y sposób w y w o ły w a nia funkcji. Sposób, który m o że nam uprościć zapis. Jest to w ię c sy m p aty czn e u d o g o d n ien ie, ale nie w ejście w inny w ym iar. W ten in n y w y m ia r w ejdziem y teraz d zięk i zag ad n ien io m o m aw ian y m w następnych d w ó ch ro zd ziałach .
9
886
Rozdz. 19. Przeładowanie operatorów Ćwiczenia WAiJ^
« iwwfWWHr.iWiw>iwJiWi>ii)i
m
m
m
H WI.WW II!'JWt»H^
19.24 Ć w i c z e n i a Wybierz poprawne zakończenia następującego zdania. Jeśli przeładowujemy operator do pracy z obiektami jakiejś klasy i realizujemy go jako funkcję nie-składową, to przynajmniej jednym z argumentów powinien być... a) obiekt tej klasy b) referencja obiektu tej klasy c) wskaźnik do obiektu tej klasy Które z poniższych operatorów nie mogą być przeładowane? a) c) e)
g) i) k) m) o) r)
Hi
■
# —
b) d)
## &
f) h)
*
j) 1) n)
? : delete new
P) s)
> n e w [] sizeof static >>
ca
.
_* d e l e t e []
m
Które z powyższych operatorów muszą zwracać rezultat ściśle określonego typu?
IV
Czy i jak można zamienić priorytet operatorów > oraz &? Ilu argumentowy jest operator T. Czy można sprawić, by był dwuargumentowy? Przeładowujemy operator &&. Są dw a sposoby jego realizacji. Ile argum entów będzie miała funkcja operator& & , jeśli zrealizujemy ją jako funkcję składową, a ile jeśli zrealizujemy ją jaką funkcję nie-składową (z jakiejś przestrzeni nazw, np. globalnej)?
IX
Czy można przeładować operatory na okoliczność pracy z typem wyliczeniowym (enum)? Czy argumenty przeładowanego operatora mogą mieć wartości domniemane? Czy są wyjątki? Czy w programie w zakresie globalnym mogą istnieć dwie funkcje o nazwie o p e ra to r * ? Czy operatory *, &, mogą zostać zrealizowane jako funkcje składowe statyczne ( s t a t ic)? Dlaczego? Przeładowany operator do pracy z obiektami danej klasy zrealizowany został jako funkcja nie-składowa (np. globalna). Czy i w jakich warunkach potrzebna jest deklaracja przyjaźni tej klasy z tą funkcją? Jeśli wobec obiektów danej klasy nie predefiniow aliśm y jakiegoś operatora, a mimo wszystko go użyjemy w programie, kompilator najczęściej zgłosi błąd kompilacji. Nie stanie się tak jednak w przypadku siedmiu operatorów. (To znaczy można je zastosować nawet bez definiowania ich). Wymień te operatory. Wobec obiektów danej klasy, jednoargumentowy operator ' ~ ' (tylda), można przeła dować na dwa sposoby. He argumentów w tych obu przypadkach - będą miały funkcje operatorowe. Wobec obiektów danej klasy, dwuargumentowy ten sam operator &&, można przełado wać na dwa sposoby. Ile argumentów w tych obu przypadkach - będą miały funkcje operatorowej.
Rozdział. 19. Przeładowanie operatorów Ćwiczenia
887
Jeśli mamy w programie klasę Ki chcielibyśmy mieć przeładowany operator + potrafiący dodawać obiekt typu long i obiekt klasy K. Np. K
objk;
//. .. 993815
+
objk;
Czy te n o p e r a t o r + m oże być funkcją globalną? C zy ten o p e r a t o r + m o ż e byc funkcją sk ład o w ą klasy K? W przypadku czterech operatorów U , O, -> jest wymaganie, by byty one zrealizo wane jako funkcje składowe (a nie np. globalne). Dlaczego jest to wymaganie? Co ma one kompilatorowi zagwarantować? Czy przeładowany operator = może zwracać rezultat typu v0j-^ •
Generowany automatycznie operator przypisania dla obiektów danej klasy - przypisuje według zasady „składnik po składniku". Wymień okoliczności, kiedy może byc to niewystarczające? Woperatorze przypisania można wyróżnić ttzy zasadnicze części. Jakie jest ich zadanie? W programie mamy obiekty m i p klasy k. W której z poniższych instrukcji pracuje konstruktor kopiujący, a kiedy operatora przypisania a) b)
m
=
p;
k
g
=
p;
Co możemy dodatkowo zyskać, jeśli zdecydujemy, by operator przypisania jako rezultat zwracał referencje od obiektu, na rzecz którego pracował (czyli obiektu stojącego po lewej stronie znaczka =)? Dlaczego w operatorze przypisania trzeba sprawdzać, czy nie odbywa się przypisanie tego samego obiektu, temu samemu? Jakie składniki klasy mogą spowodować, że operator przypisania me zostanie wygene rowany automatycznie? _ ....................___ _____— -- ------------- --------------llu-argumentowy jest operator [ ] ?Jeśli go przeładujemy, to ile argumentów ma funkcja o p e r a to r (]? _________ ________________ Co należy zrobić, by przeładowany operator [ 1 mógł być stawiany po lewej stronie operatora przypisania? Ile argumentów może mieć przeładowany o p e r a t o r () ? Napisz definicję klasy reprezentującej tablicę dwuwymiarową elementów typu d o u b l e Do poszczególnych elementów docierać mamy za pomocą funkcji o p e r a t o r f ) , której dw£ argumenty typu in t określać mają rząd i kolumnę w tablicy. Operator m a s* nadawać do postawienia po lewej stronie przypisania. Np. przypisanie do elementu szóstym wierszu i trzeciej kolumnie ma wyglądać tak: moja t a b l i c a (6, 3 = 0. 33; Przy przeładowaniu operatora -> są zalecenia, co do typu jego rezultatu (czyli wartości zwracanej przez funkcję oPerator-> ). Co może być rezultatem tego operatora? W linijce definicji „zręcznego wskaźnika" nie ma gwiazdki- do czego p r^ y k h śm y w przypadku wskaźników. Dlaczego nie ma tej gwiazdki? Gdyby była, to czego byłaby to definicja?
888
Rozdz. 19. Przeładowanie operatorów Ćwiczenia
W jednym z paragrafów tego rozdziału zobaczyłeś klasę „zręczny wskaźnik". Przerób operator przypisania w tej klasie tak, by możliwe było wielokrotne, „kaskadowe", przypisanie w stylu: a = b = c; Ć w ic z e n ia z w ią z a n e z o p e ra to ra m i new, delete, k tó ry c h o p is m o g łe ś o p u ś c ić. X Jaki typ rezultatu ma zwrócić przeładowany operator new, new [ ] i jakie ma znaczenie ten rezultat. / Jaki jest typu argumentu w przypadku przeładowania operatorów operator new, new [ ] i co jego wartość oznacza w przypadku każdego z tych operatorów? X Czy przeładowane operatory operator new, new [ ] ,delete, delete [ ] są zwykłymi funkcjami składowymi klasy? X Wymień podstawowe sposoby reakcji na niemożliwość rezerwacji pamięci odkrytą w trakcie w pracy przeładowanego operatora new. X Jeśli do tworzenia obiektów klasy K przeładowano operator new, to czy mimo wszystko da się tworzyć obiekty klasy K innym operatorem? Jak? X W klasie K nie ma przeładowanego operatora new [ ] , ale jest przeładowany operator new. W programie następuje instrukcja tworząca w zapasie pamięci tablicę obiektów klasy K. Jakiego operatora użyje w tym celu kompilator? X Jeśli definiujemy wdanej klasie przeładowane operatory delete, delete [ ] - to jaki ma być typ (i sens) ich argumentu, a jaki typ rezultatu? X Napisz definicję klasy, a w niej definicję przeładowanego operatora new, który przy tworzeniu pierwszego obiektu tej klasy od razu zarezerwuje pamięć na 100 takich obiektów. Potem, przy każdym następnym użyciu operatora new, będzie on po prostu przydzielał fragment z tego zapasu, który zrobił. Obiekty mogą być tworzone i likwido wane, a operator powinien umieć tą pamięcią gospodarować. W przypadku likwidacji ostatniego obiektu tej klasy, operator delete zwolni tę „100 obiektową" rezerwację. Dla jeszcze bardziej ambitnych: zdefiniuj dla tej klasy także podobnie działające operatory new [ ] i delete [ ] ? X Czy można przeładować globalne wersje jakichś operatorów - czyli takie, które pracują nawet dla typów wbudowanych? X Dlaczego przeładowany globalny operator new może (lub nie może) korzystać ze strumienia cout?
K onie c ć w ic z e ń na te m a t new i delete, czyli d a le j je s t "d la w s z y s tk ic h " Dla klasy K napisz deklarację operatora pre inkrementacji i post inkrementacji. Większości operatorów mamy wybór. Możemy je zdefiniować jako funkcje składowe lub jako funkcje z jakiejś przestrzeni nazw (np. globalne). Czy pamiętasz rady, kiedy której wersji użyć? Dokończ więc następujące zdania słowami „składowa" lub „nie-składowa". a) Jeśli operator zmienia obiekt, na którym pracuje, to powinien być raczej zdefinio wany jako funkcja... b) Jeśli operator ma dopuszczać, by po jego lewej stronie stał typ wbudowany, to powinien być zdefiniowany jako funkcja... c) Jeśli operator ma dopuszczać, by po jego lewej stronie stał typ wyliczeniowy, to powinien być zrealizowany jako funkcja...
889
Rozdział. 19. Przeładowanie operatorów Ćwiczenia
d) Jeśli chcemy, by na operandzie stojącym z lewej strony przeładowanego operatora mogła bvć (niejawnie) wykonywana konwersja standardowa, to operator powi nien bvć zrealizowany jako funkcja... Czy można zrealizować przeładowany operator «
jako funkcję składową klasy
ostream?
Do kilku klas z poprzednich rozdziałów dopisz definicje przeładowanego operatora « . Jedyną zmianą, którą Ci wolno zrobić w danej klasie jest umieszczenie w niej deklaracji przyjaźni. (Nawet w najbardziej rozbudowanej formie.... Wiesz, co mam na myśli?). W danym typie komputera największe liczby całkowite mogą być przechowywane w obiektach typu l o n g . Do obliczeń astronomicznych może być to za mało. Co prawda, większe liczby można wyrazić za pomocą typów zmiennoprzecinkowych, ale niestety nie mają one dużej dokładności. Rażący przykład: - jeśli w jakim obiekcie typu d o u b l e jest obecnie wartość milion, to po dodaniu do tego jedności - w obiekcie nadal jest milion. Pewnemu studentowi astronomii potrzebne jest wyrażenie wielkich liczb, z dużą dokła dnością. Ma on ten problem, że chciałby modelować przebieg lotu statku kosmicznego z Ziemi na Marsa - metr po metrze. Potrzebny jest więc typ całkowity o dokładności większej, niż ogólnie dostępny l o n g in t. więc z d e f i n i o w a ć sobie s w ó j w ł a s n y typ Nazwijmy te klasę p o z y c j a . W klasie tej na kilku słowach typu i n t (po kawałkach) zapisana będzie wielka liczba. W sumie oznaczała ona będzie jakiś punkt na drodze Ziemia - Mars, liczony w odległości od Ziemi z dokładnością do metra.
Student m u s i
Zrealizuj tę klasę. Załóżmy, że chodzi o liczbę, którą można zapisać na 60 bitach. Taką liczbę dodatnią powinna móc przechowywać klasa studenta astronomii. To jednak nie wszystko. Jeśli obiekty tej klasy mają reprezentować powoli zmieniającą się pozycję statku kosmicznego, powinno być możliwe stosowanie wobec obiektów tej klasy kilku operatorów. Operatory += oraz -= dla argumentów typu ( p o z y c ja , i n t ) - realizujące zmianę pozycji statku o określoną ilość metrów dalej/bliżej Ziemi. O p e r a t o r + dla a r g u m e n t ó w ( p o z y c j a , i n t ) - d o d a w a n i e pozycji i jakiejś liczby i n t . R e z u l t a t e m jest inna w a r t o ś ć pozycji, od legła o d danej pozycji o p e w n ą liczbę m e t r ó w .
Operator / (dzielenie) dla argumentów ( p o z y c j a , i n t ) - operator ten potrafiłby podzielić dany odcinek: Statek kosmiczny - Ziemia, na kilka części. (Np. coś jest w jednej trzeciej odległości między statkiem, a Ziemią). Operator przypisania. (Jeśli jest w Twojej klasie p o z y c j a konieczny). Dla ambitnych: Spróbuj przeładować operator « do wypisywania pozycji na ekranie w postaci długiego ciągu cyfr tej liczby całkowitej.
19
890
Rozdz. 20. Dziedziczenie Istota dziedziczenia
D zied ziczen ie
20
20.1 Istota dziedziczenia ziedziczenie to tech n ik a pozw alająca na defin io w an ie now ej k lasy przy w y k o rzy stan iu k la sy już w cześniej istniejącej.
D
Z ałó żm y , że m am y k lasę class
punkt
l public: double
x,
y;
punkt(double, void void
double
w y p i s z () ; przesuń(double,
);
// ko n stru k to r
double);
); Z ałó żm y też, że b a rd z o napracow aliśm y się, ab y zd efin io w ać tę klasę z jej w szy stk im i funkcjam i sk ład o w y m i. W reszcie w szy stk o m am y . I o to w p ro g ra m ie w y n ik a konieczność użycia d o d atk o w ej k lasy - p o d o b n ej d o tej, z tym , że różniącej się w kilku szczegółach.
2
C zy trzeba, w obec tego, pisać definicję k lasy od now a? C zy n ie m o żn a by po p ro stu pow iedzieć: „Chcę mieć klasę taką, jak tamta, z małymi różnicami. Oto te różnice..." D obra w iadom ość: m o ż n a tak zrobić! S łuży d o tego specjalna technika zw an a dzied ziczen iem , albo inaczej: tw orzeniem k la s p o chodnych. S p raw a jest b ard zo p ro sta. Jeśli na p rz y k ła d chcem y, by n o w a klasa m iała d o d a tk o w y składnik - d an ą: char
o p i s [10 ] ;
891
Rozdział. 20. Dziedziczenie Istota dziedziczenia
o ra z in n ą w ersję funkcji w y p i s z - taką, k tóra w y p isze nie tylko w sp ó łrzęd n e p u n k tu , a le i jego nazw ę (sch o w an ą w tablicy o p i s ) - to tę now ą klasę d efin iu je m y tak: class
lepszy_punkt
: public
punkt
M am y, d z ię k i tem u, now ą klasę. Klasa
lepsZy_punkt
w yw odzi się o d klasy
punkt.
K la sa p o d s ta w o w a , klasa p o c h o d n a [ jtg 3 M ó w im y , że klasa l e p s z y _ p u n k t jest klasą pochodną klasy N a to m ia st klasa
punkt
jest klasą podstaw ow ą dla k lasy
punkt.
iepszy_punkt
W klasie po ch o d n ej m ożem y: ♦♦♦ zd efin io w ać d o d atk o w e dane składow e, ♦♦♦ zd efin io w ać d o d atk o w e funkcje składow e, Definiowanie nowych funkcji składowych - bez definiowania dodatkowych danych składowych także ma sens. Jest to jakby wyposażeń te klasy w nowe *
zachowania; zd efin io w ać składnik (najczęściej funkcję sk ład o w ą), który istnieje już w k lasie podstaw ow ej. Powoduje to jakby korektę czegoś, co nam z klasy podstawmoej nie bardzo odpowiadało i czego nie chcemy w tym kształcie dziedziczyć.
U s ta p o c h o d z e n ia Z w róć u w a g ę na pierw szą linijkę definicji klasy l e p s z y j p u n k t . Po d w u k ro p ku jest n ap isan e , że klasa l e p s z y _ p u n k t w y w o d zi się od klasy p u n k t . To w yrażenie po dw u k ro p k u to tzw . lista pochodzenia. N a tej liście um ieszczona jest informacja od czego w yw odzi się d an a klasa pochodna. D zięki te m u zapisow i klasa pochodna dziedziczy w szystkie składniki klasy p u n k t . Z a te m składnikam i klasy l e p s z y _ p u n k t są teraz także np.. x,
y,
void
przesuń(double,
double)
Do p o w y ższy ch składników m ożem y odnosić się tak , jakby były one zdefin io w a n e w zw y k ły sposób w obrębie klasy l e p s z y _ p u n k t . N a liście pochodzenia obok nazw y klasy p o d staw o w ej, p u n k t w id zim y tez specy fik ato r dostępu - w tym przypadku jest to słow o p u b l i c. Szczegółow o o tym b ęd ziem y mówić w jed n y m z następnych paragrafów . Tu tylko dla w yjaśn ien ia - dodam , że ten specyfikator d o stęp u odpow iada za to, czy
892
Rozdz. 20. Dziedziczenie Istota dziedziczenia o d zied ziczo n e n ie p ry w atn e składniki klasy p o d staw o w ej m ają w klasie p o ch o d n ej wejść w sk ła d części private, protected, czy public.
C zy o z n a c z a to, że d z ie d z ic z e n ie klasy to ja k b y p rz e la n ie z a w a rto ś c i je d n e g o g a rn k a do d ru g ie g o , tro c h ę w ię k s z e g o ? W p ew n y m sensie tak, bo (pokazane w yżej) sk ład n ik i o d z ie d z ic z o n e z klasy p o d staw o w ej stają się składnikam i obiektu klasy pochodnej. T o zn a c z y m ożna się do nich od n o sić w w yrażeniach tak sam o, jakby były sk ła d n ik a m i klasy pochodnej. W tym p rzy p ad k u tak, ale nie m usi tak być zaw sze. Co p r a w d a , sk ład n ik i są rzeczyw iście o d zied ziczo n e, ale nie z a w sz e m uszą być d o stę p n e . A lbow iem odziedziczone sk ład n ik i nie mają te g o sam eg o zakresu co sk ład n ik i n o w odefiniow ane w klasie p o ch o d n ej.
I
S koro w y stęp u je zasłan ian ie, to oczyw iście nie m a m ow y o zle w a n iu czegoś do je d n eg o g arnka. To w łaśn ie zag n ieżd ża n ie zakresów . A b y się o tym p rzek o n a ć, w róćm y d o n aszy ch klas punkt i lepszy punkt. Z a u w a ż , że w definicji klasy p o d staw o w ej punkt jest ta k a sam a funkcja sk ład o w a wypisz. W rezultacie w klasie p o ch o d n ej są d w ie funkcje wypisz. Funkcja wypisz z k lasy pochodnej zasła n ia funkcję wypisz z klasy p o d sta w ow ej. Pow tarzam : zasłan ia. N ie jest to p rz e ła d o w a n ie , g d y ż funkcje m ają inny z a k re s w ażności. P ierw sza - m a zakres w ażn o ści klasy p o d staw o w ej, a d ru g a z a k re s w ażności k lasy pochodnej. Jeśli, m im o w szystko, to do C ieb ie nie p rzem aw ia, to spójrz na listę a rg u m e n tó w obu funkcji wypisz. Tutaj a k u ra t, tak się d o b rze składa, ż e te listy arg u m e n tó w są identyczne. C zy p am ięta sz , jak kiedyś m ów iliśm y: w jed n y m zak resie w ażn o ści nie m o ż e być funkq'i o id en ty czn ej n azw ie i iden ty czn ej liście
893
Rozdział. 20. Dziedziczenie Istota dziedziczenia
arg u m en tó w . To ch y b a najlepszy d o w ó d na to, że m e m a tu p rzeład o w an ia. Istnieją w ięc d w ie funkcje w y p is z: pierw sza m ająca z ak re s klasy p o d staw o w ej, a d ru g a m ająca zak res klasy pochodnej. Ile razy p racując na obiektach k lasy p o ch o d n ej w y w o łan a zostanie funkcja w y p i s z , to k o m p ilato r ak ty w u je tę z k lasy p o ch o d n ej.
C z y to z n a c z y , ż e - je śli ja k iś s k ła d n ik (d a n a lub fu n k c ja ) je s t z a s ło n ię ty - to n ie m o żn a s ię d o n ie g o o d n o s ić ? M ożna. T rz e b a jed n ak uczynić to posługując się kw alifik ato rem zak resu : : • C hod zi o d w a d w u k ro p k i, zw an e inaczej - o p erato rem zak resu . W n aszy m p rz y p a d k u d o zasłoniętej funkcji w y p i s z o d n o sim y się tak: le p s z y _ p u n k t
o b ie k t;
// definicja obiektu
o b i e k t . w y p i s z () ;
// wywołanie funkcji w y p isz ^ // z klasy pochodnej „lepszy_punkt‘
obiekt.punkt::wypisz()
1/wywołanie funkcji w ypisz // z Klasy podstawowej „punkt"
W y w o łu jąc funkcję
wypisz
bez tego kw alifikatora zak re su
u ru ch o m im y tę
z k lasy p o ch o d n ej. Jeszcze ra z okazało się, ż e nie jest to p rzelan ie z m niejszego g a rn k a d o w ięk szeg o . W klasie pochodnej tw orzy się jakby częsc, która jest ze tak p o w iem - d zied zictw em p o klasie podstaw ow ej. To w a ż n a w łaściw ość. ' D efiniując obiekt klasy pochodnej p o w in n iśm y pam iętać, że ró w nocześnie w jego w n ętrzu tkw i o d zied ziczo n y frag m en t będący jakby obiektem klasy podstaw ow ej.
B ardzo w a ż n a uw aga:
9Msmammommmvrtn\' rutmstsnkK
N ie n a le ż y je d n a k s ą d z ić , że to o b ie k ty m o g ą tw o rz y ć o b ie k ty p o c h o d n e D z ie d z ic z e n ie d o ty c z y e g z e m p la r z y o b ie k tó w .
klas (czyli ty p ó w d a n y c h ), a n ie ja k ic h ś k o n k re tn y c h iw*..■mii—mum M— rmwm ■m
P orów naj:
Jeśli mamy klasę „Volkswagen" i chcemy by z niej wywodziła się klasa V o lk sw a g e n _ k a b r io le t" , to nie możemy się spodzieioać, że w fabryce 'samochodów bierze się zwykły obiekt klasy Volkswagen odcina m u się dach i powstaje obiekt klasy Volkswagen_kabriolet. Każdy wie, że robi się inaczej. Bierze się plany konstrukcyjne klasy „ Volkswagen " (czyli definicję klasy Volkswagen), następnie dokonuje się na tych planach zmian zaznaczając różnice (definicja klasy pochodnej przy wyko rzystaniu klasy podstawowej). Powstają nowe plany zwane „Volkswa-
894
Rozdz. 20. Dziedziczenie Dostęp do składników
gen_kabriolet". Dopiero na podstawie tych nowych planów (definicjaklasy pochodnej) fabryka samochodów może konstruować samochody klasy Volkswagen_kabriolet.
20.2
D o s tę p do s k ła d n ik ó w N asz ostatni p rz y k ła d był najprostszy z m ożliw ych: w sz y stk ie składniki obu klas były publiczn e. Istnieją jednak sposoby o k reślan ia w jaki sp o só b składniki klasy p o d staw o w ej zostają dziedziczone. To z n a c z y o k re śla n ia , jaki w rezultacie będ zie d o nich dostęp w klasie pochodnej. M ogą tu b y ć d w ie sytuacjeP o ro zm aw iam y tu o tym , co się w klasie p ochodnej d zieje z o d zied ziczo n y m i z klasy p o d staw o w ej: «$♦ sk ład n ik am i p ryw atnym i, sk ład n ik am i niep ry w atn y m i (czyli
20.2.1
public
i
protected).
Prywatne sk ład n ik i klasy podstaw ow ej Są o n e d zied zic zo n e - (jak w szystkie in n e sk ład n ik i), ale w zak resie klasy pochodnej nie m a d o nich dostępu. A w ięc n ib y są, ale nie m o ż n a ich ruszyć. W iem , co pom yślałeś: „—No to, po co mi takie składniki?" Z a u w a ż jednak, ż e napisałem , iż nie m a się d o nich d o s tę p u w z a k re sie klasy p o c h o d n e j. To nic. M ogą istnieć p rzecież o d zied zic zo n e jakieś funkcje składo w e nie-p ry w atn e. P oniew aż funkcje te m ają zak res k la sy p o d staw o w ej - więc o n e m ogą p raco w ać na p ry w atn y ch sk ład n ik ach sw ojej klasy. Z kolei jeśli te funkq'e sam e są n ie-p ry w atn e (np. p u b l i c ) , to z z a k re su klasy pochodnej m o żem y je uru ch o m ić. M ogą w ięc o n e dla n as zrobić to, czeg o nie m ogliśm y zrobić b ezp o śred n io . I P rzy p o m in a się tutaj p o rzek a d ło o w y jm o w an iu z og n ia kasztaI n ów c u d zy m i rękam i.
O to p rz y k ła d : #include using
namespace
std;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // klasa p o d sta w o w a
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii class
ryba
{ private: i n t a; protected: int
1)
prots;
Zakładam, że nie ma żadnej deklaracji przyjaźni klasy pochodnej z klasą podstawową
895
Rozdział. 20. Dziedziczenie Dostęp do składników public: int pubs;
U------------- publiczne funkcje składowe--------void int
class
wstaw(int c z y t a j ()
rekin
{
m
)
{
a
return
: public
= a;
m;
}
}
ryba
d o u b l e x; public: v o i d fu n k ();
Z *************************************************************/ void
i
r e k i n : : f u n k ()
x = 1 5 .6 ; / / a = 6; ws t a w (6); cout « "\n « prots pubs cout
=
//
ta k się n ie da, ale...
. funkcja
, 'czytaj'
składnik
a
c z y t a j () ;
//
O
77;
//
©
= 1 0 0 ; « "\n
bezposr.
odczytany
« <<
prots "\n bezposr.
«
pubs;
składnik
protected
„
odczytany
składnik
public
—
z*****-****************************** ******★ ★ *★ ★ ***************/ int
main()
i rekin wacek; w a c e k . f u n k () ;
□
Po uruchomieniu programu na ekranie zobaczymy wyjęty funkcja 'czytaj' składnik a - 6 bezposr. odczytany składnik protected — bezposr.
odczytany
składnik
public
=
77
100
Komentarz N a p o c z ą tk u w id zim y klasę r y b a , w której są sk ład n ik i p r i v a t e , p r o t e c t e d @ o raz p u b l i c . Są w niej też d w ie funkcje składow e pracujące na sk ład n ik u p ry w a tn y m o n azw ie a. Klasa r e k i n - jak to w id zim y na liście pochodzenia - jest klasą pochodną od klasy r y b a . W tej klasie r e k i n jest funkcja składow a f u n k . W nętrze tej funkcji
interesuje n a s najbardziej.
896
Rozdz. 20. Dziedziczenie Dostęp do składników
W e w n ę trz u te j fu n k c ji z n a jd u je m y s ię w z a k re s ie w a ż n o ś c i klasy p o c h o d n e j. Z atem w tej funkcji sytuacja p rzed sta w ia się następująco: O D ostęp do p ry w atn y ch składników k lasy pochodnej ( r e k i n ) jest m ożliw y. Tu w iem y i sto su jem y o d daw na. 0 W zakresie klasy pochodnej - d o stęp d o p ry w atn y ch s k ła d n ik ó w klasy p o d sta w ow ej jest niem o żliw y . Tę linijkę m u sia łem w ięc u jąć w k o m en tarz, gdyż inaczej k o m p ilato r protestow ał. @ je d y n y m sp o so b em w pisania tej liczby 6 d o sk ład n ik a a, je st posłużenie się w y w o łan iem funkcji składow ej klasy r y b a , o n azw ie w s t a w . Funkcja ta ma p ra w o w p isan ia czegoś do składnika a. (M a praw o , bo jest fu n k cją składow ą tej sam ej klasy, co sk ład n ik a). O P odobnie, w celu o dczytania zap isan ej tam w artości, p o słu g u je m y się (inną) funkcją sk a d o w ą klasy podstaw ow ej. Jak w id ać, z zakresu k lasy p o ch o d n ej do p ry w a tn y c h składników klasy podstaw ow ej m o ż e m y sięgać ty lk o "cu d zy m i rękami". |j K onkretnie: w yw ołując fu n k cje sk ład o w e k lasy podstaw ow ej - I jeśli tak ie są. © N ato m iast d o sk ład n ik ó w p r o t e c t e d i p u b l i c k lasy p o d staw o w ej mamy d o stę p bezp o śred n i.
W W p rzy k ład zie ty m w ybiegliśm y nieco d o p rzo d u . Za to (z p u n k tu © ) w iem y już, jak zach o w u ją się inne sk ład n ik i k lasy po d staw o w ej. Sform ułujm y to jeszcze raz:
20.2.2
N iepryw atne składniki klasy podstaw ow ej czyli składniki
protected
i p u b l i c są d o stę p n e w zak re sie klasy p o ch o d n ej-
bezpośrednio. Tutaj po raz p ie rw sz y w yjaśni się sek ret, jaka jest różnica m ię d z y składnikiem p r o t e c t e d , a sk ład n ik iem p r i v a t e . D o tej po ry bow iem sk ład n ik i opatrzone tak im słow em (specyfikatorem d o stęp u ) trak to w aliśm y id e n ty czn ie - tak, jakby ch odziło o d o stę p p r i v a t e . Słow o p r o t e c t e d znaczy, że składnik jest zastrzeżony d la klas pochodnych. To znaczy, że: 2
•
d la całego św iata m a być o n d o stęp n y tak, ja k b y był p r i - czyli n iedostępny,
•
d la klas pochodnych o d d an ej klasy b ęd zie o n do stęp n y tak. ja k b y był p u b l i c .
vat e j
n ato m iast
Z atem :
897
Rozdział. 20. Dziedziczenie Dostęp do składników
W niosek z te g o p arag rafu : K lasa p o d staw o w a p rzez o d p o w ied n ią specyfikację d o stęp u sło w a m i p u b l i c , p r o t e c t e d , p r i v a t e m o ż e decydow ać:
20.2.3
•
które składniki zam ierza u d o stęp n ić szerokiej publiczności,
•
które zastrzeg a ty lk o dla sw oich k las p o ch o d n y ch ,
•
a które zach o w u je tylko na sw ój p ry w a tn y użytek.
Klasa pochodna też decyduje Istnieje jeszcze d ru g i m echanizm , za pom ocą k tó reg o klasa - tym razem pochodna - d ec y d u je sobie jak chce odziedziczyć (o trz y m a n y od klasy p o d s ta w ow ej) n ie p ry w a tn y składnik. P o d k reślam w y raźn ie: n ie p ry w a tn y . C o do p ry w atn eg o , to nic się nie da zrobić - jest o n d zied zic zo n y , ale zap ieczęto w an y (czyli n ied o stęp n y ). S p raw a d o ty c zy w ięc składników p u b l i c i p r o t e c t e d z klasy podstaw ow ej. O ba są je d n a k o w o d o stęp n e w zak resie klasy p o chodnej. K lasa pochodna m oże sobie jednak w y b rać czy na przy k ład chce, b y o d zied ziczo n y sk ład n ik p u b l i c był u niej t a k ż e p u b l i c . M oże p rzecież zdecydow ać w łożyć go u siebie d o szu flad k i p r i v a t e i n ik o m u już go nie pokazyw ać. W yboru tak ieg o dokonuje się p rzy definicji klasy p o chodnej. Z auw aż, że w pierw szej linijce definicji, na liście pochodzenia tu ż p rz e d nazw ą klasy p o d s ta w ow ej r y b a jest słow o p u b l i c . Inną ew entualnością było by um ieszczenie tam słow a p r i v a t e lu b p r o t e c t e d . Jaką rolę sp ełn iają te określenia? Jeśli p rz y definiow aniu klasy pochodnej na liście pochodzenia p o p rz e d z im y nazw ę klasy p o d staw o w ej słow em p u b l i c - w ów czas składniki p u b l i c z klasy podstaw ow ej mają w klasie p o ch o d n ej rów nież d o stęp p u b l i c , a odziedziczone składniki p r o t e c t e d są w klasie pochodnej ta k ż e p r o t e c t e d . ♦> Jeśli n a liście pochodzenia p rz e d nazw ą klasy p o d staw o w ej stoi o kreśle nie p r o t e c t e d , w ów czas składniki p u b l i c o raz p r o t e c t e d są w 20 k lasie pochodnej jako p r o t e c t e d . ♦♦♦ jeśli na liście pochodzenia p rz e d nazw ą klasy p o d staw o w ej stoi o kreśle nie p r i v a t e , w ów czas sk ład n ik i p u b l i c o ra z p r o t e c t e d są w klasie p o ch o d n ej jako p r i v a t e . Nie p rzejm u j się, nie trzeba tu niczego uczyć się na p am ięć - w szystko staje się jasne, g d y sp o jrzy m y na zam ieszczony rysunek.
898
Rozdz. 20. Dziedziczenie Dostęp do składników
Zakres klasy podstawowej
Zakres klasy pochodnej Na liście dziedziczenia jest słówko:
private
private
protected
protected
public public
public
Rys. 2 0 -1 . Stawiając na liście pochodzenia specyfikatory dostępu można zdecydować jaki dostęp m ają w klasie pochodnej składniki p r o t e c t e d i p u b l i c z klasy' podstawowej.
DS3
Z w racam je d n a k uw agę, ze strzałki pokazują to, do k tó re g o o b szaru dostępu klasy po ch o d n ej następuje dziedziczenie, a nie to, czy d z ie d z ic z e n ie następuje w ogóle. D ziedziczone są w szystkie składniki. Brak strz a łk i p rz y obszarze p r i v a t e w klasie p o d staw o w ej oznacza, że zm ian a ro d zaju d o s tę p u nie następuje. O d zied ziczo n e elem en ty mają n ad al d o stęp p r i v a t e , ale w zak resie w ażności klasy p o d staw o w ej. Ten zakres je st zasad n iczo n ie d o stę p n y z zakresu klasy pochodnej . Z obaczm y w ięc, jak to d zied ziczen ie jest p o strzeg an e o d s tro n y obiektu klasy pochodnej. O to rezu ltat dzied ziczen ia p o k aza n y na ry s u n k u : O b ie k t kla s y pochodnej
private protected
public
Rys. 20-2. Dziedziczone są wszystkie składniki. Mogą one jednak znaleźć się w rożnych zakresach. Zależy to od tego, co klasa podstawowa chce udostępnić, ale także od tego, jaki dostęp tym składnikom klasa pochodna zamierza nadać u siebie.
O gólnie m ożna p o w ied zieć, że: za sa d n ic zo n ie d o s tę p n y - gdyż są sytuacje, kiedy dostęp jest możliwy. Na przykład wtedy, gdy klasa podstawowa deklaruje przyjaźń z klasą pochodną.
899
Rozdział. 20. Dziedziczenie Dostęp do składników
d z ied zic zen ie p ry w a tn e d ecy d u jem y się w ted y , g d y chcem y, by nie było
tajem nicy, co d la p ro sto ty późniejszej pracy z nią. Z a k ilkanaście stro n p o ro z m a w ia m y o m ech an izm ach , k tó re pozw olą n a m na taki w niosek: D zied ziczen ie p ry w a tn e stosujem y w te d y , g d y me chcem y, b y na o b iek ty klasy p o ch o d n ej kiedykolw iek m ó w io n o , że są szczegól n y m rodzajem o b iek tó w klasy p o d staw o w ej. M im o że tak jest w istocie —chcem y ten fak t zatajać.
D o m n ie m a n ia Jeśli w definicji klasy po ch o d n ej na liście p o ch o d ze n ia p rz e d n azw ą klasy p o d staw o w ej n ie um ieścim y ani sło w a private, ani sło w a p u b l i c a n i protected - to p rz e z d o m n iem an ie przyjęte zo stan ie d z ied zic zen ie private. Jeśli m am y d o czy n ien ia ze s tru k tu rą - to w an alo g icz n y m p rzy p ad k u - p rzez d o m n iem an ie p rzy jm u je się tam d zied ziczen ie p u b l i c . class
przodek
{ /*...*/ class {
{
klasa_dziedzic
/*...*/
struct
1; : przodek
};
struktura_dziedzic
/*...*/
: przodek
};
W obu p rz y p a d k a c h zostały o p u szczo n e słow a o kreślające sposob d zied zi czenia. P rzez d o m n iem an ie k o m p ilato r przyjm ie, że d efin icje tych klas pochodnych z a p isa liśm y tak class
klasa_dziedzic
{ /*...*/ struct
: private
przodek
1;
struktura_dziedzic
: public
przodek
{/*...*/};
20.2.4
Deklaracja dostępu u s i n g - czyli udostępnianie w ybiórcze Ten p a ra g ra f m ó w i o p ew nej nieczęsto u żyw anej technice, dlatego - przy p ie rw szy m c z y ta n iu tej książki - m o żesz go om inąć.
X
900
Rozdz. 20. Dziedziczenie Dostęp do składników W iem y ju ż, ż e klasa pochodna m oże albo w szystkie o d z ied zic zo n e składniki u k ry ć (d z ie d z ic z e n ie p r i v a t e , p r o t e c t e d ) , albo p o z o sta w ić d o nich taki d o stęp , ja k i m iały oryginalnie (d zied ziczen ie p u b l i c ) . Decyzja ta k a m oże czasem nie być łatw a. - N o bo z jed n ej s tro n y chcielibyśm y u k ry ć to i tam to , a z drugiej stro n y m u si pozostać d o s tę p p u b l i c lu b p r o t e c t e d d o kilku w ybranych. C o w te d y zro b ić? Jest prosty sposób. Jeśli w jakiejś klasie pochodnej d z ie d z ic z y sz klasę podstaw ow ą p ry w a tn ie ( p r i v a t e ) , a są nazw y sk ła d n ik ó w , którym - m im o p ry w atn eg o d ziedziczenia - chciałbyś d a ć „ p rz e p u stk i" , pow inie neś, w w ybranej części klasy po ch o d n ej, um ieścić tak zw aną deklarację dostępu'* (deklarację u s in g ).
J a k się to robi ta k ą d e k la ra c ję d o s tę p u ? N ajp ierw m u sisz w ybrać o d p o w ied n ią część klasy. To zn aczy : ❖
Jeśli w klasie podstaw ow ej d a n y składnik m iał d o s tę p p u b l i c , to w k lasie pochodnej um ieścisz deklarację w części p u b l i c .
❖
Jeśli z a ś d a n y składnik był w swojej klasie p o d sta w o w e j p r o t e c t e d , to d e k larację dostępu um ieszczasz w klasie p o c h o d n e j w części p r o tected.
Sam a d ek laracja w y g ląd a tak: u s i n g nazwajdasy_podstawcnvej: : nazwa_składnika ; Jak w idać, je st tu słow o kluczow e u s i n g , po którym n a s tę p u je tzw . k w a lifik o w an a n a z w a s k ła d n ik a . Innym i słow y, nazw a s k ła d n ik a p o p rz e d z o n a nazw ą klasy p o d sta w o w e j i operatorem z ak re su : : . Z w róć u w a g ę , ż e jest to deklaracja sam ej nazw y sk ład n ik a. •
Z atem , jeśli jest to n azw a funkcji s k ła d o w e j - to nie m a tam określenia żad n y c h arg u m en tó w .
•
Jeśli to nazw a d an ej składow ej, to nie m a ta m określenia typu tego składnika.
Z o b a c z m y , ja k p ro s to w y g lą d a to w p ra k ty c e
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIHIIIIIIIIIIIIIIIklasa ^dstawoumllllllllllllllll/llllllllllllllllllllllllllllllllli; class
przodek
protected: i n t n; double void public:
3)
W
x;
funprot(double*,
int
&);
starszych w e r s j a c h języka C + + s ł o w o u s i n g k l u c z o w e u s i n g nie istniało, w i ę c m o ż n a
b yło tę deklarację z r o b i ć tak s a m o , ale b e z s ł o w a u s i n g . D z i ś ten d a w n y zapis na dal jest d o z w o l o n y , ale u z n a w a n y za przestarzały.
901
Rozdział. 20. Dziedziczenie Dostęp do składników
int szer; double
*
funpublfint
m ) ;
}illllllllllllllllllllllllllllllll!IIIIIIIIIHklasa Pochoclnn tlllllllllllllllllllllllllllltllllllllllllllllllllllllll class
t
//
potomek
: private
przodek
...
pr°using ' przodek : :x ; using
// «------------ deklaracje dostępu
przodek::funprot;
public:
using using
przodek::szer; przodek::funpubl;
/» ///////////////////////» D zięki d ek laracjo m d o stęp u - m im o że klasa przodek została o d zied zic zo n a p ry w a tn ie - w y b r a n e n azw y m o g ą być u d o stę p n io n e tak, jakby dla nich o d b y ło d zie d z ic z e n ie ty p u
public.
W e d łu g ja k ic h z a s a d d z ia ła ta d e k la ra c ja d o s tę p u ? Z ałó żm y , że w klasie p o d staw o w ej był jakiś s k ła d n ik public, ale - p rzez p ry w a tn e d zied ziczen ie —1w y ląd o w ał on w k lasie p o ch o d n ej jako private. D eklaracja d o stęp u m oże tem u zapobiec. To z n a c z y m oże sp raw ić, ż e m im o w sz y stk o b ęd zie on publiczny. Czyli "wyląduje" on tam, gdzie wylądowałby przy dziedziczeniu typu p u b lic . P o d o b n ie ze sk ład n ik iem protected, który z e w z g lę d u na sp o só b d z ie d z i czenia "w ylądow ał" w klasie pochodnej jako private. D zięki tej w ybiórczej d ek laracji d o stęp u m ożna sp raw ić, że m im o w sz y stk o będzie o n protected. Czyli "wyląduje" on tam, gdzie wylądowałby przy dziedziczeniu typu p u b l i c . Przypominam (zob. rys. 20-1), że przy dziedziczeniu typu p u b l i c , składniki p r o t e c t e d dziedziczone są do części p r o t e c t e d .
C zyli deklaracja d o stęp u m oże je d y n ie ja k b y "uniew ażnić" o g ra n i czenie w prow adzone przez sposób dziedziczenia. (O sposobie d zied ziczen ia decy d u je klasa p o ch o d n a). W rezu ltacie: jeśli w klasie podstaw ow ej nazw a b y ła protected - to w klasie p o ch o d n ej m oże być tylko protected. Jeśli tam n a z w a była public - to w klasie p o ch o d n ej m oże być tylko public. N ato m iast
902
Rozdz. 20. Dziedziczenie Dostęp do składników D eklaracja dostępu nie m oże "u n iew ażn iać" d o stęp u , który/ w y b rała dla swoich sk ładników klasa p o d s ta w o w a . (O tyrm do stęp ie zad ecydow ała kiedyś klasa p o d sta w o w a ).
To zrozumiałe, przecież byłaby to droga do oszustwa, gdyby deklaracja dostępu mogła odtajnić to, co klasa pochodna postanoioiła trzymać pocą nadzorem. v m m m m .^0.^-n-w ir.K L^rT trrttfiinnairm ^rr i ii* t at
- uuw ru u ri
mi r - “
w— w wm|
K ró tk o m ó w ią c : D e k la ra c ją d o s tę p u nie m o ż e m y a n i z a o s tr z y ć o ry g in a ln e g o d o s tę p u , a n i go ro z lu ź n ić .
Wybiórcze u d o stęp n ian ie za pom ocą deklaracji d o stęp u n ie d o ty c zy w ięc sk ła d ników p ry w a tn y c h klasy podstaw ow ej. Z tym i nic się n ie d a zrobić, bo k la sa p o d sta w o w a zdecydow ała, że m ają być p ryw atne. W ybiórcze u d o stęp n ian ie do ty czy jedynie sk ła d n ik ó w n ie p ry w atn y ch , k tó re ("hurtow o") zo stały p rzez k lasę p o ch o d n ą n ie o p a trz n ie o d zied ziczo n e w sposób p r i v a t e , lub p r o t e c t e d .
M an k am en tem tej techniki jest to, ż e d eklarujesz n o w y d o s tę p d o sam ej nazwy. N ie m ożesz ro zró żn ić nazw p rzeład o w an y ch - d ek laracja d o stęp u w y m ien ia przecież ty lk o n azw ę (np. funkcji) nic nie m ów iąc o a rg u m e n ta c h . D eklarow ać m o żem y d o stęp d o n a z w y określającej n p . 5 p rzeład o w an y ch funkcji sk ład o w y ch - pod w a ru n k ie m , że te w sz y stk ie 5 funkcji są n am dostępne. Jeśli choć jedna z nich jest n ied o stęp n a (bo je st p r i v a t e ) , to taka deklaracja d o stę p u jest niem ożliw a.
D la w ta je m n ic z o n y c h : D eklaracja d o stę p u ( u s in g ) nie m o że u su n ąć e w e n tu a ln e j w ieloznaczności p rzy o d zied zic zen iu tak sam o n a z w a n e g o sk ład n ik a z g ałęzi "ojca" i g ałęzi "matki". O bow iązuje stara zasad a, ż e n ajpierw ro z są d z a n a je st w ieloznaczność, a dop iero e w e n tu a ln ie potem (jeśli w ieloznaczności nie m a) ro zsąd zan y jest dostęp. D eklaracja d o stę p u nie m oże p o słu ży ć d o odsłonięcia n a z w y zasłoniętej. Jeśli w klasie p o ch o d n ej n azw a jednego z e sk ład n ik ó w zasłan ia n a z w ę z klasy p o d s ta w ow ej, to d ek laracja dostępu nie zm ie n i tego faktu. P odobnie z n azw ą funkcji w irtu aln ej. D eklaracja d o stę p u n ie m oże u n iew ażn ić faktu, że w k lasie p ochodnej jest n o w sz a realizacja funkcji w irtualnej.
Rozdział. 20. Dziedziczenie Czego się nie dziedziczy im n imi 11 id u im jm jiu u w j jj j a u 'J I..UJ.JI iu
20.3
903
^ l L l (r ^y .u a .m T J j . a iai T U M i — ■ w ^ 'm |iJ>W'"HM»au* >im»iiirfiri
C z e g o się n ie d z ie d z ic z y Jeśli klasa ma w szy stk ie sk ła d n ik i n ie p ry w a tn e i jeśli p o słu ży n am d o zdefinio w an ia klasy p o chodnej, to w św ietle tego, co d o tej p o ry p o w ied zieliśm y — w szy stk ie sk ład n ik i z o sta n ą o d zied ziczo n e d o klasy p o ch o d n ej i b ę d ą mogły być u ż y w a n e tak, jakby były zd efin io w an e w klasie pochodnej. T eraz czas p o w ied zieć, że dla n aszeg o d o b ra —nie w szystkie! N ie zo stan ą w ten sposób o d zied ziczo n e ♦> 1) k o n stru k to ry , ♦♦♦ 2) o p erato r p rz y p isa n ia (operator= ), 3) d estru k to r.
P o w ó d tego zro zu m ieć b a rd z o prosto. O b iek t klasy po ch o d n ej to jakby obiekt klasy p o d staw o w ej, p lu s coś jeszcze. To „co ś jeszcze", to sk ład n ik i zdefinio w an e w klasie p o ch o d n ej. O tych sk ład n ik ach k o n stru k to r klasy podstaw ow ej nic nie wie. P o słu żen ie się takim k o n stru k to re m (z klasy p o d staw o w ej) spraw i łoby, ż e inicjalizow ane b y ły b y tylko sk ład n ik i—d a n e p o ch o d zące z klasy podsta w ow ej, a reszta z ig n o ro w an a . Na to zgodzić się nie m ożem y . A lbo zrobić całość, albo nie robić nic! Dlatego w ięc ob o w iązu je z a sa d a , że k o n stru k to ry k lasy p o d staw o w ej nie stają się au to m aty czn ie k o n stru k to ra m i k lasy pochodnej. W p rak ty c e z n ac zy to że; jeśli nie zd efin o w aliśm y w klasie po ch o d n ej żadnych k o n stru k to ró w , nie liczm y na to, ż e ich funkcję przejm ą te z klasy podstaw ow ej. N a w e t jeśli w k lasie p o d sta w o w e j są k o n stru k to ry , to klasa p o ch o d n a i tak nie ma żad n y ch . K o n stru k to ry d la klasy p o ch o d n ej trzeba sobie p o prostu zd efin io w ać. N ie jest to ż m u d n e , b o w iem - p rz y inicjalizacji części odziedziczonej - można pom óc sobie w y w o łu jąc k o n s tru k to r z klasy podstaw ow ej. O szczegółach po w iem y niebaw em .
D la cze g o to "n ie d z ie d z ic z e n ie " n a p is a n e je s t w c u d z y s ło w ie ? Kilka razy p o d k reślałem , że d zied ziczy się w szy stk o , czasem tylko to dziedzi ctw o jest n ied o stęp n e, ale i tak jest o d zied ziczo n e. Tu jednak sens jest inny. Co z tego, ż e k o n stru k to r k lasy p o d staw o w ej zostaje o d ziedziczony i jest nawet d o stę p n y bo p u b l i c . 1 tak , to n arzę d zie nie n ad aje się do konstrukcji obiektu klasy pochodnej. O n o p o p ro stu um ie k o n stru o w ać jedynie obiekty swojej w łasnej klasy (czyli p o d staw o w ej).
20
904
Rozdz. 20. Dziedziczenie Czego się nie dziedziczy
Więc to tak jakbyśmy odziedziczyli po ojcu jego okulary czy sztuczną szczękę. Co z tego, że je mamy, skoro (tak bezpośrednio) używać ich nue możemy. P o d o b n ie jest z "nie dziedziczeniem " operatora p rz y p is a n ia , o którym porozm a w iam y teraz.
20.3.2
"Nie dziedziczenie" operatora przypisania Te sam e w zg lęd y spraw iają, że:
O p erato r ten słu ży przecież do przypisania czegoś s k ła d n ik o m obiektu klasy pod staw o w ej. N ie w ie on nic o now ych składnikach, k tó re zo stały zd efin io w an e w klasie pochodnej, w ięc ew en tu aln e przypisanie b y ło b y niek o m p letn e.
T u ta j trz e b a u w a ż a ć Jeśli zap o m n im y o tym nie d ziedziczeniu operat ora= i m im o w szy stk o w yko n am y p rz y p isa n ie do obiektu klasy pochodnej (która s a m a o p erato ra p rzy p isa nia nie m a), to oczyw iście operator= z klasy p o d sta w o w e j n ie zadziała. Za to zad ziała o p e ra to r g enerow any autom atycznie p rz e z k o m p ilato r. B ędziem y w ięc m ieli p rzy p isan ie „sk ład n ik p o składniku", a to n ie zaw sze m u s i nam od pow iadać!
20.3.3
MN ie dziedziczenie" destruktora Jak w iem y , rolą destru k to ra jest ostateczne u p o rz ą d k o w a n ie na chw ilę przed zlik w id o w an iem obiektu. D estru k to r często u n ie w aż n ia to, co zrobił k o n stru ktor. Skoro jest tak ściśle p o w iąza n y z konstruktorem - k tó ry m usi być d efin io w an y o so b n o dla klasy pochodnej - dlatego d estru k to r k la sy po d staw o w ej nie m oże być au to m aty czn ie u ż y ty , jeśli nie zd efin iu jem y now ego w klasie pochodnej.
Fakt nie d zied zic zen ia k o n stru k to ró w idestruktora jest, m o im zdaniem , łatw y d o zap a m ię tan ia . Po prostu ich n az w y są zw iązane z n a z w ą klasy (k o n stru k to r nazyw a się tak, jak jego klasa). Jeśli tw orzym y klasę p o c h o d n ą od tej klasy, to intuicyjnie jest oczyw iste, że k o n s tru k to ro w i/d e s tru k to ro w i klasy p o d sta w o wej nie zo sta n ie zm ieniona n azw a na nazw ę w łaściw ą k o n s tru k to ro w i/d e s tru ktorow i k lasy pochodnej. N ato m iast ry zy k o zap o m n ien ia o nie dziedziczeniu o p e ra to ra p rzy p isan ia jest w iększe.
905
Rozdział. 20. Dziedziczenie Drzewo genealogiczne
20.4
D rz e w o g e n e a lo g ic z n e D zied ziczen ie m oże być - ż e tak pow iem - k ilk u p o k o leń ,o w e T o z n a c z y klasa p o c h o d n a m oże ró w n o cześn ie zostać klasą p o d staw o w ą d la m nej klasy poch o d n ej. Ł atw o to sobie u zm y sło w ić za pom ocą an alo g ii w życiu ro d z in n y m : mój d z ia d e k to jakaś klasa p o d sta w o w a . Z tej klasy w y w o d z i się m oj ojciec który jest klasą p o ch o d n ą od k la sy p o d staw o w ej „ d z ia d e k " . T ym czasem ja w y w o d zę się o d klasy „ojciec" - k tó ry jest dla m nie klasą p o d staw o w ą. P rz y takich analogiach nie należy zapom inać, ż e n ie jest ona z u p e łn a . W życiu b o w iem d z ia d e k , ojciec i ja, to ko n k retn e obiekty, a nie klasy. I rzy p o m m am , ze w C + + d zied ziczen ie d o ty c z y klas, a nie ob iek tó w . class
A
// — cia ło
k la sy A
}; class
B
: public
// — ciało
A
k la sy B
); class
II
C
: public
— ciało
B
k la sy C
} Z k lasy A w y w o d z i się k lasa B, a z niej z kolei w y w o d z i się klasa C. M ożem y to p rz e d s ta w ić w postaci ta k zw an eg o grafu dziedziczenia.
Rys .20-3
S trzałk a na ty m grafie o zn ac za jakby słow a: „ p o ch o d z i o d ". Jak w id ać, klasa C w y w o d z i się z klasy B b ezp o śred n io , ale tak że w y w o d z i się p o śre d n io od klasy A . M ów im y, ż e d la klasy C klasa B jest k la są p o d sta w o w ą bezpośred nią, n ato m iast klasa A jest k la są p o d sta w o w ą pośrednią.
I
T ak jak mój ojciec jest d la m nie klasą p o d staw o w ą b ezp o śred n ią, a mój dziadek k la są p o d staw o w ą p o śre d n ią . B ardziej precyzyjnie w y ra z ić m ożna to tak:
20
906
Rozdz. 20. Dziedziczenie Dziedziczenie - doskonałe narzędzie program ow ania
J e ś li ta k nie je s t, a ta k la s a P R Z O D E K z n a jd u je s ię n a liś c ie p o c h o d z e n ia je d n e j k la s p o d s ta w o w y c h d la kla sy K, to ta k a k la s a (P R Z O D E K ) je s t klasą
podstawową pośrednią.
P rzy ty m kilku p o koleniow ym (inaczej jakby: „ k a s k a d o w y m " ) d ziedziczeniu w id z im y zn aczen ie d zied ziczen ia p ry w atn eg o lu b p u b liczn eg o . •
D ziedziczenie publiczne p o w o d u je , ż e sk ład n ik i b ędące na samej g ó rze k askady (dziadek), są d o s tę p n e na sam y m dole (ja).
•
Z asto so w an ie dziedziczenia p ry w a tn e g o na k tó ry m ś etapie p o w oduje, ż e w tym miejscu z a m y k a s ię n astęp n y m pokole niom d o stę p d o odziedziczonych sk ła d n ik ó w . (O d tej p o ry są jakby d zied ziczo n e dalej w z a la k o w a n e j kopercie).
C zasem m o ż e być to k o rzy stn e —np. g d y d efin iu jem y k la sę biblioteczną będącą p o c h o d n ą o d w ielu innych (roboczych) klas i z a le ż y n a m na tym , aby jak najm niej sk ład n ik ó w było d o stęp n y ch i w id zian y ch z z e w n ą trz tej klasy biblio tecznej. T ak że z ew en tu a ln y ch następnych p o k o le ń , bo ta klasa biblioteczna m oże p rzec ież posłużyć u ży tk o w n ik o w i, jako klasa p o d sta w o w a d o jego w łasnej.
2 0 .5
D z ie d z ic z e n ie - d o s k o n a łe n a rz ę d z ie p ro g ra m o w a n ia D zied ziczen ie jest jedną z n ajw spanialszych cech o b ie k to w o o rien to w an y ch języ k ó w p ro g ram o w an ia. Z a sta n ó w m y się d o czego m o ż e nam się d zied zic zen ie p rzy d ać.
1) O s z c z ę d n o ś ć p ra cy D zied ziczen ie p rzy d a się w te d y , g d y m am y ju ż k lasę, k tó ra jest p o d o b n a d o takiej, ja k ą potrzebujem y. O sz częd za m y sobie b a rd z o d u ż o pracy d efin iu jąc 2I
N ie c h o d z i tu n aw et o n a sz e lenistw o. Z au w aż , ż e a b y zd efin io w ać k lasę p o ch o d n ą, nie m u sim y zn ać k o d u źró d ło w eg o k la sy pod staw o w ej. N ic je st w ażne, jak klasa p o d staw o w a ro b i to, czy tam to. A lb o to ak cep tu jem y i d z ie d z i czym y n a ślepo, albo nie ak cep tu jem y i (m im o że d z ie d z ic z y m y także) z a sła n ia my n o w y m składnikiem . N p . defin iu jem y now ą fu n k cję sk ład o w ą, która zro b i d o k ła d n ie to, o co nam ch o d zi. M ożem y też d o p isa ć z u p e łn ie now ą fu n k cję
Rozdział. 20. Dziedziczenie Dziedziczenie - doskonałe narzędzie program ow ania
907
sk ład o w ą, która w y w o ła jak ąś funkcję o d z ie d z ic z o n ą , by w y k o n ała część pracy, p c czy m sam a coś p o p ra w i lu b uzu p ełn i. Z a u w a ż , że d zięk i d zied zic zen iu u p ra sz c z a się nam sp raw a. N ie m u sim y b o w iem w iedzieć i ro zu m ieć, jak klasa p o d sta w o w a realizuje jakieś funkcje. Jeśli to ktoś in n y n ap isał tę klasę p o d sta w o w ą , to nie m u sim y ślęczec nad w y d ru k a m i i myśleć: „ w k tórym miejscu trzeba zro b ić p o p raw k ę, ab y funkcja zm ien iła się na taką, k tó ra n am o d p o w ia d a ? " Fakt ten, to jakby jeszcze bard ziej w y sz u k a n y a sp e k t enkapsulacji: zę b y doko nać zm ian nie m u sim y d o sta w a ć się d o w n ętrza k a p su ły (tutaj: klasy p o d sta w o wej). A by to zrobić, p o trzeb u jem y tylko d eklaracji ciała klasy p o d staw o w ej w w ersji źródłow ej. T— ta deklaracja jest zw y k le d o stęp n a w sp ecjaln y m pliku nag łó w k o w y m . N ato m iast definicje funkcji sk ład o w y ch tej klasy (czyli ich ciała) m ogą być już n aw et w sk o m p ilo w an ej w ersji binarnej. W aż n y w niosek: D zięki tem u o d d zielam y o d siebie d w ie spraw y: to, jak d a n a klasa jest zrea lizo w an a , od tego, jak się n ią p o słu g iw ać - n a w e t w celu d zied ziczen ia. A by użyć klasy d o tw o rzen ia klas p o c h o d n y ch , me m u sim y w ied zieć d o k ła d n ie za po m o cą jakich k ru czk ó w o teza się w klasie to, czy tam to. Ta z a sa d a jest zb liżen iem zasad p ro g ram o w an ia d o zasad w y p raco w an y ch w życiu co d zien n y m . K oniec z ty m u p ajan iem się! G d y b y to tylko o oszczędność p racy cho d ziło , nikt nie n azy w ałb y p ro g ram o w an ia o b iektow ego - przełom em ...
2) H ie ra rc h ia W p o p rz e d n im p u n k cie m ów iliśm y o sytuacji, g d y m am y klasę, k tó ra p rzy p o m ina to, co chcielibyśm y mieć. D zięki d zied zic zen iu m ożem y tanim kosztem m ieć klasę, k tóra jest ty m , o co nam chodzi d o k ład n ie. Jest jed n ak coś o w iele w ażniejszego. B ardzo często w p ro g ram ie m am y d o czynienia z klasam i, k tó re są w jakiejś zależn o ści logicznej. N a p rz y k ła d m am y k lasę sam ochód, n astęp n e k lasy to samochód ciężarow y, klasa sam o ch ó d o so bow y, klasa sam ochód o so b o w y m arki Porsche. K ażdą z tych klas m o żn a zd efin io w ać osobno, jednak od razu narzuca się, ż e sam oc o o sobo w y to szczególny p rz y p a d e k sam o ch o c u , a sam ochód m arki Jag u ar to przecież ro d zaj sam o ch o d u osobow ego. P ow staje w ów czas coś w rodzaju hierarchii klas. Spójrz na p o m zszy grat dziedziczenia. (Także i na tym grafie strzałk ę czy tam y jako: „w y w o d zi się od ).
908
Rozdz. 20. Dziedziczenie Dziedziczenie - doskonałe narzędzie program ow ania
H ierarch ia taka, a ogólniej: proces d ziedziczenia, p o z w a la na w p ro w a d z e n ie re lacji m ięd zy poszczególnym i klasam i. Z a m ia s t osobnych klas, m am y teraz p e w n ą logikę - jakaś klasa jest szczególnym ro d zajem innej k lasy bardziej ogólnej. U m ie ję tn e s k o n s tru o w a n ie ta k ie j h ie ra rc h ii je s t je d n ą z p o d s ta w o w y c h ce ch d o b re g o o b ie k to w o o rie n to w a n e g o p ro g ra m u .
j
U staw ien ie klas w hierarchię dziedziczenia, to nie ty lk o o szczęd n o ść czasu przy d efin io w a n iu klas znajdujących się na dole hierarch ii. W ażn iejsze jest to, że: O b ie k ty , k tó re z n a jd u ją s ię na d o le h ie ra rc h ii, m o g ą b y ć c z a s e m tra k to w a n e ta k , ja k b y b y ły o b ie k ta m i z g ó ry h ie ra rc h ii.
P rzy k ład o w o : Jeśli m a m y funkcję pracującą d la o b iek tu klasy sam o ch ó d , to m o ż n a ją rów nież u ży ć d o pracy z obiektem klasy Porsche. W m yśl tego, co p o w ied zieliśm y pow yżej - Porsche m oże być tak że tra k to w a n y jako sam ochód. H ierarchia w p ro w a d z a więc b a rd z o n a tu ra ln e relacje m iędzy klasam i.
3) K la sy o g ó ln e M o żem y p rzy b u d o w an iu klas nastaw iać się na to, ż e jakaś klasa p rzezn aczo n a jest w y łączn ie do d zied ziczen ia. Sama klasa nie o zn acza nic sen so w n eg o , ale ch cem y ją m ieć po to, by m ogła być d zied ziczo n a p rz e z in n e klasy. T aka klasa m oże np. słu ży ć do pracy z jakim iś ty p am i, k tó re d o p ie ro później zo sta n ą zdefiniow ane. C hoćby w m om encie d zied zic zen ia tej klasy d o klasy pochodnej. P rz y k ła d e m takiej klasy ogólnej jest kolejka. W takiej kolejce m ogą u staw ić się lu d z ie (pam iętasz n aszą klasę o s o b a ? ). W kolejce m o g ą też na p rz y k ła d „stać ' fra g m e n ty ek ran u , k tó re chcielibyśm y su k cesy w n ie odśw ieżać. K olejka sam a z siebie nie istnieje. Do tego p o trz e b n e są ob iek ty (jakiejś klasy), k tó re w takiej kolejce b ę d ą u staw ian e.
909
Rozdział. 20. Dziedziczenie Kolejność wywoływania konstruktorów
N ie jest to tylko in n y w a ria n t sp ra w y o m ó w io n ej w p o p rz e d n im p u n k cie. Tam o b iek t klasy p o ch o d n ej był szczeg ó ln y m p rz y p a d k ie m k lasy podstaw ow ej. T utaj tak nie jest. C zło w iek stojący w kolejce nie jest szczeg ó ln y m p rzy p ad k iem kolejki.
W T rzeb a pam iętać, że d zied zic zen ie jest ty lk o sposobem d e fin io w a n ia now ych klas - takim , k tó re w p ro w a d z a m ięd zy k lasam i p ew n e relacje. D zied ziczen ie nie daje jed n ak obiektom k lasy p ochodnej ż a d n y c h w iększych p ra w w obec o b iek tó w k la sy pod staw o w ej. K onkretniej: o b iek t k lasy pochodnej n ie m a d o stęp u d o n iep u b liczn y ch sk ła d n ik ó w obiektu klasy p o d staw o w ej. Jeśli chce się to u m o ż liw ić - klasa p o d staw o w a p o w in n a zad e k la ro w a ć p rzy jaźń z klasą p o ch o d n ą. C zyli tak , jak w p rz y p a d k u jakiejkolw iek obcej klasy. K lasa p o d staw o w a i klasa p o ch o d n a m o g ą m ieć - k ażd a sw oich - przyjaciół. A ż się prosi, by teraz p o d a ć p rzy k ład o w y p ro g ra m , w k tó ry m jest jakaś hierar chia. Z aczekajm y z ty m jednak d o n astęp n e g o p arag rafu .
2 0 .6
K o le jn o ś ć w y w o ły w a n ia k o n s tru k to ró w W obrębie obiektu k la sy pochodnej tkw i, w p ew n y m sensie, o b iek t klasy p o d staw o w ej. Tak, jak w obiekcie k lasy "sam ochód osobow y", jest coś z "sam ochodu". K o n stru o w a n ie o b iek tu klasy pochodnej to sk o n stru o w an ie o b iek tu klasy pod staw o w ej i d o b u d o w a n ie tego, co jest w łaściw e klasie pochodnej. Pracują więc d w a k o n stfu który. Skoro tak, to m oże się n asu n ąć pytanie: który z ko n stru k to ró w w y k o n a się najpierw ? Czy k o n stru k to r klasy p o d staw o w ej czy klasy pochodnej? O d p o w ie d ź jest intuicyjnie w yczuw alna: n ajp ierw rusza d o p racy konstruktor
—lJ
klasy p o d staw o w ej. M oże bvć też d o d a tk o w o taka sytuacja, że sk ład n ik iem obiektu klasy pochodnej są obiekty innych klas (obiekty składow e). O biektem sk ład o w y m klasy sam o ch ó d o so b o w y jest, często, w b u d o w an e radio. N ie ma to nic w sp ó ln eg o z dzie d ziczen iem . R adio nie jest przecież szczeg ó ln y m rodzajem sam ochodu! Jeśli ob iek t ra d io m a sw ój k o n stru k to r, to k ied y o n się w ykona? O d p o w ie d ź na takie p y ta n ia m ożna zaw rzeć w jednym z d a n iu : * ___ ................. . i i i w i i MiiwuTrr T~ r ------ K la s a u s z a n u je n a jp ie rw s ta rs z y c h , p o te m s w o ic h g o ś c i, a d o p ie ro na sam ym k o ń c u z a jm ie s ię s o b ą .
Ł ad n ie to brzm i, ale co to znaczy? Starsi to p rzo d k o w ie, czyli klasy p o d staw o w e. Im kto starszy, tym bardziej g o d n y szacu n k u , czyli klasa dziadek b ęd zie m iała p ierw szeń stw o przed klasą
20
910
Rozdz. 20. Dziedziczenie Kolejność wywoływania konstru ktorów
ojciec. Najpierw ruszy do pracy konstruktor klasy dziadek, potem kolejrui konstruktor klasy ojciec. Gdy już cala starszyzna zostanie obsłużona ruszają dii pracy konstruktory ewentualnych gości tej klasy - czyli konstruktory obiektów składowych goszczących w klasie pochodnej. Na samym końcu rusza do pracy konstruktor klasy pochodnej. Nasi goście - na końcu, ale jeśli dziadek miał swojego gościa (jego składnik!), to oczywiście ta sprawa zostanie załatwiona już przy konstrukcji dziadka. K o n s tru k to r k la sy p o c h o d n e j w y g lą d a tak,
...jak zwykły konstruktor, z tym, że na liście inicjalizacyjnej można umieścić w ywołanie konstruktora klasy podstawowej. Przypominam, że lista inicjalizacyjna znajduje się przy definicji konstru która class A public: A () (); A (double) ;
//
//k o n s tr u k to r d o m n ie m a n y //i n n y k o n s tr u k to r
. . .
); /////////////////////////////////////////////////////////// class B : public A { public: B () ; //d e k la ra c ja k o n s tr u k to r a
); /////////////////////////////////////////////////////////// // d e fin ic ja k o n s tr u k to r a k la s y B B : :B (int x ) : A () //<— lis ta in ic ja liz a c y jn a z w y w o ła n ie m /I k o n s tr u k to r a k la s y p o d s ta w o w e j A
(
//... ciało k o n s tr u k to r a
Powyżej, na liście inicjalizacyjnej konstruktora klasy B, w idzim y umieszczone w yw ołanie konstruktora klasy podstawowej (A). Jeśli klasa podstawowa nie ma żadnego konstruktora, w ów czas oczywiście na liście inicjalizacyjnej nie umieszczamy niczego. Jeśli klasa podstawowa ma konstruktor domniemany (czyli nie wymagający żadnych argumentów) i właśnie ten konstruktor chcielibyśmy w tym przypad ku uruchomić, to możemy go nawet nie umieszczać na liście inicjalizacyjnej bowiem w razie braku na liście iniq'alizacyjnej jakiegokolwiek konstruktora klasy podstawowej - automatycznie uruchamiany jest jej konstruktor domniemany. Jeśli na liście inicjalizacyjnej nie umieściliśmy wywołania konstruktora klasy podstawowej, a klasa podstawowa w zestawie swoich konstruktorów nie ma konstruktora dom niemanego - to wówczas kompilator uzna to za błąd. Po prostu kompilator wie, ie któryś konstruktor musi zostać uruchomiony ale nie powiedziano mu który. (Domniemanego przecież nie ma).
911
Rozdział. 20. Dziedziczenie Kolejność wywoływania konstruktorów
W sumie zasada jest taka: na liście inicjalizacyjnej można pominąć wywołanie konstruktora bezpośredniej klasy podstawowej tylko w tedy, gdy: • klasa podstawowa nie ma żadnego konstruktora. • ma konstruktory, a wśród nich jest konstruktor domniemany. P rzykład
Zagadnienie kolejności uruchamiania konstruktorów klas podstawowych i składowych zilustrujmy przykładem. Mamy tu do czynienia z następującą _________________ - _____ —_____
hierarchią Rys.20-6
______________________
środek transportu 1 samochód
t Mercedes
Dodatkowo w klasie samochód składnikiem jest obiekt klasy silnik W klasie Mercedes składnikiem jest klimatyzacja. Na tym grafie proponuję zaznaczyć to tak:_____ ________ __________ ___________ _________________ Rys.20-5
środek transportu
..
1 sam ochód { s iln ik }
I
M ercedes { k lim a ty z a c ja }
W naszym przykładzie pokażemy konstrukcję i destrukcję dw óch obiektów: 1. obiektu klasy samochód - który ma klasę podstawową środek transportu, a gości on w swojej klasie obiekt klasy silnik, 2. obiektu klasy Mercedes, którego klasa w yw odzi się bezpośrednio od klasy samochód (czyli pośrednio od klasy środek transportu). Obiekt klasy Mercedes gości w sobie składnik klasy klimatyzacja, K o n stru k to ry i d e s tru k to ry będą „gadatliw e", co p o zw o li n am prześledzić ko
lejność ich pracy include using namespace std; class silnik protected: typ; int
912
Rozdz. 20. Dziedziczenie Kolejność wywoływania konstru ktorów
public: silnik(int cout
«
n)
: typ(n)
"\tKonstruktor
silnika
(składnik
s a m o c h o d u ) \n";
} ~ s i l n i k ()
{
cout
«
"\tDestruktor
silnika
(składnik
samochodu)\n";
) M W ///////////////////////* class
klimatyzacja
( d o u b l e temperat; public: i n t nic; klimatyzacja(double *
cout
«
t)
: temperat(t)
"\t\tKonstruktor klimatyzacji "(składnik m e r c e d e s a ) \ n " ;
"
) - k l i m a t y z a c j a ()
{
class
cout
sr
«
"\t\tDestruktor klimatyzacji "(składnik mercedesa)\n";
transportu
protected: double po z_ x , poz_y; public: sr_transportu()
(
cout
"
<<
/ / b ieżą ce w s p ó łr z ę d n e / / n p . g e o g ra fic zn e
" Konstruktor
sr_transportu\n";
} ~ s r _ t r a n s p o r t u () *
cout
«
" Destruktor
sr_transportu\n";
} /W ///////////////////////////////////* c la ss
sam o c h ó d : p u b l i c s r _ t r a n s p o r t u
{ protected: i n t aa; s i l n i k jego silnik; public: s a m o c h ó d (int t y p _ s i l n i k a ) ....... ., 4 : s r _ t r a n s p o r t u (), j e g o _ s i l n i k ( t y p _ s i l m k a ) cout
«
- s a m o c h ó d ()
"\tKonstruktor
samochodu
\n";
,,
Cl
//
V
913
Rozdział. 20. Dziedziczenie Kolejność wywoływania konstruktorów cout
«
"\tDestruktor
W /////////////////////W ///W class
mercedes
: public
samochodu
\n";
samochód
{ protected: double x x x ;
klimatyzacja Casablanca; ^ m e r c e d e s ( d o u b l e x, in t t y p _ m o t o r u , d o u b l e xxx(x), samochód(typ_motoru) , Casablanca(klim) {
cout
<<
k l im)
: ' '
**
"\t\tKonstruktor mercedesa\n" ;
- m e r c e d e s ()
( COl
lt < <
"\t\tDestruktor mercedesa\n";
} i* * * * * * * * * * **************************************************/ m a i n ()
{ cout
<<
"Kreacja
obiektu
klasy
samochód
\n
,
samochód czarny(500); . . . „ cout << "\nobiekt czarny samochód istnieje \n "teraz bedzie likwidowany !\n\n"; cout {
"obiekt
samochód
zlikwidowany\n\n";
cout « "Kreacja obiektu klasy mercedes m e r c e d e s p o p i e l a t y (6 .5, 1200, 22.5); cout « " o b i e k t M e r c e d e s i s t n i e j e \n ^ " t e r a z b e d z i e l i k w i d o w a n y !\n";
cout
□
<<
«
"obiekt
Mercedes
zlikwidowany\n";
Na ekranie zobaczymy Kreacja obiektu klasy samochód Konstruktor sr_transportu Konst r u k t o r silnika (składnik Konstruktor samochodu
samochodu)
obiekt czarny samochód istnieje teraz bedzie likwidowany ! Destruktor samochodu D e s t r u k t o r s i l n i k a (składnik Destruktor sr_transportu obiekt samochód zlikwidowany Kreacja o biektu klasy mercedes Konstruktor sr_transportu
samochodu)
\n";
914
Rozdz. 20. Dziedziczenie Kolejność wywoływania konstru ktorów K o n s t r u k t o r s i l n i k a (składnik samoc h o d u ) Konstruktor samochodu Konstruktor klimatyzacji (składnik mercedesa) Konstruktor mercedesa obiekt Mercedes istnieje teraz bedzie likwidowany ! Destruktor mercedesa Destruktor klimatyzacji (składnik mercedesa) Destruktor samochodu D e s t r u k t o r s i l n i k a (składnik samochodu) Destruktor sr_transportu obiekt Mercedes zlikwidowany
Komentarz P ro g ram m a dw ie części, w których w id zim y konstrukcję i d estru k cję obiektów klas: s a m o c h ó d o raz m e r c e d e s . P a trz ą c na ek ran w id zim y , że obiekt klasy sam o ch ó d k o n stru o w a n y jest tak, iż n ajp ierw ru sza do p racy konstruktor klasy p o d staw o w ej s r _ t r a n s p o r t u (p rzo d ek ), po nim k o n stru k to r obiektu będącego sk ład n ik iem k lasy sa m o c h ó d czyli silnik (gość), a na końcu dopiero rusza d o pracy k o n stru k to r sa m o c h o d u . O Z a u w a ż listę inicjalizacyjną p rzy k o n stru k to rze klasy s a m o c h ó d . W id zim y tu w y w o ła n ie k o n stru k to ra klasy s i l n i k (gość) i klasy s r _ t r a n s p o r t u (przo d ek ). N ie m a znaczenia w jakiej kolejności u m ieścim y te k o n stru k to ry na liście. Z aw sze:
-najpierw zostanie wywołany konstruktor klasy podstawowej, ♦♦♦ -n a s tę p n ie k o n stru k to ry gości (jeśli jest ich k ilk u , to w kolejności w jakiej w y stęp u ją w definiq'i klasy). ♦♦♦ -n a s tę p n ie lista zostanie p rzeg ląd n ięta raz jeszcze i z o stan ą w y k o n an e w szystkie zn ajd u jące się na niej inicjalizacje p o zo stały ch sk ład n ik ó w klasy pochodnej. © N a liście inicjalizacyjnej k o n stru k to ra klasy m e r c e d e s jest inicjalizacja sk ła d n ik a x x x . M im o że jest p ierw sza na liście - w y k o n a się o statn ia, bo w ażniejszy jest k o n stru k to r p rzo d k a: sa m o c h ó d i składnika-gościa: k l i m a t y z a c j a .
Z a u w a ż b ard zo w a ż n ą rzecz. N a liście inicjalizacyjnej k o n stru k to ra klasy m e r c e d e s nie m a w y w o łan ia ko n stru k to ra klasy s r _ t r a n s p o r t u . To d late go, że:
Rozdział. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia
915
na liście inicjalizacyjnej u m ie sz c z a się tylkcr* k o n stru k to ry klas p o d sta w o w y c h bezpośrednich. N a tej liście nie m o ż n a um ieścić w y w o łan ia k o n stru k to ra k la sy s r _ t r a n s g d y ż k o n stru k cja i inicjalizacja sk ład n ik ó w p o ch o d zący ch od śro d k a tran sp o rtu d o k o n a ła się już raz, dzięki k o n stru k to ro w i klasy s a m o c h ó d . To on spowodował w y w o ła n ie tego k o n stru k to ra. Składniki te ju ż w ięc istnieją i nie m o żn a p ró b o w ać inicjalizow ać ich po raz d ru g i.
portu,
D estrukcja ob iek tu n astęp u je w o d w ro tn ej kolejności.
20.7
P rzy p is a n ie i in ic ja liz a c ja o b ie k tó w w w a ru n k a c h d z ie d z ic z e n ia W sp o m n ieliśm y , że o p erato ra p rzy p isan ia się "nie d ziedziczy". Czyli operator przypisania klasy podstawowej nie staje się automatycznie operatorem przypisania klasy pochodnej. W p o d o b n y m se n sie nie dziedziczy się - o d p o w ied zialn eg o za inicjalizację k o n stru k to ra ko p iu jąceg o . (Nie d zied zic zy się p rzecież żadnych k o n stru k to ró w ). Jak zatem zac h o w u je się klasa po ch o d n a, w p rzy p ad k u operacji p rzy p isan ia lub inicjalizacji ? Z an im o d p o w iem y n a to p y tan ie p rzy p o m n ijm y , że obiekt k la sy pochodnej to obiekt klasy p o d sta w o w e j, p lus d o d a tk o w e składniki, które zd efin io w aliśm y w klasie pochodnej. P raca polegająca n a p rzy p isan iu (lub inicjalizacji) sk ład a się w ięc jakby z dw óch części: p rz y p isa n ie (lu b inicjalizacja) części odziedziczonej o d p rzo d k ó w , ♦♦♦ p rz y p isa n ie (lub inicjalizacja) części nowej, w łaściw ej tylko klasie pochodnej. Jasne jest chyba, ż e dla przy p isan ia lu b inicjalizacji części odziedziczonej, w y g o d n ie by b y ło p o słu ży ć się o p erato rem przypisania lub k o n stru k to rem ko piu jący m z k lasy p o d staw o w ej. O czyw iście jest to m ożliw e ty lk o w tedy, jeśli o n e istnieją i w d o d a tk u są d o stęp n e (czyli niepryw atne). W iem y od n ie d a w n a , że klasa po ch o d n a - o peratora p rzy p isan ia i konstruktora - nie d zied ziczy . K lasa p o ch o d n a w ięc ich nie ma - ch yba, że je dla klasy p o ch o d n ej zd efin iu jem y . P o ro zm aw iam y te ra z o p rzypadkach: ♦♦♦ a) g d y k la sa p o chodna nie definiuje swojego o p erato ra przypisania, b )
4)
g d y k lasa po ch o d n a
n ie d e fin iu je
sw ojego k o n stru k to ra kopiującego.
Uwaga wtajemniczeni: nie mówimy tu na razie o dziedziczeniu wirtualnym.
20
916
Rozdz. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia
20.7.1
Klasa pochodna nie definiuje sw ojego operatora przypisania Jeśli klasa p o ch o d n a K nie definiuje sw ojego o p e ra to ra p rz y p isa n ia , wówczas k o m p ilato r postara się o autom atyczne w y g en ero w an ie o p erato ra przypisania K
&
K : : o p e r a t o r = ( K&
);
p racu jąceg o w ed łu g zasady: przypisanie „ sk ła d n ik p o s k ła d n ik u ". M e to d a „składnik po składniku"
M eto d a ta oznacza, że: <$♦ W stosunku d o składników , które są ty p ó w w b u d o w a n y c h , zro b i się ich kopie. «$♦ Jeśli chodzi o sk ład n ik i typów z d e fin io w a n y c h p rz e z u ży tk o w n ik a (klasy), to z o stan ą one przypisane za p o m o cą o p erato ró w przypisania ze sw oich klas. P odobnie d la k la s y p o d s ta w o w e j-je śli m a o n a o p e ra to r p rzy p isan ia, to dla p rzy p isan ia tej części obiektu będącej d z ie d z ic tw e m - u ż y ty zostanie o p erato r p rzy p isan ia jej klasy. Jeśli jed n ak jakiś sk ład n ik lub klasa p o d staw o w a m ają o p e ra to r p rzy p isan ia, ale p ry w a tn y - oznacza to, ż e te obiekty przy p isu je się w jakiś sp ecy ficzn y sposób, i d a n a klasa nie życzy sobie, by robił to ktokolw iek n ie u p o w a ż n io n y . K om pilator w ó w c zas zro zu m ie to i nie w ygeneruje dla całości, czyli d la klasy pochodnej, o p e ra to ra p rzy p isan ia. R ó ż n i c a m i ę d z y p r z y p i s a n i e m „bit p o b ici e", a „ s k ł a d n i k p o s k ł a d n i k u "
Nie jest to, jak widać, ślepe przypisanie metodą „bit po bicie" - bo to zrobiłoby identyczną kopię. Przecież jeśli jakiś składnik zdecydował, że ma być przypisywany za pomocą swego specjalnego operatora - to kompilator generując operator przypisania klasy pochodnej powinien ten operatom przodka/składnika użyć. Zasada „składnik-po-składniku" to nie bezmyślne skopiowanie jednego obiektu na drugi. Zgodnie z tą zasadą, jeśli składnik jest obiektem innei klasy (gość), a ta klasa ma dostępny (nieprywatny) operator przypisania to zostanie on uruchomiony dla tego składnika. Podobn ie w przypadku części będącej dziedzictwem po klasie podstawowej Zasada kopiowania „składnikpo składniku" mówi, że dla tego dziedzictwo uruchomiony zostanie operator przypisania właściwy klasie podstawowej Pod warunkiem, że istnieje i jest dostępny.
const
i referen cje s ą p rz e sz k o d ą
G d y b y klasa m iała jak iś sk ład n ik const lub sk ład n ik b ęd ący referencją, to o p e ra to r p rzy p isan ia nie b ę d z ie autom atycznie w y g en ero w an y . P o p ro stu d la teg o ,
Rozdział. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia
917
że d o tych sk ła d n ik ó w nie w o ln o nic p rz y p isy w a ć . M ogą być ty lk o inicjalizow a n e raz na zaw sze. Ich obecność su g eru je, ż e nie jest to zw y k ła sytuacja. Jeśli chcem y, m im o w szy stk o , m ieć o p e ra to r p rzy p isan ia - m u sim y go sam i zdefiniow ać. Jak to się robi? Z o b aczy m y to za kilka stro n (str. 918). N a razie ciągnijm y ro zw ażan ia o d ru g iej sytuacji, gdy...
20.7.2
Klasa pochodna nie defin iu je sw ojego konstruktora kopiującego Jeśli klasa p o c h o d n a K nie definiuje sw ojego k o n stru k to ra k o piującego - to ko m p ilato r p o sta ra się sam w y g en ero w ać d la tej klasy k o n s tru k to r kopiujący K : :K ( K
&) ;
- pracujący w e d łu g z a sa d y „sk ład n ik p o sk ład n ik u O tym czym jest k o p io w an ie w ed łu g z a s a d y „sk ład n ik po s k ła d n ik u , m ów ili śm y w p o p rz e d n im parag rafie. W m y śl tej zasad y - jeśli k lasa p o d staw o w a (przodek) lu b sk ład n ik (gość) m ają k o n stru k to ry kopiujące, to ten g enerow any au to m aty c zn ie k o n s tru k to r klasy p o ch o d n ej - je w ykorzysta.
Kiedy konstruktor kopiujący klasy pochodnej nie może być generowany automatycznie? - W tedy, g d y s k ła d n ik i tej klasy (goście ) lu b klasa p o d sta w o w a (przodek) mają jakiś k o n stru k to r k o p iujący, k tó ry jest n ie d o stęp n y (bo p ry w a tn y ) . O znacza to, że, z jed n ej stro n y , w ich p rz y p a d k u jest jakiś specjalny sposób k o n stru o w an ia ich o b iektów , a z d ru g ie j - ze me zyczą sobie, by robił to ktokolw iek n ie u p o w aż n io n y . D la k o m p ilato ra je st to p rzesła n k a, b y nie gen ero w ać autom atycznie k o n stru k to ra k o p iu jąceg o klasy p o chodnej. Jeśli u ży tk o w n ik chce go mieć, to niech zd efin iu je so b ie go sam - p o d ejm u jąc p rzy tym decyzje, co zrobić z tam ty m i d z iw n y m i sytuacjam i. Jak się tym zająć - zo b aczy m y n ieb aw em (str. 918).
20.7.3
Inicjalizacja i przypisyw anie w ed łu g obiektu w zorcow ego będącego c o n s t A by p o d cza s p rz y p isa n ia czy inicjalizacji móc posłużyć się takim obiektem w zo rco w y m klasy K, który m a p rzy d o m e k c o n s t , należy m u zag w aran to w ać \ 0 nietykalność. O m ó w io n e p rzed ch w ilą g en ero w an e au to m aty czn ie o p erato r przypisania i k o n stru k to r k o p i u j ą c y _____________________
5)
Jest prywatny, a przyjaźń z klasą pochodną nie jest zawarta.
918
Rozdz. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia K &
//o p e r a to r p r z y p is a n ia //k o n s tr u k to r k o p iu ją c y
K::operator=(K &); K: :K!K &) ;
takiej gw arancji nie dają Zatem, posługując się nim i, n ie m o żem y p o p raw ej stro n ie zn ak u = postaw ić obiektu z p rzy d o m k iem c o n s t . A by tak było, kom pilator m usiałby w y g en ero w ać tak ie funkcje: K &
K : : o p e r a t o r = ( c o n s t K &); K ::K ( c o n s t K &) ;
//o p e r a to r p r z y p is a n ia j j k o n stru k to r kopiu jący
Tak też dla nas zrobi, ale tylko w tedy, gdy sp e łn io n y jest d ra k o ń sk i w a ru n ek , że: ♦♦♦ w szystkie klasy podstaw ow e tej klasy, ♦♦♦ o raz w szystkie klasy składników tej k lasy - mają takie operatory p rzy p isan ia (cw . k o n stru k tó ry kopiujące), które przyjm ują argum enty c o n s t . In n y m i słow y w arunek jest spełniony w ted y , g d y w szy scy w sp ó łp raco w n icy o p e ra to ra przypisania (konstruktora k o p iu jąceg o ),zag w aran tu ją arg u m e n to w i nietykalność.
20.7.4
D efin iow an ie konstruktora k op iu jącego i operatora przypisania dla klasy pochodnej P o p rzed n io m ów iliśm y o sytuacjach, kiedy k o n s tru k to r k o p iujący i o p erato r '= ' g e n ero w an e są autom atycznie. T eraz po ro zm aw iam y o tym , jak się sam em u zająć ich definicjam i. P oniew aż d a w n o ju ż nie było przy k ład ó w - zacznijm y o d tego. W przykładzie zauważysz przeładowanie operatora << . Nie ma to nic wspólnego z treścią tego paragrafu, ale skoro ju ż znamy owo wspaniałe narzędzie, to ćwiczmy posługiwanie się nim.
O to d łu g o o c z e k iw a n y p rzykła d : #include u s i n g n a m e s p a c e std; e n u m k o l o r { c z a r ny ,
czerwony,
niebieski,
biały
};
I lllllllllllllllllllllllllllllllllllllllt lllS illllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll^ class pojazd
lllj ^ ' '
{ protected: int ilosc_kol; public: p o j a z d ( i n t ile) : i l o s c _ k o l ( i l e ) {} p o j a z d ( c o n s t p o j a z d &); po j a z d & operator=(const pojazd & w z ) ;
u
...
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIH class
automobil
: public pojazd
int stan_licznika; kolor kolor_karoserii; public: a u t o m o b i l ( i n t kola, k olor k,
int l i c z n i k)
I I
: pojazd(kola)
lJ l ”
919
Rozdział. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia
stan_ l i c z n i k a = licznik; k o l o r k a r o s e r i i = k;
a u t o m o b i l ( c o n s t a u t o m o b i l fi) ; automobil
& operator=(const
friend o s t r e a m & o p e r a t o r « (ostream
automobil
& ekran,
sww); . . . . . . a u t o m o b i l & obu),
const
I; z*************************************************************^ .................................................... p o j a z d : : p o j a z d (const ^
ilosc_kol
=
pojazd
& wzór)
w z ó r . ilosc_kol;
L * ********** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / a u to m o b il:: a u to m o b il( c o n s t a u to m o b il & w zorzec)
@
: p o j a z d ( w zo r z e c) stan licznika = 0 ; kolor karoserii =
//
wzorzec .k o l o r _ k a r o s e r n ,
} * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * "* ;“ ' > ;;;
.............................................................. /
pojazd
& p o j a z d ::o p e r a t o r = (c o n s t
pojazd
& wz)
ilosc_kol = wz.ilosc_kol; return *this;
, ,
q
L , * * * * , * . * ************************************************** 1 automobil
& a u t o m o b i l : : o p e r a t o r - (const a u t o m o b i l
&wzor)
// ®
-trzy sposoby zrobienia tego samego ■wywołanie jawne *this) . p o j a z d : : o p e r a t o r - ( w z ó r ) ; ((
_____ nicjawnie: operator =
z
klasy podstawowej wywołujemy tak
p o j a z d * w s k p o j a z d = this; ( ♦ w s k p o j a z d ) = w z ór ; I / ------- a lb o ta k (referen cja m i)— ---- p o j a z d & r e f p o j a z d = * this; r e f p o j a z d = wzór;
'
//d a le j tulko d o k a ń c z a m y robotę
// 0
k o l o r k a r o s e r i i = wzór . k o l o r _ k a r o s e r n » s t a n l i c z n i k a = wzór .s t a n _ l i c z n i k a + 2; return
*this;
}************************************************************y
/*** Abu sie oswoić z przeładowaniem operatora « o s t r e a m fi o p e r a t o r « (ostream & ekran, '
ekran
« << <<
const automobil
" L i c z b a kol " « o b j .i l o s c ^ k o l ••, L i c z n i k a M << o b j .s t a n _ l i c z n i k a ", k o l o r n r " « o b j .k o l o r _ k a r o s e r i i
, & obj)
«endl;
920
Rozdz. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia return
ekran;
l1*******************************************************/ int main{)
{ automobil moj(4, biały, automobil twój = m o j ;
30000);
// ©
cout << "Lista istniejących automobili \n" << "moj " << moj << "twój " << twój;
// ©
automobil dziwak(3, biały, 5000); cout
<<
"Jeszcze
«
dziwak;
jeden o nazwie
dziwak:\n"
// ©
dziwak = moj; cout << "dziwak po przypisaniu:\n"<< dziwak; const automobil muzealny(4, czerwony, 25000); dziwak = muzealny; cout cout
<< <<
// ©
"Muzealny: " « muzealny; "Dzi w a k po... " << dziwak;
Po wykonaniu tego programu na ekranie pojawi się Lista istniejących automobili moj Liczba kol 4, Licznika 30000, kolor nr 3 twój Liczba kol 4, Licznika 0, kolor nr 3 Jeszcze jeden o nazwie dziwak: Liczba kol 3, Licznika 5000, kolor nr 3 dziwak po przypisaniu: Liczba kol 4, Licznika 30002, kolor nr 3 Muzealny: Liczba kol 4, Licznika 25000, kolor nr 1 Dziwak po... Liczba kol 4, Licznika 25002, kolor nr 1
v*
2
Komentarz © O to lista p o ch o d zen ia klasy automobil. Jak w id zim y z tej listy - klasa a u t o m o b i l jest klasą p o ch o d n ą od klasy p o j a z d O , k tó rą d zied zic zy w sp o só b publiczny. Pytanie: czy klasa zamiast a u to m o b il mogłaby się nazywać krócej: a u to ? Nie! Słowo a u to jest słowem kluczowym w językach C i C++. Wiedzia łem, że o tym zapomnisz, bo jest rzadko używane (patrz str. 164).
© O to definicja k o n stru k to ra k o p iu ją c eg o k la s y
automobil.
W tej linijce w id zim y listę inicjalizacyjną tego k o nstruktora. C ytuję: automobil(const automobil & wzorzec)
: pojazd(wzorzec)
921
Rozdział. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia
N a tej liście (czyli p o d w u k ro p k u ) w id z im y w y w o łan ie k o n stru k to ra k o p iu jącego klasy p o d sta w o w e j p o j a z d . Tym czego k o n s tru k to r kopiujący klasy p o j a z d nie zain icjalizu je - czyli sk ła d n ik am i z części p o ch o d n ej - zajm ujem y się poniżej, ju ż w ciele k o n stru k to ra. C iekaw ostka: k ie d y k o n stru u je się n o w y sam o ch ó d na w zó r ju ż istniejącego, to - m im o w ierności k o p io w an ia - nie m a sensu n astaw iać m u licznika przeje chanych k ilo m etró w na tę sam ą w artość, k tó rą ma na liczniku te n starszy. D ecy dujem y w ięc, ż e n o w o n a ro d z o n y a u t o m o b i l niech m a liczn ik = 0. Tak dla hecy, choćby p o to, by nie była to bliźn iacza kopia - jaką m ó g łb y nam przecież zrobić k o n stru k to r g en ero w an y au to m aty czn ie.
Stawiając sprawę bardziej formalnie: S y tuacja tak a zach o d zi zw y k le w tedy, g d y o b iek t w zorcow y ma jak iś sta n w ew n ętrzn y , k tó ry św iadczy, że on ju ż sobie trochę p o ż y ł", a now y, ten w łaśn ie inicjalizow any o b iek t pow inien mieć s ta n w ew n ę trz n y ch arak tery sty czn y dla n o w o w y tw o rzo n eg o o b iek tu . Jeśli m am y taką sytuację, to jest to p rz e sła n k a do zdefinio w a n ia swojej w ersji tego k o n stru k to ra. O Definicja operatora przypisania dla klasy podstawowej p o j a z d . Nic ciekawego, znamy to Przypominam, że zwyczajowo rezultatem takiej funkcji operatoro wej - jest referencja do obiektu, na którym operator pracował. Umożliwia to potem kaskadowe używ anie tego operatora, czyli zapis: a = b = c = d;
© Definicja operatora przypisania klasy pochodnej automobil. Oczywiście nie ma tu mowy o jakiejś liście inicjalizacyjnej, bo to przecież nie jest konstruktor. Tu jest w y zw an ie: Jak sobie uprościć życie - i tę część obiektu, która je st dziedzictw em o d klasy p o j a z d - p rzy p isać za pom ocą (gotow ego) operatora p rz y p isa n ia z klasy p o d staw o w ej p o j a z d ? Innymi słowy: Jak, w ciele o p erato ra p rzy p isan ia klasy p o ch o d n ej w y w o łać o p erato r p rzy p isan ia z klasy p o j a z d ?
automobil,
N ie jest to tru d n e , sam zobacz. Ten nasz o b iek t klasy a u t o m o b i l , to oczywiście ( * t h i s ) . T o na jego rzecz m am y w y w o łać o p e r a t o r = z klasy p o j a z d . N ajłatw iej je st to zrobić stosując jaw ne w yw ołanie (*this).pojazd::operator=(wzór);
czyli
obiekt, zakres :.funkcja_operatorowa(argument);
Jak w id zisz,
operator-
klasy pochodnej.
jest z klasy podstaw ow ej, a jego a rg u m e n t
(wzór)
922
Rozdz. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia
C zy w o ln o n a m zrobić takie "oszustw o"?
"O szustw o", bo przecież o p e r a t o r = z klasy p o d sta w o w e j p o j a zd, sp o d ziew a się a rg u m e n tu b ęd ąceg o referencją d o p o j a z d u (klasa podstawowa), a tym czasem m y tutaj w ysyłam y m u - jako a r g u m e n t - coś, co jest in n eg o typui. (Ten w z ó r , to przecież referencja obiektu k la sy pochodnej a u t o m o b i l ) . W olno tak? W olno! U m ożliw ia to zasada, o której już n ie d a w n o w spom inaliśm y. M ianow icie: obiekt klasy p o ch o d n ej m oże być, w pew nych sytuacjach, traktow any jak o b iek t k lasy p o d staw o w ej. A u t o m o b i l to przecież rodzaj p o j a z d u ! Funkcji, która spodziewa się nie obiektu, ale w skaźnika do klasy podsta wowej lub referencji do klasy podstawowej - m ożna w ysłać w skaźnik lub referencję do klasy p o c h o d n e j. .immm.iiL.inM■lini■11 —iiihw imn iwu—■hm «t— i■ii1i iri-n-m ----- ,,<• motema js; mmmmmm* as;■I
[
To w łaśn ie robim y tu taj: w z o r jest referencją o b iek tu klasy a u t o m o b i 1 , a m im o to, m o że zostać w ysłany d o funkcji sp odziew ającej się referencji do p o j a z d u . W ten sposób u sp okoiłem Cię, że ten arg u m e n t, czy li to, co by stało p o praw ej stro n ie operatora= m oże być obiektem k lasy poch o d n ej. o b ie k t_ k la s y p o d s ta w o w e j = o b ie k t _ k la s y _ p o c h o d n e j;
Z o b aczm y teraz co stoi p o lewej stronie o p erato ra= . P rzecież nie jest to w cale ob iek t klasy podstaw ow ej... C zy m o ż n a tę funkcję o p e ra to ro w ą z klasy
podstawowej pojazd
wywo
łać n a rz e c z obiektu * t h i s ?
W y w o ły w an ie tej funkcji p o j a z d : : o p e r a t o r = n a rzecz obiektu ( * t h i s ) , będ ąceg o przecież ob iek tem klasy a u t o m o b i 1 - to sp ra w a zu p e łn ie oczyw ista: Funkcja p o j a z d : : o p e r a t o r = p rzez d zied zic zen ie staje się także sk ład n ik iem jego klasy a u t o m o b i l tyle, że jest zasłonięta p rz e z funkcję o p erato ro w ą o p e r a t o r = klasy pochodnej. Jeśli jednak p o słu g u jem y się o p erato rem zak re su , to zasło n ięte nam się o d słan ia. N ie m a p ro b lem u , sp ra w a załatw io n a. Z atem instrukcja
Jeśli tak ie jawne w y w o łan ie operatora= z k lasy p o d staw o w ej nie w y d aje Ci się eleganckie, to teraz b ę d z ie m y trochę w y d ziw iać. S pójrz zatem jeszcze ra z d o ciała o p erato ra.
923
Rozdział. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia W id zim y
tam
jeszcze
dwa
inne
sposoby
zro b ien ia
tego
sam ego
przypisania.
W iem y oczyw iście, że funkcja o p e ra to ro w a op e r a t o r = z k la sy pod staw o w ej, ru sz y d o pracy (bez konieczności stosowania operatora zakresu) w ted y , g d y po lew ej stronie zn ak u p rzy p isan ia z o b a c z y obiekt klasy podstaw o w ej. obiekt_klasy_podstawowej = c o ś _ ta m A by ru szy ła d o p ra c y m im o p rz y p isy w a n ia d o obiektu k la s y pochodnej (za m iast p o d staw o w ej) trzeb a ją oszukać. S p o s ó b z e w skaźnikiem
Jedny m ze sp o so b ó w jest zd efin io w an ie sobie w sk a źn ik a d o obiektu klasy p o d staw o w ej i u s ta w ie n ie g o tak, by p o k aza ł na pojazd
*wskpojazd
= this
Jest to n ajzupełniej p o p ra w n a operacja: W sk aźn ik klasy p o d sta w ow ej m oże p o k azy w ać n a obiekt klasy p o ch o d n ej. Podobnie z referencją. Nic w tym dziwnego. W końcu jak przyjeżdżasz swoun samochodem do myjni samochodowej i mówisz „Proszę umyć ten p o ja zd " - to nie
oszukujesz. M ając ju ż tak u sta w io n y w skaźnik, m o żem y napisać in stru k cję przypisania: (*w sk p ojazd )
// ob iekt J d a s y j p o d s t a w o w e j = coś J a m
= w z ó r;
Jest to wydaniem w myjn i takiego polecenia: Niech ten m ój pojazd będzie tak wymyty, jak ta (wzorcowa) "bryka " A by w iedzieć, k tó ry o p erato r p rzy p isan ia ruszy tutaj d o p ra c y - w ystarczy zastan o w ić się, co stoi po lewej stronie zn ak u =. Stoi tam obiekt p o k a z y w a n y w sk aźn ik iem na pojazdy. Z atem ru sz y operator dla klasy pojazd. C zyli ten o p e ra to r,o k tó ry m m arzyliśm y. S p o s ó b z referencją
W n astęp n y ch linijkach jest p o w tó rzen ie tego sam ego p rz y p isa n ia , ale przy zasto so w an iu c h w y tu z referencją d o obiektu klasy po j azd. pojazd
& refpojazd
r e f p o j a z d = w zór;
=
*this;
II d e fin ic ja referen cji //operacja p r z y p is a n ia
W szy stk ie trzy p o k azan e tu sposoby - są ró w n o p raw n e . Przyz nasz jed n ak , że te nie-jaw ne w yw ołania w y g ląd ają prościej.
I
© To jed n ak nie koniec pracy. O perator p rzy p isan ia z klasy p o d staw o w ej bardzo nam p o m ógł, ale pam iętaj, że o p erato r przypisania klasy p o j a z d przypisał 6)
"Bryka" - to, w niektórych środowiskach, referencja (przezwisko) samochodu.
924
Rozdz. 20. Dziedziczenie Przypisanie i inicjalizacja obiektów w w arunkach dziedziczenia tylko d o tych sk ład n ik ó w , które były m u zn an e . C zyli tych odziedziczonych z klasy p o d staw o w ej p o j a z d . Nic nie w ie on p rzecież o sk ład n ik ach do d an y ch w klasie pochodnej a u t o m o b i l . D lateg o teraz m usim y jeszcze uzu p ełn ić p rz y p isa n ie tego, czego on nie umiał. W n aszy m p rzy p ad k u są to proste dw ie instrukcje: kolor karoserii stan licznika
= =
w z ó r .k o l o r _ k a r o s e r i i ; w z ó r . s t a n l i c z n i k a + 2;
Jak w id ać, tu taj też nie p rzy p isu jem y licznika b e z k ry ty czn ie - ż e b y nie pow stała kopia całkiem bliźniacza. Przecież, gdyby chodziło nam o kopię bliźniaczą, to urystarczyłoby automa tyczne przypisanie metodą "składnik po składniku ".
Z obaczy liśm y więc, jak sp ry tn ie m ożna zd efin io w ać o p e ra to r p rzy p isan ia i k o n s tru k to r kopujący d la klasy p o c h o d n e j-a b y się p rz y tym nie napracow ać. T e r a z z o b a c z m y , j a k m o ż n a z t e g o s k o r z y s t a ć w funkcji
main
© Tu w m a in w id zim y z n a k '= '. Jaka funkcja tu taj ru sz y d o pracy? M ów iliśm y o tym w cześniej, ale nie zaszk o d zi jeszcze ra z p rzy p o m n ieć, że jed y n ą sytuacją, gdy n a w idok zn ak u '= ' ru s z a k o n s tru k to r k o piujący, jest linijka definicji obiektu. T o w łaśnie ta sytuacja. D efiniujem y tu obiekt, który ma być tak i, jak in n y obiekt p o k azy w an y m u na w zór. W szystk ie d alsze zn ak i '= ' w tym p ro g ram ie to ju ż w y w o łan ia o p erato ra p rzy p isan ia. © P ra w d a , że opłacało się p rzeład o w ać op erato r « w y p isy w a n ia na ek ran ? T eraz zap is m am y tak prosty, jak b y śm y chcieli w y p isać na ek ra n liczbę i n t . cout << moj;
D robn a u w ag a o przyjaźni: Ponieiuaż operator ten ma wypisywać na ekran składniki, które w klasie a u tomobi 1 są p r o t e c t ed, dlatego musi dostać do tego od klasy au tomobil pozwoleń ie. Tym pozwoleniem jest deklaracja przyjaźni złożona w definicji klasy automobil. Składnik i l o s c _ k o l z klasy podsta wowej p o j a z d wchodzi do klasy pochodnej jako p r o t e c t e d , dlatego opera t o r « niema problemu z odczytaniem tego składnika- wystarcza na to zgoda klasy automobil. Gdyby jednak ten składnik był w klasie p o j a z d prywatny, to ta zgoda by nie wystarczyła. Aby mógł być odczytany —deklaracja przyjaźni z naszą funkcją opera t o r « musiałaby być także w klasie p o j a z d . © O to w y w o ła n ie o p erato ra p rzy p isan ia na rzecz ob iek tu k lasy pochodnej. dziwak
- moj;
© Definicja obiektu z p rzy d o m k iem c o n s t . O biekt ten m a tak że słu ży ć za w z ó r d o p rz y p isy w a n ia i k o n stru o w an ia. A by to c o n s t było tutaj m ożliw e, m u siałem
925
Rozdział. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne)
um ieścić słow a c o n s t w różnych in n y ch m iejscach w p ro g ra m ie . W szęd zie tam , g d zie w y sy ła m y ten o b iek t p rz e z referencję d o ró żn y ch funkcji. W y stęp u jące tam w d ek laracjach arg u m e n tó w form aln y ch sło w a c o n s t są obietnicam i tych funkcji, że n ie b ę d ą nic zm ien iać w obiekcie. Bez u sły szen ia tych w szy stk ich obietnic, k o m p ila to r nie d o p u ś ciłby d o u ży cia tego stałeg o obiektu w zo rco w eg o w linijce inicjalizacji an i p rzy p isan ia. (M im o że przecież tak i o b iek t stałby p o p raw ej, "bezpiecznej", stro n ie zn ak u =).
2 0 .8
D z ie d z ic z e n ie o d kilku " ro d z ic ó w " (czyli w ie lo k ro tn e ) D otychczas zajm o w aliśm y się d zied ziczen iem je d n o k ro tn y m - czyli takim , g d zie klasa p o c h o d n a m a tylko jedną b ezp o śred n ią klasę p o d staw o w ą. T eraz d o w iem y się, że: K lasa m o że w y w o d zić się b ezp o śred n io od w ięcej n iż jednej klasy. T akie d zied ziczen ie n az y w a m y d zied ziczen iem w ielokrotnym .
I
P osługując się a n a lo g ią ro d zin n ą m o ż n a pow iedzieć, że k lasa m oże mieć nie tylko ojca, ale ta k ż e m atkę. I „ojciec" i „ m atk a" są d la niej bezp o śred n im i klasam i p o d sta w o w y m i. Z nich b e z p o ś re d n io się ona w y w o d z i. D ziedziczenie ta k ie jest rzadziej u ż y w a n e niż d zied ziczen ie jednokrotne.
Z aletą d zied ziczen ia w ielo k ro tn eg o jest to, że d zięk i niem u m oże m y p o w ią z a ć ze sobą n iezależn e od siebie ty p y klas. Przydaje się to na p rz y k ła d w ted y , g d y k lasa p o chodna m a m ieć dw oistą naturę O to p rzy k ła d ta k ieg o dziedziczenia. W ystępują tu sam ochód i łó d k a. Z obacz, co z nich pow staje: class
klasy
podstaw ow e
samochód
{
II ... 1; / / --------------------------------------class
{
lodka
II ...
class
amfibia
20
: public
samochód,
{
// 1; N a grafie w y g lą d a to następująco:
public
lodka
926
Rozdz. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne)
Rys. 20-7
Jak w idać, dziedziczenia w ielokrotnego d o k o n u je się p rz e z u m ieszczen ie na liście p o chodzenia - więcej niż jednej klasy p o d staw o w ej. Lista m o że być d o w o ln ie dłu g a, klasa m oże w y w o d zić się od d o w o ln ej ilości klas p o d staw o w ych.
Trzeba jednak pamiętać o kilku rzeczach 1) D an a klasa po d staw o w a m oże na liście p o ch o d z e n ia pojaw ić się tylko raz. Z atem błędem byłoby po w ied zen ie, że am fibia p o c h o d z i o d sam o ch o d u , łódki i jeszcze raz od sam ochodu. < 0
2) Definicja klasy um ieszczonej na liście p o ch o d ze n ia - m u si być ju ż znana kom p ilato ro w i. N ie w ystarcza deklaracja zap o w ia d ając a (zw iastująca): class
samochód;
C zyli nie w y starcza p o w ied zen ie: „jakby co, to sam o ch ó d jest n azw ą klasy". K om pilator m usi w tym m om encie już znać sam ą definicję klasy podstaw ow ej, a w ięc w ied zieć w szystko o typie jej sk ład n ik ó w . Ta w ied za jest m u niezbędna p rz y p racy n ad w łaśnie d efin io w an ą klasą p o ch o d n ą. 3) N a liście p o ch o d zen ia p rz e d n azw am i klas p o d sta w o w y c h m ogą staćo k reślenia sp o so b u d ziedziczenia ( p r i v a t e , p r o t e c t e d , p u b l i c ) . P o d o b n ie jak w z n a n y m nam d zied ziczen iu jed n o k ro tn y m , d ecy d u ją o n e o tym , jaki d o stęp będą m iały o d zied ziczo n e n ie p ry w atn e sk ład n ik i klas p o d staw o w y ch . D o m n iem an ie jest identyczne, jak p rz y d zied ziczen iu jed n o k ro tn y m . O p u sz czenie tego sło w a przed n azw ą klasy p o d staw o w ej u z n a w a n e jest jako u m iesz czenie tam sło w a p r i v a t e . W p rz y p a d k u s tru k tu ry (która jest rodzajem klasy) - p rz e z d o m n iem an ie za k ła d a się d zied ziczen ie p u b l i c .
2
P am iętać należy, że na liście p o ch o d zen ia klasa p o d s ta w o w a m u si m ieć w ła s n e o k reślen ie sposobu d ziedziczenia. O puszczając je nie m o ż em y liczyć n a to , że ob o w iązu je n ad al to określenie, k tó re n ap isaliśm y p rz e d p o p rzed n ią klasą p o d sta w o w ą . K om pilator w takim p rz y p a d k u z a k ła d a d o m n iem an y sposób d zied zic zen ia - czyli p r i v a t e (a dla stru k tu r: p u b l i c ) . Tak w ięc zapis class
//
amfibia
: public
...
}; O d p o w iad a zap iso w i
samochód,
lodka
927
Rozdział. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne) class
t
amfibia
: public
samochód,
private
lodka
II ...
}; D o m n ie m an e o k reślen ie jest p r i v a t e , ale nie rad zę w ty m p rz y p a d k u k o rzy s tać z teg o d o m n iem an ia. P rak ty k a w y k azu je, że - nie w ia d o m o d laczeg o - w szy scy p ro g ram iści w tym p rz y p a d k u są d z ą , ż e d o m n ie m a n e je st ok reślen ie p u b l i c . N ajlep iej więc p rzy j m ijm y zasad ę , że z a w s z e piszem y to ok reślen ie sposobu d zied ziczen ia p r i v a t e lub p u b l i c , a nie zd ajem y się na d o m n iem an ie k o m p ilato ra.
20 .8.1
Konstruktor k lasy pochodnej przy w ielokrotnym d zied ziczen iu W p o w y ższy m p rz y k ła d z ie d la p ro sto ty nie m ów iliśm y o k o n stru k to rach . P rzy w ielo k ro tn y m d zied ziczen iu - czyli w sytuacji, g d y klasa p o chodna m a w ięcej niż jedną b ezp o śred n ią klasę p o d sta w o w ą - definiując k o n stru k to r klasy p o ch o d n ej m u sim y jakoś to u w zg lęd n ić. Jak? O tó ż, n a liście inicjalizacyjnej k o n stru k to ra tej klasy p o ch o d n ej m ożem y um ieścić w y w o łan ia k o n stru k to ró w w szystkich b ezp o śred n ich klas p o d staw o w y ch . _ To znaczy na liście inicjalizacyjnej amfibii może być wywołanie konstruk tora samochodu i konstruktora łódki. W y w o łan ie takiego k o n stru k to ra klasy po d staw o w ej m o żn a opuścić, gdyby m iało to być w y w o łan ie k o n stru k to ra d o m n iem an eg o (czyli takiego, który m o ż n a w y w o łać b ez żad n y ch arg u m en tó w ). O czyw iście, jeśli k tó raś z klas p o d staw o w y ch nie ma żad n e g o k o n stru k to ra, to jasne jest, że na liście inicjalizacyjnej nie m a też o d n o śn eg o w y w ołania. N a liście inicjalizacyjnej m oże się w ięc zn aleźć kilka w y w o łań k onstruktorów klas p o d staw o w y ch . Po jednym dla każdej klasy podstaw ow ej. M a się rozum ieć, że p rzy k o n stru o w an iu obiektu na p o d sta w ie tej listy inicjalizacyjnej n ad al obow iązuje z asad a , iż najpierw u sz a n o w a n i są starsi, p o te m goście. Skoro starszych-rodziców (b ezpośrednie k lasy pod staw o w e) jest kilkoro - ich k o n stru k to ry b ęd ą w y w o ły w an e w kolejności, w jakiej rodzice w ystępują n a liście p o c h o d z e n ia klasy . P otem d o p ie ro w y w o łan e zostaną k o n stru k to ry gości (składniki będące obiek 20 tam i innych klas) - w kolejności, w jakiej obiekty tych gości w y stęp u ją w ciele definicji klasy. ■ . • j ■ Na pewno zgadłeś, że destrukcja odbywa się w kolejności odwrotne].
Oto przykład... ...klasy łó d k a, sam ochód, am fibia u z u p ełn io n e zostały k o n stru k to ram i.
928
Rozdz. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne) finclude using namespace std; class samochód
{ protected: int a; public: samochód(int arg)
: a(arg)
{ cout «
"Konstruktor samochodu\n";
}
im iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiim class lodka
{ protected: int b; public: l o d k a (int x) : b(x)
{ cout << "Konstruktor lodki \n";
) }; /////////////////////////////////////////////////////////////// class amfibia : public samochód, public lodka
// 0
{ public: amf i b i a O
: samochód (1991), lodka(4)
// 0
{ cout << "Konstruktor amfibii \n";
} void pisz_skladniki()
{ cout « "Oto odziedziczone skladniki\na = " « << "\t b = " << b << endl;
a
} }; int main() amfibia aaa; a a a .pisz_skladniki();
Na ekranie pojawi się Konstruktor samochodu Konstruktor lodki Konstruktor amfibii Oto odziedziczone składniki a = 1991 b = 4
Komentarz P rogram nie robi jeszcze nic sen so w n eg o poza w y p isa n ie m o d zied ziczo n y ch skład n ik ó w . To p rzy d a n am się w n astępnym p arag rafie.
929
Rozdział. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne) O Lista p o c h o d z e n ia k lasy
amfibia.
© T eraz zw ró ć u w a g ę na listę in icjaliza cy jn ą ko n stru k to ra k lasy niej w y w o łan ia obu b ezp o śred n ich klas podstaw ow ych.
amfibia.
Są na
Z c z y m to s ię je ?
N ie chciałbym je d n ak , byś m yślał, że dziedziczenie w ielo k ro tn e (czyli d zied zi czenie od w ielu ro d zicó w ) służy jed y n ie d o tw orzenia klas w rodzaju radio-m ag n eto fo n ", ch łop-robotnik , czy "baba-dziwo". Tego ty p u d z ie d zic zen ie jest b ard zo p o p u la rn e w św iecie rzeczy w isty m . Co p ra w d a jesteś studentem, w ięc w zasad zie pow inieneś być o b ie k te m klasy s t u d e n t . A le p rzecież k lasa s t u d e n t nie m ó w i całej p ra w d y o Tobie. Przecież rów n o cześn ie jesteś też rodzajem kierowa/ pojazdu mechanicznego, rodzajem melomana, ro d zajem krwiodawcy. I N ależ y sz w ięc rów nocześnie do zupełnie o d ręb n y ch hierarchii.
S k ą d mi p rz y sz e d ł d o głow y ten k rw io d aw c a?
O tóż, k ied y ś p isałem p ro g ram , w któ ry m w ystępow ało w iele ty p ó w u rząd zeń p o m iaro w y ch . O w e ty p y u staw io n e były w ro zb u d o w an ą h ierarch ię, na której szczycie była k lasa e l e m e n t p o m i a r owy . Oczywiście w p ro g ra m ie były także d ziesiątk i innych klas. W m iarę p racy n ad p ro g ram em okazało się, że przy d ało b y się, by w ybrane klasy p o zw ala ły się "podsłuchiw ać”, czyli udostępniały p e w n e g o typu infor macje. Tego ty p u zach o w an ie zdefiniow ałem w osobnej klasie, k tó rą nazw ałem dawca
inkrementorów.
N astęp n ie n azw ę tej klasy dopisałem do list pochodzenia tych w y b ran y ch klas, od których o czek iw ałem o d d aw an ia m i tej usługi. To nic, że n ie k tó re z tych klas były ju ż d zied zicam i klasy e l e m e n t _ p o n i a r o w y . D o p isan ie na ich liście p o ch o d ze n ia d o d a tk o w e j klasy d a w c a i n k r e m e n t o r ó w zam ien iło d ziedzi c z e n ie - na w ielo k ro tn e. W ten prosty sposób obiekty w y b ran y ch klas uzyskały nagle d o d a tk o w e zach o w an ia - z zu p ełn ie "innej bajki".
7)
Zwróć uwagę, nie "chłoporobotnik", tylko "chłop-robotnik". To jest w Polsce, chyba do lat 50-tych, nagminny błąd językowy. (Ale: w wyrazie "rzymskokatolicki" w środku pauzy się nie stawia, bo to nie "rano r z y m s k i, a p o p o łu d n iu k a to lic k i", tylko "ka to licki w e d łu g obrządku r z y m s k ie g o " . Ten błąd językowy możesz zobaczyć na co drugim budynku parafii).
930
20.8.2
Rozdz. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne)
R yzyko w ieloznaczności przy d zied ziczen iu Ten p a ra g ra f p rzy p ierw szym czytaniu tej książki p ro p o n u ję opuścić. Jest m a ta p ra w d o p o d o b n e , byś w pierw szej fazie nauki p o s łu g iw a ł się dziedziczeniem w ielo k ro tn y m , zatem te ro zw ażan ia na razie nie są Ci p o trzeb n e.
X P rzy w ielo k ro tn y m dziedziczeniu pojaw ia się p ro b lem w ielo zn aczn o ść. W ieloznaczność to sytuacja, g d y w y ra ż e n ie o d n o szące się d o jakiegoś sk ład n ik a klasy p o d staw o w ej, ró w n ie d o b rze m o że odnosić się d o in n eg o sk ład n ik a d ru g iej k la sy podstaw ow ej. W y o b raź sobie, że w naszym p o p rzed n im p rz y k ła d z ie w klasie sam ochód i w klasie łó d k a jest składnik o identycznej nazw ie. P rz y k ła d o w o - niech w k lasie sam o ch ó d będ zie d o d atk o w y składnik i n t x;
Klasa am fibia dziedziczy te oba identycznie n azy w ające się składniki. Czy jest to błąd? Jeszcze nie - nazw y tych sk ła d n ik ó w m ają przecież ró ż n y zakres w ażności: zakres klasy sam o ch ó d - w zg lę d n ie k lasy łódka. P am iętać przecież należy, że m im o iż d o sk ład n ik ó w odzied ziczo n y ch o d n o sić się m o ż n a tak, jakby były sk ład n ik am i klasy p o ch o d n ej - są one jednak sk ład n i kam i klas p o d staw o w y ch „za sz y ty m i" w obiekcie k lasy pochodnej. P roblem p o w stan ie jednak, g d y w obrębie funkcji sk ład o w ej klasy p o ch o d n ej a m f i b i a sp ró b u jem y od w o łać się d o n azw y x - k o m p ila to r uzn a to za błąd. Błąd p o le g a na tym , że w id ząc nazw ę x k o m p ilato r nie w ie, czy chodzi n am o sk ład n ik o d zied ziczo n y z k lasy sam ochód, czy z k la sy łódka. Obie m ożliw ości są jed n ak o w o popraw ne. Aby nie b y ło tej d w u zn aczn o ści, czyli aby o d n iesien ie się tak ieg o składnika b y ło m ożliw e, w ystarczyć p o p rzed z ić jego n azw ę o p e ra to re m zakresu z n a z w ą klasy, w której jest żąd a n y sk ład n ik . samochód::x
lub l o d k a ::x
Rozdział. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne)
931
Tak sam o m o żn a o d n ieść się d o innych sk ład n ik ó w - tych, k tó re pojaw iają się tylko jeden je d y n y ra z , np. s a m o c h ó d ::a
jed n ak nie jest to konieczne, alb o w iem w y rażen ie a jednoznacznie określa, o który sk ład n ik ch o d zi - jest p rzecież ty lk o jeden sk ład n ik o n azw ie a. Jak m yślisz, co by b y ło, g d y b y w klasie sam o ch ó d sk ład n ik o n azw ie x był p r i v a t e , n ato m iast w klasie łó d k a sk ła d n ik x był p u b l i c l u b p r o t e c t e d ? M ów iliśm y, ż e sk ła d n ik p r i v a t e jest dzied ziczo n y ja k b y w zalakow anej kopercie - z z a k re su klasy po ch o d n ej n ie m a do niego d o s tę p u . W obec tego w klasie p o ch o d n ej m ielibyśm y: * o d z ie d z ic z o n y jeden sk ład n ik o n azw ie x - n ie d o stę p n y (bo w kopercie), *
a d ru g i (ten z o d zied ziczo n y z k la sy łódka) d o stę p n y bez problem u.
C zy w takich okolicznościach nadal o d n ie sien ie się z klasy p o ch o d n ej a m f i b i a d o n azw y x b y ło b y b łęd n e —bo w ieloznaczne? Pom yślałeś p e w n ie, ż e to ju ż zu p ełn ie in n a sytuacja. A je d n a k nie. Byłby to m im o w sz y stk o b łąd . W ynika to z z a sa d y , że: I Najpierw spraw dzana jest jednoznaczność, a dopiero potem ewentualny
D latego k o m p ilato r, n ap o tk aw szy w k la sie am fibia na o d n ie sien ie się do nazw y x , z a p ro te stu je ju ż n a etapie s p ra w d z a n ia jednoznaczności.
Operator zakresu to nie jest dobre rozwiązanie P osłu żen ie się operato rem zakresu w celu uniknięcia w ieloznaczności m a tę w a d ę że e w e n tu a ln e dalsze klasy po ch o d zące od klasy a m f i b i a dziedziczą to ry zy k o w ieloznaczności. Czyli jeśli zdefiniujem y sobie p o ch o d zą cą od amfibii klasę am fib ia_ o d rzu to w a, to w ew n ątrz tej klasy odniesienie się d o nazw y x jest n a d a l błęd em . S p raw a jest tym g o rsza, że - definiując kolejną klasę p o ch o d n ą będącą jakby p ra w n u k ie m w tej hierarchii - m o żem y już nie p am iętać d o k ład n ie jakie k w alifik ato ry z ak resu należy postaw ić p rz e d (dw uznaczną) n azw ą. M oże nam się nie chcieć w głębiać w te historie rodzinne. Tak zresztą p o w in n o być: uży w ając k la sy nie m usim y znać całej jej genealogii. P o w in n a ona nam życie u łatw iać, a n ie u tru d n iać. U w a g a d la w tajem niczonych: Dodatkową wadą jest to, że jeśli taka wieloznaczna nazwa jest nazwą funkcji wirtualnej, to poprzedzenie jej operatorem zakresu anuluje caty mechanizm wywołania funkcji wirtualnej (polimorfizm).
Jest sposób na uniknięcie takich błędów wieloznaczności: W klasie p o ch o d n ej zdefiniow ać m ożna składnik o tej samej nazw ie. Zasłoni on oba sk ła d n ik i z klas podstaw ow ych.
932
Rozdz. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne) Tym pom ocniczym - zasłaniającym - sk ładnikiem n iech b ęd zie funkcja skła dow a k la sy pochodnej. W jej w n ętrzu odniesiem y się d o te g o sk ład n ik a z klasy po d staw o w ej, o który nam chodzi. To odniesienie się m o ż e być ju ż z użyciem op erato ra zak resu int
{
a m f i b i a : : x ()
return samochód: :x;
1 R ozw iązanie to jest o tyle dobre, że teraz - naw et jeśli klasa am f i b i a m a dalsze klasy p o ch o d n e - to do nich w ieloznaczność już się nie p rzen iesie. N astępne pokolenia nie m uszą więc już ciągle pam iętać o ry z y k u w ieloznaczności i nie m uszą z n a ć sposobu jego uniknięcia. To znaczy programiści piszący jedną z tych klas pochodnych nie muszA wiedzieć jaką nazwę klasy umieścić przed operatorem zakresu. W ieloznaczność została bow iem u su n ięta w z aro d k u .
20.8.3
Czy b liższe pokrew ieństw o usuwa w ieloznaczność? Także i ten p arag raf proponuję o p u ścić p rz y p ie rw szy m czy tan iu tej książki.
W iem y, że klasa m oże mieć d o w o ln ą liczbę b ezp o śred n ich klas p o d staw o w y ch , konieczne są zatem pew ne z a s a d y określenia w ieloznaczności. Załóżm y, że m am y taką hierarch ię klas: Rys. 20-9
dziadek {xxx}
matka
ojciec
(xxx)
dziecko
W idzim y, ż e skoro i m atka i d z ia d e k m ają sk ład n ik i o n a z w ie x x x , to k lasa d z i e c k o d zied zic zy d w u k ro tn ie tę n a z w ę xxx. (W szystko jedno czy to n a z w a funkcji, czy danej). Czy jest to d o zw o lo n e? Czy n ie n astęp u je w ieloznaczność? Do tej p o ry n a p ew n o nie, bo w iem w ieloznaczność m o że w y stąp ić d o p ie ro p r z y próbie odniesienia się d o n azw y x x x . Zatem: czy p o p ra w n e jest w o b ręb ie k lasy d z i e c k o o d w o ła n ie się do n a z w y xxx r
Jest w ieloznaczność! Co p ra w d a , d ro g a do n a z w y x x x w k lasie p o d staw o w ej m a t k a jest w y raźn ie k ró tsza, niż d ro g a d o
933
Rozdział. 20. Dziedziczenie Dziedziczenie od kilku „rodziców” (czyli wielokrotne)
id en ty czn ej n a z w y w klasie d z i a d e k , ale to „b liższe p o k re w ie ń stw o " n ie m a znaczenia. O bie n a z w y p o ch o d zą z ró żn y ch gałęzi d rz e w a genealogicznego, w ięc się nie zasłaniają. Są ró w n o cześn ie w id z ia ln e w klasie d z i e c k o , co jest p o w o d e m w ieloznaczności. (U w aga: w d a w n y c h w ersjach ję zy k a C++ była tu różnica).
20.8.4
Poszlaki Także i ten p a ra g ra f p ro p o n u ję o p u śc ić p rzy p ie rw szy m czy tan iu tej książki.
Jeśli m im o w sz y stk o m u sim y p o słu ż y ć się kw alifikatorem zak resu , by o d n ieść się d o n azw y , to na szczęście nie m u sim y p o d aw ać d o k ła d n eg o określenia, w której klasie d a n a n azw a się zn ajd u je. W ystarczy, jeśli p rz y o p erato rze b ęd zie n azw a klasy po d staw o w ej, o d k tó rej k o m pilator m a zacząć poszu k iw an ia. W naszy m o statn im p rzy k ład zie, ab y odnieść się d o tego sk ład n ik a o n azw ie x, k tó ry zo stał o d zied ziczo n y p o d z ia d k u , m ożna też n ap isać o j c i e c : :x
Z obaczym y bard ziej sk o m p lik o w an ą hierarchię Rys. 20-10
A(n} V
B s
C ______
E{n} t D ł ___ -------- I H
__ F
G {n} t
Jeśli ch o d zi n a m o odniesienie się d o składnika n z k lasy A, to m ożem y posłużyć się zapisem A: : n ale także zap isem C: : n To dlateg o , że jeśli poszukiw ania polecim y k o m pilatorow i p ro w ad zić w gałęzi C, to jedno zn aczn ie znajdzie się sk ła d n ik n. (O czyw iście te n A : : n). P o d o b n ie z po zo stały m i gałęziam i D : : n to to sam o co E : : n F : : n to to sam o co G: : n K om pilator zad o w ala się po p ro stu inform acją, g d zie zacząć szukać.
934
Rozdz. 20. Dziedziczenie Pojedynek: Dziedziczenie klasy, contra zawieranie obiektów składowych
2 0 .9
P o je d y n e k : D z ie d zic ze n ie k lasy, c o n tra z a w ie ra n ie o b ie k tó w s kła d o w y ch M ów iliśm y już kiedyś, że k o n stru u jąc klasę m o żn a jej sk ład n ik am i uczynić obiekty innych klas. Klasa rad io zaw iera w sobie o b ie k ty k lasy tran zy sto r, klasa pokój z aw ie ra obiekty krzesło, obiekt stół. Teraz p o z n a liśm y inny sposób użycia klas ju ż w cześniej zdefiniow anych: dziedziczenie. Z astan ó w m y się jaka jest m ięd zy nim i różnica.
K iedy z a s t o s o w a ć którą te c h n ik ę ? C zy m o ż n a te m eto d y sto so w ać w ym iennie?
A by o d p o w ied zieć na te p y tan ia, w y starczy b ard zo p ro s ta regułka: Jeśli m ówiąc o klasach obiektów używam y zwrotu „ A s k ła d a s ię z B '\ w ów czas mamy do czynienia z zawieraniem obiektów składowych. littw m iii—w u Ttwrmn>'MWtrmrHiimnfiirnwiHHi»nniWiii"itln^
4 ....... .. 11 1 1 1 "1 ' 1 1 " 11"1...... n" 1 1 ir" 1inni " """ 1 | Je śli u żyw a m y zw rotu „ A j e s t r o d z a je m B “ , w ó w c z a s sto su je się dziedziczenie klas Sam zobacz: Jeśli m ó w im y "pokój sk ład a się z 4 k rzeseł i szafy" - to je st teraz oczyw iste, że następuje tu zaw ieranie obiektów sk ład o w y ch in n y ch klas. W klasie pokój sk ład n ik iem jest obiekt klasy szafa i cztery obiekty k la sy krzesła. Jeśli m ó w im y : "sam ochód je st ro d z a je m pojazdu" (albo "am fibia je st ro d zajem sam o ch o d u ") to jest od razu jasne, że p o w in n o się zasto so w ać technikę d zied ziczen ia. Sposoby te nie są oczyw iście w y m ien n e. Z au w aż, że o ile na am fibię m o żn a pow ied zieć sam ochód (dziedziczenie), o tyle nie m o żn a n a krzesło p o w ied zieć pokój, an i n a pokój - krzesło.
I
W p rzy p ad k u d zied zic zen ia jest, jak w id a ć , jakaś szczególna relacja. Jedna klasa jest szczególnym ro d z a je m drugiej.
Z tej o statniej w łaściw ości b ęd ziem y w przyszłości w ielo k ro tn ie korzystali. O d n o s z e n ie się d o składników w o b u sy tu a c ja c h
P o ró w n am y teraz sposoby o d n o szen ia się d o sk ła d n ik ó w w p rzy p ad k u o d z ie dziczenia ich, lu b zaw ierania ich jako części obiektów sk ład o w y ch . Jest to ty lk o p o ró w n an ie na zasad z ie stw ierd ze n ia faktów - nie jest to żad en a rg u m e n t na tem at w y b o ru tej, czy innej, techniki. W naszych najbliższych ro zw ażan iach zak ład am , ż e d a n y sk ład n ik jest d o stęp n y w klasie - to zn aczy nie strzeże go w tej d ru g ie j klasie słó w k o private.
A zatem :
935
Rozdział. 20. Dziedziczenie Pojedynek: Dziedziczenie klasy, contra zawieranie obiektów składowych
♦♦♦ P rzy d zied ziczen iu o d n o sz e n ie się do o d z ie d z ic z o n e g o sk ład n ik a jest tak sa m o proste, jak b y b y ł on sk ład n ik iem klasy pochodnej.
Przykładowo: jeśli w amfibii chcemy coś powiedzieć o kierownicy - to mówimy po prostu: kierownica. kierownica
♦♦♦ N a to m ia st w p rz y p a d k u zaw ieran ia obiektu innej k lasy - obiekt ten jest s k ła d n ik e m klasy. Jeśli chcem y odnieść się d o jak ieg o ś sk ład n ik a tego o b ie k tu (czyli jakby: "sk ład n ik a tego sk ładnika"), to m u sim y p rz e d jego n a z w ą pow iedzieć, d o jakiego obiektu s k ła d o w e g o dany sk ład n ik n ależy .
Przykładowo: Klasa "pokój" zawiera składnik "szafa". Jeśli chcemy coś powiedzieć o "drzwiach od szafy", to musimy ury raźnie dopowiedzieć te słowa „od szafy". Składnik szafa ma swój składnik o nazwie "drzioi". s z a fa .d r z w i
Podobnie, jeśli w tym pokoju chcemy coś powiedzieć o oparciu od trzeciego krzesła, to również samo słowo „oparcie" nie wy star cza. Trzeba dodać, że od obiektu trzecie krzesło k rze s lo [2 ]. o p a r c ie
O to oba p r z y k ła d y w sk ró co n y m zapisie. N ajpierw o d n o s z e n i e się d o sk ła d n ik a class
lodź
odziedziczonego
{ };
/ / / / / 1/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ! / / / f / / / / / / / / / / / ^ i ^ i ^ class
samochód
i protected: in t k i e r o w n i c a ;
//////////////////////////////////////////////////////z '//////// class
amfibia
: public
lodź,
public
samochód
i // . . . void fun() cout
<<
kierownica;
/ / + — tu ta j s k ła d n ik je s t o d z ie d z ic z o n y , w ię c ^ U s ta ł się częścią a m f ib ii (s k ła d n ik ie m a m fib ii)
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / h ' ! / / / / / / I I i I I / / / / / / / / 1/ / T e r a z o d n o s z e n i e s ię d o sk ła d n ik a obiektu class
1111/ 1
zawartego w danej klasie
szafa
i public: int d r z w i ;
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / 111f 111/ 111
936
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu c l a s s pokoj < szafa szafa_brazowa; void p i s z ()
c o u t << szafa brązową.drzwi; //< — tutaj! // drzwi sq składnikiem (stojącego w pokoju) składnika o nazwie szafa_brazowa
} };
2 0 .1 0 K o n w e rs je s ta n d a rd o w e p rzy d z ie d z ic z e n iu M ów iliśm y ju ż kilkakrotnie, że jeśli jedna klasa p o c h o d z i o d drugiej, to obiekt klasy p o ch o d n ej ma w sobie coś, co sp raw ia, iż m o żn a g o u z n a ć także za obiekt klasy po d staw o w ej. Przykładowo - jeśli patrzymy na najróżniejsze modele samochodów, to w każdym z nich jest coś, co sprawia, że można o nich powiedzieć - to jest pojazd! Po to, ab y m o żn a było się także i w C++ p o słu ży ć ta k im i zależnościam i — p red efin io w an e są następujące konw ersje sta n d a rd o w e :
W skaźnik do obiektu klasy pochodnej - może być n iejaw nie przekształcony na w skaźnik dostępnej jednoznacznie klasy podstaw ow ej.
P o d o b n ie z referencjam i (czyli przezw iskam i): ■n_u.il ■wmuiMinrwwnwrnTr i irr----------------------
l-wartość (czyli obiekt lub referencja) typu klasy pochodnej - może być przypisana referencji typu jednoznacznie dostępnej klasy podstawowej.
C o to zn aczy , ż e klasa p o d staw o w a jest dostępna? T o z n a c z y , że nie strzeże tej klasy d z ie d z ic z e n ie pryw atne. Tylko bez paniki. Za chwilę w przykładzie odwiedzimy "Deutsche Oper" i zobaczysz w tym logikę. W s z y s t k o to b r z m i t r o c h ę s k o m p l i k o w a n i e , a l e s a m z o b a c z , ż e m o ż n a tc w yrazić prostym i po jęciam i
^
W y o b raźm y sobie taką scenke. P rzed nam i leżą d w a w sk a ź n ik i. N a jed n y m jest napisane: „ d o p o k azy w an ia na sam o ch o d y ", a na d ru g im : „ d o p o k azy w an ia na pojazd y ". Pytanie: ~
■ Jeśli na jakiś obiekt koło nas p o k azałem w sk aźn ik iem „ty lk o d o p o k azy w an ia na sam o ch o d y " - i było to p o p ra w n e - to czy m o ż esz na ten sam obiekt p o k azać „w sk aźn ik iem d o p o k azy w an ia na p o ja zd y "?
937
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
O d p o w ied zia łeś zap e w n e, ż e "m ożesz" - bo sam o ch ó d to także pojazd. Jeśli tak o d p o w ied ziałe ś, to w te n sposób odkryłeś w ła ś n ie treść naszej p ie rw szej reg u ły m ów iącej, że: w sk a ź n ik d o obiektu klasy p o ch o d n ej (sam ochód) m oże być n iejaw n ie p rzek ształco n y na w sk aźn ik d o stęp n ej jednoznacznie k la sy p o d staw o w ej (pojazd). Zauważ, że odwrotna zasada nie byłaby prawdziwa. Na wóz drabiniasty możemy pokazać „wskaźnikiem do pojazdu", ale nie możemy pokazać na niego „wskaźnikiem do samochodu" Z reg u łą d ru g ą , d o ty czącą referencji, jest podobnie. Jeśli na jakiś obiekt k lasy Fiat 126, m ó w ię p rzezw isk iem „m alu ch ", to m ogę na ten obiekt ró w n ież po w ied zieć p rzezw isk iem „ w ó z " - k tóre jest p rzez w isk iem pojazdu. Ale n ie odiurotn ie: jeśli ktoś powiedział o swoim samochodzie przezwiskiem „wóz", to wcale nie oznacza, że ma coś o przezwisku „maluch". Z asad y , o k tó ry ch m ó w im y są tak sform ułow ane, b y o d p o w iad ały n aszy m d o św iad cze n io m ze św iata realnego. D latego są tak p ro ste.
W P om yślałeś p ew n ie, że w szy stk o to są rozw ażania czy sto akadem ickie. N ie jest tak. To, o czy m m ó w im y teraz, m a ogrom ne zn aczen ie w praktyce. O znacza to m ianow icie, że jeśli m asz funkcję, k tóra przyjm uje referencję obiektu k lasy podstawowej, to m ożesz ją w yw ołać ta k ż e dla obiektu (lub referencji) k lasy pochodnej. O to tak a sy tu acja: class saraochod
i( O
public: int zbiornik;
/ / ..ew . coś jeszcze }; /////////////////////////////////////////////////////////////zy class VW : public samochód
// “
{ II ... }; Z*************************************************************/ // zwykła funkcja globalna Z* * * * * * *************** * * ** * * * * * *******************************/ void stacja_benzynowa(samochód & klient)
// C/
{ klient.zbiornik = 50;
} z************************************************************ // int main() ( samochód prawdziwy samochód; staćja_benzynowa (prawdziwy_samochod) ; VW golf_huberta; staćja_benzynowa(golf_huberta) ;
o
II ©
938
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu //
Komentarz M am y, jak w idać, dw ie klasy. «♦ O Klasa samochód m a składnik zbiorni k, k tó ry sym bolizuje zb iornik p aliw a. Oczywiście m o g ą być w klasie jeszcze jakieś in n e składniki, to» je d n ak jest dla nas te ra z nieistotne. ❖
© Klasa VW(jak: V olksw agen) jest klasą p o c h o d n ą o d klasy sam ochód — a w ięc dziedziczy zbi o r n i k paliwa.
© W idzim y też funkcję staćj a_benzynowa. Jest to zw y k ła funkcja globalna,której arg u m e n tem form alnym jest referencja o b ie k tu klasy sam ochód. © W m a i n w id zim y defin iq ę ob iek tu klasy s a m o c h ó d - o b iek t ten n azy w am y prawdziwy_samochod.
© Funkcja s t a c j a _ b e n z y n o w a zostaje w yw ołana d la tego arg u m e n tu . O dbiera go p rz e z referencję i d o k o n u je tankow ania. S p o d z ie w a ła się a rg u m e n tu s a m o c h ó d i go dostała. Jak d o tą d - nie ma tu nic n ad zw y czajn eg o . © A teraz u w ag a: m am y klasę VW, która jest p o ch o d n ą k la sy samochód. D efiniu jem y o b iek t tej klasy. N azy w am y go golf huberta.
O Dzięki tej zasadzie, o której m ów im y w tym p a ra g ra fie - funkcja stac j a _ b e n z y n o w a m oże ró w n ież przyjąć referencję d o k lasy VW. N astąp i w ted y niejaw na konw ersja sta n d a rd o w a tej treści: referencja d o ob iek tu klasy p o ch o d nej V W zo stan ie zam ieniona na referencję d o obiektu k lasy samochód. C zyli tak, jakbyśm y napisali staćja_benzynowa( (samochód &) golf_huberta);
W życiu co d zien n y m taka ko n w ersja jest b ard zo częsta. M im o że H u b e rt m a V olksw agena m oże go z a p a rk o w ać na parkingu samochodowym - a nie jed y n ie na p ark in g u „d la VW " P rogram ow anie o b iek to w o o rie n to w a n e w języku C++ d ą ż y d o tego, by m ożliw ie blisko o d w zo ro w ać zac h o w an ia i sytuacje ze św iata realnego. T o bow iem u łatw ia p ro g ra m o w a n ie . D latego też C++ został w y p o sa żo n y w te konw ersje. T o s a m o przy konw ersji w s k a ź n ik a
P okazaliśm y jak odbyw a się k o n w ersja referencji - te ra z pok ażem y , jak o d b y w a się ko n w ersja "w skaźnika d o obiektów klasy p o c h o d n ej" na "w skaźnik d o obiektów k la sy podstaw ow ej". M am y inną funkcję globalną void spalanie(samochód *wskaz_sam)
{ wskaz_sam->zbiornik -= 3;
)
/ / s p a l e n i e 3 litr ó w
939
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
A rg u m en te m fo rm aln y m tej funkcji jest w sk a ź n ik do obiektu k lasy samochód. Z n aczy to, że d o funkcji tej m ożna p rzy słać a rg u m e n t b ęd ący a d re se m obiektu k lasy samochód. O d teraz - na m ocy z a s a d y , którą p o zn aliśm y w tym p a ra g ra fie - m ożna tej funkcji w ysiać n a w e t a d re s obiektu klasy pochodnej od klasy samochód. Klasa VW jest w łaśn ie ta k ą k la są p o ch o d n ą, z a te m p o p raw n e są oba poniższe w y w o łan ia spalanie(&prawdziwy_samochod) ; // <— w y s ł a n i e spalanie(&golf_huberta);
a d r e s u o b j. k l. p o c h o d n e j!
D l a c z e g o tylko p rzy d z ie d z ic z e n iu p u b lic z n y m ?
W y p ad a te ra z w ytłum aczyć się z faktu d laczeg o taka konw ersja m oże n a stą p ić tylko w tedy, g d y klasa p o d staw o w a jest "dostępna".
I
P rzy p o m in am , klasa p o d staw o w a jest d o stę p n a jest w te d y , g d y została o d z ie d z ic z o n a p u b liczn ie . W p rz y p a d k u d zied zic zen ia p u blicznego p ierw sza linijka definicji klasy VW w y g lą d a tak class VW : public samochód f
//
...
} G d y b y na tej liście p o ch o d ze n ia było słow o private, (albo nic nie było, c o p rz e z d o m n iem an ie - u z n a n e jest za d zied ziczen ie private), w ó w czas wspo m n ia n a konw ersja n ie m o ż e zajść au to m aty czn ie. C z y intuicyjnie „ cz u jesz" dlaczego? Jeśli nie, to przy w o łajm y so b ie taki obrazek, g d z ie d zied ziczen ie je st private. O tó ż w D eutsche O p e r, na p rzed staw ien iu W olnego Strzelca (K. M . W ebera), jest ak t ro zg ry w ający się nocą w mrocznej p u szczy . N a olbrzym iej scenie widzi m y w ielką ro m an ty czn ą dekorację: ogro m n e zw alo n e d rzew a, g óry, strum ienie - w sz y stk o w niesam o w itej atm osferze czarów . Słowem: p rz e d sio n e k piekła. Scena ta m u si być u sta w io n a w m iarę sp ra w n ie , więc w ięk szo ść elem entów dekoracji m a kółka i w czasie p rzerw y w jeżd ża na scenę. Jest tam też ogrom na g ó ra - w ielka i ciężka. O na też ma uk ry te kółka, a d o d atk o w o silnik i układ kiero w n iczy . O p e ra to r sceny w o d p o w ied n im m om encie siad a za kierow nicą i sp ra w n ie w jeżdża ty m fragm entem dekoracji na scenę. C z y silnik jest elek try czn y czy spalinow y - nie w iem - dla n aszeg o przykładu załó żm y , że jest sp alin o w y . Skoro ta góra m a koła, silnik i k iero w n icę, to znaczy jest rodzajem pojazdu samochodowego. „Jest ro d z a je m "-c z y li: d zied ziczy go.
8)
Jest wtedy d o s t ę p n a dla szerokiej publiczności. Jeśli jednak te sztuczki będziemy chceli robić w środowisku przyjaciół, to wówczas klasa podstawowa może sobie być nawet odziedziczona nie-publicznie, a i tak będzie dla przyjaciół dostępna.
940
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu Z d ru g ie j jednak strony, nikt z w id zó w nie p o w in ien teg o zauw ażyć. Dlatego w sz y stk o jest u kryte - czyli d ziedziczenie jest p ry w a tn e . Jeśli n aw et któryś z w id z ó w to przeczuw a - nie pow inien potrafić w sk a zać n a te sk ład n ik i ukrytego sam o ch o d u . T akie je st dziedziczenie p ry w atn e. C iągnijm y dalej tę analogię. P ew n eg o razu w y je ż d ż a m y tą górą z teatru i zajeżd żam y d o pobliskiej stacji benzynow ej - po to, by nabrać paliw a do zbiornika. K ierow nik stacji w id z i, że p rz e d nim stoi góra. C z y obsłuży taką górę? Nie! K aże nam się pu k n ąć palcem w czoło i z a d z w o n i p o poliq'ę (sygnalizacja b łędu w czasie kompilacji). G óra nie zostanie w ięc p o trak to w an a p rz e z funkcję s t a c j a b e n zynow a jako potom ek sam o ch o d u , b o ten „sam o c h ó d " jest w tej górze sprytnie u k ry ty . W ięc co? Nic się nie da zrobić? N ie m ożna w y tłu m a c z y ć k ierow nikow i stać i tajem nicy tej góry? M ożna, ale to ju ż b ęd zie jawna k o n w ersja, p o d czas g d y my tutaj m ó w im y o niejaw nych, czyli m ogących zajść au to m aty czn ie. H u b e rt m ógł sw oim G olfem zajechać d o stacji benzynow ej i bez tłu m aczen ia czegokolw iek zostać obsłużonym . Góra z u k ry ty m ( p r i v a t e ) sam o ch o d e m w śro d k u - tego nie m oże. P rzełó żm y to na język C++. O to klasa: class operowa_gora : private samochód {
//
...
); Jak w id ać, dziedziczenie jest p ry w atn e. Jeśli te ra z w p ro g ram ie w y stąp i instrukcja operowagora
gora_akt 2 ;
II . . .
sta cja benzynowa (góra akt 2 );
// ///
To k o m p ilato r u zn a to za błąd. K onw ersja niejaw na nie m o że tutaj zajść, jak o że dzied ziczen ie było p ry w atne. Jeśli jed n ak zasto su jem y jaw n ą konw ersję (czyli rzu to w an ie): staćja_benzynowa( (samochód &) gora_akt2);
To k o m p ilato r już nie zap ro testu je. U zna, że sk o ro p iszem y to rzu to w a n ie jaw nie, to p ew nie w iem y, co robim y.
20.10.1
Panorama korzyści K onw ersje standardow e, o k tórych m ów iliśm y o statn io , pozw alają nam k o rz y s tać z fak tu , iż klasa p o ch o d n a jest w logicznej relacji w sto su n k u d o klasy pod staw o w ej. To jest jeden z najw ażniejszych a sp ek tó w dziedziczenia. D z ie dziczy się nie tylko z lenistw a (by sobie oszczędzić p ra c y p rz y nowej klasie) —
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
941
d zied zic zy się głó w n ie po to, by o p ero w ać klasam i u staw io n y m i w p ew ien u k ła d zależności, czyli hierarchię. D o tej p o ry , poza sy tu acją d efin io w an ia k lasy po ch o d n ej - n ie m o żn a było w naszych p ro g ram a ch z a u w aży ć korzyści z takiej h ierarchii. T era z, kiedy m a m y do dyspozycji w sp o m n ia n e konw ersje s ta n d a rd o w e - m o ż em y w reszcie w p ełn i korzystać z d o b ro d ziejstw a, jakie n am to u staw ien ie klas w hierarchie daje. K iedy z a c h o d z ą takie s y tu a c je ?
K onw ersje sta n d a rd o w e w sk aźn ik a (w zg lęd n ie referencji) d o obiektu klasy p o cho d n ej na w sk a ź n ik (referencję) ob iek tu publicznej klasy p o dstaw ow ej m ogą zajść w sytuacjach ty p o w y ch dla in n y ch konw ersji sta n d a rd o w y c h , czyli: •
a) p rz y p rzy słan iu a rg u m e n tó w d o funkcji (k tó re odbierają a r g u m e n t jako referencję lu b w skaźnik),
•
b) p rzy zw racan iu p rz e z funkcję rezu ltatu b ęd ąc eg o referencją lu b w sk aźn ik iem ,
•
c) p rz y p rzeład o w an y ch o p erato rach ,
•
d ) w w y rażen iach inicjalizujących.
W y ja ś n i jm y p o kolei t e p r z y p a d k i
a d a) - To ju ż z n a m y z n ied aw n eg o p rz y k ła d u ze stacją b en zy n o w ą: funkcję, k tó ra o d b ie ra a r g u m e n t jako referen c ję ob iek tu klasy p o d staw o w ej, m ożna było w y w o łać z a rg u m e n te m będącym 1-wartością (obiektem lu b referencją do obiektu) k lasy p o ch o d n ej. P odobnie ze w skaźn ik am i. a d b) To sam o z ac h o d zi w p rz y p a d k u re z u lta tu zw racan eg o p rz e z funkcję. W funkcji, która z w ra c a referencję (w zgl. w sk aźn ik ) d o ob iek tu jakiejś klasy podstaioowej, m o żn a koło słow a r e t u r n po staw ić 1-wartość (w zgl. w skaźnik) obiek tu klasy pochodnej. a d c) Jeśli m am y o p e ra to r, k tó reg o a rg u m e n te m jest referen c ja k la sy p o d sta w o w e j —czyli in n y m i słow y jest to o p erato r, k tóry sp o d ziew a się, że p o którejś z jego stro n stać b ęd zie o b iek t (lub jego referencja) jakiejś klasy po d staw o w ej - to m o żem y tam ró w n ie d o b rze postaw ić 1-wartość (obiekt lu b referencję) klasy p o ch o d n ej od niej. O p erato r potraktuje ten o b iek t tak, jakby b y ł o n obiektem klasy p o d staw o w ej. N ic w tym specjalnie now ego. P rzecież n aszą funkcję s t a ć j a b e n z y n o w a m ogliśm y zrealizo w ać w postaci o p erato ra. A le u w ag a: ko n w ersja w y stąp i tylko w sto su n k u d o tych arg u m e n tó w , które są w y sy łan e d o funkcji operatorow ej. To scf te argumenty, które (gdyby to było w zapisie jaumym) byłyby w nawiasie, jako argumenty jawnego wywołania funkcji operatorowej. o b ie k t, o p e r a t o r@ (a rg ); o p e r a t o r @ (e rg 1 , a rg 2 );
942
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu C o to zn aczy "tylko tych argumentów, które są w nawiasie'”? M o że pam iętasz z p o p rzed n ieg o ro zd ziału , że jeśli funkcja o p erato ro w a jest funkcją sk ad owa jakiejś klasy, to: w spom niana konw ersja s ta n d a rd o w a n ie n astąp i w p rz y p a d k u pierw szego u k ry teg o a rg u m e n tu ( * t h i s ) - czyli o b iek tu , na rzecz którego w yw ołuje się o p erato r. Jeśli w ięc n am na tym zależy - to o p erato r trzeb a z d e fin io w a ć jako funkcję n ie sk ład o w ą (globalną). W spom inaliśm y ju ż o ty m p r z y o m a w ian iu p rzeład o w a n ia o p erato ró w (str. 877). a d d ) P rzy w y rażen iach in icjalizu jący ch - m o ż e n astąp ić ta k a sytuacja, że ch cem y zdefiniow ać obiekt klasy p o j a z d i ró w n o cześn ie go zainicjalizow ać. R o zg ląd am y się po p ro g ram ie i znajd u jem y , ż e najlepiej jako w zo rzec do inicjalizacji nadaje się p ew ien konkretny o b iek t k la sy samochód. Jak o obiekt k la sy po ch o d n ej ma on, co p raw d a, więcej s k ła d n ik ó w -d a n y c h , ale te ra z są one d la n a s nieistotne. Interesuje nas jego trzon - czyli to, co jest w nim z p o jazd u . To ty m w łaśn ie zain iq alizo w ać chcem y obiekt klasy pojazd. K onw ersja stan d ar d o w a sp raw ia, że jest to m ożliw e.
samochód s; pojazd p = s;
//
zajdzie niejawna konwersja
P rz y p o m in a m , po raz m ilionow y, że znak = w linijce inicjalizacji jest jedynym p rz y p a d k ie m , gdy d o p racy ru sza nie o p e r a t o r = p rz y p isa n ia , tylko k o n stru k to r k o p iujący klasy p o j a z d . pojazd::pojazd(pojazd &);
W n aw iasie w idzisz, że a rg u m e n t jest referencją o b iek tu klasy podstaw ow ej p o j a z d . M y natom iast z praw ej stro n y z n a k u = p o staw iliśm y o b iek t klasy p o ch o d n ej s a m o c h ó d . P e w n ie zaw ołałeś teraz: „ -P rz e c ie ż to już o m ó w iliśm y . -T o ż to p rz y p a d e k a ) ! ' B raw o, m asz rację. O czyw iście o d w ro tn a inicjalizacja nie jest m o żliw a. T o znaczy, nie m o ż n a zb u d o w a ć sam o ch o d u , m ając za w zo rzec obiekt k la sy p o jazd
pojazd p; samochód s = p;
I I b łą d !!!
D ygresja:
Gdyby nam bardzo zależało, by ta ostatnia instrukcja była mimo wszystko możliwa, to powinniśmy sobie zdefiniować operator konwersji z typu p o j a z d na typ sam ochód. Mówiliśmy już o tych sprawach (str. 763) i jak wiesz, nie jest to trudne. Z tym, że nie są ju ż to konwersje standardowe, a konwersje definiowane przez użytkownka. Tu mówimy tylko o standar dowych.
943
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
W O g ó ln y w n io s e k je s t ta k i: d z ię k i is tn ie n iu ty c h k o n w e rs ji s ta n d a rd o w y c h (ty c z ą c y c h k la s p o c h o d n y c h ) p e w n e fra g m e n ty p ro g ra m u m o g ą b y ć b a rd z ie j u n iw e rs a ln e . M o ż e m y u ż y w a ć ta k ic h fra g m e n tó w k o d u , k tó re b y ły p rz e z n a c z o n e d la k o g o in n e g o . O w o w ie lo k ro tn e u ż y w a n ie - to je d e n z a s p e k tó w reu sab ility
-
M U C «M nM W M l
■-
— *-.am— n■!■i~it
Uwaga dla programistów C M am nadzieję, że zau w a ż y łe ś już d o n io sło ść tego faktu. W k lasy czn y m języku C funkcja m ogła być w y w o łan a z a rg u m e n tem b ęd ący m w skaźnikiem d o jakiegoś o b iek tu .
struct plansza; void
narysuj(plansza
*wskaz);
Jeśli n aw et z a chw ilę zb u d o w aliśm y b a rd z o p o d obną s tru k tu rę opisującą np. m enu (struct menu) - to funkcji tej nie d ało się już u ży ć d o tego celu. Trzeba było p o ra z d ru g i n ap isać p o d o b n ą funkcję:
void narysuj_menu(menu *wskaz); N ato m iast w C++, jeśli definiując s tru k tu rę menu p o w iem y , że jest ona p o ch o d n ą o d s tru k tu ry pi ans za, to funkcja narysuj m oże praco w ać także dla ob iek tó w s tru k tu ry menu. O szczęd zam y pracy, o szczęd zam y k o d u - program robi się m n iejszy !
20.10.2
Czego robić się nie opłaca Z achw ycając się h ierarch ią zap ew n iałem , że dziedziczenie p o zw ala nam trak tow ać o b ie k ty klas p o chodnych jako szczególny rodzaj o b iek tó w klasy podsta w ow ej. In n y m i sło w y , sam ochód to szczególny rodzaj p o jazd u . Z a u w a ż je d n ak , że m ów iąc o konw ersjach stan d ard o w y ch zaw sze podkreśla łem , że k o n w ersje te m ogą zajść w ted y , g d y na przykład fu n k cja o d b ie ra arg u m en t b ęd ący referencją obiektu klasy podsta w o w ej *t* funkcja o d b ie ra a rg u m e n t b ędący w skaźnikiem d o ob iek tu klasy pod staw o w ej
Pytanie: C o b y było, g d y b y śm y mieli d o czynienia z funkcją odbierającą obiekt klasy p o d staw o w ej? (N ie referencję, nie w skaźnik, ale obiekt). C o się stanic, jeśli w y ślem y d o takiej funkcji obiekt klasy pochodnej? O to, co się stanie: k o m pilator w eźm ie ostry nóż i obetnie ten obiekt klasy pochodnej tak, że „w y p atro szy " z niego obiekt klasy podstaw ow ej. Na użytek tej funkcji p o w sta n ie w ięc obiekt klasy podstaw ow ej, który na zaw sze zapomni o tym , że k ied y k o lw iek był klasy pochodnej. H orrendum !
20
944
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu
O, Sancta Simplicita! Z aw o łałeś teraz zapew ne: N o i czem u ro b isz z tego trag ed ię? P rzecież taka funkcja i tak potrafiłaby ty lk o korzystać z tej w ła śn ie „p o d staw o w ej" części obiektu, w ięc nie ma znaczenia, że ta „ p o c h o d n a " część obiektu na zaw sze o d eszła w przeszłość. N ie rozpaczaj, kolego au to rze, nie ro b iłeś p rzecież d ram atu , gdy w skaźnik k lasy pochodnej zam ien iał się na w skaźnik k lasy p o d staw ow ej. A p rzecież takim „ p o d s ta w o w y m " w sk aźn ik iem też nie m ożna się p o słu ży ć w celu w y d o b y cia „ p o ch o d n y c h " składników obiektu. Ta „p o ch o d n a " inform acja - w funkcji odbierającej nie obiekt, ale w sk a źn ik - też p rzecież b y ła z a p o m n ia n a i niedostępna! Jeśli tak w łaśnie arg u m en to w ałeś, to jestem z C iebie d u m n y . Rzeczyw iście - z tego, co d o tej pory w książce tej p ow iedzieliśm y - ta k by logicznie w ynikało... A jed n ak , nie m asz raqi... W n a stę p n y m rozdziale d o w iesz się, że (dzięki w sp a n iało ści p ro g ram o w an ia obiek to w o orientow anego) są m echanizm y, k tóre - m im o konwersji w skaźni ka na "w skaźnik p o d staw o w y " - i tak m ogłyby p o zw o lić nam p racow ać na sk ład n ik ach „p o ch o d n y ch ". P odobnie w p rz y p a d k u konwersji referencji. Jeśli jed n ak nastąpiło obcięcie obiektu - to u m a rł w butach!
Dla wtajemniczonych, w formie przypomnienia: C h o d zi o to, ż e iv p rzy p a d k u , g d y w fu n k c ji m a m y w sk a źn ik d o k la sy p o d sta w o w e j lu b referencję k la sy p o d sta w o w e j, m o że m y za ich pom ocą w y w o ła ć fu n k c ję w irtu a ln ą z w ła śc iw e j k la s y poch odn ej. C z y li m o że m y s k o rzy sta ć z e w s p a n ia ło śc i p o lim o rfizm u ! N a to m ia st, g d y m a m y obcięty obiekt - p r z e p a d ło . N a rzecz obiektu p o lim o rfizm n ie d zia ła .
-
Jeśli jednak czytasz tę książkę po raz pierwszy... ...to p o lim o rfizm u jeszcze nie zn asz. Jakby C ię zatem p rz e k o n a ć , byś p isał funkcje odbierające n ie obiekt, ale referencję to niego? N ajlepiej tak: O tóż jeśli p rzesy ła sz do funkcji o b ie k t p rz e z w artość, to k o pio w an y jest ta m (czasem b a rd z o d uży) obiekt - a to trw a . Jeśli w d o d atk u funkcja ta jest często w y w o ły w an a - m oże to w y raźn ie sp o w o ln ić w y k o n y w a n ie p ro g ram u . Jeśli zaś p rzesy łasz a rg u m e n t p rzez referencję - to tro ch ę tak, jakbyś p rz e sy ła ł sam ad res. A dres zajm uje ty lk o kilka bajtów , w ięc p rz e sła n ie takiego a rg u m e n tu jest o w iele szybsze. P ro g ram w ięc d ziała szybciej!
945
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
20.10.3
Tuzin samochodów nie jest rodzajem tuzina pojazdów D o w ied zieliśm y się, że m oże s ta n d a rd o w o nastąpić taka konw ersja: pochodna*
------------------------- ► p od s ta w o w a *
Czyli w sk a źn ik d o obiektu klasy p o ch o d n ej k o m p ilato r p o trafi zam ienić na w sk aźn ik d o o b ie k tu klasy p o d staw o w ej. I Z atem w skaźnik d o sam o ch o d u m oże być o b słu ż o n y tam , gdzie I o czek u je się w sk aźn ik a d o pojazdów . Jeszcze inaczej: Do funkcji m o żn a w ysiać ad res obiektu k lasy pochodnej, naw et jeśli o n a sp o d ziew a się a d re s u obiektu klasy p o d staw o w ej W yobraź sobie te ra z ze m am y klasę K. Możemy z b u d o w a ć tablicę obiektów tego ty p u . K
tabl
p o d s t a w o w a [ 10] ;
Jest to oczy w iście tablica dziesięciu o b iek tó w klasy K. M ożem y sobie w yobrazić też funkcję, do k tó rej w ysyła się taką tablicę. void
funl(K
t t t [] ) ;
Jest to fu n k cja o n azw ie f u n l , która ja k o arg u m en t p rzy jm u je tablicę obiektów klasy K. O czy w iście pam iętasz, że tablicę m ożna rów nie d o b rz e odebrać jako w sk aźn ik . Jeśli ta k w łaśnie w olim y, w ów czas deklaracja funkcji w ygląda tak. void
f u n l (K
ttt*);
// argum entem
jest w skaźnik d o obiektów klasy K.
To w sz y stk o z n a m y do d aw n a. T eraz w y o b ra ź sobie jeszcze, że klasa K stała się klasą p o d staw o w ą d la klasy KK. S koro tak, to m ożem y tw orzyć w program ie nie tylko obiekty klasy K, ale tak że o b ie k ty k lasy KK. Te obiekty k lasy KK są oczyw iście nieco w iększe, bo n a jp ra w d o p o d o b n ie j w klasie pochodnej dodaliśm y jakieś n o w e składniki. „ r o w e r " z a m ie n ia s ię n a „ r o w e r z p r z e r z u tk o
p o d o d a n iu s k ła d n ik a
„ p r z e r z u łk a " .
Mam do Ciebie teraz kilka pytań: P y tan ie p ie rw sze: C zy d o funkcji f u n l m ożna w ysłać tablicę obiektów klasy K? O d p o w ie d z ia łe ś pew nie: -N o jasne, p rze c ie ż po to tę fu n k cję zd efin io w a liśm y. O to s to s o w a n e in stru k cje: funl(tabl
podstawowa);
*wskK = t a b l f un 1 (ws kK ) ;
K
//
tak w ysyła m y
//
tak w ysyłam y w skaźnik pokazujący na tę tabluę
podstawowa;
tablicę .
946
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu D obrze; jak w idać - sp raw a jest prosta. A teraz p rz y p o m n ij sobie te konw ersje sta n d a rd o w e , o których ciągle tu rozm aw iam y, p o czym sp ró b u j o d p o w ied zieć na... P y tan ie drugie: Jeśli w pro g ram ie m am y tablicę obiektów (pochodnej) k la sy KK KK
t a b l _ p o c h o d n a (10] ;
to czy m ożna w ysłać ją d o funkcji
funl? Czyli czy m o żn a n ap isać w yrażenie:
funl(tabl_pochodna); N o, jak Tw oim zdaniem - m ożna czy nie m ożna? O d ra z u odpow iem : M ożna, ale nigdy tak nie rób! D laczego m ożna? Dlatego, ż e om aw ian a tu k o n w ersja s ta n d a rd o w a u m o ż liw ia przecież kom pilatorow i zam ian ę w skaźnika k lasy p o ch o d n ej ( KK*) na w sk aź nik d o obiektów klasy podstaw ow ej (K*). R obiliśm y tak i nie ma żad n e g o problem u, jeśli funkcja pracu je z je d n y m obiektem . Pokazuje przecież na początek obiektu i już. Jeśli jed n ak w sk a źn ik u staw iliśm y na początek tablicy... Spójrz na rysunek.
R ysunek ten sk ład a się z trzech części. S k u p m y się na nich kolejno.
Część a) Tu w id z isz sytuację jak funkcja f u n l w id zi tab licę o b iek tó w klasy K. (D la u p ro szczen ia nary so w an e są ty lk o d w a elem enty. P o n iew aż tablica jest k lasy K,
947
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
a funkcja jej a d re s o d eb rała za pom ocą w sk aźn ik a k lasy K, w ięc nie m a p ro b le m u z o d n o szen ie m się d o poszczeg ó ln y ch elem en tó w tablicy. W skaźnik klasy K „w ie" jak d u ż e są ob iek ty k lasy K (ile zajm ują bajtów w pam ięci), w ięc p o trafi on skakać p o elem en tach tej tablicy.
Część b) N a tej części ry su n k u w id z im y sytuację, g d y d o funkcji zam iast a d ie su tablicy obiektó w k lasy K w y słan o a d re s tablicy obiektów klasy pochodnej KK. W olno nam tak, w m yśl om aw ianej tu konw ersji stan d ard o w ej. Funkcja o d eb rała ten ad res i w sta w iła d o w sk a źn ik a klasy po d staw o w ej K. Tutaj w id zim y , co się dzieje w p rz y p a d k u , gdy fu nkcja f un 1 chce odnieść się d o zero w eg o elem en tu p rzed sta w io n ej jej tablicy. O tó ż nie dzieje się nic złego! Ten obiekt k lasy KK jest oczyw iście nieco w iększy n iż o b iek t klasy K, ale nie m a z tym p ro b lem u . M im o że w tej funkcji na tablicę o b iek tó w klasy po ch o d n ej KK p o k azu jem y w sk aźn ik iem d o obiektów klasy p o d staw o w ej klasy K, i tak p o p ra w n ie ad re su je m y ten zero w y obiekt, praw da? K łopoty zac zy n ają się poniżej
Część
C)
Co się d zieje, g d y chcem y o d n ie ść się w tablicy nie d o elem en tu zerow ego, ale d o n astęp n eg o ? W zasad zie nie m a problem u, bo w sk a źn ik „w ie jak d u ż e są p o je d y n cze ob iek ty (ile zajm u ją bajtów w pam ięci), w ięc potrafi skakać po tablicy o d elem en tu d o elem entu . N iestety , w tej funkcji m am y d o czynienia ze w sk aźn ik iem K*. O n w ie jak d u ż e są elem en ty k lasy K. Potrafi sk ak ać po tablicy elem en tó w klasy K. T ym czasem u sta w io n o go tak, że p o k azu je n a tablicę KK! A elem enty klasy KK są w iększe... To w łaśn ie w id zisz na ry su n k u c). Jeśli w skaźnik p o k aże na miejsce, gdzie w e d łu g jego obliczeń jest elem en t o indeksie 1, to w sk a że źle! Jeszcze bardziej się pom yli, g d y ch o d zi o o d n iesien ie się do elem entu o in d ek sie 2. P ow ód jest w obu p rz y p a d k a c h ten sam: w sk aźn ik „m yśli", że elem enty m ają rozm iar obiektów klasy K, ty m czasem n a p ra w d ę pokazuje na tablicę elem entów , k tóre są nieco w iększe. W rezultacie tak ieg o nieporozum ienia n astąp i błąd n aru szen ia pam ięci (tzw . seg m en t violation) i p ro g ram zostanie zatrzy m an y . Błąd „seg m en t yiolation" oznacza w łaśnie, że coś źle adresujem y. K o n w e rs ja s ta n d a rd o w a w s k a ź n ik a k la sy p o c h o d n e j n a w s k a ź n ik k la sy p o d s ta w o w e j l nlie p e łn i n. a s z y c—h... o c... ze kiw. a ń• , •je ś li w s k a ź n ik p. o k.a. z u je na iu s...... tablicę. Konwersja ta została wymyślona do pracy z pojedynczymi obiektami.
L
*
*
*
.
..
■
Zatem: Tablica sam o ch o d ó w nie m oże być obsłużona w funkcji, która obsługuje tablicę p o ja zd ó w . T o dlateg o , że tablica sam ochodów nie jest rodzajem tablicy pojaz d ó w . T u zin sam o ch o d ó w nie jest rodzajem tuzina pojazdów . To po prostu tylko
948
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu tuzin . K toś kiedyś dow cipnie p o w ied ział, że to rb a p e łn a jab łek nie jest ro d zajem torby pełnej ow oców . F unkcja, która obsługuje tablicę p o jazd ó w - p o trafi s k a k a ć p o jej elem en tach (bco w ie, ż e n astęp n y elem ent jest np. 53 bajty dalej). T ak a fu n k cja nie m o że jednaŁ o b słu ży ć tablic sam ochodów , tablicy w ó zk ó w d z ie c in n y c h , tablicy p ro m ó w kosm iczn y ch (m im o że to k lasy p o ch o d n e od p o ja z d u ) - bo n ie m oże w iedzieć, jak w ie lk ie są elem enty w szy stk ich takich tablic, czyli ile bajtó w m a każdynn p rz y p a d k u d anej tablicy p rzeskoczyć, by przejść d o ele m e n tu n astęp n eg o . Z apam iętaj: Do funkcji spodziew ającej się a d re su tab licy o b iek tó w klasy* podstaw ow ej nie należy w ysłać a d re s u tab licy ob iek tó w klasy* pochodnej.
P ow yżej m ów iliśm y o tym , ż e - co p raw d a - k o n w ersja jest m ożliw a (kom pila tor n ie zaprotestuje), ale w p rz y p a d k u tablic m o ż em y n aszeg o czy n u poża łow ać. Inne k o n w ersje są już p iln ie sp ra w d z a n e p rz e z k o m p ilato r. Na p rz y k ła d k o m p ila to r nie dopuści d o ko n w ersji pochodna**
--------- x-------- »
podstawowa**
W sk aźn ik pokazujący na w sk aźn ik , k tóry za w ie ra a d re s obiektu klasy p o ch o d n ej, nie m oże zostać zam ien io n y na w sk a źn ik , p o k azu jący na w sk a ź n ik zaw ierający a d re s obiektu klasy p o dstaw ow ej. W iem , to zu p ełn ie nie d ziała na w yo b raźn ię, w ięc p o w ie m to inaczej, języ k iem Fredry: • w dow a p o p re n u m e ra to rz e czaso p ism a "P rzekrój’’ nie m o ż e być zam ieniona na • w dow ę p o p re n u m e ra to rz e czaso p ism ilustrow anych Co p r a w d a „P rzekrój" był rodzajem czasopism a ilu stro w a n eg o , ale to już b y łab y "dziesiąta w o d a p o kisielu ". W d o w a to w dow a.
W W tym ponurym miejscu radośnie kończy się program obowiązkowy tego rozdziału C zy teln ik o m czytającym tę k siążk ę po raz p ie rw szy - p ro p o n u je w ięc te ra z o puścić w szy stk ie dalsze p a ra g ra fy tego ro zd ziału i p rzesk o czy ć od ra z u n a stro n ę 968, g d zie zaczyna się ro z d z ia ł 21. M uszę C i się teraz p rzy zn ać, że ju ż kilka razy - g d y o d ra d z a łe m Ci c z y ta n ie jakiegoś frag m en tu , to - czasem w cichości d u ch a liczyłem na to, iż m im o w sz y stk o g o przeczytasz. T era z jed n ak n ap raw d ę u w a ż a m , ż e nie p o w in ie n eś czytać n a stęp n y c h zag a d n ień . Są one n ap raw d ę z b y t szczegółow e, jak d la
949
Rozdział. 20. Dziedziczenie K onwersje standardow e przy dziedziczeniu
początkującego. Z a p ra sz a m o d razu d o z a c z a ro w a n e g o św iata funkcji w irtu a l nych. To d zięk i nim - p ro g ram o w an ie n a p ra w ę jest obiektow o o rien to w an e.
20.10.4 Konwersje standardowe wskaźnika do składnika klasy Ten p a ra g ra f p ro p o n u ję zd ecy d o w an ie opuścić p rz y p ierw szy m czy tan iu . Rze czy, o k tó ry ch tu ro zm a w iam y , są b ardzo ciekaw e, ale dopóki nie o sw o isz się z k o n w ersjam i o m ó w io n y m i p o p rz e d n io - nie ra d z ę sobie tym za p rz ą ta ć głow y. Z ro zu m ien ie tego p ara g ra fu nie jest n ie zb ęd n e d o czytania d alszej części książki.
(Z ak ład am tu, że p rzeczy tałeś rozdział o w sk a źn ik ac h do sk ład n ik ó w klasy str. 734). W sk ró cie m ożna ująć to tak: jest tu d o k ład n e o d w ró cen ie pop rzed n iej zasad y . W skaźnik d o sk ład n ik a klasy podstawowej m oże być niejaw nie zam ien io n y na w sk aźn ik do sk ład n ik a klasy pochodnej. Jest to m ożliw e, g d y klasa p o d staw o w a jest d zied ziczo n a p u b liczn ie i taka konw ersja jest jed n o zn aczn a. O d w ro tn a zam ian a nie następuje au tom atycznie. A by to p rzek o n u jąco w y tłu m aczy ć posłużm y się rysu n k am i. Z o b aczy m y na nich o b ie k ty klasy p o d staw o w ej i pochodnej. P rzy p o m in am , że w sk a źn ik do p o k a z y w a n ia na sk ład n ik i klasy zaw iera inform ację określającą nie tyle g dzie jest d a n y sk ład n ik , ale ja k d a le k o od początku obiektu klasy d an y sk ład n ik zw y k le się znajduje. C zy pam iętasz jeszcze tę n aszą historyjkę o planach kon stru k cy jn y ch sam o lo tu c o n c o r d e ? R y su n ek poniżej p rz e d sta w ia klasę p o d staw o w ą i p ochodną. W skaźniki p o k a zujące na ich sk ład n ik i są zobrazow ane jako su w a k i p o ich lewej stronie. Rys. 20-12
k la s a p o d s ta w o w a
k la s a pochodna
częsc odziedziczona
część nowa
[g S 3
N a stę p n y ry su n ek pokazuje to, co jest p rzed m io tem tego paragrafu: inform acja niesio n a p rzez w sk aźn ik d o składników klasy po d staw o w ej nadaje się do u sta w ie n ia w sk aźn ik a d o składników klasy pochodnej. Skoro tak, to istnieje
950
Rozdz. 20. Dziedziczenie Konwersje standardow e przy dziedziczeniu ko n w ersja stan d ard o w a m ogąca zam ienić p ie rw s z y z tych w sk aźn ik ó w na drugi.
Klasa pochodna
Rys. 20-15
częsc odziedziczona
częsc
nowa
O .K .!
O d w ro tn a zam iana nie zaw sze m iałaby sens i d la te g o nie m a takiej konw ersji stan d ard o w ej. Jeśli chcem y to uczynić - m u sim y zro b ić to jaw nie i na w łasne ryzy k o . M oże się zdarzyć, że pokażem y na coś, co je st p o z a obiektem . Rys. 20-14
kla sa p o d s ta w o w a
N
o n s e n s
-
Jedn ak ta k a konw ersja p rzep ro w ad zo n a jaw nie ("na siłę") n ie zaw sze m u si byc błędem . Jeśli w sk aźn ik sk ład n ik ó w klasy po ch o d n ej a k u r a t u staw io n y jest tak że p o k a z u je n a coś, co jest o d zied ziczo n e o d klasy p o d sta w o w e j - to b łąd nie nastąpi. Rys. 2 0 -1 3
kasa p o d s ta w o w a
951
Rozdział. 20. Dziedziczenie W irtualne klasy podstawowe
Przykładowe zastosowanie tej konwersji to oczyw iście sytuacja, g d y w skaźnik tego ty p u w y sy łam y do funkcji. Z ałóżm y, że funkcja sp o d ziew a się, iż otrzym a w sk a ź n ik d o p o k azy w an ia n a składniki klasy pochodnej (np. M erced es), a m y w y sy ła m y d o niej w sk a źn ik u staw io n y na sk ład n ik sam o ch o d u (n p . sk ład n ik kierow nica). P oniew aż w M erced esie też jest kierow nica, d la teg o k o n w ersja typu w sk a źn ik a jest oczyw ista.
Przypomnienie N ie m a stan d ard o w ej k o n w ersji "w skaźnika d o składnika" na w sk a ź n ik v o i d * , z u w ag i na zu p e łn ie in n ą n atu rę tego w sk aźn ik a.
20.11 W irtu a ln e k la s y p o d s ta w o w e W ty m p arag rafie m ó w ić będziem y o specyficznym sposobie d zied ziczen ia. Jeśli czy tasz tę k siążk ę p o ra z pierw szy, to o p u ś ć to zagadnienie. O p isan e jest tu sw eg o ro d z a ju curiosum. Jest i ta k i sposób d zied zic zen ia - ale nie stosuje się go często. P a ra g ra f jest łatw y, je d n a k przy p ie rw szy m czytaniu te w szy stk ie sp raw y m o g ą u Ciebie w yrobić p o g ląd , że d zied ziczen ie to coś o k ro p n ie sk o m p lik o w an eg o .
X M ów iliśm y, że p rz y tw o rzen iu klasy po ch o d n ej na liście b ezp o śred n ich klas p o d staw o w y ch d a n a klasa m oże się pojawić ty lk o raz. Nic jednak nie przeszka dza, by tak a klasa w y stąp iła jeszcze raz w hierarchii, jako dalszy p rzo d ek . Oto p rzy k ła d takiego grafu: Rys. 20-16
W rezu ltacie takiego dziedziczenia w obiekcie klasy D m am y kilka obszarów będących „d zied zictw em p o przodkach". O biekt klasy D m ożna sobie schem a tycznie w y o b razić tak:
952
Rozdz. 20. Dziedziczenie W irtualne klasy podstawowe
Rys. 20-18
Ą_ .. _ *
B - -—
D
C V._____________ y ':
D
Z atem w o b ręb ie obiektu klasy D w ystępują d w u k ro tn ie sk ład n ik i odziedziczo ne od k la sy A. ■ C zasem to do b rze, czasem źle. Z ależy to o d tego, co d a n a klasa reprezentuje. R ozw ażm y dziedziczenie p rzed staw io n e na p o n iż szy m grafie: ś ro d e k transportu f T łó d ź
środek transportu Rys. 20-17 T sam ochód^^ ^ a m fib ia "
Klasa śro d e k tran sp o rtu m oże w y g ląd ać np. tak c l a s s s r o d e k _ tr a n s p o r tu 1 // ... protected: double polozenie_x; double void
//
polozenie_y;
zmiana
położenia(double
delta_x,
double
delta_y);
- .
}; To, co w a m f i b i i jest ze śro d k a tran sp o rtu - czyli n p . jego bieżące położenie albo d z ia ła n ie polegające na zm ia n ie położenia - d z ie d z ic z o n e jest zaró w n o od sam o ch o d u jak i od łodzi. N ie jest to dobre, bo: <$♦ 1) D w u k ro tn ie o d zied ziczy liśm y tę sam ą inform ację.
2
♦♦♦ 2) Jest to ryzykow ne. Jeśli g ra f w ygląda d o k ła d n ie tak, jak to p rz e d s ta w i liśm y - d o stęp do sk ła d n ik ó w tych nie jest je d n o z n a c z n y . Próba o d n ie sienia się do sk ład n ik a p o ł o ż e n i e m m o ż e d o ty c zy ć zaró w n o tego o d zied zic zo n eg o od sam o ch o d u , jak i tego o d zie d z ic z o n e g o od ło d zi.
Rozdział. 20. Dziedziczenie W irtualne klasy podstawowe
9)
Ciekawostka: Jeśli klasa sro d ek _ tra n sp o rtu ma składnik statyczny (czyli taki, który jest wspólny dla wszystkich obiektów tej klasy - to dla niego wieloznaczność nie wystąpi. Taki składnik w obiekcie klasy pochodnej będzie przecież jednokrotnie, bo jest wspólny dla wszystkich dziedziczeń tej klasy. Nie będzie też wieloznaczności w przypadku odnoszenia się do zdefiniownaych w klasie typów wyliczeniowych ( e n u m ) i innych zdefniowanych w niej typów (klasy zagnieżdżone). One przecież też są wspólne dla wszystkich obiektów tej klasy, prawda?
953
954
Rozdz. 20. Dziedziczenie W irtualne klasy podstawowe class
samochód
: public
virtual
srodek_transportu
{ .
}; class {
//
lodź
: public
virtual
srodek_transportu
...
}; P oniew aż k la sa a m f i b i a nie d zied zic zy b e zp o śred n io k la sy s r o d e ^ t r a n s p o r t u - w ięc definicja jej jest b ez zm ian. N a liście p o c h o d z e n ia nie w ystępuje tam p rzec ież klasa s r o d e k _ t r a n s p o r t u . class
{
amfibia
: public
samochód,
public
lodź
(t ...
1; Zrobione! T e ra z zastanów m y się, co na tym zy sk aliśm y : •
1) O biekt klasy am f i b i a się zm n iejszy ł, bo rzeczone składniki opisujące poło żen ie geo g raficzn e ju ż się n ie d u p lik u ją.
•
2) N ie ma ju ż ry zy k a w ieloznaczności. C o p raw d a, są teraz d w ie drogi d o celu (przez sam o ch ó d , alb o p rz e z łódź), ale cel jest w obu p rz y p a d k a c h ten sam: d o k ła d n ie te sam e kom órki w pam ięci.
W arto to zap am iętać:
Jak w idać, d zied ziczen ie w irtu a ln e jest sposobem na z a m ia n ę zw ykłego m echa nizm u d zied ziczen ia. P o w ied zm y jednak jasno: to d z ied zic zen ie jest w irtu aln e, a nie sam a klasa pod staw o w a. M im o to, m ów i się „ w irtu a ln a klasa p o d sta w o w a ", bo to prościej niż: „k lasa p o d staw o w a d z ie d z ic z o n a w irtu aln ie". M ów iąc ta k - pam iętajm y jed n ak , że sam a klasa jest n ajzw yklejszą klasą. T akże do jej sk ła d n ik ó w odnosim y się tak sam o, jakby były o n e ze zw ykłej klasy po d staw o w ej.
Ta sama klasa może być odziedziczona wirtualnie i nie wirtualnie (czyli tradycyjnie). W yobraź so b ie, że bu d u jem y u le p szo n ą am fibię - taką, która w razie aw arii m oże p o d zielić się na pół. Jedna część m oże iść na d n o , a d ru g a od d ziela się o d niej tra n sp o rtu ją c rozbitków . M a ona w ów czas in n ą pozycję niż reszta am fibii. Oto g ra f te g o obrazka:
955
Rozdział. 20. Dziedziczenie W irtualne klasy podstawowe Rys. 20-21 środek transDortu
łódź
samochód
środek transportu
amfibia ulepszona amfibia
a to definicja klasy: class
U
ulepszona_amfibia
:
public
srodek_transportu,
public
amfibia
...
}; K lasa p o d staw o w a n ie jest tu d zied ziczo n a w sposób w irtu a ln y (brak p rz y d o m k a v i r t u a l ) . Jest więc d zied ziczo n a norm alnie. W obiekcie klasy u l e p s z o n a a m f i b i a b ęd ą więc d w a ró żn e k o m p lety sk ła d n ik ó w środka tra n sp o rtu . C zyli w ła śn ie to, o co nam chodziło.
Co to naprawdę oznacza - dziedziczyć jakąś klasę wirtualnie i równocześnie nie wirtualnie ? K®
Jeśli m am y jakąś h ie ra rch ię klas, w której ta sam a klasa dziedziczona jest czasem w irtu a ln ie , a czasem n o rm aln ie - w ów czas: a) w obiekcie k la sy pochodnej (tej na sa m y m dole hierarchii) u tw orzo ny zo stan ie jeden w spólny zestaw sk ład n ik ó w , jako rezu ltat wszy stkich w irtu aln y ch dziedziczeń tej klasy. b) w obiekcie tym będą także o d rę b n e zestaw y sk ład n ik ó w tej klasy p o d staw o w ej - po jednym od k a ż d e g o dziedziczenia nie w irtu al nego (zw ykłego).
20.11.1 Publiczne i prywatne dziedziczenie tej samej klasy wirtualnej Jeśli opuściłeś p o p rz e d n i parag raf - opuść i ten.
X W ró ćm y d o p rz y p a d k u najprostszego. Z ałó żm y , że klasa p o d staw o w a jest w h ie ra rch ii d zied ziczo n a tylko w irtualnie. M ogą b y ć jednak dw a różne sposoby. Z n a m y je oczyw iście. W irtu a ln e d zied zic zen ie klasy podstaw ow ej m o ż e się odbyć zaró w n o w sposób p r i v a t e jak i p u b l i c . O znacza to, że te sam e sk ład n ik i znajdą się ostatecznie
956
Rozdz. 20. Dziedziczenie W irtualne klasy podstawowe w obiekcie klasy p o ch o d n ej jednokrotnie - ale d zied zic zo n e b ęd ą na dw a różne i w y k lu czające się sp o so b y __________ __________________________________
Rys. 20-22
A
C
B
D Jaki jest rezu ltat takiego działania? O d p o w ied ź jest prosta: W y starczy , by choć je d n o dziedziczenie w irtu a ln e tej klasy p o d staw o w ej byle p u b liczn e, a efekt jest taki, jakby w szystkie p o zo stałe d z ie d zic zen ia tej klasy b y ły także publiczne. Z n o w u o d p o w iad a to naszem u d ośw iadczeniu z życia co d zien n eg o : O tym samym fakcie dowiaduje się 3 dziennikarzy pracujących dla te] samej gazety (dziedziczą wirtualnie tę samą informację). Dwóch z nich zo bowiązuje się do zachowań ia tego samego faktu w tajemn icy ( p r i v a t e ) , n trzeci tajemnicy nie obiecuje ( p u b l i c ) . Wówczas ta informacja dostaje się do gazety i jest publikowana. Nie ma znaczenia dyskrecja dwóch pienoszych dziennikarzy. Liczy się to, że ten trzeci wygadał.
20.11.2
Uwagi o konstrukcji i inicjalizacji w przypadku klas wirtualnych T en p a ra g ra f przy p ie rw szy m czytaniu rad zę opuścić.
X S koro d ziedziczenie w irtu a ln e spraw ia, że w rezu ltacie w obiekcie klasy p o ch o d n ej istnieje ty lk o jeden „p o d o b iek t" będący d zie d z ic tw e m od klasy w ir tu aln ej - zatem k o n stru k to r tej klasy po d staw o w ej p o w in ien ru szy ć d o p racy tylk o raz.
Rys. 20-23
A B D
A
\
t
C
B
E
D
\ F
A
t
C E F
/
G d y d efin iu jem y obiekt klasy pochodnej, k tóry m a jakieś w irtu a ln e k lasy p o d staw o w e, to te p o d sta w o w e klasy w irtu aln e k o n stru o w a n e są na sam y m p o czą tk u , p rzed w szy stk im i in n y m i klasam i p o d staw o w y m i. Bez w zg lę d u na to , na k tó rej pozycji na liście pochodzenia się pojaw iły. Jeżeli w h ierarchii je s t
957
Rozdział. 20. Dziedziczenie W irtualne klasy podstawowe
więcej niż jedna k lasa d zied ziczo n a w irtu a ln ie , to ich kolejność konstrukcji w y n ik a z kolejności w jakiej pojaw iły się n a listach p o ch o d zen ia. Jest to tzw. z a sa d a „o d lewej d o p raw ej"._______________________________________________ Rys. 20-24
E
A / B
/ F
\ C
\ G
D W tym p rz y p a d k u n ajp ie rw będzie k o n stru o w a n a klasa
A
a potem klasa
E.
Za konstrukcję tego wirtualnego dziedzictwa odpowiada klasa „najbar dziej pochodna“ K lasą najbardziej p o c h o d n ą n azyw am y tę klasę, która tw o rzy obiekt nie będący ju ż p o d o b iek tem ż a d n e g o innego obiektu. C zyli np. M am y hierarchię:
Jeśli defin iu jem y o b ie k ty różnych klas z tej hierarchii, to klasam i najbardziej p o ch o d n y m i są p rz y ich kreacji: A
obja;
BI C
objbl; obje;
D B2
obj d ; objb2;
// klasa // klasa // klasa
najbardziej pochodna najbardziej pochodna najbardziej pochodna // klasa najbardziej pochodna // klasa najbardziej pochodna
to to
A BI fo -> C
to •+ D to -> B 2
T o w k lasie najbardziej pochodnej m usim y z a d b a ć o uruchom ienie konstruk tora k lasy w irtu aln ej A. D o tej p o ry taka rzecz była niedopuszczalna. K ażda klasa p o ch o d n a mogła u ru c h o m ić tylko k o n stru k to ry swoich b ezpośrednich klas p o d staw o w y ch (czyli rodziców ). N ie m ogła uruchom ić k o n stru k to ró w klas podstaw ow ych pośred nich (czyli d z ia d k ó w , pradziadków ). K on stru k to ry d ziadków w yw oływ ali ro dzice, a p ra d z ia d k ó w - dziadkow ie, i tak dalej, a ż do szczytu hierarchii. W p rz y p a d k u d zied zic zen ia w irtualnego, w ko n stru k to rze klasy najbardziej p o ch o d n ej o d p o w ia d a m y za uruchom ienie kon stru k to ra klasy w irtualnej (czyli u nas k lasy A).
Ę
RftRaiz // R u u iw n u iu o p .iisuo)i //
"7/ (łU T )v
■{
{ ) ()v
: D Tjąnd } V s s e jo m ij u n i J ia i o u o z o t z p ą z p J iz p ią u u o // ?///////////////////////////////////////////////////////////// a j s n d b j b d bfeuu n p e j > jX z j d A j o j s o i d B jp - A jo j> jr t x łS u o > [
3 i >[4sXzsą \
ai.jB .i8 u i i u j b j s o u i A z s b u m A u i s i p i z p i M a io :j> i
u i Aj o B i U B p i u i B d n > jz b iA 'io q o p o m
b u [ e r ijjiM
A sej>{ fo ł a iu a z B S o d X y v \
seu
'sej^ ib iu y a p
sAj b z o jo
b iu j b m z A u B u i a i u u i o p jo j> jn x is u o > j
B > jp o z jd o S a i ^ a j B p o 8 a j o d is a A z a z s o j j
X q o { B i s n u i a i u a z a i z p a i z p a u d d j S B u a p z B > [ ( a u iB n jjiA Y X sb [>j B i o j ^ r u j s u o j j a i u a i u i - o i p n i n o D e q p e z X q { B i s n u i a i u z a b j M B iu a [o > jo d o S a u z a b i s A j o p ( a u p o i p o d X sbj >i
io j^ n ijs u o ^ X p z e > j 'A sbj>{ (ai>|B ł o 3 a u [ B r q .iiA \ B i u a z c u z p a i z p o n > jp t ? d X z jd m oj A i o j ^ r u j s u o ^ a u u t bjb | u i bsb [>j b - o j A q a iu o S a u B U i a i u u i o p B JO j> fru js u o > i A q A p f )
‘XsB|>{ s(9l>JB( ą A z n X q 'B i u a z a i z p a r z p u q o j B a a i q f o jB J obuz i s n u i a i u X sbj>( ^ i u m - o > p A z n a z 'A p B S B Z a i u B M O i p o p u i b u o; a [ iq u B J B M ^ ) A u B u i a i u u i o p jO ł) { r u ą s u o ) j m a z > jB j b[ ą X z B S o d X M o d iM js a ( a z j ą o p - a i u [ B n j i i M b u -o z a i z p a i z p a X q b u i t A jo :)> {n .risiio > [ sa i> jB f b u i bjoj >[ 'dSB j>| a b ( n i u i j3 ( ]
•i){ałOi[q!q z jsa[ £sb[>{ i[sa( aaizpaiAv aiu u jso jd o d paabu 0834 azopv 'BujBrqjiM bsb [>( sb>jb( uipiB jaiij m B{Xq az pB piuiB d aiu zn( azo u i BjsiuiBiSojd B iuazoizpaizp n iu a jo jo d uiAjSBUpid m
epBJ euzoA^ejd bui
djl
a iu 3 jo 8 o m n u i 3 j q o i d o ; - B i o j ^ j r u j s
-UO>J o S a u p B Z BUI 3 IU B U IB U JIIM BSB[>J l[S a ( 3 I 3 SI m X z 3 0
A u B u ia iu u io p j o j ^ r u j s
-uo>i (af j s a ( A u B i u i B i p r u n s b z 3 m .o aa '( a u iB n jJiA \ X s b j >[ B -io j> jru jsu o > | b i u b j o a a A m bu i
3 iu f a u p o i p o d ( a iz p .iB q ( B u X s B jq B i o j ^ r u j s u o ^ j f a u t A o B z ijE b iu i a p s i [ b u i j s a f a U B M O JO u S lZ b U E J S O Z - ( ’ p }l 'M O)[
- p B iz p 'M p a i z p o i ) q a X M O M B ;s p o d s e j >{ A W i o j ^ r u j s u o ^ ip X u ( X a E z i[ -B b jU I i p B J S I J BU X u lS l{IA V B jS O d ajO }>{ BIUBJO m A m
3U U ] 3l>[lsXzSyW
a X s' b j >j z i[ X z 3 - f e u p o i p o d f e iz p jE q ( B U X s b j >{ z 3 i u b { o m X m o j o^ ja j
d S e A \ n p o d 3 i u i z d a \ a /is u j y p p i ą o o b f n t i j j s n o ^ j o j e i ] d u i o > [ - i i i p
- j B J 3 i q b ( B 3 o d a u B i s z o j b p d q f3 U [B r q jiA v X s b [>j B i o ^ n i ł s u o j j b i u b { -oaa X m a z o u i i u i - o 8 3 U [ B r q ii A \ B i u o z o i z p a i z p n > ( p B d X z jd m ^ B u p a f B i u a z o i z p a i z p o 8 3{> {X a \ z n ^ p s d A z j d a \
„O b s b [>i
zbi
azozsaf
u ia ^ o d
'z a
ojXq Xq >j b x
‘d b e j z s b u i a p s i M A z o a z y
e s e i> { z b i a z a z s a f u ia ^ o d
'i e
b s b i >(
io ł> jn a łS u o > i
u o ; i u i o q D r u n A v ja i d ( c u o X s b [>j r q > j3 iq o n iu B M o r u ^ s u o ^ X z i d X p o ;A \ z a p a z j j ii p i > p s A z s M m u ib u i
B fq ^ f a u i B r p i i M
X s b j >[ B J O ł) ( n q s u o ^ 3 i u b } o a \X m
a p s a iu m
B JO ł> jru łS u o > { o 8 3 JO ł> i '[ a u W o B z ijB b iu i a p s i j b u o 8 a ; aaqo^v\
(3 t z p J B q f e u
bSBf>j i b B r q X s
s [o i >j b
(
m
a iz p d q
s b [>{
ip A }
z
nauo>i
b u p o ijo o d
BpzB>( a z
'X z o b u z
o? ' u i p i B i a i i j [a ł z s b [>[ ip i> p s A z s A Y X ł5 ja iq o 3 B A \ o i u q a p a t u i B i S o i d m u i B z i a i u i B z ijs a f
n 4 > ja iq o o 8 a u B A \ o i u q a p a i u s B | M X s b i >{ p o X z a jB Z 'B u p o ą a o d ( a iz p jB q [ B U
jsa( b s b [>[ B J i q q ' o ^ p s o u z a a z j d s 0 } z a p a z j j " - a i u M a d z s b j o a \ b z - „ j o j ^ B f - "
a vvo vviqs|)od Xsi?pi auiBnj.ii/W aiuazaizpaizQ ’q z ‘zpzoy
8S6
959
Rozdział. 20. Dziedziczenie W irtualne klasy podstawowe
class
BI
: public
virtual
A
{
//
©
public: B I () : A (4 )
};
{
};
u
/////////////////////////////////////////////////////////////ZZ class B2 : p u b l i c v i r t u a l public: B 2 () : A (4 ) { } ;
A
{
//
©
H}; /////////////////////////////////////////////////////////////// c l a s s C : p u b l i c BI, p u b l i c B 2 { public: _ C() : (6) , B l ( ) // ©
A
{ }; }; /////////////////////////////////////////////////////////////// class D : public C { public: _ D ()
: C O
//
©
{ }; }; ///////////////////////////////////////////////////////////////
O P o n iew aż p o d ejrzew am y , ż e klasa A będzie d zied ziczo n a w irtu aln ie, dlatego z a o p a tru jem y ją w k o n stru k to r dom niem any. © K lasa B I d zied zic zy klasę A w sposób w irtu aln y . Poniew aż jest to „p ierw sze p o k o le n ie" d zied ziczen ia tej klasy - dlatego nie jest to tak interesujące. N a liście inicjalizacyjnej k o n stru k to ra klasy BI jest oczy w iście w yw ołanie k o n stru k to ra k lasy A - i nic w ty m d ziw n eg o , skoro klasa A jest rów nocześnie bezp o śred n ią p o d sta w o w ą . P o d o b n ie w p rzy p ad k u klasy B2. © O G d y b y nie istniało d zied ziczen ie w irtualne, to n a liście inicjalizacyjnej k o n stru k to ra klasy C w ż a d n y m p rz y p a d k u nie m ogłoby się znaleźć w y w o łan ie kons tru k to ra klasy p o d staw o w ej niebezpośredniej (czyli nie rodziców , a d ziad k a). Jed n ak p o n iew aż klasa A jest w tym d rzew ie genealogicznym dziedziczona w irtu a ln ie - to p rz y d e fin io w a n iu o b ie k tu k la s y C - w yw ołanie ko n stru k to ra klasy A zostanie d o k o n a n e z listy inicjalizacyjnej tego © konstru która. W yw oła nia u m ieszczo n e na listach inicjalizacyjnych ro d zicó w klasy C czyli klas B I i B2 z o sta n ą zig n o ro w an e. © K lasa D jest p o ch o d n ą od klasy C.Jeśli definiujem y obiekt klasy D,to oczyw iście klasą najbardziej p o ch o d n ą jest tutaj D.Z listy inicjalizacyjnej k o n stru k to ra tej klasy D © zo stan ie w y w o łan y konstruktor k lasy w irtualnej A.P oniew aż jednak na liście inicjalizacyjnej n ie ma tu w yw ołania określonego k o n stru k to ra klasy w irtu aln ej A, w o b ec tego u ruchom iony zostanie konstru k to r d o m n iem an y z klasy A. N ato m iast w szy stk ie ew entualne w y w o łan ia konstruktora klasy A,
960
Rozdz. 20. Dziedziczenie W irtualne klasy podstawowe k tó re m ogą być n ap o tk an e w hierarchii: u ro d zica z o sta n ą zignorow ane.
20.11.3
C, u d z ia d k ó w BI, B2 -
Dominacja klas wirtualnych T ak że i ten p arag raf p ro p o n u ję opuścić p rz y p ie rw szy m czy tan iu . M ów im y tu o sp raw ach , których w pierw szej fazie n a u k i C++ nie b ęd ziesz na pew no potrzeb o w ał.
P rzy p o m in asz sobie zap ew n e, że w p ro w a d z e n ie w irtu aln eg o dziedziczenia klas u ch ro n iło nas p rz e d w ieloznacznością d o stę p u . W idać to na poniższych grafach . Rys. 20-26
V {skl}
V {skl}
t A
t B
\
C
V {skl} X A
N
\
X
B
X C
N a g rafie z lewej stro n y - jeśli klasa C o d n o si się d o n azw y skl to o k azu je się, że są d w ie takie nazw y. W y stęp ie w ięc w ieloznaczność. P rzy e w en tu a ln y m o d w o łaniu się z klasy C d o takiej nazw y skl - k o m p ilato r zap ro testu je. N a g rafie z praw ej, g d z ie klasa Vjest d zied zic zo n a w irtu aln ie, n ie m a w ielo zn a c z n o ś c i-b o nazw a skl w y stęp u je tu tylko raz. C o p raw d a, są d o niej d w ie drogi A::V::skl
oraz
B::V::skl
ale obiem a tym i d ro g am i trafiam y d o tego s a m e g o celu. M oże b y ć jed n ak inna sytuacja. Spójrz na o d n o śn y graf. Rys. 20-27
2
W klasie V (dziedziczonej w irtu aln ie) jest sk ład n ik skl. N ato m ia st d o d a tk o w o klasa p o ch o d n a A także defin iu je sk ład n ik skl. Są w ięc d w a sk ła d n ik i o tej sam ej nazw ie.
961
Rozdział. 20. Dziedziczenie Ćwiczenia
Z ałó żm y , że teraz w k lasie D użyjem y w w y ra ż e n iu n azw y skl. D o którego z tych dw óch s k ła d n ik ó w w ów czas się o d n iesiem y ? N ie próbuj w y m y ślać o d p o w ied zi, bo intuicja m oże tutaj zaw ieść —O tó ż nazw a A: :skl d o m in u je tu ta j nad nazw a V: :skl C o to znaczy d o m in u je? M ó w im y , ż e nazw a A: :skl d o m in u je n a d n a z w ą V: :skl w tedy, g d y k lasa A jest jedną z klas p o ch o d n y ch klasy V. To była definicja ja k b y bardziej form alna. P oniew aż ja lu b ię definicje mniej form aln e w ięc u jąłb y m to tak: Jeśli, gdzieś w h ierarch ii, jedna nazw a s k l zasłan ia choć raz in n ą n azw ę s k l — to znaczy, że ta zasłan iająca dom inuje. C h o ćb y n aw et obecnie to zasłonięcie nie b y ło skuteczne. W n a sz e j hierarchii p atrz ąc z klasy B, C n a z w a s k l z klasy A zasłoniła n azw ę s k l z klasy V. N ic nie sz k o d zi, że teraz z k lasy D w idać obie n a z w y s k l . Z ostaje ta niesław a: w szyscy w ro d zin ie i tak szep czą po kątach: „ z d a rz y ło się k ied y ś, ż e ten s k l z klasy A zasło n ił tego z klasy •J .
P o słu g iw an ie się k la sa m i w irtualnym i jest, jak w idać, nieco tru d n iejsze niż klasam i nie w irtu a ln y m i. N ie m a się czym je d n ak przejm ow ać, bo teg o sposobu d zied ziczen ia nie sto su je się często. IMHMHMWWM
20.12 Ć wiczenia E
Dokończ poprawnie następujące zdanie. Na liście pochodzenia klasy ustawiamy nazwy. a) klas pochodr.ych b) klas podstawowych Klasa B dziedziczy klasę A, w której jest (nieprywatna) dana-składowa x. W klasie Bjest jedynie funkcja s k ł a d o w a f. Napisz wyrażenie pokazujące, jak w funkcji f można najprościej skorzystać ze składnika x. Jeśli w klasie B z poprzedniego ćwiczenia dodatkowo byłby składnik o nazwie x, to jak w funkcji f można by się odnieść do składnika x z klasy B, a jak do składnika x z klasy podstawowej A. Czy klasa pochodna dziedziczy prywatne składniki klasy podstawowej? Jeśli w klasie pochodnej jest składnik o nazwie identycznej z nazwą składnika z klasy podstawowej, to jak nazywamy taką sytuację? a) przeładowanie b) zasłonięcie Mamy obiekt klasy M. Czy możemy go odziedziczyć tak, by powstał obiekt klasy K? Dokończ następujące zdanie. Jeśli klasa K dziedziczy klasę P w sposób p r dostęp mają w klasie K a) składniki p r i v a t e klasy P? b) składniki p r o t e c t e d klasy P?
ivate,
to jaki
20
962
Rozdz. 20. Dziedziczenie Ćwiczenia c) składniki p u b l i c klasy P Dokończ następujące zdanie. Jeśli klasa K dziedziczy klasę P w sposób p r o t e c t e d , to jaki dostęp mają w klasie K a) składniki p r iv a t e klasy P? b) składniki p r o t e c t e d klasy P? c) składniki p u b l i c klasy P Dokończ następujące zdanie. Jeśli klasa K dziedziczy klasę P w sposób p u b l i c , to jaki dostęp mają w klasie K a) składniki p r i v a t e klasy P? b) składniki p r o t e c t e d klasy P? c) składniki p u b l i c klasy P Co byłoby potrzebne, aby z zakresu klasy pochodnej dotrzeć do prywatnych składników klasy podstawowej ? Mamy klasę K, która jest pochodną od klasy A i klasy B
W jaki sposób odziedziczona zostaje klasa B ( p u b l i c , p r i v a t e , czy p r o t e c t e d ) ? Odpowiedz na to samo pytanie w przypadku, gdy B jest strukturą. Odpowiedz na to samo pytanie w przypadku, gdy K jest strukturą. (X) Klasa K dziedziczy klasę G w sposób pr i v a t e , to znaczy wszystkie odziedziczone publiczne składniki klasy C stają się niedostępne (bo p r i v a t e ) w klasie K Co możp przeciwdziałać temu i sprawić, by jakiś odziedziczony składnik x, mimo wszystko stał się p u b l i c ? Jak wyglada stosowny zapis? (X) Jeśli klasa K dziedziczy klasę G w sposób p r i v a t e , to czy (i jak) mimo wszystko można sprawić, by wybrane odziedziczone składniki p r o t e c t e d klasy G miały w klasie K dostęp p r i v a t e ? (X) W klasie podstawowej G jest kilka funkcji składowych o przeładowanej nazwie f f f . Jedna z nich jest p r i v a t e , pozostałe mają dostęp p u b l i c . Klasa ta zostaje odziedzi czona do klasy K dziedziczeniem typu p r i v a t e . Spowodowało to, że wszystkie publi czne wersje funkcji f f f zostały odziedziczone do zakresu p r i v a t e . Czy (i jak) można sprawić, by jedna z tych publicznych funkcji f f f mimo prywatnego dziedziczenia zachowała swój dostęp p u b l i c ? Są dwie klasy podstawowe A i B. W obu z nich jest funkcja f. Klasa K dziedziczy klasę A w sposób p r i v a t e , oraz klasę B w sposób p u b ! i o. (Czyli ma dwie klasy „rodzicielskie ). Czy na rzecz obiektu klasy K można w zwykły sposób wywołać funkcje składową f ? Wytłumacz, co znaczy stwierdzenie, że konstruktóry, destruktory i operatory przypisa nia „nie są dziedziczone"? (Skoro przecież dziedziczy się wszystko). Klasa A ma operator przypisania ( public). Klasa K, która dziedziczy klasę A - nie ma swojego operatora przypisania. Czy można zatem użyć operatora przypisania wobec dwóch obiektów klasy K? Jeśli nie, to dlaczego? Jeśli tak, to który operator przypisania zadziała? Kiedy klasa podstawowa jest klasą podstawową pośrednią, a kiedy klasą podstawową bezpośrednią?
963
Rozdział. 20. Dziedziczenie Ćwiczenia
Wyjaśnij, co oznacza (metaforyczne) stwierdzenie, że przy konstrukcji obiektu klasy pochodnej, „klasa najpierw uszanuje starszych, potem swoich gości, a na końcu zajmie się sobą". Klasa A ma kilka konstruktorów (dla uproszczenia - publicznych). Klasa B dziedziczy klasę A. W klasie B jest też konstruktor. Jak sprawić, by przy konstrukcji klasy B do konstruowania części odziedziczonej wywołany został wybrany konstruktor klasy A? Jeśli w poprzednim ćwiczeniu, klasa A miałaby konstruktor domniemany - i to ten konstruktor chcielibyśmy użyć, to jakie mamy ewentualności? Czy i jakie znaczenie ma kolejność umieszczania poszczególnych pozycji na liście inicjalizacyjnej konstruktora klasy pochodnej. Czy na liście inicjalizacyjnej konstruktora klasy pochodnej można umieścić: a) wywołania konstruktorów bezpośrednich klas podstawowych b) wywołania konstruktorów pośrednich klas podstawowych c) wywołania konstruktorów składników tej klasy d) wywołania konstruktorów składników klasy podstawowej Klasa pochodna ma składniki będące obiektami innych klas i składniki typów wbudowa nych. Wyjaśnij, jak w przypadku przypisania metodą „składnik po składniku', przypi sywane są poszczególne fragmenty obiektu tej klasy. Klasa M ma operator przypisania w części pr iv a t e . Klasa K operatora przypisania nie ma. Klasa K dziedziczy klasę M. Pod jakim warunkiem mogą się odbywać przypisania obiektów klasy K? Jaka jest zasadnicza różnica między kopiowaniem metodą „składnik po składniku , a kopiowaniem „bit po bicie"? Które składniki klasy pochodnej mogą przeszkodzić kompilatorowi w automatycznym generowaniu operatora przypisania: a) składnik statyczny b) składnik będący wskaźnikiem c) składnik będący referencją d) składnik z przydomkiem
const
e) składnik z przydomkiem
volatile
f) składnik typu wyliczeniowego Kiedy konstruktor kopiujący dla klasy pochodnej nie może być generowany automatycznie? W jakimś punkcie programu może się okazać konieczne przypisanie (do istniejącego obiektu klasy K) lub inicjalizacja (nowego obiektu klasy K) treścią istniejącego już, "wzorcowego", obiektu Klasy K. W tym celu kompilator wykorzystuje operator przypisania lub konstruktor klasy K. Jeśli jednak ów wzorcowy obiekt ma przydomek c o n s t , to może się to okazać niemożliwe. Jakie wymagania muszą spełniać operator przypisania i konstruktor kopiujący, aby był to możliwe? Zdefiniuj klasę o nazwie p o d s t a w o w a , a w niej operator przypisania i składniki prywat ne i n t a l f a , i n t b e t a . Zdefiniuj klasę o nazwie p o c h o d n a , dziedziczącą klasę p o d s t a w o w a . Klasa pochodna ma mieć dodatkowo składniki d o u b l e x, d o u b l e y, oraz swój operator przypisania. W ciele tego operatora przypisania skorzystaj z operatora przypisania klasy podstawowej.
20
964
Rozdz. 20. Dziedziczenie Ćwiczenia Do klasy podstawowej z poprzedniego ćwiczenia dodaj konstruktor kopiujący. Następ nie zdefiniuj konstruktor kopiujący w klasie pochodnej korzystając z konstruktora kopiującego klasy podstawowej. Zmodyfikuj klasy z poprzednich dwóch ćwiczeń tak, by możliwe było przypisywanie lub inicjalizacja według wzoru będącego stałym obiektem klasy K (c o n s t). Czy klasa pochodna K może dziedziczyć więcej niż jedną klasę podstaw ow ą bezpośre dnio? .......................................................................... Ile nazw klas może się pojawić na liście pochodzenia. Czy nazwa jakiejś klasy może wystąpić dwukrotnie? Wyjaśnij różnicę miedzy dziedziczeniem klas A, B i C w poniższym zapisie. class
K
: public
A,
B,
C
{
};
Jeśli klasa pochodna ma dwie bezpośrednie klasy podstawowe („dwoje rodziców"), to w jakiej kolejności uruchomione zostaną konstruktory obojga rodziców? Napisz program, w którym będą trzy hierarchie. - Na pierwszą hierarchię składają się klasy: urządzenie wyświetlające, wyświetlacz wskazówkowy, wyświetlacz cyfrowy, - Na drugą hierarchię składają się klasy: miernik wielkości fizycznej, miernik tempera tury, miernik temperatury rtęciowy, miernik tem peratury termoparą, miernik prądu, woltomierz, amperomierz"(Jak widać, są w tej hierarchii dw a pokolenia dziedziczenia). - Na trzecią hierarchię składają się klasy: urządzenie sterowalne, urządzenie sterowane gałkami, urządzenia sterowane zdalnie. Z tej klasy urządzenia sterowane zdalnie pocho dzą następujące klasy: urządzenie sterowane łączem USB, urządzenie sterowane łączem szeregowym, oraz urządzenie sterowane protokołem sieciowym SOAP. Powyższe klasy możesz zdefiniować bardzo symbolicznie. Gdy to już zrobisz, to posłu gując się dziedziczeniem wielokrotnym (czyli od wielu rodziców)... a) Zdefiniuj klasę reprezentującą: woltomierz cyfrowy sterowany protokołem siecio wym SAOP, b) Zdefiniuj klasę reprezentującą: miernik tem peratury termoparowy wskazówko wy sterowany łączem USB, c) Zdefiniuj klasę reprezentującą: miernik tem peratury rtęciowy wskazówkowy (bez żadnego sterowania)
Następnych kilka ćwiczeń dotyczy zagadnień z „nadobowiązkowej” części tego rozdziału poświęconej zagadnieniu wieloznaczności (X) Wyjaśnij dlaczego w przypadku dziedziczenia wielokrotnego, może nastąpić dw u znaczność w dostępie do nazwy składnika, a nie po prostu zasłonięcie nazwy. (X) Jeśli klasa pochodna C ma dwie bezpośrednie klasy podstawowe A, B, i we wszystkich tych klasach jest składnik o nazwie f f f - to czy może nastąpić dwuznacz ność? (X) w dwóch bezpośrednich klasach podstawowych jest składnik o takiej samej nazwie. Czy kompilator uzna to za błąd? Jeśli tak, to jak nazywam y taki błąd? Jeśli nie, to kiedy w takiej sytuacji błąd może nastąpić? (X) Jak operator pozwala nam uniknąć sytuacji dwuznaczności przy dziedziczeniu wielokrotnym?
Rozdział. 20. Dziedziczenie Ćwiczenia
965
Czy przy dziedziczeniu wielokrotnym można uniknąć wieloznaczności w odnosze niu się do identycznie nazywających się składników bezpośrednich klas podstawowych a) gdy tak nazwany składnik w swojej klasie jest p r i v a t e ? b) gdy klasa podstawowa mająca taki składnik jest dziedziczona na sposób p r i v a te? (X ) Co zrobić, by ewentualne ryzyko wieloznaczności nie przenosiło się na ewentualne następne pokolenia dziedziczenia? (X ) Mamy sytuację wielokrotnego i wielopokoleniowego dziedziczenia. Klasa „ojciec ma składnik f f f . Klasa „matka" jest pochodną od klasy „dziadek ze strony matki", a ów dziadek też ma składnik o nazwie f f f ■C z y to może doprowadzić do wieloznaczności w dostępie do składnika f f f? (X ) Klasa A jest klasą podstawową dla klasy AA. Klasa Bjest klasą podstawową dla klasy BB. Klasa C jest klasą pochodną od klas A A i BB. W klasie A jest składnik o nazwie mmm. W klasie B jest także składnik o nazwie iraiun. Aby unikając dwuznaczności w klasie C, odnieść się do odpowiedniego składnika mmm - możemy posłużyć się nazwami kwalifikowanymi. (To znaczy nazwą z kwalifikatorem zakresu). Napisz cztery możliwe formy takich wyrażeń odnoszących się w klasie C do składników o nazwie mmm.
Dalsze ćwiczenia są znowu „dla wszystkich” Jeśli o jakichś pojęciach możemy powiedzieć „A składa się między innymi z M - to jak taką zależność wyrazić w programie C++? O jakichś pojęciach możemy powiedzieć: „X jest rodzajem Y". Jak taką zależność wyrazić w programie C++? O jakichś pojęciach możemy powiedzieć: „X jest rodzajem Y, a Y jest rodzajem Z . Jak taką zależność wyrazić w programie C++? O jakichś pojęciach możemy powiedzieć: „X jest rodzajem Y, a równocześnie X jest rodzajem Z, natomiast Y i Z nie mają ze sobą nic wspólnego".Jak taką zależność wyrazić w programie C++? O jakichś pojęciach możemy powiedzieć: „B składa się między innymi z dwóch M oraz czterech N, a w dodatku M jest rodzajem K. Jak taką zależność wyrazić w programie C++? Klasa K dziedziczy klasę M. W klasie M jest publiczny składnik o nazwie p. Dodatkowo w programie mamy klasę T. Również i una ma publiczny składnik o nazwie p. W klasie K składnikiem jest tablica T t a b l [ 4 ] ; Napisz wyrażenia odnoszące się w zakresie klasy K do wszystkich możliwych składników p. (Jest ich pięć). Mamy klasę K, która dziedziczv (publicznie) klasę B. Mamy funkcję globalną f, której argumentem aktualnym jest wskaźnik B*. Jakiego typu mogą być argument)' wywołania funkcji f ? Mamy klasę K, która dziedziczy (publicznie) klasę B. Mamy funkcję globalną f, której argumentem aktualnym jest referencja do B. Jakiego typu mogą być argumenty wywołania funkcji f ? Odpowiedz na postawione w poprzednich dwóch ćwiczeniach pytania, w przypadku, gdy klasa K dziedziczy klasę B sposobem p r i v a t e . Wymyśl przykład, gdy dziedziczenie na sposób prywatny odwzorowuje jakąś sytuację rzeczywistą z dziedziny Twojego hobby.
966
Rozdz. 20. Dziedziczenie Ćwiczenia Klasa M jest dziedziczy (publicznie) klasę E. Dwuargumentowy o p e r a to r + zrealizo wany na użytek dwóch obiektów klasy E może być: a) funkcją składową klasy E, b) funkcją globalną (lub z jakiejś przestrzeni nazw). Konwersja standardowa dopuszcza, ze taki operator może w pewnych warunkach pracować również dla obiektów klasy M. 1) Jak zrealizować tę funkcję operatorową, żeby możliwe było postawienie obiektu klasy M po prawej stronie tego operatora 2) Jak zrealizować tę funkcję operatorową, żeby możliwe było postawienie obiektu klasy M po lewej stronie tego operatora Napisz deklaracje tych wariantów funkcji operatorowej. (Przyjmijmy, że typ rezultatu to M ).
W programie definiujemy obiekt klasy podstawowej X i inicjalizujemy go obiektem klasy pochodnej V. Dzięki jakiej konwersji standardowej jest to możliwe? Mamy klasę X i pochodną od niej klasę Y. Jest funkcja v o id f (X). Czy możemy wywołać ją z argumentem będącym obiektem klasy Y? Jakie będą następstwa? W programie jest klasa X i dziedzicząca ją (publicznie) klasa S. Istnieje funkcja g, której argumentem formalnym jest tablica obiektów klasy X. Czy można funkcję tę wywołać z argumentem będącym tablicą elementów klasy S? Jeśli można, to jakie będą konsekwen cje? Jeśli nie można, to dlaczego?
Dalsze ćwiczenia są z „nieobowiązkowej” części rozdziału. (X) Czy (na mocy konwersji standardowej) wskaźnik do pokazywania na składnik klasy podstawowej może zostać zamieniony na wskaźnik do pokazywania na składnik klasy pochodnej? A może odwrotnie? (X) Czy można na liście pochodzenia klasy dwukrotnie na napisać tę samą nazwę klasy?
(y) Czy na liście pochodzenia klasy można napisać nazwę klasy, która już raz wystąpiła w hierarchii dziedziczenia? (X) Na czym polega ryzyko wieloznaczności w przypadku dziedziczenia? O wieloznaczność czego tu chodzi? (X) Jeśli w hierarchii dziedziczenia nigdy nie powtórzyliśmy dwukrotnie tej samej klasy, czy jest ryzyko wieloznaczności? (X) Klasa K ma składnik statyczny o nazwie sss. Klasa M dziedziczy klasę K. Klasa H także dziedziczy klasę K. Z kolei klasa R dziedziczy od obu klas: H i M. Czy wolno się w klasie R odnieść do składnika sss? Czy mamy do czynienia z wieloznacznością? (X) Podaj przykład, kiedy może być korzystne dziedziczenie wirtualne.
(X) Dokończ następujące zdanie: Dziedziczenie wirtualne: a) powiększa ryzyko wieloznaczności, b) pomniejsza ryzyko wieloznaczności, c) nie ma wpływu na problem wieloznaczności
(X> Która klasa jest sprawia, że dziedziczenie klasy K jest wirtualne? a) - klasa najbardziej pochodna od klasy K b) - klasa, dla której klasa K jest bezpośrednią klasą podstawową.
Rozdział. 20. Dziedziczenie Ćwiczenia
967
(X) Jeśli w hierarchii dwukrotnie odziedziczono tę samą klasę K na sposób wirtualny, z tym, że raz jest to dziedziczenie prywatne, a innym razem publiczne, to: -a) Następuję blad, bo najpierw sprawdzana jest dwuznaczność, a potem dopiero dostęp. -b) Składniki klasy K w obiekcie klasy najbardziej pochodnej mają dostęp p u b lic . -b) Składniki klasy K w obiekcie klasy najbardziej pochodnej są niedostępne. (X) Podaj poprawne dokończenia następującego zdania. Jeśli definiujemy obiekty klas z hierarchii, w której były jakieś klasy podstawowe dziedziczone wirtualne, to wywołania tych konstruktorów klas wirtualnych (umieszczone na listach pochodzenia klas najbardziej pochodnych) spowodują, że konstruktory klasy wirtualnych wykonane zostaną: a) tuż przed wykonaniem konstruktorów klasy najbardziej pochodnych, b) przed wykonaniem konstruktorów wszystkich innych klas podstawowych, c) Wirtualność dziedziczenia nie ma wpływu na kolejność pracy konstruktorów. (X) Na czym polega zjawisko dominacji nazwy składnika w hierarchii?
968
Rozdz. 21. Funkcje w irtualne
Funkcje w irtualne rozd ziale tvm m ów ić będziem y o w sp a n iały m u k o ro n o w a n iu eałegc m echanizm u dziedziczenia czyli o tzw . funkcjach w irtu a ln y c h . To one w łaśn ie spraw iają, że p ro g ram jest n a p ra w d ę ob iek to w o o rie n to w a n y - inacze; m ów iąc: orientuje się w ed łu g typu obiektów . Brzmi to b ard zo patety czn ie, jednak sp ra w a jest b ard zo p ro sta. Dla przy k ład u
W
na poniższą
y
Rys. 21-1
in s tr u m e n t
t
\
bęben
trą b k a
fo r t e p ia n
___ -__
A oto realizacja tych klas. W zasadzie nie m a w nich nic szczeg ó ln eg o poza jed n y m słów kiem v i r t u a l p rzed funkcją sk ład o w ą w k lasie i n s t r u m e n t . ^ (Jeśli czytałeś końcowe paragrafy poprzedniego rozdziału, to nie daj się zmylić - słrnoo to nie stoi tu na liście pochodzenia - zatem nie ma nic wspólnego z poprzednim znaczeniem). Tutaj po staw iłem to słow o p rzy deklaracji funkcji sk ładow ej. /
*
—
----------------------—
--------------------------------------------- —
plik:
—
--------------------
"
instrume.h
____________________________ ____________________ ___________________ * / #include
m M m SW IiU m m iiim class
11
instrument
int
cena;
public:
virtual {
cout
void «
"
w y d a j _ d z w i e k () Nieokreślony
brzdęk
!\n";
969
Rozdział. 21. Funkcje w irtualne
} ił
...
class
trąbką
: public
instrument
//
©
i public: void
w y d a j _ d z w i e k ()
i cout
// class
«
"
Tra-ta-ta
!\n";
• • -
beben
: public
instrument
//
{ public: void
w y d a j _ d z w i e k ()
( cout
) //
<<
"
Bum-bum-bum
!\n";
. . .
class
fortepian
: public
//
instrument
&
{ public: void
w y d a j _ d z w i e k ()
{ cout
) ił
<<
"
Plim-plim-plim
!\n";
...
}iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii P o w y ż sz y ko d u m ieszczam y w pliku i n s t r u m e . h - po to, by m óc później d o niego jeszcze w racać. N ie m a to nic w spólnego z tem atem tego ro zd ziału . R ów nie d o b rz e m oglibyśm y to zrobić po starem u. Skoro jednak m am y to już w pliku n ag łó w k o w y m , to w ystarczy dyrektyw a i n c l u d e , która w łączy nam ten tekst w p o n iż sz y program . #include void
" i n s t r u m e . h"
m u z y k (instrument
&
powierzony
instrument);
y**»***************************************************** int
*★ * * /
m a i n ()
{ instrument trąbką fortepian beben cout
<<
zlota_trabka; steinway_giseli; moj werbel;
"Zwykle "na
// ©
jakis_instrument;
wywołania
rzecz
obiektów
funkcji -
jak
jakis_instrument.wydaj_dzwiek() ; z l o t a _ t r a b k a .w y d a j _ d z w i e k () ; s t e i n w a y _ g i s e l i . w y d a j _ d z w i e k () ; m o j _ w e r b e l .w y d a j _ d z w i e k () ;
skladowych\n" dotąd
nic
szczegolnego\n"; //
O
970
Rozdz. 21. Funkcje w irtualne
cout
«
"Wywołanie
funkcji
"pokazywanego instrument
na
rzecz
wskaźnikiem
obiektu
\n"
instrumentu\n";
*wskinstr;
// ©
// ustawieńic wskaźnika wskinstr
=
wskinstr->
cout
<<
&
jakis_instrument;
w y d a j _ d z w i e k () ;
"Rewelacja "pokazaniu " na
obiekty klasy
klas
pochodnych\n"
instrument
!
\n";
&zlota_trabka;
//
©
wskinstr->
wydaj_dzwiek 0 ;
//
©
wskinstr = wskinstr->
&steinway_giseli; w y d a j _ d z w i e k {) ;
wskinstr = wskinstr->
&moj_werbel; w y d a j _ d z w i e k () ;
cout
<<
=
// 0
okazuje sie p r z y " wskaznikiem\ndo instrumentów'
" od wskinstr
// 0
"Podobne zachowanie jest także "w s t o s u n k u d o r e f e r e n c j i \n";
\n"
m u z y k (jakis_instrument); muzyk(zlota_trabka); m u z y k ( s t e i n w a y _ g i s e l i ); muzyk(moj_werbel);
}
/ ★ ★ ★ ★ * ★ ★ * ★ + * * ★ * ★ ★ * * * * * * ★ * * * * ★ ★ * * ★ ★ * * ■ * * * * ★ * ★ * * * * * * * ★ ★ ★ ★ * * ★ ★ * * ■* * / void
muzyk(instrument
&
powierzony_instrument)
{ powierzony
i n s t r u m e n t .w y d a j _ d z w i e k ( );
Po uruchomieniu programu, na ekranie zobaczymy: Zwykle wywołania funkcji składowych n a rzecz o b i e k t ó w - jak dotąd n ic s z c z e g ó l n e g o Nieokreślony brzdęk ! Tra-ta-ta ! Plim-plim-plim Bum-bum-bum ! Wywołanie funkcji na pokazywanego
! rzecz
wskaźnikiem
obiektu
instrumentu
Nieokreślony brzdęk ! Rewelacja okazuje sie przy pokazaniu wskaźnikiem do instrumentów na obiekty klas p o c h o d n y c h od
klasy
instrument
!
Tra-ta-ta ! Plim-plim-plim ! Bum-bum-bum ! Podobne zachowanie jest także w stosunku do referencji Nieokreślony brzdęk !
//
o o
971
Rozdział. 21. Funkcje w irtualne
Tra-ta-ta ! Plim-plim-plim ! Bum-bum-bum !
G d y b y śm y jed n ak z tego n aszeg o program u u s u n ę li słow o v i r t u a l , stojące p rz y fu n k cji w y d a j d z w i e k w klasie p o d staw o w ej i n s t r u m e n t ,
wówczas na „drugim" ekranie pojawi się coś takiego: Zwykle wywołania funkcji składowych na r z e c z o b i e k t ó w - j a k d o t ą d nic s z c z e g ó l n e g o Nieokreślony brzdęk Tra-ta-ta !
!
Plim-plim-plim ! Bum-bum-bum ! W y w o ł a n i e f u n k c j i na r z e c z o b i e k t u pokazywanego wskaźnikiem instrumentu Nieokreślony brzdęk ! R e w e l a c j a okazuje sie p r z y pokazaniu do od
instrumentów klasy
na
obiekty
instrument
klas
wskaźnikiem
pochodnych
!
Nieokreślony brzdęk ! Nieokreślony brzdęk ! Nieokreślony brzdęk ! Podobne zachowanie jest także w s tosunku do referencji Nieokreślony brzdęk !
Nieokreślony brzdęk ! Nieokreślony brzdęk ! Nieokreślony brzdęk !
jak w id ać, teraz obiecana rew elacja nie nastąpiła...
Porozmawiajmy O W p o w y ższy m program ie m am y d o czynienia z czterem a klasam i. Jedna z nich ( i n s t r u m e n t ) jest klasą p odstaw ow ą. Z niej b ezp o śred n io w y w o d zą się trzy klasy p o ch o d n e 0 . Dla p ro sto ty w każdej z nich je st tylko jeden sk ład n ik funkcja składow a w y d aj d ź w i ę k . W klasie p o d staw o w ej i n s t r u m e n t p rz e d deklaracją tej funkcji stoi tajem nicze słowo v i r t u a l . © P rzejd źm y teraz do analizy funkcji m ain . N ajp ierw w idzim y tutaj definicje czterech obiektów tych klas. O N astęp n ie na rzecz tych obiektów w yw ołujem y funkcję wyda j _ d z w i e k. F u n k cji takich jest w naszym pro g ram ie cztery (po jednej w każdej klasie). Dla k ażd e g o obiektu w yw ołana zostanie ta wersja funkcji, która jest sk ładnikiem klasy, d o której obiekt należy. Słowem: jego w łasna. I rzeczyw iście: na ekranie w id zim y potw ierdzenie - k a ż d y obiekt o d zy w a się charakterystycznym dla siebie głosem. Nic w tym n ad zw y czajn eg o - to w sz y s tko zn a m y ju ż od daw na. O gólnie w ięc mówiąc: do tej po ry mieliśmy w y w o łan ia funkcji w taki sposób o b ie k t .w y d a j d ź w i ę k ()
21
972
Rozdz. 21. Funkcje w irtualne
w ięc ko m p ilato r sp ra w d z a ł do jakiej klasy należy obiekt i u ru c h a m ia ł dla niego funkcję sk ład o w ą z w łaściw ej mu klasy. © A teraz uw aga! B ędziem y posługiw ać się w sk aźn ik iem . P rzeczy tajm y jego definicję: w s k i n s t r jest w skaźnikiem d o p o k azy w an ia na o b ie k ty klasy i n stru m e n t.
B ardzo w ażn e jest, że ten w sk aźn ik jest w sk a źn ik ie m d o pokazy w an ia n a instrumenty, a nie w sk aźn ik iem d o p o k azy w an ia na trąbki, fortep ian y czy bębny. B ędziem y się zatem po słu g iw ać zapisem w sk aźn ik o w y m . P rzy p o m in am :
uskaźnik-> funkcja_$kładowa(); © N ajp ierw w ięc u sta w ia m y w skaźnik tak, by p o k azy w ał na j a k i s _ i n s t r u m e n t i... © ...w yw ołujem y funkcję. Jak się spo d ziew aliśm y , u ru c h o m io n a zo staje funkcja w y d a j d ź w i ę k b ęd ąca składnikiem klasy i n s t r u m e n t . D o w o d e m na to jest „N ieo k reślo n y b rz d ę k ". © N a d c h o d z i jed n ak rzecz niesłychana. U staw iam y w sk a źn ik d o in stru m en tu tak, b y p o k azy w ał na o b iek t klasy t r ą b k ą . C zy w ogóle w olno n am było tak zrobić? Tak, w o ln o było w myśl konw ersji sta n d a rd o w e j, o której m ów iliś m y w p o p rzed n im rozdziale: w sk a źn ik d o o b iek tu klasy p o d staw o w ej nadaje się tak że d o p o k aza n ia na o b iek t klasy pochodnej. W końcu trąbka - to ta k że in stru m en t. B ardziej form alnie: w sk a źn ik klasy p o ch o d n ej m o że być niejaw n ie zam ieniony n a w sk aźn ik do je d n o zn aczn ie dostępnej klasy p o d staw o w ej. - O to , dlaczego w sk a źn ik o w i in stru m en tu m ogliśm y p rz y p isa ć a d re s trąbki. Jed n o zn aczn o ść jest tu zap ew n io n a, bo jest ty lk o jed n a klasa pod staw o w a. D ostęp n a jest o n a także, bo klasy t r ą b k ą , f o r t e p i a n , b e b e n dzied ziczą ją publicznie. T y p w skaźnika jest in n y niż obiektu, ale n ie je s t z u p e łn ie ró ż n y od typu obiektu. Łączą ich w sz ak w ięzy rodzinne. Tego p o k re w ie ń stw a w y m ag am y w ty m rozdziale. © S k o ro w sk aźn ik jest ju ż u staw io n y , to zn o w u w y w o łu jem y funkcję. Jest to linijka będąca kw intesencją tego ro zd ziału . P y tan ie za sto złotych:
Którą funkcję składową wyda j_dzwiek uruchomi teraz kompilator ? Dlaczego? O d p o w ie d ź na pytan ie: "którą?" - jest p ro sta - w y starczy sp o jrzeć na ekran. Z ostała u ru ch o m io n a funkcja z klasy trąb k a. T ru d n iej jest o d p o w ied zieć na pytanie: "dlaczego?". P rześled ź m y dw ie m o żliw e d ro g i ro zu m o w an ia k o m p ilato ra. M am y d o czynienia z zap ise m w skaźn ik -> fnnkcja( )
Rozdział. 21. Funkcje w irtualne
973
P rzy p o m n ijm y , ż e w sk a źn ik to ad res jakiegoś m iejsca w pam ięci, p lu s w ied za o typie ob iek tó w , d o k tó ry ch p o k azy w an ia ten w sk a ź n ik służy. N asz w sk aźn ik w ie o tym , że słu ży d o p o k azy w an ia na obiekty k lasy i n s t r u m e n t .
Rozumowanie a) K o m p ilato r w id zi, że m a d o czynienia ze w sk aźn ik iem d o p o k a z y w a n ia na i n s t r u m e n t y . O bok tego w sk aźn ik a stoi w y w o łan ie jakiejś funkcji składow ej. Sięga w ięc „n a ślep o " d o k lasy i n s t r u m e n t i u ru c h a m ia tę funkcję. K om p ilato r u ru ch o m ił w ięc funkcję w łaściw ą ty p o w i w skaźnika.
Rozumowanie b) K o m p ilato r w id zi, że ma d o czynienia ze w sk aźn ik iem d o in stru m en tu . N ie sięga na ślep o d o klasy in stru m en t, tylko u ży w a swojej inteligencji: n ie daje się zw ieść ty p e m w sk aźn ik a, tylko sp raw d za, n a co on pokazuje. O rien tu je się n aty ch m iast, że w sk aźn ik p o kazu je aktu aln ie n a obiekt klasy t r ą b k ą zatem : o rie n tu ją c się w e d łu g ty p u o b ie k tu u ru ch am ia funkcję w łaściw ą p o k a z y w a n e m u obiektow i. (A nie w łaściw ą typow i w sk aźn ik a).
Które z tych rozumowań jest poprawne, a które bez sensu? O b y d w a są p o p raw n e. Co zatem spraw iło, że k o m p ilato r w ybrał w a ria n t b)? S praw ił to p rzy m io tn ik v i r t u a l um ieszczony w deklaracji funkcji składow ej w y d a j _ d z w i e k w klasie po d staw o w ej i n s t r u m e n t . Z p rzy m io tn ik iem v i r t u a l już się sp o tk aliśm y p rzy k lasach dzie d ziczonych w irtualnie. (Był lo materiał nadobowiqzkoun/ )C). M ów i łem w te d y , że m ożna sobie to sło w o w w olnym p rzek ład zie zastąpić z w ro te m „inteligentnie d zied zic zo n a". P o d trzy m u ję to. M am też takie odległe skojarzenie. W irtu o z -p o c h o d z i od w łoskie go virtuoso: uczony, w praw ny. Z atem m echanizm w irtu aln o ści to w y jątk o w o w y szu k an y sposób d ziedziczenia. N a p ra w d ę słow o wirtualny znaczy: (teoretycznie) m o żliw y , m o gący zaistnieć. I słu szn ie -fu n k c ja o zn aczo n a jako w irtu aln a, m oże (bo nie m usi) być zrealizow ana w klasach pochodnych jeszcze raz, lepiej, dokładniej. P o staw ien ie tego p rzy m io tn ik a koło funkcji w klasie podstaw ow ej m ów i, że od tej p ory, p o w szystkie d alsze pokolenia - ko m p ilato r ma uży ć swojej inteligencji, g d y c h o d z i o w y w ołania tejże funkcji na rzecz obiektu p o k azy w an eg o przez w skaźnik^. 1)
a także - jak się za chwilę przekonamy - na rzecz obiektów, do których odnosimy się za pomocą referencji.
974
Rozdz. 21. Funkcje w irtualne
Z au w aż, że g d y usu n ęliśm y z program u to słow o v i r t u a l - to ko m p ilato r nie p o stąp ił już tak inteligentnie. R ozum ow ał w ed łu g w aria n tu a). W idzim y to na d ru g im ekranie. Z au w aż też, że jeśli funkcję w yw ołujem y na rzecz k o n k re tn e g o obiektu - stosując zap is obiekt.funkcja() to kom pilator zach o w u je się zaw sze po p raw n ie. D latego, że tu ta j nie w ym aga się od niego żad n ej inteligencji: ma obiekt klasy X i u ru c h a m ia fu nkcję składow ą k lasy X. Zaw sze! Inteligencja jest mu p o trzebna tylko w ted y , g d y n a o b iek t p o k azu jem y w skaźnikiem , a typ w skaźnika jest in n y n iż ty p p o k azy w a nego obiektu. N a n aszy m drugim ekranie w idzim y, że tu taj k o m p ilato ro w i za b rak ło tej inteligencji. Z ab rak ło bow iem tego sło w a virtual przy d ek laracji funkcji składow ej. Funkcja nie b y ła „inteligentnie d zied ziczo n a". W ró ćm y je d n a k do n a s z e g o program u
W idzim y , że w sk a ź n ik do pokazyw ania n a i n s t r u m e n t y u s ta w ia n y jest na obiek ty klasy f o r t e p i a n lub beben i - p o d o b n ie jak p o p r z e d n i o - kom pilator u ż y w a swojej inteligencji, by u ruchom ić w łaściw e funkcje. © W dalszej części m a in przekonam y się, ż e podobnej intelig en cji kom pilator u ż y je przy w y w o ły w an iu funkcji sk ład o w ej na rzecz o b ie k tu określanego przezw isk iem (czyli referencją). Referencja - jak p a m ię ta m y - to jakby inna fo rm a określenia a d re su . A b y było b ard ziej pouczająco, tym razem nie d efin iu je m y sobie luźnej referencji, lecz d efin iu jem y funkcję g lobalną, która p rz y s ła n y d o niej obiekt o d b ie ra w łaśnie p rz e z referencję, o o O to ta funkcja. void muzyk (instrument &
powierzony_instrument)
Jest to funkcja g lo b aln a, której arg u m e n tem form alnym jest referencja obiektu k lasy instrument. W iem y od d aw n a —czyli z p o p rz e d n ie g o rozdziału —że arg u m e n tem w y w o łan ia takiej funkcji m oże być ró w n ie ż obiekt klasy p ocho d n ej od k lasy instrument. Konwersja standardowa zam ień i przecież obiekt (ogóln iej: l-wartość) klasy pochodnej na referencję do obiektu klasy podstawowej - a wtedy typ argumentu będzie się już zgadzał. Funkcja m a n a z w ę muzyk, a p rzy słan y d o niej obiekt o d b ie ra n y jest jako refe rencja o n azw ie p o w i e r z o n y instrument. To na rzecz tak przezyw anego obiektu w yw oła się tu taj funkcję sk ład o w ą wyda j_dzwiek. W yw ołanie © to w łaśn ie w y w o łan ie tej funkcji z a rg u m e n te m aktualnym będący m ob iek tem k lasy instrument. muzyk(jakis_instrument);
Rozdział. 21. Funkcje w irtualne Polimorfizm
975
Tu nic nas nie d ziw i: o b ie k t jest klasy instrument, referencja d o niego jest ta k ż e klasy in stru m e n t. M u z y k uruchomi w ięc funkcję wyda j _ d z w i e k z klasy instrument.
O © O to w y w o łu jem y fu n k cję muzyk z a rg u m e n te m b ęd ący m o b iek tem klasy trąbką.
muzyk(zlota_trabka); D zięki w sp o m n ian ej konw ersji sta n d a rd o w e j na o b iek t k lasy trąbką w e w n ą trz funkcji m u z y k - m ów i się p rzez w isk iem p o w i e r z o n y _ i n s t r u m e n t b ęd ący m , jak w iad o m o , p rzezw isk iem i n s t r u m e n t u (a nie trąbki!) . W e w n ą trz funkcji m u z y k n ap o ty k am y instrukcję powierzony_ir.strument.wydaj_dzwiek () ; //rcfercncja.funkcja( ) K o m p ilato r n ap o ty k a te n fragm ent kodu i się zastanaw ia: Referencja, czyli przezioisko, które widzę jest przezwiskiem nadawanym i n s t ru m sn tom. Obok tego przzezwiska stoi wywołanie funkcji składo wej. Normalnie —myśli sobie kompilator —nie zastanawiałbym się wcale, fest przezwisko in s tr u m e n t u, więc natychmiast uruchomiłbym funkcję składoioą w yda j _ d z w i e k z klasy i n s t r u m e n t . Pamiętam jednak, że tam, przy jej deklaracji, stoi słowo v i r t u a l , czyli pouczenie, że mam być teraz inteligentniejszy. Patrzę więc na obiekt przezwany tym przezwiskiem i orientuję się, że tak naprawdę, to przezwi sko owo nadano obiektowi klasy pochodnej tr ą b k ą . Uruchamiam więc funkcję w y d a j_ d z w ie k charakterystyczną dla klasy tr ą b k ą . D o w o d e m tego jest o d p o w ie d n i w ypis na ek ran . O © G d y za chw ilę m u z y k o w i pow ierzam y s t e i n w a y giseli, to - p rz y przesła n iu a rg u m e n tu d o fu n k cji - zostanie on ró w n ież o d eb ran y p o d przezw iskiem p o w i e r z o n y instrument. Znow u k o m p ilato r m usi p ow ziąć decyzję. Ten sam frag m e n t kodu zro z u m ia n y zostanie teraz jako w yw oła nie funkcji w y d a j _ d z w i e k z k lasy fortepian.
21.1
P o lim o rfiz m G d y b y nie było słów ka virtual przy deklaracji funkcji wyda j _ d z w i e k w kla sie instrument, to w opisanej sytuacji zaw sze w yw o ły w an a by była funkcja sk ła d o w a z klasy i n s t rument - ta w ypisująca na ekranie nieokr e ś l o n y brzdęk.
Z atem ten frag m en t k o d u w środku funkcji m uzyk: referencja . wydaj d ź w ię k ();
w k tó ry m o d b y w a się w yw ołanie funkcji w irtu aln ej - w y k azu je różne formy. C zasem referencja . i n s t r u m e n t : :wyda j dzwiek () ;
czasem
976
Rozdz. 21. Funkcje w irtualne Polimorfizm re fe re n c ja .trąbką::wyda j_dzwiek
() ;
czasem referencja .fortepian ::wyda j_dzwiek () ;
W ielość-form to z greki: poli-m orfizm . M ó w im y , że w y stą p ił tu polim o rfizm . T en fragm ent kodu, g d zie jest w y w o łan ie funkcji muzyk, z m ie n ia się tak, że raz o d p o w iad a w y w o łan iu tego, raz tam teg o . K lasa, w której jest zdefiniow ana (lub o d zied zic zo n a p o p rzo d k ac h ) funkcja w irtu aln a, n azy w a się klasą polim orficzną. D ygresja To ten frag m en t kodu, w k tó ry m w yw ołuje się funkcję w irtualną, w y k azu je polim orfizm (w ielość form ). Sam a funkcja w irtu aln a nie. Z atem nie funkcja jest p o lim o rficzn a - to jej w y w o łan ie jest takie! A le to jeszcze nie koniec dziw ów . W y o b raź sobie, że ja tę funkcję muzyk skom p ilo w ałem i um ieściłem w bibliotece. M am ju ż ją n a d y sk ietce w postaci binarnej. O d d aję ją notariuszow i na p rzec h o w an ie. Ty, d ro g i C zy teln ik u , jesz cze nie um iesz d o b rz e p rogram ow ać w języ k u C++, ale w k ró tc e się nauczysz. P ew nego d n ia zdefiniujesz sobie klasę, k tó ra b ęd zie klasą p o ch o d n ą od mojej k lasy instrument. N a p rzy k ład tak: class czytelnikofon : public instrument { public: void wydaj_dzwiek() { cout « " Jazzy-jazzy
D M ając plik n ag łó w k o w y mojej klasy i n s t r u m . h oraz ty lk o w ersję binarną mojej fu n k cjim u zy k w y w o łasz ją w sw o im p ro g ram ie dla sw ojego instrum entu #include "instrum.h" main() { czytelnikofon czczcz; muzyk(czczcz); Jeśli skom pilujesz Tw ój p ro g ram oraz z lin k u jesz go z moją funkcją muzyk, to w rezultacie na ek ra n ie zobaczysz Jazzy-jazzy ! W ynika stąd , że funkcja muzyk, k tó rą zło ży łem u n o ta riu sza - by dać Ci pew ność, ż e nic w niej nie zm ien iam - za re a g o w a ła p o p ra w n ie w yw ołaniem
Twojej funkcji składow ej. Z aw arta w niej in strukcja powierzony_instrument.wydaj_dzwiek();
977
Rozdział. 21. Funkcje w irtualne Polimorfizm zam ie n iła się tak, że m a teraz formę: powierzony_instrument.czytelnikofon ::wydaj_dzwiek() ;
S tało się to w śro d k u fu n k cji muzyk, m im o że w m om encie, g d y ją p isałem istn ia ł jeszcze an i Tw ój p ro g ram , ani definicja k lasy czytelnikofon.
nic
Jeśli m iałeś kied y ś d o czy n ien ia z p ro g ram o w an iem w asem blerze, to jeszcze b a rd z ie j docenisz ten c u d . Mój kom pilator p racując nad ciałem funkcji m uzyk m u s ia ł tę linijkę w y w o ła n ia funkcji w y d a j d z w ic k zam ien ić n a instrukcję „sk o c z d o p o d p ro g ra m u " (Jum p t o S u b r o u t i n e ) JSR
a d r e s _ iu ła ś c iw c j_ fn n k c ji
P o n ie w a ż tych w łaściw y ch funkcji m ogło być te ra z kilka, w ięc w su m ie p ow sta ło co ś - co, w zap isie p o d o b n y m d o C++, p rzed sta w iało b y się następująco: ( r e f - oznacza u nas w skrócie referencję) i f ( r e f określa o b ie k t k la s y in s tr u m e n t ) { ref.instrument::wydaj_dzwiek(); } else if(ref
o k re śla o b iekt k la sy
fortepian)
{
r e f . f o r t e p i a n : : w y d aj_ d zw ie k ( ) ; } e l s e i f ( r e f o kreśla o b ie k t k la s y tr ą b k ą ) { r e f . t r ą b k ą : : w y d a j_ d z w ie k ( ) ; } _ e l s e i f ( r e f o k re śla obiekt k la sy b e b e n ) ( r e f . b e b e n : : w y d a j_ d z w ie k () ; } return;
Jeśli n a w e t kom p ilato r zrobił to w łaśnie tak, to zrobił to raz na zaw sze — p o ró w n u jąc klasę obiektu ze znanym i mu klasam i p o chodnym i klasy i n s t r u m e n t . W tym fragm encie kodu nie ma jednak miejsca na p o ró w n an ie z klasą, k tó rą Ty m oże jeszcze kiedyś napiszesz. Po skom pilow aniu przep ad ło ! A je d n a k m usi być to zro b io n e jakoś sprytniej. To sk o m p ilo w an e ciało funkcji - zachow uje się tak, jakby miało tam jeszcze
m u z y k
else if(ref
o kreśla obiekt k la sy czytelnikofon)
{ ref.czytelnikofon::wydaj_dzwiek();
> A ta k że, jakby m iało frag m en t porów nujący z klasam i, o których n a w e t nie w iesz, bo je w ym yślisz d o p iero dzisiaj po kolacji.
978
Rozdz. 21. Funkcje w irtualne Polimorfizm
Dlaczego to takie ważne? Oczywiście jest to efektowna sztuczka, ale jaki z niej pożytek? Taki m ianow icie, że jeśli mam y gotow y działający p ro g ra m o p eru jący na kia sach, a w p ew n y m momencie pojawi się konieczność ro z sz e rz e n ia tego p ro g ra mu o now e obiekty, to dodanie now ej klasy będącej k lasą p o ch o d n ą o d już istniejącej - nie w ym aga zm ian w tysiącach miejsc p ro g ra m u , w szęd zie tam, gdzie decyduje się jakiej to klasy jest obiekt, p o k a z y w a n y w sk aźn ik iem lub przezy w an y referencją. P rzykładow o p iszem y program na obsługę różnych u rz ą d z e ń elektronicznych. Jest ich p o w ied zm y 100 typów . Jeśli jednak zechcem y d o d a ć ty p 101, k tó ry jest pochodny od jakiegoś już istniejącego, to w tych m iejscach w p ro g ram ie, gdzie następują decyzje: „jeśli to ten typ, to... —nie m usim y nic zm ien iać. N aw et jeśli d o d am y następne 500 typów ! Jest jednak w a ru n e k : te now e klasy (typy) u rząd zeń m uszą być pochodnym i od klas już istniejących. Było to bardzo ogólnikow e tłum aczenie. Do spraw tych jeszcze w rócim y. Po znaliśm y jednak pew ną specyficzną cechę języka C++. N a z y w a m y ją ro zsze rzaln o ścią (ang. extensM ity). Program , w razie p o trze b y m odyfikacji, m ożna łatw o rozszerzać o now e klasy - bez konieczności g ru n to w n y c h zm ian reszty kodu. Pozostała część kodu potrafi na te now e klasy p o p ra w n ie reagow ać. Jest to cecha b a rd z o w ażn a - gdy pisze się program w ied zą c, że b ęd zie on w przyszłości ciągle m odyfikowany.
Nic za darmo Skoro k o m pilator zachow uje się tak inteligentnie w sto su n k u d o funkcji w irtu al nych, to dlaczego nie u ży w a swojej inteligencji zaw sze, w sto su n k u do każdej funkcji składow ej? Inaczej mówiąc: dlaczego w szystkie funkcje nie są w irtu a ln e a priori? P ow ó d jest p ro zaiczn y - za w irtualność się płaci: • •
jeśli w klasie jest fu nkcja w irtu aln a, to o b iek t tej klasy jest nieco w iększy, podejm ow anie decyzji o tym , którą fu n k q 'ę w d an y m p rz y p a d k u w ykonać, trw a p ew ien d o d atk o w y czas.
Ten m echanizm n ie zaw sze jest nam po trzeb n y , d lateg o ak ty w o w an y jest tylko na nasze życzenie. Klasy bez funkcji w irtualnych są (nieco) oszczędniejsze, jeśli chodzi o zużycie miejsca. Ich funkcje składow e - w p rz y p a d k u , gdy w y w o łu jem y je dla obiektów pokazy w an y ch w sk aźn ik am i - w yw ołują się szybciej, n iż g d y b y ta sam a fun kcja została ok reślo n a jako w irtualna Są języki p ro g ram o w an ia (np. Smalltalk-80), gdzie w sz y stk ie fu nkcje są, z defi nicji, w irtualne. C ++ p o zw ala program iście na św iad o m y w ybór.
Rozdział. 21. Funkcje w irtualne Typy rezultatów różnych realizacji funkcji w irtualnej
979
21.2 Typy rezultatów różnych realizacji funkcji wirtualnej Z dotychczasow ych ro z w a ż a ń w iem y już, że funkcja w irtu a ln a to specjalny rod zaj funkcji sk ład o w ej. M echanizm w y w o łan ia jej, jako funkcji w irtualnej, u ja w n ia się tylko, g d y w yw ołujem y ją na rzecz referencji lu b w sk a źn ik a do obiek tu klasy p o d staw o w ej. O tym , którą w ersję funkcji w yw ołać, d ecy d u je nie ty p w sk aźn ik a (referencji), ale to, na co one w łaśn ie po k azu ją. (W zw ykłych funkcjach byłoby o d w ro tn ie). C ały m ech an izm fu n k cji w irtu aln y ch sto so w a n y jest p rzez k o m p ila to r niejaw nie. U w alnia on w ięc p ro g ram istę od n iep o trzeb n eg o w y siłk u . P rogram ista m yśli w ted y , co trz e b a zrobić, a nie jak to zrobić. W naszej funkcji m u zyk napisaliśmy tylko co zrobić: w y d a j_ d z w ie k . Kompilator sam decydował ja k na pokazywanym instrumencie wydać dźwięk. O d su n ięcie tych d ecy zji od używ ającego klasy p ro g ram isty - jest jak b y w y szu k an ą form ą en k ap su lacji. Funkcja sk ład o w a je st w irtu a ln a w ted y , g d y w definicji klasy p rzy jej deklaracji sto i słow o v i r t u a l , lu b g d y w jednej z klas p o d staw o w y ch tej k lasy id en ty czn a funkcja z a d e k la ro w a n a jest jako v i r t u a l . Id en ty c zn a - to z n a c z y z identyczną n azw ą, a rg u m e n tam i i o d p o w ied n im ty p e m re z u lta tu . (M ó w im y : o identycznej sy g n atu rze). M usi b y ć w ięc do k ład n e d o p aso w an ie. Jeśli ta k nie jest, to funkcja z k lasy p ochodnej nie jest funkcją w irtu a ln ą .
Co to znaczy: "o d p o w ie d n i" typ re z u lta tu ? T y p rezu ltatu nie m o ż e być oczyw iście d o w o ln y . W n ajp ro stszy m p rz y p a d k u typ rezultatu w funkcji w irtu aln ej zdefiniow anej w klasie pochodnej - jest taki sam , jak typ rezu ltatu w klasie p o d staw o w ej. Tak jest najczęściej. O k azało się jed n ak , ż e czasem jednak b y łoby to trochę krępujące. Dlatego d o p u sz c z o n e zo stały te ż niew ielkie różnice: to zn aczy realizacja funkcji w irtual nej w klasie p o ch o d n ej m oże mieć ty p re zu ltatu nieco inny, niż ta realizacja z k lasy p o d staw o w ej. T y p rezultatu m o że być w ięc in ny, byle był... tzw. "kowariantem".2 (To co ś w rodzaju bliskiego krew nego). N ie p rzerażaj się, s p ra w a jest raczej p ro sta i m o ż n a by w cale tego straszliw ego ' sło w a „ k o w aria n t" tu , w Symfonii, nie w p ro w a d z a ć - g d y b y nie to, że czasem k o m p ilato r m oże u ż y ć w łaśn ie tego słow a (ang. cooariant), ab y p o w iad o m ić Cię, iż p rz esad ziłeś ze s w o b o d ą , bo on aż tak w ielk ą różnicę w ty p ach rezultatu T w o ich funkcji w irtu a ln y c h - zgodzić się nie m oże. i 21
Uwaga: Jest to w standardzie nowość, więc możliwe, że nie wszytkie kompilatory na to pozwalają. Kompilator GNU g++ jest tu zgodny ze standardem, ale na przykład Visual C++ wersja 6.0 - nie.
980
Rozdz. 21. Funkcje w irtualne Typy rezultatów różnych realizacji funkcji w irtualnej Teraz form alnie w yjaśnim y sobie, k ied y ty p now ej realizacji funkcji w irtualnej w klasie po ch o d n ej jest kow ariantem . N ie p rzestra sz się je d n a k - tu ż po tych suchych form alnych w yjaśnieniach, p o g a d a m y jak k o leg a z kolegą. A zatem , jeśli m a m d o czynienia z klasą p o d staw o w ą PODST i p o c h o d n ą POCt, w których są d w ie realizacje funkcji w irtu aln ej o n a z w ie f , to ty p y rezultatów tych funkcji p o w in n y spełniać następujące w y m ag an ia: ♦♦♦ - albo być identyczne, «$♦ - albo ty p rezu ltatu funkcji POCH: : f p o w in ien b y ć k o w a ria n te m typu rezu ltatu funkcji PODST: : f . A by tak rzeczy w iście było, oba typy rezu ltató w p o w in n y być w sk a źn ik am i (lub referencjam i) d o klas takich, że klasa w typie rezu ltatu funkcji PODST: : f (n azw ijm y ją klasą M), jest: • tą sam ą klasą, co w ty p ie POCH: : f , c z y li na p rzy k ła d oraz
PODST::f (: POCH::f ()
lub klasa w typie rezu ltatu funkcji w irtu aln ej z k la sy podstaw ow ej P O D S T : :f jest jed n o zn aczn ą i d o stę p n ą k la są p o d staw o w ą w sto su n k u do tej w ystępującej w ty p ie rezu ltatu funkcji P O C H : :f np.
owoc* PODST: : f O; gruszka* POCH::f();
(Liczę na to, że - tak jak ja - uznajesz klasę ow oc za publiczną klasę podstaioową klasy g r u s z k a ).
Uff! Pewnie się pogubiłeś N ie przejm uj się, sp ra w a jest, w brew p o zo ro m , b a rd z o łatw a. T rzeba tylko sobie uśw iadom ić, że m a m y tu do czynienia z d w o m a h ierarch iam i. Pierw sza to ta, do której należą k lasy m ające te funkcje w irtu aln e. U n as to b y ły klasy PODST, POCH. D ruga h ie ra rch ia to ta, do której należą ty p y re z u lta tó w . Żeby nasz ro z m o w a była bardziej o b razo w a, w y o b raź sobie w ięc taką hierar chię (dla u p ro szczen ia u nas hierarchia ta sk ład a się z d w ó ch klas). ♦♦♦ Klasa p o d sta w o w a instrument i w y w o d z ąca się od niej klasa p o ch o d n a skrzypce M am y też w p ro g ra m ie inną h ierarch ię - ta opisuje ty p y u szk o d zeń . Klasa p o d staw o w a: u s z k o d z e n i e i klasę p o c h o d n a zerwaniestruny, d zied ziczącą tę klasę us z k o d z e n i e w sp o só b nie-pry w atny. W róćm y d o tej p ierw szej hierarchii. W klasie p o d staw o w ej instrument jest funkcja w irtu aln a
uszkodzenie
instrum ent: : s t a n ( ) ;
Jak w idać, re z u lta te m tej funkcji jest o b ie k t klasy uszkodzenie.
Rozdział. 21. Funkcje w irtualne Typy rezultatów różnych realizacji funkcji w irtualnej
981
N a d c h o d z i chw ila, g d y m u sim y się zastan o w ić, co m oże być rezu ltatem funkcji w irtu aln ej stan(), k tó rą zam ierzam y zrealizo w ać w klasie pochodnej skrzypce.
4
-
Tu sp ra w a jest o czyw ista: Skoro rezu ltatem funkcji w klasie p o d staw o w ej jest o b ie k t klasy u s z k o d z e n i e - to znaczy, że w klasie pochodnej skrzypce — re z u lta t m u si być ta k i sam , czyli też: o b iek t k lasy uszkodzenie. Jeśli jed n ak rezu ltatem funkcji instrument: :stan() byłby wskaźnik do o b ie k tu klasy uszkodzenie, uszkodzenie*
instrument::s tan() ;
to re z u lta t funkcji s k r z y p c e : : stan () m ógłby być oczyw iście taki sam (czyli w sk a ź n ik d o klasy uszkodzenie). D o d a tk o w o m óg łb y być też jakim ś k o w arian tem tego typu uszkodzenie*, czyli n a p rzy k ład w sk aźn ik iem do obiektu k lasy zerwana_struna. W sk aźn ik d o ty p u zerwana struna jest kow arian tem w sk aźn ik a d o typu uszkodzenie, bo klasa u szkodzenie jest jed n o zn aczn ą i d o stęp n ą klasą p o d s ta w o w ą dla k lasy zerwana_struna. J a k ł a t w o z a p a m i ę t a ć , c o j e s t „ k o w a r i a n t e m ”, a c o n i e ?
Ja m a m taki sposób, ż e p rzy p o m in am sobie z a sa d ę konw ersji stan d ard o w y ch w p rz y p a d k u d zied ziczen ia. R ozm aw ialiśm y o tym w p o p rzed n im rozdziale. P rz y p o m n ę in extenso: W skaźnik d o obiektu klasy p ochodnej - m oże być niejaw nie p rzek ształco n y na w skaźnik do stęp n ej jednoznacznie klasy pod staw o w ej. W y starc zy sobie u św iad o m ić, że...
N ieśm iało p rzy p o m n ę , że klasy, o których tu m ow a, to te klasy, które w idzim y w ty p ach rezu ltatu , (a nie klasy, do których należą ow e funkcje w irtualne). 3)
4)
Dostępną, bo dziedziczoną publicznie - możemy przecież powiedzieć otwarcie, że zerwana struna „jest rodzajem" uszkodzenia. A pamiętasz dziedziczenie prywatne z klasą „operowa_góra"? Wskaźnik do operowej góry nie byłby kowariantem dla wskaźnika do samochodu, gdyż tam dziedziczenie było ściśle tajne, czyli prywatne.33 W szczególnym przypadku te dwie hierarchie mogą być jedną i tą samą hierarchią.
982
Rozdz. 21. Funkcje w irtualne Dalsze szczegóły Z apam iętaj:
Jeśli d efiniujesz n o w ą realizację funkcji w irtualnej, a w jej p o d s ta w o w e j defini cji, w typie re z u lta tu , w idzisz w sk aźn ik do obiektu jakiejś klasy - to masz do w y b o ru uczy n ić rezultatem Twojej wersji: ♦♦♦ albo taki sam ty p w skaźnika, ♦J* albo taki w sk aźn ik , który konw ersja s ta n d a rd o w a m o g łab y zam ienić na ten, z p ie rw o tn ej realizacji. T eraz łatw iej zap am iętać, praw da? P odobnie ro zu m u jem y w p rzy p ad k u , g d y w typie re z u lta tu funkcji wirtualne] w id zim y referencję - z tym, że tu taj m yślim y o k o n w ersji standardow ej w y łączn ie referencji na referencję (a nie: 1-w artości na referencję).
U w aga: jeśli zam ie rz a sz skorzystać z tych całych k o w a ria n tó w , pam iętaj, że gdy n azw ę klasy staw iasz w deklaracji rezu ltatu funkcji w irtu aln ej, to nie w y starczy , by w cześniej pojawiła się jed y n ie zw y k ła d ek laracja zapow iadającą tej klasy
("jakby co, to zerwanie_struny jest nazwą typu"). Kompilator m usi już znać definicję tej klasy, choćby po to, by z w yglądu jej listy pochodzenia stw ierdzić, czy naprawdę taki typ rezu ltatu byłby kowariantem
W ir tu a ln ą m o ż e b y ć tylko fu n k cja s k ł a d o w a
Funkcja globalna nie m oże być w irtu aln a. Łatw o to z a p a m ię ta ć p rzypom inając sobie nasze w o ln e tłum aczenie słow a vir t u a l - „ in te lig e n tn ie dzied ziczo n a". Skoro d zied ziczo n a, to tylko sk ła d o w a , bo przecież d zied ziczen ie jest atry b u tem klas. G lo b aln e funkcje p rzecież nie d zied ziczą. A ni inteligentnie, ani głupio. S łow o
virtual
- g d z i e s i ę je s t a w i a (lub n ie s t a w i a ) ?
Słow o virtual m o że w ystąpić tylko ra z w klasie p o d sta w o w e j i nie m usi ju ż p o w tarzać się p rz y analogicznych funkcjach w klasach p o ch o d n y ch . (C hociaż może). W szystkie funkcje o takiej s y g n a tu rz e w n astęp n y c h p o k o len iach (n aw et tych jeszcze nie w ym y ślo n y ch ) będą a u to m aty c zn ie w irtu a ln e . Słow o virtua 1 p o jaw ia się tylko p rzy d e k la ra c ji funkcji w e w n ą trz ciała klasy. Jeśli definicja (czyli ciało) funkcji sk ład o w ej jest p o z a ciałem klasy, to p rz y definicji funkcji nie p o w tarza się już sło w a virtual. class X // . ■ public:
983
Rozdział. 21. Funkcje w irtualne Dalsze szczegóły
T o dlatego, że to k la sa m a w iedzieć czy jej funkcja jest funkcją w irtu aln ą. Pu p ro stu to klasa m u si się d o tego, w p ew ien u k ry ty sposób, p rzy g o to w ać. C z y k l a s a p o c h o d n a m u s i k o n i e c z n i e z d e f i n i o w a ć s w o j ą w e r s j ę funkcji w irtualnej?
N ie, nie m usi. Jeśli n ie zdefiniuje, to b ę d z ie w y w o łan a o w a w ersja z klasy podstaw o w ej. T o ch y b a oczyw iste. G d y b y śm y w naszej klasie beben nie defi niow ali swojej w ersji-funkcji wyda j_d zwiek, to dla niej u ż y ta b ęd zie ta w ersja z klasy p o d staw o w ej instrument. M o ż n a z a s ło n ić , ale... i t a k s ię
o d rad za -jak
Feniks
G d y b y w jakiejś z k la s pochodnych nie zd efin io w ać now szej realizacji wersji funkcji w irtualnej, a za to zdefiniow ać jakiś sk ład n ik o iden ty czn ej n a z w ie - to te n składnik, o czy w iście, zasłania nazw ę funkcji w irtualnej. T o nic - i tak ta w irtu aln o ść, m im o zasłonięcia, m oże przejść na ew en tu aln e n a stę p n e p o kolenia. C zyli następne p o k olenia m ogą sobie d efiniow ać swoje realizacje tej funkcji w irtualnej. B ardziej fachow o: Funkcja w irtu aln a nie m u si być w danej k lasie pochodnej "w id zialn a" (m oże być zasłonięta), a i tak w o ln o tw orzyć jej a k tu a ln ą realizację. J a k i j e s t d o s t ę p d o fu n k c ji w irtu a ln ej, k tó r a w k la s ie p o d s t a w o w e j m a inny d o s t ę p niż w klasie p o c h o d n e j ?
K onkretniej: fu n k cja w irtu aln a w klasie p o d staw o w ej P0DST jest zdeklarow ana z do stęp em public, a tym czasem w klasie pochodnej POCH - jest protected lu b private. Jaki jest w ów czas d o stęp d o takiej funkcji w p rzy p ad k u w yw o łan ia? Z ależy to tylko od w sk a źn ik a (referencji), któ ry m po słu g u jem y się przy w yw o ły w an iu . D o stęp jest taki, jaki ma ta wersja tej funkcji w irtualnej w klasie, z której jest w sk aźn ik (referencja). Jeżeli w ięc w w y w o łan iu : wsk -> funkcja()
w sk a ź n ik p o ch o d zi z klasy podstaw ow ej PODST PODST *wsk;
to funkcja ma d o s tę p taki, jak w klasie PODST. W naszym p rz y p a d k u public.
984
Rozdz. 21. Funkcje w irtualne Wczesne i późne wiązanie Jeśli n ato m iast w skaźnik jest z klasy pochodnej POCH *wsk;
to funkcja m a taki dostęp, jaki m a w klasie pochodnej (u n a s private). Zw racam u w ag ę: nie w ażne jest, na co w skaźnik b ieżąco p o k azu je. P rzy roz sądzaniu d o stęp u (w czasie kom pilacji oczywiście) k o m p ila to r zna przecież tylko typ tego w skaźnika, więc ty lk o w jego klasie m o że s p ra w d z ić dostęp do "jego" funkcji w irtualnej.
static
- zabroniony
Funkcja w irtu a ln a nie m oże być funkcją składow ą ty p u static. Przypominam, że przydomek s t a t i c mówił, że funkcja je st w yw oływ an a nie na rzecz jakiegoś konkretnego o b iek tu , ale na rzecz całej klasy . M echanizm w y w o ły w an ia funkcji w irtualnej jest jed n ak taki, że o ty m , k tó rą w ersję funkcji w ywołać, d ecy d u je się na p o d staw ie o b ie k tu . M usi b y ć w ięc w yw ołanie na rzecz obiektu (znanego z przezw iska, lub p o k azy w an eg o w sk aźn ik iem ) - ale obiektu! Do tego m oże dojść tylko w tedy, g d y funkcja sk ład o w a n ie jest static. W ir tu a ln o ś ć nie z a b r a n i a przyjaźni
Funkcja sk ład o w a w irtu alna m oże być p rzez jakąś z u p e łn ie in n ą klasę obdarzo na p rzy jaźn ią (zd ek laro w an a jako przyjaciel - f riend). C zy li jakaś inna klasa m oże po zw o lić tej funkcji na d o s tę p do swoich s k ła d n ik ó w pryw atnych. W irtualność oczyw iście nie p rzeszk ad z a takiej deklaracji p rzy ja źn i. N ie przesz kadza, ale też o w a w irtualność nie ro zszerzy przyjaźni n a n a s tę p n e pokolenia. Czyli i tak p rzy ja źń zaw ierana jest ty lk o z tą jedną fu n k cją, a nie - jakby ktoś m oże na to liczył - ze w szystkim i e w en tu a ln y m i p ó ź n ie jsz y m i realizacjam i tej w irtualnej funkcji w klasach p o tom nych. Po prostu p rz y ja ź n i się nie d zied ziczy ani norm alnie, a n i w irtu aln ie.
21.4
W c z e s n e i p ó ź n e w iązan ie W tym p arag rafie nie pow iem y nic now ego. C hodzi tu ty lk o o to, by nazw ać zjaw isko, k tó re w łaśn ie zaobserw ow aliśm y.
W c z e s n e w iązanie
Jeżeli k o m p ilu jem y klasyczny p ro g ram -„ k la sy c z n y " , to zn a c z y taki bez funkcji w irtualnych - w ó w czas, już na etap ie kom pilacji, o d b y w a się pow iązan ie w y w ołań funkcji z ad resam i określającym i, g d zie te funkcje są. R ezultatem takiej decyzji jest instrukcja, by skoczyć w d o k ła d n ie takie-a-takie miejsce w p am ięci i w y k o n ać zn ajd u jącą się tam funkcję. P o n iew aż w szystkie te decyzje m ogą się o d b y ć w czasie k o m p ila c ji- n azy w am y to w iązan iem w czasie kom pilacji - lu b krócej: w czesn y m w ią z a n ie m (ang. early binding).
985
Rozdział. 21. Funkcje w irtualne Wczesne i późne wiązanie
N ie znaczy to, że w p ro g ram ie nie m o ż em y n ig d z ie p o d e jm o w a ć decyzji o tym którą funkcję w y k o n ać. Z nasz p rzecież zapis: switch(wariant)
{ case 1 : obj.funkcjal (); break; case 2 : obj.funkcja 2 (); break; case 3 : obj.funkcja3(); break;
// tu jest pewne !
0
// ...już w czasie kompilacji!
0
// takie i lu !
©
} Rzecz w ty m , ż e m u sim y to p o d ejm o w an ie decyzji s a m i zap ro g ram o w ać. K om p ilato r zaś - w id z ą c takie linijki jak O , 0 , © - nie p o d ejm u je tu żadnej decyzji. G d y w id z i w yw ołanie: obj.funkcj a 2 ()
tłum aczy to jed n o zn aczn ie i zaw sze -
na skok d o w y k o n an ia funkcji
f u n k c j a 2 (v o i d ) .
P ó ź n e w iązanie
W p rz y p a d k u , g d y p o sługujem y się funkcjam i w irtu aln y m i, to m o żem y uw ol nić się od ko n ieczn o ści p ro g ram o w an ia podejm ow ania decyzji, co do typu p o k azy w an eg o o b iek tu . Tę pracę zleca się kom puterow i. K om pilator w ygene ru je taki k o d , że d ecy zja o pow iązaniu w y w o łan ia funkcji z o kreśloną wersją funkcji w irtu aln ej - będzie p o d ejm o w an a dopiero na e tap ie w ykonyw ania p ro g ram u . M ów im y, że jest to w iąza n ie n a etap ie w y k o n a n ia lub p ó ź n e w ią z a n ie (ang.
late binding).
W n aszy m zap isie wskaźnik->funkcja_składowa()
(będącym przecież w yw ołaniem funkcji) nie m ożem y na etapie kompilacji pow ied zieć na jaki ty p obiektu będzie p o k azy w ał w skaźnik. N ie w iem y więc, o w y w o łan ie której w ersji funkcji w irtualnej chodzi. K om pilator nie m oże tego w ięc, na etap ie kom pilacji, zastąpić instrukcją: skocz d o funkcji pod tak im -a-tak im a d re se m . Decyzja o tym m u si się odbyć w czasie w ykonyw ania p ro g ram u . K o m p ilato r więc generuje tu pew ien ukryty ko d , k tóry - w trakcie w y k o n y w a n ia p ro g ra m u - um ożliw i procesorow i podjęcie takiej decyzji. A nie b ęd zie to k o d tak prym ity w n y , jak instrukcja s w i tc h . Ten u k ry ty kod gotowy
986
Rozdz. 21. Funkcje w irtualne Kiedy dla wywołań funkcji w irtualnych, mimo wszystko, zachodzi wczesne wiązanie? jest ro zsąd zić naw et takie p rz y p a d k i funkcji, których jeszcze n ik t nie napisał i które k ied y ś (m oże) zlin k o w an e zostaną z p ro g ram em .
P óźne w ią z a n ie po zw ala u w o ln ić nas od p ro g ram o w an ia ak tu podjęcia decyzji. Tę pracę zro b i za nas kom pilator. Program uje on to (sp ry ciarz) tak, że ten kod potrafi za re a g o w a ć naw et na takie m odyfikacje p ro g ra m u , k tó re zrobią następ ne p o k o len ia program istów . Z dru g iej stro n y w idzim y, że w czesne w iązanie, a k o n k retn ie instrukcja s w i t ch u niem ożliw ia tę elastyczność. Kiedyś m ó w iłem , ż e u ży w a n ie in stru kcji g o t o z d ra d z a , że się jest złym program istą. Teraz m o ż n a p o d o b n ie pow iedzieć o u ży w a n iu s w i t c h p rzy ro zsąd zan iu typu o b iek tu . Jeśli stosujem y konstrukcję s w itc h ( typ_obicktu) 1 c a s e typ_A: c a s e typ_B:
) to u tru d n ia m y sobie w tym m iejscu ew en tu aln ą p ó ź n ie jsz ą ro zb u d o w ę p ro g ra m u. Lepiej w ięc po słu ży ć się tu taj funkcją w irtu aln ą.
21.5 Kiedy dla wywołań funkcji wirtualnych, mimo wszystko, zachodzi wczesne wiązanie? N ie k ażd e w y w o łan ie funkcji w irtu aln ej oznacza p ó ź n e w iązan ie. Są sytuacje, g d zie k o m p ilato r m oże so bie u p ro ścić spraw ę i z a sto so w a ć zw ykłe, w czesne w iązanie. O to kilka takich sytuacji. W y w o łan ie n a rz e c z obiektu
Jak już w sp o m n ieliśm y , jeśli funkcję w irtu aln ą w y w o łu je m y na rzecz obiektu zn an eg o z n a z w y obiekt.funkcja( ); to w szy stk o jest oczyw iste ju ż na etapie kom pilacji. Z d ek laracji n azw y tego obiektu k o m p ilato r w ie, d o jakiej klasy on należy, w ia d o m o więc, którą z w ersji funkcji w irtu aln ej m a on w yw ołać. N astępuje w ięc p o w ią z a n ie tego w y w o łan ia z w łaściw ą funkcją - ju ż w czasie kom pilacji. J a w n e u ży cie kw alifikatora z a k r e s u
Jeśli n ato m iast w yw ołujem y funkcję w irtu aln ą z a p o m o c ą w skaźnika lu b referencji wskaźnik -> fu n k c ja (); referencja. fu n k c ja ();
Rozdział. 21. Funkcje w irtualne Kiedy dla wywołań funkcji w irtualnych, mimo wszystko, zachodzi wczesne wiązanie?
987
w ów czas to, na co w d a n y m m o m en cie p o k azu je w sk a źn ik c z y referencja m oże być w ia d o m e d o p ie ro w czasie w y k o n an ia p ro g ram u . W naszej funkcji m u z y k - z a każdym jej wywołaniem - referencja ozna czała coś innego, dlatego decyzja, którą funkcję wywołać w danej chwili, może być podjęta dopiero w czasie wykonania programu w każdym kon
kretnym przypadku. P olim orfizm u ja w n ia się tylko p rzy w y w o łan iach za po m o cą w sk aźn ik a lub referencji. A le jeśli zap isz em y tak:
wskaźnik->klasa::funkcja(); to m im o że jest w tej instrukcji w sk aźn ik , o p e ra to re m zakresu jest w y ra ź n ie określone ży czen ie, która w ersja funkcji w irtu aln ej ma być u ru ch o m io n a. C hcem y tę obow iązującą w k lasie k l a s a , a nie ż a d n ą inną. Skoro nie m a w ątpliw ości juz na etap ie kom pilacji, to k o m p ilato r uznaje, że m o ż n a zastosow ać w czesn e w iązanie. Tego sposobu z k w alifik ato rem zak resu p rz y nazw ie nie n ależy jed n ak n ad u ży w ać. N ie jest on eleg an ck i, bo ma w sobie p ew n ą sprzeczność. Z jednej strony użycie w w y w o ła n iu w sk aźn ik a (referencji) sugeruje p ew n ą elastyczność, z drugiej stro n y k w alifik ato r zakresu ' : : ' m ów i, że żad n a elastyczność nas me obchodzi. Ma być ta w ersja funkcji i już! P rogram , który m a takie niekonsekw encje, trudniej później m odyfikow ać. Z ogólnego sp o jrzen ia na sąsiednie linijki w id zim y przecież, ż e używ a się tu w sk aźn ik a (referencji), w ięc m ożna tu oczekiw ać elastyczności. Jeśli me dojrzy m y tej w łaśn ie linijki z kw alifikatorem zak resu , to m ożem y p o zo stać w błogiej naiw ności.
W y w o ła n ie z k o n stru k to ra (destruktora) klasy p o d sta w o w e j
Trzecią sytuacją, g d y funkcja w irtualna w iązan a jest na etapie kom pilacji (wcze sn e w iązanie), jest w yw ołanie jej z konstruktora (d estru k to ra) klasy podstaw ow ej. Jeśli k reu jem y obiekt klasy pochodnej, to w ed le zw yczaju najpierw startu je k o n stru k to r części odziedziczonej - czyli k la sy podstaw o wej. Jeśli w ew n ątrz tegoż k o nstruktora jest w y w o łan ie funkcji w irtu aln ej, to m im o że pracujem y dla obiektu klasy pochodnej (na to p o k a z u je w skaźnik t h i s ) - uruchom iona zo stan ie jej wersja z klasy podstaw ow ej. Decyzja o tym zapadnie już na etap ie kom pi lacji (-w czesn e w iązanie).
988
Rozdz. 21. Funkcje w irtualne Kulisy białej magii, czyli: J a k to jest zrobione? P o w ó d takiej decyzji jest prosty: konstruktor klasy p o d staw o w ej p racu je w ó w czas, g d y obiekt klasy pochodnej nie jest jeszcze w całości sk o n stru o w a n y . P rzecież d o p iero po nim n isz a konstruktor klasy p o ch o d n ej. K o m p ilato r wiec n ie u ru c h a m ia funkcji w irtu aln ej z klasy pochodnej, bo ta p o w in n a p racow ać ty lk o na gotow ym , sk o n stru o w a n y m obiekcie. D la bezp ieczeń stw a u ru c h a m ia n a jest zaw sze ta w ersja z. klasy p o d staw o w ej. T o sa m o dotyczy d estru k to ra. G d y pracuje d e s tru k to r klasy p o d staw o w ej o b ie k t jest ju ż częściow o ro zm o n to w an y . Zrobił to p rzecież pracu jący p rzed ch w ilą d estru k to r klasy pochodnej. Dla b ezpieczeństw a, w y w o ły w a n a jest w te d y zaw sze w ersja z klasy pod staw o w ej. R ów nież i ta d ecy zja p o d ejm o w an a jest jako w czesn e w iązanie. S praw a jest bow iem oczy w ista ju ż na etap ie kom pilacji.
21.6
K u lis y b ia łej m ag ii, c z y li: J a k to je s t z ro b io n e ? T en p a ra g ra f jest tylko dla tych, którzy są ciek aw i, jak k o m p ilato r k oduje p o d ejm o w an ie decyzji w p rz y p a d k u w yw ołania fu n k cji w irtu aln y ch . Przy p ie rw szy m czytaniu p ro p o n u ję ten p arag raf opuścić, ch y b a że n a p ra w d ę nie m o ż esz z ciekaw ości w y trzy m ać.
W N ie zam ierza m tu o pisyw ać szczegółów im plem entacji. P o d am tylko hasła. O tó ż klasa p o d staw o w a w ch w ili lin k o w a n ia d o w ie się o w szystkich tych definicjach jej funkcji w irtu aln ej - które w y stąp iły w n astęp n y c h pokoleniach d zied ziczen ia. Z b u d o w an a b ęd zie tablica w sk a źn ik ó w d o funkcji zaw ierająca a d re sy w szystkich wersji funkcji w irtualnej. (W ty m w ięc miejscu moja klasa i n s t r u m e n t dow ie się o tym , że Ty w łaśnie zd efin io w ałeś sw oją klasę pochodną c z y te ln ik o f o n ) . N ato m iast jeszcze w czasie kom pilacji w m iejscach, g d z ie w p ro g ram ie są w y w o łan ia funkcji w irtu aln y ch , pojaw ia się kod u ru c h o m ie n ia funkcji, p o k azy w a nej p rz e z w sk aźn ik sch o w an y w którym ś elem en cie tej tablicy. W którym ? Jeszcze nie w iadom o. To się d o p ie ro okaże w trak cie w y k o n a n ia p ro g ram u . Czyli w y w o łan ie wskobj -> funkcja!)
zam ien ia się w ięc na w y w o łan ie funkcji p o k azy w an ej w sk a źn ik ie m - i d o tego takim , który jest zap isa n y w tablicy t (tablica w sk a ź n ik ó w d o funkcji w irtu aln y ch ) (* (wskobj -> t [rj] ) ) ();
gdzie n - zależy od typu ro zp o zn a n ej klasy. To w szystko. Tablica ta nie w ch o d zi w skład klasy p o d sta w o w e j. Jest ona sobie gdzieś indziej w pam ięci, a w klasie p o d staw o w ej u k ry ty m sk ładnikiem jest tylko w sk aźn ik do niej. D lateg o klasa p o d staw o w a (m ająca funkcję w irtu aln ą)
989
Rozdział. 21. Funkcje w irtualne Funkcja w irtualna, a mimo to i n l i n e
jest tylko tro szeczk ę w ięk sza (o ro zm iar teg o w sk aźn ik a) - n ie zale żn ie o d tego, czy tablica m a 2 elem en ty czy 100. Jeśli chcesz się p rzek o n a ć o istnieniu tego u k ry teg o sk ład n ik a, to zdefiniuj sobie taką klasę class instrument {
in t cena; p u b lic : virtual
void funkcja() {}
); R ozm iar o b ie k tu tej klasy ocenisz choćby tak cout << "obiekt klasy instrument ma rozmiar " « sizeof(instrument);
A teraz sk asu j sobie to słów ko v i r t u a l z deklaracji funkcji f u n k c j a . Zoba czysz jak z m ie n i się ro zm iar obiektu. Ta różnica, to w łaśn ie obecny lub nieobe cny w sk a źn ik d o w sp o m n ian ej tablicy.
W U staliliśm y ju ż w ięc, że klasa p o d staw o w a n a etapie lin k o w am a do w iad u je się o w szystkich realizacjach swojej funkcji w irtu aln ej, które p o jaw iły się w łańcuchu d zied zic zen ia aż d o ostatn ieg o pokolenia. D ow iaduje się d la teg o , że pow inna je u m ieć u ru ch o m ić. W skaźniki do tych w szystkich funkcji zostają złozone w tablicy. K lasa p o d sta w o w a nie w ie jednak o z u p ełn ie innych funkcjach w irtualnych, któ re p o jaw iły się d o p ie ro p o raz p ierw szy w dalszych pokoleniach (u jej w n u k ó w ). Jest to zro zu m iałe. K lasa i n s t r u m e n t nie m usi w iedzieć o funkcji w irtualnej c h w y t b a r r e () zdefiniow anej po raz pierw szy w klasie g itara na użytek dalszych k la s pochodzących od niej: klas g i t a r a _ a k u s t y c z n a i klasy g i t a r a _ e l e k t r y c z n a . Klasa i n s t r u m e n t nie m oże m ieć przecież zachow a nia c h w y t b a r r e , bo takiego zach o w an ia nie ma przecież in stru m en t typu trąb k a czy g w izd ek . W skrócie m ówiąc: To tylko w tej klasie, g d zie pojaw ia się po raz p ie rw sz y deklaracja funkcji F będącej v i r t u a l - tw o rzy się tablica d o w szystkich funkcji w irtu aln y ch o nazw ie F. O w szy stk ich tych tru d n y ch spraw ach nie m usisz jednak w iedzieć, by móc p o słu g iw ać się funkcjam i w irtualnym i. N ajlepszy dow ód: jak prosty był pro g ram z in stru m en tam i!
21.7 F u n k c ja w irtu a ln a , a m im o to iniine C hoć na p ie rw szy rz u t oka w ydaje się to ab su rd aln e - funkcja w irtualna może być z d e k la ro w a n a jako i n i i n e (w-linii).
990
Rozdz. 21. Funkcje w irtualne Pojedynek - funkcje przeładowane contra funkcje w irtualne Jak pam iętam y, w yw ołanie funkcji inline jest k o m p ilo w an e w ten sposób, że k o m p ilato r zam iast w yw oływ ać fu n k c ję -w p is u je jej ciało (całą treść/ w linii, w której w yw ołanie nastąpiło. N ie ma tu w ięc żad n e g o w y w o łan ia funkcji - jest b ezp o śred n ie w staw ian ie wszystkich jej instrukcji w d a n e miejsce w p ro g ram ie. N ie trzeba się d łu g o zastanaw iać, by w y czu ć, że to ab so lu tn ie przeczy polim orfizm ow i - czyli m echanizm ow i elasty czn eg o w y w o ły w a n ia funkcji w irtualnych. A jed n ak , m im o to m ożna funkcję w irtu aln ą z d e k laro w ać jako inline. Efekt b ęd zie taki: 1) W sytuacjach, gdy chodzi nam o rzeczy w isty po lim o rfizm (późne w iązanie) - czyli gdy w y w o łan ie funkcji o d b y w a się dla obiektu klasy pochodnej po k azy w an eg o w sk a źn ik ie m (referencją) d o kla sy podstaw ow ej - w ted y p rz y d o m e k inline b ęd zie zig n o ro w a ny. Z niszczyłby przecież w sp an iało ść polim o rfizm u . 2) Są jed n ak sytuacje, g d y już n a etap ie kom pilacji w ia d o m o dla jakiego obiektu następuje w y w o łan ie funkcji. (W czesne w iązanie) - O tych sytuacjach m ów iliśm y kilka stro n w cześniej. W tedy w łaśnie kom pilator w e ź m ie p o d u w ag ę to, że funkcja w irtu aln a jest (dodatkow o) inline. Ju ż n a w e t tak robiliśm y - przypom nij sobie n a sz e in stru m en ty . Definicje w sz y stkich funkcji w irtu aln y ch były przecież w e w n ą trz ciała klas - to p rzez d o m n ie m a n ie oznacza, że mają być inline! Jed n y m z miejsc n aszego program u, gdzie k o m p ila to r sk o rzy sta z fak tu , że funkcja jest i n l i n e będzie instrukcja steinway_giseli.wydaj_dzwiek();
bo tu taj ko m p ilato r m oże zrobić w czesne w iązan ie. N ie b ęd zie je d n ak u w z g lę d nio n a cecha inline w instrukcji wskinstr-> wydaj_dzwiek();
bo instrukcja ta w y m ag a późnego w iązania (jest w sk aźn ik ).
21.8
P o je d y n e k - fu n k c je p rz e ła d o w a n e c o n tra fu n k c je w irtu a ln e P olim orfizm u - czyli m echanizm u w y w o ły w a n ia funkcji w irtu aln y ch nie nale ży m ylić z p rzeład o w an iem funkcji. Jedyne, co te sy tu acje cechuje, to identycz ność n a z w funkcji.
O to p o d s t a w o w e ró ż n ic e
Funkcje w irtu aln e m ają w szystkie •
tę sam ą n azw ę
•
i te sam e arg u m en ty ,
991
Rozdział. 21. Funkcje w irtualne Pojedynek - funkcje przeładowane contra funkcje w irtualne co jest m o żliw e ty lk o w tedy, g d y k a ż d a z nich m a in n y z a k re s w ażności. Funkcje p rz e ła d o w a n e mają •
tę sam ą nazw ę
•
leżą w tym sam y m zak re sie w ażności
co jest m o żliw e ty lk o w tedy, g d y m ają ró żn e listy a rg u m e n tó w ■ Funkcje składow e 1 W IRTU A LN E
PRZEŁA D O W A N E
M ają nazw y
A rg u m en ty
Z ak res
te sam e
te s a m e
ró żn y (bo są io różnych klasach)
te sam e
ró żn e
ten sam (bo są w tej samej klasie)
;j
Jeśli m am y funkcję w irtualną w klasie podstaw ow ej O i pochodnej © , ale d o d atk o w o w k lasie pochodnej jest jeszcze jakaś inna funkcja o tej samej nazw ie © (silą rzeczy m u si mieć inne arg u m en ty ). C zy jest o n a w ów czas także w irtu aln a ? O d p o w ied ź: N ie. M echanizm w y w o łan ia funkcji w irtu aln ej ujaw ni się tylko w sto su n k u d o tej funkcji, która m a identyczne a rg u m e n ty , jak ta z klasy p od staw o w ej Ta funkcja © jest zw ykłą funkcją p rzeładow ującą nazw ę j a z d a w zakresie klasy s a m o c h ó d . P r z e ł a d o w a n i e funkcji - to w c z e s n e w ią z a n ie ,
dlatego ż e już na etapie kompilacji (na podstaw ie oceny listy argum entów w yw o łan ia), k o m p ilato r m oże określić, która z wersji funkcji ma zostać u ru ch o m io n a.
992
Rozdz. 21. Funkcje w irtualne Klasy abstrakcyjne N ato m iast w p rz y p a d k u funkcji w irtu aln y ch nie d a się tak określić na po d staw ie arg u m e n tó w - w szystkie w ersje tej funkcji m ają p rz e c ie ż identyczną listę arg u m en tó w .
2 1 .9
K la s y a b s tra k c y jn e Klasa abstrakcyjna to klasa, która n ie rep rezen tu je ż a d n e g o konkretnego obiektu. N a p rzy k ład klasa: ssak. N ie w idziałem n ig d y k o n k retn e g o o b ie k tu klasy ssak. O w szem , w id ziałem psy, krow y, konie, ludzi, k tó re są p rzecież p o ch o d n y m i od ssaka, ale sam ego ssaka nie w idziałem . Klasa ssak sam a o b ie k tó w nie ma istnieje po to, by m ieć klasy pochodne. W y o b raź sobie, że jesteś rzeźb iarzem i że kazano Ci w y rzeźb ić pom nik ssaka. C o w y rzeźb isz? C zy to b ę d z ie ssak? Ssak to pojęcie ab strak cy jn e - jak je p rzed staw isz? Z klasy abstrakcyjnej ssak w yw odzą się klasy koń, pies, człow iek. Te klasy mają już sw oje k o n k retn e obiekty, więc nie są już abstrakcyjne. N a s u n ą ć się m oże pytanie: Po co n am w obec tego klasa, k tó ra nie zam ierza m ieć ż a d n e g o obiektu ? N ajp ro stsza o d p o w ie d ź brzm i: Po to, b y ją d zied ziczy ć. R ozw ażm y taką sytuację. M am y d w ie klasy: klasa tró jk ąt i k lasa k w ad rat. Nie pochodzą one od siebie więc nie m ożna ich po w iąza ć zależn o ścią dziedziczenia (żadna nie jest szczeg ó ln y m rodzajem drugiej). Z d ru g iej stro n y jednak obie klasy mają p ew ien zbiór w spólnych cech. P rzy k ład o w o : p o zy cję na ekranie, g d zie n ary so w an a jest dana figura, k o lo r w jakim jest to n ary so w an e, pole pow ierzchni. Klasy te m ają też g r u p ę w spólnych zach o w ań : d a n ą fig u rę m o żn a narysow ać, czy też p rzesu n ąć w inne miejsce na ekranie. G d y potrafim y ta k ie w spólne cechy w y o d ręb n ić, to jest to p rzesła n k a do tego, by zdefiniow ać k la sę abstrakcyjną opisującą tę w sp ó ln ą część
993
Rozdział. 21. Funkcje w irtualne Klasy abstrakcyjne class figura
t
protected: int pozycja_x, pozycja_y, kolor; public: void p r z e s u ń (int delta_x,
int delta_y)
{ pozycja_x += delta_x; pozycja_y += delta_y;
} }i C zy nasza klasa abstrakcyjna rzeczyw iście nie m oże m ieć żad n y c h obiektów ? A leż oczyw iście m oże, oto on: figura f;
Tyle, że nie za m ie rz a m y tego ro b ić N o bo co zn aczy łb y taki obiekt? Z astan ó w się do czego m o ż e p rzy d ać się obiekt f ? M ożna nim ty lk o w y k o n y w ać operację przesuń. To ta k , jakbyśm y cudem stw o rzy li obiekt k lasy ssak, który by tylko ssał, ssał i ssał. N a w e t by nie w y g ląd ał jak cokolw iek, b o - żeb y w y glądać trzeba m ieć choćby funkcję sk ład o w ą p o k a z _ s i e (). Jak n a razie w id zim y w ięc, że korzyść z klasy abstrakcyjnej jest tak a, iż oszczędzam y pracy: w sp ó ln e cechy d la kilku klas d efin iu jem y jed n o k ro tn ie - w łaśnie w klasie abstrakcyjnej. Klasa ab strak cy jn a to jakby nied o k o ń czo n a klasa. D okończenie jej jest dopiero zrealizo w an e p rz e z jej klasy pochodne. P rzejdźm y dalej. Jest jeszcze jedna cecha łącząca klasy trój kat i kwadrat. Jest to działan ie po leg ające na n ary so w an iu tych figur na ekranie. O bie te figury m ogą być na e k ra n ie narysow ane. Z ap y tasz p ew n ie: „ -N o to d laczeg o od razu tego zach o w an ia (funkcji składow ej) nie w y o d ręb n iliśm y i nie um ieściliśm y w k lasie abstrakcyjnej?" O d p o w ied ź b rzm i: bo każda z tych fig u r m oże być n a ry so w an a - ale każda na swój w łasn y sp o só b . Sposób ry so w an ia k w ad ratu jest in n y niż sposób ry so w a nia trójkąta. N ie m ożna w ięc tych d w ó ch różnych w ersji funkcji narysuj um ieścić w e w sp ó ln ej klasie abstrakcyjnej figura. Ich p rak ty c zn e realizacje są bow iem o d m ie n n e . W rezultacie po d ejm u jem y decyzję o definiow aniu tych funkcji w klasach pochodnych - trój kat i kwadrat. O znacza to, że m o żn a n ary so w ać trójkąt, m ożna n ary so w ać k w adrat, ale nie m o żn a narysow ać figury. Szkoda. W koń cu w życiu co d zien n y m m ów im y: „n ary su jm y teraz tę figurę" m yśląc czasem o trójkącie, a czasem o kw adracie. Z au w aż, co pow iedzieliśm y: n ary su jm y tę figurę. Tę- o zn acza to, że na coś pokazujem y, czy li m am y w skaźnik. W skaźnik jest do fig u ry - to jest oczywiste. Z drugiej stro n y , tym w skaźnikiem pokazujem y w łaśn ie na k w ad rat albo trójkąt.
994
Rozdz. 21. Funkcje w irtualne Klasy abstrakcyjne
I
P an ie i Panow ie - jest to klasyczna sytuacja, k ie d y n asu w a się u ży cie funkcji w irtualnej. W skaźnikiem d o k la sy abstrakcyjnej f i g u r a pokazujem y obiekt klasy pochodnej ( k w a d r a t lu b t r ó j k ą t ) i żąd am y w y konania funkcji w łaściw ej d la k lasy pochodnej. O to, jak p o w in n y w yglądać nasze klasy: class figura { protected: int pozycja_x, pozycja_y, kolor; public: void przesuń(int delta_x, int delta_y)
{ pozycja_x += delta_x; pozycja_y += delta y;
1 virtual
void narysuj() = 0 ;
m iiiiiiiitiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiim class kwadrat
: public figura
{ public: void narysuj ()
{
// instrukcje rysowania kwadratu
} class trójkąt : public figura { public: void n arysuj()
// instrukcje rysowania trójkąta
) }; O to fragm ent p ro g ra m u , w którym to w y k o rzy stu jem y kwadrat k; trójkąt t;
//definicje obiektów
figura *wskfig; wskfig = &k;
// definicja wskaźnika do figury // ustawienie wskaźnika na kw adrat k
wskfig
// w y w o ła n ie
->
n a r y s u j ();
funkcji lu irtu aln ej
O statnia linijka o zn acza w y w o łan ie funkcji zd efin io w an ej w klasie k w a d r a t. M im o że w s k f i g jest w sk aźn ik iem d o k la sy f i g u r a , to d ziała m echanizm funkcji w irtualnej. W trakcie w y k o n an ia p ro g ra m u n a sz k o m p u ter zorientuje się na obiekt jakiej k lasy p o k azu je w łaśn ie w sk a źn ik — i u ruchom i funkcję w łaściw ą k lasie tego obiektu.
995
Rozdział. 21. Funkcje w irtualne Klasy abstrakcyjne
P o d s u m u j m y tu, j a k d o s z liś m y d o w n io s k u , ż e fu n k c ja m a b y ć w irtu a ln ą
♦> 1) W naszej hierarchii w y stą p iło zac h o w an ie czyli funkcja, k tó re b y ło cechą kilku klas. To był w ięc p o w ó d , by funkcja zn alazła się w klasie abstrakcyjnej. ♦♦♦ 2) Z d ru g ie j stro n y jed n ak , p rak ty c zn a realizacja tej funkcji jest o d m ie n na d la k ażd ej z klas. D latego w klasie abstrakcyjnej p o staw iliśm y p rz y tej fu n k cji słow o virtual.
Skoro klasa je st abstrakcyjna to zn aczy , że nigdy n ie b ęd ziem y w y k o n y w ać tej realizacji funkcji narysuj, k tó ra jest w klasie fiqura. D latego, że ry su je się k w ad raty , tró jk ąty , m oże kiedyś w przyszłości kółka, ale nie ry su jem y o b iek tó w klasy fig u ra —klasa figura ż ad n y c h zd efin io w an y ch o b iek tó w nie ma. P oniew aż ta realizacja funkcji jest niep o trzeb n a - m o ż em y ją zd efin io w ać w k la sie abstrakcyjnej tak virtual
void narysuj O
= 0;
O takiej fu n k cji m ów im y, że jest czysto w irtu aln a, (ang. pure uirtual). O znacza to, że tej w ersji funkcji w irtualnej n ie m a się n ig d y w y k o n y w ać. M oże być w y k o n y w a n a ta z klasy pochodnej kwadrat, lub trój kat, ale nigdy ta. Ma to oczyw iście se n s, b o przecież klasa figura nie zam ierzała m ieć żadnych obiek tów - a tylko na rzecz jej obiektów ta w ersja funkcji w irtu aln ej m ogłaby być w yw o łan a. Z d efin io w an ie tej funkcji w irtu aln ej jako czysto w irtu aln ej m a jeszcze jeden o d w ro tn y sk u te k : oznacza to, że klasa n a p ra w d ę nie m oże mieć żad n e g o obiektu. Do tej p o ry ostatecznie m ogliśm y sobie zd efin io w ać o b iek t klasy figura —m im o że n ic sen so w n eg o nie oznaczał. Jednak, g d y klasa m a w sobie choćby jedną funkcję czysto w irtu aln ą —w ó w czas nie d a się zdefiniow ać żad n eg o obiektu tej k lasy . K lasa jest n a p ra w d ę abstrakcyjna. Po prostu abstrakcyjna i już.
Z a s a d a , ż e n ie m o ż e b y ć ż a d n e g o o b i e k t u takiej k l a s y d o t y c z y n a p r a w d ę k a ż d e j sytuacji
Zatem : K om pilator z a p ro testu je nie tylko w ted y , g d y b y śm y chcieli zdefiniow ać obiekt tej klasy figura f;
T akże w sy tu acjach , kiedy tw orzyłyby się obiekty ch w ilow e tej klasy, czyli..
996
Rozdz. 21. Funkcje w irtualne Klasy abstrakcyjne
N ie m ożem y w ięc nigdzie zdefiniow ać funkcji, która o d b ie ra ła b y obiekt takiej klasy przez w artość, void funkcja(figura x);
W ów czas bow iem kom pilator tw o rzy zw ykle na stosie o b ie k t (automatyczny!) tej klasy. Skoro jest zabronione tw o rzen ie jakichkolw iek o b ie k tó w tej klasy, to kom pilator w id ząc taką funkcję zaprotestuje. (Ale p rz e s ła n ie p rz e z adres jest m ożliw e, bo w sk aźn ik do takiej k lasy istnieć może). Z tego sam eg o p o w o d u funkcja nie m oże zw racać p rzez w a rto ść obiektu klasy abstrakcyjnej. Klasa abstrakcyjna nie m oże też być ty p em w jawnej k o n w ersji. I tutaj pow sta w ałb y przecież o b iek t tej klasy - co je st zabronione. Jeśli jeszcze pamiętasz nasz przykład z instrumentami, to w przypadku gdybyśmy funkcję wirtualną w y d a j_ d z w ie k z klasy i n s t r u m e n t zdefiniowali (zamienili) jako czysto wirtualną, wówczas niemożliwe byłoby zdefiniowanie w programie obiektu klasy i n s t r u m e n 1.1słusznie — przecież instrument to jakaś abstrakcja. Konkretnie mamy do czynienia z waltornią, skrzypcami, grzechotką czy gwizdkiem. N ato m iast n asza funkcja m uzyk - pracująca na referencji o b iek tu klasy i n s t r u m e n t - jest n ad al p opraw na. Referencja d o obiektu - to p rzecież nie sam obiekt. J e śli d e c y d u je m y , ż e j a k a ś funkcja m a
być w irtualna —
to m a m y trzy
ew entualności:
P ^ 3
a) Z d ek laro w ać funkcję jako w irtu a ln ą (taką z w y k łą , n ie będącą: czysto w irtu aln ą). M usim y w ted y d o starczy ć także definicję tej fu n k cji dla tej klasy (czyli jej ciało) c l a s s KI { u ... virtual (
void funkcja ,
//... ciało funkcji
} P^y3
b) Z dek laro w ać funkcję jako czysto w irtu aln ą. Definicja fu n k cji w tej klasie jest w ted y n ieo b o w iązk o w a, w ięc jej nie p o d ajem y , class K2
//
...
virtual
void funkcja() = 0 ;
Rozdział. 21. Funkcje w irtualne Klasy abstrakcyjne
997
♦> P r z y p a d e k a )
(czyli funkcja w klasie abstrakcyjnej jest w irtu aln a i jest też tam jej definicja) daje n am to, ż e jeśli w jakiejś klasie pochodnej nie zd efin iu jem y swojej w ersji funkcji w irtu aln ej, to u ru ch o m io n a zostanie ta z k la sy podstaw ow ej. —C zy m a to sens? —pom yślałeś. —N a przykład, jeśli w n o w o zdefiniow anej k lasie d w u n a s t o k a t nie zd efin io w ałem swojej w ersji fu n k cji n a r y s u j - to funkcja z klasy p o d staw o w ej nie p o trafi g o przecież narysow ać! R zeczyw iście, ale zo stan ie uru ch o m io n a p o to, by ch o ciaż napisać na ek ra n ie „n ie w ie m jak to zrobić'”, albo zapiszczeć o strzeg aw czo , albo zrobić coś, co p rzy n ajm n iej o d leg le zasy m u lu je p raw d ziw e d z ia ła n ie —na p rzy k ład n ary su je na e k ra n ie krzyżyk. O statecznie m oże też nic nie ro b ić - jej ciało d efiniujem y w te d y ja k o p u ste { }. ♦> P r z y p a d e k b)
(w klasie abstrakcyjnej jest funkcja czysto w irtu aln a i nie ma jej definicji) daje n am to, że jeśli nie zdefin iu jem y w klasie po ch o d n ej swojej wersji funkcji w irtualnej, to w ó w czas przez d o m n iem an ie zostanie ona o d z ie d ziczo n a do k la sy p o ch o d n ej jak o czy sto w irtu aln a. R o zu m iesz co to oznacza? Klasa pochodna staje się w ów czas au to m aty czn ie abstrak cy jn ą. Jeśli nie było to naszym celem, a tylko o zdefiniow aniu w klasie p o ch o d n ej zap o m n ieliśm y - to kom pilator nam o ty m zaraz p rzy p o m n i, bo zap ro testu je , g d y zobaczy w program ie liczne definicje obiektów tej klasy (obecnie abstrakcyjnej). Jeśli fu n k cji w irtu aln ej w klasie pochodnej nie zd efin io w aliśm y celow o - to zn ac zy , ż e rzeczyw iście chcem y, by i ta klasa była abstrakcyjna. Jest to b a rd z o częste zjaw isk o . Z w ykle w hierarchii jest w iele p ięter klas abstrakcyjnych, a d o p iero te o statn ie klasy na dole reprezentują k o n k retn e obiekty.
998
Rozdz. 21. Funkcje w irtualne Klasy abstrakcyjne
Rys. 21 -3 Hierarchia wielu klas abastrakcyjnych. Tylko te ostatnie na dole tego grafu są nieabstrakcyjne, czyli tworzą rzeczywiste obiekty w programie
❖
P r z y p a d e k c)
to w sk ró cie m ów iąc przypadek b) w yposażony d o d a tk o w o w funkcję, której nigd y n ie m ożna użyć. N o, może praw ie nigdy. D la c ze g o „nigdy“ ?
Funkcja w irtu a ln a z klasy podstawowej m a szansę być ty lk o w ted y u ży ta, gdy w y w o łu jem y ją na rzecz obiektu klasy podstaw ow ej. W n aszy m p rzy p ad k u klasa p o d sta w o w a nie m a obiektów. Za pomocą w sk a ź n ik ó w i referencji też się nic d a, b o nasz w spaniały mechanizm w irtualności z a w s z e u ru ch o m i nam funkcję z w łaściw ej klasy pochodnej. D la c z e g o „praw ie" nigdy ?
D latego, ż e są sytuacje, w których m echanizm w irtu a ln o śc i nie działa. M ów iliś m y już o ty m (str. 986). Jedną z tych sytuacji jest u ży cie o p erato ra zakresu wskaznik->K3::funkcja() ;
W w y n ik u tej instrukcji ruszy do pracy funkcja c z y sto w irtu aln a z k lasy p o d staw o w ej K3 - norm alnie niemożliwa do u ru c h o m ie n ia . Inną taką sytuacją, jest w ywołanie funkcji w irtu aln ej z w n ętrza k o n stru k to ra danej k lasy podstaw ow ej. Z apam iętaj: I O definicję (ciało) funkcji czysto w irtu aln ej p o w in n iśm y się zatro I szczyć w tedy, g d y sp o d ziew am y się, iż będziem y tę fu n k c ję w yw oływ ać ja w n ie (przy użyciu operatora zakresu),
999
Rozdział. 21. Funkcje w irtualne D estruktor? to najlepiej wirtualny! *
g d y n astę p u je jej wyw^ołanie z w n ę trz a k o n stru k tó ra (destru k tora) klasy, w której jest ona cz y sto w irtu aln a.
W Z astan o w ić b y się m o żn a, co by było, g d y b y śm y w y w o łali funkcję czysto w irtu a ln ą (z u życiem o p e ra to ra zakresu), a tu n a g le o k azało się, ż e je d n a k nie m a w klasie p o d staw o w ej definicji tej funkcji czy sto w irtu aln ej — czyli że n a p ra w d ę m am y d o czyn ien ia z p rzy p ad k iem b)? Bjarne m ó w i, że w te d y efek t takiego w y w o łan ia jest n iezd efin io w an y . Jeśli tak m ów i, to zn aczy , ż e m o żesz się spodziew ać najgorszego. C iekaw ostka:
Jak zau w aży łeś, funkcja czy sto w irtu aln a zw y k le w jakim ś pok o len iu zastęp o w an a jest ju ż p ra w d z iw ą funkcją w irtu aln ą, w y k o n u jącą jakąś k o n k retn ą pracę. N ie m a je d n ak p rzeszk ó d , b y ś potem w jakiejś klasie, w kolejnym pokoleniu d zied z ic z e n ia , nie zd efin io w ał tej funkcji z n o w u jako czysto w irtualnej. O czy w iście —jeśli w d a n y m p rzy p ad k u u zn asz, ż e p o w ró t d o abstrakcyjności m a sens.
2 1 .1 0 D e s tru k to r? to n a jle p ie j w irtu a ln y! D o b rze jest przyjąć taką zasad ę : Jeśli klasa deklaruje jedną ze swoich funkcji destruktor deklarujem y także jako v i r t u a l
Z an im p o w ie m jaka z tego korzyść, p rag n ę Cię uspokoić: w irtu aln o ść d e stru k to ra jest m ożliw a, m im o że w klasie pochodnej m a o n przecież inną n a z w ę niż w klasie p o d staw o w ej. Nazwa destruktora to nazwa jego klasy, poprzedzona znakiem ~ (wężyk). Jest to oczyw iście w yjątek, b o zw ykle funkcje m o g ą być w irtu aln e ty lk o w ted y , g d y m ają id e n ty czn e nazwy i argumenty. «£» A rg u m en ty — to w p rzy p ad k u d e stru k to ra sp raw a p ro sta — k ażd y d e stru k to r musi m ieć przecież pustą listę arg u m e n tó w . N ato m iast co d o nazw y... no cóż, u d aw ajm y , że po p ro stu n azw a d e s tru która b rzm i d e s t r u k t o r - w tedy n ie b ęd ziem y się bu ntow ali, że z a s a d a została pogw ałcona. 21
N a j c i e k a w s z e j e s t c h y b a j e d n a k to, po c o je s t n a m d e s tr u k to r w irtu aln y
Ł atw o to zrozum ieć: skoro w klasie d eklarujem y jakąś funkcję w irtu aln ą, to zn ac zy , ż e zam ierzam y na obiekty klas po chodnych od tej klasy m ów ić czasam i jak na o b ie k ty klasy podstaw ow ej.
1000
!
Rozdz. 21. Funkcje w irtualne D estruktor? to najlepiej w irtualny!
Przykładow o: skoro w klasie instrum ent zd efin io w aliśm y funkcję w irtu aln ą, to znaczy, że na obiekty klas pochodnych (gitary, trąby, fortepiany) zam ierza m y czasem m ówić przezw iskiem (w skaźnikiem ) instrument. M im o ż e chcem y, by w y k o n ały akcję charakterystyczną nie dla in stru m e n tu , ale dla siebie. M ów im y: „Zagraj na tym instrum encie" - chcąc usłyszeć u p o jn e d źw ię k i forte p ia n u , a nie: nieokreślony brzdęk. Skoro tak, to m ożna się sp o d ziew ać tak że p o w ied zen ia: „zniszcz ten instrument" - p o w o d u jąceg o u ru ch o m ien ie d estru k to ra . W ażne jest, że w tedy - jeśli tym in stru m en tem b y ł fortepian pow inien ruszyć do pracy d e s tru k to r fortepianu. Tę pozornie trudną sp raw ę rozw iązuje je d n o słow o virtual p o staw io n e w klasie instrument przy deklaracji d estru k to ra. To w szystko. D zięki tem u rów nież d estru k to ry będą u ru c h a m ia n e in teligentnie. O tym, jak ważnie jest uruchomien ie destruktora właściwego obiektowi nie muszę nikogo przekonywać. Wystarczy pomyśleć sobie, że np. konstruktor klasy pochodnej f o r t e p i a n dodatkowo rezenoował jakieś miejsce zapasie pamięci. Destruktor klasy f o r t e p i a n powinien ten obszar pamięci zwolnić. W przypadku destruktora nie wirtualnego przy powiedzeniu - „proszę zniszczyć ten in stru m en t " wykonałby się destruktor klasy i n s t r u m e n t , a destruktor klasy f o r t e p i a n - nie. Miejsce nie zostałoby zwolnione. Jeśli w jakiejś klasie d estru k to r z d e k la ro w a n y jest jak o virtual, to o d tąd d e stru k to ry w szystkich klas w yw odzących się o d tej k la sy - aż d o ostatniego pok o len ia - będą także virtual.
10
Z atem , jeśli w którym ś pokoleniu b ędziem y k aso w ali in stru m e n t k lasy (bardzo) p o ch o d n ej -fle t poprzeczny „piccolo", w ów czas - m im o iż w sk a źn ik (referencja) jest do klasy in stru m en t - u ru ch am ian y b ęd zie d e s tru k to r w łaściw y klasie jjlet poprzeczny „piccolo". S koro w irtu aln y d estru k to r jest tak p rzy d atn y , to d laczeg o k ażd y d e stru k to r nie je st auto m aty czn ie w irtualny? P ow ód je st prosty - i zn am y go już. Z a w irtu aln o ść się płaci w ielkością obiektu i czasem w y konania funkcji. Tym czasem nie z a w s z e je st nam p o trze b n y ten m ą d ry m echanizm u ru ch am ian ia d e stru k to ró w . S to so w an y jest w ięc tylko na życzen ie program isty. Ż yczenie to w y rażan e je st je d n y m jed y n y m słów kiem vi rt ual um ieszczonym p rz e d deklaracją d e s tru k to ra w klasie podstaw ow ej. Nawiasem mówiąc niektóre kompilatory - zauważywszy, że w danej klasie są funkcje wirtualne - podpowiadają, żeby destruktor także uczynić wirtualnym.
Zobaczmy przykład klas z wirtualnymi destruktorami #include #include using namespace std;
Rozdział. 21. Funkcje w irtualne D estruktor? to najlepiej wirtualny!
1001
1002
Rozdz. 21. Funkcje w irtualne D estruktor? to najlepiej w irtualny! tabl_numerow[0] = rok_produkcj i ; tabl_numerow[1] = nr_seryjny;
} //- d e s tr u k to r ( w i r t u a l n y ) ----------------~gitara()
{ cout « delete
"Destruktor gitary + []
tabl_numerow;
//
©
i void wydaj_dzwiek{)
{ cout « «
"brzdek-brzdek nazwa « ")\n";
("
} );
y*************************************-*•*»**★**★****•****•*•*★■*■** +y
int main()
{ cout << "Definiujemy w zapasie pamieci\n" "trzy instrumenty orkiestry\n "/ instrum *pierwszy = new skrzypce("Stradivarius", 1736, 630); instrum ‘drugi = new gitara("Ramirez", 1997, 14822); instrum ‘trzeci = new gwizdek; // © cout << "Gramy polimorficznie ! \n";
// O
pierwszy->wydaj_dzwiek(); drugi ->wydaj_dzwiek(); trzeci ->wydaj_dzwiek(); cout << "\nKoncert sie skończył, " "likwidujemy instrumenty\n\n"; delete pierwszy; cout << string(50, delete drugi; cout << string(50, delete trzeci;
// © '*') «
endl;
'*') << endl;
return 0;
W rezultacie na ekranie zobaczymy Definiujemy w zapasie pamięci trzy instrumenty orkiestry Gramy polimorficznie ! tirli-tirli (Stradivarius) brzdek-brzdek (Ramirez) fiu-fiu Koncert sie skończył,
likwidujemy instrumenty
Destruktor skrzypiec + Destruktor instrumentu ************************************************** Destruktor gitary + Destruktor instrumentu
1003
Rozdział. 21. Funkcje w irtualne D estruktor? to najlepiej wirtualny! ************************************************** Destruktor instrumentu
Omówmy ciekawsze punkty programu W klasie i n s t r u m w id zim y funkcję w irtu aln ą wydaj d ź w i ę k (znam y to!). Skoro m am y choć je d n ą funkcję w irtu a ln ą , to stosujem y n a sz ą harcerską zasa dę, by d e s tru k to r b y l także w irtu aln y . T ak też robim y w O . Co p ra w d a , w cale nie zam ierzaliśm y definiow ać d e s tru k to ra w tej klasie instrum, ale ro b im y to dla d obra ew en tu aln y ch p rzy szły ch pokoleń. D estru k to r w tej klasie jest pusty, bo u zn ajem y , że nie m a co sprzątać przed likw idacją o b iek tu tej klasy. Jedynie - ab y w n aszy m p rz y k ła d z ie w iedzieć kiedy ten d e s tru k to r pracuje - u m ieszczam y sobie w jeg o ciele instrukcję w ypisującą na ek ran . © Klasę i n s t r u m d zied zic zą publicznie k lasy gitara, s k r z y p c e i gwizdek. O to graf:
t
instrum
gitara
sk r z y p c e
(m a d e stru k to r)
(m a destruktor)
g w iz d e k
Rys. 21-4 Hierarchia klas z programu przykładowego
Klasa g w iz d e k © jest najprym ityw niejsza, bo nie ma k o n stru k to ra ani d estru k to ra. O K lasy git a r a i s k r z y p c e są już bardziej w yszukane. Mają k o n stru k to ry , które rezerw u ją w z a p a sie pam ięci miejsce na schow anie kilku d a n y ch . (określających rok produkcji i numer seryjny instrumentu) Skoro robią tę rezerw ację, to p o w in n y m ieć d estru k to ry , k tó re o d d ad zą te o b szary pam ięci instrukcją delete. To w łaśn ie w id zim y w d estru k to rach . © D estru k to ry są w irtu aln e, choć nie stoi p rz y nich słow o virtual. W ystarczy jed n ak jeśli ra z pojaw iło się w d estru k to rz e klasy p o d staw o w ej instrum. © W funkcji m a i n w id zim y kreację trzech in stru m en tó w . Robim y to nie w zw ykły sposób, ale za pom ocą instrukcji new. A dresy tych ob iek tó w odbieram y od op erato ra n e w i zapam iętujem y w e w skaźnikach ty p u instrum*. Zatem, do tak zdefiniowanych obiektów klasy s k r z y p c e , g i t a r a , g w i z d e k tnamy dostęp jedynie za pomocą wskaźników typu: i n s t r u m * O to n am w ła śn ie chodzi, bowiem b ęd ziem y bawić się w d estru k cję obiektów klasy p o ch o d n ej p o kazyw anych za pom ocą w skaźników klasy podstaw ow ej. © N ajp ierw je d n a k g ram y na tych instrum entach krótki koncert. © K asow anie o b iek tó w u d ow adnia nam , że - m im o iż p o k azu jem y na obiekty w sk aźn ik am i d o klasy podstaw ow ej - ruszają do pracy w łaściw e destru który.
1004
Rozdz. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale... C zy li w p rzy p ad k u ob iek tu klasy skrzypce n ajp ierw d e s tru k to r klasy pocho d n ej sk rzy p ce, a potem podstaw ow ej instrum. D zięki tem u zw aln ian ie rezer w acji znajdujące się w d estru k to rze skrzypiec d o ch o d zi d o sk u tk u . Z o b acz, jak w y g ląd ałb y od nośny fragm ent na ek ran ie, g d y b y śm y zlikw idow ali s łó w k o v i r t u a l stojące przy deklaracji d e stru k to ra
Koncert s i e skończył, likwidujemy instrumenty Destruktor instrumentu Destruktor instrumentu Destruktor instrumentu A w ię c d estru k to ry klas pochodnych nie b y ły b y u ru ch o m io n e. K lasa g w i z d e k nie m a d estru k to ra, więc jej jest w sz y stk o jedno, czy d estru k to r w k la sie po d staw o w ej jest w irtualny, czy nie. A le tylko jej. In n y m klasom to m o ż e b a rd z o przeszkadzać.
P o d su m u jm y więc, co jest istotą tego p rzy k ła d u . Dzięki tem u , że w klasie p o d staw o w ej (zaw ierającej jakieś funkcje w irtualne) zd efin io w aliśm y d e s tru k to r jako w irtu a ln y - m o żliw e jest p o p raw n e kasow anie n aw et tak ich ob iek tó w , na k tóre p o k a z u jem y w sk aźn ik iem do klasy p o d staw o w ej. N a koniec, w ram ach dygresji, zagadka: Co by było, gdyby destruktor zo klasie podstawowej i n s t r u m był zdefi niowany jako czysto zuirtualny? Jak by to zmieniło proces likwidacji obiektu klasy g w iz d e k ? Gwizdek swojego destruktora nie miał, urucha miał tylko ten od instrumentu. Co by uruchamiał teraz ? O d p o w ie d ź brzm i: N a w e t się nie zastan aw iaj, co by g w i z d e k u ru ch am iał. O b iek tu klasy gwizdek w ogóle by nie było. P am iętasz zap e w n e, że funkcja czy sto w irtu aln a d zied ziczy się jako czysto w irtu a ln a . S koro gwi zde k w swojej klasie nie m a w łasnej w ersji d estru k to ra, to ta cecha czystości w irtu aln ej p rz e sz ła na klasę gwi zde k - i o n a p rzez to stała się klasą abstrakcyjną. C zyli już w linijce definicji obiektu klasy gwi z d e k k o m p ila to r zap ro testu je.
T y tu ł tego paragrafu sp raw ił, że zaczy n asz czeg o ś żało w ać. W ierz m i, n ie m a pow odu. K ied y b y łby po trzeb n y k o n stru k to r w irtu aln y ? W te d y g d y b y śm y p o w ied zieli tak:
Rozdział. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale...
1005
C hcę, ab y p o w stał o b iek t klasy - nie w ie m jeszcze jakiej. T eraz, w trak cie pisania p ro g ra m u , w iem tylko, ż e k lasa ta będzie klasą p o c h o d n ą od w iadom ej m i ju ż klasy (np. in stru m e n t). T eraz n ie w iem jednak jeszcze jak daleki p o to m e k in stru m en tu to b ęd zie. To, jakiej d o k ła d n ie klasy ma b y ć ten o b iek t (czyli jaki d o k ła d n ie będzie to in stru m en t), okaże s ię d o p ie ro w trakcie w y k o n an ia p ro g ram u . C hcielibyśm y z a te m , by m ożliw a była taka instrukcja new w i r t u a l n y _ k o n s t r u k t o r _ m s t r u m e n t u ;
O czyw iście to n iem ożliw e. Już ch y b a w id zisz dlaczeg o . A by m ógł zad ziałać polim o rfizm (czyli w y w o łan ie o d p o w ied n iej funkcji w irtu a ln e j) m usi być w sk a źnik lu b referencja d o obiektu, którego klasę się ro zp o zn a . T ym czasem k o n stru ktora nie w y w o łu je się dla obiektu, bo on jeszcze nie istnieje. N ie ma w ed łu g czego ro z p o z n a w a ć . Z resztą zo b acz - tak w y w o ły w aliśm y zw ykłe funkcje s k ła d o w e obiekt . funkcja _składowa(); wskaźnik -> funkcja_składowa(); referencja . funkcja _składowa(); ty m czasem k o n stru k to r w yw ołuje się bez tego zapisu z k ro p k ą . lub -> P iszem y p o p ro stu k o n s tr u k to r O ;
Czyli n a s z k o m p ilato r nie m oże się tu zastan o w ić n a co, w m om encie w y k o n an ia p ro g ra m u , pokazuje w sk aźn ik lub co jest p rz e z y w a n e referencją.
N iby to ro z c z a ro w a n ie - ale w takich chw ilach ro zczaro w an ia trzeba pom yśleć o tym , co w z a sa d z ie chcieliśm y osiągnąć i czy czasem , rozum ując, nie poszliśm y z u p e łn ie b łędnym tropem . Z a t e m u d a jm y , ż e nic s i ę nie sta ło i p o m y ś l m y od p o c z ą t k u . C o c h c e m y osiąg n ąć:
C hcielibyśm y, by m ożna było pow iedzieć tak: m am w p ro g ra m ie obiekty ró ż nych klas. Tu na p rzykład klasa instrum enty. Z araz w y p ro d u k u jem y jakiś k o n k retn y in stru m en t. N a razie nie w iadom o, jakiej klasy pochodnej konkretn ie. O k a ż e się to dopiero w ostatniej chw ili. W trakcie w ykonania p ro g ram u p rzy ch o d z i jednak taki m om ent, kiedy już w iad o m o . O to m onolog w e w n ętrzn y k o m p u tera: Człowiek obsługujący program odpozoiada mi, że to ma być instrument klasy ... Na czym polega taka odpowiedź? Ponieważ ja, komputer nie rozumiem ludzkiego głosu, więc ta odpowiedź polegać może np. na pokaza niu m i (wskaźnikiem) jakiegoś istniejącego obiektu danej klasy.
1006
Rozdz. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale...
Patrząc na obiekt, który mi wskazano, zorientuję się według niego (wg typu obiektu) i stworzę obiekt tego samego typu. (Tu nastąpi polimorfizm, czyli przy tej orientacji wykażę się wirtualnością czyli inteligencją). W opow ieści tej, m im o że m ow a o w irtu aln o ści, nie m u si istnieć konstruktor w irtu aln y . W zasad zie m ógłbym już po k azać rozw iązan ie, ale z atrzy m am się jeszcze. Otóż nie tylko, że ro zw iążem y ten problem , lecz n a w e t d o starczy m y d w a w arianty rozw iązania
«$♦ N o w o w y tw o rzo n y obiekt m oże być tw o rzo n y tak, jakby działał k o n s tru k to r d o m n ie m a n y - czyli by now y o b iek t m iał stan d ard o w e w artości, jakie m ają now orodki tej klasy. N o w y obiekt m oże też pow stać tak, jakby był z ro b io n y k o n stru k to re m k o p iu jący m . C zyli będzie m iał w arto ści takie, jakie m iał p o k aza n y w os tatniej chw ili obiekt p rzy k ład o w y . Co p ra w d a , do tej p o ry interesow ał nas tylko jego ty p (czy fortepian, czy trąbka, czy bęben), ale co nam szk o d zi zain tereso w ać się jego bliższym i d an y m i (skoro ju ż fortepian, to jakiego koloru, ile w aży i kto jest p ro d u cen tem ). N o w o ro d k a w y p o saży m y w te sam e cechy. Tajem nica ro zw iązan ia problem u k o n stru k to ra w irtu aln eg o p o leg a na tym , że nie trzeba tego robić za pom ocą k o n stru k to ra. Z robim y to z a p o m o cą w irtu al nych funkcji składow ych. O czyw iście n ad ać im m o żn a d o w olne n azw y . Ja zastosuję takie: •
stworz nowy dziewiczy - funkcja zastę p u jąca w irtualny
k o n stru k to r d o m n iem an y , •
stworz_nowy_wzorowany - funkcja zastę p u jąca w irtu aln y k o n stru k to r kopiujący,
O to p ro g ra m
#include using namespace std;
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllflllli class strunowy! public: int liczba_lat; // . . . strunowy() : liczba_lat(0) i )
ii 6
//k o s tr u k to r d o m n ie m a n y
li
virtual strunowy* .stworz_nowy_dziewiczy () = 0; virtual strunowy* stworz_nowy_wzorowany() = 0; //---------------------------------------------------------------------------virtual void jestem() = 0;
jljlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllltLl class skrzypce : public strunowy f II . . . strunowy* stworz_nowy_dziewiczy( )
//ty //
©
1007
Rozdział. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale...
return
new skrzypce; _____________
)
strunowy*
// w y w o ła j k o n s tr u k to r d o m n ie m a n y
stworz_nowy_wzorowany()
return new skrzypce (*this) ;
//w y w o ła j k o n s tr u k to r
k o p iu ją c y
} //public: void jestem() cout << "Jestm klasy skrzypce, mam lat << liczba lat << endl; } };
iiiiiiiiiiiiiitiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiMiiiiiiiiiiiiiMi class wiolonczela : public strunowy t u ... strunowy* stworz_nowy_dziewiczy()
Ireturn new wiolonczela () ; // ^ w y w o ł a j } II ------------------------------------------------------- --------------strunowy* stworz_nowy_wzorowany()
k o n s tr . d o m n ie m a n y
(return new wiolonczela (*this); //^ - w y w o ł a j k o n s tr . } I -----------------------------------------------------------------------public: void jestem(){ cout « "Jestm klasy wiolonczela, mam lat = " << liczba_lat « endl; }
k o p iu jq c y
)l]llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllljllIL int main() { skrzypce wiolonczela
''
**
skrz; wiol;
;iol. liczba lat = 157;
// n ie c h b ę d z ie ta k stara
skrz.jestemf); wiol.jestem() ; cout « "Teraz będziemy wirtualnie konstruować "dodatkowe obiekty\n"; strunowy * wskaźnik; wskaźnik = &skrz; strunowy * wskl; wskl = wskaznik->stworz_nowy_dziewiczy(); II w s k a ź n ik p o k a z y w a ł na s k r z y p c e w ię c p r z e k o n a jm y się c z y to II p o w s ta ły n a p ra w d ę skrzy p c e .
wskl->jestem() ; 11-------- ------------wskaźnik = &wiol;
// © // ©
1008
Rozdz. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale...
// n a s t ę p n a
t a k k r e o w a n a w io l o n c z e la n ie c h b ę d z i e d o k ła d n ą k o p ią t e j s t a r e j
strunowy * wsk3 = wskaznik->stworz_nowy_wzorowany () ; wsk3->jestem();
//O©
delete wskl; delete wsk 2 ; delete wsk3;
//O ©
Po wykonaniu programu na ekranie zobaczymy Jestm Jestm Teraz Jestm Jestm Jestm
klasy skrzypce, mam lat = 0 klasy wiolonczela, mam lat = 157 będziemy wirtualnie konstruować dodatkowe obiekty klasy skrzypce, mam lat = 0 klasy wiolonczela, mam lat = 0 klasy wiolonczela, mam lat = 157
Jak to działa? O W programie widzimy klasę strunowy - inaczej mówiąc,,instrument struno w y J e s t ona klasą abstrakcyjną, bo widzimy w niej funkcje czysto wirtualne. W naszym przykładzie nie było to konieczne, ale dzięki temu deklaracja klasy jest czytelniejsza - bo krótsza. (Nie muszę definiować tych funkcji). W klasie jest składnik liczba lat, w którym będzie zapisane, ile to lat ma dany instrument. Jest też konstruktor domniemany. W zasadzie tylko po to, by nam do nowych instrumentów (strunowych) wpisywał wiek: 0 lat. Składnikami są też dwie funkcje wirtualne, które zastąpią nam niemożliwe do realizacji wirtualne konstruktory. Spójrz na typ rezultatu tych funkcji. Jest tam strunowy*, czyli funkcje zwracać mają adres nowonarodzonego instrumentu strunowego. Funkcja s t w o r z _ n o w y _ d z i e w i c z y będzie zajmowała się wirtualną produkcją nowych instrumentów, których składniki będą inicjalizowa ne wartościami domniemanymi. Funkcja s t w o r z _ n o w y _ w z o r o w a n y będzie zajmowała się wirtualną produkcją nowych instrumentów, a ich składniki będą inicjalizowane wartościami zobaczonymi u wzorca. Dodatkowo widzimy też funkcję wirtualną jestem, za pomocą której instru ment może powiedzieć coś o sobie. © Klasa s kr zypce jest klasą pochodną od instrumentu strunowego. Widzimy w niej realizacje funkcji wirtualnych. (Słowa vir t u a l już nie trzeba powtarzać) Funkcja s t w o r z _ n o w y _ d z i e w i c z y po prostu stwarza operatorem newobiekt klasy skrzypce. Robi to za pomocą konstruktora domniemanego klasy skrzypce.
1009
Rozdział. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale... stru n o w y * s k r z y p c e : : s tw o rz _ n o w y _ d z ie w ic z y ( ) re tu rn
new s k r z y p c e ;
// wywołaj konstruktor domniemany
)
Co prawda, w tej klasie nie definiowaliśmy konstruktora domniemanego, ule pamiętasz chyba, że jeśli nie ma żadnego konstruktora, to kompilator automatycznie wygeneruje nam taki konstruktor. To nie ma nic wspólnego z omawianym zagadnieniem. Mogliśmy równie dobrze taki konstruktor zdefiniować- nie zrobiłem tego jednak, by było prościej. F u n k cja s t w ó r z nowy w z o ro w a n y także s tw a rz a o p erato rem new obiekt k lasy s k r z y p c e . Z tą ró żn icą, że robi to za p o m o cą k o n stru k to ra kopiującego k lasy sk rzy p ce. Również i tego konstruktora nie definiowaliśmy, ale pamiętasz chyba, że kompilator zawsze stara się taki konstruktor wygenerować nam automaty cznie. Tu się na ten godzimy. (Jeśli by nam nie odpowiadał, to możemy również zdefiniować lepszą wersję). Z a u w a ż , że n asze funkcje w irtu aln e po p ro stu w y w o łu ją k o n stru k to ry , które sa m e w irtu a ln y m i być n ie m ogą. -y* W k la sie w id zim y też funkcję w irtu aln ą j e s te m - i z jej ciała o d czy tu jem y , co b ę d z ie n a m p o k azy w ała na ekranie. Poniżej w id z im y d e k la ra c ję n a s t ę p n e j klasy
To in n a klasa p o ch o d n a od abstrakcyjnego in stru m e n tu stru n o w eg o . Jest to w i o l o n c z e l a . Jak łatw o z o b a c z y ć -je s t z b u d o w a n a po d o b n ie jak s k r z y p c e . R óżnica jest oczyw iście ta, że jej funkcje w irtu aln e p ro d u k u ją jej typ obiektu. O W funkcji
main
d o k o n u je s ię c a ł e m isteriu m p ro d u k cji w irtualnej
N a jp ie rw w id zim y definicje obiektów różnych klas. Te obiekty w p ro g ram ie słu ży ć n a m m ogą do różnych celów, jednak w ty m p rzy k ład zie u ży jem y ich tylko d o tego, by pow iedzieć: „P roszę stw orzyć in stru m e n t stru n o w y takiego
ty p u ." Skoro takiego —to znaczy, ż e m u sim y mieć w sk aźn ik d o pok azy w an ia na in stru m e n ty stru n o w e. Jego definicję w idzisz w © . W m o m en cie jego definicji (i w og ó le w czasie kom pilacji) nie w iadom o jeszcze n a jaki konkretny in stru m en t b ę d z ie o n pokazyw ał. Jest to w skaźnik do in stru m e n tó w s tr u n o w y c h , ale to nie problem —w iem v przecież, że taki w skaźnik n ad aje się rów nież d o p o k azy w a n ia n a obiekty ty p ó w pochodnych od in stru m en tó w strunow ych. (Czyli b ęd zie m ógł ew en tu aln ie p o k azać na w iolonczelę lu b skrzypce). @ Ju ż w trakcie wykonywania pro g ram u m ożem y za p y ta ć operatora o n u m er buta i d a tę u ro d zen ia. N a p o d staw ie tej informacji d ecy d u jem y , jaki obiekt stw o rzy m y m u (w irtualnie) na u ro d z in y . Kiedy już zadecy d o w aliśm y , odnajdujem y d o w o ln y obiekt takiego ty p u >ustaw iam y na niego w skaźnik. T en ż a rt z n um erem buta - to p o to, byś uw ierzył, że w linijce © n a p ra w d ę w czasie p isan ia program u nie w iadom o, jakiego ty p u stw o rzy m y tu obiekt. To się o k aże d o p ie ro w trakcie w y k o n an ia program u. U tw o rzo n y obiekt będ zie nie
1010
Rozdz. 21. Funkcje w irtualne Co praw da, konstruktor nie może być w irtualny, ale... takiego typu, jak w skaźnik (czyli in stru m en t strunowy), a le tak ieg o typu, jak obiekt, na który ten w skaźnik w łaśnie będzie pokazyw ał. (P olim orfizm ). U nas w skaźnik pokazuje w łaśnie na obiekt klasy s k r z y p c e (tak wynikło z num eru buta) w ięc polim orficznie uruch o m io n a zostanie funkcja w irtualna z klasy skrzypce. © Skoro w jej ciele jest w yw ołanie konstruktora klasy skrzypce, w ięc taki obiekt w łaśnie się narodzi. Dzieje się to za sp ra w ą operatora n e w - a, jak w iadom o, tak u ro d zo n e obiekty nie mają nazw . O p erato r new daje nam a d re s n o w eg o obiek tu, w ięc m y p o w in n iśm y sobie zdefiniow ać w skaźnik d o tak ieg o obiektu i do niego ten o trzy m an y ad res wpisać. To dlateg o o linijkę wyżej, czyli w © , w id zim y definicję w sk a ź n ik a , a w © po prostu w pisujem y d o tego w skaźnika ad res now ego o b iek tu . strunowy * wskl; wskl = wskaznik->stworz_nowy_dziewiczy();
// © // ©
W skaźnik jest zn o w u kolejnym w skaźnikiem d o in stru m e n tó w strunowych, ale to znow u nie p rzeszk ad za - m oże on p o kazyw ać na w ła ś n ie stw orzone skrzypce. © D la n ied o w iark ó w obiekt p rzed staw ia się - okazuje się, że n a p r a w d ę pow stały skrzypce. (Ta funkcja, to jakby p o w tó rzen ie starej znajo m ej funkcji w y d a j dźwięk). D zięki w ypisow i na ekran, przekonujem y się w ięc, ż e pow stał obiekt klasy skrzypce, a nie klasy in stru m e n t strunowy.
Zresztą, skoro klasa s t ru n o w y ma funkcje czysto wirtualne - to znaczy, że jest klasą naprawdę abstrakcyjną. Utworzenie obiektu klasy instrument s tr u n o w y byłoby po prostu niemożliwe! © O to p rzy k ład tw o rzen ia innego obiektu tą techniką. Tu u s ta w ia m y n a sz w skaź n ik na jakiejś w i o l o n c z e l i . o o O to m o m en t stw o rzen ia now ej wiolonczeli. strunowy * wsk2 = wskaznik->stworz_nowy_dziewiczy () ;
Ta linijka w y g ląd a trudniej, dlatego ż e jest rów nocześnie d efin icją w skaźnika ws k 2 i w y w o łan iem naszej funkcji w irtualnej. In n y m i sło w y , to jak b y połącze nie linijek © i © . O © Jeśli chcielibyśm y teraz stw o rzy ć n astęp n ą w iolonczelę, ale b ę d ą c ą p ra w d z iw ą kopią już istniejącej, to w ystarczy, że w y w o łam y n aszą fu n k cję s t w o r z nowywzorowany. O na zastąp i nam k o n stru k to r kopiujący. S pójrz na ekran, a zobaczysz do w ó d , że o statn io s tw o rz o n a w iolonczela m a n a p ra w d ę tyle sam o lat, co ta przesłana n a w zór. O © P o d koniec p ro g ra m u , oprócz zw ykłych dw óch obiektów , istnieją w ięc d o d a t k o w o jeszcze te trzy , k tóre w irtu aln ie k reow aliśm y. M o żem y je skasow ać o p erato rem delete.
Rozdział. 21. Funkcje w irtualne Rzutowanie d y n a m i c _ c a s t jest dla typów polimorficznych
1011
Wniosek: K o n stru k to ry nie m ogą być w irtu aln e, bo nie są p rzecież funkcjam i w y w o ły w a n y m i n a rzecz obiektów . T o nic nie szkodzi. T e sam ą pracę m ogą dla nas w y k o n a ć n ap isan e p ro sto a u m iejętnie - w irtu a ln e funkcje składow e.
ł1.12 Rzutowanie dynamic_cast jest dla typów polimorficznych R o zm aw iając o rzu to w an iu s t a t i c _ c a s t zo b aczy liśm y p ew n ą "niesym etry czną" s y tu a c ję - to znaczy: konw ersja z ty p u S a m o c h ó d na ty p P o j a z d m a sens, każdy samochód jest pojazdem, ale o d w ro tn a jest ry zy k o w n a, w ięc co p raw d a, m o ż em y ją jaw nie do k o n ać, ale ty lk o n a w łasn ą o d p o w ied zialn o ść, bo nie każdy pojazd jest samochodem. T u z p o m o cą p rzy ch o d z i n am operator rz u to w a n ia d y n a m i c _ c a s t . Jest on w ła śn ie p o to, by nie było to "w yłącznie na n aszą w łasn ą odpow iedzialność". i
itiUM W W W illiW JW TrrW M Ilf r i H r n
................ < « ■ » ■ —
O perator d y n a m i c _ c a s t zrobi rzutowanie tylko pod warunkiem, że w danej konkretnej chwili ma to sens. Jeśli nie jest m ożliwe - poinformuje nas o tym (już w trakcie wykonyw ania programu). wwi iiiiWiM ..
... mi
P rzy jrzy jm y się teraz tej sy tu acji bliżej. Z ałó żm y , że w n aszy m p ro g ram ie m am y d w ie klasy: klasę p o d staw o w ą P o j a z d i p o ch o d zącą od niej klasę Sam ochód. Jak w iesz - w m yśl k onw ersji standardow ej - w sk aźn ik d o obiektu klasy S a m o c h ó d m oże być p rz y p isa n y do w skaźnika d o obiektu klasy P o ja z d . T o sam o bardziej form alnie: W skaźnik do obiektu klasy p o ch o d n ej m oże zostać niejaw nie p rzekształcony na w skaźnik d o (d o stęp n ej jednoznacznie) klasy podstaw ow ej. N a to m ia s t o d w ro tn e tw ie rd z en ie nie jest p ra w d z iw e . Jeśli w pro g ram ie m am y w sk a ź n ik d o p o jazd u , to przecież ten w skaźnik, w trakcie pracy p ro g ram u , m o że p o k azy w ać na ró żn e obiekty. C zasem sam ochody, czasem row ery, czasem p ro m y kosm iczne. W norm alnych w a ru n k a c h kom pilator nie pozw oli w ięc p rzy p isać bieżącej treści w skaźnika po jazd ó w do w sk aźn ik a sam o ch o d ó w . Z atem te ra z p o m arzm y sobie trochę: Fajnie by było, gdyby komputer sobie sprawdził, czy pod danym (pokazy wanym przez wskaźnik do klasy podstawowej Pojazd) adresem jest teraz obiekt typu Samochód czy też może obiekt typu Rower. •
Jeśli to sam ochód - niech p rzy p isan ie adresu się o d b ędzie.
•
Jeśli nie sam ochód: to niech na p rzy k ład w pisze n am adres zerow y.
1012
Rozdz. 21. Funkcje w irtualne Rzutowanie d y n a m i c _ c a s t jest dla typów polimorficznych To n asze m arzenie ziszcza w łaśnie o p erato r dynamic_cast. Słow o dynamie ma p rzy p o m in ać, ż e odbyw a się to d y nam icznie, zatem ju ż w trak c ie w ykony w an ia tej linijki p ro g ram u . (A nie w trakcie kom pilacji jej). Jest tylko w arunek:
Dlaczego? Po prostu dlatego, że operator d y n a m ie c a s t musi dysponować jakimiś danymi na temat: "kto jest potomkiem kogo w danej rodzinie klas". Takie informacje (naioet bez naszej wiedzy) gromadzi klasa mająca funkcję wirtualną. To tam sięgnie operator d y n a m ic _ c a s t. O to p rzy k ład ilustrujący użycie tego operatora / / Zakładamy, że klasa Pojazd ma clioć jedna funkcję wirtualną Pojazd p; Samochód s; Pojazd *wsk_poj; Samochód *wsk sam;
// o // 0
II ustawienie wskaźnika pojazdów na jakiś tam pojazd (może samochód, a może rower) //... tajemnicza instrukcja, na przykład: wsk_poj = &s; // wsk_sam = dynamic_cast(wsk_poj) ; i f (wsk_sam) cout « «
"Pokazywany pojazd jest teraz samochodem" end;
} else
// ©
* cout « "Blad, teraz pojazdem nie jest samochód" << endl; }
Przyjrzyjmy się kilku miejscom tego fragmentu programu O Definicja obiektu k lasy Po j a zd. O biekt m a nazw ę p. W n astęp n ej linijce w idzisz definicję obiektu k lasy Samochód. O biekt ten m a n azw ę s. 0 Definicja w sk aźn ik a m ogącego p o k azy w ać na obiekty k lasy Pojazd. O biekt ma n azw ę wsk p o j . W następnej linijce w id zim y definicję w sk a źn ik a m ogącego pojazyw ać na Samochody. Ż ad n eg o z tych w sk a źn ik ó w nie u staw iam y na razie na nic ko n k retn eg o . © P ro g ram się w y k o n u je i w jego trakcie w sk aźn ik d o p o ja zd u czasem pokazuje na taki lu b inny p o jazd . Tego tutaj w p ro g ram ie nie ma. Z ałó żm y , że w końca następ u je u staw ien ie w sk aźn ik a tak, by p o k azał na jakiś k o n k retn y obiekt. Tutaj napisałem to p rzy p isan ie w k o m en tarzu , ale u d ajem y , ż e n ie w iem y, co teraz jest w tym w sk a źn ik u do pojazdu.
Rozdział. 21. Funkcje w irtualne Rzutowanie d y n a m i c _ c a s t jest dla typów polimorficznych
1013
O To jest najważniejsza linijka tego przykładu P rz y ch o d zi chw ila, g d y chcielibyśm y w y k o n ać jakąś akcję, pod w aru n k iem , że ten p o ja z d to n a p ra w d ę sam o ch ó d . Tutaj o to w id zisz w łaśn ie linijkę p rzy p i san ia treści w sk a źn ik a p o ja zd ó w d o w sk aźn ik a sam o ch o d ó w . wsk sam = dynamic_cast(wsk poj);
O d b y w a się to p rz y p o m o c y operatora rz u to w a n ia dynamic_cast. W ostrym n a w ia sie tego o p erato ra napisaliśm y, że rz u to w a n ie m a n astąp ić na typ < S a m o c h ó d * > czyli na w sk a źn ik d o sam o ch o d ó w . Co z ro b i tu ten o p erato r? «$» Jeśli w sk aźn ik d o p o jazd ó w ws k_po j w łaśn ie jest u sta w io n y n a obiekt klasy Samochód, to ta konw ersja n astąp i. W rezultacie a d re s z e w skaź nika wsk_poj z n a jd z ie się także w e w sk a źn ik u wsk_sam. Jeśli jed n ak w sk a ź n ik do pojazdów jest obecnie u sta w io n y na zu p ełn ie in n y ty p p o ja zd u , to konw ersji nie b ęd zie - czyli w arto ścią tego w y rażen ia rz u to w a n ia będzie adres 0 (zw an y daw n iej NULL). To zero zo stan ie p rz y p isa n e d o w skaźnika wsk_sam. W ten oto sp o só b zosta jem y p o w ia d o m ie n i o niem ożliw ości sen so w n eg o rzu to w an ia. ©
O to s p ra w d z e n ie re z u lta tu rzu to w an ia. Jeśli treść teg o w sk a ź n ik a jest n ie zero w a, to znaczy, że rzu to w an ie było m ożliw e. W n aszy m p rzy p ad k u o z n a c z a to, że w sk a z y w a n y w danej chw ili p o jazd był n a p raw d ęsam o c h o d em .
0 Jeśli zaś w sk a zy w a n y w d an ej chwili obiekt sam o ch o d em nie był, w e w sk aźn ik u w sk sam zn alazło się te ra z zero. W tedy zn ajd u jem y się tutaj, w g ałęzi e l s e .
X Dla bardzo, bardzo zaawansowanych! T ego tek stu zd ec y d o w an ie nie czytaj za p ie rw szy m razem . P rzejdź poniżej, do m iejsca, gdzie w id zisz najbliższy "listek w inorośli". A z a te m eksperci - u w ag a: M oże się zd arzy ć, że o p erato rem tym rz u to w a ć chcem y nie "w skaźnik na in n e g o ty p u w skaźnik", ale "referencję na in n eg o typu referencję" . N aw iązu jąc d o naszego ostatn ieg o p rzy k ła d u , chodzi o taką konstrukcję Samochód &ref_sam = dynamic_cast( r e f p o j );
Jeśli rzu to w an ie się u d a, to referencja r e f sam staje się p rzezw isk iem p o k azy w a n e g o w łaśnie referencją r e f p o j obiektu. A jeśli rzu to w an ie się nie u d a? Tu jest problem , bo w przy p ad k u rzu to w an ia na w sk aźn ik - rezul tatem pracy tego operatora staje się w ted y ad res 0. Ten w łaśnie (zerow y) a d re s w pisyw any zo staw ał do w sk aźn ik a p o lewej stronie znaku przypisania. Tutaj tak się nie da, bo nie może być referencji d o ad resu 0.
1014
Rozdz. 21. Funkcje w irtualne Rzutowanie d y n a m i c _ c a s t jest dla typów polimorficznych Jak w ięc b ie d n y op erato r d y n a m i c _ c a s t ma nas p o w iad o m ić, że tej konwersji bieżąco zro b ić się nie da? Jest na to ciekaw y sposób. O perator ten rzuca w yjątek ty p u b a d cast. Skoro jesteś b ard zo zaaw an so w an y , to oczyw iście w iesz, co to z n a c z y ć Oto, jak w y g ląd ałb y w tedy o d nośny frag m en t pro g ram u / / Zakładami/, żc klasa Po jazd ma choć jedną funkcję wirtualną Pojazd p; Samochód s; Rower r;
// stworzenie i ustawienie referencji pojazdów na jakiś tam pojazd (może samochód, // a może rower) //... tajemnicza instrukcja, na przykład: Pojazd ref_poj = s;
// albo //
Pojazd ref_poj = r;
try { Samochód &ref_sam = dynamic_cast (ref_poj ) ; cout « "Pokazywany pojazd byl samochodem"
<< e n d ; _ // referencja stała się więc przezwiskiem jednego z samochodów // możemy teraz dalej z nim pracować II.............. } catch(bad_cast)
{ cout << "Blad, teraz pojazdem nie jest samochód"
<< e n d l ; // referencji nie udało się jej zrobić } Jak w id zisz, rz u to w a n ie um ieściłem w blo k u t r y . Jeśli się u d a , to w ram ach tego bloku o d b ę d z ie się zam ierzo n a praca z obiektem p o k a z y w a n y m p rzez referen cję. Jeśli z a ś rz u to w a n ie się nie u d a, to o p erato r d y n a m i c _ c a s t rzuci w yjątek typu b a d _ c a s t . M usim y go złapać instrukcją c a t c h , a p o te m już zachow ać się sto so w n ie d o tej sytuacji niem ożliw ości rzu to w an ia na d a n y typ.
Jak w idać, o p e ra to r rzu to w an ia d y n a m i c _ c a s t różni się tym o d s t a t i c _ c a s t tym , że rzu to w an ia nie robim y n a ślepo, na w łasn e ry zy k o , lecz p ro sim y k o m p u te r o pom oc. c a s t s p ra w d z a nas ty lk o w trakcie pracy k o m p ilato ra, to d y s p ra w d z a nas ju ż w trak c ie pracy p ro g ram u . 5)
Liczę na to, bo tu w "Symfonii C++ standard" o wyjątkach nie rozmawiamy, poświęciłem im osobną książkę pt. "Pasja C++".
Rozdział. 21. Funkcje w irtualne Wszystko, co najważniejsze
1015
Operatorem d y n a m ic _ c a s t po prostu prosimy kompilator o pomoc, by wygenerował taki kod, który będzie spraiodzał (w trakcie wykonywania programu) czy nie popełniamy straszliwego błędu.
£1.13 Wszystko, co najważniejsze G d y b y m n i e k to ś z a p y ta ł, j a k a m y śl z t e g o r o z d z ia łu , a m o ż e całej książki s ą najw ażniejsza,
to p o w ied ziałb y m , że ta z e strony 994 i stro n y 1000 (oznaczyłem je tam znaczka m i T). W miejscach tych, p o raz kolejny, ro zm aw ialiśm y o tym , jaka jest p rzesła n k a, by jakąś funkcję u czy n ić w irtualną. C y tu ję fragm ent p ierw szy: Panie i Panowie - jest to klasyczna sytuacja, kiedy nasuwa się użycie funkcji wirtualnej: Wskaźnikiem do klasy abstrakcyjnej f i g u r a pokazu jemy obiekt klasy pochodnej (kw adra t lub t r ó j k ą t ) , a żądamy wyko nania funkcji właściwej dla tej klasy pochodnej. F ra g m e n t drugi: ... skoro w klasie instrument zdefiniowaliśmy funkcję wirtualną, to znaczy, że na obiekty klas pochodnych (gitary, trąby, fortepiany) zamierzamy czasem mówić przezwiskiem (wskaźnikiem) instrument. Mimo iż chcemy, by wykonały akcję charakterystyczną nie dla instrumentu, ale dla siebie. Mówimy: „Zagraj na tym instrumencie " - chcąc usłyszeć upojne dźwięki fortepianu, a nie: nieokreślony brzdęk. **n G d y b y m b ył e g z a m in a to re m z C++, to na e g z a m in ie z a d a w a łb y m z a w s z e p y ta n ie : P ro szę p o w ie d z ie ć , ja k ie są p rz e s ła n k i, b y ja k ą ś fu n k c ję s k ła d o w ą d a n e j kla sy u c z y n ić fu n k c ją w irtu a ln ą i n a c z y m p o le g a k o rz y ś ć .
A b y ś t a k i e g o e g z a m i n u nie o b lał - oto j e s z c z e r a z p r z y k ł a d o w a s y t u a c j a
W pro g ram ie m am y w iele klas, które rep rezen tu ją jakąś sytuację z życia w ziętą. M oże to być zag ad n ien ie obsługi rezerw acji biletów lotniczych, a m oże to być sytu acja sterow ania poszczególnym i u rząd z en ia p rom u kosm icznego.^ W prom ie kosm icznym ko m p u ter steruje w ielom a m odułam i - na p rzy k ła d w celu dokonania pom iaru. W yobraźm y sobie, że m am y do czynienia z w ielom a b a rd z o różnym i u rząd zen iam i mającymi: d o k o n ać oceny sytuacji (pom iaru), a n astęp n ie zanalizow ać otrzym ane dane. N iech to będą takie u rząd zen ia jak:
6)
Tylko mi nie mów, że nie będziesz sterował promem kosmicznym. Żebyś Ty wiedział, od jakich czytelników Symfonii dostawałem już listy opisujące, jak potoczyły się ich dalsze, programistyczne, losy...
1016
Rozdz. 21. Funkcje w irtualne Wszystko, co najważniejsze •
w oltom ierz cyfrow y,
•
m iernik tem p eratu ry ,
•
detektor cząstek.
Z ałóżm y, że zdefiniow aliśm y ju ż w naszym p ro g ram ie trz y klasy w łaśnie takiego ty p u . Pytanie:
-C o dalej? Gdzie tu możemy skorzystać z polimorfizmu i funkcji wirtualnych? 1. P o p i e r w s z e t r z e b a z a u w a ż y ć , t e t r z y k l a s y t w o r z ą h i e r a r c h i ę
(Z astanów się te ra z przez chw ilę - ja k ą ...). O tóż, w sz y stk ie je m ożem y u czy n ić pochodnym i o d abstrakcyjnej klasy „u rz ą d z e n ie p o m iaro w e". Jakie m ają o n e w sp ó ln e zachow ania? To - już sobie p o w ied zieliśm y : N a d a n ą kom endę m ają one dokonać p o m ia ru , a n astęp n ie ocenić ten pom iar. U m ów m y się, ż e d o k o n a tego funkcja sk ład o w a o n azw ie z m i e r z _ i _ o c e n ( ) . ; Skoro funkcja ta jest w spólna d la w szystkich n aszy ch klas - „w oltom ierz cyfrow y", „m ie rn ik tem p eratu ry " i „d e te k to r cząstek" - u m ieścim y tę funkcję w klasie abstrakcyjnej „u rząd zen ie p o m iaro w e". 2. C z y ta funkcja p o w in n a b yć w irtualna czy nie?
Jeśli nie w iesz - to zadaj sobie n a stę p n e pytanie: C zy funkcja z m i e r z _ i _ o c e n () będzie ta k a sa m a dla w szystkich tych klas pochodnych? To znaczy: taka s a m a d la w oltom ierza cyfrow ego, jak dla d etek to ra cząstek? Jeśli o d p o w iesz sobie „-Nie, wręcz przeciwn ie, każde z tych urządzeń zrobi to na swój własny sposób" - to znaczy, że ow a funkcja p o w inna b y ć w irtu a ln a . M ógłbyś zap y tać: -Jaki sens, wobec tego, umieszczać tę funkcję w klasie abstrakcyjnej, skoro i tak dla woltomierza musimy uruchamiać wersję „woltomierzową", a dla detektora cząstek „detektorową". 3.
O t ó ż , k o r z y ś ć p o j a w i Ci s i ę w t e d y ,
g d y d o d an y ch u rz ą d z e ń (obiektów ) zaczniesz zw racać się n ie za pom ocą ich nazw , ale p o k azu jąc je w sk aźn ik iem - i to w d o d atk u w sk aźn ik iem nie ty p u klasy tego o b iek tu (np. wskaźnik do "woltomierza"), lecz w sk aźn ik iem typu klasy p o d staw o w ej (loskaźnikdo "urządzenia pomiaroioego").
1017
Rozdział. 21. Funkcje w irtualne Finis coronat opus 4. Z a p y t a s z p e w n i e :
-A p o
co
ta k w y d z iw ia ć i u t r u d n ia ć ?
N ie, nie jest to u tru d n ie n ie , w ręcz przeciw nie. Posłuchaj. W p ro g ra m ie m am y z d e fin io w an e b a rd z o w iele (np. tysiąc) o b ie k tó w klas „w o lto m ierz cyfrow y , „ m ie rn ik te m p eratu ry " i „d etek to r cząstek". T w o rz y m y sobie w ięc tablicę w sk aźn ik ó w d o obiektów klasy abstrakcyjnej „ u rz ą d z e n ie p o m iaro w e " i w tej tablicy ja k b y „zarejestrujem y" w szystkie u rz ą d z e n ia m ające w y k o n y w a ć pom iary. M ó w iąc prościej - d o tej tablicy w p iszem y a d re s y w szystkich n a sz y c h w olto m ierzy , m ierników te m p e ra tu ry i d etek to ró w cząstek. -Wolno tak? Przecież to „szwarc, mydło i powidło"! W o ln o , bo to nie „szw arc, m ydło i p o w id ło ". To obiekty klas z tej samej hierarch ii. W szystkie te obiekty są o b iek tam i klas p o ch o d n y ch o d klasy u rz ą d z e n ie po m iaro w e, a - w myśl konw ersji stan d ard o w ej - w sk a źn ik do o b ie k tu klasy pochodnej (w skaźnik d o m iern ik a tem p eratu ry ) m o ż n a p rzypisać d o elem entu tablicy w sk aźn ik ó w klasy p o d staw o w ej (w skaźnik d o "urządzenia_pom iarow ego"). -Zgoda, można - ale po co? Z obacz: jeśli w p ro g ram ie taka tablica w sk a źn ik ó w ma następującą definicję: urzadzenie_pomiarowe *
urz[ 1 0 0 0 ];
i jeśli w ypełniliśm y tę tablicę sensow nym i ad resam i, to teraz d o w y k o n an ia z a d a n ia , w ystarcza w pro g ram ie taka pętla f o r : for(int i = 0 ; i < liczba_urządzen ; i++)
i
urz[i]-> zmierz_i_ocen() ;
} R ozum iesz już w sp an iało ść tego pom ysłu? N a naszej liście ad resó w są bardzo ró ż n e typy u rząd z eń , a m im o to tutaj, bez konieczności n aszeg o udziału, p ro ceso r w yw oła w łaściw e wersje funkcji w irtu aln y ch z w łaściw ych klas. Z atem : każde z zap isan y ch na tej liście u rz ą d z e ń pom iarow ych (czym kolw iek jest), dokona pom iaru w sobie w łaściw y sposób. No i co, nadal uważasz, że to wydziwianie i utrudnianie?
21.14 F in is coronat o p u s Funkcje w irtualne, k tó ry m i zajm ow aliśm y się w tym rozdziale, są jak b y ukoro now an iem języka C++. Jeśli naw et nie w sz y stk o z tego ro zd ziału o d razu zro zu m iałeś —chciałbym , byś zapam iętał najw ażniejsze: Jest w języku C++ m echanizm , k tó ry b ardzo ułatw i C i pisanie takich p ro g ram ó w , o których juz w m om encie pisania w iesz, że w przyszłości będą musiały być ciągle m odyfikow ane. Funkcje w irtualne to doskonałe narzędzie. D om yślam się jednak, ż e teraz byś jeszcze nie potrafił pow iedzieć, w którym m iejscu Twojego p ro g ram u m ogłyby
1018
Rozdz. 21. Funkcje w irtualne Ćwiczenia Ci się p rzy d ać. Takim p raktycznym rozw ażaniom do czeg o u ży ć tego, czego się nauczyliśm y, poświęcim y ro zd ział ostatni.
W Tym czasem zakończyliśm y o m a w ian ie języka C++. N a s tę p n y ro zd ział om aw ia już bibliotekę funkcji w ejścia/w yjścia - co nie jest ju ż częścią definicji języka. To po p ro stu in stru k taż, jak po słu g iw ać się klasam i, k tó re ktoś dla naszej w y g o d y napisał.
21.15 Ćwiczenia Gdzie stawia się słowo v i r tual, by uzyskać polimorficzne zachowanie funkcji składo wej!' a) przy deklaracji funkcji w ciele klasy podstawowej, b) przy definicji funkcji na zewnątrz klasy podstawowej, c) przy deklaracji funkcji w ciele klasy pochodnej, d) przy definicji funkcji na zewnątrz klasy pochodnej, e) w miejscu wywołania tej funkcji. Polimorfizm, związany z wirtualnością funkcji składowej f, może zajść w przypadku zapisu: a) obiekt.f(); b) wskaźnik->j(); c) referencja.fO;
'. • ..____
„Polimorficzny" -oznacza: mający wiele form. Wytłumacz, co ma wiele tych form? Czy chodzi tu o wiele form funkcji wirtualnych? Jeśli tak, to przecież funkcje przeładowane też występują w wielu postaciach - a nie mówimy wtedy o polimorfizmie. Co to jest klasa polimorficzna? Jeśli definiujemy funkcję wirtualną w klasie K, to czy jej nowsze wersje (realizacje) możemy umieszczać tylko w klasach należących do tej samej hierarchii? W klasie podstawowej zdefiniowana jest funkcja wirtualna. W klasie pochodnej chcieli byśmy mieć jej nową realizację. Które z poniższych w arunków ta funkcja w klasie pochodnej musi spełniać? a) musi mieć tę samą nazwę, b) musi mieć ten sam zestaw argumentów, c) musi mieć ten sam typ rezultatu, d) musi mieć przy swojej deklaracji umieszczone słowo kluczowe virtual. Klasa K ma funkcję wirtualną K K: : f (double ) ; Klasa M jest klasą pochodną od klasy K. Jaki typ rezultatu może mieć ta funkcja wirtualna f zrealizowana w klasie M? W programie mamy klasę odtwarzacz i pochodną od niej klasę odtwarzacz_mp3. Mamy też klasę utwormuzyczny i pochodną od niej klasę piosenka rockowa. Dziedziczenia są publiczne. W klasie o d t w a r z a c z jest funkcja w irtu aln a: utwór* wyświetl wykonawców().
Rozdział. 21. Funkcje w irtualne Ćwiczenia
1019
N ap isz m o żliw e d ek laracje tej funkcji w klasie o d tw a r z a c z _ m p 3 . C zy m ogą o n e tam w y stąp ić rów nocześnie? W poprzednim ćwiczeniu rezultatem funkcji wirtualnej w klasie odtwarzacz był wskaźnik. Rozwiąż to samo ćwiczenie w sytuacji, gdy mamy tam do czynienia z referencją. W typie rezultatu funkcji wirtualnej widzisz wskaźnik do typu T (Czyli T *). Co może być kowariantem tego typu rezultatu? W programie przykładowym ze strony 1006 zmień funkcje wirtualną o nazwie stwórz nowy_dziewiczy w klasie skrzypce tak, by jej rezultat był kowariantem typu rezultatu funkcji o tej samej nazwie z klasy podstawowej.
Czy funkcja wirtualna może być globalna? Czy funkcja wirtualna może być składnikiem statycznym swojej klasy? W klasie podstawowej bilet jest funkcja wirtualna drukuj. Czy w klasie pochodnej bilet ulgowy musi być także definicja tej funkcji? W klasie podstawowej bilet funkcja wirtualna drukuj jest publiczna. W klasie pochodnej bilet_ulgowy funkcja ta zdeklarowana jest jako private. Jak zareaguje kompilator widząc, że w tekście programu, w funkcji main uruchamiamy tę funkcję drukuj na rzecz: a) obiektu klasy bilet, pokazywanego wskaźnikiem typu bilet* b) obiektu klasy biletulgowy pokazywanego wskaźnikiem typu bilet* c) obiektu klasy bilet pokazywanego wskaźnikiem typu bilet_ulgowy* b) obiektu klasy bilet_ulgowy pokazywanego wskaźnikiem typu bilet_ulgowy* Odpowiedz na tesame pytania w sytuaq'i, gdy klasa bilet deklaruje przyjaźń z klasą bilet_ulgowy. Co opisują pojęcia: wczesne i późne wiązanie. Czego z czym jest to wiązanie i kiedy się ono odbywa? Załóżmy, że mamy referencję i wskaźnik do obiektu klasy pochodnej, a klasach tych jest funkcja wirtualna. Które z poniższych form wywołań tej funkcji wirtualnej kompilator zrealizuje jako wczesne wiązanie, a które jako późne? a) o b ic k ł.f O ; b) w s k a ż n i k - > f ( ) ; c) rcferencja.fO; Czy posłużenie się kwalifikatorem zakresu może sprawić, że wczesne wiązanie zamieni się na późne? A może odwrotnie? Wymień sytuacje, gdy kompilator dla wywołania funkcji wirtualnej stosuje wczesne wiązanie. Czy można w klasie podstawowej, w której jest deklaracja funkcji wirtualnej fff, zdefiniować inną funkcję o nazwie f f f? W klasie podstawowej jest deklaracja funkcji wirtualnej f f f . W klasie pochodnej od tej klasy chcemy zdefiniować funkcję o nazwie f f f . 1. Jakie warunki mają być spełnione, by była ona wirtualną, nową realizacją funkcji f f f z klasy podstawowej? 2. Załóżmy, że te warunki nie są spełnione. Kompilator może wówczas uznać to za błąd, lub nie. Jeśli jest to sytuacja poprawna, to jakie zjawisko zachodzi?
1020
Rozdz. 21. Funkcje w irtualne Ćwiczenia 3. W jakiej sytuacji kompilator taką definicję uzna za błąd? Porównaj funkcje wirtualne i funkq'e o przeładowanej nazw ie - jeśli chodzi o nazwy., zakresy i argumenty. jak nazywamy klasę, która nie może posłużyć do wytworzenia żadnych obiektów swojej klasy? Co to jest funkcja czysto wirtualna i jak obecność takiej funkcji wpływa na klasę? Czy funkcja czysto wirtualna może mieć równocześnie definicję? Dokończ: Klasa, która dziedziczy funkcję czysto wirtualną, ale nie definiuje swojej wersji tej funkcji staje się... Jeśli w programie mamy klasy, które nie mogą mieć żadnych obiektów, to do czego można użyć takich klas? (Wymień przynajmniej dwa zastosowania). Mamy klasę abstrakcyjną K i pochodną od niej, już nie abstrakcyjną, klasę KK. Czy można by d o funkcji v o id f unkc j a (K ) wysłać obiekt klasy pochodnej KK? W klasie podstawowej mamy deklarację funkcji czysto wirtualnej. W klasie pochodnej chcemy znowu umieścić deklaracje tej funkcji wirtualnej. Jakie mamy, najogólniej mówiąc, trzy sposoby rozwiązania rodzaju jej wirtualności? Kiedy konieczne jest dostarczenie definicji funkcji deklarowanej jako czysto wirtualna? W klasie podstawowej jest funkcja wirtualna (zwykła). Czy w klasie pochodnej można ją zrealizować jako czysto wirtualną? A odwrotnie? W klasie K jest destruktor. Kiedy jest przesłanka by uczynić go wirtualnym? Czy potrafiłbyś wyjaśnić, co daje fakt, że destruktor jest wirtualny? Napisz program, w którym będą: klasa ciąg arytmetyczny i klasa ciąg geometryczny. (Matematyczna ich realizacja jest tu zupełnie nieistotna, możesz oszukiwać). W programie kilkakrotnie zadajemy pytanie użytkownikowi, jaki ciąg chciałby stworzyć. Zależnie od odpowiedzi, tworzymy mu odpowiedni ciąg, a jego adres wpisujemy do tablicy przechowującej adresy ciągów. Załóżmy, że tablica jest dziesięcioelementowa i wypełniliśmy ją już adresami właśnie stworzonych riągów. Poleć każdemu z tych ciągów, by podał sumę jego pierwszych n wyrazów. Robić mają funkcje składowe: d o u b le suma wyrazow ( i n t n) ; Przy tych poleceniach (dla uproszczenia) n ma być akurat rów ne pozycji, na której umieściliśmy w tablicy adres tego ciągu. To znaczy ciąg, którego adres jest umieszczony w tablicy na pozycji piątej zapytamy o sumę pięciu wyrazów, a ten umieszczony na pozycji siódmej, o siedem. Uwaga: To nie ciągi są przedmiotem tego ćwiczenia. Zatem jeśli nie wiesz lub nie pamiętasz, co to są ciągi i jak oblicza się sumę ich pierwszych n wyrazów, nie przejmuj się, iv takim przypadku niech Twoje wszystkie ciągi arytmetyczne odpowiadają na to pytanie wartością 7, a wszystkie ciągi geometryczne wartością 2. Program z poprzedniego ćwiczenia spróbuj przerobić tak, by zastosować w nim funkcje zastępujące (niemożliwy do realizacji) wirtualny konstruktor.
Rozdział. 22. O peracje W ejścia/W yjścia
22
1021
Operacje Wejścia/Wyjścia la o d p rę ż e n ia , porozm aw iam y te ra z o operacjach w ejścia i wyjścia, czyli o tym , jak p ro g ram p o ro zu m iew a się ze św iatem zew n ętrzn y m . Z e św iatem z e w n ę trz n y m - to znaczy z u ży tk o w n ik iem sied zący m p rzed ekranem i klaw iaturą, a ta k ż e z pam ięcią zew n ętrzn ą - czyli np. z d y sk iem m agnetycz nym . Po to, by zapisać na nim lub z niego odczytać jakieś dane. O peracje w ejścia/w y jścia nie są częścią definicji sam ego języ k a C++. U m ożli wiają je biblioteki. T akie biblioteki s ta n d a rd o w o dołączane są p rz e z p ro d u cen ta dan eg o k o m p ilato ra.
D
Biblioteki sta n d a rd o w e zajm ow ać się m ogą w ielom a z a g a d n ien iam i, nas jednak interesują tutaj te, o d p o w iedzialne za o p eracje w e /w y . Są dw ie zasad n icze biblioteki obsługujące te operacje: ♦> 1) Biblioteka stdio (standard in p u t/o u tp u t) , która istnieje po to, by p rogram iści klasycznego C p rzyzw yczajeni d o niej, m ogli używ ać jej także w C ++. Biblioteką tą nie b ęd ziem y się zajm ow ać. Jeśli zn asz język C, to znasz d o b rz e tę bibliotekę i p o trafisz się nią posługiw ać. Jeśli nie z n a s z klasycznego C i tej biblioteki, to lepiej się jej nie ucz. U żyw anie tej biblioteki w C++ jest, co p raw d a, d o p u szcza ln e, ale nie należy d o dobrego stylu pro g ram o w an ia. (Z d ru g iej stro n y jednak, m oże Ci się zdarzyć, że będziesz m odyfikow ał c u d z y p ro g ram , który używ a s t d i o . Nie ma w tedy w yjścia - będziesz m u sia ł w ziąć do ręki opis tej biblioteki). ♦♦♦ 2) Biblioteka iostream. Ta biblioteka jest zalecana d o sto so w a n ia w p ro g ram o w an iu w C++. Jest ona tak w ażn a, że jej z ało żen ia w eszły w skład obecnego stan d ard u ISO języka C++. Ta biblioteka b ęd zie w łaśnie będzie p rzed m io tem tego rozdziału.
1022
Rozdział. 22. O peracje W ejścia/W yjścia Biblioteka iostream N ie u d a m i się całkowicie przem ilczeć biblioteki s t d i o . To dlatego, że d o m y ś lam się, iż część czytelników m oże mieć dośw iadczenia z klasycznego C n asu w ać się będą uw agi, co jest lepsze, a co gorsze i d laczeg o . Z anim p rz y stą p im y do om aw iania i o s t r e a m jedna w a ż n a u w ag a: rozd ział ter nie jest d o k ła d n y m opisem jakiejś szczególnej im plem entacji bibliotek: i o s t r e a m . Służy raczej temu, by pokazać możliwości tej biblioteki, a także istotę po słu g iw an ia się nią. Jeśli chcesz naprawdę d o k ład n ie poznać bibliotekę i o s t r e a m , m u sisz przeczytać w dokumentacji sw ojego ko m p ilato ra jej opis. W ierzę je d n a k , że po lekturze tego rozdziału Symfonii zro zu m ien ie tam tegc opisu nie sp ra w i Ci kłopotu.
22.1
Biblioteka iostream N azw a je stsk ró te m od: ang. input/output stream - stru m ie ń w ejścio w y /w y jścio wy.^ Przy o m a w ia n iu tej biblioteki sp o tk am y się z trzem a zag a d n ien iam i: •
W yprow adzanie i w prow adzanie inform acji ze s ta n d a rd o w ych u rząd zeń w e/w y, takich ja k k la w ia tu ra i ekran.
•
P odobne operacje na plikach d an y ch znajdujących się na nośnikach zew nętrznych - dyskach, ta śm ach m agnetycznych itd. Są to np. takie sytuaq'e, gdy program m a w czytać inform ację przygotow aną w jakim ś pliku d y sk o w y m , lu b g d y p ro g ram m a rezu ltat swojej pracy zapisać na d y sk u w postaci pliku.
•
Trzecie zag ad n ien ie nie dotyczy w cale kom unikacji ze św ia tem zew n ętrzn y m . Jest to sytuacja, g d y w obrębie p ro g ram u chcem y w y p ro w ad zić informację n ie n a ek ran , nie na plik dyskow y, ale w pisać ją do jakiegoś m iejsca w pam ięci, by potem um ieść to w swoim obiekcie k la sy s t r i n g . Konkretnie: za m ia st wypisać liczbę 3.1416 n a ekranie - chce m y ją w pisać d o jakiegoś obiektu klasy s t r i n g tak, że p o sz czególne jego elem enty będą takie: 3, k ro p k a , 1,4,1,6. Ta możliwość jest bardzo przydatna, gdy np. chcemy stworzyć jakąś naziuę (najczęściej pliku dyskowego), a w nazwie tej występuje Iiczba. ( "p ro b k a _ 1 5 6 _ 1 0 .d a t") Z agadnienie to obejm uje także sytuację o d w ro tn ą: u m o żliw ia w czytyw anie tekstu z obiektu klasy s t r i n g i zam iana jego fragm entów go na wartości liczbowe.
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień
1023
A by sk o rzy stać z biblioteki i o s t r e a m należy d y rek ty w ą i n c l u d e w łączyć d o p ro g ram u p lik i nag łó w k o w e zaw ierające o d p o w ied n ie deklaracje. D eklaracje takie zn ajd u ją się w kilku plikach nagłów kow ych. N ieza leżn ie od tego, z której części biblioteki zam ierzam y k o rzy stać - m usim y w łączy ć plik nag łó w k o w y i o s t r e a it. D o d atk o w o m ożem y też w łączyć plik f s t r eam - jeśli zam ierzam y p rz e p ro w a d z a ć z p ro g ram u operacje na plikach. Jeśli zaś zam ierza m y doko nyw ać operacji w e /w y na obiektach klasy s t r i n g , w ó w czas m u sim y d o pro g ram u w łączy ć deklaracje zn ajd u jące się w pliku n ag łó w k o w y m s s t r e a m Oto zestaw ienie: i o s t r e a m - jakiekolw iek k o rzy stan ie z tej biblioteki,
22.2
fs tre a m
- operacje w e /w y na plikach zew n ętrzn y ch ,
s s tre a m
- operacje w e /w y na obiektach k lasy s t r i n g .
S tru m ie ń W p ro w ad zan ie i w y p ro w ad zan ie inform acji m ożna p o tra k to w a ć jako stru m ień bajtów pły n ący o d źródła do ujścia. Na p rzykład: Jeśli chcem y d o zm iennej x w czytać z k la w ia tu ry jakąś liczbę, w ó w czas strum ień (bajtów ) płynie: od u rz ą d z e n ia zew nętrznego z w a n e g o klaw iaturą, d o tego miejsca w pam ięci operacyjnej, gdzie m ieści się zm ienna x. Skoro p o ru szam y się po obszarze języka C++ - nic d ziw n eg o , ż e stru m ien ie są zrealizow ane n a zasadzie klas. W czytyw anie lu b w ypisyw anie inform acji m oże się o d b y w ać na dw a sposoby
O p e r a c je w e/w y b in arn e
Sposób "binarny" polega na tym, że określone bajty inform acji p rzesy łan e są od źródła do ujścia - a strum ień nie m a się interesow ać zn aczen iem tych bajtów. Bajty te nie są in terp reto w an e w żad e n sposób. O d b y w a się ty lk o ich przep ły w przez strum ień.
O p e ra c je w e/w y te k sto w e
Sposób "tekstow y" polega na tym , że stru m ień nie tylko m a przesłać jakąś informację, ale jeszcze ma ją do d atk o w o jakoś interpretow ać - czyli jak to często m ów im y - form atow ać. N a przykład, jeśli w jakiejś kom órce pam ięci zapisana jest w artość f l o a t 274.81 - a chcielibyśm y w yśw ietlić tę w arto ść jako liczbę na ekranie, to nie w ystarczy, by treść (binarną) tej kom órki (czyli cztery bajty)
22
1024
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień stru m ień p o prostu przesłał na ekran. Pow inien ją n ajp ierw sfo rm ato w ać tak, by zam ienić ją na sekw encję zn ak ó w : najpierw 2, potem 7, p o te m 4, p o tem kropka, potem 8, p o tem 1. To dopiero pop ły n ie stru m ien iem na ek ran . F orm ato w an ie sp raw i, że nie tylko w artości obiektów ty p u d o u b l e , i n t staną się czy teln e dla człow ieka, ale n aw et będą p o k a z a n e w ró w n iu tk o sformatowanych kolum nach pokazujących w yniki fin a n so w e przed sięb io rstw a w o statn im półroczu. Co p ra w d a , liczby były w p ro g ram ie w postaci binarnej, ale człow iekow i w pliku (czy na ekranie) trzeba je p o k aza ć w p o staci ciągów cyfr - czyli tekstów ! i w
iw m
iiM n i i W T r u i i r i i i i n m r in i— i ~ n rTT- ir r r — im * m « * * * m %'w t r t . w . im w w iw *
Sposób ten stosujemy wtedy, gdy informacja (pierw otnie binarna) ma być pokazana człowiekowi. Nie w ażne, czy ma zostać mu pokazana na ekranie, czy zapisana w pliku tekstowym.
P rzy w czy ty w an iu odw rotnie. Jeśli chcem y z k la w iatu ry p rzy ją ć liczbę, a ktoś na niej w y stu k a ł zn ak i 0 x 1 a , w ów czas te z n a k is ą s p ro w a d z a n e stru m ien iem z k la w ia tu ry m uszą d o d atk o w o zostać z in te rp re to w a n e ja k o liczba w zapisie szesn astk o w y m (heksadecym alnym ). D opiero re z u lta t tej interpretacji (czyli ja kaś liczba b in arn a) u m ieszczana jest przez strum ień w d an ej kom órce pam ięci. P odobnie, g d y Twój p ro g ram p o p ro si człow ieka, b y w y stu k a ł na k law iatu rze w arto ść liczby it. M ożesz trafić n a tak am bitnego człow ieka, k tó ry w stu k a Ci ją z d o k ład n o ścią do d w ó chsetnego miejsca p o p rzecinku. N ie m a p ro b lem u , g d y robisz to tak: double pi; cin >> pi;
stru m ień c i n i tak te w szystkie cyfry pozbiera i p rz e fo rm a tu je na o d p o w ied n ią w arto ść b in arn ą, którą w pisze d o przy g o to w an eg o o b ie k tu ty p u d o u b le . W szystkie operacje w e /w y na stru m ien iac h c o u t , c i n , k tó re do tej pory stosow aliśm y w tej książce, były w ła śn ie ty p u tekstow ego (czyli form atow ane). W iem z d o św iad czen ia, że ro zró żn ien ie tych d w óch s p o so b ó w często sp raw ia tru d n o ści p o czątkującym , d la teg o w ielokrotnie d o te g o b ęd ziem y w racać.
W A by p o słu ży ć się stru m ien iem należy:
a) najp ierw zd efin io w ać w pam ięci o śro d e k d o w o d zen ia stru m ie niem , b) w sk a zać m u, jakim u rz ą d z e n ie m z e w n ę trz n y m m a się zajm ow ać, c) p rz e p ro w a d z a ć k o n k re tn e w czy tan ia (w y p isy w a n ia ) inform acji d o w o ln ą ilość razy, d) zlik w id o w ać s tru m ie ń (razem z jego o śro d k ie m d o w o d zen ia) w te d y , g d y u zn am y , ż e stru m ień nie będ zie n a m ju ż więcej potrzebny.
1025
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie zdefiniowane standardow o
P ierw sze Tw oje w ra ż e n ie jest z a p e w n e takie: Tyle pracy po to, by z klaioiatury wczytać jedną głupią liczbę x ? Nie. Jedną g łu p ią liczbę x w czytuje sie z k la w iatu ry po p ro s tu instrukcją: cin >> x;
D laczego to tak ie p ro ste - m ów im y w n astęp n y m p arag rafie.
22.3 Strumienie zdefiniowane standardowo Dla k o m p ilato ra jest oczyw iste, że jeśli p iszesz p ro g ram , to b ęd ziesz zap ew n e chciał z tego p ro g ram u w ypisać coś na ek ran lub w czy tać coś z klaw iatury. D latego kompilator stan d ard o w o d efin iu je kilka g o tow ych stru m ien i. Co to o zn acza w praktyce? O znacza to, że praca o p isan a w pop rzed n im paragrafie w p u n k ta c h a), b) o raz d) zo staje zrobiona za C iebie. Innym i słowy: stru m ień zo stan ie założony i o tw arty , m ożesz go u ży w ać. P rzy zakończeniu program u s tru m ie ń auto m aty czn ie z o stan ie zam knięty. Są następujące sta n d a rd o w o zd efin io w an e strum ienie:
cout wcout
Cin wcin
cerr wcerr
clog wclog
W pierw szej linii w id zim y tu stru m ien ie pracujące n a zw y k ły ch znakach c h a r , w drugiej zaś - ich o d p o w ied n ik i p racujące na zn ak ach szero k ich wchar_t. Aby skorzystać z tych strum ieni trzeba w p ro g ram ie zam ieścić dyrektyw ę #include
włączającą w trakcie kompilacji o d p o w ied n ie deklaracje. S tru m ien ie te są po prostu eg zem p larzam i obiektów jakichś klas. Na p rzy k ład stru m ień c o u t jest egzem p larzem obiektu klasy o s tr e a m . ♦♦♦ Co u t — jest pow iązany ze s ta n d a rd o w y m u rz ą d z e n ie m wyjścia (zw ykle ekran). ♦> c i n — jest pow iązany ze sta n d a rd o w y m u rz ą d z e n ie m wejścia (zw ykle klaw iatura). «$♦ c e r r — jest pow iązany ze stan d ard o w y m u rz ą d z e n ie m , na które chce się w y p isy w ać kom unikaty o b łęd ach (zw ykle także ekran). Strum ień ten jest niebu forowany. ♦> c l o g — jak wyżej, z tym , że ten stru m ień jest b u fo ro w an y 2. To, że stru m ień jest niebuforow any o zn acza, że jak tylko z a ż ą d a m y w ypisania kom unikatu o b łęd zie - zrobione to zo stan ie natychm iast. Buforow anie - o znacza tutaj, że m oże być robiona p ew n a optym alizacja, polegająca np. na tym , że dopiero, gdy zb ierze się kilka k o m u n ik ató w - w tedy 2)
log pochodzi od ang. logbook = dziennik pokładowy.
1026
Rozdział. 22. O peracje W ejścia/W yjścia O peratory >> i << zostaną o n e w p isan e do d zien n ik a pokładow ego h u rte m . O szczęd za się w ten sposób czas.
22.4 Operatory » i « Przy p o słu g iw an iu się stru m ien iam i b ardzo w y g o d n e je st p o słu g iw an ie się o p erato ram i « o raz » . Jak pam iętam y, o p erato ry te w sto su n k u d o typów w b u d o w a n y ch odpow iadają za przesunięcie b itów w d an ej kom órce pam ięci o zad an ą liczbę pozycji w lew o lub praw o. W p rz y p a d k u strum ieni w e /w y konieczne było p o słu ż e n ie się jakim iś o p erato rami, k tó re by w y k onyw ały funkcje w pisyw ania (w staw ian ia , w p u szczan ia) inform acji d o strum ienia o raz odczytyw ania (w y jm o w an ia , w y ław iania) tej inform acji ze strum ienia. W ybór p a d ł na operator » o raz « - m iędzy in n y m i d la te g o , że oba w izualnie sugerują ruch. W obrębie klasy strum ień w yjściow y ( o s t r e a m ) o p erato r « został tak p rzeład o w an y , że o d p o w ia d a za w y słan ie inform acji d o strum ienia. Zapis: cout «
x;
//
cout <- x
ru c h : o d x d o c o u t ( c z y li e k r a n u )
p o w o d u je zatem , że liczba x zostaje w p u szczo n a (w staw io n a) d o strum ienia. S tru m ień ten kończy się na ekranie. (To jego ujście). O p erato r ten u ży ty w sto su n k u d o strum ieni n a z y w a się często op erato rem in sert - w sta w ia n ia (albo put to). Ja pow iedziałbym : w y sy ła n ia , g d y ż inform acja w ysłana zostaje d o stru m ien ia. O d w ro tn y o p erato r » o d p o w ied zialn y za w czy tan ie inform acji cin »
y;
cin -* y
n a z y w a n y je s t extract operator- czyli operatorem ekstrakcji, albo o p erato rem get from (czyli w n aszy m p rz y p a d k u pobierz z ..., w y łó w z ..., w yjm ij z ...) P on iew aż w ielokrotnie p o słu g iw aliśm y się już ty m i o p e ra to ra m i d lateg o w y s tarczy p rz y p o m n ie ć sposoby p o słu g iw an ia się nim i. #include using namespace std;
/z* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **************
int m a i n ()
{ int i; double d; char tekst
[80];
cout << "Podaj liczbę int: cin >> i; cout << "Podałeś: " << i << endl; cout << "Podaj liczbę double: cin >> d; cout << "Podałeś: " « d << endl;
1027
Rozdział. 22. O peracje W ejścia/W yjścia Domniemania w pracy strum ieni zde finiowanych standardow o cout << "Napisz wyraz: cin >> tekst; cout << "Napisałeś ***" << tekst << "***\n";
P o słu g iw an ie się zw rotam i: „ w staw ian ie do s tru m ie n ia " i „w y jm o w an ie ze stru m ien ia" m o ż e być dla Ciebie, C zytelniku, p o cz ą tk o w o m ało obrazow e. D latego sto su jm y też rów nolegle in n e zw roty określające to sam o - zro zu m iałe n aw et d la kogoś, kto nie w ie nic o istnieniu strum ieni: Jeśli w staw iam y inform ację d o stru m ien ia to p o to, by popłynęła ona g d zieś - m oże na ek ran - i ta m została w y p isa n a . D latego też m ów ić b ę d z ie m y często w y p is y w a n ie inform acji. ♦J* N a to m ia st, jeśli w yjm ujem y coś ze stru m ien ia (pły n ąceg o choćby od k la w iatu ry ), to jakbyśm y w czy ty w ali inform ację d o p ro g ram u z k la w ia tury. D lateg o nazyw ać b ęd ziem y to także w c z y ty w a n ie inform acji.
22.5 Domniemania w pracy strumieni zdefiniowanych standardowo O peracje w e /w y p rzep ro w ad zan e z a pom ocą o p e ra to ró w » i « są d o sk o n a łym p rz y k ła d e m na pracę sposobem tekstow ym , fo rm ato w a n y m . Teraz p o ro z m aw iam y o ty m , jakie są d o m n iem an ia tego fo rm ato w an ia
W
przypadku
wypisywania
na ekran
(wstawiania
d o stru m ie n ia , który
płynie n a ek ran ) m a m y d o czynienia z n a s tę p u ją c y m i d o m n ie m a n ia m i:
Typy w b u d o w a n e , które służą do p rzec h o w y w an ia liczb całkow itych (np. i n t lo n g , u n s i g n e d s h o r t , itd.) są w y p isy w a n e w sy stem ie dziesiątkow ym . Typy c h a r i u n s i g n e d c h a r - w y p isy w a n e są jako p o je d y n cze znaki ASCII Liczby typu f l o a t i d o u b le w y p isy w an e są z tzw . dokładnością 6 miejsc Z będnych zer n ie w ypisuje się - czyli o trzy m am y 1.04 z a m ia s t 1.04000 W skaźniki (z w yjątkiem c h a r * i u n s i g n e d heksadecym alne (szesnastkowe).
c h a r * ) w y p isy w a n e są jako
Ż ądanie w y p isan ia w skaźników c h a r * (i u n s i g n e d c h a r * ) ro zu m ian e jest jako żąd an ie w y p isan ia C -stringu, na który ten w sk aźn ik pokazuje. Czyli char tab[) = "Napis"; char *wsk = tab; cout << wsk;
1028
Rozdział. 22. O peracje W ejścia/W yjścia Domniemania w pracy strum ieni zdefiniowanych standardow o sp o w o d u je w ypisanie nie tyle ad resu , który ten w sk a ź n ik p rzech o w u je, ale C -stringu znajdującego sie p o d takim adresem (a kończącego się bajtem zero w y m - zn ak nuli). G dy b y n as jed n ak m im o w szy stk o interesow ał ten a d re s, to w y starczy posłużyć się rz u to w a n ie m - na p rzy k ła d na ty p void* cout « (void*) wsk; cout << reinterpret cast (wsk) ;
/ / s ta r e r z u to w a n ie H n o w e r z u to w a n ie
W W p rz y p a d k u w czy ty w an ia (w y jm o w an ia ze s tru m ie n ia p ły n ąceg o z k law ia tury), p rzy jm u je się przez d o m n iem an ie że: W szystkie w czy ty w an e typy m ogą p o p rzed zać białe zn ak i (spacje, ta b u lato ry itd.), k tó re są ignorow ane. Zatem : czy n ap iszem y na k la w iatu rze 11
czy też [sp a c ja ][sp a c ja ][ta b u la to r]l 1
to nie m a różnicy. D otyczy to ta k że w czy ty w an ia d o tablicy znak o w ej C -stringu lub p o je d y n czeg o znaku. Białe zn ak i poprzed zające p ie rw sz ą „literę" będą zigno ro w an e.
♦
G dy ż ą d a m y w czy tan ia d o ty p ó w reprezentujących liczby całkow ite - znaki p rzy ch o d zące z k law iatury in terp reto w an e są ja k o p o d a n e w zap isie d ziesiątk o w y m . jBimiBiwwiumm n■iiium uni win»inmiwuwniur ii—■i ri«il iTt:fr
Tak nie jest! i r y r y t ^ r rrri^datirf Tntrf-irttflw iffiff mi ti r iw it r frriffriBT^rnr^iMTtfiiariWi H iin ii i i iuhuh i w i ■ ii i » w i i r i m m n i nu w n 'i m
Tam ta k o nw encja dotyczy ty lk o tych stałych, które w y stęp u jący ch w tekście p ro g ram u , zatem tak ro zu m u je tylko k o m pilator in te rp re tu ją c tekst n aszeg o p ro g ram u . Z apam iętaj: S trum ienie nie kierują się tym i zasad a m i. S tru m ien io w i trzeb a w y raźn ie pow iedzieć, że w czy tu jąc jakieś cyfry sk ład ające się na liczbę - m a je zin terp reto w ać jako liczbę w zap isie innym niż d z ie sią tk o w y m (takie jest dom niem anie). Jeśli teg o d o m n iem an ia nie zm ien im y , to znaki •
11 zostaną z in te rp re to w a n e jako liczb a d ziesiątk o w a (11),
•
011 zostaną tak że zin terp reto w an e ja k o liczba dziesiątk o w a (czyli znow u d ziesiątk o w o = 11),
Rozdział. 22. O peracje W ejścia/W yjścia Domniemania w pracy strum ieni zde finiowanych standardow o •
1029
0x11 tak że (o, zgrozo!) zo stan ą z in te rp re to w a n e jako liczba dziesiątkow a, czyli w czytane zo stan ie zero, a skoro po nim następuje nie b ęd ąc y cyfrą zn ak V , k tó ry zak o ń czy w czyty w anie liczby. Z n ak i x l l zo stan ą na ra z ie nie w yjęte ze stru m ien ia (będą czek ały na n astę p n e g o chętnego). Zatem nasza liczba b ęd zie m iała w arto ść zero!
Za kilkanaście s tro n zobaczym y, ze ab y w czytać liczbę w zap isie szesn astk o w ym lub ó sem k o w y m , w ystarczy p o słu ży ć się tak z w an y m i m an ip u lato ram i h e x lu b o c t . O tak: cin >> hex » cin >> oct »
liczbaA ; liczbaB ;
Jeślichcem y, w y stu k u jąc na k law iatu rze, um ieścić p rz e d liczbą zn ak (+ lu b -), to m iędzy nim , a liczbą nie m oże być żad n ej spacji. Czyli p o p ra w n e jest -3.31
a błędne -
3.31
W czytyw anie liczby całkowitej zostaje zakończone, g d y n a p o tk an y zostaje znak nie b ęd ący cyfrą. 712p
albo 712
(znakiem _ oznaczamy tu spację)
Liczbę zm ien n o p rzecin k o w ą w czytuje się podobnie, z tym , że w p rzy p ad k u notacji w ykładniczej (tzw. scientific notation) m oże w y stąp ić p o raz d ru g i znak (+ lub -) jako zn ak w y k ład n ik a, a także o d p o w ied n ia litera określająca w ykładnik. Np. 2 .3e-15 - 1 .4e2 +71.3e-2
W ew nątrz nie m o ż e być spacji. Z atem błędne są np. takie zap isy 2.3 e -15 l-.4e 2 +71.3 e - 2
Jeśli w czytujem y inform ację tekstow ą d o tablicy zn akow ej np. za pom ocą nastę pujących instrukcji
1030
Rozdział. 22. O peracje W ejścia/W yjścia Domniemania w pracy strum ieni zdefiniowanych standardow o c h a r t a b l i c a [ 8 0 ]; ch ar t [8 0 ]; c h a r *wsk = t c in » ta b lic a ; c i n » wsk; to nie n ależy zapom inać, że w czy ty w an ie zacznie się po zig n o ro w an iu spacji po p rzed zający ch tekst, sk o ń czy z napotkaniem p ierw szeg o białego znaku. Innym i słow y ze zdania _To j e s t t e k s t
(znakami _ oznaczamy tu spację)
zostan ie d o tablicy w czytany tylko pierw szy w y ra z To Trzeba tak że pam iętać, że p rz y takim w czy ty w an iu nie jest sp raw d zan e za pełnienie tablicy. Zatem jeśli w d an y m fragm encie p ro g ram u char t a b [6 ]; c i n » ta b ; na k la w iatu rze w y stu k am y w y ra z o długości 100 zn ak ó w , to p ierw sze znaki rzeczyw iście zn ajd ą się w tablicy Lab, ale następne nie m ieszczące się tam , będą niszczyły (zacierały) jakieś frag m en ty pam ięci. O tym, jak bezpiecznie wczytywać tym sposobem, porozmawiamy później (patrz: funkcja w id th oraz manipulator s e tw ). Jeśli w czy tu jem y inform ację tek sto w ą d o obiektu k la sy s t r i n g za pom ocą nas tępującej instrukcji s tr in g s tu d io ; c in » s tu d io ; to p o d o b n ie, jak w p rz y p a d k u tablicy znakow ej, w czy ty w a n ie zaczn ie się p o z ig n o ro w an iu spacji pop rzed zający ch tekst, sk o ń czy z n ap o tk a n ie m p ie rw szego b iałeg o zn ak u . Także w ięc zostanie w czy tan y tylko je d en w y raz. R óżnica jest je d n ak taka, że w tym p rz y p a d k u nie m a o b aw y o p rzep ełn ien ie, g d y ż obiekt klasy s t r i n g p o trafi się sam o d p o w ie d n io pow iększać. Z atem tutaj, ten jeden w yraz m o ż e sk ład ać się n a w e t z m ilona liter.
W W sp o m n ian e d o m n iem an ia z a p isa n e są w ew n ątrz o b iek tu k lasy stru m ie ń , w danych sk ład o w y ch - tak zw a n y c h flagach o d p o w iad ający ch za fo rm at (format flags). Jeśli d o m n ie m a n e u sta w ie n ie tych flag nam n ie o d p o w ia d a , to m o ż e m y je zm ienić. W krótce się tego n au cz y m y .
To już nie d o m n iem an ie, to o g ó ln a zasad a. G dy w czy tu jem y z k la w ia tu ry d w ie liczby in strukcją
1031
Rozdział. 22. O peracje W ejścia/W yjścia Uwaga n a priorytet double x, y; cin >> x >> y;
i chcem y p o d a ć w artości 3.14 o raz 20.1 - to nie m o ż em y na klaw iaturze w y stu k ać 3.1420.1
K onieczna jest, d la w iększości ty p ó w , p rzynajm niej jedna spacja oddzielająca — pokazująca, g d z ie jed n a liczba się k o ńczy, a d ru g a zaczyna. P o p raw n ie p o w in no zatem być 3.14 20.1
Po pierw szej liczbie pow inien nastąp ić jakiś znak kończący jej w czytyw anie. N ato m iast g d y b y d ru g ą liczbą było -20.1 to m oglibyśm y n a p isa ć tak 3.14-20.1
g d y ż w ted y d a się rozpoznać g d zie się ko ń czy jedna liczba, a zaczy n a d ruga. p
i i ............ "
......................
L
I
!
■
.............. .......... —- —— — —
22.6 Uwaga na priorytet Z rozdziału o p rzeład o w an iu o p erato ró w p am iętasz z a p e w n e , że co p raw d a, op erato r m o żn a p rzeład o w ać w d o w o ln y sposób, ale p aru rzec zy zm ienić się nie da. P rzy k ład o w o nie da się zm ienić jego p riorytetu. O perator * (m nożenia) będzie zaw sze w y k o n y w an y p rz e d o p erato rem + (dodaw ania). N iezależn ie od tego, co robią n ap raw d ę. Z asada ta o b o w iązu je także w p rz y p a d k u o p erato ró w » i <<. Z tabeli p rio ry te tów (str. 123) w id zim y , że priorytet o p erato ró w >> i << jest d o ść niski, a w ięc w yrażenie int a = 5, b = 7; cout << a + b «
endl;
rzeczyw iście w y d ru k u je sum ę, bez p o trze b y ujm ow ania operacji d o d aw an ia w naw iasy cout <<
(a + b) << endl;
Są jednak o p erato ry o priorytecie jeszcze niższym niż » i << . Z atem jeśli je użyjem y w w y rażen iu przeznaczonym d o w ypisania - m u sim y w te d y posłużyć się naw iasam i. D la p rzykładu w eźm y o p erato r && W w y rażen iu int a = 1 , b = 0 ; cout << (a && b) «
endl;
w ypisane zostanie 0 - co jest rezultatem iloczynu logicznego zm ien n y ch a i b. G dybyśm y jednak zapom nieli o naw iasach i napisali p o p ro stu tak: cout «
a && b << endl;
to kom pilator w ied ząc, że priorytet < < jest w yższy niż p rio ry tet &&zin terp retu je to jako
1032
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika (cout «
a) &&
(b << endl);
i zap ro testu je. Pół b ie d y jeśli kom pilator rzeczyw iście zasygnalizuje błąd. G d y b y śm y jednak w n aszy m ostatnim p rzy k ła d zie nie pragnęli w y p isan ia końcow ego znaku ' \n ' i napisali p o prostu: cout << a && b;
w ó w c zas zostan ie to zro z u m ia n e jako: (cout «
a ) && b;
czyli, na sk u tek w yrażenia w naw iasie na ekran zo stan ie w y p isan a jed y n k a - po czym re z u lta t tego w y rażen ia - a jest to (jak n am z p o p rz e d n ic h ro zdziałów w iad o m o ) referencja do stru m ien ia cout - zo stan ie u ż y ta d o w yrażenia: (cout)
&& b;
N ie je st to żad en błąd sk ład n i. O peracja logiczna && zo stan ie w y k o n an a, a jej w y n ik p ó jd zie w próżnię. N ajw ażniejsze jednak, że w y p isu jem y nie to, o co nam ch o d ziło , a kom pilator nie sy g n alizo w ał b łęd u . T rzeba zatem w w y p ad k ach w ątp liw y ch pam iętać o u m ieszczen iu naw iasów .
22.7 Operatory « oraz » definiowane przez użytkownika Jed n ą z najw ażniejszych w a d biblioteki s t d i o (znanej z k lasy czn eg o C) jest to, że o ile p o trafi ona obchodzić się z typam i w b u d o w a n y m i, o tyle jest b ezra d n a w obec now ych typów zd efin io w an y ch przez u ż y tk o w n ik a (w obec klas). T y m czasem biblioteka stru m ie n i C++ i o s t r e a m ro zw iąz u je ten p roblem z nie . sam o w itą łatw ością. N a z y w a się to szum nie p rz e ła d o w a n ie m o p erato ra << oraz >>, ale realizaq'a tego je st b ard zo prosta. M ó w iliśm y ju ż o tym w ro zd ziale o p rz e ła d o w a n iu o p erato ró w (str. 878). Tam p o k a z a łe m p rz e ła d o w a n ie tych o p e ra to ró w na użytek k lasy w e k to r zastrzegając ró w n o cześn ie, że d o tego jeszcze w rócim y, gdyż nie je st to zrobione z b y t eleg an ck o . K lasa w e k t, którą p o słu ży m y się w tym p rzy k ład zie, je st jeszcze p ro stsza niż tam ta. #include using namespace std;
iiiiiiim iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiim class wekt { public: double x, y, z;
); /***★ ★ **★ ★ ******* ++ **★ *********■*■********•**★ *••**•***★ ★ **■*.■*■* ++★ *** j / /
g lo b a ln e fu n k c je o p era to ro w e re a lizu ją c e p rze ła d o w a n ia «
o ra z »
d la k la s y w e k t
/★★★★★★★★★★★★★★♦★★**********rt******************rt**************y ostreamfi operator<<(ostream& strumień wyj,
wekt w)
{ strumien_wyj «
w.x << " " << w.y << " " << w. z;
//O
1033
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika return
strumien_wyj;
j'*************************************************************/ istream&
operator>>(istream&
strumień
wej,
wekt
&w)
/ / ©
( strumien_wej return
int
>>
w.x
»
w.y
>>
w.z;
strumien_wej;
main()
{ wekt
a,
b;
cout
<<
"Podaj
współrzędne
wektora
a:
c i n > > a; cout << "Podaj
współrzędne
wektora
b:
cin
>>
b;
cout
«
"Wektor
a
ma
współrzędne
["
<<
a
<<
« cout
endl; « "Wektor
b
ma
współrzędne
["
<<
b
<<
"]"
Po wykonaniu tego programu na ekranie pojawi się (tłustym d ru k ie m są zaznaczone o d p o w ied zi o trzy m an e z k law iatury). Podaj współrzędne wektora a: Podaj współrzędne wektora b: Wektor a ma współrzędne [1 2 W e k t o r b ma współrzędne [7 8
•0 *
123 7 8 64.32e+10 3] 6.432e+ll]
Komentarz O Przyjrzyjm y się najpierw realizacji o p erato ra w staw ian ia <<. (P rzypom inam : operatora w staw ian ia do strum ienia płynącego na ekran). N ajpierw d eklaracja operatora << . ostream&
operator«(ostream&
strumien_wyj,
wekt
w)
T yp rezultatu to oczywiście referencja strum ienia, na rzecz którego o p erato r w yw ołano. Z achow anie tej konw encji um ożliw ia w y g o d n e k ask ad o w e łącze nie operacji cout
<<
a
<<
x
<<
b;
i w ów czas w jednej instrukcji m ożem y na ekranie w y p isy w a ć zaró w n o typy w b u d o w an e jak i typy zdefiniow ane p rzez siebie. Pierw szym arg u m en tem form alnym funkcji o p e r a t o r < < jest referencja do strum ienia k lasy o s t r e a m . N ie zaw sze bow iem stru m ien iem tym m usi być obiekt o n azw ie c o u t : jeśli tę funkcję w y w o łam y na rzecz stru m ien ia płynącego do pliku dy sk o w eg o , to w ów czas liczby zostaną zap isan e na dysku. O perator jest w ięc bardzo un iw ersaln y - służy do w staw ian ia inform acji do jakiegokolw iek strum ienia klasy o s t r e a m .
1034
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika
(Nawet więcej: także do ewentualnych strumieni klas pochodnych od klasy o s tr e a m - pliki dyskoiue obsługuje właśnie klasa pochodna od klasy o stre a m ). W naszy m p rzy k ład zie referencja d o obiektu klasy o s t r e a m b rzm i: stru m i e ń wyj, ale rów nie d o b rze m ogłaby brzm ieć „zd zich o ". P ra w d ę m ów iąc z w y k le dla skrócenia zap isu n azy w a się ją jedną literą. N a razie je d n ak , dla osw ojenia, lepiej napisać coś, co n am uprzy tam n ia z czym m a m y d o czynienia. ^ D ru g im argum entem jest o b ie k t klasy we kt, który chcem y w łaśn ie w y p isać (na e k ra n lub dysk). Ten a rg u m e n t jest przysłany p rzez w artość, bo o b ie k t tej klasy jest m ały, a poza tym nie zam ierzam y niczego w obiekcie m o d y fik o w ać. Tylko p rzec zy tam y to, co ma być w y p isan e na ekran. G d y b y jednak obiekt k lasy wekt był bardzo d u ży - m ogłoby się o p ła cać przy sła n ie do go funkcji nie p rz e z w artość, ale przez referencję (najlepiej referencję o b ie k tu const). O szczędza się w ów czas długiego k o p io w an ia. P rzesłan a zos taje w ted y referencja (przezw isk o ) czyli coś w rodzaju ad resu - a nie cały wielki obiekt. C iało funkcji op erato ro w ej to już zw ykły w ypis na ekran. P o n iew aż składniki w.x, w.y, w.z
są ty p u d o u b le , w ięc w w yrażen iu : strumien_wyj << wek.x
zo staje uruchom iona ta w ersja o p erato ra << , która pracu je z liczbam i double. Tu ju ż nie m a m ow y o „n a sz y m " p rzeład o w an y m o p erato rze. „ N a s z " działa tylko w ted y , g d y po lew ej stronie zn ak u << stoi obiekt klasy ostream, a po p ra w e j obiekt klasy w ekt
{ostream] «
[wekt] ;
© B ardzo pod o b n ie w y g ląd a realizacja o p erato ra >> w y jm o w an ia ze stru m ien ia (w yjm ow ania ze stru m ien ia pły n ąceg o z np. k law iatury). istream& operator>>(istream& strumien_wej, wekt &w)
o T yp rezu ltatu to tradycyjnie referencja do stru m ien ia w ejściow ego, na którym pracujem y. <0- P ierw szy arg u m e n t - to w łaśn ie referencja d o tego stru m ien ia klasy istream, g d y ż nie zaw sze ch o d zi nam o konkretny obiekt o n azw ie cin - będący stru m ien iem płynącym z k law iatu ry . Podobnie jak poprzednio - możemy czasem chcieć czytać coś z pliku dyskowego - wówczas do tej funkcji operatorowej przyślemy referencję do takiego konkretnego obiektu klasy i s t r e a m (lub pochodnej). D rugi a rg u m e n t to oczyw iście obiekt klasy w e k t. W p rz y p a d k u tego o p e r a tora> > d ru g i arg u m e n t m u si być p rzy słan y w sp o só b u m o żliw iający nam m odyfikację ory g in aln eg o obiektu. (Skoro chcem y d o tego obiektu coś w pisy w ać, to jest to przecież m odyfikacja). O d p ad a w ięc p rzesła n ie p rzez w artość. P rzesy łam y przez referencję.
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika N asza funkcja o p erato ro w a w y w o łan a zostanie w ty ch m iejscach w p rogram ie, gdzie p o lew ej stro n ie zn ak u » stoi stru m ień klasy i s t r eam (lub pochodnej), a p o praw ej stro n ie obiekt klasy w e k t. [ is tr e a m ]
»
[w e k t]
W ciele n aszeg o operatora w id z im y np. zapis: s tru m ie n _ w e j >> w .x P o n iew aż sk ła d n ik w. x jest typu d o u b l e , w ięc jest to sytuacja: [istre a m ]
»
[double]
Z ad ziała w ięc tutaj zw ykła w ersja o p erato ra » - ta p rz e z n a c z o n a do w czy ty w ania liczb double.
W naszej k lasie wekt składniki x, y, z są public, w ięc funkcja o p e r ator<< (oraz o p e r a t o r » ) nie p o trzeb u je żadnych p rzy w ile jó w , by móc na nich pracow ać. G d y b y jednak sk ład n ik i te były n ie p u b licz n e (private lub protected), w ó w czas nasza funkcja o p e r a t o r o w a < < (n ie b ędąca przecież fu n k cją składow ą klasy wekt ) nie m iałab y do nich d o stęp u . Jak tem u zaradzić?
„-Zróbmy tę funkcję składnikiem klasy w e k t! " - p o m y ślałeś pew nie. Nie, nie da się. Jeśli chcesz, by p ierw szy m arg u m e n tem funkcji o peratorow ej << był obiekt k lasy ostream, to funkcja nie m oże być sk ład n ik iem klasy wekt. M oże być ty lk o funkcją składow ą klasy ostream a lb o funkcją globalną. Jeśli zrobilibyśm y ją funkcją sk ład o w ą klasy wekt, to p ie rw sz y m arg u m e n tem m usiałby być obiekt klasy wekt. To nas nie u rz ą d z a , bo p rzy zw y czailiśm y się do zapisu c o u t << w; i nie zam ierzam y go zm ieniać na w << cout;
l/H!
Krótko m ów iąc był to zły pom ysł. N a d a l nie m am y ro z w ią z a n e g o p ro b lem u , jak funkcja o p erato ro w a ma mieć d o stę p do pry w atn y ch sk ła d n ik ó w klasy. Jest jednak w yjście. Klasa m oże n a d a ć funkcji o p erato ro w ej przyw ilej d o stęp u do sw ych sk ład n ik ó w niepublicznych. Robi to oczyw iście za pom ocą deklaracji przyjaźni. O to taka zm odyfikow ana definicja klasy: c l a s s w ekt i
d o u b le x , y , z; f r i e n d o s tr e a m & o p e r a to r < < ( o s tr e a m &, w e k t) ; f r i e n d i s t r e a m & o p e r a to r > > ( is tr e a m &, w ek t &);
}; Poza tym - w naszym program ie nic się nie zm ienia.
1035
1036
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika
B ard zo m ało w a ż n e przypom nienie
M ów iłem kiedyś, że definicję zaprzyjaźnionej z klasą funkcji m o żn a napisać w e w n ą trz deklaracji klasy. Zatem nasza ostatnia klasa m o g łab y w y g ląd ać tak: c l a s s wekt { double x, y, z;
//-------------f r i e n d o s tre a m s o p e r a t o r « (o stream S s tru m ie n _ w y j, w ekt w) 1 strumien_wyj << w.x << " " << w.y << " " << w.z; return strumien_wyj;
} //-------------f r i e n d istream & o p e r a t o r » (istream & stru m ien _ w e j , w ekt &w) i s tru m ie n _ w e j >> w.x » w .y >> w. z; r e t u r n s tru m ie n _ w e j; } }; C o to zm ienia? Przypom nę z rozdziału o przyjaźni: 1) Co p raw d a, funkcje są n ad al tylko przyjaciółm i, a nie funkcjam i składow ym i, ale: są teraz k o m pilow ane jako i n l i n e . 2) Funkcje leżą teraz w zakresie le k sy k a ln y m klasy we k t . O zn acza to, że: •
g d y b y w ew nątrz klasy była w m ocy instrukcja t y p e d e f nasi przyjaciele m ogliby w sw oich ciałach k orzystać z n as tę p stw tego faktu (czyli z tak zd efin io w an y ch sy n o n im ó w do istniejących typów ),
•
m ogliby też skorzystać z ew en tu aln y ch ty p ó w w y liczenio w ych enum zdefiniow anych w e w n ę trz u tej klasy.
T ak się sk ład a, że u n as nie ma niczego takiego, ale k o n ieczn ie chciałem o tym w sp o m n ieć teraz, k ied y już jesteś ekspertem o d op erato ró w . Sym etria
N a sz e o p erato ry są bardzo proste, w czytują p o p ro stu 3 liczby ty p u c o u b l e . O czyw iście m ożna by je rozbudow ać, np. o p erato r « w y p isy w a n ia m ógłby m ięd zy te liczby w staw ić przecinki. D o do b reg o stylu program ow ania należy, by o p erato ry b y ły „sy m etry czn e". To znaczy , żeby w czytyw anie m iało taką sam ą form ę jak w y p isy w an ie. O ile w p rz y p a d k u k law iatu ry i ekranu m a to m niejsze zn ac zen ie (estetyczne, lub e w e n tu a ln ie - przyzw yczajenia), o tyle w p rz y p a d k u p racy z p lik iem d y sk o w y m jest to b ard zo w ażne. Jeśli o p erato r w staw ian ia (d o stru m ien ia) zap isał coś na d y sk u , to o p erato r w yjm ow ania (ze stru m ien ia) p o w in ie n u m ieć to za ch w ilę przeczytać. K onkretnie: jeśli o p erato r w staw iania << m ię d z y tym i liczbam i d o d a w a ł p rz e cinki, to o p erato r w yjm ow ania czytając ten za p is p o w in ie n u m ieć te przecinki
1037
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika
zin terp reto w ać . Jeśli um ie on czy tać tylko bez p rz e c in k ó w , to przy w czyty w aniu d ru g ie j liczby (w sp ó łrzęd n a y) nastąpi błąd: z a m ia s t spodziew anych cyfr n a p o tk a n y został jakiś n iezro zu m iały przecinek. O to p rz y k ła d o w a realizacja o p e ra to ró w w sytuacji, g d y u ż y w a m y przecinków : ostreams operator<<(ostream& strumien_wyj, const wekt & w)
{ strunuen_wyj << w.x <<
»
<< w.y <<
,
^ w . z, <<
return strumien_wyj;
/★ **++***★ *******+*******+******************************** * ** +/ istream & o p e r a t o r » (istream & strumien_wej, wekt &w)
(
char znak; strumien_wej » w.x >> znak >> w.y >> znak » return strumien_wej ;
w.z;
} Teraz in stru k cja w czytyw ania w sp ó łrzęd n y ch w ek to ra jest zd o ln a przyjąć zapis 3.14, 7, 512efe
a także o czyw iście zapis 4.14
,
7
,512e6
bo przecież sp acje przed i po p rzecin k u są ig n o ro w an e W s z y s t k o a l b o nic, czyli k w e s t i a d o b r e g o stylu
Sym etrię m o ż em y zachow ać lub nie, ale jest coś w ażn iejszeg o . Trzeba się liczyć, z tym , że p o d c z a s w czytyw ania np. trzeciej w sp ó łrzęd n ej n a stą p i błąd. M o że go spow o d o w ać u ży tk o w n ik przez n a p rzy k ład p o m y łk o w e w ciśnięcie klaw isza "w ykrzyknik". Jakie to ma konsekw encje? D wie p ie rw sze w sp ó łrz ę d n e zo stały już p o p raw n ie w pisane, w ięc zniszczyły sw ą starą, p o p rzed n ią w arto ść i m ają już w arto ść now ą. N ato m iast trzecia w sp ó łrzęd n a, ta przy której u ż y tk o w n ik po p ełn ił błąd pozostanie z w artością starą. N ie jest to eleganckie. N a s z w ektor pow inien m ieć now e w sp ó łrz ę d n e , tylko w ted y , gdy n ie było żadnego b łęd u w trakcie p o d a w a n ia ich w szystkich trzech . Jak to rozw iązać? Nie p o w in n iśm y w czytyw ać ich b ezp o śred n io do w łaściw y ch sk ład n ik ó w , lecz raczej przyjąć je do jakichś chw ilow ych zm iennych lo k aln y ch . D opiero gdy będziem y p ew n i, że przyjęcie w szystkich trzech liczb o d b y ło się p o p raw n ie w tedy m ożna w szystkie trzy liczby przepisać ze z m ien n y ch ch w ilow ych do właściw ych składników . A jak ro zp o zn ać popraw ność? C ierpliw ości. Już n ie d łu g o p o zn am y n arzęd zia do ro zp o zn aw an ia czy nastąpił błąd stru m ien ia w trakcie p rzy jm o w an ia liczb
1038
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika
22.7.1
Operatorów wstawiania i wyjm ow ania ze strum ienia - nie dziedziczy się Z faktu, że p rzeład o w an ie operatora w staw ian ia ( « ) i w y jm o w an ia ( » ) na
rzecz obiektu klasy K n ie m oże być zrealizo w an e jako fu nkcja sk ła d o w a klasy K„ w y n ik a w ażna konsekw encja: W razie, g d y tw orzym y klasę pochodną o d klasy K, o p e ra to ry te nie są dziedziczone. D ziedziczy się przecież tylko s k ła d n ik i klasy, a o p erato ry w staw iania i w yjm ow ania - funkcjam i sk ła d o w y m i nie są. N a w e t jeśli są przyjaciółm i klasy K, to tak że nic nie daje, bo p rzy jaźn i też się nie dziedziczy. Dla now ej klasy p ochodnej należy więc zdefiniow ać n o w e o p erato ry . Ma to swój sens - skoro klasa pochodna zawiera zwykle coś więcej niż klasa podstawowa - to przecież trzeba to „coś więcej" uwzględnić przy wczyty waniu czy wypisywaniu obiektu nowej klasy. D efiniow anie takiego now ego operatora nie m usi jed n ak p o le g ać n a pisaniu całego operato ra „o d zera". Z w nętrza n o w eg o operatora m o ż n a w y w o łać ope ra to r z klasy podstaw ow ej. O to klasa pochodna: class wektor_z_opisem : public wekt { public: char opis[30]; }; Jest o n a, jak w idać, w zbogacona o now y sk ład n ik . N o w e o p e ra to ry w staw iania i w y jm o w an ia ze stru m ien ia m ożem y zd efin io w ać n astęp u jąco (zak ład am , że m am y zd efin io w an e o p erato ry dla klasy w e k t tak, jak w n aszy m ostatnim p ro g ram ie - czyli te bez przecinków ): ostream & operatorce(ostream &strumien_wyj, wektor_z_opisem &w) ^ strumień wyj << reinterpret_cast (w) ; //nowe rzutowanie O //strumien_wyj « (wekt&)w; / / stare rzutowanie strumien_wyj << " " << w.opis; return strumien_wyj;
// ©
/*************************************************************/ istream & operator»(istream & strumien_wej, wektor_z_opisem &w) {
strumień wej » reinterpret_cast (w) ; / / nowe rzutowanie //strumien_wej » (wekt&)w ; / / stare rzutowanie strumien_wej >> w.opis; return strumien_wej;
} Jak w id ać, w ciałach obu funkcji - w obu p rz y p a d k a c h - p o słu ży liśm y się w y w o łan iem o p erato ró w w staw ian ia i w y jm o w an ia z d efin io w an y m i dla
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika
1039
obiek tó w k lasy p o dstaw ow ej. M ożliw e jest to d lateg o , że stosujem y tu rz u to w a n ie o na wszelki wypadek pokazuję to samo rzutowanie i w nowym i starym stylu. R zu to w an ie to d okonuje konw ersji referencji obiektu klasy pochodnej na referen cję obiektu klasy podstawowej. D zięki tej k o n w ersji sp raw iam y , że na w idok tego sy m b o lu < < w tej linijce ru sz y d o pracy funkcja o p eratorow a dla klasy podstawowej w e k t . Tym sam y m w staw io n e zo stan ą d o stru m ien ia te sk ład n ik i, które o d zied ziczy liśm y o d k la sy w e k t . G dy tego dokonam y, pozostaje n am ju ż tylko w staw ić tam jeszcze d o d a tk o w y składnik o p i s. Jest on typu w b u d o w a n e g o c h a r [ ] , zatem w linijce 0 p racu je już op erato r w staw ian ia dla ty p ó w w b u d o w a n y ch . P rzy o p e ra to rz e w yjm ow ania ze stru m ien ia (w czy ty w an ia) - jest podobnie.
22.7.2
Operatory w staw iania i w yjm ow ania nie mogą być w irtualne. Niestety. Z faktu, że o p erato ry w staw ian ia d o strum ienia i w y jm o w an ia z niego nie są sk ład n ik am i klasy, w ynika też konsekw encja, że nie m ogą być funkcjam i w irtu aln y m i. Pamiętamy przecież, że funkcjami wirtualnymi („funkcjami inteligentnie dziedziczonymi") mogą być tylko funkcje składowe jakiejś klasy. Szkoda. D o b rze by było pow iedzieć: „-W id zisz ten sam o ch ó d ? N o to w y p isz o nim w szy stk ie inform acje". A na to ru szałb y do p racy o p e ra to r << od M ercedesa (jeśli w sk a ź n ik pokazyw ałby n a M ercedesa). M imo w sz y stk o da się to jakoś zorganizow ać. P oniżej p o k ażę jak, ale jeśli czytasz tę k siążk ę po raz pierw szy, to rad zę nie za p rz ą ta ć sobie teraz tym g ło w y i przejść d o następnego paragrafu o sterow aniu form atem .
X Jak sobie z a te m tę w irtualność zorganizow ać? O gólna z a s a d a jest taka: D efiniujem y funkcję w irtualną w klasie podstaw ow ej i pochodnej. Tę funkcję w y w ołujem y z operatora « (lub z operatora >>). iinclude iinclude usinci namespace std;
//////////////////////////////////M class samochód { public: int rok_produkcji; virtual void rzecznik (ostream & strum) ;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////y/ class mercedes : public samochód 1
1040
Rozdział. 22. O peracje W ejścia/W yjścia O peratory << oraz >> definiowane przez użytkownika public: string model; void rzecznik(ostream & strum); Z*************************************************************/ // realizacja operatora << dla klasy podstawowej Z*************************************************************/ ostream & operator«(ostream Sstrum, samochód &x) //O x.rzecznik (strum); return strum;
// ©
Z*************************************************************/
// realizacje funkcji wirtualnych
Z****»*********-******* +***************************************/ void samochód::rzecznik(ostream & strum) { strum << rok produkcji; ź*************************************************************/ void mercedes::rzecznik(ostream & strum) { samochód::rzecznik(strum); strum << " " << model;
ź************-*************************************************/ int main() samochód a, b; a. b.
rok_produkcji = 2005; rok_produkcji = 2007;
mercedes m; m.rok_produkcji = 2006; m.model = "sportowy"; cout << a << endl; cout << b << endl; cout << m << endl;
□
Po wykonaniu tego programu na ekranie zobaczymy 2005 2007 2006 sportowy
Kilka uwag W p ro g ra m ie w id z im y zd efin io w an y o p erato r w sta w ia n ia d o stru m ien ia << d la klasy p o d staw o w ej O (Dla k la sy pochodnej tego o p e ra to ra nie ma). C o ciekaw e: o p e ra to r ten nie zajm uje się w y p isan iem n a e k ra n . C h w y t polega n a tym , że on z a m ia s t w y p isać coś na ek ran , w y w o łu je fu n k cję r z e c z n i k , k tó ra jest funkcją sk ła d o w ą w irtu aln ą. To ona będ zie w y p isy w a ła . Rzecznik w y w o ł
1041
Rozdział. 22. O peracje W ejścia/W yjścia Sterowanie form atem
y w an y jest d la referencji obiektu, a w ięc m ech an izm w irtu aln o ści tu się w łaśn ie objaw i. K onkretnie: w e w n ą trz funkcji o p e ra to ro w e j« w m iejscu © u ru ch o m io n y zo s tanie w łaściw y rzecznik. A lbo samochód: :rzecznik albo mercedes::rzecznik - z a l e ż n ie o d tego, czy p o d p rzezw isk iem x ro zp o zn am y obiekt k lasy samochód czy klasy mercedes. L
...... .
22.8
S te ro w a n ie fo rm a te m Jak p o w ied zieliśm y , przy operacjach na stru m ien iu u ż y w a n e są p ew n e d o m n ie m ania d o ty czące form atu inform acji w staw ian ej lu b w yjm ow anej ze stru m ien ia. Jeśli te d o m niem ania n am nie o d p o w iad ają, to m ożem y je zm ien ić i od tej p o ry d a n y strum ień b ęd zie w czytyw ał lu b w y p isy w a ł w ed łu g now ych zasad (w e d łu g now ego form atu).
22.9 Flagi stanu formatowania Bieżące z a s a d y form atow ania zap isan e są w o śro d k u d o w o d z e n ia k o n k retn eg o stru m ien ia - w tak zw anych flagach stanu fo rm ato w a n ia (format słate flags). P rzykład: przez domniemanie typy całkowite zoypisywane są w systemie dziesiątko wym. T o domniemanie wynika właśnie z ustawienia jednej z flag. Jeśli nam to nie odpowiada, możemy - zmieniając flagę stanu formatozoania sprawić, że liczby będą wypisywane szesnastkowa (heksadecymalnie). P oniew aż flagi stanu form atow ania są potrzebne stru m ien io m klasy istream, a także stru m ien io m klasy ostream, a także jeszcze p a ru innym , d lateg o s p ra w y te zeb ra n o i um ieszczono w jednej klasie, k tóra jest p o d staw o w ą dla tych pozostałych. D zięki takiem u dziedziczeniu, au to rz y biblio tek i oszczędzili sobie w iele pracy." Klasa, w której te flagi stanu form atow ania u m ieszczo n o , jest n azw an a ios base (początek tej nazw y pochodzi oczyw iście o d słów: In p u t O u tp u t State). Klasę ios_base d zied ziczy klasa ios, której najw ażniejszą (teraz) dla nas zaletą jest krótkość jej nazw y. N ie żartuję! D zięki tem u, jeśli w jakim ś miejscu p ro g ram u chcielibyśm y skorzystać z b o g actw a klasy ios_base, to m ożem y p o słu ży ć się albo d łu ższy m kw alifiktorem ios_base: :, albo k ró t szym i o s : : Oto, jak - w uproszczeniu - w y g ląd a hierarchia, czyli g ra f dziedziczenia.
3)
Jest to dobry przykład na wielokrotne użycie raz zdefiniowanego kodu (r c u s a b i t i t y ).
1042
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania
I OS
iostream
istringstream
ifstream
' yiuni
fstream
\ stringstream ofstream
\ ostringstream
'jzyiu/Jiźi i fjhuru
Rys. 22-1. Uproszczona hierarchia klas realizujących operacje wejsica-wyjścia. Zwróć uwagę, ze klasa ios_base (a takiże ios) są klasami podstawowymi dla rózego rozdzaju strumieni, zatem te same sposoby formatowania, które stosumemy w przypadku ekranu/klawiatury, będziemy mogli stcwsować w przypadku operacji na plikach dyskowch
A u to rz y biblioteki (instrukcją typcdef) zd efin io w ali w klasie ios_base n azw ę fmtflags. Jest to nazw a typu sk ładników , k tó re p rzech o w u ją nasze decyzje, co d o obow iązującego sposobu fo rm ato w an ia.
Pamiętamy, że instrukcja t y p e d e f nie definiuje nowego typu, a jedynie wymyśla synonim do jakiegoś już istniejącego. Co zatem kryje się za tym typem f m t f l a g s ? Naprawdę nie ma to dla nas żadnego znaczenia. Tym bardziej, że standard zostawia tę sprawę twórcom danej biblioteki. Jest to więc zależne od implementacji.
1043
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania
Rys. 22-2. Przydrożne skrzynki pocztowe. "Flaga" ustawiona na tej skrzynce z lewej, informuje, że do niej listonosz włożył przesyłkę
P rzy stero w an iu form atem p o słu g u jem y się czym ś, co n azy w am y flagą, d lateg o że - p o d o b n ie jak ta flaga na sk rzy n ce - p rzek a zu je ona nam inform ację ty p u "tak /n ie". Z obaczm y, jak nazyw ają się flagi odpow iadające z a sposób form atow ania. skipws lef t right internal boolalpha dec oct hex showbase showpoint uppercase shewpos scientific fixed unitbuf
// ig n o ru j białe zn a k i // le w e //ju s to w a n ie p ra w e // " w e w n ę tr z n e " //u ż y w a j słó w tru e-falsc // d ec ym a łn a //ko n w ersja oklalna //
h cx a d ec yn ia ln a
//p o k a ż p o d sta w ę k o n w e rsji //p o k a ż kropkę d ziesiętn i) //w ie lk ie lite ry (w liczb a ch ) H z n a k + w liczbach d o d a tn ic h II n o ta c ja : w y k ła d n ic za (" n a u k o w a " ) // : " z w y k ła " // n ie buforuj.
D odatkow o, dla naszej w y g o d y , zdefiniow ane są tak zw an e m aski (pola), dzięki k tó ry m łatwiej będzie n am ustaw iać ju sto w an ie, konw ersję i notację. M aski te m ają także swoje nazw y: adj ustf ield basefield floatf ield
II n taska dla ju sto w a n ia le w e g o , p ra w e g o i " w ew n ę trzn eg o " II n taska d la typ u ko n w ersji a e c , h ec, o ct II n taska dla typ u n o ta c ji (w y k ła d n ic z a , d zie się tn a )
1044
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania S koro flagi sterow ania form atem (i m aski) zd efin io w an e są w z a k re sie klasy i o s b a s e i są tam p ubliczne, to m ożna ich u ży w ać sp o z a tego z a k re su - jeśli ich n a z w ę p o p rzed zi się kw alifikatorem zakresu, np.: ios_base::left ios_base::scientific M ożna te ż jednak pisać to krócej, np. ios: :left, ios: : s c i e ntific, bo przecież klasa i o s _ b a s e jest bezpośrednią klasą p o d staw o w ą klasy ios. M ożliw ość tego krótszego zap isu w ażn a jest nie tylko d la len iuchów , którym nie chce się pisać d łuższej nazw y w kw alifikatorze zak resu . O tóż w czasach "p rzed stan d ard o w y ch " o m aw ian e tu flagi znajdow ały się w łaśnie w klasie ios i z tego p o w o d u b ard zo w iele starszych p ro g ram ó w tak w łaśn ie o d n o si się do tych flag. N ic nie szk o d zi - jak w idać, nie m usim y zm ien iać sw oich starych p ro g ram ó w . W d o d a tk u czasem trzeba u ży ć kombinacji kilku flag, a w ted y ta krótsza m o żliw o ść staje się jeszcze bardziej cenna.
22.9.1
Znaczenie poszczególnych flag sterow ania formatem Zanim przystąpię do om ówienia tych flag, chciałbym ostrzec, byś się nie przerażał. Mimo iż początkowo wydawać się będzie, ze to ogrom na ilość inform acji do zapam iętania - to jednak wkrótce poznam y w ygodne narzędzia do sprawnego ustawiania stanu formatowania. Cierpliw ości.
skipws
- (skip w h ite s p a c e s : p r z e s k a k u j białe z n a k i)
U staw ien ie tej flagi jest su g estią, by - w procesie p rzy jm o w an ia zn ak ó w stru m ień ig norow ał ew en tu a ln e białe znaki (spacje, tab u lato ry , z n a k i nowej linii itp.), które p o p rzed zają w łaściw e, oczekiw ane zn ak i. P rzy k ład o w o : jeśli oczekujem y, że strum ieniem (choćby z k law iatu ry ) p rzy p ły ną d o nas jakieś zn ak i rep rezen tu jące liczbę, to - n ie zale żn ie od tego, czy na k la w ia tu rz e zostaną w y stu k an e znaki 247
czy też
[spacja][tabulator][spacja]2 4 7 - efekt b ęd zie ten sam . Po p ro stu dzięki u staw ien iu tej flagi białe zn ak i p rzed liczbą zo stan ą w trakcie w y jm o w an ia ze stru m ien ia zig n o ro w an e - i o trzy m am y po p ro stu sam ą liczbę. Jeśli flaga ta nie jest u staw io n a, w ów czas n ap o tk an ie b iałeg o zn ak u w sytuacji, g d y sp o d ziew an a jest liczba - stru m ień uzn a za b łąd . P rzez d o m n iem an ie ta flaga jest u staw io n a (białe z n a k i są w ięc przeskakiw ane).
1045
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania left right internal
Jeśli te k st na tej stro n ie k siążk i ma ładny w y ra ź n y brzeg z praw ej, to jest to zasłu g a ta k zw an eg o justo w an ia. Justow anie jest w ięc pracą zecerów . Ju sto w ać m oże też stru m ień , g d y go p o p ro sim y o ład niejszy w y d ru k , np. liczby. Ju sto w a n ie w ypisu liczby p o leg a na tym , że jeśli n p . liczba składająca się z 2 cyfr m a zo stać w y p isan a na o b szarze zajm ującym 10 z n ak ó w , to m oże o n a być w y p isa n a tak: -42 -
-42 42
<- left <- right <— internal
(Z n ak am i podkreślenia _ o znaczyłem tu spacje). Jak w id z im y , zależnie od try b u justow ania w y p isy w a n a liczba m oże być: <♦ d o su n ięta d o lewej k ra w ę d z i tego obszaru ( l e f t ) , •> a lb o do praw ej k raw ęd zi tego obszaru ( r i g h t ) , <♦ alb o w y p isan a tak, ż e jej ew en tu aln y z n a k d o su n ięty jest d o lewej k raw ęd zi tego o b szaru , a liczba do praw ej. W ew n ątrz ( i n t e r n a l ) są z n a k i w ypełniające - najczęściej spacje.
W przypadku, gdy wypisujemy nie liczbę, ale wy razy (y) - oczywiście tryb i n t e r n a l nie ma szansy się objawić, efekt będzie więc taki sam jak w trybie r i g h t Jest ch y b a oczyw iste, że w d a n e j chw ili m oże być u s ta w io n a tylko je d n a z tych flag na p o lu justow ania. A lbo decydujem y się na je d e n sposób w y p isan ia, albo na d ru g i, albo na trzeci. P rzez d o m n iem an ie u staw io n a jest flaga r i g h t . Jak k o n k retn ie ustaw ia się te flagi, zobaczym y w n astę p n y c h p arag rafach . Tam też zobaczysz d o czego n ap raw d ę służy to w arzy sząca im m aska io s _ b a s e ::a d ju s tfie ld . W mojej praktyce najczęściej posługuję się ty m i flagam i, g d y ch o d zi mi o w y d ru k liczb w rów nych kolum nach, a w iem , ż e m o g ą się tam pojaw iać zaró w n o liczby jednocyfrow e jak i w ielocyfrow e. Dla w tajem niczonych: Prostymi sposobami ustawiania tych flag jest posłużenie się manipulatora mi o nazwach l e f t , r i g h t , i n t e r n a l . Nazwy manipulatorów są zwykle podobne do nazzo flag, ale oczywiście manipulatormo nie trzeba poprzedzać kwalifikatorem zakresu.
boolalpha G dy stru m ien io w i polecimy w y p isan ie na ek ran ie (lu b w p liku) w arto ści w y ra żenia ty p u b o o l, to - jeśli w y rażen ie m a w artość prawda (true) stru m ień w y p isze
1046
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania 1. Jeśli zaś w yrażenie m a w artość fałsz (false) w ów czas stru m ień w y p isze na. w a rto ść 0. Tak je st przez dom niem anie. Jeśli jednak ustaw im y flagę b o o l a l p h a , to s tru m ień w y p isze w takim p rzy p ad k u na ekranie słow o t r u e lu b f a l s e . P o d o b n ie p rzy w czytyw aniu do obiektu ty p u b o o l - sp o d z ie w a ł się b ęd zie w te d y nie liczb 1 lub 0, ale słów t r u e lub f a l s e . N a z w ę tej flagi łatwo zapam iętać kojarząc a l p h a jako sk ró t od sło w a alfabetycz nie, (co jest p rzeciw ieństw em do słow a numerycznie). Dla w tajem niczonych: Łatwe ustaiuianie/kasowanie tej flagi zapewniają nam manipulatory b o o la lp h a / n o b o o la lp h a .
dec oct hex
Te trz y flagi decydują o ty m , czy w artości liczbow e naszeg o p ro g ram u p o jaw ia ły b ę d ą się na ekranie w postaci dziesiątkow ej (dec), szesn astk o w ej (hex), czy ósem k o w ej (oct). P o d o b n e przy p rzyjm ow aniu liczb (np. z klaw iatury). Jeśli b ęd zie to zap is "101" to stru m ie ń m oże to u z n a ć za liczbę w zapisie d ziesiątk o w y m , szesn astk o w y m lub ó sem k o w y m - zależn ie od bieżącego u staw ien ia tych trzech flag. W d a n e j chw ili tylko jed n a z tych flag może być u staw io n a. Jeśli nie jest u staw io na ż a d n a , w ów czas
*1* w y p isy w an ie liczb odbyw a się w notacji dziesiątk o w ej, ♦♦♦ w czytyw anie liczb odbyw a się w edług notacji dziesiątk o w ej.
Mówiliśmy już o tym przy domniemaniach strumieni - że nie należy tu liczyć na to, iż jeśli wczytyioana liczba rozpoczyna się od 0 lub 0x, to strumień sam przestawi się na notację ósemkową lub szesnastkową. Tego strumień sam nie zrobi! A by w y g o d n iej nam było ustaw iać te trzy flagi, to w arzy szy im m aska zw an a polem (m aską) o d p o w ied zialn ą za podstaw ę konw ersji i o s : : b a s e f i e l d . Jej uży cie zo b aczy m y niebaw em . Dla w tajem niczonych: Prostymi sposobami ustawiania tych flag jest posłużenie się manipulatora mi o nazwach d e c , o c t, h ex.
showbase - pokaż podstawę konwersji4 Jest to flaga, której u staw ien ie jest jakby żąd an iem , by liczby całkow ite w y p isy w an e b y ły tak, żeby łatw o m ożna było ro zp o zn ać w ja k im są system ie.
4)
[czytaj: „szoł bejs"]
1047
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania
Z atem , n a n asze życzenie p rz e d liczbą szesn astk o w ą stan ą zn ak i 0x, p rz e d liczbą ó sem k o w ą 0, p rz e d liczbą dziesiątk o w ą - nic. Poniżej zo b aczy m y w y p is ró żn y ch liczb p rzy u staw io n ej i skasow anej flad ze sh o w b ase. S posób w y p isy w an ia liczby
flaea i o s
b a s e : : sh o w b ase
u staw io n a
nie ustaw iona
hex
0xa4c
a4c
oct
077
77
dec
32
32
P rzez d o m n iem an ie flaga ta jest nie ustaw iona. Dla w tajem niczonych: Prosty sposób ustawień ia/skasowa n ta tej flagi, to posłużenie się man ipulałoratni sh o w b a s e / n o sh o w b a se . show pos
(show positive: pokaż dodatnie)
U staw ien ie tej flagi po w o d u je, ż e p rzy w y p isy w an iu d o d atn ich liczb d z ie s ią t kow ych zo stan ą one p o p rz e d z o n e znakiem + (plus). w ypis, g d y flaga io s b a s e : : show pos ustaw iona
n ie ustaw iona
+ 1 0 7 .2
1 0 7 .2
P rzez d o m n iem an ie flaga ta jest nie ustaw iona. Dla w tajem niczonych: Odpowiedni manipulator nazywa się sh o w p o s.
1048
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania
s h o w p o in t
(pokaż kropkę dziesiętną)
P rz y w ypisyw aniu liczb zm iennoprzecinkow ych flaga ta p o w o d u je, ż e w ypisy w a n e są naw et nieznaczące zera i kropka dziesiętna. Jeśli na p rz y k ła d obo w iązu je dokładność w y p isy w an ia do 6 miejsca po kropce d ziesiętn ej, to liczby _ _ 1 __. . A u . mm • a •
1/ i
W ypis gdy flaga i o s b a s e : : s h o w p o in t
O biekt typu i w artości...
d o u b l e ir. = 7 . 1 4 d o u b le n = 4 in t k = 4
nie ustaw io n a
u staw io n a
7.14 4 4
7.140000 4.000000 4
Jak w idać z ostatniej linijki tej tabeli, flaga ta dotyczy tylko o b iek tó w zm ienno przecinkow ych. N ie w p ły w a na w ypis w artości typu całkow itego. P rzez dom niem anie flaga ta jest nie ustaw io n a.
scientif ic - notacja wykładnicza, „naukowa" fixed - notacja dziesiętna żadna z flag - domniemana notacja "krótka" F o rm at w ypisyw anych p rzez strum ień liczb zależy od o bow iązującej w danej chw ili dokładności. Nie mówiliśmy jeszcze o tym , ale chodź i po prostu o określeń ie ile m iejsc po przecinku strumień ma nam wypisywać. Domniemana wartość tej dokład ności - to sześć cyfr po przecinku. U staw ien ie flagi f i x e d sp raw ia, że liczby rzeczyw iste b ę d ą w y p isy w a n e w po staci liczb dziesiętnych. Pow tarzam : „d ziesiętn y ch "— N ie dziesiątkow ych, ale dziesiętnych. Najmłodszym czytelnikom nieśmiało przypominam, że liczby rzeczywiste możemy zapisywać w postaci ułamkowej (Va) dziesiętnej (0.25) lub wykładniczej (2.5e-l). (Natomiast dziesiątkowy, szesnastkowy lub ósemkowy - może być system liczenia. W programowaniu stosujemy go wobec liczb całkowitych). G d y flaga f i x e d jest u staw io n a, to w artość obiektu
double li cz ba = 91234567.66666666; zostaje w ypisana jako:
91234567.666667 Jak w id zisz, po p rzecin k u jest sześć cyfr (g d y ż taka o b o w iązu je "d o k ład n o ść”). Z a u w a ż też, że s tru m ie ń p o p raw n ie zao k rąg lił szó ste m iejsce p o przecinku (czyli nie odciął, ale z a o k rą g lił w ed łu g reg u ł m atem atyki).
1049
Rozdział. 22. O peracje W ejścia/W yjścia Flagi stanu form atow ania
U staw ien ie flagi s c l e n t i f i c 1^ spraw ia, że liczby b ęd ą w y p isy w a n e w tak zw an ej notacji naukow ej (czyli po prostu: w ykładniczej). Z atem w arto ść następującego obiektu:
double li c z b a = 91234567.66666666; zo stan ie w ó w czas w y p isan a tak:
9 . 1234 57e+007 ja k w ia d o m o (z m atem atyki), za p is w ykładniczy sk ła d a się z cechy i m an ty sy . Z au w aż , że cecha 9 . 1 2 3 4 5 7 m a jedną cyfrę p rzed p rzecin k iem , a p o tem sześć cyfr p o przecin k u . (Sześć, bo taka ak u rat o b o w iązu je "dokładność"). Z a u w a ż też, że stru m ień p o p raw n ie zaokrąglił cechę, k onkretnie jej szó ste m iejsce p o przecinku (czyli nie odciął, a zaokrąglił). 4
Jeśli ż a d n a z tych flag nie jest u staw iona, w ów czas sp o só b zasto so w an y p rzez stru m ie ń d o w y p isy w an ia liczby zależeć będzie o d sam ej liczby. M ianow icie: •
G dy w y k ład n ik będzie m niejszy n iż - 4 lub w iększy o d obo w iązującej d o kładności (d o m n iem an ie = 6), to u ży ta zo stan ie notacja w ykładnicza.
•
W p rzeciw n y m razie użyta z o stan ie notacja dziesiętn a - taka, w której m aksym alna ilość w szy stk ich cyfr (przed i p o p rze cinku) nie p o w in n a przekroczyć w arto ści ustalonej obecnie za "dokładność".
Z atem p rz y tej notacji m alejące liczy będą w y p isy w a n e następująco:
0.0 2
0.002
0.0002,
2e-5,
...
2000000,
2e 7,
N ato m iast rosnące liczy tak:
2000,
20000,
200000,
2e8,
...
Innym i słqw y strum ień w takim p rzy p ad k u stara się w y p isy w a ć liczby w zap isie dziesiętnym d o p ó ty , dopóki zapis ten n ie robi się zb y t d łu g i. Jeśli jest zb y t d łu g i - strum ień dan ą liczbę w y p isu je w k ró tszy m (ale mniej czytelnym ) zapisie w y k ład n iczy m . Z obaczm y w tabeli zestaw ian ie w ypisyw ania trz e c h liczb o b a rd z o różnych w artościach.
5)
ang.: scicntific - naukowy, [czytaj: „sajntyfik"].
1050
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania ....
!:
U stawienie flag f i x e d i s c i e n t i f i c • ' •• .• • fix e d żadna z nich s c ie n tific ... .. .. ■
W artość w y p isy w an a
w artość
1
123.66666666 ' 0.00005678
i 91234567.66666666
dom niem ana notacja "krótka"
komentarz
notacja dziesiętna
!|
notacja w y kładnicza
normalna
123.667
123.666667
1.236667e+002
mała
5.678e-005
0.000057
5.678000e-005
wielka
9.12346e+007
91234567.666667
9.123457e+007
Tabela ta s p o rz ą d z o n a została przy założeniu, że obecnie p a ra m e tr "dokład ność" ma w arto ść 6. Z au w aż więc, że liczba 123.66666666 w dom niem anej notaq'i "krótkiej" w y p isan a zosała z pomocą 6 cyfr (trzech p r z e d przecinkiem i trzech po przecinku). P rzy u staw ian iu tych flag p rzyda się nam maska zw ana io s _ b a s e ::flo a tfie ld Dla w tajem niczonych: Łatwiejszy sposób wyboru wypisu liczb zmiennoprzecinkowych zapewnia ją nam manipulatory f i x e d , s c i e n t i f i c .
unitbuf u staw ien ie tej flagi je st rezygnacją z tak zw anego b u fo ro w a n ia strum ienia. S tru m ień n ieb u fo ro w an y nie jest tak efektywny, jak b u fo ro w an y . D latego przez d o m n iem an ie flaga ta jest ustaw iona.
22.10 Sposoby zmiany trybu (reguł) formatowania W spom niałem już, że stan em form atow ania zajm ujesię klasa i o s b a s e , którą w iele klas później d zied ziczy . W idzieliśm y lo na grafie d z ie d z ic z e n ia (rys. 22-1, str. 1042). N a tym g rafie jasno w idać, że klasę i o s b a s e d z ie d z ic z ą też klasy o s t r e a m o raz i s t r e a m — a w łaśnie obiektam i tych klas s ą zn an e nam od d a w n a stru m ien ie c o u t o raz c i n . Jeśli chcem y zm ieniać fo rm at w ypisyw ania informacji na e k ra n ie p rzez stru m ień c o u t , lu b fo rm at w czy ty w an ia informacji z k law iatu ry stru m ien iem c i n - to m u sim y p oznać, jakim i dysponujem y narzędziam i. K lasa i o s b a s e , o p ró cz sam ych flag, dostarcza nam też n a rz ę d z ia do ich ustaw ian ia. O to, jak - w przybliżeniu - w ygląda ta klasa:
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania c l a s s io s _ b a s e { p u b lic : , omówione wcześniej flagi stanu formatowania
//.... //f m tf la g s fm tfla g s
------------------ - elementarne funkcje składowe f l a g s () c o n s t ; f la g s (fm tfla g s );
fm tfla g s fm tfla g s
setf ( f m t f l a g s ) ; setf ( f m t f l a g s , f m t f l a g s ) ;
f m t f l a g s unsetf (f m t f l a g s ) ; // ------------------------ - inne funkcje składowe — s t r e a m s i z e width () c o n s t ; s t r e a m s i z e width ( s t r e a m s i z e ) ; s t r e a m s i z e precision () const;
s t r e a m s i z e p r e c i s i o n ( s tr e a m s iz e ) ; U ................ i wiele dalszych }? Jak w idać, w klasie są też jakieś funkcje sk ład o w e. Za ich pom ocą m ożem y, m niej lub bardziej w ygodnie, p o sługiw ać się flagam i. Z anim p rzy stąp im y d o o m ów ienia tych funkcji m u szę zastrzec, że jest kilka sposobów zrobienia tego sam ego. Po kolei om ów im y sposoby m odyfikacji p a ra m e tró w fo rm ato w an ia - za pom ocą: ♦♦♦ a) b a rd z o elem entarnych funkcji sk ład o w y ch klasy i o s służących d o u staw ian ia i kasow ania flag. Funkcje te, to s e t f , u n s e t f .
Osobiście nie lubię tego sposobu, bo wymaga on, bym pamiętał nazwy wszystkich omówionych w poprzednim paragrafie flag. ♦♦♦ b) funkcji składow ych z klasy i o s , które nie u staw iają flag, lecz zm ie niają tow arzyszące im param etry , takie jak, na p rzy k ład , szerokość, precyzję itd. c) m an ipulatorów , które pozw alają na w y g o d n e w y k o n an ie obu p o w y ż szych zadań.
To bardzo ciekawa rzecz. Zamiast wywoływać funkcję składowe) dla danego strumienia, xvpuszczamy do niego — jak zatrutą wodę— specjalne kody, które strumień zinterpretuje jako życzenie zmiany sposobu formato wania. Ten sposób wydaje mi się najwygodniejszy (choć nie całkiem uniwersalny). W p o p rzed n im paragrafie m ów iliśm y o klasie i o s _ b a s e i jej funkcjach sk ład o w ych, k tóre trzeba uruchom ić.
1051
1052
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
Jak uruchomić funkcję składową z tajemniczej klasy ios, o której prawie nic nie wiemy? W iem y jednak najw ażniejsze - klasa ta jest klasą p o d s ta w o w ą dla innych klas strum ieni, k tó ry m i się posługujem y. Pam iętasz z a p e w n e z ro zd ziałó w o dzie dziczeniu, że w klasie pochodnej dostęp do p u b liczn y ch sk ład n ik ó w klasy p o d staw o w ej jest taki sam , jakby były one sk ład n ik am i k la sy pochodnej. O zna cza to, że funkcje te m ożem y w yw ołać tak sam o, jak b y b y ły funkcjam i składo w ym i klasy pochodnej. O to jedna z n aszy ch klas pochodnych: os tream. Jest to k lasa opisująca strum ie nie w yjściow e. Jed n y m z obiektów tej klasy jest cout. Jeg o definicja w p rogra m ie w y g ląd ać by m ogła tak: #include using namespace std; ostream
cout;
// <^dcfmicja obiektu klasy ostream
N ie u m ieszczam y jednak takiej definicji w p ro g ram ie, g d y ż zrobił to za nas a u to r biblioteki iostream . M ów iliśm y już, iż a u to rz y biblioteki iostream dom yślając się, że program ista będ zie chciał coś w y p is y w a ć na ekranie sta n d a rd o w o d efin iu je (predefiniuje) kilka strum ieni. C zy li definiuje je za nas, by były od razu zdefiniow ane. Z atem cout jest k o n k re tn y m obiektem klasy ostream. T eraz w róćm y d o naszego problem u: jak w yw ołać fu n k cję sk ład o w ą z klasy ios? A leż to b a rd z o proste! Do tego służy zapis: obiekt.funkcjaO
czyli u nas po p ro stu : cout .funkcjaO W iem y już, jak u ru ch o m ić funkcję na rzecz d an eg o o b ie k tu klasy "strumień". T eraz p o ro zm a w iam y o tym , jakie m am y fu n k c je - i co o n e m o g ą d la nas zrobić.
22.10.1
Zmiana sposobu formatowania funkcjam i s e t f , u n se tf Jeśli chcem y zm ien ić jakąś flagę fo rm ato w an ia, w ó w c zas m o żem y to zrobić po słu g u jąc się, na p rzy k ła d , takimi funkcjam i sk ła d o w y m i klasy ios_base: fmtflags fmtflags
setf (fmtflags k to r e jla g i) ; unsetf (fmtf lags k to rejla g i) ;
M ów iliśm y już, ż e flagi są składnikam i w klasie ios_base , ich (zagadkow y) ty p m a n azw ę ty p fmtflags. Jeśli trochę Cię ta nazwa przeraża, n ie zrób isz dużego błędu umawiając się z samym sobą, że to po prostu inna nazwa typu i n t . Wtedy od razu deklaracje tych funkcji zaczną wyglądać potulnie.
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
Funkcja fmtflags
1053
< ios_base: :setf (fmtflags k t ó r e j l a g i ) ;
Funkcja ta um o żliw ia u sta w ie n ie w słow ie stan u tych flag, k tóre p rzy słaliśm y w e w z o rz e jako arg u m en t. Funkcja, jako rezu ltat, z w ra c a dotychczasow y s ta n w sz y stk ich flag fo rm ato w an ia. N p . in stru k c ja c i n .s e t f (io s ::s k ip w s ); u sta w i flag ę s k ip w s o d p o w iad ającą za ig n o ro w a n ie białych zn ak ó w p rz e z stru m ie ń w ejściow y cin. In n e flagi pozostaną bez zm ian . (S trum ień jest w e j śc io w y —b o , jak p am iętam y , ta flaga odnosi się tylko d o w czy ty w an ia inform acji - w y jm o w a n ia ze stru m ien ia). Jeśli ch ce sz rów nocześnie u s ta w ić kilka flag, to a rg u m e n tem w y w o łan ia tej funkcji n ależy uczynić w y ra ż e n ie będące tzw . O R -ow aniem kilku flag. O R -o w an ie to żarg o n o w e o k reślen ie oznaczające s u m ę bitow ą w y b ran y ch flag (żarg. a n g .: OR-ing). N a p rz y k ła d takie w y w o łan ie tej funkcji: c i n . s e t f ( i o s : : sk ip w s
| i o s : rb o o la lp h a );
u sta w ia ró w n o cześn ie d w ie flagi. Uwaga, czasem ustawienie jednej flagi wymaga, by inna - pokrewna jej została skasowana. Porozmawiamy o tym niebawem.
Dla dociekliwych: Jak p o w ied zieliśm y , rezu ltatem tej funkcji setf jest w arto ść typu fmt flags. Z a s ta n a w ia s z pew nie się, jak to m ożliw e, że funkcja ta za pom ocą tej jednej w arto ści zw raca d o ty ch czaso w y stan w sz y stk ich flag form atow ania. P ro p o n u ję w obec tego p o w ró cić d o skojarzenia, że to fmtflags to jakby ty p int, a po szczeg ó ln e flagi są p o prostu p o szczeg ó ln y m i bitam i w słow ie int. Funkcja zw racając jedną w a rto ś ć ty p u int (p rzep raszam : fmt flags) daje n am w ięc z a je d n y m zam achem inform ację o ustaw ien iu w szystkich flag. To sa m o skojarzenie jasno tłu m aczy nam , jak to m ożliw e, że a rg u m e n tem w y w o ła n ia tej funkcji m oże b y ć w yrażenie będące su m ą bitow ą (O R -ow aniem ) w ielu flag. Po prostu, tutaj bity odpow iadające za po szczeg ó ln e flagi dodają się i tw o rzą sło w o int o kilku o d p o w ied n io u staw io n y ch bitach.
ios: : zamiast ios_base: : Z a u w a ż , ż e już nie piszę n a z w flag z kw alifikatorem ios_base: tylko z k ró tszy m kw alifikatorem ios : : B ardzo to skraca zap is. D laczego to m ożliw e ro zm a w ialiśm y w p o p rzed n im paragrafie. Dla wtajemniczonych: Zamiast używać funkcji s e t f , wygodniej jest ustawiać/kasować flagi za pomocą manipulatorów s e t i o s f l a g s / r e s e t i o s f l a g s . A jeszcze
1054
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
lepiej - za pomocą manipulatorów przeznaczonych specjalnie dla jednej, wybranej flagi, której ustaioienie chciałbyś zmienić.
Kasowanie flag funkcją unsetf Aby sk aso w ać ustaw ioną flagę u ży w am y funkcji sk ład o w ej fmtflags
flagi);
unsetf (f m t f l a g s
Funkcja ta jak o rezultat zw raca w arto ść typu f m t f l a g s o pisującą do ty ch cza sow y stan w szystkich flag fo rm ato w an ia. Funkcja ta s p ra w ia , że w y b ran a flaga zostaje sk aso w a n a, (a inne nietknięte). O to p rz y k ła d jak m ożna skasow ać flagę i o s : : s k ip w s c i n . u n s e t f |i o s : : s k i p w s ) ; D om yślasz się oczywiście, że i tu taj m oglibyśm y się p o słu ż y ć O R -ow aniem w p rz y p a d k u , g d y b y chodziło o sk aso w an ie dw óch lu b w ięcej flag.
Ustawianie flag tworzących grupę (pole) Jak już w iem y , są takie flagi, k tó re w y stęp u ją w g ru p ie z w a n e j polem (ang .field). N a p rz y k ła d , jeśli chodzi o ko n w ersję liczb, to określają ją trzy flagi i o s ::d e c i o s ::h e x i o s ::o c t
Słowo konwersja występuje tu w znaczeniu: zamiana liczby przechowy wanej binarnie w pamięci komputera na jej wartość wypisaną na ekranie w postaci np. szesnastkowej. Tę g ru p ę flag n azy w am y polem odpowiadającym za podstawę konwersji i n azy w am y i o s : : b a s e f i e l d . N azw ę i o s : : b a s e f i e l d m o ż n a o też u w ażać ja k o "m askę", w ybierającą o d p o w ie d n ią część (bity) obiektu ty p u f m t f l a g s . Jeśli chcem y, by na p rz y k ła d strum ień w y jścio w y , który w ypisuje w zapisie d ziesiątk o w y m - w y p isy w ał o d tej p o ry liczby w zapisie szesn astk o w y m - to m u sim y nie tylko u s ta w ić flagę i o s : : h e x , a le także skasow ać flag ę i o s : : d e c . M ożna to zro b ić tak: c o u t . s e t f ( io s : :h e x ) ; c o u t . u n s e t f ( i o s : :d ec) ;
// ustawienie //skasowanie
N ie jest to w y g o d n e , dlatego istn ieje funkcja sk ład o w a, k tó ra zrobi to za jednym zam achem :
fmtflags
setf (fmtflags flaga, fmtflags nazwa_po la) ;
A rg u m en ta m i są tu: flaga, którą ch cem y ustaw ić, o raz n a z w a pola, do którego ona należy. W y w o łan ie tej funkcji sp o w o d u je, że:
1055
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
1. N ajp ierw funkcja zap am ięta d o ty ch czaso w e u staw ien ie flag jako w arto ść ty p u f m t f l a g s - po to, b y m ogła tę w artość z a chw ilę zw rócić jako re z u lta t funkcji. 2. N a stęp n ie funkcja ta skasuje w sz y stk ie flagi należące d o pola p rzy słan eg o , jako d ru g i argum ent. 3. W reszcie funkcja u staw i flagę p rz y sła n ą jako pierw szy a rg u m e n t. O to, jak w y g ląd a w y w o łan ie zam ieniające jedną in stru k cją p o d staw ę ko n w ersji w s tru m ie n iu c o u t , na w y p is "szesnastkow y” cout.setf(ios::hex, ios::basefield); C iekaw o stk a: Funkcją tą m o żn a też skasow ać w szy stk ie flagi należące d o tego p o la - i w ten sposób w rócić do sytuacji d o m n iem an ej. Robi się to tak, że p ie rw s z y m arg u m e n tem czynim y zero. cout.setf(0, ios::basefield);
Przykład i funkcja f lags P oniżej zam ieszczam p rz y k ła d , w którym k o rzy stam y z w łaśnie o m ó w io n y ch funkcji, a także d o d a tk o w o z now ej funkcji flags będącej jeszcze jed n ą funkcją s k ła d o w ą klasy ios. linclude using namespace std; Z************************************************************** int main() { double x = 1175; ios_base::fmtflags stare_flagi; cout << x << endl; cout <<”Zapamietanie flag formatowaniaW; stare_flagi = cout.flags(); cout.setf(ios::showpoint) ; cout << "Po ustawieniu flagi showpoint
" «
//V x «
endl;
cout.setf(ios::scientific, ios::floatfield); cout << "Po ustawieniu flagi scientific "<< x << endl; cout.setf(ios::uppercase); cout « "Po ustawieniu flagi uppercase
"<< x << endl;
cout.setf(ios::fixed, ios::floatfield); cout << "Po ustawieniu flagi fixed "<< x << endl; cout «"Powrot do starego sposobu formatowania\n"; cout.flags(stare_flagi); cout << x << endl; }
// ©
1056
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
Po wykonaniu tego programu na ekranie zobaczymy 1175 Zapamiętanie flag formatowania Po ustawieniu flagi showpoint 1175.00 Po ustawieniu flagi scientific 1 .175000e+003 Po ustawieniu flagi uppercase 1.175000E+003 Po ustawieniu flagi fixed 1175.000000 Powrot do starego sposobu formatowania 1175
W programie wystąpiły nowe funkcje 0 W p ro g ram ie w id zim y funkcję składow ą klasy i o s b a s e
fmtflags
f lags (fmt flags
zestaw);
Już się oczyw iście dom yślasz, ż e funkcja, jako re z u lta t, zw raca w artość ty p u f m t f l a g s obrazującą dotychczasow y stan w szy stk ich flag form atow ania. Jest to funkcja składow a klasy i o s _ b a s e , która p o z w a la ustaw ić w szy stk ie flagi sta n u form atow ania w e d łu g p o d an eg o w z o ru -z e s ta w u . Ten "wzór" m ożna p o d ać za pom ocą O R -ow ania kilku flag. Funkcja ta u staw ia te flagi, które m ają b y ć u staw io n e (obecne z zestaw ie), a w szy stk ie pozostałe - kasuje (zeruje).
I
Dla p o ró w n an ia p rzy p o m n ę, że: «$♦ funkcja s e t f m ogła jedynie u staw ić w y sz czeg ó ln io n e flagi - ale z u p ełn ie obcych - nie ty k a ła ś «$♦ funkcja u n s e t f potrafiła jedynie w y zero w ać w y szczeg ó ln io n e flagi u staw ić niczego nie m ogła. Ta funkcja f l a g s - i u staw ia i kasuje. Nie ma w tym żadnej magii. Jeśli znoiou wyobrazisz sobie flagi jako bity w słowie i n t, to to nowe słoioo in t zastępuję w całości dotychczasowe. W tym nowym, niektóre bity (flagi) są ustawione, niektóre nie - wiec ma to taki efekt, jakby wybrane flagi zostały ustawione, a pozostałe skasowane. O Inny, p ro stsz y w arian t funkcji f l a g s
fmtflags
flags () ;
N iczego n ie ustaw ia, a tylko „ d o w iad u je się" o b ieżące u staw ien ia flag. P o n ie w aż w n aszy m p ro g ram ie z am ierzam y zm ieniać d u ż o flag, w ięc zap a m ię tan ie
6)
Ta dwuargumentowa wersja tej funkcji setf mogła ostatecznie w trakcie ustawiania jednej flagi skasować inną, ale należącą do tego samego pola (maski). N atom iast zupełnie "obcych ” flag - rzeczywiście nigdy nie tykała.
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
1057
p o czątk o w y ch u s ta w ie ń flag pozw oli n am po tem © w ró c ić d o stary ch u staw ień za p o m o c ą jednej instrukcji.
Funkcją f lags m o żn a też na przykład s p ra w d z ić u staw ien ie jednej flagi w taki oto s p ry tn y sposób: i f ( cout.flags () & ios::fixed) cout << "Flaga fixed jest ustawiona!" << endl; } Jak w id a ć , jest tu s p ra w d z e n ie czy w o d p o w ie d z i funkcji flags jest u staw io n y bit o d p o w iad ający stan o w i flagi ios : : fixed.
22.10.2
D odatkow e funkcje do zm iany parametrów form atowania P o zn aliśm y w ięc sp o so b y zm ian flag stanu fo rm ato w an ia. Z w racam u w ag ę, że są to flagi - zatem jest w nich zapisana inform acja typu logicznego: ta k /n ie (p ra w d a /fa łsz ). (N p . p o k azy w ać kropkę d ziesiętn ą: tak czy nie?). O czyw iście nie w sz y stk o da się opisać tym sp o so b em . N ie m o żn a tak określić cho ciażb y inform acji o tym , ile miejsc po p rzecin k u liczby zm ien n o p rzec in k o w ej m a być w y p isy w an e na ekranie. I na to są je d n ak sposoby. W o p ró c z p o zn an y ch w p o p rzed n im p arag rafie funkcji setf,unsetf,flags, są jeszcze inne funkcje składow e. Z obaczym y te ra z ich deklaracje. W y stąp i w nich n o w y , n iezn an y typ o n azw ie streamsize, ale z n o w u : bez paniki! Jest to sy nonim zro b io n y za pom ocą instrukcji typedef - i m o żem y przyjąć (udaw ać), ż e to p o prostu inna n a z w a typu i n t . O to d eklaracje tych funkcji składow ych w k lasie ios base H --- ----------- inne funkcje składowe —
streamsize w i d t h () const; streamsize w i d t h (streamsize) ; streamsize p r e c i s i o n O const; streamsize p r e c i s i o n (streamsize) ; //...... iwiele dalszych oraz^Z
Ch Ch
f i l i (Ch znak); filii) const;
choć już nie w klasie ios_base lecz basic_os.
1058
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
gdzie Ch — oznacza c h a r lub w char t, zależnie od tego, z jakim stru mień iem pracujemy. P oro zm aw iajm y o tych funkcjach p o kolei.
Funkcja width width^ - jest funkcją określającą n a ilu (co najm niej) zn ak ach należy w ypisać d an ą liczbę. Jeśli liczba jest krótka, np. 3 cy fro w a, a zaż ąd am y w y p isan ia jej za pom ocą 10 zn ak ó w , to stru m ień dop ełn i jej zap is siedm iom a d o d a tk o w y m i zn ak am i (np. 7 spacjam i). W su m ie w ięc jej "w ypis" będzie m iał szero k o ść 10 znaków . D o m n iem an a w artość tego p a ra m e tru szerokość to zero, co oznacza, że liczba zo stan ie w y p isan a za pom ocą tylu zn ak ó w , ile jest w y m a g a n e , by ją w ypisać w całości. Jako re z u lta t funkcja ta zw raca dotychczasow ą w a rto ść szerokości. Dla p rz y k ła d u : liczba 107 w y m a g a trzech zn ak ó w : 1, 0 i 7. Stosując funkcję width m o żem y zm ienić sp o só b jej w ypisania int liczba = 107; cout « "Wypis:" << liczba « "\nWypis:"; cout.width(7); cout << liczba << << endl; Na ek ra n ie pojaw i się to jako Wypis:107: Wypis: 107: Funkcja ta p rzy d aje się g łó w n ie w ted y , g d y ch ce m y w y p isy w ać liczby w k o lu m n ach - jakby w postaci tabeli. long kwota[] = { 120, 1650000, 5200, 190000123}; for(int i = 0 ; i < 4 ; i++) { cout << "Rachunek nr cout.width(2); cout << i << " opiewa na sumę:"; cout.width(8); cout « kwota[i] << " EURO\n";
Na ekranie zobaczymy Rachunek Rachunek Rachunek Rachunek 8)
nr nr nr nr
0opiewa 1opiewa 2opiewa 3opiewa
na sumę: 120 EURO na sumę: 1650000 EURO na sumę: 5200 EURO na sumę:190000123 EURO
ang.: width - szerokość [czytaj: „łydf"].
1059
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
W ostatniej linijce w id a ć , że m im o w sz y stk o h arm o n ia się ro zb u rzy ła. To d la teg o , że o k reśliliśm y szerokość (width) jak o 8 znaków . Co najm niej 8 znaków ! Jeśli liczba w y m ag a d o sw ojego w y p isan ia w ięcej niż te 8 z n ak ó w , w ów czas zo sta n ie w y p isan a na ty lu znakach, ile p o trze b a. W efekcie jakby rozepchała szero k o ść k o lu m n y n iszcząc ładny p o rząd ek . S am i jesteśm y sobie w inni. N ale żało p rzew id zieć szersze kolu m n y - na p rzy k ła d : cout.width(12); Z apam iętaj: N ie m a sp o so b u określenia m ak sy m aln ej (do p u szczaln ej) liczby zn ak ó w , n a których w y p ro w a d z a n e mają być liczby. Byłoby to w ręcz w b re w zd ro w em u ro zsąd k o w i. To d lateg o , że g d y b y liczba okazała się za d łu g a - w y m ag ało b y to odcięcia k a w a łk a tej liczby (!) alb o (jak w FORTRA N'ie) w y p ro w ad z en ia w ty m miejscu z a m ia s t liczby - jakichś innych znaków , np. g w iazd ek . Jed n o i d ru g ie jest nie do przyjęcia. Lepiej m ieć ch o ćb y rozburzoną k o lu m n ę, ale za to zaw ierającą p ra w d z iw ą inform ację.
Jeśli zatem chcem y w y p isa ć kilka kolum n obok siebie, m u sim y p rz e d w y p isa n ie m każdej z nich w y k o n a ć funkcję width. M asz rację, czasem m o ż e to nie być zbyt w y g o d n e, ale - w ierz m i - od w ro tn a sy tu acja by była w ręcz nie d o zniesienia. U w a g a: u s ta w ie n ie szerokości w ypisyw ania (funkcją w y p isy w a n ie liczb, ale tak że na w ypisyw anie:
width)
ma
w p ły w
na
«$► w artości ty p u b o o l , w artości liczbow ych p rzechow yw anych w polach bitow ych, ♦♦♦ treści w sk a ź n ik ó w (czyli p rzech o w y w an y ch w nich ad resó w ), pojedynczych z n a k ó w (char, wchar_t), przykładowo: jedna litera 'm' może zostać wypisana na polu oszerokości25 znaków (oczywiście struinieri doda wówczas 24 np. spacji);
*1* tekstów , czyli z a ró w n o treści C -stringów , jak i treści obiektów klasy string czyli do za krótkiego tekstu - strumień doda coś tak, by wypis miał zadaną szerokość. (Ale nigdy nie skróci, nie utnie!) N ie b a w e m p o zn am y w y g odniejszy sposób u staw ian ia szerokości. N azy w a się o n m a n ip u la to re m s e t w (od ang. set w id th - u sta w szerokość).
1060
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania Istn ieje tak że funkcja w i d t h w y w o ły w an a z p u stą listą arg u m en tó w F unk cja ta p o zw ala n am d o w ied zieć się o w łaśn ie obow iązującą szerokość. int stara = cout.width();
Uwaga dotycząca obu tych wersji: Jeśli ch odzi o w czy tyw a n ie inform acji (w yjm ow anie ze stru m ie nia) to u sta w ie n ie szerokości funkcją w i d t h nie m a w p ły w u na w czy ty w an ie liczby. P o w tarzam : liczby! Ż ąd a n ie o kreślonej szerokości objaw i się n atom iast p rz y w czy ty w an iu ciągu z n a k ó w (tekstu). D zięk i tem u m ożem y zap o b iec p rzekroczeniu ro zm iaru tablicy przeznaczonej d o przyjęcia znaków . char napis[7]; cin.width(sizeof(napis) ); cin >> napis; Z k la w ia tu ry przyjętych z o stan ie co najw yżej tyle zn ak ó w tek stu , by taki C -strin g m ógł się zm ieścić w tablicy n a p i s . Z ag ad k a: To zn aczy ile m ak sy m aln ie zn ak ó w zo stan ie przyjętych? O d p o w ied ź: W cale nie 7, ty lk o 6. N a końcu C -stringu m u si być bow iem także zn ak nuli, k tóry zostaje tam zaw sze au to m aty czn ie d o d a w a n y . Należy jednak uważać. Jeśli na tablicę pokazujemy wskaźnikiem char *wskaznik = napis;
to instrukcje cin.width(sizeof(wskaźnik) cin » napis;
);
Wcale nie spowodują wczytania maksymalnie 6 znaków. To dlatego, ze wyrażenie s i z e o f ( w s k a ź n i k ) oblicza rozmiar wskaźnika, a nie tabli cy, na którą on pokazuje. Jeśli w Twoim komputerze wskaźnik do c h a r ma rozmiar 4 bajty, to wczytane zostaną maksymalnie 3 znaki, a do nich dodany znak nuli. Pozostałe elementy tablicy napis zostaną niewykorzy stane. Pytanie: Jak m yślisz - jeśli te k st przyjm ujem y w ten sp o só b d o obiektu klasy s trin g string t; cout << "Napisz długi wyraz:" ; cin.width(7) ; cin >> t; to ile liter z d łu giego w y razu znajdzie się w strin g u ? C z y 7, czy 6? O d p o w ied ź: Jeśli Twój k o m p ilato r jest w tym p u n k cie z g o d n y ze s ta n d a rd e m , to w obiekcie klasy s t r i n g z n ajd zie się 7 zn ak ó w . To d la teg o , ż e p rzecież w
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
1061
obiekcie klasy s t r i n g teksty nie są z a k a ń c z a n e bajtem zero w y m (znakiem nuli). S trum ień m o ż e w ięc w staw ić d o tak ieg o obiektu tyle liter, na ile opiew a
szerokość. Jeśli chcesz w c z y ty w ać tekst, a boisz się, żeb y nie p rzep e łn ić tablicy - to u p rz e d z a m , że w k ró tce p o zn am y na to lep sze sposoby (funkcję g e t l i n e ) . Znasz też ju ż pracę z obiektami klasy s t r i n g , które w razie potrzeby same powiększą swój rozmiar.
Funkcja
f ili N a początku tego p arag rafu p o k azałem deklarację, z której w y n ik ało , że zależnie od ty p u znaków, dla których p rzez n aczo n y jest d a n y stru m ień funkcja f i l i jest fu n k cją pracującą na zn ak ach w ąskich c h a r , albo szerokich wchar_t. Dalej b ę d z ie m y rozm aw iać o w ersji "wąskiej" c h a r .
rill znaczy po an g ielsk u - w ypełnij. Jeśli d ecy d u jem y (za po m o cą funkcji w i d t h), że d a n a liczba m a być w y p ro w a d z a n a na 7 m iejscach, a tym czasem jest o n a w łaśn ie d w u cy fro w a , to p ozostałe 5 m iejsc zostaje w y p ełn io n e spacjami. N ie zaw sze m u szą to jed n ak być spacje. Za pom ocą funkcji składow ej: char fili(char); m o żem y zd ec y d o w ać o innym znaku w ypełniającym . Funkcja ta jako arg u m en t p rzy jm u je w łaśn ie w y b ra n y przez nas zn ak w ypełniający. Jako re z u lta t zw raca z n a k w ypełniający, k tó ry obow iązyw ał dotychczas. Inna w ersja tej funkcji: char fili 0 ; p o zw ala się d o w ied zieć, jaki znak o b o w iązu je obecnie, bez zm ien ian ia go. P rzy k ład em zasto so w an ia funkcji f i l i m oże być sytuacja, g d y na rachunku w y p isu jem y liczbę i chcem y mieć pew ność, ż e nikt jej nie sfałszuje d o p isu jąc coś z p rzo d u . int saldo = 2573;
9) 10)
Uwaga: niezgodny w tym punkcie ze standardem - kompilator MS Visual C++ wersja 6.0 przyjmie tylko 6 liter, czyli zachowa się tak samo, jak wobec tablic znakowych. Dla chorobliwie dociekliwych: Prawdy mówiąc funkcja fili jest funkcją składową nie klasy ios base, ale klasy basic os, (jakby: basie output stream - podstawowy strumieii wyjściowy). To pewne nmmm, ale to dlatego właśnie, że - w niektórych strumieniach wy ściowych - funkcja ta ma mieć postać char, a w innych wchar_t. Nie musisz w ogóle o tym w iedzieć- i tak przecież wywołujesz tę funkcję na rzecz swojego strumienia (np. cout). Nie masz obowiązku pamiętać, że ten Twój strumień jest dziedzicem od tego tajemniczego basie os - ważne jest tylko to, że ta odziedziczona po przodkach funkcja jest do dyspo zycji i już.
1062
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania cout << "stan konta: cout.width(9); cout. fillC*’); cout << saldo << "$\n"; N a ek ran ie zobaczym y: stan konta: *****2573$ Funkcja f i l i daje efekt trw ały, to znaczy w n aszy m p rz y p a d k u g w ia z d k i będą obow iązującym zn ak iem w ypełniającym - d o p ó k i tego nie z m ien im y , pow ta rzając w yw ołanie funkcji f i l i z innym a rg u m e n te m , (p o d ając w te n sposób in n y zn ak w ypełniający). Na zakończenie warto dodać, że ten sam efekt, co wywołanie funkcji f i l i , można uzyskać (nieco wygodniej) stosując tzw. manipulator s e t f i l i , który poznamy niebawem.
Funkcja p r e c i s io r r ^ Jest to funkcja, która po zw ala nam określić d o k ła d n o ść w y p isy w a n ia liczb zm ien n o p rzecin k o w y ch . P rzez dom n iem an ie, o b o w iązu jąca d o k ła d n o ść to 6, czy li jakby: d o k ład n o ść d o 6 miejsca po k ro p ce dziesiętnej. C h y b a, ż e b y łyby to n iezn aczące zera, k tó ry ch nie chcem y (flaga i o s : : s h o w p o i n t ) , to w tedy krócej. •
N ie jest to jednak tak w p ro st, bo p rzecież p rz y za p isie w y k ład niczym (czyli g d y u staw io n a jest flaga i o s : : s c i e n t i f i c ) p a ra m e tr precision oznacza dokładność z jaką w ypisuje się samą cechę (a nie: m an ty sę, tzn. w y k ład n ik ).
•
P rzy w y p isie liczby rzeczy w istej, jako liczby d ziesiętn ej (usta w io n a flaga i o s : : f i x e d ) d o k ła d n o ść ta d o ty czy n a p raw d ę ilości miejsc po przecinku.
•
P rzy zap isie liczby rzeczy w istej w d o m n ie m a n y m trybie "krótkim " (czyli ani flaga i o s : : f i x e d , a n i flaga i o s : : s c i e n t i f i c nie są u staw ione) - d o k ła d n o ść oznacza łączną ilość cyfr przed i po kropce dziesiętnej.
Po szczegóły odsyłam do niedawnego opisu flag i o s : : f i x e d oraz i o s :: s c i e n t i f i c (na str. 1048). O to deklaracje tych fu n k cji w klasie i o s : streamsize precision() const; streamsize precision(streamsize) ;
1063
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania
P am iętasz? U m ó w iliśm y się, że ty p o tajem niczej n azw ie s t r e a m s i ze u zn aje m y za in n ą n azw ę ty p u i n t . Jeśli się na to zg odzisz, to te sam e deklaracje w y g ląd ają p rzy jaźn iej:
i n t precision () const; i n t precision (i n t ) ; P ierw sza z tych fu n k c ji-w y w o ły w a n a z p u stą listą a r g u m e n tó w - ja k o rezu ltat zw raca o b ecn ie obow iązującą dok ład n o ść. D ru g a z tych funkcji jako a rg u m e n t przyjm uje ż ą d a n ą now ą w artość d o k ład n o ści, a jako re z u lta t zw raca starą, d o tą d o b ow iązującą.
Oto przykładowe użycie tych funkcji: double
x = 72.123456789, y = 1 0 .5 5, z = 2;
// cout.setf(ios::showpoint);
cout << "Obecna dokładność = " « ( c o u t . p r e c i s i o n () ) « endl; u „ », « X cout << "domniemany : " << "x= « y << " z= " << Z « endl; << cout .setf(ios::sci entific, ios::floatfield); cout << "wykładniczy: " << "x= " << x « y= " « y « " z= " « z « endl;
c o u t . s e t f (i o s : : fixed, i o s : : f l o a t f i e l d ) ; cout << " d z i e s i ę t n y : " « "x= " << x << *• y= " << y « " z= " << z « endl; / / --------------------------------------------------------------------------------------------cout.precision (2);
cout << "\nTeraz dokładność = " « ( c o u t . p r e c i s i o n () ) « endl; c o u t . s e t f (0, i o s : : f l o a t f i e l d ) ; // ta k w r a c a m y d o cout << "domniemany : "<< "x= " « x ’= " << z « endl; « y « <<
d o m n ie m a n ia
c o u t . s e t f (i o s : : s c i e n t i f i c , i o s : : f l o a t f i e l d ) ; cout << "wykładniczy: " << "x= " « x « " y= " « y « " z= " << z « endl; c o u t . s e t f ( i o s : : fi x e d , i o s : : f l o a t f i e l d ) ; cout << " d z i e s i ę t n y : " << "x= " << x « " y= " « y « " z= " << z « endl;
Na ekranie pojawi się wówczas Obecna dokładność = 6 domniemany : x= 72.1235 y= 10.55 z — 2 wykładniczy: x= 7 . 212346e+001 y= 1 . 055000e +001 z= 2 . 000000e+000 d z i e s i ę t n y : x= 72.123457 y= 10.550000 z= 2.000000 Teraz dokładność = 2
1064
Rozdział. 22. O peracje W ejścia/W yjścia Sposoby zm iany trybu (reguł) form atow ania P am iętasz? U m ó w iliśm y się, że ty p o tajem niczej n azw ie s t r e a m s i z e u zn aje m y za in n ą n azw ę ty p u i n t . Jeśli się na to zg odzisz, to te sam e deklaracje w y g ląd ają przy jaźn iej:
i n t precision ( ) const; i n t precision (i n t ) ; P ierw sza z tych funkcji - w y w o ły w an a z p u stą listą argumentów - jako rezu ltat zw raca o b ecn ie obow iązującą d o k ładność. D ru g a z tych funkcji jako a rg u m e n t przyjm uje ż ą d a n ą now ą w artość d o k ład n o ści, a jako re z u lta t zw raca starą, d o tą d o b ow iązującą.
Oto przykładowe użycie tych funkcji: double
x = 72.123456789, y = 1 0 .5 5 , z = 2;
// cout.setf(ios::showpoint) ;
cout << "Obecna dokładność = " « ( c o u t . p r e c i s i o n () ) « endl; cout << "domniemany : " << "x= " << x << » y= " « y << " z= " << z « endl; c o u t . s e t f (i o s : : s c i e n t i f i c , i o s : : f l o a t f i e l d ) ; cout << "wykładniczy: " << "x= " << x « y= " « y « " z= " « z « endl; c o u t . s e t f ( i o s : : f ixed, i o s : : f l o a t f i e l d ) ; cout << " d z i e s i ę t n y : " « "x= " « x << *• y= " << y « " z= " << z « endl; / / --------------------------------------------------------------------------------------------cout.precision (2);
cout << "\nTeraz dokładność = " « ( c o u t . p r e c i s i o n () ) « endl; c o u t . s e t f (0, i o s : : f l o a t f i e l d ) ; // ta k w r a c a m y d o cout << "domniemany : "<< "x= " « x « » y= " « y « " z — " << z « endl;
d o m n ie m a n ia
c o u t . s e t f (i o s : : s c i e n t i f i c , i o s : : f l o a t f i e l d ) ; cout << "wykładniczy: " << "x= " « x « •• y = •' « y « " z— " << z « endl; c o u t . s e t f ( i o s : : f ixed, i o s : : f l o a t f i e l d ) ; cout << " d z i e s i ę t n y : " << "x= " << x « " y= " « y « " z= " << z « endl;
3
Na ekranie pojawi się wówczas Obecna dokładność = 6 domniemany : x= 72.1235 y= 10.55 z — 2 wykładniczy: x- 7 .212346e+001 y= 1.055000e+001 z= 2 .000000e+000 d z i e s i ę t n y : x= 72.123457 y= 10.550000 z= 2.000000 Teraz dokładność = 2
1065
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
2.11 Manipulatory M a n ip u lato ry to sp ecjaln e w artości, k tó re m ożna w staw ić d o stru m ien ia albo z niego w yjąć p o to , b y w yw ołać zam ie rz o n y efekt u b o czn y , polegający na zm ian ie sp o so b u fo rm ato w an ia. Z au w aż y łeś już ch y b a, jak n ie w y g o d n y jest sposób u s ta w ia n ia flag form ato w an ia za pom ocą p o zn an y c h w p o p rz e d n ic h paragrafach funkcji składow ych. Jeśli chcem y na e k ra n ie w ypisać liczbę ra z w zapisie d ziesiątk o w y m , a potem w zapisie sz e sn a stk o w y m , to posługując się funkcją s e t f ro b im y to tak: i n t i = 30; cout << i; . cout.setf(ios ::hex, ios::basefield); cout « ", " << i;
3
Na ekranie pojawi się wówczas 30, le Z robiliśm y to tak: najpierw w staw iliśm y do stru m ien ia c o u t zm ienną i . N astęp n ie p rz e rw a liśm y w staw ianie i w y w o łaliśm y na rzecz stru m ien ia c o u t jego funkcję s e t f . W reszcie znow u w staw iliśm y do stru m ie n ia zm ienną i , która teraz zo stała ju ż w ypisana h eksadecym alnie. P o p atrz, jak m o ż n a to zrobić spraw niej, po słu g u jąc się m a n ip u latorem o nazw ie hex cout << i «
", " «
hex << i;
P ierw szą rzeczą, k tó ra rzuca się w oczy, jest prostota zap isu . M im o ze efekt na ekran ie jest ten sam , to jednak zasad a jest inna. M an ip u la to r w staw ia się do stru m ien ia tak , jak b y śm y to robili chcąc w ypisać na ek ran ie jakiś obiekt o naz
., .±1
Bardziej obrazowe porównanie jest takie, że manipulator to jest cos, co wpuszcza się do strumienia, jak zatrutą wodę. To, co wpuszczamy, nie ma wcale popłynąć na ekran. Jest to jakby komunikat dla samego strumienia. Innym porównaniem może być chociażby działanie hormonów, jeden or gan z drugim komunikuje się nie bezpośrednio, ale przez wpuszczenie do krwiobiegu śladowych ilości hormonów, których obecność w krwi tamten drugi potrafi wykryć i na nie zareagmoać. M an ip u lato ry są w y g o d n y m n arzęd ziem do zm ian sp o so b u form atow ania p rzez stru m ien ie w e /w y . Zostały one zeb ran e także w k lasie i ° s - atego w szystkie klasy b ęd ąc e pochodnym i klasy i o s m ogą się n im i pos ugiw ac. Skoro tyle m ó w iłem o tzw. rozszerzalności języka C++, w y p a d a pow iedzieć, ze objaw ia się o n a i tutaj. M anipulatory zeb ra n e w klasie i o s , to tak zw an e m ani p u lato ry p red efin io w an e, które zdefin io w an e zostały d la n a s p rzez autorow biblioteki. Jeśli je d n a k dostępne m an ip u lato ry nie zad a w alają nas - łatw o m ożem y zd efin io w ać swoje w łasne. W następnych p arag rafach p o zn am y o ładniej m a n ip u la to ry predefiniow ane —czyli te już gotow e.
w ie h e x .
1066
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
22.11.1
M anipulatory bezargum entowe M an ip u lato r to coś w rodzaju funkcji, której nazw ę w p u szcza się do strum ienia. N azw y tych m an ip u lato ró w zd efin io w an e są w p rzestrzeni n a z w s t d : :. Jeśli obow iązuje w ięc d y rek ty w a u s i n g n a m e s p a c e s t d - to nie m u sim y tego kw alifikatora p rz e d nazw ą m an ip u lato ra zam ieszczać. Tak b ę d z ie m y robić w przy k ład ach , ale tu taj ich nazw y zo b aczy m y razem z k w alifik ato rem s t d : :
Manipulatory sterujące wypisywaniem/wczytywaniem wartości typu bool std:rboolalpha std::noboolalpha Jeśli nazw y tych m a n ip u lato ró w "w puścim y d o stru m ien ia", to sp o w o d u je to o d p o w ied n io u staw ien ie lub sk aso w an ie flagi i o s : : b o o l a l p h a . N p. bool logika = true; cout << "gdy domniemanie: " << logika << ", po boolalpha: " << boolalpha << logika « ", po noboolalpha: " << noboolalpha « logika << endl;
W rezultacie na ekranie pojawi się następujący wypis gdy domniemanie: 1, po boolalpha: true, po noboolalpha: 1 U w aga: Jeśli chciałbyś, by zam iast an g ielsk ieg o słow a t r u e , w y p isy w a n e było sło w o w lokalnym języku dan eg o k o m p u te ra (np. p o p o lsk u ), zain teresu j się tzw . obiektami locale. (Szczegółów szukaj w opisie swojej realizacji biblioteki).
Manipulatory sterujące konwersją liczb std::hex, std::dec, std::oct s td : : dec - u staw p o le b a s e f i e l d na konw ersję d ziesiątk o w ą, s t d : : hex - u staw pole b a s e f i e l d na konw ersję h ek sad ecy m aln ą, s td : : o ct - u staw pole b a s e f i e l d na konw ersję o k ta ln ą. M an ip u lato ry te d ziałają inteligentnie, to znaczy np. m a n ip u la to r s t d : : h e x nie tylko u staw ia falg ę i o s : : h e x , ale ró w n o cześn ie k asu je flagi i o s : : o c t o ra z i o s : : d e c . M an ip u lato ry te m o żn a stosow ać w obec stru m ien ia w yjściow ego lu b wejścio w ego. O to p rzy k ład : int i = 30; cout << "Dziesiatkowo " << i « ”, szesnastkowo " << hex << i « ", osemkowo " << oct << i << endl;
Na ekranie pojawi się Dziesiatkowo 30, szesnastkowo le, osemkowo 36
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
1067
Jak ju ż w iem y , ten sam efekt z a m ia n y p o d staw y k o n w ersji na h ek sad ecy m aln ą m ożna w y w o łać posługując się k ilk ak ro tn ie funkcją sk ła d o w ą s e t f int m = 30 cout.setf(ios::hex, ios::basefield); cout « m; Jednak sp o só b ten nie jest tak w y g o d n y , jak ten z u ży ciem m an ip u lato ra. Oto p rz y k ła d zasto so w an ia m a n ip u la to ró w dla stru m ie n ia w ejściow ego: int j, k; cout << "Podaj liczbę dziesiątkową: cin >> dec » i; cout << "Podaj liczbę heksadecymalna: cin >> hex >> j; cout << "Podaj liczbę oktalna: cin » oct >> k; cout << "Oto podane liczby w zapisie dziesiątkowym " << dec << i « ", " << j << "» " << k;
Jeśli na zadane pytania odpowiemy wystukując na klawia turze liczbę 15, to ekran będzie wyglądał tak Podaj liczbę dziesiątkową: 15 Podaj liczbę heksadecymalna: 15 Podaj liczbę oktalna: 15 Oto podane liczby w zapisie dziesiątkowym 15, 21, 13 G d y b y śm y na p y ta n ie o liczbę w z ap isie oktalnym o d p o w ie d z ie li 99, to nastąpi błąd, g d y ż cyfra 9 jest w zapisie o ktalnym (ósem kow ym ) niedo p u szczaln a. T em u, jak ta k ie b łęd y się objaw iają i jak z nimi p o stęp o w ać, pośw ięcony będzie osobny p arag raf. Z w racam u w a g ę , że p rzy operacjach w czytyw ania z k la w ia tu ry posługujem y się stru m ie n ie m c i n , natom iast d o w ypisyw ania na ek ran słu ż y nam stru m ień cout. Z atem d w ie instrukcje cin » hex; cout « hex; d o k o n u ją z m ia n stan u fo rm ato w an ia w zupełnie ró żn y ch stru m ien iach . Jedno nie m a w p ły w u na drugie.
Manipulator std: : f l u s h Jeśli stru m ie ń w yjściow y jest b u fo ro w an y , to d an e p rz e z n a c z o n e do w y p isy w a nia nie są w y sy łan e od razu, lecz czekają w b u fo rze (jak w poczekalni na dw o rcu ) aż zb ierze się ich w ięk sza partia. W tedy d o p ie ro n astęp u je h u rto w e w ysłanie. W su m ie p ozw ala to na oszczędność czasu.
1068
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory M oże się jednak zdarzyć, że chcemy by coś, niezależnie o d s ta n u zapełnienia bufora zostało w y p isan e natychm iast. Służy d o tego w ła ś n ie m anipulator flush Inicjuje on akcję w ypisania treści bufora. To, c o c z e k a ło w buforze niezależnie o d sto p n ia zapełnienia - zaczyna płynąć stru m ie n ie m . Do w ypisy w an eg o tekstu n ie jest nic dołączan e, w szczególności n ie je st d o łączan y znak now ej linii ( ' \ n '). M anipulator ten m oże okazać się p rz y d a tn y np. gdy p ra c u je m y z debuggerem (program em urucham iającym ). Jeśli m a m y następujący fra g m e n t program u cout <<"wypis cout <<"wypis cout « "wypis
linii 1 "l linii 2 linii 3
w ów czas - g d y za pom ocą d eb u g g era b ędziem y k ro k o w o (linijka po linijce) w ykonyw ali ten p ro g ram - to w cale nie znaczy, że po w y k o n a n iu pierw szej linijki pojawi się na ekranie tekst wypis linii 1 . M oże o n czek ać w buforze, aż uzbiera się coś więcej. Jeśli jednak chcem y m ieć to na e k ra n ie natychm iast, to akcję w ypisania zainicjow ać m ożem y m an ip u lato rem flush cout «
"wypis linii 1 " << flush;
N a ekran p o p ły n ie cała dotychczasow a zaw artość b u fo ra — łącznie z tym C-stringiem . M oże zau w aży łeś, że tak w łaściw ie to ten m an ip u lato r nie zm ien ia sposobu form atow ania p rz e z strum ień - czyli nie "m anipuluje . O n raczej daje im puls strum ieniow i, b y coś w danej chw ili zrobił. M anipulator flush robi to sam o, co funkcja sk ład o w a ostream: :flush (). Funkcję tę m ożna w y w o łać też bezpośrednio. c o u t « " N a p is t e r a z . . . " ; , , , . . . .. c o u t . f l u s h () ; // wysłanie zawartości bufora na ekran, mimo ze mc ma en d l
std ■ ■endl —
( ć tl d l i t l ć
— ZdKOPCZ linię)
Powoduje on w staw ien ie do stru m ien ia zn ak u now ej linii ' \ n ' o ra z w yw ołanie funkcji flush (robi ona to sam o, co m a n ip u la to r flush). Jeśli piszem y pro gram , który ro z m a w ia z u ży tk o w n ik iem , to lepiej sto so w ać m a n ip u la to r e n d l zam iast znaku ' \ n ' cout << "Jest czwarta \n"; cout << "Jest czwarta " << endl; g d y ż dzięki e n d l te k st zn ajdzie się na ek ran ie od razu.
s t d : i ends
[e n d s tv iu g
Z3 koncz strinQ)
M anipulator ten p o w o d u je w staw ien ie d o stru m ien ia z n a k u nuli (bajt zero). M anip u lato r ten p rz y d a je się najczęściej w ted y , g d y w y p isu je m y coś nie na 12)
ang. flush - spłukanie (np. bufora)
[czytaj: „flasz").
1069
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
ek ran , ale d o tablicy zn ak ó w . W p isan e tam zn ak i m o ż n a zakończyć zn ak iem końca C -strin g u . M oim zdaniem ten manipulator stracił znaczenie - i całe szczęście - od czasu, gdy wymyślono wspaniałe strumienie pracujące d la obiektów klasy s t r i n g . Po prostu tam nic trzeba ju ż kończyć tekstu znakiem nuli. (Porozmawiamy o tych sprawach w dalszej części tego rozdziału).
s t d : : s k ip w s { s k i p w h i t e s p a c e s - przeskakuj białe znaki) std: : n o s k ip w s { n o s k i p z u h i t e s p a c e s - nie przeskakuj białych znaków) M a n ip u la to ry
te
p o w o d u ją
o d p o w ied n io
u s ta w ie n ie /s k a s o w a n ie
flagi
i os : :ws.
Z atem u ż y c ie m an ip u lato ra s k ip w s pow oduje, że o d tej p o ry ew en tu aln e b iałe zn ak i p o p rz e d z a ją c e sp o d ziew an ą liczbę lub w y ra z b ę d ą w y jm o w an e ze s tr u m ienia, a le o d rzu can e. T akie jest zresztą d o m n iem an ie, ale m a n ip u lato rem n o s k ip w s m o żem y z tego d o m n iem an ia zrezy g n o w ać. P on iższa in stru k c ja w czyta w ięc d o tablicy w y r a z tak że i spacje p o p rzed zające w łaściw y te k st. char wyraz[80]; cin >> noskipws »
wyraz;
s t d . . ws _ w p n e s p a c e s - przeskocz teraz(!) białe znaki S praw a p o d o b n a , jak przy p o p rz e d n im m an ip u lato rze. Różnica jest jed n ak taka, że ta m te n tylko ustaw iał flagę inform ującą stru m ie ń , jak ma w p rzy szły ch operacjach po stęp o w ać. T ym czasem ten m a n ip u lato r m e inform uje ale s a m bierze się d o roboty. Po prostu u su w a ew en tu aln e białe zn ak i oczekujące w b u fo rze n a w czytanie. U suw a w szy stk o - aż d o n a p o tk a n ia p ierw szeg o nie-b iałeg o z n a k u , lub aż końca dostęp n y ch zn ak ó w .
std::showpoint std: :noshowpoint M a n ip u la to ry te pow odują o d p o w ied n io u s ta w ie n ie /s k a s o w a n ie flagi ios :: showpoint, opow iadającej za to czy p rzy w y p isy w a n iu liczby (np. n a ekranie) m a ją się pojaw iać nieznaczące zera i k ro p k a d ziesiętna. Z atem in stru k cja: << showpoint <<17.0 << ", man. noshowpoint: " « noshowpoint << 17.0 << endl;
cout << "man. showpoint:
sprawi, że na ekranie pojawi się: man. showpoint: 17.0000, man. noshowpoint: 17
1070
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
s t d ::showpos s t d ::noshowpos M an ip u lato ry te pow odują o d p o w ied n io usta w ie n ie /s k a s o w a n ie flagi i o s : : s h o w p o s , opow iadającej za to, czy p rzy w y p isy w a n ej liczbie dodatniej to w arzy szy ć m a zn ak plus (+).
s t d ::unitbuf std::nounitbut M an ip u lato ry te pow odują o d p o w ied n io u s ta w ie n ie /s k a s o w a n ie flagi i o s : : u n i t b u f , opow iadającej za to, czy p rzy stru m ień m a być buforow any.
s t d ::showbase std::noshowbase M an ip u lato ry te pow odują o d p o w ied n io u s ta w ie n ie /s k a s o w a n ie flagi i o s : : s h o w b a s e , opow iadającej za to, czy przy w y p isy w a n ej liczby całkowitej w zapisie szesn astk o w y m d o d a w a n y jest p rzed ro stek 0x, a p rz y w yp isy w an iu liczb w zap isie ósem kow ym p rzed ro ste k 0. Manipulator ten ma sens oczywiście tylko wobec strumienia wyjściowego (wypisującego). P rzy k ład zo b aczy m y poniżej, p rz y okazji n astęp n eg o m a n ip u la to ra .
std::uppercase std::nouppercase M an ip u lato ry te p o w odują o d p o w ie d n io u s ta w ie n ie /s k a s o w a n ie flagi i o s : : u p p e r c a s e , opow iadającej za to, czy p e w n e lite ry u ży w an e przy w y p isie liczba m ają być w ielkie czy m ałe. O to p rzy k ład : int v a l u e = 5342; double pi= 3.141592; cout
cout
<< <<
showbase " " <<
<< <<
value
<< hex « value s c i e ntific << pi
<<
endl;
uppercase <<
"
"
«
pi
«
endl;
W rezultacie na ekranie zobaczymy 0xl4de 0X14 DE
3.141592e+000 3.1 4 1592E+000
Krótki komentarz C h o d zi tu o w ie lk ie /m a łe litery u ż y w a n e w trakcie w y p isy w a n ia liczb, więc najpierw n a leżało sp raw ić, by te liczby b y ły w łaśn ie tak w y p isy w a n e.
1071
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
♦♦♦ Stąd u ż y liś m y tu m a n ip u lato ra h e x , który daje n a m liczbę w zapisie szesn astk o w y m , a tam - jak w iem y - cyfram i o d 10 d o 15 są litery a, b, c, d, e, f. ♦♦♦ Jakby tego było m ało, z a ż ą d a liśm y też (m a n ip u la to re m s h o w b a s e ) w y p isy w a n ia p rzed ro stk a p rzypom inającego, że to z a p is szesnastkow y (0x)
♦♦♦ Aby zo b aczy ć litery w y stęp u jące w zapisie liczb rzeczy w isty ch , stosu jem y też m a n ip u lato r s c i e n t i f i c (o nim poniżej). D ruga instrukcja c o u t w p rzy k ład zie p o k azu je w y p is tych sam y ch liczb po zastoso w an iu m a n ip u la to ra u p p e r c a s e . Jak w idać na e k ran ie - teraz litery są istotnie w ielkie. s t d ::fixed s t d : :scientific
M an ip u lato ry te p o w o d u ją s t d : : f i x e d - u sta w pole f l o a t f i e l d na notację d ziesiętn ą , s t d : : s c i e n t i f i c - u staw pole float f i e l d na notację w ykładniczą, M an ip u lato ry te działają inteligentnie, to zn aczy np. m a n ip u la to r s t d : : f i x e d nie tylko u staw ia flagę i o s : : f i x e d , ale rów nocześnie kasuje sto w arzy szo n ą z nią flagę i o s : : s c i e n t i f i c . Prosty p rzy k ła d u ży cia m an ip u lato ra przykład.
s c ie n t if ic
p o k a z y w a ł p o p rzed n i
s t d ::left s t d : :right s t d : :internal
M anipulatory te p ow odują: : : l e f t - u staw ien ie pola justow ania (d o su w a n ie do lew ego b rzeg u ),
s t d
adj ust f ie ld
: r i g h t - u staw ienie p o la justow ania right (d o su w an ie d o p raw eg o brzegu),
std :
na tryb left
a d ju s t fie ld
na tryb
: i n t e r n a l - u staw ien ie pola justow ania a d j u s t f i e l d na tryb "w ew nętrzny": znak liczby d osunięty do lew ej, sam a liczba do praw ej, a w środku - znaki w ypełniające.
std :
M anipulatory te działają inteligentnie, to zn aczy np. m a n ip u la to r s t d : : l e f t nie tylko u staw ia flag ę i o s : : l e f t , ale rów nocześnie kasuje sto w arzy szo n e z nią flagi i o s : : r i g h t oraz i o s : : r i g h t . Pokazanie, jak te m an ip u lato ry działają - je st m ożliw e tylko w te d y , g d y miejsce, na którym liczba m a zostać w y p isan a, jest szersze n iż sam a liczba. Zatem z przykładem zaczek am y do o m aw ian ia operatora s e t w (n a stę p n y paragraf), który takie "miejsce" o d p o w ied n io po szerza.
1072
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
22.11.2
M anipulatory parametryzowane Inaczej m ó w iąc m anipulatory, k tó re mają jakieś a rg u m e n ty . Z n am y to. Aby określić na ilu znakach ma zostać w y p isan a jakaś d a n a , trzeba było p o d ać tę liczbę zn ak ó w . Ta liczba to w łaśnie arg u m en t (p aram etr). Aby sk o rzy stać ze stan d ard o w y ch m anipulatorów , m ający ch jakieś param etry , m usim y um ieścić w program ie d y rektyw ę: # in clu d e
(Jeśli zaś u ż y w a m y tylko tych b e z a rg u m e n to w y c h - ta d y re k ty w a i n c l u d e nie jest konieczna).
Manipulator setw(int) Jest to sk ró t o d ang.: set widtli —u s ta w szerokość. M a n ip u la to r ten robi to sam o, co w y w o łan ie funkcji składow ej width, c z y li-ja k p a m ię ta m y - za jego pom ocą m ożem y sp raw ić, że np. liczba d w u cy fro w a zo stan ie w y p isa n a na ek ran ie za pom ocą 10 zn ak ó w . Te d o d atk o w e 8, b ęd ą to znaki w y p ełn iając e (por. fili) um ieszczo n e p rz e d lub za liczbą (por. flagi left, right, internal). O to p rzy k ła d , (w którym przy okazji zobaczym y o b ie can e w cześnie d ziałan ie m a n ip u lato ró w o d p o w ied zialn y ch za justow anie). in t valu e = -1234 ; d ou b le p i= -9 9 .5 4 3 2 ; cout
< < " # " < < setw(15) < < v a l u e < < " # < < s e t w (24) « s c i e n t i f i c << p i <<
cout
«
left
cout
« «
"#" « s e t w (15 ) << v a l u e s e t w (24) « p i << " < " «
cout
<<
internal
cout
<< «
" # " << s e t w ( 1 5 ) < < v a l u e s e t w (24) « pi « "<" «
>" " < " <<
e n d l;
; « "# en d l;
>”
<< " # en d l;
>"
;
W rezultacie na ekranie zobaczymy # #-1234
-1234# # 1234#
> - 9 . 954320e+001< > -9 .9 54 320e+001 < >9 . 954320e + 0 0 1<
Z naczki # # o ra z > < pom agają tu "zobaczyć” o b szar o za d a n e j szerokości. Jak p am ięta m y , określenie szerokości nie w pływ a n a w c zy ty w an ie liczb (s tru m ień w ejściow y), ale za to m a w p ły w n a w c z y ty w a n ie znaków . O kreślenie szerokości o k reśla m ak sy m aln ą liczbę znaków , k tó re m o ż n a przyjąć w celu um ieszczen ia ich w tablicy. char sło w o [6]; c in >> s e t w ( s i z e o f ( s ł o w o )
)
>>
słow o;
1073
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
S pow o d u je to w czy tan ie do tablicy s ło w o co najw yżej 5 z n a k ó w i do p ełn ien ie tego zn ak iem nuli.
W Kir1
P odo b n ie, jak funkcja w i d t h , tak i uży cie m a n ip u la to ra s e tw , m a efekt jed n o razo w y - to znaczy dotyczy ty lk o najbliższej operacji w e /w y na d an y m stru m ien iu . P o niej - szerokość w raca n a dom n iem an ą w arto ść 0 - czyli: „tyle, ile koniecznie trz e b a dla w y p isan ia liczby". Trzeba o tym p am iętać szczególnie w te d y , g d y zd efin io w aliśm y swój w łasn y operator << lu b >>. Jeśli nasz o p erato r m a np. w ypisać trz y liczby, to p o p rz e dzenie go m an ip u lato rem s e t w w y w o ła efekt tylko na p ierw szej z nich. N ied aw n o m ó w iliśm y o p rzeład o w an iu operatora » dla k lasy jące użycie o p erato ra: wekt
//
w e k t . N a stę p u
a;
...
cout
<<
setw (15)
«
a;
spow o d u je, że ty lk o w spółrzędna x zo stan ie w y p isan a za p o m o cą 15 zn ak ó w . O czyw iście jest w yjście. Trzeba w o b ręb ie definicji o p e ra to ra „d o w ied zieć się" o w łaśnie o b o w iązu jącą szerokość i p o w tó rzy ć ją p rzed n astę p n y m i w y p isy w a nym i w sp ó łrzęd n y m i. ostream
1
in t
& operator«(ostream szer
=
strum ien
strum ien_w yj
&s t r u m i e ń
w yj,
w y j . w i d t h () ;
//
wekt .
w.y
«
"
.
ja k a o b o w ią z u je ?
<< w . x < < " " < < setw (szer)
«
&w) .
//
o d ś w ie ż a m
//
o d ś w ie ż a m
"
<<
setw (szer)
«
w. z ;
return strumien_wyj ;
} Teraz instrukcją cout
<<
s e t w (15)
<< a ;
w szystkie trzy w spółrzędne będą p o p ra w n ie w y p isan e - k a ż d a za pom ocą 15 znaków .
Manipulator setfill(t y p _ z n a k u z) •
g d zie (typ znaku to
ch ar
lub
wchar
t)
Robi on to sam o, co funkcja sk ład o w a f i l i , czyli p o zw ala n am zd ecy d o w ać jakimi znakam i m ają być do p ełn io n e liczby, które p o leciliśm y w ypisać za pomocą w iększej liczby znaków niż potrzeb a. P rzed ch w ilą życzyliśm y sobie, by w spółrzędne w ektora były w y p isan e n a 15 znakach. Jeśli a k u ra t jakaś liczba jest rów na 0, to w ym aga tylko jednego zn ak u 0. P ozostałe 14 zn ak ó w to tzw .
1074
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory zn ak i w ypełniające, k tó re m ożem y tym m a n ip u la to re m w ybrać. P rzez dom nie m an ie znakiem w ypełniającym jest spacja. O to, jak posługujem y się ty m m anipulatorem : int
kwota
cout
<< <<
=
3200;
"Stan konta: " << s e t f i l l ( ' * ' ) setw(9) << kwota << << endl;
To sam o bez użycia m a n ip u lato ra w y g ląd ało b y tak: cout
<<
"Stan
konta:
c o u t . f i l i ('*'); c o u t .w i d t h ( 9 ) ; cout « k wota <<
"$"
«
endl;
W ob u w y p ad k ach w y p isa n y zostanie tekst: Stan
konta:
*****3200$
jak ju ż w spom inałem , ten m an ip u lato r m oże się p rz y d a ć , g d y p iszem y np. pro g ram księgujący i m usi o n d ru k o w ać rach u n k i, czeki etc. P uste miejsca p rzed kw otą dla zm niejszenia ry z y k a fałszerstw a z a d ru k o w a n e są g w iazd k am i.
M a n ip u la to r setprecision (int) Robi o n to sam o, co funkcja p r e c i s i o n - czyli p o z w a la zm ien iać obow iązującą bieżąco d o k ład n o ść w y p isy w a n ia liczb zm ien n o p rzecin k o w y ch . A rg u m en tem tego m a n ip u lato ra jest liczba i n t określająca ż ą d a n ą d o k ład n o ść. D om niem ana w arto ść dokładności to 6 cyfr. O to p rz y k ła d zastosow ania: double cout
« << <<
x
=
99.123456789;
« " " s e t p r e c i s i o n (2) << s e t p r e c i s i o n (8) < <
X
x x
<< «
" " endl;
To sam o d a się oczyw iście zrobić za pom ocą funkcji b ard ziej skom plikow any.
p re c isio n ,
ale zap is jest
cout << x « " "; c o u t .p r e c i s i o n ( 2 ) ; cout << x « " "; c o u t .p r e c i s i o n ( 8 ) ; cout
<<
x
<<
endl;
W o bu w y p ad k ach sp o w o d u je to w ypisanie: 99.1235
99
99.123457
Jeśli teraz się zastan aw iasz, dlaczego w łaśn ie ta k (d ziw n ie?) tu taj w y p isa n e zo stały te liczby, to p rz y p o m in a m , że m a n ip u la to r s e t p r e c i s i o n (p o d o b n ie jak funkcja p r e c i s i o n ) b a rd z o p o w iązan y jest z b ieżący m sp o so b em w y p isy w a n ia liczb zm ien n o p rzecin k o w y ch . (N otacja "krótka", notacja d ziesiętn a ,
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
1075
notacja w y k ład n icza). W naszym p rzy k ła d zie o b o w iązu je d o m n iem an a notacja "krótka", a w te d y dokładność dotyczy... No właśnie, jeśli zapomniałeś, ło wróć do opisu flag i o s : : f i x e d , i o s : : s c i e n t i f i c lub opisu funkcji i o s : : p c e c l s i o n - tam rozmawialiśmy o tej sprawie po raz ostatni (str. 1062). Jeśli zaś właśnie tamte strony Symfonii spłonęły Ci w pożarze zm ysłów przypominam, że - przy notacji "krótkiej" - dokładność opisuje ile cyfr przed i po przecinku (łącznie) ma zostać użytych do wypisywania liczby. Najpierw mieliśmy domniemaną dokładność 6, więc tyle cyfr ma pierwszy wypis. Potem zmieniliśmy na dokładność 2, więc mamy dwie cyfry. Wreszcie zmieniliśmy dokładność na 8, zatem ostatni wypis składa się z dwóch cyfr przed przecinkiem i sześciu po - razem ośmiu.
Manipulator s t d : :setbase(int) Służy on d o o k reślan ia p o d staw y konw ersji liczb - czyli tego sam ego, co użycie opisanych ju ż m an ip u lato ró w h e x , d e c , o c t . Jak łatw o się dom yślić, poniższe d w a zapisy o d p o w iad ają sobie:
a) b)
c o u t << dec << hex << o c t ; c o u t « s e tb a s e ( lO ) « s e tb a s e ( 1 6 ) « s e t b a s e ( 8 ) ;
D odatkow o je d n a k m ożliw e je stu ży cie m an ip u lato ra z a rg u m e n tem ró w n y m 0 c o u t << s e tb a s e ( O ) ; Pow oduje on p o w ró t do d o m n iem an eg o sposobu konw ersji, czyli takiego, gdzie żad n a z flag na polu i o s : : b a s e f i e l d nie jest u staw io n a. Jak już m ów iliśm y o zn ac za to: ♦> w obec strum ienia w yjściow ego (np. c o u t ) - u staw ien ie konw ersji na d ziesiątk o w ą, ♦♦♦ w obec stru m ien ia w ejściow ego (np. c i n ) - u sta w ie n ie konw ersji także na dziesiątkow ą. Wygląd zapisu liczby, na przykład znak 0 rozpoczynający liczbę nie ma żadnego znaczenia. I tak taka liczba nie zostanie uznana za ósemkową! Z auw aż, że sk o ro argum entem tego m an ip u lato ra jest wartość ty p u int, to m oże ona być w y n ik iem obliczenia jakiegoś w yrażenia, lub w arto ścią jakiegoś global nego obiektu ty p u i n t . Zatem jedna zm ian a w artości teg o obiektu - a w szystkie miejsca w p ro g ram ie, gdzie u ży w am y tego m an ip u lato ra z takim arg u m en tem - natychm iast przestaw iają się na now ą notację... M oże kiedyś Ci się taka sztuczka p rzy d ać.
Manipulatory setiosflags ( f m t f l a g s zestaw) resetiosflags ( f m t f l a g s zestaw) O dpow iadają funkcjom składow ym s e t f i u n s e t t . I c h d ziałan ie polega więc n au staw ien iu ( s e t i o s f l a g s ) - w zg lęd n ie sk aso w an iu ( r e s e t i o s f l a g s ) podanych flag stan u form atow ania.
1076
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory O to p rzy k ła d , w k tó ry m u staw iam y flagę int
liczba
cout
=
i o s
:
:u p p e rc a se
242;
« h e x < < l i c z b a < < ", " << s e t i o s f l a g s (ios::uppercase) < < r e s e t i o s f l a g s ( i o s ::u p p e r c a s e )
<< liczba << << liczba «
", " endl;
T o sam o , w y k o n an e za pom ocą w yw ołań funkcji, jest w y ra ź n ie d łu ższe: cout.setf(ios::hex, cout
cou
ios::basefield);
< < l i c z b a < < ", t .s e t f (i o s : : u p p e r c a s e ) ;
c o u t < < l i c z b a < < ", "; c o u t . u n s e t f ( i o s : :u p p e r c a s e ) ; cout
<<
liczba
<<
endl;
W obu przypadkach spowoduje to pojawienie się na ekranie f 2,
F2,
f2
P rz y p o m in a m , że funkcja r e s e t i o s f l a g s u ż y ta d o sk aso w a n ia określonej flagi kasuje tylko ją, in n e zostają nietknięte. M a n ip u la to r s e t i o s f l a g s m oże służyć d o u staw ien ia (a r e s e t i o s f l a g s do k aso w an ia) rów nocześnie kilku flag. Robim y to za pom ocą su m y bitow ej kilku
flag.
cout
#. .„ . . . Przypom inam - w żargon ie mówisię na to: „ OR-oiuanie" -jako że służy do tego operator bitowy OR ' \ ' <<
setiosflags(ios::uppercase
I i o s ::s h o w p o s ) ;
T ak zasto so w an y m a n ip u la to r rów nocześnie u sta w ia flagę s h o w p o s . Inne pozostają bez zm ian.
u p p ercase
i flagę
N ato m ia st takie zasto so w an ie m an ip u lato ra cout
<<
r e s e t i o s f l a g s ( i o s : :s h o w b a s e
p o w o d u je sk aso w an ie flag
showbase
i
I
unitbuf.
i o s ::u n i t b u f ) ;
In n e p o zo stają b ez zm ian.
1077
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory #define
sep
",
"
//
sep
- jak separator
w ó w czas z a p is cout
<<
a
<<
sep
<<
b;
o d p o w ia d a to zap iso w i cout
«
a
<<
",
"
«
b;
N ie jest to je d n a k p ew n y sposób. W p ro g ram ie m oże być ju ż g d zieś u ży ty sy m bol s e p , jako n a z w a obiektu lu b jakiejś funkcji. N astąp i konflikt.
22.11.4 Manipulator jako funkcja N ajpew niej zd efin io w ać m an ip u lato r jako funkcję. W ó w czas n aw et jeśli już w pro g ram ie istn ieje jakaś globalna fu nkcja o nazw ie s e p - to n a stą p i p rzeład o w a nie tej n azw y . W łaściw a funkcja będ zie ro zp o zn a w an a na p o d sta w ie zgodności arg u m en tó w . O to realizacja takiej funkcji: ostream
&
sep(ostream
&
strum)
1 s t r u m < < ", "; r e t u r n strum;
) Z w racam u w a g ę , że nie jest to p rzeład o w an ie żad n e g o o p erato ra. To zw ykła funkcja g lo b aln a o nazw ie s e p . O czywiście, skoro zn ajd u je się ona m ięd zy operatoram i, to m usi się o d p o w ied n io zachować. D latego funkcja ta jako arg u m en t przyjm uje referencję d o stru m ien ia. Jako rezultat funkcja ta zw raca referencję do tegoż strum ienia. Jeśli będziesz pisał swój m a n ip u lato r, to m usisz dochow ać tej konwencji. D zięki tem u m an ip u lato r m oże w y stąp ić w „kaskadzie" - w połączeniu z typam i w b u d o w a n y m i. cout
«
a
<<
sep
«
b;
N ie dziw się, ż e nazw a funkcji s e p została tu użyta bez n aw iasó w i arg u m e n tów. Do stru m ien ia w puszczana jest tylko nazioa funkcji (czyli jakby jej adres). Strum ień sam sobie uruchom i tę funkcję i w do d atk u jako a rg u m e n t w yśle jej swoją referencję. Pom yślałeś zap ew n e: „Tyle zachodu po to, by zaoszczędzić na pisaniu dw óch cudzysłow ów , przecinka i spacji?" Rzeczywiście m asz rację. Na uspraw iedliw ienie d o d am , że m oim zd an iem łatwiej na k law iatu rze w ystukać s e p niż " , " gdyż to w y m ag a d w u k ro tn eg o przyciśnięcia klaw isza shift - a to absorbuje trochę u w ag i. Z lenistw a m ożna więc posługiw ać się tym m anipulatorem .
1078
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
Sposobem tym m o żn a jednak zdefiniow ać m an ipulator, k tóry w y k o n a dla nas więcej pracy. Z ałóżm y, że w program ie m am y często w ypisyw ać d a n e takiego typu: •
tem p eratu rę - z dokładnością d o 1 miejsca p o k ro p ce dziesięt nej i z zaznaczeniem znaku m inus oraz plus,
•
w ysokość - w liczbach całkow itych, bez zn ak u p lu s (bo ujem na być nie może),
•
gęstość - w tzw. notacji w ykładniczej (naukow ej).
W ym aga to, jak w idać, częstego stosow ania różnych kom binacji zn an y ch nam m anipulatorów . M ożna jednak te m an ip u lato ry - w y m ag an e d o w ypisania jednego typu d a n e j- zap ak o w ać do m an ip u lato ra zd efin io w an eg o p rzez siebie. O to przy k ład o w a realizacja takich m a n ip u lato ró w i sposób ich w y k orzystania. Z astrzegam , że w tym przy k ład zie m a n ip u lato ram i p o słu g u je m y się tylko d w u k ro tn ie, w ięc tru d n o tu docenić, jak b a rd z o ułatw iają n am pracę. #include using
namespace
tinclude #include
std;
// dla lic zb y n //************************************************************* ostream
&
tem(ostream
&
strum)
{ strum return
«
s e t p r e c i s i o n (1)
<<
fixed
<<
«
noshowpos
setw(12);
strum;
//*********+★*****-+******************************************** ostream
&
wys(ostream
&
strum)
{ strum
<<
return
noshowpos
<<
setw(10);
strum;
^ ************************************************************* ostream
&
ges(ostream
&
strum)
{ strum
« <<
return
scientific s e t p r e c i s i o n (4)
«
setw(16);
strum;
}
//************************************************************* int
m a i n ()
{ double
t g
= =
20.47, 0.09273;
int
w
=
3000;
const cout
double
pi
=
2
<<
"Użytkownik
<<
fixed
<<
*
/ / tak m o żn a w y lic z y ć liczbę n
asin(l);
niszczy
obecne
s e t p r e c i s i o n (18)
ustawienia... <<
pi
<<
endl;
"
1079
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
cout
]
<< <<
"\n "\n
Gestosc Temperatura
«
"\n
Wysokosc
<<
endl;
"
<<
ges
<<
2*g
" "
« <<
tern « wys <<
2*t 2*w
Wykonanie tego programu da na ekranie następujący efekt Temperatura = Wysokosc — Gestosc = Użytkownik niszczy Gestosc Temperatura
20.5 3000 ___ 9.2730e-002 obecne ustawienia
3.141592653589793100
1 .8 5 4 6 e - 0 0 1 40.9
=
6000
Wysokosc
K orzyść staje się n a p ra w d ę w idoczna, g d y liczby tego typu m u sim y w ielokrot nie w y p isy w a ć w różnych miejscach p ro g ram u , a jeszcze lepiej w tedy, gdy p ew n eg o d n ia z m ien im y zdanie, co do sposobu w y p isy w a n ia dan y ch jednego typ u (np. gęstości). W ów czas zm ienić n ależy w p ro g ram ie ty lk o ciało m a n ip u latora
22.11.5
ges.
Definiowane manipulatora z parametrem C zy m o żn a z d efin io w ać swoje m a n ip u lato ry , k tóre będą p rzy jm o w ały jakiś a rg u m e n t - tak , jak to robi np. m a n ip u la to r s e t w ? cout
«
s e t w (7)
«
liczba;
M ożna i nie jest to tru d n e. Posłuchaj.
Drogi Jurku! ( ) w rozdziale o man ipulatorach wspominasz o tworzeniu manipulatorów z parametra mi. (Jakieś chore makrodefinicje). Jest inny , prostszy i "obiektowy " sposób: 1. Tworzymy klasę, dajmy na to: class 1
manipulator
m a n i p u l a t o r (i n t virtual
void
parametr) ;
działaj
(ostroam
// &
tu je s t sedno sp ra w y os) i
}; 2. Do tej klasy dodajemy c p e r a t o r « , który wywołuje funkcję d z i a ł a j z odpowiednim parametrem. 3. Funkcja d z i a ł a j ma "zmanipulować" strumień. 4. W odpowiednim miejscu należy użyć tego operatora np. cout
«
manipulator(5);
To jest znacznie prostszy i logiczniejszy sposób. Pozdrawiam, Jakub. P o m y sł Jak u b a jest rzeczyw iście p ro sty i działa. P oniew aż w yjaśnienia Jakuba są sk ró to w e, d la te g o zobaczm y to w postaci bardziej ro zb u d o w an ej. Cała istota tego p o m y słu p o le g a na tym , że d o stru m ien ia w p u szcza się nie tyle zatrutą
22
1080
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory w o d ę, co jakby butelkę z napisanym w śro d k u listem. Ta butelka, to obiekt klasy n a s z M a n i p . W d o d a tk u nie taki sobie zw y k ły obiekt, ale o b iek t chw ilow y, zro b io n y naprędce k onstruktorem .
Zobaczmy to w programie #include
iinclude using namespace std
;
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiitiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiim class
/ /
naszManip
{ int
// ©
arg;
public: naszManip(int
argument)
// ©
: arg(argument)
1 )
/ / -----------------------------------------void
działaj(ostream
sos)
// O
const
{ os.width(arg);
)
/W /////////////////////////////////////W & operator
ostream
<< (ostream
& strum,
naszManip
const
i man)
// © m a n . d z i a ł a j (s t r u m ) ; return
strum
//
©
;
} / ★ ★ ★ ★ ★ * ★ * * * ★ ★ * ★ ★ * * ★ ★ * * * + ***★*★★*** + ★*■*********★★*****■*•★**★****/ int
m a i n ()
i string cout cout
Na
nazwa("Musee
<< <<
"#" "#"
<< «
d'0rsay");
n a z w a << "#\n"; n a s z M a n i p (1 8) < <
nazwa
<<
"#\n";
//
©
ekranie w wyniku wykonania tego programu pokaże się
#Musee #
d'0rsay# Musee
d'Orsay#
Jak ten manipulator jest zrobiony? O N asz m a n ip u la to r b ęd zie o biektem klasy
© Klasa
naszManip.
To definicja tej klasy.
ta m a k o n stru k to r w y w o ły w a n y z je d n y m a rg u m e n tem . Mogę teraz zdradzić tajemnicę, że to ten argument odbierał będzie właśnie parametr naszego przyszłego manipulatora. Jed y n ą rolą k o n stru k to ra jest zap am iętan ie a rg u m e n tu w sk ła d n ik u o n azw ie a r g ( ) . O dbyw a się to za pom ocą listy inicjalizacyjnej.
0
1081
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory K o n stru k to r ten służy d o tego, by m ógł p o w sta ć obiekt klasy n a s z M a n i p , który pam ięta w artość p aram etru . Po co nam tak i obiekt? Po to, by go przesłać p rzeład o w an em u o p erato ro w i < <. © O to definicja tego p rzeład o w an eg o o p e r a t o r a « . ostream
&
operatorc<(ostream
&
strum,
naszManip
const
&
man)
{ man.działaj(strum); return
strum
;
) Jak w idać, z d e fin io w a n y jest on jako funkcja nie-składow a (globalna), w ed łu g zn an y ch nam od d a w n a zasad. ♦> P rzyjm uje d w a argum enty: • p ie rw szy to - jak zw y k le - referencja stru m ien ia, który będzie słu ż y ł do w y p isy w an ia •
d r u g i to referencja d o obiektu klasy n a s z M a n i p .
♦$* T yp re z u lta tu , to - jak zw ykle - referencja d o stru m ien ia. D zięki istnien iu tego o peratora m ożna o d tej pory u ży w ać n p . takiej konstrukcji cout
<<
n a s z M a n i p (69);
Z ag ad k a: P o w ied z, co w łaściw ie robi to w y rażen ie
naszManip
(69) ?
O d p o w ied ź: Jest to w yw ołanie k o n stru k to ra klasy n a s z M a n i p z arg u m en tem liczbą 69. W rezu ltacie więc pow staje o b iek t chw ilow y k lasy n a s z M a n i p . Jeśli nie działa to na Twoją wyobraźnię, to zobacz, jak to samo wyglądałoby wobec typu wbudowanego i n t . cout
<<
int(69);
Strumienie o s t r e a m w iedza, jak zach o w ać się w obec typu w b u d o w a n e g o i n t , ale nie mają pojęcia, co zrobić na w id o k ty p u n a s z M a n i p . A by to w iedziały, zd efin io w aliśm y w łaśn ie ten o p e r a t o r c e . W ciele tego o p e ra to ra pow iem y stru m ien io w i, co m a dalej z takim obiektem robić. A co m a robić? P rzek o n am y się p atrząc n a ciało tego o p e r a t o r a c c . T radycyjnie n a jej końcu jest instrukcja r e t u r n zw racająca referencję do stru m ienia. D zięki tem u m ożliw e będzie k ask ad o w e użycie te g o o p erato ra, np. cout
«
10.0
<<
n a s z M a n i p (69)
<<
"jakieś
zdanie"
;
G d y b y je d n ak to b y ło w szystko - n asz m a n ip u lato r b y łb y „p rzezro czy sty ", czyli nie robiłby nic. O A by nasz m a n ip u la to r rzeczywiście jakoś m an ip u lo w ał stru m ień , w ciele o p e r a t o r a c c jest wcześniej w yw ołanie man.działaj(strum);
Skoro m a n jest o biektem klasy n a s z M a n i p , to na rzecz tego o b iek tu w y w o ły w a n a jest funkcja o n azw ie d z i a ł a j . A rg u m en tem p rzesy ła m y mu strum ień, na k tó ry ona m a zadziałać.
1082
Rozdział. 22. O peracje W ejścia/W yjścia M anipulatory
© O to definicja tej funkcji. Jak w idać, o d b iera ona strum ień p rzez referencję (czyli będzie m ogła d o k o n ać zm ian na oryginalnym strum ieniu). N a s tę p n ie -ro b i, co trzeba. To z n ac zy tutaj um ieszczam y to w szystko, co chcem y, by m anipu lator d la nas zrobił.
I
W naszym p rzy k ła d zie w yw ołujem y tu dla p rzy słan eg o stru m ien ia funkcję i o s : : w id th z a rg u m en tem , który był p aram etrem p rzy tw o rzen iu m anipula tora. To tutaj więc n asz m an ip u lato r m a n ip u lu jestru m ie n iem . (Zrobiliśm y więc po sw ojem u now ą w ersję p aram etry zo w an eg o m a n ip u lato ra s e tw ) . To w szystko! J e ś li b ę d z ie s z c h c ia ł s tw o rz y ć ja k iś s w ó j m a n ip u la to r p a ra m e try z o w a n y z m ia n ę z ro b is z ty lk o tu ta j. N o i o c z y w iś c ie z m ie n is z n a z w ę n a s z M a n ip na ja k ą ś ta k ą , któ ra b a rd z ie j b ę d z ie p rz y p o m in a ła d z ia ła n ie T w o je g o m a n ip u la to ra . MNMMMMMMMMMłMMMMMtM
0 Tu, w funkcji m a in , w id zim y jak m o ż n a sk o rzy stać z tego m anipulatora. W ygląda to tak sam o , jak korzystanie z m a n ip u lato ró w stan d ard o w y ch . Z robiony w ten sp o só b m an ip u lato r m o żn a łatw o przerobić na taki, który ma kilka param etrów . T ak ie ćw iczenie z a p ro p o n u ję Ci na końcu tego rozdziału.
W N a tym w łaściw ie m oglibyśm y zakończyć o m aw ian ie tego p rz y k ła d u , ale dla bardziej am bitnych m a m pytanie. Czy d o m y śla sz się dlaczego w kilku m iejscach w po w y ższy m p ro g ram ie są konieczne p rz y d o m k i c o n s t ?
I
W ynika to z dw óch faktów . Po pierw sze te g o ,ż e m am y d o czy n ien ia z obiektem chw ilow ym , a po d ru g ie , że taki w łaśn ie ch w ilo w y o b iek t o p e r a t o r < < ma przyjąć nie przez w arto ść, ale przez referencję. G d y b y przyjm ow ał p rz e z w a rto ś ć -ż a d e n problem . Skoro je d n a k p rz e z referen cję, to kom pilator w y m a g a od nas paru obietnic. Z au w aż: jeśli w y sy łam y obiekt d o funkcji p rz e z referencję, to zn aczy , że w tej funkcji zam ierzam y p raco w ać na jego o ry g in ale (aby n p . zro b ić jakieś jego m odyfikacje). Praca n a o ry g in ale obiektu chwilowego nie m a je d n a k (w g kom pi latora) specjalnie se n su , bo co z tego, że m y d o k o n am y tam b u d o w y w ieży Ba bel, skoro on, k o m p ilato r, i tak za chw ilę w sz y stk o w y rzu ca d o śm ietnika. A by w ięc nam nie robić ż a d n y c h złu d zeń , k o m p ila to r ż ąd a , by p r z y o d b io rze przez referencję a rg u m e n tu b ędącego obiektem ch w ilo w y m - zam ieścili słów ko c o n s t . To słów ko m ó w i kom p ilato ro w i coś takiego: Uspokój się. Odbieram ten Twój kompilatorowy obiekt chwilowy —co prawda - przez referencję, ale nie po to, by na tym obiekcie robić jakieś zmiany. N a to k om pilato r m yśli:
1083
Rozdział. 22. O peracje W ejścia/W yjścia Nieform atow ane operacje wejścia/wyjścia
Aha, rozumiem. On sprytnie odbiera obiekt przez referencje (a nie przez wartość) tylko po to, by uniknąć niepotrzebnego kopiowania. O d tej p o ry w ię c - w o p e r a t o r ze< < o b ie k t man jest tra k to w a n y jako stały. M a to d alsze konsekw encje... P o p atrz dalej (© ). N a rzecz tego stałeg o obiektu w y w o łan a jest jego funkcja sk ład o w a. Jak p a m ię ta m y - na rzecz o b iek tu stałego w o ln o w y w o łać tylko te jego funkcje sk ła d o w e , które mają p rz y d o m e k c o n s t . D lateg o kom pilator z a ż ą d a tego p rz y d o m k a c o n s t także i p rz y definicji tej funkcji. T o tyle. Jeśli z tym c o n s t - nie w szy stk o jest dla C iebie jasne, to p o prostu u su ń znaczek & z d ek laracji d ru g ieg o a rg u m e n tu o p e r a t o r a < < , a w te d y rów no cześnie m ożesz u s u n ą ć oba p rzy d o m k i c o n s t z tego p ro g ra m u . Z apłacisz za to ty lk o m inim alnie d łu ż sz y m w y k o n an iem p ro g ra m u . D łuższym tylk o o zrobienie k o p ii obiektu klasy maszMaip, ale przecież o b ie k t ten nie jest w cale d u ży . C h o ciaż z drugiej strony, jeśli zam ierzasz u ż y w a ć teg o m an ip u la to ra w jakiejś pętli p ro g ra m u m iliony razy ...
22.12 Nieformatowane operacje wejścia/wyjścia Różnice między operacjami formatowanymi, a nieformatowanymi h- huht
-ritrmifnmfiiirt Mumiin nur-iium— n r —!——
i w iM r— r—mum—rm — n rT ‘JT r " ,"‘
Operatory « » wstawiania i wyjmowania ze strumienia przeprowadzają operacje we/wy z obecnością formatowania.
F orm atow anie ta k ie jest sp raw ą prostą ty lk o w p rz y p a d k u p rzesy ła n ia stringu __znaki d o p rz e s ła n ia są już w ted y p rzecież gotow e i w łaściw ie nie trzeba ich interpretow ać. Chociaż i tutaj - jak widzieliśmy - manipulator s t d : : s e t w może dokonać formatowania szerokości. Jednakże w p rz y p a d k u liczb - fo rm ato w an ie jest koniecznością. N a p rzy k ład s tru m ie ń odczytuje w arto ść liczby i n t (czy d o u b l e ) zapisaną binarnie w d an y ch kom órkach pam ięci, a n astęp n ie m u si tę w arto ść p rzed sta w ić za pom ocą g r u p y jakichś cyfr, k tó re m ają się pojaw ić na ekranie. P odobnie jest z w czy ty w an iem liczb. N ie sądzisz chyba, że w y stu k an a na k law iatu rze liczba -3.4e+02
jest w ten sposób p rzec h o w y w an a w ko m ó rk ach pam ięci operacyjnej? Tą inter pretacją (fo rm ato w an iem ) zajm ow ały się o p erato ry w czy ty w an ia » i w ypisy w ania « . N a ich p ra c ę m ieliśm y w p ły w zm ieniając flagi sta n u form atow ania. Są je d n a k sytuacje, fo rm ato w an ie.
I
gdy
nie
jest
n am
p o trze b n e
żadne
N p. chcem y p rzy jm o w a ć bajty płynące z k law iatu ry n iezależn ie od tego, jaką inform ację niosą, niezależnie od teg o , czy są to spacje, czy znaki
22
1084
Rozdział. 22. O peracje W ejścia/W yjścia Nieformatowane operacje wejścia/wyjścia alfanum eryczne. N ie tylko zresztą z k law iatu ry . Przecież m o żem y czytać dane ze strum ienia płynącego od skanera, czy płynące do nas z p lik u dyskow ego. Nie są o n e w żad n ej czytelnej dla człow ieka postaci, więc p ro c e d e r form atow ania jest nam niepotrzebny.
Operacje nieformatowane - prezentacja funkcji wyjmujących ze strumienia Dla operacji nieform atow anych w ejścia/w y jścia m am y d o d y spozycji zestaw funkcji składow ych znajdujących się w klasach i s t r e a m o ra z o s :r e a m . Podobne funkcje istnieją też w klasach pracujących ze znakami szerokimi w char_ t. Tam znak może mieć rozmiar np. dwóch bajtów (zależy to od implementacji). Zatem wówczas - czytając dalsze opisy - weź na to poprawkę. W klasie i s t r e a m są funkcje odp o w iad ające za n ie fo rm ato w an e wczytywanie informacji. Inaczej m ów iąc: za w y jm o w an ie inform acji ze stru m ien ia, bez interpretacji jej. O to deklaracje tych funkcji - p rzy p o m in am , że ty p s t r e a m s i z e m ożna sobie osw oić m yśląc o nim , jako o innej n azw ie ty p u i n t . O
istreams
get(char
int
g e t ();
&) ;
istreams int
g e t ( c h a r *, s t r e a m s i z e , ch a r = '\n'); g e t l i n e ( c h a r *, s t r e a m s i z e , c h a r = '\n')
istream&
read(char
*,
streamsize);
streamsize readsome (char*, istreams
ignorefint,
0 ©
streamsize);
int);
o
Funkcjom tym p o św ięcim y osobne p arag rafy . W skrócie m o żn a p o w iedzieć, że: O P ierw sze dw ie słu żą d o w czytania jed n eg o zn a k u (bajtu). N ie daj się zw ieść, tym ty p e m c h a r . Ten ty p w cale n ie oznacza, że p rzy jm o w ać b ędziem y z n a k i alfan u m ery czn e. C h o d z i po prostu o - o g ó ln ie m ów iąc - bajty. Bajt to 8 bitów - a ta k ą pojem ność ma w łaśn ie o b iek t typu c h a r . 0
N astęp n e d w ie funkcje pozw alają w czytać z a d a n ą liczbę z n a k ó w (bajtów), chyba że na p o tk a n y z o s ta n ie sp e c ja ln y b a jtu z n a w a n y p rz e z n a s z a ogranicznik,
© Funkcja r e a d słu ż y d o w czytania określonej liczby bajtów —b ez w zg lęd u na ich treść, a funkcja r e a d s o m e tym się różni od r e a d , że jeśli b ajtó w jest m niej, to w czy ta tylko tyle, ile jest. O Funkcja i g n o r e w czy tu je (w yjm uje ze stru m ien ia) g ru p ę z n a k ó w (bajtów) i ignoruje je — czyli nie u m ieszczą ich n ig dzie. Z ostają o n e o d ra z u zap o m n ian e.
W klasie
iostream streamsize int
mamy jeszcze dalsze pożyteczne funkcje: g c o u n t (); p e e k ();
1085
Rozdział. 22. O peracje W ejścia/W yjścia Nieform atow ane operacje wejścia/wyjścia istream istreara
putback(char) ;
& &
u n g e t ();
a c o u n t - P o zw ala d o w ied zieć się ile z n a k ó w (bajtów) zo stało w czy tan y ch za pom ocą o statn iej operacji n iefo rm ato w an eg o w czy ty w an ia (w y jm o w an ia ze strum ienia). p e a k - S łuży d o p o d g lą d n ięcia jaki to bajt czeka w stru m ie n iu na w yjęcie go. Funkcja ta ty lk o p o d g lą d a, nie w yjm ując niczego, p u t b a c k - P o zw ala rozm yślić się i zw rócić d o stru m ien ia w łaśn ie w czy tan y bajt. Bajt ten w raca d o stru m ien ia i m o że być jeszcze raz w yjęty najbliższą operacją w yjm ow ania z te g o strum ienia. u n g e t - P o d o b n ie jak p u t b a c k , z tym , ż e tutaj nie m a a rg u m e n tu . (S trum ień przecież sam w ie , co w yjm ow aliśm y ostatnio).
Funkcje służące do nieformatowanego wkładania do strumienia W klasie ostream są funkcję sk ła d o w e o d p o w iad ające z a n iefo rm ato w an e w y p isy w an ie inform acji. ostream ostream
put(char); write(char
*,
streamsize);
P ierw sza z n ich w k ład a d o stru m ien ia jeden bajt, d ru g a z a ś m oże w łożyć w iększą liczbę bajtów . Także i tym funkcjom p o św ięcim y d o d atk o w e parag rafy .
W W klasie iostream, która - jak w sp o m in aliśm y - je st p o ch o d n ą od klasy i s t r e a m o ra z o s t r e a m , o d zied ziczo n e są w szystkie p o w y ż sz e funkcje - te w yjm ujące i te w staw iające do stru m ien ia.
Na koniec jeszcze jedno zastrzeżenie: W po w y żej d ek laro w a n y ch funkcjach w sk aźn ik d o b u fo ra, z k tó reg o pochodzą bajty k ie ro w a n e d o strum ienia, o k reślan y jest jako c h a r * . P am iętać należy, że rów n ie d o b rz e m o żem y mieć w sk aźn ik ty p u u n s i g n e d c h a r * . Nie dlatego, żeby było to w sz y stk o jedno. W ręcz przeciw nie, są to z u p e łn ie różne typy. Po pro stu d lateg o , ż e istnieje d ru g i k o m p let takich funkcji zd ek la ro w a n y na okoli czność w sk a ź n ik ó w u n s i g n e d c h a r * . Dla o szczędności m iejsca nic zam iesz czam d ek laracji tego drugiego k o m p letu . Jednak p a m ięta ć należy, że np. funkcja ostream
&
write
(char*,
streamsize);
m a b rata b liź n ia k a ostream
&
write
(unsigned
char*,
streamsize);
W ten sposób z d u b lo w a n e są zaró w n o funkcje w staw iające d o stru m ien ia, jak i w yjm ujące z niego.
1086
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia
2 2 .1 3 O m ó w ie n ie fu n k c ji w y jm u ją c y c h ze s tru m ie n ia
22.13.1 Funkcje do pracy ze znakami i stringami Funkcja
istream
& get(char
&
znak) ;
Funkcja ta w yjm uje z e strum ienia jeden bajt i um ieszcza go w p o d an ej zm iennej z n a k . N a p rzy k ła d takie użycie tej funkcji: c h a r c; cin.get(c);
sp o w o d u je w czy tan ie jednego bajtu z e stru m ie n ia um ieszczen ie go w zm iennej c.
cin
p ły n ąceg o z k law iatury i
O czyw iście nie m u si chodzić o k law iatu rę. M oże to być bajt w czy tan y z pliku dyskow ego. Funkcja czyta - n iezależnie od tego czy jest to bajt zero, bajt "dzw onek", a m oże p o prostu bajt b ędący w artością b in a rn ą liczby, która ma być p rzec h o w y w an a w ty p ie całkow itym , jakim jest p rzecież c h a r . Jako rezu ltat funkcja g e t zw raca referencję do stru m ien ia, na którym praco w ała. P ozw ala to na w y g o d n e k ask ad o w e użycie tej funkcji c i n . g e t ( a ) . g e t ( b ) . g e t (c);
P ow y ższa linijka sp o w o d u je wyjęcie ze stru m ien ia c i n trzech bajtów i um iesz czen ie ich w a, b o raz c. Jeśli oczekujem y funkcją g e t na znak z k law iatu ry , a o k aże się, że jest to znak koń ca pliku d an y ch E O F ^ - to w ów czas re z u lta tz w ra c a n y p rz e z funkcję będzie 0 (tzw . NULL ), a nie: referencja do stru m ien ia, na k tó ry m o n a pracow ała. Z w racam u w ag ę, że to nie zero zo stan ie w czytanie. W czy tan y zostanie znak EOF, n ato m iast w artością tego w czytującego w y rażen ia b ę d z ie 0. To, czym jest w danym komputerze znak końca pliku EOF - zdefiniowane jest w pliku nagłówkowym i o s tream. N iezależn ie od rezu ltatu 0, ó w znak EO F zostanie w p isa n y d o oczekującej na n ieg o zm iennej c h a r . D zięki tem u zero w em u rezultatow i - funkcję tę m o żn a u m ieścić w pętli w h ile , k tó ra będzie w y k o n y w ała się d o póki nie n a p o tk a n y z o stan ie z n a k końca p lik u . c h a r znak; while(cin.get(znak))
// czyli dopóki nie zerowe
i cout
13)
<< «
"Wcisnales " ", k o d : " «
«
znak (int)znak
ang. end offile - koniec pliku, (czytaj: „end of faji"]
<<
endl;
1087
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
cout
Funkcja
int
«
"Pożegnalny
znak m i a ł
kod
" «
(int) z n a k
«
endl;.
g e t () ;
P odo b n ie, jak p o p rz e d n ia funkcja, ta k że i ta słu ży do w c z y ta n ia jednego z n a k u . Różnica jest ta, że w czytany zn ak je st rezu ltatem z w ra c a n y m p rzez funkcję i n t z; z = c i n .g e t () ;
Z au w aż , ż e funkcja zw raca typ i n t , a nie typ c h a r . To d lateg o , żeby funkcja m ogła zw ró cić jako rezu ltat EOF - jeśli ze stru m ien ia z o s ta ł w yjęty zn ak końca danych. O to p rz y k ła d p o d o b n y do p o p rzed n ieg o : char znak; while( (znak
=
cin.getO)
!=
EOF
)
{ cout
<< «
"Wcisnales " ", k o d : " <<
« znak (int)znak
«
endl;
) cout
«
"Pożegnalny
znak miał
kod
" «
(int)
znak
«
endl;
Jeśli m a m y w czy ty w ać d u ż o bajtów , to m ożna tego oczyw iście d o konać za pom ocą funkcji g e t um ieszczonej w pętli, jed n ak że lepiej zrobić to funkcjam i p rzezn aczo n y m i d o w czytyw ania w ielu znaków .
Funkcja i s t r e a m & g e t ( c h a r * gdzie, s t r e a m s i z e długość, c h a r ogranicznik^ ' \ n ' ) ; Tą w ersją funkcji g e t posługujem y się, g d y chodzi nam o w czy tan ie ze stru m ie nia nie jed n eg o bajtu, ale w iększej ilości. W yjaśnijm y sobie najpierw jej arg u m en ty : c h a r * - to adres tablicy, d o której polecam y stru m ien io w i w pisyw ać przyjm ow ane przez n ieg o znaki. s t r e a m s i z e - to liczba decydująca ile (m aksym alnie) zn ak ó w m o żn a wczytać. Z w y k le w tym m iejscu s ta w ia się liczbę o kreśla jącą rozm iar tablicy, w której u m ieszczan e są w yjm ow ane ze strum ienia znaki.
1088
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia U w aga: Jeśli w czy ty w an ie kolejnych zn ak ó w sk o ń czy się w wyni ku przekroczenia w arto ści tego arg u m en tu - jest to jak najbardziej p o p ra w n a sy tu acja^ . c h a r - ten trzeci a rg u m e n t to ogranicznik, k tó ry o k reśla kiedy p rzer w ać w czytyw anie. D o m n iem an ą w artością teg o arg u m en tu jest ' \ n '. Jeśli podczas w y jm o w an ia ze stru m ien ia n ap o tk an y zosta nie ten znak, to w y jm o w an ie zostanie p rz e rw a n e - n aw et jeśli nie osiąg n ęliśm y jeszcze ilości określonej p o p rz e d n im argum entem . O g ranicznik nie zostaje w yjęty ze strum ienia. P ozostaje tam. N iezależnie od tego, czy p rzerw an ie w czy ty w an ia nastąpi: ♦$* na sk u tek osiągnięcia m ak sy m aln ej liczby zn ak ó w , ♦♦♦ czy też p o napotkaniu og ran iczn ik a, - d o zapisanych w tablicy c h a r z n a k ó w , stru m ień d o p isz e jeszcze bajt zerow y kończący p o p ra w n ie C-string. To dlatego, że g d y posługujem y się tą funkcją, stru m ie ń za k ła d a , że czytać będziem y z n a k i składające się na jakiś tekst. P oniew aż za p isa ć je m a w tablicy c h a r - zatem u zn aje, że m a z tego w tej tablicy zrobić C -strin g . Jak pam iętam y, C -string u m ieszczo n y w tablicy m u si m ieć na końcu b ajt zero w y (znak nuli). W zw iązku z tym , jeśli arg u m e n t długość b ęd zie miał w a rto ść 7, to wyjętych ze strum ien ia b ę d z ie m aksym alnie 6 z n ak ó w , bo na sió d m e j pozycji w tablicy stru m ień d o p isze n am bajt zerow y. R ezultat z w racan y p rzez funkcję, to referencja d o stru m ien ia, z którym pracuje m y. P am iętam y, ż e to um ożliw ia k ask a d o w e łączenie tak ich funkcji . Jeśli oczekujem y funkcją g e t na z n a k , a o k aże się, że jest to zn ak końca pliku d any ch EOF - to w ów czas rezu ltatem zw racan y m p rz e z funkcję będzie: 0, zam iast referencji d o strum ienia, n a któ ry m pracow ała. Dla wtajemniczonych: wówczas to strumień przechodzi w stan błędu
eofbit O to ilustracja d ziałan ia tej funkcji: #include using namespace std; //*** * * * * * * * * * * * * * * * * * * * * * * ************************************ i n t m ain () { char imię[7], nazwisko[20], c; cout << "Podaj swoje imię: cin.get(imię, 7); 14) 15)
// o
W przypadku następnej omawianej funkcji getline - tak nie będzie! Dla wtajemniczonych: Umożliwia też łatwe sprawdzenie poprawności operacji za pomocą instrukcji: if [strumineń.eof ())...
1089
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia cout << "Podałeś << imię << "\nteraz nazwisko: cin.get(nazwisko, 20, 's' ); cout << "\nNazwisko brzmi — >" << nazwisko << endl; cout << "przyjęcie jeszcze jednego znaku: cin.get (c); cout << "\nKolejny wczytany znak — >" << c << endl;
// 0 // ©
u
O
u
©
Przykładowe odpowiedzi są poniżej zaznaczone tłustym drukiem Podaj swoje imię: K onstanty G ałczyński Podałeś — >Konsta teraz nazwisko: Nazwisko brzmi — >nty Galczyn przyjęcie jeszcze jednego znaku: Kolejny wczytany znak — >s
Komentarz O Tutaj p ro g ra m oczekuje tylko na (m ak sy m aln ie) 6 z n a k ó w alfanum erycznych. M im o to, w p isu je m y tutaj całość (im ię i nazw isko), co o czyw iście przekracza ż ą d a n ą liczbę. 0 C o p ra w d a , m y w szystkie zn ak i w y stu k aliśm y o d razu n a klaw iatu rze, jednak funkcja g e t w y jęła sobie z tego s tru m ie n ia tylko 6. N a d o w ó d tego w ypisujem y na ek ran z a w a rto ść tablicy imię. R eszta zn ak ó w jest n a d a l w stru m ien iu i ocze kuje na w yjęcie (stru m ień jest jakby zatam o w an y ). © N astęp n a fu n k cja g e t w yjm ie sobie z n o w u część z tych z n a k ó w . Z jej a rg u m e n tów w id zim y , ż e w yjm ie co najw yżej 19 znaków - chyba, ż e napotka o g ranicz nik: zn ak ' s '. W ted y zakończy w y jm o w an ie, a zn ak s (i ew en tu aln e dalsze) czekać b ę d ą n a „n astęp n y ch ch ętn y ch ". O N a d o w ó d teg o , co zostało w yjęte i w p isa n e do tablicy na zwis ko, w ypisujem y to na ek ran . © Reszta z n a k ó w n a d a l czeka w stru m ie n iu . Tutaj w id zim y w łaśn ie „n astęp n eg o chętn eg o ". Ta funkcja g e t bierze je d n ak tylko jeden z n a k - p ierw szym do w zięcia jest w ła śn ie znak, który sp o w o d o w ał z a trzy m an ie w yjm ow ania funkcją © czyli zn ak ' s ' . Skoro na tym k o ń czy się program , to p o zo stałe w strum ieniu zn ak i ' k ' ' i ' n ie zostają z niego w yjęte.
T ego p rz y k ła d u nie należy trak to w a ć jako prezentację w a d funkcji get. P oka zuje on raczej, jak m ożna sk o rzy stać z jej d o datkow ych m ożliw ości. O m ó w io n a funkcja get zabezpiecza n a s bow iem p rzed p rzep ełn ien iem tablicy, d o której k ie ru jem y w yjm ow ane z e stru m ien ia znaki. Z kolei m ożliw ość zdefi niow an ia o g ran ic zn ik a jest b ard zo p rzy d atn a. N ie m usi to być klaw isz E nter -
1090
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia m ożem y też kończyć w yjm ow anie z e stru m ien ia po n a p o tk a n iu k o d u klaw isza Escape i o d p o w ied n io na taką sytuację reagow ać. Jeśli w czytujem y funkcją g e t , to n a p o tk a n y o g ran iczn ik nie jestt w y jm o w an y ze strum ienia. N astęp n a o m a w ia n a fu nkq'a: g e t l i n e - różni się tym o d tej, że ogranicznik jes t w yjm ow any ze stru m ien ia i w y rzu can y . (Są jeszcze in n e różnice).
Funkcja istream & getline(char
* g d zie ,
streamsi ze ile , char
ogran=
’\n');
W sytuacji, g d y ch o d zi n am o to, by og ran iczn ik był ta k ż e w y jęty ze strum ienia posługujem y się funkcją g e t l i n e ^ . R ezultat z w ra c a n y p rzez funkcję to referencja do stru m ie n ia , z k tórym pracuje my. P am iętam y, że to um ożliw ia k ask a d o w e łączenie tak ich funkcji... (Wtajemniczeni: ...iproste sprawdzanie poprawności stanu strumienia) A rg u m en ty są d o k ła d n ie takie sam e, jak p rzy p o p rzed n iej funkcji g e t - czyli c h a r * - to ad res miejsca w pam ięci, g d zie n a le ż y lo k o w ać w yjm ow a ne z e stru m ien ia znaki. s t r e a m s i ze - to liczba decydująca ile (m ak sy m aln ie) zn ak ó w m o ż n a w czytać. Z w y k le w tym miejscu s ta w ia się liczbę okreś lającą ro zm iar tablicy, w której u m ieszczan e są w yjm ow ane ze stru m ien ia znaki. P o n iew aż C -string u m ie sz c z o n y w tablicy m usi m ieć na końcu bajt ze ro w y (znak nuli) - d la te g o jeśli ten arg u m en t b ę d z ie m iał w artość 7, to w yjętych ze s tru m ie n ia będzie m aksy m a ln ie 6 znaków , bo na siódm ej pozycji d o p is a n y zostanie bajt zero. U W A GA : Jeśli w czy ty w an ie zatrzy m a się z p o w o d u przekro czen ia w artości tego a rg u m e n tu - stru m ie ń sy g n alizu je błąd (tzw. f a i l b i t , o tym za kilka stron). c h a r - ten trzeci a rg u m e n t to ogranicznik, k tó ry o k reśla kiedy p rzer w ać w czytyw anie. D o m n ie m an a w artość te g o arg u m e n tu je s t' \ n ' . Jeśli p o d czas w y jm o w an ia ze stru m ien ia n a p o tk a n y zostanie ten z n a k , to w yjm ow anie z o sta n ie p rzerw an e - n a w e t jeśli nie osiąg n ęliśm y jeszcze ilości o k reślo n ej p o p rz e d n im arg u m en tem . N iezależn ie od teg o , czy p rz e rw a n ie w czy ty w an ia n astąp i: «$♦ po o siągnięciu m aksym alnej liczby zn ak ó w - (sy tu acja błędu), «$♦ czy też p o n ap o tk an iu o g ran iczn ik a, 16)
get linę - ang: weź linię [czytaj: „get łajn"]
1091
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
- do w czy tan y ch zn ak ó w d o p isy w a n y jest bajt zero w y (zn ak nuli) kończący p o p raw n ie C -string. W w y n ik u działania tej funkcji ew en tu a ln y o g ran ic zn ik p rzery w a w czy ty w an ie znaków , jest on jed n ak tak że w y jm o w an y ze stru m ien ia, ale nie d o łączan y d o w łaśnie w czy tan y ch znaków . Po p ro stu stru m ień w yjm uje go i w yrzuca. O to p rzy k ład : #include using
namespace
std
;
Z *************************************************************/
int
main()
{ char
k u f e r e k [10]
=
{
"xxxxxxxxx"
);
c o u t << " N a p i s z m a x 9 znaków: cin.getline(kuferek, sizeof(kuferek), c o u t << " O t o for(unsigned
zawartość int i = 0
<< <<
" k u f e r e k [" k u f e r e k [i]
<< <<
i « endl
"]= ;
) cout
<< <<
"Następnie wyjęty ( c h a r ) ( c i n . g e t ())
znak ;
:
"
Na ekranie zobaczymy poniższy tekst N a p i s z m a x 9 z n a k ó w : a bcsdefgh Oto zawartość elementów kuferka: kuferek[0]= k u f e r e k [ 1 ]= k u f e r e k (2]= kuferek[3]=
a b c
k u f e r e k [4 ]= kuferek(5]= kuferek[6]= kuferek[7]=
x x x
k u f e r e k [8]= x k u f e r e k [9 ] = Następnie wyjęty
znak:
d
)
;
elementów kuferka :\n" ; ; i < sizeof( k u f e r e k ) ; i
{ cout
's '
"
++)
1092
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia Już sam a n azw a funkcji sugeruje, do czego może się o n a p rzy d ać getline: "w eź linijkę te k stu ”J e s t to więc kolejny sposób na w czytanie s trin g u składającego sk; z kilku w y razó w . Zw róć u w ag ę, ż e w obu p rzy p ad k ach było ryzyko, że te k st nie zm ieści się vv tablicy. Funkcja g e t reaguje na to p rzerw an iem w kładania z n a k ó w do danej tablicy, a dalsze zn ak i spokojnie czekają w stru m ien iu na n astę p n e g o chętnego. Funkcja g e t l . n e także p rzery w a w k ład an ie znaków , ale w dodatku stru m ień p rzech o d zi w stan błędu. Po p ro stu funkcja ta nie b ie rz e "do dają". Jak sam a nazw a w sk a zu je - oczekuje "linii" tekstu, czyli czegoś, p o czym n astęp u je ogranicznik. D opiero, gdy ładując zn ak i d o tablicy n atk n ie się na taki ogranicz nik - uznm e, że d o b rze w ykonała sw oje zadanie, czyli w czy tała całą "linię”. O bie funkcje mają je d n ak tę w ad ę, że p ro g ra m ista pow inien p rze w idzieć, jak długi tekst m oże p rzy p ły n ąć stru m ien iem do tej tablicy.
A co zrobić, jeśli tego przewidzieć się nie da? O d d aw n a zn asz ju ż rozw iązanie tego problem u. P rzy p o m n ij sobie, rozdział o bibliotecznej klasie s t r i n g . T am , dla p racy z obiektam i k la sy s t r i n g , zd efi niow ano też funkcję o nazw ie g e t l i n e - taką, która p o tra fi w czytyw ać d o w o l nie d ługi tekst. (Str. 577). N ie jest to ż a d n a funkcja sk ład o w a stru m ien ia, ani k lasy s t r i n g . To po p ro stu zw ykła funkcja, n ap isan a p rzez tw órców klasy s t r i n g . Funkcję tę celow o nazw an o tak sam o, (getline), by jej nazw a od razu nam p rz y p o m in a ła do czego m oże służyć. P rzypom nijm y sobie deklarację tej funkcji:
istream & std::getline(istream w ej, string schowek, char o g ra n iczn ik = ’\n’); Jak w idać, z d ru g ieg o arg u m e n tu - funkcja ta z a ła d u je o trzy m y w an e od stru m ien ia zn ak i d o obiektu klasy s t r i n g - a p am iętam y , ż e taki obiekt potrafi się sam p o w ięk szać w m iarę n ap ły w u znaków . P ierw szym a rg u m e n te m jest stru m ie ń w ejściow y - bo s k o ro funkcja ta nie jest funkcją sk ład o w ą żad n ej klasy stru m ie ń , to m usi jakoś się d o w ied zieć, z którym stru m ien iem m a pracow ać. Trzeci arg u m en :... N o chyba nie m u sz ę zn o w u tłum aczyć. W artość re zu ltatu - to referencja stru m ien ia, na k tó ry m fu n k cja pracow ała.
Czym się różnią opisane tu trzy funkcje? N ajlepiej p o ró w n ajm y je zestaw iając w tabeli.
1093
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
P o ró w n a n ie trzeć h funkcji, za pom ocą których m o ż e m y w czyt) Avać w ielo w y razo w e teksty ---------------------------------- --------Skła Po przekroczeniu zadanej do liczby znaków: wa?
'.
.
.'
Gdy napotka ogranicznik:
istream::get(char*, streamsize, char);
Tak
zostawia dalsze znaki w strumieniu
zostawia go w strumieniu
istream::getline(char*, streamsize, char);
Tak
sygnalizuje błqd
wyjmuje i wyrzuca
std::getline(istream, string, char);
Nie
nie ma ograniczenia na liczbę znaków
wyjmuje i wyrzuca
-
Jak m yślisz, k tó ra z tych funkcji b ęd zie Twoją ulubioną? P od p o w iem Ci: p rzech o w y w an ie tek stó w w tablicach kłopot. D latego takim przebojem stała się klasa s t r i n
char,
to zaw sze p ew ien
g .
P roponuję w ięc, byś teraz w rócił na ch w ilę na stro n ę 577 i przeczytał §11.22 opisujący tę funkcję s t d : : g e t l i n e . Teraz, g d y jesteś ju ż znaw cą strum ieni, przy jd zie Ci to łatw o. D odatkow o ro zm aw ialiśm y tam o ty m , jak ' m ieszanie u ży w an ia o p e ra to ra » i funkcji g e t l i n e - m oże sp o w o d o w a ć kłopoty.
2.13.2
W czytyw anie binarne - funkcje r e a d i rea d so m e Kolejną funkcją sk ład o w a klasy istream
&
r e a d (char*
istream
g d z ie ,
jest funkcja
streamsize
ile);
Z deklaracji tej funkcji w idzim y, że jako rezu ltat zw raca o n a referencję do stru m ien ia, na któ ry m pracuje. Jak w iem y, cecha ta u m o ż liw ia kaskadow e łączenie w y w o łań tej funkcji. A rgum enty: •
c h a r * - miejsce w pam ięci, g dzie należy lokow ać w yjm ow a ne ze strum ienia bajty,
•
stream size
- liczba bajtów, które n ale ż y ze strum ienia
wyjąć. O to sposób zasto so w an ia. Z ałóżm y, ż e chcem y ze stru m ien ia płynącego z kla w iatu ry w yjąć d o k ład n ie 6 bajtów i um ieścić je w tablicy d a n e . Realizujemy to tak: char
d a n e [10];
cin.r e a d ( d a n e ,
6);
Funkcja ta p racu je rzeczyw iście na bajtach - nie interesuje ją, co znaczą te bajty. W szczególności - funkcji tej nie interesuje czy w czy tu jem y d an e binarne czy
1094
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia teksty. N iezależn ie od tego czy z e strum ienia w y jm o w an e będą znaki a lfa n u m eryczne, czy też znaki nowej linii, czy końca pliku - b ę d ą one u m ieszczan e w e w sk azan y m miejscu pam ięci. Tą funkcją w czytujem y n a p rzykład z pliku d y s kow ego d a n e binarne. Jeśli jej u ży jem y w stosunku d o strum ienia w ejściow ego c i n , to p am iętać należy, iż d o w y stu k iw an eg o na k law iatu rze C -strin g u nie zostanie na końcu d o p isan y auto m aty czn ie zn ak nuli. G dyby n am o to chodziło - p o w in n iśm y po prostu u ż y ć funkcji g e t ( c h a r * , s tre a m s iz e ). P raw d ziw ą d o m en ą zasto so w an ia tej funkcji są w ięc b in arn e operacje wejścia/w y jścia. T akie operacje rz a d k o p rzep ro w ad za się z k law iatu rą. N ajczęściej pracuje się w ten sposób z p lik am i dyskow ym i. F u n k cja r e a d p ozw ala n am w czytać z d y sk u parę tysięcy bajtó w będących n p . ry su n k iem lub d an y m i pochodzących z ek sp ery m en tu . T akie dan e m ogą za w ie ra ć n aw et takie bajty, które a k u ra t o d p o w iad ają zn ak o w i nuli (czyli bajty o w artości 0). N ie m a to w pływ u na p rzeb ieg przesłania. W czasie p rzesy łan ia bajtów stru m ien iem m oże n a s tą p ić błąd. N a p rzy k ła d zażąd aliśm y w czytania 1000 bajtów , a w pliku jest ich ty lk o 800. Tego, ile b ajtów zostało rzeczyw iście w czytanych, m ożem y się d o w ie d z ie ć w yw ołując funkcję g c o u n t . (P orozm aw iam y o niej za chw ilę, na str. 1096). O to p rzy k ład : char
n a p i s [10];
cout<< "Wczytam cin.read(napis,
tylko 4);
4
bajty,
napisz
cos:
W rezu ltacie w tablicy zn ajd ą się 4 bajty, k tó re p rz y p ły n ę ły stru m ien iem z klaw iatu ry . Do nich nie zo stan ie d o łączo n y bajt z e ro w y (znak nuli). O pracy tej funkcji z plikami porozmawiamy dalej.
Jak za pomocą tej funkcji wczytać informację do obiektu innego typu niż tablica c h a r [ ] ...(na p rz y k ła d d o obiektu jakiejś klasy) - p o ro z m a w ia m y p rz y o m aw ian iu siostrzanej funkcji w r i t e (str. 1100).
c h a r * - m iejsce w pam ięci, g d zie n a le ż y lokow ać w y jm o w a n e ze stru m ien ia bajty, s t r e a m s i z e - m a k sy m a ln a liczba b ajtó w , k tó re n ależy z e stru m ien ia wyjąć.
1095
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
Funkcja ta ty m różni się od p o p rzed n iej, że jeśli na p rz y k ła d zażąd aliśm y w czy tan ia 1000 bajtów , a w pliku jest ich tylko 800 - to z o sta n ie w czy tan y ch ty lk o ty le ile je s t - bez sy g n alizacji b łę d u . O tym , ile b ajtó w zostało rzeczyw iście w czytanych, in fo rm u je nas w artość rezu ltatu tej funkcji. Pam iętaj, że jeśli w buforze w d anej chw ili nie czekają n a w yjęcie ż a d n e zn ak i, funkcja nic nie w czyta i zw róci n a m w artość zero.
2.13.3
Funkcja ignore M ożna p o w ie d z ie ć obrazow o, że jest to taka funkcja d o w y jm o w an ia ze stru m ienia, k tó ra to, co przeczyta, od ra z u w yrzuca n a śm ietn ik . C zasem m oże się to p rzy d ać d o p rzeskoczenia jakichś niechcianych bajtów , k tó re m usim y ze stru m ienia u su n ą ć , by móc p o tem czytać następne. O to jej deklaracja: istream &
ignore (streamsize ile =
1, int ogranicznik= EOF);
S tarym zw y czajem rezu ltat zw racan y przez funkcję to referencja d o stru m ien ia w ejściow ego, n a którym funkcja pracuje. • Pierw szy a rg u m e n t s t r e a m s i z e - to liczba bajtów , k tóre należy zignorow ać. Z ig n o ro w an a b ęd zie taka liczba bajtów chyba, że w cześniej skończy się plik. (D o m n iem an a w artość tego arg u m en tu = 1 bajt). •
D rugi a rg u m e n t i n t jest to ogran iczn ik . Jeśli w trakcie w czy tyw ania i ig n o ro w an ia żądanej liczby bajtów n a p o tk an y zostanie bajt o d p o w iad ający tem u o g ran iczn ik o w i, w ów czas akcja w yjm ow ania ze strum ienia zo sta n ie przerw an a.
Z au w aż, że ten ogranicznik jest ty p u i n t - a nie, jak p o p rz e d n io , ty p u c h a r . To po to, b y śm y jako ogranicznik m ogli użyć zn ak E O F (takie jest zresztą dom niem anie). Jeśli ogranicznikiem jest EOF, to ign o ro w an ie nie b ęd zie p rz e rw a n e z p o w o d u żad n eg o ogranicznika. To dlatego, że w pliku w c z y ty w a n y m są bajty, a żad e n z nich nie ró w n a się dw ubajtow em u EOF. Jeśli np. w T w oim k o m p u terze EOF jest k o d o w an e jako: -1 , to jego w artość h eksadecym alna w y n o si 0 x f f f f , p o d czas g d y w y jm o w an e ze strum ienia bajty m ogą być co najw yżej z zakresu liczb 0 x 0 0 x ff I U życie EOF jako ogranicznika oznacza, że nie życzym y sobie I żad n e g o ogranicznika. Jeśli ig n o ro w an ie zostanie p rzerw an e z pow odu n a p o tk an ia znaku końca pliku, u staw ian a jest tak zw ana flaga b łędu stru m n ien ia e o f b i t . (P orozm aw iam y o tym na str. 1111)
Przykład: # in c lu d e < io stre a m > u s in g nam espace s td ; Z******************************************************* ******/
1096
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia int
m a i n ()
{ char char
k u f e r e k [10]; s k r y t k a [10] ;
cout
<<
"Napisz
około
10
znaków:
cin.get(kuferek, 4 ) . i g n o r e (2).get(skrytka, cout « "\nW kuferku j e s t : " « k u f e r e k
□
<<
",
<<
"\na
w
skrytce dwa
jest:"
znaki
«
10);
skrytka
zignorowałem
"
<<
endl;
Oto przykładowy wygląd ekranu: Napisz W a
22.13.4
około
10
z n a k ó w : abcdefghijkl
kuferku jest:abc, w skrytce dwa znaki zignorowałem
jest:fghijkl
Pożyteczne funkcje pom ocnicze
Funkcja
gcount
O tym , ile k onkretnie zn ak ó w zostało w yjętych z e stru m ien ia za pom ocą ostatniej funkcji w czytyw ania nieformatowanego, in fo rm u je n as funkcja streamsize
gcount();
Z w racam u w a g ę , że o p erato r >> d okonuje w c z y ty w a n ie formatowane, a o takich o peracjach g c o u n t nas n ie poinform uje. P rzykład: #include u s i n g n a m e s p a c e std; ^ ł ł * * * ł * * * * * * * * * ł * * ł t * * ł l H H ł ł ł ł 4 t t t t ł ł ł ł ł ł ł ł ł ł ł 4 ł ł ł ł ł ł ) l t ł ( r ł ł ł t
int
main()
{ char
tablica[200] ;
cout
<<
"Napisz
jakieś
cin.get(tablica, cout
<<
<< << <<
□
"Ze
zdanie:
sizeof(tablica),
strumienia
( c i n . g c o u n t ()
wyjete
" znaków, \noto zdanie:" tablica << endl;
jakieś
zdanie:
Ze s t r u m i e n i a wyjete o t o z d a n i e :a b c
abcxdef zostało:3
);
zostało:"
)
Oto ekran: Napisz
'x'
znaków,
// O
1097
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
Komentarz: O Z ag ad k a: g d y b y śm y tu zam iast funkcji g e t , zasto so w ali funkcję g e t l i n e , to m im o p o d a n ia tego sam ego tekstu funkcja g c o u n t o d p o w ie d z ia ła b y liczbą 4 (zam iast 3). D laczego? O d p o w ied ź: funkcja g c o u n t m ów i ile zn ak ó w zo stało w yjętych ze stru m ien ia. Jak p am ię ta m y , w przeciw ieństw ie d o funkcji g e t , fu n k cja g e t l i n e po n a p o t kaniu o g ra n ic z n ik a w yjm uje go z e stru m ien ia (i w y rzu ca). Liczba 4, z am iast 3 jest w łaśn ie d o w o d e m na to.
W n aszy m p rz y k ła d z ie ko rzy staliśm y ze stru m ien ia p ły n ąceg o z klaw iatu ry . Funkcja g c o u n t tak n ap raw d ę p rz y d a je się szczególnie p rz y binarnym o d czy ty w an iu pliku funkcją r e a d .
Funkcja
peek
Inną p o ży teczn ą funkcją pom ocniczą jest in t p eek ( ) ; Jest to funkcja, która p ozw ala n a m zajrzeć d o stru m ien ia i zobaczyć, co też tam czeka n a w yjęcie najbliższą instrukcją w yjm ującą. P o d g lą d n ą ć m ożna tylko jeden bajt. M im o że poznajem y g o - ów bajt nie jest ze stru m ien ia w czytyw any. W artość tego "podglądanego" bajtu jest w łaśnie w arto ścią (rezultatem ) tej funkcji. R ezu ltat jest typu i n t d lateg o , by funkcja m ogła zw rócić EOF w p rz y p a d k u , g d y dotrzem y d o k o ń ca pliku. Funkcji tej u ż y w a m y zw ykle, aby o d p o w ied n io z are ag o w a ć na coś, co czeka jeszcze w stru m ien iu . W zależności od tego, co ta m zobaczyliśm y m ożem y uruchom ić o d p o w ied n ią akcję w czytyw ania. # in c lu d e < io s tre a m > u s in g n am esp ace s t d ; // A # in c lu d e < c ty p e > '' “ //****»***********************»***************■****************■* i n t m a in () { c h a r n a z w a [2 0 0 ]; d o u b le x; i n t z w ia s tu n ; c o u t << " n a p is z l i c z b ę lu b nazw ę: " ; // z w ia s tu n = c i n .p e e k O ;
O
i f (is d ig it( z w ia s tu n ) ) { c i n >> x ; c o u t « "B yła to l i c z b a : } e ls e {
// // " << x «
e n d l;
o
1098
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia cin
>>
cout
<<
nazwa; "Była
// © to
nazwa:
"
<<
nazwa
<<
endl;
Oto dwa warianty ekranu: Tak b ęd zie on w yglądał w p rzy p ad k u n ap isan ia n a z w y Napisz liczbę lub nazwę: B y ł a to nazwa: B e r l i n
Berlin
A tak - po napisaniu liczby Napisz liczbę lub nazwę: B y ł a to liczba: 1 2 . 7 3
12.73
Komentarz O Tutaj p o d g lą d am y , co je st p ierw szy m oczekującym n a wyjęcie ze stru m ien ia zn ak iem . © P o słu g u jem y się funkcją b ib lio teczn ą i s d i g i t („is d ig it?" - zn a c z y po an g ielsk u : „czy to jest cyfra?"). A by sk o rzy stać z tej funkcji konieczne jest w łączen ie pliku nagłów kow ego © . F u n k q a ta o d p o w ia d a nam czyjej a rg u m e n t jest cyfrą, czy nie. W ied ząc już, czy m am y w y jąć liczbę czy n azw ę, sto su jem y o d p o w ied n ią o p e ra cję w czy ty w an ia form atow anego: © - F orm atow ane w yjęcie ze stru m ien ia liczby © - F o rm ato w an e w yjęcie ze stru m ien ia s trin g u Z a u w a ż , że takie p o d g lą d an ie by ło tutaj konieczne. N ie m ożna p o prostu w czy tać pierw szeg o z n ak u , b o „zjed lib y śm y " kaw ałek zaczynającej się w łaśn ie liczby lu b nazw y.
Funkcja
putback
N aw iązu jąc d o p rzed n ieg o p rz y k ła d u : jeśli m im o w sz y stk o zam iast tylko p o d g lą d n ą ć - w yjęlibyśm y z n a k z e stru m ien ia, to m am y o s ta tn ią szan sę g o zw rócić. S łuży d o tego funkcja p u t b a c k (ang. put back- w łóż z p o w ro tem ). istreara
&
p u t b a c k ( c h a r );
P om y ślałeś pew nie: „-Co to oznacza? Klaioiatura nie może przecież odszczekać tego, co na niej przed chwilą napisaliśmy!" R zeczyw iście. Z n ak z w ró co n y w raca tylko d o sam eg o ujścia stru m ie n ia i tam czeka, a ż g o ktoś nie w czyta.
To tak, jakbyśmy pracując w biurze krzyknęli „następny!" i do pokoju wszedł oczekujący w kolejce przed drzwiami interesant. Możemy mu jednak powiedzieć: „—Przepraszam pana, ale widzę, że nie jestem jeszcze
1099
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
gotowy pana przyjąć, proszę jeszcze zaczekać przed drzwiami." Interesant wychodzi i staje przy samych drzwiach, jako pierwszy do obsłużenia. Funkcja p u t b a c k daje szansę o d d a n ia tylko jednego z n a k u . G d y b y śm y chcieli zrobić o szu stw o : w czytać znak, p o d m ien ić go, a n a stę p n ie tę p o d m ien io n ą w artość o d d a ć - w ów czas efekt jest niezdefiniow any. Oto p rz y k ła d o w e użycie: c h a r c, z; cin » c; cout « "Przeczytany
c = ”«
c
«
cin » cout «
Funkcja
z; "Przeczytany
z="<<
z
«
istream
&
endl;
// wstawienie z powrotem // wyjęcie po raz drugi
c i n .p u t b a c k ( c ) ;
endl;
ungetO;
Funkcja ta ró ż n i się od po p rzed n iej ty lk o tym , że nie m a a rg u m e n tu , czyli tutaj liczym y na to, ż e stru m ień w ie, jaki zn ak przed chw ilą z n ieg o w yjęliśm y, jest w ięc jakby bezpieczniejsza, bo zap o b ieg a oszustw u. Z d ru g iej stro n y m ożna sobie w y o b razić sytuację, że funkcję tę u ży w am y n aw et w odległej części p ro g ram u , g dzie ta inform acja o p o p rz e d n io w yjętym zn ak u jest n ie zn an a.
>2.13.5
Funkcje wstawiające do strumienia P o zn am y te ra z d w ie funkcje sk ład o w e klasy o s t r e a m (stru m ień w yjściow y) o d p o w ie d z ia ln e za niefo rm ato w an e w y p isy w an ie inform acji. Funkcji tych m ożn a u ż y w a ć p rzy w pisyw anie (n p . d o pliku) inform acji binarnej.
Funkcja
ostream
&
put(char)
Służy d o w staw ien ia d o stru m ien ia jednego zn aku. P rzy k ład o w o następujące instrukcje: char int
n a p i s [) i
do
=
=
"Listopad";
0 ;
{ cout
.put( n a p i s [i]
)
.put
}while(napis[++i]);
sp o w o d u ją, ż e na ekranie pojawi się L-i-s-t-o-p-a-d-
jeśli w s ta w ia m y do strum ienia, k tó ry płynie do pliku d y sk o w eg o , to czasem operacja ta m o ż e się nie u d ać - bo n a przykład dy sk się całkow icie zapełnił. W takich sy tu acjach funkcja, jako r e z u lta t—zam iast zw racać referencję do stru m ie nia, n a k tó ry m pracuje - zw raca zero.
1100
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji w yjm ujących ze strum ienia
Funkcja
write
Funkcja w r i t e jest także funkcją składow ą klasy o s t r e a m . S łuży ona d o w staw ien ia do stru m ien ia żądanej liczby bajtów . Inaczej m ów iąc funkcja ta słu ż y n am d o w ypisania czegoś na ekranie, albo d o zapisania w plik u dysko w y m . O to deklaracja tej funkcji: ostream
&
write (const
char
*skąd_pisać,
streamsize
ile jb a jłó w ) ;
R ezu ltatem zw racan y m p rzez funkcję jest referencja strum ienia, na któ ry m ona pracuje. ❖
Pierw szy a rg u m e n t to w skaźnik d o tablicy c h a r , z której m ają być p o bierane bajty w celu w staw iania ich d o stru m ien ia. P rzy w sk aźn ik u w id zim y słow o c o n s t , które oznacza zap e w n ien ie, iż funkcja w r i t e zobow iązuje się niczego w tej tablicy nie zm ieniać. Tylko sobie odczyta bajty, które m a w ypisać.
♦♦♦ D ru g i arg u m en t to liczba s t r e a m s i z e - określająca ile bajtów (począw szy od m iejsca pokazyw anego w sk aźn ik iem ) n ależy do stru m ien ia w staw ić. P rzy k ład o w o instrukcje: char napis i n t i = 0;
=
"Rembrandt";
cout.write(napis+3,
4) ;
sp o w o d u ją w y p isan ie na ek ran ie tekstu: bran
Jest to, jak w idać, sp ry tn y sp o só b na w y p isy w an ie frag m en tu C -strin g u , ale p raw d ziw e zasto so w an ie tej funkcji, to w p isy w a n ie d o pliku d y skow ego w ielu tysięcy bajtów rep rezen tu jący ch jakieś d a n e bin arn e - np. d a n e z uk ład u p o m iaro w e g o , czy d a n e z u rz ą d z e n ia skanującego ry su n ek . O p racy z p lik am i trak to w ać b ęd zie n astęp n y p a rg ra f. wr i t e ,
a obiekty innego typu Jak zobaczyliśm y, o p isan a tu funkcja w r i t e p o tra fi zap isać d o p lik u b in arn ą z aw arto ść frag m en tu pam ięci (n p . tablicy) p o k a z y w a n e j w sk a źn ik ie m c h a r * . N ieza leżn ie od tego, co tam n a p ra w d ę jest. P o b ieran e b ęd ą bajty p o czą w szy od m iejsca p o k azy w an eg o w sk aźn ik iem ; ten bajt i kolejne. Ile - to o k reśla d ru g i a r g u m e n t w y w o łan ia funkcji w r i t e . P om yślałeś p ew n ie teraz: —No, ale przecież w komputerze informacje zapisane nie tylko za tablicach c h a r. Są także w innych obiektach, np. d o u b l e , 1 ong, m o j a ^ k l a s a . Co wtedy? Czy są odpowiednie przeładowane funkcje w r i t e na taką okoliczność?
1101
Rozdział. 22. O peracje W ejścia/W yjścia Omówienie funkcji wyjm ujących ze strum ienia
N ie. N ie m a. P o prostu n ie trzeba. P rogram iści, jeszcze o d czasó w k lasycznego C, p o słu g u ją się w tym m iejscu rz u to w a n ie m , k tó re ro z w ią z u je problem . Jeśli w ięc m a sz g d zieś w p ro g ram ie o b iek t ty p u d o u b l e , a chciałbyś jego tresc (binarną) za p isa ć d o p lik u , w y starczy tak a instrukcja double
pi
=3.14;
,
.
II zakładani, że praca strum ienia s tr u m ie ń . w r i t e ( ( c h a r * )
fipii
do dysku poprawnie rozpoczęta
s i z e o f (pi))
jak w id zisz, p ie rw szy m a rg u m e n te m jest ad res o b iek tu , k tó reg o treść chcieli byśm y w y p isać. P o cząw szy od bajtu o tym ad resie funkcja zacznie pobierać kolejne bajty w celu zap isa n ia ich na d y sk u . P o b ierze ich o a nie s i z e o f ( p i ) . U m nie to jest 8 bajtów . Z tylu bajtów sk ła d a się na nasz obiekt K łopot je d n a k w tym , że w y rażen ie & p i, p o staw io n e jako p ierw szy arg u m e n t w y w o łan ia tej funkcji - jest ty p u d o u b l e * . T ym czasem funkcja oczekuje ty p u ch a r* . N ie m a p ro b le m u . D o konujem y rzu to w an ia: (c h a r* )
&pi
r e i n t e r p r e t _ c a s t < c h a r * > (& P i )
<<—
s ta r y s ty l języka C n o w y s t y l C++
i w re z u lta c ie w y rażen ie (adres) ty p u d o u b le * zam ien ia się na w y rażen ie (adres) ty p u c h a r * . To d o p ie ro w y sy łan e jest do funkcji w r i t e .
Czy jest to wielkie oszustwo? Byłoby, g d y b y funkcja w r i t e b rała ten adres po to, by jakoś zin terp reto w ać znajdu jącą się tam inform ację. T y m czasem funkcja w r i t e w cale me m a o d czy tyw ać zap isa n e j tam inform acji, m a jed y n ie sk o p io w ać te cegiełki (bajty), które składają się n a ten obiekt i zap isać je w pliku. Z atem funkcji w r i t e w cale m e m u sim y m ó w ić, czy to n a p ra w d ę jest obiekt ty p u d o u b l e , czy m oże obiekt jakiejś n aszej klasy. M ów im y jej tylko: P o cząw szy od ad resu (pokazuję ci go w sk a źn ik ie m typu c h a r * ) , sk o p iu j 8 bajtów d o pliku.
I
Jeśli ch cielib y śm y w ięc w y p isać b in arn ie funkcją w r i t e treść jakiegoś obiektu naszej k la sy , to w y w o łan ie funkcji w r i t e zap isu jem y tak. nasza_klasa
zielony;
strum ień . w r i t e ( ( c h a r * )
&zielony,
sizeof(zielony));
To w sz y stk o . C zy z a u w a ż y łe ś , że nie u ży łem tutaj rzu to w an ia w n o w szy m stylu? To jest jedyna c h y b a sytuacja, g d y pro g ram iści w olą ten stary , k ró tszy styl. Po prostu w tej funkcji w r i t e , takie "stare" rzu to w an ie nie jest ry zy k o w n e.
1102
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików
2 2 .1 4 S tru m ie n ie p ły n ą c e d o lub od p lik ó w Jeśli chcem y zapisyw ać coś d o plików (np. d y sk o w y ch ) lu b czytać z nich, to m am y d o dyspozycji klasy, k tóre nam takie o p eracje zapew niają: o f s t r e a m - (o u tp u t file stream ) - z a p is d o plików , ifstream
- (in p u t file stream )
- o d czy ty w a n ie z plików ,
- (file stream ) - oba p ow yższe. A by m óc posłużyć się tym i klasam i należy do p ro g ram u w łączyć plik nagłów kow y teg o fragm entu biblioteki fstream
#include
Nazwy deklarowane w tym pliku są umieszczane w przestrzeni nazw stel, dlatego jeśli zastosujesz dyrektywy u s i n g n a m e s p a c e ste l, to nie będziesz musiał dodawać tego kwalifikatora zakresu s t e l : : . Innymi słowy, zamiast odnosić się do nich np. s t d : : o f s t r e a m możesz pisać także krócej, po prostu o f s tr e a m . K lasa of s t r e a m jest p o ch o d n ą klasy ostream, K lasa ifstream jest p o ch o d n ą klasy istream. K lasa f stream jest p o ch o d n ą od klasy iostream. Spójrz te ra z jeszcze raz na ry su n ek na str. 1042, g d zie p o ra z p ie rw szy ro zm a w ialiśm y o hierarchii klas stru m ien i. Ten ry su n ek p o m o że C i łatw o zapam iętać te trzy o statn ie stw ierd zen ia. W z a sa d z ie o tych faktach nie m u sim y p am iętać - w ażn y je st w n io sek w y n ik a jący z fenom enu d zied ziczen ia: Skoro k la sy te są p o ch o d n y m i klas istream o ra z ostream, to znaczy, że d z ied zic zą w szystkie cechy i zach o w an ia sw ych klas p o d staw o w y ch . O znacza to w prak ty ce, że w szy stk o , co p o w ied zieliśm y d o tej p o ry o stru m ien iach iostream, obow iązuje ta k ż e i tutaj. K onkretnie: m o ż em y tak że u ży w ać m an ip u lato ró w , o p erato ró w » <<, funkcji get, put, read, w r i t e itd. P o d sta w o w a różnica w p racy z tym i stru m ien iam i po leg a na ty m , że tutaj już stru m ie n ie nie są sta n d a rd o w o z d e fin io w a n e - czy li z d e fin io w a n e za n as p rzez a u to ró w biblioteki. M usim y sam i sobie te stru m ien ie zd e fin io w a ć - to oczy w is te, bo p rzecież m usim y w y ra ź n ie określić do (od) jak ieg o pliku d y sk o w eg o stru m ie ń m a płynąć. Z atem , a b y czytać z pliku (lu b pisać do ń ) należy: 1) Z definiow ać stru m ie ń czyli w y k reo w ać o b ie k t k lasy ifstream, ofstream, lub fstream.
2) O kreślić stru m ien io w i, z jakim k o n k retn ie p lik iem m a się k o m u n ik o w a ć i o tw orzyć ten plik. 3) P rz e p ro w a d z a ć ż ą d a n e operacje w e /w y . 4) Z lik w id o w ać stru m ie ń , g d y u zn am y , ż e p raca z p lik iem jest z a k o ń czona.
1103
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików
Jakie to p ro ste zo b aczm y na p rz y k ła d zie - poniższy krótki p ro g ra m otw iera na d y sk u plik o n a z w ie "księżyc.tm p" i do teg o pliku w p isy w a n e je st słow o mi s j a, p o czym p lik ten jest zam ykany. #include #include using namespace std;
y*****ł***************************** int main() ofstream ośrodek;
// etap O
ośrodek.open("księżyc.tmp"); ośrodek << "misja"; ośrodek.close ();
// etap © // etap © // etap O
W tekście programu zaznaczone są wspomniane wcześniej etapy pracy ze strumieniem: O Definicja e g z e m p la rz a obiektu klasy o f s t r e a m . O biekt ten n azy w a się u nas „ośrodek" (niby o d : ośrodek kontroli lotów ). © p o in fo rm o w an ie o śro d k a czym m a się zajm ow ać - czyli d o jakiego pliku ma płyn ąć stru m ień . Jest to po p ro stu w y w o łan ie funkcji o p e n będącej funkcją sk ład o w ą k lasy ofstream.
© Dowolna liczba operacji wstawiania do strumienia o ś r o d e k (czyli pisania do pliku). © Z am k n ięcie stru m ie n ia . P oniew aż stru m ień jest b u fo ro w an y , w ięc następuje tu tak że e w e n tu a ln e w y p ró żn ien ie b u fo ra i zapisanie do pliku oczekujących tam jeszcze d an y ch . E tapy O i 0 z w y k le łączy się w jeden instrukcją: ofstream ośrodek("księżyc.tmp");
Jest to definicja o b iek tu połączona z w y w o łan iem k o n stru k to ra . K onstru ktor ten zajm uje się o d ra z u otw arciem p o d a n e g o pliku. Oto, jak w ted y w ygląda o d n o śn y frag m e n t: ofstream ośrodek ("księżyc.tmp"); ośrodek « "misja"; ośrodek.close();
U '' '•
etap Q i © e taP St etap
1104
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików
22.14.1
Otwieranie i zam ykanie strumienia O tw arcia strum ienia m ożem y d o k o n ać albo funkcją składow ą o p e n ^ alb o za pomocą k o nstruktora.
I
W o b u p rzy p ad k ach arg u m en ty są takie sam e, zatem p rzy jrzy jm y się funkcji składowej o p en . F unkcja o p e n
void
open (char* nazwa, ios_base ::openmode tryb =***);
❖ A r g u m e n t p i e r w s z y : n a z w a pl iku
Pierwszy argument, to oczywiście nazwa pliku podana w postaci C-stringu. Jeśli tu postaw im y np. C -string " l i s t . t x t ", to znaczy, że ch o d zi o tak nazw any p lik w bieżącym k atalo g u . G dyby nam chodziło o otw arcie plik u w innym k atalo g u , w ów czas n a z w ę pliku trzeba p o p rzed z ić ścieżką. N p. "c:list.txt" "c:/korespondencja/lipiec/list.txt"
U w ag a: są sy stem y (np. MS-DOS, M S-W indow s), w których katalogi w ścieżce o d d z ie la się nie u k o śn ik iem (slesz, ' / ' ) ale o d w ro tn y m uk o śn ik iem ('V - bekslesz). P o n iew aż jed n ak o d w ro tn y u k o śn ik w C -stringu w p ro w a d z a zn ak specjalny, w ięc p ra w d z iw y o d w ro tn y ukośnik w tak im stringu n o tu je się jako ' W . (Zob. str. 64). Zatem w tak ich system ach p isząc ścieżkę trzeb a o d w ro tn y ukośnik staw iać d w u k ro tn ie . P ow yższa ścieżka w y g lą d a w te d y tak: "c:W k o r e s p o n d e n c ja W lip ie c W lis t. txt"
m
3
Jeśli polubiłeś obiekty klasy string, to nazw ę p lik u m o żesz przy g o to w ać w łaśn ie w takim obiekcie. C o p raw d a, funkcja o pen oczekuje jednak w skaźnika d o C -strin g u , ale to nic, przecież w k lasie s t r i n g m asz w y g o d n ą funkcję c_str, która po zw ala C i p rzy g o to w an ą w s t r i n g u n a z w ę w ysłać funkcji o p e n jako C-string. N a p rzy k ład : jeśli n a z w ę pliku p rzy g o to w ałeś w obiekcie klasy str i n g o n a z w ie konfig, s t r i n g konfig ("7.fistaw_duzy.c f g " ) ; to w w y w o łan iu funkcji o p e n m ożesz u ż y ć tej n a z w y w ta k i sp o só b (to p seu d o kod): s tr u tn ie i i.open (konfig.
17)
[czytaj: „ołpen"]
c_str (),
t r y b );
1105
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików ❖
A r g u m e n t drugi: try b p racy
Drugi argument funkcji o pen o k reśla tryb pracy z d a n y m plikiem . Do d y sp o z y cji m a m y kilka try b ó w , ich n azw y z e b ra n e są o n e w k la sie ios base. Jak w id a ć z d ek laracji, ten d ru g i arg u m e n t m a jakąś w a rto ść d o m n iem an ą. G w iazd k i, ja k o arg u m e n t d o m n iem an y , to o czyw iście niem ożliw e. To o szu stw o m u sia łem zrobić, d la teg o że do m n iem an ie z a le ż y o d tego, czy m ó w i m y o funkcji o p e n z klasy if stream, z klasy of stream, czy z klasy fst ream. D o m n iem an ia te są następujące:
w klasie i f stream w klasie o f s tr e a m w klasie f s tr e a m
io s _ b a s e : :in io s _ b a s e : :o u t nie ma domniemania
Określeń try b ó w m oże być kilka, m o g ą być z a sto so w an e pojedynczo lub po kilka ró w n o cześn ie. O to m o żliw e o k reślen ia trybów o tw arcia: in
- (input) O tw ó rz p lik d o czytania
out
- (output) O tw ó rz plik do pisania
a te
—(at end) O tw ó rz i u staw się na końcu zaw artości
app
- (append) O tw ó rz d o d o pisyw ania n a końcu pliku
t r u n c - (truncate) O tw ó rz, a jeśli plik istnieje - skasuj starą treść bi na r y - (binary) T ryb m a być bin arn y (d o m n iem an y jest tekstow y)
O k reślen ia te zd efin io w an e są ja k o jakiś publiczny ty p całkow ity w klasie ios base.
Nie zrobimy dużego błędu wyobrażając sobie typ o p en m o d e jako publicz ny typ wyliczeniowy enum. Klasa ios b a s e jest klasą p o d sta w o w ą także i dla k la s odpow iadających za operacje na p lik ach . Poniew aż try b y otw arcia zw y k le o k reślam y b ęd ąc poza zak re sem k la sy stru m ień - d la teg o też nazw y tych try b ó w p o p rzed z an e są w ted y k w alifik ato re m zakresu ios_base: : T utaj, podobnie jak w przypadku nazw flag stanu formatowania, wygodniej jest w kwalifikatorze zakresu użyć krótszej nazwy jednej z klas pochodnych od io s _ b a s e - klasy io s . Zatem zamiast pisać: i o s _ b a s e :: o u t , możemy równie dobrze napisać krócej: i o s : :out.
1106
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików
Przyjrzyjmy się dokładniej tym trybom18 P rzew ażn ie nie są one w ykluczające się. Określają k o n k retn e cechy, jakie ma m ieć nasz stru m ień .
ios::in Tryb ten inform uje strum ień, że chcem y otw orzyć plik po to, b y z niego czytać M ożliw e jest to tylko w ted y , g d y stru m ień d o p u szcza o p eracje czytania (wyj m ow ania ze stru m ie n ia )- c z y i ijest to stru m ień klasy i f s t r e a m lub fstream. Tak otw ieran y plik m usi w cześniej istnieć.
ios::out Inform uje stru m ień , że chcem y o tw o rzy ć plik po to, by d o n ieg o zapisyw ać.
*** Jeśli plik n ie istnieje, zostaje stw orzony. Jeśli plik istnieje, stara treść jest tracona. P ^ 5
Zapam iętaj: O tw arcie pliku w trybie i o s : :out bez d o d a tk o w o trybu i o s : : a p p albo i o s : : a t e zak ład a także m ilcząco tryb ios : :t r u n c - czyli stara treść pliku w łaśn ie o tw arte g o zostaje od rzu co n a , zaczynam y n a p u s ty m pliku. Tryb ten m ożna zasto so w ać do d a n e g o stru m ien ia tylko w te d y , g d y strum ień d o p u szcza operacje pisania (w staw ian ia d o strum ienia) - czy li jest to strum ień klasy ofstream, fstream. O ba p o w y ższe try b y ios : :in o raz ios : :out m o żn a zasto so w ać rów nocześ nie jeśli stru m ień je st klasy f s t r e a m - o zn acza to, że z pliku b ęd ziem y czytali i do niego zap isy w ali. Np. f s tr e a m s t r u m y k C n o t a t n i k . t x t " ,
io s ::in
I io s ::o u t);
Jak w idać, takie połączenie dw óch try b ó w w y rażam y za p o m o cą operatora bitow ej su m y ' 1' O R. (żarg: „OR-owanie").
ios::ate (ang.: at end - na koniec). O kreślenie to oznacza, że ch cem y , b y p o otw arciu pliku specjalny w sk a źn ik określający, w k tó ry m m iejscu p lik u w łaśn ie p racu je m y - u s ta w ił się w stę p n ie na końcu p lik u . N ie oznacza to w cale , że koniecznie m u sim y coś tam zap isy w ać. Plik m oże być ró w n ie d o b rz e o tw a rty do czytania, a u staw iam y się na końcu po to, by za ch w ilę zrobić np. c z te ry kroki w tył i p rzeczy tać czw arty bajt od końca pliku. D otychczasow a treść pliku o tw ie ra n eg o przy u ży ciu zacho w an a. 18)
te g o trybu - jest
Uwaga: w czasach przedstandardowych niektóre kompilatory oferowały też tryby i o s : : n o c r e a te i i o s : : n o r e p la c e . Tryby te nie weszły do obecnego standardu.
1107
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików
ios::appi (ang.: append - doczepiaj). O k reślen ie to oznacza, że w szelk ie operacje z a p isy w an ia d o p lik u będą p o legały w y łączn ie na d o p isy w an iu d o jego końca. (Jeśli użyjem y o k reślen ia tego try b u , to niejaw nie pociąga to za sobą tryb i o s : : o u t) . D otych czaso w a treść pliku o tw ieran eg o p rzy u ży ciu zacho w an a.
tego trybu - jest
ios::trunc (ang.: truncate- obetnij). T en tryb w y b ieram y , g d y nie interesu je nas d o ty ch cza sow a z a w a rto ść otw ieran eg o p lik u . Po otw arciu go, stara treść jest o d rzu ca n a i do pliku m o ż n a zapisyw ać tak, jakby był to św ieżo w y k reo w a n y plik. Jeśli takiego plik u d o tej po ry nie było - to jest tw o rzo n y . T ryb ten jest m ilcząco za k ła d a n y , g d y zasto so w aliśm y try b i o s : : o u t bez d o d atk o w ej specyfikacji i o s : : a p p albo i o s : : a t e .
ios::binary (ang.: binary12 - bin arn y , bin arn ie). Ten tryb k o n ieczn y jest w tych system ach operacy jn y ch , w które inaczej obchodzą się z p lik am i tekstow ym i niż binarn y m i. (Takie są np. systemy M S-D O S czy MS-Windows). Za pom ocą teg o trybu w y ra ż a m y życzenie, jak m ają być trak to w an e dan e pły n ące stru m ien iem : jako d an e b in a rn e czy tek sto w e. (D o m n iem an y jest tryb tekstow y). W za sa d z ie stru m ien io w i jest w szy stk o jedno czy p ły n ą n im dan e b in a rn e czy tekstow e. Z a w sz e przecież ch o d zi o przesłanie jakichś bajtów . N iezależnie od tego, w jak im trybie otw o rzy m y p lik - m ożna u ż y ć ró w n ie d obrze funkcji do przesłan ia z n a k u np. g e t ( c h a r ) czy p u t ( c h a r ) jak i funkcji do czytan ia /p is a n ia b in a rn e g o w r i t e lu b r e a d . Jest tylko je d n o „ALE". Jeśli (w e w spom nianych sy stem ach ) o tw ieram y plik w trybie te k sto w y m w ów czas: ♦♦♦ w p rz y p a d k u w staw ian ia d o strum ienia (p isan ie d o pliku): k a ż d e w ys tą p ien ie zn ak u ' \ n ' zostaje au to m aty czn ie zam ien io n e na sekw encję d w ó ch znaków : ' \ r ' i ' \n ' ❖ w p rz y p a d k u w yjm ow ania ze strum ienia (czy tan ie pliku) sekw encja • \ r ' i ' \ n ' zostaje au to m aty czn ie zam ien io n a n a znak \ n '' w skrócie:
19)
czytanie:
' \ r ' + ' \n '
\n '
pisanie:
' \n '
\ r ' + ' \n '
[czytaj: „bajnery"]
1108
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików W p rzy p ad k u try b u binarnego nie robi się żadnych tak ich p o d m ian . D obrze jest o tym pam iętać. Jeśli zap o m n isz i będziesz c h c ia ł zapisać w pliku kilka bajtów reprezentujących d a n e binarne, a stru m ień b ęd zie pracow ał w trybie tekstow ym c h a r r y s u n e k ! ] = { 1, 32, 1 0 , 17, 4 }; o f s tr e a m o ś r o d e k ( " z d j e c i e . tm p ", i o s : : o u t ) ; o ś r o d e k .w r it e ( r y s u n e k , 5) ; o śro d e k . c l o s e ( ) ; to w rezultacie do pliku zostanie z a p isa n a taka treść b in a rn a : 1, 32, 13, 10, 17, 4 Jak w idzisz p o jaw iła się d o d atk o w a trzy n astk a. To d la te g o , ż e plik otw arty był (przez d o m n iem an ie) w trybie tekstow ym . Strum ień z o b a c z y w sz y bajt 10 — który jest id e n ty czn y jak kod ASCII z n a k u ' \ n ' dołożył w p reze n cie znak ' \ r ' (kod ASCII = 13). Strum ień sądził, ż e pracujem y w try b ie tekstow ym , więc chciał się p rzy słu ży ć. W inni jed n ak jesteśm y my sam i, b o p o w inniśm y go uprzedzić, ż e p racu jem y na d anych bin arn y ch , a nie tek sto w y ch . Czyli p o w in niśm y ten p lik o tw o rzy ć instrukcją: o f s tr e a m o ś r o d e k ( " z d j e c i e . tm p ", i o s : : o u t
| i o s : : b in a ry );
W F u n k c ja close()
W po w y ższy m p rzy k ład zie zo b aczy liśm y użycie funkcji c l o s e , która kończy pracę stru m ien ia z d an y m plikiem . Jeśli jest to stru m ień w y jścio w y , to p rzedtem w y w o ły w an a jest jego funkcja sk ład o w a f l u s h , p o w o d u jąca w y słan ie do pliku ew entualnej oczekującej tam na w y słan ie zaw artości b u fo ra .
Wielokrotne otwieranie strumienia Strum ień w ejściow y lub w yjściow y p o zakończeniu p ra c y z jakim ś plikiem m oże p o słu ży ć do p racy z innym . N ic w tym d ziw n eg o - o ś ro d e k kontroli lotów w H uston p o zak o ń czen iu jednej misji p ro m u kosm icznego m o ż e obsłużyć inną. W praktyce o d b y w a się to w ten sp o só b , że po w y k o n an iu w szystkich operacji w e /w y z d a n y m p lik iem , plik jest zam y k an y , a potem o tw ie ra n y jest inny. O to przykład: o f s tr e a m s t r u m ( " r a z . trap ") ; stru m << "d o je d n e g o z b i o r u \ n " ; s tru m .c lo s e ( ) ;
S trum ień s t r u m słu ż y nam p ie rw o tn ie d o pisania w pliku "raz.tm p". N astępnie funkcją sk ład o w ą c l o s e zam y k am y ten plik i k o m u n ik ację z nim , po czym za
1109
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie płynące do lub od plików
pom o cą funkcji open o tw ie ra m y now y plik. Tym s a m y m sp raw iam y , ż e s tr u m ień „ p o p ły n ie " d o pliku o n a z w ie "dwa.tmp" . O czy w iście stru m ien ia m o żn a p o n o w n ie użyć tylko d o celów , d o których zo stał p rz e z n a c z o n y - czyli jeśli jest strum ieniem w y jścio w y m , to n a d a l m u si się zajm o w ać teg o ty p u operacjam i. Strum ień w ejściow y też m usi p o zo stać w ej ścio w y m . Słow em : stru m ień jest n ad al tej sam ej k lasy - zm ienia się tylko plik, który o b słu g u je.
Częste kombinacje trybów A by o sw o ić się z tym i try b am i otw ieran ia plików , z o b ac zm y p o n iższe ze sta w ie nie częsty ch kom binacji try b ó w otw arcia.
Znaczenie
Połączenie
out 1 app
Plik jest tw orzony, jeśli do tej p o ry nie istn iał, a jeśli istniał, to stara treść jest zach o w an a. Kolejne operacje p isan ia będą d o p isy w ały d o końca.
o u t 1 tru n c
Plik jest tw o rzo n y (od nowa). M ożna będzie d o niego zapisyw ać.
in 1 out
Plik o tej n azw ie m u si istnieć, m ożna będzie go czytać lub w n im pisać.
in 1 o u t 1 trunc
Plik jest tw o rzo n y (o d nowa). M ożna będzie w nim pisać lub z niego czytać.
P rzestarzałe
n o c re a te
i
n o r e p la c e
Jeśli z p rzed sta n d ard o w y c h czasó w pam iętasz tryb ios : :nocreate, to jego o d p o w ie d n ik znajdziesz w tej tabeli, jako p o łączen ie in | out. N ato m iast starożytny tryb ios: :noreplace p o le g ał na otw arciu pliku p o d w a ru n k ie m , że takiego pliku nie m a jeszcze na d y sk u . T u p oradzić m ożna sobie p ró b u jąc najpierw otw orzyć go d o sam ego czy tan ia. Jeśli się u d a, to znaczy, że plik istnieje, a tego w łaśnie n ie chcem y.
W O d p o w ie d n i k i t r y b ó w o t w a r c i a z biblioteki
stdio
Jeśli pro g ram o w ałeś w języku C, to p o sługiw ałeś się biblioteką stdio. T am oczyw iście też są tryby otw arcia plików . A by Ci b y ło w ted y p rzestaw ić się na pracę z tym i now ym i trybam i, miej na u w a d z e n astęp u jącą tabelkę.
1110
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
2 2 .1 5 B łę d y w tra k c ie pracy s tru m ie n ia N ie z a w sz e otw arcie pliku się u d aje - p rzy k ład o w o : chcem y otw orzyć dc czy tan ia plik, k tó ry nie istnieje. N ie zaw sze też m uszą się u d a ć operacje w e / wy - np. chcem y zap isać coś do pliku dyskow ego, a na d y s k u nie m a już miejsca. Inaczej m ó w iąc zażąd aliśm y operacji w e /w y , a ona n ie m ogła zostać w y k o nana p o p raw n ie. P roblem p o p raw n o ści operacji w e /w y n ie pojaw ia się dopiero p rzy o peracjach z plikam i. N asze d o tychczasow e p rz y k ła d y ze strum ieniam i c i n , c o u t też p o w in n y zaw ierać m iejsca, w których s p ra w d z a m y popraw ność operacji w e /w y . O to zn an y p rzy k ład : jeśli stru m ień c i n o czek u je na w czytanie liczby, a ty m czasem na k la w iatu rze ktoś w y stu k a coś, co liczbą nie jest — w ó w czas w cz y ta n ie liczby nie m o że nastąpić. W n aszy ch p ro g ram a ch p rz y k ła do w y ch m o g liśm y się bez sp ra w d z a n ia p o p raw n o ści obejść, lecz w program ie, który p iszem y d la publicznego u ż y tk u , należy się s p o d z ie w a ć i takich sytuacji, że u ży tk o w n ik p o m y li się. W niosek jest prosty: D o d o b reg o stylu p ro g ra m o w a n ia n ależy k o n tro la p o p raw n o ści w ła śn ie w y k onanych operacji na stru m ie n ia c h .
I
W n astęp n y ch p arag rafach p o ro zm a w iam y o n a rz ę d z ia c h , za pom ocą których łatw o to robić. Takie n a rz ę d z ia istnieją - zeb ran e zo stały w klasie i o s . Z k lasą tą spotkaliśm y się ju ż p rz y o m a w ian iu flag stanu fo rm ato w an ia. Jest o n a k lasą p o d staw o w ą dla
1111
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
w ielu klas stru m ien i - tak że d la i f s t r e a m , o f s t r e a m , o raz f s t r e a m , zatem o w e n a rz ę d z ia zostają d o nich odziedziczone. P rzy jrzy jm y się bliżej ty m n arzędziom :
2.15.1
Flagi stanu błędu strumienia W o śro d k u d o w o d zen ia p racą strum ienia - czyli w k ażd y m obiekcie klasy s tru m ie ń - jest słow o o d p o w iad ające za stan b łęd u stru m ien ia. Z n aczy to, że w sytuacji, g d y n astępuje jakiś b łąd pracy stru m ien ia - w słow ie tym u sta w ia n y jest b it o d p o w iad ający z a d a n ą kategorię błędu. Te kategorie b łędów określone są ty p e m całkow itym zd efin io w an y m w klasie i o s _ b a s e . N ie p o p ełn im y d u ż e g o b łę d u w yobrażając so b ie ten ty p całk o w ity jako n astęp u jący typ w y liczen io w y enum. enum iostate t goodbit = eofbit = failbit = badbit =
0, 1, 2,
4
}; W ó w czas słow o stanu b łę d ó w p rzedstaw ić m o żn a tak: Rys. 22-3.
Poszczególne bity słowa stanu a
x
4
failbit eofbit
2
1
O to zn aczen ie poszczególnych flag:
goodbit w id z im y , że w typie w yliczen io w y m i o s t a t e jej w artość jest 0. C zyli tak n a p ra w d ę - nie jest to ż a d n a flaga b łęd u . M ów im y, ż e stan g o o d b i t jest w tedy, g d y w szy stk ie bity stan u b łę d ó w są w yzerow ane;
eofbit flag a ta u staw ian a jest w ted y , g d y podczas czy tan ia n ap o tk an y zo stał koniec p lik u (EOF);
failbit u sta w ie n ie tej flagi oznacza, ż e jakaś operacja w e / w y nie p ow iodła się. S tru m ie ń tkw i w stanie błędu (niepow odzenia), jed n ak p o w y zero w an iu tej flagi n a d a l n ad aje się do pracy;
1112
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
badbit -
u staw ien ie tej flagi oznacza, że n astąp ił jakiś p o w ażn y b łą d n aruszający budo w ę sam eg o stru m ien ia Dalsza praca z tym stru m ien iem je st niem ożliw a.
22.15.2
Funkcje do pracy na flagach błędu Z w ykle flag am i błędu stru m ien ia nie zajm ujem y się b ezp o śred n io . Do pracy z nim i klasa b a s i e ios (to jed n a z klas b ezp o śred n ich k la s pochodnych od klasy ios_base) definiuje kilka funkcji składow ych in form ujących n a s o stanie stru m ien ia. O to one:
bool
good();
- ta funkcja zw raca w artość true, jeśli w szystko jest w p o rz ą d k u , czyli żad en z bitów b łę d u nie jest ustaw iony, np. char znak; do{ cin >> znak; cout << znak; }while(cin.good() );
Pętla b ę d z ie się w ykonyw ała d o p ó k i nie nastąpi b łą d s tru m ie n ia cin. bool
eof();
- funkcja ta zw raca w artość true, jeśli jest ustaw io n a flag a ios : :eofbit, czyli przy o p eracji w czytyw ania n ap o tk a n y zo stał koniec p lik u , n p . char d a n e [1 0 0 0 ]; ifstream s ("wtorek.tmp",
ios::in I ios::b i n a r y ) ;
// na razie pomijam sprawdzenie poprawności otwarcia pliku s.read(dane, sizeof(dane) if(s.eof ())
);
cout « "Napotkany EOF przed wczytaniem " "wszystkich danych" << endl;
) else cout <<
"Wszystko wczytane O.K. " <
} P ow yższy p rz y k ła d pozw ala na w y k ry cie czy z plik u w c z y ta ła się żąd an a ilość danych. bool
fail();
- funkcja ta zw raca w artość true, g d y failbit lu b b a d b i t są ustaw ione. Z au w aż, że w b rew swej n a z w ie interesuje się o n a n ie tylko sam ą flagą failbit, ale także flagą badbit. D zięki tem u o d p o w ia d a n a m na p y tan ie,czy jest jakiś b łąd , niezależnie czy p o w a ż n y czy błahy.
1113
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia O to p rz y k ła d użycia tej funkcji. int liczba; cin >> liczba; if(cin.taił())
{ cout << "Zle podana liczba !" << endl;
} flaga f a i l b i t zostaje u sta w io n a w tedy, gdy z a m ia s t oczekiw anej liczby - z k la w ia tu ry p rzy p ły n ie stru m ien iem tekst. bool
bad()
- funkcja ta zw raca w artość true, gdy flaga bad b i t jest u staw iona. T ak zd arz y się na w te d y , gdy stru m ień zo stan ie p o w ażnie u s z k o d z o n y ("zepsuty").
.2.15.3
Kilka udogodnień
O p e r a to r k o n w e rsji n a typ
bool
P ow yżej p o zn aliśm y funkcje, z a pom ocą których m o ż n a było sp ra w d z a ć p o p ra w no ść w łaśn ie d o k o n an y ch operacji w e /w y . S p ra w d z e n ie takie p o legało na w y w o ła n iu jednej z funkcji sk ład o w y ch stru m ien ia. Istnieje jednak w y g o d n iej sza m eto d a: jest to także w y w o łan ie jednej z tych funkcji, jednak w n ie zau w ażo n y sposób. Jak to jest zrobione? Otóż dla w y g o d y w klasie i os jest także zd efin io w a n y o p erato r k o n w ersji na typ bool. D zięki tem u m ożem y sp raw d zać p o p ra w n o ś ć /n ie p o p ra w n o ś ć operacji ze stru m ien iem w w y rażen iach w a ru n k o w y c h instrukcji if, while, itp. Z asad a jest prosta - ilustruje ją tabela. r------—------------------ -----------
Język "ludzki"
odpowiada instrukcji
Instrukcja C++
Jeśli w szystko w p o rz ą d k u , to...
if (strumień )...
i f ( ! strumień . f a i l ()). . .
Jeśli coś nie w p o rz ą d k u , to...
if ( ! strumień ) ...
if ( strumień . f a i l ( ) ) ...
Innym i słow y, jeśli coś się nie u d a ło , strum ień p rzy b ie ra w artość logiczną false. Z obaczm y to sam o w szerszy m kontekście cin >> liczba;
// czy jakiś problem? if(cin.fail() ) cout << "Niepowodzenie " << endl; if(!cin) cout << "Niepowodzenie " << endl;
// czy wszystko w porządku ? i f (!cin.fail0 ) cout « "Sukces" « if(cin) cout << "Sukces" << endl;
endl;
Inne u d o g o d n ien ie, to zd efin io w an y także w klasie i o s
1114
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
o p e r a t o r k o n w e r s ji n a typ
void*
operator void*()
O p erato r ten zw raca w artość niezerow ą (że tak pow iem : me-NULL-ową), g d y nie je st u staw ian a żadna flaga błędów. Pam iętam y, że rezultatem w yrażenia cin >> liczba
(jako całości) jest referencja d o strum ienia cin. Jeśli je d n a k operacja w czytania nie p o w ied zie się, to w artością w yrażenia (cin>>liczba) nie będzie już referencja do strum ienia cin, ale zero (NLfLL). W zw iązk u z tym , m ożna popraw ność p rzep ro w ad za n ia operacji sp raw d zać n aw et w taki sposób if(cin >> liczba)
cout << "sukces!" ;
I s t n i e n i e te j k o n w e r s j i m a j e d n a k j e s z c z e i n n y , g ł ę b s z y s e n s
Z obaczm y ,co będzie w p rzy p ad k u takiej „k ask ad o w ej" instrukcji w czytującej: double liczbal, liczbaż; cin » liczbal » liczba 2 ;
Tę in stru k q ę w czytyw ania inaczej m ożna zapisać jako: (cin »
liczbal ) >> liczba 2 ;
Jeśli operacja w czytania pierw szej liczby się nie p o w ie d z ie , to - dzięki o m a w ia nej konw ersji - m am y przed sobą taką instrukcję: (NULL) »
liczba2;
1115
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
12.15.4
U staw ianie i kasow anie flag błęd u strum ienia W ty m p arag rafie m ów ić b ęd ziem y o tym , że cza se m m ożem y chcieć sk aso w ać jakiejś flagi stanu stru m ie n ia - g d y na p rzy k ła d ro zp o zn a m y , co sp o w o d o w ało błąd. Np. próbowaliśmy ohoorzyć do czytania plik podając błędną nazwę, a taki plik nie istnieje. Jeśli chcemy wówczas spróbować jeszcze raz, ju ż z popraw ną nazwą, to najpierw powinniśmy skasować ustawienie flagi błędu. N a u c z y m y się też, jak sam em u ustaw iać flagę b łę d u . Robimy to wtedy, gdy chcemy samemu decydować, co jest poprawną, a co niepoprawną operacją wczytania/wypisania. Przydaje się to w przypadku operacji we/wy z obiektami typu zdefiniowanego przez użytkownika.
W ł a s n o r ę c z n e u s t a w i a n i e flag b ł ę d u
D o ty ch czas do in fo rm o w an ia się o stanie flag b łę d u w ystarczały n am funkcje good,
fail, eof, bad
W k la sie ios_base są jeszcze d o d atk o w e funkcje pracujące na flagach stanu b łęd u io_state voTd void
*
rdstate(); elear(io_state = goodbit); setstate(io_state);
r d s t a t e - daje n am jako rezultat słow o sta n u błędu d an eg o stru m ien ia,
♦♦♦ e l e a r - pozw ala z a jed n y m zam achem zm ien ić w szystkie flagi b łę d u . ♦>
s e t s t a t e - d z ia ła subtelniej: ustaw ia ty lk o w y b ran e flagi, pozostałych n ie kasuje.
Przyjrzyjmy się tym funkcjom bliżej. Funkcja
rdstate - (czyli read state - odczytaj stan) zw raca jako re z u lta t słow o ty p u io_state o b razu jące bieżący stan flag błędu d anego stru m ien ia. Wyobraź sobie ten typ i o _ s t a t e jako słowo typu i n t, w którym odpowicdnic bity odpoioiadają flagom stanu błędu. Jak m o ż e m y z tej funkcji skorzystać? Z definicji ty p u w yliczeniow ego iostate w ie m y jak nazyw ają się poszczególne flagi b łę d u i które bity zajm ują. O to p rz y k ła d (w form ie p seu d o k o d u ) pokazujący, jak m ożem y spraw dzić, czy w sło w ie zw racan y m p rzez funkcję ios base: :rds t a t e ustaw iony jest bit o d p o w iad ający fladze f a i l b i t . i f ( s t r u m i e ń . rdstate () cout «
)
& ios:: failbit)
"Flaga failbit jest ustawiona" << endl;
1116
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia M ożna tak, ale ta funkcja w cale nie została w ym yślona po to, by nas inform ow ać o stan ie konkretnej flagi. Przecież prościej m ogliśm y się tego dow ied zieć w yw o łując funkcje b ad , f a i l itd. Praw dziw ie zastosow anie funkcji r d s t a t e jest takie, że dzięki niej m ożem y p o zn ać całe bieżące sło w o stan u po to, by w jakiś sposób pozm ieniać je - i tę zm ienioną w arto ść um ieścić z po w ro tem w ośrodku dow odzenia stru m ien iem - z a pom ocą funkcji e le a r. Z o b aczy m y to za chwilę.
Funkcja
void
elear(io_state
= ios::goodbit)
W y w o ły w an a jest z jednym arg u m en tem typu io state, który n ależy ro zu m ieć jako "słowo stanu b łęd ó w stru m ien ia” (czyli zb ió r w szy stk ich flag błędu stru m ien ia). D ziałanie tej funkcji jest takie, że ten a r g u m e n t zastępuje d o ty ch czasow e sło w o stanu stru m ien ia (czyli w szystkie flag i s ta n u na raz). W artość d o m n iem an a tego arg u m en tu to i o s _ b a s e : :goodbit, (czyli zero) - a ozn a cza to, że w tym now ym sło w ie żad n a flaga b łędu nie jest u staw io n a. Skoro d ziałan ie tej funkcji po leg a na tym , że jej a rg u m e n t zastępuje do ty ch czasow e sło w o stanu błędu stru m ien ia, to m o żn a p o w ied zieć, że tą funkcją „ p o d rz u c a m y kukułcze jajo". S trum ień ma od tej p o ry n o w e słow o sta nu błędu. C elow o u ży łem tak o b razo w eg o p orów nania o k u k u łc z y m jaju, bo nie chcę byś m iał jakiekolw iek skojarzenia z nazw ą e l e a r . T a n a z w a (kasuj, zeru j) jest b a rd z o niefo rtu n n ie d o b ran a. Funkcja ta, co p ra w d a , k asu je w szy stk ie flagi stan u b łę d u , g d y w y w o łam y ją tak: c i n . e l e a r (0 ); c i n . e l e a r () ;
// np. dla strumienia cin
natom iast, g d y w y w o łam y ją z arg u m en tem n ie z e ro w y m , w ów czas niektóre flagi z o stan ą skasow ane, ale in n e ustaw ione. P rz y k ła d o w o instrukcja cin.elear (ios::eofbit);
u sta w ia w stru m ien iu c i n flag ę i o s : : e o f b i t , a w sz y stk ie inne kasuje. Jeśli chcem y ustaw ić dw ie flagi, to nie m ożem y u ż y ć k o lejn y ch dw óch instrukcji cin.clear(ios::eofbit); cin.elear(ios::failbit);
d lateg o , że d ru g a z nich zn iszc zy to, co ustaw iła p ie rw sz a . P o p ra w n a form a to „ O R -o w a n ie " czyli su m a b ito w a c i n .elear(ios::eofbit
| ios::failbit);
P o w y ższa instrukcja u staw ia d w ie żą d a n e flagi, a w sz y stk ie inne kasuje. U w aea: Flagi stan u stru m ien ia są tylko w sk a źn ik ie m . T ak - jak w trakcie choroby - te rm o m e tr jed y n ie w sk a zu je n a sz ą tem p eratu rę. Jeśli
I
1117
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
rozbijem y term o m etr, to w cale nie o zn ac za, że w y zd ro w ieliśm y . D latego, g d y w y k ry liśm y (za pom ocą flag stan u ) sytuację b łę d u , to p rz e d e w sz y stk im należy problem ro zw iąz ać, a nie tylko zająć się sk aso w a n iem tej flagi błędu. J a k u s t a w i ć j e d n ą lub d w i e flagi b ł ę d u - b e z w p ł y w u n a r e s z t ę ? Z a łó ż m y , ż e ch o d zi o flag ę i o s : : e o f b i t w s tru m ie n iu o n a z w ie we j s c i e io state slowo_stanu; slowo_stanu = we jscie.rdstate ();
. //1 . o d c z y ta n ie d o ty c h c z a s o w e g o
H2. "dopalenie" flagi iosreofbit słowo stanu 1= ios::eofbit;
_
// czyli słowo_słanu = słowo_stanu I iosr.eofbit;
1/3. "podrzucenie" tegonowego
wejście.elear (r) ;
To s a m o zw ięźlej wejscie.elear( wejscie.rdstate() | ios::eofbit );
ja k w id a ć - i tutaj funkcja e l e a r nie słu ży n a p ra w d ę k aso w an iu flag (niczego tu n ie kasuje) tylko sen so w n e m u u staw ien iu now ej flagi. Funkcja
void setstate (io_state
s t an) ;
M asz rację, jeśli p om yślałeś, że ten sposób u sta w ia n ia jednej flagi za pom ocą: 1) d o w ia d y w a n ia się o stan w szystkich flag ( r d s t a t e ) , 2) dopalania" jednej (O R-owanie), 3) a potem p o d rzu ca n ia funkcją c l e a n te g o n o w eg o w iz e ru n k u flag - jest niew y g o d n y . D latego od n ied aw n a m a m y do dyspozycji funkcję s e t s t a t e , która jest jakby g o to w y m p o w tó rzen iem konstrukcji, której u ży liś m y p rz e d chw ilą. Jej definicja (w klasie b a s i c _ i o s ) jest z rea lizo w an a jako funkcja i n l i n e takiej treści. void setstate(io_state f)
{
elear(rdstate() I f );
} Jak w id a ć funkcja ta w y k o n u je czynności o p isan e p o w y żej w p u n k tach jako 1), 2), 3). Z asto so w an ie
U staw ian iem flag stanu b łęd ó w nie m usim y się zw y k le zajm ow ać - ro b io n e jest to za nas autom atyczne. Są jednak sytuacje, g d y m o że nam się to okazać p rz y d a tn e . Pokażem y to na przykładzie. Istota tego p rzy k ła d u jest taka: m am y o p e ra to r w czytyw ania, k tóry w czytuje p isan e na k la w ia tu rz e w yrazy; tym cza sem n ie w szystkie p o d a n e w y razy uznajem y za p o p ra w n e - w olno napisać
,9 ,
aaa}xx
bu
pmois
Aza} CBpo
08IMI|SO|Z eje ,9 , aaa}ąx eu
eu oepfe|6AM azoui uej>ja niuej6ojd emeuojjAM
3iiunv.tdodi]p[
@ //
Aza} Cepoj
smois
/ /
:>|B i p e w A z j d n>jpedAzjd m
.'5 fB a a q
aspa
{
(q-ęqxxBj : : sot^ 5 () e}eqspa • uxo) aeexo-uto rrupezood po ; 9 XZU\„ » }noo
} (UTDi) JT 0
//
.'a p a z i; « arfinap << azsMaaąd << uto : , a, aaa}xx bu bmoxs Aza} Cspoa,, » }nóo } (X)9ITIJM
/ejoaza} 'aa&nap 'szswasid
a- eu- OMOxs } ()uxeui }ux
/ ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł ł »
ł ł ł ł ł ł ł ł ł ł i H
ł ł ł * ł ł ł ł ł ł ł ł »
ł ł ł ł ł ł ł ( f ł i | , ł ł ł ł f
ł ł t
/
.'a}s uanaaa { ( }Tqxxej::soT | () a}B}spa-a}s) aBaxo-a}s / /
q 0 // 6 0 //
{
(qxqxxBj : : soi) s}B}sqss -aqs } ( . s, =i [ 0 ] ZBaA«-M)JT / zaaAM-M << a}s
_ _ } (m 9 9 bu omoxs 'a}s 5 uiBaa}są)<< ao}eaado 5 uiBaa}si ////////////////////////////////////////////////////////////// •'{ .'zbjAm 6uxa}s :oxxqnd _ a
_ BU
OMOXS
} SSBXO
////////////////////////////////////////////////////////////// .'p}s aoBdsauiBU 6uxsn <5uxx}s> apnpouT# apnpouT# apnpouą# • } x q x x e : j A S b j j D i M B ^ s n u a i u i M o d aizBi u u A u M p a z i d no a i z p A q a z ' u i a i ^ u r u e M p o d (Biuaiuinołs a z b i u b m
Am
b i u b m o u i
o i p o z j c
(A m ) b
z b j
Am
i u b m
b u
Az
a Aja:ji[
b u
A jA z
d m
j o j B i a d o (aiu B j p A u i a f n i u i j a Q
m o j s
! o p feznfs A}>jaiqo ( a j o ^ 'b s b [>( a ( u d ó } s A M a i z p S ' p s ^ A z j d 0 4 0 pfe{q
a u u i p o Ais
m
uapaf a B M A ł A z a M u a i u i M o d ua} j o j B j a d o
o b z z b j
Am
b z
O} A u i a f e u z n 04 'Aj 341[
ijsof a Aja^ij (ajBui p o Ais b f B u A z a B z aj04>j 'at>[B4
Biuaiuin.ijs Aau.id apjpuj av ApAjg BpsfA/W/Bpsfa/W afaB.iado ' Z Z ‘łiuzpzog
o
^ jA j
8TTT
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
1119
e u ro p a e s tra g o n
Komentarz
►0*
A
O Jest to z w y k ła realizacja p rzeład o w an ia o p erato ra » . C iek aw e w niej jest tylko to, ż e b a d a m y czy istotnie p ie rw sz ą literą p rzy jęteg o w y ra z u jest ' e '. Jeśli nie, to u sta w ia m y flagę i o s : : f a i l b i t . Z a p a m ię taj tę konstrukcję. T ak dopala się je d n ą flagę i o s : : f a i l b i . do o b ecn eg o słow a stanu b łęd ó w strum ienia. ♦> F o rm a O a - robi to za pom ocą now ej, w y g o d n e j funkcji s e t s t a t e ♦♦♦ F orm a O b - robi to z a pom ocą d aw n eg o , tró jetap o w eg o sposobu. © O to m iejsce w funkcji m a in , g d z ie d o konujem y trzech „ k ask ad o w o " u s ta w io n y ch operacji w czytania. Jeśli w trakcie jednej z tych operacji u staw iliśm y flagę i o s : : f a i l b i t , to d a lsz e usiłow ania w yjęcia czegoś z tego stru m ien ia nie b ęd ą p o dejm ow ane. Tak b ęd zie dopóki stru m ień tk w i w stanie b łęd u - czyli d o p ó k i flaga jest ustaw io n a. © Jeśli po nap isan iu o strzeżen ia chcem y u ży tk o w n ik o w i p ro g ram u dać szan sę p o p ra w ie n ia się, to lik w id u jem y flagę błędu w ty m stru m ie n iu - k asu jem y flagę i o s : : f a i l b i t . Innych flag b łędu nie zm ien iam y , zeru jem y tylko tę jedną. Z a u w a ż , że stosujem y tu iloczyn bitow y z w arto ścią zan eg o w an ą. Z ap a m ię taj tę konstrukcję. c i n . e l e a r (cin . r d s t a t ę ( ) & ~ i o s : : f a i l b i t ) ;
Tak kasuje się je d n ą, w ybraną flagę i o s : : f a i l b i t z o b ecn eg o słow a stanu b łęd ó w strum ienia.
I
G d y b y śm y zapom nieli i nie zlikw idow ali tego sta n u b łę d u , to ż a d n a d alsza p ró b a w yjęcia czegoś ze stru m ien ia c i n nie b yłaby p o d ejm o w an a. W niosek: _ Mając m ożliw ość zm ian flag stan u b łę d ó w m ożem y d efin io w ać inteligentniejsze o p erato ry « » - t a k i e , k tó re zareag u ją n a to, co my sam i ok reślam y jako p o p raw n e lu b nie p o p raw n e.
22.15.5
Trzy plagi - czyli „gotowiec", jak radzić sobie z błędam i W spom inaliśm y d u ż o tym , że flagi błędu jed y n ie sy g n alizato ra m i b łę d u . Ich sk aso w a n ie nie n ap raw ia błęd u , tak jak w trakcie c h o ro b y rozbicie term o m etru nie obniża gorączki. Zatem o p ró cz skasow ania flagi, p rz e d e w szy stk im należy zająć się rozw iązaniem p ro b lem u , który sp o w o d o w a ł błąd. Na przykład, gdy próbujemy otworzyć do czytania plik, który nie istnieje, to strumień informuje nas o niepowodzeniu ustawieniem flagi f a i l b i t. Jeśli chcemy spróbować jeszcze raz, to trzeba wówczas przygotować inną, poprawną nazwę pliku (takiego, który rzeczywiście istnieje) - no i
.'„u \ • • -aąojrd Auibtmouoj,, » • " ip ś d n S m ą o M fiith jo y
A
m
ty p b z u o d on 4 so i z t i l i m i u t u j s
as ja {
ąnoo
//
(^xqxT^J ::sox~ 9 ()9 5 s^spa 'u m n s Jj e e p ■mtuąs iSvjJ muucnosty 1/
//
4 n p 3f q u v f s p b u n s n R u ą s n t u // '..iii npdM ąuv4S cn 4sof \1mu1tu4s // 04 Ms ułopn mu uiojomjo vqoxd ojoys //
1
1
u u .n u n .t s
.'Xpua >> njfrjd bmzbu >> „ :n>(Txd sąoaeM ąo Pe T9., >> ąnoo
4
()[ivJ u{)U s ifaznuin \\t\zo // l ofupn ks f\zo // 0
//
(u iru ą s i ) j t
() j^.s—o -ri3(xxd— b m z b u )usdo um-jąs
• (u x ::s o i tn a w m jo uąoud //
nuoys mffijauiscni //
^XPua >> ąnoo
:n>(Txd
.'n5(Txd BMZBU << u 10 9mzbu ęepo
}
(saoj(ns ,•) axxqM ; a s x e j = sso ^ n s x °oq
4
■ptmostys crumdluu oqazu if dSojf ‘mfijd tnouumjo śqoud pimouod Rąy // npdfq jiu u i s cn strzo m o m 100044 iim uttU 4$ // 3lm u4si m u y i / d R p S ‘m zou on pqpo/::sot n p 3 fq iS u jjp im o 4s n a zo tu j l tn u o 4Rzo o p rapid m uoum orifo cn m u a zp o m o d m p i //
m M m M M M M m m m m m ttitmitwmMMMMttttmMtłMMMMmttttii## / /
.'n>{Txd mmyjjd z Rooud op tnumiutuis olmuijap //
bmzbu
fmjaąs
.'u in aąs U IB a ją S J T
}
()UTBUI 5UT
Ł ********ł*****¥*+ ¥*¥¥**********// < 6 u ta ^ s > epnxoux#
epnxouT# .'pąs aoBdseuiBU ću xśn apnxouT#
•ppsdzDzsaiu 3UIPS łupu dis bfpzippz 1 juip>[i[d z Auiafnopjd ujAjo4>[ m 'uipjSoid AMoppj^Azjj ‘„pomc^oS" ui3 {PMC>4o2 Azjd ibeajAs ijji^p; osouzaijcpjo pu aiuspjyy\ MoSzpiąojp djpd oaiuuiodAzjd aiqos Aq yi3fzbis>j [37 3iuojłS n>[[i>{ dP4Azd zpj azozsat A401P0 plu aiu - o>[qAzs diqoiz 07 aaip 04>[ 'so4),j Aopjd [34 m dis DiMefod aafeSoui Apdjq aujpriłuaMa ąAznjsqo Aq ')(P4041 —ui3i>[i{d z oyaoblndpjd npo>j >p[iuii djpd apsidpu o>[qAzs pqazi4 uiaspz^ PMAq 04 >[pI 'oiaiM (aidajfpu uips az ośtajpjp 'jpjSpipd U94 ąepop ui3 {imoup4Soc]
Mppśłq (foenłAs yoi^e; e>||!>j zbjsj AujAzoeqoz
1134
jp/d Raipsb^cn pRzjomfo pomoąojds ouzoiu oumdop strzoaio/w ’nptyq iSv]J pomostys atosiniAzoo
muaiuiiujs Aap.id ap?|B.i4 avApdfg ppsrAyW/Bpsfa/W abp.iado ’Z Z ‘JPizpzog
OZU
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia
1121
1122
Rozdział. 22. O peracje W ejścia/W yjścia Błędy w trakcie pracy strum ienia // instrukcja wczytania liczby - rozpocznie czytac zaraz po // wczytanym poprzednio bajcie strum » liczba; if (strum. fail () )
// ©
// Czy się udało ? // nie !
< cout << "Blad failbit, bo najbliższe " "bajty\n\t nie mogą " "byc wczytane jako liczba\n";
// Aby to coś wówczas wczytaćjako string // należy skasować ustawiona flagę błędu strum.elear(strum.rdstate<) & ~ios::failbit);
//O // Strumień nadaje się do pracy, a to coś, co go // zatkało, nadal czeka na wczytanie
string słowo; strum >> słowo; cout << "Jest to słowo: "<< słowo << endl; }else{
// jeśli się udało ! cout << "Pomyślnie wczytana liczba: << liczba « endl;
"
} // dalsza praca z plikiem...
D
Na ekranie zobaczymy na przykład taki tekst Podaj nazwę pliku: nic.nic Blad otwarcia pliku: nic.nic Ponowiamy probe... Podaj nazwę pliku: plagi.cpp Plik poprawnie został otworzony do czytania Podaj numer baj tu, który chcesz poznać: 9999 Blad pozycjonowania, prawdopodobnie plik jest krótszy niz 9999 bajtów (Podaj mniejsza liczbę) Podaj numer baj tu, który chcesz poznać: 3 Ten bajt to hexadecymalnie: 0x63, a jako znak ASCII; ################################################## Próba wczytanie następnych znaków jako cyfr liczby.. Blad failbit, bo najbliższe bajty nie mogą byc wczytane jako liczba Jest to słowo: lude
Komentarz P ro g ram ten pokazuje, jak p rzy k ład o w o ra d z ić sobie z trzem a sytuacjam i, g d y n astęp u je błąd pracy stru m ien ia. Te trzy sy tu acje o d d zielo n e są d u ż y m kom en tarzem . T ekst p ro g ram u obficie opatrzy łem k o m e n ta rz a m i, d la te g o teraz opiszę g o lakonicznie:
1123
Rozdział. 22. O peracje W ejścia/W yjścia Przykład program u pracującego n a plikach
O O tw arcie pliku. Z a p ie rw szy m razem na p y tan ie o n a z w ę plik u o d p o w ia d a m y , że ch ce m y otw orzyć plik n i c .nic (a plik taki n ie istnieje). © Strumień wchodzi w stan błędu, ale m y ten błąd u s u w a m y ipon a w i a m y próbę. Z a drugim razem podajemy nazwę pliku p l a g i .cpp, która to n a zwa jest nazwą tekstu źródłowego tego programu - a więc pliku, który na p e w n o istnieje.
© Mając o tw a rty plik pozycjonujem y kursor czytania. O pozycjonowaniu pomówimy parę stron dalej (str. 1132). Mam nadzieję, że korzystając z tego "gotowca " - jako "wtajemniczony " - przeczytałeś już
choć raz ten rozdział. Ponieważ jestem przewrotny każę pozycjonować daleko za końcem pliku. To oczywiście przy próbie czytania © - wywołuje natychmiast błąd.
© [ tym ra z e m się nie przejm ujem y, tylko k asu jem y flagę błędu. T aki b łąd sy g n alizo w a n y jest p rzez u staw ien ie dw óch flag: f a i l b i t i e o fb i t , w ięc obie trzeba skasow ać. Z au w aż zn o w u O R -ow anie flag p o łączo n e z negacją ~ strum.e l e a r (strum.rdstate() & ~(ios::eofbit
I ios::failbit));
Z a d ru g im razem pozycjonujem y sensow nie. S tru m ie ń ju ż jest w p o p ra w n y m stanie, w ięc próba w czytania bajtu p o w o d zi się. D la p ew n o ści po zy cjo n u jem y gdzieś n a początku pliku - i w id zisz na ek ran ie, że w czy ta n a zostaje litera z w n ę trz a słow a „#include" (rozpoczynającego treść p liku). © P róbuję w czytać n astęp n e bajty z pliku jako liczbę. C zy tan ie oczyw iście o d b y w a się z m iejsca, na którym p o p rzed n io skończyliśm y, a tu , jak p a m ię ta m y - w śro d k u słow a "#include" - nie m a żadnej liczby. W y w o łu je to w ięc b łąd. G dy flagę b łę d u skasujem y, m ożem y spróbow ać p rz e c z y ta ć te bajty, ju ż nie jako liczbę, a jako tekst. W czytanie się p o w odzi — w id z im y na ek ran ie d a lsz ą (nie w czy ta n ą jeszcze) część sło w a i n c l u d e .
......mmmmm........... ....... ■i.........
22.16 P rzy k ła d p ro g ram u p ra c u ją c e g o na p lik a c h Z o b aczymy teraz program, który kopiuje treść jednego pliku do drugiego. Tytułem dygresji dodam, że nie jest to tylko program wymyślony dla ilustracji tego rozdziału. Program ten kilkakrotnie ratował mnie z kłopotów. #include using namespace std; #include
J z * * i* * f* * * * * i* ? * * * * * * * ********************* ****************** int m a i n ()
{
string plik_a; string plik_b;
U ---------cout << "Podaj nazwę pliku wejściowego : cin >> plik_a; ifstream czyt(plik_a.c_str() );
// o
1124
Rozdział. 22. O peracje W ejścia/W yjścia Przykład program u pracującego na plikach i f (!czyt) { . cout << "Nie mogę otworzyć takiego pliku return 1;
)
//--------------------------------------------------------------------------cout << "Podaj nazwę pliku wyjściowego : cin >> plik_b; ofstream pisz(plik_b.c_str()) ; i f (!pisz)
// 0
{ cout << "Nie mogę otworzyć takiego pliku return 1;
} / / ---- akcja przepisywania----------------------------------char c; while(czyt.get(c))
{ i f (!pisz.put (c) )
// © // O
{ cout « break;
"Blad pisania ! \n";
} ) / / ------- koniec, sprawdzam powód zakończenia--------i f (!czyt.e o f () )
// ©
{ cout «
"Blad czytania\n";
/*****************■************************»***********■********/
Komentarz O Definicja obiektu k lasy strum ień w ejściow y ifstream. S tru m ien io w i temu nadajem y n azw ę czyt. W idzim y tu od ra z u w y w ołanie k o n stru k to ra otw iera jącego plik o p o d an ej nazw ie. Jest to je d y n y a rg u m e n t k o n stru k to ra — d ru g i ar g u m e n t n ie m u si b y ć o b ecn y , bo d la s tru m ie n ia k la s y i f s t r e a m jest dom n iem an ie, że d ru g i arg u m en t ma w arto ść ios: : in. 0 Definicja obiektu klasy „strum ień w yjściow y" ofstream. S tru m ień płynie do pliku o nazw ie p o d an ej jako pierw szy a rg u m e n t k o n stru k to ra.
Ponieważ nazwa ta jest przechowywana w obiekcie klasy s t r i n g , a funkcja open oczekuje jednak C-stringu, dlatego posługujemy się funkcją składową c _ s t r ( ) , która treść obiektu klasy s t r i n g udostępnia w postaci C-stringu. D rugi a rg u m e n t jest do m n iem an y , a d o m n ie m a n ie to d la stru m ien ia klasy o f s t r e a m brzm i ios : : out. © P rzeczy tan ie jednego bajtu zestru m ien ia w ejściow ego. O d ra z u sp raw d zan a jest p o p raw n o ść operacji czytania. G dyby c z y tan ie nie p o w io d ło się, to w artością w y rażen ia w n aw iasie byłoby zero (NULL) i pętla while b y ła b y przerw ana.
Rozdział. 22. O peracje W ejścia/W yjścia Przykład program u pracującego n a plikach
1125
O Z apis p rz ec zy tan e g o bajtu do zb io ru w yjściow ego z e sp raw d zen iem p o p ra w ności p ra c y stru m ien ia w yjściow ego p i s z . © Za je d y n y p o p ra w n y sp o só b w yjścia z pow yższej p ętli w h i l e u zn ajem y u s ta w ienie flagi i o s : : e o f b i t , czyli napotkanie końca czy tan eg o pliku. Jeśli zaś p o w ó d jest in n y - to o g łaszam y błąd.
Jak w id ać, jest to zw ykły p ro g ram kopiujący treść p lik u . M ów iłem jed n ak , że w p ro g ra m ie jest coś szczególnego, co w ybaw iło m n ie ju ż kilk ak ro tn ie z kło p o tó w . N a p ie rw sz y rz u t oka jed n ak teg o nie widać. J e ś l i j e s t e ś c i e k a w , t o p o s ł u c h a j t e j historii.
P rzy tac zam ją dlatego, że m ożliw e, iż przytrafi się ta k ż e Tobie. Jeśli p o słu g iw ałeś się kiedyś program em u ru ch o m ien io w y m (d eb u g g erem ), to w iesz, ż e dzięki niem u m ożesz w ykonyw ać p ro g ra m krok o w o - linijka za linijką. D eb u g g er w ykonuje p ro g ram krokam i, a ż e b y śm y w idzieli, g d zie w d a nym m o m en cie je s t- u ż y w a tekstu źródłow ego n a s z e g o p ro g ram u . W y św ietla na ek ra n ie określony frag m en t i za pomocą strzałk i u m ieszczo n ej p rzy danej linijce (lu b podśw ietlenia tej linijki) oznajmia: „tu w ła śn ie pracuję". O tóż, p o słu g u jąc się d eb u g g erem zauw ażyłem , że c z a se m d eb u g g er p o k azy w ał b łę d n ie - strzałka była p rzy innej linijce niż p o w in n a, najczęściej p rz y sąsiedniej. Jeśli ta sąsied n ia linijka zaw ierała tylko k o m en tarz, to łatw o było ustalić, że d e b u g g e r oszukuje, bo p rzecież nie m oże tam nic w y k o n y w ać. (N ie zaw sze jednak sytuacja jest tak jasna). D ługo szu k ałem przyczyn takiego zachow ania d e b u g g e ra , w reszcie d o szed łem d o w n io sk u , że d eb u g g er jest zd ezo rien to w an y p rz e z n ie d o p aso w an ie zn ak ó w ' \ r ' i ' \ n '. Znaki te kończą k ażd ą linijkę tekstu p ro g ram u . T eoretycznie zaw sze na końcu linijki p o w in n y być oba te zn ak i - czasem jed n ak w pew n y ch niew yjaśnionych dla m nie okolicznościach - z g u b io n y był jed en z nich. Nie każd y e d y to r pokazuje taki tekst błędnie - d la teg o w szy stk o w e d y to rz e w y g ląd ało niew innie. T ym czasem d ebugger chcąc pow iedzieć mi, że w y k o n u je w łaśn ie linijkę 229 odliczał 229 p ar znaków • \ r ' i ' \ n ' i w tekście ź ró d ło w y m m ojego p ro g ram u p o k azy w ał, że tę w łaśnie linijkę w ykonuje. Jeśli z jakichś p o w o d ó w zam iast pary ' \ r ' i ' \ n ' był tylko sam zn ak ' \ n ' to d e b u g g e r był z d ez o rien to w an y i gubił się w odliczaniu (o w łaśn ie tę linijkę). Z m u siło m nie to d o napisania program u: „K o rek to r". P rogram ten w czytuje plik i robi jego kopię przy okazji napraw iając e w e n tu a ln e braki. Jego tajem nica polega na tym , że oba pliki o tw ieran e są w trybie tek sto w y m .
20)
Zresztą także każdego innego pliku tekstowego w systemie operacyjnym MS-DOS.
1126
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie, a technika rzucania wyjątków O zn acza to, że przy w czytyw aniu treści pliku k ażd a sekwencja ' \ r ' i ' \ n ' zostaj p rzez stru m ień zam ien io n a na jed en znak ' \ n ', a stru m ień zapisujący d o pliku w yjściow ego zam ie n ia w szystkie zn ak i ' \ n ' n a p a ry ' \ r ' i ' \ n ' . D ebugger p racując z takim sk o ry g o w an y m plikiem źró d ło w y m ju ż się nie mylił. Tego sam ego efek tu napraw ienia n ie osiągnąłbym za p o m o cą kom endy system u operacyjnego (np. ko m en d a copy). To d latego, ż e tak a kom enda kopiuje pliki b in arn ie. Robi się w ięc id en ty czn ą kopię. R azem z tkw iącym tam błędem .
" ''' ' .....'
'
111111111
' I11---ll— M III|
2 2 .1 7 S tru m ie n ie , a te c h n ik a rzu c a n ia w y ją tk ó w W tym p arag rafie rozm aw iać b ęd ziem y to tym , jak w p rz y p a d k u strum ieni w e /w y m ożna p o słu ży ć się techniką zw an ą o b słu g ą s y tu a c ji w y jątk o w y ch , je st to technika w p ro w ad z o n a d o języka C++ sto su n k o w o niedaw no. Szczegółow o o p isu je ją książka pt. „P asja C ++". Zatem ten p a ra g ra f p rzezn a czony jest dla b a rd zo , bardzo w tajem niczonych pasjonatów .
X N ow e w ersje biblioteki i o s t r e a m p o zw alają nam w y razić życzen ie, by dany stru m ień - w p rz y p a d k u jakiegoś n iep o w o d zen ia - o p ró cz u staw ien ia flagi błędu, d o d atk o w o sy g n alizo w ał sytuację w yjątkow ą (rzucił w yjątek). Rzucony w ów czas zostaje o b iek t specjalnej k lasy zw an ej i o s b a s e : : f a i l u r e 21. Klasa ta jest klasą p o ch o d n ą od klasy s t d : : e x c e p t i o n . J e d n ą z d an y ch skła dow ych tej klasy jest obiekt klasy s t r i n g o nazw ie m es s a g ę 22. Treść tego skład n ik a m e s s a g e m o że poin fo rm o w ać nas o p o w o d ach rz u c e n ia w yjątku. A by posłużyć się tą techniką rzucania w y jątk u w sytuacji, g d y w p racy stru m ie nia n astąp i jakiś b łą d - m am y d o d y sp o zy cji następujące fu n k cje składow e strum ienia: Funkcja
void exceptions (io_state
f la g ijn a ja c e _ p o w o d o w a ć _ w y jq te k )
;
W yw ołując tę funkcję decydujem y: których flag u staw ien ie - m a p o w o d o w ać rzu cen ie w yjątku k la sy i o s : : f a i l u r e . Jeśli na p rzy k ła d fu n k cję tę w ołam y tak:
strumień. e x c e p t i o n s ( io s : : f a i l b i t 21 ) 22 )
ang. failure- niepowodzenie, fczytaj: „fejler"]. ang. message - wiadomość [czytaj: „mesydż").
I io s ::e o fb it)
;
Hpseudokod
1127
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie, a technika rzucania wyjątków
to z n a c z y , że chcem y, aby w y jątek został rzu co n y w p rz y p a d k u u staw ien ia flagi i o s : : f a i l b i t lu b flagi i o s : : e o f b i t . C iekaw o stk a: Wyjątek zostanie rzucony nie tylko wtedy, gdy nastąpi sytuacja błędu i flaga ustawi się automatycznie. Także wtedy, gdy my sami ustawimy tę flagę, (choćby funkcją s e t s t a te). To dobra wiadomość, bo to znaczy, że możemy sami zdecydować, co jest błędem, a co nie. (Pamiętasz jeszcze nasz program, w którym decydowaliś my, że błędne są wyrazy rozpoczynające się od litery innej niż 'e'?). Funkcja s k ła d o w a
ios_state
exceptions() const;
S łuży d o teg o , by d o w ied zieć się o bieżąco o b o w iązu jący w y b ó r (d o k o n an y funkcją o m ó w io n ą pow yżej). T o znaczy obecna funkcja, jako rezultat, zw raca sło w o ty p u i o s t a t e zaw ierające inform ację o ty m , które flagi b łędu m ają e w e n tu a ln ie p o w o d o w a ć rzu ce n ie w yjątku. Z o b a c z m y i l u s t r u j ą c y to krótki p r o g r a m :
# in c lu d e < io s tre a m > u s in g n am esp ace s t d ; t i n c l u d e < fs tre a m > iin c lu d e < s trin g > v o id c z y t a j _ 4 0 _ l i c z b _ i n t ( i f s t r e a m v o id b i e z a c e _ u s t a w i e n i e ( i f s t r e a m //*★ ★ ★ *****★ ★ **★ ★ **★ ★ **★ *★ ★ ★ ★ ★ ★ **★ i n t m a in ( ) ( i f s t r e a m s tru m ; s t r i n g n a z w a _ p lik u ( " t . tm p " ) ;
& s); & s); ★ ★ ★ **■*•*★ *★ ★ ★ ★ ********★ ★ ★ *★ *★
.,
// definicja strumienia do pracy z plikiem
s tr u m .o p e n ( n a z w a _ p lik u . c _ s t r ( ) , i o s : : i n ) ;
// O
// czy się udało ? i f ( ! stru m ) // czyli inaczej: s tru m . f a i l () { cout << "Blad otwarcia pliku: " « n a z w a _ p lik u << e n d l; r e tu r n -1 ; 1 bieżące ustawienie(strum);
//
©
cout << "Zmiana powodow rzucenia wyjątku" << endl; strum.exceptions(ios::failbit | ios::badbit) ;
//
©
biezace_ustawienie(strum);
// O
try ( }
czytaj 40 liczb_int(strum) ;
~
'
//
©
1128
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie, a technika rzucania wyjątków catch(ios::failure const & kapsuła)
{ cout << "^ie udało sie bo: " « kapsuła.what() « endl;
// ©
) )
Z/*****************************.*****************.*************** void czytaj_40_liczb int(ifstream & s)
i for(int i = 0; i < 40; i++)
{ int wartość; s >> wartość; cout « "I" «
// © wartość «
")";
> )
Z/************************************************************* void bieżące ustawienie(ifstream &s) // ©
{ cout «
"Wyjątek bedzie rzucony,
ios: :io_state
rzuc_gdy = s .exceptions();
cout << "ios::failbit - "; if(rzuc_gdy i ios::failbit) cout « "TAK\n"; else cout << "nie\n"; cout << "ios::eofbit - "; if(rzuc_gdy s ios::eofbit) cout << "TAK\n"; else cout << "nie\n"; cout << "ios::badbit - "; if(rzuc_gdy & ios::badbit) cout << "TAK\n"; else cout << "nie\n";
I
gdy ustawione sa flagi:\n"; // ©
1129
Rozdział. 22. O peracje W ejścia/W yjścia Strum ienie, a technika rzucania wyjątków ios::eofbit - nie ios::badbit - TAK (1) (2) (3) (4) (20)(45) (55)Nie udało sie bo:
ios:rfailbit set
Ciekawsze miejsca tego programu O O tw ie ra m y plik d y sk o w y o n azw ie „t.tm p". Jeśli by takiego nie było, to co p ra w d a u staw io n a zostanie flag a i o s : : f a i l b i t , ale tu jeszcze n asze w yjątki nie zo stały w ynajęte d o pracy, w ięc następuje z w y k łe sp raw d zen ie if ( !strum) ...
Jeśli o tw arcie pliku się nie u d a ło (bo na p rzy k ład tak i plik nie istnieje), to p ro g ra m koń czy m y . U nas się u d a je , bo plik taki istnieje, a w d o d atk u jest w nim p o k a z a n a pow yżej treść. © N a p o c z ą tk u , dla orientacji, d o w iad u jem y się o b ie żący w ybór k a n d y d a tó w do rzu ca n ia w y jątk u . Służy n am d o tego funkcja o n a z w ie b i e ż ą c e _ u s t a w i e n ie .
O O to jej definicja. Jej a rg u m e n tem jest strum ień, k tó ry m a ona zbadać. W ciele tej funkcji w id zim y w y w ołanie na rzecz tego s tru m ie n ia funkcji e x c e t p i o n s . R ezu ltat je st zap isan y w obiekcie o nazw ie r z u c _ g d y . N a stę p n e instrukcje p o p ro stu so n d u ją, które z flag w ty m słow ie r z u c _ g d y są u staw io n e, po czym w y p isu ją sw oje obserw acje n a ek ran ie. Spójrz na ek ran . N a p oczątk u p ro g ram u - żad n a z flag nie m a rzu cać w y jątk u . © W róćm y d o funkcji m ain . O to instrukcja, w której inform ujem y stru m ie ń , że ży czy m y sobie, aby w p rz y p a d k u ustaw ien ia flagi i o s : r f a i l b i t lu b flagi i o s : : b a d b i t - stru m ień rzu cił w y jątek . © P o n o w n ie w yw ołujem y tu funkcję b i e ż a c e _ u s t a w i e n i e , by p rzek o n ać się czy stru m ie ń to życzenie p rzy jął d o w iadom ości. (P o ró w n aj teraz w y p isan y na ekran ie tekst). © O to w y w o łan ie funkcji c zy t a j _ 4 0_1 i c z b _ i n t , k tó re zn ajd u je się już w bloku t r y , czyli w bloku, z którego m o g ą być rzu ca n e w y jątk i. © G d y sp o jrzy sz na definicję tej funkcji, to zo b a c z y sz , że nie m a w niej nic niezw ykłego. Po prostu pętla f o r , w której w n ę trz u z a pom ocą o p erato ra » w czytuje się liczby typu i n t . G dzie w tej funkcji jest rzu cen ie w yjątku? N igd zie, to już zrobi za nas sam stru mień. Jeśli nie u d a m u się w czy tan ie jednej z liczb i n t , rzuci w yjątkiem k lasy i o s : : f a i l u r e . Spójrz na treść pliku „t.tm p ". Będzie, czy nie b ę d z ie p o w o d u d o rzu cen ia w yjątku? O czyw iście, że będzie. W śród tych liczb jest jed n a liczb a d o u b l e (55.2). P onie w aż stru m ień będzie n astaw io n y na czy tan ie liczb ty p u i n t , w ięc p rzeczy ta cześć całkow itą (55), a k ro p k a p rzerw ie d alsze w c z y ty w a n ie tej liczby. (To jeszcze nie pow oduje błędu!)
1130
Rozdział. 22. O peracje W ejścia/W yjścia W ybór miejsca czytania lub pisania w pliku Poniew aż teraz funkcja daje polecenie czytania następnej liczby int, stru m ie ń zacznie czy tan ie i od razu natknie się na kropkę, k tó ra nie m o że p rzecież rozpoczynać żad n ej liczby typu int. W tedy to stru m ień
*1* l.u sta w i flagę ios : :fail, ♦> 2.p rzy g o tu je obiekt klasy i os: :failure (czyli z a p isz e w jego sk ład n i ku m e s s a g e opis pow odu rzucenia w yjątku), ♦> 3.w yjątek ten rzuci. @ Po bloku t r y um ieszcza się zw y k le blok catch, k tó ry m a łap ać rzu co n e wyjątki. Biblioteka iostream rzu ca n am w yjątkiem b ęd ąc y m obiektem klasy i o s : : failure. O dbieram y go tu p rzez referencję. W b lo k u c a t c h m o żem y zapytać ten o b iek t (funkcją wha t )jaki był pow ód jego rz u c e n ia (to znaczy: k tó ra z flag go sp o w odow ała). Jego o d p o w ie d ź w idzisz na ek ran ie. Dla ciekaw ości: funkcja wha t jest wirtualną funkcją składową klasy i o s : : f a i l u r e (odziedziczoną po klasie e x c e p tio n ) . virtual const char*
failure: :what () ;
W N ie n a d u ż y w a ć m e c h a n i z m u r z u c a n ia w y jątk ó w
Z obaczyliśm y w ty m paragrafie jak, zam iast s p ra w d z a n ia flag stan u stru m ie nia, m ożna sk o rzy stać z m echanizm u rzu can ia w y jątk ó w . Jak w idzisz, jest i tak a m ożliw ość, ale p am iętasz chyba z a s a d ę , że: mechanizm rzucania wyjątków pom yślany został dla sytuacji rzeczywiście wyjątkowych.
P o w iedzm y sobie otw arcie: n ap o tk an ie zn ak u końca plik u EO F nie jest przecież katastrofą, w ięc w tak im p rz y p a d k u p o słu g iw an ie się te c h n ik ą rzucania w yjąt ku, jest chyba p rz e sa d ą . O czyw iście to Ty sa m zdecydujesz, pam iętaj jed n ak , że s k o ro m echanizm ten w y m yślony zo stał d la sytuacji bard ziej drasty czn y ch , w ięc n ie jest optym alizo w an y p o d w z g lę d e m efektyw ności czy prędkości.
2 2 .1 8 W y b ó r m ie js c a c z y ta n ia lu b p is a n ia w p lik u Pisanie z n a k ó w (czy ogólniej bajtów ) d o pliku, to nie je st w rzu can ie ich d o d u żeg o w o rk a o określonej nazw ie. Jest to bow iem „p lik ". O k reślen ie plik su g e ruje, że inform acja je st tam u m ieszczo n a w jakiejś kolejności. M uszę wyznać ze skruchą, że sam często posługuję się starszym określe niem „zbiór"-wiedząc jednak, że nie jest todobre tłumaczenie angielskiego „file".
Rozdział. 22. O peracje W ejścia/W yjścia W ybór miejsca czytania lub pisania w pliku
1131
Z ap is d o p lik u n astępuje z a w s z e w jakieś o k reślo n e miejsce. P isząc tę k siążk ę stosuję z a s a d ę , że kolejne litery um ieszczam na sa m y m końcu tego, co n a p isa łem d o tej pory. M am w ięc ja k b y w głow ie jakiś w sk a źn ik , który m ów i mi: tu należy p o staw ić e w en tu a ln ą n astęp n ą literę. P o d o b n ie Ty - czytelniku - czytając tę książkę m asz jakby p ew ien w sk aźn ik , k tóry m ó w i: „tu w łaśn ie czy tam ". Jeżeli z a d z w o n i d zw o n ek d o d rzw i, to z a z n a c z a s z to miejsce z a k ła d k ą , by w iedzieć, g d z ie zacząć czytac p o tem . W sk aźn ik ten, w trakcie czy tan ia, cały czas się zm ien ia. Ten w sk aźn ik , to po pro stu n u m e r litery w książce. O p isan a sytuacja nie jest je d n a k jedynym sp o so b em czytan ia. Jeśli w eźm iesz do ręki en cy k lo p ed ię w celu w y jaśn ien ia słow a „ M ed y ce u sz" - to nie będ ziesz czytał en cy k lo p ed ii od p ierw szej strony z literą A aż do m o m en tu , g d y n a tk n ie sz się na to hasło. N ajp ierw o d szu k asz so b ie to miejsce, g d zie jest in teresu jąca Cię inform acja. Inaczej m ów iąc ok reślisz pozycję w sk aźn ik a, g d zie zacząć czytać. Po p rzeczy tan iu w yjaśnienia tego h asła m oże zn ajd ziesz o d s y łacz n a h a sło „ r e n e s a n s " - z a te m znow u pozycjonujesz w skaźnik (p rzew racasz strony ) i zaczy n asz czytać w łaściw ą rzecz. N ie p o trz e b u ję chyba w yjaśniać, że tak sam o jest w p rzy p ad k u p isania. N ie m u szę k o niecznie ciągle coś dopisyw ać na końcu tekstu. M ogę p rzecież w śro d k u te k stu , w miejscu jak ieg o ś w yrazu, n ap isać inny. S tru m ien ie pracujące na plik ach są tak zd efin io w an e, by u m o żliw iać n am p o d o b n y sposób p racy z inform acją zapisaną w plik u . W z w ią z k u z tym, stru m ien ie m ają także an alo g iczn e w skaźniki. Jeśli stru m ień jest w ejścio w y , to m a taki w sk aźn ik do czytania. C iekaw e, że w sk aźn ik taki p o k azu je nie tyle na sam ą literę, co na miejsce m ię d z y dw o m a literam i - ta „z lew ej" to ostatnio p rzeczy tan a, a tę „z p raw ej" z a ra z będę czytał. S tru m ień w yjściow y ma p o d o b n y w skaźnik, ale d la pisania. S trum ień, k tóry m oże z a p e w n ia ć i pisanie i czy tan ie - ma oba tak ie w sk a źn ik i - i są o n e nieza leżne. M o żn a przecież w jed n y m miejscu pliku czytać i zap isy w ać coś w innym . W skaźnik c z y ta n ia - w skaźnik
get
Jeśli o tw ie ra m y plik do czy tan ia, to w skaźnik ten zn ajd u je się na początku p lik u . Tak jak n o w ą książkę zaczy n a się zw ykle czytać o d początku. W sk aźn ik ten jednak m o ż n a natychm iast p rzesu n ąć w w y b ran e miejsce. W skaźnik d o p isa n ia - w sk aźn ik
put
Jeśli o tw ie ra m y plik do d o p isy w an ia (tryb ios : : app) to w skaźnik te n p o k a z u je na k o n iec pliku. D o p isy w an ie będzie o d byw ało się d o końca p lik u . W sk aźn ik i d o pokazyw ania w ew n ątrz plików są ty p u pos_type
T yp ten zd efin io w an y jest w klasach istream i ostream. Z w ykle jest to, po p rostu , ty p unsigned long. W skaźnik taki określa n u m e r znaku (bajtu), k tóry czytasz. N a p rzy k ład - w czytałeś już 58 literę.
1132
Rozdział. 22. O peracje W ejścia/W yjścia W ybór miejsca czytania lub pisania w pliku W sto su n k u d o takich w skaźników w ykonujem y zw y k le d w a typy operacji:
22.18.1
•
- ustalenie, g d zie w skaźnik obecnie p o k azu je,
•
- ustaw ienie go na żądaną pozycję.
Funkcje składow e informujące o pozycji w skaźników A by d o w ied zieć się o bieżącą pozycję w skaźników w s tru m ie n iu , posługujem y się funkcjam i składow ym i: pos_type
tellg();
//-fułikcjn składowa kłosy ifstream
pos_type
tellpO;
//-funkcjo składowa kłosy ofstream
♦t* funkcja t e l l g - co jest z a p e w n e skrótem od „tell get" - pow iedz [sk ąd l b ra n e - czyli pow iedz, g d z ie pokazuje w skaźnik do czytania,
*X* fu n k cja t e l l p - analogicznie: tell put - p o w ied z [gdzie] w staw iane H czyli p o w ied z, gdzie p o k azu je w skaźnik pisania. P oniew aż klasa f s t r e a m jest p o ch o d n ą obu pow yższych, d lateg o obie funkcje są w niej o d zied ziczo n e. Zatem w obec strum ienia k lasy f s t r e a m - czyli piszącego i czytającego - m ożna u ż y w a ć obu tych funkcji. P rzy k ład o w o , p o zapisaniu czegoś w pliku za pom ocą s tru m ie n ia w yjściow ego, m ożem y p o z n a ć bieżącą pozycję w sk aźn ik a p u t: ofstream strura("wiersz.tmp", ios::app); strum « "Zapytajcie Artura, słowo daje nie kłamie \n"; cout << "Pozycja wskaźnika = " << (strum.tellp () );
22.18.2
Wybrane funkcje składow e do pozycjonow ania w sk aźn ik ów istream & seekg(off_type, seek_dir = ios::beg); ostream & seekp(off_type, seekdir = ios::beg); Pierw sza z nich pozycjonuje w sk a źn ik czytania (get), d ru g a w sk aźn ik p isania (put).
A r g u m e n t y ty ch funkcji:
❖ P ierw szym a rg u m e n t jest jest typu o f f _ t y p e . Pod tą n azw ą k ry je się jakiś typ z d e fin io w a n y in strukcją t y p e d e f . Co to jest n a p r a w d ę - z a le ż y to od konkretnej im p lem entacji biblioteki, ale najczęściej jest to po p ro stu in n a nazw a typu l o n g . Jeśli w T w oim k o m p u terze o d p o w ia d a on typow i l o n g , to deklaracje tych funkcji sk ła d o w y c h m ożem y n ap isać prościej: istream & ostream &
seekg (long k tó ry, seekp (long k tó ry,
seek_dir w zględem = ios::beg); seek_dir w zględem = ios::beg);
T ym p ierw szy m a rg u m e n te m m a m y szan se o k reślić, na który b ajt p lik u m a o d tą d p o k a z y w a ć w skaźnik.
1133
Rozdział. 22. O peracje W ejścia/W yjścia W ybór miejsca czytania lub pisania w pliku
❖
Z a łó ż m y , że np. na 34 bajt pliku. D ru g i a r g u m e n t to ty p s e e k _ d i r , (z d e fin io w a n y w k lasie i o s _ b a s e ) . W y o b raź sobie go jako ty p w yliczeniow y, k tó ry m o że p rzy jm o w ać n astęp u jące w artości: enum s e e k _ d i r beg,
/ / sk ró t o d
cur, end
/I skrót od c u r r e n t - bieżący // skrót od en d - koniec
begin
- p o c zą te k
}; Te trz y w artości tego ty p u i o s : : b e g , i o s : : e n d , i o s : : c u r słu żą n a m do o k reślen ia p u n k tu odniesienia dla pozycjonow ania. D zięki nim m ożem y w yrazić czy ch o d zi n am o u staw ien ie w sk a ź nika na np. bajt 34 licząc: - od początku pliku, - od końca pliku, - czy też m oże od bieżącej pozycji w sk aźn ik a. Bieżącą pozycję w sk aźn ik a do czytania / (lub p isan ia), m ożem y zaw sze p rz y p o m n ieć sobie w yw ołując funkcję t e l l g / (lub t e l l p ) . Z d ek laracji funkcji s e e kp, s e e kg w idać, że p rzez d o m n iem an ie zak ład ają one p o zy cjo n o w an ie w zg lęd e m p o czątk u pliku. Nie k a ż d e p o z y c jo n o w a n i e m a s e n s
N ie jest d o zw o lo n e pozycjonow anie w sk aźn ik a p is a n ia /c z y ta n ia g d zieś PRZED p o czątk iem pliku. N o bo, co by to m iało o zn aczać? Jeśli m im o w szy stk o spróbujesz takiego p o zy cjo n o w an ia - w stru m ien iu u s ta w io n a zo stan ę flaga f a i l b i t . A czy je st d o z w o l o n e p o z y c jo n o w a n ie ZA k o ń c e m pliku?
Jeśli m a m y w sk aźn ik d o pisania, to - uw aga - m o żem y g o pozycjonow ać n aw et g d z ie ś za obecnym końcem pliku. To zn ac zy jeśli plik ma 400 bajtów , a m y ustaw im y w sk a źn ik pisania na bajcie nr 555, to nie m a p roblem u. Jeśli te ra z zapiszem y tam jak iś bajt (na p rz y k ła d bajt V ) , to plik się p o w ięk szy w taki sposób, że: •
bajty od 401 do 554 zostaną zap e łn io n e m d/am i (bajt zero ),
•
po nich, jako ten n r 555 będzie nasz b ajt 'x'.
Jeśli c h o d zi o w skaźnik do czytania, to tak że m o żesz pozycjonow ać g o za ł końcem plik u i sam ten fafkt nie w yw oła błędu. D o p iero jeśli sp ró b u jesz coś sta m tą d p rzeczytać - to oczyw iście błąd nastąpi. O to przykład:
in c lu d e < io stre a m > u s in g nam espace s t d ; # in c lu d e < fstre a m > # in c lu d e < s tr in g >
1134
Rozdział. 22. O peracje W ejścia/W yjścia W ybór miejsca czytania lub pisania w pliku Z************************************************************^ int main()
{ string tekst =
// O tw ie r a m
"Coz, według Ben-Alego, \n" "czarnomistrza Krakowa\n" "nie jest to nic wielkiego\n" "dorożkę zaczarować." ;
istn ie ją c y p lik i go ze ru ję , lu b je ś li g o nie ma - tw o r z ę g o
fstream strum("wiersz.tmp", ios::trunc | ios::in | ios::out); i f (!strum) cout << "Blad otwarcia pliku " << endl; return - 1 ;
) strum << tekst ;
// p o z y c jo n o w a n ie
//Z a p isa liśm y te n c a ły tek st d o p lik u ,
ku rsora pisania na lite rze 2 5
strum.seekp(25); strum « "ABCDE";
// p o z y c jo n o w a n ie k u rsora
pisania na zn a k u 6 o d końca
strum.seekp(-6 , ios::end); strum « "X";
□
W rezultacie plik wiersz.tmp zawiera następującą treść Coz, według Ben-Alego, cABCDEmistrza Krakowa nie jest to nic wielkiego dorożkę zaczaXowac.
Zamiast komentarza - uwaga ideologiczna Z w ró ć u w a g ę na b ard zo w ażną rzecz. J e ś li w p is u je m y co ś d o pliku, w k tó ry m ja k a ś z a w a rto ś ć ju ż je s t, a w p is u je m y n ie n a k o ń c u , a le w ja k ie ś m ie js c e , k tó re p o k a z u je m y w s k a ź n ik ie m
do
p is a n ia , to te n n o w y w ła ś n ie w p is y w a n y te k s t, (ta k ja k u n a s z n a k i A B C D E ) n ie "ro z s u w a ", n ie "ro z p y c h a " d o ty c h c z a s o w e j z a w a rto ś c i. T e n o w e z n a k i po p ro s tu z a s tę p u ją z n a k i is tn ie ją c e ta m d o te j p o ry.
W idać to jak o efekt pracy n aszeg o ostatn ieg o p ro g ra m u : litery ABCDE p o pro stu za stą p iły (zatarły) p o p rzed n io b ęd ące tam litery w ie rsz a .
Jeśli masz jakieś wątpliwości, to przywołajmy taką analogię: Plik to jakby k aseta m ag n eto fo n o w a, na której w czoraj n a g ra łe ś 15 piosenek, jeśli teraz p rz e w in ie sz ją tak, by b y ła w pozycji p o czą tk u p io sen k i n u m er 6, i zaczniesz w ty m miejscu n ag ry w ać n ow ą piosenkę, to ty m sam y m zacierasz, (kasujesz) tę p io sen k ę, k tóra tam b y ła d o tej pory. G d y s k o ń c z y s z n ag ry w an ie tej now ej pio sen k i, to:
Rozdział. 22. O peracje W ejścia/W yjścia W ybór miejsca czytania lub pisania w pliku ♦♦♦ jeśli będzie o n a k ró tsza od tej starej - za ko ń cem tej now ej u sły szy sz jeszcze o statn ie se k u n d y tej starej; ♦♦♦ jeśli zaś ta n o w a p io sen k a będzie b ard zo d łu g a , to m oże się o k azać, że nie tylko zatarłeś s ta rą piosenkę nr 6, ale ta k ż e i nr 7, a m o ż e n aw et p o ło w ę pio sen k i n r 8. R o zu m iesz już? Jeśli tak, to pow iedz jak p o stąp ić, gdy się okaże, iż n ie n aw id zisz p io sen k i nr 4, w ięc chciałbyś ją u su n ąć z p lik u (taśm y) - w ten sposób, by na tej ta śm ie po p io sen ce nr 3 od razu n astęp o w ała piosenka n r 5? O d p o w ie d ź : N ie da się. Jeśli chcesz to zrobić, to m u sisz p rz y g o to w a ć d ru g ą k asetę, najpierw p rzeg rać na nią piosenki 1-3, n astęp n ie u staw ić p ie rw o tn ą taśm ę na pozycji początku piosenki nr 5 i k o n ty n u o w a ć p rz e g ry w a n ie aż do sa m e g o końca (przegrają się w ięc teraz piosenki n r 5-15). W rezu ltacie będziesz m iał d w ie kasety. • Pierw sza "stara" kaseta z 15 p io sen k am i, w tym ta p io se n k a nr 4, której nie lubisz. • D ruga "now a" kaseta będzie m iała tylko 14 piosenek, b o - p r z y p rzeg ry w an iu - jedną z nich (n ielu b ian ą) opuściłeś. P o d o b n ie jest z plikam i d y sk o w y m i. W zasad z ie nie m ożna ze ś ro d k a pliku d y sk o w e g o coś skasow ać tak, żeby po tym nie z o s ta ło n aw et w olne m iejsce. W takiej sytuacji najprościej w y p ro d u k o w ać n o w y p lik , a d o niego p rz e p isa ć ze stareg o to, co chcesz. P otem ten stary kasujesz, a te m u now em u n a d a je sz taką n a z w ę jak m iał ten stary. Jest jeszcze inne wyjście, ale d o ść żm u d n e. W racając d o naszej m etafo ry - przepisujesz p io s e n k ę z tej samej ta śm y na tę sam ą "nuta po nucie". To z n a c z y u staw iasz ♦♦♦ w skaźn ik do czytania w miejscu początku p io sen k i n r 5, ♦♦♦ a w skaźnik pisania w miejscu początku n ielu b ian ej piosenki n r 4. Z ac zy n asz przepisyw anie: char z; do { z = czytaj_znak(); zapisz znak(z); J w h i l e (sil Jeszcze z n a k i ) ;
W sk aźn ik i czytania i p isan ia oczyw iście sam e się przesuw ają po k ażd y m zap isie i odczycie, w ięc p o w y ższa pętla przep isze w s z y s tk i e dalsze z n a k i aż do końca p lik u , o pew ien o d stę p d o przodu. ( O d s t ę p r ó w n y d ł u g o ś c i n ie lu b ia n e j p i o s e n k i ) .
Tę pętlę, to przepisyw anie, m u sisz niestety z o rg an izo w ać sam . W racając d o naszej m etafory z piosenkam i: Z asta n ó w się, co b ędziem y m ieli na taśm ie w rezultacie takiej operacji? B ędziem y mieli (starą) kasetę, na której n ag ran e są obecnie piosenki:
1135
1136
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego program u 1, 2, 3, 5, 6, 7, 8, 9 ,1 0 ,1 1 ,1 2 ,1 3 ,1 4 , i "stary" kaw ałek 14 N a końcu b ęd zie krótszy lub dłuższy kaw ałek piosenki n r 14, zależnie od tego jak b ard zo p io sen k a nr 4 była krótsza lu b dłuższa od piosenki nr 14.
O czyw iście, życie realizatora n ag rań dźw iękow ych b y ło b y o w iele prostsze, g d y b y w szy scy kom pozytorzy kom ponow ali piosenki o stan d ard o w ej d łu g o ści: 3 m in u ty 00 sek u n d . N ie jest to tak ie nierealne. Tak w łaśnie m oże się z d arz y ć w p rogram ow aniu. Przecież jeśli zapisujem y do pliku nie tekstow o, ale b in arn ie, to k ażda liczba ty p u double -
niezależnie czy mająca wartość zero, czy wartość siedemset milionów dwieście trzydzieści tysięcy pięćset osiemdziesiąt dwa m a d o k ła d n ie ten sam rozm iar ró w n y sizeof (double). Skoro tak, to łatw o jest w takim pliku (binarnym !) zastęp o w ać stare liczby now ym i. Zobaczm y p o d o b n ą rzecz w p oniższym paragrafie.
2 2 .1 9 P o z y c jo n o w a n ie w p rz y k ła d z ie w ię k s z e g o p ro g ram u W mojej p ra k ty c e najczęściej pozycjonow aniem w sk a ź n ik ó w czytania i pisania p o słu g iw ałem się w obec plików zaw ierających nie tyle d a n e tekstow e, co d a n e binarne. Z o b aczy m y teraz p rz y k ła d o w y program . R o zw iązu je on problem , o k tó ry m ju ż kied y ś w sp o m in ałem - jak p o słu ży ć się d u ż ą tablicą typu i n t - tak d u żą, że aż nie m ieści się w pam ięci operacyjnej k o m p u te ra . R ozw iązaniem jest realizacja takiej tablicy na dysku w p o staci pliku. Z ałóżm y, że tak i plik już m am y i ż e istnieje już stru m ień , k tó ry nas z tą tablicą kom unikuje. D ow o ln a operacja sięgnięcia do w y b ran eg o elem en tu tablicy p o le ga w ięc na ♦J* o b lic z e n iu , g d zie w pliku z a p isa n y jest w łaśn ie p o trz e b n y elem ent. N a stę p n ie p o z y c jo n u je się tam w sk aźn ik czytania... ♦$* ...po czy m o d czy tu je się sta m tą d elem ent tablicy. ♦♦♦ Po e w e n tu a ln ej m o d y fik a c ji elem entu... ...z a p isu je się go na d y sk p o z y c jo n u ją c w cześniej w sk a ź n ik pisania.
D o pam ięci nie s p ro w a d z a się p o je d y n czy ch elem en tó w , ale cały w iersz. Mieści się on w jed n o w y m iaro w ej tablicy lini jka. M ożna to p o k a z a ć na ry su n k u
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego pro gram u
1137
Rys. 22-4.
Jeśli ch ce m y odnieść się d o elem entu tablicy np. t a b [ 20] [ 6 ] , to d o roboczej tablicy l i n i j k a s p ro w a d z a m y z pliku 20 w iersz tablicy, po czym o d n o sim y się d o ele m e n tu l i n i j ka [ 6 ] . P racę m o ż n a sobie u sp raw n ić: nie odsyłam y za k a ż d y m razem w iersza z p o w ro tem na dysk, bo m ożliw e, ż e zaraz p o trzeb o w ać b ęd ziem y jakiegoś innego elem en tu z w łaśnie tego samego wiersza. D opiero, g d y m am y n astęp n e od n iesie nie się d o elem entu tablicy t a b - oceniam y sytuację: •
jeśli rzeczy w iście jest to elem en t z tego sam ego w iersza - to jesteśm y szczęśliw i - opłaciło się zaczekać!
•
jeśli nie - to najpierw o d sy łam y na d y sk w iersz, na którym p raco w aliśm y poprzednio, p o czy m s p ro w a d z a m y żąd an y .
A b y p ra c a z taką tablicą b y ła tak n atu raln a, jak p raca ze zw y k ły m typem w b u d o w a n y m -w s z y s tk ie operacje pisania ic z y ta n ia plik u d y sk o w e g o realizu jem y z a pom ocą p rz e ła d o w a n ia operatora [ ] . O p e ra to r ten o d p o w ia d a tylko za o p eracje n a w ierszach tablicy. O dniesienie się d o poszczeg ó ln y ch elem en tó w w w ie rsz u załatw ia ju ż zw y k ły o p erato r [ ] Z apis t a b [2 0 ][6 ]
o d p o w ia d a zapisow i (tab [2 0 ]) [6 ]<--- z w y k ł y operator
T te n o perator je s t p r z e ła d o w a n y
To w y ra ż e n ie w naw iasie w y w o ła sp ro w ad zen ie d o tablicy linijka w iersza nr | 20. D efiniując p rzeład o w an ie o p erato ra - rezu ltatem tego w y rażen ia u czy n im y a d re s tablicy linijka, d zięk i tem u nasze w y ra ż e n ie będzie ró w n o w aż n e ta k ie m u (linijka)
[6 ]
Tu taj ju ż o p erato r [ ] zasto so w an y jest w sto su n k u d o zw ykłej (jed n o w y m iaro w ej) tablicy int - d lateg o nie zad ziała tu „n asz" p rz e ła d o w a n y , ale te n zw ykły p racu jący zaw sze dla tablic int
1138
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego program u
Oto program: #include linclude tinclude #include using namespace std;
/ / dla
exit{)
llllllilllllllllltlllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllljljllL U class duża tablica
//
// nazwa pliku dyskowego
string nazwa_pliku; fstream plik; const int rozmiar;
// linijka w zapasie pamięci int * linijka; long dlug_linijki; // nr wiersza, nad którym pracujemy int nr_obecnego; public: duza_tablica (string nazwa, int wym, int wypełniacz = 0 ); -duża tablica(); int * operator[](int wiersz); private: void e r r o r ();
}; Z *************************************************************/ duza_tablica::duza_tablica(string nazwa,
int wym, int wypełniacz)
: rozmiar(wym), nazwa pliku(nazwa) p l i k . o p e n ( n a z w a . c _ s t r () ,
io s::trun c
I io s ::in
I // 0
|
ios::out
j i o s ;:binary);
//
®
i f ( !plik)
cout << "Blad otwarcia pliku " << nazwa_pliku << endl; e r ror();
// O
linijka = new int(rozmiar]; dług linijki = rozmiar * sizeof(int);
---------------- wstępne wypebtienie for (int i = 0 ; i < rozmiar; i++) linijka[i] = wypełniacz; ------- z a p is tego d o p lik u for (int k = 0; (k < rozmiar) && p lik.goodO; plik.write( (char*)linijka, dlug_linijki );
k++)
if (.'plik)
{ cout << "Blad podczas wypełniania tablicy" << endl; e r ror();
} nr_obecnego = 0 ;
}
Z************************************************************* duza_tablica::-duża tablica ()
1139
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego pro gram u
// skoro koniec, to musimy odesłać na dysk wiersz, który jest jeszcze w tablicy linijka i f (! p l i k.seekp(nr_obecnego * dlug_linijki))
.
{
.
cout << "Blad pozycjonowania wskaźnika pisania\n"; e r r o r ();
} // zapisanie tam -------i f (!plik.write( (char*)linijka , dlug_linijki)
)
cout << "Blad przy odsylaniu\n"; error () ;
} // zamknięcie pliku // zwolnienie rezerwacji
p l i k .close(); delete [] linijka;
} Z *************************************************************/ / / przeładowanie operatora []
/*****★****★***+******★★★★*★*★**★★★*★*★**★**★*★*★★★**★********/ int * duża tablica::operator(](int nr_wiersza_chcianego)
// o
( // może dany fragment tablicy jest pod ręką ? if(nr wiersza chcianego == nr obecnego)
// tak, to nic nie trzeba robić !
return linijka;
// jeśli nie, to najpierw odsyłamy obecny wiersz= H obliczenie, gdzie go posłać i f (! plik.seekp(nr_obecnego * dlug_linijki) )
,
(
cout << "Blad pozycjonowania wskaźnika pisania\n"; e r r o r ();
} // zapisanie tam i f (iplik.write( (char*)linijka , dlug_linijki)
)
{ cout « "Blad przy odsylaniu\n"; e r r o r () ;
) // sprowadzamy potrzebny wiersz
===
// obliczenie, gdzie on w pliku jest i f (! plik.seekg(nr_wiersza_chcianego * dlug_linijki))
{ cout << "Blad pozycjonowania wskaźnika czytania" << nr_wiersza_chcianego * dlug_linijki << endl; e r r o r () ;
) // przeczytanie tego fragmentu
1140
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego program u if(!plik.read( (char*) linijka, dlug_linijki) ) ( cout << "Blad czytania wiersza " << nr_wiersza_chcianego << endl; error(); ) // uaktualnienie notatek nr_obecnego = nr_wiersza_chcianego; // zwracamy wskaźnik do tablicy roboczej return linijka; } /*******-************* + *********+**r******************* +**i r* * * * f void duzatablica::error() { if(plik.eof()) cout << "Koniec pliku if(plik.fail()) cout << " -FAIL strumienia do pliku : " << nazwa pliku << endl; exit(1) ; ) ★ * * * * /★**************************r** + ***************** + *******i
int main() { int i, k; const int wymiar = 50;
//
©
duza_tablica t("macierz.tmp", wymiar);
// ©
// wpisanie jakichś danych do tablicy t for(i = 0; i < wymiar; i++) for(k = 0 ; k < wymiar; k++) t[i][k] = i * 100 + k;
// ©
cout << "Element t [24][7] ma wartość = " « t [24][7] « endl;
//O O
//■ cout << "Przykładowo robimy transpozycje " <
//O 0 //O ©
cout << "transpozycja skończona, oto rezultat\n" "Przykładowo elementy t[7][31] = " << t[7][31] « ", \1 1 [31] [7] = " « t [31] [7] « endl; cout << " Natomiast elementy m[7][31] = " << m[7][31] « ", \tm[31][7] = " « m[31][7] « endl;
□
Po wykonaniu programu na ekranie pojawi się następujący tekst Element t [24][7] ma wartość = 2407 Przykładowo robimy transpozycje
1141
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego pro gram u transpozycja skończona, oto rezultat Przykładowo elementy t [7 ] [31] = 731, Natomiast elementy m[7] [31] = 3107,
^
t[31] [7] = 3107 m[31] [7] = 731
Komentarz © Jeśli przyjrzymy się funkcji m a i n to, poza dwoma linijkami (© ) oraz (O © ) , nie ma w niej nic osobliwego. To jest najważniejsze przesłanie tego przykładu: mimo że dokonujemy niesamowitych operacji z tablicą na dysku - ten, kto posługuje się tą tablicą, nic o tym nie musi wiedzieć. © Definicja obiektu klasy duza tablica. Odpowiada ona mniej więcej takiemu zapisow i int t [501[50] ;
© Następnie widzimy pętlę, w której wpisuje się do takiej tablicy jakieś wartości. Instrukcja przypisania jest tak naturalna, jak do tego przywykliśmy przy typach wbudowanych. O O Podobnie wypisanie na ekranie treści jakiegoś elementu wygląda normalnie. O © Załóżmy, że konieczna jest transpozycja tablicy, czyli zamiana jej wierszy z kolumnami. Aby tego dokonać definiujemy sobie inną tablicę, w której znajdzie się w ynik tej operacji. Definicja obiektu, który tu widzim y, odpowiada jakby definicji int m [50] [5C]
O © Sama transpozycja, to po prostu operacja m[i][k]
= t[k][i]
przeprowadzona dla każdego elementu tablicy. Znowu ten sam wniosek: pa trząc na ten zapis nie można się nawet domyślić, że tablice są przechowywane na dysku.
Gdzie tkwi tajemnica tej prostoty ? O Tablice na dysku są zrealizowane jako obiekty klasy - którą w tym celu w ym yśli liśmy. Oto składniki-dane tej klasy: ♦♦♦ - składnik typu s t r i n g zapamiętujący nazw ę pliku dyskowego, w którym tablica będzie przechowywana na dysku. - obiekt klasy f streara. Tak! - Ośrodek dowodzenia strumieniem jest składnikiem klasy duza_tablica. To zrozumiałe - przecież każdy następny egzemplarz duże j_tablicy musi mieć swój strumień, swój ośrodek dowodzenia. ♦♦♦ - stała określająca rozmiar tablicy; jeśli definiujemy tablicę o rozmiarach [50][50], to tutaj zapisana zostanie liczba 50. - składnikiem w klasie powinna być też jednowymiarowa tablica l i n i j k a - do której będziemy sprowadzali poszczególne wiersze tablicy. Ponieważ jednak nie wiemy, jakie rozmiary ma tablica, którą użytkownik
1142
Rozdział. 22. O peracje W ejścia/W yjścia Pozycjonowanie w przykładzie większego program u klasy sobie zażyczy - (może 50x50, a m oże 8192x8192 ?), dlatego też nio w iem y jak długa m a byc linijka. Skoro jeszcze teraz nie w iem y, to z ałatw i m y to w k o n stru k to rze za pom ocą dynam icznej rezerw acji tablicy: tablica l i n i j k a zostanie zało żo n a z zapasie pam ięci operatorem n e w ju ż w trakcie w y k o n y w a n ia program u. Składnikiem duza t a b l i c a klasy zostaje tylko w sk aźn ik d o tak założonej tablicy. To on n azy w ać się b ęd zie l i n i j k a . <$♦
- d lu g _ lin i j ki
tach) jest l i n i p ro stu w artość
jk
- aby un ik n ąć ciągłego obliczania jak d łu g a (w baj a zapisujem y sobie jako sk ład n ik klasy. Jest to p o
rozmiar * sizeof(int)
<♦ - sk ładnikiem klasy jest też zm ienna i n t , w której zap am iętu jem y n u m er w iersza, k tóry jest sp ro w a d zo n y z tablicy dyskow ej i jest obecnie d o stę p n y w l i n i j c e . K lasa m a także funkcje składow e: konstruktor, d estru k to r, o p erato r [ ] oraz funkcję e r r o r w ypisującą inform acje o błędach stru m ien ia p l i k .
© Konstruktor M a trz y arg u m en ty . P ierw szy to n azw a, p o d jaką na d y sk u m a być zap isan a d u ż a tablica. D rugi a rg u m e n t to rozm iar tej tablicy - w tym p rzy k ła d zie z a k ła d a m y , że tablica jest „k w ad rato w a", czyli m a tyle sam o w ierszy , co kolum n . (W m oich zasto so w an iach tak w łaśnie najczęściej jest). Trzeci a rg u m e n t to w artość, którą n ależy w p isać do w szy stk ich elem en tó w tablicy w m om encie, g d y ją zak ład am y . P o n iew aż r o z m i a r jest stałą ( c o n s t ) , dlatego n a d a n ia m u w artości musimy d o k o n ać w liście inicjalizacyjnej k o n stru k to ra. W ciele k o n stru k to ra w id z im y kolejno: © O tw a rc ie pliku d y sk o w eg o . Plik o tw ie ra n y jest zaró w n o d o pisania i czytania. D o d atk o w o w idzim y „O R -o w an y " try b i o s : : b i n a r y - tablice mają p rzec h o w y w ać d an e z a p is a n e w sp o só b b in a rn y © Rezerw acja w za p a sie pam ięci jed n o w y m iaro w ej tablicy o żąd an ej d łu g o ści
lin ijk a
© W ypełnioną ż ą d a n ą w artością l i n i j k ę zap isu jem y d o tablicy dyskow ej w ielo k ro tn ie, jako w sz y stk ie kolejne w iersze. O czy w iś cie takie p o stę p o w a n ie nie m usi n a m o d p o w ia d a ć . Tablica na d y sk u m oże być p rzy g o to w an a p rz e z in n y p ro g ram i być zbiorem d an y ch p o ch o d zący ch z trw ająceg o ty d z ie ń ek sp ery m e n tu — czyli już w m om encie o tw arcia pliku m o że zaw ie rać w artościow o inform acje. W ów czas oczyw iście ten fra g m e n t ciała k o n stru k to ra jest n iep o trzeb n y - n ależy go zm ien ić lu b p o p ro stu n ap isać d ru g i k o n stru k to r na okoliczność o tw arcia takiej „w arto ścio w ej" tablicy. © D estru k to r po w in ien d o k o n ać ostatnich operacji p r z e d zak o ń czen iem p racy z tablicą. D o tych prac należy o d esłan ie na d y sk w iersza, z k tó ry m o statn io
1143
Rozdział. 22. O peracje W ejścia/W yjścia Tie - harm onijna p raca dwóch strum ieni
praco w aliśm y , zam k n ięc ie pliku oraz zw o ln ien ie rezerw acji pam ięci na jedno w y m ia ro w ą tablicę l i n i j ka. O P rz e ła d o w a n y o p e r a t o r [ ] . Jak w iem y funkcja o p e r a t o r [ ] o b o w ią z k o w o m u si być funkcją s k ła d o w ą klasy. Tak jest i u nas. A rg u m en tem jest tu nu m er w iersza, o k tó ry cho d zi. R ezultat to typ i n t * . R ezultatem b ęd zie tu zaw sze a d re s tablicy l i n i j ka. D laczego w łaśnie tak jest najlepiej - o tym szczegółow o m ów iliśm y w ro zd z. o p rzeład o w an iu - p a trz str. 843. P o n iew aż ciało teg o o p erato ra szczegółow o o p isałem k o m en tarzam i, dlatego nie trzeb a chyba d alszy ch w yjaśnień. W id zim y tam tak że operacje pozycjono w an ia w sk a źn ik ó w czy tan ia i pisania. W idać tutaj, jak b a rd z o są p rzy d atn e.
W Bazy danych wymagają właśnie częstego pozycjonowania P ozycjonow anie w sk a ź n ik ó w czytania jest koniecznością w p ro g ram a ch obsłu gujących tak zw an e bazy danych. P rogram n ajp ierw p o w in ien ocenić g d zie w pliku -b azie p o w in n a być istotna dla niego inform acja, po czym sięg n ąć tam i przeczytać. P odobnie z zapisem .
2.20 Tie - harmonijna praca dwóch strumieni Ten p arag raf m ożesz p rz y pierw szym i d ru g im czy tan iu tej książki opuścić.
M ów iliśm y już, że stru m ie ń c o u t jest b u fo ro w an y . O zn acza to, że p rzy p ad k u takiego fragm entu p ro g ram u : cout << "Raz"; cout << "Dwa"; cout « "Trzy";
p rzesła n ie zn ak ó w na ek ran nie następuje p o k ażd ej poszczególnej linijce. Dla u sp raw n ien ia sobie pracy stru m ień c o u t czek a, aż u zb iera się w ięcej zn ak ó w d o w y p isan ia i w ted y w y p isu je w szystko h u rtem . Jed n o k ro tn e p rzesła n ie d łu ż szej p artii znaków zajm uje m niej czasu niż k ilk a k ro tn e p rzesłan ie k rótszych. W rezultacie program pracuje szybciej. R ozw ażm y jednak taką sytuację cout << "Podaj energie zderzenia: cin >> e; O cout << "Podaj parametr zderzenia: cin >> m;
"
G d y b y i tutaj strum ień c o u t czekał z w szy stk im i p y ta n iam i aż u zb iera się ich więcej, w ów czas (w O ) stru m ień c i n czekałby na o d p o w ie d ź na p y ta n ie, które jeszcze nie pojaw iło się na ekranie. To bez se n su . N ie p ró b u ję C ię tu jednak
1144
Rozdział. 22. O peracje W ejścia/W yjścia Tie - harm onijna p raca dwóch strum ieni p rz e k o n a ć , że idea b u fo ro w an ia strum ienia cout jest bezsensow na. C hodzi je d n a k o to, by robić to m ądrze. N iech stru m ie ń cout będzie buforow any i rzeczyw iście opty m alizu je sobie p racę , czekając aż u zb iera się więcej tekstów. Ale - gdy tylko stru m ień w ejścio w y cin zechce w czytyw ać jakieś znaki (np. o d p o w ied ź) - niech najpierw zo stan ie n a ekranie w y p isan e w szystko, co czeka jeszcze w buforze strum ieni* cout. D o p iero w tedy stru m ień cin zajmie się w czytyw aniem . T aki sp o só b jest b ard zo k orzystny, gdyż nie rezy g n u jąc z do b ro d ziejstw a bufo ro w an ia m a m y p o p ra w n e w spółdziałanie stru m ie n i wejściowego i w yjściow e go. P artie p ro g ram u , które tylko piszą na ek ran ie, nie są dzięki tej m etodzie sp o w a ln ia n e , n atom iast te, k tó re rozm aw iają z uży tk o w n ik iem , n a d a l pracują p o p ra w n ie . 2*3 , .J Zjaw isko tak ie n azy w am y p o w iązan iem strum ieni . P o w iązan ie dotyczyć m o że nie tylko strum ieni cout i cin, ale ta k ż e innych W iązać m o żn a d o w o ln y strum ień (w ejściow y lub w yjściow y) z ja kim ś stru m ien iem wyjściowym - to o n będzie miał w y p ró ż n ia n y bufor. D laczego jed n ak do tej p o ry n ig d y nie z au w aży liśm y w ad liw eg o d ziałan ia s tru m ie n i w takich sytuacjach? To dlatego, że stru m ien ie p red efin io w an e są juz d la n as p o w ią z a n e au to m aty czn ie. S tru m ien ie cin, cerr, clog, są pow iązane ze stru m ien iem cout - o zn acza to, że jeśli ch cem y dok o n ać jakiejkolw iek akcji stru m ien iam i cin, clog, cerr, w ó w czas najp ierw w y p ró ż n ia n y jest bufor stru m ien ia cout. P o w iązan ie ze sobą innych stru m ien i m oże o d b y ć się na życzenie pro g ram isty . W y starczy w tym celu w y w o łać o d p o w ied n ią funkcję s k ła d o w ą klasy ios_base ostream* ostream*
ios_base::tie(ostream * ) ; ios_base::t i e ();
P ierw sza z tych funkcji p o w o d u je zw iązanie d a n e g o stru m ien ia z ja k im ś stru m ieniem w yjściow ym . A rg u m en te m funkcji jest w sk a źn ik d o s tru m ie n ia w yjś ciow ego, z k tórym należy w iązać. Funkcja, jako re z u lta t, zw raca w sk a ź n ik do stru m ien ia, z którym b y ło p o w iązan ie do tej p o ry . D ru g a z tych funkcji tylko zw raca rezultat, b ęd ąc y w sk aźn ik iem d o stru m ien ia, z k tóry m obecnie d an y s tru m ie ń jest p o w iązan y . P rzy k ład o w o m am y stru m ien ie istream wejście; ostream wyjście,
raport
O to, jak p o w iązać stru m ien ie wyj scie oraz wyj scie, d o stru m ie n ia w yjścio w eg o r a p o r t : 23)
ang. tie - związać [czytaj: „taj"]
1145
Rozdział. 22. O peracje W ejścia/W yjścia Dlaczego ta k nie lubimy biblioteki s t d i o ? wejscie.tie(raport); wyjście.tie(raport);
[Jig 3
O d tej po ry d w a s tru m ie n ie w e j ś c i e o raz w y j ś c i e p o w ią z a n e są ze stru m ie n ie m r a p o r t . Z n aczy to, że jakakolw iek operacja w e /w y na strum ieniach w e j ś c i e o raz w y j ś c i e , spo w o d u je n ajp ierw w y p ró żn ien ie b u fo ra stru m ien ia ra p o rt. Jeśli istniejące p o w ią z a n ie stru m ien i nam nie o d p o w iad a, m o żem y je przerw ać. O d b y w a się to ró w n ież za pom ocą funkcji t i e z tym , że teraz a rg u m e n te m jest 0. P rzerw ać m ożem y p o w iązan ie, które zro b iliśm y m y sam i lu b p o w iąza n ie już w cześniej istniejące. N p . instrukcja c i n .t i e (0 ); p rz e rw ie p o w iązan ie stru m ien ia c i n ze stru m ien iem c o u t .
W Kiedy takie powiązanie strumieni może się przydać ? P rz e d e w szystkim w te d y , g d y dw a różne stru m ie n ie pracują na tym sam ym p lik u . P rzykładow o: jeśli jeden stru m ień w p isu je do pliku d a n e o rezerw acji m iejsc w sam olocie, n ato m iast d rugi odczy tu je tę inform ację, to stru m ień w p isu jący m oże być b u fo ro w a n y dla u sp raw n ien ia jego pracy. Jeśli jed n ak stru m ień czytający ten plik sp ró b u je dow iedzieć się o s ta n rezerw acji miejsc - to w szystkie d a n e o ostatnio zajętych miejscach czekające jeszcze w b u fo rze p o w in n y być n ajp ierw u ak tu aln io n e n a dysku, a dopiero w te d y stru m ień w ejściow y odczyta czy jeszcze jakieś m iejsca są wolne. Taką p racę zap e w n i nam w łaśnie zw iązanie z e so b ą tych strum ieni. S trum ień czytający p o w in ie n po prostu w y w o łać funkcję sk ła d o w ą t i e m ów iąc, ż e chce zw iązać ze so b ą stru m ień piszący.
12.21 D la c z e g o tak m e lu b im y b ib lio te k i stdio? P a ra g ra f ten jest p rzez n aczo n y dla tych, k tó rz y zetknęli się z biblioteką s t d i o (program iści k lasycznego C). Pozostałym czy teln ik o m rad zę ten (oraz nastę p n y ) p arag raf opuścić i przejść do czytania z a g a d n ie ń o strum ieniach łączących n a s z obiektam i klasy s t r i n g (str. 1150).
Jeśli program ując w klasycznym C p rzy w y k łeś d o korzy stan ia z biblioteki s t d i o zapew ne zad asz m i pytanie: -Dlaczego właściwie tak nie lubisz biblioteki s td io . O dp o w iad am : Ależ lu b ię, lubię! Tak sam o, jak lubi się stareg o przyjaciela. D ok ład n ie tak samo: p rzeżyliśm y ze sobą d o ść d u ż o czasu, zn am y sw oje liczne w a d y , ale lubim y się m im o w szystko - d la teg o , że się dobrze ro zu m iem y .
1146
Rozdział. 22. O peracje W ejścia/W yjścia Dlaczego ta k nie lubimy biblioteki s t d i o ?
Trudno jednak nie zauważać wad. Oto one: 1) Z u w ag i na to, ż e p o d staw o w e funkcje •
w ypisyw ania: p r i n t f
•
i w czytyw ania: s c a n f
są zd efin io w an e jako m ogące zaw ierać zm ien n ą liczbę a rg u m e n tó w - kom pila to r nie potrafi nas o strzec w przy p ad k u , g d y zapom nim y o jakim ś argum encie, a lb o d am y o kilka arg u m en tó w za d u żo w sto su n k u do tego, co napisaliśm y w specyfikacji form atu. p r i n t f ( "Wypis t r z e c h l i c z b : %d %d %d \n " , l i c z b a l ) ; K om p ilato r nie sk ontroluje też ty p u tych arg u m en tó w , jeśli liczbę całkowitą p ró b u jem y w y p isać za pom ocą form atu dla liczb zm ien n o p rzecin k o w y ch , tc k o m p ilato r nie m oże takiego błędu o d szu k ać i nas ostrzec. i n t a = 4; p rin tf(" % 6 .2 f" , a ) ; W y d ru k , k tóry pojaw i się na ekranie w rezu ltacie takiej błędnej instrukcji, wcale n ie m u si od razu w y g ląd ać bezsensow nie - to w łaśnie u tru d n ią w y k ry cie błędu jeszcze bardziej. Biblioteka pow inna być tak sk o n stru o w an a, by d ać kom pilato ro w i szansę w ykrycia takich - elem entarnych w końcu - b łęd ó w , jak n iezg o d ność typu i liczby arg u m e n tó w w y w oływ anej funkcji. 2) P rzypom nij sobie ile tru d u kosztow ało Cię przysw ojenie sobie w ie d z y kiedy to w funkcji s c a n f należy użyć zn ak u & N a przy k ład : c h a r t a b l i c a [1 0 ], c ; in t i, j; scanf("% d %s %c %s", &i, t a b l i c a , & ta b lic a [ 3 ] , & t a b li c a [ 4 ] ) ; C zy w tej linii jest błąd, czy nie? T rzeb a trochę pom yśleć, tyle że to szlach etn e m yślenie nie jest zb y t tw órcze P ro g ram ista p o w in ien m yśleć bardziej n a d sen sem tego, co p isze, a nie nac g ram aty k ą. O ileż p ro stszy jest zapis c in
>> i >> t a b l i c a >> t a b l i c a [ 3 ]
>> ( c h a r * ) t a b l i c a [ 4 ] ;
g d y ż nie m u sim y p a m ięta ć o u ży w an iu z n a k ó w &. 3) Jeśli piszesz k ró tk i p ro g ram , k tó ry w y p isu je na ek ran ie tylko jed n ą liczby całkow itą instrukcją p r i n t f ("%d", i ) ; to m im o w szy stk o z biblioteki s t d i o w łą c z a n e są frag m en ty o d p o w ied zialn e za: w y p isan ie strin g u , w y p isan ie p o je d y n czeg o z n a k u , w y p isa n ie liczb)
Rozdział. 22. O peracje W ejścia/W yjścia Dlaczego ta k nie lubimy biblioteki s t d i o ?
1147
zm ien n o p rzec in k o w ej z precyzją 10 m iejsc zn aczący ch itd ., itd. T ym czasem u ży w a ją c instrukcji cout << i ;
p o w o d u je m y , że w p ro g ra m ie znajdą się tylko te frag m en ty , k tó re służą d o w y k o n a n ia w y p isu liczby całkow itej w notacji dziesiątk o w ej. O szczęd zam y m iejsce w pam ięci, co m o ż e być czasem b a rd z o w ażne. P ro g ram w y n ik o w y jest w te d y m niejszy. 4) N ajw ażniejszą ch y b a w ad ą biblioteki s t d i o jest fakt, iż p o słu g u ją c się nią, n ie m am y eleg an ck ieg o s p o so b u n a w czy ty w a n ie i w y p isy w a n ie o b ie k tó w ty p ó w z d e fin io w a n y c h p rz e z u ż y tk o w n ik a . T o, co w bibliotece i o s t r e a m załatw ia się za pom ocą p rzeład o w an ia o p e ra to ró w » oraz << - tutaj, w bibliotece s t d i o , staje się b a rd z o skom pliko w a n e . W p ro g ram ie n ie m ożem y po p ro stu w y d a ć polecenia - w czytaj z pliku d y sk o w e g o dw ie liczb y i n t , dw a obiekty klasy o s o b a , liczbę d o u b l e i obiekt k la sy w e k to r e k . P o słu g u jąc się klasą zd efin io w an ą p rzez kolegę m u sim y zn ać w szy stk ie jej sk ład n ik i, m ieć d o n ich dostęp, by ż m u d n ie z dysku w czy ty w ać d o tych sk ła d n ik ó w liczby czy stringi. G w ałci to zasad ę en k ap su lacji - u ży tk o w n ik m u si zn ać w szy stk ie try b ik i i kółka k la sy po to, by jej o b ie k ty sw obodnie w czy ty w ać z dysku. Jedną ze sztandarowych zasad języka C++, jest jego rozszerzalność o nowe typy-zdefiniow ane przez użytkownika. Biblioteka s t d i o nie zapewnia tego. Dlatego radzę się z nią pożegnać.
♦
5) U życie p r i n t f z biblioteki s t d i o jest po p ro stu w olniejsze niż c o u t . To d la teg o , że p r i n t f d o p ie ro w czasie w y k o n y w a n ia p ro g ram u an alizu je string fo rm atu i zależnie od tego, co tam znajdzie, u ru ch am ia o d p o w ie d n i p o d p ro g ra m w ypisujący d an y ty p danej. T ym czasem c o u t p rz y g lą d a się typow i a rg u m e n tu ju ż w czasie k o m p ila c ji i w te d y decyduje, co b ę d z ie m u potrzebne. T ym sposobem , c o u t w ykonując p ó źn iej daną linijkę p ro g ra m u miliony razy , nie m usi się nad niczym zastan a w iać. (G łupszy p r i n t f będzie m iliony ra z y p o w tarza ł o g ląd an ie tego sam ego fo rm atu ).
♦
6) Jeśli w bibliotece s t d i o coś nam się n ie p o d o b a , to nie m o żem y sobie tego odzied ziczy ć, zasłonić lepszą w ersją i być szczęśliw ym . P odobnie: jeśli w bibliotece s t d i o coś nam się b a rd z o p o d o b a, to nie m ożem y so b ie tego o d ziedziczyć, by w swojej klasie jeszcze bardziej udo sk o n alić. Ta biblioteka nie nadaje się d o dziedziczenia. Jest b ezpłodna. T ym czasem jeśli ch o d zi o i o s t r e a m , to za chw ilę zobaczysz, jak d la potrzeb tz w . form atow ania w ew n ętrzn eg o
1148
Rozdział. 22. O peracje W ejścia/W yjścia Synchronizacja biblioteki i o s t r e a m z biblioteką s t d i o ?
czyli operacjach tworzenia formatowanych napisów w obiektach klasy
string, o d zied ziczo n o frag m en ty biblioteki i o s t r e a m narzęd zie. Sam też m o żesz coś tak w ykorzystać.
i jakie p o w stało św ietne
22.22 S y n c h ro n iz a c ja b ib lio teki iostream z b ib lio te k ą stdio T en p arag raf opisuje zagad n ien ie, którego znajom ość w pierw szej fazie nauki n a p ew n o nie będzie Ci potrzebna, dlatego p rz y p ierw szy m czy tan iu tej książki m o żesz go opuścić. P arag raf ten p rzy d ać się m oże tym program istom , k tórzy b ęd ą zm u sze n i - w ty m sam y m p ro g ram ie - korzystać także i ze starej biblioteki stdio.
X M ów iliśm y, że p rzy k o rzystaniu z biblioteki ios t r e a m m am y następujące p red efin io w an e stru m ien ie (strum ienie d efin io w a n e stan d ard o w o ): c i n , c o u t, c e r r ,
c lo g
Jak Ci za p e w n e w iad o m o , w bibliotece s t d i o są o d p o w iad ające im s td in , s td o u t,
s td e rr
P am iętać trzeba, że n ie ma żadnej łączności m ięd zy nim i, p o za faktem , ii u ży w ają o n e tych sam y ch , tak zw anych, deskryptorów plików. Jak za p e w n e w iad o m o Ci z o pisu bibilioteki stdio - d e s k ry p to r pliku, to n u m e r oznaczający p lik lu b u rząd zen ie w yjściow e (k law iatu ra, ek ran ). ♦♦♦ d esk ry p to r 0 - oznacza s ta n d a rd o w e w ejście (k la w ia tu r a ) , ♦> d esk ry p to r 1 - o znacza sta n d a rd o w e w yjście (ekran), ♦> d esk ry p to r 2 - oznacza s ta n d a rd o w e w yjście d la k o m u n ik ató w o błędach - (u ż y w a g o stru m ień cerr i clog). Co z tego "w sp ó łd zielen ia się d esk ry p to ram i" m oże w y n ik ać złego? O tó ż jeśli m ieszam y p o słu g iw an ie się jedną i d ru g ą b iblioteką, to m o ż em y mieć k ło p o ty z sy nchronizacją. P rzy k ład o w o - p o n iż sz y frag m e n t k o d u : cout p rin cout p rin cout
« " Je d e n t f ("Dwa ” ) ; << "T rzy t f ( " C z te r y " ) ; << " P ie c " << e n d l;
w cale nie m usi sp o w o d o w a ć pojaw ienie się na ek ran ie tekstu: J e d e n Dwa T rzy C z te r y P ie c M o żem y o trzy m ać
Rozdział. 22. O peracje W ejścia/W yjścia Synchronizacja biblioteki i o s t r e a m z biblioteką s t d i o ?
1149
Jeden Trzy Piec Dwa Cztery
alb o na p rzy k ła d Dwa Cztery Jeden Trzy Piec
D laczego? O tóż c o u t jest bufo ro w an y , a s t d o u t nie. In n a jest z a sa d a w y p i sy w an ia, d la teg o nie pracuje) o n e ram ię w ram ię. A b y biblioteka i o s t r e a m u w zg lęd n iała o p eracje w y k o n y w a n e z biblioteką s t d i o , p o w in n iśm y ją u p rzed z ić o tym , że zam ierza m y k o rzy stać z s t d i o . K o n k re tn ie -s tru m ie ń z now ej biblioteki u p rz e d z a m y o tym za pom ocą funkcji składo w ej
bool
ios_base: :sync_with_stdio (bool s y n c = true) ; (n azw a tej funkcji oznacza: synchronizuj z s t d i o ) . *$♦ A rg u m en tem w y w o łan ia tej funkcji czy n im y w arto ść ty p u b o o l ozn a czającą czy chcem y (true)/ nie chcem y (false) synchronizacji naszych stru m ien i z biblioteką s t d i o . D o m n iem an a w artość tego arg u m e n tu to true. ♦> R ezultatem tej funkq'i jest w artość ty p u b o o l inform ująca nas o d o ty ch czas obow iązującej sytuacji (true - o zn ac za była sy nchronizacja, false nie było). Instrukcję ios::sync_with_stdio();
należy um ieścić na początku n aszeg o p ro g ram u , k o n ieczn ie jesz cze p rz e d d o k o n an iem pierw szej operacji jakiejś operacji w e /w y na stru m ien iach . Jeśli w yw ołanie tej funkcji um ieścim y na p o czątk u naszego ostatn ieg o p rzy k ła d u , to poszczególne w y razy zaw sze pojaw iać się będą na ek ran ie w e w łaściw ej kolejności. Jeden Dwa Trzy Cztery Piec
T a tajem nicza funkcja jest to statyczna funkcją sk ład o w a klasy ios base s ta tic
bool
ios_base::sync with stdio();
a w ięc nie jest w y w o ły w an a na rzecz jakiegoś k o n k retn eg o stru m ien ia (np c i n , c o u t , c l o g , ...), ale na rzecz całej klasy tych obiektów . Czyli: w szystkich stru m ien i pochodzących o d klasy i o s _ b a s e . W yw o łan ie tej funkcji (z arg u m en tem true) na p o czątk u p ro g ram u sp raw ia, że biblioteka i o s t r e a m b ęd zie postępow ać o stro żn iej - stru m ien ie c i n , c o u t b ę d ą pracow ały w nieco in n y sposób, co u m o żliw i im zg o d n ą p racę z s t d i o , s td o u t, s td e rr.
1150
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g
Co na tym zyskaliśmy? P o p ra w n ą synchronizację i m ożliw ość m ieszania użycia starej i nowej biblioteki.
Co na tym tracimy? T racim y na szybkości stru m ien i stan d ard o w o zdefiniow anych (tzw . predefi n io w an y ch ), k tóre p rzestają być buforow ane. R ezygnacja z b u fo ro w an ia je st obniżeniem sp raw n o ści pracy tych stru m ien i, ale robią o n e to po to, by staru szk o w ie ze starej biblioteki s t d i o m ogli za nimi nad ąży ć. P o n iew aż jest to czasem cena dość w ysoka, dlatego synchronizacja ta k a nie jest robiona p rzez dom niem anie. P rogram ista sam decy d u je, czy tego chce. D o tego w łaśn ie słu ży przed staw io n a funkcja.
22.23 S tru m ie ń z a p is u ją c y d o ob iektu k la s y string K iedy m ó w iliśm y , co to są operacje wejścia i w yjścia - p o w ied ziałem , ż e chodzi o z a g a d n ie n ie k o m u n ik o w a n ia się program u z u rząd zen iam i ze w n ę trz n y m i — k la w ia tu rą , ekranem , p lik am i na dyskach m agnetycznych itd. T ym czasem teraz zajm iem y się operacjam i w e /w y nie k o m u niku jącymi nas w c a le z plikam i na u rząd z en iac h zew n ętrzn y ch .
W czasach p rz e d sta n d a rd o w y c h takim m iejscem pam ięci byw ała tablica znako w a. Dziś to już w y ch o d zi z użycia, bo tablice z n a k o w e m ają p o d sta w o w ą w adę: nie m ają d estru k to ra , k tó ry m m ogłyby po so b ie p o sp rzątać, a p o z a tym nie p o trafią w razie konieczności sam e po w ięk szy ć sw ojego rozm iaru*^. Dla programistów klasycznego C nadmienię, że w tym paragrafie chodzi o to samo, co w bibliotece s t d i o wykonywane było funkcjami s p r i n t f i s s c a n f , natomiast dla znających FO R TRA N wystarczy powiedzieć, że chodzi o ENCODE i DECODE P roblem jest taki. M am y instrukcję: 24)
Przedstandardowe wersje książki "Symfonia C++" opisywały te właśnie klasy pracujące z tablicami znakowymi. Tu nie będziemy się nimi zajmować, bo o tych starych klasach należy jak najszybciej zapomnieć. Jeśli tych starych klas używałeś w swoich programach, to kompilując te program y nowym kompilatorem zobaczysz na ekranie wielokrotne ostrzeżenia doradzające, by te stare klasy zastąpić nowymi (opisywanymi w tym i następnych paragrafach).
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g cout
« s e t w (2) « nr_silnika « "Awaria s i l n i k a " s e t p r e c i s i o n (3) << ", t e m p e r a t u r a oleju " « endl; « t e m p e r a t << " stopni C " «
w y p isu jącą na e k ran ie k om unikat. T y m czasem z jakichś p o w o d ó w chcielibyś m y m ieć cały ten te k st n ie na ekranie, ale w p is a n y do obiektu k la sy s t r i n g . string
k omunikat;
In n y m i słow y, ch o d zi n am o strum ień, ty le z e płynący m e d o zew n ętrzn eg o p lik u , ale do jakiegoś u m ó w io n eg o m iejsca, s k ą d w stosow nej ch w ili przep isze m y sobie go d o n aszeg o obiektu klasy s t r i n g . Z u w ag i na p o d o b ie ń stw o akcję tę także n a z y w a m y operacją w c /w y . Istnieją w bibliotece w e / w y klasy stru m ien i, k tó re oddają nam w łaśn ie p o d o b n e u słu g i. Aby p o słu ży ć się takim i stru m ie n ia m i niezbędne jest w łączenie do p ro g ra m u pliku n ag łó w k o w eg o < s s t r e a m > zaw ierającego d ek laracje o d p o w ied n ich klas. R obim y to dyrektyw ą: # i n clude < s s t r e a m >
Deklaracje te umieszczają nazw y klas w p rz e strz e n i n azw std. Wiesz, co to znaczy. Jeśli wolisz się nazwami tych klas posługiwać bez kwalifikatora s t d : :, to wystarczy znana Ci od dawna instrukcja using namespace std; W róćm y d o n aszeg o problem u. A by w y k o n a ć zad an ie w y p isan ia naszego te k stu do jakiegoś s t r i n g u , posłużym y się stru m ien iem klasy. ostringstream
C o jest zap ew n e sk ró tem od Output STRING STREAM - w yjściow y stru m ień do o b iek tu klasy s t r i n g . G ra f pochodzenia, na którym u w id o czn io n a jest klasa o s t r i n g s t r e a m w id zieliśm y na p o czą tk u rozdziału, na stro n ie 1042. Klasa o s t r i n g s t r e a m jest p o ch o d n ą klasy o s t r e a m , a zatem d zied zic zy w szystkie jej zach o w an ia - m ożem y w ięc sobie wobec niej u ż y w a ć operatorów « , m an ip u lato ró w , czy zn an y c h nam funkcji sk ład o w y ch w r i t e , p u t , czy naw et p o zy cjo n o w ać w sk aź niki p isan ia, itd. M ógłbyś teraz pow iedzieć tak: — Wpisanie tekstu do obiektu klasy s t r i n g to ostatecznie nic trudnegorobiliśmy to wielokrotnie operatorem lub +—. string komunikat = "Awaria silnika!"; komunikat += " Temperatura wynosi: ";
czy ten sposób nie wystarczy? N ie. N iestety nie da się w ten sposób d o s t r i n g u w pisać liczby. C zy pam iętasz jakie akrobacje m u sieliśm y robić w ro zd ziale o s t r i n g a c h , n a stronie 517 (§ 11.3.1), aby d o strin g u w pisać w artość liczby i n t ? T eraz otrzym am y eleganckie rozw iązanie.
1151
1152
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g D zięki klasie, którą teraz p o zn am y , m ożem y sk o rzy stać z całego d o b ro d zie jstw a form atow ania oferow anego nam p rz e z strum ienie w e /w y . M o żem y do s t r i n g u w pisać w artość liczby (jej zapis w postaci cyfr), m ożem y z a d a ć precyzję, szerokość, znaki w y p ełniające, typ notacji itd. Tego w szy stk ieg o nie d a ło się zrobić p rzy uży ciu operatorów =, +=.
Jak to działa? S tru m ień typu o s t r i n g s t r e a m s a m m a (w śro d k u ) składnik b ęd ąc y obiektem k lasy s t r i n g . To tam g ro m ad z i w szystkie n asze w y p isy w an e z n ak i i cyfry. W dow o ln ej chw ili (n aw et w ielokrotnie) m o ż em y strum ień p o p ro sić o p o k azan ie nam tego, co d o tej p o ry zostało w ypisane. S tru m ień u d o stęp n i nam tę treść w postaci s t r i n g u , w ięc - jeśli chcem y - m o ż em y taką w arto ść sk o p io w ać d o in n eg o (sw ojego) o b ie k tu klasy s t r i n g , albo m ożem y ten te k st w ysłać d o jakiejś funkcji. N asz s tru m ie ń klasy o s t r i n g s t r e a m z a p e w n ia nam fo r m a to w a n ie - a ponie w aż to „ fo rm a to w an ie" o d b y w a się nie na u ż y te k pliku zew n ętrzn eg o , tylko na u ży te k strin g u w p am ięci - dlatego czasem zag ad n ien ie to n a z y w a się też fo rm a to w a n ie m w e w n ę trz n y m . (Nie p rzejm u j się za b ard zo tą nazw ą).
Kiedy możemy potrzebować usług strumienia ostringstream? W m o im p rz y p a d k u d zieje się tak najczęściej w d w ó ch sytuacjach. *♦* 1 - Jeśli m am w p o k aza ć jakim ś o k ie n k u ek ran o w y m k o m u n ik a t o skom p likow anej treści (tekstow o liczbow ej), to d o funkcji w yśw ietlającej to o k ien k o p o w in ien em przesłać g o to w y strin g , zaw ierający cały sk o m p li k o w an y opis sytuacji. Do sp o rząd zen ia tego strin g u w y g o d n ie p o słu ży ć się w łaśn ie o m a w ian ą tu klasą ostringstream. 2. C zęsto w p ro g ram ie zachodzi k o n ieczn o ść otw arcia je d n e g o z całej serii p lików (aby w czytać z niego ja k ąś inform ację). N a z w a takiego pliku sk ład a się z w ielu członów , często m a w sobie liczby. N a p rzy k ład " P r o b k a _ 0 0 2 5 3 _ iz o t o p _ F m 2 5 2 . pom". " K o s z ty _ 2 8 _ m a j_ 2 0 0 5 . d a t" N azw y poszczególnych plików trzeb a b u d o w a ć ju ż w tra k c ie w y k o n y w an ia p ro g ram u - zależnie od tego, czego z aż ąd a u ż y tk o w n ik . Do sp o rząd zan ia tak ich (sko m p lik o w an y ch ) n a z w p lików najlepiej p o słu żyć się w łaśnie o m aw ian ą tu klasą ostringstream. Jeśli za m ie rz a m y sk o rzy stać z tego w sp a n iałe g o n arzę d zia, to - o p ró cz całego d zied zic tw a po klasach p o d staw o w y ch -
1153
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g
...mamy w klasie ostringstream do dyspozycji następujące funkcje składowe K o n stru k to ry (mają o n e p rzy d o m ek e x p l i c i t
):
ostringstream(ios base::openmode tryb = ios::out); ostringstream(string const & treść wstępna,
OST:Ł
io s _ b a s e : : openmode tryb = i o s : : o u t ) ;
y
// O // 0
Z w y k łe funkcje sk ład o w e: string void
str() const, str (const string &
,,
.
tr c s c _ w s tę p n a )
. const;
// //
Co robią te funkcje? O K o n stru k to r d o m n ie m a n y . Jego arg u m en tem jest tryb pracy. O czyw iście w iem y, ż e ch o d zić tu musi o pisanie, ale jeśli zam iast d o m n iem an eg o try b u i o s : : o u t w y b ierzem y tryb i o s : : a p p , to będziem y zaw sze d o p isy w ać d o końca do ty ch czasow ej treści. P rz y k ła d zasto so w an ia tego konstruktora: ostringstream ostringstream
info; = .. ostrzeżenie(ios..app),
W pierwszym przypadku tworzymy obiekt klasy o s t r i n g s t r e a m o nazwie i n f o , gdzie zamierzamy tworzyć jakiś tekst - najróżniejszymi sposobami. Wdrugim przypadku tworzymy obiekt o nazioie o s t r z e ż e n ie , gdzie zamierzamy tworzyć jakiś tekst, a wiemy, że tioorzeme tego tekstu polegać bidzie zawsze na dopisywaniu tekstu do końca obectjej
treści. 0 K o n stru k to r z d w o m a arg u m en tam i. Z n aczen ie tego d ru g ieg o a rg u m e n tu (tryb) jest takie sam o, jak o p isan eg o przed chw ilą arg u m e n tu k o n stru k to ra dom nieN atm n iast p ierw szy arg u m e n t dostarcza o b iek to w i tekst, k tó ry m a być zapisa ny w obiekcie już o d ch w ili jego u ro d zen ia. To p o prostu jak b y jego w artość w stęp n a. Jest teraz problem , co z tym tekstem w stęp n y m zrobim y dalej: Jeśli sk o rzy stam y z dom niem anego try b u i o s : : o u t to zap isy w an ie przy użyciu teg o strum ienia ro zp o czn ie się od p o czątk u , w ięc ten w stępny tekst b ęd zie stopniow o zacieran y , zastęp o w an y n ow ym i zna kami. (N o chyba, że w odpow iedniej chw ili sam i p rzesu n ie m y w skaźnik pisania w e w łaściw sze miejsce). 25)
Małe przypomnienie: Przydomki e z p l i c i t przy obu konstuk.oraeh sa po to. by zapobiegać ewentualnej przypadkowej, niejawnej konwers,. na typ klasy o s t n n g s t ream | (z typów s t r i n g i i o s _ b a s e : : openmode).
1154
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g ❖
Jeśli sk o rzy stam y z trybu i o s : : a t e , lub i o s : : a p p to zapisyw anie ro zp o cz n ie się od końca tego w stęp n eg o tekstu.
P rzy k ład użycia tej w ersji konstruktora: o s trin g s tre a m o s trin g s tre a m
i n f o (" * * * * * "); o s t r z e ż e n i e ( "Uwaga:" , i o s : : a p p ) ;
Powstają tu dwa obiekty klasy o s t r i n g s t r e a m . Pierwszy o nazwie i n f o zawiera tekst wstępny, a najbliższa operacja pisania rozpocznie się od początku (stopniowo zacierając tekst wstępny). Drugi obiekt, o nazwie i n f o też zwiera tekst wstępny, ale skoro pracuje w trybie i o s :: app, więc następna operacja wypisywania spowoduje doda nie nowych znaków do końca istniejącego już tekstu wstępnego. © To jest funkcja s t r , dzięki której stru m ień u d o stęp n ia nam d o ty ch czaso w y efekt naszej pracy. F unkcja ta zw raca (przez w artość) obiekt klasy s t r i n g zaw iera jący tekst, który p raco w icie w ytw orzyliśm y. Tekst ten m ożem y albo przypisać d o innego (już n aszeg o ) obiektu klasy s t r i n g , lub n aw et w y słać do jakiejś funkcji oczekującej s t r i n g u . O czyw iście n a stę p n ie m ożem y k o n ty n u o w ać dalsze tw o rzen ie tekstu, a w dow olnej chw ili z n o w u w yw ołać funkcję s t r ( ) , by o trzy m ać ten najnow szy efekt naszej całej p racy . s t r i n g moj = i n f o . s t r () ; f u n k c ja ( i n f o . s t r () ) ;
//jeśli istnieje f u n k c ja ( s t r i n g s ) ;
© Ta w ersja funkcji s t r uniew ażnia do ty ch czaso w y u robek, jak b y zeruje całą dotychczasow ą treść. Po jej w yw ołaniu p isan ie rozpocznie się od now a. A rg u m en tem tej fu nkcji jest now y tekst w stęp n y , oczyw iście jeśli g o nie chcemy, p o pro stu należy a rg u m e n te m uczynić C -strin g pusty. W tedy n a stę p n a operacja zaczn ie tw o rzen ie tekstu „o d z e ra " ^ . P rzy k ład użycia tej funkcji. i n f o . s t r ("Od n o w a"); o s tr z e ż e n ie .s tr (" " ); W obu poioyiszych instrukcjach dotychczasowa treść obiektów i n f o i o s t r z e ż e n i e jest zapominana, a treścią wstępną staje się C-string, który użyliśmy jako argument.
W pracy z tym stru m ien iem m ożesz p rzy jąć d w a p u n k ty w id zen ia. Pierw szy to ten, że m am y do czy n ien ia ze stru m ien iem , k tó ry p ły n ie nie na e k ran , nie do pliku, ale d o ob iek tu klasy s t r i n g . 26 )
Uwaga, funkcja ta jest prawdopodobnie źle zaimplementowana w kompilatorze Visual C++ v. 6.0, gdyż pewne jej użycia powodują, ze destruktor obiektu klasy o s t r i n g s t r e a m wywołuje błąd naruszenia segmentu pamięci.
1155
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g
M o ż n a też m yśleć inaczej: że to jakby o b iek t k lasy s t r i n g z taką niezyw kłą inteligencją, ż e m o żn a polecić mu n a w e t w p isan ie liczb z zd an ą precyzją, fo rm ato w a n ie, p o zy cjo n o w an ie i tak dalej. O czy w iście ten d ru g i p u n k t w idzenia nie u p ra w n ia nas d o p o w ied zen ia, ze s tru m ie ń ten jest rodzajem s t r i n g u - b o w k o ń c u jego klasa nie jest p o ch o d n ą od k la sy s t r i n g - ale ta k ie m yślenie m oże C i po zw o lić osw oić się z tą klasą.
>2.23.1 Program przykładowy ilustrujący użycie klasy ostringstream Z o b aczy m y teraz p ro sty p ro g ram , w k tó ry m o peracje tw o rzen ia tekstów p o w ie rz a m y klasie o s t r i n g s t r e a m . i i n c l u d e < io s tre a m > u s i n g nam espace s t d ; # in c lu d e
// o
H n c l u d e < sstre a m > ll<~bo używamy o s t r i n g s t r e a m v o i d p o m i a r ( s t r i n g n azw a); v o i d o d c z y t (c o n s t c h a r * n azw a); r****** * ************ / / t ************** ***************************
i n t m ain () i n t n r _ s i l n i k a = 4 ;^ d o u b le te m p e r a t = 1 5 6 .7 1 2 3 ; // @
o s t r i n g s t r e a m sk o m u n ik a t ; //a tera z w ła ś c iw e w y p is a n ie
sk o m u n ik a t « "A w aria s i l n i k a " « s e t w (2) « n r _ s i l n i k a << " , te m p e r a tu r a o l e j u ” << f i x e d << s e t p r e c i o i o n (2) // © « te m p e r a t << " s to p n i C \ n " ; i f ( ! sk o m u n ik at) . . c o u t « " J a k i ś b la d p ra c y s t r u m i e n i a «
encu,
// dopisujemy dalej s k o m u n ik a t. f i l i ( ' • ' ) ' s k o m u n ik a t.w id th ( 2 5 ) ; . sk o m u n ik a t << " M u sisz cos z r o b i ć . . . \ n ,
// O // © // ©
r o u t << "Abv s i e p rz e k o n a ć czy w t a b l i c y " " z n a l a z ł s i e \ n r z e c z y w i s c i e zad a n y t e k s t "w y p isu jem y j e j t r e s c \ n n a e k r a n \n " ; cout << <’****************************************k**^n ''
« s k o m u n i k a t .s t r () .. n u *******************************************\ n ;r <<
skomunikat.seekp(8, io3_base::beg);
sk o m u n ik a t << "XYZ"; f| c o u t << "W ta k im s t r i n g u można n aw et
II
O
©(
1156
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g "p o zycjonow ać w sk a źn ik p i s a n i a : \ n " << s k o m u n ik a t. s t r ( ) ;
// ©
skomunikat.s t r ("Nowa wstępna tresc niszczącą stara");
// ©
// Uwaga, jeśli mywasz kompilatora VC++ 6.0, to powyższe wywołanie funkcji // może w spowodować błt]d w trakcie wykonywania destruktora tego obiektu c o u t << s k o m u n ik a t. s t r () << e n d l ; d o u b le p i = 3 .1 4 1 5 ; sk o m u n ik a t << p i ; sk o m u n ik a t << ", a
dwa p i = " << 2 * p i ;
/ o o
c o u t << s k o m u n ik a t. s t r () << e n d l ; c o u t << "\n E u d u jem y in n y s t r i n g , p o tr z e b a " "nam nazwę ja k ie g o ś p l i k u " « e n d l; o s trin g s tre a m
s n a z w a _ p lik u ( "P ró b k a ", i o s : : a t e ) ;
//O ©
// Dla wielbicieli VC++ v. 6.0 - inna wersja poprzedniej instrukcji / / o s trin g s tre a m s n a z w a _ p lik u ; / / s n a z w a _ p lik u << "P ró b k a " ; in t in t in t in t
n r _ z e s ta w u = 167; n r _ d n ia = 9; n r _ m ie s ia c a = 5 ; n r ro k u = 2005;
s n a z w a _ p lik u << << << << «
s e t f i l l ( ' 0 ' ) << s e tw (5 ) << n r z e sta w u "_ z_ " « se tw (2) « n r _ d n ia « s e t w (2) << n r _ m ie s ia c a "_" << s e t w (4) << n r ro k u " .d a n e " ; //O ©
pomiar(snazwa_pliku.str() ) ; odczyt(snazwa_pliku.str().c str ());
Z/******************************************************.*.****,*.*
v o id p o m i a r ( s t r i n g nazwa)
//O ©
c o u t << "P o m ia r, a wynik zap isy w a n y w p l i k u : \ n << nazwa << e n d l;
/Z************************************************************* v o id o d c z y t ( c o n s t c h a r * nazwa) /O © c o u t << "O d czy t p o m iaru , z a p is a n e g o w p l i k u : \ n << nazw a << e n d l;
Na ekranie pojawi się następujący tekst Aby s i e p rz e k o n a ć czy w t a b l i c y z n a l a z ł s i e r z e c z y w iś c ie zad a n y t e k s t w y p isu jem y j e j t r e s c na e k ra n
"
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g
1157
***** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * _ 4 , temperatura oleju 156.71 stopni C
Awaria silnika
;;;**fiff***f******************************
W ta k im s t r i n g u m ożna naw et p o zy cjo n o w a ć w sk a źn ik p i s a n i a : A w a ria sXYZika 4 , te m p e r a tu r a o l e ] u 1 5 6 .7 1 s t o p n i C . . .M u sisz co s z r o b i ć ! ! ! Nowa w stę p n a t r e s c n is z c z ą c ą s t a r a 3 1 4 ę a dwa p i = 6 .2 8 c z a c a s t a r a Budujemy inny string, potrzeba nam nazwę jakiegoś pliku Pomiar, a wynik zapisywany w pliku: Próbka 00167_z_09_05_2005.dane Odczyt pomiaru, zapisanego w pliku:
P ró b k a 0 0 1 6 7 _ z_ 0 9 _ 0 5 _ 2 0 0 5 .d an e
Kilka ważnych uwag O A by skorzystać z k lasy o s t r i n g s t r e a m m u sim y na g ó rze p ro g ra m u um ieścić in stru k cję w łączającą d ek larację tej klasy. Jej n azw a pojaw i się w p rzestrzen i n a z w s t d : :, ale p o n ie w a ż w cześniej jest instrukcja u s i n g n a m e s p a c e s t d , d la te g o do tej klasy b ęd ziem y m ogli o d n o sić się bez k w alifikatora zak resu . © D efinicja obiektu k lasy ostringstream. M a o n nazw ę s k o m u n i k a t . Tą literą s rozpoczynającą tę n a z w ę chciałem tu zazn aczy ć, że ten o b iek t to strum ień. D alsza część n azw y p rzy p o m in ać nam b ęd zie, d o czego ten s tru m ie ń ma nam służy ć. W nim b ęd ziem y tw orzyli sk o m p lik o w a n y tekstow y k o m u n ik at. © O to jak w jednej in stru k cji, ten k o m u n ik at tw orzym y. Z a u w a ż w a ż n ą rzecz: instrukcja ta w y g ląd a tak, jakby chodziło o w y p isan ie tekstu n a e k ra n ie strum ie n ie m c o u t Ta sam a p ro sto ta. U żyw am y tu m a n ip u lato ró w , fo rm ato w an ia i tak dalej, ale rezu ltat zo staje zap isan y g d zieś w śro d k u w obiekcie s k o m u n ik a t. O P odobnie, jak w obec in n y ch strum ieni, i tutaj w p ro sty sp o só b m o ż em y sp raw dzić, czy operacja się pow iodła. © W obec strum ienia m o żn a stosow ać zw y k łe funkcje sk ład o w e. O czyw iście najczęściej w olim y z ro b ić to sam o za pom ocą m a n ip u lato ró w - ale w ied z, że i ta form a jest m ożliw a. © K olejna instrukcja d o p isu jąca jeszcze jakiś fra g m e n t tekstu. O Jeśli u zn am y że tek st ju ż jest g o to w y - m o żem y posłużyć się funkcją składow ą s t r () , która u d o stęp n i n am bieżącą treść. T utaj w ysy łam y ją na ek ran . © T ym czasem m ożem y z ty m tekstem z m ag azy n o w an y m w obiekcie s k o m u n i k a t pracow ać dalej. O to instrukcja pozycjonująca w sk aźn ik do p isa n ia tak, ze następ n a operacja p isan ia rozpocznie się od ó sm eg o zn ak u (licząc o d początku tekstu). © A by się przekonać, że w szy stk o odbyło się w ed łu g naszych p rz e w id y w a ń , w ypisujem y na ekranie bieżącą treść z obiektu s k o m u n ik a t. Spójrz n a w yd ru k ek ran u - praw da, ż e to działa?
1158
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień zapisujący do obiektu klasy s t r i n g © Jeśli w y w o łam y na rzecz tego ob iek tu funkcję s t r (c o n s t s t r i n g & )z a rg u m en tem (b ęd ący m stringiem lub C -stringiem ), to niniejszym cały dotychczaso w y te k st zostaje u su n ię ty i zastą p io n y tym now ym . Z n o w u m ożesz zobaczyć na ekranie, że tak się stało w istocie.
Uwaga: Jak już wspomniałem w poprzednim paragrafie, ta wersja funkcji s t r może przysporzyć kłopotóui jeślipracujesz z kompilatorem MS VC+-1v. 6.0 O to kolejna o p eracja wpisująca do n aszeg o tekstu w artości liczbow e? O dszukaj to m iejsce n a ek ran ie. Co się stało? Liczby zostały w p isa n e od początku zacie rając p ie rw sze z n a k i tego p o p rzed n ieg o tekstu. Dlaczego ta k się stało? Z dw óch pow o d ó w : 1- T en te k st był tak zw an y m tekstem w stęp n y m - czyli był wcześniej p o d a n y ja k o arg u m en t funkcji s t r ( c o n s t s t r i n g &) . ^
2. S tru m ień ten pracow ał w try b ie i o s : : o u t - b o takie jest dom nie m an ie k o n stru k to ra u ży teg o w linii © - więc p ie rw sz a operacja w p isa nia zaczęła pisać o p o czątk u , czyli niszcząc tekst w stęp n y .
Z w racam u w ag ę, ż e gdyby w obiekcie był nie „tek st w stę p n y " , ale taki, k tóry w pisaliśm y jak ąk o lw iek operacją p isan ia —to now y tek st d o p isałb y się grzecz nie d o jego końca. S praw a, o której rozm aw ialiśm y , d o ty c zy tylko m arnego losu tekstó w w stęp n y ch .
Nazwa pliku budowana przy pomocą obiektu ostringstream Z ajm iem y się te ra z zu p ełn ie innym tekstem , konkretnie b ę d z ie m y chcieli z b u dow ać n azw ę p lik u , na którą sk ład ają się n u m er p ró b k i, a także jakieś inne w artości liczbow e. U nas będą to n u m e r d n ia, m iesiąca i ro k . O © O to definicja ob iek tu o nazw ie s n a z w a _ p l i k u . K o rzy stam y tu z k o n stru k to ra, który p o zw ala na um ieszczenie w stęp n eg o tekstu. N ie z o stan ie on zatarty najbliższą operacją pisania, bo try b u s ta la m y na i o s : : a t e . D zięki tem u dalsze d o p isy w an ie ro zp o czn ie się na końcu tego w stępnego te k stu . Uwaga: Jeśli posługujesz się kompilatorem M S VC++ v. 6.0, to i tutaj może objaioić się niespodzieioane zachowanie. Wówczas tę instrukcję zastąp takimi dwoma ostringstream snazwa_pliku «
snazwa_pliku; "Próbka " ;
Ta możliwość nadawania wstępnego tekstu nie jest aż tak istotna, In/ uniemożliwiała Ci używanie kompilatora, który tego nie robi poprawnie. Skoro można obejść to inaczej —to nie ma dramatu. Tych tekstów wstęp nych używa się zresztą bardzo rzadko. O © O to instrukcja, w której buduje się n a z w ę p lik u . Jest to ta s a m o p ro ste, jak pisanie n a ekran. W rezultacie p o w stał tek st będący n a z w ą p lik u , ale nie p o d z iw ia m y go, tylko od razu w ysy łam y do jakiejś funkcji ( O © ) , która oczekuje o b ie k tu k lasy s t r i n g będącego n azw ą pliku.
1159
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g
O © M o że być jed n ak fu nkcja, która odbiera a rg u m e n t b ędący C -stringiem . (Taka je st p rzecież na p rz y k ła d funkcja o s t r e a m : : o p e n ). N ie m a p ro b lem u , w ó w czas d o w y rażen ia nazwa_pliku.str()
U
< -to w y r a ż e n ie je s t t y p u
string
d o łą czam y k a sk a d o w o jeszcze jeden człon - u ru ch o m ien ie funkcji składow ej c s t r . (Jest to funkcja sk ład o w a z klasy s t r i n g ) . nazwa_pliku.str () .c_str()
// * - t o w y r a ż e n ie je s t ty p u const char*
Funkcja ta sp raw ia, ż e obecnie całe to w y ra ż e n ie jest ju ż ty p u C -string. O © Z obacz d o funkcji, k tó ra odbiera to w y ra ż e n ie i zobacz na ek ra n -ja k w id ać i do tego n ad ał się re z u lta t pracy naszego o b iek tu klasy o s t r i n g s t r e a m .
N a koniec chciałbym podzielić się n astęp u jącą refleksją. C z y w iesz, kiedy zacząłem trak to w ać ob iek ty klasy o s t r i n g s t r e a m jak o sw o jo n e po d ręczn e n arzęd zie? W tedy, g d y zacząłem im d a w a ć n azw y nie tyle zw ią z a n e z tym , czy m są strumień, potok,
kar.ał_wyjściowy,
skryba
ale zw iązan e z tym , co z a ich pom ocą p ro d u k u ję komunikat, opis_awarii, nazwa_pliku, tytul_utworu
N o , m oże czasem , n a po czątk u , dodaję m ałą literę s - jak stru m ień .
22.24 S tru m ie ń c z y ta ją c y z o b iek tu k la s y s t r i n g Z a pom ocą stru m ien i m o żn a też w y czy ty w ać inform ację zn ajd u jącą się w jakim ś obiekcie klasy s t r i n g . Tak sam o, jak b y znaki te p rz y ch o d z iły z klaw ia tu ry lub pliku. Inn y m i słow y jeśli m a m y tablicę string schowek(" 7 .15");
w której zap isa n y jest p ew ien ciąg zn ak ó w —(u nas znaki: 7 , . , 1 , o ) — to m ożem y te zn ak i w czy tać i zin terp reto w ać tak, jakby były p is a n e na klaw ia tu rz e i w czy ty w an e instrukcją double liczba; cin » liczba;
A by tego dokonać, p o słu g u jem y się stru m ien iem , który płynie nie o d k law iatu ry , a od u k ry te g o w ew n ątrz stru m ien ia sc h o w e k p ry w a tn e g o obiektu k la sy s t r i n g . Klasa tak ich strum ienia n a z y w a się i s t r i n g s t r e a r a (Input STRING STREAM - czyli s tru m ie ń w czytujący ze s t r i n g u ) .
1160
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g Skąd n a św ięcie w zięła ta klasa - zobaczyć m ożesz na jej drzew ie genealogicz nym , czyl^jej g rafie pochodzenia (str. 1042). K lasa istringstream jest pochodną klasy istrcam, a zatem d zied ziczy w szy stk ie jej zachow ania - m ożem y więc sobie w obec niej używ ać o p erato ró w » , m an ip u lato ró w , czy zn an y ch nam funkcji składow ych read, get, a n aw et pozycjonow ać w skaźniki czytania, itd.
Jak to działa? S tru m ien io w i pokazujem y C -string, lub obiekt klasy s t r i n g , g d zie są znaki, które chcielibyśm y czytać tak, jak się czyta z pliku lub k law iatury. S trum ień kopiuje sobie tę treść i od tej p o ry m ożem y p rzep ro w ad za ć operacje w czy ty w ania frag m e n tó w tego tekstu. Np. wczytanie do zmiennych typu zmiennoprzecinkowego (jeśli ten frag ment, to jakiś zapis liczby). W d o w o ln ej chw ili m ożem y ten cały tekst zam ienić na in n y ("nowszy") i dalej p o słu g iw ać się tym stru m ien iem do kolejnych operacji w czy ty w an ia.
Kiedy możemy potrzebować usług tego strumienia istringstream? S zta n d aro w y m zasto so w an iem jest zam iana stringu (składającego się z g ru p y cyfr) na w arto ść liczbową. Tak na p rz y k ła d dzieje się w ted y , g d y uru ch am iając p ro g ram ktoś podaje d o d a tk o w y p aram etr liczbow y. Taki p aram etr - m im o że to liczba - system o p eracy jn y dostarcza do n aszeg o pro g ram u jako te k st - g ru p ę cyfr. W p ro g ram ie trzeba zam ienić ten z e sta w cyfr na w arto ść o b iek tu ty p u d o u b le . Do tego św ietn ie nad aje się stru m ień i s t r i n g s t r e a m . Jeśli z a m ie rz a m y skorzystać z te g o w sp an iałeg o n a rz ę d z ia , to - oprócz całego d zied zic tw a po klasach p o d staw o w y ch -
...mamy w klasie istringstream do dyspozycji następujące funkcje składowe K o n stru k to ry (m ają one p rz y d o m e k e x p l i c i t ) , oto ich d ek laracje w p ew n y m up ro szczen iu : i s t r i n g s t r e a m ( c o n s t s t r i n g & tekst); is tr in g s tr e a m ! ) ;
// //
Z w ykłe funkcje składow e: v o id s trin g
s tr(c o n s t s trin g s t r ( ) c o n s t;
& tekst) c o n s t ;
// u
Co robią te funkcje? O K onstruktor, k tó ry m bud u jem y s o b ie s tru m ie ń . A rg u m e n te m tym d o starczam y stru m ien io w i tekst, który m a b y ć z a chw ilę c zy tan y z e stru m ie n ia , (tak jakby były to z n a k i będące treścią ja k ieg o ś pliku lu b p rz y c h o d z ą c e z k law iatury).
1161
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g
© K o n stru k to r d o m n ie m a n y . U ży w am y go w ted y , gdy tekst, n a k tó ry m strum ień m a pracow ać, za m ie rz a m y p o d ać d o p ie ro za jakiś czas. Z ro b ić m ożem y to po n iższą funkcją. © T o jest funkcja s t r , dzięki której w dow olnej chwili m o ż em y przed staw ić stru m ien io w i n o w y tekst, na którym stru m ień ma dalej p raco w ać. © Ta w ersja funkcji s t r nie ma arg u m en tu . Funkcja ta zw raca (p rzez w artość) obiek t klasy s t r i n g zaw ierający tekst, który został jej w cześniej p rzysłany (p rzez nas) d o p racy .
►2.24.1 Prosty przykład użycia strumienia istringstream Z obaczm y d z iałan ie k lasy i s t r i n g s t r e a m w p rzy k ład o w y m p rogram ie. A by było ciekaw iej, w tab licy jest jeszcze d o d atk o w a treść.
tinclude #include using namespace std ;
//<-- bo u ż y w a m y istringstream
O
bool szukacz(istringstream & s, string k lu c z o w e , double & z m ie n n a ) ; /***+★★★******************************************************/ int m a i n O { double ld ; string wyraz; int li; char c; char t a b [] = "Abc"; string próbka(”2157.15 wtorek 0x44");
// ©
istringstream schowek(próbka);
// © // O
schowek >> ld ; cout << "Wczytana ld = " << ld « endl; ld = 2 * ld; cout « " 2 * ld = " << ld << endl; schowek » wyraz ; cout << "Wczytany wyraz = " << wyraz «
//
© // ©
endl;
schowek » hex » li; cout << "Wczytana (w zapisie hex) liczba li = « li << endl; cout « "To wczytaliśmy ze strumienia,\n który zawiera: << schowek.str() << endl;
// O
U © "
1162
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g
i f ( s c h o w e k . e o f ()) { c o u t << ” \nUwaga: F la g a i o s : : e o f b i t u s ta w io n a ! " << e n d l ; s c h o w e k .e l e a r ( sch o w ek . r d s t a t e () & ~ i o s : : e o f b i t ) ; // © // demonstracja możliwości pozycjonowania s c h o w e k .s e e k g (1, i o s : : b e g ) ; s c h o w e k .g e t( c ) ; c o u t << "\n w y d o b y ty zn ak c = " «
//O O c «
endl ;
schow ek . se e k g (7, i o s : : c u r ) ; s c h o w e k .g e t( c ) ; c o u t << "Siódmy zn ak d a l e j t o = " << c << e n d l ;
//O ©
sch o w ek , s e e k g ( 2 ) ; schow ek » l a ; c o u t << " L ic z b a w y dobyta w in n y sp o so b : " << ld «
//O © e n d l;
/ / -------------------------------------------------------------------------------------sch o w ek , s t r ("Honni s o i t q u i mai y p e n s e " ) ;
//O ©
c o u t << " \n Z m ie n ilis m y t r e s c s t r u m i e n i a . \n T e ra z j e s t t o : << s c h o w e k .s t r () ; s tr in g c y ta t; g e t lin e ( s c h o w e k , c y t a t , ' q ' ) ; c o u t << " \n W c z y ta n e : " « c y t a t « schow ek » w yraz; c o u t << "W czytane: " << w yraz «
cout «
s t r i n g (40,
"
//O ©
e n d l;
//O ©
e n d l;
<< e n d l ;
sch o w ek , s t r ( "C zynnik z y ro m a g n ety cz n y 3 .4 w zm o cn ien ie " / / O © 7 2 .6 d e c y b e le suma w p ła t 237500 p o w ię k s z e n ie " "m ik ro sk o p u 580 k r o t n e " ) ; d o u b le x ; if ( s z u k a c z ( s c h o w e k , ^ cout «
"a)
" p o w ię k s z e n ie m ik r o s k o p u " , x ) )
x = " «
x «
e n d l;
c o u t « " \n \n P r z e c z y ta m y t e r a z in n a l i c z b ę " « if ( s z u k a c z ( s c h o w e k , " z y ro m a g n e ty c z n y " , x ) ) cout «
"b)
x = " «
X «
//O ©
e n d l; //O ©
endl;
> c o u t << " \r.\n P rz e c z y ta m y t e r a z in n a l i c z b ę " << e n d l ; i f ( s z u k a c z (schow ek, "m oduł Y ounga", x ) )
//© ©
1163
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g cout «
"b)
x = " «
X «
endl;
} //************************************************************* bool szukacz (istringstream & s, string nazwa_danej, //©O double & zmienna)
//< //<
s trin g t r = s .s t r ( ) ; s t r i n g : : s i z e _ t y p e n r = t r . f i n d (n a z w a _ d a n e j) ; i f ( n r == s t r i n g : : n p o s ) ^ c o u t << " B la d : Nazwa d a n e j >" << n azw a_ d an ej << "< n i e z n a le z io n a " << e n d l; re tu rn f a ls e ; c o u t << "Nazwa d a n e j z n a le z io n a na p o z y c j i " « n r << e n d l; s . s e e k g ( n r + n a z w a _ d a n e j. l e n g t h ( ) ) ;
//©O
s t r i n g k; d o u b le w a r to ś ć ; s » w a r to ś ć ; i f ( ! s)
//< //(
c o u t << " B la d w czytyw ania w a r to ś c i o n a z w ie ' « n azw a_ d an ej « e n d l ; re tu rn f a ls e ; } e lse {
cout << "Za nazwa\n\t>" << nazwa_danej « "<\n wczytana wartość " « wartość « zmienna = wartość; return true;
Na ekranie pojawi się następujący tekst Wczytana ld = 2157.15 2* ld = 4314.3 Wczytany wyraz = wtorek Wczytana (w zapisie hex) liczba li = 68 To wczytaliśmy ze strumienia, który zawiera: 2157.15 wtorek 0x44 Uwaga: Flaga ios::eofbit ustawiona!
w ydobyty znak c = 1 Siódmy znak dalej to = t
L ic z b a w ydobyta w in n y sp o so b : 5 7 .1 5 Z m ien iliśm y t r e s c s tr u m ie n ia . T era z j e s t t o : Honni s o i t q u i m ai y p e n se W czytane: Honni s o i t W czytane: u i
endl;
//© ©
1164
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g Nazwa d a n e j z n a le z io n a na p o z y c ji 78 Za nazw a > p o w ie k sz e n ie m ikroskopu< w c z y ta n a w a r to ś ć 580 a) x = 580 P rz e c z y ta m y t e r a z in n a l i c z b ę Nazwa d a n e j z n a le z io n a na p o z y c ji 8 Za nazw a > zyro m ag n ety czn y < w c z y ta n a w a r to ś ć 3 .4 b) x = 3 .4 P rz e c z y ta m y t e r a z in n a l i c z b ę B la d : Nazwa d a n e j >modul Younga< n ie z n a le z io n a
Kluczowe punkty programu O P o n iew aż zam ierza m y p o słu ży ć się klasą i s t r i n g s t r e a m dlatego k o n ieczn e jest w łączen ie pliku n ag łó w k o w eg o < s trc a m > . © O to definicja obiektu klasy s t r i n g , k tóry zaw ie ra jakiś tekst. W id zisz tam m ięd zy in n y m i zap is, który m y ludzie ro zu m iem y jako zapis liczby. Nie zap o m in aj je d n ak , że jest to te k st składający się z jakichś cyfr. © Tak p ro sto d efin iu je się obiekt klasy i s t r i n g s t r e a m . A rgum entem k o n stru k tora in fo rm u jem y obiekt, z jakim tekstem ma p raco w ać. To tak, jakby p o w ie dzieć p lik o w i, jaką ma on zw arto ść. O P ierw sza operacja w czytania. Tutaj w czytanie n astęp u je d o obiektu ty p u d o u b l e, z a te m stru m ie ń w ie, że ma w czytać teraz jakieś zn ak i, p o czym zam ien ić je na w arto ść liczbow ą (typu d o u b le ) . © Ze to rzeczy w iście o d tą d jest liczba, a nie ciąg cyfr, p rzek o n u je nas operacja p o m n o ż en ia jej p rzez 2. N a ek ran ie m ożesz zo b aczy ć p o tw ie rd z e n ie tego m nożenia. © W czytanie z e stru m ien ia kolejnych zn ak ó w o p e ra to re m » . Jak p am iętam y , o p erato r ten p rzesk ak u je białe zn ak i poprzed zające w łaściw y tekst. Skoro teraz w czy ta n ie n astęp u je do obiektu k lasy s t r i n g , z a te m tam w łaśnie z n a jd z ie się w y raz „ w to r e k " . © Dalej w n aszy m stru m ien iu czeka n a w czytanie n astęp u jący wyraz: 0x4 4. Skoro w czytać chcem y coś do obiektu o n azw ie l i , a o b ie k t ten jest typu i n t , zatem p rzez d o m n iem an ie stru m ień sp o d ziew a się zap isu liczby w postaci d z ie sią tk o wej. Tak! N ie popełnij błędu sąd ząc, że liczba ro zp o czy n ająca się o d 0x zo stan ie au to m aty czn ie z in te rp re to w a n a jako zap is szesn astk o w y . Ta konw encja d o ty czy tylko k o m p ilato ra, n ato m iast s tru m ie n ie w ejściow e w tym się n ie kierują.
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g
1165
M usisz w y ra ź n ie p o in fo rm o w ać stru m ie ń , że to, co w id zi te ra z m a zin terp reto w ać jako liczb ę w z ap isie szesn astk o w y m (h ek sad ecy m aln y m ). Spraw ę załat w iam y p ro sty m m an ip u lato rem hex. schowek »
hex »
li;
© W tej instrukcji w y p isu jem y na ek ran ie liczbę, którą p rzeczy taliśm y . W ypisuje m y oczyw iście w z ap isie d ziesiątk o w y m , w ięc na ek ran ie p o ja w ia się liczba 68 (szesn astk o w o 0x44). © O to w y w o łan ie funkcji s t r ( ) , k tóra p o z w a la nam się d o w ie d z ie ć o cały tekst, z którego stru m ień czytał. Jak w id zisz z e k ra n u , jest to cały te k st, a nie jakiś frag m en t „już p rz e c z y ta n y " lub „jeszcze nie d o czy tan y ". C ały p ie rw o tn y tekst, bo przecież m o żem y chcieć go czytać po raz drugi. © T ym czasem s p ra w d z a m y stan stru m ien ia. N ie jest to nic d z iw n e g o , że teraz m a on u staw io n ą flagę e o f b i t . Przecież p rz e d chw ilą p rzec zy taliśm y go d o końca (Liczba „0x44" kończyła jego tekst). Jeśli chcem y dalej p racow ać z tekstem zło żo n y m w tym s tru m ie n iu , to p o w in niśm y n ajp ierw sk aso w ać flagę i o s : l e o f b i t . To cz y n im y w łaśnie w tej instrukcji.
OO Pozycjonujemy w sk aźn ik czytania z tego stru m ien ia na zn ak u n u m e r 1 (liczy się je od zera!). W następnej instrukcji odczytujem y ten zn a k (funkcją sk ła d o w ą g e t) . O © O to inne pozycjonow anie, tym razem w zg lęd em bieżącej po zy cji w skaźnika, czyli o 7 zn ak ó w dalej. O © C iekaw e zjaw isko: pozycjonujem y w sk a źn ik do czytania na d ru g im znaku od początku i o d tą d zaczynam y w czy ty w ać liczbę. Skoro o d tą d , to znaczy że u s ta w iliś m y w s k a ź n ik już p o z n a k a c h '2 ', 'V . S tr u m ie ń w ięc z a c z n ie w czytyw anie zn a k ó w '5', '7', 7 , T , '5 '. T o w czyta i z in te rp re tu je jako liczbę d o u b le . Z obacz n a ekranie, jaką o trzy m ał liczbę: 57.15 To tyle. Skończyliśm y zabaw ę z cały m tym tekstem p rzed sta w io n y m strum ieniow i. 27 O © M ożem y m u jed n ak dostarczyć teraz in n y te k s r 7 R obim y to dzięki funkcji s k ła d o w e j s t r (co n st s tr in g &) .C o p ra w d a a rg u m e n te m czy n im y zw y k ły C-string, ale oczyw iście wiesz, że to nie ro b i różnicy. Tułaj powstanie obiekt chwilowy klasy s t r i n g , którego argumentem konstruktora będzie nasz C-string. Ten obiekt chwilowy zostanie wystany funkcji s t r , a ona skopiuje jego treść do wnętrza strumienia (do jego bufora). O d tej pory m o żem y w ięc pracow ać z tą n o w ą zaw artością. O © Tu w idzim y, że w obec naszego obiektu k lasy i s t r i n g s t r e a m m ożem y n aw et posłużyć się funkcją g e t l i n ę . (To funkcja nie-składow a, ale zd efin io w an a d la naszej w y g o d y p rzez tw órców klasy s t r i n g ) . 27)
Starofrancuskie (ok. XII w) motto angielskiego Orderu Podwiązki: "Ten niech się wstydzi, kto dojrzał w tym coś nieprzystojnego".
1166
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g Funkcją tą w czytujem y d o obiektu cytat frag m en t tekstu, aż d o w ystąpienia o g ran ic zn ik a - litery q. (O granicznik ten jest w yjęty ze stru m ien ia, ale w y rzu ca ny; nie je st dołączony d o stringu cytat). © 0 Dalej w czy tu jem y operato rem » do obiektu klasy string. ja k w iadom o, w czy ty w a n ie tym spo so b em - w czytuje tylko jeden w yraz. P o n iew aż p o p rz e d n ia operacja „pożarła" ju ż literę 'q' z w yrazu qui, w ięc teraz w czy ta się tylko ”u i ”.
Z n o w u k o ń czy m y zab aw ę. T eraz je s t c o ś d la w yjątkow o am bitnych
O © O to p o d ajem y stru m ien io w i jeszcze inny tekst d o pracy. Tym razem zaw iera on jakieś d a n e d o p ro g ram u . Z asada jest taka, że najpierw n astęp u je n azw a danej (jakby: sło w a kluczow e), a zaraz za tym jest z a p is liczby. N a sz p ro g ram w różnych miejscach m oże chcieć dow iedzieć się o jakieś w arto ści. P o n iew aż będ zie to często w yw oływ ana czynność, d lateg o zrealizu jem y ją w postaci funkcji. O © O to w y w o łan ie takiej funkcji. Ma ono d o p ro w ad zić d o w czy tan ia w artości liczbow ej, która jest zap isan a zaraz za tekstem „pow iększenie m ik ro sk o p u ". T o definicja funkcji o n azw ie s z u k a c z , która w y k o n y w ać będ zie d la nas takie usłu g i. O to jej deklaracja. b o o l s z u k a c z ( i s t r i n g s t r e a m &s, s t r i n g nazwa_dancj, d o u b le &zmienna) ; N ie jest to, ż a d n a funkcja biblioteczna. To n a s z w łasny pom ysł. D o tej funkcji jak o a rg u m e n ty w ysyłać będziem y: ♦♦♦ stru m ień ty p u istringstream, w k tó ry m tkw i tekst, k tó reg o fragm ent b ęd ziem y chcieli w czytać, string - będący opisem , nazw ą dan ej, którą chcielibyśm y w czytać, ♦♦♦ zm ien n ą ty p u double - ( p r z e z referencję), dzięki tem u d o o ry g in ału tej zm iennej funkcja w p isz e nam w artość liczbow ą (jeśli tylko u d a się jej tę d a n ą o d n aleźć i p o p ra w n ie wczytać). Funkcja zw raca w artość (false).
bool inform ującą n a s o sukcesie (true) lu b porażce
Z obaczm y , jak fu n k q a ta realizuje sw oje zad a n ie. © 0 N ajpierw funkcja robi sobie lo k aln ą k o p ię tek stu , na którym p racuje stru m ień . 0 © W tej lokalnej kopii o d szu k u je p o d a n ą n a z w ę danej. Jeśli p o szu kiw anie się n ie udaje, to funkcja zw raca w arto ść false
0O
Jeśli zaś p o sz u k iw a n ie się u d a ło , to w iem y, na której pozycji w tekście z n ajd u je się (zaczyna się!) p o szu k iw an a n a z w a danej. 1 M ożem y w ięc zająć się p o zy cjo n o w an iem w sk a ź n ik a czytania tego stru m ien ia. Pozycjonow ać b ę d z ie m y nie na tej n azw ie , ale na miejscu tuż z a nią, bo tam e w e n tu a ln ie m o że zacząć się zapis
1167
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g
liczby. Stąd d o pozycji znalezionej funkcją fin d d o d ajem y liczbę b ęd ąc ą długością tej n a z w y . 3.seckg(nr + nazwa_danej .length ());
0©
W czytanie ew en tu a ln ej liczby o p erato rem » . W czytujem y w artość d o zm iennej lokalnej, na w y p ad ek , g d y b y coś się nie pow iodło, ż e b y w ów czas nie z n iszc zy ć dotychczasow ej w arto ści zm iennej.
0 © Jeśli rzeczyw iście się nie pow iodło, to p o z a w y p isan iem infor m acji funkcja zw raca w arto ść false. 0 © Jeśli zaś w czy tan ie o d było się p o p ra w n ie , to d o p iero w ted y w p isu jem y tę w arto ść liczbow ą d o p rzy słan e j zm iennej. Funkcja z w ra c a w artość true. W róćm y d o fu n k cji m a in O © Oto w czy ta n ie kolejnej w artości p rz y użyciu tej funkcji. Z a u w a ż , że w cale nie m usim y tu trz y m a ć się jakiejś kolejności. P rzedtem w czy ta liśm y jedną z ostat nich d an y ch w tekście, a teraz p y ta m y o jedną z p ie rw szy ch . N ie ma problem u. N asza funkcja s z u k a j sam a zn ajd zie, w którym m iejscu je st w y m ag an a d an a.
0©
O to w y w o łan ie funkcji s z u k a j , k tó re nie m oże d a ć p o p ra w n e g o w y niku. Ż ąd am y tu d a n e j o nazw ie m o d u ł Y ounga, której p rz e c ie ż w strum ieniu nie ma.
--------------
22.24.2
W czytyw anie argum entów w yw oływ an ia programu Z obaczyliśm y ju ż, jak w czytyw ać coś z obiektu klasy s t r i n g . M ożesz zapytać: „Dobrze, ale po co? Po co zapisywać liczbę w stringu za pomocą ciągu znaków - a za chwilę przetłumaczyć te znaki na liczbę. Nie można od razu?" C zasem rzeczyw iście nie m ożna. N a przykład, jeśli d o p ro g ram u przesyłasz jakąś liczbę z linijki w yw ołania p ro g ra m u program 6.33 Goethe 777.1
to te trzy p a ra m e try dostają się do w n ę trz a program u ja k o stringi. (M ów iliśm y o tym na str. 363 ). W p ro g ram ie natom iast, chcem y ta k p odane liczby u m ieścić w obiektach typu int czy double. Tak sam o, jakbyśm y w czytyw ali je z klaw iatury. M usim y więc d o k o n ać w czytania z form atow aniem - czyli w ła śn ie form atow aniem w ew n ętrzn y m za pom ocą klasy istringstream. Oto, jak to łatw o zrobić: iinclude using namespace std; iinclude
//< —bo
u ż y w a m y istringstream
/★*★★★★★****************'**★★•*'*:**************************★***** j
int main(int arge, char* argv[])
{
//
0
1168
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g d o u b le x , z; s t r i n g n a z w is k o ; c o u t << "P ro g ram " << a rg v [0 ] << "\nMa argum entów : " << a rg c -1 << e n d l; i f f a r g c < 4) f c o u t << "P ro g ram n a le ż y w ywołać z 3 arg u em tam i: " " l i c z b a , wyrazem, l i c z b a " << e n d l ; re tu r n -1 ; ) //
P ierw szy argu m en t
// © // © c o u t << " L ic z b a b e d a c a pierw szym argum entem : " << x << e n d l ; is trin g s tre a m
sp »
s p (a rg v [l]);
x;
// Drugi argumait odbieramy innym strumieniem is trin g s tre a m
// O
s l(a rg v [l]);
s l.s tr(a rg v [2 )) ; s l » n a z w is k o ; c o u t << "W czytane nazw isko = " «
// © n azw isk o «
e n d l;
// Można też tak n a z w isk o = a r g v [ 2 ] ; c o u t << "W czytane nazw isk o = " << n azw isk o << e n d l ;
// ©
// Trzeci argument dla odmiany wczytamy starym strumieniem sp // ale pamiętajmy, że jest on w stanie eofbit i f ( s p .e o f ()) { c o u t « " s p ma u s ta w io n a f l a g ę i o s : : e o f b i t " « s p .c le a r ( s p .r d s ta te ( ) & ~ i o s : : e o f b it) ; } sp .s tr(a rg v [3 ]); sp »
// ©
z;
c o u t << « << << <<
□
e n d l;
"Oto w a r t o ś c i p r z y j ę t y c h p a ra m e tro w \n " x « endl n a z w is k o << e n d l z << e n d l "suma ty c h l i c z b = " « (x+z) « e n d l ;
W rezultacie na ekranie zobaczymy P ro g ram C :\ s y m f o n i a _ p r z y k l a d y \ p r z y k l a d .e x e Ma argum entów : 3 Ma argum entów : 3
// ©
1169
Rozdział. 22. O peracje W ejścia/W yjścia Strum ień czytający z obiektu klasy s t r i n g Liczba bedaca pierwszym argumentem: 6 .3 3 Wczytane nazwisko = Goethe Wczytane nazwisko = Goethe sp ma ustawiona flagę ios: reofbit Oto wartości przyjętych parametrów
6 .3 3 Goethe 0.741
suma tych liczb = 7.071
Komentarz O Jak p am ię ta m y , przy słan e z linii w y w o łan ia p ro g ram u p a ra m e try d o stę p n e są w specjalnych tablicach. N astęp u jące w y w ołanie p ro g ra m u p r z y k l a d .e x e
6 .3 3
G o e th e
7 4 .1 e -2
sp o w o d u je u m ieszczen ie ich w tablicach i p rzy słan ie ich d o funkcji m a i n jako jej arg u m en ty . Jeśli o d b ie rz e m y je pod takim n azw am i, jak to jest w id o czn e w O , to w rezultacie m a m y tablicę, w której elem entam i są w sk a źn ik i d o następujących C -stringów : a r g v [ 0 ] - z aw ie ra w skaźnik d o C -stringu " C : \ s c i e z k a \ p r z y k l a d . e x e " oczywiście owa ś c i e ż k a zależy od tego na jakim dysku iw jakim katalogu jest ten program a rgv [1 ] - z aw ie ra w skaźnik d o C -stringu - "6.33" argv [2 ] - za w ie ra w skaźnik d o C -stringu - "Goethe" argv [3 ] - z a w ie ra w skaźnik d o C -stringu - " 7 4 . 1 e - 2 "
N atom iast a rg u m e n t a r g c (a rg u m e n t counter) o k reśla liczbę takich C -stringów — w n aszy m p rzy p ad k u 4. © Definicja stru m ien ia typu i s t r i n g s t r e a m , który zajm ie się w czytaniem a rg u m entu a r g v [ l ] - bo to w łaśn ie tu w definicji w y sy łam y konstruktorow i. W yrażenie a r g v [1] jest, co p ra w d a , w skaźnikiem d o C -stringu, ale nie m a problem u - sp ry tn y konstruktor zro b i kopię tego C -strin g u i złoży ją w sw oim p ry w atn y m obiekcie klasy s t r i n g . © Tak oto w czy tu je się arg u m en t d o zm iennej typu d o u b l e . To tutaj stru m ień zam ienił n am ciąg cyfr na w arto ść liczbow ą i um ieścił w obiekcie o n azw ie x. © D rugi a rg u m e n t w czytam y in n y m strum ieniem ( s l ) tego sam ego ty p u . Tym razem jest to a rg u m e n t będący nazw iskiem , więc m am y p rzy g o to w an y obiekt klasy string i to do niego w czy tu jem y prostą instrukcją sl >> nazwisko;
Z w racam u w a g ę , że poniew aż „G o eth e" to jest w y raz, a nie zapis liczby, więc nie m usieliśm y w tym celu w ytaczać arm aty pod ty tu łem stru m ień . W ystarczy łaby tu zw ykła... © ...instrukcja p rzy p isan ia C -stringu d o obiektu klasy s t r i n g . O p erato r = z klasy s t r i n g d o k o n ałb y popraw nego skopiow ania. To jest łatw e. Problem w y stęp u -
1170
Rozdział. 22. O peracje W ejścia/W yjścia Ożenek: strum ień s t r i n g s t r e a m - czytający i zapisujący do stringu je tylko w tedy, jeśli argum entem jest zestaw cyfr, które mają być zam ienione na liczbę. Tak w łaśnie będzie z trzecim argum entem . Zapis TEKSTOWY tej liczby brzmi tak "74 . l e - 2 " . To w łaśnie m usim y kom uś pow ierzyć d o zam ian y na binarną w arto ść liczbow ą. M oglibyśm y w ięc tutaj zdefiniow ać trzeci obiekt strum ienia klasy i s t r i n g s t r e a m , a on p o p raw n ie w ykonałby za d a n ą pracę. Z ro b im y jed n ak inaczej: dla odm iany użyjem y jednego z ju ż istniejących stru m ieni. N ie m a znaczen ia - którego. Ja w ybrałem ten o n azw ie sp . N ieza leżn e o d tego, czy w ybralibyśm y s p , czy s l - należy pam iętać, że: o b y d w a w ykonały sw ą dotychczasow ą pracę do końca, czyli oba m ają u staw ioną flagę i o s b a s e : : e o f b i t . Jeśli chcem y któryś z nich użyć, to trzeba tę flagę skasow ać. © O to instrukcja kasująca flagę e o f b i t w stru m ien iu o nazw ie s p . Jesteś już chyba ek sp ertem od k aso w an ia tej flagi. Jeśli nie, to zostań - bo to jest chyba najbardziej imponująca (kolegom) instrukcja. No przyznaj, że kilka tygodni temu na widok tej instrukcji dostałbyś zawału. Naucz się właśnie tej instrukcji, bo innych, tak imponu jących, w tej książce już nie będzie. © S kasow aw szy tę flagę m ożem y pod rzu cić stru m ien io w i n o w y tekst do pracy. W łaśnie ten trzeci arg u m en t. Robim y to za pom ocą w y w o łan ia funkcji składo wej s t r ( c o n s t s t r i n g &). Funkcja ta kop iu je (do bufora stru m ien ia) poka zan y jej tekst trzeciego argum entu... © ...i ju ż n astęp n ą instrukcją » m ożem y d o k o n ać w czy tan ia. S trum ień nam po słu szn ie zam ien i ten w ykładniczy zapis liczby na wartość liczbow ą, po czym um ieści ją w zm iennej o nazw ie z. © N astęp n a instrukcja pokazuje w szystkie w czy tan e arg u m e n ty , a tak że i to, że x oraz z nie są tek stam i - tylko rzeczyw iście liczbam i, na których m o żn a p rzep ro w ad zać operacje arytm etyczne.
22.25 Ożenek: strumień stringstream- czytający i
zapisujący do stringu Istnieje klasa p o c h o d n a s t r i n g s t r e a m łącząca w łaściw ości k la sy i s t r i n g s t r e a m i o s t r i n g s t r e a m . N a grafie d zied zic zen ia s tru m ie n i (str. 1042) m oż na łatw o zobaczyć, sk ąd ta klasa p o ch o d zi. Skoro jest ona k lasą po ch o d n ą od klasy i o s t r e a m - to zn aczy , że I jest to stru m ień , k tóry m o że z a ró w n o pisać jak i czy tać. W tym p rz y p a d k u ch o d zi oczyw iście o o p eraq 'e nie na p lik u , n ie na ekran ie /k la w ia tu rz e , ale na jakim ś tajem n iczy m m iejscu w p am ięci. T ym tajem ni czym m iejscem jest b u fo r stru m ien ia - coś w ro d zaju o b iek tu k lasy s t r i n g będącego p ry w a tn ą w łasnością teg o stru m ie n ia .
Rozdział. 22. O peracje W ejścia/W yjścia Ożenek: strum ień s t r i n g s t r e a m - czytający i zapisujący do stringu
1171
W klasie stringstream do dyspozycji następujące funkcje składowe K o n stru k to ry (mają one p rz y d o m e k e x p l i c i t ) :
stringstream (
string const & tr e ś ć _ w s tę p n a , // O ios_base: : openmode tr y b = ios :: out | i o s : : i n ) ;
stringstream( ios_base: : openmode
tr y b =
ios::out I ios::in); / /
0
Z w ykłe fu n k cje składow e:
string void
s t r ( ) const; s t r (const string & tr e ś ć jw s tę p n a ) const;
// 0 // O
Co robią te funkcje? To sam o, co o d p o w ie d n ie funkcje, k tó re om ów iliśm y w k lasie o s t r i n g s t r e a m . Jed y n a różnica, to in n e do m n iem an ie try b u p racy strum ienia. O K o n stru k to r o dw óch arg u m en tach . <♦ P ierw szy m arg u m e n tem d ostarczam y tekst, k tó ry m a być zap isa n y w o b iek cie już od chw ili jego urodzenia. To p o p ro stu jakby w arto ść w stę p n a jego "pliku". C o z ty m tekstem w stę p n y m będzie się d ziało dalej? •
Jeśli sk o rzy stam y z d o m n iem an eg o trybu i o s : : o u t , to zap isy w an ie d o tego strum ienia ro zp o czn ie się od p o czątk u , w ięc ten w stę p n y tekst będzie sto p n io w o zacierany, zastę p o w an y now ym i znak am i. (No chyba, ż e w o d pow iedniej ch w ili sam i przesu n iem y w skaźnik pisania w e w łaściw sze miejsce).
•
Jeśli sk o rzy stam y z trybu i o s : : a t e , lu b i o s : : a p p to z a p i syw anie ro zp o czn ie się od końca tego w stęp n eg o tekstu.
♦♦♦ D ru g im arg u m en tem ok reślam y w łaśnie tryb p racy . P rzez d o m n ie m a nie jest to O R -ow anie try b ó w i o s : : i n I i o s : : o u t ,
czuli: czytanie od początku tekstu, a zapisywanie także (zatem: niszczące powoli znaki "wstępne "). Jeśli wolałbyś nie zniszczyć "tekstu wstępnego ", to użyj trybu i o s : : in | i o s : : o u t | i o s : : a t e
0
K on stru k to r z jed n y m (do m n iem an y m ) argum entem , określającym tryb p racy. D om n iem an ie jest i o s : : i n | i o s : : o u t.
© To jest funkcja s t r , dzięki której stru m ień u d o stęp n ia n am tekst, k tóry w nim obecnie jest (jakby w jego "pliku"). Funkcja ta zw raca (p rzez w artość) o b iek t klasy s t r i n g zaw ierający tekst, k tó ry jest p rzec h o w y w an y przez stru m ień . W d ow olnej chw ili w yw ołać funkcję s t r ( ) , by o trzy m ać obecny "obraz" p rzec h o w y w a n e g o /w y tw o rz o n e g o w strum ieniu tekstu.
1172
Rozdział. 22. O peracje W ejścia/W yjścia Ożenek: strum ień s t r i n g s t r e a m - czytający i zapisujący do stringu O Ta w ersja funkcji s t r uniew ażnia dotychczasow ą zaw artość, jakby zeruje całą dotychczasow ą treść, nadając now ą. Po jej w ywołaniu p isan ie rozpocznie się od now a.
22.25.1
Przykładow y program posługujący się klasą s tr in g s tr e a m Z obaczym y w ięc, że w tym stru m ien iu m ożna pisać i ró w n o cześn ie (naw et z innego m iejsca) czytać. Operacji tych dokonujem y w buforze strum ienia, będącym czym ś w rodzaju p ry w atn eg o obiektu klasy s t r i n g . # in c lu d e < io s tre a m > u s in g n am esp ace s t d ; l i n c l u d e < s s tre a m > // <-- bo używamy i s t r i n g s t r e a m /★ ★ ***★ ★ ★ ****★ ★ ★ ★ ***+*★ ****★ ****★ *+*★ ★ ******★ ★ ★ *****★ *★ ******★ / i n t m ain{) { i n t i =0, j = 0; d o u b le x =0; s t r i n g s t r e a m pp;
// O
pp «
// 0
"032 10 4 5 .6 7 o siem " ;
c o u t << "W s tr u m ie n iu j e s t " << p p . s t r ( ) pp » pp »
i; j »
// © x;
c o u t << " i = "<< i « «
<< e n d l ;
",
X = "«
", j = "<< j
X
<< " \ n l c h suma = "<< ( i + j + x) << e n d l ; c o u t << "W s tr u m ie n iu j e s t : \ n " «
p p .s tr O
<< e n d l ;
// załadowanie nowego tekstu p p . s t r ( "S łow a n i e ty lk o d z iw ią s i e swoim s ą s i e d z t w e m " ) ; / / Cz c o u t << "W s tr u m i e n iu j e s t : \ n " << p p . s t r O << e n d l ; p p .s e e k g ( 1 6 ,
// można także pozycjonować kursory pisania i czytaiua io s _ b a s e ::b e g ); // wskaźnik czytania // ©
// i stosować funkcje składowe klas podstawowych c h a r zn ak ; p p .g e t(z n a k ); c o u t << "Znak 1 6 - ty = " « z n a k << e n d l; p p .s e e k p (2 0 );
// wskaźnik pisania
II O
// wpisywanie (zacierające dotychczasowe znaki) pp « "ABCD"; l/O c o u t << "W s t r u m i e n i u j e s t : \ n " << p p . s t r O << e n d l ; //------------------------------------------------s t r i n g s t r e a m h a m le t(" T o be o r n o t ", io s ::in | io s ::o u t | io s :: a te ) ;
Rozdział. 22. O peracje W ejścia/W yjścia Ożenek: strum ień s t r i n g s t r e a m - czytający i zapisujący do stringu
1173
cout << "W strumieniu hamlet jest:\n " << hamlet.str() << endl; hamlet «
"to be. This is a ąuestion" ;
// ©
cout << "Po wpisaniu w strumieniu hamlet jest:\n " << hamlet.str() << endl; string pierwsze; hamlet » pierwsze ; cout << « << <<
_ // ©
"W strumieniu hamlet jest:\n " hamlet.str() "\n a pierwsze słowo to >" pierwsze « "<" << endl;
}
Jeśli masz dobry, zgodny ze standardem kompilator, to na ekranie zobaczysz W strumieniu jest 032 10 45.67 osiem i = 32, j = 10, x = 45.67 Ich suma = 87.67 W strumieniu jest: 032 10 45.67 osiem W strumieniu jest: Słowa nie tylko dziwią sie swoim sąsiedztwem Znak 16-ty = d w strumieniu jest: Słowa nie tylko dziwABCDie swoim sąsiedztwem W strumieniu hamlet jest: To be or not Po wpisaniu w strumieniu hamlet jest: To be or not to be. This is a ąuestion W strumieniu hamlet jest: To be or not to be. This is a ąuestion a pierwsze słowo to >To<
Ciekawsze miejsca tego programu O D efinicja stru m ien ia pp, jest on ty p u stringstream. K orzystam y tu z d o m n ie m a n e g o try b u i o s : :in | i o s : : o u t i nie p o d a je m y ż a d n e g o te k stu w stęp n eg o . 0 W p isan ie zaw artości p rzep ro w ad za m y za pom ocą o p erato ra « . N a d o w ó d , że u m ieszczen ie w strum ieniu tekstu się udało, w n astęp n ej instrukcji w y p isu jem y jego z aw arto ść funkcją sk ład o w ą str (). © Ta i n a stę p n a instrukcja to w czytanie operatorem » d o obiektów ty p u int i d o u b l e w artości liczbow ych. Z nam y to z p arag rafu o strum ieniu i s t r i n g stream. W następnych instrukcjach w ypisujem y na ekranie w artości tych obiektów .
1174
Rozdział. 22. O peracje W ejścia/W yjś cia Ćwiczenia O Funkcją sk ład o w ą str (const string &) m ożem y zad ać nową zaw artość tekstu w stru m ien iu . (Jakby podm ienić całą zaw artość jego „pliku"). © M ożliw e je st oczywiście pozycjonow anie w skaźnika czytania. Tu w idzim y p rz y k ła d pozycjonow ania na znaku 16-tym licząc od początku tekstu. W nastę pnych instrukcjach dokonujem y czytania i po czym w yśw ietlam y znak na ekranie, p o to, by się przekonać, że to rzeczywiście p o p raw n ie działa. © S trum ień u m ie też zapisyw ać (w sw oim buforze-stringu), zatem pozycjonujem y w sk aźn ik d o pisania na d w u d ziesty m znaku tekstu, a n astępnie (© ) d okonuje m y tam z ap isu . W yśw ietlenie zaw artości bufora tego strum ienia na ek ran ie p rzek o n u je nas, że rzeczyw iście m ogliśm y selektyw nie w pisać w określone miejsce. Aby zobaczyć
in n y rodzaj k o n s tr u k t o r a d e fin iu je m y d ru g i s tr u m ie ń tej
s a m e j klasy
© M a on n a z w ę h a m le t. Tym razem zadajem y p ew ien tek st w stępny, oraz tryb ios::in
| ios::out I ios::ate
in fo rm u jący strum ień, żeby o d razu ustaw ił w sk aźn ik p isan ia na końcu tego tekstu. D zięk i tem u, tego tekstu w stępnego nie zniszczy m y . Uwaga: Jak już kilkakrotnie wspominałem, kompilator MS VC++ w. 6.0 nie respektuje tu zasad standardu, jeśli chodzi o te teksty wstępne. © O to op eracja w staw ienia stru m ien ia tekstu do stru m ien ia. W rezultacie tekst do p isu je się d o końca tekstu w stęp n eg o . © Jak w iem y , w skaźnik p isan ia i czytania są od sieb ie niezależne, w ięc jeśli z a p ra g n ie m y w czytać z tego stru m ien ia jakieś sło w o - w czyta się „To", bo obecnie w sk a ź n ik czytania u staw io n y był na sam y m p o czątk u tekstu.
2 2 .2 6 Ć w ic ze n ia i
W rozdział o operacjach w e/w y ten można podzielić na trzy zasadnicze części. Omawia liśmy w nich strumienie płynące o d /d o trzech rodzajów ujść/źródeł. Pierwsze - to strumienie, które płyną na ekran i od klawiatury. Gdzie (ogólnie mówiąc) jeszcze mogą płynąć strumienie, czyli - innymi słowy - jaka jest grupa druga i trzecia? Mamy obiekt typu d o u b le , w którym jest zapisana jakaś wartość, np. 4723.55 Wyjaśnij różnicę między binarną, a znakową (tekstową) reprezentacją tej wartości. Która forma
jest najczęściej krótsza? Jeśli na ekranie wyświetlamy liczbę, to czy przesyłana jest tam w postaci binarnej czy w postaci tekstowej? Jeśli z klawiatury przyjmujemy liczbę, to czy przez strumień płynie w postaci binarnej czy tekstowej? Czy nazwa c o u t oznacza klasę czy obiekt? Co to są strumienie standardowo zdefiniowane (czasem mówi się predefiniowane). Ile ich jest? Wymień je wszystkie.
Rozdział. 22. O peracje W ejścia/W yjścia Ćwiczenia
1175
Jaka jest różnica między cerr i clog ? Jakiego typu są strumienie, które kryją się pod nazwą cout icin? Co jest ujściem strumienia cout? Co jest źródłem strumienia cin? Jeśli z jakimś strumieniem pracujemy posługując się operatorem « , to czy jest to formatowane, czy nieformatowane wypisywanie informacji? A w przypadku operatora
» ? Dokończ poprawnie następujące zdania na temat domniemań strumienia cout przy wypisywaniu informacji operatorem « . Jeśli nie zadecydujemy inaczej, to: a) - obiekty całkowite int, long zostaną wypisane w systemie liczenia.. (dwójkowym / ósemkowym / dziesiątkowym / szesnastkowym). b) - obiekt typu char zostanie wypisany jako.. c) - obiekt typu f loat, double zostaną wypisane z dokładnością wynoszącą.. d) - wskaźnik typu char* lub unsigned char* jako.. e) - pozostałe typy wskaźników są wypisywane jako.. Jak sprawić, by wskaźnik wypisać adres kryjący się w e wskaźniku typu char*? M a m y następujące instrukcje int m; cin >> m Wybierz poprawne zakończenie następującego zdania. Przy wczytywaniu strumieniem cin liczby całkowitej wystukane na klawiaturze cyfry zostaną zinterpretowane jako zapis liczby w postaci.. (binarnej / dziesiątkowej / szesnastkowej / to zależy od tego, jakie znaki rozpoczynają zapis tej liczby). Przy wczytywaniu liczby zmiennoprzecinkowej wolno postawić znak spacji a) przed cała cyfrą b) między znakiem, a właściwą liczbą c) przed znakiem wykładnika Czy w zapisie liczby całkowitej, którą wystukujemy na klawiaturze mogą pojawić się jakieś litery? Co zrobić, by przyjmowana z klawiatury operatorem » liczba została zinterpretowana jako liczba w zapisie szesnastkowym? Wczytujemy trzy liczby całkowite następującą instrukcją int m, k, j ; cin >> m >> k »
j;
Na klaw iaturze napisano następujące znaki 4
04
0x4
Jakie wartości w rezultacie tej operacji będą miały obiekty m, k, j? Czy zdarzy się coś dodatkow o? Mamy wystukać na klaw iaturze dwie liczby całkowite, które zostaną przyjęte przez strum ień cin operatorem » . Zwykle powinniśmy je oddzielić jakimś białym znakiem:
1176
Rozdział. 22. O peracje W ejścia/W yjś cia Ćwiczenia spacją, tabulatorem lub klawiszem Enter. Kiedy nie musimy tego robić, czyli kiedy te liczby mogą być do siebie niejako „przyklejone"? Kiedy priorytet operatorów » i « przy operacjach wejścia/wyjścia może mieć znaczenie? Podaj przykład kłopotliwej instrukcji. Na stronie 1037rozmawialiśmy o tym, co robić, jeśli w czasie podawania trzech danych (określających nowe współrzędne wektora) użytkownik popełni błąd. Teraz, kiedy potrafisz już pracować z flagami stanu błędu strumienia, zmodyfikuj przeładowany ope rator » z tego programu tak, by pracował przyjaźniej, (czyli według omawianej zasady „pełny sukces albo stara, poprzednia wartość wszystkich współrzędnych"). Jeśli wobec obiektów klasy podstawowej możemy posługiwać się przeładowanymi operatorami » i« , to czy dziedziczy się je tak, iż mogą zostać również wywołane dla obiektów klasy pochodnej? Jakie działanie ma ustawienie flagi formatowania ios_base: :skipws w strumieniu wejściowym, a jakie w wyjściowym? Jak zachowa się strumień wyjściowy, gdy ustawiona jestflaga ios base: :internal, a za pomocą 100znaków (funkcja width (100),lub manipulator setw (100) )polecimy m u wypisać a) - liczbę dodatnią, b) - liczbę ujemną, c) - string jednowyrazowy, d) - string trzywyrazowy.
Mamy obiekt typu bool. Co zobaczymy na ekranie, jeśli wypiszemy ten obiekt operatorem « ? Co zrobić, by na ekranie strumień sam wypisał nam wartość true lub false?
Czy poprawna jest sytuacja, gdy żadna z flag dec, oct, hex nie jest ustawiona? Czy poprawna jest sytuacja, gdy ustawione są dwie z tych flag? Jakie działanie ma flaga ios::showbase? Jak najprościej sprawić, by wypisywaną strumieniem liczbę dodatnią poprzedzał znak plus? W ćwiczeniu tym chodzi o to, byś pokazał jak bardzo różne znaczenie m a wartość precision w przypadku trzech sposobówwypisywania liczby zmiennoprzecinkowej. Oto pytanie: W strumieniu wyjściowym cout obecnie obowiązujący parametr precision m a wartość 6. Napisz jak na ekranie zostanie wypisana liczba 91.66666666, gdy a) ani flaga f ixed, ani flaga scienzific nie są ustawione b) ustawiona jest flaga f ixed b) ustawiona jest flaga scientif ic
Czy można za pomocą jednego wywołania funkcji składowej ios_base: :setf ustawić więcej niż jedną flagę? Uzasadnij. Wyjaśnij jak to jest możliwe, że funkcja ios : :setf zwraca (jeden) rezultat, w którym są zapisane stany wszystkich flag formatowania? Załóżmy, że chodzi nam o ustawienie flagi scientific w strum ieniu cout. Napisz dwa warianty wykonania tego zadania:
Rozdział. 22. O peracje W ejścia/W yjścia Ćwiczenia
1177
a) za pomocą funkcji setf, b) za pomocą manipulatora. W poprzednim ćwiczeniu ustawiona została flaga scientif ic. Jak dwuargumentową funkcją setf sprawić, by znowu obowiązywało domniemanie? C z y m różni się funkcja setf od funkcji unsetf? Czy w pewnych warunkach funkcja setf może skasować jakąś flagę? Jaka jest zasadnicza różnica między funkcją ios: :setf, a funkcją ios: :f lags? Jak, posługując się funkcją funkcji ios : :f lags (), sprawdzić ustawienie jednej z flag formatowania w strumieniu cin. Chcemy wypisać liczbę całkowitą na ekranie. Czy ijak można określić, jaką minimalną szerokość, którą może zająć ten wypis? A jakz można określić maksymalną szerokość? Za pomocą odpowiednich funkcji możemy zadać takie parametry formatowania stru mienia jak precyzja, szerokość (w id th ), znak wypełniający. Które z tych parametrów ustawia się trwale, a które jednorazowo? Funkcja width ma wpływ na ustawienie sposobu wypisywania liczb i tekstów. Czy zadziała też na sposób wypisywania: a) jednego znaku? b) wartości wyrażenia typu bool? c) adresu przechowywanego we wskaźniku? d) wartości liczbowej przechowywanej w polu bitowym? Czy można użyć funkcji ios: :width przy wczytywaniu C-stringu i jakie będzie działanie? M a m y taki fragment programu char w [35] ; char *t = w ; cin.width(sizeof(w)) ; cin >> w ; cin.widthfsizeof(t)) ; cin >> t ; Ile maksymalnie z n a k ó w przyjmie pierwsza i druga pokazana tu instrukcja wczytywania znaków ze strumienia cin? Wyjaśnij do czego służy funkcja filii kiedy można zobaczyć efekt jej pracy. Jeśli w jakimś strumieniu są ustawione flagistanu formatowania, to jak (jedną instrukcją) te ustawienia przenieść na inny strumień? Czy manipulatory hex, dec, oct zastosowane do strumienia mają efekt jednorazowy czy długotrwały? D o strumienia cout można wstawić a) manipulator endl, b) manipulator flush, c) znak nowej linii '\n'. Wyjaśnij, jak zachowa się strumień w każdej z tych sytuacji? Jaka jest różnica między manipulatorami ios : :ws i ios::skipws? Jeśli w strumieniu cout ustawiona jest flaga ios: :scientif ic, a do strumienia „wpuścimy" manipulator f ixed. Jaka jest sytuacja flag ios: :f ixed i ios: :scien tif ic?
1178
Rozdział. 22. O peracje W ejścia/W yjś cia Ćwiczenia Czy jeśli używamy manipulatora io s : : s c i e n t i f i c - konieczne jest dołączenie jakie goś pliku nagłówkowego? Kiedy to jest wymagane, a kiedy nie?
)C Zdefiniuj swój manipulator, który będzie się nazywał „cen t r u j " . Ma on następować bezpośrednio po standardowym manipulatorze setw i ma sprawiać, że jeśli najbliższy wyraz wypisuje się na ilości znaków większej niż długość tego wyrazu, to wyraz będzie justowany nie do lewej, nie do prawej, ale po środku danego obszaru. Przykładowo, jeśli będzie to wypis wyrazu „xxx" na polu o szerokości 11 znaków, to wypis będzie wyglądał tak (znak minus oznacza tu spację). ------ x x x------Oczywiście zależnie od tegoczy liczba znaków w wyraziei szerokość pola jest parzysta / nieparzysta może to być naprawdę dokładne centrowanie, lub tylko przybliżone. Ale nie bądźmy drobiazgowi - masz tu sobie tylko udowodnić, że umiesz zdefiniować m anipu lator.
Ą Manipulator z poprzedniego ćwiczenia zbuduj tak, by mógł przyjmować param etr (argument) oznaczający szerokość pola, na jakim ma nastąpić najbliższy wypis. Czyli chodzi o to, by teraz już nie było konieczne poprzedzanie go manipulatorem setw . Ma on sam zadać szerokość obszaru i na tym obszarze (lepiej lub gorzej) wycentrować wyraz. ) ( W paragrafie o definiowaniu swoich manipulatorów z parametrem - jest przykład manipulatora przyjmującego jeden argument (parametr), Zmodyfikuj ten manipulator tak, by przyjmował jeszcze drugi argum ent odpowiadający za znak wypełniający. Pracując ze strumieniami posługujemy się wobec typów wbudowanych a) operatorami » i « b) funkcjami g e t, p u t c) funkcjami w r ite , read Które z tych operacji są formatowanie, a które nieformatowane? Dlaczego funkcja składowa strumienia wejściowego g e t ( ) , która jako rezultat ma zwrócić znak, ma zdeklarowany typ rezultatu jako i n t (a nie ch ar)?
W klasie i s t r e a m są funkcje składowe g e t i g e t l i n e - obie przyjmujące takie same trzy argumenty. Jaka jest między tymi funkcjami różnica? Porównaj je z jeszcze inną, tym razem globalną funkcją o nazwie g e t l i n e . Jaka jest różnica między funkcją i s t r e a m : : re a d , a funkcja i s t r e a m : : readsom e? Funkcja i s t r e a m : : r e a d wczytuje bajty do miejsca pokazywanego wskaźnikiem char* . Jak zatem można wczytać dane bjnarne będące stanowiące obraz obiektu jakiejś klasy? Jaka jest różnica między funkcjami i3 t r e a m : :u n g c t oraz i s t r e a m : :p u tb a c k ?
Jeden (krótki) wyraz możesz przyjąć z klawiatury do tablicy znakowej za pomocą na przykład takich dwóch instrukcji ch ar w y ra z [1 5 0 ]; n) c i n >> w yraz ; b) c in .r e a d s o m e ( w y r a z , s i z e o f ( w y r a z ) ) ; Czym będzie się różniła treść tablicy wyraz w zależności od zastosowanej instrukcji? Jeśli posłużymy się funkcją i s t r e a m : : i g n o r e bez żadnego argum entu, jaki odniesie to skutek?
1179
Rozdział. 22. O peracje W ejścia/W yjścia Ćwiczenia
Funkcja is tr e a m : : g c o u n t pozwala otrzymać pew ne informacje na temat ostatniego wczytania danym strumieniem. O jakich typach operacji informuje - formatowanych, czy nie formatowanych? Jaka jest zasadnicza różnica między fukcjami i s t r e a m : :p e e k i i s t r e a m : : g e t? Oczym może poinformować nas funkcja i s d i g i t ? Jeśli wystukany na klawiaturze znak przyjmiemy np. funkcją g e t, a potem zwrócimy go funkcją p u tb a c k , to gdzie on wraca? W strumieniu o s tr e a m mamy funkcje p u t i w r i t e . Która z nich służy do operacji formatowanych, a która do nieformatowanych. Która się nadaje do operacji binarnych? Wymień trzy zasadnicze klasy strumieni, którymi się posługujemy się przy pracy z plikami dyskowymi. Czym się różnią? Czy w pracy ze strumieniami płynącymi do plików dyskowych możemy się posługiwać manipulatorami? Plik dyskowy może zawierać informację binarną lub informację tekstową. Jaka jest m iędzy nimi różnica?
Jeśli pisząc na klawiaturze stworzysz plik zawierający tablicę matematyczną funkcji si nus, z dokładnością do szóstego miejsca po przecinku, to czy taki plik jest plikiem tekstowym, czy binarnym? Czy można stworzyć strumień przeznaczony do pracy z plikiem nie określiwszy, z którym plikiem ma on pracować? Jaka jest różnica między likwidacją strumienia, a użyciem wobec niego funkcji c lo s e ? Jak można otworzyć plik znajdujący się w katalogu innym niż ten, z którego uruchomio no program? Chcielibyśmy otworzyć strumień do pliku, którego nazwa nie jest C-stringiem, lecz jest zapisana w obiekcie klasy s t r i n g . Skoro funkcja o p en oczekuje C-stringu, co najproś ciej zrobić? Tryby otwarcia pliku i o s : : in , i o s : :o u t - określają czy strumień ma służyć do pisania czy czytania pliku. Do czego zatem potrzebujemy pozostałych trybów: tru n c , ap p , a l e , b in a ry . Na dysku jest plik o rozmiarze 1000 bajtów. Otwieramy go polecając strumieniowi tryb i o s :: o u t i przeprowadzamy operację zapisania w tym pliku 20 bajtów. Plik zamy kamy. Jaki ma teraz przybliżony rozmiar? Otworzyliśmy nowy plik w trybie tekstowym i zapisaliśmy tam 100 bajtów, posługując się funkcją w r ite . Plik zamknęliśmy. Powinniśmy mieć, zatem, plik o rozmiarze 100 bajtów. Czasem tak jest rzeczywiście, a czasem nie. Od czego to zależy? Chcemy otworzyć do pisania plik, który musi już wcześniej istnieć na dysku. Jaką użyć kombinację trybów otwarcia? Wymień flagi stanu strumienia i omów ich znaczenie. Czy funkcja i o s : : f a i l ( ) io s : : fa ilb it?
zwraca rezultat
oznaczający
Jak inaczej można napisać instrukcję i f ( !strumień) . . . Przeprowadzamy kaskadowo operację wczytania:
bieżący
stan
flagi 22
1180
Rozdział. 22. O peracje W ejścia/W yjś cia Ćwiczenia i n t k, m, p ; c i n >> k >> m >> p ; Użytkownik wpisał w linijce trzy liczby i wcisnął Enter. Nie zauważył jednak, że pierwsza liczba zawierała omyłkowo znak ' w' zamiast jednej z cyfr. Natomiast dwie pozostałe liczby są poprawne. Jaką wartość umieści strumień w obiektach m i p? Napisz instrukcje kasującą w strumieniu c in flagę io s :: f a i l b i t . Napisz dwa sposoby ustawienia w strumieniu c in flagi i o s : : f a i l b i t . Napisz instrukcję kasującą w strumieniu wejściowym s s s dw ie flagi io s : : f a i l b i t i i o s : : e o fb it.
X
Napisz program, w którym bedzie wielokrotnie wywoływana funkcja przyjmująca liczby podzielne przez 7. W sytuacji, gdy użytkownik poda liczbę niepodzielną przez 7, powinien zostać rzucony wyjątek. Od tej pory program wywołuje już wielokrotnie inną funkcje - uznająca tylko liczby parzyste. Podanie liczby nieparzystej ma wówczas spowodować rzucenie wyjątku. Jeśli tak sie właśnie stanie, program ma sie wtedy zakończyć wypisując stosowną informację. Obie wspomniane funkcje nie mogą wypisy wać niczego na ekranie. Ewentualne wyjaśnienia powodu błędu mogą być przesłane do funkcji m ain, tylko za pomocą obiektu wyjątku (składnik m essage). Napisz edytorem tekstu jakiś 10 linijkowy fragment wiersza Gałczyńskiego. Załóżmy, że ma on N znaków. Napisz także program, który: a) Otworzy ten plik i wypisze na ekranie wszystkie znaki tak, że pojawią się kolejno znaki o numerach 0, N, 1, N -l, 2, N-2,... b) Przeliczy wyrazy z tego wiersza. c) Wypisze ten wiersz na ekranie zamieniając znak spacji na trzy spacje. d) Wypisze ten wiersz w taki sposób, że najpierw będzie wers ostatni, potem przedostatni, i tak dalej - aż do linijki pierwszej. e) To samo, co powyżej, ale teraz wypis ma zostać przeprowadzony do nowego pliku tekstowego. Uwaga: Jeśli ćwiczenie jest trudne, to dla ułatwienia tekst Gałczyńskiego można zastąpić tekstem piosenki. Co się stanie, gdy w pliku do pisania pozycjonujemy wskaźnik do pisania na pozycji -6 względem początku pliku? LXXXVI B
B
I
Co się stanie, jeśli w pliku do pisania pozycjonujemy wskaźnik do pisania na pozycji +6 względem końca pliku? Co się stanie, jeśli w pliku do czytania pozycjonujemy wskaźnik do pisania na pozycji +6 względem końca pliku? Czy wpisanie 6 dodatkowych znaków w środek pliku tekstowego powoduje, że jego dotychczasowa długość się zmienia i o ile? Napisz program, który zapisze do pliku binarnego wartość obiektu d o u b le . Na przyk ład niech ma on zawartość równą liczbie n, z bardzo dużą dokładnością. (Przypominam, że nawet jeśli dokładność wynosi 200 cyfr po przecinku - i tak obiekt typu d o u b le ma s i z e o f (d o u b le ) bajtów. (Najczęściej 8). Zamknij ten plik. N astępnie w innym miejscu programu otwórz ten plik do czytania i przeczytaj tę wartość binarną. Napisz program, którym jest 200 elementowa tablica obiektów typu d o u b le . Poszcze gólne elementy tej tablicy mają wartości będące wielokrotnością liczby 0.01. Tzn.: 0.01,
Rozdział. 22. O peracje W ejścia/W yjścia Ćwiczenia 0.02,0.03, Następnie niech program zapisze do pliku binarnego (funkcją w rite ) warto ści z tej 200 elementowej tablicy, a tablicę wyzeruje. Następnie niech program zapyta użytkownika o numer elementu tablicy, który chciałby poznać. Jeśli użytkownik odpo wie, że na przykład nr 173, pozycjonuj odpowiednio wskaźnik do czytania, przeczytaj tę liczbę, wstaw do tablicy we właściwe miejsce i pokaż tę liczbę na ekranie, po to, by się przekonać, czy operacja Ci się udała. (Powinieneś wtedy zobaczyć wartość 1.73). Zadanie z poprzedniego paragrafu zmień tak, by teraz była to 200 elementowa tablica obiektów jakiejś klasy, która ma trzy składniki-dane. Typu double, typu i n t , typu char.
Napisz program, w którym będzie funkcja: int
raport(int nr_probki, int liczba_masowa, string izotop);
W funkcji tej, na podstawie argumentów tej funkcji należy otworzyć do pisania tekstowy plik o nazwie: Raport_xxxx_reakc j i_LLL_ZZZZZ.txt
gdzie xxxx - oznacza tu czterocyfrową liczbę numer probki LLL - oznacza tu trzycyfrową liczbę 1iczhę ma sowa ZZZZZ -jestdowolnym stringiem określającym izotop Jeśli wywołujący funkcję umieścił w stringu izotop kilkuwyrazowy tekst, to w funkcji zastąp białe znaki tego stringu znakami podkreślenia. W pliku tym zapisz symboliczny tekst, a następnie opuść funkcję. (Czy potrzebujesz zamykać ten plik?). W funkcji main napisz wywołania tej funkcji z rożnymi wartościami argumentów. O poprawności pracy programu przekonasz się sprawdzając czy w bieżącym katalogu pojawiły się pliki o nazwach odpowiadających argumentom każdego z wielu wywołań funkcji raport.
Program z poprzedniego ćwiczenia zmodyfikuj tak, by funkcja raport w tym samym wywołaniu tworzyła dwa pliki raportu. Jeden o nazwie wg schematu: Raport_xxxx_reakc ji_LLL_ZZZ2Z .txt Drugi o nazwie wg schematu: Raport_xxxx_ZZZZ_LLL. t x t Funkcja powinna te obie nazwy plików konstruować używając dwukrotnie tego samego strumienia ostringstream. Napisz program, który wywoływany będzie z wieloma parametrami będącymi bądź to liczbami (zmiennoprzecinkowymi), bądź to wyrazami. Parametrów tych może być dowolnie dużo i nigdy nie wiesz, który z nich będzie liczbą, a który słowem.
Np. program 3.14 Horacy 2 11.2 15 Ofelia Poloniusz 3e-3 Laertes W programie wszystkie parametry, które są zapisem liczb poumieszczaj w tablicy dou ble. Wszystkie nie-liczby zgromadź w tablicy obiektów typu string. Wypisz na ekranie zawartość obu tablic.
1181
1182
23
Rozdział. 23. Projektow anie program ów orientowanych obiektowo
Projektowanie programów orientowanych obiektowo o tarliśm y w reszcie do o statn ieg o rozdziału tej k siążki. W iem y już, jak p o słu g iw ać się językiem C++. M asz oto w ręce n a rz ę d z ie d o p ro g ram o w ania o b iek to w o orientow anego - w iesz, jak tego n a rz ę d z ia u ży w ać, nie w iesz tylko k ied y i p o co. P rzy zn am się, ż e g d y św ieżo n au czy łem się C++, m ia łe m ten sam problem : w iedziałem , jak się buduje klasy, czy definiuje obiekty, n ie w ied ziałem tylko co, w p rzy p ad k u pisan ia konkretnego p ro g ra m u , m am zd efin io w ać jako klasę i co robić z jej obiektam i.
D
W tym ro zd ziale porozm aw iam y w łaśn ie o tym , jak w p rz y p a d k u pisania k o n kretnego p ro g ra m u w ym yślić klasy, które nam u p ro szczą i u p rzy jem n ią p ro gram o w an ie. Inaczej m ów iąc b ęd ziem y tu m ów ić, nie ty le o ob iek to w o o riento w an y m języku p rogram ow ania, co o obiektow o o rien to w a n ej technice^ p ro g ram o w an ia. T radycyjne techniki p ro g ram o w an ia, którym i p o słu g iw ałeś się, C zytelniku, w innych zn a n y c h Ci językach p ro g ra m o w a n ia , p o zw alały C i na pisan ie m ałych i d u ży ch p ro g ra m ó w z lepszym czy g o rszy m rezu ltatem . T eraz zaś pojawia się now a technika p ro g ram o w an ia: p ro g ra m o w a n ie o b ie k to w o orien to w an e. Co to oznacza w p rak ty c e? C zy wobec te g o tam te techniki n ie są ju ż w ażn e? O d p o w ied ź jest taka: nie m usisz w cale sto so w ać techniki o b ie k to w o o rien to w a nej. N aw et p ro g ram u jąc w C++ m o ż e sz stosow ać sta re sp o so b y , d o których jesteś tak p rz y w ią z a n y . To tak, jak z e stary m p o czciw y m ch irurgiem , który polega tylko na in stru m en tach ch iru rg iczn y ch zn an y ch m u od czasu studiów . O
Technice, a nie tcchonologiil
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Przegląd kilku technik program ow ania
1183
Jest rzec zą b ard zo d e lik atn ą w y tłu m aczen ie m u , by u ż y w a ł n arzę d zi n ow o czesn y ch . Jesteś d la m nie w łaśnie ta k im chirurgiem . S p ró b u ję Cię p rzek o n ać, że p ro g ra m u jąc tech n ik ą obiektow o o rien to w an ą zao szczęd zisz w iele w y siłk u . Jeśli jesteś m e n a d ż e re m p rzew o d ząc y m zespołow i p ro g ram istó w , to sto so w an ie p rzez Tw ój z e sp ó ł tej techniki p o zw o li zm niejszyć k o sz t pracy n ad projektem .
23.1 Przegląd kilku technik programowania A by so b ie d obrze u św iad o m ić czym jest w n aszy ch rękach ob iek to w o o rien to w a n y języ k C++, n ajp ierw przyjrzyjm y się k ilk u p o d staw o w y m technikom p ro g ra m o w a n ia , które p ra w d o p o d o b n ie stosujesz. T echniki te p rzed sta w iam w p o rz ą d k u chronologicznym , a więc jest to h isto ria o tym , jak p ro g ram o w an ie s ta w a ło się coraz łatw iejsze.
23.1.1
Programowanie lin iow e - m ó w ią c obrazow o po leg ało na tym , że p o szczeg ó ln e instrukcje pro g ram u p isało się jedna p o d d ru g ą. N a górze był p o czątek p ro g ram u , g d zieś na dole kon iec p ro g ram u , a p o m ięd zy - w szystkie instru k cje - łącznie z d ziesiątk am i lub se tk a m i instrukcji g o t o . P ro g ra m pracow ał na d an y ch , które w szystkie b y ły d o stęp n e w czasie całego p rzeb ieg u p rogram u - d ziś nazw alibyśm y je glo b aln y m i. N ie było zm iennych lokaln y ch ; jeśli raz w jakiejś definicji użyliśm y n a z w y zm iennej k - to p rzep a d ło - n ig d z ie już definicja tej n azw y nie m ogła się p o w tó rzy ć. O becność licznych instru k cji skoków g o t o sp raw iała, że d łu ższy p ro g ra m był n iezro zu m iały dla in n e g o p ro g ram isty - był to k o d , który n azw an o o b razo w o : „kod a la sp ag h etti". Jeśli p ro g ram u jesz w języku BASIC (w wersji b ez in s tru k q i GOSUB), to sto su jesz w łaśn ie tę technikę p ro g ram o w an ia. P o w ied zm y dosadniej: jeśli p ro g ra m u jesz w BASICU to znaczy, że zatrzym ałeś się na tym etapie.
23.1.2
Programowanie proceduralne (czyli "orientowane funkcyjnie") G d y p o w stało - o d razu u z n a n e zostało za o g ro m n y krok w now oczesność. T ech n ik a ta cechow ała się tym , że w program ie m o ż n a było w y o d ręb n ić części realizu jące pew ne o k reślo n e czynności. T ym i częściam i były p ro ced u ry (funkcje). W ram ach takiej jednej funkcji m ożna było definiow ać d o d atk o w e zm ie n n e - tzw. zm ienne lokalne. Lokalne - g d y ż d o stęp n e tylko d la niej. P rzy k ład e m języka p ro g ram o w an ia w ym uszającego tę technikę p ro g ra m o w a nia jest język FORTRAN. O gó ln ie m ów iąc p ro g ram o w an ie proceduralne po leg a na tym , by pisząc p ro g ram dla danego p ro b lem u , zam ienić ten p ro b lem na serię z a d a ń do
1184
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Przegląd kilku technik program ow ania w y k o n an ia. T e konkretne z ad a n ia realizują w łaśnie funkcje. W ykonyw anu takiego p ro g ram u , to określona sekw encja w yw ołań różnych funkcji. N ap isan ie dobrego pro g ram u tą techniką polegało na tym, by zdecydow ać jakie funkcje będą potrzebne, n astęp n ie zdefiniow ać je, po czym starać się w ym yślić najlepszy alg o ry tm w yw oływ ania ich. N iew ątp liw ą w ad ą tej techniki program ow ania jest to, że zajm uje się on
23.1.3
Programowanie z ukrywaniem danych Jest to krok d o p rzo d u , w sto su n k u do techniki poznanej p o p rzed n io . Polega on na w zb o g acen iu program u w m ożliw ość zapakow ania d an y ch w jednym m o dule. W ten sposób dan e te m ogą być traktow ane jako g ru p a - pew n a całość. Tę technikę pro g ram o w an ia stosujesz, jeśli p rogram ując w PA SC A U u p o słu g u jesz się tzw . rek o rd am i, w zg lęd n ie g d y program ując w języ k u C (klasycznym ), p o słu g u jesz się strukturam i. Jeśli nie m a sz dośw iadczeń z ż a d n y m z tych ję zy k ó w program ow ania, a chciałbyś tę technikę sobie jakoś uśw iadom ić, to p o słu ż ę się term inam i z p o zn an eg o ju ż języka C++: jest to tak, jakbyś posłużył się klasą, która m a tylko sk ład n ik i d a n e - a w szystkie publiczne. W czym jest lepsza ta technika o d p o p rzed n io om ów ionej? C hociażby w tym , że w yw ołując funkcję m ożesz w y słać d o niej w szystkie d a n e naraz. M oduł tak (stru k tu ra), p rzesy ła n y jest jako je d en arg u m en t. W ażniejsze jednak, że d an e, które „w ż y c iu " p o w iąza n e są ze sobą logicznie - są także p o w ią z a n e ze sobą w p ro g ra m ie . Jeśli jesteś na tym etap ie p ro g ram o w an ia - to z łatw ością p rzy jd z ie Ci p rzesta w ienie się n a technikę obiektow o orien to w an ą.
23.1.4
Programowanie obiektow e - program owanie „bazujące" na obiektach Jest w Polsce często m ylone z p ro g ram o w an iem o b iek to w o o rien to w an y m . A n gielską n a z w ę tej techniki konstrukcji p ro g ram ó w : object based design - m ożna p rzetłu m a czy ć jako: p ro g ram o w an ie bazujące na o b iek tach . W stosunku do techniki p o p rzed n iej - innow acja p o leg a na tym , że z g ru p o w a n y m d an y m dodaje się m eto d y po stęp o w an ia z nim i - m y w term inach C++
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Przegląd kilku technik program ow ania
1185
pow ied zielib y śm y : funkcje składow e. P ow staje w ted y nie tylko m o d u ł kryjący w sobie d an e, ale w ręcz nowy typ danej. Są to u k ry te w m o d u le d an e, plus m ożliw e d ziałan ia n a nich. P ro g ram o w an ie tą techniką cechuje się tym , iż d a n y m z g ru p o w a n y m w m oduł (obiekt), nie m ów i się już jak mają coś zrobić, ale tylko co m ają z r o b ić (To: jak m ają robić, w ied zą na podstaw ie sw oich funkcji składow ych). N ied o g o d n o ścią tej techniki jest fakt, ż e poszczególne ty p y d a n y c h są sobie obce. Typ danej opisujący np. sam ochód nie ma nic w sp ó ln e g o z typem opisu jący m pojazd. Jest mu tak sam o obcy, jak liczbie zm ien n o p rzecin k o w ej. Z n ac zy to, że jeśli funkcja może przyjąć, jako arg u m en t, jed en ty p dan ej, to nie m o ż e przyjąć innego. Dla tego innego typu m u si być z d e fin io w an a osobna funkcja, choćby jej ciało miało być id en ty czn e, jak już istniejącej. W zw iązku z ty m m odyfikacja p ro g ram u polegająca na d o d a n iu u lep szo n eg o ty p u danej w sto su n k u d o typu ju ż istniejącego - jest b a rd z o kłopotliw a i w y m a g a zdefinio w a n ia od now a w szystkich funkcji, na których m a on pracow ać.
£3.1.5
Programowanie Obiektowo Orientowane (OO) N ajchętniej n azy w ałb y m to: program ow anie obiektow o o rientow a(L )ne. Jest to technika obiektow a w zbogacona o d zied ziczen ie i polim orfizm (czyli funkcje w irtu aln e). S praw ia to, że istniejący kod p o trafi się sam zo rie n to w ać w trakcie p ra c y program u, z jakim obiektem p rzy szło m u w danej chw ili pracow ać i o d p o w ied n io na to reaguje. Jest to jakaś „w y ższa inteligencja" d o d a n a do p ro g ram u . N ie zro z u m żle nazw y tej techniki: to nie m y p ro g ram u jąc jesteśmy „zo rien to w an i na obiek ty ". To kod p ro g ram u jest zdolny się sam zo rien to w ać z jak im obiektem p racu je - dlatego napisałem orientow a(L )ne. N ie lansuję tej n a z w y , bo na p ew n o się nie przyjmie - ale p o w tarzaj sobie to czasem . T ego typu technika doskonale odzw ierciedla zależności klas o b iek tó w otacza jących nas w realn y m świecie. To d lateg o tak d obrze nadaje się d o opisania św ia ta realnego. M im o że lubię p o w tarzać, iż w p ro w ad zen ie techniki obiektow o orientow anej jest rew olucją w p rogram ow aniu - to sam ch y b a w idzisz, że jest to raczej ew olucja: technika O O jest logicznym rozw inięciem technik p o p rzed n ich .
J ę z y k C + + n i e j e s t j e d y n y m , który u m o ż l iw ia t ę t e c h n i k ę p r o g r a m o w a n i a . M a on je d n a k jed n ą c e c h ę szczególną:
M ożna w nim stosow ać technikę OO w sto p n iu , który się już o p an o w ało - lub nie stosow ać jej w cale. M ówimy - język ten jest językiem h y b ry d o w y m u m o żliw ia n aw et program ow anie „p ó ł na p ó ł" - trochę tej techniki, trochę tam tej. To dlatego C++ jest tak popularne. U m ożliw ia łagodne przejście w świat pro g ram o w an ia OO. Z techniki OO stosujem y tylko to, czego się d o tej pory nauczyliśm y. N ie m u sim y od razu u m ieć w szystkiego. W języku C ++ można n ap isać program techniką proceduralną, lub n a w e t - o zgrozo - tech n ik ą linio w ą. Program iści C zaczynając C++ w ybierają sobie najpierw z niego ty lk o jakieś
1186
Rozdział. 23. Projektow anie program ów orientowanych obiektowo O wyższości program ow ania obiektowo orientowa nego nad Świętami Wielkiej Nocy ro d zy n k i - np. referencje, potem nieśm iało oswajają się z coraz bardziej w y sz u kanym i elem entam i. Inne języki program ow ania - takie, które w ym agają o d razu wejścia na głęboką w odę i sto so w an ia p rogram ow ania obiektow o o rientow anego od razu - m ają m oim z d an iem m niejsze szanse na taką popularność.
23.2 O wyższości programowania obiektowo orientowanego nad Świętami Wielkiej Nocy Z bierzm y sobie te cechy p ro g ram o w an ia OO, które sp raw iają, że jest ono lep sze od tradycyjnych technik p ro g ram o w an ia, którym i z a p e w n e posługiw ałeś się do tej pory. (Przez tradycyjne techniki uznaję te, które m o żliw e są w klasycznym języku C). Będzie to p o w tó rzen ie rzeczy, p oznanych w różnych miejscach tej książki. Teraz zo b aczm y je na tle technik, którym i p o sługiw ałeś się do tej pory.
Reusability - wtórne użycie wcześniej zdefiniowanych fragmentów kodu T radycyjne m eto d y p rogram ow ania nie kładły nacisku na m ożliw ość w tó rn eg o użycia k o d u funkcji. N ie chodzi o to, że funkcję m ożna w y w o łać d w a razy - bo przecież m ożna w yw ołać n aw et m ilion razy - to ż a d e n cud. O co w ięc chodzi? O tóż w dotychczasow ych technikach p ro g ram o w an ia jeśli funkcja przy jm o w ała jako a rg u m e n t stru k tu rę o n azw ie p o j a z d , to nie m ogła zostać w y w o łan a dla stru k tu ry samochód. Dla sam o ch o d u trzeba było n ap isać id en ty czn ą funkcję jeszcze raz, tyle, że ze zm ien io n y m typem arg u m e n tu form alnego. P ro g ram o w an ie O O um ożliw ia i w ręcz zachęca d o w tó rn e g o u ży w an ia k o d u funkcji. Jeśli tylko klasy (pojazd - sam ochód) są p o łączo n e zw iązk am i d z ie d z i czenia, to funkcja pracująca na arg u m en cie pojazd n ad aje się d o p racy z a rg u m e n tem klasy sam ochód. O szczęd zam y w ten sp o só b w ysiłku p o w tó rn eg o k o d o w an ia funkcji, pro g ram staje się p rzez to m niejszy, a w reszcie jakakolw iek późniejsza z m ian a p o stęp o w an ia w sto su n k u do p o ja z d ó w nie w y m ag a w p ro w ad zan ia tych sam ych zm ian p o stęp o w an ia w sam o ch o d ach - sam o ch o d y są przecież ta k ż e pojazdam i.
Extensibility - rozszerzalność, rozbudowalność T radycyjne m e to d y p ro g ram o w an ia b ard zo u tru d n ia ją e w e n tu a ln e p ó źn iejsza m odyfikacje p ro g ram u . Jeśli p ro g ra m pracu je na obiektach będących np. r ó ż n y mi ty p am i sam o ch o d ó w , to w w ielu miejscach p ro g ra m u są zap ew n e ta k ia frag m en ty kodu: Jeśli to R enault - to zró b tak... Jeśli to Porsche - to zró b siak... Jeśli Rolls-Royce - to z ró b ow ak...
Rozdział. 23. Projektow anie program ów orientowanych obiektowo O wyższości program ow ania obiektowo orientowanego nad Świętami Wielkiej Nocy
1187
T akie fra g m e n ty są rozsiane p o całym program ie. W y o b raź sobie, że p ro g ram p o w sta w a ł w w ięk szy m zespole i p ew n e g o dnia Tw ój s z e f m ó w i Ci, że w p ro w a d za się d o p ro g ra m u now ą m ark ę sam o ch o d u - Ferrari. M u sisz teraz z aa n g ażo w ać w szy stk ich lu d z i po to, by w sw oich częściach o d n a le ź li w p ro g ram ie w szy stk ie te liczn e miejsca i do p isali linijkę: | Jeśli Ferrari, to zró b tak... W yższość tech n ik i obiektow o orien to w an ej polega na ty m , że żad n y ch takich m odyfikacji robić nie trzeba. D zięki polim orfizm ow i (w y w o ły w an iu funkcji w irtu aln y ch ) istniejący już kod potrafi zareag o w ać na n o w e ty p y obiektów . To dlatego, ż e w ta k im program ie o b iektow o oricn to w a(l)n y m nie ma w cale linijek typu: Jeśli R enault, to tak... w ym ieniających w szystkie sam ochody, na których p ro g ra m pracuje. Jest za to w takim m iejscu tylko jedna linijka: Dla tego konkretnego ty p u sam ochodu (sam się zorientuj jakiego) w y k o n aj p rzew id zian ą dla niego funkcję. W ierz mi, k o rzy ść z takiego ro zw iązan ia oblicza się w tysiącach dolarów . Doś w iad czy łem teg o osobiście. D aw niej (w latach osiem dziesiątych), g d y kw itło p ro g ra m o w a n ie p ro ced u ral ne, w y m ó g łatw ej m odyfikow alności nie był jeszcze tak istotny. Dziś - g d y pro g ram y stały się ogrom nie ro zb u d o w an e i pracują n a d nim i rów nocześnie dziesiątki lu d z i nie b ard zo w iedzących, co robią koledzy z in n eg o zespołu - dziś m usi istnieć łatw o ść w szelkich m odyfikacji. K ażda g o d z in a pracy nad projek tem to o g ro m n e koszty. M o d elo w an ie św ia ta rzeczyw istego
Jeśli p ro g ram o d n o si się do jakiegoś zag ad n ien ia (system u) z e św iata rzeczyw is tego, to p isan ie pro g ram u m etodą proceduralną polega na rozbiciu danego zag ad n ien ia na zb ió r liczb oraz funkcji, które na tych liczbach pracują. Po prostu sam a m atem aty k a. P rogram o w an ie w technice obiektow o orientow anej po leg a na zb u d o w an iu w program ie m o d eli obiektów ze św iata rzeczyw istego (np. czek ó w bankow ych, pionków d o gry , albo w nętrza u k ła d ó w elektronicznych). Praca program u to ożyw ienie tych obiektów i pozw olenie im na o d eg ran ie swojej roli. O tym w szystk im s z c z e g ó ło w o m ów ić b ędziem y na następnych stronach. Ł atw iejsze o p ro g ra m o w a n ie sy ste m u okienek
P rogram ow anie O O bardziej przystaje d o now ego w izeru n k u kom puterów . Na przestrzeni lat b a rd z o zm ieniły się sposoby rozm aw iania k o m p u tera z użytko w nikiem . D aw niej program zad aw ał pytania i oczekiw ał n a odpow iedzi. Później zasto so w an o technikę polegającą na tym, że p ro g ra m w ypisyw ał na ekranie kilka ew entualności i py tał o n u m er tej, którą m iał wybrać. Słowem: daw niej ro zm o w a z użytkow nikiem polegała na w y p isan iu krótszej lub d łu ż szej linijki tekstu.
1188
Rozdział. 23. Projektow anie program ów orientowanych obiektowo O wyższości program ow ania obiektowo orientowa nego nad Świętami Wielkiej Nocy Dziś tak już się nie robi. Przed u ży tkow nikiem na ek ran ie otw ierają się okienka, pojaw iają się na ekranie ikony, przyciski, które należy w skazać na ek ran ie m yszk ą - cały ten zestaw, k tó ry zn asz zapew ne z u ży w an ia system ów , w których n azw ie pojaw ia się słow o window (okno). T akie pro g ram y nie jest łatw o pisać w technikach tradycyjnych (ale oczywiście d a się). Do takich p ro g ram ó w doskonale nadają się języki obiektow o orientow ane. N a ekran ie w id z im y przecież obiekty klasy ikona, obiekty klasy m enu itd. P ro g ra m ow an ie o b iek to w o orien to w an ie ułatw ia zadanie program iście m ającem u napisać taki pro g ram .
W ięk szy o d siew błędów
P ro g ram o w an ie pro ced u raln e k ład ło p o dstaw ow y nacisk na definiow anie fu n kcji, k tó re o d zw iercied lać m iały p ew n e czynności konieczne do w ykonania. P am iętasz z a p e w n e algorytm y p ro g ram ó w rysow ane w postaci schem atów blokow ych. D ane w tej technice nie są zin teg ro w an e z funkcjami. D an a typu d o u b le re p re zentująca te m p eratu rę m ogła zostać w ysłana ja k o arg u m en t funkcji o b l i c z _ p o l e _ k o l a ( d o u b le ) i nie zostałaby w y k ry ta żad n a n ie z g o d n o ść m im o, że zac h o w an ie ob 1 i c z _ p o 1 e__ko 1 a nie jest zach o w an iem , które stosuje się w obec tem p eratu ry . Przy sto so w a n iu techniki obiektow o orientow anej m o że być to od razu w y k ry te jako błąd. Funkcja nie m oże być zasto so w an a d o d a n y c h (obiektów ), na których pracow ać nie jest upraw n io n a. Już n a etap ie kom pilacji zo stan iem y p o in fo rm o w ani o n ied o p u szczaln y m zasto so w an iu funkcji i u c h ro n i nas to przed błęd am i logicznym i. To d lateg o m ów i się, ż e jeśli program w C + + u d a się p o p raw n ie skom pilow ać, to jest d u ża szan sa, ż e od razu będzie d z ia ła ł po p raw n ie. P rzy p ro g ram o w an iu pro ced u raln y m trzeba się o wiele w ięcej nam ęczyć p rzy u r u cham ianiu p ro g ram u .
U ogólnianie b e z ko n ieczn o ści p re c y z o w a n ia s z c z e g ó łó w
Jeśli w tradycyjnej technice p ro g ra m o w a n ia chciałeś zd efin io w ać kolejkę to m usiałeś w y ra ź n ie określić jakie e lem en ty mają w takiej kolejce stać. C zyli, ab y uzyskać u o g ó ln ien ie jakim jest kolejka, najpierw szczeg ó ły („kolejkow icze") m usiały być ściśle określone. W p ro g ram o w an iu O O jest o d w ro tn ie. M ożesz zd e fin io w a ć u o g ó ln ien ie nio definiując, an i n aw et nie znając s z c z e g ó łó w . P rzy k ład o w o - m ożna napisać definicję klasy kolejka, w której stoją elem en ty znane, a tak że elem enty, których w m om encie p isan ia p ro g ra m u jeszcze nie znam y. Z o stan ą w ym yślone d o p ie ro w te d y , g d y życie p o k a ż e , że są p o trzeb n e, N ie w yw oła to konieczności z m ia n y tego u o g ó ln ien ia, ja k im jest kolejka.
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Obiektowo Orientowane: Projektowanie
1189
pss
23.3 N ajp rzy jem n iejszy m etap em p ro g ram o w an ia o b ie k to w o o rien to w a n eg o jest sam o projektowanie p ro g ram u . P rzy p o m n ij sobie jak to było d o tej pory. Siadałeś p rz e d m o nitorem , u ru c h a m ia łeś e d y to r i zaczynałeś pisać p ro g ra m w ystukując kolejne instrukcje w p ro st na k la w ia tu rz e . N ajp ierw zaczy n ałeś pisać seg m en t g łó w n y p ro g ram u (w języku C jest to fu n k cja m ain ). To, co m iałeś zap ro g ram o w ać, rozbijałeś na p ro s te czy n ności, z k tó ry ch robiłeś funkcje. To było w łaściw ie kw intesencją p ro g ram o w an ia - n ap isan ie określonej liczby funkcji w y w o ły w a n y ch w o k reślo n y m p o rz ą d k u . Z p ro g ram o w an iem O O tak się nie da. N ie m ożna u siąść p rzed m o n ito rem i a vista p isać program . Trzeba so b ie ten pro g ram n ajp ierw obm yślić.
Eeee" - p om yślałeś sobie z a p e w n e - „miało być wspaniałe narzędzie, a tymczasem już utrudnienia..." N ie zniechęcaj się. O czyw iście, ż e m ożesz od razu zasiąść p rzed m onitorem i pisać, ty lk o że to, co n ap iszesz, będzie trochę p o k raczn e. - Tak, jak b y ś chcąc b u d o w ać d o m , poszedł od razu na plac b u d o w y i zaczął m u ro w ać. To, co zb u d u jesz, dom em m im o w sz y stk o będzie. Jed n ak b ęd zie pok raczn e. K ażdy w ie, że lepiej najpierw n ary so w ać sobie p ro jek t tego dom u. P o d o b n ie z pro g ram o w an iem . Ten etap obm yślania p ro g ram u (projektow ania) d o starcza n a p ra w d ę dobrej zabaw y. T ak, jak kraw iec arty sta cieszy się najbardziej, g d y projektuje. N ato m iast w te d y , g d y siada do m a sz y n y i by u szyć - to już nie jest takie p o d n ie cające. Po p ro stu rzem iosło. P o ro zm aw iam y teraz w łaśnie o projektow aniu. Ironia losu polega na tym , że na etapie p ro jek to w an ia p ro g ram u m usim y w alczyć z naszym i d o ty ch czaso w y m i naw ykam i z tradycyjnych tech n ik p ro gram o w an ia. N atom iast ktoś, k to nigdy nie p ro g ra m o w a ł - technikę O O uzna za z u p e łn ie oczyw istą. O to dlaczego: Pisany p ro g ra m zaw sze odnosi się jakoś do zjaw isk w św iecie realnym . Jeśli jest to p ro g ram d o księgow ania - odzw ierciedla obiekty i m etodykę p racy księgo w ego. Są czeki, faktury, kasa, konta. Z kolei p ro g ram , który służy d o gry w "Chińczyka" - operuje takim i obiektam i jak pionki, p lan sza, kostka d o gry. W prog ram ie sterującym prom em kosm icznym o b iek tam i są p oszczególne p o d zespoły, k tó ry m i trzeba w o k reślo n y sposób sterow ać. W sytuacji, g d y posługujem y się tradycyjnym i m eto d am i p ro g ram o w an ia — m usim y ten w ycinek św iata realn eg o zapisać jakoś w kom puterze. Rozbija się więc w szy stk o na luźne liczby, n a których później p racu je program . P ro g ram o w anie w ó w czas polega na - m ów iąc obrazow o - w y d aw an iu poleceń m ałpie, którą n au czo n o dodaw ać. M ów i się: „a teraz dodaj tę liczbę do tam tej i rezu ltat um ieść tutaj". Aby w ten sp osó b sterow ać prom em kosm icznym - trzeb a się bardzo n atru d zić.
1190
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO P ro g ra m o w a n ie obiektow o o rientow ane opiera się na innej idei. Jeśli program zajm uje się jakim iś obiektam i i zjaw iskam i ze św iata rzeczywistego, to nie rozbijam y te g o w szystkiego na lu źn e liczby. Budujem y m odel. W program ie tw o rzy m y m o d e le rzeczyw istych obiektów . Tak zb u d o w an e obiekty w y p o sa żan e są nie ty lk o w dane, ale także w zachowanie. M ój przyjaciel Dirk Fischer mówi, że w y k o n y w an ie program u O O p rzy p o m in a mu party , na które zaproszono w iele różnych obiek tów . O biekty te chodzą, rozm awiają ze sobą, załatw iają ze sobą ró ż n e spraw y. K ażdy obiekt m a swój stan w ew n ętrzn y - po leg a jący na grubości sw ojego portfela, lub ilości w ypitej whisky. O biekty mają też sw oje określone zachow ania - ktoś jest kelnerem serw ującym drinki, ktoś jest bardzo w ażn ą osobą, ktoś jest g o sp o d arzem . O stanie w ew n ętrzn y m obiektów nie m ożna dow iedzieć się w p ro st (private!), ale m ożna to w y w n io sk o w ać z rozm ow y z tak im obiektem . Napisanie program u obiektowo orientowanego polega na zorganizowaniu takiego party.
P o d k reślm y jeszcze raz: p ro jektow anie pro g ram u O O po leg a na m odelow aniu. W k o m p u te rz e b u d u je się m odel zag ad n ien ia, którego d o ty c zy program .
W Jeżeli chcem y n auczyć się now ego języka p ro g ram o w an ia - to spraw a nie jesl tru d n a. Z a w sz e jest jakaś pętla do, p ętla f o r , instrukcja i f . N au k a polega tylko na u ch w y cen iu tych niew ielkich ró żn ic w składni p o w y ższy ch instrukq'i. Jeśli z d a rz y ło Ci się kiedyś tłum aczyć p ro g ram z PascaTa n a C - to p rzy zn asz mi chyba rację. Jest to dość łatwe: instrukcje tłum aczy się - ż e tak p ow iem - w skali 1:1. Tu w P ascalu jest tak zap isa n a pętla f o r - to w języ k u C zapisuję ją w taki-a-taki sp o só b . Przejście od p ro g ram u w C d o o b iek to w o o rien to w a n eg o p ro g ram u w C++, to ju ż nie tłu m acz en ie 1:1. Pojaw ia się n o w a technika p ro g ra m o w a n ia - zatem trzeba zm ien ić sposób ro zu m o w an ia, sposób p atrzen ia n a ro zw iązan ie d an eg o problem u. N a szczęście p rzy technice O O p ro p o n u ję Ci z m ia n ę sposobu p a trz e nia na b ard ziej n atu raln y - taki, jak im p o słu g u jesz się n a co d zień w życiu. Inaczej m ó w ią c p ro g ram O O nie jest w sto su n k u 1:1 d o p ro g ra m u tradycyjnego. O n jest w sto su n k u 1:1 d o rzeczyw istości.
23.4 Praktyczne wskazówki dotyczące projektowania programu techniką OO Pracę n ad p ro g ra m e m m ożna p o d zielić na następ u jące fazy: ❖
R o zp o zn a n ie problem u
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO
1191
P rojektow anie p ro g ra m u «*» Im plem entacja (k o d o w an ie) T estow anie N a jp ie rw należy zap o zn ać się z zag ad n ien iem i zro zu m ieć je - to faza ro z p o z n a nia. N a stę p n ie - w iedząc, co m am y z ro b ić - p ro jek tu jem y p ro g ram . K olejna faza to im p lem en tacja, czyli m o m e n t w łaściw ego k o d o w an ia p ro g ram u (np. w języ ku C ++). Potem n astępuje testow anie. T esto w an ie - jak p o w sz ech n ie w iadom o - nie w ieńczy dzieła. Najczęściej o k azu je się, że trzeba w rócić i coś przep ro jek to w ać lub n aw et lepiej zro zu m ieć, bo n a stą p iło n ie p o ro zu m ien ie, albo zam aw iający p ro g ram z a ż ą d a ł d o d a tk o w ych m odyfikacji. P raca n a d program em o d b y w a się m eto d ą kolejnych p rzy b liżeń . P rześled z im y teraz d o k ład n iej kolejne fazy p racy n ad program em .
23.4.1
R ekonesans - czyli rozpoznanie zagadnienia D o w cip n i tw ierdzą, że p ra c a n ad pro g ram em zac zy n a się w tedy, g d y szef lub klient p rzy ch o d z i do C iebie i - m achając rękam i o ra z b ard zo d u ż o i n ie zro zu m iale m ów iąc - w y su w a serię dziw acznych ż ą d a ń . W tym m om encie p rzy s tąpić trz e b a do ro zp o zn an ia zagadnienia. Jest to sz tu k a sam a w sobie, w kraczająca niem al w psychologię - nie m ożem y tutaj d łu ż e j się n ad tym zatrzy m y w ać. W skrócie po w iem tylko, że jeśli klient jest n erw o w y , to sad zam y g o w w y godnym fotelu i n ie d o p u szczam y , b y z niego w staw ał. N astęp n ie z ad a jem y m u proste py tan ia, k tó re zm uszą go d o u d z ie la nia p ro sty ch odpow iedzi. M eto d ą takich p y ta ń p o d łu ższy m czasie d o ch o d zi m y d o tego, co p ro g ram m a robić i w jaki sposób. A te ra z uw aga: jeśli klient p o w ie, że program m a sp ełn iać takie-a-takie w a ru nki, ale liczyć się należy, że w przyszłości będzie m u siał spełniać inne, n iezn an e jeszcze dzisiaj - to jest to sy g n ał, że program m u sisz napisać w języku obiektow o o rien to w an y m (np. w C++). Jeśli tak nie pow ie, to oczyw iście też m ożesz pisać techniką OO, bo: <♦ 1) lubisz ten n o w o czesn y styl pro g ram o w an ia, ♦♦♦ 2) znasz klientów i w iesz, że za m iesiąc zm ieniają zd an ie i już w tedy b ęd ą potrzebne m odyfikacje. To w szy stk o , co o p o w ied ział klient o jakim ś sw oim zagadnieniu, które należy opro g ram o w ać - ten k aw ałek rzeczywistości, k tó ry m zająć się ma p ro g ram - to n azy w ać będziem y sy stem em . Księgowość danej firm y to jakiś system . G ra w "C hińczyka" to także jakiś system . C ho d zi w ięc o rozpoznanie system u. Takie ro zp o zn an ie już kiedyś z a p e w n e w życiu p rzep ro w ad załeś - w te d y , gdy ktoś uczył C ię w tego "Chińczyka" grać. Ktoś C i to zaw ile tłum aczył, Ty zadaw ałeś p ro ste pytania po to, b y jego sko m plikow aną w y p o w ied ź uprościć. Co jakiś czas upew niałeś się czy d o b rze zro zu m iałeś d an y fragm ent. D odatkow o jeszcze przyjrzałeś się, jak grają w tego
1192
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO "C hińczyka" inni. Sum a tych d ziałań stanow iła dla C iebie rozpoznanie tego system u. N ie inaczej jest w p rzy p ad k u ro zp o zn an ia pow ażnego system u - księgowości, stero w an ia sam olotem , czy system u kontrolującego a p a ra tu rę elektroniczną w ek sp ery m en cie fizycznym. D opiero, g d y ro zpoznam y sy stem - m ożem y p rzy stąp ić d o projektow ania. M ożesz b y ć jed n ak pew ien, że jeszcze pow rócisz do tego etap u . W cześniej czy później o k a ż e się, że czegoś d o k ład n ie nie zrozum iałeś. Nic w tym złego. To jest w k alk u lo w a n e w m etodę.
23.4.2
Faza projektowania Po an alizie sy stem u (zagadnienia) w iem y już co m am y zrobić. P rzystępujem y w ięc d o p ro jek to w an ia p ro g ram u , czyli d o ustalenia jak to zrobim y. O gólnie biorąc c h o d z i teraz o znalezienie obiektów , za pom ocą których m odeluje się system . Istnieje w iele m etod projektow ania p ro g ram u OO, tak jak m oże istnieć w iele dró g p ro w ad zący c h do tego sam eg o celu. Osobiście p o słu g u ję się połączeniem dw óch m e to d . Ich n azw y brzm ią trochę skom plikow anie: „A n aliza Z achow ań O b iek tó w " o ra z „P rojektow anie na Z asadzie O bserw acji O bow iązków System u ". N azw a „ A n a liz a Z achow ań O b iek tó w " - jest raczej jasna. N ajw ażniejszym słow em w niej jest: „...zachow ań..." Z kolei n a z w a „P rojektow anie na Z asad z ie O bserw acji O b o w iązk ó w S ystem u" m ów i, że p rz y projektow aniu zw ra c a m y u w ag ę na o b o w iązk i, które spełnia system . O b o w iązk i w tym kontekście, to w szystkie u słu g i, które w ykonują obiekty w o b e c innych obiektów . P rzy k ład o w o : obow iązkiem rad aru jest n arysow ać p u n k t na ekranie. O bow iąz kiem kostki d o g ry jest p o d ać liczbę p rzy p ad k o w ą z p rz e d z ia łu [1,6]. Jak w życiu - o b o w iązek obiekt m oże sp ełn ić sam , a m o ż e g o zlecić obiektow i w sp ó łp racu jącem u . Kostka do g ry sam a spełnia sw ój o b o w iązek . O bow iązek au to m a ty c z n e g o po d lew an ia plantacji p o m arań czy s p e łn ia n y jest p rzez obiekt zaw ó r w o d n y i obiekt czasom ierz. Gdy zlecimy jakiemuś obiektowi wykonanie jakiegoś działania, może on delegować kilka podległych mu obiektów do wykonania poszczególnych etapów tego działania. M ó w m y wtedy o delegowaniu. P rojek to w an ie w ed łu g o b o w iązk ó w system u polega n a o bserw acji tych obo w iązk ó w i ro zd zielen iu ich m ięd zy obiekty: KTO m a robić CO. To sam o w y ra ż o n e w term inach C f+ : k tóra klasa m a m ieć jakie funkcje składow e. P rojek to w an ie takie sk ład a się z k ilk u etap ó w .
E tap y projektow ania p ro g ram u obiektow o o rie n to w a n eg o :
❖
1) Identyfikacja zach o w ań sy ste m u ,
1193
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO ♦♦♦ 2) Identyfikacja o b iek tó w w y stęp u jący ch w system ie, ♦♦♦ 3) K lasyfikacja obiektów : • p o d w zg lęd em d zied ziczen ia (hierarchie), •
p o d w zg lęd em m ieszczenia w sobie o biektów sk ład o w y ch ,
♦♦♦ 4) O kreślenie w zajem nych zależności klas obiektów , ♦♦♦ 5) S kładanie m o d e lu - O kreślanie sekw encji d ziałań o b iek tó w , E ta p 1 - to jakby u sy ste m a ty z o w a n ie w ied zy o problem ie. E tap 2 - to jakby etap p o szu k iw an ia . E tap y 3, 4, 5 - to an aliza tego, co znaleźliśm y. W rezultacie takiego projekto w an ia pow staje m odel, k tóry staje się podstaw ą p rz y ko d o w an iu p ro g ra m u . W ym yślonych jest kilka klas, o k reślo n e ich cechy i w y m a g a n e funkcje s k ła d o w e (zachow ania). M ożna przy stąp ić d o fazy im ple m entacji, czyli k o d o w a n ia p rogram u. K o d o w an iu pośw ięcone b y ły w szystkie p o p rz e d n ie ro zd ziały tej książki. Isto tą tego ro zd ziału , k tó ry w łaśnie czytasz, jest pokazanie, jak w praktyce zrealizo w ać p o w y ższe p ię ć p u n k tó w fazy projektow ania. N ie m a rtw się jeśli p o czątk o w o czegoś nie zro zu m iesz - p o teoretycznym p rz e d sta w ie n iu zasad p o k aże m y p rzy k ład projek to w an ia k o n k retn e g o pro g ram u .
23.4.3
Etap 1: Identyfikacja zachowań system u C ałą swoją w ied zę o sy stem ie zapisujesz w form ie scenariusza. C hronologia z d a rz e ń nie jest w ty m m iejscu konieczna, je d n ak jeśli u d a Ci się ją zachow ać, w ó w czas oszczędzisz sobie pracy p rzy etap ie 5 (składanie m odelu). S p o rząd zen ie takiego scen ariu sza p o w in n o w y g ląd ać tak, że k a rtk ę papieru dzielisz na kolum ny, k tó ry m dajesz tytuły: Kto - D ziałanie - K ogo - R ezultat N astęp n ie chw ilkę się zastan aw iasz i zaczy n asz w ypełniać te k o lu m n y . Pow sta je lista zachow ań sy stem u . Zachow ań — bo w a ż n a tu jest k o lu m n a d ru g a i czw arta. Oto, jak m o że zaczynać się taki „scen ariu sz":
T; .... Kto
'
Gracz
rzuca
Działanie
Kogo kostką
R ezultat Liczba p rzy p ad k o w a z za kresu 1-6
1—
23.4.4
Etap 2: Identyfikacja obiektów (klas obiektów )
P o sz u k iw a n ie obiektów
Próbujem y odnaleźć obiekty działające w system ie, który p rzy szło n am opro gram ow ać. Cały w y siłek sk u p io n y jest tu na to, by znaleźć obiekty jak najnatu ralniej o dw zorow ujące rzeczyw iste zach o w an ia system u. Jak się to robi?
1194
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO S praw a nie jest w cale trudna. W ystarczy spojrzeć na sp o rząd zo n y przed chwilą scenariusz. W rubrykach: KTO, KOGO mam y w łaśnie nazw y klas tych obiektów . U w aga:
klasy, które projektujemy mogą być nie tylko reprezentacją istniejących namacalnie obiektów. Klasą może być też okienko menu na ekranie monitora-a także szerzej - cały sposób rozmowy programu z użytkownikiem. O k r e ś le n i e jakie o b iek ty m ają obow iązki, w s p ó łp ra c o w n ik ó w o ra z w id o cz n e w łasności
W tym m iejscu p o słu ży m y się narzęd ziem zw anym k artam i modelującymi^K arty te p o m o g ą n am nauczyć się identyfikow ać obiekty, ich zachow ania i w sp ó łp raco w n ik ó w . S porządza się je na luźnych kartkach p o to, by m ożna było nim i łatw o m a n ip u lo w a ć - rozk ład ać na b iurku czy s t o l e - a n iektóre po prostu w y rzu cać jeśli z nich rezygnujem y. Jak zrobić taką kartę? N a gó rze pisze się n azw ę k lasy o b iek tó w , które reprezentuje. Poniżej, p o lew ej stronie spisuje się obow iązki tej klasy, natom iast p o praw ej w sp ó łp raco w n ik ó w w ym aganych dla spełnienia d a n e g o obow iązku. N a dole rubryka na w id o czn e w łasności obiektów tej klasy.
Z a u w a ż jeszcze jedno: na tym etap ie je d y n ie id en ty fik u jem y obiekty i ich zależności n ato m iast szczegóły ich im plem entacji nie są tu w cale w ażne. Tym zajm iem y się d o p ie ro na sam ym końcu projektow ania. D zięki tem u , nic nie staje się p rz e są d z o n e o d razu i jakiekolw iek zm ia n y w y m ag an e p ó źn iej w im plem en tacji - nie rujnują nam w stęp n y ch etap ó w projektu. Z apam iętaj to jak o zasadę:
2)
Zaproponowane przez K. Becka i H. Cunninghama - Laboratory for Teaching Object-Ori ented thinking - in Proceedings of OOPSLA'89
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO
1195
W y p isan e w łaściw ości n ie są zw ykle ostateczne. Z d arz a się, że n ie k tó re obo w ią z k i p rzen o sim y z je d n y ch klas obiektów n a d ru g ie. P rz y p o m in a m - projek to w a n ie o d b y w a się m e to d ą kolejnych p rzy b liżeń . S p o rz ą d z o n e w te n sp o só b karty m odelujące p o słu żą n am te ra z d o określenia z a c h o w a ń i w łasności k o n k retn y ch klas. Inaczej m ów iąc z a sta n o w im y się jakie w id z ia ln e w łaściw ości (cechy) m usi m ieć k a ż d a klasa. Te w id z ia ln e w łaściw ości m o g ą stać się k ied y ś d a n y m i składow ym i k lasy , n ato m iast o b o w iązk i - funkcja m i sk ład o w y m i. O to p rzy k ła d w y p e łn ie n ia takiej k arty m odelującej dla h ip o tety czn ej klasy: p ra lk a au to m aty czn a
Klasa: PRALKA AUTOMATYCZNA O b o w iązk i:
W sp ó łp raco w n icy :
w iro w an ie n ab ieran ie w o d y o d p o m p o w an ie w o d y
silnik zaw ó r pom pa w odna
W id o czn e w łaściw o ści: -p o jem n o ść b ęb n a -cen a , w aga, -b ieżący p ro g ra m -b ieżąca te m p e ra tu ra
W rezu ltacie tego etap u m a m y dobrze zid en ty fik o w an e klasy o b iek tó w w ystę pujących w naszym system ie. Z id en ty fik o w aliśm y też ich o b o w iązk i i ich w spółp raco w n ik ó w .
13.4.5
Etap 3: U system atyzow anie klas obiektów Pora teraz przystąpić d o an alizy tego, co zn aleźliśm y na etap ie po szu k iw an ia. T em u, co zid en ty fik o w aliśm y - przyjrzym y się teraz krytycznie. T utaj chodzi k on k retn ie o to, b y zasta n o w ić się czy klasy nie są p o w iązan e (zw iązkam i dziedziczenia) w jakieś hierarchie, e w e n tu a ln ie czy obiekty jednej klasy nie m ieszczą w sobie o b iek tó w innej klasy. Etap ten jest b ard zo w ażn y , g d y ż tutaj zd ecy d u je się czy m am y szan se na reusability — w tórne użycie jakichś klas, funkcji, ogólniej: części p ro g ram u .
E tap 3 a) U s ta le n ie hierarchii
D obrze zaprojektow ane hierarchie d zied ziczen ia są istotą do b reg o pro g ram o w an ia OO. Przy d o b ry m zdefin io w an iu klas abstrakcyjnych m ożem y korzystać
1196
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO z nich w ielo k ro tn ie w różnych klasach pro g ram u i w ten sposób zaoszczędzim y sobie d u żo pracy. D latego na tym całym etapie projektow ania należy mieć w y o strzo n ą u w a g ę na w szelkie p o d obieństw a zachow ań m ięd zy klasami. E w en tu aln e p o w ią z a n ie klas w zw iązk i dziedziczenia sp raw d za się bardzo łatw o. W y starczy w yp o w ied zieć głośno zdanie | O b iek t klasy A jest szczególnym rodzajem obiektu klasy B Jeśli zab rzm i to n o n sensow nie, to znaczy, że nie ma tu zw iązku dziedziczenia. O to p rz y k ła d o w e zd an ia: Pojazd jest szczególnym rodzajem czasomierza - b zd u ra Pojazd jest szczególnym rodzajem samochodu - b zd u ra A le zdanie:
Samochód jest szczególnym rodzajem pojazdu - brzm i sen so w n ie Jeśli zd an ie b rzm i sen so w n ie - to znaczy, że klasa A (sam ochód) jest klasą p o ch o d n ą od k lasy B (pojazd). Jeśli klasy, k tó re zn aleźliśm y w p o p rzed n im etapie, nie są p o w ią z a n e zw iązka m i d zied ziczen ia, to nie św iadczy jeszcze o tym , że żad n y c h hierarchii nie będzie. M ożem y spróbow ać poszukać klas abstrakcyjnych — w spólnych dla dw óch k o nkretnych klas. Robi się to rozw ażając jakie w sp ó ln e obow iązki mają d w ie klasy. Z au w aż o n e p o d o b ie ń stw a p o w in n y w tym w łaśnie m o m en cie ow ocow ać kla sam i abstrakcyjnym i: jed n o w sp ó ln e zach o w an ie, które m ają d w ie klasy w ys tarczy, by z b u d o w a ć klasę abstrakcyjną. Starajm y się w y sz u k a ć tak d u żo klas abstrakcyjnych, jak to tylko m ożliw e. C zy w ytrzym ają one p ró b ę czasu - to już sp ra w a n astępnych etap ó w . (Jest rzeczą b ard zo w ażn ą, by zn aleźć te klasy ab strak cy jn e jeszcze przed rozdziałem o b o w iązk ó w , który robim y w następ n y ch etap ach ). E t a p 3 b) M i e s z c z e n i e ( z a w i e r a n i e ) w s o b i e o b i e k t ó w i n n y c h k l a s
Z w iązek klas p o leg ający na m ieszczeniu w sobie obiektu in n ej klasy łatw o s p ra w d z a m y w y g łaszając stw ierdzenie: | O b iek t klasy A składa się m ię d z y in n y m i z o b ie k tu klasy B Jeśli zd an ie zab rzm i sensow nie, to obiekt klasy B jest sk ład n ik iem klasy A. P rzykładow o: Samochód składa się między innym i z krowy (b zd u ra) Samochód składa się między innym i z kierownicy (praw da!) Krowa składa się między innym i z dwóch oczu (p raw d a)
1197
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO
3.4.6
Etap 4: O kreślenie w zajem nych zależności klas N a tym etap ie m u s im y zdecydow ać o w zajem n y ch relacjach klas - czyli o tym , która jest w ażn ie jsza od której, która w y d a je polecenia, a k tó ra polecenia te spełnia, albo k tó re klasy są p artn eram i - p ro sząc się n aw zajem o przysługi. O kreślanie takich re la q i dokonuje się d w o jak o . Po p ie rw sze sp o rz ą d z a się spis relacji łączących ob iek ty , a po d ru g ie ry su je się tzw . g raf w sp ó łp ra c y obiektów .
S p i s relacji
sp o rząd za się w te n sposób, że m ając p rz e d sobą scen ariu sz, a także karty m odelujące w y p isu je się poszczególne relacje. P ow inny o n e być zeb ra n e w g ru py. To znaczy, ż e najpierw w y p isu jem y w szy stk ie relacje takie, gdzie jakiś obiekt zm u sza in n y obiekt do w y k o n an ia jakiegoś d ziałan ia, p o tem na przykład relację zw y k łeg o p o w ia d a m ia n ia się o czy m ś (k o m u n ik o w an ie się). Inaczej kto żąd a - a kto ty lk o realizuje żąd an ia. O to p rzy k ład autom atycznej
O b ie k t
d la hipotetycznego obiektu
klasy: p ro g ra m a to r pralki
Z a le ż n o ś ć
Kogo
zm u sza d o otw arcia zm u sza d o zam knięcia z m u sz a d o w irow ania
Z a w ó r w odny Z aw ór w odny Silnik
d o w ia d u je się o bieżący p o zio m w o d y d o w ia d u je się czy drzw iczki są zam k n ięte
h y d ro sta t czu jn ik drzw i
P ro g ram ato r
Następny obiekt .--------- ~ -----------Grał w sp ó łp racy obiektów
G raf w spółpracy m a tę zaletę, że p o k azu je jasno ścieżki kom unikacji m iędzy obiektam i. G raf ta k i sp o rząd zan y zostaje n a p o d staw ie u sta le ń z dotychcza sowych etapów projektow ania. R ysujem y g o w ten sposób, że prostokątam i oznaczam y p oszczególne obiekty, a strzałk am i o zn aczam y zależności. Jest w iele szkół ry so w an ia takich grafów . Ja osobiście stosuję najp ro stszy sposób. , Strzałkam i o zn aczam kto kom u ro zk azu je. G roty z obu k o ń có w strzałki oznaczają, że d w ie klasy naw zajem p ro sz ą się o p rzysługi. N arysow anie g rafu to jeszcze nie w szy stk o . Teraz trzeba m u się krytycznie przyjrzeć. To b a rd z o w ażn e zobaczyć n asze obiekty n ary so w an e w ten sposób, gdyż za jednym rz u te m oka w idzim y m iejsca n iep otrzebnego skom plikow ania lub sytuacje, gdy n aru szo n a jest enkapsulacja.
1198
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO
Rys. 2 3 -1 . G ra f w s p ó łp ra c y obiektów (nie n ależy go mylić z g ra fe m dziedziczenia klas). Tutaj w id zim y , k tó re obiekty w yd ają p o le c e n ia innym obiektom .
Jeśli p atrz ąc na g ra f zau w aży sz, że jakaś klasa w spółpracuje z więcej niż trzem a innym i k lasam i - oznacza to zw y k le, że coś strasznie pokom plikow aliśm y. Tak, tak - i tutaj m o ż e sz w y p ro d u k o w ać „ k o d a la spaghetti". Jeśli z Twojego grafu w ynika, ż e w sz y sc y w spółpracują z w szystkim i - to tru d n o C i będzie opanow ać taką hołotę. Z acznij od m inim alizacji liczby w sp ółpracow ników . Zw róć też u w ag ę czy n ie k tó re klasy nie d ziałają na jakieś obiekty tkw iące w e w n ętrzu innych klas. To b y ło b y p o g w ałcen iem z asad y enkapsulacji. Po to obiekt tkw i w śro d k u in n e g o o b iek tu , aby b ezp o śred n io d o niego się n ie zw racać (tylko p rzez jego szefa). Inny m ro d zajem upro szczen ia je st zm in im alizow anie ty p ó w obow iązków spełn ian y ch p rz e z klasę (czyli tzw . kontraktów ). N iech k la sa zajm uje się tylko jed n y m z a d a n ie m . N iech np. p io n ek d o gry zajm uje się ty lk o p o ru szan iem się p o szachow nicy, a nie oblicza d o d a tk o w o sinusa i lo g a ry tm u . Jeśli ten ty p o b o w iązk ó w (sin , log) m usi być sp ełn ian y , to ro zw ażm y b u d o w ę dodatkow ej klasy. P atrząc na g ra f w sp ó łp racy , m o żesz łatw iej zo rien to w ać się co i jak m ożna uprościć. Z w ra c a m uw agę: do tego najlepiej nadaje się g raf, b o tu w idać w sz y st ko, jak na d ło n i. Jeśli w ięc p atrząc na niego w ym yślisz ja k ą ś m odyfikację, to w róć d o je d n eg o z p o p rzed n ich e ta p ó w i popraw .
Podsystemy Jeszcze jed n ą z a le tą grafu jest to, ż e w id ać na nim tak że g ru p y klas, które n ie m ają ze sobą z u p e łn ie nic w spó ln eg o . W ów czas m o ż em y sp ró b o w a ć z g ru p o w ać klasy w tak z w a n e p o d sy ste m y , czyli g ru p y klas zajm u jący ch się sp ełn ia niem jednej, b a rd z o ogólnie pojętej roli. N a p rzy k ła d - jeśli p ro g ram na g rę w C hińczyka w zb o g a c im y o g ru p ę klas o d p o w ied zialn y c h za g ran ie chińskiej m u zy k i, to ta now a g ru p a klas nie ma nic w spóln eg o z g ru p ą d o której należą p ionki, kostka, itd. O to inny p rz y k ła d : p ro g ram o b słu g u jący ek sp ery m en t fizy c zn y , w którym to pro g ram ie jest k ilk a klas realizujących ro zm o w ę z u ż y tk o w n ik ie m za pom ocą eleganckich m e n u . Ta g ru p a klas to w łaśn ie p o d sy stem . Jeśli w tym sam y m
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Praktyczne wskazówki dotyczące projektow ania program u techniką OO
1199
p ro g ram ie jest in n a g ru p a klas o d p o w ied zialn y ch za s te ro w a n ie d o p ły w u ciekłego azotu d o d etek to ró w g erm an o w y ch - to klasy te tw o rzą kolejny pod sy stem . N a grafie takie g r u p y klas o d g ran iczam y czerw o n ą linią - co d aje n am pojęcie, k tó re klasy należą d o d a n e g o p o d sy stem u , a które do sąsied n ieg o . P rzy w ykreś laniu tej linii jako kry teriu m przy jm u jem y zasadę: klasa n ależy do danego pod sy stem u w te d y , g d y spełnia zad an ia jed y n ie na rzecz tego p o d sy stem u . Z ap y tasz pew nie: „-D laczeg o to takie w a ż n e - p rzecież w C ++ istnieje coś takiego jak klasa, ale nie istnieje nic w stylu 'podsystem klas?" Rzeczyw iście, je d n a k praca nad p ro g ram em i projektem b a rd z o u p raszcza się jeśli do k o n aliśm y p o d ziału na p o dsystem y. N ajp ierw zajm iem y się zaprojekto w an iem - i n a w e t im plem entacją - części o d p o w ied zialn ej za ciekły azot i nie p am iętam y o istn ien iu reszty. Jeśli pracuje się w zespole, to jed en z program is tów opracow uje ten podsystem , a d ru g i inny. Ich klasy n aw zajem się nie kontaktują, w ięc tak a rozłączna p raca jest m ożliw a. A by p ró b n ie uruchom ić jed en p o d sy stem - d ru g i m oże naw et jeszcze nie być zaczęty.
23.4.7
Etap 5: Składanie m odelu. O kreślanie sekw encji działań ob iek tów i cykli życiow ych C zas na to, by sk o n stru o w ać m odel sy stem u będącego p rz e d m io te m naszego pro g ram u . Jeśli podzieliliśm y zag ad n ien ie na kilka podsy stem ó w , to teraz czas, by m odelo w ać jeden z nich. G d y to zrobim y, to w eźm iem y na w a rsztat inny podsystem . Ta m etoda b ard zo u ła tw i nam pracę n ad m o d elo w an iem całości sy stem u .
Jak postępuje się w przypadku składania modelu? Bardzo prosto: b ie rz e się do ręki scenariusz, k tóry pow stał w etap ie 1. Jeśli udało Ci się go w ted y zro b ić chronologicznie — to jest d u żo p racy zrobionej. Jeśli nie — to czas zrobić to teraz. To jednak nie w szy stk o . Teraz m usim y ustalić sekw encje d z ia ła ń obiektów każdej z klas. To zn a c z y u stalam y kto inicjuje jaką akcję, co jest ro b io n e potem. Pow stają w ów czas tak ie sform ułow ania: Jak tylko obiekt klasy A otrzyma sygnał, by zrobił to - wydaje on następu jące polecenie obiektowi klasy B i... (np. czeka na rezultat). O biekty mają sw oją indyw idualność, w ięc nie zaw sze m usi być to takie bez m yślne. N p. po o trzy m an iu sygnału obiekt A m oże dow iedzieć się o p arę spraw , po czym zależnie o d oceny sytuacji m oże w y b rać jeden z w arian tó w postępow a nia. Jak w życiu. N iew olnictw o i feu d alizm d aw n o już się skończyły. N astępnie opisujem y cykle życiow e poszczególnych obiektów . Np. w grze w Chińczyka - najpierw obiekt pionek jest wstanie „w drodze" potem, gdy zdarza się coś szczególnego, przechodzi w stan „w Pekinie" itd. Rezultat tego etap u p rzy d a się przy defin io w an iu ciał poszczególnych funkcji składow ych d anej klasy.
1200
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza im plem entacji
Z w ró ć u w a g ę , ż e etap ten nie p ro d u k u je niczego nadzw yczajnego. Jest to raczej u sy ste m a ty z o w a n ie naszej w iedzy o obiektach. Praca tutaj przypom ina trochę reży sero w an ie spektaklu: najpierw w chodzisz ty i robisz to... Albo: jeśli ty lk o on zw róci się do ciebie z takim żądaniem , w ted y zrób tamto. N a ty m zak o ń c z y ł się proces projektow ania program u obiektow o orientow ane go. T eraz m o ż n a przy stąp ić do im plem entacji, czyli zam ieniania tego na instru kcje w języku C++.
23.5
Faza implementacji P rzy stęp u jąc d o im plem entacji staram y się napisać definicje klas. N ie jest to tru d n e , g d y ż ro b im y to w oparciu o określone wcześniej:
*t* w id o czn e w łasności (cechy) klasy - to będą d an e sk ład o w e, zac h o w an ia klasy - to będą funkcje składow e. Funkcje s k ła d o w e piszem y na razie jed y n ie w postaci deklaracji. Ich definiow a niem zajm iem y się na sam ym końcu. K iedy w szy stk ie klasy m am y zd efin io w an e - m ożna p rzy stąp ić do pisania definicji funkcji składow ych. Tutaj d o p ie ro p rzydaje się tradycyjna technika p ro g ram o w an ia: rozbijanie w y m ag an ej czynności na serię m niejszych. N iektó re z nich m ogą o kazać się często u ż y w a n e , w ięc opłaci się z nich zrobić p ry w atn ą funkcje sk ła d o w ą klasy. Jak p o w ied ziałem - technikę p ro ced u raln ą stosuje się d o p iero d o d z ia ła ń w ew n ątrz klasy. Z z ew n ątrz obiekt m u si być w idoczny jako obiekt, n ato m iast jaką sobie techniką zrealizu jem y w n ętrze funkcji składow ej — to nasza sp raw a. W ew n ętrzn a stru k tu ra obiektu nie jest już (zw ykle) obiektow a. Jeśli d an e s k ła d o w e są liczbam i, to w y m ag ają tradycyjnego podejścia. O czyw iś cie - jeśli d an ą sk ład o w ą jest obiekt jakiejś innej klasy, to w ted y w stosu nku do niego u ży w a się techniki OO. M ów i się, że funkcja składow a p o w in n a być tak d u ża, by m ieściła się w całości na ekra nie m o n ito ra. Jeśli jest w iększa, to znaczy, że p ew n ie jest z b y t skom pliko w an a i p o w in n a zostać u p ro szczo n a - zw y k le p rzez w y o d ręb n ien ie z niej jakichś m niejszych funkcji (robi się je zw y k le p ry w atn y m i funkcjam i sk ład o w y m i klasy).
N a tym kończy się cykl moich ra d , alb o w iem o tym , jak p o stęp o w ać p rzy pisaniu k o n k retn y ch funkcji - tra k to w a ły w szy stk ie p o p rz e d n ie ro zd ziały tej książki. Z obaczym y teraz, jak o p isan y p ro ces p ro jek to w an ia p rzeb ieg a w praktyce. T ytułem p rz y k ła d u w ed łu g p o w y ższy ch z a sa d z a p ro jek tu jem y k o n k retn y program .
Rozdział. 23. Projektowanie program ów orientowanych obiektowo Przykład projektow ania —-'rmrwriwwwwnT"
23.6
» i ir» wiwwk■wwwiffiwwjłii
1201
—tt.i»
Przykład projektowania N ie jest to p rz y k ła d w y d u m a n y - taki właśnie p ro g ram m usiałem kiedyś napisać. T u p rz e d sta w ię go w w ersji b ard zouproszczonej - ch o d zi nam bow iem o to, by p o z n a ć sam ą istotę p ro jek to w an ia. Nie przejm uj się jeśli „fizyczn o -e le ktro n iczn a " strona tego przykładu je st dla Ciebie nieinteresująca lub niezrozum iała. Jest to przecież dla nas tylko pretekst do napisania programu.
23.7
Faza: Rozpoznanie naszego zagadnienia W tej fazie w y jaśn im y sobie, co p ro g ra m ma robić. Otóż w y o b raź sobie, że m asz d u ż e u rz ą d z e n ie pom iarow e słu żące d o eksperym entów odkryw ających stru k tu rę jąd ra ato m o w eg o . U rządzenie, o którym myślę, rzeczyw iście istnieje i na zyw a się OSIRIS. W uproszczeniu m ów iąc jest to 12 czujników prom ieniow a nia3 u staw io n y ch p o d różnym i k ątam i w stosunku do m iejsca skąd prom ie nio w an ie p o ch o d zi. To w szystko! Z tych 12 czujników w y ch o d zą sygnały elek try czn e inform ujące o zarejestrow aniu kwantu p ro m ieniow ania - tak, jakby czujniki w y k ry ły błysk św iatła. Sygnał elektryczny - niosący inform ację o tym p rzech o d zi z czu jn ik a przez cały tzw. tor pom iarowy i w ęd ru je do kom putera, którego ro lą jest tę inform ację zap isać n a taśmie m agnetycznej. (Schem atycznie pokazuje to ry su n e k poniżej).
terno
Akcelerator
JM ~
~pmikh Komputer zbierający dane i zapisujący na taśmę
Eksperym ent fizyczny
Eksperymentator ręcznie nastawiający parametry urządzeń z toru pomiarowego
Rys. 2 3 -2 .
urządzeń
Przepływ
s yg n ałó w
w
przykładow ym p o m ia rze fizycznym, przy z a ło że n iu , że param etry
elektronicznych u s ta w ia n e są ręcznie.
K om puter, k tó ry te inform acje zbiera, ju ż istnieje i jest p o p raw n ie zaprogram o w an y - zatem d a n e rzeczyw iście zapisyw ane są na taśmach. 3)
fizycy mówią: detektorów promieniowania gamma
^
1202
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Rozpoznanie naszego zagadnienia P rzep ły w inform acji w przykładow ym p o m iarze fizycznym, p rz y założeniu, ż p a ra m e try u rząd z eń elektronicznych u staw ian e są ręcznie.
Zapytasz pewnie: „Skoro tak, to gdzie tu jest nasza rola ?“ O tóż, sam tor p o m iaro w y to dziesiątki u rz ą d z e ń elektronicznych, z których k a ż d e m a m nóstw o p rzełączników i gałek. A by móc p rzep ro w ad zić ekspery m e n t trzeb a te w szystkie u rząd zen ia nastroić. To m oże trw ać n a w e t parę dni. P otem p rz e p ro w a d z a się w łaściw y ek sp ery m en t (trwający np. 7 dni). Kiedy po ja kim ś czasie chce się w y k o n ać następny ek sp ery m en t - znow u trzeb a w szystko u staw iać od now a. Pom yśl: - jakby to było cu d o w n e, gdyby m óc nastaw y jakoś zap am ięty w ać i w p a rę sek u n d o d tw arza ć te, które u ży w aliśm y w trakcie ek sp ery m en tu w ze szły m m iesiącu, albo k ied y indziej o d tw o rzy ć te z zeszłego roku. T o jest w łaśnie nasza rola.
Nie jest to takie trudne w realizacji O tóż w iększość u rz ą d z e ń , o które chodzi, p rzy sto so w an a jest d o reg u lo w an ia sw ych n astaw za pom ocą kom putera. N ie m a ją one n aw et gałek, bo gd y b y trzeba reg u lo w ać je ż a pom ocą g ałek i p rze łączn ik ó w - to gałek tych m usiałyby być setki). Są to tzw . u rz ą d z e n ia serii ECL (Electronic C ontrollcd Logic - logika stero w an a elektronicznie). N asi specjaliści elektronicy postarali się o k o m p u te r i dołączyli g o do toru p o m iaro w eg o . Teraz m y m am y napisać p ro g ram . Sytuacja w y g ląd a m niej więcej tak, jak na kolejnym rysunku.
__ ____ __
Komputer zbierający dane i zapisujący na taśm ę
Eksperyment fizyczny
3 7
J Komputer "kręcący za nas gaikami" - ten właśnie mamy oprogramować
R ys. 23 -3 . S c h e m a t p rzep ływ u d anych w e ks p e ry m e n c ie fizy c zn y m przy z a s to s o w a n iu ko m p utera sterującego p a ra m e tra m i pracy u kład ó w elektronicznych
Skoro n asz k o m p u ter m a stero w ać n astaw am i k o n k retn y ch u r z ą d z e ń w torze p o m iaro w y m - p rzyjrzyjm y się, co to za u rz ą d z e n ia . W szystkie te u rz ą d z e n ia są czym ś w rodzaju p io n o w y ch szu flad ek - d la te g o n azy w am y je „b lo k am i . S zu flad k ę m o żn a w su n ą ć d o kasety, w k tó rej je st m iejsce na o k . 20 takich bloków . Słow em w kasecie takiej m oże o b o k siebie p raco w ać 20 u rząd z eń .
Rozdział. 23. Projektow anie program ów orientowanych obi ektowo Faza: Rozpoznanie naszego zagadnienia
1203
D w adzieścia - to mało, d lateg o u s ta w ia się więcej k a s e t - jedna na drugiej. S tanow i to ja k b y „w ieżę" - sk ład ającą się z pięciu kaset. (Jak na kolejnym r y s u n k u ) ___________________________ ___ ____
N asi elek tro n icy spraw ili, że m o ż em y z kom pu tera zw ró cić się d o bloku stojące go na p rz y k ła d w kasecie n r 2 n a m iejscu nr 19. Jest d o tych celów specjalna funkcja - b ę d z ie m y ją tylko w y w o ły w ali.
Jeśli nic nie zro zu m iałeś z do ty ch czaso w y ch w yjaśnień, nie przejm uj się. W skrócie c h o d zi o to, by: • 1) z kom putera m ó c regulow ać u rz ą d z e n ia stojące w o d p o w iednich szu fla d k a c h wieży, •
2) móc zapisać n a s ta w y w szystkich tych u rz ą d z e ń na dysku,
•
3) móc o d tw arzać te nastaw y na p o d sta w ie danych zap isa nych w cześniej n a d y sk u .
Skoro ro z p o z n a n ie zagadnienia p o le g a na zad aw an iu zleceniodaw cy pytań — zapytajm y n aszeg o kolegę:
„-Co chcesz, by program robił?" Kolega o d p o w ia d a tak: ♦> - G dy u ru ch o m ię p ro g ram chciałbym na ek ran ie zobaczyć tę wieżę. W całości, albo lepiej w postaci w ybranej jednej z pięciu kaset. ♦♦♦ - G dy u ru ch am iam p ro g ra m po raz p ierw szy , to program nie zna jeszcze konfiguracji mojej rzeczyw istej w ieży. C hciałbym więc mieć
1204
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Rozpoznanie naszego zagadnienia m ożliw ość p o w ied zen ia program ow i, w którym miejscu w ieży włożo ny jest jaki blok. N iestety te u rząd zen ia są tak zb udow ane, że kom puter nie m o że ich sam rozpoznaw ać. - K iedy ju ż p ro g ram zna um iejscow ienie w szystkich bloków , chciałbym mieć m o żliw o ść w ybrania jednego konkretnego bloku, p o to, by móc z nim sam y m rozm aw iać. W tedy w łaśn ie dokonam regulacji bloku. Za m iast kręcić g ałk am i chcę (m yszką) na ekranie k o m p u tera regulow ać o d p o w ied n ie w artości. «$♦ - Potem chciałbym m ieć m ożliw ość zrobienia tego sam eg o z dow olnym innym blokiem . ♦♦♦ - G dy już b ę d ę zadow olony ze w szystkich nastaw , chciałbym mieć m ożliw ość zap isa n ia ich na dysku. ♦♦♦ - O czyw iście m u si rów nież istnieć m ożliw ość o d czy tan ia tych zapisa nych na d y sk u nastaw . «$♦ - P oniew aż d o różnych ek sp ery m en tó w potrzebuję czasem innych zes taw ó w bloków , dlatego chciałbym m ieć m ożliw ość u su n ięcia bloku, który m i obecnie nie o d pow iada. - P oniew aż n asz OSIRIS ciągle się rozw ija, dlatego n ależy się liczyć z tym , że d o k u p im y (lub sk o n stru u jem y ) n ow e bluki ECL. Program p o w in ien p o zw ala ć na łatw e m odyfikacje. Tak, by w p ro w a d z e n ie no w ego typu b loku nie w ym agało p rz e ra b ia n ia całości p ro g ra m u . Jak im i u rz ąd z en iam i m am y regulow ać? N o cóż, ty m ak u rat nie chcę Ci, drogi C zy teln ik u , specjalnie zaw racać głow y. W aż n e jest, ż e spełniają o n e dla fizyków jakieś istotne role. U rząd zeń tych m oże być d u żo , d la p rzy k ła d u : •
a) blok w ysokiego napięcia,
•
b) m ły n ek d o kaw y,
•
c) ek sp res d o kaw y,
•
d) je d n o stk a logiczna,
•
e) m atry ca opóźnień,
•
0 przelicznik.
O to, jak ie znaczenie m ają dla fizyka p o szczeg ó ln e u rząd z en ia:
ad a) Blok wysokiego napięcia. C zujn ik i (detektory) p ro m ien io w an ia są z w y k le zasilane w y so k im napięciem . W arto ść tego napięcia jest reg u lo w an a w zależn o ści od d e te k to ra . N asz blok p o trafi sterow ać 256-ściom a źró d łam i ta k ieg o napięcia.
ad b) Młynek do kawy ad c) Ekspres do kawy E k sp ery m en t na w iązce jo n ó w z ak celerato ra trw a zw y k le 4—7 d n i — bez p rzerw y : d zień i noc. N ie tru d n o w ięc z ro z u m ie ć jak w ielką rolę o d g ry w a ją dla fizy k a p o w y ższe d w a u rz ą d z e n ia .
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
1205
To o czy w iście żart. N ie z b y t jed n ak płochy, g d y ż m a d w a cele: •
d ać sz a n sę zro zu m ien ia czy teln ik o m , którzy chcą n a d a l czytać ten ro z d z ia ł, a fizyka jąd ro w a ich zu p ełn ie nie in teresu je,
•
u n ao czn ić, że u rząd z en ia zn ajd u jące się w w ieży n ie m u szą m ieć nic w sp ó ln eg o ze sobą. K onkretniej: nie m u szą kon iecz nie z e so b ą w spółpracow ać. M łynek d o k aw y nie w sp ó łp ra cuje z blokiem w ysokiego napięcia.
ad d) Jednostka logiczna To p o p ro stu d w a p rzełączniki. To, w jakiej mają b y ć pozycji, stero w an e b ęd zie w ła ś n ie k o m p u terem .
ad e) Matryca opóźnień W ty m bloku jest 16 u k ła d ó w potrafiących opóźnić 16 o d d zieln y ch sy g n a łó w — k a ż d y o w y b ran y czas. Fizycy b ard zo lubią takie sztuczki.
ad f) Przelicznik (tzw. scaler) To jak b y zespół 32 liczników . N a nasz sygnał o tw iera się do nich w ejście i p rzez o k reślo n y czas liczniki liczą przychodzące do nich sk ą d ś im pulsy. Po p ew n y m czasie wejście im p u lsó w zam y k am y i p atrzy m y ile k ażd y z liczników tych im p u ls ó w zliczył.
Tyle ro zm o w a z kolegą. R ozpoznaliśm y zag ad n ien ie. Teraz m ożem y p rz y s tąpić d o projektow ania. U w aga kolegi - ta p rzew id u jąca ciągłe m o d y fik a c je - od razu su g eru je, że najlepiej zrobim y posługując się techniką obiektow o o rien to w a n ą.
23.8 Faza: Projektowanie Jak w iem y , faza ta składa się z pięciu etapów . O to one:
13.8.1
Etap 1 - Identyfikacja zachowań naszego systemu To oczyw iście ten scenariusz. U m ó w m y się, że to miejsce w kasecie, w które m o żn a w sunąć blok n azy w ać b ęd ziem y tu szufladką. Bardziej fachow a nazw a to: „stanowisko", lub z angielska: „slot" O to scen ariu sz w postaci tabeli:
1206
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
D ziałanie
Kto
Kogo Przygotow uje to program d o
rysuje
się
rysuje
się
U ży tk o w n ik
określa n u m e r
kasety
U ży tk o w n ik
określa n u m e r
szufladki
U ży tk o w n ik
w tak w y b ran e miejsce w staw ia
blok
oferuje zestaw
bloków
U m ożliw ia to n am w ybór k o nkretnego u rz ąd z en ia
blok
Tak odbyw a się „kręcenie g ałk am i" konkretnego u rz ą d z e n ia w to rze p o m iaro w y m
w ieży
N astaw y w szystkich b lo k ó w w to rze p o m iaro w y m zostają zap a m ię tan e na d y sku
Kaseta M enu |-------- —------------
pracy G otow ość przyjm ow ania poleceń Do tej kasety będzie się zara z zw racał Z tą szufladką chce p racow ać __________________ _________________ ii . W ieża zapełnia się konkretnym i blokam i
-— — — — M enu i U ży tk o w n ik
reguluje
U żytkow nik
ż ą d a zapisu w szystkich n a sta w
U żytkow nik
żąd a odczy tan ia nastaw
w ieży
N a sta w y w szystkich b lo k ó w w to rze p o m iaro w y m zostają o d c z y ta n e z d y sk u i p o szczeg ó ln e bloki są tak u staw ian e.
U żytkow nik
u su w a z szu flad k i
blok
K o n k retn y blok jest u s u w a n y z sz u fla d k i i staje się ona p u s ta .
W scen ariu szu p o m in ąłem w szystkie sytuacje, g d y u ży tk o w n ik ro zm aw ia z k o n k re tn y m blokiem - blokiem w ysokiego n ap ięcia, czy z m ły n k iem d o kaw y. To p o p ro stu dla oszczędności miejsca w tej książce.
23.8.2
Etap 2 - Identyfikacja klas ob iek tów , z którym i m am y do czynienia Jak p am iętam y , trzeba się p rzy g ląd n ąć tabeli. K o lu m n ie p ierw szej i trzeciej. W szy stk o jest w z a sa d z ie jasne. Jed y n ie ch ciałb y m się w y tłu m a c z y ć z klasy „u ż y tk o w n ik ". O czyw iście nie chodzi tu o s a m e g o człowieka. C zło w iek zleca zro b ien ie czegoś p ro g ram o w i za pom ocą w y b ra n ia jakiejś akcji z m e n u . Zatem u ż y tk o w n ik jest re p re z e n to w a n y p rzez k lasę „ m e n u " .
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
1207
S zybki r z u t oka na k o lu m n y 1 i 3 pow yższego sc e n a riu sz a i w idzim y, ż e m am y tu d o czy n ien ia z n astęp u jący m i klasami obiektów : w ieża, kaseta, m e n u (a przez nie: u ży tk o w n ik ), szu flad k a, blok w ysokiego n ap ięcia, m łynek d o k aw y , ek sp res d o kaw y, jed n o stk a logiczna, m atry ca opóźnień, p rzeliczn ik . Skoro zn aleźliśm y już k lasy obiektów w y stęp u jący ch w naszym sy stem ie - to s p o rz ą d ź m y dla nich k a rty m odelujące. W ybacz, ż e nie zrobię tego ju ż w ła d n y c h tabelach - zajęłyby o n e zbyt d u żo m iejsca na stronach tej książki.
Klasa: Wieża ♦♦♦ O bow iązki: - um ożliw ić o k reślenie, którą z kaset w ieży chcem y zobaczyć na ekranie, - p rzep ro w ad ze n ie zap isu nastaw w szy stk ich u rząd zeń na d y sk , - p rzep ro w ad ze n ie odczytu w szystkich n a sta w z dysku. W spółpracow nicy: - kasety, któ ry m w ieża zleca działania, - m enu, od którego otrzym uje rozkazy. ♦♦♦ W idoczne w łasności: - ma pięć kaset, - ma określony n u m e r kasety, którą o feru je w łaśnie do operacji, - m a zestaw bloków , które oferuje d o m o żliw eg o w staw ienia.
Klasa: Kaseta ♦♦♦ O bow iązki: - narysow ać się na ekranie zaznaczając w staw io n e do niej bloki, - skoro kaseta m a 20 szufladek - to trzeba u m ożliw ić użytkow nikow i, pow iedzenie, o której szufladce w d an ej chw ili myśli, - w staw ić blok do d an ej szufladki (jeśli jest pusta), - u sunąć blok z danej szufladki, - regulow ać blok tkw iący w danej szufladce. ♦♦♦ W spółpracow nicy: - wieża, która jej coś zleca, - bloki, które w tkw ią w kasecie, - użytkow nik (m enu). ♦♦♦ W idoczne własności: - 20 szufladek, - num er w ybranej w łaśnie przez u ży tk o w n ik a szufladki, - zestaw bloków , k tó re właśnie w kasecie tkw ią.
1208
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
Klasa: Szufladka «$♦ O bow iązki: - mieścić w sobie blok. «$♦ W idoczne w łasności: - czy jest w niej blok czy nie, i ew entualnie, jaki to blok. «$♦ W spółpracow nicy: - blok, kaseta.
Klasa: Menu O bow iązki: - p o k azanie się na ek ran ie po starcie program u, - um o żliw ien ie w y b ran ia kasety, - um o żliw ien ie w y b ran ia szufladki w kasecie, - oferow anie u ży tk o w n ik o w i następujących działań: •
załad o w an ie z d y sku konfiguracji w ieży w raz z nastaw am i,
•
zapis na dysku konfiguracji w ieży w raz z n astaw am i,
•
p ro p o n o w an ie zestaw u blo k ó w m ożliw ych do w y k o rzy stan ia w w ieży i akcja w staw ienia ich,
•
zakończenie p ro g ram u .
♦$» W spółpracow nicy: - w ieża, k asety , poszczególne bloki. W idoczne w łasności: - sklepik z n azw am i bloków d o w y k o rzy stan ia, - ro zm iar m e n u , kolory itd.
Klasa: Młynek do Kawy «$♦ O bow iązki: - p o zw alać reg u lo w ać n astaw ien ie grubości m ielenia, - p ozw alać u ru ch o m ić akcję m ielenia. (M łynek zg asi się sam , jak skończy mleć). ♦J* W spółpracow nicy: - kaseta, w której ten m łynek tkw i. (U waga: m ły n ek w cale nie w sp ó łp racu je z ek sp resem d o k aw y . To n asza sp raw a, by zm ieloną w d o m u , lu b w tym m ły n k u k aw ę um ieścić w jakim ś ekspresie). W idoczne w łasności: - nastaw io n a g ru b o ść m ielenia. K lasa: E k s p r e s d o k aw y
Obowiązki:
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
1209
- pozw alać regulow ać swoje n astaw y , «*» W spółpracow nicy: - kaseta, w której tkw i ten m łynek. ♦J» W idoczne w łasności:
Klasa: Blok Wysokiego Napięcia ♦$» O bow iązki: - pozw alać u staw iać napięcia sw ych 256 źródeł. ♦{* W spółpracow nicy: - kaseta, w której tkwi. W idoczne w łasności: - w artości 256-ściu napięć w y tw arzan y ch przez ten blok.
Klasa: Przelicznik •i* O bowiązki: - daw ać reg u lo w ać swoją nastaw ę, czyli czas, p rzez k tóry m ają być zliczane im pulsy, - na d an y zn ak u ru ch am iać proces zliczania, - po k azy w ać zliczone w artości na ekranie. <♦ W spółpracow nicy: - kaseta, w której tkwi. ♦♦♦ W idoczne w łasności: - czas zliczania.
Klasa: Jednostka Logiczna ♦♦♦ O bowiązki: - regulow ać nastaw ienie dw óch przełączników . W spółpracow nicy: - kaseta, w której tkwi. ♦♦♦ W idoczne w łasności: - dw a p rzełączniki o określonym w d a n y m mom encie u staw ien iu .
Klasa: Matryca opóźnień ♦$» Obowiązki: - regulow ać nastaw ienie szesnastu o b w o d ó w opóźniających.
*** W spółpracow nicy: - kaseta, w której tkw i blok.
1210
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie ♦♦♦ W id o czn e w łasności: - o b o w iązu jące w danym m om encie ustaw ienie 16 czasów opóźnień.
W N a ty m zak o ń czy liśm y p o szu k iw an ie klas. Pora teraz przejść d o następnego e ta p u .
23.8.3
Etap 3 - U system atyzow anie klas obiektów z w ystępujących w naszym system ie T utaj, jak p am iętam y , m am y sp raw d zić istn ien ie zw iązków d zied ziczen ia oraz z w ią z k ó w polegających na m ieszczeniu (zaw ieraniu) w klasie o b iek tó w innych klas.
Ustalenie hierarchii Jak p am ięta m y , d zied zic zen ie sp raw d zam y w ygłaszając zdania: „o b iek t klasy A jest rodzajem obiektu klasy B". Próbując takich z d a ń na n aszy ch klasach d o stajem y sam e zd a n ia n ie p ra w d z iw e - bo: a n i m łynek nie je st rodzajem kasety , an i kaseta rodzajem m enu itd, itd. Z atem nie ma tu żad n y ch zw iązk ó w d zied ziczen ia? Jak w idać tak ich zw ykłych rzeczyw iście nie ma. Skoro n ie ma, to nie m a - n ie n ależy d z ie d z ic z e n ia robić na siłę ! Z a p y ta sz pew nie: „ -A dziedziczenie od jakichś klas abstrakcyjnych ?“ Brawo! C zekałem na to, bo to najw ażniejsza rzecz w tym całym p rzy k ła d zie. O tóż ch y b a od sam eg o początku było jasne, ż e w naszym sy stem ie jest jed n a b a rd z o w ażn a klasa abstrakcyjna. Jaka? Z a u w a ż , że ciągle m ów iliśm y: w k ła d a m y blok, u su w a m y blok, regulujem y blok. Blok jest czym ś ab strak cy jn y m , bo czasem jest to przelicznik, czasem m łynek, czasem blok w y so k ieg o napięcia, a czasem jed n o stk a logiczna. C o jed n o czy te w szy stk ie u rząd zen ia? M ian o w icie to, że w szy stk ie są w k ła d a n e d o kasety, w szystkie pozw alają się reg u lo w ać, w szy stk ie będą m iały n astaw y z a p isy w a n e na d y sku lub o d tw a rz a n e z niego. W rezultacie w ięc o d k ry liśm y , że kilka klas z n aszeg o system u p o w iąza n y ch jest taką hierarchią:
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
1211
R ys. 2 3 -5 . O d k ry c ie klasy ab strakcyjnej jest p rzeło m o w ym krokiem w p ro jeko w an iu program u
Jest to fakt w ielce d o n io sły - i p ra w d ę m ów iąc dzięki istn ien iu tej w łaśnie hierarchii nasz p ro g ra m będzie w łaśn ie ob iek to w o o rien to w a n y , a nie jedynie: „bazujący na o b iek tach ". W rócim y d o tego jeszcze.
Mieszczenie (zawieranie) w sobie obiektów innych klas S praw dzając tę cechę w y p o w iad am y z d a n ia typu: „A sk ład a się m ięd zy innym i z obiektu klasy B". W rezultacie tych p ró b zn ajdujem y kilka takich zd ań , które brzm ią sensow nie: •
W ieża składa się m. in. z k aset (pięciu!)
•
K aseta składa się m .in. z bloków (maks. d w u d z ie stu )
•
K aseta składa się z szu flad e k
Innym i słow y o zn acza to, że sk ładnikiem obiektu klasy w ieża m a być 5 obiek tó w klasy kaseta. (L ub w skaźników d o takich obiektów ). Z kolei składnikiem obiektu klasy kaseta jest 20 obiektów k lasy szu fladka. S k ładnikiem klasy szu flad k a może być obiekt klasy blok (lub w sk a źn ik d o takiego obiektu). Widzisz: ju ż zaczynamy korzystać z klasy abstrakcyjnej! Gdyby nie ona musielibyśmy mówić: kaseta składa się czasem z młynka do kawy, czasem bloku wysokiego napięcia, czasem... Proponuję całość o d razu trochę uprościć. Skoro szufladka jest tak m ało inteli g en tn ą klasą - to p o p ro stu otw ór w kasecie - dlatego nie m a sen su z szufladki robić osobnej klasy. M uszę ci się już te ra z przyznać, że z 20 szu flad ek zam ierzam zro b ić zwykłą 20-elem entow ą tablicę (w skaźników ).
23.8.4
Etap 4 - O kreślenie w zajem nych zależności klas Robim y to dw ojako: sporządzając spis relacji i sporządzając graf. Porozm aw iajm y je d n ak najpierw o tym , jak na dysku zam ierzam y zapisyw ać inform ację o stanie w ieży . Zapisać trzeba: ♦$. -ja k ie g o ty p u bloki um ieszczone są w jakich szu flad k ach kaset, ♦♦♦ - jakie te k o n k re tn e bloki m ają n astaw y . Najlepiej zrealizow ać to w ten sposób, że w pliku zapisyw ać b ęd ziem y tak:
23
1212
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie kaseta 1 szufladka 4 jej zawartość i nastawy
szufladka 15 jej zawartość i nastawy
kaseta 2 .... itd O d c z y t tej zapisanej na dysku informacji o d b y w a się tak, że zaczyna czytanie w ieża. W iąże się to oczywiście z tym, że m usi otw orzyć plik, w którym te inform acje są zap isa n e i zacząć go czytać. K iedy napotka linijkę treści „kaseta n r..." o d d aje inicjatyw ę właściwej kasecie, b y sobie sam a poczytała odpow iedni d la niej fragm ent. Z atem kaseta czyta dalej i dow iaduje się, ż e np. w szufladce nr 4 m a stać blok w y so k ieg o napięcia. U m ieszcza więc tam taki blok. Teraz n astąpią nastaw y. Kto najlepiej odczyta z d y sk u nastaw y tego b loku? O czyw iście o n sam! Zatem k aseta zleca tem u blokow i, by sam czytał dalej. Blok w ysokiego napięcia wie, że p o trz e b n e są m u n a sta w y 256 n a p ię ć - więc czy ta z dysku 256 liczb regulując się naty ch m iast. G dy blok skończy czytanie, to czytanie przejm uje kaseta i d o w ia d u je się, że w szu flad ce następnej m a stać n p . m łynek d o kaw y. W staw ia więc tam blok tej klasy. N o w o n a ro d z o n y blok sam odczytuje z dysku sw oja nastaw ę - g ru b o ść m ielenia. G d y ju ż się „n areg u lu je" kaseta czyta dalej i d o w iad u je się, ż e w szu flad ce n r 15 stoi jeszcze jeden m łynek d o kaw y. W staw ia w ięc tam taki blok i p rzek azu je inicjatyw ę blokow i - tem u d ru g ie m u m łynkow i. Blok w czy tu je sobie sw oje n astaw y , p o czym oddaje inicjatyw ę kasecie. W ten sp o só b kaseta o d czytuje inform acje o w szystkich blokach tkw iących w szu flad k a ch . G dy sk o ń czy się o d czy ty w an ie inform acji o tej kasecie, w ieża poleca to sam o zrobić kasecie następnej - i ta k w szy stk im pięciu. C o tu jest w ażne? To m ianow icie, że ani w ieża ani kaseta nie m a rtw ią się o o d czy ty w a n ie o k reślo n y ch nastaw . To k a ż d y blok sam o d zieln ie odczytuje inform acje dla siebie. Wieża mówi kasecie, by odtworzyła swą konfigurację, ale nie poucza ją, jak to robić. Kaseta wie, że składa się z 20 szufladek, więc czyta ich zawartość i doioiaduje się jakie to bloki w niej stoją. Kaseta jednak nie interesuje się tym , jakie to nastawy zapisują sobie na dysku poszczególne bloki. Po prostu poleca im, by same sobie poczytały a to, jak to zrobić i co przeczytać, wiedzą już one same najlepiej.
Spis relacji W ed łu g p o p rzed n ich zaleceń m usim y sp isać zależności klas. N a p o d staw ie scen ariu sza, a tak że na p o d sta w ie p o w y ższy ch ustaleń. M enu: - zleca w ie ż y n ary so w an ie s ię na ek ran ie, - zleca w ie ż y zap isan ie na d y s k u jej: k o n fig u racji (w jakich k asetach stoją jakie bloki) i w sz y stk ich n a s ta w ty c h bloków , - zleca w ie ż y o d czy tan ie z d y s k u jej k o n fig u racji i n astaw ,
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
1213
- zleca w ieży ro zm o w ę w celu d o k o n an ia reg u lacji nastaw , - zleca w ieży, by w staw iła w y b ran y blok do określonej kasety i szu flad k i, - zleca w ieży w y rzu cić blo k z określonej k asety i szufladki. W ieża - d o w iad u je się o za p isa n ą na dysku k o n fig u rację, - zleca kasecie, by o d czy tała z dysku za w a rto ść sw ych 20 szuf la d e k (jakie bloki w nich stoją), zleca kasecie, by zap isa ła na dysk sw ą zaw arto ść, - w ybranej kasecie zleca, by narysow ała się n a ekranie. Kaseta - zleca blokowi, by z a p isa ł/o d c z y ta ł sw oje n a sta w y n a /z dys- zleca blokowi, by p o w ied ział kim jest (o d p o w ied ział skró te m nazw y swojej klasy), - zleca blokowi, by ro zm a w iał z u ży tk o w n ik iem (jest to regu lacja nastaw ),
Graf A oto, jak w y g ląd a g raf dla takiego p o d ziału obow iązków Rys. 2 3 -6
Menu
Z auw ażm y kilka rzeczy: 1) Jasno w idać, że składnikiem klasy w ieża są obiekty k lasy kaseta Z kolei w e w n ętrzu kaset tkwią bloki. A bstrakcyjna klasa blok reprezen tuje na tym g rafie wszystkie kon k retn e realizacje b lo k ó w — tak w ym yś liliśmy w p o p rzed n im etapie.
1214
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie «$♦ 2) Ż ad n a klasa nie rozm aw ia n ig d y bezpośrednio z obiektem będącym sk ład n ik iem innej klasy. N aru szało b y to zasadę enkapsulacji. W niektó rych m om en tach m ożna odczuć taką p o k u sę -je d n a k na d łu ż szą metę to się nie opłaca. <$♦ 3) G raf w y g ląd a stosunkow o prosto. Nie popełniliśm y tu b łęd u , polega jącego na tym , że k a ż d y w spółpracuje z każdym .
23.8.5
Etap 5 - Składam y model naszego systemu N a p o d staw ie tego, co w ym yśliliśm y w p o p rzed n ich etapach, spró b u jem y w re szcie dokonać zło żen ia m odelu. Aby to uczynić, najpierw p ró b u jem y napisać definicje klas. P am iętam y p rz y tym, ż e zach o w an ia poszczególnych klas stają się ich funkcjam i sk ład o w y m i, a w id o czn e w łasności stają się danym i sk ład o w y m i.
Właściwie sprawa jest prosta i jasna, gdyby nie istnienie klasy abstrakcyj nej Jak ie zachow ania m a abstrakcyjna klasa blok? P rzy k ład o w o m u si ro zm aw iać z u ży tk o w n ik iem i pozw alać m u regulow ać k o n k re tn y blok. C zyli w klasie abstrakcyjnej blok pow inna być jakaś funkcja sk ła d o w a o n azw ie „ ro zm o w a ". Z drugiej stro n y jednak w p rz y p a d k u każdego z k o n k retn y ch b lo k ó w taka rozm ow a w y g ląd a inaczej. R ozum iesz już, co to o zn acza? A leż tak, oczyw iście - funkcja ta m usi być funkcją w irtu aln ą! P o d o b n ie w p rz y p a d k u innych zachow ań klasy blok - czyli za c h o w a ń polega jących na zap isan iu lu b o d czytaniu sw oich n a sta w z d y sku. To oczyw iste — in n e n astaw y z a p isu je /o d c z y tu je z d y sku m ły n ek do kaw y, a in n e blok w yso k ieg o napięcia. O to , jak w obec tego w y g ląd a definicja takiej klasy: class
blok
{ public:
//---------- fu n k c je
sk ła d o w e
void
r o z m o w a ()
virtual virtual
void void
zapisz_sie(ofstream & plik_wyj) odtworz_sie(ifstream & plik_wej)
virtual
string
virtual
~blok();
=
0;
/ / c z y s t o w irtualna
virtual
= =
0; 0;
k t o _ j e s t e s () ;
}; S am e n azw y m ów ią d o czego funkcja słu ż y . Jeśli jesteś m ało d o m y śln y , to w y jaśn iam , że funkcja o d t w o r z s i e s łu ż y d o o d czy tan ia z d y sk u n astaw . Jej a rg u m e n te m jest referencja d o stru m ien ia czy tająceg o z pliku. To tak, jakby nauczyciel w klasie miał jedną, archiwalną, X IX wieczną książkę, z której kolejno wyznaczani uczniowie mają na głos odczytać fragmenty. W odpowiedniej chwili nauczyciel daje wybranemu uczniowi książkę i mówi: "Dalej czytaj ty, począwszy od tego miejsca".
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
1215
Ten argument funkcji c d t w o r z _ s i e - to właśnie oryginał tej księgi, którą podają sobie różne obiekty, by czytać przeznaczone dla nich fragmenty. Pojaw iła się też now a funkcja k t o _ j e s t e s . Jest ona n ajlep szy m d o w o d em na to, że p ro jek to w an ie robi się m eto d ą kolejnych p rz y b liżeń . K onieczność istnienia tej funkcji w yp ły n ęła m i n a p ra w d ę d o p ie ro w trak c ie im plem entacji klasy k a s e t a . Jeśli kaseta ma narysować się, to musi narysować także wszystkie bloki, które aktualnie w niej tkioią. Blok na płycie czołowej powinien mieć jakiś napis, który odróżnia go od innych bloków. Kaseta pyta więc po kolei wszystkie bloki tkwiące w jej szufladkach. Blok zapytany ktoje ste ś odpo wiada stringiem będącym nazwą bloku. Tę nazwę wypisuje się na obrazku. Po to, by użytkownik patrząc na obrazek łatwo rozpoznawał, co w kasecie jest. W zestaw ie funkcji składow ych jest też d estru k to r. P o n iew aż sp o d ziew am y się, że w p ro g ra m ie m ożem y stosow ać z d a n ia typu: „w y rzu cam w diabły ten blok" (zam iast: „w y rz u c a m w diabły tę jed n o stk ę logiczną lub ten m ły n ek do k a w y " ), zatem - ab y w ów czas zad ziałał n a p ra w d ę d estru k to r w y rz u c a n e g o m łynka, a nie d e s tru k to r abstrakcyjnego bloku - d estru k to r m u si być w irtu aln y . Przyjrzyjm y się teraz deklaracjom innych klas.
O S kladnikiem -daną tego m enu jest w sk aźn ik do w ieży, której m e n u ma rozkazy wać. P om inąłem tutaj składniki opisujące kolor m enu, czy jego rozm iary na ekranie. To w szystko, to kosm etyka i zależy od tego, jakim kom puterem się posługujem y. W klasie tej są także funkcje składow e. O to kilka słów o ich roli: © K onstruktor p o w o d u je pojaw ienie się tego m enu na ekranie, a także informuje klasę, który to obiekt klasy w ieża ma być przed m io tem jego zainteresow ania. © Funkcja sk ład o w a wybór_opc j i to ta, k tó ra oferuje cały g a rn itu r usług. To w niej oferujem y u żytkow nikow i w y b ó r d ziałań na w ieży. To tu taj decyduje on, którą z w ielu szu flad ek w ieży zam ierza się w d anym m om encie zajmować. To tutaj u ży tk o w n ik ma szanse coś do szu flad k i w staw ić, czy to coś, co tam jest w staw ione - regulow ać, lub coś stam tąd usunąć. To tutaj też podejm uje on decyzje o zap isie w szystkich n astaw w ieży na dysku, lub też decyzje o odczy taniu starych n astaw z dysku.
1216
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie
Oto deklaracja klasy wieża: t
c o n s t i n t i l e k a s e t = 5;
/ / u i i i n i i i n m / i i / i i i i n i i i u i i i i n i i i i i n i i i i i n 11111111111
c l a s s w ieża
{ k a s e t a k [i l e _ k a s e t ] ; i n t a k t_ k a ;
// pięć obiektów klasy kaseta // nr aktywnej kasety
public:
// funkcje składowe----------------- —— ----------------------w ie ż a ( ) ; . . . . . v o id n a r y s u j () ; ff narysowanie wieży na ekranie _ // całej albo tylko interesującej części
v o id v o id v o id v o id
// 0
// funkcje odpowiadające za zapis na dysk _ __ // wszystkich nastaw danej wieży z a p is ( ) ; o d c z y t( ) ; .. . . . // będziemy pracować nad konkretnym miejscem w tej wieży zatem: w y b ie rz ( i n t n r ) ; // wybranie numeru kasety wyb s z u f ( i n t n r ) ; // wybranie numeru szufladki
// dowiadujemy się o bieżący nr kasety i szufladki in t k to ra _ s z u fla d k a ( ) ; i n t k tó ra k a s e ta ( ) ; v o id d o d a j O ; v o id u s u ń ( ) ; v o id r e g u l a c j a O ;
// w konkretne miejsce wieży można coś wstawić // można TO usunąć // można TO regulować
ł; O S kład n ik iem ob iek tu klasy w ieża jest pięć obiektów klasy k aseta. W świecie realn y m są o n e na stałe um ieszczo n e w w ieży za pom ocą śru b , w ięc w klasie w ieża w id zim y p o p ro stu pięcioelem entow ą tablicę ob iek tó w k la sy kaseta. © W idoczną w łasnością w ieży jest też n u m e r kasety, z k tó rą w łaśn ie się zajm uje m y. (O czyw iście m a m y d o w yboru jedną z pięciu kaset). Na ekranie mojego komputera jest to naprawdę widoczne. To jeden z 5 kwadratów, który muszę kliknąć myszką. Kiedy to uczynię, guzik ten staje się od tej pory czarny, przypominając m i ciągle, którą to kasetę na ekranie właśnie widać. N az w y funkcji sk ład o w y ch od razu su g eru ją, co d a n a funkcja robi. U w ag w y m ag a tylko k o n stru k to r 0 . Skoro rezy g n u ję z ry so w an ia całości w ieży na ek ran ie - k o n stru k to r nie robi nic szczególnego. W m oim p rz y p a d k u p o prostu inicjalizuje tylko d a n ą sk ład o w ą a k t k a. P rzy p o m in am , że jeśli w klasie są obiekty sk ład o w e (a są!), to ich k o n stru k to ry ru sz ą do p racy p rz e d w y k o n an iem k o n stru k to ra klasy w ieża.
Deklaracja klasy kaseta c o n s t i n t i l e s z u f l a d e k = 20;
///!////U H 17///!//////HI/I//I///!/////!f//ft//!t/1/// c l a s s k a s e ta b lo k * s z u f la d k a [ i l e s z u f l a d e k ) ;
// tablica wskaźników
O
1217
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Faza: Projektowanie int
akt_sz;
// nr właśnie wybranej szufladki
©
II konstruktor II rysowanie kasety
8
public:
-funkcje składowe kaseta void void int
() ;
n a r y s u j _ s i e () ; w y b i e r z (int
n) ;
a k t ();
// wybranie szufladki II mform. o aktywnej szufl.
// wieża zleca takie funkcje każdej z 5 kaset ( o f s t r e a m & p l i k _ w e j ) ;// zapis bloków kasety j b l o k ( i f s t r e a r a & p l i k _ w y j ) ; // oaczyt bloków
void
zapisz_bloki
void
czyta
void void void
s k a s u j (); w s t a w (); e d y c j a ();
void
s t w ó r c a (int
// wyrzucanie bloku II wstawianie bloku II będzie regulacja nr);
// pomocnicza funkcja
); O Jak p am iętam y , obiekt klasy kaseta sk ła d a się z k ilk u n astu szu fladek, w których w sunięte m ogą być bloki. Bloki te stają się składnikam i k asety . O biekt m oże być składnikiem k asety albo na tej zasad z ie, że jest jej sk ład n ik iem n ap raw d ę, albo na tej z a sad z ie, że klasa kaseta m a d o niego w skaźniki. Ten d ru g i w arian t o d p o w iad a n a m bardziej, bo skoro b lo k i mają być czasem w staw ian e, a czasem w yrzucane, to lepiej je tw orzyć w za p a sie pam ięci, a d o s tę p d o nich mieć za pom ocą w sk aźn ik ó w . D latego w łaśn ie d an ą składow ą k la sy kaseta jest tablica w skaźników d o obiektów klasy blok. © D rugą d an ą sk ład o w ą jest n u m e r szu flad k i, n ad którą w d an y m m om encie zam ierzam y się pastw ić. Co do funkcji składow ych to: ♦$» - k o n stru k to r © o d p o w iad a z a w stępne w y zero w an ie kasety (kaseta zaraz p o w y p ro d u k o w an iu jest przecież pusta); ♦$» - funkcja narysuj sie O ak ty w o w an a jest p rz e z w ieżę. Tym sposo bem w ieża rozkazuje jednej z 5 kaset, by w łaśnie o n a narysow ała się na ekranie; ♦♦♦ - funkcja wybierz © , którą w ieża nakazuje kasecie, by nastaw iła się na pracę z określoną szufladką kasety; ♦♦♦ - na w y p ad ek , gdyby się ktoś potrzebow ał d o w ied zieć o num er tej a k tu aln ie w ybranej szufladki je st fu n k q a a k t © ; ♦♦♦ - jeśli m y z menu zażądam y, by w ieża zapisała sw e n astaw y na dysku, w ieża przygotow uje plik d y sk o w y (czyli otw iera g o i coś tam w stępnie zapisuje). N astępnie rzeczona w ieża zm u sza do p ra c y po kolei w szyst kie 5 kaset. Robi to w yw ołując dla każdej kasety jej funkcję zapiszbloki © .W ram ach tej funkcji każda kaseta z ałatw i się ze w szystkim i tkw iącym i w niej blokam i zm u szając je d o pracy; - łatw o się domyślić, że p o d o b n ie jest w p rz y p a d k u funkcji czyta j_blok © , za pomocą której w ieża zm usza daną k asetę d o odczytania z dysku inform acji o w szystkich tkw iących w niej blokach;
1218
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu ♦♦♦ - jeśli m y za pom ocą m enu nakażem y w ieży w yrzucenie jakiegoś bloku, to w ieża - w iedząc, gdzie ten blok stoi - w y d a polecenie odpow iedniej kasecie. Zrobi to w łaśnie tą funkcją s k a s u j © ; «$♦ - n ato m iast do w staw ienia bloku służy funkcja w staw © ; - na id en ty czn y ch zasadach o d b ęd zie się aktywacja funkcji edycja O O , g d y zap rag n iem y regulow ać jakiś blok; - w klasie w id zim y też funkcję s t w ó r c a O 0 Ta funkcja służy do p o w o ły w an ia d o życia o d p ow iednich bloków . Stw órca stw arza je w zap asie pam ięci.
A oto jedna z klas pochodnych od abstrakcyjnej klasy blok - klasa młynek Z a u w a ż p u bliczne d ziedziczenie klasy blok. c l a s s m łynek : p u b l i c b lo k { i n t g ru b o ść; / / ---------------------- fu n k c je składow e p u b lic ; m ł y n e k () { g ru b o ś ć = 5; ) m i e l ();
v o id rozm ow a( ) ; v o id z a p i s z _ s i e ( o f s t r e a m & p lik _ w y j); v o id o d tw o r z _ s ie ( i f s t r e a m & p lik _ w e j); s trin g k to _ je s te s (); ~ m lynek() { c o u t << " d e s t r u k t o r m łynka" << e n d l;
}
}; Z w ró ć uw agę, ż e są tu funkcje, które w abstrakcyjnej klasie b l o k są określone jako w irtu aln e. Tutaj w ięc określim y, co to znaczy regulow ać m ły n k i, czy też zap isy w ać n astaw y takich m łynków na dy sk .
23.9 Implementacja modelu naszego systemu T eraz w zasad z ie należało b y przy stąp ić d o im plem entacji czy li definiow ania poszczególnych funkcji. Z e w zg lęd u na o szczęd n o ść miejsca w tej książce ca łości listingu Ci oszczędzę^. P rzyjrzym y się tylko m iejscom w y jątkow ym . Z czego w tym p ro g ram ie jestem d u m n y n ajbardziej? O czyw iście z tych miejsc, g d z ie opłaciło m i się stosow ać technikę O O . In n y m i słow y z ty ch miejsc, gdzie polim orfizm u łatw ił mi życie.
Regulacja nastaw bloków W eźm y na p rz y k ła d sytuację, g d y chodzi n a m o regulację d a n e g o bloku. 4)
Cały program możesz ewentualnie sobie sprowadzić przez Internet z mojej strony WWW. Razem z innymi programami z tej książki.
1219
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu Rys. 23-7 ^ ► w i e z a : :regulacja () ; X
Regulacja
(k o n k re tn a k a s e ta )
kaseta: :edycja () ; {(k o n k re tn a s z u fla d k a )
b l o k ::r o z m o w a ();
M enu w y w o łu je w naszej w ie ż y funkcję sk ład o w ą r e g u l a c j a . Jej deklarację w id z ie liśm y 2 strony w cześniej. Teraz ją zdefiniujm y. (Jest to tak p ro sta funkcja, że o czy w iście zrobię ją i n l i n e - jednak nie z a w raca jm y sobie teraz g ło w y takim i sp raw am i). void wieża::regulacja{) { k [akt_ka].edycja(); 1 Funkcja ta w iedząc, którą k asetę m am y na m yśli (określiliśm y to w cześniej) w y w o łu je funkcję składow ą e d y c j a dla tej w łaśn ie kasety. Jej deklarację tak że w id zieliśm y na p o przednich stronach (w klasie k a s e t a ) . O to definicja tej funkcji vcid kaseta::edycja() { . , i f (szufladka [akt_sz] ) // j e ś l i s z u f l a d k a n i e j e s t p u s t a . . . { // ...t o p o r o z m a w i a m y z b lo k ie tn , szufladka[akt sz] -> rozmowa(); // !!
k tó r y w n ie j t k w i
W ew n ątrz tej funkcji w idzim y, instrukcję i f, która sp ra w d z a czy cokolwiek jest w su n ięte d o danej szufladki. (N um er interesującej szufladki określiliśm y wcześniej). Jeśli w szu flad ce rzeczywiście coś jest to... Tak, tak m a m y tu w łaśnie cu dow ność polim orfizm u. Poniew aż s z u f l a d k a jest tablicą w sk aźn ik ó w , więc linijkę tę m ożem y prościej zapisać tak: w s k a ź n ik _ d o _ b lo k u
-> rozmowa () ;
Jak p am iętam y , w klasie b l o k funkcja rozmowa jest funkcją w irtualną, a zatem nasz k o m p u ter spraw dzi sobie najpierw na jaki typ bloku ten w skaźnik p o k a z u je i u ru ch o m i funkcję rozmowa z konkretnej (już nie-abstrakcyjnej) klasy. Czyli będzie to rozm ow a z m łynkiem , albo rozm ow a z m atrycą opóźnień. A teraz p ytanie: jak trzeba zm ienić tę linijkę, gdy d o k u p im y d o naszego system u now y ty p bloku?
1220
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu O d p o w ied ź: w cale nie trzeba zm ieniać - i to właśnie jest najw spanialsze w p ro g ram a ch o b iek to w o orientow anych. W ystarczy, że pisząc klasę dla tego no w eg o bloku u czy n im y ją klasą pochodną od klasy blok i wtedy a u to m aty c zn ie n a d a je się ona do takich miejsc w program ie.
Zapis konfiguracji i nastaw na dysk T eraz zobaczm y, co (przez technikę O O ) zyskaliśm y p rzy zapisie nastaw na d y sk
Rys. 2 3 - 8 f
>
MENU
wieża: :zapis () ;
(pętla po 5 kasetach) kaseta::zapisz_bloki(plik_wyj); pętla po 20 szufadkach tej kasety X
/
Zapis
J
. V
'•>
1 blok::zapisz_sie(plik_wyj);
s
_______________________________________________
A kcję tę inicjuje się w m enu. Klasa m enu w yw ołuje w ted y funkcję składow ą z a p i s z klasy w ieża. Oto, jak ją zdefiniujem y:
void wieża: : z a p i s () ■otwarcie pliku
ofstream plik_wyj("t. sav", i o s : : o u t ) ; // zapis treści poszczególnych kaset pętla po wszystkich kasetach
f o r (int i = 0; i < i l e kaset; i++) plik_wyj << "Kaseta " << i << endl; k [ i ] . zapisz_bloki(plik_wyj) ; i f ( !plik_wyj ) // —
// 0 // ©
imitujemy obsługę błędów
cout << "\aBlad w trakcie zapisu << i << ” kaseLy" << endl;
}
p l i k . close ( ) ;
- zamknięcie pliku
Idea jest taka: w ieża, której zlecono d o k o n a ć zap isu w szystkich n a s ta w d o pliku d y sk o w eg o —n ajp ierw otw iera p lik © . P o m ijam tu p y tan ia o n a z w ę pliku. To jest przecież try w ialn e. Tu dla p ro sto ty p lik n azy w a się z a w s z e t . sa v 0 N astęp n ie w ieża kolejno zm usza k asety b ęd ące jej sk ład n ik am i, b y kolejno zap isały sw e n astaw y . (Pętla). O stateczn ie plik jest z a m y k a n y i ew en tu a ln ie sp ra w d z a się p o p ra w n o ść operacji z d y sk iem . © Z ap is nastaw bloków znajdujących się w k asecie o d b y w a się - jak w id z im y p rz e z w y w ołanie funkcji składow ej zapisz bloki. O to jej definicja:
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu v o id k a s e t a : : z a p i s z _ b l o k i ( o f s t r e a m
1221
& p lik _ w y j)
// pętla po wszystkich szufladkach kasety f o r ( i n t i = 0; i < i l e _ s z u f l a d e k ; i+ + ) // jeślicoś w szufladce jest // zapis numeru szufladki p l i k wyj « " \ t s z u f l a d k a " << i << e n d l; // zapis informacji specyficznej dla // konkretnego bloku wykonuje sam ten blok s z u fla d k a [i]-> z a p is z _ s ie (p lik _ w y j) ;
i f ( s z u f la d k a [ i ] ) {
} } ) jak w id zim y , jest tu zn o w u pętla po w szystkich (zapełnionych) szu flad k ach k asety . Jeśli szu flad k a jest zap ełn io n a, to z a p isz e się jej „ad res", czyli n u m er k asety i n u m er szufladki. M ożna to załatw ić jak o ś inaczej, zap isu jąc n u m er k asety tylko raz, p rzy p ierw szej szufladce d an ej kasety. To jest już szczegół. W aż n e jest, że na d y sku zn ajd zie się inform acja o ty m , gdzie jaki blok sto i. Czyli o tym , jaka jest „k o n fig u racja" w ieży. A te ra z sp raw a sam ych n astaw . N ajw ażniejsza jest tu linijka następna, k ie d y to kaseta zleca abstrakcyjnem u blokow i, by sam z ap isa ł sw oje nastaw y. P o n iew aż za p o m o c ą w skaźnika w y w o łu jem y funkcję, k tó ra jest funkcją w irtu a ln ą — d la teg o znow u objaw i się tu cud polim orfizm u. To znaczy, że w y startu je funkcja z a p i s z s i e nie z klasy abstrakcyjnej b l o k , ale z takiej klasy p o ch o d n ej od niej, do jakiej ó w konkretny, p o k azy w an y przez w sk aźn ik blok należy. Jeśli pokazuje na m łynek, to będzie to z a p i s z _ s i e z m ły n k a, jeśli p o k azu je na jednostkę logiczną, to będzie to z a p i s z _ s i e z tej jednostki. N ie z a p y ta m Cię już jak zm ien ić tę funkcję w sytuacji, g d y d o k u p im y do system u now y blok. O czyw iście cud polega na tym , ż e żad n a zm iana nie b ęd zie po trzeb n a. P o w tó rzy m y jeszcze raz: Zapis nastaw na d y sk polega na tym , ż e wieża przelatuje sobie w szystkie składające się na nią szu flad k i (5 *20) i mówi: „N iezależn ie od tego, co stoi w tej s z u fla d c e - proszę się z a p isa ć na d ysk". N ie po w iedziałem tu jeszcze jednego: otóż blok za n im zapisze swoje n astaw y , zap isze inform ację jakim jest blokiem . To po to, by czytając potem tę inform ację, kaseta w iedziała, kom u zlecić pracę. Po sam ym z b io rz e nastaw nie m usi b y ć to od razu jasne. Zapam iętajm y to, b o ż a chw ilę z tego będziem y korzystali: a w ięc p rzed n astaw am i konkretnego bloku zapisany jest jego identyfikator.
Odczyt konfiguracji i nastaw z dysku N a d y sk u oprócz n astaw zn alazła się także inform acja o tym, w jakich szuflad k ach um ieszczono jaki ty p bloków.
1222
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu ____
-— —----- ---
Rys. 23-9 C
MENU Odczyt
^ ► w i e z a : :odczyt () ; — '
i kaseta :: stwórca () ;
V
1 T kaseta::czytaj_blok(plak_wej);
1
,x
blok::odtwórz_sie(plik_wej) ,
s_____________ y
G dy chcem y te ra z odczytać to, co w zeszłym tygodniu p o w y ższy m sposobem zapisaliśm y, to w y b ieram y w m enu opcję „odczyt". W ów czas m enu wywołuje w naszej w ieży jej funkcję składow ą o d c z y t .
vo id w i e ż a : : o d c z y t [) // l o k a l n e n r k a s e t y i s z u f l a d k i i n t sz; i n t typ; s t r i n g wyraz; -o tio a r c ic p lik u if s t r e a m p l i k _ w e j ( " t . s a v " , i o s : : i n ) ; //i f ( !plik_wej) ( cout << "\aBlad otwarcia p li k u do czytania" << endl; // s u k c e s y w n e c z y t a n i e p li k u i t w o r z e n i e w g t e j i n f o r m a c j i // o d p o w i e d n i c h b lo k ó w w o d p o w i e d n i c h m i e js c a c h i n t nr_ka se ty = 0 ; w h i l e ( ! p li k _ w y . f a i l () ) { plik_wej >> wyraz; i f ( ! p l i k _ w e j ) break; if( w yra z == "kaseta")
// ©
// © // S t y
k o n ie c p l i k u
// O
// b ę d z i e
n r k a s e ty
plik_wej >> nr_kasety; e l s e if ( w y r a z == "szufladka")
// ©
// b ę d z i e
plik_wej >> sz; k [ n r _ k a s e t y ] .w ybi er z(sz) ;
//
p l i k wej »
// m a
typ;
// k r e a c ja t a k i e g o b lo k u , k t ó r y j e s z c z e n i e k [ n r _ k a s e ty T -s tw ó r c a (t y p );
n r s z u fla d k i
u a k ty w n ie n ie te j s z u f la d k i b y ć te n t y p
is tn ie je
// n ie c h k a s e t a i n i c j u j e a k c ję w c z y t a n i a p a r a m e t r ó w b lo k u k[nr k a s e t y ] . c z y t a j _ b l o k ( p l i k _ w e j ) ; ) p lik .clo se();
// ©
// O
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu
1223
Przyjrzyjmy się tej definicji. O Jest o czy w iste, że plik z d a n y m i m usi zostać o tw arty , zam k n ięty , i ż c p o w in n a być ja k a ś kontrola czy p raca z plikiem była p o p ra w n a . Z o b a c z m y , jak o d b y w a się sa m o czytanie. Jest to pętla while 0 , k tó ra skończy się, g d y n ap o tk an y zo stan ie koniec pliku. Z a łó ż m y , że kaseta w chw ili rozpoczęcia czytania jest z u p e łn ie pusta. Z a c z y n a m y czytać © Jeśli p rzeczy tan e słow o b rzm i „k aseta" O , to o zn ac za, że z a ra z n astąp i n u m e r kasety, której dotyczyć m a d a lsz y fragm ent. C z y ta m y więc ten n u m e r © . Jeśli p rzec zy tan e słow o brzm i: „szu flad k a" 0 , to oznacza, że te ra z m ożem y w czy ta ć n u m er szu flad k i O . Po nim nastąpi n u m e r identyfikacyjny ty p u bloku © . T en iden ty fik ato r to nic strasznego - oto, jak g o zrealizow ałem : enum typy_blokow { młyn = 1, expr, napi, skal, jedn, matr }; Jak w id ać, to zw y k ły ty p w yliczeniow y. Jeśli d o k u p ię jakiś now y blok, to po p ro stu na końcu tej listy n ap iszę skrót jego n azw y . © G d y p rzec zy tam id en ty fik ato r (do zm iennej typ), to w yw ołuję funkcję stwór ca. Ta funkcja w zap asie pam ięci kreuje (o p erato rem new) obiekty ż ąd a n eg o typu . O to, jak w ygląda stwórca: void kaseta::stwórca(int nr) { switch(nr){ default: cout « "nieznany blok"; break; case młyn: szufladka[akt_sz] - new młynek; break; case expr: szufladka[akt_sz] = new express; break; case napi: szufladka[akt_sz] = new wys_napiecie; break; case skal: szufladka[akt_sz] = new skaler; break; case jedn: szufladka[akt_sz] = new jednostka_logiczna; break; case matr: szufladka[akt_sz] = new matryca_opoznien; break; / / ........ tu ewentualne nowe bloki } // end switch }
1224
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Im plem entacja modelu naszego systemu Jak w id a ć - nic tu specjalnego. Instrukcja new w kilku w ariantach. Rezultatem d ziałan ia o p erato ra new jest w skaźnik do now ostw orzonego obiektu. Ten to w skaźnik w staw iam y w odpow iednie miejsce tablicy szufladka. Jeśli d o k u p ię n o w y blok do naszego system u, to to jest jedno z tych niewielu miejsc, g d z ie będ ę m usiał dokonać m odyfikacji. Pojawi się po prostu następny p rz y p a d e k case. Tylko tutaj. Przy klasycznych technikach program ow ania trzeba by p rzek o p ać cały program . © Skoro ju ż w iem y, jak działa stwórca, w róćm y na p o p rzed n ią stronę do funkcji odczyt, k tó ra g o w yw ołała. N o w o n aro d zo n y blok ma n astaw y dom niem ane, co nas n iek o n ieczn ie zadow ala. N ajw ażniejsze jednak, ż e już istnieje. Skoro istnieje, to m o żn a mu od razu kazać, by sobie w łaściw e nastaw y z dysku odczytał. D latego w yw ołujem y funkcję składow ą klasy kaseta k [ a k t _ k a ] .c z y t a j _ b l o k ( p l i k _ w e j ) ;
Oto, jak w y g ląd a definicja tej funkcji w klasie kaseta: void kaseta::czytaj^blok(ifstream & plik_wej) { // tu jest polimorfizm szufladka[akt_sz] -> odtworz_sie(plik_wej); } W idzim y, ż e m ając w skaźnik d o obiektu klasy blok w y w o łu jem y funkcję w irtu aln ą z tej klasy - zatem zn o w u polim orfizm . Tutaj w ięc ru szy do pracy nie funkcja sk ład o w a abstrakcyjnego bl oku, ale funkcja z m ły n k a, m atrycy opóź nień czy innego ty p u bloku, k tó ry n a p ra w d ę tam się o d n ie d aw n a znajduje. Teraz p o w in n y z n o w u nastąpić m oje zach w y ty nad polim orfizm em . (Patrz: zachw y ty p rz y zap isie danych na dysk).
Ręczne wstawianie bloków do pustych szufladek Bloki m ogą się p o jaw iać w kasecie nie ty lk o dlatego, że tak zo sta ło p rzeczytane z dysku. R ów nie d o b rz e mogą tam zo stać um ieszczo n e p rz e z u ży tk o w n ik a „ręcznie". To jest jak b y łatw iejsza w ersja tego, o czym m ó w iliśm y p o p rzed n io . Z am iast czy tać coś z d y sku u ży tk o w n ik o kreśla szu flad k ę, a następ n ie z m enu w ybiera n a z w ę b lo k u , który chciałby tam um ieścić. To je st o statn ie miejsce kiedy w razie z a k u p u now ego b lo k u m u sim y dokonać p o p ra w k i w program ie. Po prostu p o w in n iśm y do w ach larza stary ch bloków d o p is a ć ten now y ty p oferując go d o u ż y tk u . Oto, jak (w w ersji p ry m ity w n ej - b ez o k ie n ek czy m yszki) w ygląd ać m o że taka funkcja. U ru c h a m ia m y ją z klasy m e n u w ybierając opcję „w staw ian ie". P o w iadom iona o tak im z a m iarze w ie ż a w y w o łu je funkcję składo w ą wstaw z klasy kaseta. T o ta funkcja d a ofertę. T ak to w ym yśliłem sądząc, że kaseta najlepiej p o w in n a w ied zieć, co do niej m o ż n a w staw ić. N ie upierałb y m się p rz y tym jednak i m o że funkcja ta p o w in n a być częścią klasy menu. void kaseta::wstaw{) { int nr; char c ;
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Symfonia C++, Coda
1225
if(szufladka[akt_sz]) { cout << "szufladka jest zajeta\n\a" "Wciśnij ENTER..."; cout << "Masz do wyboru takie bloki:\n" " 1 - młynek do kawy\n" " 2 - ekspres do kawy\n" " 3 - blok wysokiego napiecia\n" " 4 - przelicznik\n" " 5 - jednostka logiczna\n" " 6 - matryca opoznien\n" " 0 - ŻADEN Z POWYŻSZYCH \n" " Podaj numer wstawianego bloku: cin >> c; if (! isdigit (c) )return; //spraw dzen ie legalności cin.putback(c); cin » nr; _ stwórca ( (typy_blokow) nr); //akt stworzenia
W N ie potrzebuję d o d a w a ć , że z polim orfizm u korzystam y ta k ż e w kilku innych sytuacjach. Te w szy stk ie sytuacje łatw o ro zp o zn ać po sło w ie v i r t u a l przy od p o w ied n ich funkcjach w definicji abstrakcyjnej klasy b l o k . P o n iew aż jednak ten rozdział nie jest o polim orfiżm ie, ale o projektow aniu - sk o ń czm y na tym.
Nie od razu Kraków... O czyw iście zdajesz sobie spraw ę, że nie od razu w p ad łem na w szystkie te pom ysły. Jak w ielo k ro tn ie pow tarzałem - p raca nad takim pro jek tem odbyw a się m etodą kolejnych przybliżeń. D latego nie m artw się jeśli w Twoim pierw szy m projekcie nie pójdzie Ci tak g ład k o .
23.10 Symfonia C++, Coda T ym sposobem d o tarliśm y do końca tej książki. J e ś li-ja k rad ziłem - opuściłeś jakieś paragrafy - teraz m ożesz do nich w rócić. M yślę, że zro zu m ien ie ich przy jd zie Ci z łatw ością skoro teraz jesteś ju ż „w tajem niczony". Zaczniesz teraz pisać w C++ swoje w łasne, w iększe program y. Początkow o posługiw anie się C++ w ym agało będzie częstego zaglądania do podręcznika. To nic. N ajw ażniejsze, by językiem tym m óc w yrazić w szystko, co pomyśli głow a. M am nadzieję, że w trakcie tej opow ieści zacząłeś już doceniać język C++ i jego finezję.
1226
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Posłowie P arafrazu jąc z n a n e pow iedzenie: z językiem C++ jest jak z sym fonią — to, co dla jednych jest zgiełkliw ą kakofonią, dla innych jest harm onijnym , niebiańskim śpiew em
23.11 Posłowie Tak sk o ń czy ła się Symfonia pierw o tn y ch w ydaniach. Dziś, z perspektyw y tych kilku lat, k tó re m inęły od w y d an ia pierw szeg o - m ogę d o d a ć coś jeszcze. G d y p isałem Symfonię, zap y tałem autora słynnej książki „N uklearo F estk o rp erp h y sik ", a mojego przyjaciela G untera Schatza: „ -G u n te r, jeśli doko nasz p o d su m o w a n ia tego w ysiłku, k tó ry w kładasz w p isa n ie książki, a także czasu, k tó ry to zab iera - czy potem m a sz poczucie, że w a rta ło ,/' G u n ter zam y ślił się i odrzekł: „-T ak , książka nap raw d ę d aje satysfakcję". G u n ter nie p o trafił w ów czas tej satysfakcji nazw ać dokładniej, ale ja postanow i łem m u p o p ro stu u w ierzy ć i pisałem dalej. Dziś - z p ersp ek ty w y tych lat, które m inęły o d p ie rw szeg o w ydania Sym fonii- w iem , jak b a rd z o G u n ter miał rację. Pew nie b ard ziej niż m yślał. Satysfakcja, która stała się m oim udziałem p rzew y ższy ła p ew n ie to, o czym m y ślał prof. Schatz. G d y w ch o d zę do księgarni n aukow ej - stoi tam także Symfonia, a księgarze m ów ią o niej „bestseller" i „lo n g seller". Z astanaw iam się, co było pow odem takiego su k cesu książki? C hyba to, ż e co p raw d a, jest n a p isa n a z rzetelnością p o d ręczn ik a d la stu d en tó w , ale sty lem takim , jak ro zm o w a z szesnastoletnim chłopcem . Te d w a czynniki sp raw iły , ż e Symfonia stała się podręcznikiem na w ielu u n iw ersy tetach i politech n ik ach w Polsce, i - co najw ażniejsze p o d ręczn ik iem , k tó ry studenci po p ro s tu lubią. Poeto, czy m oże być jeszcze piękniej? D ostałem w iele w sp an iały ch listów o d czytelników . O k a z u je się, że Symfonię czytają n a w e t Polacy w N iem czech, S zw ajcarii, Francji, U SA i K anadzie, Szkocji, Izraelu, P ortugalii. W sp an iały m d o św iad czen iem były też b ezp o śred n ie s p o tk a n ia z czytelnikam i. C zasem był to zaczy tan y w Symfonii s tu d e n t w p o ciąg u , czasem sąsiad na bankiecie na zam k u w N iedzicy, c z a se m n iew id o m y n au k o w iec , którem u Symfon ię czy ta lektor. W k ażd y m z tych sp o tk a ń o d k ry w a łe m lu d z i, którzy (nie znając m nie) o d d a w n a u w ażali m n ie z a sw ojego d o b reg o p rzy jaciela. Czy m oże być jeszcze piękniej? W iększość czy teln ik ó w p o dkreślała, z jaką pasją c zy tała Symfonię. P ew na d ziew c zy n a n ap isała tak: ...Kiedy kupiłam książkę, wróciłam do domu, zaparzyłam herbatę, usiadłam i zaczęłam czytać. I na tym zakończył się mój dzień... P rzem ek S. z Łodzi n ap isał tak:
Rozdział. 23. Projektow anie program ów orientowanych obiektowo Posłowie
...Symfonię często czytałem u mojej dziewczyny i często zastawała mnie z książką. Pewnego razu, gdy na chwilę wyszła i wróciła - widząc, że tym razem nie czytam - powiedziała: Masz szczęście, bo właśnie postanowiłam, że jeśli wrócę i będziesz czytać, to dostaniesz w twarz. Profesorze S chatz, czy m oże być jeszcze piękniej? N astęp n ą k siążk ę, — tę w której p o ro zm a w iam y o szab lo n a ch i obsłudze sytuacji w y jątk o w y ch — zaty tu ło w ałem „Pasja C++".
S zanow ny P an ie Jurku! Dziękuję za tak miłą i ciepłą odpowiedź na mój list. Szkoda, że nie napisze Pan książki, na temat, który zaproponowałem. Pan zrobiłby to na pewno wspaniale, a bardzo wielu młodych Polaków, znowu wiele by Panu zawdzięczało. Szkoda, że rozstaje się Pan z pisaniem. Rozumiem jednak Pana. To, że ludzie, których na kartach książki traktuje Pan jako przyjaciół, udostępniają pirackie wersje Pana książki-nie zachęca do dalszego pisania. Właściwie to nie dziwię się Panu - ja też pewnie w takiej sytuacji nie poświęciłbym półtora roku na pisanie kolejnej książki. Ktoś kiedyś powiedział, że tak zabija się polskich autorów książek. Jak widać, w końcu tracą na tym także czytelnicy. Zagraniczne książki są bardzo drogie (nie wiedziałem, że niska cena „Symfonii" była warunkiem, który zawsze stawiał Pan wydawcom). Ale tu nie o cenę chodzi. Raczej o Pana niezioykłą zdolność do opowiadania o rzeczach trudnych, w tak prosty i porywający sposób. Pański list przyioołał m i na myśl zakończenie „Władcy Pierścieni", gdy bohaterowie ostatecznie odpływają gdzieś daleko, za morze. Do jakiejś nieznanej krainy. I oto Pan tak właśnie, m ilcząco, odph/wa do krainy badań naukowych. My zaś zostajemy tutaj. Co prawda, bogatsi o spotkanie z Panem, ale nieco zasmuceni....
WWW
1227
1228
A
A dodatek: Systemy liczenia Dlaczego kom puter nie liczy ta k ja k my?
Dodatek: Systemy liczenia życiu co d zien n y m p o sługujem y z p o w odzeniem liczeniem w system u d ziesiątk o w y m . T ym czasem k o m putery liczą w tak zw an y m system u dw ó jk o w y m , a rezu ltat zam ieniają na system d ziesiątk o w y i dopiero t< w yśw ietlają n a m na ekranie. M o żn a by zap y tać - a w łaściw ie, dlaczego?
W
A.1
Dlaczego komputer nie liczy tak jak my? W brew p o zo ro m o d p o w ied ź na to p y tan ie nic jest łatw a. Szczególnie dla osób, które nigdy nie interesowały się elektroniką. (Komputer jest urządzeniem elektronicznym ). A by o d p o w ie d ź to pytanie była ja sn a - najpierw p o ro zm a w iajm y o naszym, d ziesiątk o w y m , pozycyjnym sp o so b ie liczenia. G d y - w d a w n y c h czasach - lu d z ie chcieli zazn aczy ć na ścianie jaskini, ż | up o lo w ali 7 m a m u tó w - rysow ali p o prostu siedem k resek (obok sym bolu m am uta). S p ra w a była zro zu m iała, choć nie zaw sze w y g o d n a . R ysow ać kreski ostatecznie m o ż n a, ale jak sw ojem u w spółplem ieńcow i p o ch w alić się słownie? Przecież nie m o żn a pow iedzieć u p o lo w ałem m a m u tó w : kreska, kreska, kreska... i p o w tó rz y ć to siedem razy. L udzie szybko w p a d li na p o m y sł symboli* cznego n a z w a n ia tych m ożliw ych liczb. M am y więc d o d ziś o sobny symbol określający ró ż n e w arian ty zd o b y czy : "1", "2", "3", "4" itd . Taki sym bol n azy w am y "cyfrą". N ie będę o p isy w ał tego dalej z pu n k tu w id zen ia historii m y śli lu d zk iej, bo wcale nie jestem p e w ie n w jakiej kolejności to w szy stk o się d ziało , ale pom ysł jest n ap raw d ę d o b ry . Za pom ocą tych cyfr łatw iej się p o ro z u m ie w a ć . Problem jednak w tym , że w ym yślenie sy m b o lu dla każdej z m o żliw y ch liczb jest niepraktyczne.
A dodatek: Systemy liczenia Dlaczego kom puter nie liczy ta k ja k my?
1229
P rzecież g d y b y k a ż d a z liczb w z ak re sie od 0 d o 1000 m iała swoją w łasną i n iep o d o b n ą d o niczego innego cyfrę - tru d n o by to było sp am iętać. Byłoby to tak jak w alfabecie chińskim, gdzie jest graficzny odrębny symbol określający (prawie) każde pojęcie. Pojęć jest bardzo wiele, więc i tych symboli jest bardzo wiele. Koszmar dla uczących się takiego języka.... L udzie w ięc nie w ym yślili osobnego sym bolu (cyfry) dla k ażd e j z m ożliw ych w artości. U ży li osobnych cyfr tylko d la p ew n eg o zak resu m ały ch liczb. Potem jakby się "poddali". W n aszy m o b szarze k u ltu ro w y m to "poddanie się" nastąp iło p rz y dziesięciu cyfrach. To zn aczy w artości od z e ra do dziew ięciu m ają sw oje sy m b o le (cyfry), a w artości w y ż s z e - ju ż w łasn y ch sym boli nie mają. Jest tylko d ziesięć cyfr. Dlatego nasz system liczenia nazywa się d z ie s ią tk o w y m
System pozycyjny N asz system liczenia nazyw a się d ziesiątk o w y m , ale o prócz tego - nazyw a się także systemem pozycyjnym . Co to zn aczy ? Otóż, jeśli tw ó rcy system u zrezy gnow ali z w y m y ślan ia cyfr dla w ięk szy ch w artości - to zn ac zy , że mieli jakiś inny pom ysł na te w iększe liczby. W skrócie p o w ie m tak: na w iększą liczbę jest taki sposób, że do jej zapisu uży w am y w ięcej niż jednej cyfry. S taw iam y je obok siebie. Pozycja danej cyfry decyduje o jej p raw d ziw y m zn ac zen iu . Z atem cyfra 6 nie z a w sz e oznacza liczbę sześć. Po p ro stu jej znaczenie zależy o d pozycji, na której ją postaw iono. Z asada jest tak a, ż e w takim zapisie liczby pozycja najbardziej skrajna z praw ej m ów i o jed n o stk ach , czyli tu postawiona cyfra 6 —znaczy naprawdę wartość 6, ale po staw ien ie cyfry na lewo od niej (o jedną pozycję) n ad aje jej w agę 10 razy w iększą. M ów im y, że pozycja ta o d p o w ia d a za dziesiątki. czyli tu postawiona cyfra 6 - znaczy naprawdę wartość 60 (6*10) Każda n astęp n a pozycja w lewo m a 10 razy w iększą w agę. C zyli m am y pozycje setek, tysięcy, dziesiątk ó w tysięcy i tak dalej. To jest w łaśnie istota system u pozycyjnego i w agow ego: Z n aczen ie ma także pozycja, g d zie postaw iliśm y cyfrę. N asz system liczenia jest więc d ziesiątkow y, pozycyjny i w ag o w y . Co w takim sy stem ie jest najistotniejsze? ♦> Jest dziesięć cyfr: 0 ,1 ,2 , 3, 4 ,5 , 6, 7, 8, 9. ♦> Staw iam y je na określonych pozy cjach , z których k a ż d a m a określoną w agę. Pozycja pierw sza od praw ej ma w agę 1 (jednostki). Pozycja n+1 ma w ag ę 10 razy w iększą od pozycji n. (Dziesiątki, setki, tysiące i tak dalej). Dalej w yjaśniać nie będę, bo myślę, że sp raw a jest prosta - w końcu posługujesz się tym system em o d czasu szkoły podstaw ow ej i chyba sam p rzy zn asz, że jest niezły.
1230
A dodatek: Systemy liczenia Dlaczego kom puter nie liczy ta k ja k my?
D l a c z e g o k o m p u te ry w olą sy ste m dw ójkow y?
Jak w id ać, w sy stem ie pozycyjnym dziesiątkow ym - d an a pozycja w zapisie liczby m o że p rzy jm o w ać jedną z 10 wartości. P o w ied zm y jak fizycy: d an a pozycja może przyjm ow ać jeden z 10 stan ó w .
I
D laczego s ta n ó w jest w łaśnie dziesięć? Dlatego, że m am y tylko dziesięć róż nych cyfr. A d laczeg o jest tylko dziesięć cyfr? No nie w iem , ale um ów m y się, że d lateg o , iż m a m y dziesięć palców. P ytanie: A co b y było, gdyby na skutek jakiegoś figla ew olucji u homo sapiens w ykształciło się nie dziesięć, ale ty lk o osiem palców. C zy cała nasza m atem a tyka by łab y w ogóle m ożliw a? P ra w d o p o d o b n ie w ted y by w ym yślono tylko osiem cyfr: (0-7). Cyfry 8 i 9 nie zo stały b y n ig d y w ym yślone. Jak w y g lą d a łb y w ów czas system liczenia? Oczyw iście byłby pozycyjny, bo to d o b ry w y n alaz ek . Byłby też z konieczności ósem kow y. Jakie b y ły b y cechy takiego ósem kow ego, pozycyjnego system u liczenia? ♦> O siem cyfr: 0 , 1 ,2 ,3 ,4 ,5 , 6, 7. ♦$* S taw iam y je na pozycjach, z których k ażd a m a o k reślo n ą w agę. Pozycja p ie rw sza o d praw ej ma w ag ę 1 (jednostki). Pozycja n+1 m a w agę 8 razy w ię k s z ą o d pozycji n. Z atem pozycje n azy w ały by się tak: jed n o stk i, ósem ki, szcśćdziesięcioczw órki i tak dalej. Jak w id zim y , m o żliw e są system y liczenia inne niż d ziesiątk o w y . N ie jest to tylko z ab a w n a h ipoteza: taki ó sem k o w y pozycyjny sy stem liczenia nap raw d ę istnieje.
T eraz m o żem y w rócić d o pytania, k tó re po staw iłem całkiem n ied aw n o : D lacze g o k o m p u tery w o lą system dw ójkow y? O d p o w ied ź je st taka: bo w elektronice k o m p u tera o czy w iste są dw a stany: w łą c z o n y /w y łą c z o n y (albo stan n apięcia n isk i/w y so k i). U k ła d y logiczne k o m p u tera k o n stru o w a n e są za p o m o cą tran zy sto ró w . W yjście takiego p o je dyn czeg o u k ła d u tran zy sto ro w eg o m o ż e być w je d n y m ze sta n ó w : niskim albo w ysokim . Z ap y tasz p ew ne: To jak... N a p ra w d ę in ży n iero w ie nie p o tra filib y zb udow ać u k ła d u tran zy sto ro w eg o , który na w yjściu d aw ałb y 10 m o ż liw y c h stanów ? (N apięcie zero w e, b ard zo małe, m ałe, nieco w ięk sze, śre d n ie , w ięcej niż średnie, itd). O czyw iście, ż e in ży n iero w ie m o gliby takie u k ła d y w y m y ślić i zasto so w ać z tym , że to ko szto w ało b y drożej. P o n iew aż takich u k ła d ó w w je d n y m k o m p u terze są m iliony - w su m ie byłoby to o w iele drożej; a m y m u sie lib y śm y za to płacić.
A dodatek: Systemy liczenia Dlaczego kom puter nie liczy ta k ja k my?
1231
Z p u n k tu w id z e n ia arytm etyki: w k o m p u terze m am y d o d y sp o zy cji dw ie cyfry, zatem m o ż em y się tu posługiw ać sy stem em pozycyjnym d w ó jk o w y m . C zasem z a m ia s t m ów ić dw ójkow y m ów im y: b in a rn y 1. Jakie są cechy ta k ieg o dw ójkow ego (binarnego), p o zy cy jn eg o system u liczenia? ♦♦♦ M am y d w ie cyfry: 0,1 . ♦♦♦ S taw iam y je na pozycjach, z k tó ry ch k ażd a ma o k reślo n ą w agę. Pozyq'a p ie rw sza o d praw ej m a w ag ę 1 (jednostki). Pozycja n+1 m a w agę 2 razy w ię k s z ą o d pozycji n. Zatem pozycje n azy w ać m ogłyby się tak: jednostki, d w ó jk i, czw órki, ósem ki, szesnastki i tak dalej. O to p rzy k ład z a p isu kilku liczb w sy stem ie dw ójkow ym : dziesiętnie
czyli
dw ójkow o 0
(0*1)
1
(1*1)
1
10
(1*2) + (0*1)
2
11
(1*2) + (1*1)
3
100
(1*4) + (0*2) + (0*1)
4
101
(1*4) + (0*2) + (1*1)
5
110
(1*4) + (1*2) + (0*1)
6
111
(1*4)+ (1*2)+ (1*1)
7
1000
(1*8)+ (0*4)+ (0*2)+ (0*1)
8
1001
(1*8) + (0*4) + (0*2) + (1*1)
1010
(1*8)+ (0*4)+ (1*2)+ (0*1)
1011
(1*8) + (0*4) + (1*2) + (1*1)
0
2 ________ _ J 10
1
11
W tej tabeli na szczycie kolum n jest w y raźn ie opisane, w jak im system ie jest zanotow ana d an a liczba. N ie jest tak zaw sze. Jeśli p o ru sz a m y się w różnych system ach liczenia - czasem z w y g ląd u liczby nie p o trafim y pow iedzieć, 1)
łac. binarius - podwójny, złożony z dwóch elementów.
1232
A dodatek: Systemy liczenia Dlaczego kom puter nie liczy ta k ja k my? w jakim sy stem ie została zapisana. W takich p rzypadkach staw ia się mały dopisek: tu ż po liczbie, nieco poniżej, um ieszczam y w naw iasie informację o p o d staw ie liczenia. Dla sytem u dziesiętnego jest to liczba (io> a dla dw ójkow e go liczba (2). O to w artość 9 zapisana w dw óch system ach liczenia 1 0 0 1 (2 )
9 (1 0 )
Jak to się o d b y w a? Pam ięć k o m p u te ra podzielona jest na tak zw ane słowa. Słow o - to jakby taka w y d zielo n a część pam ięci, w której przechow ać m ożna je d n ą liczbę. Liczba ta zo stan ie p rzec h o w an a w postaci binarnej - czyli przy użyciu dw óch dostępnych cyfr: zer lu b jedynek. Liczba 9, to b in arn ie 1001 i to te w łaśn ie cyfry kom puter zap a m ię ta w d a n y m słow ie pam ięci. O d razu pojaw ia się pytanie: Czy m o żn a zapisać do w o ln ie d u ża liczbę całko w itą? Nie. To zależy od k o m p u tera i kom pilatora. K onkretnie od tego na ilu bitach d a n y k o m p ilato r zap am iętu je liczbę całkowitą.
Gdy słowo pamięci komputera może zapamiętać kombinację 16 cyfr (zer i jedy nek), wtedy największa liczba binarna możliwa do zapamiętania to: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 (2 )
co o d p o w ia d a liczbie:
65535(20)
T akie słow a (k om órki pam ięci) p rz e d sta w ia m y sobie graficznie. Oto, jak n a p ra w d ę w y g ląd ałab y liczba 9 um ieszczo n a w słow ie pam ięci: r 10
0
o
1 ..f... ■■■ jo jo 0
0
~T 10
0
0
1 0 0 ____ ____
D
0
7 1 i
W idzisz tu w ięc jakby szesnaście kratek. Jed n a kratka o zn acza miejsce w słow ie pam ięci, k tó re o d p o w ia d a za z a p a m ię tan ie jednej z tych cyfr (0 lub 1). To miejsce n azy w a się z ang ielsk a bit. Bit to p o p o lsk u kawałek (tu: k aw ałe k informacji). N asze słow o sk ład a się zatem z szesn astu bitów . P rz y p o m in a m , że co p raw d a, obecnie n asz system liczenia jest d w ó jk o w y (binarny), ale n ad al pozycyjny. Z atem pozycja p ierw sza z praw ej jest najm niej znacząca. T ak się to zresztą określa: najm niej znaczący bit. Gdy młodzi programiści mówią na ło po prostu "najmłodszy b it", to ich nauczyciele bardzo się denerwują. Ł atw o się dom yślić, ż e b it najbardziej z lew ej to tzw . najbardziej znaczący bit. O wi nauczyciele denerwują się także, gdy uczniowie mówią "najstarszy bit", - j a nie iestem taki rygorystyczny, oba określenia są obrazowe, fest jednak ryzyko, że jeśli będziesz programował w zespole międzynarodowym, to swoje żargonowe określenie "najstarszy bit" przetłumaczysz na angielski jako "the oldest bit" - a to jest ju ż śmieszne. Poprawne określenia to:
A dodatek: Systemy liczenia Dlaczego kom puter nie liczy ta k ja k my?
1233
- the most significant bit (w skrócie MSB) - najbardziej znaczący bit - the least significant bit (w skrócie LSB) - najmniej znaczący bit. A by n a z w a ć b ity - po p ro stu je n u m eru jem y (od praw ej). M o żem y w ięc p o w ie dzieć, że g d y p rzech o w y w an e jest bin arn e słow o 1001, to jedynki są w bicie p ie rw szy m i czw arty m . M o ż l i w a j e s t a r y t m e t y k a liczb b i n a r n y c h
To znaczy, ż e jeśli w dw óch słow ach pam ięci z a p isa n e są d w ie liczby całkow ite w system ie dw ó jk o w y m , to k o m p u ter (bez z am ian y ich na zapis dziesiętny) potrafi je d o sieb ie dodać na sposób dw ójkow y, a w y n ik (oczyw iście bin arn y ) m oże za p isa ć g d zieś w pam ięci. Ta a ry tm e ty k a jest dość łatw a, d o d aw an ie czy o d ejm o w an ie takich liczb na sposób d w ó jk o w y m ożem y sobie naw et zrobić "piśm iennie" n a papierze, ale nie będę tego tu ta j przed staw iał z dw óch pow odów : ♦♦♦ Po p ie rw sz e trochę przekracza to ram y tej k siążk i (czy tego d o d atk u )
-je ś li jesteś zainteresowany, to jest wiele książek omawiających te sprawy, ♦♦♦ Po d ru g ie - ta ary tm ety k a jest sp raw ą k o m p u tera i m y nie m usim y się tym przejm ow ać. To on m a coś dodać i już. N ie b ęd ziem y przecież go sp ra w d z a ć .
Ale siooją drogą, to wypadałoby wiedzieć, jak zmienia się dane słowo binarne, gdy dodamy do niego jeden. M ożesz zatem zapytać: C o n a s w ięc o b c h o d z ą te w szystkie s p ra w y d o ty c z ą c e k o d u d w ó jk o w e g o ?
M asz rację - p y tan iem tym d otknąłeś istoty zagad n ien ia. Otóż, k o m p u te r to nic tylko m aszyna obliczająca - p o d o b n a d o kalkulatora. K om puter d o ść często służy do sterow ania jakim iś u rząd z en iam i. N ie d o d a jemy w ted y jakichś tam liczb, lecz na przykład w łączam y jed en z wielu silników korekcyjnych prom u kosm icznego lub p rz e su w a m y ram ię robota przem ysłow ego. Z atem nie zaw sze p atrzy m y na słow o w pam ięci w ko m p u terze pró b u jąc odczytać tam zakodow aną tam (binarnie) liczbę. C zasem inform acja tam zapisana ma n atu rę inform acyjną (a nie m atem atyczną). Słyszałeś p ew n ie o tak zw anych u k ład ach sprzęgających (interfejsach). Sprzę gają one k o m p u ter z jakimś urządzeniem zew n ętrzn y m . N a p rzykład ze stero w nikiem ro b o ta przem ysłowego. Jak ko m puter rozm aw ia z takim urządzeniem p rzez u k ła d sprzęgający? Na przykład w y sy ła mu tak zw ane słow o rozkazu. W d an y m słow ie pam ięci m oże być p rzy g o to w an e w łaśnie słowo rozkazu i nie m a sensu p atrzen ie na nie, jak na liczbę. W yobraźm y sobie takie słow o rozkazu dla ram ienia robota przem ysłow ego, bit nr 0: określa aktyw ność (1 - ruch ram ienia, 0 - stop)
1234
A dodatek: Systemy liczenia System szesnastkowy (heksadecymalny) b it n r 1:
określa kierunek zginania (1 - zginanie, 0 - rozginanie).
b it n r 8:
określa jazdę robota po szynach (1 - jazd a, 0 - stop)
bit n r 9:
kierunek jazdy (1 - w przód, 0 - w tył)
Inne bity o d p o w iad ają za jeszcze inne fragm enty robota - robot przecież m oże mieć nie tylko ram ię, ale i p rzed ram ię, dłoń, etc. Dla u proszczenia pomijamy to. Taraz s k u p się trochę, zadaję p ytanie: Jaki rozkład bitów n ależy przygotow ać w sło w ie rozkazu układu sprzęgającego, aby ro b o t zaczął zginać ram ię i rów nocześnie zaczął się poruszać na szynach do przodu? O d p o w ied z: w y m ag an y rozkład bitów to 00000001 100 00 011
P ytanie dru g ie: Jak m a w yglądać n a stę p n e słow o rozkazu - k tó re spraw i, aby robot się zatrzy m ał, ale ram ię n ad al zginał?
I
O d p o w ied ź: p o trzeb n a jest kom enda: 0 00 000 00 000 00 011
O czyw iście m o żn a się uprzeć i pow ied zieć, że ten p ie rw sz y rozkład bitów o d p o w iad a dziesiątkow ej liczbie 387, a ten d ru g i o d p o w ia d a dziesiątkow ej liczbie 3. Jed n ak te d w ie tajem nicze liczby dziesiątkow e nie m ó w ią wiele osobie, która p ro g ram u je ru ch y tego robota. Tutaj sło w o pam ięci w olim y in terp reto w ać jako rozkład b itó w słow a dw ójko w ego i już. Po to w łaśn ie istotna jest d la nas binarna rep reze n tacja zaw artości sło w a pam ięci, a nie p o to, by ćw iczyć d o d a w a n ie liczb binarnych. N ie ma sen su przecież d o d an ie tej liczby 387 d o liczby 3, b o to tak, jakby d o d ać bieg p rz e z p ło tk i d o rzu tu o szczepem . Operacje na bitach słowa i operacje binarne — są dla nas istotne nie w celach arytm etycznych. W ręcz przeciwnie. Potrzebujemy ich w tedy, gdy rozkład bitów w słow ie nie reprezentuje ja k ie jś wartości liczbow ej, ale wtedy, gdy te bity (s a m o tn ie lub w n ie w ie lk ic h g ru p ka ch ) m ają ja k ie ś od ręb n e — ' "niearytm etyczne" - znaczenie.
A.2
System szesnastkowy (heksadecymalny) W p o p rz e d n im p arag rafie zo b aczy liśm y , że w p rz y p a d k a c h takich, g d y chodzi nie o ary tm ety k ę , ale o stero w an ie - b in a rn y za p is liczby m a w ięcej zalet niż zap is d ziesiętn y . Ma jed n ak jed n ą p o d s ta w o w ą w adę: I za p is b in a rn y jest tro ch ę nieczy teln y .
A dodatek: Systemy liczenia System szesnastkowy (heksadecymalny) W y o b raź sobie taką sytuację: razem z kolegą p racu jesz nad p ro g ram e m dla ro b o ta p rzem y sło w eg o (jak w p o p rz e d n im p arag rafie). W pew nej chw ili pro sisz go: "podaj m i słow o k o m en d y , która sp raw i, że ro b o t zacznie zginać ra m ię i ró w n o cześn ie będzie się p o ru sz a ć na szynach do p rzo d u ". N a to kolega rzuci beztrosko: "To będzie: siedem zer, jeden, jeden, zero, zero, zero, zero, zero, jeden, jeden W ted y T y w piszesz to g d zieś d o instrukcji p ro g ra m u . G w a ra n tu ję , że jeden z w as się pom yli. Jak nie p rz y tym rozkazie, to p rz y je d n y m z następnych. D obry język p ro g ram o w an ia n ie po w in ien stw arzać sytuacji, w których p ro g ra miści m o g ą zrobić b łęd y - w szy stk o jedno czy na sk u tek zm ęczenia, czy n ie p o ro zu m ien ia. Z ap e w n e d lateg o w C++ n a w e t n ie m a stałych d o sło w n y ch w p o staci dw ójkow ej. B ardzo do b ry m ro zw iązan iem jest bow iem p o słu żen ie się system em szesn astk o w y m (zw anym czasem heksadecym alnym ). Jakie są cechy system u szesnastkow ego: ♦♦♦ Jest szesn aście cyfr (0,1, 2 ,3 , 4, 5, 6, 7, 8, 9, a, b, c, d, e, f)
Jak widzisz, nie wykazano dużo fantazji w wymyślaniu specjalnych cyfr dla oznaczenia wartości od 10 do 15. Funkcję tę pozoierzono po prostu literom a-f. ♦♦♦ C yfry staw iam y na pozycjach, z których k a ż d a ma określoną w agę. Pozycja pierw sza o d p raw ej m a w agę 1 (jednostki). Pozycja n+1 m a w ag ę 16 razy w ięk szą o d pozycji n. O to tabela pokazująca kilka liczb zapisanych w system ach: dw ó jk o w y m , szesn astk o w y m i dziesiętnym .
1235
1236
A dodatek: Systemy liczenia System szesnastkowy (heksadecymalny)
1101
d
13
1110 1111
e
14
f
15
K iedy w p r o g r a m o w a n iu k o rz y sta m y z zap isu s z e s n a s t k o w e g o ?
System szesn astk o w y daje nam tę możliwość, że jeśli spojrzym y na zapisana, liczbę b in a rn ą , to łatw o, bez żad n y c h przeliczeń, m ożem y ją "w ypow iedzieć szesn astk o w o . Jest to dość łatw e, bo w ystarczy sobie p o g ru p o w ać bity po cztery. (Do zap isu w artości 0 - 15 p o trzeb n e są cztery bity). N a rysunku poniżej w id zisz 4 g ru p y ; k ażd a z nich sk ła d a się z 4 bitów.
A by w y p o w ied zie ć zaw artość te g o słow a w postaci szesn astk o w ej - z każdej z czterech p o w y ższy ch g ru p w y starczy odczytać z a n o to w a n ą tam w artość b in a rn ą (0-15) i zapisać ją w postaci cyfry szesnastkow ej (0 -0 . Pow staną w ów czas cztery cyfry szesnastkow e. U tw orzą one razem liczbę w zap isie szesn astk o w y m . B ardzo proste! P rzy k ład . W n aszy m słow ie (pow yżej) w id zim y p ew ien ro z k ła d bitów. Z a p isu jąc k ażd ą z g r u p bitów w postaci cyfry szesnastkow ej o trz y m a m y łącznie: 0 1 f b (i 6) N a końcu d o d a łe m tu sym bol Q6) d la p rzy p o m n ien ia, że to liczba zap isan a szesn astk o w o , ale w języku C++, nie stosuje się tego n a w ia su z szesnastką. Po prostu liczbę p o p rz e d z a się z n ak a m i 0x 0x01fb J a k p o s łu g u je m y s ię tym w p ra k ty c e ?
Jeśli m a m y jak iś obiekt typu i n t o n a z w ie r o z ka z, to - ab y sp raw ić, by jego bity m iały ta k i ro z k ła d bitów jak p o w y żej - w y starczy zapis: i n t ro z k a z ; ro zk az = 0x1fb ; C o z ro b i k o m p ila to r w id ząc taki zap is? ♦> Jeśli k o m p ilato r, z k tó ry m p racu jesz, o b iek to m ty p u i n t p rzy d ziela w p am ięci 16 bitów , to w o b iek cie r o z k a z z n a jd z ie się taki ro zk ła d b itów , ja k to w id zim y g raficzn ie pow yżej. Jeśli je d n a k Twój k o m p ilato r p rzy d ziela o b ie k to w i i n t 32 bity (tak b y w a te ra z najczęściej) - to o czyw iście te b a rd z ie j zn aczące bity b ęd ą
A dodatek: Systemy liczenia Ćwiczenia
1237
z a w ie ra ły zera. R o zkład bitów w takim 32 bitow ym obiekcie ro zk az b ę d z ie w yg ląd ał tak, jak n a p oniższym ry s u n k u . Rys. A-2
D w a w y z w a n ia , którym p o w in ie n e ś u m ieć s p ro s ta ć
W p ra k ty c e szesn astk o w y zap is liczb potrzeb n y b ę d z ie Ci w dw óch z a s a d n i czych sytuacjach. (D obrze, byś je poćwiczył). ♦♦♦ M am y gdzieś (na p ap ierze, w dokum entacji) n ary so w an e słow o z o d p o w ie d n io oznaczonym i bitam i. (Ich zn aczen ie m o że być różne). Z ależn ie o d d an ej sytuacji m o żesz potrzebow ać jak ieg o ś konkretnego ro zk ład u bitów . Pow inieneś u m ieć d a n y rozkład b itó w zap isać w sw oim p ro g ra m ie w postaci stałej dosłow nej szesnastkow ej. (K rótko m ów iąc p o w in ie neś um ieć zam ienić liczbę binarną na szesn astk o w ą).
Poprzedni przykład zołaśnie taka sytuację ilustrował. ♦♦♦ P ro g ram na ekranie w y św ietla zaw artość jakiegoś słow a w postaci szesnastkow ej. (Albo analizujesz cudzy p ro g ra m i napotykasz na stałą d o sło w n ą w postaci szesnastkow ej). P ow inieneś um ieć z tego odczytać, na k tó ry ch bitach są jedynki, a na których zera. Krótko m ów iąc pow inieneś u m ie ć liczbę szesn astk o w ą zam ien ić na binarną.
Czyli jeśli zobaczysz zapis O x f e f , to powinieneś wiedzieć, że (między innymi) w bitach 1 i7 są akurat jedynki.
A.3
Ćwiczenia
H RI
B H E !! M
M
m es i-iś ja
Co to znaczy, że system zapisu liczb jest dziesiątkowy i pozycyjny? Jakie względy techniczne sprawiły, że w komputerach zastosowano binarny (dwójkowy) zapis liczb? Możliwy jest na przykład czwórkowy (pozycyjny) zapis liczb. Wymień jakie występują w nim cyfry. Jakie cyfry występują w dwójkowym systemie liczenia? Poszczególne pozycje w pozycyjnym systemie liczenia mają różną "wagę". W przypadku systemu dziesiątkowego pozycje te nazywamy: jedności, dziesiątki, setki, tysiące, dziesiątki tysięcy, itd. Nazwij pierwsze szesnaście pozycji w systemie binarnym. Od pewnej pozycji będzie to już brzmiało niezręcznie, więc wtedy podaj jaką wartość ma postawiona na takiej pozycji cyfra 1. • . . D l W dziesiątkowym systemie liczenia poszczególne pozycje można określić jako 10u, 101, 102, itd. Podaj podobne określenia dla systemu szesnastkowego. Jakiej wartości dziesiątkowej odpowiadają następujące liczby zapisane szesnastkowo: Oxa, Oxb, Oxe, Oxf
1238
A dodatek: Systemy liczenia Ćwiczenia W języku C++ można tę samą stałą dosłowną napisać w postaci dziesiątkowej, ósemkowej lub szesnastkowej. Czy (i jak) wpływa to na przechowanie danej liczby w pamięci? Mamy 32 bitowe słowo pamięci. Jak inaczej można nazwać bit numer 1 i bit numer 32? Podaj polskie określenie i angielski skrót. Oto kilka liczb w zapisie binarnym. Napisz jak się one zmienią, gdy do każdej dodamy jeden. 001, 010, 111,011 Czy w C++ można zapisać stałą dosłowną binarnie? Czym się różni system szesnastkowy od heksadecymalnego? Jakie widzisz przewagi zapisu szesnastkowego nad binarnym? Poniżej widzisz kilka liczb zapisanych binarnie. Zamień ich zapis na zapis szesnastkowy 1111000000011111
=
0000000001010011 = 1111111111111111 = 1000111111100000 = Poniżej widzisz kilka liczb zapisanych szesnastkowo. Jeśli taka liczba znajdzie się w pamięci komputera, to zostanie tam zapamiętana w postaci binarnej. Powiedz, w której z nieb na bitach 7 i 12 są jedynki 0x1001, 0x0ef3 0xefda 0x1111 0xeee 0xffff
W
Skorowidz
1239
Skorowidz
& operator p o b ran ia ad resu 257 && operator iloczynu logicznego 103 # dy rek ty w a p u sta 213 ~ a przeładowanie 796 ##sklejacz 203 ~ a przeładowanie 796 #define 197-199 ~ a const 82 ■#elif 208 #else 207 -flendif 206 te r r o r 210 #if 206 ■#ifdef 209 #ifndef 209 #include 211-212 ~ a nazwa pliku stwórz, preprocesorem 212
, (przecinek) o p e ra to r 123
/*
kom entarze V 14
//k o m e n ta r z e 14 :: operator z ak re su 748 ~ a zasłanianie 79 '\ a ' 63 '\ b ' 63 ' \ f 63 '\ n ' 13,63
’\ f 63 ' \ ł 63
'W
63
__cplusplus 215 __DATĘ__214
#line 210
__LINĘ__ 214
#pragm a 213
__NAM E__ 214
#undef 200
__STDC__ 215
% (operator m odulo) 96
__TIME__214
1240
Skorowidz
♦ funkqi składowej 450 ♦ kotejne definiowane "na raty" 151
I I o p erato r su m y logicznej 103
♦ a zakres ważności 152 formalny a aktualny 141 formalny funkcji 141 funkq'i będący tablicą 224-227 funkq'i, jego nazwa 135 identyczny czy różny dla przeładowania 386-393 ~ nienazwany 156 ~ przesyłanie przez wartość 140-141 ~ przesyłany przez referencję 142-144 ~ ~ ~ ~ ~
abstrakcyjna klasa 992-998,1196 adjustfield m aska, pole 1045 a d re s ~ 0 zero (dawniej zwany NULL) 281 ~ funkcji 347 ❖ a jej nazwa 339 ♦ przeładowanej 394-397 ~ tablicy 225 ~ zamiana go na liczbę całkowitą 263 ~ zerowy (dawniej zwany NULL) 281 ag re g a t 223, 633, 724 a k tu aln y arg u m e n t funkq'i 141
~ wskaźnikiem do const 288 ~ wywołania funkcji 141 ~ wywołania programu 363-365 argument counter (ang.) 1169 argum entow ość o p erato ra 798 arg v 364,1169
arytmetyczny ~ operator 95-100
alarm (znak speq'alny) 63
~ typ 54 ASCII kod 62 ,2 3 0
algorytm transform 568
assign - f. w kl. std::string 573-575
altern aty w a 103
at - f. w kl. std::string 527-531
am p ersa n d 225
ate, tryb otwarcia 1104
analiza zachowań obiektów 1192
atof (f. Ascii To float) 366
anonim ow a ~ przestrzeń nazw 174
auto 16,164, 628, 920
~ unia 633 ANSI C 5, 267
awans 402
automatyczny obiekt 163
ap o stro f 63 ap p , try b otw arcia 1104 a p p e n d - f. w kl. std ::strin g 576 argc 364,1169 a rg u m e n t ~ aktualny funkcji 141 ~ będący obiektem 452-456 "bit po bicie" 916
~ będący tablicą 286 ~ będący wskaźnikiem do funkqi 348
o babci p rzy p o w ieść 142
~ domniemany 147-155
backslash 63
Skorowidz
1241
b ack sp ace (znak specjalny) 63 b a d - f. sp raw d zając a stan stru m ien ia 1113 bad_alloc - klasa w y ją tk u 310,312,862 b ad_cast - klasa w y jątk u 1014 b ad b it (flaga sta n u stru m ien ia) 1111-1112
C klasyczny 5
basefield, m aska, p o le 1046,1054
c_str -f. w k l. std ::strin g 551-554
begin - f. u staw iająca iterato rstd ::strin g u 588
capacity - f. w kl. std::string 521
bekslesz 63
carriage re tu rn (zn ak specjalny) 63
b ezpośrednia kl. p o d sta w o w a 905
case 35
białe znaki 9
catch 311-312
biblioteczna funkcja 186-188
cctype (nagłów ek) 568
biblioteka ~ a przestrzeń nazw 72
cecha i m an ty sa 1049,1062
~ iostream 1021 ~ standardowych strumieni w e/w y 1021 ~ stdio 1021,1145-1149 biblioteka sta n d a rd o w a 505 binarne w czy ty w an ie 1093 binarny system liczenia 1231 Dinary, tryb otw arcia 1104 bit 105,1232 ~ najbardziej znaczący 1232 ~ najmniej znaczący 1232 Ditowy operator 105-108 błędy pracy stru m ien ia 1110-1122 blok ~ funkq'i 71 ~ instrukcji 25 ~ lokalny 71 bool 52 bool, typ 23 boolalpha, flaga 1045 boolalpha, m an ip u lato r 1066 break 35,40 "bryk" czyli tabela z zestaw ieniem często używ anych funkcji kl. std::string 599-607 buforow anie stru m ien ia 1025,1067
cechow anie a p a ra tu ry 699 cerr 1025 char, typ 51 chw ilow y obiekt 455 ciąg znaków 65 ciało ~ funkq'i 7,133 - klasy 413 cin 18,1025 elear - f. w kl. std ::strin g 527 elear - f. w kl. stru m ien ia 1116 clog 1025 close, fu n k q a w kl. strum ienia 1108 com pare - f. w kl. std::string 557 const 80-83 ~ a #define 82 ~ a przeładowanie 485 ~ funkq'a składowa 480-484 ~ obiekt 266 ~ przy wysyłaniu argumentu do funkcji 288 ~ wskaźnik 315 ~ wskaźnik do takiego obiektu 316-317 const_cast 116,119,321-325 ~ a przeładowanie 797 ~ a volatile 325 continue 43
1242
Skorowidz
c o p y - f. w kl. std ::strin g 572
default 36
copyfm t, funkcja 1064
defined 206
c o u t 8,1025
definiqa 17,49,169,172 ~ „w biegu" 56
c o v arian t 979 C -strin g 65,503, 508 ~ argumentem funkcji 328
~ a deklaracja 17,49 ~ funkcji 133
~ bardzo długi 66 ~ długość a rozmiar 233
~ funkcji zaprzyjaźnionej w klasie 621 ~ klasy 413
~ jego typ 67
~ klasy zagnieżdżonej 649-653
~ konkatenaq'a 335 ~ w cudzysłowie - jest przechowywany jako static 336
~ klasy, lokalna 654-656 ~ obiektów klasy string 511-515 ~ obiektu klasy 422
~ wariaq'e na temat 328-336 ~ zapis w kilku linijkach 66
~ przeładowanego operatora 796
~ po łacinie 50
~ ze znaków wchar_t 68 C -stringi b ezp o śred n io przylegające 66
~ składnika statycznego 467 ~ stałej za pomocą #define 199
cyfra 1229
~ w wyrażeniu inicjalizującym pętli for 57
cykl życiow y ob iek tu 1199
~ w wyrażeniu warunkowym instr. if 57 ~ w wyrażeniu warunkowym instr. while 58
czas życia 295 ~ a zakres ważności 70-77 ~ obiektów globalnych 676 ~ obiektów lokalnych 675 ~ obiektów new 677 ~ obiektu 162-167 ~ obiektu - definiqa 70 czy sto w irtualn a funkcja 995,997
~ wskaźnika 255-256 deklaracja 17 ,16 9 ,17 2 ~ a definiq'a 17,49 ~ dostępu 899 ~ dostępu using 899 ~ funkcji 133 ~ funkcji - nieobowiązkowa 145-146 ~ po łacinie 49 ~ przyjaźni 616 ~ using 77,900 ~ zapowiadająca 619,926 d ek rem en taq i o p e ra to r 98 delete 294 ~ a adres zerowy (NULL) 682
d a n a składow a 414 ~ publiczna - odwołanie się 434 d a ta - f. w kl. std ::strin g 555 d e b u g g e r 9, 8 3 ,159,1068, 1125 dec, flaga 1043-1044,1046 dec, m a n ip u lato r 1066, 1075
~ globalny, przeładowanie 869-870 destrukcja o b ie k tu zło żo n eg o 695
destruktor 462-464,660-719 ~ a const i volatile 682 ~ a dziedziczenie 904 ~ a przeładowanie 681 ~ a unia 682
Skorowidz
1243
~ jawne wywołanie 682 ~ kolejność pracy 693 ~ kolejność wywołania 909-914 ~ wirtualny 999-1003
duża tablica, p rz y k ła d 1136-1142 dw ójkow y ~ kod 1233
~ wywołanie z this 683 ~ zastosowania 465,681 D eutsche O p er (ilustracja pry w atn eg o ) 939
~ specyfikator 891 double, ty p 52
d zied zic zen ia
długość a ro zm iar C -stringu 233
~ system liczenia 1230 d w u a rg u m e n to w y op erato r 96 d w u zn aczn o ść a d o stęp 788
do... w hile... 30
dynam ic_cast 116,121 ~ a przeładowanie 797
d o d aw an ie treści ob iek tó w klasy std ::strin g 509
~ a typy polimorficzne 1011-1014 dynam iczna alokaq'a tablicy 293-314
dom eny zasto so w an ia w skaźników 267
dyrektyw a ~ preprocesora 195
dom inacja 960 dom niem any ~ argument 147-155 ~ argument "na raty" 151 ~ argument a przeładowanie 379 ~ konstruktor 683 dopasow anie ~ a konwersja 784-788 ~ dokładne 401 ~ etapy 400-407 ~ funkcji przeładowanych 398-399 ~ z awansem 402 ~ z promocja 402 D opplera zjaw isko 749 dorzecze, schem at konw ersji 790 dosłow ne stałe 58-67 dostęp ~ a dwuznaczność 788 ~ do skł. odziedziczonych 894-902 ~ do składników klasy 418-420 ~ private 419 ~ protected 420 ~ public 420 ~ wybiórczo 899 dostępu ~ deklaracja 899 ~ deklaraq’a using 899
~ pusta # 2 1 3 ~ using 76 dziedziczenie 890-967 ~ a operatory w e/w y 1038 ~ a wieloznaczność 930 ~ a zawieranie 934-935 ~ jednokrotne 925 ~ kilkupokoleniowe 905 ~ od kilku rodziców 925-933 ~ operatorów 843 ~ prywatne 940 ♦ kiedy 899 ♦ np. Deutsche Oper 939 ~ prywatne, kiedy 906 ~ wielokrotne 925-933 ~ wielopokoleniowe 906 ~ wirtualne 951-960 dzielenie z resztą 96 dziesiątkow y sy stem liczenia 1228
1244
Skorowidz
exceptions, fu n k q a 1126-1127 EXCLUSIVE OR operator 108 explicit 675, 711 ~ a konstr. konwertujący 763 extensibility (cecha języka C++) 978,1186 ed y tu r tekstu p ro g ra m u 9 ekran, u rząd zen ie w yjściow e 1022
extern 49,169,173 extract w e /w y j op erato r 1026
eksplozja, sch em at konw ersji 790 else 25-28 em pty - f. skl. kl. std ::strin g 520 encapsulation 416 e n d - f. iteratora kl. std::string 592 endl, m a n ip u lato r 19,1068 ends, m a n ip u la to r 1068 enkapsulacja 416-417,899,907,1198
fail - f. spr. stan stru m ien ia 1112
en u m 51,87-90 ~ a przeładowanie 386,877
failbit (flaga sta n u stru m ien ia) 1111 failure, klasa ios_base::failure 1126
~ bez nazwy 89 ~ jako indeks tablicy 451
false 53
~ jego rozmiar 90 ~ tablica takich elementów 228-229
fałsz-praw da 22-24
fałsz 24,53 fili - f. w klasie stru m ien ia 1061,1073
~ w klasie 450 ~ w operatorze w ej/w yj 1036 EO F 1087,1095
find_first_not_of 540
eof - f. sp r. stan stru m ien ia 1112
find_first_of 540
eofbit (flaga stan u stru m ien ia) 1111
find_last_not_of 540
erase - f. w kl. std ::strin g 542-543
find_last_of 540
etykieta 41 ~ (prawdziwa, czyli nie-'casc', nie-'default') 36 ~ case 36 ~ default 36 ~ gdzie jest znana 72
fixed, flaga 1044,1048
~ private 418-420 ~ protected 418-420,896 ~ public 418-420 — zakres ważności 71,162 exception (klasa std::exception) 1126 exception hand lin g 4
find - f. skł w kl. std::string 534-537
fixed, m an ip u lato r 1071 flaga ~ skąd określenie 1042 ~ stanu błędu strum ienia 1111 ~ stanu form atow ania 1041-1049 flags(fm tflags), f. w e /w y 1051,1056 float, typ 52 flu sh , m a n ip u la to r 1067 fm tflags, ty p 1052 fo r - instrukcja pętli 31-33
Skorowidz
form feed (zn ak specjalny) 63 form alny a rg u m e n t funkcji 141 form at operacji w e /w y 1041-1049 fo rm ato w an ie inform acji 1023 fo rm ato w a n ie w ew n ętrzn e 1150-1173 free storę, (h eap ) 296, 722 free, f. b iblioteczn a 870 fu n d am e n taln y ty p 50,68 funkcja 132-194,1183 ~ adres f. przeładowanej 394-397 ~ argument aktualny 141 ~ argument będący wskaźnikiem 282-291 ~ argument formalny 141 ~ argument wskaźnikiem do const 288 ~ biblioteczna 186-188 ~ ciało 7 ~ czysto wirtualna 995 ~ deklaracja 133 ~ domniemane przesyłanie obiektów 455 ~ dwa sposoby wysyłania jej tablicy 286 ~ inline 157-161 ~ jej blok 71 ~ jej definicja 133 ~ jej nazwa 133,339 ~ konwertująca 772-778 ~ main 7 ~ nawiasy w wywołaniu 339 ~ operatorowa jako przyjadel 805 ~ operatorowa jako składowa 801-804 ~ outline 159 ~ przekazywanie jej tablicy 224-227 ~ przeładowana a wirtualna 990-991 ~ przeładowana dopasowywanie 398-399 ~ przeładowanie nazwy 375 ~ przesyłanie argumentu przez wartość 140-141 - przesyłanie obiektów przez referencję 455 ~ rekurencyjna 174-185
1245
~ rozmieszczenie tychże w kilku plikach 168-173 ~ składowa 414-415, 423-430 ♦ a arg. domniemany 450 ♦ const 480-484 ♦ definiowanie jej 425 ♦ inline 427 ♦ static 803 ♦ statyczna 476 479 ♦ statyczna - a this 478
♦ wskaźn. do niej 747-755 ♦ wywołanie dla obiektu 424 ♦ wywołanie dla referenq'i 425 ♦ wywołanie dla wskaźnika 425 ♦ wywołanie jej z listy inicjalizacyjnej 687 ~ wirtualna 968-1020,1214 ♦ dostęp do niej 983 ♦ inline 989 ♦ a przyjaźń 984 ♦ a static 984 ♦ w klasie pochodnej 983 - wskaźnik do f. 337-362 ~ wysłanie do f. C-stringu 328 ~ wysłanie do f. elementu tablicy 227 ~ wysyłanie do f. obiektu 452-456 ~ z wielokropkiem, dopasowanie 407 ~ zaprzyjaźniona 615-628 ♦ a wirtualność 984 ♦ zdefiniowana w klasie 621 ~ zatwierdzająca 349 ~ zwracanie rezultatu 136-139 funkq'a w e /w y - bad() 1113 ~ clear(io_state) 1116 ~ eofO 1112 ~ failO 1112 ~ fiU(char) 1061,1073 ~ flags(fmtflags) 1056 ~ gcountO 1096 ~ get(char&) 1086
1246
Skorowidz
~ get(char*, int, char = '\n ') 1087 ~ get(void) 1087 ~ getline(char *, int, char '\n ') 1090 ~ goodO 1112 ~ ignore 1095 ~ open(char*, int, int) 1104
~ zmienna 1183 globalny ~ obiekt - jego wstępna inicjalizacja 168 ~ obiekt a klasa 439 ~ operator new 861 good - f. spr. sta n strum ienia 1112
~ peek() 1097
goodbit (flaga stan u strum ienia) 1111
~ precision(int) 1062 ~ put(char) 1099
goto 41-42, 72,1183
~ rdstate 1115 ~ read(char*, int) 1093 ~ setf 1075 ~ setf(fmtflags) 1052 ~ tellgO 1132
gotow iec - jak rad zić sobie z b łęd am i 1119 graf - dziedziczenia 905 ~ współpracy klas 1197,1211 g w iazdolot 260
~ tellpO 1132 ~ unsetf 1075 ~ unsetf(fmtflags) 1052 ~ wid th (int) 1058,1072 ~ write(const char*, int) 1100 heap 296 heksadecym alny ~ system liczenia 1234-1236 ~ zapis 59 hex, flaga 1043-1044,1046 hex, m a n ip u lato r 1066,1075 g co u n t, fu n k q a 1096
hierarchia 907,9 4 1 ,1 1 9 3 ,1 1 9 5 ,1 2 1 0
g e tfro m 1026
hyb ry d o w o ść 3,1 1 8 5
g et(ch ar &) 1086 get(char*, int, ch ar = '\ n ') 1087 get(void) 1087 g etlin e - p u ła p k a 581 g etlin e a std ::strin g 577-585 g etlin e(ch ar *, in t, c h a r '\ n ') 1090 g lo b aln e ~ nazwa 72 ~ obiekt 162 ~ wskaźnik 318
id e n ty fik aq a ~ obiektów (klas obiektów) 1193 ~ zachowań systemu 1192-1193 if - instr. steru jąca 25-28
Skorowidz
1247
ignore, f. s tru m ie n ia 584,1095
ios::beg 1132
iloczyn lo g iczn y 103
ios::binary 1104,1107
in - try b o tw arcia pliku 1104
ios::cur 1132
inicjalizacja 830 ~ a klasy podst. wirtualne 956 ~ definicja 81
ios::dec 1043-1044
~ konwersja w niej 782 ~ obiektu 457 ~ przy dziedziczeniu 915-924 ~ std::stringu 514 ~ struktury 728 ~ tablicy 223, 732 ~ tablicy obiektów 724-732
ios::end 1132 ios::eofbit (flaga stan u stru m ien ia) 1111 ios::faiIbit (flaga stan u stru m ien ia) 1111 ios::fixed 1043-1044 ios::goodbit (flaga stan u stru m ie n ia ) 1111 ios::hex 1043-1044 ios::in 1104,1106 ios::internal 1043-1044 ios::left 1043-1044
~ unii 632 - zbiorcza 223, 633, 725 inicjalizator kop iu jący 697-716
ios::noreplace (przestarzałe) 1109
in k rem en tag i o p erato r 98
ios::oct 1043-1044
inline 157-161 ~ a makrodefinicja, porównanie 201
ios::out 1104,1106
~ a wirtualność 989 ~ funkcja składowa 427 ~ gdzie definiować 159 ~ takiż przyjaciel 1036 insert - f. w ki. std::string 544-545 in stru k g a ~ blok tychże 25 ~ kroku pętli 32 ~ składana 25 ~ sterująca 22-47 int, typ 51 integer (ang.) 51 interfejs 1233 internal, flaga 1043-1045 internal, m a n ip u lato r 1071 ios:: zam iast ios_base:: 1053 ios::app 1104,1107 ios::ate 1104,1106 ios::badbit (flaga stanu strum ienia) 1111 ios::basefield, m ask a, pole 1046,1054
ios::nocreate (przestarzałe) 1109
ios::right 1043-1044 ios::scientific 1043-1044 ios::seek_dir 1132 ios::show base 1043-1044 ios::show point 1043-1044 ios::show pos 1043-1044 ios::skipw s 1043-1044 ios::stdio 1043-1044 ios::trunc 1104,1107 ios::unitbuf 1043-1044 ios::uppercase 1043-1044 ios_base::failure 1126 iostream biblioteka 1022 isdigit, f. biblioteczna 1098 istringstream 1159-1169 iteracja 587 iterator ~ do obiektu stałego 590 ~ stringu 586-598
1248
jaw na konw ersja ~ jej zapis 782-783 ~ typy 116 jaw n e w y w o łan ie k o n stru k to ra 678 je d n o arg u m en to w y operator 98
Skorowidz
~ podstawowa 891 ♦ bezpośrednia 905-906 ♦ pośrednia 905-906 ♦ wirtualna 951-960 ~ prywatna 695 ~ rozmieszczenie w plikach 440-451 ~ składniki 414-415 ~ std::string 503-543,545-614 ~ wskaźnik do jej obiektów 721
język obiektow o o rien to w an y 1182
~ zaprzyjaźniona 625 klasyczny C 5
ju sto w an ie 1045
k law iatu ra 1022 kod ASCII 62 kod d w ó jk o w y 1233 kod źró d ło w y program u 10 kod źró d ło w y p rzy k ład ó w z tej książki 6 kolejka 908 kolejność n a liście inicjalizacyjnej 687 k o m en tarze 14
k a lib ra q a 699
kom ora d ru to w a 740
k arty m odelujące 1194 k a sk ad o w e p rzy p isy w an ie 836
kom órka pam ięci ad reso w an a w sk aźn ik iem 292
k aso w an ie tablicy w zapasie pam ięci 723
kom pilacja w aru n k o w a 205-209
k d ev elo p 12,449
k o m p ilato r 10 ~ optymalizacja pracy 680 k o m p u ter steru je pom iaram i 800
k ilk u p o k o len io w e dzied ziczen ie 905 k lam ry 44 klasa 412-502 ~ a obiekt - różnica 421-423
konflikt n a z w 73 ko n iu n k q 'a 103
~ a typ 413
kon k aten acja strin g ó w 335
~ abstrakcyjna 992-998,1196
k o n s tru k q a obiektu sk ład o w y m i 688-694
~ ciało 413 ~ definicja 413 ~ dominuje 960 ~ enum w niej zdefiniowane 450
z
k o n stru k to r 457-461, 660-719 ~ 2 etapy pracy 686 ~ a const i volatile 661 ~ a dziedziczenie 903
~ lokalna 654-656 ~ najbardziej pochodna 957
~ a static 661
~ o zagnieżdżonej definiq'i 649-653
~ a unia 661
~ ogólna 908
~ a virtual 661
~ pochodna 891
~ do konwersji 763-771
obiektam i
Skorowidz
~ domniemany 683,862, 958 ♦ dla elementów tablicy 731 ♦ prywatny 697 ~ explicit 675 ~ i new 677 ~ jawne wywołanie 678 ~ jego adres 661 ~ klasy pochodnej 910,927 ~ klasy std::string 513 ~ kolejność wywołania 909-914 ~ konwertujący 763-771 - konwertujący a explicit 763 ~ kopiujący 697-716,830 ♦ dla obiektów const 709 ♦ generowany automatycznie 710 ♦ klasy pochodnej 918 ♦ użyty niejawnie 699 ~ n a c u d z e j liście inicjalizacyjnej 7 2 8 ~ nie-publiczny 695-696 - przeładowanie nazwy 460 ~ przeładowany 661 ~ pułapka przy domniemanym 674 ~ wirtualności jego symulacja 1004-1010 k o n stru o w an ie obiektu złożonego 694
1249
~ rezultatu funkcji 941 ~ rzutowaniem 773, 783 —standardowa 117,405 ~ standardowa przy dziedziczeniu 936-950 ~ standardowa wskaźnika do skł. klasy 949 ~ trywialna 401 ~ typów całkow. i zmiennoprzecink. 405 ~ typów zmiennoprzecinkowych 405 ~ typu całkowitego 405 ~ wskaźnika do klasy pochodnej 936-950 — wskaźników 406 ~ wywołanie funkcji 773,783 ~ zachodzi niejawnie gdy 781 k o nw ertujący k o n stru k to r 763-771 kopiujący k o n stru k to r 697-716 k o w arian t 979 król M id as 841 k w alifikator zakresu 893 k w alifikow ana nazw a sk ład n ik a 900
kontrakt 1198
konwersja 101,760-792 ~ a dopasowanie 784-788 ~ argumentów operatora 941 ~ argumentu funkcji 941 ~ arytmetyczna 405 - d w a warianty 7 7 9 - 7 8 0 ~ jawnie 7 8 2 - 7 8 3 ~ kaskadowo 785 ~ konstruktorem 763-771 ~ ~ ~ ~
niejawna 877 niejawnie 762, 781 operator tejże 772-778 przy inicjalizacji 941
~ referencji 406 ~ referencji do klasy pochodnej 936-950
łączność 126,798 least significant bit 1233 left, flaga 1043-1045 left, m a n ip u lato r 1071 leksykalny zakres 621,653 lengthO 519 liczba przeciw n a 98 liczby zesp o lo n e 760, 793-794 liczenia system y 1228-1238 lik w id aq a obiektu 462, 681 liniow e p rogram ow anie 1183 linker 11,169, 294, 382,426, 628
1250
Skorowidz
lin k o w an ie z innym i językam i 382 lista ~ inicjalizacyjna 684-687,909-914 ♦ a lista iniq'a!izatorów - to co innego 725 ♦ kolejność wykonywania 687 ♦ wyw. z niej funkqi składowej 687 ~ iniqalizatorów ♦ a lista iniq'alizacyjna - to co innego 725 ♦ a na niej wywołania konstruktorów 728 ~ pochodzenia 891,899,926 ♦ a rodzaj dziedziczenia 897 ~ wyliczeniowa 88 logiczny o p e ra to r 101-104 lo k aln e ~ klasa ♦ jej definiq'a 654-656 ♦ jej zakres leksykalny 655 ~ nazwa typu (typedef) 657 ~ obiekt ♦ a wstępna iniqalizacja 168 ♦ automatyczny 168,675 ♦ statyczny 168,676 ~ zakres ważności 71 ~ zmienna 1183 lo n g d o u b le, ty p 52
~ brak wywołania 139 ~ rezultat 139 — typ rezultatu 139 m akrodefinicja 200-202 ~ i nawiasy 202 — rozwinięcie 201 m alloc 861,870 m a n ip u lato r 1051,1065-1082 ~ bezargumentowy 1066 ~ dec 1066 ~ definiowanie przez użytkownika 1076 ~ endl 1068 ~ ends 1068 ~ flush 1067 ~ hex 1066 ~ oct 1066 ~ parametryzowany 1072 ~ precision(int) 1074 ~ predefiniowany 1065 ~ resetiosflags(ftmflags) 1075 ~ setbase(int) 1075 ~ setfill 1073
long, ty p 51
~ setiosflags(fmtflags) 1075 ~ setw 513,1072 m a n ty sa i cecha 61,1049
LSB 1233
m ax_size() - f. w kl. std ::strin g 521
l-value 260 — Zob. też: 1-wartość 1-w artość 260, 528, 846
m e m b er function 431
lo n g int, typ 51
m em set 646 m e to d a "odtąd-dotąd" 593 m e to d a (inna nazw a f. sk ład o w ej) 431 M idas 841 m n o ż en ie (szybkie) 111 m odel ~ składanie 1199 — widoczne własności 1200
m ag iczn e oko 486
~ zachowania 1200 m o d e lo w an ie 2 ,1 187,1190
m ain 7 ~ brak deklaracji 139,147
m o d y fik ato r ~ Zob. też: przydomek, specyfikator
~ brak return 139
Skorowidz
~ const 80-83 ~ explicit 675 ~ m utable 485-498 ~ register 84 ~ static 166 ~ volatile 84 m ost sig n ifican t bit 1233 MSB 1233 m u tab le 485-498
1251
~ zasłanianie 78-79, 435-438 n eg aq i o p e r a to r ! 104 new ~ ~ ~
294 bad_alIoc 312 globalny operator 861 globalny, przeładowanie 869-870
~ i konstruktor 677 ~ nadanie obiektowi wartości w momencie stworzenia 297 ~ nothrow 310 ~ a tablica wielowymiarowa 299 ~ umiejscawiający 301 ~ wstępna zawartość tak stworzonego obiektu 295 ~ wyjątku rzucenie 310 n ew linę (zn ak speq'alny) 13, 63 n ieb u fo ro w an y strum ień 1025
nagłów ka stra ż n ik 212, 442
n ie fo rm ato w an eo p e raq e w e / w y 1083-1085
nagłów kow y plik 169
niejaw na konw ersja 877
najbardziej poch o d n a klasa 957
n ie n azw an y arg u m en t 156
nam espace 72,174 ~ Zob. też: przestrzeń nazw
n ieru ch o m y w skaźnik 315,318
~ a przeładowanie 385 ~ zakres 72 napis 65 naukow a notaq'a 1047 naw ias p u sty w deklaracji fu n k q i 134 nazw a ~ argumentu funkqi 135 ~ funkcji 133,339,347 ~ konflikt 73 ~ nie jest obiektem 276 ~ obiektu 295 ~ statyczna globalna 173 ~ tablicy 225, 723 ~ tablicy a wskaźnik 275 ~ typu, lokalna 657 ~ wC++ 16,48 ~ w funkcji, zakres ważności 162 ~ w klasie, zakres ważności 415
niesk o ń czo n a pętla 32 n o b o o lalp h a, m anipulator 1066 nocreate 1109 noreplace 1109 n o sh o w b ase, m anipulator 1070 n o sh o w p o in t, m anipulator 1069 n o sh o w p o s, m an ip u lato r 1070 n o sk ip w s, m an ip u lato r 1069 n o taq a ~ dziesiętna, flaga 1048 ~ naukowa (wykładnicza) 1029,1047 ~ wykładnicza, „naukowa", flaga 1048 ~ wykładnicza, „naukowa", wczytywanie 1029 n o th ro w 310 n o u n itb u t, m an ip u lato r 1070 n o u p p ercase, m anipulator 1070 n ow a linia 13
1252
N U L L - d aw n a m akrodefinicja 231 N U L L ad res 281 ~ a wskaźnik 318 n u li, czyli zn ak o k o d zie 0 64-65,231 n u m e ra cja elem entów tablicy 221
Skorowidz
~ volatile 84 ~ w środku innego obiektu 688-694 obiektow e p ro g ram o w an ie 1184 obiektow o orientow ana technika 2 obiektow o o rientow ane program ow anie 1185-1188 obiekty tw orzone dzięki n ew 294 oct, flaga 1043-1044,1046 oct, m an ip u lato r 1066,1075 odejm ow anie dw óch w skaźników 277 odniesienie się do o b iek tu 257 "odtąd-dotąd" (m etoda) 593
obiekt ~ a klasa - różnica 421-423 ~ automatyczny 163 ~ automatyczny a destruktor 681 ~ chwilowy 455, 764
off_type, typ 1132 oktalny zapis 59 on flight 56 open(char*, int, int) 1104
~ const 80,266
o p enm ode, ty p 1105
~ definiowanie 422 - globalny 162, 676 ~ globalny, definiowanie 676
operacja rzu to w an ia 262
~ iniqalizacja 457 ~ klasyfikacja wg. dziedz. lub zawierania 1193 ~ konstruowany new 677 — likwidacja 681 ~ lokalny automatyczny 675 ~ lokalny statyczny 676 ~ lokalny, definiowanie 675
operaq'e w ejścia/w y jścia 8, 1021-1035, 1037-1181 ~ błędy 1110-1122 ~ na plikach 1102-1109 ~ nieformatowane 1083-1085 o p era n d 106 o p e ra to r 95-131 ~ ! negacji 104 ~ % modulo 96 ~ % reszta z dzielenia 96 ~ & (adres) 257
~ new a destruktor 682 ~ przesyłany przez wartość 943
~ & I ~ * 108
~ składnikiem klasy 688-694 ~ składowy bez konstruktora 693
- && oraz I I 103 ~ (), przeładowanie 848-849
~ stały 80 ~ stały a konstruktor 684 ~ static 165 ~ statyczny a destruktor 682 ~ statyczny globalny 173 ~ statyczny lokalny 165
~ * (odniesienie się do obiektu) 257
~ ~ ~ ~
, (przecinek) 123, 240 .(kropka) 415 « 106 -> 415 » 107 argumentowość tegoż 798,807
Skorowidz
~ arytmetyczny 95-100 ~ bitowego iloczynu 108 ~ bitowej negacji 108 ~ bitowej sumy 108 ~ bitowy 105-108 ❖ a logiczny - porównanie 109 ~ dekrementacji 98 ~ delete 294 ♦ a dwukrotnie kasowanie 306 ♦ przeładowanie 863-864 ~ dwuargumentowy 96 ❖ przeładowanie 810-813 ~ exclusive or (XOR) 108 ~ indeksowania tablicy, przeładowanie 843-847 ~ indeksowania tablicy (w kl. std::string) 527-531 ~ inkrementacji 98 ~ jako f. składowa, czy globalna 876-877
1253
~ przypisania 100,830, 842 ♦ +=,-=,/=,*=,% =,»=,& =, l=,*= lio ♦ dziedziczenie tegoż 904 ♦ klasy pochodnej 918 ♦ private 843 ~ relaqi 101 ~ różnicy symetrycznej 108 ~ rzutowania 114-122 ~ sizeof 113 ~ tablicy indeksowania 1137 ~ tworzący typ pochodny 69 ~ wkładania do strumienia 1026,1032-1035, 1037-1040 ~ wyjmowania ze strumienia 1026,1032-1035, 1037-1040 ~ zakresu 79, 748 optym alizacja pracy k o m p ilato ra 680 ó sem k o w y zapis 59
~ jako funkcja globalna 809
OSIRIS - u rząd zen ie p o m ia ro w e 1201
~ jako funkcja składowa 809 ~ jednoargumentowy 98
o strin g stream 1150-1158
~ jednoargumentowy + , - 98
out_of_range (std::) - w y jątek 531
~ jego łączność 798 ~ jego priotytet 123-125 ~ konwersja w jego obecności 781
"outline" funkcja 159
out, try b otw arcia pliku 1104
o v erlo ad , słow o kluczow e 375
~ konwersji 772-778 ~ łączność operatorów 126 ~ logiczny 101-104 ❖ a bitowy - porównanie 109 ~ new 294 ♦ przeładowanie go 859-862 ~ postdekrementaqi 100 ~ postinkrementaq'i 100 ~ predefiniowany 806 ~ preinkrementacji 100 ~ priorytet 798 ~ przeładowany, definiq'a 796 ~ ~ ~ ~
przeładowany, lista 796 przemienność przy przeładowaniu 812 przesunięcia bitów w lewo 106 przesunięcia bitów w prawo 107
p am ięć k o m p u tera 1232 p ara m e tr ak tualny funkcji 141 p a ra m e tr form alny funkcji 141 peek, funkq'a 1097 pętla nieskończona 32 pętla p ro g ram o w a 29 pisak X-Y 476 plik
1254
Skorowidz
~ dyskowy 1022
proces - opisany p rzez klasę 849
~ dyskowy, operaq'e w e/w y 1102-1109 — nagłówkowy 169
program ~ OO, projektowanie 1182-1227
~ rozmieszczenie klas 440-451 ~ tryby otwarcia do oper. w e/w y 1104 p lo te r 476 p o d c ią g zn ak ó w 532 p o d p ro g ra m 132 p o d s ta w a konw ersji 1046 p o d sy ste m y 1198 p o k a ż kropkę d ziesiętn ą, flaga 1048 p o la bitow e 256, 634-638 ~ a rozpakowanie słów 639-646 p o le justow an ia 1045 polim orficzny ty p 1012 p olim orfizm 975-978,1185,1187 ~ anulowany 931 p o ró w n y w an ie ~ stringów (alfabetyczne) 563 ~ wskaźników 279 p o s_ ty p e, typ 1131 p o śre d n ia kl. p o d sta w o w a 905 p o stm o rtem d u m p 319
~ składający się z kilku plików 168-173 ~ wywołanie go z argumentami 363-365,1167 ~ zawiesza się 277 program ow anie ~ liniowe 1183 ~ obiektowe 1184 ~ obiektowo orientowane 1185-1188 ~ proceduralne 1183 ~ z ukrywaniem danych 1184 projektow anie O O 1182-1227 projektow anie p ro g ram u O O 1189 prom oq'a arg u m e n tu 402 protected 418-420, 896-897 p ry w atn a klasa 695 przech w y ty w an ie w y jątków 531 p rzecinek 123,240 przecinki (dw a obok siebie) 149 p rzeład o w an ie ~ a const 485
p o zy cy jn y system liczenia 1229
~ a przyjaźń 622 ~ a volatile 485
p ra w d a 53
~ a wirtualność 990-991
p ra w d a-fałsz 22-24
~ a zakres ważności nazwy funkqi 383-385 ~ a enum 877
p recisio n , funkcja 1051,1062 p red efin io w an y ~ manipulator 1065 ~ strumień 1025 p re p ro c e so r 195-218 ~ dyrektywa 195 p rin tf 870,1146 p rio ry te t ~ operatora 123-125, 798 ~ operatorów w e/w y 1031 p riv a te 418-420 p ro c e d u ra 1183 p ro c e d u ra ln e p ro g ra m o w a n ie 1183
~ funkcji 374-411 ~ globalnych ncw, dclctc 869-870 ~ i zasłonięcie równocześnie 439 ~ nazw funkcji a technika OO 381 ~ nazwy funkcji 374-411 ♦ a argumenty domniemane 379 ~ operatora 793-889 ♦ () - wywołania funkcji 848-849 ♦ delete 863-864 ♦ dwuargumentowego 810-813 ♦ indeksowania tablicy 843-847
♦ indeksowania tablicy przykładowy 1137
-
program
Skorowidz
♦ jednoargumentowego 807-809 ♦ lista możliwych 7% ♦ new 859-862 ♦ odniesienia się wskaźnikiem 850-858 ♦ ogólna definiq'a 796 ♦ postdekrementaq’i 871-873 ♦ postinkrementacji 871-873 ♦ przypisania 827-842 ♦ wypisywania 878-883 ~ a przestrzeń nazw 385 ~ zaoszczędzone dzięki konwersjom 764 p rz e s trz e ń nazw 174 ~ "zaludnianie" stopniowe 75
1255
~ "kaskadowe" 836 ~ operator = 100 ~ przy dziedziczeniu 915-924 p seu d o -k o d 45 p t r d i f f j 277 p u b lic 418-420 p u t(ch ar) - f. w kl. stru m ie n ia 1099 p u tb a c k - f. w kl. stru m ie n ia 1098 p u ts 870 p u ts - f. bibblioteczna 231
~ a przeładowanie 385 ~ a zagnieżdżanie klas 651 ~ anonimowa 174 ~ dyrektywa using 76 ~ std:: 188,507,1102 ~ zakres 72 p rzesu n ię cie bitów ~ w lewo 106 ~ w prawo 107 p rz e z w artość przesłanie o b iek tu 943 p rz y d o m e k ~ const 80-83 ~ explicit 675 ~ Zob. też: modyfikator, specyfikator ~ mutable 485-498 ~ register 84 ~ static 166 ~ volatile 84 p rzy ja źń ~ a zakres 628 ~ jej deklaracja 616 przylegające C-stringi 66 p rzy p isan ie 830 ~ "bit po bicie" 916 ~ "składnik po składniku" 916 - definicja 81 ~ dodatkowe operatory +=, -=, *=, /=, &=, etc. 110
rd state, fu n k q a 1117 rd state, funkcja str. stan stru m ien ia 1115 read(char*, int) 1093 read so m e - f. w kl. stru m ien ia 1093 referencja 69, 256 ~ a przeładowanie 390 ~ argumentem funkqi 142-144 - do klasy pochodnej 936-950 reg ister 84 reinterpret_cast 116,121 ~ a przeładowanie 797 ~ a wskaźniki 261-263 ~ przy ustaw, wskaźnika 292 re k u re n q a 174-185 —bezpośrednia 175 ~ pośrednia 176 ~ warunek zatrzymujący 180 ~ zatrzymanie jej 175 re la q i o p erato r 101 replace - f. w kl. std::string 546-550 reserve() - f. w kl. std::string 523
1256
resetiosflags, manipulator 1075 resize - f. w kl. std::string 524 reszta z dzielenia 96 return 134,136-139,761 ~ mechanizm 283 reusability 943,1186,1195 rezerwa qa obszarów pamięci 293-314 rezultat zwracany przez funkcję 136-139 rfind - f. w kl std::string 538 right, flaga 1043-1045 right, manipulator 1071 robot przemysłowy 1233 rozbudowalność C++ 1186 rozmiar a długość C-stringu 233 rozmiar tablicy (wymagania) 219 rozmiar unii 630 rozpakowanie słów 639-646 rozszerzalność C++ 978,1186 rozszerzona dokładność 52 rzucenie wyjątku przez operator new 310 rzutow anie 114-122 ~ const_cast 119,321-325 ~ dynainic_cast 121,1011-1014 ~ nowymi operatorami 116 — reinterpret_cast 121
Skorowidz
seek_dir 1132 seek_dir, typ 1133 seekg, funkcja 1132 seekp, funkqa 1132 segment violation 319 sekwencja działań obiektu 1199 sekwencje trzyznakowe 196 set_new_handler 313-314,862 setbase, manipulator 1075 setf, funkcja 1051-1052,1075 setfill, manipulator 1073 setiosflags, manipulator 1075 setprecision, manipulator 1074 setstate, funkq'a 1115,1117 setw, manipulator 1072 short 51 short int, typ 51 show positive 1047 showbase, flaga 1043-1044,1046 showbase, manipulator 1070 showpoint, flaga 1043-1044,1048 showpoint, manipulator 1069 showpos, flaga 1043-1044,1047 showpos, manipulator 1070 signed 51 sizeO 519 size_t 859-860 size_type, typ w klasie std::string 519,534 sizeof operator 113 ~ a przeładowanie 797
środowisko programowania 440, 448 scanf 18,1146 scientific - flaga 1043-1044,1048 - manipulator 1071 - notation 1029
sizeof(wskaźnik) 1060 skip white space 1044 skipws, flaga 1043-1044 skipws, manipulator 1044,1069 "składnik po składniku" 916 składana instrukq'a 25
Skorowidz
s k ła d n ik ~ będący obiektem innej klasy 416 ~ const w klasie 843 ~ dana 414 ~ funkcja 415 ~ ~ ~ ~
klasy 414-415 klasy, dostęp 418-420 odziedziczony 892 odziedziczony, dostęp 894-902
~ referencja w klasie 843 ~ statyczny 465-475 ♦ definicja tegoż 467 ♦ deklaracja a definicja 466 ♦ inicjalizacja w klasie 470 ♦ jego typ - jaki jest? 470 ♦ kiedy się przydaje 480 ♦ miejsce jego definicji 469 ♦ trzy sposoby odniesienia się 467 ♦ wskaźnik doń 758 ~ zasłonięty 893 "składnik po składniku" 916
1257
śro d o w isk o p ro g ra m o w a n ia 12 stała ~ dosłowna 58-67 ♦ całkowita 59 ♦ C-string 65 ♦ zmiennoprzecinkowa 61 6 znakowa 62 ♦ znakowa typu wchar_t 64 ~ tekstowa 65 ~ za pomocą #define 199 stały ~ obiekt 80 ~ wskaźnik 315,723 sta n niski i stan w y so k i 1231 stan d ard o w a biblioteka 186 stan d ard o w a biblioteka strum ieni w e /w y 1021 static - a funkq'a wirtualna 984
słowa kluczowe C++ 16
~ funkcja 476-479 ~ funkq'a składowa 803 ~ modyfikator 166
sło w o (jednostka inform acji) 105,1232
~ obiekt 165,168
spacje 1028,1030
- składnik w klasie 465-475 - specyfikator 166
spaghetti kod a la 118 3 ,119 8 specjalny znak 63 specyfikator ~ Zob. też: przydomek, modyfikator ~ a deklaracja przyjaźni 628 ~ const 80-83 ~ dostępu 891 ~ explidt 675 ♦ a konstruktor konwertujący 763 ~ mutable 485-498 ~ przestrzeni nazw 77 ~ register 84 ~ signed, unsigned 51 ~ static 166 ~ volatile 84 spis relacji klas 1197,1211
~ string 336 static_cast 116 ~ a przeładowanie 797 statyczne - funkcja składowa 476-479 ~ funkcja składowa - wywołanie 478 - obiekt globalny 173 ~ obiekt lokalny 165 ~ składnik ♦ definiqa - gdzie ją umieścić? 469 ♦ incjalizacja w klasie 470 ♦ jego typ - jaki jest? 470 ♦ zastosowanie 480 std:: "uporczywość" z a p isu 19 std::bad_alloc, typ w y jątk u 310 std::exception 1126
1258
std ::strin g 503-543,545-614 ~ alfabetyczne porównywanie 563 ~ append 576
Skorowidz
~ operatory ==, !=,, >, =, >= 563 ~ pojemność 519-526
~ assign 573-575
~ porównywanie 556-564
~ at 527-531
~ replace 546-550
~ begin - f. iteratora 592 ~ bryk, czyli zestawienie funkcji 599-607
~ reserve 523 ~ resize 524
~ c_str() 551-554 ~ capacity 521
~ rfind 538 ~ rozmiar 519-526
~ elear 527
~ size 519
~ com pare 557 ~ copy 572
~ size_type, typ w klasie string 534 ~ substr 533
~ data() 555
~ swap 573
~ definiowanie obj. tej klasy 511-515 ~ długość tegoż 519-526 ~ dopisywanie do końca 516 ~ em pty 520 ~ end - f. iteratora 592 ~ erase 542-543 ~ find 534-537 ~ find_first_not_of 540
tychże
~ zamiana na C-string 551-554 std ex cep t - plik n ag łów kow y 531 std io 871 ~ biblioteka 1021,1145-1149 ~ flaga ios:: 1044 stero w an ie form atem 1041-1049, 1052
o p eraq 'i
strażn ik nagłów ka 212,442
~ find_last_not_of 540 ~ find_last_of 540
strep y 234, 236-237,333,426 ~ uwaga na pułapkę 429 stream size, typ 1057
~ getline - korzystanie 577-585 ~ inicjalizacja 514 ~ insert 544-545 ~ iteratory 586-598
strin g , klasa biblioteczna ~ Zob. std::string strin g ::iterato r 587 strin g ::n p o s 534 strin g ó w d o d aw an ie 509
~ konstruktory 513 ~ length 519
strin g stre am
~ liczby wpisanie do niego 517,1151
s tru k tu ra 629, 899,926 ~ agregatem 728
~ liter małych na wielkie zamiana 565-571 ~ litery wielkie i małe - a porównywanie 557 ~ max_size() 521 ~ npos, wartość (string::npos) 534 ~ out_of_range, typ wyjątku 531 ~ operator indeksowania tablicy 528 ~ operatory =,+,+= 516-518
w e /w y
stos 140
~ find_first_of 540
~ fragm ent tegoż 532
(alfabetyczne)
1170-1173
~ iniqalizaq'a 728 strumień 1023-1024 ~ buforowany 1025 ~ niebuforowany 1025 ~ otwieranie 1104 ~ predefiniowany 1025 ~ predefiniowany, domniemania 1027-1030
Skorowidz
~ tryb pracy 1104 s tru m ie n ie a w yjątki 1126-1129 s trz a ł na oślep 318 s u b s tr - f. w kl. std ::strin g 533 s u b -s trin g 532 s u m a logiczna 103
1259
~ new, kasowanie jej 723 ~ numeraq'a elementów 221 ~ ob. stałych jej inicjalizacja 224 ~ obiektów definiowana new 722-723 ~ obiektów inicjalizaqa 724-732 ~ obiektów jakiejś klasy 720-733
s w a p - f. w kl. std ::strin g 573
~ określanie rozmiaru w definicji 219 ~ przekazywanie do funkcji 224-227
sw itc h 34-36
~ rezerwowana dynamicznie 293-314
sy m e tria operacji w e / w y 1036
~ rozmiar 227
sync_w ith_stdio , funkcja 1149
~ wielowymiarowa 240-246,388, 849 ♦ jej typ 243 ♦ a new 299,388 ~ wskaźników 326-327 ♦ do danych składowych 756 ♦ do funkcji 358 ♦ do funkcji składowych 757 ~ wymaganie konstr. domniemanego 731
sy ste m (część zag ad n ien ia) 1191 sy ste m liczenia 1228-1238 ~ dwójkowy 1230 ~ dziesiątkowy 1228 ~ pozycyjny 1229 ~ szesnastkowy 1234-1236 ~ wagowy 1229 szab lo n y 4 szero k i znak 52 szesn astk o w y ~ system liczenia 1234-1236 ~ zapis 59 szy b k ie m nożenie 111
~ wysłanie do funkqi jednego jej elementu 227 ~ z czego można tworzyć 220 ~ znakowa 230-239 tab u lato r (znak specjalny) 63 technika obiektow o o rien to w an a 2,1182 ~ a przeładowanie nazw funkcji 381 technika p ro ced u raln a 1200 tek st źródłow y p ro g ra m u 10 tekstow a stała 65 tellgO 1132 tellpO 1132 tem plates 4
tablica 68,219-252 ~ bardzo duża, przykład 1136-1142
th is 431-433 ~ a funkq'a składowa statyczna 478 ~ a funkq'a zaprzyjaźniona 620
~ dwa sposoby wysyłania jej do funkcji 286
~ dla destruktora 683
~ dynamiczna alokacja 298
~ przykł. zastosowanie 433
~ inicjalizacja 223 ~ inicjalizacja zbiorcza 223
~ typ tego wskaźnika 433,484 th ro w 311
~ jej adres 225 ~ jej elementy 220-222
tolow er 568
- jej nazwa 225,723
transform (algorytm ) 568
1260
Skorowidz
true 53
ukryw anie d anych, program ow anie 1184
tru n c, try b otw arcia p lik u 1104
ukryw anie in fo rm aq i 418-420
try 311-312
umejscaw iający n e w 301
try b y otw arcia p lik u 1104
unget, funkcja 1099
try w ialn a konw ersja 401
u n ia 630-633 ~ a rozpakowanie słów 639-646
trzyznakowe sekwencje 196 ty p 48-94 ~ a klasa 413 ~ arytmetyczny 54
~ anonimowa 633 ~ inicjalizacja 632 ~ rozmiar 630 un itb u f, flaga 1043-1044,1050
~ C-stringu 67 ~ definiowany przez użytkownika 412-413
u n itbuf, m an ip u lato r 1070
~ fundamentalny 50-57,68
unsetf, funkq'a 1051-1052,1075
~ pochodny 414
u n sig n ed 51
~ polimorficzny 1012
uppercase, flaga 1043-1044,1047
~ size_t 860 ~ systematyka 50
uppercase, m a n ip u lato r 1070
~ void 70 ~ void* 70 ~ wbudowany 50 ~ wskaźnika do funkcji 346 ~ wyliczeniowy enum 87-90 ♦ bez nazwy 89 ♦ jako indeks tablicy 451 ~ zdefiniowany przez użytkownika 50
u sin g ~ deklaraqa 77 ~ deklaracja dostępu 899 ~ dyrektywa 76 u staw ian ie w sk a źn ik a 257-259
~ złożony 50, 68-69 typedef 85-86, 784 ~ a przeładowanie 386 ~ lokalne 657 ~ w operatorze wej/wyj 1036
vertical tabulator (zn ak speq'alny) 63 v irtu a l ~ funkqa 968-1020 ~ funkcja a klasa - inne znaczenie 973 ~ klasa podstawowa 951 -960 v o id 70 void* w skaźnik 264-266
u d o s tę p n ia n ie w ybiórcze 899 u jście stru m ien ia 1023 u k ła d sprzęgający 1233
volatile 84 ~ a const_cast 325 ~ a przeładowanie 485 ~ funkcja składowa 480-484
Skorowidz
1261
~ przy konwersji 780, 787, 790 w irtu aln a ~ czysto (funkcja) 995 w irtu aln e~ destruktor 999-1003 ~ dziedziczenie 951-960 ~ funkcja 968-1020,1214
w a g o w y system liczenia 1229 w a lid a to r 349 w a ru n e k zatrzym ujący rek u ren cję 175,180 w a ru n k o w a kom pilacja 205-209
~ klasa podstawowa 951-960 w irtu aln o ść ~ a operatory w e/w y 1039
w a ru n k o w e w y rażen ie 111-112
- a przeładowanie 990-991
w b u d o w a n y typ 50
~ a wczesne wiązanie 986-987 w o ln y form at zap isu 8
w c h a r_ t 52 ~ stałe znakowe 64 w czy ty w an ie fo rm ato w an e 1096
w ritefconst char*, int) 1100 w s, flaga 1069
w czy ty w an ie n iefo rm ato w an e 1096 w czy ty w an ie z k la w iatu ry d łu giego stringu 577-585 w d o w a p o p ren u m e rato rze 948 w ejścia/w y jścia 1037-1181
operacje
w s, m an ip u lato r 1069 w sk aźn ik 68,253-373 ~ a nazwa tablicy 275 ~ a reinterpret_cast 261-263
1021-1035,
~ argumentem funkcji 282-291 ~ arytmetyka 276
w h at, funkcja 1130
~ będący obiektem statycznym 318
w h ile 29
~ definiowanie 255-256 — do adresu zerowego (dawniej zwanego NULL) 318
w iązan ie ~ późne 984-985 ~ wczesne 984-985 ~ wczesne a wirtualność 986-987 w id e character 52 w id m o prom ieniow ania 801
~ doconst 316-317 ~ do funkcji 337-362 ♦ argumentem innej funkcji 348 ♦ definiowanie i odczytywanie deklaraq'i 341 ♦ składowej 747-755
w id o czn e własności 1200 w id th , funkcja 1051,1058,1072 w ielokropek 407
—
w ielokrotne dziedziczenie 925-933
~
w ielow ariantow y w y b ó r 2 8 ,3 4 ,3 7
~ ~ ~
w ielow ym iarow a ~ tablica 240-246,849 ~ tablica a przeładowanie 388 w ieloznaczność ~ a dziedziczenie wirtualne 954 ~ przy dziedziczeniu 930
~ ~ ~
♦ tablica takich wsk. 358 ♦ typ tegoż 346 do klasy pochodnej 936-950 do obiektów klasy 721 do składnika-danej 736-746 do składników klasy 734-759 do składników statycznych 758 do stałej 316-317 dodawanie i odejmowanie liczby całkowitej 276 domeny zastosowania 267
1262
Skorowidz
~ dostęp do komórek pamięci 292 ~ globalny 318 ~ konwersja 406 ~ odejmowanie dwóch od siebie 277 ~ pisania i czytania 1130-1135 ~ porównywanie 279 ~ poruszanie nim 267 ~ sposoby ustawiania 319-320 ~ stały 315,723 ♦ iniq'alizaqa go 316 ~ tablica tychże 326-327 ~ this 431-433 ♦ jego typ 433,484 ~ typu void 264-266 ~ ustawianie 257-259 ~ ustawianie za pom. reinterpret_cast 292 ~ w zastosowaniu do tablic 267-281 ~ zręczny 852, 872 ~ zwykły, we w nętrzu obiektu 735 w sk a ź n ik czytan ia g e t 1131 w sk a ź n ik pisania p u t 1131 w y b ió rcze u d o stę p n ia n ie 899 w y b ó r w ielo w arian to w y 28, 34,37 w y jątk i 4, 310 ~ a strumienie 1126-1129 ~ przechwytywanie ich 531 w y k ład n icza notacja 61 w yliczeniow y ty p e n u m 87-90 ~ bez nazwy 89 ~ jako indeks tablicy 451 w y ra ż e n ie logiczne 22 w y ra ż e n ie w aru n k o w e 111-112 ~ a w nim definiq'a obiektu 57-58 w y w ołanie zw ro tn e 349
zagnieżdżanie k om entarzy 14 zagnieżdżanie zak resó w 892 zagnieżdżona ~ klasa 649-653 ~ klasa a przyjadele 653 zakres ~ a przyjaźń 628 ~ klasy 892 ♦ pochodnej 892 ♦ podstawowej 892 ~ kwalifikator tegoż 893 ~ leksykalny 621,653,1036 ~ ważności ♦ Zob. niżej: 'zakres ważności' ~ zagnieżdżanie ich 892 zakres w ażności ~ definicja 70 ~ definiqi klasy wewnętrznej 650 ~ etykiety 162 ~ funkqa 71 ~ klasa 72,417 ~ lokalny 71 ~ namespace 72 ~ nazw w funkcji 162 ~ nazw w klasie 415 ~ nazwy funkq'i a przeładowanie 383-385 ~ nazwy obiektu 162-167 ~ nzawy 295 - obiektów globalnych 676 ~ obiektów lokalnych 675 ~ obiektów new 677 ~ pliku 72 ~ zagnieżdżanie 892 zakresu o p erato r 748 ~ a zasłanianie 79 zam ian a liczby n a rep rezen tu jący ją te k st (string) 671-672
Skorowidz
z a p a s pam ięci 296,722, 859 ~ wyczerpanie go 309 z a p is b in a rn y 105,1231 ~ jak dokonać zmiany 182 zap o w ia d ając a d ek laracja 619 za p rz y ja ź n io n a klasa 625 zasła n ia n ie n azw 78-79, 435-438 zasła n ia n ie składnika 892-893 zasło n ięcie i p rz e ła d o w a n ie 439 z a sło n ięta nazw a g lo b a ln a - dostęp 437 z a trz y m a n ie rekurencji 175 zatw ierd zająca funkcja 349 z a w ie ran ie ~ obiektów a dziedziczenie klas - różnica 934-935 ~ obiektu 1193,1196,1211 zaw ieszający się p ro g ram 277 zb io rcza inicjalizacja tab licy 223 z d a rz e n ie 644 zesp o lo n e liczby 760, 793-794 zg ru p o w a n ie dan y ch 724 zjaw isko D opplera 749 zło żo n y typ 50,68-69 zm ia n a form atu operacji w e /w y 1052 z m ien n a 15 ~ automatyczna 163 ~ statyczna globalna 173 ~ statyczna lokalna 165 znak ~ specjalny 63 ~ szeroki 52 ~ wypełniający 1073 zn ak o w e stałe dosłow ne 62 zręczn y w skaźnik 852, 872 źró d ło w y kod p ro g ram u 10 źró d ło strum ienia 1023 zw ro tn e w y w ołanie 349
1263
Opinie czytelników S trona (A)
Ponieważ książka drukowana jest na arkuszu wydawniczym mieszczącym 16 stron, dlatego kilka ostatnich stron zwykle zostaje pustych. Postanowiliśmy więc zamieścić tu fragmenty listów do autora. Są one wypowiedziami o poprzednich wydaniach "Syfmonii”.
i
W niektórych listach postanowiliśmy cytowane nazwy nazwiska zastąpić gwiazdkami. Dobrze wychowani ludzie zrozumieją, dlaczego nie wypadało nam inaczej.
najwięcej, najlepiej i najszybciej. To ją trzeba wykorzystywać, K Cześć /urku! Książka jest naprawdę wspaniała. aby odnieść sukces. Napisana niesamowitym, nowym i twórczym językiem! Ostatnio zdałem sobie sprawę z tego, że CUDOWNIE Dziwie się, że minęło tyle lat, podczas których odbyto i byłoby, gdybyś nam - uczniom, studentom i wszystkim innymwynaleziono wiele "potrzebnych" nam do życia rzeczy, ale podarował prezent w postaci napisania książki uczącej fizyki, w chyba jeszcze nikt nie wpadł na taki idealny pomysł pisania sposób Tobie właściwy!! Gdybyś i te dziedzinę wiedzy potrafił książki. Liczne powtórzenia danego materiału, którymi się tak super wyłożyć, jak programowanie, zmniejszyłbyś wiele posłużyłeś, naprawdę nie są wyrazem Twojego braku wiedzy smutków, złości i ułatwił nam BARDZO życie. Wierze w to, językowej, czy też braku wyobraźni, ale właśnie bardzo ponieważ nie ma na świecie rzeczy zawiłych, są tylko ludzie, pomocne czytelnikowi - człowiekowi, który aby coś przyswoić, którzy je takimi czynią! musi często powtarzać. To naturalne i każdy nOryotym wie, ale Szkoda, że nie mogę się z Tobą spotkać, bo bardzo żaden znany mi autor książek popularnonaukowych nie chciałbym, ale mieszkam zbyt daleko, a i Ty pewnie czasu masz odważył się na taki krok - powtarzanie nie do znudzenia, ale dla przyswojenia! Częściej powinno być to stosowane nawet w niewiele... szkołach! Wspaniały jest też fakt, iż napisałeś książkę w sposób "popatrzcie jakie to proste", a nie "popatrzcie, jaki to ja jestem Prowadzę zajęcia ze studentami z czystego C, ale na mądry". Bo przecież, jaki jest rzeczywisty cel pisania książek każdych pierwszych zajęciach polecam zakup Pana książki. Nie popularnonaukowych? Wydaje mi się, że autorzy piszący w dlatego, że uczyłem się z niej C++(-sa), ale dlatego, że traktuje ten drugi sposób - ukazujący ich nieprzebrany zasób słów problem łopatologicznie, bez wymądrzania i niepotrzebnego nikomu niepotrzebnych - są całkowicie naiwni, bez wyobraźni i komplikowania tematu. Poza tym jest to pierwsza książka o w ogóle nam - czytelnikom - niepotrzebni. Przy Twojej książce programowaniu napisana z humorem, jaką czytałem. można się naprawdę odprężyć, a czasaminawel dobrze bawić. Serdecznie pozdrawiam i czekam na dalsze pozycje Cieszę się, że puściłeś wodze wyobraźni tworząc tak (wydawnicze) Pana autorstwa (niekoniecznie dotyczące ciekawe, śmieszne, a co istotne, nasiąknięte ogromną dozą programowania). praktyczności opowiastki! To niesamowite wiedzieć, że można tyle niby niezrozumiałych rzeczy przełożyć na tak prosty język. Udało mi się ostatnio pożyczyć twoja Symfonie i Język prosty i skuteczny - język wyobraźni. Do teraz utrwaliła K rzeczywiście jest ona dobrym podręcznikiem, którego język mi mi się historia z babcia i dzieckiem ukazująca działanie przesyłania wartości/adresów do funkcji, a książki nie miałem odpowiada. Nawiasem mówiąc nie zgadzam się tylko z jednym stwierdzeniem - że jest to książka "do poduszki" - spróbowałem w ręce od kilku miesięcy, nie wspominając o samym i... noc miałem do tylu, ba mnie wciągnęło. Pozdrawiam programowaniu! serdecznie x. Wojciech Sądzę, że jeśli dalej pojedziesz w takim kierunku kierunku, w którym pragniesz pokazywać wiedzę w sposób ..to jest książka naprawdę genialna. Widziałem "kupę" najbardziej dostępny dla czytelników - (a inni autorzy razem z książek o językach programowania i nie spodziewałem się, że Tobą) i będziesz go rozwijać, todla nas ludzi-czytelnikowżycie jest podręcznik C+ + napisany w sposób tak lekki, a jednocześnie nie tylko stanie się łatwiejsze, ale i (co jest ważniejsze) będzie przyjemniejsze... Oto chyba nam chodzi! Takie książki, jak zwięzły sposób. C++ znałem praktycznie bardzo mało, więc Pana książka (pożyczona od kolegi) niezwykle mi pomogła. Jak Twoja będą się sprzedawać i sprzedają się (a jeśli nie, to chyba na początku zobaczyłem na okładce komentarz, że książka jest wina wydawnictwa, które za mało je reklamuje). pisana "prostym, wręcz przyjacielskim stylem", to pomyśla Nie wiem, czy wiesz, ale człowiek ma dwie półkule łem (bez obrazy): "znowu jakiś głupek napisał » lu z a c k ą « mózgowe przy czym każda z nich działa w odmienny sposób. książkę dla idiotów" - i miło się zaskoczyłem. Dotychczas Lewa odpowiada za myślenie, zdolności analityczne, wolałem książki przekazujące w czysty sposób zwięzłą teorię, >czytanie<, a prawa za zdolności twórcze, myślenie niczym dokumentacje techniczne. No i zmieniłem zdanie:) >obrazowe< i emocje. Wykorzystanie całego potencjału Wiele o tym czytałem, ale dopiero po lekturze mózgowego polega na połączeniu działania obu tych półkul. Zrozumiałem (tak myślę) wskaźniki, Dlatego Twoja książka jest bardzo dobra, bo oprócz treści dla "Symfonii..." lewej półkuli, aktywizuje do działania także drugą - ważniejszą dynamiczną rezerwację pamięci. Książkę czyta się jednym ___*_materiału -1 l. , mz życiem, porównania nnr/WMtt/IMf/I 1i tchem. Sam jestem zaskoczony, g d y ż m im o sporej' ilości - prawa przez skojarzenia uosi obrazy (historyjki). To właśnie prawa półkula zapamiętuje
I materiału w yd a je mi się. że nie
"wywietrzał" m i on z głow y
Opinie czytelników S trona (B) czytań ia tej książki i zająłem się innymi sprawom i dotyczącym i informatyki - sieciami komputerowymi. W końcu postarałem się jednak dowiedzieć o lepszej książce - bardzo chciałem się nauczyć C++ (potrafiłem już bardzo dobrze C, ale przejście do C++ nie było takie łatwe m i się zdawało - aż do zakupu symfonii). Na szczęście, gd y w Internecie zacząłem się dopytywać i wszedłem na IRC, tam chórem powiedziano mi, że najlepsza pozycja jest właśnie Symfonia. Zacząłem wiec zbierać pieniądze, choć szkoda mi było wydatku 120 z ł na tamtą książkę, którą już posiadałem. Na ostatnich wakacjach postanowiłem wiec spróbować ja przeczytać ponownie. Przez Nazywam się *** i jestem redaktorem prowadzącym cale wakacje przeczytałem połowę (w międzyczasie przeczytałem jeszcze dwie książki o tematyce informatycznej) i książki polskie w wydawnictwie ***. Pisze do Pana jako do tam spasowałem. Książka nie była dla mnie zbyt trudna, ale autora wspaniałej książki "Symfonia C++ . Musze przyznać, zbyt nudna. ie niewiele książek zrobiło na mnie takie wrażenie, jak właśnie Postanowiłem wiec wydać pieniądze i kupić Symfonię ta pozycja. Rzeczywiście Paniska książka jest napisana w stylu okazało się moim drugim najlepszym zakupem, jeśli chodzi o 'popatrzcie jakie to proste". Rzadko zdarza mi się trafić na książkę napisaną w tak kapitalny sposób. Książkę, w której książki informatyczne (pierwszy to oczywiście "Język A N SIC " autor potrafił połączyć lekkość języka z tak trudną w przekazie Briana Kerninghama i Denisa Ritchiego). Teraz planuje zakup treścią. Naprawdę jestem pod wrażeniem. Nie piszę tego “Pasji C++" - uważam, że zarówno pojemniki, szablony jak i ■wszystkiego tylko po to, by Panu schlebić, ja naprawdę tak sytuacje wyjątkowe to na tyle ważny temat, że powinny się uważam, jednak moim głównym celem jest nakłonić Pana do zawierać w Symfonii, ale skoro tak nie jest... wznowienia "Symfonii C++". Bardzo, naprawdę bardzo W związku z przeczytanie książki mam parę chętnie wydalibyśmy Paniska książkę, lako wydawnictwo, pytań/zastrzeżeń: koncentrowaliśmy się do tej pory na wydawaniu książek 1. Czy naprawdę na każdym razem, gdy projektuje Pan tłumaczonych, jednak teraz chcemy przede wszystkim program, postępuje Pan według tych wszystkich zasad promować polskich pisarzy. Uważamy, że naprawdę warto podanych w książce? wydawać takie książki jak "Symfonia..." Byłbym niezmiernie 2. Do kogo są skierowane te bajki tylko dla dzieci? Myślę, że zobowiązany, gdyby rozważył Pan tę propozycję. 10 letnie dziecko nie będzie się uczyć C++, a jeśli tak, to znaczy, z jest ono na tyle dojrzale, że tego typu bajki nie są na jego ^ Cześć, mam 16 latek, czytam twoją książkę. To moja poziomie. pierwsza książka o programowaniu (jakoś tak wypadło) i 3. Czy napisał Pan jeszcze jakieś książki o tematyce naprawdę misię podoba. Doszedłem już mniej więcej do środka informatycznej oprócz Symfonii i Pasji? drugiego tomu i naprawdę nie rozumiem dlaczego napisałeś we Kończę ten list wyrazami wdzięczności za napisanie tak wstępie, że książka jest przeznaczona dla ludzi znających choć jeden język programowania. Nie przeczytałem tego w wspaniałej książki, dzięki której mile spędziłem wiele godzin. księgami, a pewno jak bym to przeczytał, to bym się przestraszył i nie kupił - a tak przestraszyłem się w domu, lecz - E L . Uważam, że osoba która potrafi napisać tak ciekawą, jak na razie - nie napotkałem żadnych problemów, choć me pasjonującą, wciągającą książkę o tematyce tak "nudnej" jak programowanie, po prostu "marnuje” się!!! (Oczywiście z znalem żadnego innego języka. Kupiłem oprócz tego inna książkę: "Programowanie dla korzyścią dla takich ludzi jak ja). Sądzę, że gdybyś napisał praktyków w win9x" i mam duże problemy ze zrozumieniem. książkę dla szerszej grupy ludzi, czyli na przykład sensacyjno Naprawdę chciałbym, abyś ty napisał coś o programowaniu w przygodową, to ilość sprzedanych książek szybko przekroczy łaby ilość sprzedanych płyt " Ich Troje" w Polsce, a nakład Windowsie. Pozdrawiam Reksio "Harrego Poterra" na świecie. Pisze naprawdę poważnie. /.../Widać, że pisze Pan o czym ś, co Pan dobrze zna i nie odnosi się do swoich uczniów-czytelników z lekceważeniem, ale stara się naprawdę ich czegoś nauczyć. Zauważyłem, że jest to cecha ludzi dobrze wykształconych w jakiejś dziedzinie. Dlatego wielkie dzięki za powtarzanie do znudzenia ważnych spraw, obszerne komentarze do programów, wyczerpujące opisy zagadnień, celne trafianie w wątpliwości czytelnika. Największy plus tej pozycji to jednak przedstawianie konkretnych sytuacji, w których mogą się przydać poznane mechanizmy. To jest strzał w dziesiątkę.
Xl
/ * \ Właśnie wczoraj skończyłem czytać Pana książkę "Symfonia C++". Czytanie jej zajęło mi cały miesiąc, ale zdecydowanie nie żałuję spędzonego przy niej czasu. Pana książka nie jest pierwszą moją książką o takiej tematyce. Wcześniej kupiłem "Podstawy języka C++" Stanleya B. Lipmana i josee Lajoie. Było to jakieś l b roku temu. Nie wiedziałem wtedy jeszcze o istnieniu Symfonii - książkę kupiłem bez narady z kimkolwiek. Do jej przeczytania zabierałem się chyba z 5 razy, ale za każdym razem me mogłem przebrnąć dalej niż 1/10 książki. Zdenerwowało mnie to na tyle (książka kosztowała ok. 120 zł, a ja mam 16 lat- wtedy jeszcze 15, więc to dla mnie sporo kasy), że na jakiś czas zaprzestałem
Ps. Jesteś super, bardzo chciałbym spotkać kiedyś osobiście tak fajnego człowieka, można powiedzieć, że jestem tobą trochę zafascynowany. Tomek.
IX]
Zaczytałam się w Symfonii od pierwszej strony, jak żyję, nie miałam w ręku tak rewelacyjnego podręcznika, a musisz wiedzieć, że ładnych parę lat temu, kiedy jakoś minęłam się z powołaniem, ukończyłam studia dzienne na zupełnie innym kierunku, więc przebrnęłam przez stosy wszelakich pomocy naukowych. Wiem, że przede mną długa i ciernista droga, ale mam szaloną ochotę udowodnić, że z gruntu fałszywe jest
Opinie czytelników S trona (C) twierdzenie, że "kobieta informatyk jest jak świnka morska: ani to świnka, ani morska"... K I Po raz drugi z rzędu polccłcm moim studentom "Symfonię" oraz "Pasję". Pozwalam sobie, w czasie zajęć, na dosłowne cytaty. Muszę prawdopodobnie kupić jeszcze jeden komplet. M ój dotychczasowy jest ju ż w bardzo złym stanie. Przejechał ze mną całą Europę. Bywa ze mną wszędzie tip.: (zgodnie z ustawą z dnia 1997... itd.
X Myślę, ze zarówno seria książek "Symfonii" jak i dwa nowe tomy "Pasji", są znakomitymi książkami zwłaszcza do nauki języka C++ zorientowanego obiektowo. Jest to jedna z pierwszych polskich prób, udanych zresztą, pisania o rzeczach trudnych i b. trudnych w sposób ciekawy i wyczerpujący merytorycznie. Jestem osobiście egzemplarzem człowieka, który bardzo dużo z "Symfonii" i "Pasji" ju ż nauczył się i skorzystał. Mam nadzieję nauczyć się jeszcze zwłaszcza z "Pasji", którą ostatnio się więcej zajmuje./.../
PS. Czy nie zechciałby Pan dać się namówić na G dy przeczytałem pierwszą stronę to chciało mi się czytać dalej, ale nie jak podręcznik, ale jak powieść. Jurek, Ty wygłoszenie wykładu na wybrany temat w naszej Politechnice. chyba jesteś naprawdę dobrym pisarzem. Z Twoim łagodnym Bylibyśmy bardzo zainteresowani. Oczywiście jesteśmy poczuciem humoru możesz pisać o wszystkim. Może byś gotowi spełnić postawione przez Pana warunki. pisyw ał do naszej gazety nowojorskiej. Kolega mój jest wydawca "Kuriera plus". Chyba nic nie płaci za pisanie, bo Uniwersytecie Skończyłem informatykę ia sam ma ledwo na opłacenie rachunków Jagiellońskim. Obecnie pracuję na *** w Instytucie Matematyki i Informatyku Doktorat z logiki w 1990r. na Hej', cześć...Czyżbym uńdział we wczorajszej Wyborczej, Uniwersytecie Wrocławskim. Język C++ wykładam od trzech a ściśle mówiąc w dodatku krakowskim Twoje zdjęcie? I to z lat. "Symfonie" polecam od samego początku. Piotrem Skrzyneckim? Bardzo fajnie! Studenci bardzo ja lubią. Ja osobiście lubię też "Podstawy języka C++" Lippmana, ale dla większości studentów ta Hello, I am a French student in nuthematics actually in książka jest (wiem to od nich) trochę za trudna. A może po trainingperiod in Switzerland and Iam kam ing C++ .One Pol- prostu wymaga większej koncentracji przy czytaniu... ish friend of minę told me your book Symfonia C++ is a very good reference for leaming C++. Unfortunately I don’t speak K IV zeszłym semestrze pisałem Ci, że jeden z moich polish, and so l wanted to know ifthere is an English (or French, wykładowców bardzo objechał twoją "Symfonię...", or German) oersion ofthis book. Thankyou. Jean-Francois Pamiętasz? Otóż, teraz mam drugą część z programowania obiektowego z innym doktorkiem, który był studentem tego Szanowny Panie Jurku, Trochę głupio wyszło, że pierwszego i na wykładzie powiedział coś takiego: pierwszy napisał do Pana Jean-Francois, bo w sumie od dawna "Niech Państwo sobie powtórzą wszystko o C++, bo się do tego zabierałem. Zacznijmy od tego, ze już od dawna zachwycony jestem Pana "Symfonia C + + “.Jest doskonała, bo z będziemy mówić o Jacie Iw tym momencie na ekranie pojawił jednej strony łatwo i bardzo sympatyczn ie napisana, a z drugiej się tytuł Twojej książkij. Jeśli mieliście w zeszłym semestrze strony kompletna i z masa przykładów na wszystko. Szczerze zajęcia z panem X, to z pewnością odradzał wam Grębosza. No, mnie też odradzał, ale teraz stojąc po tej stronie katedry mam gratuluje Panu tak udanego dzieła. prawo mieć własne zdanie: Jest to najlepsza książka jaką zdarzy / . . . / mam 33 lata, zajmuje się ogólnie symulacją się wam przeczytać na temat języka C++. Dzięki niej numeryczną i oprogramowaniem (łącznie z GUI). Innych zaliczyłem kiedyś przedmiot w l £ miesiąca". języków poza C++ nie uznaję, choć niestety czasami trafia się Widzisz! Potwierdziło się coś, co ja wiedziałem od robótka w C albo o zgrozo w Fortranie. Pracuję w niedużej, ale dobrze rozwijającej się firmie. Pracuję z dwoma młodymi początku! ludźmi (jednym z nich jest Jean-Francois), których staż w C + + jest dość krótki. Zatem często zdarza misięopowiadaćimjaksię Tak na marginesie to mamy tu w Motoroli mały FUN różne rzeczy robi i na ogół kończy się sięganiem po Pana CLUB Twojej osoby. Prawie w szyscy czytali Twoje książki i książkę, żeby cos sprawdzić albo pokazać przykład. (Nie wyrażają się o nich bardzo pochlebnie. Edek. oznacza to, że sam dla siebie po nią nie sięgam :-)
K
M am y tu różne inne książki do C++, ale żadna nie sięga "Symfonii" nawet do pięt. Szukaliśmy po księgarniach, ale nie udało nam się niczego znaleźć. Doszło do tego, ze Jean-Francois w mojej nieobecności sam po "Symfonie" sięga (po listingach przykładów orientuje się mniej więcej w którym rozdziale się znajduje). Pytał się czy "Symfonia" istnieje w jakimś zachodnim języku, i obiecałem mu, że kiedyś do Pana wyślę e mail w tej sprawie. Widocznie za długo czekał i w końcu sam w książce po polsku znalazł Pana adres e-mail i napisał./.../ Software Deoelopment Manager, Lausanne, Switzerland
[XI
Szanowny panie Jurku! Jestem studentem 3 roku fizyki na Uniwersytecie ***. Nigdy wcześniej nie miałem nic wspólnego z programowaniem, dopóki nie sięgnąłem po pana książkę. W pewnym sensie to Pan umożliwił mi zanurzenie się w cudownie logiczny i przejrzysty ocean C++. Nasi wykładowcy tego przedmiotu bardzo skutecznie starali się (świadomie, czy też nie) zagmatwać ten język. Jednym słowem widzę bardzo duże podobieństwo w pojmowaniu rzeczywistości między Panem, a R. P. Feynman'em. Dzięki Bogu, że jeszcze tacy ludzie istnieją!
Opinie czytelników S trona (D)
Szanowny Panie Autorze! Symfonia jest cool! I muszę przyznać, że naprawdę jest to książka w stylu "Patrzcie, jakie to proste!". Doszłam też do wniosku, że jest Pan bardzo mądrym programistą (i to wcale nie z powodu "Patrzcie, jaki ja jestem mądry!"), jest Pan o wiele mądrzejszy od połowy tych panów profesorów, którzy piszą książki o programowaniu. Dlaczego Bo potrafi Pan napisać coś, co zrozumie nawet taki młotek jak ja! 1 w dodatku jest Pan zdolny oprzeć się pokusie chwalenia się swoją wielką wiedzą programistyczną, używaniem skomplikowanych definicji i pisaniem bzdur, byleby tylko brzmiało to w miarę profesjonalnie. A Pana przykłady są świetne! Ma Pan bardzo fajny styl programowania - naprawdę tego zazdroszczę... Nie jestem zawodową programistką. Nie jestem też studentką informatyki (ale będę z całą pewnością). Nie jestem też 14-letnią amatorką programowania. Kim więc jestem? Eee, lepiej nie mówić. Mam dopiero jedenaście lat i chodzę do piątej klasy szkoły podstawowej. Bardzo mi to NIE pasuje... Na informatyce uczymy się robienia stron internetowych w MS Publisher... Babkę nawet nie za bardzo obchodzi, o czym je robimy... Zrobiłam stronę o programowaniu w C oraz C++, wszystko napisałam własnoręcznie, a kobieta wstawia mi do dziennika niższą ocenę niż mojej koleżance (strona o AvrU Laoigne, w dodatku wszystkie teksty zostały ściągnięte z Internetu), tylko dlatego, że... wstawiłam za mało linków. Nauczycielka skarży się. że nie robie z informatyki nic ponad program - dla niej robienie czegoś ponad program to ECDL, czyli Europejskie Komputerowe Prawo Jazdy (nie chciałam tego zdawać, bo tematyka mnie po prostu nudzi). A programowanie w trzech językach plus HTML? Kiedy przerabialiśmy obróbkę tekstu w Wordzie, kazała nam napisać tekst na całą stronę w dwóch kolumnach. Napisałam obszerny artykuł na temat protokołu TCP/IP. Co dostałam? Czwórkę na szynach. Ponieważ napisałam to na dwóch stronach zamiast na jednej i mniejszą czcionką niż trzeba było (a zrobiłam to tylko po to, żeby mi się to wszystko zmieściło)... Koleżanka, która napisała o rodzajach cieni do powiek - dostała szóstkę... Polskie szkolnictwo (przynajmniej pod kątem informatyki) jest ODRAŻAJĄCE... Jedynym ratunkiem jest uczenie się wszystkiego samemu, a dzięki Symfonii moje umiejętności zostały poszerzone o C++/ Dziękuję za Symfonię, naprawdę bardzo dziękuję... Mam nadzieje, że rozmawiam z Jerzym Gręboszem, a nie jakimś palantem z wydawnictwa, który kontroluje wszystkie wiadomości przychodzące i wychodzące (bez urazy). Pytam się o 4 tom Symfonii C++. Dowiedziałem się, że coś takiego istnieje i chciałbym todostać... Mam (niestety) wydanie 5-te poprawione, a chciałbym mieć z pierwszego co najmniej 4 tom słyszałem, że istnieje. I pytanko: czy można go dostać, czy nie istnieje cos takiego... (I tak wiem, że jest, bo kumpel ma, ale jest 500 km ode mnie i nie może mi tego pożyczyć). pzw. .pragnę Panu podziękować za tak wspaniały podręcznik do nauki C++ jakim jest "Symfonia C ++“. To bardzo konkretna i merytorycznie wyczerpująca 'trylogia'
(chodzi mi naturalnie o dokładne opisanie tematu, a nie o wyczerpanie fizyczne podczas czytania, bowiem czyta się ją jak Sienkiewiczowska sagę - po prostu jednym ciągiem). P rzy tym jest napisana językiem zrozumiałym dla początkującego programisty i dzięki temu, aż chce się zrobić krok dalej. Dlatego mam jedna prośbę - pytanie: czy mógłby mi Pan polecić jakąś literaturę (równie merytorycznie dokładną i przystępnie napisana) z zakresu Visual C++ i C++ pod Windows? Do Jurka, Serdeczne dzięki za pomoc. Twoja książkę już prawie przeczytałem (i jestem z niej bardzo zadowolony, jest lepsza od jakiejkolwiek lektury szkolnej) i już mniej, więcej wiem, o co chodzi w tym c++, ale do pełni szczęścia musiałbym przeczytać ją jeszcze z raz i dopiero wtedy uczyć się tradycyjna metoda "wkuwania". M yślę, że zrobienie prostej gierki lub programu nie sprawi mi większego problemu w przyszłości (oczywiście dzięki twojej książce). To tobie powinni dać Nobla, a nie jakiejś Szymborskiej Inawet jej nie znam:) J. Piszesz, że zazdrościsz mi - i że spełnię się jako programista. Zaskoczyłeś mnie, bo jesteś chyba ostatnią osobą, która mogłaby mi tego zazdrościć. Przecież sam jesteś idolem wielu programistów w Polsce (i nie tylko) i wykorzystujesz swoje umiejętności programistyczne w bardzo ważnych badaniach naukowych. To Ty się spełniasz, podczas g d y ja mam to tylko w planach. Swoją drogą - nie wiadomo, co się jeszcze okaże. P * s l Twoja książkę poleciłem mojemu koledze, którego od wielu lat uczę programowania. Jeszcze tydzień temu pytał mnie "ipo co ci te obiekty? ". Dzisiaj rano kiedy zadzwonił, w słuchawce usłyszałem głos szczęśliwego człowieka. Sam pewnie wiesz, że nic tak nie cieszy jak radość ucznia. 1 3 Mam na imię Maciek, jestem studentem 5-go roku geologii na Uniwersytecie Jagiellońskim i niedawno nabyłem napisana przez Pana książkę - "Symfonia C++". Pierwsze, co chce zrobić, to wyrazić moje najszczersze słowa podziwu dla swego rodzaju "majstersztyku" dydaktycznego, jaki eman uje z owej trylogii. Naprawdę, czytając Pańską książkę, ten język sam wbija się do głowy. Moje gratulacje perfekcyjnie napisanego dzieła!:-) Z kimkolwiek bym się nie spotkał, każdy twierdzi (włącznie ze mną), że "Symfonie C++" czyta się jak znakomitą powieść kryminalną. Szanowny Panie! Z Pańską książką "Symfonia C++" zetknąłem się cztery lata temu, będąc jeszcze studentem. Obecnie prowadzę wykłady z języka C++ w policealnym studium informatycznym i muszę przyznać, że do dzisiaj uważam, że "Symfonia" pozostaje niedoścignionym wzorem podręcznika programowania dla mas. Swoboda języka, który stosuje pan, by opisać nieraz nietrywialnie zagadnienia, rzuca na kolana. Swoim podopiecznym polecam Pańską pozycję jako genialną skarbnicę wiedzy o języku C++. Ostatnio pojawiła sip nowa pozycja sygnowana Pańskim nazwiskiem - Pasja C++ . Sądzę, że jest ona równie dobra jak "Symfonia" - lepsza chyba
Opinie czytelników S trona (E) nie, bo nie za bardzo mogę sobie wyobrazić coś bardziej genialnego. Łączę wyrazy szacunku.
polecił Twoją książkę na pierwszym wykładzie- przekręcił itdnak tytuł: Ekstaza C++ ).
- wykładowca od Projektowania obiektowego powiedział, K Witaj! Jestem Ci niezmiernie wdzięczny za napisanie i i C++ nie jest właściwie obiektowym językiem 'Symfonii C++'. Stała się ona moim podstawowym źródłem programowania, nie nadaje się do pisania programów wiedzy i wyrocznią w sprawach C+ +! Odkąd wziąłem ją do ręki obiektowo orientowanych, a najlepszy to jest Smalltalk. ciągle chodzę niewyspany - na czytanie mam czas tylko Zastanawiam się tylko wspólnie z kolegami, dlaczego wszyscy wieczorami, a książka wciąga jak (może to kiepskie porównanie) tego używają iw 99% ogłoszeń pracy dla informatyka pojawia najlepszy kryminał! Krótko mówiąc książka jest SUPER! się konieczność znajomości C++? A na naszej uczelni C++ ewidentnie uczyć nie chcą. (Słyszałem, że na niektórych K . ../Tylko trochę mi żal, że za Symfonie i Pasje nie renomowanych uczelniach w USA C++ jest wykładany przez wziąłem się wcześniej, gdy przed trzema laty zabierałem się do 2 lata, u nas przez 4 wykłady). konstruowania mojego obiektowego (?) interfejsu z kasami fiskalnymi. To można było zrobić o wieeeele lepiej! Książkę kupiłem nie dlatego, że, jak powiedział mój Ach, nie przedstawiłem się lepiej. Otóż jestem programistą wykładowca, "na kolokwium obowiązują trzy tomy Grębosza ", w ***, firmie produkującej rodzinę programów dla małych i ale po prostu ujęła mnie swoim luźnym stylem - pierwszy tom średnich firm. Dotychczas były to programy osadzone w przeczytałem w dwa dni od deski do deski, jak beletrystykę. I środowisku FOX Pro z 'wstawkami' w jęz. środowisku oraz dużo z tego zapamiętałem! Gratuluje!:-) C++. Do tych wstawek należą również programy współpracy z Skoro lak wciąga, to może w następnych wydaniach dodać urządzeniami fiskalnymi, którymi ja z moim zespołem się zajmujemy. Ostatnio przenosimy wszystko "pod Windows" i jeszcze jakiś watek kryminalny? ;-))) powstaje ( na szczęście) nowe APl dla sterowników urządzeń fiskalnych. Głównym projektantem tego API jest mój kolega, ^ Witam! /.../ Dopiero teraz znalazłem czas, aby który ucząc się C++ korzystał również z Symfonii podziękować Ci za Twoje książki! Zacząłem (swoim Możesz wiec, Jurku, uważać, że windowsowe programy firm y *** zawierają dużo wiedzy, którą Tiooje książki pozwoliły przyswoić.
zwyczajem) czytać Symfonie od końca i natknąłem się na listy od Twoich czytelników. Czytałem i czytałem i pomyślałem sobie, że pewnie są mocno przesadzone! Ale postanowiłem przekonać się sam i.....w3 dni skończyłem 2 tomy, a z trzeciego przeczytałem rozdział o operacjach wejścia/wyjścia! Znalazłem w nich wszystko to, czego szukałem! "Programuję" dopiero od około 2 miesięcy i na zaliczenie musiałem wym yślić i napisać program. Zabierałem się do tego kilka razy, a bez większych sukcesów (może dlatego, że ćwiczenia z C++ to była w sumie parodia). Wszystkiego musiałem nauczyć się sam. Nie dość tego, że po lekturze Symfonii skończyłem go w 2 dni, to jeszcze pan profesor powiedział, że to jest najlepszy program z całej 40 osobowej grupy (5 w indeksie)! Zapytał skąd nauczy łem się min. tablic, odczytu, wyświetlania zawartości i zapisu plłców, bo jak stwierdził (i słusznie) na ćwiczeniach o tym przecież nie mówił! Powiedziałem, że wszystko znalazłem w Symfonii. O na to, że w przyszłym semestrze w szyscy będą zobligowanidojej lektury! Poleciłem też twoją książkę koledze z innej uczelni, bardzo mu się spodobała. Jeszcze raz bardzo
D>*sj Mam nadzieje, że mnie jeszcze pamiętasz (chyba tak, bo w "starej Symfonii" widziałem dwa fragmenty moich listów), może wiec zaciekawi Cię, jak m i się powodzi. Od czerwca ubiegłego roku pracuje dla amerykańskiej firm y *** jako programista/analityk systemowy. Powodzi mi się dobrze, pierwsze 3 miesiące spędziłem w Telecelu Lizbonie, w luksusowym apartamencie, gdzie łóżko ścieliły i naczynia zm yw ały pokojówki, zwiedziłem kilka pobliskich miast (np. Caiscais i Cintre), potem przeniesiono mnie do Polkomtela. Od czasu do czasu wysyłają mnie na "treningi" w jakieś fajne miejsca - ostatnio do H agi Nie programuje obecnie, co prawda w C++, ale przecież programista bez C++, to jak ktoś bez znajomości angielskiego, wiec to, że mogłem umieścić C++ na CV zawdzięczam między innymi Tobie (ależ musisz być teraz dumny!). Byłoby mi milo, gdybyśmy mogli się kiedyś (z dzękuję! Justyna, a jakże) spotkać na piwie.
/.../ P.S Jeszcze Ci opowiem parę anegdot z moich 1 3 / . . ./ Bardzo zastanawiają mnie poniekąd przewrotne (jak na publikacje informatyczne) tytuły, jakie Pana dał swoim studiów: - wykładowca od Wstępu do Informatyki tuńerdził, że w książkom. Przeważnie są to jakieś "nieludzkie" nazwy - czy to C++ można płodzić tylko potworki i kto w C++ będzie odnoszące się do kompilatorów, systemów czy przemądrze programował, ten się nigdy nie nauczy porządnie nazwanych cech języka, produktu... Tymczasem Pan nawiązał programować, najlepszy jest Pascal i lcon (niestety nigdy me do genialnej i wspanialej (bardzo mi bliskiej) duchowej spuścizny ludzi, jaką je st muzyka, o ileż starszej od wyjaśnił dlaczego). komputerów - swoją teorią - z tego, co pamiętam - sięga - wykładowca od C++ twierdził, że C++ jest dziedzicznie Pitagorasa i jego kwintowego stroju, a pewnie i dalej. Dlaczego obciążony jako nadbudówka nad istniejącym językiem, wiec go takie tytuły? Czy ze względu na dostojność i wielkość i głębię przerobd w 4 niespełna dwugodzinne wykłady (baaardzo dużo symfonii i pasji jako takiej, czy może z innych powodów? wynieśliśmy z tych wykładów- jedyne, co wyniosłem to, że
Opinie czytelników S trona (F) informatyki od roku i właśnie to rok temu zetknąłem się Zatem bardzo dziękuję za te fantastyczną książkę. Do tej pierwszy raz z językiem C. Materiały do nauki, które pory korzystałem tylko z "Język Cm Bjarne a S. (nie posiadałem nie przedstawiały zagadnień w sposób tak prosty, pamiętam nazwiska - chodzi mi o twórcę języka) i miałem jak zrobił to Pan w swojej książce. Dowiedziałem się o niej pewne problemy ze zrozumieniem niektórych zagadnień dopiero pod koniec drugiego semestru, gdy zawisło nade mną widmo zaliczenia kilku programów, w których należało Dzięki Pańskim książkom i zawartych w nich przykładom teraz programowanie w Cm jest dla mnie oprócz nauki także zastosować programowanie obiektowe (w jednym zwykła klasa, w następnym dziedziczenie, a w kolejnym już polimorfizm). wspaniałą zabawą Dzięki pana książce zrozumiałem te zagadnienia, poznałem ciekawe techniki programowania i nie sprawiały mi trudności X Witam jeszcze raz!!!!! Jak zwykle zacznę troszkę o mnie W mojej nowej pracy powodzi mi się coraz lepiej. W końcu robię pytania prowadzącego nasze laboratoria z programowania w to, co lubię najbardziej, czyli pisze program. Już mam część języku Cm . Oczywiście "Symfonia" nie była moja jedyną zrobioną i jestem ju ż po wstępnej demonstracji, którą 'pomocą naukową", ale w dużym stopniu (i raczej w przedstawiłem prezesom firmy i ich opinie podtrzymały mnie największym) przyczyniła się do zrozumienia programowania na duchu. W końcu, jak dobrze pójdzie, to założymy nowa firmę obiektowego. o profilu internetowym, w której ja mam przejąć kierownictwo Przed przeczytaniem "Symfonii" nie mogłem sobie dać A zawdzięczam to też Tobie. Twoje odpowiedzi na moje listy rady ze wskaźnikami, ogólnie nie potrafiłem tego zrozumieć i bardzo mi pomogły uwierzyć w siebie i swoją wiedzę. Teraz są zastosować... Uwierzy Pan? Ale po przeczytaniu książki tego efekty. 1 mogę się jeszcze rozwijać w nowych systemach wreszcie "załapałem" i okazało się w końcu, żeto n ie jest takie trudne. No i naturalnie, nie było problemów z odpowiedziami komputerowych. na pytania w stylu: "dlaczego tu jest gwiazdka, a tu nie?". Nie Chciałbym, Panu pogratulować wydania tak doskonałej chce się przechwalać, ale wszystkie programy zaliczyłem na 5 książki, jaką je s t" SYMFONIA C m Jestem tu w Niemczech oraz uzyskałem ocenę końcową również 5:-) Dziękuję:-) studentem zaocznej informatyki i w każdej wolnej chwili z niej Programowanie wydawało mi się kiedyś trudne, ale gdy korzystam. Chciałbym Panu jeszcze raz podziękować. Dawid ju ż zacząłem poszerzać swoją wiedzę wciągnęło mnie i sprawia satysfakcje, gdy uda się napisać działający program. M yślę, że Drogi Jurku (mój najlepszy kumplu)! Bardzo lubię jest to właśnie zasługą Pańskiej "Symfonii", która zaraża konwersować z osobami z zaimplementowanym interfejsem entuzjazmem do języka C m i po którą chętnie się sięga, jak po poczucia humoru. Drwer "Pan używam zawsze na początku, dobrą książkę "rozrywkowa". Jeszcze raz dziękuję. Student by nie urazić ego osób nie wyposażonych w ten jakże przydatny informatyki. moduł./...I Ależ tych słów pochwały i tak było mało Jureczku. Uważam Symfonię za najlepszą książkę o Cm na naszym Drogi Jurku! Kiedyś zaczynałem naukę z Symfonią rynku dla uczących się tego języka, a słowa oszczędzałem, byś C m . Teraz, niedługo wyjeżdżam do Stanów, ponieważ mam nie pękł z nadmiaru pozytywnych emocji i samozadowolenia podpisany kontrakt z Microsoft’em. Jeśli będąc tam mógłbym Cipomoc w zakresie tłumaczeń, wydania Twoich publikacjilub r)■ . Chciałem wczoraj jeszcze zamówić książkę "Tajemniczy czegokolwiek innego, pisz śmiało. Świat Jąder Atomowych", ale po chwili połapałem się, że to Przeczytałem te 3 tomiki już kawałek czasu temu i teraz niestety film. Może by tak jednak napisać taką książeczkę? Jeszcze w liceum kupiłem książkę Franka Close "Kosmiczna używam jak narzędzia. Są genialne, wspaniale napisane, cebula". Fantastyczna książka popularno-naukowa o budowie polecam każdemu. Mam tylko jeszcze takie pytanie: Czy atomów i innych nukleodupereli wraz z historii ich Symfonia jest przetłumaczona na angielski lub czy będzie w odkrywania. Przedstawiała stan nauki do połowy lat 80-tych i późniejszym czasie? właściwie w tym okresie straciłem z tą dziedziną przyczepność. Paru (anglojęzycznych) znajomych ze Stanów ju ż mnie o Trochę informacji czerpię z Discooery channel (był takt to pytało inie wiem czy mówić im, żeby szukali, czy znaleźli coś niezły serial o fizyce współczesnej). Może mógłbyś mi coś innego... polecić popularno-naukowego, co się niedawno pojawiło na A jak się dowiedzieli o istnieniu Symfonii? Po prostu rynku? A jak nie, to pisz! widzieli, iż niektórzy Polacy mieszkający / pracujący w Stanach korzystali z tej książki. Paru ludzi coś tam pisze w ____ ] Piszesz: "Taki jest przecież proces uczenia: Musisz od C m , jeden ma na biurku lekturę, widać, że się przydaje, a czasu do czasu powiedzieć coś luźniejszego, inaczej ludzie się reszta nic z tego nie rozumie. Poza tym pytano mnie, jakich podręczników używałem, więc odpowiadałem, tyle że nie zmęczą". Dziś studenci byli zmęczeni i mi nawiali, więc nawet nie mogłem potwierdzić istnienia wersji anglojęzycznej. miałem dziś okazji powiedzieć coś lekkiego. początku chciałbym podziękować panu za "łatwą książkę", jaką jest "Symfonia C++". Jestem studentem
Co do sukcesów wydawniczych, to nie wiem, czego można się spodziewać. Tu, w Polsce każdy student na Politechnice Warszawskiej, czy AGH spytany o najlepszy podręcznik do C++, wymieni właśnie Symfonię / Pasję. Wiem, że ciężko
Opinie czytelników S trona (G) wkroczyć na nowy rynek, zwłaszcza, że Amerykanie raczej skłonni są kupować "produkty krajowe". Żeby taka książka zaistniała, potrzeba by jakiejś reklamy, a najlepiej ludzi, którzy przeczytali "Symfonie", jest stanowczo najbardziej intuicyjna i ciekawa. Za dowód tego, mogę poświadczyć, że za pierwszym podejściem, wchłonąłem I tom bez potrzeby włączania komputera. Ludzie też doceniają przyjazny język, adnotacje z właściwą wymową i trafniejsze tłumaczenie "ooerloaded (rzeczywiście "przeciążony" nie oddaje charakteru tej techniki). Wracając do tłumaczeń na języki obce, to szczerze popieram Pana ideę. Nie chodzi o to, że książka źle przetłumaczona traci swoje właściwości, lecz każda, która nie jest przetłumaczona perfekcyjnie. Może kiedyś będzie można dostać poza Polską "przyjazny podręcznik". K Uczyłem się sam w domu. Wcześniej znałem dość dobrze język C, a z Twoich książek uczyłem się tego "++" (choc dowiedziałem się też kilku niuansów o samym C). "Symfonie czytałem 1 tom dziennie i robiłem sobie notatki, z których teraz korzystam. Podziwiam, jak czytelnie potrafiłeś wszystko wyłożyć. "Pasje" czytałem w podobnym tempie, chociaż materiał jest tam trudniejszy, ale świetnie objaśniony. Na razie nie wykorzystuje pełnych możliwości C++, lecz korzystam głównie z szablonów, przeładowanych operatorów i rzucania wyjątków. Jeśli masz ochotę (i czas), to zapraszam do obejrzenia moich programów udostępnionych w Internecie. Załączam pozdrowienia z deszczowej (chwilowo), chocjuż wiosennie cieplej i kwitnącej Atlanty. Jestem tu na pozycji PostDoca.
książkach przyszły następne. I n a w e t jak kilku ludziie* mnie ściągnąć z tej drogi, ja w c ią ż miałem w pań>a\ maile lak do pana pisałem w ted y, to prócz nauki p ro g o m zajmowałem się także m uzyką. Choć nie miałem ęrti wiele wspólnego z kompozycją m u z y k i w studiu, to He trakcie czytania pańskich k sią żek zrozumiałem jaeja procesory efektów audio. S kutek b ył tego taki, żeiłei realizatorem, programistą kilku brzm ień (tzn. robie c początku, a nie z gotowych barw ), a także autorem k ilm i do nich, oraz oczywiście w ystępow ałem na tej płyiYń marzeniem było także wydać sw o ją własną płytę, a deka > tamtej, która nie była moją, zbliżałem się do tego. Cite sobie wtedy, że - jeśli trzeba b ęd zie- zostawię muzykę t r s tylko nauką, ponieważ to ona umożliwiła mi zrozci czym dane urządzenie jest, co robi i jak działa. W pierwszych dwóch tygodniach po premierze, p>y odpowiednio na 6 i 8 miejscu ogólnopolskiej listy p i sprzedających się albumów. P óźniej poznałem dzucn która non-stop wmawiała mi, że mam tak świetną b a rtn i że warto jest zostawić studia, żeby wydać płytę* dostałem pierwszy list od pana. Zostawiłem postav» sytuacji, że miałem do wyboru - uwierzyć panu lułf N wierzyłem jej, bo ona nie lubiła nauki. Wiem, że nienwĄ tego we mnie, że chciałem i chcę się uczyć i rozw ijać^ była zazdrosna o to, że czerpałem z tego powodu cnr radość i byłem z tego powodu szczęśliwy. Uwierzyłem żc pana słowo w tych madach. Oddałem za to dużo. Prafyi całe moje poprzednie życie. Ludzi, z którymi grałem koto) nie podobało im się to, że w trakcie jazdy, np. na kaz J myślałem o tym, czego się wczoraj nauczyłem, bo po p et książce zacząłem czytać "The Scientist and Engineer':(«• to Digital Signal Processing", a także "The Inner Lighti" of Consiousness" Steoena Smitha. Nie podobało m i i zamiast brać z nimi narkotyki - wolałem się uczyć. lU byP swój album, marzyłem o tym od 5 lat przed premiertfUi płyty. Ale poczułem wtedy, że trzeba dokonać u p Wybrałem drogę, którą pan mi wskazał.
Zastanawiasz się czy w "nowej Symfonii" omawiać tablice znakowe? jak najbardziej! To źródło kłopotów początkujących. Znam wielu programistów, którym płacą za to, że "znają" C++,ajak przychodzi do tablic znakowych, to się bardzo plączą. Sugerowałbym abyś dodał to, co jest w nowym standardzie, nic nie ujmując ze starej zawartości. Przychodzą do mnie ludzie, Jak do pana pisałem - studiowałem ekonomię *. którzy programowanie w C++ zaczynali od środowisk RAD i Chciałem studiować informatykę. Pojechałem do S zv i mają spore kłopoty z podstawami, za to świetnie umieją ciężko pracowałem fizycznie, żeby zarobić sobie n d k budować z klocków. Gorzej jak te klocki się kończą i trzeba miesięcy na studia. Igdy miałem dość, myślałem tylko u stworzyć coś samemu od zera. studiach i o pana słowach. Od tego roku jestem stuam Od wszystkich, których czasem na zlecenie zatrudniam, informatyki. Ostatnio zacząłem się uczyć platformy N i t wymagam przeczytania "Symfonii" i "Pasji ^ chciałbym niedługo zacząć pracę jako programista, a ti przynajmniej pierwszego tomu "Thinking inC++" - jedynego książce do DSP (której jeszcze do końca nie przeczyta godnego Ciebie konkurenta. Ja sam zanim sięgnąłem po mogłem napisać sobie sam efekt stereofonicznego delayz si Symfonię długo pisałem w C, więc większość takich problemów w postaci dli'a i chodzi pod każdym sekwencerem m uzyce na PC obsługującym format VST. Poziom nauczania w i miałem za sobą. obecnej uczelni jest pięć razy wyższy niż na Ale ter%il ____ N ie wiem, czy mnie pan pamięta, panie Jurku, ale chcę już mieć 4.5 z matematyki. Chcę mieć 5.0, a 4.5 - t « ‘c pisałem kiedyś do pana na temat książek Symfonia i Pasja. mieć przynajmniej średnią ocen. Nie dla ocen. Po to, że>< rozumieć i umieć. Po to, że napisał mi pan w listach t a k i e ż Odpisał m i pen dwa razy, przysyłając mi dwa piękne maile. Przeczytałem Symfonię, przeczytałem Pasję, a także które jak czytałem, to płakałem.
K
posłowie. Była tam wzmianka o tym, że trzeba być wiernym swoim marzeniom. Ja jestem wiem y, każdego dnia. Po pana
*
Jeśli szukasz nowoczesnego języka programowania, to jest to właśnie język C++. Jeżeli chcesz nauczyć się tego języka w łatwy, pogodny sposób - to jest to właśnie książka dla Ciebie. Książka ta powstała na podstawie doświadczeń autora w pracy programisty we francuskich i niemieckich instytutach fizyki jądrowej i nosi znamiona książek pisanych na Zachodzie: p ro s ty m , w rę c z p rz y ja c ie ls k im s ty le m w prow adza czytelnika w fascynujący świat program owania obiektowo orientowanego. ,•
•
*
--I
-1 -
'
Dodatkową zaletą książki jest to, że porusza zagadnienie n a jczę ście j p o m ijane przez innych autorów : jak stosow ać technikę obiektowo orientowaną przy projektowaniu własnego programu. Autor podjął się zadania karkołomnego - napisał książkę, która może być czytana zarówno przez zawodowego informatyka jak i przez programistę-amatora znającego tylko język BASIC Tego samego autora:
Pasja C + + Szablony, pojemniki i obstuga sytuacji wyjątkowych'% języku C+ + 2 Łomy, 610 stron
W
Y
D
A
W
N
I
C
T
W
O
£ D I T I 0 lii sIP
2000 NASZEJ PEŁNEJ OFERTY SZUKAJ W INTERNECIE: WWW.edition2000.COm.pl