145 73 31MB
Polish Pages [1326] Year 2006
AddNf1Mtr9(pR•sourc•->C•tsia()); li check to see iF any ouerallocation has taken place if( !Checkfot'Ouerallocation()) t'eturn false;
Stephen Prota
Języ Doskonały podręcznik dla początkujących programistów [>Typy danych i konstrukcje sterujące [> Programowanie proceduralne i obiektowe [> Biblioteka STL [> Obsługa plików
SAMS PUBLISHING Helion
IR
Tyn1ł oryginału: Sams C++ Primer Plus Tłumaczenie: Tomasz Żmijewski (wstęp. rozdz. Przemysław Szeremiota (rozdz.
9
I -
- 8), 12),
Tomasz Walczak (rozdz. 13 - 16), Przemysław Steć (rozdz. 17, dod. A
-
G).
ISBN: 83-7361-958-5 Authorized translation from the English languagc edition, entitełed: SAMS C++ PRIMER PLUS, Fifth Edition, ISBN 0672326973 by Stephen Prata. published by Pearson Education, Inc. publishing as Sams Publishing, Copyright
,e;; 2005 by Pearson Education. Inc.
Transłation copyright© 2006 by Wydawnictwo Helion. Polish language edition published by Wydawnictwo Helion. Copyright
it• 2006
All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, elcctronic or mcchanical. including photocopying, recording or by any information storage retrieval system, without pcrmission from Pearson Education, Inc. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabromone.
kserograficzną. fotograficzną. a także kopiowanie filmowym, magnetycznym lub innym powoduje naruszenie
Wykonywanie kopii metodą książki na nośniku
praw autorskich niniejszej publikacji. Wszystkie znaki występuj ące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorąjednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z
tym ewentualne
naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Chopina 6, 44-1 OO GLIWICE tel. (32) 231-22-19, (32) 230-98-63 e-mail: [email protected]
WWW: hllp:llhelion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres h11p://he/io11.pl/mer/opinie:'cpp1i111 Możesz tam wpisać swoje uwagi, spostrzeżenia. recenzję. Printed in Poland.
Dedykacja Moim kolegom i studentom z College of Marin. Praca z Wami jest przyjcmnoscią. - Stephen Prata
Podziękowania Podziękowania do wydania piątego Chciałbym podziękować Loretcie Yates i Songlin Qiu z wydawnictwa Sams Publishing za prowa dzenie tego projektu. Dziękuję mojemu koledze, Fredowi Schmittowi, za życzliwe porady. Jeszcze raz chcę podziękować Ronowi Liechty'emu z Mctrowerks za jego pomoc.
Podziękowania do wydania czwartego Nad tym projektem pracowało kilkoro redaktorów z wydawnictw Perason i Sams. Dziękuję Lin dzie Sharp, Karen Wachs i Laurie McGuire. Dziękuję też Michaelowi Maddoksowi. Billowi Crau nowi, Chrisowi Maunderowi i Phillipe'owi Bruno za redakcję techniczną i korektę. Dziękuję po nownie Michaelowi Maddoksowi i Billowi Craunowi za wszelkie materiały. W końcu chcę podzię kować Ronowi Liechty'emu z Metrowerks i Gregowi Comcau z Comeau Computing za ich pomoc związaną z obsługą kompilatorów C++.
Podziękowania do wydania trzeciego Chciałbym podziękować redaktorom wydawnictw Macmillan i The Waite Group za ich wkład w wydanie tej książki. Są nimi: Tracy Dunkelberger. Susan Walton i Andrei Rosenberg. Dzięku ję także Russowi Jacobsowi za redakcję techniczną i merytoryczną. Za pomoc i współpracę pra gnę podziękować pracującym w Metrowerks: Dave'owi Markowi, Aleksowi Harperowi, a szcze gólnie Ronowi Liechty'cmu.
Podziękowania do wydania drugiego Chciałbym podziękować Mitchellowi Waite'owi i Scottowi Calamarowi za pomoc przy drugim wydaniu książki oraz Joelowi Fugazzotto i Joanne Miller za doprowadzenie tego projektu do końca. Dziękuję Michaelowi Marcotty'emu z Metrowerks za udzielane mi wyjaśnienia dotyczą cego kompilatora CodeWarrior. Chcę też podziękować następującym nauczycielom za to, że po święcili swój czas na zaopiniowanie pierwszego wydania tej publikacji: Jeffowi Buckwalterowi, Earlowi Brynnerowi, Mike'owi Hollandowi, Andy'emu Yao, Larry'emu Sandersowi, Shahinowi Momtaziemu oraz Donowi Stephensowi. Chcę w końcu podziękować Heidi Brumbaugh za po moc w merytorycznej redakcji nowego i poprawionego materiału.
Podziękowania do wydania pierwszego Do powstania tej książki przyczy ni ło sic; wicie osób. Szczególnie pragnę podziękować Mitchowi Waitc'owi za pomoc w tworzeniu. kształtowaniu i modyfikowaniu tej pracy. a także za sprawdza nie rękopisu. Doceniam wkład pracy Harry'ego Hendersona w przegląd ostatnich kilku rozdziałów oraz testowanie programów na kompilatorze Zortech c+.... Dziękttję Davidowi Gerroldowi za przej rzenie całego rękopisu i uwzgl,dnienie potrzeb mniej doświadczonych czytelników. Dziękuje; też Hankowi Shiffmanowi za testowanie programów na kompilatorze Sun C++ oraz Kemowi William sowi za testowanie ich na AT &T cfront i G++. Dziękuję Nan Borreson z Borlanda za to, ze tak chętnie pomagała mi przy pracy z kompilatorami Turbo C++ i Borland C++. Dziękuję Ruth Mey ers i Christine Bush za to, że zajęły się całą masą papierowej pracy, nieuniknionej przy tego typu projektach. Dziękuję w km1cu Scottowi Calamarowi za dopilnowanie całości.
SPIS TREŚCI
WSTĘP ROZDZIAŁ
.. „.„„„„„„.„.„.„ ..„„.„.„.„„„„.„.„ .. „„.„„„„„.„„„„„„„„„„„„„„„„„„„„„„„„„„.
I Zaczynamy
„„„ „„„„„„ „„„„ „„„„„„„ „„„ „„„ „„„ „„„„„„„„„„„„„ „„„„
Nauka C++ - co nas czeka? Pochodzenie języ ka C++ - krótka historia
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
J ęzyk C
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
.„„„„„„„„.„„„.„.. „.„„.„„„„„.„„„„„„„„„„„.„„„„„ ..„„„„„.„„„„„„.„„„ ..„.
F il ozo fia programowania w C Zmiana w C++ - prog ramowanie obiektowe C ++ i programowanie og ólne Pochodzen ie C.,-+ Przenośność i standard y Mechan i ka tworzenia programu Pisanie kodu źródł owego Kompilacja i konsolidacja
„„„„„„„„„„„„„.„„„„„„„„„„„„„„„„„„„„„„„„„„„.
„„„„„„„„„„„„„„„„„ „„„„„„„„ „„„.
„„„ „„„„„„„„„ „„„„ „„ „„„„„„„„„„„„ „„„„ „„„„„„„.
. .„.„ ...„.„.„„ ..„„„„.„„„„„„„„„„.„„„„„„„.„„„„„„„ .. „.„„.„.„„.„.
„„.„„ „„„.„.„ „„„„„„„„„„„ „„„„ „„ „„„„„„„.„ .„„„„ „„.„ .„„.„ .
„„.. „„.„„„.„„„„„.„„„„„„„„.„„„„„„„.„„„„„.„„„„„„
.„..„.„.„ „„ „„„.„„„„ „„„„
„. „„„„.„„„„ „.„„„„ „„„.„..„ .. „..
„.„„„„„.„„.„„„.. „.„„„„„„„„„.„„„ ..„„„„.„„„.„.„„.„.„„
Podsumowanie
...„.„ ........... „...... „ ......„....„.......................„ ......... „ .......... „..........„ .........
ROZDZIAŁ 2
Pierwszy program w C++
C++ - początek
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
.„„„„„...„„.„„„„„„„„„„„.„„„„„.„„„„„„„„„ .. „„.„„„„„.„„„.. „„„.„. .
F u nkcj a ma in()
„„„. „„ „„„„.„„„„„. „.„„„ „.„„„.„„.„„„.„. „„..„„„..„.„„ „.„„. „„„„...
Komentarze w C++
„„„„„.„„„„„„„„.„.„„.„„„„„„„.„„„„„„„„„.„„„„„„.„„„.„„„„
Preprocesor i plik iostream N azwy plików nagłówkowych Przestr zenie nazw Wypisywanie danych - cout Formatowanie kodu źródłowego C ++ Instrukcje C++ . . Instrukcje deklaracji i zm i enne In stru kcj a przypisania . Nowa sztuczka z cout
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
„„„.„„„„„„„„„„„„„„„„„.. „„„.„„„„„„.„.„„.„„„„„.
...„......„ .. „ ..........„.......... „. .„ ......„... . ......„..........„ .............„.„ ......
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
.
„ ..„..„..„. .„..„
.........
„„„„„„„„„„„„„„„„„„„„„„ „„„„„„„„„ „„„„
„ ..„...„.„..........„.......... „..........„...... „ ................
.
...
„„„„„„„„„„„„„„„„„„„„„„„„.„„„„„„„„„„„.„„„„
.
.„..„„„..„.... „.„ ..„
.
..
„ ......... „..„..„..............„......„ .........„ .....
„„„„ „„„„ „„„„„„„„„„ „„„„„„„„„„„„ „„„„„„„„„„„„„„„„„.
Inne instrukcje C++
„.„.„„„„„„„„„„. .„„„„„„„„„„„„„„„„„„.„„„„„ „„.„„„„„„„.„„.„.
Użycie obiektu cin
„„„.„„„„.„„„.„„.„„„„„„.„„„„„„„„„„„„.„„„„„ ..„.„„.„„.„„„„.
Złączanie za pomocą cout
„„„.„„„„„„„„„„„„.„„„„.„„„„„„„„„„„„ „„„.„„„„„„„„
cin i cout - klasy po raz pierwszy Funkcje
..
.
......
.
...
.
„„„„„„„„„„„„„„„„„.„„ „„„. .„„„„ „„„„ „„„„„.
..........„ ..... „...............„..„„ ....„..„„„..„.. „„.„..„..„.. „„„ ..„..„ ......... „ ... „
Użycie funkcji zwracającej wartość Odmiany funkcji Funkcje definiowane przez u ży tkownika Funkcje uży tko wn i ka zwracające wartość
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
„„„„„.„„.„„„„„„„„„.„ ..„ . . „..„„„.. „„„„„„„„.. „.„..„ .. „„„ ..„„„„„.
„„„„„„„„„„„„„„.„„„„„„„„„„„„„„„„„„
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
Dyrektywa using w programac h z wieloma funkcjami
Pods umowanie
........... „.....„.........„..........„..........„..„„„..„„ ........„...„..... „... . . .
Pytania sprawdzające
..............
Ćwiczenia programistyczne
ROZDZIAŁ
„„„„„„„„„„„„„„„„„„„„„„
3 D an e
Zmienne proste
.
„ ...
. . .
. . . . . „ .....
„................„..........„„.„...„.............. „..„.. „..................
„
1
11 11 12
13 13 15
16 16 18 20 21 22 27 29 29 3I 34 35 36 37 38
41 43 43 45
46 47 48 48
49
51 51
55 55 59
61 63
. 63 .
.. „.„„. „. „. „ „ „ „.. „„„ „„ „„„.„„„ „„„„„„„. „„.„„„„„„„.
„„.„„„„„.„„„.„.„„„„„„„„„„„„„„„„„„„„„„„„„„„„.„„„„„.„„„ .
64
. 67
..........„............. „ ......„......................„..„.„...........„......... . . . . . .......„......„.
68
Nazwy zmiennych Typy całkowitoliczbowe Typy short, int i long Typy bez znaku Dobór właściwego typu Stałe całkowitoliczbowe Jak C++ ustala typ stałej? Typ char - znaki i małe liczby całkowite Typ danych bool Kwalifikator const Liczby zmiennoprzecinkowe Zapis liczb zmiennoprzecinkowych Zmiennoprzecinkowe typy danych Stałe zmiennoprzecinkowe Zalety i wady liczb zmiennoprzecinkowych Operatory arytmetyczne C++ Kolejność działań - priorytety operatorów i łączność Odmiany dzielenia Operator modulo Konwersje typów Podsumowanie Pytania sprawdzające Ćwiczenia programistyczne
.............. „....„..„„ ....„...„„..„.„..„.„ ..... „„....... „.............„....„......
70
„„„„ „„„„„„„„„„„„„.„„„„„„„„„„„.„„„„„„„„„„„„„„„„„„„
70
... „...„.. „.... „....„....„.... „....„... „„..„.„..... „.....„......„„ ..„...... „...........
75
.. „. .„....„....„...„„. .„.....„„....„..... „.„..........„ .... „............„.....
78
....„......... „.....„..........„.„..„„ ...„„..„„......„ ...........„............
79
„„„„„„„.„„„„„„„„„„„„„„„„„„„„„„„„„„.„„„„„„„„„„.
81
„.„.„„„„„„„„„„„„„„„„„„„„„„„ „„„„„.
81
.... „.... „..„......„...„.... „„ ..„.„ ..„„.. „„....„„„..„„ .. „„ ..„„....„... „..... „..
89
....„.„....„....„...„„...„„. .„„..............„. .„„. .„.„. .„..... „.........................
90
.......„..........„.„. .„„...„„...„ „.....„..... „....„....„ „..........„...... „„.„..„.„ ...„„ .....„....„„...„„ ..„„ .. „„.. „„.... „.. „..
91 92
.„.„.„...„„...„„..„.„.„..„„..„„....„... „...„„....„....„
93
....... „....„............ „„ ... „„.....„....„.....„„ ...„...„„.. „....„„ ...
96
„„„„„„„„„„„„„„„„„„„„„„.„„„„„„„.
96
..„....„...............„.....„.....„„....„„ ..„....„.„...„..........„........
97
„„„„„„„„„„„„„„„„„„„„„.
99
„......„„.„„.„„.. „.„...„...„„ ..„„..„.„ ..„.„....„„..„....„.„..„„.„„..„.
I OO
..„.............„.„„..„„.. „„.. „....... „....„„...„„...„„..„„..„„...„„.„....„ ..
I 02
... „.. „..„„..„..„ ........... „....„.....„„ ..„„„.„„ ...„...... „..„„.... „....„..„.
I 03
........„..„ ......„..„„ .. „„.. „„ ..„„...„„..„„...„.„....„ ....„„............... „....„......
I 09
„................„....„....„....„....„„.....„.„.. „„... „„...„....„....„....„....„.
1 1O
....„..„....„.... „„..„„ .....„.....„„ ...„.„ ..„„.„.....„„ ..„„..„„..„.
1 1O
ROZDZIAŁ 4 Typy złożone Tablice w skrócie Uwagi o programie lnicjalizacja tablic Łańcuchy Łączenie stałych łańcuchowych Łańcuchy w tablicy Problemy z wprowadzaniem łańcuchów znakowych Wczytywanie łańcuchów znakowych wierszami Mieszanie w danych wejściowych łańcuchów i liczb Klasa string - wprowadzenie Przypisanie, konkatenacja i dołączanie Inne operacje klasy string Klasa string a wejście i wyjście Struktury Użycie struktury w programie Czy w strukturze można użyć pola typu string? Inne cechy struktur Tablice struktur . Pola bitowe Unie
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
1 13
„„„„„„„„„.„„.„„„„„„„„„.„„„„„„„„„„„.„„„.„„„„„„„„„„„„„„ „„.
1 14
„„„„„.„„„„„„„.„„„„„„„.„„„„„„„„„„„„„„„„„„„„„„„.„„„„.
1 16
„„„ „„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
1 17
„...„. „„......„...„. „.... „....„..„„.„.„..„„....„.... „„ ..„........„.„„.„..„..........„„......
1 18
„„„„„„„„„„„„„„„„„„„„„„„„.„„„„„„„„„„„„„„
1 20
..... „...„„ .... „.„.„ ..„.„.„„..„„„... „..„.. „„ ...„„.„„...„„...„...„„...„
1 20
.„„„„„„„„„„„„„„„„„„„„„„
1 22
„„„„„„„„„„„„„„„„„„„„„„.„.„.
1 24
„„„„„.„„„„„„.„„„„„„„„„„
1 28
„„„ „„„„„„ „„„ „„„. „„„„„„„„„ „„.„„„.„„„ „„„ „„„„„„
1 30
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
131
„„„„„„„.„„„„„„„„„„.„„„„„„„„„„„„.„„„„„„„„„„„„„
1 33
„„„„„.„„„„„„.„.„„„„„„.„„„„„„„„„„„„„„„„„„„
135
.„ ...... „..„..„...„...„..„...„„..„„ .. „„..„.„ .. „„..... „.„... „„ ..„„.....„ .. „......„ ..„„....„
137
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„ „„„„„
1 39
„„„„„„„„„„„„„„„„„„„„„„„„„„
1 42
... „....„...„...„.... „„ .. „„....„„ .. „.„..... „„... „„...„„.„...................„
1 42
„......... „„ .. „...„„.„.„ .. „„...„„...„.„.....„„..„„„.„„.....„...........„ .......
1 44
.„ ..............„............. „............. „....„„....„„...„„„ ...„„ ..„„................ „.....
1 45
.„„ ..„..........„......„ ..„.. „„ ....„.„ .... „.„....„....„„..„.„ ..... „„...„„..„.„....„....„..........
VIII
68
.......„.... „..„„....„....„....„.„ .... „.......„.... „..............„„ ..„.......
„. 1 46
Typy wyliczeniowe Ustawianie wartości enumeratorów Zakresy wartości w typach wyliczeniowych Wskaźniki i różne drobiazgi Deklarowanie i i n icJ alizacja wskazników Niebezpieczeństwa związane ze wskaźn kami Wskaźniki i l iczby Użycie operatora new do alokowania pamięci Zwalnianie pamięci za pomocą delete Użycie new do tworzenia tablic dynamicznych Wskaźniki, tablice i a rytme tyka wskaźników Uwagi o programie Ws ka ź niki i łańcuchy Użycie new do tworzenia struktur dynam icznych Alokacja pamięci: automatycz n a , statyczna i dynami c zn a Podsumowanie Pytania sprawdzające Ćwiczenia prog ram ist yczne
...... „„„„.„„„„„.„„„„.„„„„„„„„„.„„„.„„„„„„„„„„„.„„.„„„.„„„.
„„.„.„„.„.„„„„„„„.„„„„.„„„.„„.„.„„„.„„„„.
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
„„„„„„„„„ „„„„ „„„„„„ „„„„„ „„„ „„„„„ „„ „„„
„ „„„„„„
„„„„„„„ „„„„„„„„„„„„„„„„„„„ „„„„ „
i
„„„„„„„„„„„„„„„„„„„„„„„„„„„
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
„„„„„„„„„„„„„„„„„„„„„„„„„„„.
„„„„„„„„„„„„.„„„„„„„„„„„„„„„„„„„„„
„„.„„„„„.„.„„„„„„„„„„„ .„„„„„.
„„.„„„„„„„„„„„„„„„„„„„„„„„.„„„„„
„.„.„„„.„„.„„„„„„„„„„„„„„„„„„„„„„„„.„„.„„„.„ ..„.„„.„.„.
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.„„
„„„„„„„„„„„„„„„„„„„„„„„„.
.. „. „„
„„„„„„„„„„„„.„„„„„.
„ „ .... „ .„„„. „„„.„. „„„„. „.„ . „.„„„... „„. „.„„.„.„. „ „„ „.„„.„. „..„..„
„.„„.„„„„„„„„.„„.„„„.„„„„„„„„„„„.„„„„„„„.„.„„.„„.„ ... „„ ..
„.„„„ ..„.„„„„„„„„„„„„„„„.„„„„„„„„„„„.„„.„„„.. „.„.„
ROZDZIAŁ 5 Pętle i wyrażenia relacyjne Pętle for Elementy pętli for Wr acamy do pętli for Zmiana wielkości kroku Pętla for i łańcuchy znakowe Operatory inkrementacji (++) i dekrementacji (--) Efekty uboczne i punk ty odn i es i enia Formy przedrostkowe a fonny przyrostkowe Operatory inkrementacji i dekrementacji a wskaźniki Złożone operatory przypisania Instrukcje złożone czy li bloki Przecinek jako operator (i pewne sztuczki składniowe) Wyrażenia relacyjne Bardzo typowy błąd Porównywanie łańcuchów w stylu C Porównywanie łańcuchów klasy string Pętla while Uwagi o programie Pętla for a pętla while Chwileczkę - tworzymy pętlę opóźnienia Pętla do w hile Pętle i wprowadzanie danych tekstowych Najprostsza wer sja cin cin.get(char) na odsiecz Która cin.get()?
„„„„„ „„„„„„„„„„ „„„„„„„„„„ „„„„„ „„.
„„.„. „ „„ ..„ „„ „ „.„.„„ „ „„ .. „. „„ „„„„ „„ .„ „ „„ „„ „ „ „„„ .„„ „„„„„„ „„.„„„„„„ „
„„„„„„„„.„„„„„„.„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
„„„„ „„„„„„„„„„„„„„„„„„„„„ „„„„„„„„.„„„„„„„„„„„„ „.
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
„„„„„„ „„„„„ „„„„„„„.„„„„„„„„„„. „„„„„„„„„„„„
1 50 15 1 154
157 1 57 1 58 160 161 1 64 166 170
1 75 17 9 181
l 82 183
1 85 186 1 87 I 94 195 1 96 1 97
„„„„„„„„„„„„„„„„„„„„.„„„„„„„„„„„„„„.
199
„„.„„„„„„„„„„„„„„„.„„„
.„„„„„„„„.. „. „„ „„„„„„.„. „„ „„„„„„„„„„„. „„ „„
„
„.„„„„„.„„„„„„„„„„„„„„„„„„„„„„„„„.„„„„„„„„
„„„„„„„„„„„„„„„„„„„. .
......„ .............„ ..„ ..... „...„„„..„.„.„ ....„„ .. „.......„.„ ...„„...„ ..... „.. „.
„ „....„„. „
1 50
„„„„„„„„„„„„„„„„„„„„„„„„
„„ „„ .• „„„„„„„„„„„„„„„„„„„„ „„„.
.
148
.. „.„„ „„„.„„ .. „.„. „„ „„. „„„ „ „ .„. „„.„ ..„.„„.
„ „„.„„.
„„„.„.„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
„„„ „„„„ „„ „„ „„.„ „„„ „„„ „„ „„„„„„ „„ „„.
„„„. „„ „.„„ „„ „. „ •.„.„„ „„„„„„ „„„.„„„. „„.„.. „„„. „„.„„„„ „„
.
„. „... „ .„.„„„
„„„„„„„„„„„„„„.„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
199 200 201 202
204 207 208
21 1 213 215 216
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
217
„„„„„„„„„„„„„„„.„„„„„„„„„„„„„„.
219
.„„. „„ ....„. „....„„„„.„.„„„„„„„„„ ..„.„„„.„„„ „„„.„„„„„„„„„„„.„„„„ „
„„„.„„„„„ „„„„ „„„„„„„.„„„„ „„ „„„„„„„„
„„„„„.„„.„„.„„„„„„„„„.„„„„„„.„„„„„.„.„„.„„.„„„„.„„
.
.
.„„„„„„.„„„„„„„„„.„.„.„„„„„„..„„„„„„„„„.„„„.„.„.„„.
.......„.„.........„. .„ . „. . .„.....„..„ .. „. .„.„„„ .. „ ..„ ...„.„ .. „ . „. .„. . .„ . „.. .„
.
.
221 2 24
224 225 227
IX
Koniec pliku Jeszcze inna wersja cin.get() Pętle zagnieżdżone i dwuwymiarowe tablice Inicjalizacja tablic dwuwymiarowych Podsumowanie Pytania sprawdzające Ćwiczenia programistyczne
............ ..... . . ..... . . . ..... .............................................. . . . . ................... ..... .
227
...„ .....„.„ .......„ ......„ ......... „„ ...„„.„ ..„ „ „ „ . „ „ „ „ . . ......
23 I
..........„ . „ „ „ ...... „ „ „ .„ ..„ „ „ „.„ .........„.„
.. 234
„ „ „ „ „ „ . „ „ .. „. „„„ ....„ „ „ ....... „.„ ..„.„„ ..„ „ „ .
236
....................„ ......„ ........„ .........................................„ .......... ..... . ...........
238
„ ...................... „ ........„ ......„ .......... „.„ .......„ .......„ ....... „ . . . . .. ... . .. .
239
„ „ .....„ ... „. „„„ ......„.„„ „„ .. „ ..„ ..... „ ..„ ..... „ . „ „ . „ „ .. „ „„ . . „.
230
ROZDZIAŁ 6 Instrukcje warunkowe i operatory logiczne „ „ „ „ „ „ .....„ ....„ „ .... „„„.„ 243
Instrukcja if Instrukcja if else Fonnatowanie instrukcji if else Konstrukcja if else if else Wyrażenia logiczne Logiczny operator alternatywy li Logiczny operator koniunkcji && Operator negacji logicznej ! O operatorach logicznych Zapis alternatywny Biblioteka cctype Operator ?: Instrukcja switch Użycie enumeratorów jako etykiet switch versus if else Instrukcje break i continue Uwagi o programie Pętle wczytywania liczb Uwagi o programie Proste wejście-wyjście z pliku Tekstowe wejście-wyjście i pliki tekstowe„ Zapis do pliku tekstowego Odczyt danych z pliku tekstowego Podsumowanie Pytania sprawdzające Ćwiczenia programistyczne
. . . . . ... „.„ ...„ „ ... „ .. „ .........„„„ ...„ „ ...„ ..„ ..„ ......„ ...........„ „ . „ „ . „ „ . „ . „ . „ . „ . „ „ .„„.„„.„ „ „ „ „ „ „ . „ „ „ „ . „ .„.„„.„.„ „ „ „ „ „ . „ „ „ „ „ „ . „ „ . „ „ „ „ „ . „ „ .. „„.
243 245
„.„„.„„.„„.„.„ „ „ „ „ „ . „ „ „ .„ ..„ „ „ .... „.„ ...„ ..„ ... „„ ..
247
...„ ....„.„ ......„ ..........„ ..„ ..„...„ ........„ „ ......„ „ . „ . „ „ „ . „ „ „ .
248
..„ ........................................„ „ ..........„ ........„ ................„..............
250
-
-
. „ ... „ .....„ .......„ „ „ ........„ ....„ „ ......„.„ ..„ „ „ . . . . „
250
„ . „ „ „ „ „ „ „ „ „ „ .„ „ „ „ „ „ „ „ . „ „ „ „ „ „ „ „ „ „ „ „ „ .
252
„ „ „ „ „ „ „ „ „ „ „ ..„ „ „ „ „ . „ „ „ „ .„ „ „ „ „ „ „ „ „ „ „ „ ..„ ....„
257
..„ ...„ „ . „ „ „ . „ „ . „ „ „ ..„.„.„„.„.„„.„.„ „ „ „ . „ „ . „ „ . „ .. „ „ „ . . „
259
-
„ ...............................„ ........... „ ................„ „ ......„ ....„ . . . . ..... . . ....
....„ .......... „ ..„ .....„ .....„ ....„ ... „„.......„ „ „ .........„ ......„ ......„ ............. ....
260 26 1
„ ........... „ ..„ ....„ .... „ „ ...„ ..„ ..... „„ .... „„ ........ „ „ ......„ „ „ .............„ ............ .... . .
263
............................... „ ...........„ .............................„ .....„ ................„ ....
265
.„„„.„.„„.„ ..„ „ „ „ „ . „ „ „ „ .. „.„„ ...„ ..„ . „ „ „ „ „ „.„
269
„ ..„ „ „ .„ „ „„ „ „ . „ ..„ ..„ „ „ „ „ „ „ . „ „ „ „ . „ „ „ . „ „ .„ „ „ „ . „ „ „ . „ „ „ „ „ „
270
„ ..„ ....„ ...„„„ .....„„.„.„.„ ...„ „ „ „ „ „ . „ „ .„ „ „ „ „ „ .„ „ „ „ „ „ „ „ „
270
„ ......„ . .„ .......„ ................„ .......„ . „ ......„ .........„ ..............„ .........
272
......„ .......................................„ ......„ . „ .........„ ....„ .....„ „ .........
273
. . .„ .........„ „ .„ „ ......„ . „ „ „ „ „ „ . .„ „ .„ . .„ . „ „ . „ . „ „ „ „ „ „ ........... „ ......
277
„ . „ „ „ „ . „ „ „ „ „ „ „ „ „ ..„ „ „ .....„.„„.„.„„.„.„ „ „ . „ „ „ „ „ „ „ .„
277
......„ „ „ „ .„ . „ „ „ ..„.„„.„ „ „ „ „ „ „ .„ „ ....„ .
277
„ ....„ „ . „ „ „ „ .....„ „ . .„.„.„ .......„.„ ......„ „ „ ....„ „ ....„ ... . .„ „ .
279
„„„„„.„„„.„„„„„...„ „ .„ . „ „ . „ „ „ . „ ..„ „ „ „ .„„ „ . „
283
„ ......„ „ . „ „ . .„ ....„ „ .....„ .........„ ...... „.„ ...„ ...........„ .........„ ................... . . ..
288
......„ ....„ ....„ „ ...... „.„ ...„ „ ........„ „ ........ „ „ ........„ ......„ ........„ ....„ .
289
....„ .....„ .......„ ...........„ ......„ ..........„ .........................„ .......
ROZDZIAŁ 7 Funkcje - składniki programów w C++ Funkcje w skrócie Definiowanie funkcji Prototypowanie i wywoływanie funkcji Parametry funkcji i przekazywanie przez wartość Wiele parametrów Jeszcze jedna funkcja dwuargumentowa Funkcje i tablice Jak wskaźniki umożliwiają tworzenie funkcji przetwarzających tablice? Skutki użycia tablic jako parametrów
29 1
....„ . „ „ „ „ .....„ .. „.„.„„.„.„„.
295
.........„ ....„ ...„ ............................„ ............................. „ ................„ ....
295
.................... „..........„ ............................„ ..............................
296
.„.„„„ ......„ ..„ ......„ „ „ ......„ ..... „„ .... „„ .... „.
299
......„ ......... „.„ ..„ ...„ .. „ „ „ . „ „ „ „ „ „ „
302
...........„ .....„ .......„ ....„.„ .......„„ .......„.„ ........„ ................ „ ..........
304
„.„ „ ..„ „ „ .„ .......„ „ „ ............„ ..„ ....„ .....„ ..
306
...„ .......................... „ ................„ ............„ ........„ „ ..........„ ....„ ........ .. . .
309
.„ ..„ ......
...„ „ „ ..... „.„ ...„ „ „ „ „ „ „ „ „ „ „.„.„„ „ . „ „ „ „ . „ „
31 O 31 1
Dodatkowe przykłady funkcji i tablic . 314 Funkcje korzystające z zakresów tab lic .................................................................. 320 Wskaźniki i modyfikator const 322 Funkcje i tablice dwuwymiarowe 325 Funkcje i łańcuchy w stylu C 327 Funkcje z łańcuchami w stylu C jako parametrami 327 Funkcje zwracające łań c uchy w formacie C 329 Funkcje i struktury .. 331 PrLekazywanie i zwracanie struktur „.„„„..„ 332 Inny przykład użycia funkcji i struktur „„„.„„„.„. 334 Przekazywanie adresu struktury 339 Funkcje i obiekty klasy string 341 Rek ure ncj a . 343 Rekurencja w pojedynczym wywołaniu „..„.„.„„....„„„„„„„.„„ . „.„„„.„..„.„„.„„ 343 Rekur enc ja wielu wywołaniach ..„„„ 345 Wskaźniki na funkcje 347 Wskaźniki na funkcje podstawy 347 Przykład użycia wskaźników na funkcje ... „.„„.„„. 350 Podsumowanie 351 Pytania sprawdzające ...„„.„.„.„. 352 Ćwiczenia programistyc zne 353 ROZDZIAŁ 8 Funkcje - zagadnienia zaawansowane .. „„„„„.„„„ 357 Funkcje inline 357 Zmienne referencyjne 361 Tworzenie zmiennej referencyjnej .. „ „„.„ 361 Referencje jako parametry funkcji 364 Właściwości referencji 368 Ostrożnie ze zwracaniem referencji „„„„„. 375 Użycie referencji z obiektami 376 Obiekty po raz wtóry - obiekty. dziedziczenie i referencje„„„„„.„„„„„„„„„.„„ 380 Kiedy korzystać z referencji jako parametrów? 383 Parametry domyślne 384 U w a gi o programie 387 Przeciążanie funkcji .. „„„.„„ 387 PrLyklad przeciążania funkcji 390 Kiedy korzystać z przeciążania funkcji? „ „ „ .. „ 392 Szablony funkcji . 393 Przeciążone szablony 397 Specjalizacje j awne „ „ „ 399 Tworzenie egzemplarzy i specj alizacj e 404 Którą wersję funkcji kompilator wybierze? „„.„„„ 406 Podsumowanie. „ ...„„„ 412 Pytania sprawdzające 413 Ćwiczenia programistyc�ne ...„ . 414 „..
. . „.. „.„„............„.„ ...........................„.... .
.... . . ................. . . .. . . ...... . . . . .......................... . . . . . . . .. . . . . .
.„.„ ..„ ..... „.„„.„.„„„„ ..„„„„„.„„„ .. „„„„„„„„„„„„„
„ .. „.„.„„.„„.„.„„.„„„„„„.„.„„„ ..„„„„„„„ ..„„„„„.„. .. ...
.....„„„..„ ..... „„„„ ....„ .. „.„„„„
„„„„...„„„„„„.„...„.„.„„ ...„„„.„„„„.
.„ .. „„„..„.„...„„„ .. „„„ ..„„...„.„„.„„...„.„.„.„.„„.„.„„„..„„.„„„.„
„.„ ..„„„......
..„„.„„„„„.„„„„„.„.„.„„
„„.„„„.„„.„.„.„„..„.„„.„.„„„...
.„.„„„„.„„„„.„„„.„„.„.„„.„.„....„„„„„.„„„.„„ .. „
„.„.„„„„„„.....„.„„„„„....„„„.„„ ..„„„„„„„.........„.„...„
.............„.............. „......„...... „ .... . .... „
. „..............„ ............ . . ...„ ..... ... . .....
„ ...
.
w
.„..„„.„.„.......„„.„„...„ ..„„„„„„.„„.„„.„.„
„ .. „.„.„....„.„„„.„„„„„.„.„„.. „.„„..„.„.„„.„„„..„.„„„„„„..„ .. „„
-
.„.„.„.„„„„„„.„.„.„„„„„.„.. „„.„.„ .
. . „ ..„.„„„„
.„„„ ..„„ ..„..„„.„„„.„.. „.„„.„.„
. .... . . „.........„ ... „................................„.......„.........„...„ .. „.„ . . . . . . . . . . „ . . .....
.„„.„.„.„.„.„„.... „.„„.„.„.„.„„„„„„„„ ..„„„„„.„„.„.„
......„„„.„ ..„„„„„ ..„„„„.„„„ ...„.....„.„„„...„„„.„ ...... „„.„.
.„.„.„„„„„„„„.„
.„.„„.„..„.„.. „„.„„„„„„.... „„„ ....„„.„. .„„.„.„...„„„„„„.. „„„ ..„„.„.„„.„.
„„„„ .. „„„„.„.„„„.„„.„„„„„„.„.„„„„..„„„„„„„„„.„.„„„„.„.„„.
„ ..„„„„.„..„.„„„.„.„„„.„ . . „ .. „.„
.. . „.„ .. .
. .
„„...„„..„.„„„..„„„.„.„„„„.„.„„„„„„„.„„.„.„.„
„„„..„„.„„„„„...„„...„.„.„..„„„.„„„„......„..„ .. „.„......„.....„ „„„.„. .„„„„........„„„„.„.... „.„„.„„.„ . ..
„..„.....„.„„„.„„„.„„.„„„.....„„....„.„„„.„.„.. „„„.. „ ..
„„.„„„„„.„...„ . „„.„...„ .. „„„.„.„.„
.... .... .....
. . ..... .....„ ..„...„.....„...„ ..........„... „ . . „„ ....„.„......„„„...„ ....
„„„ . . .„„...„„.„.. „.„...„..„„„.„ ..„.„„..„..„„.„..„.„.„„........„.„„. .
.. „ .. „...„.„„„..„„.„.„„„......„... „..... „...„„„.„.„.„„„„.„.„„
„„„.„„.„.„„„.„.„„„.„...„....„„.......„„„„.........„„„„. .„ .
.
. „„
. „„
..„.„„.„„„„„„.„„„„.„„„.
. . . „„ .„... „.„...................„.....„ .......... „...„......„ .....................„ . . . ... . . . .. .
„.„.„ .. „...„ .. „.„.. „„.„..„..„„„ ..„„.„.„„.„„„.„„.„.„„.„„... „.„„
„
„.
.. „„„..„„.„.„.„.„„„„„„„„„„.„„„.„„.„„.„..„.„ ..„ ...„„ .. „
„....„„.„.„.„„.„„.„„„..„„.„„.„.„.„...„„„„ .. „... „„.„„„„„.... „.„. . . ..
„„„ ... „. „„ „.„„..„„„. „..
..„.„„„.„„..„„.„..„.„
...
.....„.„„„
. . „„ „. . „„„. „. „ „ „.
...„.„..„.„.„ .. „„.„„„„.„.„„„ .. „...„..„..„.„„.„„„„ .... „.„..„„„„„„„
„„„..„.... „.„„.„...„.„
..
...„„„„„.„.. „.„„„„„„ ..„.„„.„
..„
XI
ROZDZIAŁ 9 Model pamięci i przestrzenie nazw Kompilacja rozłączna Czas życia, zasięg i łączenie Zasięg i łączenie Przydział automatyczny Zmienne statyczne Specyfikatory i kwalifikatory Łączenie a funkcje Łączenie językowe Kategorie przydziału a przydział dynamiczny Miejscowa wersja operatora new O programie Przestrzenie nazw Tradycyjne przestrzenie nazw języka C++ Nowe mechanizmy przestrzeni nazw Przestrzenie nazw - przykład Przyszłość przestrzeni nazw Podsumowanie Pytania sprawdzające Ćwiczenia programistyczne
...„.„ .....„„„ .....„..........................
417
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
424
..„...„..„„.„.„..„„.„„„„„„.„.„.„..„„„ ..„„..„.„„....„„ ... „„.„„..„.....
425
.„„..• „...„. .„„„..•„.„. .„ „.•.„ „. •„. .„.„. .„ „....„ „. .„....„ „.......„
425
„. .„ „. .„....„ „. .„ „. .„ „. .„„„. .„ „....„. .„.„„„. .„ „.....„......„ „............
43 1
..„„..„„ ..„„.....„.....„.„ ...„„...„.......„.„.... „...„„ ... „.. „.
443
„...„..„„..„.„.„„.....„.„.„„...„„....„„......„„...„....„„...„......„..„„.
445
.„.„„„..„„„.„.„..„„..„„„ ...„.„..„„„ ..„„..„.„„...„„..„„.... „„ ....„..
446
„„„„„„„„„„„„„„„„„„„„„„„„„„„.
447
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
448
.„„.„. .„.„• . .„ „ „..„ „..„„„„.„„„„„.„„...„.„. .„„„„„„.„„.....„....„....„.... „
45 1
.„..„„„ ..„.„........„„.„„..„..„.„...„„..„.„„...„„.„„.„..„„....„....„..„„..„
452
„„„„„„„„.„„„„.„„„„„„.„„„„„„„„„„„
452
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
454
„„„„„„.„„„„„„„„„.„„„„„„.„„„„„„„„„„„„„„„„„
463
.„.„ ..„„..„....„.„.„.„.„..„„„..„.„.„„„...„ ....„ .... „.... „....
468
.„..„.„„„„.„...„..„„.„„...„„....„...„„.„.„„„„„„.„„„..„„ ..„„„„„....„..„„..
468
„..„.„„..„„„„„.„„...„.„„.„ ..„„..„.„„„.„„„.„„„„.„..„„....„..„„.. „
469
...„„.„„..„„..„.„..„„..„„„.•..„„...„„... „.„.„„ .. „„....„..........
4 73
ROZDZIAŁ 10 Obiekty i klasy
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„
Programowanie proceduralne a programowanie obiektowe Klasy a abstrakcje Czym jest typ? Klasy w języku C++ Implementowanie metod klas Stosowanie klas Podsumowanie poznanych wiadomości Konstruktory i destruktory Deklarowanie i definiowanie konstruktorów Stosowanie konstruktorów Konstruktory domyślne Destruktory Ulepszenia klasy Stock Konstruktory i destruktory - podsumowanie Tożsamość obiektu - wskaźnik this Tablice obiektów Jeszcze o interfejsach i implementacji Zasięg klasy Stałe zasięgu klasy Abstrakcyjne typy danych Podsumowanie Pytania sprawdzające Ćwiczenia programistyczne
Xll
417
„....„..„„ .. „„.„.„..„„ ....„...„„...„.„.„ ... „„ .....„.........„.„„ .......... „.
477
„„„„„„„.„„„„„„„„„„„„.
478
.......„....„.„ .... „....„....„....„„...„ „ ...„ „. . .„„„„.„ .....„ „. .„....„......„......
479
. .„. .„.„....„...„„.„.„. .„ „..•„ „. .„ „...„ „......„ „. .„ „..• „..... „.... „.... „.... „„ .
4 79
„. .„...„.„„..„ „....„..„.„.„. .„ „....„ „. .„ „.....„.„. .„ „. .„ „.... „....... . .
480
„. .„ „.••„ „. •„ „••..„„„. .„„„.„„„.„„. .„.„.„.„....„ ..... „....
485
„.„.„„.„„. .„„„„. .„.„.„„„.„„. .„„„. .„„„...„„„„„„. .„„• .„ „. .„„.„„ ....
49 1
.„„„„„„„„„„„„„„.„„„.„„„„„„.„„„.„„„„
495
...„„„„ ..„„..„„..„„....„„...„„...„„„•.„.„•..„„„.„....„.... „.••„..
496
„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
497
„..„.„„.....„ .....„„..„„..„„„..„„....„„„.„.„..„„ ..„„....„...„
499
„„.„„.„.„.„.„.„.„.„„„.„„„..„„..„.„.„.„„..„„„.„„.... „...„....
500
.„„.„„„.••• „„ „. .„„.„ „. .„ „. . „.„.„„„.„ .„. .„ .„. .„.„„„.„ „. •„„„.„„.„ .„. .„ „. .
50 1
„.„„.„„.„„..„„...„„ ..„„...„„..„..„„..„.„ ..„„ ..„..... „...„„ ...„„
502
„.„„„„„„„„„„„ „„„„„„„„„„„„„„„
511
„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„„.
512
.„„.•„••„ „.„•...„.„„..„„ ..„„..„.„....„.„...„„ .•.„„.„..„....„....„.„.„.„..„....
519
„„.„„„.„„.„„„„„„„„„„„„„„„„„„„„„„„„„„„„
522
„„.„„„.„„. .„. .„ „. .„„. .„ „. .„ „. .„„„.„„„.„„„„. .„ „. .„„„„„„.„„...„ „. .„....
524
.„. .„....„.„ .......„ „. .„ „. .„ „.....„.„. .„„. .„ „...„ „.......„ ...„.....„ „. .„ „. .„. .
525
„.„„. .„...„ „. .„ „. .„„„.„„••...„„„.„.„. .„ „...„„.„„„. .„ „. .„„.„...
527
„„.„...„.„„.„„. .„„.„....„•..„ „••.„.„•...„...„ „...„.„ ...„ „....„ „. .„ „.... „. .„ „....
533
„„„„. .„.„.„.„„. •„ „.•...„ „. .„.„. .„„„.„„„.„„„. .„„„.„.„. .„.„.„„. .„..
534
„.„„• •„ „• •„ „. .„„„„„„„. .„„„„„„. .„„„•.•„.„.„.„. .„ „. .„...„.„
534
ROZDZIAŁ 11 Stosowanie klas . .... . . . .. . .. . . . . . . . ... . . . . . . . .. . . . .. . . 539 Przeciążanie operatorów . .. . . . .. 540 Raz, dwa. trzy - próba przeciążenia operatora .................................... ...................... 54 1 Dodatkowy operator dodawania ............................................................................. 545 Ograniczeni a przeciążania operatorów . . . . . . . .. . 549 Jeszcze o przeciążaniu operatorów . . ... .. . .. . . . . . .. . . .. . 551 Przyjaciele najważniejsi ............. .................................................................................. 554 Deklarowanie przyjaźni .......................................................................................... 556 Typowa przyjaźń - przeciążanie ope ratora obiektu cin „widzi" dane wejści owe
I 052 I 055
..............................
I 056
.......................................
1 075
Fonnatowanie danych wyjściowych z a pomocą obiektu cout Realizacja operacj i wejścia z wykorzystaniem obiektu cin Stany strumienia
1 047
.....................................
Real izacja operacj i wyjścia z wykorzystaniem obiektu cout
..........................................
I 077
...................................................................................................
I 079
Inne metody klasy i stream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 084 Pozostałe metody klasy istream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I 093 Wejście-wyjście pl ikowe
............................................... ..............................................
Proste operacje wej ścia-wyjścia plikowego Kontrola strumienia i metoda is_open() Otwieranie wielu plików
..........................................................
1 098
I 098
..............................................................
1 1 02
......................................................................................
1 1 03
Przetwarzanie argumentów wiersza polecenia
....................................................
1 1 04
Tryby otwarcia pliku . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 07 Dostęp swobodny
..................................................................................................
Formatowanie wewnętrzne
XVI
........................................................................................
1 1 18 1 1 28
I co dalej?
. . . . . . . . . „ ......................„ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . ..
1 1 30
Podsumowanie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 3 1 Pytania sprawdzające
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . „.„ ..... „ .. „ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ... . . . . . . . .
Ćwiczenia programistyczne
„. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 1 33
„ . „„ ....„ ......„ ..„ . . . . . . . . . . . . . . . . . . . . . 1 1 34
DODATEK A Systemy l iczbowe . „ ...................................„ .....„ ..„ .....„ . . . . . . . . . . . . . . . . . . . . . 1 1 3 9 Liczby dziesiętne ( o podstawie 1 0)
„ „........................... „„„.„. .„.„„ ...„. .„ ......... „ ...„ „
Liczby całkowite ósemkowe (o podstawie 8) Liczby szesnastkowe
„„„„„„„„ ...........„„.„.„„ .. „ ... „ .. „ ... „ . . . .
„ „ „. „„ „.„ „ „ „ „ ............................ „ . . . . .
Liczby dwójkowe (o podstawie 2)
„„„ ......... „ . . . . . . . . .
1 1 39 1 1 39
„ . . . . . . . . . . . . . . . . . . „„„ „„ ....„.„ 1 1 40
„ .„..„ „..„. . .„ „............„„„„„„ „„„ 1 1 4 1
Zapis dwójkowy a szesnastkowy „ „..„ „ . . . ....„.....„ .........„„„„„„„„.„..„ ..„ .......... „ ..„ . . 1 1 4 1
DODATEK B Słowa zastrzeżone j ęzyka C++ . . . . . . . . . . . . . . . „ ..„„ ...„ ..........„„„„„ „ „ „„„ 1 1 45 Słowa kluczowe języka C++ „„ . . „.„.„„. .„ .....................„„„„„„.„.„ . . „ .....„...„. .„.„. .„ 1 1 45 Leksemy alternatywne „„„„.„„„„„. .„ „ .....„.„.„. .„.„„„„ ...........„ ........„..............„ . . . . . . 1 1 46 Nazwy zastrzeżone bibliotek języka C++
„„„„.„
. . . . . „ . . „„„.„„„„„.„„„„„. . .„ „ . „. . .„„. 1 1 46
DODATEK C Zestaw znaków ASCII „ „ „.....„ „..„.„„„ ........................„ „ ....„ . „. . . . . . . . 1 1 49 DODATEK D Priorytety operatorów
. . . . „ ........„ ..........„.„„„„„ ....„ . .
„ . . . . . . . . „ „ . „ „ „. .„ 1 1 5 5
DODATEK E Inne operatory . . . „ .......„„.„„„„„„„„„..„ . . . . . . . . . . . „ „ . . „„ ..„„.„ . . . . . . . . . . . . . . . 1 1 5 9 Operatory bitowe
. . . . . „„„„„„„„.„ ....„„„„„ ..„„.„„„„„„„ ................„„„„ . . . . . . . . . . . . . . . . . . . . .
Operatory przesunięcia
. . . . . . . . . . . . „„.„ .......................... „„.„„ ...„ .... „ ......„.„ .. „„.„.„.
Bitowe operatory logiczne
„„.„„„ ...... „....„„„„„„.„„
Alternatywne reprezentacje operatorów bitowych
1 1 59 1 1 59
. . . . . . . . . . . „ . . . . „ ...„ „...........„ . . . . . I 1 6 1
„ ..„„ ...„„„„„„ „„ „ .........„ ... „ . .
1 1 64
Kilka typowych technik wykorzystujących operatory bitowe . . . . . . . . . . . . „ „„ ..„ ..„ . . . . 1 1 64 Operatory dereferencj i składowych „ „ „„„„„„„ . . „ „ „ . . „ . . . . . . . . . . . . . . „ . . . . . „ „ . . . . „ ...... „ . . . . . . . 1 1 66
DODATEK F Klasa szablonowa string „„„„„„„„„„„„.........„ . . . „ „ „ . „ „ „ . . . . . . . . . . . . . . . . . 1 1 7 1 Trzynaście typów i stała .„„.„ . . „ . . . . . . „.„„„.„.„„„.„„....„„„„„„„„„„„„„„„.........„ . . . . . . 1 1 72 Informacje o danych, konstruktory i różne drobiazgi . „ „ „ „ „ „ „ „ „ „ . . . . . . . . „ .„„ „„„.„„. 1 1 73 Konstruktory domyślne . . „ . . „.„„„ ...„ ............ „„.. „.„„„ .......................„.„„„ ..„.„„ 1 1 75 Konstruktory wykorzystujące tablice . . . „„„.„„„„„„..„„..„„„.„„„„„„ . . . . . . . . . . . . . . . . . . . 1 1 7 5 Konstruktory wykorzystujące fragment tablicy . . . . . . . . . . . . . . . . . . . . . „ . „ „ „ „ „ . . „ . . . . . . . . . . . . . . 1 1 76 Konstruktory kopiujące
„.„„„„„„.„„„„„„„„„„„„. .„ „. . „„„„..............„„„„„.„ . . . . .
1 1 76
Konstruktory wykorzystujące n kopii znaku „.. „„.„„„„ . . „ „ „ „ . . „ ....... „„„„ „„ „„„ 1 1 77 Konstruktory wykorzystujące zakres
. . „ ................„ ...................... „„„ . . . . . . . . . . . . . . . ...
1 1 78
Metody zarządzające pamięcią „ „ „ . . . . . . . . . . . . . „„„„„„.....„„„„„„„„„„„...„ ..„„„„„„ 1 1 78 Dostęp do łańcucha Proste przypisanie
. . . . . . . . . . „ „ „„ „ „„
„ „ .......... „„„„„„„„ „ . . „.„ „„„„„ ..„„„ „..„„„„ ..„ „. 1 1 79
„ ..„„„„„„„ ..„ ...... „„„„„„„„„ .....„ .... „.„ ..„ ..„ „ ............„ „ . „ „ . . . . . . . . .
Przeszukiwanie łańcuchów Rodzina funkcj i find()
„. .„ . .„ ...... „„ „„. .„„. .„.„ .... . . „„ ...„ ................ „. .„ ... „. „ . . . . . .
. . . . . „ .....................„ ...„„„„„„ ..........„„„„„„.„„ . . . .
1 1 80 1 1 80
„ . . . . . . „. .„ 1 1 80
Rodzina funkcj i rfind() „ „ . „ . . „ ....„ „„„ „ „..„ .............„„„.....................„ . . . . . . . . . . . . . . . . 1 1 8 1 Rodzina funkcj i find_first_of() „ „ . . . . . . . . . „„„„„ „„„„„„„„„„„ „„„ . . „ . . „ .„„„„„„„ . . . 1 1 82 Rodzina funkcji find_last_of() . . . . . „ „ „ „ . . „ . . . „ . . „ . . „ „ . . . . . . . . . . „ „ . . „„„„ „ ........„„ .. „„. 1 1 82 Rodzina funkcj i find_first_not_of() Rodzina funkcj i find_last_not_of()
. . . . . . „„„„„„. . .„ „ „..„„„„„.„ ....„ . . . „„.„„„.„ ..........„.„ . . . . . . . . . . . . . . .
„ „ „ .. „ „ . . . . . . 1 1 83
„ „ „ . „ . . . „ . „ . . . „„ .. 1 1 83
XVll
Metody i funkcje porównania .„ . . . . . . . „ . . . . . . . . . . . . . . . . . . . . „ . . . . . . . . . . . . . . . . . . . . . . . . . . . „ . . . . . . . . . . . . . . . . . . . . . . 1 1 84 Modyfikatory łańcuchów
.......
..................................................................................... 1 1 85
Metody dołączania i dodawania Inne metody przypisania Metody wstawiania Metody usuwania
...........
.........
Metody zastępowania
...
........................................................................ 1 1 85
...................
................................................................... 1 1 86
.................................................................................... 1 1 87
.................... .................................................... ........ ......... 1 1 88
......................................
..................................................... 1 1 88
Pozostałe metody modyfikuj ące: copy() oraz swap{)
.....................................
. . . . . . 1 1 89
Wej ście i wyj ście . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 8 9 DODATEK G Metody i funkcje z bibl ioteki STL Składowe wspólne dla wszystkich kontenerów
......
.............................................. 1 1 9 1
......................................................
Dodatkowe składowe wektorów, list i kolej ek dwustronnych
.....
... 1 1 9 1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 94
Dodatkowe składowe zbiorów i map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 97 Funkcje STL
...............................................................................................................
Niemodyfikujące operacje sekwencyjne M utujące operacje sekwencyjne
...........................................
Operacje sortowania i pokrewne Operacje liczbowe
..................
................................................
......................
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 203
.................................................... 1 2 1 1
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 224
DODATEK H Wybrane pozycje książkowe i zasoby internetowe Wybrane pozycje książkowe
1 1 98
. . . . . . . . . . . . . . . 1 1 99
....................
.........
. . . . . . . . . . . . . . . . . . 1 22 7
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 22 7
Zasoby internetowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 22 9 DODATEK I Dostosowywanie d o standardu AN SI/ISO C++
....
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 1
Stosuj rozwiązania alternatywne zamiast niektórych dyrektyw preprocesora
.........
. 1 23 1
••..•...•...„ ....••.•„••
1 23 1
Do definiowania niewielkich funkcji używaj specyfika tora inłine zamiast makrodefinicji #define . . .
1 23 3
Do definiowania stalych używaj modyfikatora const zamiast dyrektywy #define
Używaj prototypów funkcj i Stosuj rzutowanie typów
.....
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 4
. . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . .. . . . . . . . .
Poznaj i wykorzystuj mechanizmy języka C++
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 5
...............................................
. . . . . . . . . . 1 23 6
Używaj nowej organizacj i plików nagłówkowych . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 6 Korzystaj z przestrzeni nazw Używaj szablonu autoptr Używaj klasy string
............
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 6
............................................................................................
. . . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Korzystaj z b iblioteki STL
.........
1 23 7
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 8
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 23 8
DODATEK J Odpowiedzi do pytań kontrolnych
.............................................
. . . . . . . . 1 24 1
Odpowiedzi do pytań z rozdziału 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 24 1 Odpowiedzi do pytań z rozdziału 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 242 Odpowiedzi do pytań z rozdzia.łu 4
...........................................................................
1 243
Odpowiedzi do pytań z rozdziału 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 244 Odpowiedzi do pytań z rozdziału 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 24 5 Odpowiedzi do pytań z rozdziału 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 24 7 Odpowiedzi do pytań z rozdziału 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 249 Odpowiedzi do pytań z rozdziału 9
...........
Odpowiedzi d o pytań z rozdziału I O
XVIII
.........
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . 1 250 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 25 2
Odpowiedzi do pytań z rozdziału 1 1
............„„............................................... „.........
1 25 4
Odpowiedzi d o pytań z rozdziału 1 2
. .„. . „.„„.„.„„ ..„ ..... „.„ . . „„ .. „ „ „ „ . „ „ „ „ „ „ . „ „ „ „
1 25 5
Odpowiedzi d o pytań z rozdziału 1 3
. „ „ . „ . „ „ „ .. „ . „ „ „ . „ . „ „ „ „ . „ „ „ „ „ „ „ . „ „ „ . „ . „ „ „ „ .
1 25 8
Odpowiedzi do pytań z rozdziału 1 4
„„„„„„„„.„„.„„„„„„„„„„„„.„„„„.„„„„.„„.„.
1 260
Odpowiedzi do pytań z rozdziału 1 5
. „ „ „. „ „ „ „ „ „ „„ „ .„ „„ „ „. „ „ „ „ „ . „. „ „ „ „. „ „ „. „ . „
1 26 1
Odpowiedzi do pytań z rozdziału 1 6
„„„„„„„„„„„„„„„.„„„„„„„„„„.„.„„„.„„„„„.
1 263
Odpowiedzi do pytań z rozdziału 1 7
„ „ „ „ „ „ „ „ . „ „ „ „ . „ „ . „ „ „ „ „ „ „ „ . „ „ „ „ . „ „ . „ „ „ „.„
1 264
INDEKS . . . „ . . . . „ „ . . „„ . . „ . . „ „ . . „ „ . . . . . . . . . . . . . . . . . . . . „ „ . . . . . . . „ . . . . . . . „ . . „ „ . . . . „ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 267
XIX
O autorze Stephen Prata wykłada
astronomię, fizykę i infonnatykę w Col lege of Marin w Kentfield w Ka lifornii. Magisterium uzyskał na Politechnice Kali fornijskiej , a doktorat na Uniwersytecie Sta nowym Kalifornii w Berkeley. Jest autorem i współautorem ponad tuzina książek wydanych przez The Waite Group, w tym książki New C Primer Plus, która uzyskała w 1 990 roku nagrodę za najlepszy podręcznik komputerowy przyznawaną przez Stowarzyszenie Prasy Informatycznej oraz niniejszej , nominowanej do tej samej nagrody w roku 1 99 1 .
XXI
Wstęp
Wstęp do wydania piątego
N
auka C++ t o wyzwanie d l a odkrywców, szczególnie ż e język ten pozwala wykorzystać kilka różnych paradygmatów programowania, w tym programowanie obiektowe, pro gramowanie ogólne oraz klasyczne programowanie proceduralne. Język C++ zmieniał się w mi arę, jak dodawano do niego nowe możliwości. ale obecnie, kiedy jest już standard ISO/ANSI C++ {druga edycja, 2003), język ten się ustabilizował. Używane teraz kompilatory obsługują większość możliwości wymaganych przez standard, a programiści mieli czas przyzwy czaić się do istniejącej wersji języka. Piąte wydanie książki wykorzystuje standard ISO/ANSI. opisując właśnie tę dojrzałą wersję j ęzyka C++.
W niniejszej książce omawiamy podstawy j ęzyka C oraz dodatkowe możl iwości C++, dzięki czemu niepotrzebna jest żadna lektura uzupełniaj ąca. Omawiamy podstawy C++ oraz ilustruje my je krótkimi, a celnymi programami, które łatwo jest skopiować 1 na których łatwo jest ekspe rymentować. Powiemy o wej ściu-wyj ściu (1/0), o tym, jak wymusić na programie wielokrotne powtarzanie tych samych działań, jak dokonywać wyborów, jak na różne sposoby obsługiwać dane i jak używać funkcj i . Poznamy wicie cech C++ stanowiących rozszerzenie względem C. w tym podyskutujemy o:
• klasach i obiektach, • dziedziczeniu, • polimorfizmie, funkcj ach wirtualnych oraz wykrywania typu podczas wykonywania programu {RTTI), • przeciążaniu funkcji, • zmiennych referencyj nych, • programowaniu ogólnym, czyli ni ezależnym od typów, j akie jest dostępne za pośrednic twem szablonów STL (ang. Slandard Temp/ale Library, standardowa biblioteka szablo nów), • obsłudze błędów za pomocą mechanizmu wyjątków, • przestrzeniach nazw pomagających zapanować nad funkcjami, klasami i zmiennymi.
Podeiście podręcznikowe Niniejsza książka ma być podręcznikiem, co nakłada na nią pewne wymogi odnośnie sposobu prezentacji materiału. Korzystamy tu z doświadczeń z poprzednią książką tego typu, wydaną bli sko dwadzieścia lat temu C Primer Plus. Przyjęta tam filozofia wykładu okazała się sukcesem: -
• Podręczni k powinien być łatwy w użyciu i powinien prowadzić Czytelnika za rękę.
Język Ctt. Szkoła programowania
• W podręczni ku nie zakłada się, że Czytelnik będzie znał już odpowiednie pojęcia z zakre su programowania. • W podręczniku nacisk kładzie się na praktyczne ćwiczenia z prostymi przykładami uła twiaj ącymi zrozumienie materiału. Nie wprowadza się więcej niż j ednego, dwóch zagad nień naraz. • Nowe pojęcia są wyj aśniane na obrazach. • Podręcznik zawiera pytania i ćwiczenia, które pozwalaj ą sprawdzić stopień opanowania materiału, dzięki czemu książka taka może być wykorzystana zarówno do samodzielnej nauki, jak i podczas kursów. Zgodnie z powyższymi zasadami w książce tej staramy się pomóc Czytelnikom zrozumieć ob szerny język, jakim jest C++, oraz zaprezentować zasady j ego użycia. Na przykład: • Mówimy, kiedy używać omawianej cechy - na przykład dziedziczenia publicznego uży wa się do modelowani a związków typu generalizacja/specjalizacja. • Prezentowane są typowe idiomy i techniki programowania w C++. • Dodajemy mnóstwo dopisków: wskazówek, ostrzeżeń, infonnacj i do zapamiętania, uwag o zgodności oraz odniesień do praktycznych zastosowań. Autor i redaktorzy robili, co mogli, aby wykład był na temat, prosty i ciekawy. Chodzi o to. aby kończąc lekturę tej książki. Czytelnik potrafił pisać poprawne, niezawodne i wydajne programy oraz aby sprawi ało mu to przyjemność.
Przykładowy kod W książce tej znajduje się mnóstwo przykładu kodów, zwykle w fonnie kompletnych progra mów. Tak jak w poprzednich wydaniach koncentrujemy się na ogólnym kodzie C++, czyli kodzie niezwiązanym z j akimkolwiek konkretnym komputerem, systemem operacyjnym czy kompilato rem. Przykłady były testowane w systemach Windows XP, Macintosh OS X i Linux. Tylko poje dyncze programy są zależne od cech poszczególnych kompilatorów. Od czasu pierwszego wyda nia tej publikacj i zgodność kompi latorów ze standardem C++ znakomicie się poprawiła. Kod do przykładów omawianych w książce znajduje się pod adresem: http://helion.pl/ksiazki/cpprim. htm
2
Wstęp
Układ książki Książka ta została podzielona na 1 7 rozdziałów oraz I O dodatków.
Rozdział 1 . „Zaczynamy" W pierwszym rozdziale mówimy, jak Bjame Stroustrup stworzył język programowania C++, dołączając do języka C programowanie obiektowe. Powiemy o różnicach między j ęzykami pro ceduralnymi, jak C, a językami obiektowymi, jak C++. Dowiemy się o wspólnej pracy ANSI i ISO nad standardem C++. Omówimy też techniczne aspekty pisania programów C++, pokazu jąc podstawowe działania w przypadku kilku używanych obecnie kompi latorów C++. W końcu zaprezentujemy konwencje przyj ittc w książce.
Rozdział 2. „Pierwszy program w C++" W rozdziale drugim praktycznie uczymy się pisać proste programy w C++. Dowiemy się o zna czeniu funkcji mai n ( ) oraz o niektórych rodzajach instrukcj i używanych w C++. Do obsługi wejścia i wyjścia użyjemy predefiniowanych obiektów c o u t i c i n, nauczymy się tworzyć zmienne i potem zmiennych tych używać. W końcu powiemy nieco o funkcj ach - podstawowych ele mentach składowych programu w C++.
Rozdział 3. „Dane" Język C++ ma wbudowane typy pozwalające przechowywać dane dwóch rodzajów: liczby całko wite (bez części ułamkowej) oraz liczby zmiennoprzecinkowe (z częścią ułamkową) . Aby spełn ić rozmaite potrzeby różnych programistów, w każdej z tych gmp zdefiniowano różne typy danych. W rozdziale omawiamy te typy, w tym tworzenie zmiennych i zapisywanie stałych. Nauczymy też posługiwać się j awnymi i niejawnymi konwersjami miitdzy typam i C++.
Rozdział 4. „Typy złożone" Język C++ pozwala tworzyć na bazie typów prostych typy bardziej złożone. Naj bardziej zaawan sowanymi typami są klasy, omawiane w rozdziałach od 9. do 1 3 . W rozdziale prezentujemy prostsze typy złożone, w tym tablice mające wicie wartości j ednego typu, struktury mające wicie wartości różnych typów oraz wskaźniki odnoszące się do wybranych miejsc w pamięci. Pokazujemy, jak tworzyć i zapisywać łańcuchy tekstowe oraz jak obsługiwać wej ście i wyjście znakowe za po mocą tablic znakowych znanych już z C oraz za pomocą klasy s t r i n g . W końcu powiemy wiit cej o sposobach alokacj i pamięci w C++, w tym o operatorach new i de l e t e pozwa lających jawnie zarządzać pamięcią. 3
Język (++. Szkoła programowania
Rozdział S. „Pętle i wyrażenia relacyine" Progra m y często wielokrotn ie wykonują te same d z i ała n i a ; języ k C++ wyko rzy sn1j e do tego trzy rodzaje p l,! t l i : f o r . w h i l e o ra z do w h i l e . Pi
int mai n
()
using name space std;
cout
/! /11b math.h # i n c l ude < c ma t h > int
ma i n ( ) u s i ng n ame space
doubl e
std;
a re a ;
cout > a rea ; doubl e s i de ; s q r t ( a re a ) ; s i de cou t < < " Odpow i a d a to kwa d r a t o w i o b o k u " < < s i de < < " me t r ów . " < < e n d l ; c o u t < < " N i e s amow i t e 1 " < < e n d l ; return
O;
Uwaga o zgodności ze standardem W p rzypadku ko rzystania ze starszego ko m p i l atora w l isti n g u 2.4 kon ieczne może być za stąpienie wie rsza # i n c l u de < cm a t h > wierszem # i n c l u de < ma t h . h > .
53
Rozdział 2 - Pierwszy program w ( ++
Użycie funkcji bibliotecznych Funkcje b i b l i oteczne C++ są u m ieszczane w p l i kach bibliotecznych . Kiedy ko m p i lator kompi luje p rogra m , musi odnaleźć w bibliotekach użyte funkcje . Różne kompilatory auto matyczn ie przeszu kują różne b i b l i oteki. Jeśli po uruchomien i u programu z l i sti n g u 2 . 4 otrzymamy ko m u n i kat m ówiący, że _ s q r t nie zostało zdefi n i owane , być może kom p i lator auto matycznie nie sprawdza b i b l i oteki mate matycznej (kompilatory często dodają do nazw funkcj i podkreśle nie - kolejna rzecz, o któ rej warto wiedzieć). Po otrzyman i u takiego ko m u n i katu błędu należy sprawdzić w d o k u mentacji używanego ko mpilatora, jak odszukać właściwą b i b l iotekę. Przy kładowo. w syste m i e Unix używa się opcji - l m (co jest skrótem od library math) : CC s q r t . C - l m
Kompi lator G N U w system i e U n i x działa podobnie: g++
s q r t . C - lm
Samo włączen i e p l i k u nagłówkowego cmath zapewn ia dostępność p rototyp u , ale nie musi jeszcze spowodować, że kompi lator przeszuka prawidłowy plik b i blioteczny. Oto przykładowy wynik wykonania powyższego programu: P o d a j p o w i e r z c h n i ę s w o j e g o m i e s z ka n i a w me t r a c h 153 Odp o w i a d a t o k w a d r a t o w i o b o k u 1 2 . 3 6 9 3 me t r ó w . N i e s amo w i t e '
kwad rat owych :
Funkcja s q r t ( ) używa wartości typu double, więc w przykładzie używamy takiej właśn ie zmiennej . Zauważmy, że zmienną typu doub l e deklaruj emy tak samo, jak deklarowal iśmy zmienną typu i n t : n a z wa - t yp u
nazwa- zmienne j ;
Typ doub l e pozwala zm iennym a r e a i s i d e zawierać część ułamkową, na przykład 1 5 3.0 lub 1 2,3693. Liczba pozornie całkowita, 1 5 3 , j est przechowywana wraz z częścią ułamkową równą zeru , czyl i ,O. Jak zobaczymy w rozdziale 3„ typ double może pomieścić znaczn ie większy zakres wartości niż typ i n t . Język C+.,. pozwala deklarować nowe zmienne w dowolnych miej scach w programie, więc w pli ku sqrt. cpp zmiennej s ide n i e deklaruj emy na zapas, ale dopiero wtedy, kiedy będziemy jej potrzebować. C++ pozwala też w chwili deklaracj i zmiennej od razu przypisać jej wartość, z czego skorzystal iśmy i tym razem : double
s i de
=
sqrt ( a rea ) ;
Więce1 o tym procesie, nazywanym inicjalizacją, powiemy w rozdziale 3 . Zauważmy, ż e obiekt c i n potrafi zamienić informacj e z e stmmienia wejściowego n a typ do ub l e , a c o u t potrafi wstawić wartość typu double do strumienia wyjściowego. Jak wspo mniano wcześniej , obiekty te są naprawdę inteligentne.
54
Funkcje
Odmiany f unkcii Niektóre funkcje wymagają więcej niż jednej danej . Funkcj e takie mają wiele parametrów roz dzielanych przecinkami. Na przykład funkcja matematyczna p o w ( ) ma dwa parametry i zwraca wartość pierwszego parametru podniesionego do potęgi wyznaczonej przez drugi parametr. Oto jej prototyp: double
p o w ( double ,
double ) ;
li prototyp .funkcji
dwuparametrowej
Jeśli na przykład chcielibyśmy wyliczyć 5" (pięć do potęgi ósmej), moglibyśmy użyć tej funkcj i następująco: a n s we r = p o w ( S . O ,
8 . 0) ;
li
wywołanie funkcji z listą parametrów
Są też funkcje bezparametrowe. Na przykład jedna z bibliotek języka C (związana w plikiem nagłówkowym cstdlib lub stdlib.h) ma funkcję r a n d ( ) , która nie ma żadnych parametrów i która zwraca losową liczbę całkowitą. Jej prototyp wygląda następująco: int
r a n d ( vo i d ) ;
li prototyp funkcji
bezparametrowej
Słowo kluczowe void j awnie infonnuje, że funkcja nie ma parametrów. Jeśli pominiemy to słowo i zostawimy puste nawiasy, C++ zinterpretuje to j ako niejawną deklarację braku parame trów. Moglibyśmy użyć tej funkcj i następuj ąco: myGue s s = r a nd ( ) ;
li wywołanie funkcji
bezparametrowej
Zauważmy, że w przeciwieństwie do niektórych języków programowania w C++ do wywołania funkcji trzeba dodawać nawiasy, nawet jeśli jest to funkcja bezparametrowa. Istnieją też funkcje niemające wartości zwracanej . Załóżmy na przykład, że mamy napisać funk cję wyświetlającą liczbę w formie złotych i groszy. Jeśli funkcj a ta otrzyma wartość 2 3 , 5 , powin na wyświetl ić ją jako 2 3 zł 50 gr. Funkcja ta pokazuje wartość na ekranie i nie zwraca żadnej wartości. Jej prototyp będzie wykorzystywał słowo kluczowe void: void
z l o t o w k i ( double ) ;
li prototyp funkcji
niezwracającej wartości
Funkcja z l o t o w k i ( ) nie zwraca żadnej wartości, nie może być częścią instrukcj i przypisania ani innego wyrażenia. Występuje j ako samodzielna instrukcj a wywołania funkcj i : z lotowki ( l 2 3 4 . 5 6 ) ;
li wywołanie funkcji,
bez wartości zwracanej
W niektórych językach programowania funkcje muszą zwracać wartość; jeśli coś nie zwraca wartości, jest procedurą. Jednak w językach C++ i C pojęcie funkcji obejmuje jedno i drugie.
Funkcie definiowane przez użytkownika Standardowa biblioteka C zawiera ponad 1 40 funkcj i predefiniowanych. Jeśli któraś z nich jest nam przydatna, to używajmy jej . Często jednak trzeba pisać własne funkcje, szczególnie podczas tworzenia klas. Poza tym pisanie własnych funkcj i to ciekawe zajęcie, więc przyjrzyjmy się temu. Używaliśmy już kilku funkcj i zdefiniowanych przez użytkownika; nazywały się m a i n ( ) . SS
Rozdział 2 Pierwszy program w C++ -
Każdy program C++ musi mieć funkcję ma i n ( ) i funkcję tę definiuje użytkownik. Załóżmy, że chcemy dodać drugą funkcję użytkownika. Tak j ak w przypadku funkcj i bibliotecznych możemy wywołać funkcj ę użytkownika przez jej nazwę i tak j ak w przypadku funkcj i bibliotecznych przed wywołaniem funkcj i musimy podać jej prototyp; zwykle robi się to ponad definicj ą funkcj i ma i n ( ) . Tym razem jednak musimy także podać kod źródłowy nowej funkcj i . Najprostszym sposobem jest umieszczenie tego kodu w tym samym pliku, po kodzie funkcj i ma i n ( ) Pokaza no to na l istingu 2 . 5 . .
Listing 2.5. ourlunc.cpp li ourfimc. cpp - definiujemy własną .funkcję # i n c l u de
< i o s t ream>
voi d s i m o n ( in t ) ;
li prototyp funkcji simon()
int ma i n ( ) us ing namespace s t d ; s i mon
( 3) ;
cout
>
coun t ;
s imon ( coun t ) ; cout
using name space s t d ; int mai n ( ) using name space s t d ; int c h e s t = 4 2 ;
li dziesiętna stała całkowitoliczbowa
int wa i s t = O x 4 2 ;
li szesnastkowa stała całkowitoliczbowa
int i n s eam = 0 4 2 ;
li ósemkowa stała całkowitoliczbowa
c o u t < < " Co z a f i g u r a ! \ n " ; cout us ing name space s t d ; con s t int A r S i z e = 1 6 ; int ma i n ( )
li przykład deklaracji zewnętrznej
doubl e f a c t o r i a l s [ Ar S i z e ] ;
factorials [ l ]
= factorial s [ O ] = 1 . 0 ;
/ / in t i ; for ( in t i = 2 ;
i < Ar S i z e ; i + + ) factorial s [ i ] = i * factorials [ i- 1 ] ; for ( i = O ; i < A r S i z e ; i + + ) cout # i n c l ude < c s t r i ng> int ma i n ( ) u s ing name space char for
word [ S ] ( ch a r
eh
li prototyp .funkcji strcmp()
std; " ?ate " ;
=
'a' ;
s t rcmp ( wo r d ,
"mate " ) ; eh+ + )
cout 9tli) instrukcja2 in1trukcja3
inatrukcjal
prawda
„
-
-
-
- -
-
-
fałsz
- - - - -
-
-
- - - - - - -
instrukcja2
-
-
pętla while '
- - - - - - - - - - - '
inatrukcja3
Rysunek 5.3. Struktura pętli whi/e Na listingu 5 . 1 3 pokazano użycie pętli whi le. Pętla przechodzi po kolejnych znakach łańcucha i pokazuje znak oraz jego kod ASCII. Pętla kończy swoj e działanie po dojściu do znaku N U L . Technika takiego przechodzenia p o znakach a ż do natknięcia s i ę na znak \ O j est standardową metodą przetwarzania łańcuchów w C++. Łańcuch zawiera własny znacznik końca, więc pro gram nie musi sam przechowywać informacj i o długości łańcucha. 215
Rozdział 5 Pętle i wyrażenia relacyjne -
Listing 5.1 3. while.cpp
li while. cpp - wprowadzenie do pętli white
# i n c l ude < i o s t r eam> con s t int A r S i z e = 2 0 ; int ma i n ( ) us ing name space s t d ; char n ame [ A r S i z e ] ;
cout I/ zawiera deklarację funkcji clock() i typu clock_t int ma i n ( ) u s i n q name space s t d ;
c o u t < < " Poda j c z a s op ó ź n i e n i a
w
s e ku n d a c h :
"
;
float s e c s ;
cin >> secs ; secs C L O C K S PER S E C ; li konwersja na jednostki =egara _ de l a y c o u t < < " z a c z yn amy \ a \ n " ; c l o c k_ t s t a r t = c l o c k ( ) ; whi l e ( c l o c k ( ) - s t a r t < d e l a y ) l/ czekaj, aż upłynie cws li uwaga na średnik c o u t 9tli} ; inatrukcja3
inatrukcjal
in1trukcja3
Rysunek 5.4. Struktura pętli do white Zwykle pętle sprawdzające warunek na początku są lepszym wyborem od pętli ze sprawdzaniem warunku na końcu pętli, gdyż kontrola przeprowadzana j est w nich przed wykonaniem treści pętli . Na przykład gdyby w kodzie z listingu 5 . 1 3 użyć zamiast while pętli do whi le, przed stwierdzeniem, że doszliśmy już do końca łańcucha, pokazywal ibyśmy znak NUL. Czasami j ed nak pętla do whi l e sprawdza się lepiej . Jeśli na przykład żądamy podania danych przez użyt kownika, program musi te dane odczytać, zanim będzie mógł je sprawdzać. Na listingu 5 . 1 5 pokazano użycie do whi l e w takiej właśnie sytuacj i . listing 5.1 5. dowhile.cpp li dowhile. cpp - pętla # i n c l ude
z
kontrolą warunku na koniec
< i o s t r e am>
int ma i n ( ) us ing name space s t d ; int n ; cout
i n t ma i n ( ) using name space s t d ; char e h ;
O; li najprostsza metod wprowadzania c o u t > eh ; li sprawdź znak whi l e ( e h ! = # ) int c o u n t
=
'
cout > eh ; cou t
const int C i t i e s = 5 ; const int Y e a r s = 4 ; int ma in { ) using name space s t d ; const char
*
li tablica wskaźników li 5-ciu łańcuchów
cities [ Ci t ie s ]
" Je l e n i a G ó r a " , " Z ie l ona Góra " , " S tara Góra " , " Gó r k i " , "Wi l g a G ó rn a " )
;
int maxt emps [ Ye a r s ] [ C i t i e s ]
{ 95, { 95 , { 96, { 97 ,
li tablica dwuwymiarowa
99, 86, 100, 104 } , 9 7 , 90 , 10 6 , 102 ) , 100, 940, 107 , 105 ) , 102 , 89, 108, 104 I
li wartości li wartości li wartości li wartości
maxtemps[OJ maxtemps[I] maxtemps[2] maxtemps[3]
}; cout
«
for
( int c i t y = O ;
" Tempe r a t u r y
ma k s yma l n e
(F)
w l a t ach
2 0 02-2005\n\n " ;
city < Cities ; ++city)
cou t < < c i t i e s [ c i t y ] < < " : \ t " ; for { int y e a r = O ; ye a r < Ye a r s ; + + ye a r ) c o u t < < ma x t emp s [ ye a r ] [ c i t y ] < < " \ t " ; cout < < endl ;
return O ;
Oto wyniki działania programu z l istingu 5 .2 0 : Tempe r a t u r y ma k s yma l n e
(F)
w
Jelen i a Góra : Zie lona Góra : S tara Góra : Górki : Wilga Górna :
96 100 940 107 105
97 102 89 108 104
95 99 86 100 104
95 97 90 106 1 02
l a t a ch 2 0 0 2 - 2 0 0 5
237
Rozdział 5 - Pętle i wyrażenia relacyjne Użycie w wynikach tabulatorów daje formatowanie bardziej eleganckie niż można byłoby uzy skać w przypadku spacj i . Jednak przy różnych ustawieniach tabulacj i w różnych systemach moż na uzyskać różne wyniki. W rozdziale 1 7 . pokażemy precyzyjniej sze, ale bardziej złożone meto dy formatowania wyników. Można byłoby przykład j eszcze skomplikować i zamiast tablic wskaźników na łańcuchy użyć tablic znaków char. Deklaracj a wyglądałaby następuj ąco: char c i t i e s [ 2 5 ) [ C i t i e s )
li tablica 5-ciu tablic po 25 znaków
" Je l e n i a G ó r a " , " Z i e l o n a Gó ra " , " S t a r a Góra " , " Gó r k i " , "Wi lga Górna " )
;
W tym wypadku długość wszystkich nazw byłaby ograniczona do 24 znaków. Tablica wskaźników znaków przechowuje pięć l iterałów łańcuchowych, ale tablica tablic char spowoduje skopiowa nie każdego z pięciu literałów do odpowiednich pięciu tablic 25 znaków char. Wobec tego tablica wskaźników ekonomiczniej wykorzystuje pamięć. Jednak j eśli trzeba będzie łańcuchy modyfiko wać, tablica dwuwymiarowa byłaby lepszym rozwiązaniem. Co ciekawe, w obu rozwiązaniach sto sowana jest taka sama lista inicj alizacj i i taka sama pętla for wyświetlaj ąca łańcuchy. Można też użyć tabl icy obiektów klasy s t r i n g ; deklaracj a wyglądałaby wtedy tak: con s t s t r i n g c i t i e s [ C i t i e s )
li tablica na 5 obiektów string
" Je l e n i a G ó ra " , " Z i e l on a G ó ra " , " S t a r a G ó ra " , " Gó r k i " , "Wi lga Górna " )
;
Jeśl i chcemy modyfikować powyższe napisy, musimy pominąć słowo kluczowe cons t. Używa na jest tu taka sama l i sta inicjal izacj i i taka sama pętla for pokazująca dane. Jeśli będziemy modyfikować obiekty s t r i n g , automatyczne ustawianie wielkości w tej klasie znakomicie uła twi nam pracę.
Podsumowanie Język C++ udostępnia rozmaite pętle: for, whi l e i do whi l e . Pętla powoduj e wielokrotne wykonywanie tych samych instrukcj i tak długo, jak długo warunek pętli j est prawdziwy, czyli nie jest zerem; jeśli warunek ten przybierze wartość fal s e lub O, działanie pętli zostanie przerwa ne. Pętle for i wh i l e sprawdzaj ą swoje warunki przed wykonaniem treści pętli, zaś pętla do while sprawdza swój warunek po każdorazowym wykonaniu instrukcj i z treści pętl i .
238
Pytaniu sprowdzojqce
Składnia wszystkich rodzajów pętl i wymaga użycia j ako treści poj edynczej instrukcj i . Jednak tak naprawdę może to być instrukcj a złożona. czyli blok, składająca się z szeregu poj edynczych instrukcji ujętych w nawiasy klamrowe. Wyrażenia relacyjne służą do porównywania wartośc i ; używane są one często j ako warunki pętli. Wyrażenia te zawieraj ą j eden z 6 operatorów: < , < = . ==. > = , > oraz ! = . Wyrażen ia relacyjne mogą mieć ostatecznie wartość typu true lub fal s e (typu boo l ) . Wiele programów wczytuje dane tekstowe lub pliki tekstowe znak po znaku . Klasa i s t re am zawiera stosowne po temu narzędzia. Jeśli e h j est zmienną typu char, instrukcj a :
e i n >> eh ; spowoduje wczytanie do eh następnego znaku wej ściowego. Jednak pomij ane będą wszelkie spacje i tabulatory. Wywołanie funkcj i składowej :
e i n . g et ( e h ) ; powoduje odczyt następnego znaku i umieszczenie go w zmiennej e h . Funkcj a składowa cin . get ( ) zwraca następny znak wej ściowy, w tym spacje, znaki nowego wiersza i tabulato ry, zatem można jej użyć następująco:
eh = e i n . g e t ( ) ; Wywołanie funkcj i e i n . g e t ( char ) w przypadku poj awienia się EOF zwraca wartość dającą po konwersj i na typ b o o l wartość f a l s e . Funkcj a e i n . g e t ( ) z kolei zwraca wartość E O F zdefiniowaną w pliku nagłówkowym iostream. Pętle zagnieżdżone to pętle umieszczone j edna w drugiej . Pętle zagnieżdżone stanowią natura l ny sposób przetwarzania tablic wielowymi arowych .
Pytania sprawdzaiące I . Jaka jest różnica między pętlami sprawdzającymi warunek pętli na początku i na końcu? Jakiego typu są poszczególne pętle j ęzyka C++? 2. Co pokaże poniższy fragment kodu, jeśli będzie częścią poprawnego programu?
int i ;
for ( i = O ; i < 5 ; i + + ) eout < < i ; eou t < < e n d l ; 3. Co pokaże poniższy fragment kodu, j eśli będzie częścią poprawnego programu?
int j ; for ( j = O ;
j < 1 1 ; j += 3 ) eout " ; char eh ;
c i n > > eh ; ' t ' I I eh = = ' T ' J c o u t < < " O s t r z e g a l em ! \ a \ a \ n " ; else i f ( eh = = ' n ' I I e h = = ' N ' ) if
( eh = =
li t lub T li n lub N
251
Rozdział 6 Instrukcje warunkowe i operatory logiczne -
c o u t < < " B a r d z o ro z s ądn i e . . . Do w i d z e n i a \ n " ; el se
cout < < " N i e w yb r a n o t ani n , ale s p róbu j e z n i s z c z y ć " " swój dys k j e s z c z e raz . \ a \ a \ a \ n " ; re turn O ;
Oto wynik uruchomienia programu z listingu 6.4:
P r o g r a m ten mo ż e s fo rm a t o w a ć twój d y s k i z n i s z c z yć w s z y s t k i e z n a j du j ące s i ę n a n i m d a n e . C z y mam k o n t yn u o wa ć ? < t l n > N Ba r d z o ro z s ądn i e . . . Do w i d z e n i a Program odczytuj e tylko j eden znak, więc istotny j est tylko pierwszy znak odpowiedzi, zatem użytkowni k zamiast N może wpisać N I E ! Program i tak wczyta tylko N. Jeśl i j ednak dalej program próbowałby wczytywać dalsze dane, zacząłby od litery I .
Logiczny operator koniunkcii
-
&&
Logiczny operator koniunkcj i , zapisywany j ako & & , także łączy dwa wyrażenia w całość. Wynik jest prawdą tylko wtedy, gdy oba wyrażenia składowe są prawdziwe. Oto kilka przykładów :
5 5 5 5 5 5
---
> > < >
5 && 4 3 && 4 3 && 5 > 8 && 5 < 8 && 5 > 8 && 5
c h a r * qu a l i f y [ 4 ]
li tablica wskaźników lańcuchów
" b i e g n a l O km . \ n " , " p r z e c iąga n i e l i n y w b ł o c i e - \ n " , " p o j e d yn e k m i s t r z ó w c a n o e . \ n " , " f e s t i w a l r z u c a n i a p l a c k i em . \ n " )
;
int
ma i n ( ) us ing name space s t d ;
age ; c o u t < < " Po d a j s w ó j w i e k : " ; cin >> age ; int i n d e x ; i f ( a ge > 1 7 & & a g e < 3 5 ) i ndex = O ; else if ( a ge >= 3 5 & & age < 5 0 ) index = 1 ; el se if ( a ge >= 5 0 & & age < 6 5 ) i ndex 2; int
el se
i ndex = 3 ; cout
1 7 & & a g e < 3 5 s łu ż y do sprawdzenia, czy wiek mieści si ę mi ę dzy dwiema wartościam i : czy j e s t w zakresie od 1 8 do 34. W wyrażeniach a g e > = 3 5 & a g e < 5 O u ż ywany j est operator < = , dzięki czemu d o zakresu włączane jest 3 5 . a zatem uzys kan y zakres t o 3 5 d o 4 9 . Jeś l i w programie uż yli b y ś m y warnnku a g e > 3 5 & & age
50,
b ? a cout
> code ;
(0-6) :
";
c o u t =
35
&&
wiek
=
50
&&
wiek
< 65)
O:
(wiek
index
el se
17
>
i ndex
if
j ak i f e l s e pozwalaj ą p ro gr a m o w i wyb i e ra ć z w i c i u moż l i woś c i . j est b a r d zi ej uniwersa lna: na p rz y kł a d p ozw a ł a obsłużyć z ak re s y :
swi t c h , el s e
(wiek
el se
else
=
50)
1;
(wiek 2;
i ndex else i ndex
3;
=
l n stm k cj a swi tch n i c umożl iwia obsługi zakresów: każdy przypadek musi być oznakowany p oj e dync z ą wa rtoś c i ą Oznakowanie musi być wartością całkowitą (wliczaj ąc w to typ char). w i ę c nie sposób ustawić w swi tch wamnków n a wartości zmiennoprzecinkowe. Poza t y m ety k i e ty muszą być stałymi . Jeś l i po t rz e bne j e st nam sprawdzani e zakresów, porównywanie zmien noprze c i nkowe lub p o rów ny w an i e d w ó c h z m i ennych n a l eży użyć konstrukcj i if el s e . .
,
wszystkie m o żliw e p rz y padki da s i ę opisać stał y m i c ałk o wit y m i można użyć tak jak i if el s e . Jako że dokł adnie do takich s ytuacj i prz ew i dz i a n o swi tch, to ta i n s t rukcj a zwyk l e jest wy d aj n i ej s za d aj e mn i ej sz y kod wyni kowy oraz dzi ała s z yb c i ej chyba że do wyboru są tyl ko dwie możliwośc i .
Jeśl i j ednak
.
swi tch,
-
Wskazówka Jeś l i w danej sytuacji można u żyć za równo sekwencji if else i f , jak i i n strukcji swi tch, o g ó l n a zasada jest taka , aby stosować tę d ru g ą , kiedy 1stn ieJą trzy l u tl więcej możl 1wośc 1 .
lnstrukcie break i
con tinue
Instruk cj e break i con tinue umożl iwiaj ą programowi p o m ija n i e c z ęś c i kodu. I nstrukcj a break może być użyta wewnątrz i n s tr u k cj i swi tch lub w dowolnej pętli . Instru k cj a c o n t i nue znaj duj e zastosowanie w pętlach i pow o duj e że program pomij a resztę treści pęt l i i za c z y n a nowy cykl pętli (rysunek 6.4). ,
270
Instrukcje break i continue
ł
whila (cin . gat ( ch) )
I
instrukcjal i f C ch = ' \n ' ) I continua ; instrukcja2
instrukcja3
continue pomija resztę treści pętli i zaczyna nowy cykl pętli
while (cin . get (ch) )
I
instrukcja2 ins trukcja3
break pomija resztę treści pętli i przechodzi do instrukcji znajdującej się za pętlą
Rysunek 6. 4. Działanie instrukcji break i continue Na listingu 6. 1 2 pokazano użycie obu omawianych instrukcj i . Program pozwala wprowadzić j eden wiersz tekstu. W pętli każdy znak j est pokazywany, a jeśli znak ten j est kropką, instrukcja break powoduje przerwanie pętli. W ten sposób widzimy, jak można zakończyć pętlę bezpośrednio z j ej wnętrza w chwi li spełnienia ustalonego warunku. Następnie program zlicza same spacje. W pętli używamy continue do pomij ania części pętli w sytuacj i . kiedy znak nie jest spacją. Listing 6.1 2. jump.cpp
li jump. cpp
użycie continue i break < i o s t r e am > const i n t A r S i z e 80; int ma i n ( ) -
# i n c l u de
=
us ing name space s t d ; char l i n e [ A r S i z e ] ; int s p a c e s
=
O;
c o u t < < " Wp i s z w i e r s z t e k s t u : \ n " ;
271
Rozdział 6 Instrukcje warunkowe i operatory logiczne -
c i n . ge t ( l i ne , A r S i z e ) ; c o u t < < " Ca ł y w i e r s z : \ n " < < l i ne eh ; if ( e h IPI ) goto p a r y z ; cout > n ; Co
się stanie, jeśli użytkownik zamiast liczby wpisze słowo? Odpowiedzi są cztery :
• Wartość n pozostaje b e z zmian. • Nieprawidłowe dane wejściowe pozostaj ą w kolejce wej ściowej . • Ustawiana j est flaga błędu obiektu e i n . • Jeśli metoda e i n zostanie wywołana i przekonwertowana n a typ b o o l , d a wynik false. To, że metoda zwraca fa l s e , oznacza, że podając nieliczbowe wartości, można zakończyć pętlę wczytującą l iczby. Ustawienie flagi błędu po podaniu danych innych niż liczbowe sugeruje, że fl a gę tę trzeba wyzerować przed dalszym odczytywaniem danych. Służy do tego metoda e l e a r ( ) , która zeruj e także flagę końca pliku (EOF - zobacz rozdział 5 . ). Pamiętać trzeba, że
obiekt e i n zwraca fal se w wyniku tak ustawienia flagi błędnych danych, jak i flagi końca pliku. W rozdziale 1 7 . powiemy, j ak obie sytuacj e błędów od siebie odróżnić. Teraz przyj rzyjmy się kilku przykładom, w których pokazane zostanie użycie omawianych technik. że chcemy napisać program wyliczaj ący średnią wagę dziennego połowu ryb. Liczba ryb jest ograniczona do pięciu, więc wystarczy nam tablica 5-elementowa. Może się zdarzyć, że złapiemy jednak mniej ryb. W kodzie z listi ngu 6 . 1 3 użyto pętli kończonej , kiedy tablica j est pełna lub kiedy podana zostanie dana inna niż liczba. Załóżmy,
273
Rozdział 6 Instrukcje warunkowe i operatory logiczne -
Listing 6. 1 3. cinfish.cpp
cinfish. cpp dane n ieliczhowe km/czą wprowadzanie # i n c l u de < i o s t r e a m > con s t int M a x = 5 ; int ma i n ( )
/I
-
using name space s t d ; li pobierz dane
doubl e f i s h [ Ma x ] ;
cout cout < < " ryb c o u t:
< < " P r o s z ę poda ć w a g ę s w o i c h r yb . \ n " ; < < " Ma k s yma l n i e mo ż n a poda ć: " < < M a x < q k on c z y > . \ n " ; < < " r yba 1 . : " int i = O ; whi l e ( i < M a x & & c i n > > f i s h [ i ] ) if ( + + i < Max ) c o u t < < " r yba " . r yb a 1 . : 3 0 r yba 2 . : 3 5 r yb a 3 . : 2 5 r yb a 4 . : 4 0 ryba 5 . : q ś redn i a w a g a 4 ryb 32 . 5 G o t o we . Ma k s yma l n i e
=
Zwróćmy uwagę na poni ższy wiersz kodu : while
( i < Max & & c i n > > f i s h [ i ) )
Przypomnijmy, że jeśli lewa strona operatora logicznej koniunkcji jest fałszywa, C++ prawej strony już nie wyl icza. W takim razie wyliczanie prawej strony oznacza wczytywanie przez obiekt cin danych do tablicy. Jeśli i j est równe Max, wykonywanie pętli kończy się bez próby odczy tywania wartości. Powyższy przykład zakładał, że nie są wczytywane żadne dane znajdujące się p o liczbach i pierw szej danej nieliczbowej . Załóżmy, że chcemy pozyskać do programu dokładn ie pięć wyni ków golfowych i na ich podstawie wyznaczyć średnią. Jeśli użytkown ik poda dane niebędące liczba mi, program powinien zgłosić błąd i zażądać podan ia l iczby. Jak widzieli śmy, do sprawdzenia, czy dane są liczbami , możemy użyć wyrażenia z obiektem c i n . Załóżmy, że się okaże, że użyt kownik podał dane nieprawidłowe. Musimy teraz podjąć trzy krok i :
I . Wyzerować obiekt
c i n , a b y był gotów do przyj ęcia dalszych danych.
2. Pozbyć się błędnych danych wej ściowych.
3. Poprosić użytkown i ka o ponowne podanie wartości . Zauważmy, że program musi naj pierw wyzerować obiekt c i n , a dopiero potem może pozbywać się błędnych danych. Na listingu 6. 1 4 pokazano, jak to zrobić.
Listing 6.1 4. cingolf.cpp li cingo(f cpp - pomijanie danych niebędących liczbami
# i n c l ude < i o s t r e am > const int M a x
=
5;
int ma i n ( ) us ing name space s t d ; li pobierz dane
int g o l f [ Ma x ) ;
c o u t < < " P r o s z ę p o d a ć s w o j e w yn i k i w g o l f i e . \ n " ; c o u t < < " Mu s i s z p o d a ć w y n i k " < < Max < < " g i e r . \ n " ;
275
Rozdział 6 Instrukcje warunkowe i operatory logiczne -
int i ; for
i++ )
(i
O;
cout
using name space s t d ; void n_c h a r s ( char , int ) ; int m a i n ( ) int t ime s ; char e h ; c o u t < < " Po d a j c i n >> eh ; whi l e {
( eh
!=
'
znak : q ) '
"; li q to quit
c o u t < < " Po d a j l i c z b ę c a ł kow i t ą : " ; c i n > > t ime s ; n _ c h a r s ( eh , t i me s ) ; //funkcja dwuparametrowa c o u t > eh ; c o u t < < " Wa r t o ś ć z m i e n n e j c o u t > t e mp ; li błędne dane if ( ! cin )
(i + 1 )
> c l a s s_s i z e ; while ( c i n . ge t ( ) ! = ' \ n ' ) continue ;
";
s tuden t * p t r _ s t u = new s t ude n t [ c l a s s _ s i z e ] ; int e n t e r e d = g e t i n f o ( p t r _ s t u , c l a s s s i z e ) ; for ( int i = O ; i < e n t e r e d ; i + + ) d i s p l a y l ( p t r_ s t u [ i ) ) ; d i s p l a y 2 ( & p t r_ s t u [ i ] ) ; d i s p l a y 3 ( p t r _ s t u , e n t e re d ) ; delete [ ] p t r s t u ; cout < < " Go t o we \ n " ; return O ;
355
Rozdział 7 - Funkcje - składniki programów w (++
9. Zaprojektuj funkcj ę c a l c u l a t e ( ) , która pobierze dwie wartości typu double i wskaź nik funkcj i mającej dwa parametry double, zwracającej wartość double. Sama funkcja ca l c u la te ( ) też ma być typu double i ma zwracać wartość, którą wyliczy wskazywana funkcja po przekazaniu j ej obu parametrów c a l c u l a te ( ) . Załóżmy na przykład, że mamy następującą defin icję funkcj i add ( ) : doubl e add ( double
x,
double y )
{ re turn
x
+
y;
Wtedy wywołanie: double q = c a l c u l a t e ( 2 . 5 ,
10 . 4 ,
a dd ) ;
ma spowodować, że ca l e u la te ( ) przekaże funkcj i add ( ) wartości 2,5 oraz 1 0,4, a na stępnie zwróci wartość uzyskaną z add ( ) (czyli w tym wypadku 1 2,9). Użyj w programie opisanych funkcj i oraz przynajmniej jednej funkcji dodatkowej podobnej do add ( ) . Program ma w pętli umożliwić użytkowni kowi wprowadzanie par liczb, dla któ rych będzie wywoływana funkcj a c a l c u la te ( ) wywołująca add ( ) i przynajmniej jedną funkcję dodatkową. Osoby chcące wziąć się za bary z C++, mogą stworzyć tablicę wskaźni ków funkcji podobnych do add ( ) i w pętli stosować c a l c u l a te ( ) z tymi funkcj ami. Pod powiedź: deklaracja takiej tablicy z trzema wskaźnikami powinna wyglądać następująco: double
( * p f [ 3 ] ) ( doubl e ,
doubl e ) ;
Tablicę można zainicj alizować, korzystając z nonnalnej składni inicjalizacj i tablic, podając nazwy funkcj i j ako adresy.
356
Rozdział 8
Funkcie - zagadnienia zaawansowane W rozdziale zajmiemy się następujqcymi zagadnieniami: • Funkcje i n l i n e. • Zmienne referencyjne. • Przekazywanie funkcji parametrów przez referencję. • Parametry domyślne. • Przeciqżanie funkcji. • Szablony funkcji. • Specjalizacje szablonów funkcji. rozdziale 7. omalże m i mochodem dowi edzie liśmy się wiele o funkcjach w C++, ale jeszcze więcej jest przed nami. Funkcje C++ mają sporo nowych właściwości. któ ryc h nie było w starym, dobrym C. Tc nowe możliwości to funkcj e inline, przekazy wanie zmiennych przez referencję, wartości domyślne parametrów, przeciążanie funkcj i i sza blony funkcj i. W niniej szym rozdziale bardziej niż dotąd zajmiemy się tym i e l ementami C++, któn: nie występują w C, zatem rozdział ten to p ierwszy i s tot ny wypad w k i e nm ku dwóch plusów.
W
Funkcie
inl ine
Funkcje in lin e to rozs ze rz enie C++ stworzone z myślą o przyspieszeniu d z i a ł a 11 1 a p rogr amó w Podstawową różn icą m i ęd z y zwykłym i fun k cj a m i a fu n k cj ami inline n i c j est sposób i c h kodowa nia. ale metoda włączania ich do programu prz ez kompi lator. Aby zrozumieć r óż n i c ę mi ędzy zwykłym i funkcj a m i a fu n k cj a m i in l ine, m u s im y dok ł a d n i ej niż robil iśmy to dotąd, przyj rzeć się .
,
wrn;trznościom programu. Przystąpmy od razu do rzeczy.
Ostatecznym wynikiem procesu kompi lacj i jest program wykonywalny. który składa się wu
z zesta
instrukcj i języka maszynowego. Ki edy uruchamiamy program, system operacyjny ładuje wszyst kie te i n s t m k cje do pamięci komputera, tak że każda instrukcja ma własny adres w pamięci. N astęp n i e komputer kolejno wykonuje in strukcj ę po in strukcj i . Czasam i , k i ed y występuje na p rzyk ła d pętl a albo instrukcja wanmkowa. wykonywanie programu przeskakuje wprzód lub wstecz. pod inny adres. Wywołania zwykłych funkcj i oznaczaj ą skok programu pod inny a d re s \ adres funkcj i), a po wykonaniu całej funkcj i skok powrotny. Przyjrzyj my się typowej real izacj i rego
Rozdział 8 Funkcje - zagadnienia zaawansowane -
procesu nieco dokładniej . Kiedy program dochodzi do instrukcj i skoku funkcj i , zapamiętuj e ad res instrukcji znajduj ącej się za instrukcją skoku, kopiuj e parametry funkcj i na stos ( specj alnie przygotowany w tym celu fragment pamięci), skacze do miej sca w pamięci zawieraj ącego począ tek kodu funkcj i , wykonuje kod funkcj i (ewentualnie umieszczaj ąc wynik funkcj i w rejestrze), a następnie przeskakuje z powrotem do instrukcj i , której adres został zapisany wcześniej 1• Skoki wprzód i wstecz oraz zapamiętywanie lokalizacj i skoków zajmuj ą czas - jest to koszt wywoły wan ia funkcj i . Funkcje inline stanowią alternatywę d l a takiego rozwiązania. W funkcj i inline kod skompilowa ny jest włączony bezpośrednio w resztę programu - kompilator zastępuje wywołanie funkcj i całym jej kodem. Wobec tego program n i e musi przeskakiwać w inne miej sce a n i potem wracać. dzięki czemu funkcj e takie działaj ą nieco szybciej niż ich zwykłe odpowiedniki, ale ceną za to jest większe zużycie pamięci. Jeśli program w I O różnych miej scach wywołuje funkcję inline, w uzyskanym kodzie poj awi się I O kopii tej funkcj i (rysunek 8. 1 ).
int main ( )
I
int main ( ) ( - hubba . . .
- hubba
...
- hubba
)
.
. .
n • 2;
(2) ;
for (int i
(4) ;
=
O;
i
cout
( T ype
(blot
&
t) ;
t) ;
li szablon li specjalizacja biot
" spot s " } ;
recycle ( in k ) ;
li użycie specjalizacji
Termin najbardziej wyspecjalizowana nie musi oznaczać specj alizacj i j awnej ; wskazuj e raczej, że przy ustalaniu użytych typów kompilator musi przeprowadzi ć mniej konwersj i . Weźmy na przykład pod uwagę dwa przykłady szablonów :
408
' temp l a t e < c l a s s T ype > vo i d r e c y c l e
( T ype t ) ;
11 #1
temp l a t e < c l a s s T yp e > v o i d r e c y c l e
( T ype * t ) ;
11 #2
Przeciqżanie funkcji
Załóżmy, że program zawiera powyższe szablony oraz kod : struct blot blot i n k
=
{ int a ; char b [ l O ] ; ) ; " spots " ) ;
{ 25,
recyc l e ( & i n k ) ;
li adres struktury
Wywołanie r e c y c l e ( & i n k ) pasuj e do szab lonu # I , gdzie T ype j est interpretowany jako blot * . Wywołanie r e c yc l e ( & i n k ) pasuj e także do szab lonu #2, tym razem T ype to i n k . Powoduje to dwa niejawne utworzenia egzemplarzy, r e c yc l e < b l o t * > ( b l o t * ) oraz rec ycle ( b l o t * ) . Spośród dwóch powyższych szablonów bardziej wyspecjalizowany j est szablon rec yc 1 e < b 1 o t * > ( b l o t * ) , gdyż j ego generowanie wymaga mniej konwersj i . Wobec tego szablon # 2 wska zuje już jawnie, że parametr funkcj i był wskaźnikiem na typ Type, więc może być bezpośrednio utożsamiony z b l o t . Jednak w szablonie #I T yp e j est parametrem funkcj i , zatem T ype musi być interpretowany jako wskaźnik b l o t . Wobec tego w szablonie #2 T ype był specjalizowany jako wskaźnik i dlatego jest „bardziej wyspecjalizowany". Zasady znajdowania najbardziej wyspecjalizowanego szablonu to zasady uporządkowania częścio wego szablonów funkcji. Tak jak jawne tworzenie egzemplarzy są one nowym elementem C++.
Przykład zasad uporządkowania częściowego Teraz przyjrzyjmy się kompletnemu programowi używaj ącemu uporządkowania częściowego do decydowania, którego szabl onu należy użyć. Na listingu 8 . 1 4 pokazane są dwie definicje szab lo nów wyświetlających zawartość tablicy. Pierwsza definicja, szablon A, zakłada, że tablica zosta nie przekazana jako parametr z danymi do pokazania. Szablon B zakłada, że parametr tablicowy zawiera wskaźniki na wyświetlane dane. listing 8.14. tempover.cpp li tempover. cpp - przeciążanie szablonów
h n c l ude
< i o s t re am>
templa te < t yp e n ame T> void ShowA r r a y ( T a r r [ ] ,
li szablon A int n ) ;
templa te < t yp e n ame T > vo i d ShowAr r a y ( T * a r r [ ] ,
li szablon B int n ) ;
struct deb t s char name [ S O J ; doub l e amou n t ; );
409
Rozdział 8 Funktje - zogodnienio zoawonsowone -
i n t ma i n { vo i d ) u s i n g n a me s p a c e s t d ; int things [ 6 ]
=
( 13,
31,
103,
301,
310,
130 ) ;
s t ru c t deb t s m r _E [ 3 ] { " I ma Wo l f e " , 2 4 0 0 . 0 ) , { " U r a Fox e " , 1 3 0 0 . 0 ) , { " I by Stout " , 1 8 0 0 . 0 ) }; do u b l e * pd [ 3 ] ; li ustawienie wskaźników na pola amount struktur z tablicy Mr_E for
{ int i pd [ i ]
O;
= =
i < 3;
i++)
&mr _ E [ i ] . amoun t ;
c o u t < < " Wy l i c z a n i e r z e c z y p a n a E . : \ n " ; li things to tablica int S h o wA r r a y { t h i ng s , 6 ) ; ll używamy szablonu A c o u t < < " Wy l i c z a n i e d ł ug ó w p a n a E : \ n " ; li pd to tablica wskaźników na double S howAr r a y { p d ,
3) ;
li używa szablonu B (bardziej wyspecjalizowanego)
return O ;
temp l a t e < t yp e n ame T > vo i d S h owA r r a y { T a r r [ ] ,
int n )
{ u s i n g n ame s p a c e s t d ; c o u t s truct
cha f f dro s s [ 2 0 ] ;
char
slag;
int
); char
bu f f e r l [ 5 0 ] ;
char
bu f fe r 2 [ 5 0 0 ] ;
int
ma i n ( ) chaf f * p l , *p3 ,
int
*p2 ;
*p4 ;
li najpierw wersje zwykłe: pl
=
new
p3
=
new i nt [ 2 0 ] ;
cha f f ;
li przydział struktury w pamięci sterty li przydział tablicy int w pamięci sterty
li teraz wersje miejscowe: pl p3
( bu f f e r l )
new =
( bu f fer2 )
new
c h a f f ; li przydział struktury w pamięci bufora bufferl int [ 2 0 ] ;
li przydział tablicy int w pamięci bufora bujfer2
Dla uproszczenia w przykładzie tym przydzie l i l iśmy dwie tablice statyczne, które mają być wy korzystywane jako źródło przydziału w miejscowej wersj i operatora new. Kod ten przydziela następnie strukturę c h a f f w buforze b u f f e r l i tablicę 20 elementów typu i n t w pamięci bufora bu f f e r 2 . Znając już miejscową wersję operatora new, możemy przyjrzeć s i ę przykładowemu programowi. Program ten (patrz listing 9.9) wykorzystuje do tworzenia dynamicznie przydzielanych tablic obie znane nam wersje operatora new, ilustrując przy okazji pewne istotne różnice pomiędzy new zwy kłym a miejscowym - różnice te omówimy po obejrzeniu wyników działania programu: Listing 9.9. Plik newplace.cpp li newplace. cpp - stosowanie miejscowej wersji operatora new # i n c lude
< i o s t r e am>
# i n c l u de
li plik nagłówkowy miejscowej odmiany new
con s t int
BUF = 5 1 2 ;
con s t int
N
char
int
=
5;
bu f f e r [ BU F ] ;
li obszar pamięci do realizacji przydziałów
mai n ( ) u s i ng name space
std;
449
Rozdział 9 Model pamięci i przestrzenie nazw -
double * pd l ,
* pd 2 ;
int i ; c o u t < < " W ywo ł a n i e n e w z w y k ł e go i m i e j s c o w e g o : \ n " ; p d l = new double [ N ] ; li przydział w pamięci sterty pd2 = new ( b u f fe r ) double [ N ) ; li przydział w pamięci bufora for ( i = O ; i < N ; i + + ) pd2 [ i ) = pdl [ i ) = 1 0 0 0 + 2 0 . 0 * i ; c o u t < < " Ad re s y b u f o ró w : \ n " < < " s t e r t a : " < < p d l > r p . l n ame ;
";
void s h o w P e r s o n ( cons t Pe r s o n & rp ) s t d : : c o u t x ) )
c o u t < < " N a l e ż y wp r o w a d z i ć l i c z b ę : cin . clear ( ) ; whi l e ( c i n . ge t ( )
!=
";
' \n ' )
continua ; c o u t < < " P o d a n o " < < x using name space s t d ; void o t h e r ( ) ; void a n o t h e r ( ) ; int X = 1 0 ;
int y ; int mai n ( ) x
cout <
dys t a n s do p r z e j ś c i a
(k,
aby z a ko ń c z y ć ) :
"
;
t a rg e t )
{
c ou t < < " Podaj if
długość kroku :
";
( ! ( c i n >> d s t e p ) ) break ;
whi l e
( r e s u l t . ma g v a l ( )
< t a r ge t )
{
direction
=
rand ( )
s t ep . s e t ( d s t e p , result
=
% 360;
direct ion ,
'p' ) ;
r e s u l t + s t ep ;
s teps + + ;
581
Rozdział 1 1 Stosowanie klas -
c o u t > " ;
",
" > n am e ; c o u t < < n ame <
n > : : A r r a yT P ( const T
& v)
{
for
( int i = O ;
i < n;
i++ )
v;
ar [ i ]
template < c l a s s T , T & Ar rayTP
n > : : operator [ ] ( int i )
{ if
(i < o
I I
i >= n )
s t d : : c e r r < < " B ł ąd z a k r e s u t a b l i c y :
"
n > : : operator [ ] ( in t
i)
con s t
829
Rozdział 1 4 Wielokrotne użycie kodu -
if
= n l
std : : cerr
eggwe 1 gh t s ;
Deklaracje ta powoduj e zdefiniowanie przez kompilator klasy A r r a y T P < double , 1 2 > 1 urwo rzenie obiektu tej klasy o nazwie e g g w e i g h t s . Definiując klasę, kompilator zamienia T na doub le oraz n na 1 2 . Argumenty niebędące typami posiadaj ą pewne ograniczenia. Jego typem może być li czba całko wita. typ wyliczeni owy, referencja lub wskaźnik. Dlatego niepoprawne j est użycie double m. ale już doub l e & rm i double * pm są dozwolone. Ponadto kod szablonu nic może zmieniać wartości tego argumentu ani pobierać j ego adresu. W szablonie A r r a y T P niedozwolo ne są więc wyrażenia n + + lub & n . W momencie tworzenia egzemplarza szablonu wartość użyta jako argument n iebędący typem musi być wyrażeniem stałym. Określanie rozmiaru tab l i cy za pomocą argumentu ma jedną przewagę nad podejściem wykorzy stującym konstruktor. Konstruktor używa pamięci na stercie obsługiwanej za pomocą słów klu czowych new i de lete, podczas gdy argument n iebędący typem pozwala używać pamięci na stosie. Powoduje to szybsze działani e programu, szczególnie jeśli używasz wielu małych tablic. Główna wada podejścia wykorzystującego argumenty niebędące typami to generowanie różnych klas dla szablonów o różnych rozmi arach tabl i cy. Oznacza to, że deklaracje: A r r a y T P < double ,
12>
e g g w e i gh t s ;
A r r a y T P < double ,
1 3>
don u t s ;
powoduj ą powstanie dwóch odrębnych deklaracj i klasy. Z kolei deklaracje: S t a c k < in t >
eggs ( 1 2 ) ;
S t a c k < in t >
dun ke r s ( 1 3 ) ;
powoduj ą powstani e tylko j ednej deklaracj i klasy, a informacja o rozmiarze tabl icy przekazywa na jest do konstruktora tej klasy.
830
Szablony klas
Kolejna różn ica wiąże się z większą elastycznością podejścia wykorzystuj ącego konstruktor. pon ieważ rozmiar tabl icy jest w tym przypadku przechowywany jako składowa k lasy i nic jest na stałe wpisany w definicj ę klasy. Umożliwia to zdefiniowan ie przypisania tabl icy jednego roz rn iam do tab l icy innego rozm iaru lub utworzenie klasy pozwalającej na zmianę rozm iaru tabli cy.
Elastyczność szablonów Do szablonów klas możesz zastosować te same techniki co do zwykłych klas. Szablony klas mogą służyć jako k lasa bazowa. Mogą też być zawierane przez inne k lasy. Szabl ony mogą być argumentami typu d l a innych szablonów. Możesz na przykład zaimplementować szablon do ob sługi stosu za pomocą szab lonu do obsługi tabl icy. Możesz też wykorzystać szablon tab l i cy do utworzenia tablicy, której e lementami są stosy oparte na szablonie stosu. Poni żej znajduj ą s 1 .,; fragmenty kodu przedstawi aj ące te techn i k i : template < c l a s s T > class A r r a y private : T encry; t ;
template < c l a s s T y p e > class G r o wA r r a y
:
public A r r a y < T ype >
template < c l a s s
1
•
•
•
};
li Dziedziczenie
Tp>
class S t a c k Array a r ;
li Używa szablonu A rray < > jako li komponentu
} ;
Array < S t a c k < i nt> > a s i ;
li Tablica s/osÓl\' liczh calkowitych
ostatnim wyrażeniu musisz rozdzielić dwa symbole > przynajmniej jedną spacj ą, aby kompi lator nic pomylił pary nawiasów z operatorem > > .
W
Rekurencyine używanie szablonów Kolejny przykład wszechstronności szabl onów to możliwość używania ich za pomocą rekurencj i . Możesz na przykład zdefi niować następuj ącą tab l icę:
831
Rozdział 1 4 - Wielokrotne użycie kodu w C ++
A r r a y T P < A r r a yT P < in t ,
5> ,
1 0 > t w o de e ;
W wyniku tego wywołania powstanie tab lica t w o d e e , która zawiera dziesięć tab l ic, z których każda posiada pięć l i czb całkowitych. Deklaracj a odpowiednika tej struktury w przypadku zwy kłych tablic wygląda następuj ąco: int
twodee [ l O ] [ 5 ] ;
Zwróć uwagę, że w składni szabl onu wymiary poj awiaj ą się w kolej ności odwrotnej niż w przy padku składni zwykłych tablic. Program przedstawiony na l istingu 1 4. 1 8 wykorzystuj e tę techni kę, a także używa szablonu A r r a y T P do utworzenia tablic j ednowymiarowych do przechowy wania sumy i średniej każdego z I O zestawów pięciu liczb. Wywołanie metody c o u t . w i d t h ( 2 ) powoduje, że kolej ny element zostanie wypisany w polu o szerokości dwóch znaków, chyba że do wyświetlenia całej l iczby potrzeba więcej miejsca.
Listing 1 4. 1 8. twod.cpp li twod. cpp - tworzenie tablic dwuwymiarowych
# i n c l u de < i o s t r e am> # i n c l u de " a r r a y t p . h " int ma i n ( vo i d } { us ing s t d : : c o u t ; us ing s t d : : e n d l ; A r r a yT P < i n t , 1 0 > s um s ; A r r a y T P < double , 1 0 > a v e s ; A r r a yT P < A r r a yT P < in t , 5 > , int i , for {
1 0 > t w o de e ;
j;
(i = O;
i < 10;
i++ }
s um s [ i ] = O ; for ( j = O ; j < 5 ; j + + ) { twodee [ i ] [ j ] = ( i + 1 ) * s um s [ i ] + = t w o d e e [ i ] [ j ] ; ave s [ i ] for {
=
(i = O; for {
(j
( double } i < 10;
= O;
j
s um s [ i ]
< 5;
832
T2 > : : f i r s t ( )
re turn a ; temp l a te < cl a s s T l , T2
&
class T 2 >
Pa i r < T l , T 2 > : : s e c o n d ( )
{ re turn b ;
int ma i n ( ) us ing s t d : : c o u t ; us ing s t d : : e n d l ; us ing s t d : : s t r i n g ; Pair r a t i n g s [ 4 ]
Pa i r < s t r i n g ,
int> ( " Po d C z e r w o n ym B a r o n em " ,
Pa i r < s t r i n g ,
int> ( " S z yb k o i t a n i o " ,
Pa i r < s t r i n g ,
int> ( " S u f l e t y M a g dy " ,
Pa i r < s t r ing ,
int> ( " U G e r da " ,
5) ,
4) , 5) ,
3)
}; int j o i n t s cout ) ;
i++ )
cout < < rat ings [ i ] . second ( ) < < r a t i ng s [ i ] . fi r s t ( ) cout c l a s s T o p o
{ . . . ) ;
Powoduje to użycie przez kompilator typu int dla parametm T 2 , jeśli j ego wartosc zostan ie pominięta: Topo m l ; Topo m 2 ;
li TI jest typu double. T2 jest typu double li Tl jest
typu double.
T2 jest typu int
Biblioteka STL, opisana w rozdziale 1 6 . , często wykorzystuje tę właściwość, przy czym domyś l nym typem jest klasa. Chociaż możesz określać domyślne wartości parametrów typu szablonów k l asy, nie możesz robi ć tego w przypadku parametrów szablonów funkcj i . Możesz j ednak określać domyślne wartości parametrów niebędących typami zarówno w szabl onach klasy, jak i w szab lonach funkcj i .
Specializacia szablonu Szablony klas są podobne do szablonów funkcj i w tym, że możesz posiadać konk retyzacj e ni e jawne, konkretyzacje j awne oraz specjal izacje jawne, określane wspól nym mianem S/H'l/ali::a cji. Oznacza to, że szablon opisuj e k lasę w kategoriach typu ogólnego, podczas gdy specj a l i zacj a jest deklaracją kl asy utworzoną z a pomocą typu konkretnego.
Konkretyzacia
nieiawna
Przykładowe szablony prezentowane we wcześniej szych fragmentach tego rozdziału używaj ą konkretyzacji niejmvnej (ang. implicit insta11tiation). Polega ono n a deklarowaniu j ednego lub wi ę c ej obiektów określających żądany typ, a kompilator generuje konkretną definicj ę klasy, uży wając przepisu dostarczonego przez szabl on ogólny: A r r a yT P < in t ,
1 00>
s tu f f ;
li Niejawne tworze11ie egzemp larza
835
Rozdział 1 4 Wielokrotne użycie kodu w C ++ -
Dopóki program nie potrzebuje obiektu, kompilator n i e generuje niej awnej konkretyzacj i klasy: A r r a y T P < double , pt
=
new
30>
*
pt ;
A r r a y T P < double ,
30>;
li Wskaźnik - obiekt nie jest jeszcze potrzebny li W tym momencie obiekt jest p ot rze bny
Drugie wyrażenie powoduje wygenerowanie definicj i klasy przez kompilator, a także utworze nie obiektu według tej definicj i .
Konkretyzacia iawna Kompilator wygeneruje konkretyzację jawną (ang. explicit instantiation) deklaracj i klasy, kiedy zadeklarujesz klasę za pomocą słowa kluczowego temp l a te i określ i sz żądany typ lub typy. Deklaracj a powinna znaj dować się w tej samej przestrzeni nazw co definicja szablonu. Na przy kład wyrażen ie: template class ArrayTP< s t r i n g ,
1 0 0 > ; li
Generuje klasę ArrayTP
deklaruje klasę A r r a yT P < s t r i n g , 1 0 0 > . W tym przypadku kompilator generuje definicję klasy wraz z definicjami metod, chociaż żaden obiekt tej klasy nie został utworzony. Podobnie jak w przypadku konkretyzacj i niejawnej do tworzenia konkretnej klasy używany jest szablon ogólny.
Jawna specializacia Jawna specjalizacja to definicj a konkretnego typu lub typów, które maj ą zostać użyte zamiast
szablonu ogólnego. Czasem może zajść potrzeba modyfikacj i szablonu, aby działał inaczej , kiedy do jego konkretyzacj i służy dany typ . W takiej sytuacj i możesz posłużyć się specjalizacją jawną. Załóżmy, że zdefiniowałeś szablon klasy reprezentującej posortowaną tablicę, której elementy sortowane są w momencie dodawania do tabl icy: templ ate clas s
S o r t e dA r r a y li Szczegóły pominięto
} ;
Ponadto szablon ten używa operatora > do porównywania wartości. Takie rozwiązanie jest po prawne dla typów liczbowych. Jest ono poprawne także wtedy, gdy T reprezentuję klasę, w któ rej znajduje się definicja funkcj i T : : ope rator> ( ) , jednak rozwiązanie to nie będzie działać dla typu char * . W rzeczywistości szablon będzie funkcj onował, ale ciągi znaków zostaną posortowane według adresu, a nie al fabetycznie. Potrzebna jest wtedy definicj a k lasy, która uży wa funkcj i s t r c mp ( ) zamiast operatora >. W tym celu możesz określić jawną specjalizację sza blonu. Zamiast szablonu ogólnego korzysta ona z szablonu zdefiniowanego dla określonego typu. Kiedy kompilator stanie przed koniecznością wyboru szablonu ogólnego i skonkretyzowanego, a oba pasują do wywołania generującego egzemplarz, wybrana zostanie wersja skonkretyzowana.
836
Szablony klas Definicja skonkretyzowanej wersj i szablonu k l asy ma następującą postać: template
class
Na z wa Kl a sy
{ . . . };
Starsze kompilatory mogą rozpoznawać jedynie starszą postać, która nie zawiera wyrażenia template class
Na z wa Kl a sy
:
{ . . . };
Aby udostępnić szablon S o r t e dA r r a y w wersj i skonkretyzowanej dla typu char * , powi nieneś użyć nowej notacj i : template < > c l a s s
S o r t e dA r r a y < char
*>
{ li Szczegóły pominięto };
Tak zdefiniowany kod użyj e do porównywania wartości tablicy funkcj i s t r cmp ( ) zamiast ope ratora > . Wywołanie generujące szablon S o r t e dAr r a y dla typu char * spowoduje użycie definicji skonkretyzowanej zamiast definicj i ogólnej szablonu: S o r t edA r r a y < int>
scores ;
li Używa definicji ogólnej
S o r te dAr r a y < char
*>
li Używa definicji skonkretyzowanej
dates ;
Specjalizacja częściowa Język C++ pozwala na specjalizację częściową, co nieco ogranicza ogólność szablonu. Specjali zacja częściowa może udostępniać konkretny typ dla jednego z parametrów będącego typem: li Szablon ogólny template < c l a s s T l ,
class T 2 > class P a i r
{ . . . };
li Specjalizacja z parametrem T2 ustawionym na typ int template < c l a s s T l > c l a s s P a i r < T l ,
int>
{ . . . } ;
Nawiasy po słowie kluczowym templa te wskazuj ą parametry będące typami, które j esz cze nie są określone. Druga deklaracj a przypi suj e parametrowi T2 typ int, ale n i e określa wartości parametru T 1 . Zauważ, że określenie wszystkich typów prowadzi do pustej pary nawia sów i pełnej specjalizacj i j awnej : li Specjalizacja z parametrami TI i T2 określonymi jako typ int template < > c l a s s
Pa i r < int ,
int>
{ . . . } ;
Jeśli istnieje taka możliwość, kompilator wybiera zawsze najbardziej skonkretyzowaną wersj ę szablonu: Pai r p l ;
Pai r p 2 ;
P a i r p 3 ;
li Używa ogólnego szablonu Pair li Używa częściowej konkretyzacji Pair< Tl , int> li Używa jawnej konkretyzacji Pair< int, int>
Możesz też udostępnić częściową specjalizację istn i ej ącego szablonu tworząc specjalną wersj ę dla wskaźni ków:
837
Rozdział 1 4 Wielokrotne użycie kodu w (++ -
ternplate < cl a s s
} ;
ternplate < c l a s s class Feeb
li Wersja ogólna
T>
class F e e b {
li C::ę.friowa specjalizacja dla wska:':ników
T*>
li Zmodyfikowany kod
} ;
{
Jeś l i przekażesz j ako parametr typ niewskaźnikowy, kompilator użyj e wersj i ogólnej . Jeśli prze każesz wskaźnik, komp i l ator użyje częściowej specj a l i zacj i dla wskaźników: Fe e b < char>
li Używa ogólnego s::ablonu Feeb. Tjest typu char
fbl ;
F e e b < char * >
li Używa specjalizacji
fb2 ;
Feeb T*. T jest
typu char
Bez częściowej specj a l izacj i druga deklaracj a wiązałaby się z użyc iem szablonu ogól nego i zin terpretowaniem parametru T jako typu char * . Dzięki częściowej specj a l i zacj i kompi lator imerpretuje parametr T jako typ char i używa szablonu skonkretyzowanego. Częściowa specj a l izacj a pozwal a wprowadzić szereg ograniczeń. Możesz na przykład użyć na stępuj ących dekl aracj i : li S::ahlon ogólny
ternp l a te < c l a s s T l ,
class T2 ,
li Specjali::acja :: parametrem TJ
class T 3 > class T r i o {
.
warto.ki parametru T2 ternpl ate < c l a s s T l , c l a s s T 2 > c l a s s T r i o < T l , T 2 , T 2 > /I Specjali::acja :: parametram i T2 i T2 o wartości Tl * ternp l ate < c l a s s T l > c l a s s T r i o < T l , T l * , T l * > { . . . ) ;
.
.
) ;
I
.
o
.
.
} ;
Kompi lator korzysta z tych deklaracj i , wybierając odpowiednią wersję szablonu: T r i o < int , short , char * > t l ; T r i o < int , short> t 2 ; T r i o < char , char * , char *> t 3 ;
li Używa szablonu ogólnego li Używa szablonu Trio< Tl . T2. T2 > li Używa szablonu Trio< Tl. Tl *. Tl *>
Szablony iako składowe Kolejna nowa cecha szabl onów j ęzyka C++ to możliwość używania ich jako składowych struktu ry, klasy lub szablonu k lasy. Właściwość ta j est potrzebna do pełnej implementacj i biblioteki STL. Na l i sti ngu 1 4 . 2 0 znajduj e się krótki przykład szablonu klasy ze składowymi w postaci innego szablonu klasy i szabl onu funkcj i . Listing 1 4.20. tempmemb.cpp 11 tempmemb . cpp
# i ncl ude
-·
s::ahlony jako składowe
< i o s t r e am >
us ing s t d : : c o u t ; us ing
s td : : endl ;
ternp l a te < typenarne T > clas s be t a
838
Szablony klas
private : templa te < typename V> li Zagnieżdżony szablon klasy jako skladowa class h o l d private : V val ; publ i c : hold ( V v
0)
=
void s h o w ( ) V Va l u e ( )
:
val ( v ) {
con s t
const
{ )
cout li Metoda szablonu U b l ab ( U
u,
void S h o w ( )
T t) c on s t
{
re turn {
( n . Va l u e ( )
q . show ( ) ;
+
n . show ( ) ;
q . Va l u e ( ) )
* u
I
t;
)
);
int ma in ( ) 3) ;
beta g u y ( 3 . 5 ,
g uy . S h o w ( ) ; cout < < guy . b l a b ( l O , c o u t U blab ( U u,
T t) ;
void S h o w ( )
con s t
{
q . s how ( ) ;
n . show ( ) ;
) ;
li Definicja składowej template < typename T > template < typename V > class b e t a < T > : : h o l d private : V va l ; publ i c : hold (V v
0)
=
voi d s h ow ( ) V Va l u e ( )
:
val ( v )
con s t
con s t
{
{
{ )
s t d : : c o u t U be t a < T > : : b l a b ( U u ,
T t)
{ re turn
( n . Va l u e ( )
+ q . Va l u e ( ) )
* u I t;
Definicje muszą zinterpretować T , V oraz U j ako parametry szablonu. Ponieważ szablony są zagnieżdżone, musisz użyć składni : template < typename T > template < typename V >
zamiast: template< typename T ,
typename V>
Definicje muszą także określać h o l d oraz b l ab j ako składowe klasy b e t a < T > , do czego służy operator zakresu.
Szablony iako parametry Wiesz już, że szablony mogą posiadać p arametry będące typami, jak typename T , oraz para metry niebędące typami, jak int n. Szablon może też posiadać parametr, który sam jest szablo nem. Takie parametry to kolejna cecha używana do implementacj i biblioteki STL.
841
Rozdział 1 4 Wielokrotne użycie kodu w C ++ -
Listing 1 4. 2 1 przedstawia przykład użycia szablonu templ ate < typename T> class pko parametru. temp l a te < typename T > clas s jest tutaj typem. a T h i n g parametrem. Co t o oznacza? Załóżmy, ż e istniej e poniższa deklaracja: Thing
C rab
legs ;
Aby taka fonna została zaakceptowana, argument K i n g musi być szablonem klasy, której dekla racja pasuj e do parametru T h i n g szablonu: templ ate
< typename T >
clas s K i n g
{
.
. . } ;
Deklaracj a C r a b deklaruj e dwa obiekty: T h i n g < in t >
sl;
T h i n g < doub l e >
s2 ;
Wcześniej sza deklaracj a dla obiektu l e g s spowodowałaby zastąpienie T h i n g < int> przez K i n g < i n t > oraz T h i n g < doub l e > przez K i n g < doub l e > . Jednak deklaracja na l istingu
1 4. 2 1 wygląda następuj ąco : C rab< S t a c k>
n eb u l a ;
Z tego powodu w tym przypadku konkretyzacj ą T h i n g < int> jest S t a c k < in t > , a konkrety zacją Th i n g < doub l e > jest S t a c k < doub l e > . Podsumowując, parametr T h i n g jest zastę powany dowolnym typem szablonu, który zostanie podany j ako argument szablonu w momencie deklaracj i obiektu C r a b . Deklaracj a klasy C r a b wymaga trzech dalszych założeń odnośnie szablonu klasy reprezentowa nego przez parametr T h i n g . Klasa powinna posiadać metody p u s h ( ) i pop i ) udostępniaj ące określony interfej s. Klasa Cr ab może użyć dowolnego szablonu klasy. który odpowiada deklara cj i typu T h i n g oraz posiada wymagane metody p u s h ( ) i pop ( ) . W niniej szym rozdziale zde finiowaliśmy już taki szablon --- jest nim szablon S t a c k zdefiniowany w p l iku stacktp.h. W przy kładzie użyj emy właśnie tej klasy. Listing 1 4.21 . tempporm.cpp li tempparm. cpp -- szablony jako parametry
# i n c l ude # i n c l ude
< i o s t ream> " s t a c k tp . h "
templ ate < temp l a te < typename T > c l a s s T h i n g > class C rab private : Th i n g < in t > s l ; T h i n g < doubl e > s 2 ; publ i c : C r ab ( ) { } ; li Za/dada, że klasa Thing posiada składowe push() oraz pop() bool
842
p u s h ( int a ,
double
x)
I
return s l . pu s h ( a l
&&
s 2 . pu s h ( x ) ;
}
Szablony klas
bool pop ( int & a , double & x )
{
return s l . pop ( a )
&&
}
s 2 . po p ( x ) ;
.) ,·
int mai n ( ) using s t d : : c o u t ; us ing s t d : : c i n ; us ing s t d : : e n d l ; C r ab < S t a c k > n e b u l a ; li Stos musi pasować do argumentu template < typename T> class Thing int n i ; double nb ; cout
«
while if
" Poda j p a r y int double w p o s t a c i 4 3 . 5
( 0 O koń c z y wp i s ywa n i e } : \ n " ;
( ci n t > > n i >> nb & & n i > O & & n b > 0 ) ( ! nebu l a . pu s h ( n i , break ;
nb ) )
whi le ( n e b u l a . p o p ( n i , n b ) ) c o u t < < n i < < " , " < < nb < < e n d l ; cout c l a s s T h i n g ,
typename U ,
typename V> class C ra b private : Thing s l ; Thing s 2 ;
843
Rozdział 1 4 - Wielokrotne użycie kodu w C ++
Teraz typy składowych s 1 i s 2 są typami generycznymi , a nie na stałe wbudowanymi w kod. Deklaracja obiektu n e b u l a w programie wymaga wprowadzenia pewnych zmian: C rab< S t a c k ,
int ,
doub l e > n e bu l a ;
ll T = Stack. U = int. V = douhle
Parametr T określ a typ szablonu. a parametry U oraz V określaj ą typy nieszablonowe.
Szablony klas i zaprzyiaźnienie Deklaracj e szablonów k lasy także mogą posiadać przyjaciół. Można ich zaklasyfi kować do trzech kategori i : • Jednostki zaprzyj aźnione w postaci różnej o d szablonu. • Zależne szablony zaprzyj aźnione, w których typ przyjaciela określany j est przez typ klasy w momencie jej konkretyzacj i . • Niezależne szablony zaprzyj aźnione, w których wszystkie konkretyzacje szablonu s ą za przyjaźnione z wszystkimi konkretyzacjami klasy. Przyjrzyj my się teraz przykładom z wszystkich tych kategorii.
Funkcie zaprzyiaźnione spoza szablonu Zadeklarujmy zwykłą funkcję w szablonie klasy j ako zaprzyjaźnioną: template < cl a s s class
T>
Has Friend
friend void c ou n t s ( ) ;
li Zaprzyjaźniona
z
wszystkimi konkretyzacjami szah/01111
} ;
W wyniku tej deklaracj i funkcj a c o u n t s ( ) jest zaprzyjaźniona z wszystkimi możliwymi kon kretyzacjami szablonu; na przykład jest zaprzyjaźniona z klasami H a s F r i e n d < i nt> oraz H a s F r i end< s t ri ng>.
Funkcja c o u n t s ( ) n i e jest wywoływana d l a obiektu, ponieważ n i e jest funkcją składową, a także nie posiada parametrów w postaci obiektu. W jaki sposób funkcja uzyskuj e dostęp do obiektu H a s Fr i e n d? Istn i ej e kilka możliwośc i . Funkcja może korzystać z obiektu globalnego, może też korzystać z obiektu nieglobalnego za pomocą wskaźnika globalnego, może tworzyć własne obiekty, w kot'lcu może używać statycznych danych składowych szablonu kl asy. które istnieją niezależnie od obiektu.
844
Szablony klas
Załóżmy, że chcesz udostępnić w funkcj i zaprzyj aźnionej argument w postaci szablonu klasy. Czy możesz zadeklarować funkcj ę zaprzyj aźnioną w przedstawiony poniżej sposób? friend void repo r t ( H a s Fr i e n d
&) ;
li Możliwe ?
Nie jest to możliwe. Dziej e się tak, ponieważ w rzeczywistości obiekt H a s Fr i e n d nie istniej e . Istn iej ą tylko konkretyzacje, jak H a s F r i e n d< shor t > . A b y udostępnić argument
w
postaci
szablonu klasy, musisz określ i ć specj a l i zacj ę . Możesz na przykład użyć poniższej postaci : template < c l a s s T > class
Has Friend
friend void r e p o r t ( H a s F r i e n d < T >
&) ;
li Zależna fimkcja zaprzyjaźniona
};
Aby zrozumieć działanie tego mechanizmu, wyobraź sobie konkretyzację utworzoną w wyni ku
deklaracji obiektu danego typu: HasFrie nd
hf;
Kompilator zastępuj e parametr szablonu T typem int, w wyniku czego deklaracj a funkcj i za przyjaźnionej przyj muje poniższą fonnę: clas s
H a s F r i e n d < int>
fr i end void r e p o r t ( H a s Fr i e n d & ) ; li Zależna .funkcja zaprzyjaźniona };
Funkcja r e p o r t ( ) z parametrem H a s F r i e n d < i n t > staj e się zaprzyj aźniona z k l asą HasFri end. W ten sam sposób funkcja r e p o r t ( ) z parametrem H a s F r i end
staje się przeciążoną wersją funkcj i rapo r t ( ) i zaprzyjaźnia się z klasą H a s F r i e n d .
Zwróć uwagę, że repo r t ( ) nie jest szablonem funkcji. Jedynie jej parametr to szablon. Oznacza to, że musisz zdefiniować jawną specjalizację dla funkcj i zaprzyjaźnionych, których chcesz używać: void report ( Ha s F r i e n d void r e p o r t ( H a s F r i e nd
&)
&)
{
•
{
•
•
•
•
•
} ;
} ; li Jawna specjalizacja dla typu short li Jawna specjalizacja dla typu int
Techniki te przedstawione są na l istingu 1 4.22. Szablon H a s F r i e n d posiada składową sta tyczną e t . Oznacza to, że każda specjalizacj a klasy posiada własną składową statyczną. Metoda coun t s
{ ) , która jest zaprzyj aźniona z wszystki m i specj al izacjami szablonu H a s F r i e n d , prze
kazuje in formacj e o wartoś c i s kładowej
c
t dwóch o k re ś l o nych specj a l izacj i
-
Has-
F r i end oraz H a s F r i e n d . W programie znajdują się także dwie funkcje repo r t ( )
, każda zaprzyjaźniona z określoną specjalizacj ą szablonu H a s F r i e n d.
845
Rozdział 1 4 - Wielokrotne użycie kodu
w
C++
Listing 1 4.22. lrnd2tmp.cpp ll.frnd2tmp. cpp - szablon klasy z funkcjami zaprzyjaźnionymi # i n e l u de
< i o s t ream>
us ing
s td : : eou t ;
us ing
std : : endl ;
temp l a te < typename T > class
Has Friend
private : T i t em ; s ta t i c i n t
et ;
publ i c : H a s F r i e n d ( cons t T &
i)
-Has Fri end ( )
)
{
et-;
i t em ( i )
{
et++ ;
friend vo i d e o u n t s ( ) ; friend void r e p o r t s ( H a s F r i e n d < T >
&) ;
li Parametr
w
postaci szablonu
i ;
li Każda specjalizacja posiada własną statyczną daną składową temp l a te
< typename T >
int HasFri end : : e t =
O;
li Funktja (nie szablon) zaprzyjaźniona z wszystkimi klasami HasFriend< T> vo id e o u n t s ( ) c o u t < < " Ko n k re t y z a c j e i n t :
" eout &
e o u t < < " H a s F r i e n d < in t > :
"; «
" H a s F r i e n d :
&
hf}
" < < h f . i t em < < e n d l ;
Szablony klas
cout
h f i 2 ( 2 0 ) ; cout <
( ) ; cout
( ) ; re turn O ;
Poniżej znajduj e się wyn ik działania programu przedstawionego na l istingu 1 4.23 : Szablon
s i ze :
4;
S z ab l on coun t s ( ) :
O
10 20 10 . 5 Wyn i k
d z i a ł an i a
S z ablon Wyn i k
s i ze :
4;
działania
S z ablon
s i ze :
8;
c o u n t s < int> ( ) : S z ablon coun t s ( ) :
2
c o u n t s ( ) : S z ablon coun t s ( ) :
1
Jak widzisz. funkcja c o u n t s podaje inne wartości niż funkcja c o u n t s < int>, co stanowi i lustracj ę faktu, że każdy typ T posiada własną wersję funkcj i zaprzyjaźnionej co u n ts ( ) .
Niezależne szablony funkcii zaprzyiaźnione z szablonem klasy Ogran iczone szabl ony funkcj i zaprzyj aźn ionych opisane w poprzedni m punkcie to specjalizacja szablonów zadeklarowanych poza klasą. Na przykład specjalizacja klasy dla l i czb całkowitych współpracuje ze specjal izacją funkcj i dla l iczb całkowitych. Kiedy zadeklaruj esz szablon w kla sie, możesz tworzyć niezależne funkcj e zaprzyj aźnione, w przypadku których każda specjaliza cj a funkcj i jest zaprzyj aźniona z każdą specjalizacj ą klasy. Dla niezależnych szablonów zaprzy jaźni onych parametry typu szablonu zaprzyj aźn ionego są inne niż parametry typu szablonu klasy: temp l a te class
< typename T >
ManyFriend
template < typename C ,
typename D > friend void s h o w 2 ( C & ,
D &) ;
} ;
Na listingu 1 4.24 przedstawiony j est przykład zastosowania niezależnych szablonów zaprzyjaź nionych. W przykładzie tym wywołanie funkcj i s h o w 2 ( h f i 1 , h f i 2 ) dopasowywane jest do poniższej specjal izacj i :
850
Szablony klas
void s h o w 2
&,
Man y F r i e n d < int>
{ M a n y F r i e n d < i nt > & c ,
&>
Man y F r i e n d < int> & d ) ;
Ponieważ funkcja jest zaprzyj aźniona z wszystkimi specj a l i zacj ami szablonu M a n y Fr i e n d , posiada dostęp d o składowej i t e rn w dowolnej specjalizacj i , a l e używa j edynie obiektów typu Ma n y F r i e n d < i n t > .
Podobnie funkcja s h o w 2 { h f d ,
h f i 2 ) łączona j est ze specjalizacją:
void s h o w 2 < Ma n y F r i e n d < double >
&,
Ma n y F r i e n d < int>
{ M a n y F r i e n d & c ,
&>
Ma n y F r i e n d < int> & d ) ;
Także ta funkcja jest zaprzyjaźniona z wszystkimi specjalizacjami szablonu Ma n y F r i e n d. ale uży wa jedynie składowych i tern obiektów typu Man y F r i e n d oraz Ma n y F r i e nd . Listing
1 4.24. manylrnd.cpp
li manyfrnd. cpp
lli n c l ude
-
niezależny szablon zaprzyjaźniony z szablonem klasy
< i o s t r e am >
using s t d : : c o u t ; using s t d : : e n d l ; template < typename T > class Man y F r i e n d { priva te : T i t em ; public : Ma n y F r i e n d { co n s t T & template friend void show2 ( C & ,
D
&) ;
};
template < typename C ,
typename D> void s ho w 2 ( C & c ,
D & d)
{ cou t h f i 2 { 2 0 ) ; Ma nyFr i e n d h f db ( l 0 . 5 ) ; cout class Rev
li To samo co template
{ . . . } ;
Definicje (konkretyzacje) klasy generowane są, kiedy zadeklaruj esz obiekt tej klasy lub okre ślisz konkretny typ. Na przykład deklaracja: clas s
I c < short>
sic;
li Konkretyzacja niejawna
powoduje, że kompi lator generuje deklaracj ę klasy, w której każde wystąpienie parametm typu T jest zastępowane rzeczywistym typem short. W tym przypadku nazwą klasy j est I c < short>. a nie samo I c . I c < short> nazywane jest specjalizacją szab lonu. W tym szczególnym przy padku jest to konkretyzacj a niej awna. Jawna konkretyzacja ma miej sce, kiedy deklarujesz określoną specjalizacj ę klasy za pomocą słowa kluczowego temp l a te : template c l a s s
I c < int> ;
li Konkretyzacja jawna
W tym przypadku kompilator używa szablonu ogólnego do utworzenia specjalizacj i I c < int> dla typu int, chociaż w programie nie ma j eszcze żadnego wywołania tworzącego obiekt tej klasy.
Możesz udostępnić j awne specj a l i zacj e , które są skonkretyzowanym i d e k l aracj am i k l a s przesłaniającymi definicj ę szablonu. Możesz po prostu zdefiniować klasę, używając wyrażenia templa te< > , a następnie użyć nazwy szablonu klasy razem z ostrymi nawiasami zawieraj ącymi nazwę typu, który określa specjal izację. Możesz na przykład utworzyć skonkretyzowaną klasę I e dla wskaźników na znaki : template < > c l a s s s
I c < char * >
{
char * s t r ; public : I c ( const char * s )
str ( s )
{ }
};
853
Rozdział 1 4 - Wielokrotne użycie kodu
w
C ++
Wtedy deklaracja w postac i : clas s I c < char * > c h i c ;
wykorzysta definicj ę skonkretyzowaną, a nie szablon ogólny. Szablon kl asy może określać więcej niż j eden typ generyczny. Może także posiadać parametry ni ebędące typami : templ ate < c l a s s T , clas s P a l s
class TT ,
int n >
{ . . . } ;
Deklaracj a : Pa l s < doub l e ,
string,
6 > mix ;
generuje konkretyzacj ę niej awną, używając typu doubl e d l a parametru T, typu s t r i n g dla parametru TT oraz wartości 6 dla parametru n . Szablon kl asy może też posiadać parametry, które s ą szablonam i : template < temp l ate < typename T > c l a s s C L , class T rophy
typename U ,
int z >
{ . . . } ;
W tym miej scu z to wartość typu int, U to nazwa typu, a CL to szablon klasy zadeklarowanej za pomocą wyrażenia temp l a te < typename T > . Szablony k l a s mogą podlegać części owej specjalizacj i : T,
( . . . } ;
templ ate < c l a s s T>
Pa l s < T ,
templ ate < c l a s s T ,
clas s TT> Pa l s < T ,
10>
template < c l a s s T ,
int n > P a l s < T ,
T* ,
TT ,
100>
n>
( . . . } ;
{ . . . };
Pi erwsza deklaracj a tworzy specj a l i zację, w której oba typy są takie same, a n posiada wartość 1 0 . Druga deklaracj a tworzy specjal izacj ę d l a parametrn n o wartości 1 0 0 , a trzecia deklaracja tworzy specjalizację, w której drngi typ jest wskaźnikiem na pierwszy typ. Szablony klas mogą być składowymi innych klas, struktur i szablonów. Celem wszystkich tych technik jest umożliwienie powtórnego wykorzystania przetestowanego kodu bez potrzeby kopiowania go. Upraszcza to programowanie i powoduje, że programy są mniej zawodne.
Pytania sprawdzaiące 1.
854
Określ dla każdej grupy klas, czy klasa w kolumnie B powinna być dziedziczona publ icznie czy prywatnie. A
B
class W i l k
c l a s s Wil kSte powy
class K u c h n i a
c l a s s M i eszkanie
class Osoba
class Prog ram i sta
class Osoba
class K o n l Dzokej
class Osoba, class Auto
class Kierowca
Pytania sprawdzajqce 2. Przyjrzyj się poniższym definicj om :
{
class Fra b j o u s priva te :
char f a b [ 2 O J ; publ i c : =
"C++ " )
F r a b j o u s ( const char *
s
virtual void t e l l ( )
cout < < fab ;
fab ( s )
{ }
}
} ; class G l o am
{
private : int g l i p ; F r ab j o u s f b ; publ i c : G l o am ( int g
O
=
,
con s t char * s
const F r ab j o u s
G l o a m ( int g ,
&
=
"C++" ) ;
f) ;
void t e l l ( ) ; };
Wiedząc, że wersj a metody t e l l ( ) w klasie G l o am powinna wyświetlać wartości składo wych g l i p oraz fb, napisz definicj e trzech metod klasy G l o am. 3. Przyjrzyj się pon iższym definicjom: {
class F r a b j o u s private :
char f a b [ 2 O J ; publ i c : Frab j o u s ( cons t char * s virtual void t e l l ( )
=
fab ( s )
"C++ " )
cout < < fab ;
{ }
}
} ; class G l o a m
:
private F r ab j o u s
{
private : int g l i p ; publ i c : G l oam ( in t g G l oam ( in t g ,
=
O
,
con s t char * s
con s t F r a b j o u s
&
=
"C++" ) ;
f) ;
void t e l l ( ) ; };
Wiedząc, że wersja metody t e l l ( ) w klasie G l o am powinna wyświetlać wartości składo wych g l i p oraz f a b , napisz definicj e trzech metod klasy G l o am. 4. Załóżmy, że napisałeś poniższą definicj ę opartą na szablonie s t a ck z listingu 1 4. 1 3 oraz na klasie W o r k e r z listingu 1 4. 1 O : S t a c k< Wo r k e r
*>
sw;
855
Rozdział 1 4 - Wielokrotne użycie kodu
w
C ++
Napisz deklarację klasy, j aka zostanie wygenerowana. Skoncentruj się na samej deklaracji i nie wypisuj metod, które nie są i n line. 5 . Użyj definicj i szablonów z tego rozdziału do utworzenia definicj i : • tablicy obiektów typu s t r i n g , • stosu tablic typu doub l e , • tabl icy stosów wskaźników na obiekty klasy Wo r k e r . Ile definicj i szablonów k l a s zostani e utworzonych w wyni ku wykonania programu z listingu 1 4 . 1 8'1 6. Opisz różnice m iędzy wirtualnymi a niewirtualnymi klasami bazowymi . ,
Cwiczenia programistyczne I.
Klasa W i n e posiada obiekt składowy typu s t r i n g (patrz rozdział 4.). który przechowuje nazwę wina. oraz obiekt typu P a i r (opisany w tym rozdziale) zawierający obiekty typu va l a r r a y < i n t > (także opisane w tym rozdziale). Pierwsza składowa każdego obiektu Pa i r przechowuje roczni k wina, a druga składowa przechowuj e liczbę posiadanych butelek danego rocznika. Na przykład pierwszy obiekt v a l a r r a y obiektu P a i r może przechowy wać l ata 1 9 8 8 , 1 992 i I 996, a drugi obiekt va l a r r a y może przechowywać l i czbę butelek 24, 48 i 1 44. Kl asa W i n e może też posiadać składową w postaci l iczby całkowitej , która przechowuj e l iczbę roczników. Do ułatwienia pisania kodu mogą Ci się przydać pon iższe definicj e typu: typede f
s t d : : va l a r r a y < in t >
typede f
P a i r
P a i rA r r a y ;
Dzięki temu nazwa P a i r A r r a y reprezentuj e typ P a i r < s t d : : v a l a r r a y < i n t > , s t d : : v a l a r r a y < int> > . Zaimplementuj klasę W i n e , używając mechanizmu zawiera nia. Klasa powinna posiadać konstruktor domyślny oraz przynajmniej wymienione poniżej konstruktory: li lnicjali=ltje składową label parametrem I, liczbę lat parametrem y, li roczniki parametrem yr[}, składową bottles parametrem bot[}
W i n e ( const char * 1 ,
int y ,
cons t int y r [ ] ,
cons t int b o t [ ] ) ;
li lnicjalizitje składową label parametrem I, liczbę lat parametrem y, li tworzy tablicę obiektów o długości y
W i n e ( cons t char *
1,
int y ) ;
Klasa W i n e powinna posiadać metodę G e t B o t t l e s ( ) , która przyj muj e obiekt Wi n e o liczbie roczników równej y i prosi użytkownika o podani e odpowiednich roczników oraz liczby butelek każdego rocznika. Metoda L a b e l ( ) powinna zwracać referencj ę do nazwy wina. Metoda s um ( ) ma zwracać łączną liczbę butelek w drugim obiekcie va l a r ra y< int> obiektu Pa i r . Program powinien prosić użytkownika o podanie nazwy wina, l iczby elementów tablicy oraz rocznika i l iczby butelek dla każdego e lementu tablicy. Dane te powinny służyć do utworze856
Ćwiczenia programistyczne nia obiektu W i n e oraz wyświetlenia przechowywanych w nim danych. Poniżej przedstawio ny j est przykładowy program testowy:
11 pe1 4_ l .cpp - używa klasy Wine karzystajqcej z zawierania # i n c l u de
< i o s t re am >
# i n c l u de
" winec . h "
int ma i n
( vo i d )
u s ing s t d : : c i n ; us ing s t d : : c o u t ; us ing s t d : : e n d l ;
c o u t < < " Po d a j
nazwę wina :
";
char l ab [ 5 0 l ;
c i n . g e t l i n e ( l ab , cout <
> yrs ; W i n e h o l d i n g ( l ab ,
y r s ) ; li Zapisuje nazwę, liczbę roczników i przekazuje li informację o tej liczbie do tablicy
holding . GetBott l e s ( ) ;
li Pobiera dane o roczniku i liczbie butelek
holding . Show ( ) ;
li Wyświetla zawartość obiektu
con s t int
YRS
3;
=
int y [ Y R S ]
=
{
1993,
int b [ YR S ]
=
{
48,
1 995 ,
60,
72
1 998
} ;
};
li Tworzy noiry obiekt, inicjalizuje za pomocą danych w tablicach y oraz b
W i n e mo r e ( " G u s h i n g G r a p e Re d " ,
YRS ,
y,
b) ;
ma re . S h o w ( ) ; cout i tem =
li Tworzy obiekt Node
NULL ;
W kodzie wartości przypisywane sąj awnie do składowych klasy N o d e po utworzeniu obiektu tej klasy. Są to operacj e , które powinny być wykonywane w konstruktorze. Wiedząc gdzie i j ak użyć konstruktora, możesz utworzyć jego poprawną definicj ę : class Queue li Dąfinicje w zakresie klasy li Node to klasa zagnieżdżona, lokalna dla klasy Queue
class Node publ i c : I t em i t e m ; N o de * n e x t ; N o d e ( cons t I t em & } ; ) ;
876
i)
i t em ( i ) ,
next ( O )
{ }
Klasy zagnieżdżone
Konstruktor inicj a l izuje składową i t e rn za pomocą zmiennej i oraz ustawia wskaźnik n e x t:. na O . co jest jednym ze sposobów zapisywania wskaźników pustych w j ęzyku C++. Używanie N U L L wymagałoby dołączen ia pliku nagłówkowego z definicją N U L L . Ponieważ wszystkie obiekty N o d e tworzone przez klasę Q u e u e posiadają początkowo wskaźni k pusty, jest to j edyny konstruktor potrzebny w klasie N o d e . Następnie musisz przepisać metodę
enqueue,
b o o l Q u e u e : : e n q u e u e ( cons t if
używając konstruktora:
I t em
&
i t em )
( i s fu l l ( ) l re turn fal s e ;
Node if
* add
( a dd
==
=
li Twor::y i in icjali::uje Node
new N o d e ( i t e rn ) ;
0) li Jeśli n iemożliwe, ko1iczy działan ie
return fal s e ;
Rozwiązanie to skraca nieco kod metody e n q u e u e ( ) , a także jest bezpieczniejsze, ponieważ inicjalizacja staje się automatyczna i programista nie musi o niej pamiętać. W przykładzie konstruktor zdefiniowany jest w deklaracj i klasy. Załóżmy. że chcesz zdefiniować go w pliku implementacj i . W takiej sytuacj i definicj a musi odzwierciedlać fakt, że kl asa N o d e jest zdefiniowana w klasie Q u e u e . W tym c e l u dwukrotnie użyty j e s t operator zakresu : Q u e u e : : N o d e : : N o d e ( cons t
I t em
&
i)
:
i t em ( i ) ,
next ( O )
{ }
Dostęp do klas zagnieżdżonych Sposób dostępu do klas zagnieżdżonych wynika z dwóch elementów. Po pierwsze, miejsce deklaracji klasy zagnieżdżonej wpływa najej zasięg, czyli miejsca w programie, w których można tworzyć obiekty tej klasy. Po drugie, jak w przypadku każdej klasy, sekcje publiczna, chroniona i prywatna klasy za gnieżdżonej kontrolują dostęp do składowych tej klasy. Gdzie i jak można używać klasy zagnieżdżonej zależy więc od jej zasięgu i kontroli dostępu. Zastanówmy się teraz nad tymi zagadnieniami.
Zasięg Jeśli kl asa zagnieżdżona jest zadeklarowana w sekcj i prywatnej innej k lasy, j est ona dostępna tylko w tej klasie. Dotyczy to na przykład kl asy N o d e zagnieżdżonej w deklaracj i k l asy Q u e u e w poprzednim przykładzie. Wprawdzie klasa N o d e zdefiniowana jest przed sekcj ą prywatną. należy jednak pamiętać, że p rywatny poziom dostępu jest w klasach domyślny - d l atego składo we klasy Q u e u e mogą używać obiektów N o d e i wskaźników na obiekty tego typu, ale w innych miejscach programu nie j est to możliwe. Jeśl i utworzyłbyś klasę pochodną klasiy Q u e u e , k l asa Node byłaby w niej niewidoczna, ponieważ klasa pochodna nie posiada bezpośredniego dostępu do składowych prywatnych k lasy bazowej . 877
Rozdział 1 5 Zaprzyjaźnienie, wyjqtki i nie tylko -
Jeśli klasa zagnieżdżona jest zadeklarowana w sekcj i chronionej , jest dostępna w swojej klasie zawierającej , ale nie poza n ią. Jednak w tym przypadku w klasie pochodnej istniałby dostęp do klasy zagni eżdżonej i możliwe byłoby bezpośrednie tworzen ie obiektów tego typu. Jeśli klasa zagnieżdżona j est zadeklarowana w sekcj i publ icznej innej klasy, j est dostępna w tej klasie. w j ej klasach pochodnych oraz, ponieważ jest publiczna. także poza tymi klasami. Jednak klasa zagnieżdżona znajduj e się w zakresie innej kl asy, dlatego poza klasą zawieraj ącą należy z niej korzystać za pomocą kwa l i fikatora tej klasy. Załóżmy, że napisałeś poniższą deklarację: clas s T e a m publ i c : clas s Coach
{
.
.
.
} ;
} ;
Następnie załóżmy, że poj awił się trener bez posady. który nie opiekuje się żadną drużyną. Aby utworzyć obiekt C o a c h poza klasą T e a m, musisz użyć składni : Team : : C o a c h
forhire ;
li Tworzy ohiekt Coach poza klasą Team
Rozważania związane z zasięgiem dotyczą również zagnieżdżonych struktur i wy liczei1. Wiciu programistów tworzy pub l i czne wyl iczenia, żeby określić stałe klasy. które mogą być używane przez j ej użytkown ików. Wiele implementacj i klas zdefiniowanych do obsługi strumieni wejścia i wyj ścia używa tej techniki. udostępniaj ąc różne możliwości formatowania. Niektóre z nich już widziałeś, a więcej na ten temat możesz przeczytać w rozdziale 1 7 „Obsh1ga wej ścia. wyjścia oraz pli ków". W tabel i 1 5 . 1 znaj duj e się zestawienie właściwości zasięgu zagnieżdżonych klas. struktur i wyliczei1. .•
Tabela 1 5. 1. Właściwości zasięgu zagnieżdżonych klas, struktur i wyliczeń W któ rym m i ejscu
kl asy zaw i e rającej znaj duje s i ę dekla racja
D ostę p n a w klasie zawi e rającej
Dostępna w klasie pochodnej od kl asy zawi e rającej
Dostęp n a poza h i e ra rch i ą klasy zawi e rającej
Sekcja p rywatna
Tak
Nie
Nie
Sekcja chroniona
Tak
Tak
Nie
Sekcja p u b l i czna
Tak
Tak
Tak , z kwal ifi katorem klasy
Kontrola dostępu Oprócz sekcj i klasy zawierającej drugim istotnym czynnikiem jest poziom dostępu w klasie za gnieżdżonej . Obowiązuj ą tu te same zasady co w przypadku zwykłej klasy. Zadeklarowanie kla sy Node w klasie Q u e u e nie oznacza, że klasa Q u e u e posiada specjalny dostęp do danych klasy N o d e lub że klasa N o d e posiada specj alny dostęp do danych klasy Qu e u e . Obiekt klasy Q u e u e posiada bezpośredni dostęp j edyni e do składowych publ icznych obiektu N o d e i dlatego
878
Klasy zagnieżdżone
w przykładzie klasy Q u e u e wszystkie składowe klasy Node są publiczne. Namsza to powsze chn ą praktykę deklarowan ia danych s kładowych jako prywatnych, ale klasa N o d e j est wewnętrznym e lemen tem implemen tacj i klasy Q u e u e i nie j est dostępna poza tą klasą, ponieważ zadekl aro wana jest w sekcj i prywatnej klasy Q u e u e. Z tego powodu metody klasy Q u e u e mogą bezpo ś redn io używać s kładowych klasy N o d e, ale nie mogą tego robić metody spoza klas y. Podsumowuj ąc. miej sce deklaracj i klasy determi nuje j ej zasięg ( widoczność). Jeśli dana klas a zagn ieżdżon a j es t widoczn a, dostęp d o j ej składowych określony j est według zwykłych zas ad w oparciu o słowa kluczowe publ i c, pro tected, private oraz friend.
Zagnieżdżanie
w
szablonie
Przekon ałeś się j uż, że szabl ony są dobrym mechanizmem do implementacj i klas kontenero wych, jak klas a Q u e u e . M oże sz się zastanawiać, czy posiadanie klasy zagnieżdżonej utrudnia pr ze kształcenie definicj i klasy Q u e u e w szablon. Na szczęście nie. Na listin gu 15.5 przedsta wiony jes t s posób przekształcenia takiej klasy. Jak się to często zdarza w przypadku s zablonów. plik nagłówkowy zawiera szablon klasy wraz z metodami.
Listing 15.5. queuetp.h li que ue.h - szah /on Q ue ue
z
klasą zagnieżdżo ną
#1fndef QUEUETP_H_ #defin e
QUEUETP_H_
templ a t e
class Q u e u eTP private : enum { Q_SIZE
=
1 01;
li Definicja klasy zagnieżdżonej Node
class Node publi c : Item i t em ; Node * n e xt ; I;
Node ( con s t I t e m &
Node *
fro n t ;
Node
rea r ;
*
i)
item ( i ) ,
n e xt ( O )
li
li Wskaź nik na przód kolejki li Wskaźnik na tył kolejki
int i t ems ;
li Aktualna liczba elementów w kolejce
const int ą s i ze ;
li Maksymalna liczha e lementów
Qu eu eTP ( const Q u e u e TP & ąl
:
ą s i ze ( 0 )
Queu eTP & operator= ( con s t Q u e u eTP & q )
w
kolejce
{} {
re turn * thi s ;
public : Queu eTP ( int u s
=
Q_SIZE ) ;
879
Rozdział 1 5 - Zaprzyjaźnienie, wyjątki i nie tylko - Q u e u e TP ( ) ; bool i s emp t y ( )
con s t
{
==
return i t em s bool i s f u l l ( )
O;
cons t
1
==
re turn i t ems int ąu e u eco u n t ( )
ąsize ;
con s t
{
return i t ems ; bool enqueue (const I tem & it em ) ;
I;
bool dequeue ( I tem & i tern) ;
li Dodaje element na koniec kolejki li Usuwa element
z
początku kolejki
li Metody klasy QueueTP
temp l a te
< cl a s s
I t e m>
Q u e u eTP < It e m > : : QueueTP ( in t q s ) fron t
rea r
i t ems
O;
templ ate
< cl a s s
qs i ze ( qs )
O;
Item>
Q u e ueTP< Item > : : -QueueTP ( )
* t emp ;
Node whi l e
( fr o n t
t e mp
=
fr o n t
=
!
=
0)
f r on t ; fro n t- > n ext ;
de l e te t emp ;
li Dopóki kolejka nie jest pusta li Zapisuje adres początku kolejki li Ustawia wskaźnik na następny element li Usuwa dawny początek
li Dodaje element do kolejki templ ate bool {
if
QueueTP< I tem> : : enąueue ( cons t I t e m
( i s fu l l ( l ) re turn fal s e ;
Node
if
880
& i tem)
•
add
(a cid
==
=
new Node (i t em) ; li Tworzy elem ent
NULL )
Klasy zagnieżdżone return fal se ; i t ems + + ; if ( f r o n t = = 0 ) front = add ; el se rea r - > n e x t a dd ; rea r = add ; return true ;
li Jeśli to niemożliwe, kończy działanie li Jeśli ko lejka jest pusta
li Dodaje element na początek kolejki li W przeciwnym razie dodaje na ko niec li Koniec kolejki wskazuje na dodany element
li Um ieszcza początko wy element w o biekcie item i usuwa
template < c l a s s I t em > bool Qu e u e T P < I t em > : : de ą u e u e ( I t em & {
if ( f r o n t == 0 ) return fal s e ; i t em = f r o n t - > i t em ; i t ems- ; Node * t e mp = f r o n t ; front = f r o n t - >next ; delete t emp ; if ( i t ems == 0 ) rea r = O ; return true ;
z
kolejki
i t em )
li Ustawia item na pierwszy element kolejki li Zapisuje adres pierwszego elementu li Ustawia początek na następny element li Usuwa poprzedni początek
#endi f
Ciekawostką na listingu 15.5 j est fakt zdefiniowania klasy Node w kategori ach generycznego typu I t em. W ten sposób deklaracja: Qu eueTP
dą ;
powoduj e, że klasa Node może przechowywać elementy typu double, podczas gdy deklaracj a : Qu euTP c ą ;
pozwala przechowywać elementy typu char. Te dwie klasy Node zdefiniowane s ą w dwóch różnych klasach Que u e T P i dl atego nie powoduj ą one konfl iktu nazw. Jeden typ elementów to Qu eueTP : : N o d e , a drugi to Q u e u e T P < char> : : N o d e . Na listingu 15.6 przedstawiony jest krótki program do testowania nowej k lasy.
881
Rozdzial 15 Zaprzyjaźnienie, wyjątki i nie tylko -
Listing 15.6. nested.cpp 1/ 11estedcpp
---
11żyll'a kolejki :: klasą ::agnie:':dżoną
#1nclude
#include
#include
"queuetp.h"
mai n ( )
int
using
std::string;
using
std::cin;
using
std: :cout;
QueueTP cs(5); string temp; while(!cs.isfull())
{
cout
#i n c l ude < c s t d l i b > double hme a n ( double a ,
doubl e b ) ;
int ma i n ( ) doubl e x ,
y,
z;
s t d : : c o u t < < " Po d a j dw i e l i c z b y : whi l e ( s t d : : c i n > > x > > y )
";
z = hme a n ( x , y ) ; s t d : : c o u t < < " Ś r e dn i a h a rmon i c z n a l i c z b " kl asy i s t r eam używa pewnej odmiany tej techniki. I n formując funkcj ę wywołuj ącą o powodzen iu lub błędzie, możesz umożliwić zachowan ie inne od natychmiastowego zakończenia programu . Przykład tego podej ścia przedstawiony j est na listingu 15.8. Funkcj a hme a n () zwraca true lub fal se, co odpowiada powodzeniu lub błędowi . Został także dodany trzeci argumen t. który pozwala pobrać wynik działan ia funkcj i .
Listing 15.8. error2.cpp li error2.cpp - zwracanie kodu błędu
# i n c l ude
< i o s t r e am >
# i n c l u de < c f l o a t > // (/ub.floal. h) dla DBL_MAX bool hme a n ( double a ,
double b ,
double *
ans ) ;
int mai n ( ) double x ,
y,
s td : : c o u t < < while
z; " Poda j
dw i e l i c z b y :
";
( s td : : c i n > > x > > y )
885
Rozdział 1 5 Zaprzyjaźnienie, wyjqtki i nie tylka -
if
( hme a n ( x ,
y,
&z) )
s td::c o u t < < " Ś redn i a h a rmon i c z n a l i c z b " <
x >> y)
";
whi l e
try
li Początek bloku try
{
z
=
catch {
hme a n ( x , y ) ; li Koniec bloku try ( cons t char * s )
li Początek bloku catch
s t d : : c o u t < < s > x > > y ) try { z
=
"; li Początek bloku fly
hme a n (x ,
yJ ;
c o u t < < "Sredn i a h a rmon i c z n a l i c z b " <
= MONTH S )
throw b a d _ i n dex ( i ) ; return g r o s s [ i ] ;
&
double if(i
S a l e s : :operator [ ] (int i ) < O I I
throw ( s t d : : l o g i c _ e r r o r )
i > = MONTH S )
throw b a d _ i n de x ( i ) ; return g r o s s [ i ] ;
Labe l e d S a l e s : : nb a d i n d e x : : nb a d i n de x ( cons t char * const char s ) : S a l e s : : ba d_i n dex ( i x , s l
lb,
int
ix ,
*
s td : : s t rcpy ( lb l ,
lb ) ;
Labe l e d S a l e s : : L abe l e dS a l e s ( const char * l b , : S a l e s ( yy ) s t d : : s t r e p y ( l ab e l ,
gr,
f
if
i)
i > = MONTH S )
throw n b a d_ i n de x ( Labe l ( ) ,
91 2
const double * g r ,
int � )
lb ) ;
Labe l e dS a l e s : : operator [ ] ( int
(i < O I I
int yy ,
n)
s t d : : s t r c p y ( l ab e l ,
double
yy )
lb ) ;
Labe l e d S a l e s : : La b e l e d S a l e s ( cons t char * l b ,
: S a l e s ( yy ,
int
i) ;
cons t
th row ( s t d : : l o g i c_e r r o r )
Wyjqtki
re turn S a l e s : : operator [ ] ( i ) ;
double if
&
Labe l e dS a l e s : : operator [ ] ( in t
(i
< O
I I
i)
throw ( s t d : : l o g i c _ e r r o r )
i > = MONTH S )
throw n b a d_i n dex ( Labe l ( ) ,
i) ;
return S a l e s : : operator [ ] ( i ) ;
Na listingu 1 5 . 1 6 klasy te wykorzystane są w program ie, który najpierw próbuje użyć indeksu poza tablicą obiektu s a l e s 2 klasy L a b e l e d S a l e s , a następnie poza tablicą obiektu s a l e s 1 klasy S a l e s . Próby te odbywaj ą się w dwóch odrębnych blokach try, co pozwala prze testować oba rodzaje wyjątków.
Listing 15.1 6. use_sales.cpp li use_sales. cpp
# i n c l u de
# i n c l u de
wyjątki zagnieżdżone < i o s t re am > "sales . h" ---
int ma i n ( ) using s t d : : co u t ; us ing s t d : : c i n ; using s t d : : e n d l ; double va l s l [ l 2 ]
=
{ 1220, 2884,
1100, 2393 ,
1122 , 3302 ,
2212 , 2922,
1232 , 3002 ,
2334 , 3544
}; double va l s 2 [ 1 2 ]
=
{
12, 28,
11, 29,
22, 33 ,
21 , 29,
32 , 32 ,
34 , 25
}; S a l e s s a l e s l ( 2 0 0 4 , va l s l , 1 2 ) ; Labe l e d S a l e s s a l e s 2 ( " B l o g s t a r " ,
2005,
va l s 2 ,
12) ;
cout < < " P i e r w s z y b l o k try : \ n " ; try
{ int i ;
c o u t < < " Ro k = " < < s a l e s l . Y e a r ( ) for ( i = O ; i < 1 2 ; + + i )
< < endl ;
913
Rozdział 1 5 Zaprzyjaźnienie, wyjqtki i nie tylko -
cout Speak ( ) ; if ( p s = dynamic_c a s t< S u p e rb * > ( pg ) ) ps->Say ( ) ; re turn O ;
Grand * GetOne ( )
li Generuje losowo jeden
Grand * p ; swi tch ( s t d : : r a n d ( )
%
z
trzech rodzajów obiektów
3)
{ case O :
p = new G r a n d ( s t d : : r a n d ( )
%
100) ;
break ; case
1 : p = new S u p e rb ( s t d : : r a n d ( )
%
100) ;
break ; case 2 :
p = new Magn i f i c e n t ( s t d : : r a n d { ) 2A2 + s td : : rand ( ) % 2 6 ) ; break ;
return p ;
924
%
100,
RTTI
class Grand
virtual void speak () ;
class Superb class Grand
virtual void speak () ;
v o i d speak ( ) ;
----+--
virtual voi d say ( ) ;
• �
Redefinicja Nowa funkcja
class Maqni ficent
class Superb class Grand
...
virtual void speak ( ) ;
void speak ( ) ;
virtual void say ( ) ;
char eh ; void speak ( ) ; void s ay ( ) ;
+ f--
..._ f--
Redefinicja Redefinicja
Rysunek 1 5. 4. Rodzina klas Grand Nie możesz bezpośredn i o wywołać funk ej i S a y ( ) , pon i eważ nic j est ona zdefin i owana w k l asie G r a n d . Możesz j ednak użyć operatora dynami c cas t, aby sprawdzić, czy możliwe j est zrzu _ towanie pg na wskaźn i k na typ Su pe rb. Jest to możliwe, jeśli typem obi ektu j est S upe rb lub M a g n i f i c e n t . W obu tych przypadkach możesz bezp ieczn ie wywołać funkcj ę S a y ( ) : if
(ps
=
dynami c cas t < S u p e r b _
* > ( pg ) )
ps->Say ( ) ;
Pamiętaj , że wartość wyraże n i a przypisania to wartość danych po lewej stronie operatora = Dlatego wartością warunku i f j est wartość wskaźn ika p s . Jeś l i rzutowanie typu zakończy się sukcesem, wa11ość p s jest n i ezerowa . Jeś l i rzutowanie się nic powiedzie. co ma miej sce, ki edy pg wskazuje na obiekt typu G r a n d , wartością ps j est zero. Na l i stingu 1 5 . 1 7 przedstaw iony jest cały kod. Mała dygresj a - programiści zwykle używaj ą w wyrażeniu warunkowym if operato ra dl atego niektóre komp i l atory mogą zgłosić ostrzeżenie o n i ezamierzonym przypisan i u . .
==,
Uwaga o zgodności ze standardem Nawet jeś l i Twój ko m p i l ator o bsłu g uje mechan izm RTT I . może być on domyś l n i e wyłączony. Jeśli mechanizm ten nie działa, kod ma szansę się s ko m p i l ować, ale działa n i e progra m u może się wiązać z błę d a m i czasu wykonania. J e ś l i t a k s i ę d z i e j e , powi n i e neś zaj rzeć do d o kumentacj i l u b o p c j i m e n u . Jeś l i używasz kom p i l ato ra M i c rosoft V i s u a l C++ 7 . 1 , wyb i e rz z menu opcję Project, n astęp n i e projekt (gdzie projekt oznacza nazwę używanego proje kt u ) , przejdź n a zakładkę CIC++, wciśnij p rzyc i s k Language i zm i e ń u staw i e n i e Enable Run - Time Type Info na Yes.
925
Rozdział I 5 Zaprzyjaźnienie, wyjqtki i nie tylko -
Program pokazany na l istingu 1 5 . 7 stanowi i lustrację ważnego zagadnienia. Kiedy to możl iwe, powinieneś używać funkcj i wirtualnych . RTTI powinieneś stosować tylko wtedy, kiedy to ko nieczne. Poniżej znajduje się przykładowy wynik działania programu : Je s t em k l a s a S u p e rb ! ! P r z e c h o w u j ę wa r t o ś ć k l a s y S u p e rb , Je s t em k l a s a Magn i f i c e n t ! ! ! P r z e c h o w u j ę z n a k R o r a z l i c z bę 6 8 Je s t em k l a s a Mag n i f i c e n t ! ! ! P r z e c h o w u j ę z n a k D o r a z l i c z bę 1 2 Je s t em k l a s a Mag n i f i c e n t ! ! ! P r z e c h o w u j ę z n a k V o r a z l i c z bę 5 9 Je s t em k l a s a G r a n d !
k t ó r a wyno s i 6 8 ! ! ! !
Jak widzisz, metoda S a y ( ) wywoływana jest tylko dla obiektów k las S up e rb oraz Ma gn i f i ce n t . Wynik działania programu jest różny dla każdego wykonania, ponieważ typ obi ektu wybierany jest za pomocą funkcji r a n d ( ) . Operator dynamic_cas t działa także dla referencj i , jednak używanie tego mechanizmu jest wtedy nieco inne. Dziej e się tak, ponieważ referencje nie posiadają specjalnej wartości, która oznacza n i epowodzen i e . D l atego j e ś l i żądane rzutowanie j e st n i epoprawn e , operator dyn ami c_ c a s t zgłasza wyj ątek b a d_c a s t , który dziedziczy po klasie e x c e p t i o n . a zdefini owany jest w pliku nagłówkowym typeinfo. N a przedstawionym poniżej przykładzie użycia operatora rg jest referencją do obiektu typu G r a n d : # i n c l u d e < t yp e i n f o > // Dla wyjątku bad_cast try
{ S up e r b
&
rs
catch ( b a d_ c a s t
dynamic_cas t< S up e rb
& > ( rg ) ;
&) {
} ;
Operator typeid i klasa type_inf o Operator typei d pozwala określić, czy dwa obiekty są tego samego typu. Podobnie jak funkcja si zeof, type i d przyj muje argumenty w dwóch postaciach: • jako nazwę klasy, • jako wyrażenie, którego wartością jest obiekt. Operator type i d zwraca referencj ę do obi ektu typu t ype_i n f o , który to typ jest k lasą zdefiniowaną w pliku nagłówkowym typeinfo (dawna nazwa tego p liku to typeinfo.h). W klasie t ype _ i n f o znajduj ą się przeciążone wersje operatorów oraz ! co umożliwia użycie tych operatorów do porównywania typów obiektów. Na przykład wyrażenie: ==
926
=,
RTTI
typeid ( Ma gn i f i c e n t )
==
typei d ( * pg )
zwraca wartość true , j eśli pg wskazuje na obiekt typu Magn i f i c e n t . W przeciwnym razie wyrażenie zwraca wartość fal se. Jeśli pg j est wskaźnikiem pustym, program zgłasza wyjątek b ad_ t ype id . Wyjątek ten dziedziczy po klasie e x c e p t i o n . a jego deklaracja znajduje się w pliku nagłówkowym typeinfo. Implementacja klasy t yp e_ i n f o jest zależna od producenta. ale zawsze zawiera składową name ( ) . która zwraca zależny od implementacj i ciąg znaków - zwykle j est to nazwa klasy. Na przykład wyrażenie : c o u t < < " Te r a z p r z e t w a r z am obi e k t t ypu " < < typeid ( * pg ) . n ame ( ) < < " . \ n " ;
powoduje wyświetlenie ciągu znaków zdefiniowanego dla klasy obiektu, na który wskazuj e pg. Listing 1 5 . 1 8 to modyfikacja listingu 1 5 . 1 7. która używa operatora typeid oraz funkcj i składowej name ( ) . Zauważ, że operator ten wykorzystany jest do operacji, w których funkcje wim1alne i ope rator dynamie_cas t nie mogą zostać użyte. Warunek wykorzystujący typeid służy do określe nia działania, które nie jest nawet metodą klasy, nie można więc go wywołać za pomocą wskaźnika. Wyrażenie używające metody name ( ) pokazuje, jak da się wykorzystać operator typeid do usu wania błędów. Zwróć uwagę, że do programu dołączony jest plik nagłówkowy typeinfo.
Listing 1 5. 1 8. rtti 2.cpp li rtti2. cpp - zastosowanie dynamic_cast, typeid oraz type_info
# i n c l u de < i o s t re am > ł i ncl ude < c s t d l i b > # i n cl ude < c t ime > lłi n c l ude < t yp e i n f o > using name space s t d ; class G r a n d private : int ho l d ; publ ic : Grand ( int h = 0 ) : h o l d ( h ) { ) virtual void Spe a k ( ) const ( cout Say ( ) ; i f { typeid ( M a gn i f i c e n t ) == type id ( * pg ) ) c o u t < < " T a k , r z e c z yw i ś c i e j e s t e ś w s pan i a ł a . \ n " ; re turn O ;
Grand * GetOne ( ) Grand * p ; swi tch ( r a n d ( )
%
3)
{
case O : p = new G r a n d ( r a n d ( ) % 1 0 0 ) ; break ; case 1 : p = new S u p e rb ( ra n d ( ) % 1 0 0 ) ; break ; new Magn i f i ce n t ( ra n d ( ) % 1 0 0 , case 2 : p break ; =
re turn p ;
928
2A2 + r a n d ( )
%
26) ;
RTTI Poniżej znajduje się przykładowy wynik działania programu przedstawionego na li stingu 1 5 . 1 8 : Tera z p r z e t w a r z am ob i e k t t ypu Mag n i f i c e n t . Je s t em k l a s a Magn i f i c e n t ! ! ! P r z e c howu j ę z n a k P i l i c z b ę 5 2 ! T a k , nap rawdę j e s t e ś w s p a n i a ł a . T e r a z p r z e t w a r z am ob i e k t t ypu S u p e rb . Je s t em k l a s a S u p e rb ! ! P r z e chowu j ę w a r t o ś ć k l a s y S u p e rb , k t ó r a wyno s i 3 7 ! Tera z p r z e t w a r z am ob i e k t t ypu G r a n d . Je s t em k l a s a G r a n d ! Teraz p r z e t w a r z am o b i e k t t ypu S u p e rb . Je s t em k l a s a S u p e rb ! ! P r z e c howu j ę w a r t o ś ć k l a s y S u p e rb , k t ó r a w yn o s i 1 8 ! Tera z p r z e t w a r z am ob i e k t t ypu G r a n d . Je s t em k l a s a G r a n d !
Podobnie jak w poprzedn i m przykładzie dokładny wynik działani a programu zmienia się za każ dym uruchomieniem, ponieważ do wyboru typu sh1ży funkcj a r a n d ( ) .
Nadużywanie RTTI RTTI posiada wielu krytyków wśród programistów j ęzyka C++. Według nich mechanizm RTTI jest niepotrzebny, stanow i potencj alne źródło braku efektywności programu i może przyczyniać _ się do stosowania złych technik programowania. Bez wdawani a się w debatę na temat RTTI przyjrzyjmy się, czego powinieneś unikać, stosuj ąc ten mechanizm.
Zastanów się nad głównym fragmentem l istingu 1 5 . 1 7 : Grand * pg ; S upe rb * p s ; for ( int i = O ;
i < 5;
i++ )
pg = G e t O n e ( ) ; pg - > S p e a k ( ) ; if ( p s = dynami c_cas t< S u p e rb * > ( p g ) ) ps->Say ( ) ;
Możesz zrezygnować z operatora dynamic_cast oraz funkcj i wirtualnych i przepisać ten kod. używając operatora type id: Grand * p g ; S upe rb * p s ; for ( int i = O ; pg
i < 5;
i++)
GetOne ( ) ;
929
Rozdział 1 5 - Zaprzyjaźnienie, wyjqtki i nie tylko
( type id ( M a g n i f i c e n t )
if
pm =
( Ma g n i f i c e n t
*)
type i d ( * p g ) )
==
pg;
pm- > S p e a k ( ) ; pm- > S a y ( ) ;
ps
=
type id ! * pg ) )
( type i d ( S u p e r b )
else if
( S uperb
� 1
pg ;
p s - >Spea k ( ) ; ps->Say ( ) ; else pg - > S p e a k ( ) ;
Takie rozwiązan ie j est n i e tylko m n i ej e l eganck i e i dłuższe od poprzedniego, a l e posiada też poważną wadę w postaci potrzeby j awnego nazywania wszystkich klas. Załóżmy. że potrzebu jesz kl asy I n s u f f e r a b l e pochodnej k lasy M a g n i f i c e n t . W tej nowej k l asie znajduj ą sit; redefin icj e metod S p e a k ( ) oraz S a y ( ) . W wersj i u żywaj ącej operatora type i d do j awnego sprawdzan ia każdego typu musisz zmodyfikować kod pęt l i for, dodaj ąc nową sekcj t; e l se if . Z dmgiej strony oryginalna wersj a kodu nie wymaga żadnych zmian. Wyrażen i e : pg - > S p e a k ( ) ;
jest poprawne d l a obiektów wszystkich klas h i erarchii G r a n d , a wyrażen i e : if
(ps
=
dynami c_ca s t < S u p e rb
* > ( pg ) )
ps->Say ( ) ;
działa d l a wszystk ich obiektów k l a s pochodnych od k l asy S u p e r b .
Wskazówka Jeś l i w swo i m kodzie używasz operatora type i d wraz z serią wyrażeń e l s e i f , zasta nów s i ę , czy n i e m ożesz w zam i a n użyć f u n kcji w i rtualnych i operatora dynamic_ca s t.
Operatory rzutowania typu Operator rzutowa n i a typu j ęzyka C j est, zdaniem Bj arne S troustmpa, za mało restrykcyj ny. Spójrz na pon iższy przykład : s truct
} ;
Da t a
double d a t a [ 2 0 0 ] ;
s truc t J u n k 930
Operatory rzutowania typu
int j u n k [ l O O ] ;
l ;
Data d char
•
char eh Junk
•
( 2 . 5e3 3 ,
pj
3 . Se- 1 9 , 2 0 . 2e32 } ; li Rzutowanie 1 przekształca adres na ciąg znaków char ( & d ) ; li Rzutowanie 2 przekształca adres na znak li Rzutowanie 3 - przeksztalca adres na wskaźnik na typ J1111k ( Ju n k * ) ( & d ) ;
=
pch = =
( char * )
( &dl ;
-
-
Które z tych trzech przekształceń ma jakiś sens? Ż adne z nich. Które rzutowanie j est dozwo lo ne? W języku C wszystki e trzy. Odpowiedzią Stroustrupa na ten brak kontroli było ograni cze11 1 e możl iwych konwersj i ogólnego rzutowania typu oraz dodanie czterech nowych operatorów rzuto wania, które pozwalaj ą lepiej kontrolować proces rzutowania. Operatory te to: dynamic_cas t const cas t static c a s t reinte rpret_c a s t
Zamiast używać ogólnego rzutowania typu, możesz wybrać operator, który jest przystosowany do konkretnego celu. Stanowi to dokumentacj ę celu rzutowania typu, a także pozwala kompi latoro wi sprawdzić poprawność tego rzutowania. We wcześniejszych fragmentach rozdziału widziałeś już operator dynami c_cas t. Załóżmy, że H i g h i Low to dwie klasy, wskaźn i k ph wskazuje na typ H i gh , a pl na typ Low. Wyrażenie: pl
=
dynamic_cas t < L o w *> p h ;
przypisuje wskaźnik Low * do p l tylko wtedy, kiedy Low jest klasą bazową klasy H i gh . W prze ciwnym razie wyrażenie przypisuje do p l wskaźnik pusty. Składnia operatora wygląda następująco: dynami c_cas t
( wyra ż e n i e )
Zadaniem tego operatora jest umożliwienie rzutowania w górę wewnątrz hierarchii klas, które jest bezpieczne dzięki relacj i jest-czymś, a także uniemożl iwienie innego rzutowania. Operator cons t_cas t służy do rzutowania typu, którego jedynym celem jest zmiana etykiety wartości między con s t a vo latile. Składnia tego operatora j est taka sama jak operatora dynamie_cas t: const cas t
( wyra ż e n i e )
Wynikiem takiego rzutowani a j est błąd, jeśli zmianie podlega jakikolwiek inny aspekt typu. Typy obiektów nazwa-typu oraz wyrażenie muszą być takie same i mogą różnić się jedyn ie obec nością (lub brakiem) słów kluczowych const oraz volatile. Ponownie załóżmy, że H i g h i Low to dwie klasy: H i gh ba r ; const High * pba r
=
&ba r ;
H i g h * pb con s t_cas t < H i gh * > ( pba r ) ; const Low * p l = con s t cas t < const Low * >
li Poprawne ( pba r ) ; li Niepoprawne
931
Rozdział I 5 Zaprzyjaźnienie, wyjqtki i nie tylko -
Pierwsze rzutowanie powoduje, że wskaźni k * p b może zostać użyty do zmiany wartości obiektu bar rzutowan ie usuwa etykietę cons t. Drugie rzutowanie j est niepoprawne, ponieważ pró buje zmienić typ z con s t H i g h * na cons t Low * . -
Operator ten może być przydatny, kiedy posiadasz wartość, która j est stała przez większą część programu, ale w kilku miejscach musisz ją zmienić. W takiej sytuacj i możesz zadekl arować war tość jako cons t i użyć operatora cons t_cas t, kiedy chcesz ją zmodyfikować . Można to zrobić za pomocą ogólnego rzutowania typu, ale takie ogólne rzutowanie umożliwia także zmianę typu : H i gh b a r ; con s t H i gh * pb a r
&ba r ;
( H i g h * ) ( pb a r ) ; H i gh * p b con s t Low * pl = ( cons t Low * )
li Poprawne
( pb a r ) ; ll Także poprawne
Ponieważ j ednoczesna zmiana typu oraz stałości może być przypadkowym błędem programisty, bezpieczn iej sze jest używanie operatora cons t cast. _ Operator con s t_cas t nie jest wszechmocny. Może zmienić dostęp wskaźnika do wartości. jednak próba zmiany wartości zadeklarowanej j ako con s t j est niezdefin iowana. Listing 1 5 . 1 9 obrazuje, co to oznacza w praktyce.
Listing 1 5. I 9. constcast.cpp li constcast. cpp - używa operatora const_cast< > # i n c l u de < i o s t r e am> us ing s t d : : c o u t ; us ing s t d : : e n d l ;
vo id c h a n g e ( cons t int * p t ,
int n ) ;
int ma i n ( ) int p o p l = 3 8 3 8 3 ; con s t int p o p 2 = 2 0 0 0 ; c o u t < < " p op l , p o p 2 : c h a n g e ( & po p l , - 1 ) ; c h a n g e ( & pop2 , - 1 ) ; c o u t < < " pop l , p o p 2 :
" < < pop l < < "
" < < pop2 < < e n d l ;
" < < pop l
( wyra ż en i e )
Rzutowanie to jest poprawne tylko wtedy, kiedy obiekt nazwa-typu może zostać niejawnie prze kształcony na obiekt wyrażenie lub na odwrót. W przec iwnym razie rzutowanie jest błędne. Za łóżmy, że H i gh jest klasą bazową klasy Low, a klasa P o n d nie należy do tej h ierarchi i . Konwer sje między typem H i g h i Low są poprawne w obie strony, jednak konwersj a z typu Low na typ Pond jest niedozwolona : Hi gh b a r ; Low b l o w ; H i gh * pb = s tatic_cas t< H i g h Low * pl
Pond *
=
pme r
*>
s tatic cast =
s tatic cast< Pond
( &b l o w ) ;
( &bar ) ; *>
li Poprawne rzutowanie w górę li Poprawne rzutowanie w dól
( & b l o w ) ; li Niepoprawne. klasa Pond li spoza hierarchii
Pierwsza konwersj a jest poprawna, ponieważ możl iwe j est j awne rzutowanie w górę. Drugie rzutowanie, ze wskaźn ika klasy bazowej na wskaźni k klasy pochodnej , nie jest możliwe bez jawnego rzutowania typu, jednak dzięki temu, że możl iwe jest niej awne rzutowanie w przeciw nym kierunku, można użyć operatora s tatic_cast do rzutowan ia w dół. Podobnie wartość wyliczenia może być przekształcona na liczbę całkowitą bez j awnego rzutowa nia typu, dzięki czemu operator s tatic cas t pozwala na dokonanie konwersj i w odwrotnym _ kierunku, czyli z liczby całkowitej na wartość wyliczenia. Według tej samej zasady s tatic_cas t umożl iwia konwersję z typu double na typ int, z typu float na typ long, a także na wiele innych konwersj i między różnym i typami liczbowym i.
933
Rozdział 1 5 Zaprzyjaźnienie, wyjqtki i nie tylko -
Operator re i n te rpre t_cas t służy do dokonywania ryzykownego rzutowania typu. Nie po zwala na usunięcie etykiety con s t , ale umożl iwia inne ni ebezpieczne dzi ałan ia. Czasem pro grami sta musi wykonywać ni ebezpieczne operacje zależne od implementacj i , a używanie opera tora re interpre t_ca s t ułatwia kontrolę tych poczynań . Składnia tego operatora j est taka sama jak trzech poprzednich: re interpre t_ca s t
( wyra ż en i e )
Poniżej znajduj e się przykład użycia tego operatora : stuct dat long v a l u e
da t
*
{ short a ;
=
short b ) ;
OxA2 2 4 B l l 8 ;
p d = reinte rpre t_ca s t < d a t
*>
( &value ) ;
li Wyświetla dwa pierwsze bajty wartości
co u t < < p d - > a ;
Zwyk l e takie rzutowanie służy do operacj i niskopoziomowych i zależnych od impl ementacji, przez co nie są one przenośne. Powyższy przykładowy kod daje inne wyniki na komputerach zgodnych z IBM, a inne na komputerach Maci ntosh, ponieważ oba te systemy przechowują w in nej kolej ności bajty w wielobajtowych typach l iczbowych. Operator re i n te rpre t_ca s t nie pozwal a jednak na wszystko. Możesz zrzutować typ wskaź nikowy na typ l iczby całkowitej , który j est wystarczająco poj emny, aby pomieścić reprezentację wskaźnika. Nie możesz jednak zrzutować wskaźnika na mniej pojemny typ l iczby całkowitej lub na typ zmiennoprzecinkowy. Kolejne ograniczen ie to brak możliwości rzutowania wskaźnika na funkcj ę na wskaźnik na dane lub na odwrót. Zwykłe rzutowanie typu także zostało ograniczone. Pozwala na to samo, na co pozwalają opera tory rzutowania, umożliwiaj ąc dodatkowo pewne kombinacj e, jak użycie możliwości operatora s ta t i c_c a s t lub r e i n te rpre t_c a s t wraz z możl iwościami operatora con s t_cast. Nic pozwa l a j ednak na nic więcej . D latego rzutowanie: char e h = char
( &d) ;
li Rzutowanie 2
-
przeksztalca adres na znak
jest dozwolone w j ęzyku C, ale j est zwykle niepoprawne w j ęzyku C++, pon ieważ w większości implementacj i tego j ęzyka typ char jest za mały, aby pomieści ć reprezentacj ę wskaźnika. Ograniczenia te maj ą swoj e podstawy, jeśli j ednak czujesz się zbyt ograniczony tym narzuconym dobrodziej stwem, zawsze możesz wrócić do j ęzyka C.
Podsumowanie Zaprzyj aźnienie umożliwia Ci zaproj ektowanie bardziej e lastycznego interfej su k lasy. Z klasą mogą być zaprzyj aźnione funkcje, inne klasy oraz funkcj e s kładowe innych klas. W niektórych przypadkach musisz użyć deklaracj i wyprzedzających i umieścić deklaracje klas oraz metod w od powiedn iej kolejności, aby zaprzyj aźnienie działało poprawnie. K l asy zagnieżdżone to klasy zadekl arowane wewnątrz innych klas. Klasy zagni eżdżone ułatwiają projektowanie klas pomocniczych, które współpracuj ą z inną klasą. ale r:ie muszą posiadać pu bl icznego interfejsu.
934
Pytania sprawdzajqce Wyjątki w języku C++ umożliwiaj ą elastyczne radzenie sobie z niespodziewanymi zdarzen iami. jak ni eprawidłowe wartości czy nieudane próby dostępu do pli ków. Zgłaszanie wyjątku ko11czy działanie aktualnie wykonuj ącej się funkcj i i przekazuj e sterowanie programem do odpow ied niego bloku catch. Bloki c a tch znajduj ą się bezpośrednio po bloku try, a wywołanie funk cj i, która pośrednio lub bezpośrednio prowadzi do zgłoszenia wyjątku, musi się znajdować w bloku try. Następnie program wykonuj e kod w b loku catch. Kod ten może prowadzić do pr ób y naprawienia błędu lub do zakoi'lczenia programu. W klasie można umieścić zagnieżdżoną k l asę wyjątku, który j est zgłaszany w momencie natrafi enia na problemy specyficzne dla klasy. Funk cja może posiadać specyfi kacj ę wyj ątków, która zawiera wyjątki zgłaszane przez tę funkcj ę . Nieprzechwycone wyj ątki , które n i e posiadaj ą odpowiedniego bloku catch, domyś lnie powo dują zakoi'lczenie działan ia programu. Podobne skutki powoduj ą wyj ątki nieoczek iwane, które nie znajdują się w specyfikacj i wyj ątków. Mechanizm RTTI pozwala na określenie typu obiektu . Operator dynami c_cas t służy do rzu towania wskaźnika klasy pochodnej na wskaźn ik klasy bazowej . Jego główne zastosowa111 c to sprawdzanie, czy możliwe j est wywołanie danej funkcj i wirtualnej . Operator type i d zwraca obiekt typu t ype _ i n f o . Wartości zwracane przez typeid można porównać ze sobą, co po zwala sprawdzić, j akiego typu obiekt j est u żywany, a zwracany obiekt t ype _ i n f o może służyć do uzyskania informacj i o tym obiekcie. Operatory dynami c_c a s t , s ta t i c_c a s t, c o n s t_c a s t oraz re i n te rp r e t_c a s t umożliwiają bezpieczniej sze i lepiej udokumentowane rzutowanie typów n i ż ogólny mechanizm rzutowania.
Pytania sprawdzaiące 1 . Znajdź błędy w poniższych próbach zaprzyjaźnienia. a. class s n a p { friend c l a s p ; } ; class
clasp
{
.
.
.
} ;
b. class c u f f
{
publ i c : void s n i p ( mu f f
&)
I ;
class mu f f friend void c u f f : : s n i p ( mu f f
&) ;
} ;
935
Rozdział 1 5 Zaprzyjaźnienie, wyjqtki i nie tylko -
c. clas s mu f f { friend voi d c u f f : : s n i p ( mu f f & ) ; } ;
clas s c u f f publ i c : void s n i p ( mu f f & )
. . .
}
I ;
2. W rozdziale widziałeś przykład tworzenia klas wzaj emnie zaprzyj aźnionych . Czy możesz ograniczyć tę formę przyjaźni w taki sposób, aby j edynie niektóre składowe klasy B były zaprzyj aźn ione z klasą A, a niektóre składowe klasy A z klasą B? Wyjaśnij dlaczego.
3. Jakie problemy może sprawiać dekl aracja przedstawionej pon iżej klasy zagnieżdżonej? clas s R i b s private : clas s S a u c e int soy; int s u ga r ; publ i c : S a u c e ( int s l ,
int s 2 )
s oy ( s l ) ,
sugar ( s 2 )
{ }
} ; } ;
4. Czym różnią się od siebie instrukcje throw i re turn? 5.
Załóżmy, że utworzyłeś hierarchię klas wyjątków, które dziedziczą po klasie bazowej . W ja kiej kolejności powinieneś umieścić bloki catch?
6. Przypomnij sobie zdefini owane w tym rozdziale klasy G r a n d , S u p e rb i Mag n i f i c e n t . Załóżmy, ż e pg t o wskaźnik n a typ G r a n d , d o którego przypisany jest adres obiektu jednej z tych trzech klas, a p s to wskaźn ik na typ S u p e rb. Czym różnią się w działaniu dwa poniższe fragmenty kodu? =
if
(ps
dynami c_ca s t< S upe rb * > ( p g ) )
if
ps->say ( ) ; ( typeid ( * p g ) == typeid ( S upe rb ) ) ( S up e rb * ) p g - > s a y ( ) ;
li Fragment I
li Fragment 2
7. Czym różni się operator s tatic_cas t od operatora dynami c_cas t?
936
Ćwiczenia programistyczne
Ćwiczenia programistyczne 1. Wprowadź w klasach T v oraz Remo t e następujące zmiany:
a. Klasy powinny być wzaj emnie zaprzyj aźnione. b. Dodaj do klasy Remo te zmienną składową, która okreś la, czy pilot pracuje w trybie zwykłym czy interaktywnym. c. Dodaj do klasy Remo t e metodę wyświetlaj ącą ten tryb. d. Dodaj do klasy Tv metodę zmieniającą tryb pracy pi lota. Metoda ta powinna działać tylko wtedy, kiedy telewizor jest włączony. Napisz krótki program do testowania tych właściwości. 2. Zmodyfikuj l isting 1 5 . 1 1 w taki sposób, aby typami obu wyj ątków były klasy pochodne kl asy l o g ie _ e r r o r z p l iku nagłówkowego s t d e x c e p t . Metoda w h a t ( ) każdej z tych klas powinna informować o nazwie funkcj i oraz o naturze problemu. Obiekty wyjątków nie muszą przechowywać n iepoprawnych wartości, powinny jedynie obsługiwać metodę wha t ( ) . 3.
To ćwiczenie j est takie samo, j ak poprzednie, ale wyj ątki powinny dziedziczyć po klasie (która sama dziedziczy po klasie l o g i c _e r r o r ) przechowującej wartości argumentów. Wyjątki powi nny mieć metody wyświetlaj ące te wartości wraz z nazwą funkcj i . Oba wyjątki mają być przechwytywane przez j eden blok catch przechwytujący wyjątek klasy bazowej . Oba wyjątki powinny kończyć działanie pętl i .
4 . N a listingu 1 5 . 1 6 po każdym bloku try znajdują s i ę dwa bloki catch, dzięki czemu zgło szenie wyjątku n b a d_ i n d e x prowadzi do wywołania metody l a b e l _ va l ( ) . Zmodyfi kuje ten program tak, aby po każdym bloku try występował jeden blok catch. Do spraw dzania, czy należy wywołać m etodę 1 ab e l _ va 1 ( ) , użyj mechanizmu RTTI.
937
Rozdział 1 6
Klasa string oraz biblioteka STL W rozdziale: • Standardowa klasa string • Szablon a u t o _p t r • Biblioteka standardowa STL • Klasy kontenerowe • lteratory • Obiekty funkcyjne (funktory) • Algorytmy STL
N
a pewno dobrze już znasz ideę powtórnego wykorzystania kodu w j ęzyku C++ . Jedną z wielkich zalet tego podejścia jest możliwość wykorzystan ia kodu napi sanego przez in nych programi stów. Właśnie do tego sh1żą bibl ioteki klas. Istniej e wiele komercyj nych bibliotek klas języka C++. Także w skład samego języka wchodzi zestaw bibl iotek. Jedną z nich stanowią klasy do obsługi wej ścia i wyjścia znaj dujące się w pliku nagłówkowym ostream . W ni niejszym rozdzi ale poznasz więcej możl iwości ułatwienia programowania dzięki powtórnemu wykorzystaniu kodu.
Zetknąłeś się już z klasą s t r i n g, która w tym rozdziale opisana j est bardziej dogłębnie. Oprócz niej poznasz klasę a u t o _p t r szablon k lasy do obsługi „inteligentnych wskaźni ków", który ułatwia zarządzanie dynamicznym przydziałem pamięci. Na końcu rozdzi ału znajduj e się opis biblioteki STL, która stanowi zestaw użytecznych szablonów obsługujących różne kl asy konte nerowe. Bibl ioteka STL jest przykładem nowego paradygmatu programowania programowa nia generycznego. -
--
Klasa
s tring
W wielu apli kacj ach potrzebne jest przetwarzanie ciągów znaków. Język C ułatw ia to za pomocą rodziny funkcj i do obsługi ciągów znaków znajdujących się w pliku string. h (es/ring w C++ ) . Także wiele wczesnych implementacj i j ęzyka C++ udostępnia w tym c e l u proste klasy. Rozdział 4„ „Typy złożone", wprowadza k l asę s t r i n g standardu ANSI/I SO j ęzyka C++. W rozdziale
Rozdział 1 6 Klasa string oraz biblioteka STL ·
1 2 . , „Klasy i dynamiczna al okacj a pamięci", na przykładzie skromnej klasy S t r i n g przedsta wione są pewne aspekty proj ektowania klasy reprezentującej ciągi znaków.
Klasy s t r i n g możesz używać za pomocą pliku nagłówkowego string. Zauważ, że pliki na główkowe string. li oraz cstring obsługuj ą funkcj e dla ciągów znaków języka C z bibl ioteki tego języka, a nie klasy s t r i n g . Podstawą używania tej klasy j est znaj omość j ej interfejsu. Klasa s t r i ng posiada bogaty zestaw metod, zawierający kilka konstruktorów, przeciążone operatory do przypisywania ciągów znaków, łączenia ich, porównywania czy uzyskiwania dostępu do po szczegó lnych znaków ciągu. Istniej ą też metody do wyszukiwania znaków i podciągów w ciągu znaków. a także wiele i nnych. Podsumowując. klasa s t r i n g m a wiele do zaoferowania.
Tworzenie obiektu s tring Przyjrzyj my się konstruktorom k lasy s t r i n g . W końcu jedną z podstawowych rzeczy, j akie trzeba wiedzieć o klasie, są sposoby tworzeni a j ej obiektów. Na l i stingu 1 6. I przedstawione są wszystkie konstruktory tej klasy, oznaczone komentarzem c t o r, co w j ęzyku C++ jest tradycyj nym skrótem pochodzącym od angielskiej nazwy konstruktora (ang. constructor). W tabeli 16. 1 znajduje się krótki opis konstruktorów. Przedstawione są one w takiej samej kolej ności. w jakiej pojawiaj ą się w programie. Reprezentacje konstruktorów są uproszczone, ponieważ ukrywają fakt, że nazwa s t r i n g j est w rzeczywi stości definicj ą typu konkretyzacj i szablonu ba s i c s t r i n g < c h ar> a także pomij aj ą dodatkowy argument zwi ązany z zarządzaniem pamię cią. Aspekt ten opisany j est w dalszej części tego rozdzi ału oraz w dodatku F, „Klasa-szab lon string". Typ s i z e _ t yp e to zależny od implementacj i typ liczbowy, którego definicj a znajduje się w pliku nagłówkowym string. Klasa definiuj e wartość s t r i n g : : n p o s j ako maksymalną możliwą długość ciągu znaków. Zwykle równa się ona maksymalnej wartości typu unsigned int. W tabe li używany jest także popu larny skrót NBTS 1 na oznaczenie ciągu znaków zakoń czonego zerem, czyli tradycyj nego ciągu znaków j ęzyka C, który kończy się znakiem pustym. -
,
Listing 1 6. 1 . str 1 .cpp li strl . cpp - wprowadzenie do klasy string
# i n c l u de < i o s t r e am> # i n c l u de < s t r i n g > li Ufywanie konstruktorów klasy string int ma in ( ) using namespace s t d ; s t r i n g o n e ( " Z w yc i ę z c a l o t e r i i ! " ) ; ll ctor l li Przeciążony operator < < cout > f n ame ; cin
>>
l n ame ;
c 1 r. . g e t l i n e ( f n a m e ,
get l i ne ( c i n ,
10) ;
f n a me ) ;
II Może powstać problem, jeślifiiamejest większe niż 9 znaków li Potrafi wczytać bardzo. bardzo długie słowo li Może obciąć nadmiarowe znaki li Nie obcina nadmiarowych znaków
Automatyczne dopasowywanie wiel kości pozwala w funkcj i g e t l i n e ( ) klasy s t r i n g pomi nąć parametr ograniczający liczbę wczytywanych znaków. Różnica na poziomie proj ektu polega na tym, że narzędzia do wczytywania ciągów znaków języ ka C należą do kl asy i s t re am, podczas gdy ich odpowiedniki w klasie s t r i n g są samodziel nymi funkcjami. Dl atego w przypadku ciągów znaków języka C funkcje wywoływane są dla obiektu c i n , a w przypadku ob iektów s t r i n g dla tych właśnie obiektów, które są argumenta mi odpowiednich funkcj i . Dotyczy to także obsługi wejścia za pomocą operatora > > , co j est szczególnie widoczne, kiedy wyrażenia napisane są w formie funkcj i : c i n . ope rator> > ( f n a me ) ; operator> > ( c i n ,
l n ame ) ;
li Metoda klasy ostream li Zwykła funkcja
Przyjrzyjmy się teraz bliżej funkcj om klasy s t r i n g służącym do pobierania znaków. Obie do pasowują rozmiar obiektu s t r i n g do długości wprowadzanego ciągu. Istniej ą jednak pewne ograniczenia. Pierwszym z nich j est maksymalny dopuszczalny rozmiar ciągu przechowywany przez stałą s t r i n g : : n p o s . Zwykle j est to maksymalna wartość typu uns igned int, nie stanowi to więc praktycznego ograniczenia dla zwykłego, interaktywnego wpisywania znaków. Może to jednak być ograniczeniem, jeśli chcesz wczytać do j ednego obiektu s t r i n g zawartość całego pliku. Drugie ograniczenie to ilość pamięci dostępnej w programie. Funkcja ge t l i n e ( ) klasy s t r i n g wczytuje wprowadzane znaki i zapisuje j e w ciągu, dopóki nie zajdzie jedna z trzech okoliczności : • Program natrafi n a koniec pl iku, w wyniku czego w strumieniu wej ścia ustawiany jest e o fb i t , co powoduj e zwrócenie wartości true przez funkcje f a i l ( ) oraz e o f ( ) . • Program natrafi na znak kończący wczytywanie, którym domyś lnie jest \ n . Znak ten pobierany j est ze strumienia wejściowego, ale nie j est zapisywany do obiektu s t r i n g . • Zostanie wczytana maksymalna liczba znaków, czyli mniejsza z wartości s t r i n g : : n p o s oraz liczby baj tów pamięci dostępnych n a zapisanie ciągu. W tym przypadku w strumie niu wejścia ustawiany jest f a i lbi t , co powoduje zwrócenie wartości true przez funkcję f a il ( ) .
Obiekt strumienia wej ścia posiada system kontroluj ący poprawność stanu strumienia. W syste mie tym ustawi enie e o fb i t pozwala wykryć koniec pliku, ustawienie f a i l b i t wykrywa błąd wczytywania, ustawienie b a d b i t oznacza nierozpoznany błąd, związany na przykład z awarią sprzętu, a g o o d b i t oznacza, że wczytywanie odbywa się poprawnie. Dalsza analiza tego za gadnienia znajduje się w rozdziale 1 7 . Funkcja ope rator > > ( ) w klasie s t r i n g zachowuje się podobnie, ale zamiast pomijać wczy tywane znaki kończące, dochodzi do znaku białego (ang. white space) i zostawia go w kolejce
945
Rozdział 1 6 Klaso string oraz biblioteko STL -
wejścia. Znak biały to spacja, znak nowej linii lub znak tabulacj i . Uogólniając, jest to każdy znak, dla którego funkcja i s s p a c e ( ) zwraca wartość true. We wcześniej szych fragmentach tej książki znajdują się przykłady wprowadzania znaków do obiektu s t r i n g za pomocą konsoli. Ponieważ funkcje obsługi wejścia klasy s t r i n g współ pracują ze strumieniami i rozpoznają koniec pl iku, możesz ich użyć także do wczytywania pli ków. Li sting 1 6 . 2 przedstawi a krótki przykład wczytywania ciągów znaków z pl iku. Zakładamy, że w pliku znaj duj ą się ciągi znaków oddzielone dwukropkami , a do określenia znaku kończące go służy funkcj a ge t l i n e ( ) . Następnie program numeruje ciągi znaków i wyświetla w każdej linii jeden obi ekt s t r i n g .
Listing 1 6.2. strfile.cpp li strfile. cpp - wczytuje ciągi znaków z pliku
# i n c l u de # i n c l u de # i n c l u de # i n c lude int ma i n
< i o s t ream> < f s t ream> < s tring>
()
u s ing name space s t d ;
i f s t re a m f i n ; f i n . o p e n ( " z a k up y . t x t " ) ; i f ( f i n . i s open ( ) == fal s e ) c e r r < < " N i e mo ż n a o t w o r z y ć p l i ku . \ n " ; e x i t ( E X I T FA I LURE ) ; s t ring i tem; int coun t
=
O;
get l i ne ( f in , i tem, 2 : 2 ) ; ( f i n ) li Dopóki wprowadzane dane są poprawne
whi l e
+ + coun t ; c o u t < < c o u n t < < " : " < < i t em < < e n d l ; g e t l i n e ( f i n , i t em , 2 : 2 ) ; c o u t < < " Ko n i e c \ n " ; fin . close ( ) ; re turn O ;
Poniżej znajduje się przykładowa zawartość pliku zakupy. txt: s a rdyn k i : l o d y c z e ko l adowe : p r a ż o n a k u k u r y d z a : p o r y : s e r e k w i e j s k i : o l e j z o l i w e k : ma s ł o : t o fu :
946
Klasa string
Zwykle, jeśli program ma zna leźć plik tekstowy, powinieneś umieścić go w tym samym katalogu co plik wykonywalny programu. Czasami może to być katalog z plikiem projektu. Możesz także podać pełną nazwę pliku. Pamiętaj , że w systemach Windows i DOS w ciągu znaków j ęzyka C sekwencja ucieczki \ \ reprezentuj e poj edynczy lewy ukośnik: f i n . open ( " C : \ \ C P P \ \ Progs \ \ z a k u p y . tx t " ) ;
li plik = C: \ CPP\Progs \zakupy. txt
Poniżej znajduje się wynik działan ia programu przedstawionego na listingu 1 6. 2 : 1:
s a r dyn k i l o d y c z e ko l a d o w e 3 : pra ż o n a ku ku r y d z a 4 : pory
2 :
5:
serek w i e j s ki 6 : olej z ol iwek 7 : ma s ł o 8 : tofu 9:
Kon i e c
Zauważ, ż e jeśli ustal isz znak : j ako znak kończący, symbol nowej linii traktowany j est tak samo jak inne znaki. D l atego symbol nowej linii na końcu pierwszej linii l isty zakupów staj e się pierwszym znakiem ciągu „ s e r e k w i e j s k i " . Znak nowej linii na końcu dmgiej linii pliku wejściowego staje się całą treścią dziewiątego ciągu wejściowego.
Używanie obiektów string Nauczyłeś się już tworzyć na różne sposoby obiekty s t r i n g , wyświetlać ich zawartość, wczyty wać, dodawać i przypisywać do nich dane, a także łączyć ze sobą dwa obiekty s t r i n g . Co jeszcze możesz z nimi zrobić? Możesz porównywać ze sobą ciągi znaków. Wszystkie sześć operatorów porównania j est prze ciążonych dla obiektów s t r i n g . Jeden obiekt j est uznawany za mniejszy od dmgiego. jeśli pojawia się wcześniej według porządku obowiązuj ącego na danym komputerze. Jeśli porządek ten jest wyznaczany przez kod ASCII, wtedy cyfry są uznawane za mniej sze od dużych li ter, a duże litery są mniej sze od małych liter. Każdy operator porównania posiada trzy wersj e prze ciążone, umożl iwiaj ące porównywanie obiektu s t r i n g z innym obiektem s t r i n g , porówny wanie obiektu s t r i n g z ciągiem znaków języka C oraz porównywanie ciągu znaków j ęzyka C z obiektem s t r i n g : s t r ing s n a k e l ( " ko b r a " ) ; s t r i n g s n a k e 2 ( " ko r a l " ) ; char s n a k e 3 [ 2 0 ] = " a n a k o n da " ; i f ( s n a ke l < s n a ke 2 ) ll operator< (const string &, const string &)
947
Rozdział 1 6 - Klasa string oraz biblioteka STL if
! snakel
snake 3 )
if
( snake3
! = s n a ke2 )
li operator= = (const string & , const char *) li operatu r ' = (const char *, cunst string &J
Możesz też sprawdz i ć długo ść obiektu s t r i n g . F u n k cj e sk ł adowe si ze ( ) oraz l e n g t h ( ) zwracaj ą l i czbę znaków w c i ągu: if
( s n a ke l . l e n g t h ( ) " Ob a
cout # i n c l u de < s t r i n g > # i n c l ude < e s t d l i b > # i n c l ude < c t im e > # i n c l u de < c c t yp e > using s t d : : s t r i n g ; const i n t NUM 26; con s t s t r i n g w o r d l i s t [ NUM ] = ( " a g r e s t " , " b i g o s " , " c z a r a " , " d rewno " , " e k i pa " , " f i g u ra " , " go r yc z " , " h i e n a " , " i s k r a " , " j u n i o r " , " k r a t a " , " l i z a k " , " m i s ka " , " n u re k " , " ob u w i e " , " p i s a k " , " r o z um " , " s ma l e c " , " t a r y f a " , " um i a r " , " v i d e o " , " w e l o n " , " ye t i " , " z a l e w " , " z imn o " , " z apa s " } ; =
int ma i n ( ) using s t d : : co u t ; u s i ng s t d : : c i n ; u s i ng s t d : : t o l owe r ; u s i ng s t d : : e n d l ;
s t d : : s ra n d ( s t d : : t ime ( O ) ) ; char p l a y ;
cou t < < " C z y c h c e s z z a g r a ć w g r ę s ł own ą ? < t / n > " ;
949
Rozdział 1 6 Klasa string oraz biblioteka STL ·
cin > > play; play = t o l ower ( p l a y ) ; whi l e ( p l a y = = 2 t 2 ) { s t r i n g t a r g e t = w o r d l i s t [ s t d : : ra n d ( )
% NUM ] ;
int l e n g t h = t a rg e t . l e n g t h ( ) ;
s t r i n g a t t empt ( l e n g t h , s t r ing badcha r s ; int g u e s s e s ·= 6 ;
2 -2 ) ;
< < " Z gadn i j mo j e s e k r e t n e s ł owo . Ma o n o " < < l e n g t h < < " l i t e r . Mo ż e s z z g adywa ć \ n " < < " p o j ednej l i ter ze n a r a z . Możes z pomy l i ć s i ę " < < g ues ses O & & a t t e mp t ! = t a r ge t )
cout
char l e t t e r ;
c o u t < < " Z g a dn i j l i t e r ę : " ; c i n > > l e t te r ; i f ( b a d c h a r s . f i n d ( l e t t e r ) ! = s t r i n g : : np o s 1 1 a t t empt . f i n d ( l e t t e r ) ! = s t r i n g : : npo s ) cout 0)
c o u t < < " N i e p o p r a w n e l i t e r y:
cout
if
«
"Mo ż e s z p omy l i ć s i ę j e s z c z e "
"
< < badc h a r s < < e n d � ;
«
gue s s e s
«
" ra z y \ n " ;
( gue s s e s > 0 ) c o u t < < " T o j u ż c a ł e s ł owo ! \ n " ;
el se
c o u t > play; p l a y = t o l ow e r ( p l a y ) ;
cout t Zgadn i j m o j e s e k re t n e s ł owo . Ma o n o 6 l i t e r . Mo ż e s z z ga d y w a ć p o j ed n e j l i t e r z e n a ra z . Mo ż e s z pomy l i ć s i ę 6 r a z y . Two j e s ł o w o : Zgadn i j l i t e r ę : o Nie ma t a k i e j l i t e r y ! Two j e s ł o w o : Ni epop r a w n e l i t e r y : o Może s z pomyl i ć s i ę j e s z c z e 5 r a z y Zgadn i j l i t e r ę : a Poprawna l i t e r a ! Two j e s ł o w o : aN i epop r a w n e l i t e r y : o Moż e s z pomyl i ć s i ę j e s z c z e 5 r a z y Zgadn i j l i t e r ę : k Nie ma t a k i e j l i t e r y ! Two j e s ł o w o : a N i epop r a w n e l i t e r y : o k Mo ż e s z pomyl i ć s i ę j e s z c z e 4 r a z y Zgadn i j l i t e r ę : r Poprawna l i t e ra ! Two j e s ł owo : a - r--
-
951
Rozdział 1 6 - Klasa string oraz biblioteka STL N i e p op r a w n e
l itery :
Mo ż e s z p o my l i ć s i ę l i terę :
Zgadn i j Pop r a w n a Two j e
N i ep o p r a w n e
l i t e rę :
Pop r a wn a
N i e p op r a w n e
a-resl itery :
agres się
l i te rę :
Pop r a w n a
ra z y
g
l i tery :
Mo ż e s z p o my l i ć Z g a dn i j
ok j es zcze 4
l i tera !
s łowo :
N i epoprawne
Tw o j e
się
l iterę :
Pop r a w n a
ra z y
ok
s
Mo ż e s z p o my l i ć
Tw o j e
j es zcze 4
l i tera !
s ł ow o :
Zgadn i j
ra z y
a - rel i tery :
Mo ż e s z p o my l i ć s i ę
Tw o j e
4
e
l i tera !
s ł ow o :
Zgadn i j
ok j es zcze
ok 4
j es zcze
ra z y
t
l i tera !
s ł ow o :
a g re s t
To j u ż c a ł e s ł ow o ! C z y chce s z
zagrać j es zcze raz ? n
Kon i e c
Uwagi d o programu Dzięki przeciążeniu operatorów porównania na listingu 1 6. 3 możesz traktować obiekty s t r i n g tak samo jak zmienne liczbowe : whi l e
( gu e s s e s >
O & & a t t emp t
! = t a r ge t )
Jest to bardziej naturalne podejście niż używanie funkcj i s t r cmp ( ) w przypadku ciągów zna ków j ęzyka C . Program używa funkcj i f i n d ( ) do sprawdzania, czy dany znak został j u ż wcześniej wybrany. Jeś l i tak, to musi znaj dować się w ciągu b a dc h a r s (niepoprawne litery) lub w ciągu a t t emp t (poprawne litery): if
(badcha rs . find ( le t t e r ) 1 1
!=
a t t emp t . f i n d ( l e t t e r )
s t r i n g : : np o s ! = s t ri n g : : npos )
Zmienna n p o s j est składową statyczną klasy string. Jej wartość, jak pewnie pamiętasz, to mak symalna liczba znaków obiektu s t r i n g . Ponieważ indeksowanie zaczyna się od zera, n p o s posiada wartość o j eden większą o d największego możliwego indeksu i może służyć j ako znacz nik niepowodzenia operacj i wyszukiwania znaku lub podciągu.
952
Klaso string W programie wykorzystana j est możl iwość dodawania do obiektu s t r i n g poj edynczych zna za pomocą operatora + = :
ków
bad c h a r s
+=
let t e r ;
li Dodaje znak do obiektu string
Najciekawszy fragment programu rozpoczyna się w miej scu, w którym program sprawdza, czy wybrana litera znaj duj e się w sekretnym słowie : loc
int
= target . f ind ( letter) ;
Jeśli l o c jest wartością poprawną, litera może zostać umieszczona na odpowiednim miej scu w ciągu, który służy do przechowywania odpowiedzi : a t temp t [ l o c ]
= l e t ter ;
Jednak dana litera może poj awić się w słowie więcej niż raz, dlatego program musi kontynuować sprawdzanie. Służy do tego dodatkowy drugi argument funkcj i f i n d ( ) , który pozwala okreś l i ć pozycję, o d której m a się rozpocząć przeszukiwanie. Poni eważ pierwsze wystąpienie litery zo stało znalezi one na pozycj i l o c , dalsze przeszukiwanie rozpoczyna się od pozycj i l o c l. Pętla while kontynuuj e p rzeszukiwanie, dopóki nie zostanie odnalezione ostatnie wystąpieme tej litery. Zauważ, że f i n d ( ) sygnalizuj e błąd, jeśli l o c posiada wartość większą niż koniec obiektu s t. r i n g : +
l i Sprawdza, czy litera nie pojawia się jeszcze w innym miejscu
loc = t a r g e t . f i n d ( l e t t e r , whi l e
( loc
loc +
l) ;
! = s t r i n g : : np o s )
{ a t t e mp t [ l o c ]
= letter ;
loc = ta rget . fi n d ( lette r ,
loc +
l) ;
Co ieszcze oferuie klasa s tring? Biblioteka s t r i n g udostępnia wiele innych możl iwości. Znaj duj ą się w niej funkcj e do usuwa nia fragmentów lub całości zawartości ciągu , funkcje do zamieniania części łl1b wszystkich zna ków ciągu na inne ciąg i , funkcj e do wstawiania znaków do ciągu, funkcj e do porównywania fragmentów lub całości ciągu z innymi ciągami czy też do pobi erania podciągów z ciągu macie rzystego. Możesz skopiować część ciągu do innego ciągu, a także zamienić zawartość dwóch obiek tów s t r i n g . Większość tych funkcj i j est przeciążona i działa nie tylko dla obiektów s t r i n g , ale też dla ciągów znaków języka C. Krótki opis biblioteki funkcj i do obsługi obiektów s t r i n g znajduje się w dodatku F, lecz n a razie przyjrzyjmy się kilku innym właściwościom tej klasy. Po pierwsze, zastanów się nad automatycznym dopasowywaniem rozmiaru. Co się dzieje, ki edy program dodaje do ciągu kolejny znak? Obiekt s t r i n g nie m oże zwiększać rozmiarów w miej scu, ponieważ mógłby zapisać pamięć zaj mowaną przez i n n e dane. D l atego potrzebny j e s t przy dział nowego bloku pamięci i skopiowanie starej zawartości do nowego miej sca. Wielokrotne powtarzanie tej operacj i byłoby ni eefektywne, dlatego w wielu implementacjach języka C++ przydzielany blok pamięci j est większy od aktualnego rozmiaru ciągu, co pozwala zarezerwować
953
Rozdział 1 6 - Klasa slring oraz biblioleka STL miejsce na nowe znaki. Jeśl i ciąg stanie się dłuższy niż przydzielona pamięć, p rogram przydziela nowy, dwukrotnie większy blok, który pozwala na dalsze dodawanie znaków bez konieczności ciągłego przydzielania nowej pamięci. Metoda ca p a c i ty ( ) zwraca aktualną wielkość bloku, a metoda re s e rve ( ) pozwa la określić minimalną i lość przydzielanej pamięci . Przykład uży cia tych metod przedstawiony j est na listingu 1 6.4.
Listing 1 6.4. str2.cpp li str2. cpp - metody capacity() oraz reserve()
# i n c l u de # i n c l u de
< i o s t r e am> < s t ring>
i n t ma i n ( ) us ing name space s t d ;
s t r i n g emp t y ; s t r i n g s ma l l = " b i t " ; s t r i n g l a rg e r = " S ł o ń - n a j l ep s z y p r z y j a c i e l kob i e t " ; c o u t < < " W i e l ko ś ć : \ n " ; c o u t < < " \ t e mp t y : " < < emp t y . s i z e ( ) < < e n d l ; c o u t < < " \ t s ma l l : " < < sma l l . s i z e ( ) < < e n d l ; c o u t < < " \ t l a rg e r : " < < l a r g e r . s i z e ( ) ( p r e g ) ; _ a u t o p t r p a u t o = p r e g ; _ a u t o p t r p a u t o ( p r e g ) ; _ _
li Niedozwolone (konwersja niejawna) li Dozwolone (konwersja jawna) li Niedozwolone (konwersja niejawna) li Dozwolone (konwersjajawna)
Szablon pozwala na użycie zwykłego wskaźnika do in icjal izacj i obiektu a u t o p t r j edynie _ konstruktorze.
w
Klasa a u to pt r to przykład inteligentnego wskaźnika ( ang. smart pointer), czyli obiektu, któ _ ry działa jak wskaźnik, ale posiada też dodatkowe cechy. Klasa ta zdefiniowana jest w taki sposób, aby w większości sytuacj i obiekt a u t o _ p t r działał j ak zwykły wskaźnik. Jeśl i ps to obiekt a u t o p t r , możesz go wyłuskać ( * p s ) , zwiększyć ( + +p s ) , użyć w celu uzyskania do _ stępu do składowych struktury ( p s - >p u f f i n d e x ) oraz przypisać do zwykłego wskaźnika wska zującego na obiekt tego samego typu. Możesz także przypisać j eden obiekt a ut o P t r do inne _ go obiektu tego samego typu, ale wiąże się to z zagadnieniami opisanymi w kolejnym punkcie.
959
Rozdział 1 6 Klasa string oraz biblioteka STL ·
Zagadnienia związane z klasą au to_ptr Klasa a u t o _ p t r n i e stanowi rozwiązania wszystk ich prob l emów. Przyjrzyj s i ę poniższemu fragmentowi kodu: a u t o p t r < in t > p i ( new int
[200 ] ) ;
li NIE 1
Pami ętaj , że de l e te musi zawsze iść w parze z new, a de l e te [ J z new [ ] Szab lon au t o p t r używa de l e te . a nic de l e te [ J , dlatego można go używać z new. ale już nic _ z new [ J . Nie istn i eje odpowiednik klasy a u t o _ p t r dla tab l i c dyn amicznych . Możesz sko piować szab lon au to _ p t r z pliku nagłówkowego memo1y, nazwać go a u t o _ a r r _ p t r i prze kształc ić tę kopię w taki sposób, aby używal a instru k cj i de l e te [ ] zam iast de lete. W tym przypadku musisz dodać obsługę operatora [ ] . .
A czy poprawny j es t poniższy kod? s tring
va c a t i o n ( " W ę d r o w a ł e m s am o t n i e
au to p t r< s t r i ng > _
pvac ( &vaca t i on ) ;
j a k pa l ec . " ) ;
li NIE'
Nie j est poprawny, ponieważ powoduj e zastosowanie operatora de l e te do pamięci spoza ster ty, co jest n i edozwo l o n e . 11
Ostrzeżenie
Powi n i e n e ś używać o b iektu a u to _ p t r tylko do pamięci p rzydzielanej za pom ocą i n st r u k cj i new, a n i e d l a pam i ę c i p rzydzi elanej za pomocą wyrażen i a new [ J l u b d l a zwykły c h z m i e n n yc h . Teraz zastanów się nad przypisaniem: a u t o_p t r < s t r i !1 g >
ps
a u t o_p t r < s t r i n g > vo c a t i o n
=
( new
s t r i n g ( " R z ą d z i ł em
s amo t n y
jak
palec . " ) ) ;
voca t ion ;
ps ;
Co się dziej e w wyniku operacj i przypisania? Jeś l i p s oraz v o c a t i o n byłyby zwykłymi wskaź nikam i , w wyn iku powstałyby dwa wskaźniki wskazuj ące na ten sam obiekt s t r i n g . W przy padku obi ektów a u t o _p t r n i c j est to dopuszczalne, pon ieważ program próbowałby dwukrot nie usunąć ten sam obiekt - raz w momencie usuwania wskaźnika p s . a drugi raz w chwi l i usuwan ia wskaźn ika v o c a t i o n . Istniej e k i l k a sposobów uniknięcia tego problemu : • Można zde fi n i ować operator przypisania, aby dokonywał kopiowania głębokiego. Dzięki
temu w wyniku przypisania powstaną dwa wskaźn iki wskazuj ące na dwa odrębne obi ek ty, z któ ry ch j eden j est kopią drugiego. • Można usta l i ć poj ęcie własności, dzięki czemu tyl ko jeden wskaźni k może posiadać dany obiekt. D estruktor inteligentnego wskaźn ika u suwa wskazywany przez n iego obiekt tylko wtedy, kiedy wskaźnik jest jego właścicielem, a przypisanie p owo duj e przekazanie wła sności obiektu. Właśnie takie rozwiązan ie stosowane jest w klasie a u to pt r . • Można utworzyć j e szcze bardzi ej intel igentny wskaźn ik. który przechowuj e informacj e o tym . i l e wskaźni ków wskazuj e na d a n y obiekt. Taki mechan izm nazywa się =licwniem
960
Biblioteka STL
referencji. Przy p i s a n i e może wtedy z w i ę k s z a ć wartość l i c z n i k a . a usuwan i e w s k aź n i k a zmniej szać j ą. [ n struk cj a de lete j es t wywoływana d o p i ero
w t ed y,
k i edy u s uwany J e s t
ostatn i wskazuj ący na dany obiekt w s k aźn i k .
Tc
same
techn i k i d otyczą także k o nstruktora
Każda z tych tec h n i k posiada acj a ,
w
zastosowa n i a . Poniżej przedstaw 1 0 n a j e s t przykładowa sytu
której wskaźn i k i a u t o _p t r
auto ptr _ {
)
swoj e
;
kopiującego.
mogą
d z i ał a ć mepoprawni e :
f i lm s [ S ]
auto p t r< s t r ing>
( new s t r i n g ( " P t a s i
turniej " ) ) ,
auto ptr _ auto ptr< s t r ing>
( new s t r i n g ( " K i w k i
ka c z k i " ) ) ,
auto ptr< s t ring>
( new s t r i n g ( " B l ą d
a u t o _p t r < s t r i n g >
( new s t r i n g ( " G ą s k a n a b r amc e " ) ) ,
a u t o _p t r < s t r i n g > int
i ;
( new
s t ring ( " S t rzelaj ący kurczak" ) ) , indyka " ) ) ,
pwin ( f i lms [ 2 ] ) ;
c o u t < < " N om i n owa n i w k a t e g o r i n a j l e psz y f i l m o l a t a j ą c ym p i ł k a r z u t o \ n " ; (i
for
=
O;
cout n ; ve c t o r s c o r e s ( n ) ;
us ing name space
962
li Wektor 5 liczb całkowitych
li Wektor
11
liczb zmiennoprzecinkowych
Biblioteka STL
Po utworzeniu obiektu v e c t o r przeciążenie operatora [ J umożliwia dostęp do poszczegó l nych elementów za pomocą nonnalnej notacj i dla tab l i c : ra t i ngs [ O ] for
cou t
9;
=
O;
=
( i nt i
i
< n;
< < scores [ i J
i++)
< < endl ;
Ponownie alokatory Podobnie jak klasa s t r i n g różn e konte n e ry b i b l i oteki STL p rzyj m ują dodatkowy argu ment, któ ry pozwala okre ś l i ć o b i e kt alokatora używany d o zarządzan ia pamięcią. Na p rzy kład szablon ve c t o r rozpoczyna s i ę od pon iższej deklaracj i : temp l a te
J e ś l i p o m i n i e s z w a rt o ś ć t e g o a rg u m e n t u , sza b l o n k o n te n e ra u żyje d o myś l n i e klasy a l l o c a t o r < T > . Klasa ta wykorzystuje w zwykły sposób i n stru kcje new oraz de l e te . Klasa ve c t o r użyta jest w prostej ap l ikacj i przedstawionej na l i stingu 1 6. 5 . Ten konkretny program tworzy dwa obiekty ve c t o r jeden to konkretyzacj a dla typu int, drugi to konkre tyzacj a dla typu s t r i n g . Każdy z tych obiektów posi ada 5 elementów. -
Listing 1 6.5. vectl .cpp li vect l . cpp
# inc lude
-
przedstawienie pod�taw011:rch .fimkcji s::ahlonu vec/or
< i o s t ream>
# i n c l u de
< s t r i ng>
# i n c l u de
con s t int N U M
=
5;
i n t ma i n ( ) u s i ng s t d : : v e c t o r ; u s i ng s t d : : s t r i n g ; u s i ng s t d : : c i n ; u s i ng s t d : : c o u t ; u s i ng s t d : : e n d l ;
ve c t o r < i n t >
r a t i n g s ( N UM ) ;
ve c t o r < s t r i n g > cou t <
ratings [ i ] ; c i n . ge t ( ) ; cout # i n c l u de struct Rev i e w
{ std : : s t ring t i t l e ; int r a t i n g ;
); bool Fi l !Revi ew { Re v i e w & r r ) ; void ShowRe v i e w { cons t Rev i e w &
rr) ;
int ma i n { ) (
using s t d : : c o u t ; using s t d : : ve c t o r ;
vec to r < Rev i e w > b oo k s ; Re view t emp ; while ( F i l l Rev i e w { t emp ) ) boo k s . pu s h_b a c k { t emp ) ; books . s i ze { ) ; int num if ( n um > 0 ) c o u t < < " D z i ę k u j ę . Wp i s a ł e ś n a s t ę p u j ące d a n e : \ n " < < " Oc e n a \ t K s i ą ż ka \ n " ;
967
Rozdziol 1 6 Klaso string oraz biblioteko STL -
for ( i n t i = O ; i < n um ; i + + ) S howRe v i e w ( b o o k s [ i ] ) ; c o u t < < " P r z ypomn i e n i e o c e n : \ n " < < " O ce n a \ t K s i ą ż ka \ n " ; ve c t o r < Re v i e w > : : i t e r a t o r p r ; for ( p r = b o o k s . b e g i n ( ) ; p r ! = b o o k s . e n d ( ) ; p r + + ) S h owRev i e w ( * p r ) ; v e c t o r o l d l i s t ( b o o k s ) ; li Używany konstruktor kopiujący i f ( n um > 3 ) { li Usuwa 2 elementy b o o k s . e r a s e ( bo o k s . b e g i n ( ) + 1 , b o o k s . b e g i n ( ) + 3 ) ; cout < < " Po u s u n i ę c i u : \ n " ; for ( p r = b o o k s . b e g i n ( ) ; p r ! = b o o k s . e n d ( ) ; p r + + ) S h owRe v i e w ( * p r ) ; li Wstawia I element books . i n s e rt (books . begin ( ) , oldl i s t . begin ( ) + 1 , o l dl i s t . b e g i n ( ) + 2 ) ; c o u t < < " Po w s t a w i e n i u : \ n " ; for ( p r = b o o k s . be g i n ( ) ; p r ! = b o o k s . e n d ( ) ; p r + + ) S h ow Re v i e w ( * p r ) ; b o o k s . s wap ( o l d l i s t ) ; c o u t
cool s t u f f ;
sort ( coo l s t u f f . b e g i n ( ) ,
cool s tu f f . end ( ) ) ;
powoduj e posortowanie zawartości wektora c o o 1 s t u f f w porządku rosnącym, używając do porównywania wartości wbudowanego operatora < . Jeśli elementy kontenera t o obiekty zdefiniowane przez użytkownika, wtedy musi istnieć funkcj a opera tor< ( ) porównuj ąca obiekty tego typu. Możesz posortować wektor zawierający obiek ty typu Rev i e w , jeśli udostępnisz składową lub nieskładową wersję funkcj i ope rato r < ( ) . Ponieważ Re v i e w j est strukturą, j ej składowe są publiczne i poniższa funkcj a nieskładowa działa poprawnie: bool opera tor< ( cons t Rev i e w
& rl ,
con s t Re v i e w &
r2 )
{ if
( r l . t i t le < r2 . t i t l e ) re turn true ;
else i f
( r l . title
==
r2 . t i t le & & r l . rating < r2 . rat ing )
return true ; el se re turn fal s e ;
Jeśli udostępnisz taką funkcję, możesz posortować wektor obiektów Rev i e w, na przykład wek tor boo k s : sort ( b oo k s . b e g i n ( ) ,
boo ks . end ( ) ) ;
Ta wersja funkcj i opera tor< ( ) sortuj e obiekty leksykograficznie według składowych t i t l e. Jeśli dwa obiekty maj ą taką samą składową t i t l e , wtedy sortowane są według składo wych rating. Załóżmy j ednak, że chcesz posortować wektor w porządku malej ącym lub we dług ocen , a nie według tytułów. Do tego celu służy druga wersj a funkcj i s o r t ( ) . Przyjmuj e ona trzy argumenty. Dwa pierwsze to ponownie iteratory, które określają zakres. Ostatni argu ment to wskaźnik na funkcj ę (obiekt funkcyjny), która zastępuje przy porównywaniu funkcj ę operator< ( ) . Musi istnieć możl iwość przekształcenia zwracanej wartości na typ logiczny, a wartość fal se powinna wskazywać, że argumenty tej funkcj i uporządkowane są niepopraw nie. Poniżej znajduj e się przykład takiej funkcj i : bool Wo r s e T h a n ( con s t Rev i e w if
& rl ,
con s t Rev i e w
& r2 )
( r l . rating < r2 . r a t ing ) re turn true ;
el se return f a l s e ;
Korzystając z tej funkcji, możesz za pomocą poniższej i nstrukcj i posortować wektor b o o k s obiektów Rev i e w rosnąco według wartości ocen: sort ( books . begin ( ) ,
b o o k s . end ( ) ,
WorseTha n ) ;
971
Rozdział 1 6 Klaso string oraz biblioteka STL -
Zauważ, że funkcja W o r s e T h a n ( ) j est mniej precyzyjna w porządkowaniu obiektów Rev i e w o d funkcj i ope r a t o r < ( ) . Jeś li dwa obiekty posiadają taką samą składową t i t l e , funkcja opera tor< ( ) sortuje je według składowej ra t i ng. Jeśl i jednak dwa obiekty posiadają taką samą składową ra t i n g , funkcja Wo r s eThan ( ) traktuje je jako równe. Pierwszy rodzaj upo rządkowania nazywa się uporządkowaniem wyczerpującym (ang. lota/ ordering), a drugi rodzaj to uporządkowanie ścisłe słabe (ang. strict weak ordering). W przypadku uporządkowania wy czerpującego jeśli nie j est prawdą, że a < b lub że b < a, to a i b muszą być identyczne. W przy padku uporządkowania ścisłego słabego nie musi tak być. a i b mogą być takie same, lecz mogą również posiadać identycznąjedynie jedną wartość, jak składową ra t i n g w przykładowej funkcji Wo rs e T h a n ( ) . Zamiast więc mówić, że dwa obiekty są identyczne, naj lepiej jest wtedy okre ślić je j ako równoważne. Na listingu 1 6. 7 przedstawione j est zastosowanie tych funkcj i .
Listing 1 6.7. vect3.cpp li vect3 . cpp - użycie .funkcji biblioteki STL
# in c l ude # i n c l u de # i n c l ude # i nc l ude
< i o s t r e am> < s t r ing> < ve c t o r > < a l g o r i thm>
s truc t Rev i e w
I;
( s td : : s t r i ng t i t l e ; int r a t i ng ;
b o o l ope r a t o r < ( co n s t Rev i e w
& r l , cons t Rev i e w & r2 ) ; & r l , cons t Rev i e w & r2 ) ; b o o l F i l l R e v i e w ( Re v i e w & r r ) ; vo i d S h owRev i e w ( cons t Rev i e w & r r ) ; i n t ma i n ( ) ( u s ing name space s t d ; b o o l wo r s e T h a n ( cons t Re v i e w
ve c t o r < R e v i e w > b o o k s ; Rev i e w t emp ; whi l e ( F i l l R e v i e w ( t emp ) ) b o o k s . p u s h _ b a c k ( temp ) ; c o u t < < " D z i ę k u j ę . Poda ł e ś " r r . r a t i n g ; if ( ! s t d : : c i n )
";
return false ;
std : : cin . get ( ) ; return true ;
void ShowRev i ew ( cons t Rev i e w
&
rr)
std : : cout i t em val ) re turn s t a r t ; re turn O ;
start
& val )
s t a r t - >p_n e x t )
Ponownie możesz użyć szablonu w celu uogólnienia funkcji na listy dowolnego typu obsługującego operator ==,jednak taki algorytm wciąż będzie zależny od jednej struktury danych - listy związanej . Jeśli zastanowisz s i ę nad szczegółami impl ementacj i , zobaczysz, ż e obie funkcje f i n d używają nieco odmiennych algorytmów. Jedna do poruszania się po liście używa indeksów tablicy ele mentów, druga w tym celu przesuwa wskaźnik z pozycji s t a r t na s t a r t - > p_ n e x t . Jednak idea dzi ałania obu tych algorytmów jest taka sama i polega na porównywaniu wartości każdego elementu kontenera z szukaną wartością. Celem programowania generycznego w takiej sytuacj i jest utworzenie pojedynczej funkcj i f i n d, która może działać dla tablic, dla list związanych, a także dla dowolnego innego kontenera. Oznacza to, że funkcj a powinna być niezależna od typu danych przechowywanych w kontenerze, ale także od struktury danych samego kontenera. Szablony udostępniają generyczną reprezentację typu danych przechowywanych w kontenerze, lecz potrzebna jest jeszcze generyczna reprezentacja procesu poruszania się po danym kontenerze. Taką uogólnioną reprezentacją są właśnie iteratory. Jakie właśc iwości powinien mieć iterator, aby możliwe było zaimplementowan ie funkcji f i n d"7 Poniżej maj duj e się krótka lista: Powinna istnieć możl iwość wyłuskania iteratora w celu uzyskania dostępu do wartości, : • a którą wskazuje. Jeśli p jest iteratorem, powinno być zdefiniowane wyrażenie „ p . Powinna istnieć możl iwość przypisania j ednego iteratora d o drugiego. Jeś l i p i q s ą itera torami , powinno być zdefiniowane wyrażenie p = q. " Powinna istnieć możl iwość porównania j ednego iteratora z drugim. Jeśl i p i q są iterato rami , powinny być zdefiniowane wyrażenia p == q oraz p ! = q. • Powinna istnieć możliwość odwiedzania za pomocą iteratora wszystkich elementów kon tenera. Można to osiągnąć, definiuj ąc dla iteratora p instrukcje + + p oraz p + + . •
•
976
Programowanie generyczne Istnieją także inne rzeczy, które mógłby robić iterator, jednak do implementacj i funkcj i f i n d niezbędne s ą tylko t e wymienione. W rzeczywistości w bibl iotece STL występuj e kilka rodzaj ów iteratorów o różnych możliwościach, do którego to zagadnienia j eszcze wrócimy. Zauważ, że podstawowe wymagania stawiane iteratorowi spełn ia zwykły wskaźnik, dlatego możesz przepi sać funkcję f i n d_a r ( ) w następujący sposób: i te rato r ; i t e r a t o r f i n d_a r ( i t e r a t o r a r ,
typede f double
( int i
*
=
O; i < n; va! ) re turn a r ; return O ; for
if
( *ar
==
i++ ,
int n ,
const double
& va ! )
ar++ )
Następnie możesz zmienić l istę parametrów funkcj i , aby przyjmowała j ako argumenty określaj ą ce zakres wskaźnik na początek tablicy oraz wskaźnik na pozycję za ostatnim elementem tablicy. Listing 7.8 w rozdziale 7 . , „Funkcje - składniki programów w C++", przedstawia podobną technikę. Funkcja może zwracać końcowy wskaźnik j ako sygnał, że wyszukiwanie się nie powio dło. Poniżej przedstawiona j est poprawiona wersja funkcji f i n d_ ar ( ) : typedef doub l e i terator
*
i te ra t o r ;
f i nd a r ( i t e r a t o r beg i n ,
i t e ra t o r e n d ,
con s t double
&
val l
iterator a r ; ( a r = begin ; a r ! = end; ar++ ) if ( * a r == va ! ) re turn a r ; re turn e n d ; li Sygnalizuje, że nie znaleziono wartości va/
for
W funkcji f ind _ 1 1 ( ) możesz utworzyć klasę iteratora, w której zdefiniowane są operatory * oraz + + :
struct Node double i t e m ; ) ;
Node * p_n e x t ;
class
i tera tor
Node * pt ; publ ic :
i terator ( ) : pt ( O ) { ) i t e r a t o r ( Node * pn ) pt (pn ) { ) double operato r * ( ) { re turn p t - > i tern ;
977
Rozdział I 6 - Klaso string oraz biblioteko STL i t e r a t o r & ope rator+ + ( )
li Dla preinkrementacji + + it
I
pt
=
p t - >p n e x t ;
re turn * th i s ;
i t e r a t o r operato r + + ( int )
li Dla postinkrementacji it+ +
i t e r a t o r tmp = * thi s ; p t = p t - >p n e x t ; return tmp ; li
. . . operator= =(), operator!={) itd.
} ;
Do rozróżnienia przedrostkowej od przyrostkowej wersj i operatora + + język C++ używa konwen cj i określaj ącej postać operato r + + ( ) j ako przedrostkową, a postać o p r e r a t o r + + ( int ) jako przyrostkową. Argument operatora nigdy nie j est używany, dlatego nie musi posiadać nazwy. W tym miej scu szczegóły definicj i klasy i t e r a t o r nie są najważniej sze. Naj bardziej istotne jest, j ak za pomocą tej klasy napisać drugą funkcj ę find: i t e ra t o r f i n d_l l ( i t e r a t o r h e a d , i terator start ; ( s ta r t = head ; s t a r t i f ( * s t a r t == va l ) re turn s t a r t ; re turn O ; for
cons t double &
!= O;
va l )
+ + s tart )
Funkcja ta jest prawie i dentyczna jak funkcja f i n d_a r ( ) . Różnica polega na innym sposobie określania osiągnięcia końca kontenera. Funkcj a f i n d_a r ( ) używa w tym celu iteratora na pozycję za ostatn i m elementem , podczas gdy funkcj a f i n d_l l ( ) używa do tego wartości zero wej przechowywanej w ostatnim elemencie. Jeśli uda się usunąć tę różnicę, obie funkcj e staną się identyczne. M ożesz na przykład ustalić, że l ista związana posi ada j eden dodatkowy element po ostatni m rzeczywistym elemencie kontenera. Oznacza to, że zarówno tabl ica, jak i lista zwią zana będą znaj dować się za ostatni m e l ementem. Wtedy w funkcj ac h f i n d a r ( ) oraz fi n d_ 1 1 ( ) m ożesz wykrywać koniec danych w taki sam sposób, przez co obie funkcje staną się identyczne. Zwróć uwagę na to, że potrzeba pozycji za ostatni m elementem w l iście związa nej wiąże się ze stawianiem wymagań klasie kontenerowej , a nie iteratorom. W bibliotece STL używane j est właśnie takie podejście. Po pierwsze, w każdej klasie kontenero wej (v e c t o r , l i s t , d e q u e i innych) znajduj e się definicj a odpowiedniego iteratora klasy. Dla jednej klasy może to być wskaźnik, dla innej m oże to być obiekt. W zależności od implemen tacji iterator udostępnia potrzebne operacje, j ak * i + + . Niektóre klasy m ogą wymagać udostęp niania większej liczby operacj i . Po drugie, każda klasa kontenerowa posiada wartość oznaczającą pozycję za ostatni m elementem, która jest przypisywana do iteratora, kiedy zostanie przesunięty poza ostatni element kontenera. Każda klasa kontenerowa posiada metody b e g i n ( ) oraz end ( ) ,
978
Programowanie generyczne które zwracają iteratory na pierwszy element kontenera oraz na pozycję za ostatnim elementem. Każda klasa kontenerowa udostępnia też operację + +, która pozwala iteratorowi odwiedzić wszystkie elementy kontenera między pozycją pierwszego elementu a pozycją za ostatnim elementem. Do używan ia klasy kontenerowej nie musisz znać szczegółów implementacj i i teratorów ani implementacj i pozycji za ostatnim elementem. Wystarczy wiedzieć, że dana klasa posiada itcratory. funkcja b e g i n ( ) zwraca iterator na pierwszy element, a funkcja e n d ( ) zwraca iterator na pozycj ę za o s t a t n i m e l e m e n t e m . Z a ł ó ż m y, że c h c e s z w y ś w i e t l i ć w a rt o ś c i o b i e k tu ve c t o r . Możesz użyć do tego poniższego kodu : v e c t o r : : i t e r a t o r p r ; for ( p r = s c o r e s . b e g i n ( ) ; p r cou t
s o r t ( Ra n d omA c c e s s i t e r a t o r
first ,
R a n domA c c e s s i t e r a t o r
last) ;
informuje, że algorytm sortujący wymaga iteratora dostępu swobodnego. lteratory wszystkich pięciu rodzaj ów można wyłuskać (zdefini owany j est dla nich operator * )_ a także można je porównać na okoliczność równości (używając przeciążonego zazwyczaj opera tora = = ) oraz nierówności (używając przeciążonego zazwyczaj operatora ! = ). Jeśli dwa i teratory są równe, wtedy wyłuskanie j ednego z n ich powinno dać taki sam wynik co wyłuskanie drugie go. Oznacza to, że jeśli prawdą j est : iterl == i ter2
to prawdą j est również: * i te r l == * i te r 2
Oczywiście właściwości t e s ą prawdziwe również dla wbudowanych operatorów i wskaźników, dlatego wymagania te stanowią wskazówkę, co musisz zrobić, przeciążając te operatory dla klasy iteratora. Przyjrzyj my się teraz właściwościom poszczególnych iteratorów.
lteratory weiściowe Pojęcie wejście używane j est z punktu widzenia programu. Oznacza to, że infonnacj e przekazy wane z kontenera do programu traktowane są j ako wejściowe, podobnie jak infonnacj e pocho dzące z klawiatury. /terator wejściowy to taki iterator. który umożliwia odczytywanie w progra mie wartości el ementów kontenera. W szczególności wyłuskiwanie iteratora wej ściowego musi pozwalać na odczytywanie w programie wartości elementów kontenera, ale nie powinno pozwa lać na zmianę tych wartości . Algorytmy, które wymagają iteratora wejściowego, to algorytmy niezmieniaj ące wartości elementów kontenera. Jterator wejściowy musi pozwalać na uzyskanie dostępu do wszystkich elementów kontenera. Czyn i to za pomocą operatora ++ w postaci przedrostkowej oraz w postaci przyrostkowej . Jeśli ustawisz iterator wej ściowy na pierwszy element kontenera i będziesz zwiększać go, dopóki nie osiągn ie pozycj i za ostatnim elementem, po drodze odwiedzi on każdy element kontenera. Co ciekawe, nie ma gwarancj i , że powtórne przejście po kontenerze za pomocą iteratora wej ściowe go spowoduje odwiedzenie wszystkich elementów w tej samej kolejności. Ponadto po inkremen-
980
Programowanie generyczne
tacj i iteratora wej ściowego nie możesz mieć pewności, że wciąż istnieje możl iwość wyłuskania jego poprzedniej wartości . Każdy algorytm oparty na iteratorze wej ściowym powinien być algo rytmem jednoprzebiegowym, który nie polega na wartościach iteratora z poprzedn iego przebiegu ani na wcześniej szych wartościach iteratora z tego samego przebiegu. Zauważ, że i tera tor wej ściowy j est i tera torem j ednokierunkowym - można zwiększyć j ego war tość, ale nie można j ej zmniej szyć.
lteratory wyjściowe W bibl iotece STL termin wyjście oznacza, że iterator używany j est do przekazywania informacji z programu do kontenera, więc wyj ście programu j est j ednocześnie wej ściem kontenera. lterator wyjścim�y jest podobny do iteratora wejściowego, ale wyłuskanie musi pozwalać na zmianę war tości kontenera przez program, nie musi jednak pozwalać na odczyt tej wartości. Jeśli możl iwość zapisu bez możl iwości odczytu wydaje Ci się dziwna, pamiętaj , że taka sama właściwość przy sługuje wyj ściu wyświetlającemu dane na monitorze. Strumień cou t zmienia wyświetlane zna ki, ale nie może odczytać tego, co poj awia się na monitorze. Biblioteka STL j est na tyle ogólna, że jej kontenery mogą reprezentować urządzenia wyj ścia, dlatego możesz natrafić na podobną sytuację, pracując z kontenerami. Ponadto, jeśli algorytm modyfikuje zawartość kontenera (na przykład tworząc nowe wartości do zapisania) bez wczytywania j ego zawartości, nie ma potrze by, aby używał iteratora umożliwiaj ącego odczyt wartości elementów.
Możesz używać iteratorów wej ściowych dla jednoprzebiegowych algorytmów wymagających je dynie odczytu elementów, a iteratorów wyj ściowych dla jednoprzebiegowych algorytmów wy magających jedynie zapisu danych.
lteratory postępujące Podobnie jak iteratory wejściowe i iteratory wyj ściowe iteratory postępujące używają j edynie operatora ++ do poruszania się po kontenerze. D latego iteratory postępujące pozwalają poruszać się po kontenerze tylko do przodu i tylko po j ednym elemencie naraz. Jednak w odróżnieniu od iteratorów wejściowych i wyj ściowych kolejność poruszania się po sekwencj i wartości j est za wsze taka sama. Ponadto po inkrementacj i iteratora postępującego wciąż możesz wyłuskać j ego poprzednią wartość, jeśli tylko ją zapisałeś, i będzie ona taka sama. Właściwości te umożliwiaj ą stosowanie algorytmów wieloprzebiegowych. Iterator postępujący może umożl iwiać równocześnie odczyt i zapis danych, może też pozwalać jedynie na ich odczyt: int * p i r w ; const int * p i r ;
li !tera/or do odczytu i zapisu // !tera/or tylko do odczytu
981
Rozdział 1 6 Klaso string oraz biblioteko STL -
lteralory dwukierunkowe Załóżmy, że posiadasz algorytm, który wymaga poruszan ia się po kontenerze w dwóch kierun kach. Na przykład funkcj a odwracająca dane może zamieniać pierwszy element z ostatnim, zwięk szać wskaźnik na pierwszy element, zmniej szać wskaźnik n a ostatni element. a następn ie powta rzać tę operację. lterator dwukierunkowy ma wszystkie właściwości iteratora postępującego oraz dodatkowo obsługę dwóch postaci operatora dekrementacj i - przedrostkowej i przyrostkowej .
lteralory dostępu swobodnego Niektóre algorytmy, jak standardowe sortowanie i przeszukiwanie binarne, wymagają możliwo ści bezpośredniego przej ścia do wybranego elementu kontenera. Takie przechodzenie to dostęp sll'ohodny i wymaga iteratora dostępu swobodnego. Ten typ iteratora posiada wszystkie cechy iteratora dwukierunkowego oraz dodatkowo obsługuj e operacje, które pozwalają na dostęp swo bodny (jak dodawanie wskaźników), a także operatory relacyjne do porównywania elementów. W tabel i 1 6 . 3 zebrane zostały operacje iteratora dostępu swobodnego, których nie obsługuje iterator dwukierunkowy. W tej tabel i X reprezentuje iterator dostępu swobodnego, T reprezentu je typ, na który wskazuj e iterator, a oraz b to wartości iteratora, n j est liczbą całkowitą, a r jest dowolnym iteratorem lub referencj ą
Tabela 1 6. 3. Operacje iteratora dostępu swobodnego Wyraże n i e
Opis
a+n
Wskazuje na n-ty element po elemencie, na który wskazuje a
n+a
To samo co a
+
n
a-n
Wskazuje na n-ty element przed elementem , na który wskazuje a
r += n
To samo co r = r + n
r -= n
To samo co r = r - n
a[n]
To samo c o *(a + n )
b-a
Wartość takiego n, że b
a O
a>b
Prawda, jeśli b < a
a >= b
Prawda, jeśli ! (a < b)
a b)
=
a-n
Wyrażenia takie jak a + n są prawdziwe tylko wtedy, kiedy a oraz a w zakresie kontenera, włączając w to pozycję za ostatnim elementem.
982
+
n znaj dują się
Programowanie generyczne
Hierarchia iteratorów Prawdopodobnie zauważyłeś, że rodzaj e iteratorów tworzą pewną hierarchię. Iterator postępuj ą cy ma wszystki e właściwości iteratora wej ściowego oraz iteratora wyjściowego wraz z pewnym i dodatkowymi cecham i . Iterator dwukiernnkowy ma wszystkie właściwości iteratora postępuj ą cego oraz własne cechy. lterator dostępu swobodnego łączy w sobie właściwości wszystkich po zostałych iteratorów oraz ma własne dodatkowe cechy. W tabeli 1 6 .4 przedstawi one są główne właściwości iteratorów. W tabeli i oznacza iterator, a n li czbę całkowitą.
Tabela 1 6.4. Właściwości iteratorów
Właściwość iteratora
lterator wejści owy
lterator wyjśc i owy
lterator p ostępujący
lterator dwu k i e runkowy
lterator dostępu swo bodnego
Wyłuskiwanie do odczytu
Tak
Nie
Tak
Tak
Tak
Wyłuskiwanie do zapisu
Nie
Tak
Tak
Tak
Tak
Stała i powtarzalna kolejność przechodzenia
Nie
Nie
Tak
Tak
Tak
++i oraz i++
Tak
Tak
Tak
Tak
Tak
-i oraz i-
Nie
Nie
Nie
Tak
Tak
i[n]
Nie
Nie
Nie
Nie
Tak
i+n
Nie
Nie
Nie
Nie
Tak
i-n
Nie
Nie
Nie
Nie
Tak
i += n
Nie
Nie
Nie
Nie
Tak
i -= n
Nie
Nie
Nie
Nie
Tak
Algorytm napisany dla konkretnego rodzaju iteratora może używać tego właśnie iteratora lub dowolnego innegoposiadaj ącego wymagane właściwości. Dlatego na przykład kontener z iterato rem dostępu swobodnego może używać algorytmów napisanych dla iteratora wejściowego. Po co istnieją różne rodzaje iteratorów? Umożliwiają one pisanie algorytmów, które używaj ą iteratorów z najmniej szymi wymaganiami, dzięki czemu można i c h używać d l a większej liczby kontenerów. Funkcja f i n d ( ) używa prostego iteratora wej ściowego i dzięki temu m ożna uży waćjej dla dowolnego kontenera zawieraj ącego wartości , które można odczytać. Funkcja s o r t ( ) wymaga iteratora dostępu swobodnego i można j ej używać j edynie dla tych kontenerów, które obsługują iteratory tego typu. Zauważ, że różne rodzaj e iteratorów nie stanowią odmiennych typów, a raczej zestawy właści wości. Każda klasa kontenerowa posiada w swym zakresie definicj ę typu o nazwie i t e r a t o r . Na przykład klasa ve c t o r < in t > m a iteratory typu v e c t o r < in t > : : i t e r a t o r , j ednak
983
Rozdział 1 6 Klaso string oraz biblioteko STL -
w dokumentacj i tej klasy możesz przeczytać, że iteratory wektorów to iteratory dostępu swobod nego. Pozwala to używać algorytmów opartych na iteratorach dowolnego rodzaju, ponieważ ite rator dostępu swobodnego posiada wszystkie ich właściwości. Także klasa l i s t < int> ma iteratory typu li st < in t > : : i t e r a t o r . W bibliotece STL znajduj e się implementacja l isty podwójnie związanej , dlatego iteratory list to iteratory dwukierunkowe. Z tego powodu nie mo żesz używać d l a list algorytmów opartych na iteratorach dostępu swobodnego, ale możesz uży wać algorytmów opartych na mniej wymagaj ących iteratorach.
Poięcia, uściślenia i modele Bibl ioteka STL posiada pewne elementy, jak rodzaj e iteratorów, których nic można wyrazić w ję zyku C++. Oznacza to, że choć możesz zaproj ektować k l asę posiadającą właśc iwości iteratora postępującego, nie m ożesz sprawić, aby kompilator ograniczył a lgorytm do używania jedynie tej kl asy. Dziej e się tak, ponieważ iterator postępuj ący to zestaw właściwości , a nie typ. Zaprojekto wana klasa iteratora spełnia wymagan ia, ale spełnia je również zwykły wskaźnik. A lgorytm bi blioteki STL działa z dowolną implementacj ą iteratora, która ma właściwości spełniaj ące dane wymagania. Literatura dotycząca biblioteki STL używa słowa pojęcie (ang. concept) do opisu ze stawu właściwości. Istn ieją na przykład pojęcia iteratora wej ściowego oraz iteratora postępujące go. Jeśli potrzebuj esz iteratora w klasie kontenerowej , którą projektujesz, możesz skorzystać z bi blioteki STL zawierającej szablony iteratorów gotowych do wykorzystania w różnych sytuacjach. Relacje między pojęciami mogą przypominać dziedziczen ie. Na przykład iterator dwukierunko wy dziedziczy właściwości iteratora postępuj ącego. Nie możesz jednak używać mechanizmów dziedziczenia w stosunku do i tcratorów. Możesz zaimplementować i terator postępujący jako kla sę, a iterator dwukiemnkowy j ako zwykły wskaźnik. W języku C++ ten konkretny iterator dwu kienmkowy, j ako typ wbudowany, nie może dziedzi czyć po klasie, j ednak pojęciowo j est to typ pochodny iteratora postępuj ącego. Niektórzy autorzy piszący o STL używają poj ęcia uściślenie (ang. r4inement) do określenia dziedziczenia pojęciowego. Iterator dwukierunkowy jest więc uściśleniem pojęcia iteratora postępuj ącego. Konkretna implementacj a pojęcia to model. Zwykły wskaźnik na typ int jest modelem pojęcia iteratora dostępu swobodnego. Jest on także modelem iteratora postępuj ącego, ponieważ posiada wszystkie właściwości tego pojęcia.
Wskaźnik iako iterator lteratory są uogólnieniem wskaźników, a wskaźnik posiada wszystkie wymagane właściwości iteratora. lteratory tworzą interfejs algorytmów biblioteki STL, a wskaźniki są iteratorami. więc algorytmy biblioteki STL mogą używać wskaźników do operowania na kontenerach spoza tej bi blioteki, które są oparte na wskaźnikach. Możesz na przykład używać algorytmów STL dla tablic. Załóżmy, że Rece i p t s to tabl ica wartości typu double, którą chcesz posortować rosnąco: con s t int S I Z E = 1 0 0 ; Re ce i p t s [ S I Z E ] ;
double
984
Programowanie generyczne Funkcja s o r t ( ) z biblioteki STL przyj muje j ako argument iterator wskazuj ący na pierwszy element kontenera oraz iterator wskazuj ący na pozycję za ostatnim elementem. Adres pierwsze go elementu to & Re c e i p t s [ O ] lub po prostu Re c e i p t s , a adres elementu występującego za ostatnim elementem tablicy to & Re c e i p t s [ S I Z E J lub Re c e i p t s + S I Z E . Dlatego wywo łanie funkcj i : s o r t ( Re c e i p t s ,
Re c e i p t s + S I Z E ) ;
pozwala posortować tablicę. Język C++ zapewnia, że wyrażenie Re ce i p t s + n j est zdefi nio wane, jeśli jego wynik znaj duj e się w tablicy lub na pozycj i za ostatnim elementem . Język C-H obsługuje pojęcie pozycj i za ostatnim elementem, dzięki czemu możesz używać algorytmów bi blioteki STL dla zwykłych tablic. Jest to możliwe, ponieważ wskaźniki są iteratorami , a algoryt my są oparte właśnie na iteratorach. W podobny sposób możesz stosować algorytmy STL do własnych struktur danych pod warunkiem, że udostępnisz odpowiednie iteratory, którym i mogą być wskaźniki lub obiekty, oraz wskaźnik pozycj i za ostatnim elementem.
copy ( ) ,
os tream_i tera tor, i s tream i tera tor
Biblioteka STL udostępnia ki lka gotowych iteratorów. Ustalmy pewne fakty, aby zrozumieć ich zastosowanie. Algorytm copy ( ) służy do kopiowania danych z jednego kontenera do drugiego. Algorytm ten jest wyrażony w kategoriach iteratorów, możesz więc kopiować dane z j ednego rodzaju kontenera do drugiego lub nawet z tablicy lub do niej , ponieważ możesz użyć wskaźni ków elementów tablicy j ako iteratorów. Poniższy przykładowy kod powoduje przekopiowanie zawartości tabl icy do wektora : int cas t s [ l O ] = { 6 , 7 , 2 , 9 , 4 , 1 1 , 8 , 7 , 1 0 , 5 } ; vec t o r < int> d i ce [ 1 O J ; copy ( c a s t s , c a s t s + 1 0 , d i ce . b e g i n ( ) ) ; l/ Kopiuje tablicę do wektora
Pierwsze dwa argumenty funkcj i c o p y ( ) to iteratory reprezentujące kopiowany zakres, a trzeci argument to iterator określaj ący miej sce, do którego zostanie przekopiowany pierwszy element zakresu. Pierwsze dwa argumenty muszą być przynajmniej iteratorami wej ściowym. a ostam i argument musi być przynaj mniej iteratorem wyj ściowym. Kontener, do którego kopiowane są dane, musi mieć odpowiednią wielkość, aby pomieścić nowe elementy, a jego stare wartości nadpisywane są kopiowanymi. Nie możesz więc użyć funkcj i c o p y ( ) do umieszczenia danych w pustym wektorze - przynajmniej nie bez sztuczki, która opisana zostanie w dalszej części tego rozdziału. Załóżmy teraz, że chcesz przekopiować dane do strumienia wyj ścia. Możesz użyć funkcj i copy ( ) . jeśli istnieje iterator reprezentujący taki strumień. Bibl ioteka STL udostępnia taki iterator w po staci szablonu o s t r eam_ i t e r a t o r . Używając terminologii biblioteki STL, szablon ten to model pojęcia iteratora wyj ściowego. Jest to także przykład adaptatora, czyli klasy lub funkcj i , która przekształca określony interfej s w i nterfej s używany przez bibliotekę STL. Możesz utwo rzyć iterator tego typu, dołączając plik nagłówkowy iterator (wcześniej iterato1: h) i używając deklaracji:
985
Rozdział 1 6 Klaso string oraz biblioteko STL -
# i n c l ude
< i terator>
o s t ream i t e r a t o r < i n t ,
char> o u t i t e r ( c o u t ,
"
") ;
lterator o u t i t e r służy dzięki temu j ako interfej s pozwalaj ący używać strumienia c o u t do wyświetlania in formacj i . Pierwszy argument szablonu - w tym przypadku typ i n t określa dane prze s y ł a n e do strum ienia wyj ścia. Drugi argument - w tym przypadku typ char - okre śla typ znaków używany przez ten strumi er1 . A lternatywą dla typu char jest tu typ wchar_ t. P i erwszy argument konstruktora. c o u t, to używany strumier1 wyjścia. Może to być także stru m i e 11 używany do zapisu do p l i ku. Ostatni argument, w postaci ciągu znaków. to separator wy świetlany między kolejnymi e l ementami przesyłanymi do strumienia. -
Ostrzeżenie Starsze i m pl e m entacje języka C++ używają jedynie pierwszego arg u mentu sza b l o n u o s t r e am i terator: o s t r e a m i t e r a t o r < int>
out i t e r ( cout ,
"
") ;
li Starsze implementacje
Możesz użyć iteratora w następujący sposób : *out iter++
=
15;
li Dzialajak cuut
< < 1 5 < < " ":
W przypadku zwykłych wskaźników oznacza to przypisanie wartości 1 5 do wskazywanego miej sca, a następnie inkrementacj ę wskaźnika. Jednak dla iteratora o s t r e a m_ i t e r a t o r wyraże nie to oznacza przesłanie wartości 1 5 oraz znaku spacj i do strumienia wyjścia obsługiwanego przez c o u t. Następnie iterator musi być gotowy do kolejnej operacj i . Możesz użyć go w funkcj i c o p y ( ) w następujący sposób: copy ( d :i. ce . begi :i ( ) ,
d i c e . e:id ( ) ,
o u t_ i t e r ) ;
li Kopiuje wektor do strumienia 11yj.l'cia
Oznacza to przekopiowan ie całego zakresu z kontenera d i c e do strumienia wyj ścia, czyli po prostu wyświetlenie całej zawartości kontenera. Możesz też pominąć tworzenie iteratora o danej nazwie i utworzyć iterator anonimowy. Taki adaptator wygląda wtedy następuj ąco: copy ( d ice . beg i n ( ) ,
di ce . end ( ) ,
o s t ream_ i terator ( cou t ,
"
")
)
;
W pl iku nagłówkowym iterator znaj duj e się także szablon i s t r e am_ i t e r a t o r przystoso wujący strumień wejścia do interfej su iteratora. Jest to model pojęcia iteratora wej śc iowego. Możesz użyć dwóch obiektów i s t r e a m _ i t e r a t o r do zdefiniowania wej ściowego zakresu funkcj i c o p y ( ) : copy ( i s t ream i te r a t o r ( c i n ) , char> ( ) , d i c e b e g i n ( ) ) ; .
Szab l o n i s t r e a m _ i t e r a t o r przyj m uj e dwa a rg u m e n ty, p o d o b n i e j a k s z a b l o n o s t r e am i t e r a t o r . Pierwszy określa typ pobieranych danych, a drugi typ znaków używany przez strumień wej ścia. Używanie ci n jako argumentu konstruktora oznacza, że funkcja używać będzie strumienia wej ścia obsługiwanego przez c i n . Brak argumentu drugiego konstruktora
986
Programowanie generyczne
sugernje błąd wejścia, więc powyższy kod pobiera dane ze strumienia wejścia do czasu natrafienia na znaczn ik kot1ca pliku, napotkania danych ni epoprawnego typu lub i nnego błędu wejścia.
Inne użyteczne iteratory Plik nagłówkowy iterator udostępnia także inne prcdcfi n iowanc typy specjal nyc h i t c r a t o rów oprócz o s t r e a m _ i t e r a t o r oraz i s t r e a m i t e r a t o r . Są to r e v e r s e i t e r a r :- :· . ba c k i n s e r t i t e r a t o r , f r o n t i n s e r t i t e r a t o r oraz i n s e r t i t e r a t o r . Zacznijmy od zapoznania się z działaniem iteratora odwrotnego rcvcrse_iterator. M ó w i ąc krótko. próba jego inkrementacji powoduje dekrementację. Dlaczego po prostu nic dekrcmentować zwy kłego iteratora? Główny powód to ułatwienie używan ia istniejących funkcj i . Załóżmy, że chcesz wyświetlić zawartość kontenera di ce. Jak wiesz. możesz użyć do tego funkcj i copy ( ) oraz i tcra tora o s t r eam_i t e r a t o r i przekopiować zawartość kontenera do stnun ienia wyjścia: o s t r e am_ i t e r a t o r < in t ,
copy ( d i ce . beg i n ( ) ,
char >
di c e . e n d ( ) ,
out_i t e r ( cou t ,
o u t_ i t e r ) ;
"
") ;
li U)'świetla elemen�v w normalnej kolejnosci
Załóżmy teraz, że chcesz wyświet lić zawartość tego kontenera w odwrotnej kolej ności. może w ramach prowadzonych badat1 nad cofaniem się w czasie. Istniej e ki l ka możłiwos c i , które 11 1 c działają, a l e n i e będziemy i c h anal izować. Przyj rzyj my s i ę o d razu podejściu. które funkcjonuj e poprawnie. Klasa v e c t o r posiada funkcj ę składową o nazwie rb e g i n ( ) , która zwraca itera tor odwrotny wskazujący na pozycj ę z a ostatnim el ementem, oraz funkcję składową r e n d ( ) zwra cającą iterator odwrotny wskazuj ący na pierwszy e l ement. Poni eważ inkrcmentacj a i t c ra t o ra odwrotnego powoduje zmniej szenie j ego wartości, możesz wyświetlić zawartość kontenera w od wrotnej kolejności, używając poniższego wyrażenia: copy ( d i ce . rbe g i n ( ) ,
d i c e . rend ( ) ,
o u t_ i t e r ) ;
li Jf}:1'i,vietla elementy w od11mtnej kolejno.�ci
Nie musisz nawet deklarować j awnie iteratora odwrotnego.
Zapamiętaj! Funkcje rbe g i n ( ) oraz e n d ( ) zwracają tę samą wartość ( pozycja za ostatn i m e l e m e n tem ) . a l e i n n e g o typ u - odpowi e d n i o r e v e r s e i t e r a t o r o raz i t e r a t o r . To s a m o dotyczy funkcji r e n d ( l oraz b e g i n ( > , zwracających iterator na p i e rwszy e l e m e nt , ale różnego typu . lteratory odwrotne muszą posiadać pewne dodatkowe właściwości . Załóżmy, ż e rp jest iterato rem odwrotnym zainicjal izowanym za pomocą funkcj i di c e . rbe g i n ( ) . Czym jest * rp') Po nieważ rbeg in ( ) zwraca pozycję za ostatn im elementem, nie powinieneś wyłuskiwać tego adresu . Jeśli r e n d ( ) jest rzeczywiście adresem pi erwszego el ementu, funkcj a c o p y ( ) zatrzy ma się o jedną pozycj ę wcześniej , poni eważ koniec zakresu już do niego nie należy. Iteratory odwrotne pozwalają rozwiązać problemy zarówno z dekrementacją pierwszego elemen tu, jak i z wyłuskiwaniem. Wyrażenie r p * wyłuskuj e wartość iteratora znajdującego się bezpo średnio przed aktualną wartością * rp. Jeś l i r p wskazuje na szóstą pozycję , to * rp jest warto-
987
Rozdział 1 6 Klasa string oraz biblioteka STL -
ścią pozycj i p i ątej . Li sting 1 6. 8 przedstawia zastosowan ie funkcj i c o p y ( )
.
iteratora i s t r e
a m_ i t e r a t o r oraz iteratora odwrotnego .
Listing 1 6.8. copyit.cpp
li copyit. cpp - fimkcja capy() i itera/01y # i n c l ude
< i o s t ream>
# i ncl ude
< i t e ra t o r >
# i ncl ude
< ve c t o r >
int m a i n ( ) u s ing name space s t d ;
int c a s t s [ 1 o l
=
{ 6,
2,
7,
9,
4
11
I
I
8
I
7,
1oI
5} ;
v e c t o r < in t > d i c e ( l O ) ;
li Kopiz(je dane :: tablicy do wektora copy ( ca s t s , cou t o u t i t e r ( c o u t ,
" ") ;
li Kopiuje :: ll'ektora do strnmienia w;,jścia copy ( di c e . begin ( ) ,
dice . end ( ) ,
out ite r ) ;
cout < < endl ; cou t <
: : r e v e r s e i t e r a t o r for
( ri
= d i c e . rb e g i n ( ) ;
ri
ri ;
! = dice . rend ( ) ;
++ri )
cout .
988
Programowanie generyczne
Poniżej znajduje się wynik działania programu przedstawionego na listi ngu N i e c h ko ś c i 6 7 2
9 4
N i e j awne
1 6. 8 :
zos taną rzucone !
11
8 7
u ż yc i e
5 10 7 8 1 1
4
Jawne u ż yc i e
10 5 i teratora
9 2
7
i t e r a t o ra
5 10 7 8 1 1
4
9 2
7
o dw r o t n e g o .
6 o dw r o t n e g o .
6
Jeśli możesz wybierać między jawną deklaracją iteratorów a używaniem funkcji biblioteki STL do ich obsługi, na przykład przekazując wartość zwracaną do funkcj i przez rbe g i n ( ) powi nieneś wybrać tę drugą możliwość. Jest to jedna rzecz mniej , którą musisz zrobić, a także j edna okazja mniej do popełnienia błędu. ,
Pozostałe trzy iteratory
b a c k _ i n s e r t i t e r a t o r , f r o n t i n s e r t i t e r a t o r oraz także zwiększaj ą ogólność algorytmów biblioteki STL. Wiele funkcji biblioteki STL, na przykład c o p y ( ) , wysyła wynik działania do miej sca określanego przez iterator wyjścia. Jak pewnie pamiętasz, wyrażenie: i n s e rt_i t e r a t o r
copy ( c a s t s ,
-
-
cas ts
+
10,
dice . begin ( ) ) ;
powoduje przekopiowanie wartości do miej sca, na którego początek wskazuje d i c e . b e g i n ( ) Wartości te nadpisuj ą wcześniej szą zawartość kontenera d i c e , a kontener ten musi być wystar czająco pojemny, aby pomieścić nowe wartości. Wynika to z faktu, że funkcja c o p y ( ) nie przy stosowuj e rozmiaru kontenera do ilości przesyłanych danych. Na li stingu 1 6 . 8 wielkość kontene ra di ce jest wcześniej zadeklarowana j ako 1 O elementów. ale załóżmy, że nie wiesz wcześn iej , jak pojemny powinien być ten kontener. Możesz też chcieć dodać nowe elementy d o kontenera, zamiast nadpisywać jego zawartość. .
Trzy iteratory wstawiaj ące rozwiązuj ą te problemy, przekształcaj ąc proces kopiowania na proces wstawiania. Wstawianie powoduj e dodawanie nowych elementów bez nadpisywania istniej ą cych danych, używając automatycznego przydziału pamięci do zapewnienia odpowiedniej ilości miejsca dla nowych danych. lterator b a c k _ i n s e r t _ i t e ra t o r wstawia elementy na koniec kontenera, a f r o n t _ i n s e t _ i t e r a t o r na j ego początek. lterator i n s e r t _ i t e r a t o r wstawia nowe elementy przed miej scem okreś lonym za pomocą argumentu j ego konstruktora. Wszystkie trzy iteratory to modele poj ęcia iteratora wyj ściowego. lstniejąjednak pewne ograniczenia. lteratora b a c k _ i n s e r t _ i t e r a t o r można używać j edy nie dla tych kontenerów, które umożliwiaj ą błyskawiczne wstawianie elementów na koniec. Określenie błyskawic=ne dotyczy algorytmów czasu stałego. Więcej infonnacj i o pojęciu algoryt mów czasu stałego znajduje się w punkcie „Pojęcia związane z kontenerami" w dalszej części rozdziału. Wymaganie te spełnia klasa ve c t o r . lterator f r o n t _ i n s e r t _ i t e ra t o r wymaga stałego czasu wstawiania elementów na początek. Tego wymagania nie spełnia klasa ve c t o r, ale spełnia je klasa qu e u e . lterator i n s e r t _ i t e r a t o r nie ma tego typu ograniczeń, dlatego mo żesz używać go do wstawiania danych na początek wektora, jednak w przypadku kontenerów, które obsługują iterator f r o n t _ i n s e r t _ i t e r a t o r , jest on szybszym rozwiązaniem.
989
Rozdział 1 6 Klasa string oraz biblioteka STL -
Wskazówka Możesz użyć 1teratora i n s e r t i t e ra t o r d o p rzekształcenia algorytm u kop i ującego dane na a l g o rytm wstawiający je. Iteratory te przyjmuj ą jako argument szablonu typ kontenera, a j ako argument konstruktora iden tyfikator rzeczywistego kontenera. Aby utworzyć i terator b a c k_ i n s e r t_ i t e r a t o r dla kon tenera d i ce typu ve c t o r < in t > . musisz użyć następuj ącej deklaracj i : ba c k i n s e r t i t e r a t o r < v e c t o r < in t >
>
b a c k_ i t e r { d i c e ) ;
Potrzeba deklaracj i typu kontenera wyn ika z tego, że iterator musi używać odpowiednich metod kontenera. Kod konstruktora b a c k_ i n s e r t_ i t e r a t o r wymaga, aby przekazany do niego obiekt pos iadał metodę p u s h _b a c k { ) . Funkcj a c o p y { ) , która j est funkcj ą samodzielną, nic ma praw dostępu pozwalaj ących zmieniać rozmiar kontenera, ale przedstawiona wcześn i ej de k I a racj a p o z w a l a i t e r a t o r o w i b a c k i t e r na s k o r z y s t a n i e z m e t o d y v e c t o r < in t > : : p u s h_ba c k { ) , która posi ada takie prawa. Dekl aracj a i teratora f r o n t i n s e r t i t e r a t o r m a taką samą postać. Deklaracj a i teratora i n s e r t i t e r a t o r posiada dodatkowy argument konstruktora, który pozwa la określ ić miej sce wstawian i a elementów : i r. s e r t i t e r a t o r < ve c t o r < int>
>
i n s e r t_ i t e r ( di ce ,
di ce . beg i n { )
Zastosowan i e dwóch z tych iteratorów przedstawione j est na l isti ngu 1 6. 9 .
Listing 1 6. 9. inserts.cpp
li inserts. cpp - fimkcja capy() i iterato1y wstawiania # i n c l ude
< i o s t ream>
# i n c l ude
# i n c l ude
< i t e ra t o r >
# i n c l u de
int ma i n { ) u s ing name space s t d ;
string
sl [ 4 ]
{ " ka ra " ,
" ka r p " ,
" ka rma " ,
s t ring s2 [ 2 ]
{ "pus t y " ,
"pies " } ;
string s 3 [ 2 ]
{ " m a s ka " ,
" mu z y k " } ;
" ku r a " ) ;
vec t o r < s t ring> words ( 4 ) ; copy ( s l ,
sl + 4,
w o r d s . be g i n { ) ) ;
o s t r e a m_ i t e r a t o r < s t r i n g , copy
{ w o rd s . b e g i n ( ) ,
char> o u t ( c o u t ,
words . end { ) ,
cout < < endl ;
li Tworzy anonimowy obiekt typu back_insert_ iterator
990
out ) ;
"
") ;
) ;
Programowanie generyczne
s2
copy ( s 2 ,
•
2,
ba c k i n s e r t i t e r a t o r < ve c t o r < s t r i n g > _ _ words . end ( ) , ou t ) ;
> ( wo r ds ) ) ;
copy ( wo r d s . beg i n ( ) , cout
t r a n s f o rm ( g r B . b e g i n ( ) ,
out ( cout ,
g r 8 . end ( ) ,
ou t ,
"
") ;
sqrt ) ;
Powyższy fragment kodu pozwala obliczyć pierwiastek kwadratowy każdego elementu i przesyła wyniki do strumienia wyj ścia. lterator określający miej sce przesyłania wyniku może wskazywać na oryginalny zakres. Zamiana w przykładzie iteratora o u t na g r B . b e g in ( ) spowodowałaby przekopiowanie nowych wartości w miej sce starych. Oczywiście zastosowany funktor musi dzia łać dla jednego argumentu. Druga wersja używa funkcji przyjmującej dwa argumenty, która przyj muje po jednym argumencie z każdego z dwóch zakresów. Ta wersja funkcj i t r a n s f o rm ( ) przyj muje dodatkowy argument, który przekazywany jest j ako trzeci, określający początek drugiego zakresu. Jeśli mB to drugi obiekt
1014
Obiekty funkcyjne (funktory)
double ) zwraca średnią z dwóch wai1ości , po
typu v e c t o r < doubl e > , a m e a n ( double ,
niższa instmkcja powoduje wyświ etlenie średnich z par wartości wektorów gr 8 oraz m B : t r a n s f o rm ( g r B . b e g i n ( ) ,
out ,
m B . be g i n ( ) ,
g r B . e nd ( ) ,
mea n \ ;
Załóżmy, że chcesz dodać dwie tablice. N i e możesz użyć j ako argumentu operatora
+,
poni eważ
dla typu double jest to operator wbudowany, a nic funkcj a . M ożesz zdefiniować fu nkcj ę doda
jącą dwie liczby i użyć j ej jako argumentu funkcj i t r a n s f o rm : double a d d ( doub le x ,
doub l e y )
t r an s f o r m ( g r S . b e g i n ( ) ,
(
re turn x + y ;
g r B . e nd ( ) ,
m B . be g i n ( ) ,
ou t ,
add ) ;
jednakże musisz wtedy zdefiniować odrębną funkcj ę dla każdego typu . Wygodniej sza byłaby
definicja szablonu. ale nie j est to potrzebne, pon ieważ odpowi edni szab lon istn i ej e J UŻ w b i b l io tece STL. Plik naglówkowyJi111ctional (dawn i ej Ji111ction. h) definiuj e kilka szablonów klas obiek
tów funkcyjnych, wśród których znajduj e się szablon p 1 u s < > ( ) . Używanie kl asy p l u s < > do zwykłej operacj i dodawania j est możliwe, choć nieco dziwaczne: # i nc l u de
< fu n c t i o n a l >
p l u s do ub le y
=
li Tworzy ob iekt plus < double>
add ;
add ( 2 . 2 ,
3.4) ;
li Używa operatora plus < double> : : operator(} ()
Jednak ten obiekt funkcyjny można łatwo przekazać j ako argument: t r a n s f o rm ( g r S . b e g i n ( ) ,
grS
. end ( ) ,
mB . b e g i n
() ,
ou t ,
) ;
p l u s < double> ( )
Zamiast tworzyć nazwany obiekt, w kodzie użyty j est konstruktor p l u s tworzący funktor odpowiedzialny za dodawan ie. Nawiasy wskazują na użyci e konstruktora domyś lnego, a do funkcj i t r ans f o r m ( ) przekazywany j est obiekt funkcyj ny utworzony z a pomocą tego konstruktora.
Bibl ioteka STL udostępnia funktory, które stanowią odpowiedn i k i wszystkich wbudowanych operatorów arytmetycznych, porównania oraz logicznych. W tabe l i 1 6 . 1 1 znaj duj ą się nazwy
tych funktorów. Można ich używać d l a typów wbudowanych j ęzyka C++ oraz dla typów zdefinio wanych przez użytkownika, które przeciążaj ą odpowiedni operator.
Ostrzeżenie Starsze i mplementacje języka C++ używają nazwy t ime s w m i ejsce m u l t i p l i e s .
1 01 5
Rozdział I 6 Klaso string oraz biblioteko STL -
Tabela 16. 1 1 . Operatory i ich odpowiedniki w postaci funktorów Operator +
Funktor
plus minus m u ltiplies divides
%
modu Ius negate eq ual_to
!=
not_eq ual_to
>
greater
=
g reater_equal
można zidentyfikować jako p l u s < int> : : r e s ul t _ t y pe, który w tym przypadku j est definicj ą typu dla typu int. Funktory adaptowalne mogą być używane przez obiekty będące adaptatorami, które korzystają z ich składowych definicj i typu. Na przykład funkcj a z argumentem w postaci funktora adapto walnego może używać składowej r e s u l t_t ype do zadeklarowania zmiennej pasuj ącej do typu wartości zwracanej przez funkcję. Biblioteka STL udostępnia k lasy adaptatorów, które używaj ą tej techniki. Załóżmy, że chcesz pomnożyć każdy element wektora g r 8 przez li czbę 2 . 5 . Nasuwaj ące się rozwiązanie to użycie
1016
Obiekly funkcyjne (funktory)
wersj i funkcj i t r a n s f o rm ( ) z argumentem w postaci funktora j ednoargumentowego. podob nie jak we wcześniej szym przykładzie: t r a n s f o rm ( g r 8 . b e g i n ( ) ,
gr8 . end ( ) ,
out ,
sqrt ) ;
Funktor mu 1 t i p 1 i e s ( ) potrafi mnożyć l iczby, ale jest funkcj ą dwuargumentową. Potrzebu jesz więc adaptatora. który przekształca funktor dwuargumentowy na funktor j ednoargurnento wy. We wcześniejszym przykładzie przedstawiaj ącym funkcj ę T o o B i g 2 widzi ałeś jeden sposób użycia adaptatora, j ednak biblioteka STL pozwa la na automatyzację tego procesu za pomocą klas b i n de r l s t oraz b i n d e r 2 n d, które przekształcaj ą adaptowalne funktory dwuargumentowe na adaptowalne funktory j ednoargurnentowe. Przyjrzyj my się klasie b i n de r l s t . Załóżmy, że posiadasz adaptował ny dwuargumentowy obiekt funkcyjny f 2 ( ) . Możesz utworzyć obiekt b i n d e r 1 s t , który przypisuj e daną wartość, v a 1, do pierwszego argumentu obiektu f 2 ( ) : binde r l s t < f 2 ,
va l )
fl ;
Dzięki tej operacj i wywołanie f 1 ( x ) z j ednym argumentem zwraca tę sarną wartość, co wywo łanie f 2 ( ) z v a l j ako pierwszym argumentem oraz z wartością przekazaną do f 1 ( ) j a k o drugim argumentem. Oznacza t o , że f 1 ( x ) j e s t odpowiednikiem f 2 ( v a 1 , x ) tylko ż e j est funktorem jednoargurnentowym, a nie dwuargumentowym. W ten sposób funktor f 2 ( ) została zaadaptowany. Jest to możl iwe tylko wtedy, kiedy f2 ( ) jest funktorem adaptowalnym. .
Może to wyglądać nieco dziwaczn ie. Bibl ioteka STL udostępnia funkcj ę b i n d l s t , która uła twia używanie k lasy b i n d e r l s t . Do funkcj i tej przekazujesz nazwę funktora oraz wartość, której używać ma obiekt b i n d e r l s t , a funkcj a zwraca gotowy obiekt tego typu . M ożesz na przykład przekształcić funktor dwuargumentowy mul t i p l i e s ( ) na funktor j ednoargumento wy, który mnoży argument przez 2 . 5 . Wystarczy że napiszesz poniższe wyrażenie: b i n d l s t ( mu l t i p l i e s < double > ( ) ,
2 . 5)
Rozwiązanie pozwalaj ące pomnożyć każdy element kontenera g r 8 przez 2 . 5 , a następn ie wy świetlić wyniki wygląda następuj ąco: t r a n s f o rm ( g r 8 . b e g i n ( ) ,
g r 8 . end ( ) ,
out ,
b i n d l s t ( mu l t i p l i e s < doub l e > ( ) ,
2.5) ) ;
Klasa b i n d e r 2 s t jest podobna, tylko przypi suje stałą wartość do dmgiego argumentu fun kto ra, a nie do pierwszego. Klasa ta posiada funkcj ę pomocniczą b i n d 2 n d, która działa analogicz nie do funkcj i b i n d l s t .
Wskazówka Jeśli funkcja biblioteki STL wymaga funktora jednoarg u m e ntowego, a posiadasz funktor dwu argumentowy, któ ry wykon uje potrze bne operacje, możesz użyć funkcj i b i n d l s t ( ) oraz b i n d2nd ( ) i zaadaptować funktor dwuarg umentowy do jednoarg u m entowego inte rfejsu. Na l istingu 1 6. 1 4 niektóre z ostatnich przykładów połączone są w krótki program.
1 01 7
Rozdział 1 6 Klasa string oraz biblioteka STL -
Listing 1 6. 1 4. funadap.cpp !lfunadap. cpp - używanie adaptatorów # i n c l u de < i o s t r e a m > # i n c l u de < ve c t o r > # i n c l u de < i t e r a t o r > # i n c l u de < a l g o r i thm> # i n c l u de < f u n c t i o n a l > void S h o w ( double ) ; con s t i n t L I M = 5 ; int ma i n ( ) us ing name space s t d ; double a r r l [ L I M ] = { 3 6 , 3 9 , 4 2 , 4 5 , 4 a ) ; double a r r 2 [ L I M ] = ( 2 5 , 2 7 , 2 9 , 3 1 , 3 3 ) ; ve c t o r g r a ( a r r l , a r r l + L I M ) ; v e c t o r < doubl e > m a ( a r r 2 , a r r 2 + L I M ) ; c o u t . s e t f ( i o s _b a s e : : f i x e d ) ; c o u t . p re c i s ion ( l ) ; cout
las t ,
n e w_ va l u e ) ;
wynikowe kopiowane są do nowego miejsca, na które wskazuje i terator r e określenia orygina lnego zakresu wystarcza iterator wejściowy.
Zauważ, że typ zwracany przez funkcję
r ep l a c e_copy ( ) to O u t p u t i t e r a t o r . Konwen
cja przyjęta dla a lgorytmów kopiuj ących to zwracanie iteratora, który wskazuje na pozycję za ostatnim przekopiowanym e l ementem. Inne dodatkowe wersje funkcj i mogą wykonywać ni ektóre działania warunkowo, w zależności od wyniku działan ia funkcj i dla danego elementu kontenera. Wersj e te posi adają zwykle w na zwie przyrostek _ i f . Na przykład funkcj a
rep l a c e_ i f ( ) zamien i a stare wartości na nowe,
jeśli działanie funkcj i dla starej wartości zwraca true. Poniżej znajduje się prototyp tej funkcj i :
template < c l a s s Fo rwa r d l t e r a t o r , c l a s s P r e d i c a t e c l a s s T > void rep l a c e_i f ( Fo rw a rd i t e r a t o r fi rs t , Forwa rd l t e r a t o r l a s t , P r e d i c a t e p r e d , con s t T & n e w_ va l u e ) ; Jak sobie pewnie przypominasz, predykat to funktor j ednoargumentowy zawracający wartość logiczną. Istnieje także wersja tej funkcj i o nazwie
rep l a c e _ c o p y_i f ( l . Prawdopodobni e
potrafisz wywnioskować, do czego służy t a funkcj a i jak wygl ąda j ej prototyp. Podobnie jak równie dobrze
I npu t r t e r a t o r , P r e d i c a t e to nazwa parametru szablonu, którą może być T lub U. Jednak w bibl iotece STL używana j est nazwa P r e d i c a t e , co ma przy
pominać użytkownikom, że rzeczywisty argument powinien być modelem poj ęcia predykatu. Do tego samego służy używanie nazw
Ge n e r a t o r oraz B i n a r y P r e d i c a t e , które identyfikuj ą
argumenty modeluj ące inne pojęcia związane z obiektami funkcyj nymi.
1 021
Rozdział 1 6 - Klaso string oraz biblioteko STL
Biblioteka STL i klasa s tring Klasa s i::r i n g , choć nie j est częścią biblioteki STL, została zaproj ektowana z myślą o tej bi bliotece. K lasa ta posiada na przykład składowe b e g i n ( ) , e n d ( ) , rbeg i n ( ) oraz rend ( ) . dzięki czemu może używać interfej su biblioteki STL. Listing 1 6 . 1 5 używa STL do wyświetlenia wszystkich permutacj i , które możesz utworzyć z l iter danego słowa. Permutacja to nowe upo rządkowan ie elementów kontenera. A lgorytm n e x t _p e rmu t a t i o n ( ) przekształca zawartość zakresu na kolejną pennutację. W przypadku ciągów znaków permutacj e układane są w porząd ku al fabetycznym . Algorytm zwraca true, jeśli j ego działanie zakończyło się sukcesem, oraz fal s e , jeśli dany ciąg stanowi ostatni ą możliwą permutacj ą. Aby otrzymać wszystkie permuta
cje zakresu, należy zacząć od uporządkowani a elementów w pierwszą pennutację, do czego słu ży algorytm s o r t ( ) bibl ioteki STL.
Listing 1 6. 1 5. strgstl.cpp li strgstl. cpp - używanie biblioteki STL dla klasy string
# i n c l u de
< i o s t r eam>
# i n c l u de
< s tring>
# i n c l u de
< a l g o r i thm>
int ma i n ( )
u s ing name space s t d ;
s t ri n g l e t t e r s ; c o u t
Show ( in t ) ;
cons t int L I M
10;
int ma i n ( ) us inq name space s t d ;
int a r [ L I M ] = { 4 , 5 , 4 , 2 , 2 , l i s t < int> l a ( a r , a r + L I M ) ; l i s t < int> l b ( l a ) ;
3,
4,
8,
1,
c o u t < < " O r yg i n a l n a z a wa r t o ś ć l i s t y : \ n \ t " ; f o r_e a c h ( l a . be g i n ( ) , l a . en d ( ) , S h ow ) ; cout ( ) ) ;
Klasa va l a r r a y posiada przeciążone wersje wszystkich operatorów arytmetycznych, dlatego możesz użyć następującego wyrażenia: vad3
=
vadl
+ vad2 ;
li Przeciążony
operator +
li Przeciążony
operator *
Równie poprawne jest wyrażenie: vad3
=
vadl * vad2 ;
które powoduje zapisanie w tablicy vad3 iloczynu odpowiednich elementów tablic vadl oraz vad2 . Załóżmy teraz, że chcesz zamienić każdą wartość tablicy na tę wartość pomnożoną przez 2 . 5 . Rozwiązanie korzystające z biblioteki STL wygląda następuj ąco: t r a n s f o rm ( ve d 3 . be g i n ( ) , v e d 3 . e n d ( ) , v e d 3 . b e g i n ( ) , b i n d l s t ( mu l t ip l i e s < double> ( ) , 2 . 5 ) ) ;
Klasa va l a r r a y przeciąża mnożenie obiektu va l a r r a y przez daną wartość. Przeciąża także operator przypisania, możesz więc użyć każdego z obu poniższych wyrażeń : vad3 = 2 . 5 * v a d 3 ; vad * = 2 . 5 ;
li Przeciążony li Przeciążony
operator * operator *=
Załóżmy, że chcesz obliczyć logarytm naturalny każdego elementu tablicy i zapisać wynik w odpo wiednich elementach drugiej tablicy. W przypadku biblioteki STL kod wygląda następująco: t r a n s f o rm ( ve d l . b e g i n ( ) , log) ;
1 030
vedl . end ( ) ,
ved3 . begin ( ) ,
Inne biblioteki Klasa va larray przeciąża popularne funkcje matematyczne, co pozwala przekazać do nich argument typu valarray, a dana funkcja zwraca obiekt tego samego typu. Dzięki temu możesz użyć wyrażenia: vad3
=
li Przeciążona .funkcja log(}
l o g ( va d l ) ;
Możesz także użyć metody a pp l y ( ) , która działa także dla funkcj i n ieprzeciążonych: vad3
=
v a d l . a pp l y ( l o g ) ;
Metoda app l y ( ) nie zmienia obiektu wywoh1jącego, lecz powoduje zwrócenie nowego obiek tu, który zawiera obl iczone wyniki. Prostota interfejsu klasy v a l a r r a y j est jeszcze bardziej widoczna, kiedy chcesz wykonać kilka obliczeń jednocześnie: vad3
=
10 . 0 *
( ( vadl + vad2 )
I
2 . 0 + v a d l * c o s ( va d 2 ) ) ;
Wersję tych obliczeń dla wektora bibl ioteki STL pozostawiam j ako ćwiczen ie dla odpowiednio zmotywowanych czytelników. Klasa va l a r r a y udostępn ia także metodę s um ( ) , która dodaj e wartości elementów obiektu va larray, metodę s i ze ( ) , która zwraca liczbę elementów, metodę m a x ( ) , która zwraca największą wartość w obiekcie, a także metodę min ( ) zwracającą najmniej szą wartość. Jak widzisz, w przypadku operacji matematycznych klasa v a l a r r a y ma dużo bardziej zwięzły zapis niż klasa ve c t o r , ale jest też dużo mniej wszechstronna. Klasa va l a r r a y posiada wpraw dzie metodę res i ze ( ) , ale nie umożliwia automatycznej zmiany wielkości, na co z kolei pozwa la metoda push_b a c k ( ) klasy ve c t o r . W klasie v a l a r r a y nie istnieją też metody pozwala jące na wstawianie wartości, ich wyszukiwanie lub sortowanie. Podsumowując, klasa v a l a r r a y jest mniej wszechstronna o d klasy ve c to r , ale pozwala to znacznie uprościć jej interfejs. Czy prostszy interfejs klasy v a l a r r a y ma odzwierciedlenie w lepszej wydaj ności? W większo ści przypadków nie jest to prawdą. Prosty zapis posiada zwykle implementację wykorzystującą te same pętle, których użyłbyś w przypadku zwykłych tablic. Jednak niektóre rozwiązania sprzę towe umożliwiają operacje na wektorach, w których wartości wektora wczytywane są jednocze śnie do odpowiednich rejestrów, a następnie są przetwarzane równolegle. Większość operacj i klasy v a l a r r a y można zaimplementować w sposób wykorzystujący t ę możl iwość. Czy można używać biblioteki STL dla obiektów v a l a r r a y? Odpowiedzi na to pytanie dostar czy krótka powtórka wiadomości o właściwościach tej biblioteki. Załóżmy, że utworzyłeś obiekt va l a r r a y posiadający 1 0 elementów: v a l a r r a y
vad ( 1 O ) ;
Czy po wprowadzeniu liczb do tab l icy możesz je posortować, używając funkcj i bibl ioteki STL? Klasa v a l a r r a y nie posiada metody b e g i n ( ) ani metody end ( ) , dlatego nie możesz ich użyć jako argumentów wyznaczających zakres: sort ( va d . b e g i n ( ) ,
v a d . e n d ( ) ) ; li NIE, nie ma metod begin() i end()
Nie możesz też zastosować techn iki używanej dla zwykłych tablic i określić zakres , używając wyrażeń vad oraz v a d + 1 O , ponieważ v a d nie jest nazwą tablicy. Jest to nazwa obiektu i dlatego nie jest adresem. Z tego powodu nie zadziała także poniższe rozwiązanie:
1 031
Rozdział 1 6 Klosa string oraz biblioteka STL -
s o r t ( va d ,
vad + 1 O ) ; li NIE, vad to obiekt, a nie adres
Możesz spróbować użyć operatora adresu : s o r t ( & va d [ O ] ,
&vad [ l O ] ) ;
li Może
tak?
To rozwiązanie wygląda zachęcaj ąco. v a d j est typu va l a r ra y, vad [ O J j est typu double, a & v a d [ O J to typ doubl e * , więc wartość ta może zostać przypisana do wskaźnika na typ double, który działa j ak iterator. Sprawdźmy j eszcze, czy wskaźni k ten (nazwijmy go pt) posiada wymagane właściwości iteratora dostępu swobodnego. Po pierwsze, można go wyłu skać. *pt jest równoznaczne z vad [ O J , czyli wartością pierwszego elementu tablicy. A co z ope racją pt + +? Oczywiście można zwiększyć wartość wskaźnika, ale czy będzie on wskazywał na drugi element tablicy? Z opisu klasy wynika. że & a [ i + j J oraz & a [ i J + j oznaczają to samo. W szczególności & a [ i J + 1 jest równoznaczne z & a [ i + 1 J . Oznacza to, że dodanie jedynki do adresu udostępnia adres kolej nego elemenn1, więc inkrementacja wskaźnika pt powoduje. że bę dzie on wskazywał na następny element tablicy. Ogólna zasada równoważności oznacza równocze śnie, że istniej e dostęp swobodny do elementów. Do tej pory wszystko działa poprawnie. W końcu j ednak program dochodzi do &vad [ 1 O J . W tym miej scu pojawiają się dwa problemy. Reguła pozwal aj ąca traktować & a [ i + j J tak samo jak & a [ i J + j posiada pewne ogranicze nie - wartość i + j musi być mniejsza od rozmiaru tablicy. Z tego powodu w przykładzie poprawne wartości kończą się na & v a d [ 9 J . Ponadto skutki używania indeksów równych roz miarowi tablicy lub większych są niezdefiniowane i dlatego zachowanie wynikające z użycia wyrażenia vad [ 1 O J j est niezdefiniowane. Nie oznacza to, że funkcja s o r t ( ) nie będzie dzia łać. (W rzeczywistości działała ona poprawnie w przypadku wszystkich sześciu kompilatorów, na których testowałem ten fragment kodu.) Oznacza to jednak. że funkcj a może nie działać . Aby powyższy kod spowodował błąd, muszą zaj ść bardzo mało prawdopodobne okoliczności, jak umiej scowienie tablicy na końcu bloku pamięci umieszczonego na stercie. Jeśli jednak interes wart 350 milionów złotych zależy od Twojego kodu, nie możesz sobie pozwolić nawet na takie ryzyko. Możesz obejść problem pozycj i za ostatnim elementem, umieszczając w tablicy va l a r r a y je den element więcej niż to jest potrzebne. Tworzy to j ednak nowe problemy w postaci używania metod s um ( ) , max ( ) , m i n ( ) oraz s i z e ( ) . Na listingu 1 6. 1 8 przedstawione są niektóre mocne strony klas ve c t o r oraz va l a r r a y. Przy dodawaniu danych program używa funkcj i push_ba c k ( ) oraz właściwości automatycznej zmiany rozmiaru wektora. Następnie, po posortowaniu l iczb, program kopiuje je z obiektu ve c t o r do obiektu va l a r r a y tego samego rozmiaru i wykonuj e kilka operacj i matematycznych.
Listing 1 6.1 8. valvect.cpp li
valvect. cpp - porównanie klas vector oraz valarray
# i n c l u de
< i o s t r e am >
# i n c l u de
< va l a r r a y >
# i n c l u de
< ve c t o r >
# i n c l u de
< a l g o r i thm>
# i n c l ude < cma t h > int ma i n ( )
1 032
Inne biblioteki
using name space s t d ;
vec t o r d a t a ; double t emp ; ( < = O a b y z a ko ń c z yć ) : \ n " ;
l i c zby
cout < < " Po d a j while
( c in > > t emp & & t e mp > 0 )
da t a . pu s h _b a c k ( t emp ) ; da t a . e n d ( ) ) ;
s o r t ( da t a . b e g i n ( ) ,
int s i z e = da t a . s i z e ( ) ;
va l a r r a y n u mb e r s ( s i z e ) ; int i ; for
(i = O;
i < size ;
numb e r s [ i ]
i++ )
= da t a [ i ) ;
va l a r r a y s ą_ r t s ( s i z e ) ; s ą_r t s = s ą r t ( n umb e r s ) ; va la r r a y r e s u l t s ( s i z e ) ; re s u l t s = n umbe r s + 2 . 0 * s ą_ r t s ; cout . s e t f ( i o s _b a s e : : f i x e d ) ; cout . p r e c i s i on ( 4 ) ; for
(i = O;
i < size;
i++ )
cout . width ( 8 ) ; c o u t < < numb e r s [ i )
9 , czyli true lub f a l s e . Istnieją bardziej zaawansowane sposoby indeksowania. Przyjrzyj my się j ednej z nich -- klasie s l i c e . Obiekty tej klasy mogą służyć j ako indeks tablicy, w którym to przypadku reprezentują nic pojedynczą wartość, ale ich podzbiór. Obiekt s l i c e inicjalizowany jest za pomocą trzech warto ści l i czbowych - początku, liczby oraz kroku. Początek określa indeks pierwszego wybranego elementu. Liczba określa liczbę elementów, które zostaną wybrane. Krok to odległość między ele mentam i . Na przykład obiekt utworzony za pomocą wywołania s l i c e ( 1 , 4 , 3 ) oznacza wybranie czterech elementów, których indeksami są I , 4, 7 oraz I O. Wybierany jest element okre ś lany przez początek, kolejny el ement wybierany jest dzięki dodaniu kroku aż do wybrania ilości elementów określ onych przez liczbę, w tym przypadku czterech. Jeśli va r i n t jest obiektem typu v a l a r r a y < i n t > , poniższe wyrażenie przypisuj e el ementom I , 4, 7 oraz I O wartość I O : va r i n t [ s 1 i ce ( 1 ,
4,
3) ]
=
1O;
li Przypisuje t1-:vbra11ym elemen/0111 warto.fr: 1 O
To specj a lne narzędzie do indeksowania pozwala używać jednowymi arowych obiektów va l a r r a y do reprezentowania dwuwym iarowych danych. Załóżmy, że chcesz przedstawić tab licę o 4 rzędach i 3 kolumnach. Możesz zapisać dane w dwunastoelementowym obiekcie v a l a r r a y . Obiekt s l i c e ( 0 , 3 , 1 ) użyty j ako i ndeks reprezentuj e elementy O, I i 2, czyli pierwszy wi ersz. Indeks s l i c e ( O , 4 , 3 ) reprezentuj e elementy O, 3 . 6 oraz 9, czy li pierwszą kolum nę. N iektóre z właściwości obi ektu sl i c e przedstawione są na l i sti ngu 1 6 . 1 9 . Listing 1 6. 1 9 . vslice.cpp li vslice. cpp - używanie wycinków obiektu valarray
# i n c l ude # i nc l ude # i n c l ude
< i o s t ream>
< c s tdl ib>
con s t i n t S I Z E
=
12 ;
typedef s t d : : v a l a r r a y < int> v i n t ;
1 034
li Upraszcza deklaracje
Inne biblioreki void s h o w ( const v i n t & v ,
int c o l s ) ;
int ma i n ( ) {
using s t d : : s l i ce ; using s t d : : co u t ; v i n t va l i n t ( S I Z E ) ;
li Z pliku li
< valarray>
ijjlobraź sobiejako 4 rzędy po 3 elementy
int i ; for ( i = O ; i < S I Z E ; + + i ) va l i n t [ i ] s t d : : rand ( ) % 1 0 ; cout < < " O r y g i n a l n a t ab l i c a : \ n " ; li ijjlświetla w 3 kolumnach show ( va l i n t , 3 ) ; li Wyciąga drugą kolumnę v i n t vcol ( v a l i n t [ s l i ce ( 1 , 4 , 3 ) ] ) ; c o u t zamiast funkcj i g e t l i n e ( ) . W pliku słowa powinny być oddzielone za pomocą znaków spacj i , tabulacj i lub nowej l i n i i . 4. Napisz funkcję z klasycznym interfej sem. która posiada następujący prototyp : int r e d u c e ( long a r [ ] ,
int n ) ;
Rzeczywi stymi argumentami powinna być nazwa tablicy oraz l iczba jej elementów. Funkcja powinna sortować tablicę, usuwać powtarzające się wartości oraz zwracać l i czbę elementów skróconej tabli cy. Napisz tę funkcję, korzystaj ąc z funkcj i bibl ioteki STL. Jeśl i zdecyduj esz się użyć ogó lnej funkcj i un i q u e ( ) . pamiętaj o tym, że zwraca ona koniec zakresu wyni ko wego. Sprawdź działanie funkcj i za pomocą prostego programu. 5 . Wykonaj to samo zadanie co w ćwi czen iu 4 . . tym razem tworząc szablon funkcj i : temp l ate < c l a s s T > int redu c e ( T a r [ ] ,
int n ) ;
Sprawdź dzi ałan ie tej funkcj i za pomocą prostego programu, używając konkretyzacj i dla typu long oraz dla typu s t r i n g. 6. Przekształć przykład przedstawiony na l i sti ngu 1 2 . 1 5 , używając szablonu klasy q u e u e bi blioteki STL zamiast klasy Q u e u e opisanej w rozdziale 1 2 . 7. Lotto jest popularną grą. Napisz funkcj ę L o t t o ( ) , która przyjmuje dwa argumenty. Pierw szy powinien być l iczbą pól na kuponie, a drugi powin ien być l i czbą losowo wybieranych numerów. Funkcj a powinna zwracać obiekt ve c t o r < in t > , który zawiera posortowane losowo wybrane l iczby. Korzystanie z funkcj i może wyglądać następuj ąco: ve c t o r < in t > winners
winners ;
= Lot t o ( S l ,
6) ;
Powyższy fragment kodu powinien przypisywać do obiektu w i n n e r s wektor zawierający sześć losowo wybranych liczb z zakresu od 1 do 49. Zauważ, że proste użycie funkcj i r a n d ( ) nie jest dobrym rozwiązan iem, ponieważ może tworzyć powtarzające się wartości. Podpowiedź - utwórz w funkcj i wektor zawierający wszystkie możliwe wartości, wymieszaj wartości za pomocą funkcj i r a n dom_ s h u f f l e ( ) , a następnie użyj początkowych wartośc i wymiesza nego wektora. Napisz także krótki program, który pozwoli sprawdzić działanie tej funkcj i . 8.
B o l e k i Lolek chcą zaprosić przyj aciół na urodziny. Poprosi l i C i ę , abyś napisał program o następującej specyfikacj i : • Program powinien pozwolić Bolkowi wpisać l i stę jego przyjaciół. Imiona t e zapisy wane są w kontenerze i wyświetlane w porządku al fabetycznym . • Program powinien pozwolić Lolkowi wpisać l i stę jego przyj aciół. Imiona te zapisy wane są w drugim kontenerze i także wyświetlane w porządku al fabetycznym. • Program powinien tworzyć trzeci kontener, który scala obie listy, usuwa powtarzające się imiona i wyświetla zawartość nowego kontenera.
1 040
Rozdział 1 7
Obsługa weiścia, wyiścia oraz plików W rozdziale: • Operacje wejścia-wyjścia z punktu widzenia języka C ++ • Rodzina klas i o s t r e am • Przekierowanie • Metody klasy o s t r e am • Formatowanie wyjścia • Metody klasy i s t r e am • Stany strumienia • Wejście-wyjście plikowe • Wykorzystanie klasy i f s t r e am do realizacji wejścia z plików • Wykorzystanie klasy o f s t r e am do realizacji wyjścia do plików • Wykorzystanie klasy f s t r e am do realizacji wejścia i wyjścia plikowego • Przetwarzanie wiersza polecenia • Pliki binarne • Dostęp swobodny do plików • Formatowanie wewnętrzne
O
mawianie obsługi w ejścia-wyjścia w j ęzyku C++ stanowi pewien problem. Z j ednej strony praktycznie w k ażdym programie używa się operacj i wej ścia-wyjścia, a więc nauka sposobu korzystania z nich jest j ednym z pierwszym zadań, przed którymi staj e osoba poznająca język programowania. Z drugiej zaś strony obsługa wej ścia-wyjścia jest w języ ku C ++ real izowana z wykorzystaniem wielu bardziej zaawansowanych możliwości tego j ęzyka, do których należą kl asy, klasy pochodne, przeciążanie funkcj i , funkcj e wirtualne, szabl ony i wie lodziedziczenie. Ż eby zatem w pełni zrozumieć obsługę wejścia-wyjścia w C++, trzeba dyspono wać sporą wiedzą z zakresu samego języka. Celem wprowadzenia czytelnika w to zagadnienie we wcześniejszych rozdzi ałach tej książki przedstawiono podstawowe sposoby realizacj i wej ścia-wyjścia z wykorzystaniem obiektu c i n k l asy i s t r e a m oraz obiektu c o u t kl asy o s t r e am, a także, w mniejszym stopniu, sposoby real izacj i wejści a-wyj ścia p l ikowego przy użyciu obiektów klas i f s t r e a m oraz o f s t r e am. W niniej szym rozdziale przyjrzymy się dokładniej klasom wejścia-wyjścia w języku C++, pokażemy, jak są one zbudowane, i obj aśnimy, j ak stero-
Rozdział 1 7 Obsługa wejścia, wyjścia oraz plików -
wać formatem wyj ściowym. Czytelnik, który ominął kilka rozdzi ałów wyłącznie po to, aby po znać tajniki zaawansowanego fonnatowania, może przeczytać punkty dotyczące fonnatowan ia. zwracając uwagę na techniki, a pomij ając objaśnienia. Mechanizmy języka C++ służące do realizacj i wejścia i wyj ścia plikowego są oparte na tych samych podstawowych definicjach klas co obiekty c i n oraz c o u t , a więc omówienie wejścia-wyjścia kon soli (klawiatura i ekran) będzie w tym rozdziale trampoliną do zgłębienia wejścia-wyjścia pl ikowego. Prace komi tetu standaryzacj i języka C++ organizacj i ANSI/ISO mi ały na celu poprawę zgodno ści obsługi wejści a-wyj ścia w j ęzyku C++ z istniej ącym mechanizmem obsługi wejścia-wyjścia języka C i zaowocowały pewnymi odstępstwami od tradycyj nych praktyk programistycznych j i;: zyka C++ w tym zakresie.
Ogólna charakterystyka obsługi weiścia-wyiścia w ięzyku C++ Większość j ęzyków komputerowych posiada wbudowaną obsługę wejścia-wyj ścia. Weźmy na przy kład li stę słów kluczowych j ęzyków takich j ak BASIC czy Pascal, a zobaczymy, że instrukcje PR I NT , instrukcje wri tel n i tym podobne wchodzą w skład słownictwa języka. Obsługa wejścia-wyj ścia nie została j ednak wbudowana ani do j ęzyka C, ani do C++. Przeglądając słowa kluczowe tych języków, napotkamy i f i f o r , lecz nie znajdziemy nic, co wiązałoby się z operacj ami wej ścia wyjścia. W przypadku języka C obsługę wejścia-wyjścia pierwotnie pozostawiono implementato rom kompilatorów. Jednym z powodów takiego rozwiązania była chęć pozostawienia implcmenta torom swobody umożliwiaj ącej zaprojektowanie takich funkcj i 1/0, które będą naj lepiej odpowia dać wymaganiom sprzętowym platfonny docelowej . W praktyce w większości implementacj i ob sługę wej ścia-wyj ścia oparto na zestawie funkcj i bibliotecznych opracowanych pierwotnie dla śro dowiska systemu Unix. W standardzie ANSI C temu pakietowi nadano fonnalną nazwę Standard /11p111/011tp111 i uczyniono go obowiązkowym składnikiem biblioteki standardowej j ęzyka C. W j ę zyku C++ pakiet ten j est również dostępny, więc jeśli ktoś j est przyzwyczajony do rodziny funkcj i języka C zadeklarowanych w pl iku stdio.h, może i c h także używać w programach napisanych w C++ (w nowszych implementacj ach funkcj e te są zadeklarowane w pliku nagłówkowym cstdio). Niemniej jednak w zakresie obsługi wejścia-wyjścia język C++ opiera się na rozwiązaniu charaktery stycznym dla języka C++, a nie języka C, czyli na zestawie klas zdefiniowanych w plikach nagłówko wych iostream (dawniej iostream.h) orazfstream (dawniej fttream.h). Ta biblioteka klas nie wchodzi w skład fonnalnej definicj i języka ( c i n oraz i s t ream nie są słowami kluczowymi) - w końcu język komputerowy definiuje jedynie reguły wykonywania pewnych operacji, np. tworzenia klas, a nie określa, co, postępując zgodnie z tymi regułami, należy tworzyć. A więc tak jak implementacje języka C zawierają bibliotekę standardową funkcji, tak język C++ dostarczany j est z biblioteką stan dardową klas. Na początku standardowa biblioteka klas była niefonnalnym standardem i składała się wyłącznie z klas zdefiniowanych w plikach nagłówkowych i o s t ream oraz f s t re am. Komitet standaryzacj i języka C++ organizacj i ANSI/ISO podjął decyzję o fonnalnym nadaniu tej bibliotece miana standardowej biblioteki klas i dodaniu do niej innych klas standardowych, z których niektóre omawiane są w rozdziale 1 6. W niniejszym rozdziale omówimy standardowe wejście-wyjście języka C++. Najpierw jednak poznamy podstawowe pojęcia i koncepcje związane z tym mechanizmem. 1 042
Ogólna charakterystyko obsługi wejścia-wyjścia w języku (++
Strumienie i
bufory
W programie w j ęzyku C++ wejście i wyjście są postrzegane jak strumień bajtów. Podczas ope racj i wejścia program pobiera bajty ze strumienia wej ściowego, a podczas operacj i wyj ścia wsta wia baj ty do strumienia wyj ściowego. W przypadku programu operującego na danych teksto wych każdy bajt może reprezentować j eden znak. Mówiąc bardziej ogólnie, bajty mogą składać się na binarną reprezentacj ę danych znakowych lub liczbowych. Bajty w strum ieniu wejściowym mogą pochodzić z klawi atury, lecz także z urządzenia pamięci masowej , takiego jak dysk twardy, lub z innego programu. Analogicznie, bajty w strumieniu wyj ściowym mogą być wysyłane na ekran, na drukarkę, do urządzenia pamięci masowej lub do innego programu. Strumiei1 odgrywa rolę pośredn ika pomiędzy programem a źródłem lub miejscem docel owym strumienia. Takie podejście umożl iwia w j ęzyku C++ traktowanie wejścia z klawiatury w taki sam sposób jak wejścia z pl iku - program w j ęzyku C++ analizuj e j edynie strumień danych i nie musi „wie dzieć", skąd one pochodzą. Analogicznie, dzięki zastosowaniu strumieni dane wyj ściowe można w języku C++ przetwarzać w sposób niezależny od miej sca przeznaczenia tych danych. Obsługa wejścia składa się więc z dwóch etapów: • skojarzenia strumienia z wej ściem do programu, • połączenia strumienia z p l i kiem.
Inaczej mówiąc, strumień wej ściowy wymaga dwóch połączeń, po jednym z każdej strony. Połączenie po stronie plik"U zapewnia źródło danych dla strumienia, natomiast połączenie po stronie programu wprowadza dane wyjściowe stnnnienia do programu (połączenie po stronie pliku może być plikiem, lecz może to także być inne urządzenie, np. klawiatura). Analogicznie obsługa wyjścia wymaga połą czenia strumienia wyjściowego z programem oraz skojarzenia ze strumieniem jakiegoś miejsca doce lowego. Przypomina to wodociąg, w którym zamiast wody płyną bajty (patrz rysunek 1 7 . 1 ).
ze strumieniem należy połączyć jedno żródło wejścia
PROGRAM
PROGRAM
Q
strumień należy skojarzyć z programem
ze strumieniem należy skojarzyć
PROGRAM
jedno miejsce docelowe wyjścia
Rysunek 1 7. 1 . Obsługa wejścia-wyjścia w języku C++ 1 043
Rozdział 1 7 Obsługa wejścia, wyjścia oraz plików ·
Obsługa wejści a-wyj ścia będzie zazwyczaj wydajniej sza. jeśli użyj emy bufora. Bufor jest blo kiem pamięci stanowiącym pośrednie, tymczasowe miejsce przechowywania przy przesyle infor macj i z urządzenia do programu lub z programu do urządzenia. Urządzenia takie jak napędy dyskowe przesyłaj ą zazwyczaj dane w blokach po 5 1 2 lub więcej bajtów, podczas gdy programy często operuj ą na poj edynczych baj tach. Bufor umożliwia pogodzenie tych dwóch różnych szyb kości przesyłu i przetwarzania informacj i . Założymy na przykład, że zadan iem programu Jest zliczenie l i czby znaków $ znaj duj ących się w p l i ku zapi sanym na dysku twardym . Taki program mógłby odczytywać z p l i ku j eden znak, przetwarzać go, odczytywać następny znak itd. Odczyt z pliku poj edynczych znaków wymaga wysokiej aktywności sprzętu i jest wolny. Podejście buforo wane polega na odczycie z dysku dużej porcj i da nych, umieszczeniu tej porcj i w buforze, a następ nie odczycie poj edynczych znaków już z bufora. Poni eważ odczyt poj edynczych bajtów z pamięci jest znacznie szybszy niż z dysku twardego. takie Bufor strumienia wypełniany blokiem danych rozwiązan ie jest zdecydowanie wydaj n i ej sze, a także mniej obci ążaj ące dla sprzętu. Po osiągnię ciu ko1\ca bufora program powinien oczywiście od czytać z dysku następną porcj ę danych. Zasada j est podobna jak w przypadku zbiornika, który pod czas wielkiej burzy gromadzi hektolitry wody, aby następnie dostarczać tę wodę do naszych domów Strumień dostarcza dane do programu bajt po bajcie z bardziej „cywil izowaną" prędkości ą przepływu (patrz rysunek 1 7 .2). Analogicznie przy operacj i wyjścia program najpierw wypełnia bufor, a na stępnie przesyła cały blok danych na dysk twardy, opróżniaj ąc bufor przed następną porcj ą danych wyjściowych (ang . .flushing). Być może czyteln icy mogą zaproponować własne analogie tego proceBufor strumienia wypełniany kolejnym blokiem danych su z instal acj ą wodociągową.
Rysunek 1 7. 2. Strumień z buforem Wejście z klawi atury dostarcza pojedyncze znaki, a więc w tym przypadku program me potrzebu je bufora pozwal aj ącego dopasować do siebie różne prędkości przesyłu . Buforowane wej ście z klawiatury umożliwia j ednak użytkownikowi skopi owanie i poprawienie danych wejściowych przed przesłaniem ich do programu. W przypadku programu w j ęzyku C++ bufor wej ściowy J est normalnie opróżniany po naciśnięciu klawisza Enter. To dlatego przetwarzanie danych w przy kładach zamieszczonych w tej książce rozpoczyna się dopiero po naciśnięciu klawisza Enter. Przy wyświetlaniu danych wyjściowych program w j ęzyku C++ opróżnia bufor wyjściowy po przesłaniu znaku nowego wiersza. W zależności od implementacj i program może opróżniać bu for także w innych sytuacj ach, na przykład w przypadku oczekującej operacj i wejścia. Oznacza to, że po napotkaniu instrukcj i wej ścia program opróżni a bufor wyj ściowy ze wszystkich danych wyjściowych . Takie właśnie działanie powinny zapewniać implementacj e j ęzyka C++ zgodne ze standardem ANSI C.
1 044
Ogólna charakterystyka abslugi wejścia-wyjścia w języku C++
Strumienie i bufory a plik i o s tream Zarządzan ie strumieniami i buforami może się okazać dość skompli kowane. lecz dołączenie pliku iostream (dawn iej iostream. h) udostępn ia k i l ka klas, których zadaniem jest wyręczeme programisty w tworzeniu i zarządzaniu tymi elementami . W najnowszej wersj i bibl ioteki I/O języka C++ definiowane są właściwie szablony klas umożliwiaj ące obsługę zarówno danyc h typu char, jak i w c h a r _ t . Dzięki zastosowaniu defini cj i t yp e de f specja lizacj e szablonów dla typu char imin1j ą klasyczne n ieszablonowe implementacj e klas 110 . Oto niektóre z tych klas (patrz rysunek 1 7 . 3 ) : • Klasa s t reamb u f zapewnia obszar pamięci na bufor wraz z metodami służącymi do wypełniania bufora, odwoływania się do zawartości bufora, opróżniania bufora oraz za rządzania pamięcią bufora. • Klasa ios _b a s e reprezentuj e ogólne właściwości strumienia infonnujące m.in. o tym, czy jest on otwarty do odczytu oraz czy jest strumieniem tekstowym czy też binarnym. • Klasa ios jest pochodną klasy i o s _b a s e i zawi era wskaźnik składowy na obiekt klasy s t reamb u f . • Klasa o s t ream wywodzi s i ę z klasy i o s i udostępnia metody d o obsługi wyjścia. • Klasa i s t r eam wywodzi się z kl asy ios i u dostępnia metody do obsługi wejścia. • Klasa i o s t r e am j est pochodną klas i s t r e a m oraz o s t r e am, a więc dziedziczy zarówno metody do obsługi wej ścia, j ak i wyjścia. Klasa streambuf: zarządza pamięcią buforów wejściowych i wyjściowych
Klasa ios: ogólne wlaściwości strumienia, zawiera wskażnik do obiektu klasy streambuf
l
Klasy pochodne
Klasa ostream: metody do obsługi wyjścia
Klasa istream: metody do obsługi wejścia
•
Klasa ios tream: dziedziczy metody do obsługi wejścia i wyjścia po klasach is tream i ostream
Klasa pochodna Wiełodziedziczenie
Rysunek 1 7.3. Niektóre klasy wejścia-wyjścia
1 045
Rozdzial 1 7 - Obsluga wejścia, wyjścia oraz plików A b y k o rzys ta ć z tych u d ogo d n i e i\ , n a le ż y po prostu użyć obiektów o dp owi e d n ich k l a s . N a przy k ład do obs ługi wyj ś c i a używamy o b i ektu k l asy o s t r e am, j a k choćby c o u t . Utworze n i e tak i e go o b i ektu powoduj e o tw arc i e strumienia. automatyczne utworzenie bufora i s koj a rz e n i e go ze stru m i e n i e m . a także udostęp n i e n i e funkcj i
składowych d an ej
k l a s y.
Biblioteka weiścia-wyiścia zdefiniowana na nowo W standardzie I S O/AN S I Ctt zmodyfi kowano obsługę wejścia-wyjścia na k i l ka sposobów. Po p i e rwsze . p l i k ostream . h zas tą p i o n o p l i k i e m ostream, a wszystkie zdefi n i owa n e w n i m klasy u m i eszc z o n o w p r zestrze n i n azw s t d . Po d r u g i e . na n owo zdefi n i owano s a m e klasy I/O . Ż e by być język i e m m ię d zy narodowym , Ctt m u s i zapewn iac o bsłu g ę m i ęd zynarod owych zestawów z n a kó w , k t ó r e wy m a g a j ą co n aj m n i e j 1 6 - b i t o we g o typ u z n a k o we g o . D o tradycyjn e g o 8-bitowe g o typ u c h a r dodano więc 1 6- b itowy typ znakowy w c h a r t . Każdy z tych typów wymaga własn e g o m e c h a n i z m u 1/0. Zam iast two rzyć dwa oddziel n e zestawy klasy, kom itet standaryzacj i op racował zestaw szablon owych k l as wejścia - wyjścia , do które g o n a l e zą m . 1 11 . k l a s y b a s i e i s t r e a m < c h a r T , t r a i t s < c h a r T > > o raz b a s i e os t r e a m < c h a r T , t r a i t s < c h a r T > > . Sza b l o n t r a i t s < c h a r T > z kole i jest kla 5ą sza b l o n ową, która defi n i uje okre ś l o n e cechy ( a n g . traits) typu znakowe g o , takie jak sposób p o równywania pod kątem równ osci o raz wartość EOF, czyli znak końca p l i k u . W stan dardzie języka C t t zdefi n i owano specjal izacje klas I/O dla typów c h a r o raz w c h a r t . Klasy i s u e a m o raz o s t r e a m s ą d efi n i cjam i t yp e d e f spec j a l izacj i d l a typ u c h a r . Analog iczn i e , klasy w i s t r e a m o raz w o s t r e a m s ą specjal izacja m i d l a typ u w c h a r _ t . Istnieje n a p rzykład o b i e kt w c o u t przeznaczony do wysyłania na wyjście stru m i e n i znaków 1 6-b itowych . Defin icje te zawi e ra p l i k nagłówkowy o s t r e am. _
_
N i e które n i ezależne o d typ u i n formacj e , jakie zazwyczaj u m ieszczon e były w klas i e bazowej i o s , p rzen i e s i o n o do n owej klasy i o s _ b a s e . Dotyczy to r ó ż nych stałych formatowan i a , n p . i o s : : f i x e d , którą zastąp i o n o st ałą i o s _b a s e : : f i x e d . Kl asa i o s _ b a s e za wiera ponadto pewne opcje. które n i e były d ostę p n e w starej wersj i klasy i o s . B i b l ioteka klas i o s t re am j ę zyka C ++ wyręcza progra m i s tę w wi c i u szczegółach. Do łąc ze n i e do programu p l i ku i o s t re a m p owo d uj e , na przykła d , automatyczne utworzenie ośm i u obiektów stru m i en i (czterech dla strumieni znaków 8-bitowych i czterech d l a stnnnieni znaków 1 6-bitowyc h ) :
• Ob i ekt c i n o d p o w i a d a standardowemu stru m i e n i o w i wej ś c i owemu. Domyś l n i e strum i .:11 ten j e s t s k oj a rzony ze s tan d a rd ow y m u rz ąd z en i e m w ej ś c i o wym . którym z a z wyc z aj j e s t klawiatura. Obiekt we i n j est po d o b n y, z ty m że współpracuj e z t yp e m w c h a r _ t . • Obiekt c o u t odpowiada standardowemu strumieniowi wyjściowemu. D o my ś l n i e stru mie!\ te n jest skoj arzony ze standardowym urządzeniem wyj ś c i owym , którym z a zwyczaj jest m o n i tor. O b i e k t w c o u t j est po d o b n y, z ty m że w sp ół p racuj e z ty p e m w c h a r_ t . • O bi e k t c e r r o dpo w i a d a s t a n da rd ow e m u stru m i e n i ow i b łę dó w. kt ó ry można wykorzy s t ać do wyświetl a n i a komun i k atów o b ł ę d a c h . D o m y ś l n i e stnnn iei\ t e n j est skoj arzony ze standardowym urządzeniem wyj ś c i ow ym , k tó ry m z a z w y c zaj j e s t mon i tor. i nie j es t bufo rowan y. Oznacza to, że d a n e są wy s y ł a n e be z p o ś re d n i o na ekran, bez czekan i a n a wyp e ł nienie się bu fora c z y też na p oj aw i en i e się znaku nowego w i e rsza. O b i e kt w c e r r j e s t p od o b n y, z tym że w spółpracuj e z typ e m w c h a r_ t . • O b i ekt c l o g t a k ż e odpowiada standardowemu strum ieniowi błędów. Domyś l n i e stru m i e !\ ten jest s k oj a rzony ze s t a n dardo w y m urządzeniem wyj ściowym , k t ó ry m zazwyczaj j est m o n i t o r, i nie j est buforowany. Obiekt we l o g jest p odo b n y, z tym że współpracuj e z t yp e m w c h a r
1 046
t.
Ogólna charakterystyka obsługi wejścia-wyjścia w języku (++ Co oznacza stwierdzenie, że obi ekt reprezentuje strumień? No więc, gdy obi ekt c o u t zostaj e zadeklarowany w programie przez plik iostream, obiekt ten posiada dane składowe przechowu jące infonnacje dotyczące wyj ścia, takie jak szerokości pól, które maj ą być użyte przy wyświe tlaniu danych, l iczba wyświetlanych miejsc po przecinku, podstawa systemu li czbowego, która zostanie użyta do wyświetlania wartości całkowitych, a także adres obiektu klasy s t r e ambu f opisującego bufor służący do obsługi strumienia wyjściowego. Instrukcj a taka jak: cout licznik Wi ta j i żegnaj ! Contro l - Z
< - s ymu l o w a n y z n a k koń c a p l i k u S t rumień we j ś c i ow y z a w i e r a ł 1 9 z n a ków . C : \>
( EO F )
W tym przypadku dane wejściowe pochodzą z klawiatury, a wyjściowe są wysyłane na ekran. Wykorzystując operatory przekierowania wejścia (), możemy za po mocą tego samego programu zliczyć liczbę znaków w pliku tekst, a wynik umieścić w pliku liczznak: C : \>licznik licz znak C : \>
Cześć < t e k s t wiersza polecenia powoduj e skoj arzenie wejścia standardowego z plikiem tekst, co oznacza, że obiekt c i n będzie czytał dane wejściowe z tego p l iku, a nie z klawiatury. Inaczej mówiąc, system operacyjny zmienia połączenie po stronie „dopływu" do strumienia wejściowego,
1 047
Rozdział 1 7 - Obsługa wejścia, wyjścia oraz plików p od c z a s gdy j ego koniec „wypływowy" p ozo s taj e p o ł ącz o ny z prog r a mem. Cześć > l i c z z n a k wiersza po l e c eni a po woduj e skoj arzenie wyjścia standardowego z p l i kie m liczznak, dzięki czemu o b i ek t c o u t będzie wysyłał dane wyj ści owe do tego p l i ku , a nie na ekran. Oznacza to, że sys te m o p er a c yj n y zmienia p o łączen i e po stronie „wypływu" ze strumienia wyjści owego. p ozo st awiając koniec „dopływowy" nadal połączony z programem. Składn ię przek i erow a n i a autom a t ycz n i e rozp oz n aj ą systemy DOS (od wersj i 2.0). L i nux oraz Unix (systemy Unix, Linux oraz DOS od we rsj i 3 . 0 d o p u s zc z aj ą wst a w i e n i e opcj o n a l n yc h zn a k ó w od s t ęp u pom i ę dz y o p era tora m i p rzek i e ro w a n ia a nazwami p l i ków).
Standard owy strnmiet1 wyj ś ci owy, reprezentowany przez obiekt c o u t , j est no rm a l n y m kanałem dla danych wyjściowych z programu. S ta n d a rdo we strnmicnie błędów ( reprezentowane przez ob i ekty ce r r oraz c l o g ) są p rzeznaczo ne na komu nika ty o bł ę d ach prog ramu . D om yśl n ie w s zystkie te obiekty wysyłają dane na monitor. Przekierowanie wyjścia standardowego nic ma jednak wpływu na ob i e k ty c e r r oraz c l og - j e ś l i kt óreg oś z tych obi ektów u żyj em y do zgł oszeni a komunikatu o błędzie, program wyświ et l i komunikat na ekran ie, nawet w przypadku, gdy nonnalnc dane w yj ściowe obi ektu c o u t są kierowane gdzie in dzi ej . R ozw a ż my p rzykład owy fragment kodu : if
( succes s ) std : : cout .
Realizacia operacp wyiścia z wykorzystaniem obiektu cou t Jak już wsp o mni an o wcz e ś n i ej , w ję zyk u C++ dane wyj śc i ow e są p o strze ga ne j ako stru m i e1\ b ajt ów (w zależności od implementacj i i p l atformy mogą to być baj ty 8-, 1 6- l ub 32-bitowe. niemniej jed n a k są to b aj t y ). Wi ele typów danych w p ro g rami e j est je dn a k z o rg a n i zow a ny c h w większe od p oj e dyn c z ego baj ta j e dn ost k i .Na przykł a d typ i n t moż e b y ć repreze ntowany przez 1 6- lub 32-bitową wartość b i narną, a wartość typu d o u b l e może być reprezentowana p rz ez 64 bity d a n yc h b i n a rn yc h . Ki edy j edn a k wysy ł a m y strumie1i b aj tó w na ekran. ch c e m y, a by każdy baj t re pre z ent owa ł wart o ś ć znak o w ą. Oznacza to, że w celu w y s ł an i a na ekran l iczb y - 2 . 3 4 . powinni śmy wy s łać pięć znaków ( - , 2 , . , 3 o ra z 4 ) , a nie wewnętrzną 64-bitową re pr e z e n tacj ę z m ie n n op rz e c i n k ową tej wartości. Z tego wzgl ę d u j e d n y m z n aj wa żniej s zyc h zadail . przed który mi staje k l as a o s t r e a m , jest k o nw ersj a t y pów l icz bo wych , takich j ak i n t czy f l o a t , na st rum i c1\ znaków reprez e n tuj ący wartośc i w postac i t ekst o wej . Oznacza to. że k l a s a o s t r e am przeks z ta ł ca wewnętrzną bitową re p rez e n ta cj ę dan y c h w wyj ściowy stru m i eI1 baj tów znakowych ( być może pewnego dn i a b ędz i e my posiadali bion iczne i m pl a n t y umożl iwiaj ące n a m bczpośrcd-
1 048
Realizacja operacji wyjścia z wykorzystaniem obiektu cout nią interpretację danych binarnych, ale te prace rozwoj owe pozostawimy już j ako kolejne ćwi czenie czytelnikowi). Do wykonywania tych konwersj i klasa o s t r e am udostępnia kilka metod. Przyj rzymy im się teraz bliżej , podsumowując metody używane w całej książce oraz opisuj ąc dodatkowe, które oferuj ą precyzyjniejszą kontrolę na wyglądem danych wyj ściowych.
Przeciążony operator vórz s/r111nie11 pr::y
u::yciu konstruktora domyHnego
li skojarz strumień ;: plikiem fal. dat
li wykonaj wla.friwe operacje li odłącz slrwnie1i od pliku
fat.dat
1 1 03
Rozdział 1 7 Obsługo wejścia, wyjścia oraz plików -
fin . elear ( ) ; f i n . o p e n ( " ra t . da t " ) ;
li wyzeruj obiekt.fin (może nie być konieczne) li skojarz strumień z plikiem rat. dat
fin . close ( ) ;
N iedługo przedstawimy odpowiedni przykład, lecz najpierw zaprezentuj emy technikę wprowa dzania listy plików do programu w taki sposób, aby program mógł je w pętli przetworzyć.
Przetwarzanie argumentów wiersza polecenia W przypadku programów przetwarzających pliki nazwy pl ików podaj e się często j ako argumenty wiersza polecenia. A rgumenty wiersza polecenia to argumenty, które wpisuje się wraz z polece niem w wierszu poleceń. Aby, na przykład, zliczyć liczbę słów w pewnych p likach w systemie Unix lub Linux, w wierszu poleceń wpiszemy następującą komendę: we repo r t !
report2
repo r t 3
Łańcuch w e jest tutaj nazwą programu, a r e p o r t ! , rep o r t 2 oraz r e p o r t 3 to nazwy plików przekazywane do programu w postaci argumentów wiersza polecenia. Język C++ zapewnia mechanizm umożliwiaj ący programowi działaj ącemu w środowi sku wier sza poleceń dostęp do argumentów wiersza poleceń. W tym celu możemy użyć następuj ącego alternatywnego nagłówka funkcj i m a i n ( ) : i n t ma i n ( i n t a r g c ,
c h a r * a rgv [ ] )
Parametr a rg c reprezentuje liczbę argumentów podanych w wierszu poleceń. Liczba ta uwzględnia także samą nazwę polecenia. Zmienna a rgv jest wskaźnikiem na wskaźnik typu c h a r . Brzmi to nieco abstrakcyj nie, niemniej jednak zmienną a rgv można traktować jak tablicę wskaźników na argumenty wiersza poleceń, przy czym element a rgv [ O ] jest wskaźnikiem na pierwszy znak łańcu cha zawierającego nazwę polecenia, element a rgv [ 1 ] - wskaźnikiem na pierwszy znak łańcucha zawierającego pierwszy argument wiersza poleceń itd. Oznacza to, że element a rgv [ O ] jest pierw szym łańcuchem z wiersza poleceń itd. Przypuśćmy, że mamy następujący wiersz polecenia: we r e p o r t !
repo r t 2 repo r t 3
W takim przypadku argument a r g c będzie równy 4 , element a r gv [ O ] będzie zawierał łań cuch w e , element a r gv [ 1 ] łańcuch rep o r t ! itd. Następuj ąca pętla wypisze każdy argu ment wiersza poleceń w osobnym wierszu: -
for
( in t i 1 ; i < a rg c ; i + + ) c o u t < < a r gv [ i ] < < e n d l ; =
Jeśli zaczniemy od i 1 powyższa pętla wypisze po prostu argumenty wiersza poleceń, nato miast jeśli zaczniemy od i O, to zostanie także wyświetlona nazwa polecenia. =
=
Argumenty wiersza polecenia są oczywiście ściśle związane w systemami operacyj nymi dzi ała jącymi w trybie tekstowym, takimi jak DOS, Unix oraz Linux. W przypadku innych konfiguracji używanie argumentów wiersza poleceń także może być możliwe:
1 1 04
Wejście-wyjście plikowe
• Wiele zintegrowanych środowisk programowania (IDE) przeznaczonych dla systemu DOS oraz Windows oferuje możliwość podawania argumentów wiersza poleceń . Aby dotrzeć do poła, w które będzie można wpisać argumenty wiersza poleceń, musimy zazwyczaj przebrnąć przez wiele pozycj i menu. Dokładny zestaw niezbędnych kroków j est różny dla różnych producentów i różnych wersj i, warto więc sprawdzić to w dokumentacj i . • DOS-owe środowiska I D E oraz wiele z tych przeznaczonych dla systemu Windows umożli wiają wygenerowanie plików wykonywalnych działających w systemie DOS, w oknie sys temu DOS łub w oknie konsoli systemu Windows w zwyczajnym trybie wiersza poleceń. • W środowisku CodeWarrior firmy Metrowerks przeznaczonym dla komputerów Macintosh argumenty wiersza poleceń możemy symulować, umieszczając w programie następujący kod: # i n c l u de < c o n s o l e . h > li umożliwia emulację argumentów wiersza polecenia int ma i n ( i n t a rg c , c h a r * a r gv [ ] ) =
arg c
c comma n d ( & a r gv ) ;
li tak, ccommand, a nie command
Po uruchomieniu programu funkcj a c comma n d ( ) wyświetli na ekranie okno dialogowe z po lem, w którym będzie można podać argumenty wiersza polecenia. Funkcja ta pozwala również zasymułować przekierowanie. Program przedstawiony na listingu 1 7. 1 7 łączy w sobie techniki obsługi wiersza poleceń z technikami obsługi strumienia plikowego w celu policzenia znaków w plikach wymienionych w wierszu poleceń. Listing 1 7.1 7. Program count.cpp li count. cpp - policz znaki w plikach wymienionych na liście
# i n c l u de < i o s t r e am> # i n c l u de < f s t re am > l i lub stdlib. h # i n c l ude < c s t d l i b > li w przypadku komputera Macintosh li #include int ma i n ( i n t a rg c , c h a r * a rgv [ ) ) u s i n g n a me s p a c e s t d ; ccommand(&argv); 1) i f ( a rg c
li argc
=
cerr >
cout <
63
077
Ox3f
00 1 1 1 1 1 1
?
64
0 1 00
Ox40
0 1 0 00000
0101
Ox 4 1
@
65
0 1 000001
A
66
01 02
Ox42
0 1 0000 1 0
B
67
01 03
Ox43
0 1 0000 1 1
c
68
0 1 04
Ox44
0 1 000 1 00
o
69
0 1 05
Ox45
0 1 000 1 0 1
E
70
01 06
Ox46
0 1 0001 1 0
71
0 1 07
Ox47
0 1 0001 1 1
G H
N a zwa
ASCII
. a T typ przechowywany w konte ne rze , np. i n t . Z n a c ze ni e tych typów wyjaśniaj ą przykłady zaprezentowane po m ż ej w tabe l i . i den tyfi k at o r X
Tabela G. 1 .
.
Typy zdefiniowane dla wszystkich kontenerów
Typ
Wartość
X: :val ue_type
T, typ elementu
X::reference
T&
X:: const_reference
const T &
X::iterator
Typ iteratora ws kazującego typ T, zachowuje się podobnie jak typ T
X:: const_1terator
Typ ite ratora wskazującego typ const T, zachowuje się podobnie jak typ const T •
X::difference_type
Typ całkowity ze znakiem re prezentujący od ległość pomiędzy dwoma iteratorami ( np. odległość pomi ędzy dwoma wskaźnikami)
X::size_type
Typ całkowity bez znaku size_type może rep reze ntować rozmiar obiektów danych , l iczbę e l e mentów oraz indeksy
•
Składowe te ok re ś lan e są w definicj i k l a sy przy użyciu słowa k luczowego t yp e d e f . Typy te można wyko rzys tywać do dekl arowania odpowiednich z mi enn y c h Na przy kład w poniższym kodzie w ok rę ż ny sposób zastępuj emy w we k torz e o b ie k t ów k l as y s t r i n g pi erw s ze wystąpie nie ła ńc u c ha „ bo ga c z " łańcuchem „ r o ga c z " , żeby pok a z a ć jak deklarować z mi en n e przy użyci u typów składowych : .
,
Dodatek G Metody i funkcje z biblioteki STl -
ve c t o r < s t r i n g > i npu t ; s t r i n g t emp ; wh i l e ( c in > > t e mp & & t emp ! = " ko n i e c " ) i n p u t . p u s h_b a c k ( t emp ) ; vec t o r < s t r i n g > : : i t e r a t o r w a n t = f ind ( input . begin ( ) , input . end ( ) , s t ring ( " bogac z " I ) ; i f ( want ! = input . end ( ) ) vec t o r< s t ring> : : reference r = * wan t ; r = " roga c z " ;
W powyższym kodzie zmienna r jest deklarowana jako referencja do elementu w wektorze inpu t , który wskazuje iterator wan t . Analogicznie w ramach kontynuacj i tego przykładu możemy zapisać następujące instrukcj e : vec t o r < s t r i n g > : : va l u e t ype s l vec t o r < s t r i n g > : : r e f e r e n c e s 2
= =
i npu t [ O ) ; inpu t [ l ) ;
li s I jest typu string li s2 jest typu string &
W wyniku wykonania powyższych instrukcj i utworzone zostają zmienna s 1 będąca nowym obiek tem, który j est kopią elementu i n p u t [ O ] , oraz zmienna s 2 będąca referencj ą do e lemenn1 inpu t [ l J W tym przykładzi e przy założeniu, że wiemy już, że szablon j est oparty na typie s t r i n g , prościej będzie użyć poniższego kodu, który zapewnia równoważny efekt: .
st ring s l input [ O ] ; s t r ing & s 2 = inpu t [ l ] ; =
li si jest typu string li s2 jest typu string &
Bardziej złożone typy z tabel i G . 1 można jednak stosować w bardziej uniwersalnym kodzie, w którym typy kontenera oraz elementów są ogólne. Przypuśćmy na przykład, że potrzebujemy funkcj i m i n ( ) , która j ako parametr pobierałaby referencję do kontenera, a zwracała naj mniej szy element tego kontenera. Zakłada się przy tym, że dla typu wartości służącego do konkretyzacj i szablonu zdefiniowany j est operator < i ż e nie chcemy używać algorytmu m i n _ e l eme n t ( ) z biblioteki STL, który wykorzystuje iteratorowy interfejs. Ponieważ argumentem może być zarówno typ ve c t o r < i n t > , j ak i l i s t < s t r i n g > czy też dequ e < do u b l e > , do reprezentacj i kontenera używamy szablonu wraz z parametrem szablonowym, n p . B a g (tj . B a g j est typem szablonowym, który może zostać skonkretyzowany j ako typ ve c t o r< i n t > , l i s t < s t r i n g > lub jakiś inny typ kontenerowy). A więc typem argumentu tej funkcji będzie c o n s t B a g & b . A c o z typem zwracanym? Powinien t o być typ wartości kontenera, a więc B a g : : v a l u e_ t yp e . W tym momenci e j ednak B a g jest po prostu parametrem szabl onu i kompi lator nie może „wiedzieć", że składowa va l ue t ype j est właściwie typem. Możemy jednak zastosować słowo kluczowe t yp e n ame , aby wyjaśnić, że składowa klasy jest definicj ą t ypede f : ve c t o r< s t r i n g > : : va l u e t ype s t ; t ypen ame Ba g : : va l u e t ype m ;
li typ vector to zdefiniowana klasa ll Bag to jeszcze niezdefiniowany typ
W przypadku pierwszej definicj i kompi lator ma dostęp do definicj i szablonu v e c t o r , która określa, że składowa va l u e _ t yp e jest definicj ą t ypede f . W drugiej definicj i natomiast sło wo kluczowe t y p e n ame gwarantuje, że niezależnie od tego, czym okaże się parametr B a g ,
1 1 92
Składowe wspólne dla wszystkich kontenerów kombinacja Bag : : v a l u e_ t ype j est nazwą typu. Powyższe rozważania prowadzą do następu jącej definicj i : temp l a t e < t ypename B a g > t ype name Ba g : : va l u e t yp e m i n ( c o n s t B a g & b ) I
t ypen ame Bag : : c o n s t_i t e r a t o r i t ; t ypename Ba g : : va l u e t ype m = * b . beg i n ( ) ; f o r ( i t = b . be g i n ( ) ; i t ! = b . e n d ( ) ; + + i t ) if ( *it < ml m = *it; re t u rn m ;
Teraz możemy już w następuj ący sposób użyć tej funkcj i szablonowej : vect o r < i n t >
t empe r a t u r e s ;
li wprowadź wartości temperatur do wektora
int c o l de s t = m i n ( t emp e r a t u re s ) ;
Argument t e m p e r a t u r e s spraw i a , że parametr B a g zostan i e uzgodn iony z typem vec t o r < i n t > , a wyrażeni e t yp e n ame B a g : : v a l u e _ t yp e będzie potraktowane j ako wyrażenie v e c t o r < i n t > : : v a l u e_ t yp e , które z kolei j est typem i n t . Wszystkie kontenery zawierają ponadto funkcj e s kładowe lub operatory wymienione w tabeli G.2. Ponownie identyfikator X oznacza typ kontenerowy, np. ve c t o r < i n t > , a T to typ prze chowywany w kontenerze, np. i n t . Identyfikatory a i b reprezentują wartości typu X.
Tabela G.2. Metody zdefiniowane dla wszystkich kontenerów Metoda (operator)
Opis
begin()
Zwraca iterator wskazujący pierwszy element
end()
Zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem
rbegin()
Zwraca iterator odwrotny wskazujący pozycję bezpośrednio za ostatnim elementem
rend()
Zwraca iterator odwrotny wskazujący pierwszy element
size()
Zwraca liczbę elementów
maxsize()
Zwraca maksymalny dopuszczalny rozmiar kontenera
empty()
Zwraca wartość true, jeśli kontener jest pusty
swap()
Zamienia m iejscami zawartości dwóch kontenerów Zwraca wartość true, jeśli dwa kontenery są tego samego rozmiaru i zawierają te same elementy w tej samej kolejności
!=
a ! = b zwraca ! (a == b)
a > b zwraca b < a
=
a >= b zwraca ! (a < b) 1 1 93
Dodatek G - Metody i funkcje z biblioteki STL Operator < kontenera zakłada, że operator < jest zdefiniowany dla typu wartośc i . Porównanie leksykograficzne j est uog ól ni e niem sortowani a alfabetycznego . Dwa kontenery są porównywane e le me nt po e l em e ncie aż do napotkan ia elementu w jedn y m kontenerze, który j est różny od odpowiadającego mu elementu w drugim kontenerze. W takim przy padk u uważa się, że kontene ry są uporządkowane tak samo jak niezgodna p ara elementów. Jeś l i na p rzykł a d dwa kontenery są identyczne pod względem 1 O p i e rw s zy ch e l ementów, lecz 1 1 . element pi e rw s ze g o kontenera jest mn i ej sz y od 1 1 . e l em e nt u drngiego kontenera, to pi e rwszy kontener występuj e p rz e d drn g i m . Jeśli dwa ko n t e n e ry są równe aż do wyczerpan ia e l ementów w j e dny m z nich, to k r ó t s zy kontener występuj e przed dłuższym.
Dodatkowe składowe wektorów, Dst i koleiek dwustronnych Wektory, l i s t y i kolejki dwu s t ro n n e są kontenerami s ekwen c yj ny m i i w s zy stki e posiadaj ą metody wyszczegó l n i on e w ta b e l i G . 3 . P onown i e identyfikator X oznacza typ kontenerowy, n p . v e c t o r < i n t > , a T t o typ przechowywany w kontenerze, n p . i n t , identyfikator a reprezentuj e wartość typu X , t j est wartością typu X : : v a 1 u e _ t y p e , i i j t o i te ratory w ej ścio w e, q2 oraz p to iteratory, q oraz q 1 są iteratorami, na których można wy ko n ać dereferencję, a n j est wartością c a ł k owitą typu X : : s i ze t yp e .
Tabela G. 3. Metody zdefinio wane dla wektorów, list oraz kolejek dwustronnych Metoda
Opis
a.insert(p,t)
Wstawia kopię wartości t przed pozycją p 1 zwraca iterator wskazuiący na wstawioną kopię o b i e ktu t. Wartością domyślną parametru t jest T(), tj . wartość służąca za arg u ment typu T w przypadku braku jego jawnej i n i cjalizacji.
a.insert(p , n ,t)
Wstawia n kopii wartości t przed pozycją p . Brak wartości zwracanej.
a.i nsert(p,i,j)
Wstawia kopie elementów z zakresu [i, j) przed pozycją p. B rak wartości zwracanej.
a.resize(n,t)
Jeśli n > a.size ( ) , wstawia n - a.size() kopii wartości t p rzed pozycją a.end() .Wartością domyślną parametru t jest T(), tj . wartość służąca za arg ument typu T w przypadku braku jego jawnej inicjalizacji. Jeś l i n < a.size(), elementy występujące po n-tym elemencie zostają usunięte.
a.assig n ( i , j )
Zastę puje bieżącą zawartość kopiami elementów z zakresu [ i ,j ) .
a.assig n(n,t)
Zastę puje b i eżącą zawartość n kopiami wartości t. Wartością domyślną parametru t jest T(), tj. wartość służąca za arg u ment typ u T w p rzypadku b raku jego jawnej i n i cjalizacji .
a.erase ( q )
Usuwa element ws kazywany przez parametr q i zwraca iterator do elementu , który p i e rwotni e występował bezpośrednio z a pozyCJą q .
a.erase( q 1 , q 2 )
U s u wa elementy z zakresu [ q 1 ,q2) i zwraca iterator wskazujący element, do którego p i e rwot nie odnosił się iterator q 2 .
a.elear()
Działanie równoważne instrukcji erase(a. beg i n ( ) , a.end ()) .
a.front()
Zwraca wartość 'a.beg i n ( ) (czyli pierwszy element).
a. back()
Zwraca wartość ·-a.e n d ( ) (czyli ostatni element).
a.push_back(t)
Wstawia wartość t przed pozycją a.end().
a . p op_back()
Usuwa ostatn i ele ment.
1 1 94
Dodatkowe składowe wektorów, list i kolejek dwustronnych
W tabeli G.4 wyszczególniono metody wspólne dla dwóch spośród trzech klas sekwencyj nych (v e c t o r , l i s t oraz d e q u e ) .
Tabela G. 4. Metody zdefiniowane dla niektórych sekwencji Metoda
Opis
a. push_fro nt(t)
Wstawia k o p i ę wartości t przed pierwszym elemente m .
list, d e q u e
a.pop_tront()
U suwa pierwszy element.
l ist, deque
a[n]
Zwraca wartość * (a.beg i n ( ) + n ) .
vector, deque
a.at( n )
Zwraca wartość • (a. beg i n ( ) + n). Zgłasza wyjątek out_of_range, jeśli n > a . s i ze ( ) .
vector, deque
Konte n e r
Szablon ve c t o r oferuje dodatkowo metody wyszczególnione w tabeli G.5. Identyfikator a oznacza tutaj kontener klasy v e c t o r , a parametr n jest wartością całkowitą typu X : : s i z e _ t yp e .
Tabela G. 5. Dodatkowe metody wektorów Metoda
Op i s
a.capacity()
Zwraca całkowitą liczbę elementów, które można u m i eścić w wektorze bez Ko ni eczności realo kacji .
a. reserve(n)
Sygnalizuje obiektowi a, że wymagany j e s t obszar pamięci na co naj m niej n elementów. Po wywołaniu tej metody pojemność we ktora będzie wynosić co najmn iej n eleme ntów. O peracja realokacj i ma miejsce w p rzypadku , gdy n prze kracza bieżącą poje mność. Jeśli n > a.max_size (), metoda zgłasza wyjątek length_error.
Szablon l i s t posiada dodatkowo metody wyszczególnione w tabel i G . 6 . Identyfikatory a i b oznaczają tutaj kontenery klasy 1 i s t , a T j est typem przechowywanym na l i ście, np. i n t , t j est wartością typu T, i i j to iteratory wejściowe, q2 oraz p to i teratory, q oraz ql są iteratorami, na których można wykonać dereferencję, a n jest wartością całkowitą typu X : : s i z e _ t yp e . W ta beli zastosowano standardowy zapis biblioteki STL [ i , j ) , oznaczaj ący zakres od i do J , lecz z wyłączeniem j .
Tabela G . 6. Dodatkowe metody list M etoda
Opis
a.splice(p,b)
Przenosi zawartość listy b d o a, wstawiając ją p rzed pozycją p .
a.splice(p,b,i)
Przenosi element listy b wskazywany przez i na pozycję bezpośrednio p rzed pozycją p w liście a.
a.splice(p, b , i ,j )
Przenosi eleme nty z zakresu [ i , j ) l i sty b na pozycję bezpośrednio przed pozycją p w l iście a.
a. remove (c o nst T& t)
Usuwa wszystkie eleme nty o wartości t z listy a.
1 1 95
Dodatek G Metody i funkcje z biblioteki STL -
Metoda
Op is
a.remove_if Przy założen i u , że i jest iteratore m d o elementów listy a , metoda us uwa wszystki e wartości, dla (Predicate pred) których wyraże n i e p red ( * i ) daje wartość true ( Pred i cate to f u n kcja boolowska lub obiekt fun kcyj ny, co o mawiano w rozdziale 1 5., „Zap rzyjaźnienie, wyjątki i nie tyl ko" ) . a. unique()
Usuwa wszystkie eleme nty z każdej grupy kolej nych równych sobie eleme ntów poza p i e rwszym.
a.unique Usuwa wszystkie elementy z każdej g ru py kolejnych równych sobie elementów, dla których (BinaryPred icate wyraże nie b in_pred ( * i , * (i-1 ) ) daje wartość true, poza pierwszym (Bi naryPred icate to f u n kcja bin_pred) boo lowska l u b obiekt funkcyjny, co omawiano w rozdziale 1 5 „Zap rzyjaźnienie. wyjątki i nie tylko") . ..
a.merge(b)
Scala zawartość listy b z listą a z wyko rzystaniem operatora < zdefiniowanego dla typu wartości. Jeśli element w liście a jest równowazny elementowi z l i sty b , jako p i e rwszy u m i eszczony zostaje element z l i sty a. Po scaleniu l i sta b jest p u sta.
a. merge(b, Scala zawartość l i sty b z li stą a z wykorzystaniem f u n kcji l u b obiektu f u n kcyjnego co m p . Jeśli Compare comp) element na liście a jest równoważny eleme ntowi z listy b, jako p i erwszy u m ieszczony zostaje element z listy a. Po scaleniu l ista b jest p usta. a.sort()
Sortuje l i stę a z wykorzystaniem operatora . Do opisu tych cechu kontenery asocj acyjne posiadaj ą dodatkowe składowe, które wyszczególniono w tabel i G . 7 .
Tabela G. 7. Typy zdefiniowane dla kontenerów asocjacyjnych Typ
Wartość
X : : key _type
Key, typ klucza
X::key_com pare
Com pare o wartości d o myślnej less
X::val ue_com pare
Typ p redykatu dwuarg u m e ntowego, który w p rzypad ku kontenerów set i multiset jest identyczny z typem key_com pare , a który w p rzypadku kontenerów map i multimap określa u p o rząd kowanie wartości pair
X : : mapped_type T,
typ skojarzonych danych (tylko w przypad ku kontenerów map i m u ltimap)
1 1 96
Dodotkowe składowe zbiorów i map K o n ten e ry a s ocj a cyj n e udo s t ę pniaj ą metody wymienione w tabel i G . 8 . W ogólnym przypadku obi e k t musi wymagać, żeby wartości o tym samym kluczu były identyczne - określenie kluc::.e równowa::: n e o z nacza , że dwie wartości, które mogą, lecz n i e muszą być równe, posi adaj ą ten sam klucz. W poniższej tabeli X oznacza klasę kontenerową, a ide n ty fi kato r a jest o bie kte m typ u X . Jeśli klasa X j e st oparta na niepowtarzalnych kluczach (tj . s e t l u b m a p ) , i dentyfikato r a _u n i ą ozn ac za obiekt typu X. Jeś li klasa X dopuszcza klucze w i e l o krotn e ( tj . mu l t i s e t lub mu l t imap), i dentyfi kato r a e q jest obiektem typu X . Tak j a k poprz ed m o , i i j to i t erat ory wej śc i ow e odnoszące _ s i ę do elementów typu v a l ue t ype, [ i , J ) Jest prawidłowym za k re s e m , p oraz q2 to i teratory do elementów ko n te n era a, q oraz ql są iteratorami do e l em e ntów kontenera a, na któ rych można w y k o na ć d e r e fe r e ncj ę , [ q 1 , q 2 ) j e st prawi dłowym z a k r e s e m . t j es t warto ś c i ą typu X : : val ue _ t ype ( k t ó ra może być p a rą) , a k jest wartością typu X : : k e y_ t ype. porównania n i c
Tabela G. 8. Metody zdefiniowane dla zbiorów, wielozbiorów, map oraz multimap M etoda
Opis
a.key_comp()
Zwraca obiekt porównania użyty p rzy konstruowaniu obiektu a.
a.value_comp()
Zwraca obiekt typ u value_co mpare.
a_uniq.insert(t)
Wstawia wartość t d o kontenera a pod waru nkiem, że kontener a nie zawiera jeszcze wartości o równoważnym kluczu. Metoda zwraca wartość typ u pai r. Skła d n i k typ u bool ma wartość true, jeśli wstawienie miało miej sce, a w przeciwnym razie równy jest false. Składn i k iteratorowy wskazuje element. którego klucz jest ró wnoważny kluczowi wartości t.
a_eq.insert(t)
Wstawia wartość t i zwraca iterator wskazujący jej pozycję.
a.i n se rt (p,t)
Wstawia wartość t, traktując iterator p jako ws kazówkę odnośnie m iejsca, od kt ó rego metoda insert() powinna rozpocząć przeszukiwarne. Jeśli a jest kontenerem z kluczam i niepowtarzal nym i , wstawienie ma m iejsce tylko wtedy, gdy kontener a nie zawiera jeszcze wartości o równoważnym kluczu - w przeciwnym razie wstawienie nie następuje. N iezal e żnie od tego, czy wartość została wstawiona, metoda zwraca iterator wskazujący pozycję o równoważnym kluczu.
a.insert(i,j )
Wstawia d o kontenera a elementy z zakres u [i, j ) .
a.erase(k)
Usuwa z konte nera a wszystkie elementy o kluczu równoważnym parametrowi k i zwraca liczbę u s u niętych elementów.
a.erase( q )
Usuwa element wskazywany p rzez ite rator q .
a.erase( q 1 ,q2)
Usuwa elementy z zakres u (i, j ) .
a. e lear()
Działanie równoważne instrukcji erase(a. begin ( ) , a.end ( ) ) .
a. t i nd( k) a.count(k)
Zwraca iterato r wskazujący element, któreg o k l u c z jest równoważny parametrowi k - w p rzypadku nie powodze nia wyszukiwania zwraca wartość a.end(). Zwraca liczbę elementów o kl uczach równoważnych parametrowi k.
a.lower_bou n d(k ) Zwraca iterator wskazujący pie rwszy element o kluczu niemn iejszym od k. a.upper_bound(k) Zwraca i t e rator wskazujący pierwszy element o kluczu większym od k. a.equal_range(k) Zwraca parę, której pierwszą składową jest a . l ower_bound(k), a d r u g ą a.up per_b o u n d ( k ) . a.operator[] (k)
Zwraca referencję d o wartości s kojarzonej z k l uczem k (tylko kontenery map). 1 1 97
Dodatek G - Metody i funkcje z biblioteki STL
Funkcie STL Biblioteka algorytmów STL, udostępniana przez pliki nagłówkowe a l g o r i thm oraz n ume r i e , oferuje wiele nieskladowych fimkcj i szablonowych opartych n a iteratorach. Jak mówili śmy w roz dziale 1 6„ nazwy parametrów szablonów są tak dobrane, żeby pokazywać, jakie pojęcie mają mode lować poszczególne parametry. Przykładowo nazwa F o r w a r d I t e r a t o r określa, że parametr po winien mode lować przynajmniej wymagan ia itcratora postępuj ącego (ang. jinward iteratur), a nazwa Pre d i c a t e oznacza, że parametr powinien być obiektem funkcyjnym o jednym parametrze i warto ści zwracanej typu boo 1. W standardzie języka C ++ algorytmy są podzielone na cztery grnpy: nicmo dyfikttiące operacje sekwencyjne, mutujące operacje sekwencyjne, operacje sortowania i pokrewne oraz operacje numeryczne. Określenie operacja sekwencyjna oznacza, że funkcja pobiera argumenty w postaci pa1y iteratorów definiuj ących zakres, czyli sekwencję, której dotyczy dana operacja. Okre ślenie mutująca mówi. że funkcja może modyfikować zawartość kontenera.
Niemodyfikuiące operacie sekwencyine Niemodyfi kujące operacj e sekwencyjne zebrano w tabe l i G . 9 . Nic pokazano ich parametrów, a funkcje przeciążone występują tylko raz. Ich pełniej szy opis, wraz z prototypami, zamieszczo no pod tabelą. Tabela pozwala więc uzyskać ogólne wyobrażenie na temat działania danej funk cj i , a następnie, jeśli wydaj e się ona interesująca, można zapoznać s i ę ze szczegółami . Tab ela G.9. Niemodyfikujące operacje sekwencyjne Funkcja
Opis
for_each ( )
Stosuje niem odyfi kujący obiekt fu n kcyj ny d o każdego elementu z zakre s u .
find ( )
Znaj d uje p i e rwsze wystąpienie danej wartości w zakresie.
find_if()
Znaj d uje p i e rwszą wartość w zakresie, która s pełn ia kryte r i u m testowe p redykatu.
find_e n d ( )
Znajduje ostatnie wystąpienie pod se kwen cj i , której wartości s ą zgodne z wartościami d rug iej sekwe ncj i . Sprawdzenie zgodności może od bywać się za pomocą operatora równości l u b p rzez zastosowanie predykatu dwuarg u m e ntowego.
find_fi rst_of()
Znajduje pierwsze wystąpienie d owo lnego elementu z d ru g iej sekwencj i , który jest zgodny z wartością z pierwszej sekwencji. Sprawdze nie zgod ności może od bywać s i ę za pomocą operatora równości l u b przez zastosowanie p redykatu dwuarg ume ntowego.
ad jacent_fi n d ( )
Znajduje pierwszy element, który j e s t zgodny z elementem bezpośred n i o po n i m występ ują cym . S p rawdzenie zgodności może od bywać się za pom ocą ope ratora równości lub p rzez zastosowanie predykatu dwuarg u mentowego.
count()
Zwraca l iczbę wystąpień danej wartości w zakresie.
cou nt_if()
Zwraca l i czbę wartości w zakresie, które są zgodne z podaną wartością, p rzy czym s p rawdze nie zgodności od bywa się przez zastosowanie p redykatu dwuargumentowego.
mismatc h ( )
Znajd uje p i e rwszy element z jednego zakresu, który n ie j e s t zg odny z od powiadającym m u e l e m e ntem z d rugiego zakresu i zwraca ite ratory d o i c h o b u . Sprawdzenie zgodności może odbywać się za pom ocą operato ra równości lub p rzez zastosowan ie p redykatu dwuarg u m en towego.
1 1 98
Funkcje STL
Funkcja
Opis
equal()
Zwraca wartość true, jeśli każdy element z jednego zakresu jest zgodny z odpowiadającym mu elementem z d rugiego zakresu . Sprawdzenie zgodności może od bywać się za pomocą operatora równości lub przez zastosowanie predykatu dwuargumentowego.
search()
Znajduje pierwsze wystąpienie podsekwencj i , której wartości są zgodne z wartościami d rug iej sekwencj i . Sprawdzenie zgodności może od bywać się za pomocą operatora równości l u b przez zastosowanie predykatu dwuarg umentowego.
search_n()
Znajduje pierwszą podsekwencję n elementów, z których każdy jest zgodny z podaną wartością. Sprawdzenie zgodności może od bywać się za pomocą operatora równości lub przez zastosowanie predykatu dwuargumentowego.
Przyjrzyjmy się teraz bardziej szczegółowo powyższym niemodyfikującym operacjom sekwencyjnym. Opis każdej funkcj i zawiera prototyp, po którym zamieszczono krótkie objaśnienie. Pary iteratorów określają zakresy, przy czym wybrana nazwa parametru szablonu określa typ iteratora. Jak zwykle za kres w postaci [ f i r s t , l a s t ) obejmuje elementy od pozycji f i r s t , aż do pozycji l a s t, lecz zjej wyłączeniem. Niektóre z funkcji pobierają dwa zakresy, które nie muszą dotyczyć kontenerów tego samego rodzaju. Możemy na przykład za pomocą funkcj i equ a l ( ) porównać listę do wektora. Funkcje przekazywane jako argumenty są obiektami funkcyjnymi mogącymi być wskaźnikami (ich przykładem są nazwy funkcj i) lub obiektami, dla których zdefiniowana jest operacja ( ) . Jak już powiedziano w roz dziale 1 6., predykat jest funkcją boolowską o jednym argumencie, a predykat dwuargumentowy jest funkcją boolowską o dwóch argumentach (funkcje te nie muszą być typu bool pod warunkiem, że będą zwracać wartość zerową zamiast wartości f a l se oraz wartość niezerową zamiast wartości t rue). for each ( ) _ template< c l a s s I np u t ! t e r a t o r , c l a s s F u n c t i o n > Function for_each ( I npu t i t e r a t o r f i r s t , I np u t ! t e rator l a s t , Fun c t i o n f ) ;
Funkcja f o r_e a c h ( ) stosuj e obiekt funkcyjny f do każdego elementu w zakresie [ f i r s t , la s t ) . Zwraca również obiekt f . find ( ) template < c l a s s I n p u t ! t e r a t o r , c l a s s T > I nput ! t e r a t o r f i n d ( I nput ! te r a t o r f i r s t , I npu t ! terator l a s t , c o n s t T & va l ue ) ;
Funkcja f i n d ( ) zwraca i tera tor wskazujący pierwszy element w zakresie [ f i r s t , l a s t l o wartości val u e . W przypadku nieznalezienia elementu zwraca iterator l a s t . find i f ( ) _ templ ate I nput ! terator f i nd_i f ( I np u t ! t e r a t o r f i rs t , I n pu t ! terator l a s t , Predi cate p r e d ) ;
Funkcja f i n d_ i f ( ) zwraca iterator i t wskazujący pierwszy element w zakresie [ f i r s t , l a s t ) , dla którego wywołanie obiektu funkcyjnego p r e d ( * i ) zwraca wartość t ru e . W przy padku nieznalezienia elementu zwraca iterator l a s t .
1 1 99
Dodatek G Metody i funkcje z biblioteki STL -
find_end ( ) temp l a t e < c l a s s Forward i terator l , c l a s s Forwardi terator2> Forwardi t e r a t o r l f i nd_end ( Forwardl teratorl f i rs t l , Forwardl teratorl l a s t l , Forwa rdlterator2 f 1 r s t 2 , Forward! t e rator2 l a s t 2 ) ; temp l a t e< c l a s s Forward i t e r a t o r l , c l a s s Forwardl terator2 , c l a s s B 1 n a r yPredicate> Forward l t e r a t o r l f i nd_ end ( Forwardl t e r a t o r l f i r s t l , Forwa r d l t e r a t o r l l a s t l , Forwa rdlterator2 f i rs t 2 , Fo rwardl terator2 l a s t 2 , B i n ar yPredi cate pred) ;
Funkcj a f i n d_ e n d ( ) zwraca iterator i t wskazujący ostatn i element w zakresie [ f i r s t 1 , l a s t 1 l , który stanowi początek podsekwencj i zgodnej z zawartością zakresu [ f i r s t 2 , l a s t 2 ) . P ierwsza wersj a funkcj i porównuje elementy przy użyciu operatora = = dla danego typu wartości. Druga wersj a porównuje elementy przy użyciu obiektu funkcyj nego predykatu dwuargumentowego p r e d . Elementy wskazywane przez i teratory i t 1 oraz i t2 są zgodne, jeśli wywołanie p r e d ( * i t l , * i t 2 ) zwraca wartość t r u e . W przypadku nieznalezienia elemen tu obydwie wersj e zwracają iterator l a s t l . f i nd_f i r s t_o f ( ) t emp l a te Forwardi t e r a t o r l f i nd_ f i r s t_o f ( Forward l t e r a t o r l f i r s t l , Forwa rditeratorl l a s t l , Forwardlterator2 f i r s t 2 , Forwa rditerator2 l a s t 2 ) ; temp l a te Forwa rdi t e r a t o r l f i n d_ f i r s t_o f ( Forward l t e r a t o r l f i rs t l , Forwardl teratorl l a s t l , Forwardl terator2 f i r s t 2 , Forwardlterator2 l a s t 2 , B i n a r yPredicate pred) ;
Funkcj a f i n d f i r s t o f ( ) zwraca iterator i t wskazujący pierwszy element w zakresie [ f i r s t l , l a s t l ) , który jest zgodny z dowolnym elementem z zakresu [ f i r s t 2 , l a s t 2 ) . Pierwsza wersj a funkcj i porównuj e elementy przy użyciu operatora = = dla danego typu wartości . Druga wersja porównuj e elementy przy użyciu obiektu funkcyj nego predykatu dwuargumento wego p r e d . Elementy wskazywane przez iteratory i t 1 oraz i t2 są zgodne, jeśli wywołanie p r e d ( * i t 1 , * i t 2 ) zwraca wartość t ru e . W przypadku nieznalezienia elementu obydwie wersje zwracaj ą iterator l a s t l . 2 d j acent_f i n d ( ) temp l a t e < c l a s s Forwa r d l t e r a t o r > · orwardl t e r a t o r adj a c e n t_ f i n d ( Forwardl terator f i r s t , Forwardl terator l a s t ) ; temp l a t e < c l a s s Forward l t e r a t o r , c l a s s B i naryPredi cate> Forward l t e r a t o r adj acent_find ( Forwa rdlterator f i rs t , Forwa rdlterator l a s t , B i n a r yPredicate pred ) ;
1 200
Funkcje STL Funkcja a d j a c e n t_ f i n d ( ) zwraca iterator i t wskazuj ący pierwszy element w zakresie [ f i r s t , l a s t ) taki, że j est on zgodny z elementem po nim występującym. W przypadku nieznalezienia takiej pary funkcj a zwraca iterator l a s t . Pierwsza wersja funkcj i porównuj e elementy przy użyciu operatora = = dla danego typu wartości. Druga wersja porównuje elementy przy użyciu obiektu funkcyj nego predykatu dwuargumentowego p red. Elementy wskazywane przez iteratory i t l oraz i t2 są zgodne, jeśli wywołanie p r e d ( * i t l , * i t 2 ) zwraca war tość t ru e . count ( ) template < c l a s s Inpu t i t e r a t o r , c l a s s T> iterator_tra i t s < I npu t i terator> : : di f feren ce_type count ( I npu t i t e r a t o r f i r s t , I n p u t i terator l a s t , c o n s t T& v a l ue ) ;
Funkcja c o u n t ( ) zwraca liczbę elementów w zakresie [ f i r s t , l a s t ) , które są zgodne z wartością v a ! u e . Do porównywania wartości służy operator == dla danego typu wartości. Typ zwracany jest typem całkowitym na tyle poj emnym, żeby wyrazić maksymalną l iczbę elemen tów, które zawierać może dany kontener. count i f ( ) _ templ ate iterator_tra i t s < I np u t i t e r a t o r > : : di f ference_type count_i f ( I np u t i t e r a t o r f i rs t , I np u t i terator l a s t , Predicate pred ) ;
Funkcja c o u n t i f ( ) zwraca l iczbę elementów w zakresie [ f i r s t , l a s t ) , dla których obiekt funkcyjny p r e d w przypadku przekazania elementu j ako argumentu zwraca wartość t r u e . mi sma tch ( ) template< c l a s s I n pu t i terator l , c l a s s I n p u t i t e r a t o r 2 > pa i r < I npu t i t e r a t o r l , I n pu t i t e r a t o r 2 > mi sma tch ( I np u t i t e ra t o r l f i rs t l , I np u t i t e r a t o r l l a s t l , I npu t i terator2 f i r s t2 ) ; templ ate pa i r< I nput i te r a t o r l , I nput i te r a t o r 2 > m i sma tch ( I nput i teratorl f i r s t l , I nput i t e r a t o r l l a s t l , I n p u t i terator2 f i rs t 2 , B i n aryPredicate pred) ;
Każda z funkcj i m i s ma t c h ( ) znajduje pierwszy element w zakresie [ f i r s t l , l a s t l ) , który nie jest zgodny z odpowiadającym mu elementem w zakresie rozpoczynającym się od i terato ra f i r s t 2 i zwraca parę zawierającą iteratory wskazujące te dwa niepasujące elementy. W przy padku nieznalezienia takiej niepasującej pary wartością zwracaną j est p a i r< 1 a s t 1 , f i r s t 2 + ( las t l f i r s t l ) >. Pierwsza wersj a funkcj i sprawdza niezgodność przy użyciu operatora ==. Druga wersj a porównuje elementy przy użyciu obiektu funkcyjnego predykatu dwuargumento wego p red. Elementy wskazywane przez iteratory i t 1 oraz i t 2 nie są zgodne, jeśli wywołanie pred ( * i t l , * i t 2 ) zwraca wartość f a l s e . -
1 201
Dodatek G Metody i funkcje z biblioteki STL -
equal ( ) temp l a t e < c l a s s I n pu t i t e ra t o r l , c l a s s I nput i t e r a t o r 2 > bool equ a l ( I np u t i t e r a t o r l f i r s t l , I np u t i teratorl l a s t l , I n pu t i te r a t o r 2 f i rs t 2 ) ; temp l a t e < c l a s s I nput i t e r a t o r l , c l a s s I nput i terator2 , c l a s s B i naryPredi cate> bool equ a l ( I np u t i tera t o r l f i r s t l , I np u t i teratorl l a s t l , I npu t i terator2 f i r s t 2 , B i naryPredicate pred) ;
Funkcja e qu a l ( l zwraca wartość t r u e , j eśli każdy element z zakresu [ f i r s t l , l a s t 1 ) jest zgodny z odpowiadaj ącym mu elementem z sekwencj i rozpoczynaj ącej się od iteratora f i r s t 2 , a w przeciwnym razie zwraca wartość f a l s e . Pierwsza wersj a funkcj i porównuj e elementy przy użyciu operatora = = d l a danego typu wartości. Druga wersja porównuj e elementy przy użyci u obiektu funkcyjnego predykatu dwuargumentowego p r e d. Elementy wskazywane przez iteratory i t l oraz i t 2 są zgodne, jeśli wywołanie p r e d ( * i t 1 , * i t 2 l zwraca war tość t r u e . search ( ) temp l a t e < c l a s s Forwardi terator l , c l a s s Forwarditerator2> Forwardi t e r a t o r l search ( Forwardi teratorl f i r s t l , Forward i te r a t o r l l a s t l , Forwardi terator2 f i r s t 2 , Forwardi t erator2 l a s t 2 ) ; temp l a t e < c l a s s Forward i t e ra t o r l , c l a s s Forward i terato r2 , c l a s s B 1 n a ryPredicate> Forwa r d i t e r a t o r l search ( Forwardi t e r a t o r l f i rs t l , Forward i teratorl l a s t l , Forwardi terator2 f i r s t 2 , Forward i terator2 l a s t 2 , B i n a ryPredicate pred) ;
Funkcj a s e a r c h ( ) znajduj e pierwsze wystąp i enie podsekwencj i w zakresie [ f i r s t 1 , l a s t l ) , która j est zgodna z odpowiadającą j ej sekwencj ą występującą w zakresie [ f i r s t 2 , l a s t 2 ) . W przypadku nieznalezienia elementu obydwie wersje zwracaj ą i terator l a s t l . Pierw sza wersja funkcj i porównuj e e lementy przy użyciu operatora == dla danego typu wartości. Dru ga wersja porównuj e elementy przy użyciu obiektu funkcyjnego predykatu dwuargumentowego p r e d . E l ementy w skazywane przez iteratory i t l oraz i t 2 są zgodne, j e ś l i wywołanie pred ( * i t l , * i t 2 ) zwraca wartość t r u e . search_n ( ) temp l a t e < c l a s s Forwardi tera t o r , c l a s s S i z e , c l a s s T> Forward i t e r a t o r search_ n ( Forwa rditerator f i r s t , Forwa r d i t e r a t o r l a s t , S i z e count , const T & va l ue ) ; temp l a t e < c l a s s Forward i t e r a t o r , c l a s s S i z e , c l a s s T , c l a s s B i naryPredicate> Forwardi t e r a t o r search_ n ( Forwardi terator f i r s t , Forwardi terator l a s t , S i z e cou n t , const T & v a l u e , B i n a ryPredicate pred) ;
1 202
Funkcje STL
Funkcj a s e a r c h _n
( ) z najduj e
pierwsze wystąpienie podsekwencj i w zakresie [ f i r s t l ,
l a s t l ) , która jest zg odn a z sekwencją składającą się z c o u n t kolejnych wy stąp i e ń wartości
va l ue. W p rzy pad k u nieznalezienia el ementu obydwie wersje zwracają iterator l a s t 1. Pierw sza wersja funkcj i porównuj e elementy przy użyciu op e rat ora dla danego typu wartości . Dru ga wersja porównuje elementy przy u ż yc i u obiektu funkcyjnego p re dykatu dwu a rgu m e nt o weg o p r e d . E l ementy wskazywan e p rzez iteratory i t l oraz i t 2 są z g odn e j e ś l i w y w oła n i e p r e d ( * i t l , * i t 2 ) zwraca w a rt oś ć t r u e . ==
.
Mutuiące operacie sekwencyine Mutujące o p e ra cj e sekwencyjne zebrano w tabel i G . 1 0. Nie pok a za n o ic h pa r a m e tró w a funkcj e przeciążone wys t ępuj ą tyl k o raz. I c h pe ł ni ej szy opis, wraz z p roto ty p a m i zam ieszczono pod ta belą. Tabela po zw a l a więc uzyskać ogó lne wyob rażeni e na temat działania danej funkcj i . a jeśli wydaje się ona int e resuj ąc a można zapoznać się ze szc z eg ół a mi później . ,
,
,
Tabela G. 1 O . Mutujące operacje sekwencyjne Funkcja
Opis
capy()
Kopiuje elementy z zakresu w miejsce wskazane przez iterator.
co py_backward ( )
Kopiuje elementy z zakresu w m iejsce wskazane przez iterator. Kopiowanie rozpoczy na się od końca zakresu i postępuje wstecz.
swap()
Zamienia m iejsca m i dwie wartości o kreślone p rzez referencje.
swap ranges( )
Zamienia m i ejscami odpowiadające sobie wartości z dwóch zakresów.
iter swap ()
Zam ienia m i ejscami dwie wartości wskazane przez iteratory.
transform()
Stosuje obiekt funkcyjny do każdego elementu w zakresie ( l u b do każde1 pary elementów w parze zakresów) i kopiuje wartość zwróconą w odpowiednie m iejsce d rugiego zakresu.
re place()
Zastępuje każde wystąpienie wartości w zakresie inną wartością.
re place_if()
Zastępuje każde wystąpienie wartości w zakresie inną wartością, jeśli obiekt f u n kcyj ny p redykatu zastosowany do wartości pierwotnej zwraca wartość true.
replace_copy()
Kopiuje jeden zakres do d ru g i ego i zastępuje każde wystąpienie podanej wartości inną wartością
replace_copy_if()
Kopiuje jeden zakres do drugiego i zastępuje każdą wartość, dla której obiekt f u n kcyj ny p redykatu wywołany z podaną wartością zwraca wartość true.
fili()
Ustawia każdą wartość w zakresie na podaną wartość.
lill_n()
Ustawia n kolejnych elementów na podaną wartość.
generale( )
U stawia każdą wartość w zakresie na wartość zwróconą p rzez generator, który jest obiektem fun kcyj nym niepobierającym żadnych arg u mentów.
generate_n()
Ustawia n pierwszych wartości w zakresie na wartość zwróco ną p rzez generator, który jest obiektem funkcyj nym niepobierającym żad nych arg u m entów.
1 203
Dodatek G - Metody i funkcje z biblioteki STL
Fu n kcja
Opis
re move()
Usuwa z zakresu wszystkie wystąpienia podanej wartości i zwraca iterato r wskazujący element występu1ący bezpośrednio za zakresem wyn ikowym.
remove_if()
Usuwa z zakresu wszystkie wystąpienia wartości. d la których obiekt predykatu zwraca wartość true i zwraca ite rator wskazujący element występ ujący bezpośred n i o za zakresem wyn ikowym.
remove_copy()
Kopi uje elementy z jednego zakresu do drugiego, pomijając elementy równe podanej wartości .
re move_copy_if()
Kopiuje elementy z jednego zakresu do d rugiego, pomijając eleme nty, d la których obiekt funkcyj ny predykatu zwraca wartość true.
unique()
Red u kuje każdą sekwencję co najmn iej dwóch elementów równowaznych w zakresie do pojedynczego elementu.
u n i q ue_copy()
Kopiuje elementy z jednego zakresu d o drugiego, red u kując każdą sekwencję co naj m niej dwóch eleme ntów równoważnych do pojedynczego elementu.
reverse()
Odwraca ko lej n ość elem entów w zakresie.
reve rse_copy()
Kopiuje jeden zakres do drugiego w odwrotnej kolej ności.
rotate( )
Traktuje zakres jako uporząd kowanie kołowe i wykonuje przesu nięcie cykliczne w lewo.
rotate_copy()
Kopiuje jeden zakres d o d rugiego w p rzes u niętej cykl icznie kolejności.
random_shuffle()
Losowo p rzestawia eleme nty w zakresie.
partition()
U m ieszcza wszystkie eleme nty s pełn iające obiekt funkcyj ny predykatu przed elementam i , które g o nie spełniają.
stable_partitio n ( )
U m i eszcza wszystkie elementy spełn iające obiekt funkcyjny p redykatu p rzed elementam i . które g o nie s pełnia1ą. Zachowane zostaje wzg lędne uporządkowanie ele mentów w każdej g rupie.
Przyjrzyj my się teraz bardziej szczegółowo powyższym mutuj ącym operacjom sekwencyj nym. Opis każdej funkcj i zawiera prototyp, po którym zamieszczono krótkie obj aśnienie. Jak j uż wi d z i e l iśmy wcześniej , pary iteratorów ok re ślaj ą zakresy. przy czym wybrana nazwa parametm szablonu okreś la typ iteratora. Jak zwykle zakres w postaci [ f i r s t , 1 a s t ) obej muje ele menty od pozycj i f i r s t aż do pozycj i l a s t . lecz z j ej wyłączeniem. Funkcje przekazywane jako argumenty są obiektami funkcyj nymi, które mogą być wskaźnikami lub obiektami. d l a któ ryc h zdefini owana jest operacj a ( ) . Jak już powiedziano w rozdziale 1 6. , predykat j est funkcją boolowską o j ednym argumencie, a predykat dwuargumentowy jest funkcj ą boolowską o dwóch argumentach (funkcj e t e nic muszą być typu b o o l pod warunkiem, że będą zwracać wartość zerową zamiast wartości fa 1 s e oraz wartość niezerową zamiast wartości t r u e ). Tak jak poka zano w rozdziale 1 6. jednoargumentowy obiekt funkcyjny to tak i, który pobiera jeden argument, a dwu argumentowy obiekt funkcyj ny to taki, który pobiera dwa argumenty. copy ( ) temp l a te < c l a s s I npu t i t e r a t o r , c l a s s O u t p u t : terator> Outpu t i t e r a t o r copy ( I np u t i te rator f i r s t , I n pu t i t e r a t o r l a s t , Outpu t i terator r e s u l t ) ;
1 204
Funkcje STl Funkcja copy ( ) kopiuj e elementy z zakresu [ f i r s t , l a s t ) do zakresu [ re s u l t , r e s u l t + ( l a s t - f i r s t ) ) . Zwraca wartość r e s u l t + ( l a s t - f i r s t ) , tj . iterator wskazujący pozycję bezpośrednio za ostatnim przekopiowanym elementem. Funkcja wymaga, żeby i tera tor r e s u 1 t nie wskazywał pozycj i m ieszczącej w zakresie [ f i r s t , 1 a s t ) - zakres docelowy nie może pokrywać się z zakresem źródłowym. copy_backward ( ) template Bidirectiona l l terator2 copy_bac kward ( B i directiona l i teratorl f i r s t , Bidirect i o n a l i teratorl l a s t , B i d i r e c t i o n a l l terator2 r e s u l t ) ;
Funkcj a c op y_b a c kw a r d ( ) kopiuj e elementy z zakresu [ f i r s t , l a s t ) do zakresu [ r e s u l t - ( l a s t - f i r s t ) , r e s u l t ) . Kopiowanie rozpoczyna się o d przekopiowania elementu z pozycj i l a s t - 1 na pozycj ę res ul t - 1 i postępuje wstecz aż do elementu f i r s t . Zwraca wartość r e s u l t ( last f i r s t ) , tj . iterator wskazujący pozycj ę bezpośrednio za ostatnim przekopiowanym elementem. Funkcj a wymaga, żeby iterator r e s u l t. nie wskazywał pozycj i mieszczącej w zakresie [ f i r s t , l a s t ) . Ponieważ jednak kopiowanie odbywa się wstecz, zakres docelowy i źródłowy mogą się pokrywać. swap ( ) templ ate void swap ( T & a , T & b ) ;
Funkcja swap ( ) zamienia miej scami wartości znajdujące się na pozycjach określonych przez referencje. swap_range s ( ) template < c l a s s Forwardi t e r a to r l , c l a s s Forwardl terator2> Forwa rd l t e r a t o r 2 swap_ ranges ( Forward i t e r a t o r l f i r s t l , Forwa rdi teratorl l a s t l , Forwardi terator2 f i r s t 2 ) ;
Funkcja s wap_ r a n g e s ( ) zamienia miej scami wartości z zakresu [ f i r s t l , l a s t l ) z od powiadającymi im wartościami w zakresie rozpoczynaj ącym się od pozycj i f i r s t 2 . Te dwa zakresy nie powin ny się pokrywać. i ter_swap ( ) templ ate < c l a s s Forwardi terator l , c l a s s Forwardi terator2> void i t e r_ swap ( Forwardl t e r a t o r l a , Forwardi terator2 b ) ;
Funkcja i t e r _ s wap ( ) zamienia miej scami wartości znajdujące się na dwóch pozycj ach okre ślonych przez iteratory. trans form ( ) templ a t e < c l a s s I n pu t i t e r a t o r , c l a s s Outpu t i te r a t o r , c l a s s Una ryOpe ra t i o n > Outpu t l t e r a t o r t r a n s form ( I n p u t l terator f i r s t , I np u t l terator l a s t , Outpu t l terator r e s u l t , Una ryOpe ration op ) ;
1 205
Dodatek G - Metody i funkcje z biblioteki STL
template< c l a s s I nput i t e rator l , c l a s s I n pu t i terator l , c l a s s Outpu t i te r a t o r , c l a s s B i n a r yOpera t 1 o n > Outp u t i terator t r a n s form ( I np u t i t e r a t o r l f i r s t l , I nput i teratorl l a s t l , I n p u t l terator2 f i r s t 2 , Outpu t l terator r e s u l t , B i na ryOpe r a t i o n b i n a r y_op ) ;
Pierwsza wersj a funkcj i t r a n s f o rm ( ) stosuj e j ednoargumentowy obiekt funkcyj ny op do każdego elementu w zakresie [ f i r s t , l a s t ) i przypisuje wartość zwróconą odpowiedniemu elementowi w zakresie rozpoczynaj ącym się od pozycj i r e s ul t, a zatem element * r e s u l t zostaje ustawiony na wartość op ( * f i r s t ) itd. Funkcj a zwraca wartość re s u l t + ( l a s t f i r s t ) , tj . iterator wskazujący pozycj ę bezpośrednio z a ostatnim elementem zakresu docelowego. Druga wersj a funkcj i t r a n s f o rm ( ) stosuj e dwuargumentowy obiekt funkcyjny op do każde go elementu w zakresie [ f i r s t ! , l a s t l ) i do każdego elementu w zakresie [ f i r s t 2 , l a s t 2 ) , a wartość zwróconą przypisuj e odpowiedniemu elementowi w zakresie rozpoczynają cym się od pozycj i r e s u l t - a zatem e lement * r e s u l t zostaj e ustawiony na wartość op ( * f i r s t 1 , * f i r s t 2 ) itd. Funkcja zwraca wartość r e s ul t + ( l a s t - f i r s t ) , tj . iterator wskazujący pozycję bezpośrednio za ostatni m elementem zakresu docelowego. repl ace ( ) temp l a t e < c l a s s Forward i t e r a t o r , c l a s s T> void rep l a c e ( Forwar d i t e r a t o r f i r s t , Forwa rditerator l a s t , c o n s t T & o l d_v a l u e , c o n s t T & new_va l ue ) ;
Funkcja rep l a c e ( ) zastępuj e każde wystąpienie wartości o l d_v a l u e w zakresie [ f i r s t , l a s t ) wartością n e w_va l u e . replace_i f ( ) temp l a te < c l a s s Forwa r d i terator , c l a s s Predi cate , c l a s s T> void rep l a c e _ i f ( Forward i t e r a t o r f i r s t , Forwarditerator l a s t , Predicate pred, c o n s t T & new_v a l ue ) ;
Funkcja rep l a ce_i f ( ) zastępuje wartością new_value każde wystąpienie wartości o l d_va lue w zakresie [ f i r s t , l a s t ) , dla którego wywołanie p r e d ( o l d ) zwraca wartość t r u e . rep l ace_copy ( ) temp l a t e < c l a s s I nput i te r a to r , c l a s s Outpu t i terato r , c l a s s T> Outpu t i te r a t o r replace _ copy ( I np u t i terator f i r s t , I nput i terator l a s t , Outpu t i te r a t o r resu l t , c o n s t T & o l d_value , const T & new_va l ue ) ;
Funkcj a r e p l a c e _ c o p y ( ) kopiuj e e lementy z zakresu [ f i r s t , l a s t ) do zakresu rozpoczynaj ącego się od pozycj i r e s u l t, zastępując każde wystąpienie wartości o l d v a l u e _ wartością ne w_va l u e . Zwraca wartość r e s u l t + ( l a s t - f i r s t ) , tj . iterator wskazujący pozycję bezpośrednio za ostatnim elementem zakresu docelowego. replace_copy_i f ( ) temp l a t e< c l a s s I te r a t o r , c l a s s Output i terator , c l a s s Predi ca t e , c l a s s T>
1 206
Funkcje STL Outpu t i terator rep l a ce_copy_i f ( I te r a t o r f i r s t , I terator l a s t , Outpu t i terator resu l t , Predi cate pred, const T& new_v a l ue ) ;
Funkcja rep l a c e copy_i f ( ) kopiuje e lementy z zakresu [ f i r s t , l a s t ) do zakresu rozpoczynającego się od pozycji r e s u l t , zastępując wartością n e w_va l u e każdą wartość o l d, dla której wywołanie p r e d ( o l d ) zwraca t ru e . Zwraca wartość r e s u l t + ( l a s t f i r s t ) , tj . iterator wskazujący pozycj ę bezpośrednio za ostatnim elementem zakresu docelo wego. fill ( ) template< c l a s s Forwardi tera t o r , c l a s s T > void f i l l ( Forward i terator f i r s t , Forwardi terator l a s t , c o n s t T & va l ue ) ;
Funkcja f i l ! ( ) przypisuje każdemu elementowi w zakresie [ f i rs t ,
l a s t ) wartość va lue.
fill_n ( ) template void f i l l_n ( Outpu t i t e r a t o r f i r s t , S i z e n , con s t T & v a l ue ) ;
Funkcja f i l l_n ( ) przypisuj e wartość v a l u e każdemu z n pierwszych elementów począwszy od pozycj i f i r s t . generate ( ) template< c l a s s Forward i terator , c l a s s Generator> void generate ( Forwa r d i t e r a t o r f i r s t , Forwa rditerator l a s t , Generator ge n ) ;
Funkcja gene r a t e ( ) przypi suje każdemu elementowi w zakresie [ f i r s t , l a s t ) wartość zwróconą przez gen ( ) , gdzie gen to obiekt funkcyj ny generatora, tj . taki, który nie pobiera żadnych argumentów. Parametr g e n może być na przykład wskaźnikiem na funkcj ę r a n d ( ) . generate_n ( ) template< c l a s s Outpu t i terator , c l a s s S i z e , c l a s s Genera tor> void generate_ n ( Output i te r a t o r f i r s t , Size n , Generator gen ) ;
Funkcja g e n e r a te _n ( ) przypisuj e każdemu z n pierwszych elementów, począwszy od pozy cj i f i r s t , wartość zwróconą przez g e n ( ) , gdzie gen to obiekt funkcyjny generatora, tj . taki , który nie pobiera żadnych argumentów. Parametr gen może być na przykład wskaźnikiem na funkcję rand ( ) . remove ( ) templ ate Forwarditerator remove ( Forward i terator f i r s t , Forwarditerator l a s t , con s t T& v a l ue ) ;
Funkcja remove ( ) usuwa z zakresu [ f i r s t , l a s t ) wszystkie wystąpienia wartości va l u e i zwraca iterator wskazujący pozycję bezpośrednio z a ostatnim elementem zakresu wynikowego. Funkcja jest stabilna, co oznacza, że porządek n ieusuniętych elementów pozostaje niezmieniony.
1 207
Dodatek G Metody i funkcje z biblioteki STL -
Uwaga Poni eważ różne f u n kcje r e m o v e ( l o raz u n i q u e ( l nie są f u n kcjam i s kładowy m i , a po nadto n i e są ograniczone d o konte n e rów ST L , n i e mogą o n e modyfi kować rozm iaru konte n e ra . Zam i ast teg o zwracają iterato r o kreśl ający nową pozycję bezpośre d n i o za ostatn i m elementem konte n e ra . Zazwyczaj u suwane e l e m e nty s ą po prostu p rzesuwane na koniec konten e ra. W p rzypad ku kontenerów S T L m ożemy użyć zwróc onego iteratora i j e d n ej z metod e r a se ( ) do właś c i w e g o u stawie n i a pozycji end ( J . remove i f ( ) _ t emp l a t e < c l a s s F o r w a rd i t e r a t o r ,
c l a s s Predi c a t e >
Forwa r d i t e r a t o r remove _ i f ( F o r wa r d i t e r a t o r f i r s t ,
Forwa r d i t e r a t o r l a s t ,
Pred i c a t e p r e d ) ;
Funkcja r e m o v e i f ( ) usuwa z zakresu [ f i r s t , l a s t ) wszystkie wystąp ienia wartości va l , dla których wywołanie p r e d ( va l ) zwraca t r u e i zwraca iterator wskazujący pozycj ę bezpośrednio za ostatnim elementem zakresu wynikowego. Funkcja jest stabi lna, co oznacza . że porządek nieusu n i ętych el ementów pozostaje n iezm ieniony. remove_c apy ( ) t e mp l a t e < c l a s s I np u t i t e r a t o r ,
c l a s s O u t pu t i t e r a t o r ,
Outpu t i t e r a t o r remove_cop y ( I npu t i t e r a t o r f i r s t ,
c l a s s T>
I np u t i t e r a t o r l a s t ,
Outpu t i terator resu l t ,
c o n s t T & v a l ue ) ;
Funkcj a r e m o v e c o p y ( ) kopiuje wartości z zakresu [ f i r s t , l a s t ) do zakresu rozpoczy nającego się od pozycj i r e s u l t , pom ij aj ąc wystąpienia wartości va 1 u e . Zwraca iterator wska zuj ący pozycj ę bezpośrednio za ostatnim elementem zakresu wynikowego . Funkcj a j est stabi lna, co oznacza, że porządek nieusuni ętych el ementów pozostaje niezmieni ony. remove_copy_i f ( ) t emp l a t e < c l a s s I n p u t i t e r a t o r ,
c l a s s O u t pu t i t e r a t o r , c l a s s P r ed i c a t e >
O u t pu t i t e r a t o r r e move c o p y_ i f ( I n pu t i t e r a t o r f i r s t , I np u t i t e r a t o r l a s t , _ O u t pu t i t e r a t o r r e s u l t , Predi c a t e p r e d ) ;
Fun kcj a r e m o v e c o p y i f ( ) kopiuje wartości z zakresu [ f i r s t , l a s t ) do zakresu roz poczynaj ącego się od pozycj i r e s u l t, pomij aj ąc wystąpienia wartości va l , dla których wywo łanie p r e d ( v a l ) zwraca wartość t r u e . Zwraca iterator wskazujący pozycję bezpośrednio za ostatn im elementem zakresu wyni kowego. Funkcj a j est stabi lna, co oznacza, że porządek nie usuniętych e l ementów pozostaj e niezmieniony. uni que ( ) temp l a t e < c l a s s Forwa r d i t e r a t o r > Forwa r d i t e r a t o r u n i qu e ( Forwa r d i t e r a t o r f i r s t ,
temp l a t e < c l a s s F o r wa r d i t er a to r ,
c l a s s B i n a ryPredicate>
Forwa r d i t e r a t o r u n ique ( Forwa r d i t e ra t o r f i r s t , B i naryPredicate p r e d ) ;
1 208
Forwa r d i te r a t o r l a s t ) ;
Forw a r d i t e ra t o r l a s t ,
Funkcje STL Funkcja u n i qu e ( ) redukuje każdą sekwencj ę co najmniej dwóch e lementów równoważnych w zakresie [ f i r s t , l a s t ) d o poj edynczego elementu i zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem nowego zakresu. Pierwsza wersja funkcj i porównuj e ele menty przy użyciu operatora == dla danego typu wartości. Druga wersja porównuje elementy przy użyciu obiektu funkcyj nego predykatu dwuargumentowego p red. Elementy wskazywane przez iteratory i t l oraz i t2 są zgodne, jeśli wywołanie p r e d ( * i t l , * i t 2 ) zwraca war tość t ru e . unique_copy ( ) template Outpu t i te r a t o r u n i que_ copy ( I npu t i terator f i r s t , I n p u t i terator l a s t , Outpu t i terator r e s u l t ) : template < c l a s s I n p u t i t e ra t o r , c l a s s Outpu t i t e rator , c l a s s B i n a ryPredicate> Outpu t i terator u n i que_copy ( I np u t i terator f i r s t , I n p u t i terator l a s t , Outpu t i t e rator resu l t , B i naryPredicate pred) ;
Funkcj a u n i qe_c o p y ( ) kopiuj e elementy z zakresu [ f i r s t , l a s t ) do zakresu rozpoczy nającego się od pozycj i r e s u l t, redukuj ąc każdą sekwencj ę co najmniej dwóch i dentycznych elementów do pojedynczego elementu. Zwraca iterator wskazujący pozycję bezpośrednio za ostat nim elementem nowego zakresu. Pierwsza wersj a funkcj i porównuje elementy przy użyciu ope ratora == dla danego typu wartości. Druga wersja porównuje elementy przy użyciu obiektu funk cyjnego predykatu dwuargumentowego p red. Elementy wskazywane przez iteratory i t l oraz i t2 są zgodne, jeśli wywołanie p r e d ( * i t 1 , * i t 2 ) zwraca wartość t r u e . reverse ( ) template< c l a s s B i d i rec t i o na l i terator> void rever s e ( B i d i r e c t i o na l i terator f i r s t , Bidi rect i o n a l i terator l a s t ) :
Funkcja reve r s e ( ) odwraca elementy w zakresie [ f i r s t , cj i swap ( f i r s t , l a s t 1 ) itd.
l a s t ) przez wywołanie funk
-
reverse_c opy ( ) template < c l a s s B i d i r e c t i o na l i terato r , c l a s s Outpu t i terator> Outpu t i terator reve r s e_cop y ( B i di r e c t i o n a l i terator f i r s t , B i direct i ona l i terator l a s t , Output i t erator r e s u l t ) ;
Funkcj a r eve r s e_ c o p y ( ) kopiuj e elementy z zakresu [ f i r s t , l a s t ) do zakresu rozpo czynaj ącego się od pozycj i r e s u l t w odwrotnej kolej ności. Te dwa zakresy nie powinny się pokrywać. ro ta te ( ) template < c l a s s Forward i te r a t o r > void rotate ( Forwa r d i t e r a t o r f i r s t , Forwa r d i t e rator m i ddle , Forwa r d i t e r a t o r l a st ) ;
1 209
Dodatek G Metody i funkcje z biblioteki STL -
Funkcja r o t a t e ( ) wykonuje przesunięcie cykl iczne w lewo elementów w zakresie [ f i r s t , l a s t ) . Element z pozycj i m i dd l e zostaje przesunięty na pozycję f i r s t , element z pozycj i midd l e + 1 zostaje przesunięty n a pozycję f i r s t + 1 itd. Elementy poprzedzające pozycję midd l e zostaj ą przemieszczone na koniec kontenera w taki sposób, że element z pozycji f i r s t występuj e bezpośrednio p o elemencie znajdującym się poprzednio n a pozycj i l a s t 1. -
ro ta te_c opy ( ) temp l a te< c l a s s Forward i te r a t o r , c l a s s Outpu t i terator> Outp u t i t e r a t o r rotate_copy ( Forwardi terator f i r s t , Forwa r d i t e r a t o r middl e , Forwardi terator l a s t , Outpu t i terator r e s u l t ) ;
Funkcja r o t a t e c o p y ( ) kopiuj e elementy z zakresu [ f i r s t , l a s t ) do zakresu rozpo czynającego się od pozycj i r e s ul t w przesuniętej cyklicznie kolej ności omówionej w opisie funkcj i r o t a te ( ) . random_ shu f f l e ( ) temp l a t e < c l a s s Ra ndomAcce s s i terator> void random_shu f f l e ( Ra n domAcce s s i terator f i r s t , RandomAcce s s i terator l a s t ) ; temp l a t e < c l a s s Ra ndomAcce s s i te r a t o r , c l a s s RandomNumberGenerator> void r a ndom_shu f f l e ( RandomAc c e s s i terator f i rs t , RandomAc ce s s i terator l a s t , RandomNumb e rGene r a to r & random ) ;
Funkcj a r a n dom_ s h u f f l e ( ) tasuj e elementy w zakresie [ f i r s t , l a s t ) . W przypadku pierwszej wersj i funkcj i rozkład j est równomierny, tj . każda możliwa permutacj a pierwotnej kolej ności j est równie prawdopodobna. W przypadku drugiej wersj i rozkład określa obiekt funk cyjny r a n dom. Przy danych n elementach wyrażenie r a n dom ( n ) powinno zwracać wartość z przedziału [ O , n ) . parti tion ( ) temp l a t e < c l a s s B i d i r e c t i on a l i t e r a t o r , c l a s s Predi cate> Bidirect i o n a l i t e r a t o r part i t i o n ( B i d i r e c t i ona l i terator f i r s t , B i d i r e c t i o n a l i terator l a s t , Predi cate pred) ;
Funkcj a p a r t i t i o n ( ) umi eszcza każdy element o wartości v a l takiej , że wywołanie pred ( va l ) zwraca wartość t r u e przed wszystkimi elementami niespełni aj ącymi tego warun ku. Zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem o wartości, dla której obiekt funkcyj ny predykatu zwraca wartość t ru e . s table_par ti tion ( ) temp l a t e< c l a s s B i d i r e c t i o n a l i t e r a t o r , c l a s s Predi cate> Bidirect i o n a l i t e r a t o r s t a b l e_part i t i o n ( B i d i r e c t i o n a l i terator f i r s t , Bidirect i o na l i t e rator l a s t , Predi cate pred ) ;
Funkcja s t ab l e_pa r t i t i on ( ) umieszcza każdy element o wartości va l takiej , że wywoła nie p r e d ( va l ) zwraca wartość t r u e przed wszystkimi e lementami n iespełniaj ącymi tego 1210
Funkcje STL warunku. Funkcj a zachowuj e względne uporządkowanie elementów wewnątrz każdej z dwóch grup. Zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem o wartości , dla której obiekt funkcyj ny predykatu zwraca wartość t ru e .
Operacie sortowania i pokrewne Operacj e sortowania i pokrewne zebrano w tabeli G . 1 1 . Nie pokazano ich parametrów, a funkcje przeciążone występuj ą tylko raz. Każda z funkcj i występuje w wersj i porządkującej elementy przy użyciu operatora < oraz w wersj i porządkuj ącej elementy przy użyc iu obiekt funkcyj nego porównania. Pełniej szy opis funkcj i , wraz z prototypami , zamieszczono pod tabe lą. Tabela po zwala więc uzyskać ogólne wyobrażenie na temat działania danej funkcj i , a jeśli wydaje się ona interesująca, można zapoznać się ze szczegółami nieco dalej .
Tabela G. 1 1 . Operacje sorto wania i pokrewne Funkcja
Opis
sort()
Sortuje zakres.
stable_sort()
Sortuje zakres z zachowaniem względnej kolejności elementów równoważnych.
partial_sort()
Częściowo sortuje zakres, zapewniając n pierwszych elementów pełnego sortowania.
partial_sort_copy()
Kopiuje częściowo posortowany zakres do drugiego zakresu.
nth_element()
Dla danego iteratora wskazującego pewną pozycję wewnątrz zakresu znajduje element, który znajdowałby się na tej pozycji, gdyby zakres był posortowany, i u m ieszcza tam ten element.
lower_bound()
Dla danej wartości znajduje pierwszą pozycję w posortowanym zakresie, przed którą można u m ieścić daną wartość z zachowaniem uporządkowania elementów.
upper_bound()
Dla danej wartości znajduje ostatnią pozycję w posortowanym zakresie, przed którą można u m ieścić daną wartość z zachowaniem uporządkowania elementów.
equal_range()
Dla danej wartości znajduje maksymalny podzakres posortowanego zakresu taki, że umieszczenie danej wartości przed dowolnym elementem tego podzakresu nie naruszy uporząd kowania elementów.
binary_search() merge()
Zwraca wartość true, jeśli posortowany zakres zawiera wartość równoważną podanej.
W przeciwnym razie zwraca wartość false.
Scala dwa posortowane zakresy w trzeci.
inplace_merge()
Scala ze sobą dwa kolejne posortowane zakresy.
includes()
Zwraca wartość true, jeśli każdy element jednego zbioru znajduje się także w drugim zbiorze.
set_union()
Tworzy sumę dwóch zbiorów, czyli zbiór zawierający wszystkie elementy występujące w każdym ze zbiorów.
set_intersection()
Tworzy przecięcie dwóch zbiorów, czyli zbiór zawierający tyl ko te elementy, które występują jednocześnie w obydwu zbiorach.
set_difference()
Tworzy różnicę dwóch zbiorów, czyli zbiór zawierający tylko te elementy, które występują pierwszym zbiorze, lecz nie występują w drugim.
1 21 1
Dodatek G - Metody i funkcje z biblioteki STL
Funkcja
Opis
set_symmetric_difference()
Tworzy zbiór zawierający tyl ko t e elementy, które występują a l b o w jed nym zbiorze. albo w drugim, lecz nie w o bydwu naraz.
make_heap ( )
Przekształca zakres w stóg.
pu sh_hea p ( )
Dodaje e l e m e n t do stog u .
pop_heap ( )
Zdejmuje największy element z e stogu.
s o rt_ h ea p( )
Sortuje stó g .
min()
Zwraca m n i ej s zą z dwóch wartości.
max()
Zwraca wię kszą z dwóch wartości.
min_element()
Znaj d uje pie rwsze wystąpienie naj m n i ejszej wartości w zakresie.
max_e lement()
Znajduje pie rwsze wystąpienie najwię kszej wartości w zakresie.
lexicog raphic_compare()
Porów nuje l e ksykog raficznie dwie sekwencje 1 zwraca wartość true, jeśl i pierwsza sekwen cja iest w leksykog rafi cznym sensie mniejsza od drugiej. W przeciwnym razie zwraca wartość fal se.
n e xt_permu ta t ion (
)
previou s_perm utation()
Generuje następną perm utację sekwencji. Gene ruje pop rzed nią perm utację se kw encji .
Funkcj e omawiane w tym punkcie ustalaj ą kolej ność dwóch elementów przy użyc iu zdefiniowa nego dla tych el ementów operatora < lub przy użyciu obiektu porównania określanego typem szablonowym C o mp a r e . Jeś l i c o mp jest obiektem typu Comp a r e , to wyrażenie c omp ( a , b ) stanowi uogólnienie wyrażenia a < b i zwraca wartość t r u e , jeśli wartość a poprzedza war tość b zgodnie z przyjętym schematem uporządkowania. Jeśli wyrażenie a < b zwraca wartość fa 1 s e i b < a także zwraca wartość fa 1 s e , oznacza to, że wartości a i b są równoważne. Obiekt porównania musi zapewniać przynajmniej tzw. ścisłe uporządkowanie słabe (ang. strict weak ordering). Oznacza to, że: • wyrażenie c omp ( a , a ) musi zwracać wartość f a l s e , co stanowi uogólnienie faktu, że dana wartość nie może być mniej sza od siebie samej ( to jest wymaganie ścisłe); • jeśli wyrażenie c o mp ( a , b ) ma wartość t r u e i wyrażenie comp ( b , c ) ma wartość t r u e , to wyrażen ie c o m p ( a , c ) także ma wartość t r u e (tj . porównanie jest re lacją przechodnią); • jeśli wartość a jest równoważna wartości b , a wartość b jest równoważna wartości c , to wartość a jest równoważna wartości c (tj . równoważność jest relacją przechodn i ą) . Jeśli operator < zastosujemy d o wartości całkowitych, wówczas równoważność oznacza równość - taka zależność nie musi j ednak obowi ązywać w bardziej ogólnych przypadkach . Możemy na przykład zdefini ować strukturę o ki lku składowych opi sującą adres pocztowy i zdefiniować obiekt porównania c omp porządkujący struktury według kodu pocztowego . W taki m przypadku każde dwa adresy o tym samym kodzi e pocztowym będą równoważne, lecz nie równe. Przyj rzyjmy się teraz bardziej szczegółowo operacjom sortowania i pokrewnym. Opis każdej funk cj i zawiera prototyp, po którym zamieszczono krótkie obj aśnienie. Niniejszy punkt podzielono na kilka podpunktów. Jak już widzieliśmy wcześniej , pary iteratorów określają zakresy, przy czym
1 21 2
Funkcje STL wybrana nazwa parametru szablonu określa typ iteratora. Jak zwykle zakres w postaci [ f i r s t , la s t ) obejmuje elementy od pozycj i f i r s t aż do pozycj i l a s t , lecz z j ej wyłączeniem. Funkcje przekazywane jako argumenty są obiektami funkcyjnymi, które mogą być wskaźnikami lub obiek tami, dla których zdefiniowana j est operacja ( ) . Jak już powiedziano w rozdziale 1 6„ predykat jest funkcją boolowską o j ednym argumencie, a predykat dwuargumentowy jest funkcją boolowską o dwóch argumentach (funkcj e te nie muszą być typu b o o l pod warunkiem, że będą zwracać wartość zerową zamiast wartości f a l s e oraz wartość niezerową zamiast wartości t rue ) . Tak j ak w rozdziale 1 6„ jednoargumentowy obiekt funkcyjny to taki, który pobiera jeden argument, a dwu argumentowy obiekt funkcyjny to taki, który pobiera dwa argumenty.
Sortowanie Najpierw zajmiemy się algorytmami sortowania. sort ( ) template< c l a s s RandomAcce s s i te r a t o r > v o i d sort ( Ra ndomAc c e s s i t e r a t o r f i r s t , RandomAcce s s i terator l a s t ) ;
templ a t e void sort ( Ra n domAcce s s i t e r a t o r f i r s t , RandomAcce s s i terator l a s t , Compare comp ) ;
Funkcja s o r t ( ) sortuje elementy w zakresie [ f i r s t , l a s t ) w porządku rosnącym. Pierw sza wersja funkcj i ustala kolej ność elementów przy użyciu operatora RandomAc ce s s i te r a t o r part i a l_s o r t_cop y ( I np u t ! terator f i r s t , I n p u t i terator l a s t , RandomAcce s s i terator re s u l t_f i r s t , RandomAcce s s ! terator r e s u l t_l a s t , Compare comp ) ;
Funkcj a p a r t i a l _ s o r t _ c o p y ( ) kopiuj e pierwszych n elementów posortowanego zakresu [ f i r s t , l a s t ) do zakresu [ re s u l t_f i r s t , r e s u l t_f i r s t + n ) . Wartość n jest równa mniej szej z wartości l a s t f i r s t oraz r e s u l t l a s t resul t first. Funkcj a zwraca iterator r e s u l t f i r s t + n . Pierwsza wersj a funkcj i ustala kolej ność elementów przy użyciu operatora void nth_e l eme n t ( RandomAcce s s ! terator f i rs t , RandomAcces s i t e rator nth, RandomAcce s s i terator l a s t ) ; temp l a t e < c l a s s RandomAcce s s i terator , c l a s s Compare> void nth_e l eme n t ( Ra ndomAc ce s s i terator f i r s t , RandomAcce s s i terator nth , Ran domAcce s s i terator l a s t , Compare comp ) ;
Funkcja n t h _ e l eme n t ( ) znajduje w zakresie [ f i r s t , l a s t ) element, który znajdował by się na pozycj i n t h , gdyby zakres był posortowany, i umieszcza ten element na pozycj i n t h . Pierwsza wersja funkcj i ustala kolejność elementów przy użyciu operatora < , natomiast druga używa do tego celu obiektu porównania c omp.
1214
Funkcje STL
Wyszukiwanie binarne Algorytmy należące do grupy wyszukiwania b inarnego zakładają, że zakres j est posortowany. Algorytmy te wymagają j edynie iteratora postępuj ącego, lecz działają najwydajniej dla iterato rów dostępu swobodnego. lower_bound ( ) template < c l a s s Forward i tera t o r , c l a s s T> Forwardi terator lower_bou nd ( Forwardi terator f i r s t , Forwardi terator l a s t , c o n s t T & value ) ; templ ate < c l a s s Forwardi terato r , c l a s s T , c l a s s Compare> Forwardi terator l ower_bound ( Forwardi terator f i rs t , Forward i t e rator l a s t , c o n s t T & v a l u e , Compa re comp ) ;
Funkcja !ower _ bo und ( ) znajduje w posortowanym zakresie [ f i r s t , l a s t ) pierwszą po zycję, przed którą można wstawić wartość v a l u e bez naruszania porządku sortowania. Funkcj a zwraca iterator wskazujący na t ę pozycję. Pierwsza wersja funkcj i ustala kolej ność elementów przy użyciu operatora
b o o l b 1 n a r y _ s e a r c h ( F o r wa r d i t e r a t o r f i r s t ,
Forwa r d i t e r a t o r l a s t ,
c o n s t T & va l u e , Comp a r e c omp ) ;
Funkcj a b i n a r y _ s e a r c h ( ) zwraca wartość t r u e , jeśli w posortowanym zakresie [ f i r s t , l a s t ) występuj e wartość równoważna wartości v a l u e . W przeciwnym razie funkcj a zwraca wartość f a l s e . P ierwsza wersj a funkcj i ustala kolej ność elementów przy użyc iu operatora < , natomiast druga używa d o tego celu obiektu porównania c amp.
Uwaga Przypomnijmy, że jeśli porządkowanie odbywa się przy użyciu operatora
I np u t i te r a t o r l l a s t l ,
I np u t i t e r a t o r 2 f i r s t 2 ,
I npu t i t e r a t o r 2 l a s t 2 ,
O u t pu t i t e r a t o r r e s u l t ) ;
emp l a t e < c l a s s I np u t i t e r a to r l ,
c l a s s I n pu t i t e r a t or 2 ,
c l a s s O u t p u t i te r a t o r ,
c l a s s Compa r e > � � t pu t i t e r a t o r m e r g e ( I np u t i t e r a t o r l f i r s t l ,
I np u t i t e r a t o r l l a s t l ,
I np u t i t e r a t o r 2 f i r s t 2 ,
I n p u t i t e ra t o r 2 l a s t 2 ,
O u t pu t i t e r a t o r r e s u l t ,
C omp a r e comp ) ;
Funkcja me r g e ( ) scal a elementy z posortowanego zakresu [ f i r s t 1 , 1 a s t 1 ) oraz z posor towanego zakresu [ f i r s t 2 , 1 a s t 2 ) i umi eszcza je w zakresie rozpoczynaj ącym się od 1216
Funkcje STL
pozycj i r e s u l t . Zakres docelowy nie powinien pokrywać się z żadnym ze scalanych zakresów. W przypadku, gdy w obydwu zakresach występują elementy równoważne, elementy z p ierwsze go zakresu poprzedzają elementy z drugiego. Wartością zwracaną j est iterator wskazujący pozy cję bezpośrednio za ostatnim elementem zakresu wynikowego. Pierwsza wersj a funkcj i ustala kolejność elementów przy użyciu operatora void i n p l a ce_me rge ( B idi r e c t i o n a l i t e r a t o r f i r s t , B i d i r e c t i o n a l i t erator middl e , B i d i r e c t i o n a l i te r a t o r l a s t , Compa re comp ) ;
Funkcja i n p l a c e _m e r g e ( ) scala dwa następujące po sobie posortowane zakresy - [ f i r s t , l a s t ) - w poj edynczą posortowaną sekwencj ę umieszczoną w zakresie [ f i r s t , 1 a s t ) . Elementy z pierwszego zakresu poprzedzają równoważne ele menty z drugiego. Pierwsza wersj a funkcj i ustala kolej ność elementów przy użyc iu operatora < , natomiast druga używa do tego celu obiektu porównania c omp. middl e ) oraz [ m i dd l e ,
Operacie na zbiorach Operacje zbiorowe można wykonywać na wszystkich posortowanych sekwencjach, w tym kontene rach s e t oraz m u l t i s e t . W przypadku kontenerów przechowujących więcej niż jedno wystąpienie danej wartości, takich jak mu 1 t i s e t, definicje są uogólnione. Suma dwóch wielozbiorów zawiera większą liczbę wystąpień każdego elementu. a iloczyn zawiera mniej szą liczbę wystąpień każdego elementu. Przypuśćmy na przykład, że wielozbiór A zawiera łańcuch j ab l ko " siedmiokrotnie. a wielozbiór B zawiera ten sam łańcuch czterokrotnie. W takim przypadku suma A i B będzie zawie rać siedem wystąpień łańcucha j ab l ko " , a ich przecięcie - cztery wystąpienia tego łańcucha. „
„
include s ( ) template b o o l i n c l u de s ( I n p u t i t e r a t o r l f i rs t l , I np u t i t e r a t o r l l a s t l , I n p u t i t e r a t o r 2 f i rs t 2 , Inpu t i terator2 l a s t 2 ) ; temp l a t e < c l a s s I npu t i t e r a t o r l , c l a s s I np u t i terator2 , c l a s s Compa re> boo l i n c l udes ( I n pu t i te r a t o r l f i r s t l , I npu t i t e r a t o r l l a s t l , I n p u t i t e r a t o r 2 f i r s t 2 , I np u t i terator2 l a s t 2 , Compare comp ) ;
Funkcj a i n c l u d e s ( ) zwraca wartość t ru e , jeśli każdy element z zakresu [ f i r s t 2 , l a s t 2 ) występuje także w zakresie [ f i r s t l , l a s t l ) . W przeciwnym razie funkcja zwraca wartość f a l s e . Pierwsza wersja funkcj i ustala kolej ność elementów przy użyciu operatora Outpu t i terator s e t_u n i o n ( I np u t i teratorl f i r s t l , I n p u t i t e r a t o r l l a s t l , I np u t i terator2 f i r s t 2 , I nput i terator2 l a s t 2 , Outputi terator resu l t ) ; templ ate Outpu t i t e r a t o r s e t_u n 1 o n ( I nput i t e r a t o r l f i rs t l , I n p u t l teratorl l a s t l , I np u t i terator2 f i r s t 2 , I nput i terator2 l a s t 2 , Output i terator resu l t , Compare comp ) ;
Funkcja s e t un i on ( ) tworzy zbiór, który jest sumą zakresów [ f i r s t 1 , l a s t 1 ) oraz [ f i r s t 2 , 1 a s t 2 ) i umieszcza go w miej scu wskazywanym przez i tera tor r e s u 1 t . Zakres wynikowy nie powinien się pokrywać z żadnym z zakresów p ierwotnych. Funkcj a zwraca itera tor wskazujący pozycję bezpośrednio za ostatnim elementem nowo utworzonego zakresu. Suma jest zbiorem, który zawiera wszystkie e lementy występujące w j ednym lub obydwu zbiorach. Pierwsza wersja funkcj i ustala kolejność elementów przy użyciu operatora Outpu t i t e r a t o r s e t_i n te r s e c t i o n ( I np u t i teratorl f i r s t l , I n pu t l teratorl l a s t l , I np u t i terator2 f i r s t 2 , I n p u t i terator2 l a s t 2 , Outpu t l terator resu l t ) ; temp l a t e< c l a s s I n p u t i t e r a t or l , c l a s s Inpu t i terator2 , c l a s s Outpu t i te r a t o r , c l a s s C ompare> Outpu t i t e r a t o r s e t_i n t e r s e c t i o n ( I np u t i tera t o r l f i r s t l , I np u t i teratorl l a s t l , I n p u t i terator2 f i rs t 2 , I nput i t e rator2 l a s t 2 , Output i t e ra t o r resu l t , Compare comp ) ;
Funkcj a s e t i n t e r s e c t i on ( ) tworzy zbiór, który j est przecięciem zakresów [ f i r s t l , l a s t l ) oraz [ f i r s t 2 , l a s t 2 ) i kopiuj e go w miej sce wskazywane przez iterator re s u l t . Zakres wynikowy nie powinien się pokrywać z żadnym z zakresów pierwotnych. Funkcj a zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem nowo utworzonego zakresu. Przecięcie j est zbiorem, który zawiera elementy wspólne dla obydwu zbiorów. Pierwsza wersja funkcji ustala kolej ność elementów przy użyciu operatora < , natomiast druga używa do tego celu obiektu porównania c omp. set_di fference ( ) temp l a t e< c l a s s I np u t i t e r a t o r l , c l a s s I n p u t l t e r a t or2 , c l a s s Outpu t i terator> Outpu t l te r a t o r s e t_di f fe rence ( I np u t i teratorl f i r s t l , I n p u t l teratorl l a s t l , I np u t l terator2 f i r s t 2 , I npu t i terator2 l a s t 2 , Outpu t i terator resu l t ) ;
1218
Funkcje STL
templ ate < c l a s s I np u t i te r a t o r l , c l a s s I n pu t i terator2 , c l a s s Outpu t i tera t o r , c l a s s Compare> Outpu t i te r a t o r s e t_di f f e rence ( I np u t i teratorl f i r s t l , I n p u t i teratorl l a s t l , I np u t i terator2 f i r s t 2 , I np u t i terator2 l a s t 2 , Outpu t i terator resu l t , Compare camp ) ;
Funkcja s e t d i f f e r e n c e ( ) tworzy zbiór, który jest różnicą zakresu [ f i r s t l , l a s t l ) oraz zakresu [ f i r s t 2 , l a s t 2 ) i kopiuj e go w miej sce wskazywane przez iterator r e s u l t . Zakres wynikowy n i e powinien się pokrywać z żadnym z zakresów pierwotnych. Funkcj a zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem nowo utworzonego zakresu . Różnica jest zbiorem, który zawiera elementy występujące w pierwszym zbiorze, lecz niewystę pujące w drugim. P ierwsza wersj a funkcj i ustala kolejność elementów przy użyciu operatora < . natomiast druga używa d o tego celu obiektu porównania c omp. set_symme t r i c_di fference ( ) templ ate < c l a s s I n pu t i t e r a t o r l , c l a s s I npu t i terator2 , c l a s s Outpu t i terator> Outpu t i t e r a t o r s e t _ s ymme t r i c_di f ference ( I nput i t e r a t o r l f i r s t l , I n p u t i teratorl la s t l , I np u t i terator2 f i r s t 2 , I n p u t i terator2 l a s t 2 , Outpu t i terator r e s u l t ) ; temp l a t e < c l a s s I np u t i terato r l , c l a s s I np u t i terato r 2 , c l a s s Outpu t i terato r , c l a s s Compare> Outpu t i t e r a t o r s e t_symme t r i c _d i f ference ( I nput i t e r a t o r l f i r s t l , I n p u t i teratorl las t l , I np u t i terator2 f i r s t 2 , I n p u t i terator2 l a s t 2 , Outpu t i terator resu l t , Compare camp ) ;
Funkcja s e t _ s ymme t r i c_di f f e r e n c e ( ) tworzy zbiór, który jest różnicą symetryczną za kresu [ f i r s t 1 , l a s t 1 ) oraz zakresu [ f i rs t 2 , l a s t 2 ) i kopiuje go w miejsce wskazy wane przez iterator r e s u l t. Zakres wynikowy nie powinien się pokrywać z żadnym z zakresów pierwotnych. Funkcj a zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem nowo utworzonego zakresu. Różnica symetryczna jest zbiorem, który zawiera elementy występu jące w pierwszym zbiorze, lecz nie występujące w drugim oraz elementy występujące w drugim zbiorze, ale niewystępujące w pierwszym. Pierwsza wersja funkcj i ustala kolej ność el ementów przy użyciu operatora void make_heap ( Ra ndomAcce s s i terator f i r s t , Ra ndomAcce s s i terator l a s t , Compare comp ) ;
Funkcja make _ he ap ( ) tworzy stóg z zakresu [ f i r s t , l a s t ) . Pierwsza wersj a funkcj i ustala kolej ność el ementów przy użyciu operatora < , natomiast druga używa d o tego celu obiektu porównania c omp. push_heap ( ) temp l a t e < c l a s s RandomAcces s i terator> void push_heap ( Ra ndomAcce s s i te r a t o r f i r s t , Ra ndomAcce s s i terator l a s t ) ; temp l a t e < c l a s s Ra ndomAcc e s s i te r a t o r , c l a s s Compare> void push_heap ( RandomAcces s i terator f i r s t , RandomAc ce s s i terator l a s t , Compare comp ) ;
Funkcja p u s h_heap ( ) zakłada, że zakres [ f i r s t , l a s t 1 ) j est prawidłowym stogiem i dodaje do stogu wartość występującą na pozycj i l a s t 1 (tj . pozycj i bezpośrednio za końcem stogu, który z założenia j est prawidłowy), dzięki czemu zakres [ f i r s t , l a s t ) staje się poprawnym stogiem. Pierwsza wersj a funkcj i ustala kolejność elementów przy użyciu opera tora void pop_heap ( Ra n domAcces s i terator f i r s t , RandomAc ce s s i terator l a s t ) ; temp l a t e < c l a s s RandomAc c e s s i terator , c l a s s Compare> void pop_heap ( RandomAc ce s s i t e r a t o r f i r s t , RandomAc c e s s i terator l a s t , Compare comp ) ;
Funkcja pop_he a p ( ) zakłada, że zakres [ f i r s t , l a s t ) jest prawidłowym stogiem. Funk cj a zamienia wartość z pozycj i l a s t 1 z wartością występującą na pozycj i f i r s t i tworzy 1 ) poprawny stóg. Pierwsza wersja funkcj i ustala kolej ność z zakresu [ f i r s t , 1 a s t elementów przy użyciu operatora void s ort_heap ( RandomAcce s s i te r a t o r f i r s t , RandomAcce s s i terator l a s t , Compare comp ) ;
1 220
Funkcje STL Funkcja s o r t_heap ( ) zakłada. że zakres [ f i r s t , l a s t ) jest stogiem i sortuj e go. Pierw sza wersja funkcj i ustala kolej ność elementów przy użyciu operatora c o n s t T & min ( co n s t T & a , const T& b ) ; temp l a t e < c l a s s T , c l a s s Compare> con s t T & m 1 n ( co n s t T & a, const T & b, C ompare comp ) ;
Funkcja m i n ( ) zwraca mniej szą z dwóch wartości. Jeśl i te dwie wartości są równoważne, funk cja zwraca pierwszą z nich. Pierwsza wersj a funkcj i ustala kolejność elementów przy użyciu operatora c o n s t T & max ( co n s t T & a , con s t T & b ) ; temp l a t e < c l a s s T,
c l a s s Compa re>
const T & max ( co n s t T & a, c o n s t T & b , Compare comp ) ;
Funkcja max ( ) zwraca większą z dwóch wartości. Jeśl i te dwie wartości są równoważne, funk cja zwraca pierwszą z nich. Pierwsza wersja funkcj i ustala kolejność elementów przy użyciu operatora < , natomiast druga używa do tego celu obiektu porównania c omp. min_element ( ) temp l a t e < c l a s s Forwa rdi terator> Forwa rdi t e r a t o r m i n_e l eme n t ( Forwardi terator f i r s t , Forwardi terator l a s t ) ; temp l a t e < c l a s s Forward i t e r a t o r , c l a s s Compa re> Forwa r d i t e r a t o r m i n_e leme n t ( Forwardi terator f i r s t , Forward i terator l a s t , Compare comp ) ;
Funkcja mi n_e l e me n t ( ) zwraca pierwszy iterator i t występujący w zakresie [ f i r s t , l a s t ) , taki że żaden z elementów z tego zakresu nie j est mniejszy niż element * i t . Pierwsza wersj a funkcj i ustala kolejność elementów przy użyciu operatora Forwardi t e r a t o r ma x_e leme n t ( Forwardi terator f i r s t , Forwardi terator l a s t ) ;
1 221
Dodatek G - Metody i funkcje z biblioteki STL
temp l a t e < c l a s s Forwa r d i t e r a t o r , c l a s s Compare> Forwa rd i terator max_e l emen t ( Forwa r d i t e rator f i r s t , Forwa r d i t e rator l a s t , Compare camp ) ;
Funkcj a max_ e l eme n t ( ) zwraca pierwszy iterator i t występujący w zakresie [ f i r s t , l a s t ) , taki że nie istniej e w tym zakresie żaden element, od którego element * i t byłby mniej szy. Pierwsza wersj a funkcj i ustala kolej ność elementów przy użyciu operatora < , natomiast dmga używa do tego celu obiektu porównania c omp. lexi cograph i c a l_ compare ( ) temp l a t e < c l a s s I np u t i t e r a t o r l , c l a s s I n p u t i t e ra t o r 2 > boo l lexi cograph i c a l_compare ( I nput i te r a t o r l f i r s t l , I npu t i te r a t o r l l a s t l , I np u t i terator2 f i r s t 2 , I n p u t i terator2 l a s t 2 ) ; temp l a t e < c l a s s I n put i terator l , c l a s s I n p u t i terator2 , c l a s s Compare> bool l e x 1 cograph 1 c a l_compare ( I npu t i te r a t o r l f i r s t l , I np u t i teratorl l a s t l , I n p u t i terator2 f i r s t 2 , Inpu t i terator2 l a s t 2 , Compare camp ) ;
Funkcj a l e x i c o g r aph i c a l c omp a re ( ) zwraca wartość t ru e , jeśli sekwencja elemen tów w zakresie [ f i r s t 1 , l a s t 1 ) jest w sensie leksykograficznym mniejsza od sekwencj i elementów w zakresie [ f i r s t 2 , l a s t 2 ) . W przeciwnym razie funkcj a zwraca wartość f a l s e . Porównanie leksykograficzne polega na porównaniu pierwszego elementu z jednej sekwencj i z pierwszym elementem z drugiej - porównywany j est więc element * f i r s t l z elementem * f i r s t 2 . Jeśli element * f i r s t l j est mniej szy od * f i r s t 2 , funkcj a zwraca wartość t r u e . Jeśli * f i r s t 2 j est mniejszy o d * f i r s t l , funkcja zwraca wartość f a l s e . Jeśli t e dwa ele menty są równoważne, operacj a porównania przechodzi na następny element w każdej sekwen cj i . Proces ten j est kontynuowany do napotkania dwóch elementów, które nie są równoważne, lub do osiągnięcia końca j ednej z sekwencj i . Jeśl i dwie sekwencj e są równoważne aż do końca jednej z nich, to mniej szą j est sekwencj a krótsza. Jeśli dwie sekwencj e są równoważne i mają taką samą długość, żadna z nich nie jest mniej sza, a więc funkcj a zwraca wartość f a l s e . P ierw sza wersja funkcj i ustala kolej ność elementów przy użyciu operatora bool next_pe rmuta t i o n ( B i di rectiona l i terator f i r s t , B i d i r e c t i o n a l i terator l a s t ) ; template< c l a s s B i d i r e c t i o na l i t e r a t o r , c l a s s Compare> bool next_p e rmuta t i o n ( B i di r e c t 1 o na l i terator f i r s t , B i d i r e c t i on a l i terator l a s t , Compa re comp ) ;
Funkcja n e x t_p e rmu t a t i o n ( ) przekształca sekwencję w zakresie [ f i r s t , l a s t ) w następną w porządku leksykograficznym permutację. Jeśli następna permutacj a istnieje, funkcja zwraca wartość t ru e . W przeciwnym razie (tj . w przypadku, gdy zakres zawiera ostatnią w porządku leksykograficznym permutacj ę) funkcj a zwraca wartość f a l s e i przekształca zakres w pierwszą permutację. Pierwsza wersja funkcj i ustala kolej ność elementów przy użyciu opera tora bool prev_pe rmu t a t i o n ( B i di re c t i o na l i terator f i r s t , B i d i r e c t i o na l i terator l a s t ) ; template< c l a s s B i d i re c t i ona l i t e r a t o r , c l a s s Compare> bool prev_permuta t i o n ( B i d i r e c t i o na l i terator f i r s t , B i d i r e c t i ona l i te r a t o r l a s t , Compare comp ) ;
Funkcja prev_pe rmu t a t i on ( ) przekształca sekwencję w zakresie [ f i r s t , l a s t ) w po przednią w porządku leksykograficznym pennutację. Jeśli poprzednia permutacja istnieje, funkcja zwraca wartość t ru e . W przeciwnym razie (tj . w przypadku, gdy zakres zawiera pierwszą w po rządku leksykograficznym pennutacj ę) funkcja zwraca wartość fa 1 se i przekształca zakres w ostat nią pennutację. Pierwsza wersja funkcj i ustala kolejność elementów przy użyciu operatora T accumu l a t e ( I np u t i terator f i r s t , I n pu t i terator la s t , T i n i t ) ; temp l a t e < c l a s s I nput i te r a to r , c l a s s T , c l a s s B i n a r yOper a t i o n > T accumu l a t e ( I npu t i t e rator f i r s t , Inpu t i terator l a s t , T i n i t , B i n a ryOpera t i o n b i n a ry_op ) ;
Funkcj a a c cumu l a t e ( ) inicjalizuje wartość a c c wartością i n i t , a następnie po kolei dla każdego iteratora i w zakresie [ f i r s t , l a s t ) wykonuje operacj ę acc acc + * i (pierwsza wersj a) lub a c c b i n a r y_op ( a c c , * i ) (druga wersja). Na koniec funkcj a zwraca ostateczną wartość a c c . =
=
inner_produ c t ( ) temp l a t e < c l a s s I np u t i t e r a t o r l , c l a s s I np u t i terato r2 , c l a s s T> T i n n e r_p r oduct ( I np u t i t e r a t o r l f i r s t l , I n p u t i teratorl l a s t l , I np u t i terator2 f i r s t 2 , T i n i t ) ; temp l a t e < c l a s s I np u t i t e r a t o r l , c l a s s I np u t i terator 2 , c l a s s T , c l a s s B i n a ryOpe rat i on l , c l a s s B i n a r y0pera t i o n 2 > T i n ner_produc t ( I np u t i t e r a t o r l f i r s t l , I n pu t i teratorl l a s t l , I np u t i terator2 f i rs t 2 , T i n i t , B i n a r yOpera t i o n l b i nary_ op l , B i n ary0pera t i on2 b i nary_op 2 ) ;
Funkcja i n n e r _ p r o du c t ( ) inicjalizuj e wartość a c c wartością i n i t , a następnie po kolei dla każdego iteratora i w zakresie [ f i r s t l , l a s t l ) i odpowiadaj ącego mu iteratora j w zakresie [ f i r s t 2 , f i r s t 2 + ( l a s t l - f i r s t l ) ) wykonuje operacj ę a c c acc + * i * * j (pierwsza wersja) lub a c c b i n a r y_ o p l ( a c c , b i n a r y_ op2 ( * i , * j ) ) (druga wersja). Oznacza to, że funkcj a oblicza wartość na podstawie pierwszych e lementów z =
=
1 224
Funkcje STL każdej sekwencj i , następnie na podstawie drugich elementów każdej sekwencj i i tak dalej aż do napotkania końca pierwszej sekwencj i . Na koniec funkcj a zwraca ostateczną wartość a c c . partia!_sum ( ) templ ate
Outpu t l t e r a t o r p a rt i a l_sum ( I npu t i terator f i r s t ,
I np u t i terator l a s t ,
Outpu t i terator r e s u l t ) ;
temp l a t e< c l a s s I npu t i te r a t o r ,
c l a s s Outpu t i te r a to r , c l a s s B i n a r yOpe r a t i o n >
Outpu t i t e r a t o r pa r t i a l_sum ( I nput i te r a t o r f i rs t ,
I npu t i t e r a t o r l a s t ,
Outpu t i t e r a t o r resu l t , B i n a r yOpe r a t i o n b i n a r y_op ) ;
Funkcja pa r t i a l s um ( ) przypi suje elementowi * re s u l t wartość * f i r s t , następnie ele mentowi * ( r e s u l t + 1 ) wartość * f i r s t + * ( f i r s t + 1 ) (pierwsza wersja) lub wartość b i n a r y o p ( * f i r s t , * ( f i r s t + 1 ) ) (druga wersj a) itd. Oznacza to, że n-ty element sekwencj i rozpoczynającej od pozycji re s u l t zawiera sumę ( lub wynik operacj i bi nary_op) pierwszych n elementów sekwencji rozpoczynającej się od pozycj i f i r s t . Funkcj a zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem zakresu wynikowego. Algorytm pozwala, żeby pozycj a r e s u l t pokrywała się z pozycją f i r s t , tj . żeby wyniki zastą piły pierwotną sekwencję. adj acent_di fference ( ) temp l a t e
Outpu t i te r a t o r adj a c e n t_di f f e rence ( I np u t i t e r a t o r f i r s t ,
I nput i terator l a s t ,
Outpu t i te r a t o r r e s u l t ) ;
templ a t e < c l a s s I n pu t i te r a t o r ,
c l a s s Outpu t i t e r a to r , c l a s s B i n a ryOpera t i o n >
Outpu t i t e r a t o r adj a cent_di f ference ( I np u t i terator f i r s t ,
I nput i t e r a t o r l a s t ,
Outpu t i terator resu l t , B i n a ryOpe r a t i o n b i n a r y_op ) ;
Funkcja a d j a c e n t _ d i f f e re n c e ( ) wpisuje na pozycj i re s u l t wartość * f i r s t ( * r e s u l t = * f i r s t). Na kolej nych pozycj ach zakresu wynikowego wpisywane są różnice (lub wyniki operacj i b i n a r y_ op) wartości sąsiednich elementów zakresu źródłowego. Oznacza to, że na kolejnej pozycj i zakresu docelowego, czyli ( re s u l t + 1 ), zostaje umieszczona wartość * ( f i r s t + 1) * f i r s t (pierwsza wersja) lub wartość b i n a r y_ op ( * ( f i r s t + 1 ) , * f i r s t ) (druga wersj a) itd. Funkcja zwraca iterator wskazujący pozycję bezpośrednio za ostatnim elementem zakresu wynikowego. Algorytm pozwala, żeby pozycja r e s ul t pokrywała się z pozycj ą f i r s t , tj . żeby wyniki zastąpiły pierwotną sekwencję. -
1 225
Dodatek H
Wybrane pozycie książkowe i zasoby internetowe
N
a temat języka C++ i programowania istnieje wiele dobrych książek i zasobów internc towy � · Zgodnie z zamierzen i m przedstawion a poniżej lista powinna być traktowana . . . . raczej Jako reprezentatywna mz kompletna. IstmeJe bowiem wiele dobrych książek 1 wi tryn, których tutaj nie wymieniono. Niemniej jednak lista ta obejmuje szeroki zakres dostępnych pozycji.
�
�
Wybrane pozycie książkowe • Booch Grady, Object-Oriented A na/ysis and Design with Applications, wydanie drugie,
Addison-Wesley, 1 993. W tej książce przedstawiono koncepcj e programowania obiektowego, omówiono metody obiektowe i zaprezentowano przykładowe zastosowania. Przykłady są przedstawione w ję zyku C++. • Booch Grady, Rumbaugh James, Jacobson Ivar, UML przewodnik użytkownika, WNT 2002.
Książka autorstwa twórców języka UML przedstawia istotę j ęzyka UML wraz z wieloma przykładami j ego zastosowania. • Cline Marshal l , Lomow Greg, Girou Mike, C++ FA Qs, wydanie drugie, Addison-Wesl ey,
1 999.
Ta książka zawiera odpowiedzi na bardzo wiele często zadawanych pytań dotyczących języka C++. • Jacobson !var, Object-Oriented Software Engineering: A Use Case Driven Approach,
Addison-Wes ley, 1 992. W tej książce opisano skuteczne metody i wskazówki dotyczące tworzenia systemów opro gramowania wielkiej skal i . • Josuttis Nicolai M „ C+ +. Biblioteka standardowa. Podręcznik programisty, Helion, 2003 .
W tej książce opisano S tandardową Bibliotekę Szablonów ( STL), a także inne elementy bi bl ioteki C++, takie jak obsługa liczb zespolonych, obsługa wartości lokalnych oraz strumie nie wej ściowo-wyjściowe.
Dodatek H Wybrane pozycje ksiqżkowe i zasoby internetowe -
• Lee Richard C, Tepfenhart William M., UML and C+ +, wydanie drugie, Prentice Hall, 200 I .
Ta książka jest samouczkiem języka UML, a ponadto zawiera przypomnienie podstaw języka C++. • Meyers Scott. C++. 50 efektywnych sposobów na udoskonalenie Twoich programów,
Helion, 2003 . Ta książka jest adresowana do programistów, którzy znająjuż język C++, i zawiera 50 reguł i wskazówek. Część z nich jest bardzo praktyczna i wyjaśnia, na przykład, kiedy powinno się definiować konstruktory kopiujące i operatory przypisania. Inne z kolei są bardziej ogólne i omawiają, na przykład, związki generalizacji (specjalizacji) oraz agregacji. • Meyers Scott, STL w praktyce. 50 sposobów efektywnego wykorzystania, Helion, 2004.
Ta książka zawiera wskazówki dotyczące wyboru kontenerów i algorytmów, a także in nych aspektów korzystania z biblioteki STL. • Meyers Scott, Język C+ +
-
hardziej efektywny, WNT, 1 99 8 .
Ta książka stanowi kontynuacj ę pozycj i C+ +. 50 efektywnych sposobów . . . (polskie wy dania nie są chronologiczne - przyp. tłum. ) i wyj aśnia niektóre z bardziej skomplikowa nych aspektów j ęzyka oraz pokazuje j ak zreal izować pewne określone cele, takie j ak projektowanie wskaźników inteligentnych. Książka stanowi odzwierciedlenie dodatko wych doświadczeń, jakie programiści j ęzyka C++ zdobyl i w ciągu kilku lat, które upłynę ły od wydania pierwszej książki z tej serii . • Rumbaugh James, B łaha Michael, Premerlani Wil liam, Eddy Frederick, Lorensen B i l l , Lorenson Wi l l iam, Object-Oriented Mode/ling and Design, Prentice Hal l , 1 99 1 . W tej książce przedstawiono i omówiono technikę OMT, metodę problemów na odpo wiednie obiekty. • Rumbaugh James, Jacobson Ivar, Booch Grady, The Unified Modeling Language Referen ce Manual, wydanie drugie, Addison-Wesley, 2004. Ta książka autorstwa twórców języka UML zawiera pełny opis j ęzyka UML w postaci podręcznej dokumentacj i . • Stroustrup Bj arne, Język C+ +, wydanie siódme, WNT, 2004. Bj arne Stroustrup j est twórcą języka C++, j est to więc autorytatywna pozycja. Naj łatwiej będzie jąj ednak przyswoić tym, którzy posiadająjuż pewną znaj omość języka C++. Książka ta nie tylko zawiera opis samego j ęzyk, lecz także wiele przykładów jego użycia, jak również omówienie metodologii programowania obiektowego. Kolejne wydania tej książki odzwierciedlaj ą rozwój j ęzyka, a to wydanie zawiera dodatkowo omówienie składników bibl ioteki standardowej takich j ak biblioteka STL oraz łańcuchy. • Stroustrup Bj arne, Projektowanie i rozwój języka C+ +, WNT, 2004. Książka jest przeznaczona dla osób zainteresowanych genezą i ewolucj ą j ęzyka C++. • Vandevoorde David, Josuttis Nicolai M., C+ +. Szablony. Vademecum profesjonalisty, Helion, 2003 . O szablonach można powiedzieć wiele, co demonstruje ten szczegółowy podręcznik.
1 228
Zasoby internetowe
Zasoby internetowe • Dokument ISO/ANS I C++ Standard (ISO/IEC 1 48 8 2 : 2003 ) stanowi techniczną korektę wersj i z roku 1 998 tego standardu ( 1 48 8 2 : I 998) i można go uzyskać zarówno od Amery kańskiego Narodowego Instytutu Normalizacyjnego (ANSI), jak i od Międzynarodowej Organizacji Normalizacyjnej (IS0) 1 • Instytut ANS I oferuje wersję drukowaną dostępną z a 28 1 dolarów oraz wersję el ektro niczną do pobran ia (fonnat PDF. ograniczen ie poj edynczego użytkownika) dostępną za 1 8 dolarów. Obydwie wersje można zamówić w witrynie: hllp://webstore. ansi. org
Organizacja I S O oferuje ten dokument w postaci pliku PDF do pobrania za 3 5 2 franków szwajcarskich lub na płycie CD, także za 3 5 2 franków szwajcarskich, i można go zamó wić w witrynie: h11p:llwww. iso.org
• Witryna C++ FAQ Lite zawierająca odpowiedzi na często zadawane pytania (w języku angiel skim, chińskim, francuskim, rosyjskim, portugalskim i polskim2) jest odchudzoną wersją książki autorstwa Cline'a i innych. Obecnie jest dostępna pod następującym adresem: www.parashift. com/c + + �faq-lite
• Liczne pytania i odpowiedzi dotyczące języka C++ znaleźć można na moderowanej grupie dyskusyjnej (angielskoj ęzyczna): camp. /ang. c+ + . moderated
• CIC++ Users Journal Ten angielskoj ęzyczny miesięcznik j est skierowany przede wszystkim do zawodowych programistów. W j ego witrynie WWW (www. c1lj. com) dostępnych j est kilka użytecznych zasobów.
1 W Polsce nonny ISO można zakupić za pośrednictwem Wydziału Marketingu i Sprzedaży Polskiego Komitetu Nonnalizacyjnego.
' Polska wersja jest dostępna pod adresem:
http:llklub. chip.p//b. kr:emienlc+ +,faq-p//
1 229
Dodatek I
Dostosowywanie do standardu ANSI/ISO (++
N
n iejszy dodatek zawiera wytyczne i wskazówki przeznaczone dla czytel n i ków, którzy posiadaj ą programy napi sane w języku C lub w starszych wersjach C++ i pragną je przełożyć na standardowy C++. Skorzystaj ą z niego także ci, którzy chcą się pozbyć pewnych przestarzałych nawyków programistycznych. N iektóre ze wskazówek dotyczą przej śc i a z C do C + + , a inne ze starszych wersj i C++ do standardowego C++.
Stosui rozwiązania alternatywne zamiast niektórych dyrektyw preprocesora Preprocesor języków C oraz C++ udostępn ia zestaw dyrektyw. Ogólnie rzecz bi orąc. stosowaną przypadku C++ praktykąj est wykorzystywanie tylko tych dyrektyw, które służą do zarządzania procesem komp i la cj i . a unikanie dyrektyw zastępujących kod. Przykładowo dyrektywa #"in c l u de jest zasadniczym elementem do zarządzania pl ikami programu. Inne dyrektywy, takie jak il i f nde f oraz # e n d i f , umożl iwiaj ą kontrolę nad kompilacj ą poszczegól nych bloków kodu . Dyrektywa # p r a g m a pozwala sterować specyficznymi d l a danego kompil atora opcjami kompilacj i . Są to bardzo przydatne, a czasami niezbędne narzędzia. Niemniej j ednak , j e ś l i chodzi od dyrektywę # de f i n e , należy zachować ostrożność . w
Do definiowania stałych uiywai modyfikatora
cons t
zamiast
dyrektywy # de f i ne sprawiaj ą. że kod s t aj e się bardziej czytelny i łatwiejszy w utrzymaniu. Na jej z n a c z eni e , a w przypadku potrzeby zmiany wartośc i wystarczy dokonać tego raz ( w d e fi n i cj i ), a następnie ponownie skomp ilować kod. W języku C do tworzenia nazw symbol icznych stałych służy dyrektywa preproccsora : Stale sym b o l i czne
zwa s tał ej okreś l a
#define
MAX L E N G T H
100
Prcprocesor dokonuje pó ź ni ej (przed kompilacją) podstawienia tekstowego w kodzie źródłowym i zastępuje wystąpienia stałej MAX_ L E N GTH wartością 1 O O .
Dodatek I - Dostosowywanie do standardu ANSI/ISO (++
W. przypadku języka C++ preferowanym podejściem j est zastosowanie modyfikatora c o n s t do deklaracj i zmiennej : co n s t i n t MAX_LENGTH = 1 0 0 ;
Po takiej definicj i identyfikator MAX_ LENGTH będzie traktowany j ako wartość typu i n t tylko do odczytu. Podej ście oparte na modyfikatorze c o n s t ma wiele zalet. Po pierwsze. deklaracja j awnie określa typ. W przypadku dyrektywy #de f i n e , żeby określić typy inne niż cha r, i n t czy doub l e , trzeba stosować różne przyrostki. Ż eby n a przykład określ ić typ l o n g, używamy zapisu l O O L, natomiast żeby określić typ f l o a t , stosuj emy zapis 3 . 1 4 F. Co ważniej sze, modyfikator c o n s t można równie łatwo używać z typami złożonymi , jak w przykładzie : co n s t i n t b a s e_va l s [ 5 ] = { 1 0 0 0 , 2 0 0 0 , 3 5 0 0 , 6 0 0 0 , con s t s t r i n g a n s [ 3 ] = ( " t a k " , " n i e " , " mo ż e " } ;
10000 } ;
I wreszcie dla identyfikatorów z modyfikatorem co n s t obowiązują te same reguły zakresu co dla zmiennych. Możemy więc tworzyć stałe o zasięgu globalnym, o zakresie przestrzeni nazw oraz o zakresie bloku. Jeśli, powiedzmy, zdefiniuj emy stałą w określonej funkcj i , nie musimy się przej mować, że j ej definicj a będzie kolidować ze stałą globalną używaną w innym miej scu pro gramu. Weźmy pod uwagę następujący kod: # de f i n e n 5 co n s t i n t d z
12;
vo i d f i z z l e ( ) int n ; int dz ;
W takim przypadku preprocesor zastąpi deklarację: int n ;
wyrażeniem: int 5 ;
co spowoduj e błąd kompilacj i . Z kolei zdefiniowany w funkcj i f i z z l e ( ) identyfikator d z będzie zmienną lokalną. Mimo t o w treści funkcj i f i z z l e ( ) możemy w razie potrzeby uzyskać dostęp do stałej przy użyciu operatora wyboru zakresu ( : : ) w postaci wyrażenia : : d z . Słowo k luczowe c o n s t zostało zapożyczone z j ęzyka C , j ednak wersja dostępna w C++ j est bardziej użyteczna. Modyfikator c o n s t w C++ powoduj e na przykład wiązanie wewnętrzne (ang. interna/ linkage) zewnętrznych wartości typu c o n s t, a nie domyślne wiązanie zewnętrz ne, jak w przypadku zmiennych i modyfikatora c o n s t w języku C. Oznacza to, że stała c o n s t musi być zdefiniowana w każdym pliku programu, w którym jest używana. Może się wydawać. że takie rozwiązanie wymaga dodatkowej pracy, lecz w rzeczywistości bardzo ułatwia życie.
1 232
�losuj rozwiqzania alternatywne zamiast niektórych dyrektyw preprocesora
Dzięki wiązaniu wewnętrznemu definicje c o n s t możemy umieścić w pl iku nagłówkowym uży wanym przez różne pliki w proj ekcie, co w przypadku wiązania zewnętrznego spowodowałoby błąd kompi lacj i . Ponadto, z uwagi na fakt, że stała c o n s t musi być zdefiniowana w pliku, w któ rym jest używana (wymaganie to spełnia umieszczenie jej w pliku nagłówkowym dołączanym do tego pliku), wartości typu c o n s t mogą służyć jako argumenty określające rozmiar tabl icy: con s t i n t MAX_LENGTH
=
100;
doub l e l o a d s [ MAX_LENGTH ] ; f o r ( i n t i = O ; i < MAX_LENGTH ; loads [ i ] 50;
i++)
=
W języku C takie rozw iązani e n i e będzie działać, ponieważ dekl aracj a definiuj ąca stałą MAX_ LENGTH mogłaby się znajdować w oddzielnym pliku i nie być dostępna podczas kompilacji tego konkretnego pl iku. Gwoli uczciwości należy dodać, że w języku C w celu utworzenia stałej o wiązaniu wewnętrznym wystarczy użyć modyfikatora s t a t i c . Okazuje się więc, że w C++ modyfikator s t a t i c stał się domyślny, dzięki czemu mamy o jedną rzecz mniej do zapamiętania. Nawiasem mówiąc, poprawiony standard j ęzyka C (C99) umożliwia stosowanie stałych c on s t do określania rozmi aru tablicy; taka tab lica jednak jest traktowana jako nowa postać tab l icy zwana tablicą zmienną, która nie występuje w standardzie języka C++. Mimo wszystko istnieje jednak przydatne zastosowanie dyrektywy # de f i n e idiom służący do sterowania kompilacją pliku nagłówkowego: li blooper. h # i f nde f # de f i n e li kod #endi f
-
standardowy
B LOOPER H B LOOPER H
W przypadku typowych stałych symbolicznych należy j ednak wyrobić sobie nawyk używania modyfikatora c o n s t zamiast dyrektywy # de f i n e . Inną dobrą alternatywą, szczególnie w przy padku zestawu pokrewnych stałych całkowitych, jest zastosowanie wyl iczeni a en urn: enum { LEVE L l
=
1,
LEVE L 2
=
2,
LEVE L3
=
4,
LEVE L 4
=
8};
Do definiowania niewielkich f unkcii uiywai specyfikatora inline zamiast makrodefinicii #define W języku C bliskim odpowiednikiem funkcj i typu i n l i n e (rozwij anej w miejscu wywołania) są makrodefinicj e # de f i n e : # de f i n e C u b e ( X )
X*X*X
Powyższa makrodefinicj a sprawia, ż e preprocesor dokonuje podstawienia tekstowego, przy czym symbol X zostaj e zastąpiony odpowiednim argumentem makra Cube ( ) :
1 233
Dodatek I - Dostosowywanie do standardu ANSI/ISO C ++ =
Cube ( x ) ; Cube ( x + Z + + ) ; z+ + *x z + + ; y
=
y
li instrukcja zostaje zastąpiona wyrażeniem y = x *x *x; li instrukcja zostaje zastąpiona wyrażeniem
x
+ z+ + *x +
-r
Ponieważ preprocesor wykonuje podstawienie tekstowe w miej sce prawdziwego przekazywan i a argumentów, korzystanie z takich makr może prowadzi ć do nieoczekiwanych i nieprawidłowych wyników. Takie błędy można ograniczyć, używając w makrach l icznych nawiasów w celu zapew nienia właściwej kolej ności operacj i : #de f i ne C u b e ( X )
( (X) * (X) * (X) )
Jednak nawet takie podejście nie rozwiązuje problemu wyrażeń typu z + + . Charakterystyczne dla j ęzyka C++ podej ście polegające n a definiowaniu funkcj i rozwij anych w miej scu wywołania p rzy użyciu słowa kluczowego in l i n e j est znacznie pewniejsze, ponie waż polega na faktycznym przekazywaniu argumentów. Ponadto funkcje typu in l i n e mogą być zwyczajnym i funkcjami lub metodami klas: c l a s s do rma n t private : int period ; publ i c : i n t Pe r i o d ( )
const { return period;
} li automatycznie rozwijana w miejscu
v.ywolania )
;
Jedyną pozytywną cechą makrodefinicj i # d e f i n e j est jej beztypowość, która sprawia, że może ona być użyta z każdym typem, dla którego dana operacj a ma sens. W j ęzyku C++ niezależność od typu, lecz z zachowaniem przekazywania argumentów, można osiągnąć, tworząc szablonowe funkcje typu i n l i n e . Podsumowując, zamiast makr # de f i n e charakterystycznych d l a języka C należy wykorzysty wać mechanizm rozwijania funkcj i w miej scu wywołania języka C++.
Uiywai prototypów funkcii Właściwie nie mamy wyboru - chociaż prototypy są w języku C opcjonalne, są one obowiązko we w C++. Należy zaznaczyć, że funkcj a zdefiniowana przed j ej pierwszym użyciem, np. funkcj a typu i n l i n e , służy j ednocześnie za swój własny prototyp. Tam gdzie potrzeba, w prototypach i nagłówkach funkcj i należy umieszczać modyfikator c o n s t . W szczególności modyfikatora c o n s t należy używać z parametrami wskaźnikowym i i referen cyjnymi reprezentującymi dane, które nie mogą zostać zmodyfikowane. Takie podejście nie tylko umożliwia kompilatorowi wyłapywanie błędów niedozwolonej modyfikacji danych, lecz także czyni funkcje bardziej ogólnymi. Oznacza to, że funkcj a ze wskaźnikiem lub referencj ą typu 1 234
Stosuj rzutowanie typów
con s t może przetwarzać zarówno dane typu con s t , jak i nie-c o n s t , podczas gdy funkcj a używająca wskaźnika l u b referencj i b e z modyfikatora con s t może j edynie przetwarzać dane, które nie są c on s t .
Stosui rzutowanie typów Jedną z cech języka C, które szczególnie mierziły Bj arne'a Stroustrupa, był nieogran iczony ope rator rzutowania typu. Prawdą j est, że rzutowanie typu j est często niezbędne, lecz standardowy mechanizm rzutowania j est za mało restrykcyjny. Rozważmy j ako przykład taki kod: s t r u c t Doo f doub l e f e e b ; doub l e s t e e b ; char sgi f [ l O ] ;
) ; Doo f l e a m ; short * ps ( s h o r t * ) & l e am ; li stara składnia int * p i i n t * ( & l e am ) ; li nowa składnia =
=
W języku C nic nie stoi na przeszkodzie, żeby rzutować wskaźnik j ednego typu na wskaźnik zupełnie innego typu. W pewnym sensie sytuacj a przypomina tę z instrukcją g o t o . Problem z instrukcją go t o pole gał na tym, że była zbyt elastyczna i prowadziła do powstawania wypaczonego kodu. Rozwiąza niem było zaoferowanie bardziej ograniczonych strukturalnych wersji instrukcji g o t o przezna czonych do real izacj i najbardziej typowych zadań, do których potrzebna była instrukcj a g o t o . Taka była geneza pojawienia s i ę takich elementów j ęzyka j ak pętle f o r i w h i l e oraz instrukcj e i f e l s e . Standard języka C++ oferuje podobne rozwiązanie d l a problemu nieograniczonego rzutowania typów, a mianowicie mechanizmy ograniczonego rzutowania przydatne w naj bar dziej typowych sytuacj ach wymagających rzutowania typu. Oto operatory rzutowania typu oma wiane w rozdziale 1 5 . : dyn am i c_c a s t static cast con s t c a s t re i n t e rp r e t c a s t A więc w przypadku wykonywania rzutowania typu dotyczącego wskaźników należy w m iarę możliwości użyć j ednego z tych operatorów. W ten sposób zarówno dokumentuje się cel danego rzutowania typu, j ak i zapewnia sprawdzenie, czy j est ono stosowane zgodnie z przeznaczeniem.
1 235
Dodatek I - Dostosowywanie do standardu ANSl/ISD ( ++
Poznai i wykorzystui mechanizmy ięzyka
C++
Zamiast korzystać z funkcj i ma l l o c ( ) i f re e ( ) , na leży przestawi ć się na stosowanie operatorów new i de l e t e . Każdy, kto do obsługi błędów używał funkcj i s e t j mp ( ) oraz l o n g j mp ( ) , powinien je zastąpić kon strukcją t r y , t h r o w oraz c a t c h . Dane reprezentujące wartości t r u e oraz f a l s e powinny być typu b o o l .
Używai nowei organizacii plików nagłówkowych W standardzie j ęzyka C++ podano nowe nazewn ictwo dla pl ików nagłówkowych, co opisano w r ozd z i a l e 2., „Pierwszy p rogram w C++". W przypadku stosowania p l i ków nagłówkowych starego typu nal eży przestawić się na korzystan ie z nowych nazw. N i e j est to wyłąc z n i e zmiana kosmetyczna, poni eważ w n owych wersjach dodano nieki edy nowe funkcj e . Przykładowo p l i k nagłówkowy ostream zapewn ia obsługę wejścia i wyjścia dla znaków d w ub ajtow yc h . Udostępnia również nowe manipulatory tak ie jak bo o l a l ph a oraz f i x e d ( opi sane w ro z d z i a l e 1 7 . , „Obsh1ga wej ścia, wyjści a oraz p l i ków " ) . Oferuj ą one prostszy sposób ustawiania wiciu opcj i formatowania niż w przypadku korzystania z funkcj i s e t f ( ) czy też funkcj i z p l i ku i o ma n i p . W przypadku korzystan ia j ednak z funkcj i s e t f ( ) przy podawaniu stałych nal eży używać kl asy i o s _ b a s e zamiast i o s , tj . zamiast stałej i o s : : f i x e d nalezy używać stałej i o s b a s e : : f i xe d. Ponadto w nowyc h plikach nagłówkowych występuj e podział na przestrzenie nazw.
Korzystai z przestrzeni nazw Przestrzen ie nazw pomagaj ą porządkować identyfi katory używane w programie w celu unikania konfliktów nazw. Ponieważ w bibliotece standardowej zaimplementowanej w postaci nowej or ganizacj i plików nagłówkowych nazwy umieszczone są w przestrzeni nazw s t d_ używan ie tych pl ików nagłówkowych wymaga operowan ia na przestrzeniach nazw. W zam i eszczonych w książce przykładach dla uproszczenia stosuje się dyrektywę u s i n g udo stępniaj ącą wszystki e nazwy z przestrzeni nazw s t d : # i n c l ude
< i o s t ream>
# i n c l ude
# i n c l ude
u s i n g n a me s p a c e s t d ;
li dyrektywa using
Niemniej jednak hurtowy eksport wszystk ich nazw z danej przestrzeni nazw, n i ezależni e od tego, czy są potrzebne czy nie, kol iduje z ideą przestrzen i nazw. Nieco lepszym rozwiązani em jest umieszczeni e dyrektywy u s i n g wewnątrz funkcj i, dzięki cze mu nazwy zostaną udostępnione tylko w treści tej funkcj i . Jeszcze lepszym i w dodatku zal ecanym podejściem, j e s t stosowanie deklaracj i u s i n g l u b ope ratora wyboru zakresu ( : : ) w celu udostępn iania tylko tych nazw, które dany p rogram potrzebu je. Na przykład k o d : 1 236
Używaj szablonu autoptr ł i n c l u de < i o s t re am> u s ing s t d : : c i n ; using std : : cout ; us ing s td : : endl ;
li deklaracja using
udostępnia nazwy c i n , c o u t oraz en dl do końca danego pl iku. Z kolei użycie operatora wybo ru zakresu udostępnia daną nazwę jedynie w wyrażeniu, w którym zastosowano ten operator: cou t > e h w razie napotkania znaków odstępu, nowego wiersza lub tabulacj i pomija j e . Pozostałe dwie formy odczytują t e znaki.
Odpowiedzi do pytań z rozdziału 6 I . Obydwie wersje dają w wyniku te same odpowiedzi, jednak wersja z konstrukcją i f e 1 s e jest wydajniejsza. Przeanalizujmy przykładowo, co dzieje się w sytuacj i , gdy znak e h jest odstępem. Wersj a I . po inkrementacj i l icznika s p a c j e sprawdza, czy znak jest znakiem nowego wiersza. W ten sposób marnuj e się czas, ponieważ program już ustalił, że znak eh jest odstępem, a więc nie może być znakiem nowego wiersza. Wersja 2. w takiej sytuacj i pomij a test znaku nowego wiersza. 2. Obydwa wyrażenia, + + e h oraz eh + 1 , posiadaj ą tę samą wartość liczbową, jednak wyra żenie + + e h jest typu c h a r i zostaj e wyświetlone j ako znak, podczas gdy wyrażenie eh + 1 , w którym wartość typu c h a r j est dodawana do wartości typu i n t , j est typu i n t i zostaj e wyświetlone j ako liczba.
1 245
Dodatek J Odpowiedzi do pytań kontrolnych -
3 . Ponieważ w programie występuj e wyrażenie e h wej ściowe i wyj ściowe wyglądaj ą następuj ąco:
' $ ' zamiast e h
Hej ! H$eS j $ ! $ $ C o t o z n a c z y $ 1 0 a l bo $ 2 0 ? ! C $ o $ $ t $ o $ $ z $ n $ a $ c $ z $ y$ $ c t l = 1 8 ,
' $ ' , dane
ct2 = 1 8
Każdy znak przed wyświetleniem go po raz drugi zostaj e przekonwertowany na znak $ . Ponadto wartością wyrażenie e h = $ jest kod znaku $ , a więc wartość różna od zera, czyli t r u e . Licznik c t 2 j est więc inkrementowany po raz drugi. 4. a. waga > = 1 1 5 & & waga < 1 2 5 b. e h = = ' q ' I I e h = = ' Q ' C. X % 2 = = 0 && X ! = 26 d. X % 2 = = Q & & ! ( X % 2 6 = = Q ) e. d o n a c j a > = 1 0 0 0 & & don a c j a < = 2 0 0 0 f. ( e h > = ' a ' & & e h < = ' z ' ) I I ( eh >=
I I
'A'
gosc = = 1 & & eh < =
'Z' )
5 . Niekoniecznie. Przykładowo, j eś l i zmienna x j est równa 1 O , to wyrażenie ! 1 O zwraca war tość O, a ! ! x wartość 1 . Jeśl i j ednak x będzie zmienną typu b o o l , wówczas wyrażenie ! ! x będzie równe x . � ( X < 0 ) ? -x :
x;
lub: (X >= O l ?
-x ;
X
7. switch
( eh )
case
a_g r a d e + + ; break ; c a s e ' B ' : b _g r a cie + + ; break ; c a s e ' C ' : c _g r a d e + + ; break ; c a s e I D ' : d_g r a cie + + ; brea k ; f _g r a de + + ; defau l t : brea k ; 'A' :
8 . Jeśli zastosujemy etykiety całkowite, a użytkownik wpisze wartość niecałkowitą, np. q, program zawiesi się, ponieważ instrukcja wejścia pobierająca dane całkowite nie potrafi przetworzyć znaku. Jeśli natomiast zastosujemy etykiety znakowe, a użytkownik wpisze wartość całkowitą, np. 5, instrukcja wejścia znakowego przetworzy liczbę 5 j ako znak. W takim przypadku domyśl na klauzula instrukcji swi tch może zawierać sugestię wprowadzenia nowego znaku.
1 246
Odpowiedzi do pytań z rozdziału 7 9. Oto jedna wersja:
i n t line = O ; cha r e h ; wh i l e ( c i n . ge t ( c h )
& & eh
!=
'
Q
'
)
{ if
( eh = = \n ' ) l ine++ ; '
Odpowiedzi do pytań z rozdziału 7 I.
Te trzy kroki to : zdefiniowanie funkcj i , udostępnienie prototypu oraz wywołanie funkcj i .
2. a . vo i d i g o r ( vo i d ) ; li lub void igor() b. f l o a t t o f u ( i n t n ) ; li lub float tofu(int) c. doub l e mpg ( do ub l e mi l e s , d o u b l e g a l l o n s ) ; d. l o n g s umma t i o n ( l on g h a r r a y [ ] , i n t s i z e ) ; e. doub l e d o c t o r ( c o n s t c h a r * s t r ) ; f . v o i d o c z yw i s c i e ( s z e f f a c e t ) ; g. cha r * p l o t ( map * pmap ) ; 3. v o i d s e t_a r r a y ( i n t a r r [ ] , for
( int i arr [ i ]
int size,
O ; i < size; value ;
i n t va l u e )
i++ )
4. vo i d s e t_a r r a y ( i n t * b e g i n , for
i n t * end ,
( int * pt = begin ; pt *pt = value ;
i n t va l u e )
! = end; pt++ )
5. doub l e b i g g e s t
( c on s t d o u b l e f o o t [ ] ,
int s i ze )
{ d o ub l e max ; if ( size < 1 ) c o u t < < " N i e p r a w i d ł ow y r o z m i a r t abl i c y r ó w n y " < < size < < endl ; c o u t < < " Fu n kc j a z w r a c a wa r t o ś ć 0 \ n " ;
1 247
Dodatek J Odpowiedzi do pytań kontrolnych -
r e t u rn O ; e 1 s e li instrukcja zbędna, ponieważ wcześniej return powoduje uyjście z fimkcji {
max = f o o t [ O ] ; for ( int i = 1 ; i < s iz e ; i f ( f o o t [ i ] > ma x ) max = f o o t [ i ] ; r e t u r n ma x ;
i++)
6. Kwalifikator c o n s t stosuj e się wobec wskaźników w celu zabezpieczenia wskazywanych przez niego danych oryginalnych przed modyfikacją. W przypadku gdy program przekazuje do funkcj i typ i n t lub doub l e , przekazuje go przez wartość, a więc funkcj a operuje na kopii danych oryginalnych, które są w ten sposób chronione. 7. Łańcuch można umieścić w tabl icy typu c h a r , może być reprezentowany przez stałą łańcu chową ujętą w cudzysłów, a także przez wskaźni k wskazujący na pierwszy znak łańcucha. 8. i n t replace ( ch a r * s t r , int count = O ; whi l e ( * s t r ) if
char c l ,
lijeśli
char c2 )
nie na końcu łańcucha
( * st r == cl ) * s t r = c2 ; coun t + + ;
str++ ;
li przejdź
do następnego znaku
return coun t ;
9. Ponieważ język C++ interpretuje łańcuch " p i z z a " j ako adres j ego pierwszego elementu. zastosowanie operatora * daje w wyni ku wartość tego pierwszego elementu, czyli znak p. Ponieważ j ęzyk C++ interpretuje łańcuch " t a c o " j ako adres j ego pierwszego elementu, wyrażenie " t a c o " [ 2 J j est interpretowane j ako wartość elementu występującego dwie po ·rycje od początku, tj . j ako znak c . Inaczej mówiąc, stała łańcuchowa pełni tę samą rolę co nazwa tablicy. l C•
Ż eby strukturę przekazać przez wartość, podaj emy po prostu j ej nazwę g 1 i t z . Ż eby przekazać j ej adres, możemy użyć operatora adresu & g l i t z . Przekazanie przez wartość ;:iutomatycznie chroni dane oryginalne, lecz pochłania czas i pamięć. Przekazanie adresu zapewnia oszczędność czasu i pamięci, l ecz nie zabezpiecza danych oryginalnych, chyba że -
-
1 248
Odpowiedzi do pytań z rozdziału 8
dany parametr funkcj i zostanie zadeklarowany z modyfikatorem c o n s t . Ponadto przekaza nie przez wartość oznacza, że można stosować zwyczaj ny zapis składowych struktury, n ato miast przekazanie wskaźnika n akłada kon ieczność korzystani a z pośredniego operatora przy należności. I l . int j u dge
( in t
( * p f ) ( c o n s t char * ) ) ;
Odpowiedzi do pytań z rozdziału 8 I . Dobrymi kandydatami do rozwinięcia w miejscu wywołania ( i n l i n e) są niewielkich roz miarów funkcj e n ierekurencyjne. 2. a. v o i d p i o s e n k a ( c h a r * t y t u l , int ra z y = l ) ; b. Ż adnych. Tylko prototypy zawierają i nformacj e o wartościach domyślnych. c. Tak, pod warunkiem, że zachowamy wartość domyślną dla parametru r a z y : =
vo i d p i o s e n ka ( ch a r * na zwa
" Śp i j
a n i e l e mój " ,
int razy
=
1) ;
3. Ż eby wyświetlić znak cudzysłowu, można użyć łańcucha " \ " " albo znaku ' " ' . Poniższe funkcje prezentuj ą obydwie metody: # i n c l u de < i o s t r eam . h > void iquote ( i nt n ) cout < < " \ " " < < n < < " \ " " ;
v o i d i qu o t e ( d o u b l e x ) I U
cout < < ' " ' < < x ) . Funkcja zaprzyj aźniona nie jest częścią klasy, j est więc wywoływana przez proste wywołanie funkcj i . Nie ma ona bezpo średniego dostępu do składowych klasy, musi więc do obiektu przekazanego jako argument stosować operator przynależności. Warto na przykład porównać odpowiedź do pytania I . z odpowiedzią do pytania 4. 3 . Musi być funkcj ą zaprzyjaźnioną, żeby posi adać dostęp do składowych prywatnych, nie musi jednak być funkcją zaprzyj aźnioną, żeby odwoływać się do składowych publicznych . 4. Oto prototyp funkcj i do umieszczenia w pliku definicj i klasy oraz definicja funkcj i d o umiesz
czenia w pliku metod: li prototyp
f r i e n d S t o n e w t ope r a t o r * ( do u b l e mu l t ,
con s t S tonewt & s ) ;
li definicja - niech wszystko zrobi konstruktor
S t o n e w t ope r a t o r * ( do u b l e mu l t ,
con s t S t on e w t & s )
re t u r n S t o n e w t ( mu l t * s . p o u n d s ) ;
5. Nie można przeciążyć pięciu następuj ących operatorów: si zeof * ? 6.
:
Te operatory muszą być zdefiniowane w postaci funkcj i składowych.
7. Oto możliwy prototyp oraz definicja: li prototyp oraz definicja typu inline
ope r a t o r dou b l e
()
{ r e t u r n ma g ; J
Warto j ednak zaznaczyć, że bardziej sensowne j est korzystanie z metody magva l ( ) niż definiowanie tej funkcj i konwersj i .
Odpowiedzi do pytań z rozdziału 1 2 I.
a. Składnia j est poprawna, lecz konstruktor pozostawia niezainicjal izowany wskaźnik s t r . Konstruktor powinien albo przypisać mu wartość NULL albo zainicjal izować g o z a po mocą operatora n e w [ ] . b. Ten konstruktor nie tworzy nowego łańcucha, a j edynie kopiuj e adres starego. Powinien on używać operatora new [ ] oraz funkcj i st repy ( ) . 1 255
Dodatek J Odpowiedzi do pytań kontrolnych -
c. Kopiuj e łańcuch bez przydzielenia mu obszaru pamięci . Powinien użyć operatora n e w c h a r [ l e n + l ] , aby alokować odpowiednią i lość pamięci. 2. Po pierwsze, kiedy kończy się czas istnienia obiektu tego typu, dane wskazywane przez składową wskaźnikową obiektu pozostają w pamięci, a j ednocześnie stają się niedostępne, ponieważ wskaźnik na nie został utracony. Problem ten można rozwiązać, zwalniając pamięć alokowaną przez operator n e w w funkcjach konstruktora w destruktorze klasy. Po drugie, po zwolnieniu przez destruktor takiej pam ięci może się okazać, że będzie on próbował zwolnić ją dwukrotnie, jeśli w programie jeden taki obiekt posłuży do inicjalizacj i drugiego; a to dlatego, że w wyniku domyślnej inicjal izacj i j ednego obiektu drugim kopiowane są wartości wskaźników, lecz nie wskazywane przez nie dane, przez co otrzymuj emy dwa wskaźn iki na te same dane. Rozwiązaniem j est zdefiniowanie konstruktora kopiującego klasy, który za pewni kopiowanie wskazywanych danych przy inicj alizacj i . Po trzecie, przyp isanie jednego obiektu do drugiego może także doprowadzić do sytuacj i istnienia dwóch wskaźników odno szących się do tych samych danych. Rozwiązaniem j est przeciążenie operatora przypisania w taki sposób, żeby kopiował dane, a nie wskaźniki. 3. Język C++ definiuj e automatycznie następujące funkcj e składowe: • •
• •
•
konstruktor domyślny w przypadku niezdefiniowania żadnych konstruktorów, konstruktor kopiujący w przypadku niezdefiniowania takiego, operator przypisania w przypadku niezdefiniowania takiego, destruktor domyślny w przypadku niezdefiniowania takiego, operator adresu w przypadku niezdefiniowania takiego.
Konstruktor domyślny nie wykonuje żadnych operacj i , lecz umożl iwia deklarowanie tab lic i niezainicjalizowanych obiektów. Domyślny konstruktor kopiuj ący oraz domyślny operator przypisania stosuj ą przypisanie na poziomie składowych. Destruktor domyślny nie wykonu je żadnych operacj i . Niej awny operator adresu zwraca adres obiektu wywołuj ącego (tj . war tość wskaźnika t h i s ). 4. Składowa p e r s o n a l i t y powinna zostać zadeklarowana albo jako tablica znaków, albo jako wskaźnik na typ c h a r . Można j ą również zadeklarować j ako obiekt klasy S t r i n g lub s t r i n g . Deklaracja j est błędna, ponieważ metody nie są zadeklarowane j ako publiczne. Ponadto występuje kilka drobniej szych błędów. Oto możliwe rozwiązanie, przy czym zmia ny (poza usunięciami) oznaczono czcionką pogrubioną: #include
< i o s tream>
# include
us ing name space
s td ;
class nifty { li opcjonalnie char persona l i t y [ 40 ] ; int talents ; publ i c : li koniecznie li metody nifty ( } ; private :
1 256
li określ rozmiar tablicy
Odpowiedzi do pytań z rozdziału 1 2 n i f t y ( con s t c h a r * s ) ; friend o s t ream & ope r a t o r< < ( o s t r e am & o s ,
cons t n i f t y & n ) ;
li uwaga na zamykający średnik
};
nifty : : nifty ( ) {
pe r s o n a l i t y [ O ] talents O;
I
\O
I ;
=
n i f t y : : n i f t y ( cons t c h a r * s ) { s trcpy (persona l i ty ,
talents
=
s) ;
O;
o s t r e a m & ope r a t o r < < ( o s t r eam & o s , o s *, 1 1 67
>=, 208
»,
48, 1 3 5 , 1 07 5 , 1 1 60
A a.out, 23 abort(), 884 Abstract Data Type, 527 abstrakcja, 479, 483 abstrakcyjne klasy bazowe, 732. 734 funkcje czysto wirtualne, 733, 734 metodologia, 740 wymuszanie przestrzegan ia i nterfejsu, 740 zastosowanie, 735 abstrakcyjne typy danych, 527 kolej ka, 66 1 stos, 527 accumulate(}, 1 224 adaptatory, 985 adjacent_d i fference(), 1 225 adjacent_find(), 1 1 98, 1 200 adjustfield, I 068 adres dane const, 323 funkcje, 34 7 pamięć, 1 57 segmenty, 1 5 7 struktury, 339 zmienne, 1 5 1 , 36 1 A DT, 527 agregacja, 767 al fabetycznie uporządkowana l i sta słów, 1 025 algo.h, 974, I 020 algorithm, 974, I 020 algorytm, 1 3 , 1 4, 397, 962, 975, 1 0 1 9. 1 1 98 accu mulate(), 1 224 adjacent_difference( ), 1 22 5 adjacent_find(), 1 200 bi nary_search(), 1 2 1 6 copy(), 985, I 020, 1 204 copy_backward(), 1 205 count(), 1 20 I count_if(), 1 20 1 działający w miejscu, I 020
Indeks algorytm equal( ). 1 202 equal_range( ), 1 2 1 5 fi l i(), 1 207 fil l_n( ), 1 207 find( ), 1 1 99 tind _end(), 1 200 find_tirst_of(), 1 200 find_it( ), 1 1 99 for_each(). 1 1 99 generate(), 1 207 generate_n(). 1 207 grupy, 1 0 1 9 includes( ), 1 2 1 7 inner_product( ). 1 224 inplacc_merge(), 1 2 1 7 itcr_swap( ), 1 205 kopiujący. I 020 lexicographical_compare(), 1 222 !ower_bound(), 1 2 1 5 make_heap(), 1 220 max( ), 1 22 1 max_element( ), 1 22 2 merge(), 1 2 1 6 min(), 1 22 1 min_element( ), 1 22 1 mismatch(), 1 20 1 next_pennutation( ), I 022, 1 223 nth_element( ) , 1 2 1 4 operacje l iczbowe, 1 224 operacje na zbiorach, 1 2 1 7 operacje numeryczne, I 020 operacje sekwencyjne zmieniające wartość, I 020 partial_sort( ), 1 2 1 4 partial_sum( ), 1 22 5 partition(), 1 2 1 O pennutacja, 1 223 pop_heap( ), 1 220 prev_pennutation( ) , 1 223 przeszukiwanie. 979 push_heap( ). 1 220 random_shuffie(), 1 2 1 0 remove(), 1 207 rcmove_copy(), 1 208 remove_copy_i f(), 1 208 remove_if(), 1 208 replace( ), I 02 1 , 1 206 replace_copy( ), I 02 1 . 1 206 rcplace_copy_if( ). 1 207 replace_itO, 1 02 1 , 1 206 reverse(), 1 209
1 268
reverse_copy(), 1 209 rotate(), 1 2 1 O rotate_copy( ), 1 2 1 0 scalanie, 1 2 1 6 search(), 1 202 search_n( ), 1 202 sekwencyjny n iezmieniający wartośc i, I 020 set_ditference(), 1 2 1 9 set_i ntersection( ), 1 2 1 8 set_symmctric_difference( ), 1 2 1 9 set_union( ), 1 2 1 8 sort( ), I 022, 1 2 1 3 sort_ heap(), 1 22 1 sortowanie, I 020, 1 2 1 1 stable_partition(), 1 2 1 1 stable_sort( ), 1 2 1 3 STL, 975 swap(), 1 205 swap_ranges() , 1 205 transform(), 1 02 1 , 1 205 unique(}, 1 208 unique_copy( ), 1 209 upper_bound(), 1 2 1 5 wartośc i maksymalne, 1 22 1 wartości minimalne, 1 22 1 wieloprzebiegowy, 98 1 właściwośc i, I 020 wyszukiwanie bi narne, 1 2 1 5 alias typu, 220 a llocator, 963, 1 1 7 1 alokacja pamięci, 1 5 8, 1 79, 608 automatyczna, 1 80 dynamiczna, 1 80 new, 1 8 1 , 608 statyczna, 1 80 sterta, 1 79 wskaźniki, 1 8 1 wycieki pamięci, 1 8 1 alokatory, 963 alternatywa bitowa, 1 1 6 1 alternatywa logiczna, 250 alternatywne reprezentacje operatorów bitowych. 1 1 64 anal iza przypadki użyc ia, 1 1 3 1 szablon klasy, 822 and, 252, 260, 1 1 63 and_eq, 1 1 64 AN SI, 1 8 AN SI C, 1 9 AN SI/ISO, 1 8
Indeks AN S I/ISO C++, 1 23 1 aplikacje konsolowe Win32, 2 5 aplikacje M FC , 2 5 app, 1 1 07, 1 1 08, 1 1 09 append( ), 1 1 86 argc, 1 1 04 argumenty, 5 1 , 97, 303 faktyczne, 303 fon11alne, 303 niebędące typam i , 830 wi ersz pol ecci1, 1 1 04 argv, 1 1 02, 1 1 04 arytmetyka wskaźn i ków, 1 64, 1 69 dodawanie, 1 66 A S C I I , 8 1 , 26 1 , 947, 1 1 49 asembler, 1 3 assign( ) , 1 1 86. 1 1 94 at( ), 1 1 79, 1 1 95 atan(), 3 3 6 atan2(), 3 3 6 ate, 1 1 07 auto, 428, 443 auto_ptr, 920, 956, 1 03 7 , 1 23 7 intel igentne wskaźn iki, 96 1 new, 960 przyp isanie, 960 tworzenie obiektów, 958 używan ie obiektów, 957 zliczan ie re ferencj i , 96 1 automatyczna alokacja pamięc i , 1 80 automatyczna konwersja typów, 302, 5 8 5 , 595
B back( ), I OO I , 1 1 94 back_insert_iterator, 987, 989, 990 bad(), 1 080 bad_al loc, 907 bad_exception, 9 1 8 badbit, 1 079, 1 080 bajty, 7 1 , 1 1 4 1 BASIC, 1 4, 44 basic_istream, 1 046 basic_ostream, 1 046 bas ic_string, 1 1 72, 1 1 75 bcc3 2, 2 5 , 4 1 9 begin(), 965, 967, 979, 1 0 1 4, 1 022, 1 1 73, 1 1 93 białe znaki, 4 1 bibl ioteka standardowa, 1 9 język C , 1 9
biblioteki, 1 7, 20, 69 1 , 1 029 cctype, 26 1 DLL, 34 dynam icznie wiązane, 34 funkcje, 54 1/0, 1 045 klasy, 1 5 kompatyb ilność, 920 math, 575 nazwy zastrzeżone, 1 1 46 obsługa wyjątków, 920 STL, 1 9, 96 1 , 1 1 9 1 wej ście-wyj ście, 1 046 Big Endian, 1 1 42 binary, 1 1 07, 1 1 09 bi nary_search(), 1 2 1 6 B inaryPredicate, I 02 1 bind I st( ), I O 1 7 bi nd2nd(), I 0 1 7 bi nder I st( ), 1 O 1 7 bit, 7 1 , 1 1 4 1 błędu , 1 28 kasowan ie, 1 1 65 przełącza n ie, I 1 65 sprawdzanie wartości , 1 1 66 ustawianie, 1 1 65 bitand, 1 1 64 bitmask, 1 066 bitor, 1 1 64 bitowe operatory logiczne, 1 1 6 1 bitset, 992, I 066 blok, 1 80, 202 catch, 887, 903 try, 887 zasięg, 426 błądzenie losowe, 580 błędy niedomiar, 906 obsługa, 884 ochrona pam ięci, 6 1 6 wyj ątki, 883 zwracan ie kodu, 8 8 5 bool, 89, 528 boolalpha, 1 9 1 , 1 066, 1 067, 1 072 Borland C++ Bui lder, 26 Borland C++ B u i lderX, 24 braki pam ięc i , 1 60 break, 268, 270, 27 1 , 895 budowa programu, 20 bufory, I 043 , I 044 opróżn ianie, I 044, I 055 Build, 2 5 Build All, 25
1 26 9
Indeks
c c. 1 3 wejście, 3 1 wyjście, 3 1 c_str( ). 9 5 5 . 1 1 0 1 . 1 1 73 C++, 1 1 , 1 5 , 24 białe zna ki. 4 1 dynam iczny przydział pamięci, 607 dziedziczenie, 69 1 elementy języka, 4 1 fonnatowanie kodu źródłowego. 4 ł funkcje, 5 1 , 295 historia. 1 2 instrukcje, 43 instrukcje warunkowe, 243 interfej s, 48 1 klasy, 49, 479, 480 komentarze, 34 konwersja typów, ł 03 leksemy alternatywne, ł ! 46 main( ), 3 I , 3 2 model pam ięci, 4 1 7 nazwy zastrzeżone. 1 ł 46 obiekty, 49, 482 obl iczenia arytmetyczne. 97 obsługa wej ścia-wyjścia, ł 04 ł , 1 043 pętle. ł 86 pochodzenie, 1 6 preproccsor, 3 5 program, 20, 29, 3 1 programowanie obiektowe, 477, 484 programowanie ogólne, 1 6 przec iążanie operatorów, 1 O 1 przenośność. 1 8 przestrzen i e nazw, 3 7 , 4 1 7 , 452 RTTI , 920 słowa kl uczowe, 5 8 , 1 1 45 słowa zastrzeżone, 1 1 45 standardowa biblioteka szablonów, 8 1 5 standardy, 1 8 STL, 96 1 styl kodu źródłowego, 42 szablony, 8 I 5 typy danych, 67 wielokrotne użyc ie kodu, 767 wyj ątki, 883 wyrażenia, 97 wyrażenia logiczne, 250 zaprzyj aźnianie, 863 C+ + AN S !/J SO. 1 8 C + + Bui lderX, 24
1 270
C++ Console Carbon, 27 C99, 77 capacity(), 954, 1 1 73 , 1 1 76, 1 1 95 case, 265 catch, 887, 889 cc, 22 CC, 22, 23 ccommand( ), ł 1 05 cctype, 26 1 cechy typu znakowego, 1 046 cerr, 489, 1 046 ctloat, 93 cfront, 22 CGA, 79 char, 70, 78, 8 1 , 82, 1 1 8 CHAR_BIT, 72, 74, 1 1 46 CHAR_MAX, 74 CHAR_M IN. 74 char_traits, 1 1 72 char_type. 1 1 72 chronione dziedziczenie wielokrotne, 792 chronione składowe, 729 chroniony poziom dostępu, 73 1 ciągi znaków, 38, 939 cin, 36, 47, 48, 49, 54, 224, 278. 1 046. 1 075 e lear( ), 1 2 8 dane wejściowe, 1 075, 1 07 7 eof(), 1 083 excepti ons(), 1 08 1 funkcje wejścia formatowanego, I 076 get( ), 26, 30, 1 26, 225, 227, 23 1 , 234, 1 08 5 , 1 088, 1 089 get(ch), 234, 1 088 getl ine(), 1 25 , 1 27, 944 hex, 1 076 łańcuchy znaków, 1 24 operator>>(), 945, 1 075, 1 077 peek(), 1 094 read(), ł 093 stany strumienia, 1 079 znaki niewidoczne, I 077 claim(), 1 1 89 class, 394, 482, 830 elear(), 1 2 8, 1 080, 1 1 79, 1 ł 94, 1 1 97 c l i m its, 72, 7 3 , 678 clk_tck, 220 clock(), 2 1 9 CLOCKS, 2 1 9 cłocks_per_sec, 220 clog, 1 046 close(), 282, 1 1 OO cmath, 53, 54, 5 7 5
Indeks CodeWarrior, 27 Comeau, 26 compare( ), 1 1 84 Compi le, 25 compl, 1 1 64 concept, 984 const, 90, 9 1 . 1 95 , 2 5 5 , 3 1 2 . 3 1 6, 322, 3 2 3 , 324, 3 70, 443 , 444, 6 1 2, 755, 93 1 extem, 445 łączenie wewnętrzne, 445 metody, 5 1 0 przydział zmiennej , 444 wskaźn iki, 322 zwracanie referencj i , 375 const_cast, 3 2 3 , 93 1 const_iterator, 1 1 72, 1 1 9 1 const_reference, 1 1 9 1 const_reverse_iterator, 1 1 72 continue, 270. 27 1 , 272, 890 copy(), 985, 986, 990, 1 0 1 9, 1 020, 1 1 89, 1 204 copy_backward( ), 1 205 count( ), 1 026, 1 1 97, 1 1 98, 1 20 1 count_if(), 1 1 98, 1 20 I cout, 29, 36, 38, 46, 49, 54, 1 52, 1 046, 1 048 fili(), I 062 tlush, I 055 formatowanie danych wyjśc iowych, I 056 hex, 1 05 9 operator