1 - The C++ Programming Language - Bjarne Stroustrup - Hu

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 1315

I.

ktet
A kiadvny a kvetkez angol eredeti alapjn kszlt:
Bjarne Stroustrup: The C++ Programming Language Special Edition
Copyright 2000 by AT&T. All rights reserved. No part of this book, including interior desing, cover
design, and icons, may be reproduced or transmitted in any form, by any means (electronic,
photocopying, recording, or otherwise) without the prior written permission of the publisher.
Trademarked names appear throughout this book. Rather than list the names and entities that own
the trademarks or insert a trademark symbol with each mention of the trademarked name, the
publisher states that it is using the names for editorial purposes only and to the benefit of the
trademark owner, with no intention of infringing upon that trademark.
A szerz s a kiad a lehet legnagyobb krltekintssel jrt el e kiadvny elksztsekor. Sem
a szerz, sem a kiad nem vllal semminem felelssget vagy garancit a knyv tartalmval, teljes-
sgvel kapcsolatban. Sem a szerz, sem a kiad nem vonhat felelssgre brmilyen baleset vagy
kresemny miatt, mely kzvetve vagy kzvetlenl kapcsolatba hozhat e kiadvnnyal.
Fordts s magyar vltozat 2001 Szy Gyrgy, Kiskapu Kft. Minden jog fenntartva!
Translation and Hungarian edition 2001 Gyrgy Szy, Kiskapu Kft. All rights reserved!
Sorozatszerkeszt: Szy Gyrgy Mszaki szerkeszt: Csutak Hoffmann Levente
Szakmai lektor: Porkolb Zoltn Lektor: Rzmves Lszl
Fordtk: Balzs Ivn Jzsef, Bein Kornl, Ludnyi Levente, Marcinkovics Tams
Felels kiad a Kiskapu Kft. gyvezet igazgatja
2001 Kiskapu Kft.
1081 Budapest Npsznhz u. 31.
Tel: (+36-1) 303-9119, (+36-1) 477-0443 Fax: (+36-1) 303-1619
http://www.kiskapu.hu/
e-mail: kiskapu@kiskapu.hu
Els ktet ISBN: 963 9301 18 3
sszest ISBN: 963 9301 17 5
Kszlt a debreceni Kinizsi nyomdban
Felels vezet: Brds Jnos
Tartalomjegyzk
Bevezets
1. fejezet Megjegyzsek az olvashoz
1.1. A knyv szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.1. Pldk s hivatkozsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.1.2. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1.3. Megjegyzs az egyes C++-vltozatokhoz . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2. Hogyan tanuljuk a C++-t? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.3. A C++ jellemzi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.3.1. Hatkonysg s szerkezet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3.2. Filozfiai megjegyzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.4. Trtneti megjegyzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.5. A C++ hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
1.6. C s C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
1.6.1. Javaslatok C programozknak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
1.6.2. Javaslatok C++ programozknak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.7. Programozsi megfontolsok a C++-ban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.8. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.8.1. Hivatkozsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2. fejezet Kirnduls a C++-ban
2.1. Mi a C++? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.2. Programozsi megkzeltsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.3. Eljrskzpont programozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.3.1. Vltozk s aritmetika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.3.2. Elgazsok s ciklusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.3.3. Mutatk s tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.4. Modulris programozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.4.1. Kln fordts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.4.2. Kivtelkezels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.5. Elvont adatbrzols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.5.1. Tpusokat ler modulok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.5.2. Felhasznli tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
2.5.3. Konkrt tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.5.4. Absztrakt tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.5.5. Virtulis fggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
2.6. Objektumorientlt programozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.6.1. Problmk a konkrt tpusokkal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2.6.2. Osztlyhierarchik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2.7. ltalnostott programozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.7.1. Trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.7.2. ltalnostott algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
2.8. Utirat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.9. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
3. fejezet Kirnduls a standard knyvtrban
3.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
3.2. Hell, vilg! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
3.3. A standard knyvtr nvtere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
3.4. Kimenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
3.5. Karakterlncok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
3.5.1.C stlus karakterlncok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.6. Bemenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.7. Trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.7.1. Vektor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
3.7.2. Tartomnyellenrzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
3.7.3. Lista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.7.4. Asszociatv tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3.7.5. Szabvnyos trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.8. Algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
3.8.1. Bejrk hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
3.8.2. Bejrtpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
3.8.3. Bemeneti s kimeneti bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
3.8.4. Bejrsok s prediktumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
3.8.5. Tagfggvnyeket hasznl algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . 81
3.8.6. A standard knyvtr algoritmusai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.9. Matematika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
3.9.1. Komplex szmok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.9.2. Vektoraritmetika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
3.9.3. Alapszint numerikus tmogats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.10. A standard knyvtr szolgltatsai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.11. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
x
Els rsz Alapok
4. fejezet Tpusok s deklarcik
4.1. Tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4.1.1. Alaptpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.2. Logikai tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
4.3. Karaktertpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
4.3.1. Karakterliterlok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
4.4. Egsz tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
4.4.1. Egsz literlok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.5. Lebegpontos tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.5.1. Lebegpontos literlok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
4.6. Mretek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
4.7. Void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
4.8. Felsorol tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
4.9. Deklarcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4.9.1. A deklarcik szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
4.9.2. Tbb nv bevezetse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
4.9.3. Nevek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
4.9.4. Hatkrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.9.5. Kezdeti rtkads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
4.9.6. Objektumok s balrtkek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
4.9.7. Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.10. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
4.11. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
5. fejezet Mutatk, tmbk s struktrk
5.1. Mutatk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
5.1.1. Nulla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
5.2. Tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
5.2.1. Tmbk feltltse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
5.2.2. Karakterliterlok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
5.3. Tmbkre hivatkoz mutatk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
5.3.1. Tmbk bejrsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
5.4. Konstansok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5.4.1. Mutatk s konstansok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.5. Referencik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
5.6. Void-ra hivatkoz mutatk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
5.7. Struktrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
5.7.1. Egyenrtk tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
5.8. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
5.9. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
xi
6. fejezet Kifejezsek s utastsok
6.1. Egy asztali szmolgp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.1.1. Az elemz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.1.2. A bemeneti fggvny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
6.1.3. Alacsonyszint bemenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
6.1.4. Hibakezels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
6.1.5. A vezrl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
6.1.6. Fejllomnyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
6.1.7. Parancssori paramterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
6.1.8. Megjegyzs a stlussal kapcsolatban . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
6.2. Opertorok ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
6.2.1. Eredmnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
6.2.2. Kirtkelsi sorrend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
6.2.3. Az opertorok sorrendje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
6.2.4. Bitenknti logikai mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
6.2.5. Nvels s cskkents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
6.2.6. Szabad tr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
6.2.6.1. Tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
6.2.6.2. Memria-kimerls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
6.2.7. Meghatrozott tpuskonverzik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
6.2.8. Konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
6.3. Utastsok ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
6.3.1. Deklarcik mint utastsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
6.3.2. Kivlaszt utastsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
6.3.2.1. Deklarcik felttelekben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
6.3.3. Ciklusutastsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
6.3.3.1. Deklarcik a for utastsban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
6.3.4. Goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
6.4. Megjegyzsek s behzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
6.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
6.6. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
7. fejezet Fggvnyek
7.1. Fggvnydeklarcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
7.1.1. Fggvnydefincik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
7.1.2. Statikus vltozk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
7.2. Paramtertads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
7.2.1. Tmb paramterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
7.3. Visszatrsi rtk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
7.4. Tlterhelt fggvnynevek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
7.4.1. A tlterhels s a visszatrsi tpus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.4.2. A tlterhels s a hatkr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
7.4.3. A tbbrtelmsg kzi feloldsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
7.4.4. Tbb paramter feloldsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.5. Alaprtelmezett paramterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
7.6. Nem meghatrozott szm paramter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
xii
7.7. Fggvnyre hivatkoz mutatk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
7.8. Makrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
7.8.1. Feltteles fordts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
7.9. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
7.10. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
8. fejezet Nvterek s kivtelek
8.1. Modulok s felletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
8.2. Nvterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
8.2.1. Minstett nevek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
8.2.2. Using deklarcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
8.2.3. Using direktvk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
8.2.4. Tbb fellet hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.2.4.1. Fellettervezsi mdszerek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
8.2.5. A nvtkzsek elkerlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
8.2.5.1. Nvtelen nvterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
8.2.6. Nevek keresse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
8.2.7. Nvtr-lnevek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
8.2.8. Nvterek sszefzse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
8.2.8.1. Kivlaszts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
8.2.8.2. sszefzs s kivlaszts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
8.2.9. Nvterek s rgi kdok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
8.2.9.1. Nvterek s a C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
8.2.9.2. Nvterek s tlterhels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
8.2.9.3. A nvterek nyitottak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
8.3. Kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
8.3.1. Dobs s elkaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
8.3.2. A kivtelek megklnbztetse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
8.3.3. Kivtelek a szmolgpben . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
8.3.3.1. Ms hibakezel mdszerek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
8.4. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
8.5. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
9. fejezet Forrsfjlok s programok
9.1. Kln fordts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
9.2. sszeszerkeszts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
9.2.1. Fejllomnyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
9.2.2. A standard knyvtr fejllomnyai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
9.2.3. Az egyszeri definils szablya . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
9.2.4. sszeszerkeszts nem C++ kddal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
9.2.5. Az sszeszerkeszts s a fggvnyekre hivatkoz mutatk . . . . . . . . . . . 275
9.3. Fejllomnyok hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
9.3.1. Egyetlen fejllomnyos elrendezs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
9.3.2. Tbb fejllomnyos elrendezs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
9.3.2.1. A szmolgp egyb moduljai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
9.3.2.2. A fejllomnyok hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
9.3.3. llomny-rszemek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
xiii
9.4. Programok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
9.4.1. Kezdeti rtkads nem loklis vltozknak . . . . . . . . . . . . . . . . . . . . . . . . 288
9.4.1.1. A program befejezse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
9.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
9.6. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Msodik rsz Absztrakcis mdszerek
10. fejezet Osztlyok
10.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
10.2. Osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
10.2.1. Tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
10.2.2. Az elrhetsg szablyozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 298
10.2.3. Konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
10.2.4. Statikus tagok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
10.2.5. Osztly tpus objektumok msolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
10.2.6. Konstans tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
10.2.7. nhivatkozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
10.2.7.1. Fizikai s logikai konstansok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
10.2.7.2. A mutable minst . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
10.2.8. Struktrk s osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
10.2.9. Osztlyon belli fggvnydefincik . . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
10.3. Hatkony felhasznli tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
10.3.1. Tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
10.3.2. Segdfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
10.3.3. Opertorok tlterhelse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
10.3.4. A konkrt osztlyok jelentsge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
10.4. Objektumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
10.4.1. Destruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
10.4.2. Alaprtelmezett konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
10.4.3. Ltrehozs s megsemmists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
10.4.4. Loklis vltozk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
10.4.4.1. Objektumok msolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
10.4.5. A szabad tr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
10.4.6. Osztly tpus tagok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
10.4.6.1. A tagok szksgszer kezdeti rtkadsa . . . . . . . . . . . . . . . . . . . . . . . . 327
10.4.6.2. Konstans tagok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
10.4.6.3. Tagok msolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
10.4.7. Tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
10.4.8. Loklis statikus adatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
10.4.9. Nem loklis adatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
10.4.10. Ideiglenes objektumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 335
10.4.11. Az objektumok elhelyezse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
10.4.12. Unik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
10.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
10.6. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
xiv
11. fejezet Opertorok tlterhelse
11.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
11.2. Opertor fggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
11.2.1. Egy- s ktoperandus mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
11.2.2. Az opertorok elre meghatrozott jelentse . . . . . . . . . . . . . . . . . . . . . 347
11.2.3. Opertorok s felhasznli tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
11.2.4. Nvterek opertorai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
11.3. Komplex szm tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
11.3.1. Tag s nem tag opertorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
11.3.2. Vegyes md aritmetika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
11.3.3. Kezdeti rtkads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
11.3.4. Msols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
11.3.5. Konstruktorok s konverzik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
11.3.6. Literlok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
11.3.7. Kiegszt tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
11.3.8. Segdfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
11.4. Konverzis opertorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
11.4.1. Tbbrtelmsg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
11.5. Bart fggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
11.5.1. A bart fggvnyek elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
11.5.2. Bartok s tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
11.6. Nagy objektumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
11.7. Alapvet opertorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
11.7.1. Explicit konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
11.8. Indexels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
11.9. Fggvnyhvs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
11.10. Indirekci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
11.11. Nvels s cskkents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
11.12. Egy karakterlnc osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
11.13. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
11.14. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
12. fejezet Szrmaztatott osztlyok
12.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
12.2. Szrmaztatott osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
12.2.1. Tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
12.2.2. Konstruktorok s destruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402
12.2.3. Msols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
12.2.4. Osztlyhierarchik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
12.2.5. Tpusmezk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
12.2.6. Virtulis fggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
12.3. Absztrakt osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
12.4. Osztlyhierarchik tervezse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 413
12.4.1. Hagyomnyos osztlyhierarchia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
12.4.1.1. Brlat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
xv
12.4.2. Absztrakt osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
12.4.3. Egyb megvalstsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
12.4.3.1. Brlat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
12.4.4. Az objektumok ltrehozsnak adott helyre korltozsa . . . . . . . . . . . . . 424
12.5. Osztlyhierarchik s absztrakt osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
12.6. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
12.7. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
13. fejezet Sablonok
13.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431
13.2. Egy egyszer karakterlnc sablon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
13.2.1. Sablonok meghatrozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 435
13.2.2. Sablonok pldnyostsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 436
13.2.3. Sablon paramterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 437
13.2.4. Tpusok egyenrtksge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
13.2.5. Tpusellenrzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439
13.3. Fggvnysablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 440
13.3.1. A fggvnysablonok paramterei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 441
13.3.2. Fggvnysablonok tlterhelse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
13.4. Eljrsmd megadsa sablonparamterekkel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446
13.4.1. Alaprtelmezett sablonparamterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . 447
13.5. Specializci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
13.5.1. Specializcik sorrendje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
13.5.2. Fggvnysablon specializci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
13.6. rklds s sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
13.6.1. Paramterezs s rklds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
13.6.2. Tag sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
13.6.3. rkldsi viszonyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459
13.6.3.1. Sablonok konverzija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 460
13.7. A forrskd szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
13.8. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463
13.9. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
14. fejezet Kivtelkezels
14.1. Hibakezels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467
14.1.1. A kivtelek ms megkzeltsei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 470
14.2. A kivtelek csoportostsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
14.2.1. Szrmaztatott kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 473
14.2.2. sszetett kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474
14.3. A kivtelek elkapsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 475
14.3.1. A kivtelek tovbbdobsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476
14.3.2. Minden kivtel elkapsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
14.3.2.1. A kezelk sorrendje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
14.4. Erforrsok kezelse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
14.4.1. Konstruktorok s destruktorok hasznlata . . . . . . . . . . . . . . . . . . . . . . . 481
14.4.2. Auto_ptr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483
xvi
14.4.3. Figyelmeztets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
14.4.4. A kivtelek s a new opertor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485
14.4.5. Az erforrsok kimerlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 486
14.4.6. Kivtelek konstruktorokban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
14.4.6.1. Kivtelek s a tagok kezdeti rtkadsa . . . . . . . . . . . . . . . . . . . . . . . . . 490
14.4.6.2. Kivtelek s msols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
14.4.7. Kivtelek destruktorokban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 491
14.5. Kivtelek, amelyek nem hibk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 492
14.6. Kivtelek specifikcija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493
14.6.1. A kivtelek ellenrzse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 494
14.6.2. Vratlan kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 496
14.6.3. Kivtelek lekpezse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
14.6.3.1. Kivtelek felhasznli lekpezse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 497
14.6.3.2. Kivtelek tpusnak visszalltsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
14.7. El nem kapott kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 499
14.8. A kivtelek s a hatkonysg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
14.9. A hibakezels egyb mdjai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 503
14.10. Szabvnyos kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 505
14.11. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508
14.12. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 509
15. fejezet Osztlyhierarchik
15.1. Bevezets s ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 511
15.2. Tbbszrs rklds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 512
15.2.1. A tbbrtelmsg feloldsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 514
15.2.2. rklds s using deklarcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 515
15.2.3. Ismtld bzisosztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517
15.2.3.1. Fellrs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518
15.2.4. Virtulis bzisosztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 520
15.2.4.1. Virtulis bzisosztlyok programozsa . . . . . . . . . . . . . . . . . . . . . . . . . . 521
15.2.5. A tbbszrs rklds hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . 523
15.2.5.1. A virtulis bzisosztlyok fggvnyeinek fellrsa . . . . . . . . . . . . . . . . 526
15.3. Hozzfrs-szablyozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 528
15.3.1. Vdett tagok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 530
15.3.1.1. A vdett tagok hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 531
15.3.2. Bzisosztlyok elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 532
15.3.2.1. A tbbszrs rklds s az elrhetsg . . . . . . . . . . . . . . . . . . . . . . . 533
15.3.2.2. A using deklarcik s az elrhetsg . . . . . . . . . . . . . . . . . . . . . . . . . . 534
15.4. Futsi idej tpusinformci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 535
15.4.1. Dynamic_cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 536
15.4.1.1. Referencik dinamikus tpusknyszertse . . . . . . . . . . . . . . . . . . . . . . . 539
15.4.2. Osztlyhierarchik bejrsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 540
15.4.2.1. Statikus s dinamikus konverzi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 542
15.4.3. Osztlyobjektumok felptse s megsemmistse . . . . . . . . . . . . . . . . 543
xvii
15.4.4. Typeid s kiterjesztett tpusinformci . . . . . . . . . . . . . . . . . . . . . . . . . . 544
15.4.4.1. Kiterjesztett tpusinformci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 546
15.4.5. Az RTTI helyes s helytelen hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . 547
15.5. Tagra hivatkoz mutatk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 549
15.5.1. Bzis- s szrmaztatott osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 552
15.6. A szabad tr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 553
15.6.1. Memriafoglals tmbk szmra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
15.6.2. Virtulis konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 556
15.7. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 558
15.8. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 559
xviii
II. ktet
A kiadvny a kvetkez angol eredeti alapjn kszlt:
Bjarne Stroustrup: The C++ Programming Language Special Edition
Copyright 2000 by AT&T. All rights reserved. No part of this book, including interior desing, cover
design, and icons, may be reproduced or transmitted in any form, by any means (electronic,
photocopying, recording, or otherwise) without the prior written permission of the publisher.
Trademarked names appear throughout this book. Rather than list the names and entities that own
the trademarks or insert a trademark symbol with each mention of the trademarked name, the
publisher states that it is using the names for editorial purposes only and to the benefit of the
trademark owner, with no intention of infringing upon that trademark.
A szerz s a kiad a lehet legnagyobb krltekintssel jrt el e kiadvny elksztsekor. Sem
a szerz, sem a kiad nem vllal semminem felelssget vagy garancit a knyv tartalmval, teljes-
sgvel kapcsolatban. Sem a szerz, sem a kiad nem vonhat felelssgre brmilyen baleset vagy
kresemny miatt, mely kzvetve vagy kzvetlenl kapcsolatba hozhat e kiadvnnyal.
Fordts s magyar vltozat 2001 Szy Gyrgy, Kiskapu Kft. Minden jog fenntartva!
Translation and Hungarian edition 2001 Gyrgy Szy, Kiskapu Kft. All rights reserved!
Sorozatszerkeszt: Szy Gyrgy Mszaki szerkeszt: Csutak Hoffmann Levente
Szakmai lektor: Porkolb Zoltn Lektor: Rzmves Lszl
Fordtk: Balzs Ivn Jzsef, Bein Kornl, Ludnyi Levente, Marcinkovics Tams
Felels kiad a Kiskapu Kft. gyvezet igazgatja
2001 Kiskapu Kft.
1081 Budapest Npsznhz u. 31.
Tel: (+36-1) 303-9119, (+36-1) 477-0443 Fax: (+36-1) 303-1619
http://www.kiskapu.hu/
e-mail: kiskapu@kiskapu.hu
Msodik ktet ISBN: 963 9301 19 1
sszest ISBN: 963 9301 17 5
Kszlt a debreceni Kinizsi nyomdban
Felels vezet: Brds Jnos
A knyv az Oktatsi Minisztrium tmogatsval, a Felsoktatsi Plyzatok Irodja ltal
lebonyoltott felsoktatsi tanknyvtmogatsi program keretben jelent meg.
Tartalomjegyzk
Harmadik rsz A standard knyvtr
16. fejezet A knyvtr szerkezete s a trolk
16.1. A standard knyvtr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 563
16.1.1. Tervezsi korltozsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 565
16.1.2. A standard knyvtr szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566
16.1.3. Nyelvi elemek tmogatsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 571
16.2. A trolk szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
16.2.1. Egyedi cl trolk s bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 572
16.2.2. Trolk kzs ssel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 576
16.2.3. A standard knyvtr troli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 581
16.3. A vektor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
16.3.1. Tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 583
16.3.2. Bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 585
16.3.3. Az elemek elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 587
16.3.4. Konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589
16.3.5. Veremmveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 593
16.3.6. Listamveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 595
16.3.7. Az elemek kivlasztsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599
16.3.8. Mret s kapacits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 600
16.3.9. Tovbbi tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
16.3.10. Segdfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 603
16.3.11. Vector<bool> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 604
16.4. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 605
16.5. Feladatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 606
17. fejezet Szabvnyos trolk
17.1. A szabvnyos trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 609
17.1.1. Mveletek ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 610
17.1.2. Trolk ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 615
17.1.3. brzols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 616
17.1.4. Megktsek az elemekre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 618
17.1.4.1. sszehasonltsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619
17.1.4.2. Tovbbi relcis mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 621
17.2. Sorozatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
17.2.1. A vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 622
17.2.2. A list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
17.2.2.1. thelyezs, rendezs, sszefsls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 623
17.2.2.2. Mveletek a lista elejn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
17.2.2.3. Tovbbi mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 626
17.2.3. A deque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
17.3. Sorozat-talaktk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629
17.3.1. A stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 630
17.3.2. A queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 632
17.3.3. A priority_queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 633
17.4. Asszociatv trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 636
17.4.1. A map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
17.4.1.1. Tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 637
17.4.1.2. Bejrk s prok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 638
17.4.1.3. Indexels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 640
17.4.1.4. Konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
17.4.1.5. sszehasonltsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 642
17.4.1.6. Mveletek asszociatv tmbkkel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644
17.4.1.7. Listamveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 646
17.4.1.8. Tovbbi mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 648
17.4.2. A multimap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649
17.4.3. A set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 650
17.4.4. A multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 651
17.5. Majdnem-trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
17.5.1. Karakterlncok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
17.5.2. A valarray . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
17.5.3. Bithalmazok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 652
17.5.3.1. Konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 654
17.5.3.2. Bitkezel mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 655
17.5.3.3. Tovbbi mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 656
17.5.4. Beptett tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657
17.6. j trolk ltrehozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
17.6.1. A hash_map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659
17.6.2. brzols s ltrehozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 661
17.6.2.1. Keressek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 663
17.6.2.2. Trls s tmretezs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 665
17.6.2.3. Hasts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 666
17.6.3. Tovbbi hastott asszociatv trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
17.7. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 668
17.8. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 669
18. fejezet Algoritmusok s fggvnyobjektumok
18.1. Bevezet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 673
18.2. A standard knyvtr algoritmusainak ttekintse . . . . . . . . . . . . . . . . . . . . . . . . . . 674
18.3. Sorozatok s trolk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 681
18.3.1. Bemeneti sorozatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 682
18.4. Fggvnyobjektumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 684
viii
18.4.1. Fggvnyobjektumok bzisosztlyai . . . . . . . . . . . . . . . . . . . . . . . . . . . . 685
18.4.2. Prediktumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 686
18.4.2.1. A prediktumok ttekintse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 687
18.4.3. Aritmetikai fggvnyobjektumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
18.4.4. Lektk, talaktk s tagadk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 689
18.4.4.1. Lektk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 691
18.4.4.2. Tagfggvny-talaktk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
18.4.4.3. Fggvnyre hivatkoz mutatk talakti . . . . . . . . . . . . . . . . . . . . . . . . 694
18.4.4.4. Tagadk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 695
18.5. Nem mdost algoritmusok sorozatokon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
18.5.1. A for_each . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 697
18.5.2. A find fggvnycsald . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699
18.5.3. A count() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 700
18.5.4. Egyenlsg s eltrs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 701
18.5.5. Keress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 703
18.6. Mdost algoritmusok sorozatokra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 704
18.6.1. Msols . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 705
18.6.2. Transzformcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 707
18.6.3. Ismtld elemek trlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 709
18.6.3.1. Rendezsi szempont . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 711
18.6.4. Helyettests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 712
18.6.5. Trls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714
18.6.6. Feltlts s ltrehozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 714
18.6.7. Megfordts s elforgats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 715
18.6.8. Elemek felcserlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
18.7. Rendezett sorozatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 717
18.7.1. Rendezs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 718
18.7.2. Binris keress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719
18.7.3. sszefsls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
18.7.4. Feloszts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 721
18.7.5. Halmazmveletek sorozatokon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 722
18.8. A kupac . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 724
18.9. Minimum s maximum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 725
18.10. Permutcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 726
18.11. C stlus algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 727
18.12. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 728
18.13. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 729
19. fejezet Bejrk s memriafoglalk
19.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
19.2. Bejrk s sorozatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 734
19.2.1. A bejrk mveletei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735
19.2.2. A bejr jellemzi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 737
19.2.3. Bejr-kategrik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739
19.2.4. Beszr bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 742
19.2.5. Visszafel halad bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 744
19.2.6. Adatfolyamok bejri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 745
19.2.6.1. tmeneti trak adatfolyamokhoz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 748
ix
19.3. Ellenrztt bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 750
19.3.1. Kivtelek, trolk s algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 756
19.4. Memriafoglalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
19.4.1. A szabvnyos memriafoglal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 758
19.4.2. Felhasznli memriafoglalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 763
19.4.3. ltalnostott memriafoglalk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 766
19.4.4. Elksztetlen memria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 768
19.4.5. Dinamikus memria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 771
19.4.6. C stlus helyfoglals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 772
19.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 773
19.6. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774
20. fejezet Karakterlncok
20.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 775
20.2. Karakterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776
20.2.1. Karakterjellemzk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777
20.3. A basic_string osztly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 780
20.3.1. Tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 781
20.3.2. Bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 782
20.3.3. Az elemek elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 783
20.3.4. Konstruktorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 784
20.3.5. Hibk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 786
20.3.6. rtkads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 788
20.3.7. talakts C stlus karakterlncra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 789
20.3.8. sszehasonlts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 791
20.3.9. Beszrs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 793
20.3.10. sszefzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 795
20.3.11. Keress . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 796
20.3.12. Csere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797
20.3.13. Rszlncok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 799
20.3.14. Mret s kapacits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
20.3.15. Ki- s bemeneti mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 801
20.3.16. Felcserls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
20.4. A C standard knyvtra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 802
20.4.1. C stlus karakterlncok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 803
20.4.2. Karakterek osztlyozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 805
20.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 806
20.6. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 807
21. fejezet Adatfolyamok
21.1. Bevezet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 811
21.2. Kimenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 814
21.2.1. Kimeneti adatfolyamok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 816
21.2.2. Beptett tpusok kimenete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818
21.2.3. Felhasznli tpusok kimenete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 821
21.2.3.1. Virtulis kimeneti fggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 822
21.3. Bemenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
21.3.1. Bemeneti adatfolyamok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 823
21.3.2. Beptett tpusok bemenete . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 824
x
21.3.3. Az adatfolyam llapota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 827
21.3.4. Karakterek beolvassa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 829
21.3.5. Felhasznli tpusok beolvassa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 833
21.3.6. Kivtelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 834
21.3.7. Adatfolyamok sszektse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 836
21.3.8. rszemek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838
21.4. Formzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 839
21.4.1. Formzsi llapot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 840
21.4.1.1. Formzsi llapot lemsolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842
21.4.2. Egsz kimenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 842
21.4.3. Lebegpontos szm kimenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 843
21.4.4. Kimeneti mezk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 845
21.4.5. Mezk igaztsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 847
21.4.6. Mdostk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 848
21.4.6.1. Mdostk, melyek paramtereket vrnak . . . . . . . . . . . . . . . . . . . . . . . 850
21.4.6.2. Szabvnyos ki- s bemeneti mdostk . . . . . . . . . . . . . . . . . . . . . . . . . 851
21.4.6.3. Felhasznli mdostk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 853
21.5. Fjl- s karakterlnc-folyamok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 855
21.5.1. Fjlfolyamok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 856
21.5.2. Adatfolyamok lezrsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 859
21.5.3. Karakterlnc-folyamok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 860
21.6. Ki- s bemeneti tmeneti trak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 862
21.6.1. Kimeneti adatfolyamok s tmeneti trolk . . . . . . . . . . . . . . . . . . . . . . 863
21.6.2. Bemeneti adatfolyamok s tmeneti trolk . . . . . . . . . . . . . . . . . . . . . 865
21.6.3. Az adatfolyamok s az tmeneti trak kapcsolata . . . . . . . . . . . . . . . . . . 866
21.6.4. Adatfolyamok tmeneti trolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 867
21.7. Helyi sajtossgok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 873
21.7.1. Adatfolyam-visszahvsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 875
21.8. C stlus ki- s bemenet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876
21.9. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 880
21.10. Feladatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 881
22. fejezet Szmok
22.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
22.2. A szmtpusok korltozsai . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 886
22.2.1. Korltozsi makrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
22.3. Szabvnyos matematikai fggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 890
22.4. Vektormveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 892
22.4.1. Valarray ltrehozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 893
22.4.2. A valarray indexelse s az rtkads . . . . . . . . . . . . . . . . . . . . . . . . . . 894
22.4.3. Mveletek tagfggvnyknt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 896
22.4.4. Nem tagfggvnyknt megvalstott mveletek . . . . . . . . . . . . . . . . . . . 900
22.4.5. Szeletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 901
22.4.6. A slice_array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 904
22.4.7. Ideiglenes vltozk, msols, ciklusok . . . . . . . . . . . . . . . . . . . . . . . . . . 909
22.4.8. ltalnostott szeletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 912
22.4.9. Maszkok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 914
22.4.10. Indirekt tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
22.5. Komplex aritmetika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 915
xi
22.6. ltalnostott numerikus algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 918
22.6.1. Az accumulate() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 919
22.6.2. Az inner_product . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 920
22.6.3. Nvekmnyes vltozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 921
22.7. Vletlenszmok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 923
22.8. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 925
22.9. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 926
Negyedik rsz Tervezs a C++ segtsgvel
23.fejezet Fejleszts s tervezs
23.1. ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 931
23.2. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 932
23.3. Clok s eszkzk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 935
23.4. A fejlesztsi folyamat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 939
23.4.1. A fejleszts krforgsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 941
23.4.2. Tervezsi clok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 944
23.4.3. Tervezsi lpsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 946
23.4.3.1. Els lps: keressnk osztlyokat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 947
23.4.3.2. Msodik lps: hatrozzuk meg a mveleteket . . . . . . . . . . . . . . . . . . . 951
23.4.3.3. Harmadik lps: hatrozzuk meg az sszefggseket . . . . . . . . . . . . . . 953
23.4.3.4. Negyedik lps: hatrozzuk meg a felleteket . . . . . . . . . . . . . . . . . . . . 953
23.4.3.5. Osztlyhierarchik jraszervezse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 954
23.4.3.6. Modellek hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 956
23.4.4. Ksrletezs s elemzs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 957
23.4.5. Tesztels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 960
23.4.6. A programok karbantartsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 961
23.4.7. Hatkonysg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 962
23.5. Vezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963
23.5.1. jrahasznosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 963
23.5.2. Mret s egyensly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 965
23.5.3. Egynek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 967
23.5.4. Hibrid tervezs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 969
23.6. Jegyzetek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 971
23.7. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 973
24. fejezet Tervezs s programozs
24.1. ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 975
24.2. A tervezs s a programozsi nyelv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 976
24.2.1. Az osztlyok figyelmen kvl hagysa . . . . . . . . . . . . . . . . . . . . . . . . . . . 978
24.2.2. Az rkls elkerlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 981
24.2.3. A statikus tpusellenorzs figyelmen kvl hagysa . . . . . . . . . . . . . . . . . 982
24.2.4. A programozs elkerlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 985
24.2.5. Az osztlyhierarchik kizrlagos hasznlata . . . . . . . . . . . . . . . . . . . . . 987
24.3. Osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 988
24.3.1. Mit brzolnak az osztlyok? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 989
24.3.2. Osztlyhierarchik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 991
24.3.2.1. Osztlyhierarchin belli fggsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 994
24.3.3. Tartalmazsi kapcsolatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 996
xii
24.3.4. Tartalmazs s rkls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 998
24.3.4.1. Tag vagy hierarchia? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1002
24.3.4.2. Tartalmazs vagy hierarchia? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1003
24.3.5. Hasznlati kapcsolatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1005
24.3.6. Beprogramozott kapcsolatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1006
24.3.7. Osztlyon belli kapcsolatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1008
24.3.7.1. Invarinsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1009
24.3.7.2. Felttelezsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1011
24.3.7.3. Elfelttelek s utfelttelek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1015
24.3.7.4. Betokozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1016
24.4. Komponensek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1017
24.4.1. Sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1020
24.4.2. Fellet s megvalsts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1022
24.4.3. Kvr felletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1025
24.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1028
25. fejezet Az osztlyok szerepe
25.1. Az osztlyok fajti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1031
25.2. Konkrt tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1032
25.2.1. A konkrt tpusok jrahasznostsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1035
25.3. Absztrakt tpusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1037
25.4. Csompont-osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1041
25.4.1. Felletek mdostsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1044
25.5. Mveletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1047
25.6. Felletosztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1049
25.6.1. Felletek igaztsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1051
25.7. Ler osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1054
25.7.1. A lerk mveletei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1058
25.8. Keretrendszerek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059
25.9. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1061
25.10. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1062
Fggelkek s trgymutat
A. fggelk Nyelvtan
A.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1067
A.2. Kulcsszavak . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1068
A.3. Lexikai szablyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069
A.4. Programok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1073
A.5. Kifejezsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1074
A.6. Utastsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1079
A.7. Deklarcik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1080
A.7.1. Deklartorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1084
A.8. Osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1086
A.8.1. Szrmaztatott osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1087
A.8.2. Klnleges tagfggvnyek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1088
A.8.3. Tlterhels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089
A.9. Sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089
xiii
A.10. Kivtelkezels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091
A.11. Az elfeldolgoz direktvi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1091
B. fggelk Kompatibilits
B.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1095
B.2. C/C++ kompatibilits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1096
B.2.2. C program, ami nem C++ program . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1097
B.2.3. Elavult szolgltatsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1101
B.2.4. C++ program, ami nem C program . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1102
B.3. Rgebbi C++-vltozatok hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1103
B.3.1. Fejllomnyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1104
B.3.2. A standard knyvtr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1106
B.3.3. Nvterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107
B.3.4. Helyfoglalsi hibk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1107
B.3.5. Sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1108
B.3.6. A for utasts kezdrtk-ad rsze . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1110
B.4. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1111
B.5. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1112
C. fggelk Technikai rszletek
C.1. Bevezets s ttekints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1115
C.2. A szabvny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1116
C.3. Karakterkszletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1118
C.3.1. Korltozott karakterkszletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1118
C.3.2. Escape karakterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1120
C.3.3. Nagy karakterkszletek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1121
C.3.4. Eljeles s eljel nlkli karakterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1122
C.4. Az egsz literlok tpusa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1123
C.5. Konstans kifejezsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124
C.6. Automatikus tpuskonverzi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124
C.6.1. Kiterjesztsek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1124
C.6.2. Konverzik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1125
C.6.3. Szoksos aritmetikai konverzik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1129
C.7. Tbbdimenzis tmbk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1129
C.7.1. Vektorok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1130
C.7.3. Tbbdimenzis tmbk tadsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1133
C.8. Ha kevs a memria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135
C.8.1. Mezk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1135
C.8.2. Unik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1136
C.9. Memriakezels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1139
C.9.1. Automatikus szemtgyjts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1140
C.10. Nvterek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144
C.10.1. Knyelem s biztonsg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1144
C.10.2. Nvterek egymsba gyazsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1146
C.10.3. Nvterek s osztlyok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147
C.11. Hozzfrs-szablyozs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147
C.11.1. Tagok elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147
C.11.2. Bzisosztlyok elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1148
C.11.3. Tagosztlyok elrse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1150
xiv
C.11.4. Bartok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1151
C.12. Adattagokra hivatkoz mutatk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1152
C.13. Sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1153
C.13.1. Statikus tagok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1153
C.13.2. Bartok s sablonok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1154
C.13.3. Sablonok, mint sablonparamterek . . . . . . . . . . . . . . . . . . . . . . . . . . . 1155
C.13.4. Sablon fggvnyek paramtereinek levezetse . . . . . . . . . . . . . . . . . . 1156
C.13.5. A typename s a template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1157
C.13.6. Sablonok, mint minstk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1159
C.13.7. Pldnyosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1159
C.13.8. Nvkts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1160
C.13.9. Mikor van szksg specializcira? . . . . . . . . . . . . . . . . . . . . . . . . . . . 1169
C.13.10. Explicit pldnyosts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1170
C.14. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1171
D. fggelk Helyi sajtossgok
D.1. A kulturlis eltrsek kezelse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1173
D.1.1. A kulturlis eltrsek programozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1174
D.2. A locale osztly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1178
D.2.1. Nevestett loklok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1181
D.2.2. Loklok msolsa s sszehasonltsa . . . . . . . . . . . . . . . . . . . . . . . . . 1185
D.2.3. A global() s classic() loklok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1187
D.2.4. Karakterlncok sszehasonltsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1188
D.3. Jellemzk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1189
D.3.1. Jellemzk elrse a loklokban . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1191
D.3.2. Egyszer felhasznli facet-ek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1192
D.3.3. A loklok s jellemzk hasznlata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1196
D.4. Szabvnyos facet-ek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197
D.4.1. Karakterlncok sszehasonltsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1200
D.4.2. Szmok be- s kivitele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1205
D.4.3. Pnzrtkek be- s kivitele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1212
D.4.4. Dtum s id beolvassa s kirsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1220
D.4.5. Karakterek osztlyozsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1240
D.4.6. Karakterkd-talaktsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1246
D.4.7. zenetek . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1251
D.5. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1255
D.6. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1256
E. fggelk Kivtelbiztossg a standard knyvtrban
E.1. Bevezets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1259
E.2. Kivtelbiztossg . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1261
E.3. A kivtelbiztossgot megvalst eljrsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1266
E.3.1. Egy egyszer vektor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1267
E.3.2. A memria brzolsa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1271
E.3.3. rtkads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1273
E.3.4. A push_back() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1276
E.3.5. Konstruktorok s invarinsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1278
E.4. A szabvnyos trolk garancii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1284
E.4.1. Elemek beszrsa s trlse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1286
xv
E.4.2. Garancik s kompromisszumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1290
E.4.3. A swap() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1293
E.4.4. A kezdeti rtkads s a bejrk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294
E.4.5. Hivatkozsok elemekre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1294
E.4.6. Prediktumok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1296
E.5. A standard knyvtr tovbbi rszei . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1296
E.5.1. Karakterlncok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1297
E.5.2. Adatfolyamok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1297
E.5.3. Algoritmusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1298
E.5.4. A valarray s a complex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299
E.5.5. A C standard knyvtra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1299
E.6. Javaslatok a knyvtr felhasznli szmra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1300
E.7. Tancsok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303
E.8. Gyakorlatok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1304
I. Trgymutat 1307
xvi
Bevezets
Ez a bevezets ttekintst ad a C++ programozsi nyelv f fogalmairl, tulajdonsgairl s
standard (szabvny) knyvtrrl, valamint bemutatja a knyv szerkezett s elmagyarzza
azt a megkzeltst, amelyet a nyelv lehetsgeinek s azok hasznlatnak lersnl
alkalmaztunk. Ezenkvl a bevezet fejezetek nmi httrinformcit is adnak a C++-rl,
annak felptsrl s felhasznlsrl.
Fejezetek
1. Megjegyzsek az olvashoz
2. Kirnduls a C++-ban
3. Kirnduls a standard knyvtrban
Megjegyzsek az olvashoz
Szlt a Rozmr:
Van m elg, mirl meslni j: ...
(L. Carroll ford. Ttfalusi Istvn)
A knyv szerkezete Hogyan tanuljuk a C++-t? A C++ jellemzi Hatkonysg s szer-
kezet Filozfiai megjegyzs Trtneti megjegyzs Mire hasznljuk a C++-t? C s
C++ Javaslatok C programozknak Gondolatok a C++ programozsrl Tancsok
Hivatkozsok
1.1. A knyv szerkezete
A knyv hat rszbl ll:
Bevezets: Az 13. fejezetek ttekintik a C++ nyelvet, az ltala tmogatott f progra-
mozsi stlusokat, s a C++ standard knyvtrt.
Els rsz: A 49. fejezetek oktat jelleg bevezetst adnak a C++ beptett tpusai-
rl s az alapszolgltatsokrl, melyekkel ezekbl programot pthetnk.
Msodik rsz: A 1015. fejezetek bevezetst adnak az objektumorientlt s az
ltalnostott programozsba a C++ hasznlatval.
1
Harmadik rsz: A 1622. fejezetek bemutatjk a C++ standard knyvtrt.
Negyedik rsz: A 2325. fejezetek tervezsi s szoftverfejlesztsi krdseket trgyalnak.
Fggelkek: Az AE fggelkek a nyelv technikai rszleteit tartalmazzk.
Az 1. fejezet ttekintst ad a knyvrl, nhny tletet ad, hogyan hasznljuk, valamint
httrinformcikat szolgltat a C++-rl s annak hasznlatrl. Az olvas btran tfuthat
rajta, elolvashatja, ami rdekesnek ltszik, s visszatrhet ide, miutn a knyv ms rszeit
elolvasta.
A 2. s 3. fejezet ttekinti a C++ programozsi nyelv s a standard knyvtr f fogalmait s
nyelvi alaptulajdonsgait, megmutatva, mit lehet kifejezni a teljes C++ nyelvvel. Ha semmi
mst nem tesznek, e fejezetek meg kell gyzzk az olvast, hogy a C++ nem (csupn) C, s
hogy a C++ hossz utat tett meg e knyv els s msodik kiadsa ta. A 2. fejezet magas
szinten ismertet meg a C++-szal. A figyelmet azokra a nyelvi tulajdonsgokra irnytja, me-
lyek tmogatjk az elvont adatbrzolst, illetve az objektumorientlt s az ltalnostott
programozst. A 3. fejezet a standard knyvtr alapelveibe s f szolgltatsaiba vezet be,
ami lehetv teszi, hogy a szerz a standard knyvtr szolgltatsait hasznlhassa a kvet-
kez fejezetekben, valamint az olvasnak is lehetsget ad, hogy knyvtri szolgltatso-
kat hasznljon a gyakorlatokhoz s ne kelljen kzvetlenl a beptett, alacsony szint tulaj-
donsgokra hagyatkoznia.
A bevezet fejezetek egy, a knyv folyamn ltalnosan hasznlt eljrs pldjt adjk: ah-
hoz, hogy egy mdszert vagy tulajdonsgot mg kzvetlenebb s valszerbb mdon vizs-
glhassunk, alkalmanknt elszr rviden bemutatunk egy fogalmat, majd ksbb beha-
tbban trgyaljuk azt. Ez a megkzelts lehetv teszi, hogy konkrt pldkat mutassunk
be, mieltt egy tmt ltalnosabban trgyalnnk. A knyv felptse gy tkrzi azt a meg-
figyelst, hogy rendszerint gy tanulunk a legjobban, ha a konkrttl haladunk az elvont
fel mg ott is, ahol visszatekintve az elvont egyszernek s magtl rtetdnek ltszik.
Az I. rsz a C++-nak azt a rszhalmazt rja le, mely a C-ben vagy a Pascalban kvetett ha-
gyomnyos programozsi stlusokat tmogatja. Trgyalja a C++ programokban szerepl
alapvet tpusokat, kifejezseket, vezrlsi szerkezeteket. A modularitst, mint a nvterek,
forrsfjlok s a kivtelkezels ltal tmogatott tulajdonsgot, szintn trgyalja. Felttelez-
zk, hogy az olvasnak mr ismersek az I. fejezetben hasznlt alapvet programozsi fo-
galmak, gy pldul bemutatjuk a C++ lehetsgeit a rekurzi s iterci kifejezsre, de
nem sokig magyarzzuk, milyen hasznosak ezek.
A II. rsz a C++ j tpusok ltrehozst s hasznlatt segt szolgltatsait rja le. Itt (10. s
12. fejezet) mutatjuk be a konkrt s absztrakt osztlyokat (felleteket), az opertor-tlter-
helssel (11. fejezet), a tbbalaksggal (polimorfizmussal) s az osztlyhierarchik hasz-
Bevezets 4
nlatval (12. s 15. fejezet) egytt. A 13. fejezet a sablonokat (template) mutatja be, vagyis
a C++ lehetsgeit a tpus- s fggvnycsaldok ltrehozsra, valamint szemllteti a tro-
lk ellltsra (pl. listk), valamint az ltalnostott (generikus) programozs tmogats-
ra hasznlt alapvet eljrsokat. A 14. fejezet a kivtelkezelst, a hibakezelsi mdszereket
trgyalja s a hibatrs biztostshoz ad irnyelveket. Felttelezzk, hogy az olvas az ob-
jektumorientlt s az ltalnostott programozst nem ismeri jl, illetve hasznt ltn egy
magyarzatnak, hogyan tmogatja a C++ a f elvonatkoztatsi (absztrakcis) eljrsokat.
gy teht nemcsak bemutatjuk az elvonatkoztatsi mdszereket tmogat nyelvi tulajdon-
sgokat, hanem magukat az eljrsokat is elmagyarzzuk. A IV. rsz ebben az irnyban ha-
lad tovbb.
A III. rsz a C++ standard knyvtrt mutatja be. Clja: megrtetni, hogyan hasznljuk
a knyvtrat; ltalnos tervezsi s programozsi mdszereket szemlltetni s megmutatni,
hogyan bvtsk a knyvtrat. A knyvtr gondoskodik trolkrl (kontnerek list,
vector, map, 18. s 19. fejezet), szabvnyos algoritmusokrl (sort, find, merge, 18. s 19.
fejezet), karakterlnc-tpusokrl s -mveletekrl (20. fejezet), a bemenet s kimenet keze-
lsrl (input/output, 21. fejezet), valamint a szmokkal vgzett mveletek (numerikus
szmts) tmogatsrl (22. fejezet).
A IV. rsz olyan krdseket vizsgl, melyek akkor merlnek fel, amikor nagy szoftverrend-
szerek tervezsnl s kivitelezsnl a C++-t hasznljuk. A 23. fejezet tervezsi s vezet-
si krdsekkel foglalkozik. A 24. fejezet a C++ programozsi nyelv s a tervezsi krdsek
kapcsolatt vizsglja, mg a 25. fejezet az osztlyok hasznlatt mutatja be a tervezsben.
Az A fggelk a C++ nyelvtana, nhny jegyzettel. A B fggelk a C s a C++ kzti s
a szabvnyos C++ (ms nven ISO C++, ANSI C++) illetve az azt megelz C++-vltozatok
kzti rokonsgot vizsglja. A C fggelk nhny nyelvtechnikai pldt mutat be, A D
fggelk pedig a kulturlis eltrsek kezelst tmogat standard knyvtrbeli elemeket
mutatja be. Az E fggelk a standard knyvtr kivtelkezelsel kapcsolatos garanciit s
kvetelmnyeit trgyalja.
1.1.1. Pldk s hivatkozsok
Knyvnk az algoritmusok rsa helyett a program felptsre fekteti a hangslyt. Kvet-
kezskppen elkerli a ravasz vagy nehezebben rthet algoritmusokat. Egy egyszer
eljrs alkalmasabb az egyes fogalmak vagy a programszerkezet egy szempontjnak szem-
lltetsre. Pldul Shell rendezst hasznl, ahol a valdi kdban jobb lenne gyorsrende-
zst (quicksort) hasznlni. Gyakran j gyakorlat lehet a kd jrarsa egy alkalmasabb algo-
ritmussal. A valdi kdban ltalban jobb egy knyvtri fggvny hvsa, mint a knyvben
hasznlt, a nyelvi tulajdonsgok szemlltetsre hasznlt kd.
1. Megjegyzsek az olvashoz 5
A tanknyvi pldk szksgszeren egyoldal kpet adnak a programfejlesztsrl. Tisztz-
va s egyszerstve a pldkat a felmerlt bonyolultsgok eltnnek. Nincs, ami helyettes-
ten a valdi programok rst, ha benyomst akarunk kapni, igazbl milyen is a progra-
mozs s egy programozsi nyelv. Ez a knyv a nyelvi tulajdonsgokra s az alapvet
eljrsokra sszpontost, amelyekbl minden program sszetevdik, valamint az sszep-
ts szablyaira.
A pldk megvlasztsa tkrzi fordtprogramokkal, alapknyvtrakkal, szimulcikkal
jellemezhet htteremet. A pldk egyszerstett vltozatai a valdi kdban tallhatknak.
Egyszerstsre van szksg, hogy a programozsi nyelv s a tervezs lnyeges szempont-
jai el ne vesszenek a rszletekben. Nincs gyes plda, amelynek nincs megfelelje a va-
ldi kdban. Ahol csak lehetsges, a C fggelkben lv nyelvtechnikai pldkat olyan
alakra hoztam, ahol a vltozk x s y, a tpusok A s B, a fggvnyek f() s g() nevek.
A kdpldkban az azonostkhoz vltoz szlessg betket hasznlunk. Pldul:
#include<iostream>
int main()
{
std::cout << "Hell, vilg!\n";
}
Els ltsra ez termszetellenesnek tnhet a programozk szmra, akik hozzszoktak,
hogy a kd lland szlessg betkkel jelenik meg. A vltoz szlessg betket ltal-
ban jobbnak tartjk szveghez, mint az lland szlessgt. A vltoz szlessg betk
hasznlata azt is lehetv teszi, hogy a kdban kevesebb legyen a logiktlan sortrs. Ezen-
kvl sajt ksrleteim azt mutatjk, hogy a legtbb ember kis id elteltvel knnyebben ol-
vashatnak tartja az j stlust.
Ahol lehetsges, a C++ nyelv s knyvtr tulajdonsgait a kziknyvek szraz bemutatsi
mdja helyett a felhasznlsi krnyezetben mutatjuk be. A bemutatott nyelvi tulajdonsgok
s lersuk rszletessge a szerz nzett tkrzik, aki a legfontosabb krdsnek a kvetke-
zt tartja: mi szksges a C++ hatkony hasznlathoz? A nyelv teljes lersa a knnyebb
megkzelts cljbl jegyzetekkel elltva a The Annotated C++ Language Standard cm
kziknyvben tallhat, mely Andrew Koenig s a szerz mve. Logikusan kellene hogy le-
gyen egy msik kziknyv is, a The Annotated C++ Standard Library. Mivel azonban mind
az id, mind rsi kapacitsom vges, nem tudom meggrni, hogy elkszl.
A knyv egyes rszeire val hivatkozsok 2.3.4 (2. fejezet, 3.szakasz, 4. bekezds), B.5.6
(B fggelk, 5.6. bekezds s 6.[10](6. fejezet, 10. gyakorlat) alakban jelennek meg.
A dlt betket kiemelsre hasznljuk (pl. egy karakterlnc-literl nem fogadhat el), fon-
Bevezets 6
tos fogalmak els megjelensnl (pl. tbbalaksg), a C++ nyelv egyes szimblumainl
(pl. for utasts), az azonostknl s kulcsszavaknl, illetve a kdpldkban lv megjegy-
zseknl.
1.1.2. Gyakorlatok
Az egyes fejezetek vgn gyakorlatok tallhatk. A gyakorlatok fleg az rj egy programot
tpusba sorolhatk. Mindig annyi kdot rjunk, ami elg ahhoz, hogy a megolds fordtha-
t s korltozott krlmnyek kztt futtathat legyen. A gyakorlatok nehzsgben jelen-
tsen eltrek, ezrt becslt nehzsgi fokukat megjelltk. A nehzsg hatvnyozottan
n, teht ha egy (*1) gyakorlat 10 percet ignyel, egy (*2) gyakorlat egy rba, mg egy (*3)
egy napba kerlhet. Egy program megrsa s ellenrzse inkbb fgg az ember tapasztalt-
sgtl, mint magtl a gyakorlattl.
1.1.3. Megjegyzs az egyes C++-vltozatokhoz
A knyvben hasznlt nyelv tiszta C++, ahogyan a C++ szabvnyban lertk [C++, 1998].
Ezrt a pldknak futniuk kell minden C++-vltozaton. A knyvben szerepl nagyobb
programrszleteket tbb krnyezetben is kiprbltuk, azok a pldk azonban, melyek
a C++-ba csak nemrgiben beptett tulajdonsgokat hasznlnak fel, nem mindenhol fordt-
hatk le. (Azt nem rdemes megemlteni, mely vltozatokon mely pldkat nem sikerlt le-
fordtani. Az ilyen informcik hamar elavulnak, mert a megvalstson igyekv programo-
zk kemnyen dolgoznak azon, hogy nyelvi vltozataik helyesen fogadjanak el minden
C++ tulajdonsgot.) A B fggelkben javaslatok tallhatk, hogyan birkzzunk meg a r-
gi C++ fordtkkal s a C fordtkra rott kddal.
1.2. Hogyan tanuljuk a C++-t?
A C++ tanulsakor a legfontosabb, hogy a fogalmakra sszpontostsunk s ne vessznk el
a rszletekben. A programozsi nyelvek tanulsnak clja az, hogy jobb programozv vl-
junk; vagyis hatkonyabbak legynk j rendszerek tervezsnl, megvalstsnl s rgi
rendszerek karbantartsnl. Ehhez sokkal fontosabb a programozsi s tervezsi mdsze-
rek felfedezse, mint a rszletek megrtse; az utbbi idvel s gyakorlattal megszerezhet.
1. Megjegyzsek az olvashoz 7
A C++ sokfle programozsi stlust tmogat. Ezek mind az ers statikus tpusellenrzsen
alapulnak s legtbbjk a magas elvonatkoztatsi szint elrsre s a programoz elkpze-
lseinek kzvetlen lekpezsre irnyul. Minden stlus el tudja rni a cljt, mikzben ha-
tkony marad futsi id s helyfoglals tekintetben. Egy ms nyelvet (mondjuk C, Fortran,
Smalltalk, Lisp, ML, Ada, Eiffel, Pascal vagy Modula-2) hasznl programoz szre kell hogy
vegye, hogy a C++ elnyeinek kiaknzshoz idt kell sznnia a C++ programozsi stlu-
sok s mdszerek megtanulsra s megemsztsre. Ugyanez rvnyes azon programo-
zkra is, akik a C++ egy rgebbi, kevsb kifejezkpes vltozatt hasznltk.
Ha gondolkods nlkl alkalmazzuk az egyik nyelvben hatkony eljrst egy msik nyelv-
ben, rendszerint nehzkes, gyenge teljestmny s nehezen mdosthat kdot kapunk.
Az ilyen kd rsa is csaldst okoz, mivel minden sor kd s minden fordtsi hiba arra em-
lkeztet, hogy a nyelv, amit hasznlunk, ms, mint a rgi nyelv. rhatunk Fortran, C,
Smalltalk stb. stlusban brmely nyelven, de ez egy ms filozfij nyelvben nem lesz sem
kellemes, sem gazdasgos. Minden nyelv gazdag forrsa lehet az tleteknek, hogyan rjunk
C++ programot. Az tleteket azonban a C++ ltalnos szerkezethez s tpusrendszerhez
kell igaztani, hogy hatkony legyen az eltr krnyezetben. Egy nyelv alaptpusai felett
csak prroszi gyzelmet arathatunk.
A C++ tmogatja a fokozatos tanulst. Az, hogy hogyan kzeltsnk egy j nyelv tanuls-
hoz, attl fgg, mit tudunk mr s mit akarunk mg megtanulni. Nem ltezik egyetlen
megkzelts sem, amely mindenkinek j lenne. A szerz felttelezi, hogy az olvas azrt
tanulja a C++-t, hogy jobb programoz s tervez legyen. Vagyis nem egyszeren egy j
nyelvtant akar megtanulni, mellyel a rgi megszokott mdon vgzi a dolgokat, hanem j s
jobb rendszerptsi mdszereket akar elsajttani. Ezt fokozatosan kell csinlni, mert min-
den j kpessg megszerzse idt s gyakorlst ignyel. Gondoljuk meg, mennyi idbe ke-
rlne jl megtanulni egy j termszetes nyelvet vagy megtanulni jl jtszani egy hangsze-
ren. Knnyen s gyorsan lehetnk jobb rendszertervezk, de nem annyival knnyebben s
gyorsabban, mint ahogy azt a legtbben szeretnnk.
Kvetkezskppen a C++-t gyakran valdi rendszerek ptsre mr azeltt hasznlni
fogjuk, mieltt megrtennk minden nyelvi tulajdonsgot s eljrst. A C++ azltal, hogy
tbb programozsi modellt is tmogat (2. fejezet) klnbz szint szakrtelem esetn is
tmogatja a termkeny programozst. Minden j programozsi stlus jabb eszkzt ad esz-
kztrunkhoz, de mindegyik magban is hatkony s mindegyik fokozza a programozi
hatkonysgot. A C++-t gy alkottk meg, hogy a fogalmakat nagyjbl sorban egyms
utn tanulhassuk meg s ekzben gyakorlati haszonra tehessnk szert. Ez fontos, mert a ha-
szon a kifejtett erfesztssel arnyos.
Bevezets 8
A folytatd vita sorn kell-e C-t tanulni a C++-szal val ismerkeds eltt szilrd meg-
gyzdsemm vlt, hogy legjobb kzvetlenl a C++-ra ttrni. A C++ biztonsgosabb, ki-
fejezbb, cskkenti annak szksgt, hogy a figyelmet alacsonyszint eljrsokra irnyt-
suk. Knnyebb a C-ben a magasabb szint lehetsgek hinyt ptl trkksebb rszeket
megtanulni, ha elbb megismertk a C s a C++ kzs rszhalmazt s a C++ ltal kzvet-
lenl tmogatott magasabb szint eljrsokat. A B fggelk vezrfonalat ad azoknak
a programozknak, akik a C++ ismeretben vltanak a C-re, pldul azrt, hogy rgebbi k-
dot kezeljenek.
Tbb egymstl fggetlenl fejlesztett s terjesztett C++-vltozat ltezik. Gazdag vlasztk
kaphat eszkztrakbl, knyvtrakbl, programfejleszt krnyezetekbl is. Rengeteg tan-
knyv, kziknyv, folyirat, elektronikus hirdettbla, konferencia, tanfolyam ll rendelke-
zsnkre a C++ legfrissebb fejlesztseirl, hasznlatrl, segdeszkzeirl, knyvtrairl,
megvalstsairl s gy tovbb. Ha az olvas komolyan akarja a C++-t hasznlni, tancsos
az ilyen forrsok kztt is bngszni. Mindegyiknek megvan a sajt nzpontja, elfogults-
ga, ezrt hasznljunk legalbb kettt kzlk. Pldul lsd [Barton,1994], [Booch,1994],
[Henricson, 1997], [Koenig, 1997], [Martin, 1995].
1.3. A C++ jellemzi
Az egyszersg fontos tervezsi felttel volt; ahol vlasztani lehetett, hogy a nyelvet vagy
a fordtt egyszerstsk-e, az elbbit vlasztottuk. Mindenesetre nagy slyt fektettnk ar-
ra, hogy megmaradjon a C-vel val sszeegyeztethetsg, ami eleve kizrta a C nyelvtan
kisprst.
A C++-nak nincsenek beptett magasszint adattpusai, sem magasszint alapmveletei.
A C++-ban pldul nincs mtrixtpus inverzi opertorral, karakterlnc-tpus sszefz
mvelettel. Ha a felhasznlnak ilyen tpusra van szksge, magban a nyelvben definl-
hat ilyet. Alapjban vve a C++-ban a legelemibb programozsi tevkenysg az ltalnos
cl vagy alkalmazsfgg tpusok ltrehozsa. Egy jl megtervezett felhasznli tpus
a beptett tpusoktl csak abban klnbzik, milyen mdon hatroztk meg, abban nem,
hogyan hasznljk. A III. rszben lert standard knyvtr szmos pldt ad az ilyen tpusok-
ra s hasznlatukra. A felhasznl szempontjbl kevs a klnbsg egy beptett s egy
standard knyvtrbeli tpus kztt.
1. Megjegyzsek az olvashoz 9
A C++-ban kerltk az olyan tulajdonsgokat, melyek akkor is a futsi id nvekedst
vagy a tr tlterhelst okoznk, ha nem hasznljuk azokat. Nem megengedettek pldul
azok a szerkezetek, melyek hztartsi informci trolst tennk szksgess minden
objektumban, gy ha a felhasznl pldul kt 16 bites mennyisgbl ll szerkezetet ad
meg, az egy 32 bites regiszterbe tkletesen belefr.
A C++-t hagyomnyos fordtsi s futsi krnyezetben val hasznlatra terveztk, vagyis
a UNIX rendszer C programozsi krnyezetre. Szerencsre a C++ sohasem volt a UNIX-ra
korltozva, a UNIX-ot s a C-t csupn modellknt hasznltuk a nyelv, a knyvtrak, a for-
dtk, a szerkesztk, a futtatsi krnyezetek stb. rokonsga alapjn. Ez a minimlis modell
segtette a C++ sikeres elterjedst lnyegben minden szmtgpes platformon. J okai
vannak azonban a C++ hasznlatnak olyan krnyezetekben, melyek jelentsen nagyobb
tmogatsrl gondoskodnak. Az olyan szolgltatsok, mint a dinamikus betlts, a fokoza-
tos fordts vagy a tpusmeghatrozsok adatbzisa, anlkl is jl hasznlhatk, hogy befo-
lysolnk a nyelvet.
A C++ tpusellenrzsi s adatrejtsi tulajdonsgai a programok fordtsi id alatti elemz-
sre tmaszkodnak, hogy elkerljk a vletlen adatsrlseket. Nem gondoskodnak titko-
stsrl vagy az olyan szemlyek elleni vdelemrl, akik szndkosan megszegik a szab-
lyokat. Viszont szabadon hasznlhatk s nem jrnak a futsi id vagy a szksges trhely
nvekedsvel. Az alapelv az, hogy ahhoz, hogy egy nyelvi tulajdonsg hasznos legyen,
nemcsak elegnsnak, hanem valdi programon bell is elhelyezhetnek kell lennie.
A C++ jellemzinek rendszerezett s rszletes lerst lsd [Stroustrup, 1994].
1.3.1. Hatkonysg s szerkezet
A C++-t a C programozsi nyelvbl fejlesztettk ki s nhny kivteltl eltekintve a C-t,
mint rszhalmazt, megtartotta. Az alapnyelvet, a C++ C rszhalmazt, gy terveztk, hogy
nagyon szoros megfelels van tpusai, mveletei, utastsai, s a szmtgpek ltal kzvet-
lenl kezelhet objektumok (szmok, karakterek s cmek) kztt. A new, delete, typeid,
dynamic_cast s throw opertorok s a try blokk kivtelvel, az egyes C++ kifejez-
sek s utastsok nem kvnnak futsi idej tmogatst.
A C++ ugyanolyan fggvnyhvsi s visszatrsi mdokat hasznlhat, mint a C vagy mg
hatkonyabbakat. Amikor mg az ilyen, viszonylag hatkony eljrsok is tl kltsgesek,
a C++ fggvnyt a fordtval kifejtethetjk helyben (inline kd), gy lvezhetjk a fggv-
nyek hasznlatnak knyelmt, a futsi id nvelse nlkl.
Bevezets 10
A C egyik eredeti clja az assembly kd helyettestse volt a legignyesebb rendszerprog-
ramozsi feladatokban. Amikor a C++-t terveztk, vigyztunk, ne legyen megalkuvs e t-
ren. A C s a C++ kzti klnbsg elssorban a tpusokra s adatszerkezetekre fektetett sly
mrtkben van. A C kifejez s elnz. A C++ mg kifejezbb. Ezrt a jobb kifejezkpes-
sgrt cserbe azonban nagyobb figyelmet kell fordtanunk az objektumok tpusra. A for-
dt az objektumok tpusnak ismeretben helyesen tudja kezelni a kifejezseket akkor is,
ha egybknt knos precizitssal kellett volna megadni a mveleteket. Az objektumok tpu-
snak ismerete arra is kpess teszi a fordtt, hogy olyan hibkat fedjen fel, melyek ms-
klnben egszen a tesztelsig vagy mg tovbb megmaradnnak. Vegyk szre, hogy a t-
pusrendszer hasznlata fggvnyparamterek ellenrzsre az adatok vletlen srlstl
val megvdsre, j tpusok vagy opertorok ellltsra s gy tovbb a C++-ban nem
nveli a futsi idt vagy a szksges helyet.
A C++-ban a szerkezetre fektetett hangsly tkrzi a C megtervezse ta megrt programok
slygyarapodst. Egy kis mondjuk 1000 soros programot megrhatunk nyers ervel,
mg akkor is, ha felrgjuk a j stlus minden szablyt. Nagyobb programoknl ez egyszer-
en nincs gy. Ha egy 100 000 soros programnak rossz a felptse, azt fogjuk tallni, hogy
ugyanolyan gyorsan keletkeznek az jabb hibk, mint ahogy a rgieket eltvoltjuk. A C++-t
gy terveztk, hogy lehetv tegye nagyobb programok sszer mdon val felptst, gy
egyetlen szemly is sokkal nagyobb kdmennyisggel kpes megbirkzni. Ezenkvl clkit-
zs volt, hogy egy tlagos sornyi C++ kd sokkal tbbet fejezzen ki, mint egy tlagos Pascal
vagy C kdsor. A C++ mostanra megmutatta, hogy tl is teljesti ezeket a clkitzseket.
Nem minden kdrszlet lehet jl szerkesztett, hardverfggetlen vagy knnyen olvashat.
A C++-nak vannak tulajdonsgai, melyeket arra szntak, hogy kzvetlen s hatkony m-
don kezelhessk a hardver szolgltatsait, anlkl, hogy a biztonsgra vagy az rthetsg-
re kros hatssal lennnk. Vannak olyan lehetsgei is, melyekkel az ilyen kd elegns s
biztonsgos felletek mg rejthet.
A C++ nagyobb programokhoz val hasznlata termszetszeren elvezet a C++ nyelv
programozcsoportok ltali hasznlathoz. A C++ ltal a modularitsra, az ersen tpusos
felletekre s a rugalmassgra fektetetett hangsly itt fizetdik ki. A C++-nak ppen olyan
jl kiegyenslyozott szolgltatsai vannak nagy programok rsra, mint brmely nyelvnek.
Ahogy nagyobbak lesznek a programok, a fejlesztskkel s fenntartsukkal, mdostsuk-
kal kapcsolatos problmk a nyelvi problma jellegtl az eszkzk s a kezels ltalno-
sabb problmi fel mozdulnak el. A IV. rsz ilyen jelleg krdseket is trgyal.
1. Megjegyzsek az olvashoz 11
Knyvnk kiemeli az ltalnos cl szolgltatsok, tpusok s knyvtrak ksztsnek
mdjait. Ezek ppgy szolgljk a kis programok rit, mint a nagy programokit. Ezen tl-
menen, mivel minden bonyolultabb program sok, flig-meddig fggetlen rszbl ll, az
ilyen rszek rshoz szksges mdszerek ismerete j szolglatot tesz minden alkalmazs-
programoznak.
Az olvas azt gondolhatja, a rszletesebb tpusszerkezetek hasznlata nagyobb forrsprog-
ramhoz vezet. A C++ esetben ez nem gy van. Egy C++ program, amely fggvnyparam-
ter-tpusokat vezet be vagy osztlyokat hasznl, rendszerint kiss rvidebb, mint a vele
egyenrtk C program, amely nem hasznlja e lehetsgeket. Ott, ahol knyvtrakat hasz-
nlnak, egy C++ program sokkal rvidebb lesz, mint a megfelel C program, feltve term-
szetesen, hogy kszthet mkdkpes C-beli megfelel.
1.3.2. Filozfiai megjegyzs
A programozsi nyelvek kt rokon clt szolglnak: a programoznak rszben eszkzt ad-
nak, amellyel vgrehajthat mveleteket adhat meg, ugyanakkor egy sereg fogdzt is ren-
delkezsre bocstanak, amikor arrl gondolkodik, mit lehet tenni. Az els cl idelis eset-
ben gpkzeli nyelvet kvn, amellyel a szmtgp minden fontos oldala egyszeren s
hatkonyan kezelhet, a programoz szmra sszer, kzenfekv mdon. A C nyelvet el-
ssorban ebben a szellemben terveztk. A msodik cl viszont olyan nyelvet kvetel meg,
mely kzel van a megoldand problmhoz, hogy a megolds kzvetlenl s tmren ki-
fejezhet legyen.
A nyelv, melyben gondolkodunk/programozunk s a problmk, megoldsok, melyeket el
tudunk kpzelni, szoros kapcsolatban llnak egymssal. Ezrt a nyelvi tulajdonsgok meg-
szortsa azzal a szndkkal, hogy kikszbljk a programozi hibkat, a legjobb esetben
is veszlyes. A termszetes nyelvekhez hasonlan nagy elnye van annak, ha az ember leg-
albb kt nyelvet ismer. A nyelv elltja a programozt a megfelel eszkzkkel, ha azon-
ban ezek nem megfelelek a feladathoz, egyszeren figyelmen kvl hagyjuk azokat. A j
tervezs s hibamentessg nem biztosthat csupn az egyedi nyelvi tulajdonsgok jelenl-
tvel vagy tvolltvel.
A tpusrendszer klnsen sszetettebb feladatok esetben jelent segtsget. A C++ oszt-
lyai valban ers eszkznek bizonyultak.
Bevezets 12
1.4. Trtneti megjegyzs
A szerz alkotta meg a C++-t, rta meg els definciit, s ksztette el els vltozatt. Meg-
vlasztotta s megfogalmazta a C++ tervezsi feltteleit, megtervezte f szolgltatsait, s
volt a felels a C++ szabvnygyi bizottsgban a bvtsi javaslatok feldolgozsrt.
Vilgos, hogy a C++ sokat ksznhet a C-nek [Kernighan, 1978]. A C nhny, a tpusellen-
rzs tern tapasztalt hinyossgt kivve megmaradt, rszhalmazknt (lsd B fggelk).
Ugyancsak megmaradt az a C-beli szndk, hogy olyan szolgltatsokra fektessen hang-
slyt, melyek elg alacsony szintek ahhoz, hogy megbirkzzanak a legignyesebb rend-
szerprogramozsi feladatokkal is. A C a maga rszrl sokat ksznhet snek, a BCPL-nek
[Richards, 1980]; a BCPL // megjegyzs-formtuma (jra) be is kerlt a C++-ba. A C++ m-
sik fontos forrsa a Simula67 volt [Dahl, 1970] [Dahl, 1972]; az osztly fogalmt (a szrmaz-
tatott osztlyokkal s virtulis fggvnyekkel) innen vettem t. A C++ opertor-tlterhelsi
lehetsge s a deklarcik szabad elhelyezse az utastsok kztt az Algol68-ra emlkez-
tet [Woodward, 1974].
A knyv eredeti kiadsa ta a nyelv kiterjedt fellvizsglatokon s finomtsokon ment
keresztl. A fellvizsglatok f terlete a tlterhels feloldsa, az sszeszerkesztsi s tr-
kezelsi lehetsgek voltak. Ezenkvl szmos kisebb vltoztats trtnt a C-vel val kom-
patibilits nvelsre. Szmos ltalnosts s nhny nagy bvts is belekerlt: ezek
a tbbszrs rkls, a static s const tagfggvnyek, a protected tagok, a sablonok, a ki-
vtelkezels, a futsi idej tpusazonosts s a nvterek. E bvtsek s fellvizsglatok
tfog feladata a C++ olyan nyelvv fejlesztse volt, mellyel jobban lehet knyvtrakat rni
s hasznlni. A C++ fejldsnek lerst lsd [Stroustrup, 1994].
A sablonok (template) bevezetsnek elsdleges clja a statikus tpus trolk (kontnerek
list, vector, map) s azok hatkony hasznlatnak (ltalnostott vagy generikus progra-
mozs) tmogatsa, valamint a makrk s explicit tpusknyszertsek (casting) szksg-
nek cskkentse volt. Inspircit az Ada ltalnost eszkzei (mind azok erssgei, illetve
gyengesgei), valamint rszben a Clu paramteres moduljai szolgltattak. Hasonlan, a C++
kivtelkezelsi eljrsainak eldjei is tbb-kevsb az Ada [Ichbiah, 1979], a Clu [Liskov,
1979] s az ML [Wikstrm, 1987]. Az 1985-1995 kztt bevezetett egyb fejlesztsek tbb-
szrs rkls, tisztn virtulis fggvnyek s nvterek viszont nem annyira ms nyelvek-
bl mertett tletek alapjn szlettek, inkbb a C++ hasznlatnak tapasztalataibl leszrt
ltalnostsok eredmnyei.
A nyelv korbbi vltozatait (sszefoglal nven az osztlyokkal bvtett C-t [Stroustrup,
1994]) 1980 ta hasznljk. Kifejlesztsben eredetileg szerepet jtszott, hogy olyan ese-
mnyvezrelt szimulcikat szerettem volna rni, melyekhez a Simula67 idelis lett volna,
1. Megjegyzsek az olvashoz 13
ha elgg hatkony. Az osztlyokkal bvtett C igazi terlett a nagy programok jelentet-
tk, ahol a lehet leggyorsabbnak kell lenni s a lehet legkevesebb helyet foglalni. Az el-
s vltozatokbl mg hinyzott az opertor-tlterhels, valamint hinyoztak a referencik,
a virtulis fggvnyek, a sablonok, a kivtelek s sok egyb. A C++-t nem ksrleti krl-
mnyek kztt elszr 1983-ban hasznltk.
A C++ nevet Rick Mascitti adta a nyelvnek az emltett v nyarn. A nv kifejezi mindazt
a forradalmi jtst, amit az j nyelv a C-hez kpest hozott: a ++ a C nvel mveleti jele.
(A C+-t is hasznljk, de az egy msik, fggetlen nyelv.) A C utastsformit jl ismerk
rmutathatnak, hogy a C++ kifejezs nem olyan ers, mint a ++C. Mindazonltal
a nyelv neve nem is D, hiszen a C-nek csupn bvtsrl van sz, amely az ott felmerlt
problmk elhrtshoz az eredeti nyelv szolgltatsai kzl egyet sem vet el. A C++ nv
ms megkzelts elemzshez lsd [Orwell, 1949, fggelk].
A C++ megalkotsnak f oka azonban az volt, hogy bartaimmal egytt nem szerettnk
volna assembly, C vagy ms modern, magas szint nyelven programozni. Csak annyit akar-
tunk elrni, hogy knnyebben s lvezetesebben rhassunk jl hasznlhat programokat.
Kezdetben nem vetettk paprra rendszerezetten a fejlesztsi terveket: egyszerre tervez-
tnk, dokumentltunk s alkottunk. Nem volt C++ projekt vagy C++ tervezbizottsg.
A C++ a felhasznlk tapasztalatai s a bartaimmal, munkatrsaimmal folytatott vitk so-
rn fejldtt ki.
A C++ ksbbi robbansszer elterjedse szksgszeren vltozsokat hozott magval. Va-
lamikor 1987-ben nyilvnvalv vlt, hogy a C++ hivatalos szabvnyostsa immr elkerl-
hetetlen s haladktalanul meg kell kezdennk az ilyen irny munka elksztst
[Stroustrup, 1994]. Folyamatosan prbltuk tartani a kapcsolatot mind hagyomnyos, mind
elektronikus levlben, illetve szemlyesen, konferencikat tartva a klnbz C++ fordtk
ksztivel s a nyelv f felhasznlival.
Ebben a munkban nagy segtsget nyjtott az AT&T Bell Laboratories, lehetv tve, hogy
vzlataimat s a C++ hivatkozsi kziknyv jabb s jabb vltozatait megoszthassam a fej-
lesztkkel s felhasznlkkal. Segtsgk nem albecslend, ha tudjuk, hogy az emltettek
nagy rsze olyan vllalatoknl dolgozott, amelyek az AT&T vetlytrsainak tekinthetk.
Egy kevsb felvilgosult cg komoly problmkat okozhatott volna s a nyelv tjszls-
okra tredezst idzte volna el, pusztn azltal, hogy nem tesz semmit. Szerencsre a tu-
catnyi cgnl dolgoz mintegy szz kzremkd elolvasta s megjegyzsekkel ltta el
a vzlatokat, melyekbl az ltalnosan elfogadott hivatkozsi kziknyv s a szabvnyos
ANSI C++ alapdokumentuma megszletett. A munkt segtk neve megtallhat a The
Annotated C++ Reference Manual-ban [Ellis, 1989]. Vgl az ANSI X3J16 bizottsga a Hew-
lett-Packard kezdemnyezsre 1989 decemberben sszelt, 1991 jniusban pedig mr
Bevezets 14
annak rlhettnk, hogy az ANSI (az amerikai nemzeti szabvny) C++ az ISO (nemzetk-
zi) C++ szabvnyostsi kezdemnyezs rszv vlt. 1990-tl ezek a szabvnygyi bizott-
sgok vltak a nyelv fejlesztsnek s pontos krlhatrolsnak f frumaiv. Magam
mindvgig rszt vettem e bizottsgok munkjban; a bvtmnyekkel foglalkoz munka-
csoport elnkeknt kzvetlenl feleltem a C++-t rint lnyegbevg mdostsi javaslatok
s az j szolgltatsok bevezetst szorgalmaz krelmek elbrlsrt. Az els szabvny-
vzlat 1995 prilisban kerlt a nagykznsg el, a vgleges ISO C++ szabvnyt (ISO/IEC
14882) pedig 1998-ban fogadtk el.
A knyvben bemutatott kulcsfontossg osztlyok nmelyike a C++-szal prhuzamosan fej-
ldtt. A complex, vector s stack osztlyokat pldul az opertor-tlterhelsi eljrsokkal
egyidben dolgoztam ki. A karakterlnc- s listaosztlyokat (string, list) Jonathan
Shopironak ksznhetjk (azrt n is kzremkdtem). Jonathan hasonl osztlyai voltak
az elsk, amelyeket egy knyvtr rszeknt szles krben hasznltak; ezekbl a rgi ksr-
letekbl fejlesztettk ki a C++ standard knyvtrnak string osztlyt. A [Stroustrup, 1987]
s a 12.7[11] ltal lert task knyvtr egyike volt az osztlyokkal bvtett C nyelven el-
szr rt programoknak. (A knyvtrat s a kapcsold osztlyokat n rtam a Simula stlus
szimulcik tmogatshoz.) A knyvtrat ksbb Jonathan Shopiro tdolgozta, s mg ma
is hasznljk. Az els kiadsban lert streamknyvtrat n terveztem s ksztettem el, Jerry
Schwarz pedig Andrew Koenig formz eljrsa (21.4.6) s ms tletek felhasznlsval az
e knyv 21. fejezetben bemutatand iostreams knyvtrr alaktotta. A szabvnyosts so-
rn a knyvtr tovbbi finomtson esett t; a munka dandrjt Jerry Schwarz, Nathan Myers
s Norihiro Kumagai vgeztk. A sablonok lehetsgeit az Andrew Koenig, Alex Stepanov,
szemlyem s msok ltal tervezett vector, map, list s sort sablonok alapjn dolgoztuk ki.
Alex Stepanovnak a sablonokkal trtn ltalnostott programozs tern vgzett munkja
emellett elvezetett a trolk bevezetshez s a C++ standard knyvtrnak egyes algorit-
musaihoz is (16.3, 17. fejezet, 18. fejezet 19.2). A szmokkal vgzett mveletek valarray
knyvtra (22. fejezet) nagyrszt Kent Budge munkja.
1.5. A C++ hasznlata
A C++-t programozk szzezrei hasznljk, lnyegben minden alkalmazsi terleten. Ezt
a hasznlatot tmogatja tucatnyi fggetlen megvalsts, tbbszz knyvtr s tanknyv,
szmos mszaki folyirat, konferencia, s szmtalan konzultns. Oktats s kpzs minden
szinten, szles krben elrhet.
1. Megjegyzsek az olvashoz 15
A rgebbi alkalmazsok ersen a rendszerprogramozs fel hajlottak. Tbb nagy opercis
rendszer rdott C++-ban: [Campbell, 1987] [Rozier, 1988] [Hamilton, 1993] [Berg, 1995]
[Parrington, 1995] s sokan msok kulcsfontossg rszeket rtak. A szerz lnyegesnek te-
kinti a C++ engedmny nlkli gpkzelisgt, ami lehetv teszi, hogy C++-ban rhassunk
eszkzmeghajtkat s ms olyan programokat, melyek valsidej, kzvetlen hardverkezels-
re tmaszkodnak. Az ilyen kdban a mkds kiszmthatsga legalbb annyira fontos, mint
a sebessg s gyakran gy van az eredmnyl kapott rendszer tmrsgvel is. A C++-t gy
terveztk, hogy minden nyelvi tulajdonsg hasznlhat legyen a komoly idbeli s helyfog-
lalsbeli megszortsoknak kitett kdban is. [Stroustrup, 1994, 4.5].
A legtbb programban vannak kdrszletek, melyek ltfontossgak az elfogadhat telje-
stmny tekintetben. A kd nagyobb rszt azonban nem ilyen rszek alkotjk. A legtbb
kdnl a mdosthatsg, a knny bvthetsg s tesztelhetsg a kulcskrds. A C++
ilyen tren nyjtott tmogatsa vezetett el szleskr hasznlathoz ott, ahol ktelez
a megbzhatsg, s ahol az id haladtval jelentsen vltoznak a kvetelmnyek. Plda-
knt a bankok, a kereskedelem, a biztostsi szfra, a tvkzls s a katonai alkalmazsok
szolglhatnak. Az USA tvolsgi telefonrendszere vek ta a C++-ra tmaszkodik s minden
800-as hvst (vagyis olyan hvst, ahol a hvott fl fizet) C++ program irnyt [Kamath,
1993]. Szmos ilyen program nagy mret s hossz let. Ennek eredmnykppen a sta-
bilits, a kompatibilits s a mretezhetsg lland szempontok a C++ fejlesztsben.
Nem szokatlanok a milli soros C++ programok.
A C-hez hasonlan a C++-t sem kifejezetten szmokkal vgzett mveletekhez terveztk.
Mindazonltal sok szmtani, tudomnyos s mrnki szmtst rtak C++-ban. Ennek f
oka, hogy a szmokkal val hagyomnyos munkt gyakran grafikval s olyan szmtsok-
kal kell prostani, melyek a hagyomnyos Fortran mintba nem illeszked adatszerkeze-
tekre tmaszkodnak [Budge, 1992] [Barton, 1994]. A grafika s a felhasznli fellet olyan
terletek, ahol ersen hasznljk a C++-t. Brki, aki akr egy Apple Macintosht, akr egy
Windowst futtat PC-t hasznlt, kzvetve a C++-t hasznlta, mert e rendszerek elsdleges
felhasznli felleteit C++ programok alkotjk. Ezenkvl a UNIX-ban az X-et tmogat leg-
npszerbb knyvtrak nmelyike is C++-ban rdott. Ilyenformn a C++ kzsen vlasz-
tott nyelve annak a hatalmas szm alkalmazsnak, ahol a felhasznli fellet kiemelt fon-
tossg.
Mindezen szempontok mellett lehet, hogy a C++ legnagyobb erssge az a kpessge, hogy
hatkonyan hasznlhat olyan programokhoz, melyek tbbfle alkalmazsi terleten ig-
nyelnek munkt. Egyszer olyan alkalmazst tallni, melyben LAN s WAN hlzatot,
szmokkal vgzett mveleteket, grafikt, felhasznli klcsnhatst s adatbzis-hozzfrst
hasznlunk. Az ilyen alkalmazsi terleteket rgebben klnllknak tekintettk s ltal-
ban klnll fejlesztkzssgek szolgltk ki, tbbfle programozsi nyelvet hasznlva.
Bevezets 16
A C++-t szles krben hasznljk oktatsra s kutatsra. Ez nhny embert meglepett,
akik helyesen rmutattak, hogy a C++ nem a legkisebb s legtisztbb nyelv, amelyet va-
laha terveztek. Mindazonltal a C++
elg tiszta ahhoz, hogy az alapfogalmakat sikeresen tantsuk,
elg valszer, hatkony s rugalmas az ignyes projektekhez is,
elrhet olyan szervezetek s egyttmkd csoportok szmra, melyek eltr
fejlesztsi s vgrehajtsi krnyezetekre tmaszkodnak,
elg rthet ahhoz, hogy bonyolult fogalmak s mdszerek tantsnak hordo-
zja legyen s
elg kereskedelmi, hogy segtse a tanultak gyakorlatban val felhasznlst.
A C++ olyan nyelv, mellyel gyarapodhatunk.
1.6. C s C++
A C++ alapnyelvnek a C nyelvet vlasztottuk, mert
sokoldal, tmr, s viszonylag alacsony szint,
megfelel a legtbb rendszerprogramozsi feladatra,
mindentt s mindenen fut, s
illeszkedik a UNIX programozsi krnyezetbe.
A C-nek megvannak a hibi, de egy jonnan ksztett nyelvnek is lennnek, a C problm-
it pedig mr ismerjk. Nagy jelentsge van, hogy C-vel val munka vezetett el a hasznos
(br nehzkes) eszkzz vl osztlyokkal bvtett C-hez, amikor elszr gondoltunk a C
bvtsre Simula-szer osztlyokkal.
Ahogy szlesebb krben kezdtk hasznlni a C++-t s az ltala nyjtott, a C lehetsgeit fe-
llml kpessgek jelentsebbek lettek, jra s jra felmerlt a krds, megtartsuk-e a kt
nyelv sszeegyeztethetsgt. Vilgos, hogy nhny problma elkerlhet lett volna, ha
nmelyik C rksget elutastjuk (lsd pl. [Sethi, 1981]). Ezt nem tettk meg, a kvetkezk
miatt:
1. Megjegyzsek az olvashoz 17
1. Tbb milli sornyi C kd van, mely lvezheti a C++ elnyeit, feltve, hogy
szksgtelen a C-rl C++-ra val teljes trs.
2. Tbb milli sornyi C-ben rt knyvtri fggvny s eszkzilleszt kd van, me-
lyet C++ programokbl/programokban hasznlni lehet, feltve, hogy a C++
program sszeszerkeszthet s formailag sszeegyeztethet a C programmal.
3. Programozk szzezrei lteznek, akik ismerik a C-t s ezrt csak a C++ j tulaj-
donsgait kell megtanulniuk, vagyis nem kell az alapokkal kezdenik.
4. A C++-t s a C-t ugyanazok, ugyanazokon a rendszereken fogjk vekig hasz-
nlni, teht a klnbsgek vagy nagyon nagyok, vagy nagyon kicsik lesznek,
hogy a hibk s a kevereds lehetsge a lehet legkisebbre cskkenjen.
A C++-t fellvizsgltuk, hogy biztostsuk, hogy azon szerkezetek, melyek mind a C-ben,
mind a C++-ban megengedettek, mindkt nyelvben ugyanazt jelentsk (B.2).
A C nyelv maga is fejldtt, rszben a C++ fejlesztsnek hatsra [Rosler, 1984]. Az ANSI C
szabvny [C,1990] a fggvnydeklarcik formai kvetelmnyeit az osztlyokkal bvtett
C-bl vette t. Az tvtel mindkt irnyban elfordul: a void* mutattpust pldul az ANSI
C-hez talltk ki, de elszr a C++-ban valstottk meg. Mint ahogy e knyv els kiads-
ban meggrtk, a C++-t fellvizsgltuk, hogy eltvoltsuk az indokolatlan eltrseket, gy
a C++ ma jobban illeszkedik a C-hez, mint eredetileg. Az elkpzels az volt, hogy a C++
olyan kzel legyen az ANSI C-hez, amennyire csak lehetsges de ne kzelebb [Koenig,
1989]. A szz szzalkos megfelelsg soha nem volt cl, mivel ez megalkuvst jelentene
a tpusbiztonsgban, valamint a felhasznli s beptett tpusok zkkensmentes egyezte-
tsben.
A C tudsa nem elfelttele a C++ megtanulsnak. A C programozs sok olyan mdszer s
trkk hasznlatra biztat, melyeket a C++ nyelvi tulajdonsgai szksgtelenn tettek. Az
explicit tpusknyszerts pldul ritkbban szksges a C++-ban, mint a C-ben (1.6.1).
A j C programok azonban hajlanak a C++ programok fel. A Kernighan s Ritchie fle
A C programozsi nyelv (Mszaki knyvkiad, msodik kiads, 1994) [Kernighan,1988]
cm ktetben pldul minden program C++ program. Brmilyen statikus tpusokkal ren-
delkez nyelvben szerzett tapasztalat segtsget jelent a C++ tanulsnl.
1.6.1. Javaslatok C programozknak
Minl jobban ismeri valaki a C-t, annl nehezebbnek ltja annak elkerlst, hogy C stlus-
ban rjon C++ programot, lemondva ezltal a C++ elnyeirl. Krjk, vessen az olvas egy
pillantst a B fggelkre, mely lerja a C s a C++ kzti klnbsgeket. me nhny ter-
let, ahol a C++ fejlettebb, mint a C:
Bevezets 18
1. A C++-ban a makrkra majdnem soha sincs szksg. A nvvel elltott llandk
meghatrozsra hasznljunk konstanst (const) (5.4) vagy felsorolst (enum)
(4.8), a fggvnyhvs okozta tbbletterhels elkerlsre helyben kifejtett
fggvnyeket (7.1.1), a fggvny- s tpuscsaldok lersra sablonokat
(13. fejezet), a nvtkzsek elkerlsre pedig nvtereket (8.2).
2. Ne vezessnk be egy vltozt, mieltt szksg van r, gy annak azonnal
kezdrtket is adhatunk. Deklarci brhol lehet, ahol utasts lehet (6.3.1),
gy for utastsok (6.3.3) s elgazsok feltteleiben (6.3.2.1) is.
3. Ne hasznljunk malloc()-ot, a new opertor (6.2.6) ugyanazt jobban elvgzi.
A realloc() helyett prbljuk meg a vector-t (3.8).
4. Prbljuk elkerlni a void* mutatkkal val szmtsokat, az unikat s tpus-
konverzikat (tpustalaktsokat), kivve, ha valamely fggvny vagy osztly
megvalstsnak mlyn tallhatk. A legtbb esetben a tpuskonverzi a ter-
vezsi hiba jele. Ha felttlenl erre van szksg, az j cast-ok (6.2.7) egyikt
prbljuk hasznlni szndkunk pontosabb lershoz.
5. Cskkentsk a lehet legkevesebbre a tmbk s a C stlus karakterlncok
hasznlatt. A C++ standard knyvtrnak string (3.5) s vector (3.7.1) oszt-
lyai a hagyomnyos C stlushoz kpest gyakrabban hasznlhatk a programozs
egyszerbb ttelre. ltalban ne prbljunk magunk pteni olyat, ami meg-
van a standard knyvtrban.
Ahhoz, hogy eleget tegynk a C szerkesztsi szablyainak, a C++ fggvnyeket gy kell
megadnunk, hogy szerkesztsk C md legyen. (9.2.4). A legfontosabb, hogy gy pr-
bljunk egy programot elkpzelni, mint egymssal klcsnhatsban lv fogalmakat, me-
lyeket osztlyok s objektumok kpviselnek, nem pedig gy, mint egy halom adatszerke-
zetet, a bitekkel zsonglrkd fggvnyekkel.
1.6.2. Javaslatok C++ programozknak
Sokan mr egy vtized ta hasznljk a C++-t. Mg tbben hasznljk egyetlen krnyezet-
ben s tanultak meg egytt lni a korai fordtk s els genercis knyvtrak miatti korl-
tozsokkal. Ami a tapasztalt C++ programozk figyelmt gyakran elkerli, nem is annyira
az j eszkzk megjelense, mint inkbb ezen eszkzk kapcsolatainak vltozsa, ami
alapjaiban j programozsi mdszereket kvetel meg. Ms szval, amire annak idejn nem
gondoltunk vagy haszontalannak tartottunk, ma mr kivl mdszerr vlhatott, de ezekre
csak az alapok jragondolsval tallunk r.
1. Megjegyzsek az olvashoz 19
Olvassuk t a fejezeteket sorban. Ha mr ismerjk a fejezet tartalmt, gondolatban ismtel-
jk t. Ha mg nem ismerjk, valami olyat is megtanulhatunk, amire eredetileg nem szm-
tottunk. n magam elg sokat tanultam e knyv megrsbl, s az a gyanm, hogy kevs
C++ programoz ismeri az sszes itt bemutatott sszes eszkzt s eljrst. Ahhoz, hogy he-
lyesen hasznljunk egy nyelvet, behatan kell ismernnk annak eszkzeit, mdszereit. Fel-
ptse s pldi alapjn ez a knyv megfelel rltst biztost.
1.7. Programozsi megfontolsok a C++-ban
A programtervezst idelis esetben hrom fokozatban kzeltjk meg. Elszr tisztn rthe-
tv tesszk a problmt (elemzs, analzis), ezutn azonostjuk a f fogalmakat, melyek
egy megoldsban szerepelnek (tervezs), vgl a megoldst egy programban fejezzk ki
(programozs). A problma rszletei s a megolds fogalmai azonban gyakran csak akkor
vlnak tisztn rthetv, amikor egy elfogadhatan futtathat programban akarjuk kifejez-
ni azokat. Ez az, ahol szmt, milyen programozsi nyelvet vlasztunk.
A legtbb alkalmazsban vannak fogalmak, melyeket nem knny a kapcsold adatok
nlkl az alaptpusok egyikvel vagy fggvnnyel brzolni. Ha adott egy ilyen fogalom,
hozzunk ltre egy osztlyt, amely a programban kpviselni fogja. A C++ osztlyai tpusok,
melyek meghatrozzk, hogyan viselkednek az osztlyba tartoz objektumok, hogyan jn-
nek ltre, hogyan kezelhetk s hogyan sznnek meg. Az osztly lerhatja azt is, hogyan
jelennek meg az objektumok, br a programtervezs korai szakaszban ez nem szksg-
szeren f szempont. J programok rsnl az a legfontosabb, hogy gy hozzunk ltre osz-
tlyokat, hogy mindegyikk egyetlen fogalmat, tisztn brzoljon. Ez ltalban azt jelenti,
hogy a kvetkez krdsekre kell sszpontostani: Hogyan hozzuk ltre az osztly objek-
tumait? Msolhatk-e s/vagy megsemmisthetk-e az osztly objektumai? Milyen mvele-
tek alkalmazhatk az objektumokra? Ha nincsenek j vlaszok e krdsekre, az a legval-
sznbb, hogy a fogalom nem tiszta. Ekkor j tlet, ha tovbb gondolkodunk a problmn
s annak javasolt megoldsn, ahelyett, hogy azonnal elkezdennk a kd kidolgozst.
A legknnyebben kezelhet fogalmak azok, amelyeknek hagyomnyos matematikai megfo-
galmazsuk van: mindenfajta szmok, halmazok, geometriai alakzatok stb. A szvegkzpon-
t bemenet s kimenet, a karakterlncok, az alaptrolk, az ezekre a trolkra alkalmazhat
alap-algoritmusok, valamint nhny matematikai osztly a C++ standard knyvtrnak rszt
kpezik (3. fejezet, 16.1.2). Ezenkvl elkpeszt vlasztkban lteznek knyvtrak, melyek
ltalnos s rszterletekre szakosodott elemeket tmogatnak.
Bevezets 20
Az egyes fogalmak (s a hozzjuk kapcsold elemek) nem lgres trben lteznek, min-
dig rokonfogalmak csoportjba tartoznak. Az osztlyok kzti kapcsolatok szervezse egy
programon bell vagyis az egy megoldsban szerepl klnbz elemek kzti pontos
kapcsolatok meghatrozsa gyakran nehezebb, mint az egyes osztlyokat kijellni. Jobb,
ha az eredmny nem rendetlensg, melyben minden osztly fgg minden msiktl.
Vegynk kt osztlyt: A-t s B-t. Az olyan kapcsolatok, mint az A hv B-beli fggvnyeket,
A ltrehoz B-ket s A-nak van egy B tagja ritkn okoznak nagy problmt, mg az olya-
nok, mint az A hasznl B-beli adatot rendszerint kikszblhetk.
Az sszetettsg kezelsnek egyik legersebb eszkze a hierarchikus rendezs, vagyis a ro-
kon elemek faszerkezetbe szervezse, ahol a fa gykere a legltalnosabb elem. A C++-ban
a szrmaztatott osztlyok ilyen fastruktrkat kpviselnek. Egy program gyakran gy szer-
vezhet, mint fk halmaza, vagy mint osztlyok irnytott krmentes grfja. Vagyis a prog-
ramoz nhny alaposztlyt hoz ltre, melyekhez sajt szrmaztatott osztlyaik halmaza
tartozik. Az elemek legltalnosabb vltozatnak (a bzisosztlynak) a kezelst vgz m-
veletek meghatrozsra a virtulis fggvnyeket (2.5.5, 12.2.6) hasznlhatjuk. Szksg
esetn ezen mveletek megvalstsa az egyedi esetekben (a szrmaztatott osztlyoknl)
finomthat.
Nha mg az irnytott krmentes grf sem ltszik kielgtnek a programelemek szervez-
sre; egyes elemek klcsns sszefggse rklttnek tnik. Ilyen esetben megprbljuk
a ciklikus fggsgeket behatrolni, hogy azok ne befolysoljk a program tfog rendsze-
rt. Ha nem tudjuk kikszblni vagy behatrolni az ilyen klcsns fggseket, valsz-
n, hogy olyan gondban vagyunk, melybl nincs programozsi nyelv, amely kisegtene.
Hacsak ki nem tudunk eszelni knnyen megllapthat kapcsolatokat az alapfogalmak k-
ztt, valszn, hogy a program kezelhetetlenn vlik. A fggsgi grfok kibogozsnak
egyik eszkze a fellet (interfsz) tiszta elklntse a megvalststl (implementci).
A C++ erre szolgl legfontosabb eszkzei az absztrakt osztlyok (2.5.4, 12.3).
A kzs tulajdonsgok kifejezsnek msik formja a sablon (template, 2.7, 13. fejezet). Az
osztlysablonok osztlyok csaldjt rjk le. Egy listasablon pldul a T elemek listjt ha-
trozza meg, ahol T brmilyen tpus lehet. A sablon teht azt adja meg, hogyan hozhatunk
ltre egy tpust egy msik tpus, mint paramter tadsval. A legszoksosabb sablonok az
olyan trolosztlyok, mint a listk, tmbk s asszociatv tmbk, valamint az ilyen tro-
lkat hasznl alap-algoritmusok. Rendszerint hiba, ha egy osztly s a vele kapcsolatos
fggvnyek paramterezst rklst hasznl tpussal fejezzk ki. A legjobb sablonokat
hasznlni.
1. Megjegyzsek az olvashoz 21
Emlkeztetnk arra, hogy sok programozsi feladat egyszeren s tisztn elvgezhet ele-
mi tpusok, adatszerkezetek, vilgos fggvnyek s nhny knyvtri osztly segtsgvel.
Az j tpusok lersban szerepl teljes appartust nem szabad hasznlni, kivve, ha val-
ban szksg van r.
A Hogyan rjunk C++-ban j programot? nagyon hasonlt a Hogyan rjunk j przt? kr-
dsre. Kt vlasz van: Tudnunk kell, mit akarunk mondani s Gyakoroljunk. Sznleljk
a j rst. Mind a kett ppgy helytll a C++, mint brmely termszetes nyelv esetben
s tancsukat ppolyan nehz kvetni.
1.8. Tancsok
me nhny szably, amelyet figyelembe vehetnk a C++ tanulsakor. Ahogy jrtasabbak
lesznk, tovbbfejleszthetjk ezeket sajt programfajtinkhoz, programozsi stlusunkhoz
illeszkeden. A szablyok szndkosan nagyon egyszerek, gy nlklzik a rszleteket.
Ne vegyk ket tlzottan komolyan: a j programok rshoz elssorban intelligencia, z-
ls, trelem kell. Ezeket nem fogjuk elsre elsajttani. Ksrletezznk!
[1] Amikor programozunk, valamilyen problma megoldsra szletett tleteink konk-
rt megvalstst hozzuk ltre. Tkrzze a program szerkezete olyan kzvetlenl
ezeket az tleteket, amennyire csak lehetsges:
a) Ha valamire gy gondolunk, mint kln tletre, tegyk osztlly.
b) Ha klnll egyedknt gondolunk r, tegyk egy osztly objektumv.
c) Ha kt osztlynak van kzs fellete, tegyk ezt a felletet absztrakt osztlly.
d) Ha kt osztly megvalstsban van valami kzs, tegyk bzisosztlly e
kzs tulajdonsgokat.
e) Ha egy osztly objektumok trolja, tegyk sablonn.
f) Ha egy fggvny egy trol szmra val algoritmust valst meg, tegyk
fggvnysablonn, mely egy trolcsald algoritmust rja le.
g) Ha osztlyok, sablonok stb. egy halmazn bell logikai rokonsg van, tegyk
azokat kzs nvtrbe.
[2] Ha olyan osztlyt hozunk ltre, amely nem matematikai egyedet r le (mint egy
mtrix vagy komplex szm) vagy nem alacsonyszint tpust (mint egy lncolt lista)
a) ne hasznljunk globlis adatokat (hasznljunk tagokat),
b) ne hasznljunk globlis fggvnyeket,
Bevezets 22
c) ne hasznljunk nyilvnos adattagokat,
d) ne hasznljunk bart (friend) fggvnyeket, kivve a) vagy c) elkerlsre,
e) ne tegynk egy osztlyba tpusazonost mezket, hasznljunk inkbb virtu-
lis fggvnyeket,
f) ne hasznljunk helyben kifejtett fggvnyeket, kivve ha jelents optimaliz-
lsrl van sz.
Egyedi s rszletesebb gyakorlati szablyokat az egyes fejezetek Tancsok rszben tall-
hatunk. Emlkeztetjk az olvast, hogy ezek a tancsok csak tmutatsul szolglnak, nem
megvltoztathatatlan trvnyek. A tancsokat csak ott kvessk, ahol rtelme van. Nincs
ptszere az intelligencinak, a tapasztalatnak, a jzan sznek s a j zlsnek.
A soha ne tegyk ezt alak szablyokat haszontalannak tekintem. Kvetkezskppen
a legtbb tancsot javaslatknt fogalmaztam meg; azt rtam le, mit tegynk. A negatv ja-
vaslatokat pedig nem gy kell rteni, mint tiltsokat: nem tudok a C++ olyan f tulajdon-
sgrl, melyet ne lttam volna jl felhasznlni. A Tancsok nem tartalmaznak magyar-
zatokat. Helyette minden tancs mellett hivatkozs tallhat a knyv megfelel rszre.
Ahol negatv tancs szerepel, a hivatkozott rsz rendszerint alternatv javaslatot tartalmaz.
1.8.1. Hivatkozsok
Barton, 1994 John J. Barton and Lee R. Nackman: Scientific and Engineering C++. Addison-
Wesley. Reading, Mass. 1994. ISBN 1-201-53393-6.
Berg, 1995 William Berg, Marshall Cline, and Mike Girou: Lessons Learned from the OS/400
OO Project. CACM. Vol. 38 No. 10. October 1995.
Booch, 1994 Grady Booch: Object-Oriented Analysis and Design. Benjamin/Cummings. Menlo
Park, Calif. 1994. ISBN 0-8053-5340-2.
Budge, 1992 Kent Budge, J. S. Perry, an A. C. Robinson: High-Performance Scientific
Computation using C++. Proc. USENIX C++Conference. Portland, Oregon. August
1992.
C, 1990 X3 Secretariat: Standard - The C Language. X3J11/90-013. ISO Standard ISO/IEC
9899. Computer and Business Equipment Manufacturers Association. Washington,
DC, USA.
C++, 1998 X+ Secretariat: International Standard- The C++ Language. X3J16-14882.
Information Technology Council (NSITC). Washington, DC, USA.
Campbell, 1987 Roy Campbell, et al.: The Design of a Multirocessor Operating System. Proc.
USENIX C++ Conference. Santa Fe, New Mexico. November 1987.
Coplien, 1995 James O. Coplien and Douglas C. Schmidt (editors): Pattern Languages of
Program Design. Addison-Wesley. Reading, Mass. 1995. ISBN 1-201-60734-4.
1. Megjegyzsek az olvashoz 23
Dahl, 1970 O-J. Dahl, B. Myrhaug, and K. Nygaard: SIMULA Common Base Language.
Norwegian Computing Center S-22. Oslo, Norway. 1970.
Dahl, 1972 O-J. Dahl, and C. A. R. Hoare: Hierarchical Program Consturction in Structured
Programming. Academic Press, New York. 1972.
Ellis, 1989 Margaret A. Ellis and Bjarne Stroustrup: The Annotated C++ Reference Manual.
Addison-Wesley. Reading, Mass. 1990. ISBN 0-201-51459-1.
Gamma, 1995 Erich Gamma, et al.: Design Patterns. Addison-Wesley. Reading, Mass. 1995. ISBN
0-201-63361-2.
Goldberg, 1983 A. Goldberg and D. Robson: SMALLTALK- 80 - The Language and Its
Implementation. Addison-Wesley. Reading, Mass. 1983.
Griswold, 1970 R. E. Griswold, et al.: The Snobol4 Programming Language. Prentice-Hall.
Englewood Cliffs, New Jersey. 1970.
Griswold, 1983 R. E. Grisswold and M. T. Griswold: The ICON Programming Language. Prentice-
Hall. Englewood Cliffs, New Jersey. 1983.
Hamilton, 1993 G. Hamilton and P. Kougiouris: The Spring Nucleus: A Microkernel for Objects.
Proc. 1993 Summer USENIX Conference. USENIX.
Henricson, 1997 Mats Henricson and Erik Nyquist: Industrial Strenght C++: Rules and
Recommendations. Prentice-Hall. Englewood Cliffs, New Jersey. 1997. ISBN 0-13-
120965-5.
Ichbiah, 1979 Jean D. Ichbiah, et al.: Rationale for the Design of the ADA Programming
Language. SIGPLAN Notices. Vol. 14 No. 6. June 1979.
Kamath, 1993 Yogeesh H. Kamath, Ruth E. Smilan, and Jean G. Smith: Reaping Benefits with
Object-Oriented Technology. AT&T Technical Journal. Vol. 72 No. 5.
September/October 1993.
Kernighan, 1978 Brian W. Kernighan and Dennis M. Ritchie: The C Programming Language.
Prentice-Hall. Englewood Cliffs, New Jersey. 1978.
Kernighan, 1988 Brian W. Kernighan and Dennis M. Ritchie: The C Programming Language
(Second Edition). Prentice-Hall. Enlewood Cliffs, New Jersey. 1988. ISBN 0-13-
110362-8.
Koenig, 1989 Andrew Koenig and Bjarne Stroustrup: C++: As close to C as possible - but no
closer. The C++ Report. Vol. 1 No. 7. July 1989.
Koenig, 1997 Andrew Koenig and Barbara Moo: Ruminations on C++. Addison Wesley
Longman. Reading, Mass. 1997. ISBN 1-201-42339-1.
Knuth, 1968 Donald Knuth: The Art of Computer Programming. Addison-Wesley. Reading,
Mass.
Liskowv, 1979 Barbara Liskov et al.: Clu Reference Manual. MIT/LCS/TR-225. MIT Cambridge.
Mass. 1979.
Martin, 1995 Robert C. Martin: Designing Object-Oriented C++ Applications Using the Booch
Method. Prentice-Hall. Englewood Cliffs, New Jersey. 1995. ISBN 0-13-203837-4.
Orwell, 1949 George Orwell: 1984. Secker and Warburg. London. 1949.
Bevezets 24
Parrington, 1995 Graham Parrington et al.: The Design and Implementation of Arjuna. Computer
Systems. Vol. 8 No. 3. Summer 1995.
Richards, 1980 Martin Richards and Colin Whitby-Strevens: BCPL - The Language and Its
Compiler. Cambridge University Press, Cambridge. England. 1980. ISBN 0-521-
21965-5.
Rosler, 1984 L. Rosler: The Evolution of C - Past and Future. AT&T Bell Laboratories Technical
Journal. Vol. 63 No. 8. Part 2. October 1984.
Rozier, 1988 M. Rozier, et al.: CHORUS Distributed Operating Systems. Computing Systems.
Vol. 1 no. 4. Fall 1988.
Sethi, 1981 Ravi Sethi: Uniform Syntax for Type Expressions and Declarations. Software
Practice & Experience. Vol. 11. 1981.
Stepanov, 1994 Alexander Stepanov and Meng Lee: The Standard Template Library. HP Labs
Technical Report HPL-94-34 (R. 1). August, 1994.
Stroustrup, 1986 Bjarne Stroustrup: The C++ Programming Language. Addison-Wesley. Reading,
Mass. 1986. ISBN 0-201-12078-X.
Stroustrup, 1987 Bjarne Stroustrup and Jonathan Shopiro: A Set of C Classes for Co-Routine Style
Programming. Proc. USENIX C++ conference. Santa Fe, New Mexico. November
1987.
Stroustrup, 1991 Bjarne Stroustrup: The C++ Programming Language (Second Edition) Addison-
Wesley. Reading, Mass. 1991. ISBN 0-201-53992-6.
Strostrup, 1994 Bjarne Stroustrup: The Design and Evolution of C++. Addison-Wesley. Reading,
Mass. 1994. ISBN 0-201-54330-3.
Tarjan, 1983 Robert E. Tarjan: Data Structures and Network Algorithms. Society for Industrial
and Applied Mathematics. Philadelphia, Penn. 1983. ISBN 0-898-71187-8.
Unicode, 1996 The Unicode Consortium: The Unicode Standard, Version 2.0. Addison-Wesley
Developers Press. Reading, Mass. 1996. ISBN 0-201-48345-9.
UNIX, 1985 UNIX Time-Sharing System: Programmer's Manual. Research Version, Tenth
Edition. AT&T Bell Laboratories, Murray Hill, New Jersey. February 1985.
Wilson, 1996 Gregory V. Wilson and Paul Lu (editors): Parallel Progrmming Using C++. The
MIT Press. Cambridge. Mass. 1996. ISBN 0-262-73118-5.
Wikstrm, 1987 Ake Wikstrm: Functional Programming Using ML. Prentice-Hall. Englewood
Cliffs, New Jersey. 1987.
Woodward, 1974 P. M. Woodward and S. G. Bond: Algol 68-R Users Guide. Her Majesty's Stationery
Office. London. England. 1974.
1. Megjegyzsek az olvashoz 25
Kirnduls a C++-ban
Az els tennivalnk: ljnk
meg minden trvnytudt
(Shakespeare: VI. Henrik, II.
rsz ford. Nmeth Lszl)
Mi a C++? Programozsi megkzeltsek Eljrskzpont programozs Modularits
Kln fordts Kivtelkezels Elvont adatbrzols Felhasznli tpusok Konkrt
tpusok Absztrakt tpusok Virtulis fggvnyek Objektumorientlt programozs l-
talnostott programozs Trolk Algoritmusok Nyelv s programozs Tancsok
2.1. Mi a C++?
A C++ ltalnos cl programozsi nyelv, melynek f alkalmazsi terlete a rendszerprog-
ramozs s
egy jobbfajta C,
tmogatja az elvont adatbrzolst,
tmogatja az objektumorientlt programozst, valamint
az ltalnostott programozst.
2
Ez a fejezet elmagyarzza, mit jelentenek a fentiek, anlkl, hogy belemenne a nyelv meg-
hatrozsnak finomabb rszleteibe. Clja ltalnos ttekintst adni a C++-rl s hasznla-
tnak f mdszereirl, nem pedig a C++ programozs elkezdshez szksges rszletes
informcit adni az olvasnak.
Ha az olvas tl elnagyoltnak tallja e fejezet nmelyik rsznek trgyalsmdjt, egysze-
ren ugorja t s lpjen tovbb. Ksbb mindenre rszletes magyarzatot kap. Mindeneset-
re, ha tugrik rszeket a fejezetben, tegye meg magnak azt a szvessget, hogy ksbb
visszatr rjuk.
A nyelvi tulajdonsgok rszletes megrtse mg ha a nyelv sszes tulajdonsg is nem
ellenslyozhatja azt, ha hinyzik az tfog szemlletnk a nyelvrl s hasznlatnak alap-
vet mdszereirl.
2.2. Programozsi megkzeltsek
Az objektumorientlt (objektumkzpont) programozs egy programozsi md a j
programok rsa kzben felmerl sereg problma megoldsnak egy megkzeltse (pa-
radigma). Ha az objektumorientlt programozsi nyelv szakkifejezs egyltaln jelent
valamit, olyan programozsi nyelvet kell hogy jelentsen, amely az objektumokat kzp-
pontba helyez programozsi stlust tmogat eljrsokrl gondoskodik.
Itt fontos megklnbztetnnk kt fogalmat: egy nyelvrl akkor mondjuk, hogy tmogat
egy programozsi stlust, ha olyan szolgltatsai vannak, melyek ltal az adott stlus hasz-
nlata knyelmes (knny, biztonsgos s hatkony) lesz. A tmogats hinyzik, ha kiv-
teles erfeszts vagy gyessg kell az ilyen programok rshoz; ekkor a nyelv csupn
megengedi, hogy az adott megkzeltst hasznljuk. Lehet strukturlt programot rni
Fortran77-ben s objektumkzpontt C-ben, de szksgtelenl nehezen, mivel ezek a nyel-
vek nem tmogatjk kzvetlenl az emltett megkzeltseket.
Az egyes programozsi mdok tmogatsa nem csak az adott megkzelts kzvetlen hasz-
nlatt lehetv tv nyelvi szolgltatsok magtl rtetd formjban rejlik, hanem a for-
dtsi/futsi idbeni ellenrzsek finomabb formiban, melyek vdelmet adnak a stlustl
val akaratlan eltrs ellen. A tpusellenrzs erre a legkzenfekvbb plda, de a ktrtel-
msg szlelse s a futsi idej ellenrzsek szintn a programozsi mdok nyelvi tmo-
gatshoz tartoznak. A nyelven kvli szolgltatsok, mint a knyvtrak s programozsi
krnyezetek, tovbbi tmogatst adnak az egyes megkzeltsi mdokhoz.
Bevezets 28
Egy nyelv nem szksgszeren jobb, mint egy msik, csak azrt, mert olyan tulajdonsgok-
kal rendelkezik, amelyek a msikban nem tallhatk meg. Sok plda van ennek az ellenke-
zjre is. A fontos krds nem annyira az, milyen tulajdonsgai vannak egy nyelvnek,
hanem inkbb az, hogy a meglv tulajdonsgok elegendek-e a kvnt programozsi st-
lusok tmogatsra a kvnt alkalmazsi terleteken. Ennek kvnalmai a kvetkezk:
1. Minden tulajdonsg tisztn s elegnsan a nyelv szerves rsze legyen.
2. A tulajdonsgokat egymssal prostva is lehessen hasznlni, hogy olyan
megoldst adjanak, melyhez egybknt kln nyelvi tulajdonsgok lennnek
szksgesek.
3. A lehet legkevesebb legyen az l- s specilis cl tulajdonsg.
4. Az egyes tulajdonsgok megvalstsa nem okozhat jelents tbbletterhelst
olyan programoknl, melyek nem ignylik azokat.
5. A felhasznlnak csak akkor kell tudnia a nyelv valamely rszhalmazrl, ha
kifejezetten hasznlja azt egy program rshoz.
A fentiek kzl az els elv az eszttikhoz s a logikhoz val folyamods. A kvetkez
kett a minimalizmus gondolatnak kifejezse, az utols kett pedig gy sszesthet: ami-
rl nem tudunk, az nem fj.
A C++-t gy terveztk, hogy az elvont adatbrzolst, illetve az objektumorientlt s az lta-
lnostott programozst tmogassa, mgpedig az e megszortsok mellett tmogatott hagyo-
mnyos C programozsi mdszereken kvl. Nem arra szolgl, hogy minden felhasznlra
egyetlen programozsi stlust knyszertsen.
A kvetkezkben nhny programozsi stlust s az azokat tmogat fbb tulajdonsgokat
vesszk szmba. A bemutats egy sor programozsi eljrssal folytatdik, melyek az eljrs-
kzpont (procedurlis) programozstl elvezetnek az objektumorientlt programozsban
hasznlt osztlyhierarchiig s a sablonokat hasznl ltalnostott (generikus) programo-
zsig. Minden megkzelts az eldjre pl, mindegyik hozztesz valamit a C++ progra-
mozk eszkztrhoz, s mindegyik egy bevlt tervezsi mdot tkrz.
A nyelvi tulajdonsgok bemutatsa nem teljes. A hangsly a tervezsi megkzeltseken s
a programok szerkezeti felptsn van, nem a nyelvi rszleteken. Ezen a szinten sokkal
fontosabb, hogy fogalmat kapjunk arrl, mit lehet megtenni C++-t hasznlva, mint hogy
megrtsk, hogyan.
2. Kirnduls a C++-ban 29
2.3. Eljrskzpont programozs
Az eredeti programozsi alapelv a kvetkez:
Dntsd el, mely eljrsokra van szksged
s hasznld azokhoz a lehet legjobb algoritmusokat.
A kzppontban az eljrs ll a kvnt szmtshoz szksges algoritmus. A nyelvek ezt
az alapelvet fggvnyparamterek tadsval s a fggvnyek ltal visszaadott rtkekkel
tmogatjk. Az e gondolkodsmddal kapcsolatos irodalom tele van a paramtertads s
a klnbz paramterfajtk megklnbztetsi mdjainak (eljrsok, rutinok, makrk
stb.) trgyalsval.
A j stlus jellegzetes pldja az albbi ngyzetgyk-fggvny. tadva egy ktszeres pon-
tossg lebegpontos paramtert, a fggvny visszaadja az eredmnyt. Ezt egy jl rthet
matematikai szmtssal ri el:
double sqrt(double arg)
{
// a ngyzetgyk kiszmtsnak kdja
}
void f()
{
double root2 = sqrt(2);
// ...
}
A kapcsos zrjelek a C++-ban valamilyen csoportba foglalst fejeznek ki; itt a fggvny tr-
zsnek kezdett s a vgt jelzik. A ketts trtvonal // egy megjegyzs (comment) kezdete,
mely a sor vgig tart. A void kulcssz jelzi, hogy az f fggvny nem ad vissza rtket.
Programszervezsi szempontbl a fggvnyeket arra hasznljuk, hogy rendet teremtsnk
az eljrsok labirintusban. Magukat az algoritmusokat fggvnyhvsokkal s ms nyelvi
szolgltatsok hasznlatval rjuk meg. A kvetkez alpontok vzlatos kpet adnak a C++
legalapvetbb szolgltatsairl a szmtsok kifejezshez.
Bevezets 30
2.3.1. Vltozk s aritmetika
Minden nvnek s kifejezsnek tpusa van, amely meghatrozza a vgrehajthat mveleteket:
int inch;
A fenti deklarci pldul azt adja meg, hogy inch tpusa int (vagyis inch egy egsz tpus
vltoz).
A deklarci olyan utasts, mely a programba egy nevet vezet be. Ehhez a nvhez egy t-
pust rendel. A tpus egy nv vagy kifejezs megfelel hasznlatt hatrozza meg.
A C++ tbb alaptpussal rendelkezik, melyek kzvetlen megfeleli bizonyos hardverszol-
gltatsoknak. Pldul:
bool // logikai tpus, lehetsges rtkei: true (igaz) s false (hamis)
char // karakter, pldul 'a', 'z', vagy '9'
int // egsz rtk, pldul 1, 42, vagy 1216
double // ktszeres pontossg lebegpontos szm, pldul 3.14 vagy 299793.0
A char vltozk termszetes mrete egy karakter mrete az adott gpen (rendesen egy
bjt), az int vltozk az adott gpen mkd egsz tpus aritmetikhoz igazodik (rend-
szerint egy gpi sz).
Az aritmetikai mveletek e tpusok brmilyen prostsra hasznlhatk:
+ // sszeads vagy eljel, egy- s ktoperandus is lehet
- // kivons vagy eljel, egy- s ktoperandus is lehet
* // szorzs
/ // oszts
% // maradkkpzs
Ugyangy az sszehasonlt mveletek is:
== // egyenl
!= // nem egyenl
< // kisebb
> // nagyobb
<= // kisebb vagy egyenl
>= // nagyobb vagy egyenl
rtkadsokban s aritmetikai mveletekben a C++ az alaptpusok kztt elvgez minden
rtelmes talaktst, gy azokat egymssal tetszs szerint keverhetjk:
2. Kirnduls a C++-ban 31
void some_function() // rtket vissza nem ad fggvny
{
double d = 2.2; // lebegpontos szm kezdeti rtkadsa
int i = 7; // egsz kezdeti rtkadsa
d = d+i; // sszeg rtkadsa
i = d*i; // szorzat rtkadsa
}
Itt = az rtkad mvelet jele s == az egyenlsget teszteli, mint a C-ben.
2.3.2. Elgazsok s ciklusok
A C++ az elgazsok s ciklusok kifejezsre rendelkezik a hagyomnyos utastskszlet-
tel. me egy egyszer fggvny, mely a felhasznltl vlaszt kr s a vlasztl fgg logi-
kai rtket ad vissza:
bool accept()
{
cout << "Do you want to proceed (y or n)?\n"; // krds kirsa
char answer = 0;
cin >> answer; // vlasz beolvassa
if (answer == 'y') return true;
return false;
}
A << (tedd bele) mveleti jelet kimeneti opertorknt hasznltuk; a cout a szabvnyos ki-
meneti adatfolyam. A >> (olvasd be) a bemenet mveleti jele, a cin a szabvnyos beme-
n adatfolyam. A >> jobb oldaln ll kifejezs hatrozza meg, milyen bemenet fogadhat
el s ez a beolvass clpontja. A \n karakter a kirt karakterlnc vgn j sort jelent.
A plda kiss javthat, ha egy 'n' vlaszt is szmtsba vesznk:
bool accept2()
{
cout << "Do you want to proceed (y or n)?\n"; // krds kirsa
char answer = 0;
cin >> answer; // vlasz beolvassa
Bevezets 32
switch (answer) {
case 'y':
return true;
case 'n':
return false;
default:
cout << "I'll take that for a no.\n"; // nemleges vlasznak veszi
return false;
}
}
A switch utasts egy rtket ellenriz, llandk halmazt alapul vve. A case konstansok-
nak klnllknak kell lennik, s ha az rtk egyikkel sem egyezik, a vezrls a default
cmkre kerl. A programoznak nem kell alaprtelmezsrl (default) gondoskodnia.
Kevs programot rnak ciklusok nlkl. Esetnkben szeretnnk lehetsget adni a felhasz-
nlnak nhny prblkozsra:
bool accept3()
{
int tries = 1;
while (tries < 4) {
cout << "Do you want to proceed (y or n)?\n"; // krds kirsa
char answer = 0;
cin >> answer; // vlasz beolvassa
switch (answer) {
case 'y':
return true;
case 'n':
return false;
default:
cout << "Sorry, I don't understand that.\n" ; // nem rti a vlaszt
tries = tries + 1;
}
}
cout << "I'll take that for a no.\n"; // nemleges vlasznak veszi
return false;
}
A while utasts addig hajtdik vgre, amg a felttele hamis nem lesz.
2. Kirnduls a C++-ban 33
2.3.3. Mutatk s tmbk
Egy tmbt gy hatrozhatunk meg:
char v[10]; // 10 karakterbl ll tmb
Egy mutatt gy:
char* p; // mutat karakterre
A deklarcikban a [ ] jelentse tmbje (array of), mg a * jelentse mutatja (pointer to).
Minden tmbnek 0 az als hatra, teht v-nek tz eleme van, v[0]v[9]. A mutat vltoz
a megfelel tpus objektum cmt tartalmazhatja:
p = &v[3]; // p a v negyedik elemre mutat
A cme jelentssel br opertor az egyoperandus &. Lssuk, hogyan msolhatjuk t egy
tmb tz elemt egy msik tmbbe:
void another_function()
{
int v1[10];
int v2[10];
// ...
for (int i=0; i<10; ++i) v1[i]=v2[i];
}
A for utasts gy olvashat: lltsuk i-t 0-ra, amg i kisebb, mint 10, az i-edik elemet m-
soljuk t, s nveljk meg i-t. Ha egsz tpus vltozra alkalmazzuk, a ++ nvel mve-
leti jel az rtket egyszeren eggyel nveli.
2.4. Modulris programozs
Az vek sorn a programtervezs slypontja az eljrsok fell az adatszervezs irnyba to-
ldott el. Egyebek mellett ez a programok nagyobb mretben tkrzdik. Az egymssal
rokon eljrsokat az ltaluk kezelt adatokkal egytt gyakran modul-nak nevezzk. A meg-
kzelts alapelve ez lesz:
Bevezets 34
Dntsd el, mely modulokra van szksg s oszd fel
a programot gy, hogy az adatokat modulokba helyezed.
Ez az adatrejts elve. Ahol az eljrsok nincsenek az adatokkal egy csoportban, az eljrs-
kzpont programozs megfelel a clnak. Az egyes modulokon belli eljrsokra a j el-
jrsok tervezsnek mdja is alkalmazhat. A modulra a legkznsgesebb plda egy
verem (stack) ltrehozsa. A megoldand f problmk:
1. Gondoskodni kell a verem felhasznli felletrl (pl. push() s pop()
fggvnyek).
2. Biztostani kell, hogy a verem megjelentse (pl. az elemek tmbje) csak ezen
a felhasznli felleten keresztl legyen hozzfrhet.
3. Biztostani kell a verem els hasznlat eltti elksztst (inicializlst).
A C++ egymssal rokon adatok, fggvnyek stb. klnll nvterekbe val csoportosts-
ra ad lehetsget. Egy Stack modul felhasznli fellete pldul gy adhat meg s hasznl-
hat:
namespace Stack { // fellet
void push(char);
char pop();
}
void f()
{
Stack::push('c');
if (Stack::pop() != 'c') error("lehetetlen");
}
A Stack:: minsts azt jelzi, hogy a push() s a pop() a Stack nvtrhez tartoznak. E nevek
mshol trtn hasznlata nem lesz befolyssal erre a programrszre s nem fog zavart
okozni.
A Stack kifejtse lehet a program kln fordthat rsze:
namespace Stack { // megvalsts
const int max_size = 200;
char v[max_size];
int top = 0;
2. Kirnduls a C++-ban 35
void push(char c) { /* tlcsorduls ellenrzse s c behelyezse */ }
char pop() { /* alulcsorduls ellenrzse s a legfels elem kiemelse */ }
}
A Stack modul fontos jellemzje, hogy a felhasznli kdot a Stack::push()-t s
a Stack::pop()-ot megvalst kd elszigeteli a Stack adatbrzolstl. A felhasznlnak
nem kell tudnia, hogy a verem egy tmbbel van megvalstva, a megvalsts pedig anl-
kl mdosthat, hogy hatssal lenne a felhasznli kdra.
Mivel az adat csak egyike az elrejtend dolgoknak, az adatrejts elve az informcirejts
elvv terjeszthet ki; vagyis a fggvnyek, tpusok stb. nevei szintn modulba helyezhe-
tk. Kvetkezskppen a C++ brmilyen deklarci elhelyezst megengedi egy nvtrben
(8.2.).
A fenti Stack modul a verem egy brzolsmdja. A kvetkezkben tbbfle vermet hasz-
nlunk a klnbz programozi stlusok szemlltetsre.
2.4.1. Kln fordts
A C++ tmogatja a C kln fordtsi elvt. Ezt arra hasznlhatjuk, hogy egy programot rsz-
ben fggetlen rszekre bontsunk.
Azokat a deklarcikat, melyek egy modul fellett rjk le, jellemzen egy fjlba rjuk,
melynek neve a hasznlatot tkrzi. Ennek kvetkeztben a
namespace Stack { // fellet
void push(char);
char pop();
}
a stack.h nev fjlba kerl, a felhasznlk pedig ezt az gynevezett fejllomnyt (header)
beptik (#include):
#include "stack.h" // a fellet beptse
void f()
{
Stack::push('c');
if (Stack::pop() != 'c') error("impossible");
}
Bevezets 36
Ahhoz, hogy a fordtnak segtsnk az egysgessg s kvetkezetessg biztostsban,
a Stack modult megvalst fjl szintn tartalmazza a felletet:
#include "stack.h" // a fellet beptse
namespace Stack { // brzols
const int max_size = 200;
char v[max_size];
int top = 0;
}
void Stack::push(char c) { /* tlcsorduls ellenrzse s c behelyezse */ }
char Stack::pop() { /* alulcsorduls ellenrzse s a legfels elem kiemelse */ }
A felhasznli kd egy harmadik fjlba kerl (user.c). A user.c s a stack.c fjlokban lv
kd kzsen hasznlja a stack.h-ban megadott veremfelletet, de a kt fjl egybknt fg-
getlen s kln-kln lefordthat. A program rszei a kvetkezkppen brzolhatk:
A kln fordts kvetelmny minden valdi (tnyleges hasznlatra sznt) program eset-
ben, nem csak a modulris felptseknl (mint pl. a Stack). Pontosabban, a kln ford-
ts hasznlata nem nyelvi kvetelmny; inkbb annak mdja, hogyan lehet egy adott nyelvi
megvalsts elnyeit a legjobban kihasznlni. A legjobb, ha a modularitst a lehet legna-
gyobb mrtkig fokozzuk, nyelvi tulajdonsgok ltal brzoljuk, majd kln-kln hatko-
nyan fordthat fjlokon keresztl valstjuk meg (8. s 9. fejezet).
2. Kirnduls a C++-ban 37
veremfellet
#include "stack.h"
verem megvalstsa
#include "stack.h"
verem hasznlata
stack.h:
stack.c: user.c:
2.4.2. Kivtelkezels
Ha egy programot modulokra bontunk, a hibakezelst a modulok szintjn kell elvgez-
nnk. A krds, melyik modul felels az adott hiba kezelsrt? A hibt szlel modul
gyakran nem tudja, mit kell tennie. A helyrelltsi tevkenysg a mveletet kezdemnye-
z modultl fgg, nem attl, amelyik szlelte a hibt, mikzben megksrelte a mvelet
vgrehajtst. A programok nvekedsvel klnsen kiterjedt knyvtrhasznlat ese-
tn a hibk (vagy ltalnosabban: a kivteles esemnyek) kezelsi szabvnyai egyre
fontosabb vlnak.
Vegyk megint a Stack pldt. Mit kell tennnk, amikor tl sok karaktert prblunk push()-
sal egy verembe rakni? A verem modul rja nem tudja, hogy a felhasznl mit akar tenni
ilyen esetben, a felhasznl pedig nem mindig szleli a hibt (ha gy lenne, a tlcsorduls
nem trtnne meg). A megolds: a Stack rja kell, hogy tudatban legyen a tlcsorduls
veszlynek s ezutn az (ismeretlen) felhasznlval tudatnia kell ezt. A felhasznl majd
megteszi a megfelel lpst:
namespace Stack { // fellet
void push(char);
char pop();
class Overflow { }; // tlcsordulst brzol tpus
}
Tlcsorduls szlelsekor a Stack::Push() meghvhat egy kivtelkezel kdot; vagyis egy
Overflow kivtelt dobhat:
void Stack::push(char c)
{
if (top == max_size) throw Overflow();
// c behelyezse
}
A throw a vezrlst a Stack::Overflow tpus kivtelkezelnek adja t, valamilyen fggvny-
ben, mely kzvetve vagy kzvetlenl meghvta a Stack::Push()-t. Ehhez visszatekerjk
a fggvnyhvsi vermet, ami ahhoz szksges, hogy visszajussunk a hv fggvny kr-
nyezethez. gy a throw gy mkdik, mint egy tbbszint return. Pldul:
void f()
{
// ...
try { // a kivtelekkel az albb meghatrozott kezel foglalkozik
Bevezets 38
while (true) Stack::push('c');
}
catch (Stack::Overflow) {
// hopp: verem-tlcsorduls; a megfelel mvelet vgrehajtsa
}
// ...
}
A while ciklus rkk ismtldne, ezrt ha valamelyik Stack::push() hvs egy throw-t vlt
ki, a vezrls a Stack::Overflow-t kezel catch rszhez kerl.
A kivtelkezel eljrsok hasznlata szablyosabb s olvashatbb teheti a hibakezel k-
dot. Tovbbi trgyals, rszletek s pldk: 8.3, 14. fejezet, E fggelk.
2.5. Elvont adatbrzols
A modularits alapvet szempont minden sikeres nagy programnl. E knyv minden terve-
zsi vizsglatban ez marad a kzppontban. Az elzekben lert alak modulok azonban
nem elegendek ahhoz, hogy tisztn kifejezznk sszetett rendszereket. Az albbiakban
a modulok hasznlatnak egyik mdjval foglalkozunk (felhasznli tpusok ltrehozsa),
majd megmutatjuk, hogyan kzdjnk le problmkat a felhasznli tpusok kzvetlen lt-
rehozsa rvn.
2.5.1. Tpusokat ler modulok
A modulokkal val programozs elvezet az sszes azonos tpus adatnak egyetlen tpuske-
zel modul ltali kzpontostott kezelshez. Ha pldul sok vermet akarunk az elbbi
Stack modulban tallhat egyetlen helyett megadhatunk egy veremkezelt, az albbi fe-
llettel:
namespace Stack {
struct Rep; // a verem szerkezetnek meghatrozsa mshol tallhat
typedef Rep& stack;
stack create(); // j verem ltrehozsa
void destroy(stack s); // s trlse
2. Kirnduls a C++-ban 39
void push(stack s, char c); // c behelyezse az s verembe
char pop(stack s); // s legfels elemnek kiemelse
}
A kvetkez deklarci azt mondja, hogy Rep egy tpus neve, de ksbbre hagyja a tpus
meghatrozst (5.7).
struct Rep;
Az albbi deklarci a stack nevet adja egy Rep referencinak (rszletek 5.5-ben).
typedef Rep& stack;
Az tlet az, hogy a vermet sajt Stack::stack-jvel azonostjuk s a tovbbi rszleteket a fel-
hasznl ell elrejtjk. A Stack::stack mkdse nagyon hasonlt egy beptett tpushoz:
struct Bad_pop { };
void f()
{
Stack::stack s1 = Stack::create(); // j verem ltrehozsa
Stack::stack s2 = Stack::create(); // mg egy verem ltrehozsa
Stack::push(s1,'c');
Stack::push(s2,'k');
if (Stack::pop(s1) != 'c') throw Bad_pop();
if (Stack::pop(s2) != 'k') throw Bad_pop();
Stack::destroy(s1);
Stack::destroy(s2);
}
Ezt a Stack-et tbbflekppen megvalsthatnnk. Fontos, hogy a felhasznlnak nem
szksges tudnia, hogyan tesszk ezt. Amg a felletet vltozatlanul hagyjuk, a felhasznl
nem fogja szrevenni, ha gy dntnk, hogy trjuk a Stack-et. Egy megvalsts pldul
elre lefoglalhatna nhny verempldnyt s a Stack::create() egy nem hasznlt pldnyra
val hivatkozst adna t. Ezutn a Stack::destroy() egy brzolst nem hasznlt-knt jell-
het meg, gy a Stack::create() jra hasznlhatja azt:
namespace Stack { // brzols
const int max_size = 200;
Bevezets 40
struct Rep {
char v[max_size];
int top;
};
const int max = 16; // vermek maximlis szma
Rep stacks[max]; // elre lefoglalt verempldnyok
bool used[max]; // used[i] igaz, ha stacks[i] hasznlatban van
typedef Rep& stack;
}
void Stack::push(stack s, char c) { /* s tlcsordulsnak ellenrzse s c behelyezse */ }
char Stack::pop(stack s) { /* s alulcsordulsnak ellenrzse s a legfels elem kiemelse */ }
Stack::stack Stack::create()
{
// hasznlaton kvli Rep kivlasztsa, hasznltknt
// megjellse, elksztse, r mutat hivatkozs visszaadsa
}
void Stack::destroy(stack s) { /* s megjellse nem hasznltknt */ }
Amit tettnk, az brzol tpus becsomagolsa felleti fggvnyek kszletbe. Az, hogy az
eredmnyl kapott stack tpus hogyan viselkedik, rszben attl fgg, hogyan adtuk meg
ezeket a felleti fggvnyeket, rszben attl, hogyan mutattuk be a Stack-et brzol tpust
a verem felhasznlinak, rszben pedig magtl az brzol tpustl.
Ez gyakran kevesebb az idelisnl. Jelents problma, hogy az ilyen mtpusoknak a fel-
hasznlk rszre val bemutatsa az brzol tpus rszleteitl fggen nagyon vltoz le-
het a felhasznlkat viszont el kell szigetelni az brzol tpus ismerettl. Ha pldul egy
jobban kidolgozott adatszerkezetet vlasztottunk volna a verem azonostsra,
a Stack::stack-ek rtkadsi s elksztsi (inicializlsi) szablyai drmai mdon megvl-
toztak volna (ami nha valban kvnatos lehet). Ez azonban azt mutatja, hogy a knyel-
mes vermek szolgltatsnak problmjt egyszeren ttettk a Stack modulbl
a Stack::stack brzol tpusba.
Mg lnyegesebb, hogy azok a felhasznli tpusok, melyeket az adott megvalst tpus-
hoz hozzfrst ad modul hatrozott meg, nem gy viselkednek, mint a beptett tpusok,
s kisebb vagy ms tmogatst lveznek, mint azok. Azt pldul, hogy mikor hasznlhat
egy Stack::Rep, a Stack::create() s a Stack::destroy() fggvny ellenrzi, nem a szoksos
nyelvi szablyok.
2. Kirnduls a C++-ban 41
2.5.2. Felhasznli tpusok
A C++ ezt a problmt gy kzdi le, hogy engedi, hogy a felhasznl kzvetlenl adjon
meg tpusokat, melyek kzel gy viselkednek, mint a beptett tpusok. Az ilyen tpusokat
gyakran elvont vagy absztrakt adattpusoknak (abstract data type, ADT) nevezzk. A szer-
z inkbb a felhasznli tpus (user-defined type) megnevezst kedveli. Az elvont adatt-
pus kifejezbb meghatrozshoz absztrakt matematikai lers kellene. Ha adva volna
ilyen, azok, amiket itt tpusoknak neveznk, az ilyen valban elvont egyedek konkrt pl-
dnyai lennnek. A programozsi megkzelts most ez lesz:
Dntsd el, mely tpusokra van szksg
s mindegyikhez biztosts teljes mveletkszletet.
Ott, ahol egy tpusbl egy pldnynl tbbre nincs szksg, elegend a modulokat haszn-
l adatrejtsi stlus. Az olyan aritmetikai tpusok, mint a racionlis s komplex szmok, k-
znsges pldi a felhasznli tpusnak. Vegyk az albbi kdot:
class complex {
double re, im;
public:
complex(double r, double i) { re=r; im=i; } // complex ltrehozsa kt skalrbl
complex(double r) { re=r; im=0; } // complex ltrehozsa egy skalrbl
complex() { re = im = 0; } // alaprtelmezett complex: (0,0)
friend complex operator+(complex, complex);
friend complex operator-(complex, complex); // ktoperandus
friend complex operator-(complex); // egyoperandus
friend complex operator*(complex, complex);
friend complex operator/(complex, complex);
friend bool operator==(complex, complex); // egyenl
friend bool operator!=(complex, complex); // nem egyenl
// ...
};
A complex osztly (vagyis felhasznli tpus) deklarcija egy komplex szmot s a rajta
vgrehajthat mveletek halmazt brzolja. Az brzols privt (private); vagyis a re s az
im csak a complex osztly bevezetsekor megadott fggvnyek ltal hozzfrhet. Ezeket
az albbi mdon adhatjuk meg:
Bevezets 42
complex operator+(complex a1, complex a2)
{
return complex(a1.re+a2.re,a1.im+a2.im);
}
Az a tagfggvny, melynek neve megegyezik az osztlyval, a konstruktor. A konstruktor
rja le az osztly egy objektumnak elksztsi-ltrehozsi mdjt. A complex osztly
hrom konstruktort tartalmaz. Egyikk egy double-bl csinl complex-et, egy msik egy
double prbl, a harmadik alaprtelmezett rtk alapjn. A complex osztly gy hasz-
nlhat:
void f(complex z)
{
complex a = 2.3;
complex b = 1/a;
complex c = a+b*complex(1,2.3);
// ...
if (c != b) c = -(b/a)+2*b;
}
A fordt a komplex szmokhoz kapcsolt mveleti jeleket megfelel fggvnyhvsokk
alaktja. A c!=b jelentse pldul operator!=(c,b), az 1/a jelentse operator/ (complex(1),a).
A legtbb de nem minden modul jobban kifejezhet felhasznli tpusknt.
2.5.3. Konkrt tpusok
Felhasznli tpusok vltozatos ignyek kielgtsre kszthetk. Vegynk egy felhaszn-
li veremtpust a complex tpus soraival egytt. Ahhoz, hogy kiss valsghbb tegyk
a pldt, ez a Stack tpus paramterknt elemei szmt kapja meg:
class Stack {
char* v;
int top;
int max_size;
public:
class Underflow { }; // kivtel
class Overflow { }; // kivtel
class Bad_size { }; // kivtel
Stack(int s); // konstruktor
~Stack(); // destruktor
2. Kirnduls a C++-ban 43
void push(char c);
char pop();
};
A Stack(int) konstruktor meghvdik, valahnyszor ltrehozzuk az osztly egy pldnyt.
Ez a fggvny gondoskodik a kezdeti rtkadsrl. Ha brmilyen takartsra van szksg,
amikor az osztly egy objektuma kikerl a hatkrbl, megadhatjuk a konstruktor ellent-
tt, a destruktort:
Stack::Stack(int s) // konstruktor
{
top = 0;
if (s<0 || 10000<s) throw Bad_size(); // "||" jelentse "vagy"
max_size = s;
v = new char[s]; // az elemek szabad trba helyezse
}
Stack::~Stack() // destruktor
{
delete[ ] v; // elemek trlse, hely felszabadtsa jrafelhasznls cljra (6.2.6)
}
A konstruktor egy j Stack vltozt hoz ltre. Ehhez lefoglal nmi helyet a szabad trbl
(heap halom, kupac vagy dinamikus tr) a new opertor hasznlatval. A destruktor ta-
kart, felszabadtva a trat. Az egsz a Stack-ek felhasznlinak beavatkozsa nlkl trt-
nik. A felhasznlk a vermeket ugyangy hozzk ltre s hasznljk, ahogy a beptett t-
pus vltozkat szoktk. Pldul:
Stack s_var1(10); // 10 elemet trolni kpes globlis verem
void f(Stack& s_ref, int i) // hivatkozs a Stack veremre
{
Stack s_var2(i); // loklis verem i szm elemmel
Stack* s_ptr = new Stack(20); // mutat a szabad trban lev Stack-re
s_var1.push('a');
s_var2.push('b');
s_ref.push('c');
s_ptr->push('d');
// ...
}
Ez a Stack tpus ugyanolyan nvadsra, hatkrre, lettartamra, msolsra stb. vonatkoz
szablyoknak engedelmeskedik, mint az int vagy a char beptett tpusok.
Bevezets 44
Termszetszeren a push() s pop() tagfggvnyeket valahol szintn meg kell adni:
void Stack::push(char c)
{
if (top == max_size) throw Overflow();
v[top] = c;
top = top + 1;
}
char Stack::pop()
{
if (top == 0) throw Underflow();
top = top - 1;
return v[top];
}
A complex s Stack tpusokat konkrt tpusnak nevezzk, ellenttben az absztrakt tpusok-
kal, ahol a fellet tkletesebben elszigeteli a felhasznlt a megvalsts rszleteitl.
2.5.4. Absztrakt tpusok
Amikor a Stackrl, mint egy modul (2.5.1) ltal megvalstott mtpusrl ttrtnk egy sa-
jt tpusra (2.5.3), egy tulajdonsgot elvesztettnk. Az brzols nem vlik el a felhaszn-
li fellettl, hanem rsze annak, amit be kellene pteni (#include) a vermeket hasznl
programrszbe. Az brzols privt, ezrt csak a tagfggvnyeken keresztl hozzfrhet,
de jelen van. Ha brmilyen jelents vltozst szenved, a felhasznl jra le kell, hogy for-
dtsa. Ezt az rat kell fizetni, hogy a konkrt tpusok pontosan ugyangy viselkedjenek,
mint a beptettek. Nevezetesen egy tpusbl nem lehetnek valdi loklis (helyi) vltoz-
ink, ha nem tudjuk a tpus brzolsnak mrett.
Azon tpusoknl, melyek nem vltoznak gyakran, s ahol loklis vltozk gondoskodnak
a szksges tisztasgrl s hatkonysgrl, ez elfogadhat s gyakran idelis. Ha azonban
teljesen el akarjuk szigetelni az adott verem felhasznljt a megvalsts vltozsaitl,
a legutols Stack nem elegend. Ekkor a megolds levlasztani a felletet az brzolsrl
s lemondani a valdi loklis vltozkrl.
Elszr hatrozzuk meg a felletet:
class Stack {
public:
class Underflow { }; // kivtel
class Overflow { }; // kivtel
2. Kirnduls a C++-ban 45
virtual void push(char c) = 0;
virtual char pop() = 0;
};
A virtual sz a Simulban s a C++-ban azt jelenti, hogy az adott osztlybl szrmaztatott
osztlyban ksbb fellrhat. Egy Stack-bl szrmaztatott osztly a Stack felletet valst-
ja meg. A furcsa =0 kifejezs azt mondja, hogy a verembl szrmaztatott osztlynak meg
kell hatroznia a fggvnyt. Ilyenformn a Stack felletknt szolgl brmilyen osztly rsz-
re, mely tartalmazza a push() s pop() fggvnyeket.Ezt a Stack-et gy hasznlhatnnk:
void f(Stack& s_ref)
{
s_ref.push('c');
if (s_ref.pop() != 'c') throw Bad_pop();
}
Vegyk szre, hogyan hasznlja f() a Stack felletet, a megvalsts mikntjrl mit sem
tudva. Az olyan osztlyt, mely ms osztlyoknak felletet ad, gyakran tbbalak (polimorf)
tpusnak nevezzk.
Nem meglep, hogy a megvalsts a konkrt Stack osztlybl mindent tartalmazhat, amit
kihagytunk a Stack felletbl:
class Array_stack : public Stack { // Array_stack megvalstja Stack-et
char* p;
int max_size;
int top;
public:
Array_stack(int s);
~Array_stack();
void push(char c);
char pop();
};
A :public olvashat gy, mint szrmaztatva -bl, megvalstja -t, vagy altpusa
-nak.
Az f() fggvny rszre, mely a megvalsts ismeretnek teljes hinyban egy Stacket akar
hasznlni, valamilyen msik fggvny kell ltrehozzon egy objektumot, amelyen az f() m-
veletet hajthat vgre:
void g()
{
Bevezets 46
Array_stack as(200);
f(as);
}
Mivel f() nem tud az Array_stack-ekrl, csak a Stack felletet ismeri, ugyanolyan jl fog m-
kdni a Stack egy msik megvalstsval is:
class List_stack : public Stack { // List_stack megvalstja Stack-et
list<char> lc; // (standard knyvtrbeli) karakterlista (3.7.3)
public:
List_stack() { }
void push(char c) { lc.push_front(c); }
char pop();
};
char List_stack::pop()
{
char x = lc.front(); // az els elem lekrse
lc.pop_front(); // az els elem eltvoltsa
return x;
}
Itt az brzols egy karakterlista. Az lc.push_front(c) beteszi c-t, mint lc els elemt, az
lc.pop_front hvs eltvoltja az els elemet, az lc.front() pedig lc els elemre utal.
Egy fggvny ltre tud hozni egy List_stack-et s f() hasznlhatja azt:
void h()
{
List_stack ls;
f(ls);
}
2.5.5. Virtulis fggvnyek
Hogyan trtnik az f()-en belli s_ref.pop() hvs feloldsa a megfelel fggvnydefinci
hvsra? Amikor h()-bl hvjuk f()-et, a List_stack::pop()-ot kell meghvni, amikor g()-bl,
az Array_stack::pop()-ot. Ahhoz, hogy ezt feloldhassuk, a Stack objektumnak informcit
kell tartalmaznia arrl, hogy futsi idben mely fggvnyt kell meghvni. A fordtknl szo-
ksos eljrs egy virtulis fggvny nevnek egy tblzat valamely sorszmrtkv alak-
tsa, amely tblzat fggvnyekre hivatkoz mutatkat tartalmaz. A tblzatot virtulis
2. Kirnduls a C++-ban 47
fggvnytblnak vagy egyszeren vtbl-nek szoks nevezni. Minden virtulis fggvnye-
ket tartalmaz osztlynak sajt vtbl-je van, mely azonostja az osztly virtulis fggvnyeit.
Ez grafikusan gy brzolhat:
A vtbl-ben lv fggvnyek lehetv teszik, hogy az objektumot akkor is helyesen hasznl-
juk, ha a hv nem ismeri annak mrett s adatainak elrendezst. A hvnak mindssze
a vtbl helyt kell tudnia a Stack-en bell, illetve a virtulis fggvnyek sorszmt. Ez a vir-
tulis hvsi eljrs lnyegben ugyanolyan hatkonny tehet, mint a normlis fggvny-
hvs. Tbblet helyszksglete: a virtulis fggvnyeket tartalmaz osztly minden objek-
tumban egy-egy mutat, valamint egy-egy vtbl minden osztlyhoz.
2.6. Objektumorientlt programozs
Az elvont adatbrzols a j tervezshez alapfontossg, a knyvben pedig a tervezs vgig
kzponti krds marad. A felhasznli tpusok azonban nmagukban nem elg rugalmasak
ahhoz, hogy kiszolgljk ignyeinket. E rszben elszr egyszer felhasznli tpusokkal
mutatunk be egy problmt, majd megmutatjuk, hogyan lehet azt megoldani osztlyhierar-
chik hasznlatval.
2.6.1. Problmk a konkrt tpusokkal
A konkrt tpusok a modulokban megadott mtpusokhoz hasonlan egyfajta fekete
dobozt rnak le. Ha egy fekete dobozt ltrehozunk, az nem lp igazi klcsnhatsba
a program tbbi rszvel. Nincs md arra, hogy j felhasznlshoz igaztsuk, kivve, ha de-
Bevezets 48
p
max_size
top
lc
Array_stack::push()
Array_stack::pop()
List_stack::push()
List_stack::pop()
Array_stack objektum:
List_stack objektum: vtbl:
vtbl:
fincijt mdostjuk. Ez a helyzet idelis is lehet, de komoly rugalmatlansghoz is vezethet.
Vegyk pldul egy grafikus rendszerben hasznlni kvnt Shape (Alakzat) tpus meghat-
rozst. Tegyk fel, hogy pillanatnyilag a rendszernek krket, hromszgeket s ngyze-
teket kell tmogatnia. Tegyk fel azt is, hogy lteznek az albbiak:
class Point { /* ... */ };
class Color { /* ... */ };
A /* s */ egy megjegyzs kezdett, illetve vgt jelli. A jells tbbsoros megjegyzsek-
hez is hasznlhat.
Egy alakzatot az albbi mdon adhatunk meg:
enum Kind { circle, triangle, square }; // felsorols (4.8)
class Shape {
Kind k; // tpusmez
Point center;
Color col;
// ...
public:
void draw();
void rotate(int);
// ...
};
A k tpusazonost mez azrt szksges, hogy az olyan mveletek szmra, mint a draw()
(rajzols) vagy a rotate() (forgats) meghatrozhatv tegyk, milyen fajta alakzattal van
dolguk. (A Pascal-szer nyelvekben egy vltoz rekordtpust hasznlhatnnk, k cmkvel).
A draw() fggvnyt gy adhatnnk meg:
void Shape::draw()
{
switch (k) {
case circle:
// kr rajzolsa
break;
case triangle:
// hromszg rajzolsa
break;
2. Kirnduls a C++-ban 49
case square:
// ngyzet rajzolsa
break;
}
}
Ez azonban rendetlensg. A fggvnyeknek mint a draw() tudniuk kell arrl, milyen
alakzatfajtk lteznek. Ezrt az ilyen fggvnynl mindig nvekszik a kd, valahnyszor
a rendszerhez egy j alakzatot adunk. Ha j alakzatot hozunk ltre, minden mveletet meg
kell vizsglni s (lehetsg szerint) mdostani kell azokat. A rendszerhez nem adhatunk j
alakzatot, hacsak hozz nem frnk minden mvelet forrskdjhoz. Mivel egy j alakzat
hozzadsa magval vonja minden fontos alakzat-mvelet kdjnak mdostst, az ilyen
munka nagy gyessget kvn s hibkat vihet be a ms (rgebbi) alakzatokat kezel kd-
ba. Az egyes alakzat-brzolsok kivlasztst komolyan megbnthatja az a kvetelmny,
hogy az brzolsoknak (legalbb is nhnynak) illeszkednie kell abba a jellemzen rg-
ztett mret keretbe, melyet az ltalnos Shape tpus lersa kpvisel.
2.6.2. Osztlyhierarchik
A problma az, hogy nincs megklnbztets az egyes alakzatok ltalnos tulajdonsgai
(szn, rajzolhatsg stb.) s egy adott alakzatfajta tulajdonsgai kzt. (A kr pldul olyan
alakzat, melynek sugara van, egy krrajzol fggvnnyel lehet megrajzolni stb.). E megk-
lnbztets kifejezse s elnyeinek kihasznlsa az objektumorientlt programozs lnye-
ge. Azok a nyelvek, melyek e megklnbztets kifejezst s hasznlatt lehetv tv
szerkezetekkel rendelkeznek, tmogatjk az objektumkzpontsgot, ms nyelvek nem.
A megoldsrl a Simulbl klcsnztt rkls gondoskodik. Elszr ltrehozunk egy osz-
tlyt, mely minden alakzat ltalnos tulajdonsgait lerja:
class Shape {
Point center;
Color col;
// ...
public:
Point where() { return center; }
void move(Point to) { center = to; /* ... */ draw(); }
virtual void draw() = 0;
virtual void rotate(int angle) = 0;
// ...
};
Bevezets 50
Akrcsak a 2.5.4 absztrakt Stack tpusban, azokat a fggvnyeket, melyeknl a hvsi
fellet meghatrozhat, de a konkrt megvalsts mg nem ismert, virtulisknt
(virtual) vezetjk be.
A fenti meghatrozs alapjn mr rhatunk ltalnos fggvnyeket, melyek alakzatokra hi-
vatkoz mutatkbl ll vektorokat kezelnek:
void rotate_all(vector<Shape*>& v, int angle) // v elemeinek elforgatsa angle szggel
{
for (int i = 0; i<v.size(); ++i) v[i]->rotate(angle);
}
Egy konkrt alakzat meghatrozshoz meg kell mondanunk, hogy alakzatrl van sz s
meg kell hatroznunk konkrt tulajdonsgait (belertve a virtulis fggvnyeket is):
class Circle : public Shape {
int radius;
public:
void draw() { /* ... */ }
void rotate(int) {} // igen, res fggvny
};
A C++-ban a Circle osztlyrl azt mondjuk, hogy a Shape osztlybl szrmazik (derived),
a Shape osztlyrl pedig azt, hogy a Circle osztly se ill. bzisosztlya (alaposztlya, base).
Ms szhasznlat szerint a Circle s a Shape alosztly (subclass), illetve fosztly (superclass).
A szrmaztatott osztlyrl azt mondjuk, hogy rkli (inherit) a bzisosztly tagjait, ezrt
a bzis- s szrmaztatott osztlyok hasznlatt ltalban rklsknt emltjk.A programozsi
megkzelts itt a kvetkez:
Dntsd el, mely osztlyokra van szksged,
biztosts mindegyikhez teljes mveletkszletet,
az rkls segtsgvel pedig hatrold krl pontosan
a kzs tulajdonsgokat.
Ahol nincs ilyen kzs tulajdonsg, elegend az elvont adatbrzols. A tpusok kzti,
rkls s virtulis fggvnyek hasznlatval kiaknzhat kzssg mrtke mutatja,
mennyire alkalmazhat egy problmra az objektumorientlt megkzelts. Nmely terle-
ten, pldul az interaktv grafikban, vilgos, hogy az objektumkzpontsgnak risi le-
2. Kirnduls a C++-ban 51
hetsgei vannak. Ms terleteken, mint a klasszikus aritmetikai tpusoknl s az azokon
alapul szmtsoknl, alig ltszik tbb lehetsg, mint az elvont adatbrzols, gy az ob-
jektumkzpontsg tmogatshoz szksges szolgltatsok feleslegesnek tnnek.
Az egyes tpusok kzs tulajdonsgait megtallni nem egyszer. A kihasznlhat kzssg
mrtkt befolysolja a rendszer tervezsi mdja. Amikor egy rendszert terveznk s ak-
kor is, amikor a rendszerkvetelmnyeket lerjuk aktvan kell keresnnk a kzs tulajdon-
sgokat. Osztlyokat lehet kifejezetten ms tpusok ptkockiknt tervezni, a ltez osz-
tlyokat pedig meg lehet vizsglni, mutatnak-e olyan hasonlsgokat, amelyeket egy kzs
bzisosztlyban kihasznlhatnnk. Az objektumorientlt programozs konkrt programo-
zsi nyelvi szerkezetek nlkl val elemzsre irnyul ksrleteket lsd [Kerr,1987] s
[Booch, 1994] a 23.6-ban.
Az osztlyhierarchik s az absztrakt osztlyok (2.5.4) nem klcsnsen kizrjk, hanem
kiegsztik egymst (12.5), gy az itt felsorolt irnyelvek is inkbb egymst kiegszt, kl-
csnsen tmogat jellegek. Az osztlyok s modulok pldul fggvnyeket tartalmaz-
nak, mg a modulok osztlyokat s fggvnyeket. A tapasztalt tervez sokfle megkzel-
tst hasznl ahogy a szksg parancsolja.
2.7. ltalnostott programozs
Ha valakinek egy verem kell, nem felttlenl karaktereket tartalmaz veremre van szks-
ge. A verem ltalnos fogalom, fggetlen a karakter fogalmtl. Kvetkezskppen fgget-
lenl kell brzolni is.
Mg ltalnosabban, ha egy algoritmus az brzolstl fggetlenl s logikai torzuls nl-
kl kifejezhet, akkor gy is kell tenni. A programozsi irnyelv a kvetkez:
Dntsd el, mely algoritmusokra van szksg,
s gy lsd el azokat paramterekkel, hogy minl tbb tpussal
s adatszerkezettel mkdjenek.
2.7.1. Trolk
Egy karakterverem-tpust ltalnosthatunk, ha sablont (template) hozunk ltre belle s
a konkrt char tpus helyett sablonparamtert hasznlunk. Pldul:
Bevezets 52
template<class T> class Stack {
T* v;
int max_size;
int top;
public:
class Underflow { };
class Overflow { };
Stack(int s); // konstruktor
~Stack(); // destruktor
void push(T);
T pop();
};
A template<class T> eltag T-t az utna kvetkez deklarci paramterv teszi.
Hasonlkppen adhatjuk meg a tagfggvnyeket is:
template<class T> void Stack<T>::push(T c)
{
if (top == max_size) throw Overflow();
v[top] = c;
top = top + 1;
}
template<class T> T Stack<T>::pop()
{
if (top == 0) throw Underflow();
top = top - 1;
return v[top];
}
Ha a defincik adottak, a vermet az albbi mdon hasznlhatjuk:
Stack<char> sc(200); // verem 200 karakter szmra
Stack<complex> scplx(30); // verem 30 komplex szm rszre
Stack< list<int> > sli(45); // verem 45, egszekbl ll lista szmra
void f()
{
sc.push('c');
if (sc.pop() != 'c') throw Bad_pop();
scplx.push(complex(1,2));
if (scplx.pop() != complex(1,2)) throw Bad_pop();
}
2. Kirnduls a C++-ban 53
Hasonl mdon, sablonokknt adhatunk meg listkat, vektorokat, asszociatv tmbket
(map) s gy tovbb. Az olyan osztlyt, amely valamilyen tpus elemek gyjtemnyt tar-
talmazza, ltalban container class-nak vagy egyszeren trolnak (kontnernek) hvjuk.
A sablonoknak fordtsi idben van jelentsgk, teht hasznlatuk a kzzel rott kdhoz
kpest nem nveli a futsi idt.
2.7.2. ltalnostott algoritmusok
A C++ standard knyvtra tbbfle trolrl gondoskodik, de a felhasznlk sajtokat is
rhatnak (3., 17. s 18. fejezetek). Ismt hasznlhatjuk teht az ltalnostott (generikus)
programozs irnyelveit algoritmusok trolk ltali paramterezsre. Tegyk fel, hogy
vektorokat, listkat s tmbket akarunk rendezni, msolni s tkutatni, anlkl, hogy min-
den egyes trolra megrnnk a sort(), copy() s search() fggvnyeket. Konvertlni nem
akarunk egyetlen adott adatszerkezetre sem, melyet egy konkrt sort fggvny elfogad,
ezrt tallnunk kell egy ltalnos mdot a trolk lersra, mgpedig olyat, amely megen-
gedi, hogy egy trolt anlkl hasznljunk, hogy pontosan tudnnk, milyen fajta trolrl
van sz.
Az egyik megolds, amelyet a C++ standard knyvtrban a trolk s nem numerikus al-
goritmusok megkzeltsbl (18. fej. 3.8) vettnk t, a sorozatokra sszpontost s azo-
kat bejrkkal ( iterator) kezeli.
me a sorozat fogalmnak grafikus brzolsa:
A sorozatnak van egy kezdete s egy vge. A bejr (iterator) valamely elemre hivatkozik
s gondoskodik arrl a mveletrl, melynek hatsra legkzelebb a sorozat soron kvetke-
z elemre fog hivatkozni. A sorozat vge ugyancsak egy bejr, mely a sorozat utols ele-
mn tlra hivatkozik. A vge fizikai brzolsa lehet egy r (sentinel) elem, de elkp-
zelhet ms is. A lnyeg, hogy a sorozat szmos mdon brzolhat, gy listkkal s tm-
bkkel is.
Bevezets 54
Kezdet
elemek:
Vg
...
Az olyan mveletekhez, mint egy bejr ltal frjnk hozz egy elemhez s a bejr hi-
vatkozzon a kvetkez elemre szksgnk van valamilyen szabvnyos jellsre. Ha az
alaptletet megrtettk, az els helyn kzenfekv vlaszts a * dereferencia (hivatkoz
vagy mutat) opertort hasznlni, a msodiknl pedig a ++ nvel mveleti jelet.
A fentieket adottnak tekintve, az albbi mdon rhatunk kdot:
template<class In, class Out> void copy(In from, In too_far, Out to)
{
while (from != too_far) {
*to = *from; // hivatkozott elemek msolsa
++to; // kvetkez cl
++from; // kvetkez forrs
}
}
Ez tmsol brmilyen trolt, amelyre a formai kvetelmnyek betartsval bejrt adhatunk
meg. A C++ beptett, alacsonyszint tmb s mutat tpusai rendelkeznek a megfelel m-
veletekkel:
char vc1[200]; // 200 karakter tmbje
char vc2[500]; // 500 karakter tmbje
void f()
{
copy(&vc1[0],&vc1[200],&vc2[0]);
}
Ez vc1-et els elemtl az utolsig vc2-be msolja, vc2 els elemtl kezdden.
Minden standard knyvtrbeli trol (17. Fej., 16.3) tmogatja ezt a bejr- (iterator) s so-
rozatjellst. A forrs s a cl tpusait egyetlen paramter helyett kt sablonparamter, az In
s Out jelli. Ezt azrt tesszk, mert gyakran akarunk msolni egy fajta trolbl egy msik
fajtba. Pldul:
complex ac[200];
void g(vector<complex>& vc, list<complex>& lc)
{
copy(&ac[0],&ac[200],lc.begin());
copy(lc.begin(),lc.end(),vc.begin());
}
2. Kirnduls a C++-ban 55
Itt a tmbt a list-be msoljuk, a list-et pedig a vector-ba. Egy szabvnyos trolnl a begin()
a bejr (iterator), amely az els elemre mutat.
2.8. Utirat
Egyetlen programozsi nyelv sem tkletes. Szerencsre egy programozsi nyelvnek nem
kell tkletesnek lennie ahhoz, hogy j eszkzknt szolgljon nagyszer rendszerek pt-
shez. Valjban egy ltalnos cl programozsi nyelv nem is lehet minden feladatra t-
kletes, amire csak hasznljk. Ami egy feladatra tkletes, gyakran komoly fogyatkoss-
gokat mutathat egy msiknl, mivel az egy terleten val tkletessg magval vonja
a szakosodst. A C++-t ezrt gy terveztk, hogy j pteszkz legyen a rendszerek szles
vlasztkhoz s a fogalmak szles krt kzvetlenl kifejezhessk vele.
Nem mindent lehet kzvetlenl kifejezni egy nyelv beptett tulajdonsgait felhasznlva.
Valjban ez nem is lenne idelis. A nyelvi tulajdonsgok egy sereg programozsi stlus s
mdszer tmogatsra valk. Kvetkezskppen egy nyelv megtanulsnl a feladat
a nyelv sajtos s termszetes stlusainak elsajttsra val sszpontosts, nem az sszes
nyelvi tulajdonsg minden rszletre kiterjed megrtse.
A gyakorlati programozsnl kevs az elnye, ha ismerjk a legrejtettebb nyelvi tulajdon-
sgokat vagy ha a legtbb tulajdonsgot kihasznljuk. Egyetlen nyelvi tulajdonsg nmag-
ban nem tl rdekes. Csak akkor lesz jelentkeny, ha az egyes programozsi mdszerek s
ms tulajdonsgok krnyezetben tekintjk. Amikor teht az Olvas a kvetkez fejezete-
ket olvassa, krjk, emlkezzen arra, hogy a C++ rszletekbe men vizsglatnak igazi cl-
ja az, hogy kpesek legynk egyttesen hasznlni a nyelv szolgltatsait, j programozsi
stlusban, egszsges tervezsi krnyezetben.
2.9. Tancsok
[1] Ne essnk ktsgbe! Idvel minden kitisztul. 2.1.
[2] J programok rshoz nem kell ismernnk a C++ minden rszlett. 1.7.
[3] A programozsi mdszerekre sszpontostsunk, ne a nyelvi tulajdonsgokra. 2.1.
Bevezets 56
Kirnduls a standard knyvtrban
Minek vesztegessk az idt tanulsra,
mikor a tudatlansg azonnali?
(Hobbes)
Szabvnyos knyvtrak Kimenet Karakterlncok Bemenet Vektorok Tartomny-
ellenrzs Listk Asszociatv tmbk Trolk (ttekints) Algoritmusok Bejrk
Bemeneti/kimeneti bejrk Bejrsok s prediktumok Tagfggvnyeket hasznl
algoritmusok Algoritmusok (ttekints) Komplex szmok Vektoraritmetika A stan-
dard knyvtr (ttekints) Tancsok
3.1. Bevezets
Nincs olyan jelents program, mely csak a puszta programnyelven rdik. Elszr a nyelvet
tmogat knyvtrakat fejlesztik ki, ezek kpezik a tovbbi munka alapjt.
A 2. fejezet folytatsaknt ez a fejezet gyors krutazst tesz a f knyvtri szolgltatsok-
ban, hogy fogalmat adjon, mit lehet a C++ s standard knyvtrnak segtsgvel megten-
ni. Bemutat olyan hasznos knyvtri tpusokat, mint a string, vector, list s map, valamint
hasznlatuk legltalnosabb mdjait. Ez lehetv teszi, hogy a kvetkez fejezetekben jobb
3
pldkat s gyakorlatokat adjak az olvasnak. A 2. fejezethez hasonlan btortani akarom
az olvast, ne zavarja, ne kedvetlentse el, ha a rszleteket nem rti tkletesen. E fejezet
clja, hogy megzleljk, mi kvetkezik, s megrtsk a leghasznosabb knyvtri szolglta-
tsok legegyszerbb hasznlatt. A standard knyvtrat rszletesebben a 16.1.2 mutatja be.
Az e knyvben lert standard knyvtrbeli szolgltatsok minden teljes C++-vltozat rszt
kpezik. A C++ standard knyvtrn kvl a legtbb megvalsts a felhasznl s a prog-
ram kzti prbeszdre grafikus felhasznli felleteket is knl, melyeket gyakran GUI-k-
nak vagy ablakoz rendszernek neveznek. Hasonlkppen, a legtbb programfejleszt
krnyezetet alapknyvtrakkal (foundation library) is ellttk, amelyek a szabvnyos fej-
lesztsi s/vagy futtatsi krnyezeteket tmogatjk. Ilyeneket nem fogunk lerni. A sznd-
kunk a C++ nll lerst adni, gy, ahogy a szabvnyban szerepel, s megrizni a pldk
hordozhatsgt (ms rendszerekre val tltetsnek lehetsgt), kivve a kln meg-
jellteket. Termszetesen biztatjuk az olvast, fedezze fel a legtbb rendszerben meglv,
kiterjedt lehetsgeket ezt azonban a gyakorlatokra hagytuk.
3.2. Hell, vilg!
A legkisebb C++ program:
int main() { }
A program megadja a main nev fggvnyt, melynek nincsenek paramterei s nem tesz
semmit. Minden C++ programban kell, hogy legyen egy main() nev fggvny. A program
e fggvny vgrehajtsval indul. A main() ltal visszaadott int rtk, ha van ilyen, a prog-
ram visszatrsi rtke a rendszerhez. Ha nincs visszatrsi rtk, a rendszer a sikeres be-
fejezst jelz rtket kap vissza. Ha a main() nem nulla rtket ad vissza, az hibt jelent.
A programok jellemzen valamilyen kimenetet lltanak el. me egy program, amely ki-
rja: Hell, vilg!:
#include <iostream>
int main()
{
std::cout << "Hell, vilg!\n";
}
Bevezets 58
Az #include <iostream> utastja a fordtt, hogy illessze be az iostream-ben tallhat adat-
folyam-bemeneti s -kimeneti szolgltatsok deklarcijt a forrskdba. E deklarcik nl-
kl az albbi kifejezs
std::cout << "Hell, vilg!\n"
rtelmetlen volna. A << (tedd bele) kimeneti opertor msodik operandust berja az el-
sbe. Ebben az esetben a Hell, vilg!\n karakterliterl a szabvnyos kimeneti adatfo-
lyamba, az std::cout-ba rdik. A karakterliterl egy " " jelek kz zrt karaktersorozat.
A benne szerepl \ (backslash, fordtott perjel) az utna kvetkez karakterrel valamilyen
egyedi karaktert jell. Esetnkben az \n az j sor jele, teht a kirt Hell, vilg! szveget
sortrs kveti.
3.3. A standard knyvtr nvtere
A standard knyvtr az std nvtrhez (2.4, 8.2) tartozik. Ezrt rtunk std::cout-ot cout he-
lyett. Ez egyrtelmen a standard cout hasznlatt rja el, nem valamilyen ms cout-t.
A standard knyvtr minden szolgltatsnak beptsrl valamilyen, az <iostream>-hez
hasonl szabvnyos fejllomny ltal gondoskodhatunk:
#include<string>
#include<list>
Ez rendelkezsre bocstja a szabvnyos string-et s list-et. Hasznlatukhoz az std:: eltagot
alkalmazhatjuk:
std::string s = "Ngy lb j, kt lb rossz!";
std::list<std::string> slogans;
Az egyszersg kedvrt a pldkban ritkn rjuk ki az std:: eltagot, illetve a szksges
#include <fejllomny>-okat. Az itt kzlt programrszletek fordtshoz s futtatshoz
a megfelel fejllomnyokat be kell pteni (#include, amint a 3.7.5, 8.6 s a 16. fejezet-
ben szerepl felsorolsokban szerepelnek). Ezenkvl vagy az std:: eltagot kell hasznlni,
vagy globliss kell tenni minden nevet az std nvtrbl (8.2.3):
3. Kirnduls a standard knyvtrban 59
#include<string> // a szabvnyos karakterlnc-szolgltatsok elrhetv ttele
using namespace std; // std nevek elrhetv ttele az std:: eltag nlkl
string s = "A tudatlansg erny!"; // rendben: a string jelentse std::string
ltalban szegnyes zlsre vall egy nvtrbl minden nevet a globlis nvtrbe helyezni.
Mindazonltal, a nyelvi s knyvtri tulajdonsgokat illusztrl programrszletek rvidre
fogsa rdekben elhagytuk az ismtld #include-okat s std:: minstseket. E knyvben
majdnem kizrlag a standard knyvtrat hasznljuk, ha teht egy nevet hasznlunk onnan,
azt vagy a szabvny ajnlja, vagy egy magyarzat rsze (hogyan hatrozhat meg az adott
szabvnyos szolgltats).
3.4. Kimenet
Az iostream knyvtr minden beptett tpusra meghatroz kimenetet, de felhasznli t-
pushoz is knnyen megadhatjuk. Alaprtelmezsben a cout-ra kerl kimeneti rtkek
karaktersorozatra alaktdnak t. A kvetkez kd pldul az 1 karaktert a 0 karakterrel
kvetve a szabvnyos kimeneti adatfolyamba helyezi.
void f()
{
cout << 10;
}
Ugyanezt teszi az albbi kd is:
void g()
{
int i = 10;
cout << i;
}
A klnbz tpus kimenetek termszetesen prosthatk:
void h(int i)
{
cout << "i rtke ";
cout << i;
cout << '\n';
}
Bevezets 60
Ha i rtke 10, a kimenet a kvetkez lesz:
i rtke 10
A karakterkonstans egy karakter, egyszeres idzjelek kz zrva. Vegyk szre, hogy
a karakterkonstansok nem szmrtkknt, hanem karakterknt rdnak ki:
void k()
{
cout << 'a';
cout << 'b';
cout << 'c';
}
A fenti kd kimenete pldul abc lesz.Az ember hamar belefrad a kimeneti adatfolyam ne-
vnek ismtlsbe, amikor tbb rokon ttelt kell kirni. Szerencsre maguk a kimeneti kife-
jezsek eredmnyei felhasznlhatk tovbbi kimenetekhez:
void h2(int i)
{
cout << "i rtke " << i << '\n';
}
Ez egyenrtk h()-val. Az adatfolyamok rszletes magyarzata a 21. fejezetben tallhat.
3.5. Karakterlncok
A standard knyvtr gondoskodik a string (karakterlnc) tpusrl, hogy kiegsztse a korb-
ban hasznlt karakterliterlokat. A string tpus egy sereg hasznos karakterlnc-mveletet
biztost, ilyen pldul az sszefzs :
string s1 = "Hell";
string s2 = "vilg";
void m1()
{
string s3 = s1 + ", " + s2 + "!\n";
cout << s3;
}
3. Kirnduls a standard knyvtrban 61
Az s3 kezdeti rtke itt a kvetkez karaktersorozat (j sorral kvetve):
Hell, vilg!
A karakterlncok sszeadsa sszefzst jelent. A karakterlncokhoz karakterliterlokat s
karaktereket adhatunk. Sok alkalmazsban az sszefzs legltalnosabb formja valamit
egy karakterlnc vghez fzni. Ezt a += mvelet kzvetlenl tmogatja:
void m2(string& s1, string& s2)
{
s1 = s1 + '\n'; // sortrs
s2 += '\n'; // sortrs
}
A lnc vghez val hozzads kt mdja egyenrtk, de az utbbit elnyben rszestjk,
mert tmrebb s valsznleg hatkonyabban valsthat meg.Termszetesen a karakter-
lncok sszehasonlthatk egymssal s literlokkal is:
string incantation; //varzssz
void respond(const string& answer)
{
if (answer == incantation) {
// varzsls megkezdse
}
else if (answer == "yes") {
// ...
}
// ...
}
A standard knyvtr string osztlyt a 20. fejezet rja le. Ez ms hasznos tulajdonsgai mel-
lett lehetv teszi a rszlncok (substring) kezelst is. Pldul:
string name = "Niels Stroustrup";
void m3()
{
string s = name.substr(6,10); // s = "Stroustrup"
name.replace(0,5,"Nicholas"); // a nv j rtke "Nicholas Stroustrup" lesz
}
A substr() mvelet egy olyan karakterlncot ad vissza, mely a paramtereivel megadott
rszlnc msolata. Az els paramter egy, a karakterlnc egy adott helyre mutat sorszm,
Bevezets 62
a msodik a kvnt rszlnc hossza. Mivel a sorszmozs (az index) 0-tl indul, s a Strous-
trup rtket kapja.A replace() mvelet a karakterlnc egy rszt helyettesti egy msik ka-
rakterlnccal. Ebben az esetben a 0-val indul, 5 hosszsg rszlnc a Niels; ez helyette-
stdik a Nicholas-szal. A name vgs rtke teht Nicholas Stroustrup. Vegyk szre, hogy
a helyettest karakterlncnak nem kell ugyanolyan mretnek lennie, mint az a rszlnc,
amelyet helyettest.
3.5.1.C stlus karakterlncok
A C stlus karakterlnc egy nulla karakterrel vgzd karaktertmb (5.5.2). Meg fogjuk mu-
tatni, hogy egy C stlus karakterlncot knnyen bevihetnk egy string-be. A C stlus ka-
rakterlncokat kezel fggvnyek meghvshoz kpesnek kell lennnk egy string rtk-
nek C stlus karakterlnc formban val kinyersre. A c_str() fggvny ezt teszi (20.3.7).
A name-et a printf() kir C-fggvnnyel (21.8) pldul az albbi mdon rathatjuk ki:
void f()
{
printf("name: %s\n",name.c_str());
}
3.6. Bemenet
A standard knyvtr bemenetre az istreams-et ajnlja. Az ostreams-hez hasonlan az
istreams is a beptett tpusok karaktersorozatknt trtn brzolsval dolgozik s
knnyen bvthet, hogy felhasznli tpusokkal is meg tudjon birkzni. A >> (olvasd be)
mveleti jelet bemeneti opertorknt hasznljuk; a cin a szabvnyos bemeneti adatfolyam.
A >> jobb oldaln ll tpus hatrozza meg, milyen bemenet fogadhat el s mi a beolvas
mvelet clpontja. Az albbi kd egy szmot, pldul 1234-et olvas be a szabvnyos beme-
netrl az i egsz vltozba s egy lebegpontos szmot, mondjuk 12.34e5-t r a ktszeres
pontossg, lebegpontos d vltozba:
void f()
{
int i;
cin >> i; // egsz szm beolvassa i-be
double d;
cin >> d; // ktszeres pontossg lebegpontos szm beolvassa d-be
}
3. Kirnduls a standard knyvtrban 63
A kvetkez plda hvelykrl centimterre s centimterrl hvelykre alakt. Bemenetknt
egy szmot kap, melynek vgn egy karakter jelzi az egysget (centimter vagy hvelyk).
A program vlaszul kiadja a msik egysgnek megfelel rtket:
int main()
{
const float factor = 2.54; // 1 hvelyk 2.54 cm-rel egyenl
float x, in, cm;
char ch = 0;
cout << "rja be a hosszsgot: ";
cin >> x; // lebegpontos szm beolvassa
cin >> ch; // mrtkegysg beolvassa
switch (ch) {
case 'i': // inch (hvelyk)
in = x;
cm = x*factor;
break;
case 'c': // cm
in = x/factor;
cm = x;
break;
default:
in = cm = 0;
break;
}
cout << in << " in = " << cm << " cm\n";
}
A switch utasts egy rtket hasonlt ssze llandkkal. A break utastsok a switch utas-
tsbl val kilpsre valk. A case konstansoknak egymstl klnbznik kell. Ha az el-
lenrztt rtk egyikkel sem egyezik, a vezrls a default-ot vlasztja. A programoznak
nem kell szksgszeren errl az alaprtelmezett lehetsgrl gondoskodnia.
Gyakran akarunk karaktersorozatot olvasni. Ennek knyelmes mdja egy string-be val
helyezs:
int main()
{
string str;
Bevezets 64
cout << "rja be a nevt!\n";
cin >> str;
cout << "Hell, " << str << "!\n";
}
Ha begpeljk a kvetkezt
Erik
a vlasz az albbi lesz:
Hell, Erik!
Alaprtelmezs szerint az olvasst egy reshely (whitespace) karakter (5.5.2), pldul
egy szkz fejezi be, teht ha berjuk a hrhedt kirly nevt
Erik a Vreskez
a vlasz marad
Hell, Erik!
A getline() fggvny segtsgvel egsz sort is beolvashatunk:
int main()
{
string str;
cout << "rja be a nevt!\n";
getline(cin,str);
cout << "Hell, " << str << "!\n";
}
E programmal az albbi bemenet
Erik a Vreskez
a kvnt kimenetet eredmnyezi:
Hell, Erik a Vreskez!
3. Kirnduls a standard knyvtrban 65
A szabvnyos karakterlncoknak megvan az a szp tulajdonsguk, hogy rugalmasan bv-
tik a tartalmukat azzal, amit bevisznk, teht ha nhny megabjtnyi pontosvesszt adunk
meg, a program valban tbb oldalnyi pontosvesszt ad vissza hacsak gpnk vagy az
opercis rendszer valamilyen kritikus erforrsa elbb el nem fogy.
3.7. Trolk
Sok szmts jr klnbz objektumformkbl ll gyjtemnyek (collection) ltrehoz-
sval s kezelsvel. Egy egyszer plda karakterek karakterlncba helyezse, majd a ka-
rakterlnc kiratsa. Az olyan osztlyt, melynek f clja objektumok trolsa, ltalnosan
trolnak (container, kontner) nevezzk. Adott feladathoz megfelel trolkrl gondos-
kodni s ezeket tmogatni brmilyen program ptsnl nagy fontossggal br.
A standard knyvtr leghasznosabb trolinak bemutatsra nzznk meg egy egyszer
programot, amely neveket s telefonszmokat trol. Ez az a fajta program, amelynek vlto-
zatai az eltr htter emberek szmra is egyszernek s maguktl rtetdnek tnnek.
3.7.1. Vektor
Sok C programoz szmra alkalmas kiindulsnak ltszana egy beptett (nv- vagy szm-)
prokbl ll tmb:
struct Entry {
string name;
int number;
};
Entry phone_book[1000];
void print_entry(int i) // egyszer hasznlat
{
cout << phone_book[i].name << ' ' << phone_book[i].number << '\n';
}
A beptett tmbk mrete azonban rgztett. Ha nagy mretet vlasztunk, helyet pazaro-
lunk; ha kisebbet, a tmb tl fog csordulni. Mindkt esetben alacsonyszint trkezel k-
dot kell rnunk. A standard knyvtr a vector tpust (16.3) bocstja rendelkezsre, amely
megoldja a fentieket:
Bevezets 66
vector<Entry> phone_book(1000);
void print_entry(int i) // egyszer hasznlat, mint a tmbnl
{
cout << phone_book[i].name << ' ' << phone_book[i].number << '\n';
}
void add_entries(int n) // mret nvelse n-nel
{
phone_book.resize(phone_book.size()+n);
}
A vector size() tagfggvnye megadja az elemek szmt. Vegyk szre a () zrjelek hasz-
nlatt a phone_book defincijban. Egyetlen vector<Entry> tpus objektumot hoztunk
ltre, melynek megadtuk a kezdeti mrett. Ez nagyon klnbzik a beptett tmbk
bevezetstl:
vector<Entry> book(1000); // vektor 1000 elemmel
vector<Entry> books[1000]; // 1000 res vektor
Ha hibsan [ ] t (szgletes zrjelet) hasznlnnk ott, ahol egy vector deklarlsban ()-t
rtettnk, a fordt majdnem biztos, hogy hibazenetet ad, amikor a vector-t hasznlni
prbljuk.
A vector egy pldnya objektum, melynek rtket adhatunk:
void f(vector<Entry>& v)
{
vector<Entry> v2 = phone_book;
v = v2;
// ...
}
A vector-ral val rtkads az elemek msolsval jr. Teht f()-ben az elkszts (iniciali-
zls) s rtkads utn v s v2 is egy-egy kln msolatot tartalmaz a phone_book-ban l-
v minden egyes Entry-rl. Ha egy vektor sok elemet tartalmaz, az ilyen rtatlannak ltsz
rtkadsok megengedhetetlenl kltsgesek. Ahol a msols nem kvnatos, referenci-
kat (hivatkozsokat) vagy mutatkat kell hasznlni.
3. Kirnduls a standard knyvtrban 67
3.7.2. Tartomnyellenrzs
A standard knyvtrbeli vector alaprtelmezs szerint nem gondoskodik tartomnyellenr-
zsrl (16.3.3). Pldul:
void f()
{
int i = phone_book[1001].number; // az 1001 kvl esik a tartomnyon
// ...
}
A kezdeti rtkads valsznleg inkbb valamilyen vletlenszer rtket tesz i-be, mint
hogy hibt okoz. Ez nem kvnatos, ezrt a soron kvetkez fejezetekben a vector egy egy-
szer tartomnyellenrz talaktst fogjuk hasznlni, Vec nven. A Vec olyan, mint
a vector, azzal a klnbsggel, hogy out_of_range tpus kivtelt vlt ki, ha egy index kifut
a tartomnybl.
A Vec-hez hasonl tpusok megvalstsi mdjait s a kivtelek hatkony hasznlatt 11.12,
8.3 s a 14. fejezet trgyalja. Az itteni definci azonban elegend a knyv pldihoz:
template<class T> class Vec : public vector<T> {
public:
Vec() : vector<T>() { }
Vec(int s) : vector<T>(s) { }
T& operator[ ](int i) { return at(i); } // tartomnyellenrzs
const T& operator[ ](int i) const { return at(i); } // tartomnyellenrzs
};
Az at(i) egy vector indexmvelet, mely out_of_range tpus kivtelt vlt ki, ha paramtere
kifut a vector tartomnybl (16.3.3).
Visszatrve a nevek s telefonszmok trolsnak problmjhoz, most mr hasznlhatjuk
a Vec-et, biztostva, hogy a tartomnyon kvli hozzfrseket elkapjuk:
Vec<Entry> phone_book(1000);
void print_entry(int i) // egyszer hasznlat, mint a vektornl
{
cout << phone_book[i].name << ' ' << phone_book[i].number << '\n';
}
Bevezets 68
A tartomnyon kvli hozzfrs kivtelt fog kivltani, melyet a felhasznl elkaphat:
void f()
{
try {
for (int i = 0; i<10000; i++) print_entry(i);
}
catch (out_of_range) {
cout << "tartomnyhiba\n";
}
}
A kivtel dobsa majd elkapsa akkor trtnik, amikor a phone_book[i]-re i==1000 rtk-
kel trtnik hozzfrsi ksrlet. Ha a felhasznl nem kapja el ezt a fajta kivtelt, a prog-
ram meghatrozott mdon befejezdik; nem folytatja futst s nem vlt ki meghatro-
zatlan hibt. A kivtelek okozta meglepetsek cskkentsnek egyik mdja, ha a main()
trzsben egy try blokkot hozunk ltre:
int main()
try {
// sajt kd
}
catch (out_of_range) {
cerr << "tartomnyhiba\n";
}
catch (...) {
cerr << "ismeretlen kivtel\n";
}
Ez gondoskodik az alaprtelmezett kivtelkezelkrl, teht ha elmulasztunk elkapni egy ki-
vtelt, a cerr szabvnyos hibakimeneti adatfolyamon hibajelzs jelenik meg (21.2.1).
3.7.3. Lista
A telefonknyv-bejegyzsek beszrsa s trlse ltalnosabb lehet, ezrt egy egyszer te-
lefonknyv brzolsra egy lista jobban megfelelne, mint egy vektor:
list<Entry> phone_book;
Amikor listt hasznlunk, az elemekhez nem sorszm alapjn szeretnnk hozzfrni, ahogy
a vektorok esetben ltalban tesszk. Ehelyett tkutathatjuk a listt, adott rtk elemet
keresve.
3. Kirnduls a standard knyvtrban 69
Ehhez kihasznljuk azt a tnyt, hogy a list egy sorozat (3.8):
void print_entry(const string& s)
{
typedef list<Entry>::const_iterator LI;
for (LI i = phone_book.begin(); i != phone_book.end(); ++i) {
Entry& e = *i; // rvidts referencival
if (s == e.name) {
cout << e.name << ' ' << e.number << '\n';
return;
}
}
}
Az s keresse a lista elejnl kezddik, s addig folytatdik, mg az s-t megtalljuk vagy el-
rnk a lista vghez. Minden standard knyvtrbeli trol tartalmazza a begin() s end()
fggvnyeket, melyek egy bejrt (itertort) adnak vissza az els, illetve az utols utni
elemre (16.3.2). Ha adott egy i bejr, a kvetkez elem ++i lesz. Az i vltoz a *i elemre
hivatkozik. A felhasznlnak nem kell tudnia, pontosan milyen tpus egy szabvnyos t-
rol bejrja. A tpus a trol lersnak rsze s nv szerint lehet hivatkozni r. Ha nincs
szksgnk egy trolelem mdostsra, a const_iterator az a tpus, ami neknk kell. K-
lnben a sima iterator tpust (16.3.1) hasznljuk.
Elemek hozzadsa egy list-hez igen knny:
void add_entry(const Entry& e, list<Entry>::iterator i)
{
phone_book.push_front(e); // hozzads a lista elejhez
phone_book.push_back(e); // hozzads a lista vghez
phone_book.insert(i,e); // hozzads az i' ltal mutatott elem el
}
3.7.4. Asszociatv tmbk
Egy nv- vagy szmprokbl ll listhoz keres kdot rni valjban igen fradsgos mun-
ka. Ezenkvl a sorban trtn keress a legrvidebb listk kivtelvel nagyon rossz ha-
tkonysg. Ms adatszerkezetek kzvetlenl tmogatjk a beszrst, a trlst s az rtk
szerinti keresst. A standard knyvtr nevezetesen a map tpust biztostja erre a feladatra
(17.4.1). A map egy rtkpr-trol. Pldul:
map<string,int> phone_book;
Bevezets 70
Ms krnyezetekben a map mint asszociatv tmb vagy sztr szerepel. Ha els tpusval
(a kulccsal, key) indexeljk, a map a msodik tpus (az rtk, vagyis a hozzrendelt tpus,
mapped type) megfelel rtkt adja vissza:
void print_entry(const string& s)
{
if (int i = phone_book[s]) cout << s << ' ' << i << '\n';
}
Ha nem tall illeszkedst az s kulcsra, a phone_book egy alaprtket ad vissza. A map alap-
rtke int tpusra 0. Itt felttelezzk, hogy a 0 nem rvnyes telefonszm.
3.7.5. Szabvnyos trolk
Az asszociatv tmb, a lista s a vektor mind hasznlhat telefonknyv brzolsra. Mind-
egyiknek megvannak az ers s gyenge oldalai. A vektorokat indexelni olcs s knny.
Msrszt kt eleme kz egyet beszrni kltsgesebb lehet. A lista tulajdonsgai ezzel pon-
tosan ellenttesek. A map emlkeztet egy (kulcsrtk) prokbl ll listra, azzal a kiv-
tellel, hogy rtkei a kulcs szerinti keresshez a legmegfelelbbek.
A standard knyvtr rendelkezik a legltalnosabb s leghasznlhatbb troltpusokkal,
ami lehetv teszi, hogy a programozk olyan trolt vlasszanak, mely az adott alkalma-
zs ignyeit a legjobban kiszolglja:
3. Kirnduls a standard knyvtrban 71
Szabvnyos trolk sszefoglalsa
Vector<T> Vltoz hosszsg vektor (16.3)
list<T> Ktirny lncolt lista (17.2.2)
Queue<T> Sor (17.3.2)
Stack<T> Verem (17.3.1)
Deque<T> Ktvg sor (17.2.3)
Priority_queue<T> rtk szerint rendezett sor (17.3.3)
set<T> Halmaz (17.4.3)
Multiset<T> Halmaz, melyben egy rtk tbbszr
is elfordulhat (17.4.4)
Map<kulcs,rtk> Asszociatv tmb (17.4.1)
Multimap<kulcs,rtk> Asszociatv tmb, melyben egy kulcs tbbszr
elfordulhat (17.4.2)
A szabvnyos trolkat 16.2, 16.3 s a 17. fejezet mutatja be. A trolk az std nvtrhez
tartoznak, lersuk a <vector>, <list>, <map> stb. fejllomnyokban szerepel. A szabvnyos
trolk s alapmveleteik jells szempontjbl hasonlak, tovbb a mveletek jelentse
a klnbz trolkra nzve egyforma. Az alapmveletek ltalban mindenfajta trolra al-
kalmazhatk. A push_back() pldul meglehetsen hatkony mdon egyarnt hasznl-
hat elemeknek egy vektor vagy lista vghez fzsre, s minden trolnak van size() tag-
fggvnye, mely visszaadja az elemek a szmt.Ez a jellsbeli s jelentsbeli egysgessg
lehetv teszi, hogy a programozk j troltpusokat ksztsenek, melyek a szabvnyos t-
pusokhoz nagyon hasonl mdon hasznlhatk. A Vec tartomnyellenrzssel elltott vek-
tor (3.7.6) ennek egy pldja. A 17. fejezet bemutatja, hogyan lehet egy hash_map-et
a szerkezethez hozztenni. A trolfelletek egysges volta emellett lehetv teszi azt is,
hogy az egyes troltpusoktl fggetlenl adjunk meg algoritmusokat.
3.8. Algoritmusok
Az adatszerkezetek, mint a list vagy a vector, nmagukban nem tl hasznosak. Hasznla-
tukhoz olyan alapvet hozzfrsi mveletekre van szksg, mint az elemek hozzadsa s
eltvoltsa. Emellett ritkn hasznlunk egy trolt pusztn trolsra. Rendezzk, kiratjuk,
rszhalmazokat vonunk ki bellk, elemeket tvoltunk el, objektumokat keresnk bennk
s gy tovbb. Emiatt a standard knyvtr az ltalnos troltpusokon kvl biztostja a t-
rolk legltalnosabb eljrsait is. A kvetkez kdrszlet pldul egy vector-t rendez s
minden egyedi vector elem msolatt egy list-be teszi:
void f(vector<Entry>& ve, list<Entry>& le)
{
sort(ve.begin(),ve.end());
unique_copy(ve.begin(),ve.end(),le.begin());
}
A szabvnyos algoritmusok lerst a 18. fejezetben talljuk. Az algoritmusok elemek soro-
zatval mkdnek (2.7.2). Az ilyen sorozatok bejr-prokkal brzolhatk, melyek az el-
s, illetve az utols utni elemet adjk meg. A pldban a sort() rendezi a sorozatot,
ve.begin()-tl ve.end()-ig, ami ppen a vector sszes elemt jelenti. rshoz csak az els
rand elemet szksges megadni. Ha tbb mint egy elemet runk, a kezd elemet kvet
elemek fellrdnak. Ha az j elemeket egy trol vghez kvnnnk adni, az albbit r-
hatnnk:
Bevezets 72
void f(vector<Entry>& ve, list<Entry>& le)
{
sort(ve.begin(),ve.end());
unique_copy(ve.begin(),ve.end(),back_inserter(le)); // hozzfzs le-hez
}
A back_inserter() elemeket ad egy trol vghez, bvtve a trolt, hogy helyet csinljon
rszkre (19.2.4). A szabvnyos trolk s a back_inserter()-ek kikszblik a hibalehet-
sget jelent, C stlus realloc()-ot hasznl trkezelst (16.3.5). Ha a hozzfzskor elfe-
lejtjk a back_inserter()-t hasznlni, az hibkhoz vezethet:
void f(vector<Entry>& ve, list<Entry>& le)
{
copy(ve.begin(),ve.end(),le); // hiba: le nem bejr
copy(ve.begin(),ve.end(),le.end()); // rossz: tlr a vgn
copy(ve.begin(),ve.end(),le.begin()); // elemek fellrsa
}
3.8.1. Bejrk hasznlata
Amikor elszr tallkozunk egy trolval, megkaphatjuk nhny hasznos elemre hivatko-
z bejrjt (itertort); a legjobb plda a begin() s az end(). Ezenkvl sok algoritmus ad
vissza bejrkat. A find szabvnyos algoritmus pldul egy sorozatban keres egy rtket s
azt a bejrt adja vissza, amely a megtallt elemre mutat. Ha a find-ot hasznljuk, megszm-
llhatjuk valamely karakter elfordulsait egy karakterlncban:
int count(const string& s, char c) // c elfordulsainak megszmllsa s-ben
{
int n = 0;
string::const_iterator i = find(s.begin(),s.end(),c);
while (i != s.end()) {
++n;
i = find(i+1,s.end(),c);
}
return n;
}
A find algoritmus valamely rtk egy sorozatban val els elfordulsra vagy a sorozat
utols utni elemre mutat bejrt ad vissza. Lssuk, mi trtnik a count egyszer megh-
vsakor:
3. Kirnduls a standard knyvtrban 73
void f()
{
string m = "Mary had a little lamb";
int a_count = count(m,'a');
}
A find() els hvsa megtallja 'a'-t a Mary-ben. A bejr teht e karakterre mutat s nem az
s.end()-re, gy belptnk a ciklusba. A ciklusban i+1-gyel kezdjk a keresst; vagyis eggyel
a megtallt 'a' utn. Ezutn folytatjuk a ciklust s megtalljuk a msik hrom 'a'-t. A find()
ekkor elr a sorozat vghez s az s.end()-et adja vissza, gy nem teljesl az i!=s.end() felt-
tel s kilpnk a ciklusbl. Ezt a count() hvst grafikusan gy brzolhatnnk:
A nyilak az i kezdeti, kzbens s vgs rtkeit mutatjk.
Termszetesen a find algoritmus minden szabvnyos troln egyformn fog mkdni. K-
vetkezskppen ugyanilyen mdon ltalnosthatnnk a count() fggvnyt:
template<class C, class T> int count(const C& v, T val)
{
typename C::const_iterator i = find(v.begin(),v.end(),val); // typename, lsd C.13.5
int n = 0;
while (i != v.end()) {
++n;
++i; // az elbb megtallt elem tugrsa
i = find(i,v.end(),val);
}
return n;
}
Ez mkdik, gy mondhatjuk:
void f(list<complex>& lc, vector<string>& vc, string s)
{
int i1 = count(lc,complex(1,3));
int i2 = count(vc,"Diogensz");
int i3 = count(s,'x');
}
Bevezets 74
M a r y h a d a l i t t l e l a m b
A count sablont azonban nem kell definilnunk. Az elemek elfordulsainak megszmll-
sa annyira ltalnos s hasznos, hogy a standard knyvtr tartalmazza ezt a mveletet. Hogy
teljesen ltalnos legyen a megolds, a standard knyvtri count paramterknt trol he-
lyett egy sorozatot kap:
void f(list<complex>& lc, vector<string>& vs, string s)
{
int i1 = count(lc.begin(),lc.end(),complex(1,3));
int i2 = count(vs.begin(),vs.end(),"Diogensz");
int i3 = count(s.begin(),s.end(),'x');
}
A sorozat hasznlata megengedi, hogy a szmllst beptett tmbre hasznljuk s azt is,
hogy egy trol valamely rszt szmlljuk:
void g(char cs[ ], int sz)
{
int i1 = count(&cs[0],&cs[sz],'z'); // 'z'-k a tmbben
int i2 = count(&cs[0],&cs[sz/2],'z'); // 'z'-k a tmb els felben
}
3.8.2. Bejrtpusok
Valjban mik is azok a bejrk (itertorok)? Minden bejr valamilyen tpus objektum.
Azonban sok klnbz tpusuk ltezik, mert a bejrnak azt az informcit kell trolnia,
melyre az adott troltpusnl feladata elltshoz szksge van. Ezek a bejrtpusok
olyan klnbzk lehetnek, mint a trolk s azok az egyedi ignyek, melyeket kiszolgl-
nak. Egy vector bejrja pldul minden bizonnyal egy kznsges mutat, mivel az na-
gyon sszer hivatkozsi md a vector elemeire:
3. Kirnduls a standard knyvtrban 75
P i e t H e i n
bejr:
vektor:
p
Egy vector-bejr megvalsthat gy is, mint a vector-ra hivatkoz mutat, meg egy sor-
szm:
Az ilyen bejrk hasznlata tartomnyellenrzsre is lehetsget ad (19.3).
A listk bejrinak valamivel bonyolultabbnak kell lennik, mint egy egyszer mutat a lis-
tban trolt elemre, mivel egy ilyen elem ltalban nem tudja, hol van a lista kvetkez tag-
ja. A lista-bejr teht inkbb a lista valamely csompontjra hivatkoz mutat:
Ami kzs minden bejrnl, az a jelentsk s a mveleteik elnevezse. A ++ alkalmaz-
sa brmely bejrra pldul olyan bejrt ad, mely a kvetkez elemre hivatkozik. Hason-
lkppen * azt az elemet adja meg, melyre a bejr hivatkozik. Valjban bejr lehet br-
mely objektum, amely nhny, az elzekhez hasonl egyszer szablynak eleget tesz
(19.2.1). Tovbb, a felhasznl ritkn kell, hogy ismerje egy adott bejrtpust. Sajt
bejr-tpusait minden trol ismeri s azokat az egyezmnyes iterator s const_iterator ne-
veken rendelkezsre bocstja. Pldul a list<Entry>::iterator a list<Entry> ltalnos bejr-
tpusa. Ritkn kell aggdnunk az adott tpus meghatrozsa miatt.
Bevezets 76
P i e t H e i n
bejr: (kezdet == p, pozici == 3)
vektor:
cs. csompont cs. cs. ...
P i e t
p bejr:
lista:
elemek:
3.8.3. Bemeneti s kimeneti bejrk
A bejrk fogalma ltalnos s hasznos a trolk elemeibl ll sorozatok kezelsnl. A t-
rolk azonban nem az egyetlen helyet jelentik, ahol elemek sorozatt talljuk. A bemeneti
adatfolyamok is rtkek sorozatbl llnak, s rtkek sorozatt rjuk a kimeneti adatfo-
lyamba is. Kvetkezskppen a bejrk hasznosan alkalmazhatk a bemenetnl s kime-
netnl is.
Egy ostream_iterator ltrehozshoz meg kell hatroznunk, melyik adatfolyamot hasznl-
juk s milyen tpus objektumokat runk bele. Megadhatunk pldul egy bejrt, mely
a cout szabvnyos kimeneti adatfolyamra hivatkozik:
ostream_iterator<string> oo(cout);
Az rtkads *oo-nak azt jelenti, hogy az rtkad adatot a cout-ra rjuk ki. Pldul:
int main()
{
*oo = "Hell, "; // jelentse cout << "Hell, "
++oo;
*oo = "vilg!\n"; // jelentse cout << "vilg!\n"
}
Ez egy jabb md szabvnyos zenetek kirsra a szabvnyos kimenetre. A ++oo clja:
utnozni egy tmbbe mutatn keresztl trtn rst. A szerz elsnek nem ezt a mdot v-
lasztan erre az egyszer feladatra, de ez az eszkz alkalmas arra, hogy a kimenetet gy ke-
zeljk, mint egy csak rhat trolt, ami hamarosan magtl rtetd lesz ha eddig mg
nem lenne az.
Hasonlkppen az istream_iterator olyasvalami, ami lehetv teszi, hogy a bemeneti adat-
folyamot gy kezeljk, mint egy csak olvashat trolt. Itt is meg kell adnunk a hasznlni
kvnt adatfolyamot s a vrt rtkek tpust:
istream_iterator<string> ii(cin);
Mivel a bejrk mindig prokban brzolnak egy sorozatot, a bemenet vgnek jelzshez
egy istream_iterator-rl kell gondoskodni. Az alaprtelmezett istream_iterator a kvetkez:
istream_iterator<string> eos;
3. Kirnduls a standard knyvtrban 77
Most jra beolvashatnnk a Hell, vilgot!-ot a bemenetrl s kirathatnnk az albbi
mdon:
int main()
{
string s1 = *ii;
++ii;
string s2 = *ii;
cout << s1 << ' ' << s2 << '\n';
}
Valjban az istream_iterator-okat s ostream_ iterator-okat nem kzvetlen hasznlatra ta-
lltk ki. Jellemzen algoritmusok paramtereiknt szolglnak. rjunk pldul egy egysze-
r programot, mely egy fjlbl olvas, az olvasott adatokat rendezi, a ktszer szerepl ele-
meket eltvoltja, majd az eredmnyt egy msik fjlba rja:
int main()
{
string from, to;
cin >> from >> to; // a forrs- s clfjl nevnek beolvassa
ifstream is(from.c_str()); // bemeneti adatfolyam (c_str(), lsd 3.5.1 s 20.3.7)
istream_iterator<string> ii(is); // bemeneti bejr az adatfolyam szmra
istream_iterator<string> eos; // bemenet-ellenrzs
vector<string> b(ii,eos); // b egy vektor, melynek a bemenetrl adunk kezdrtket
sort(b.begin(),b.end()); // az tmeneti tr (b) rendezse
ofstream os(to.c_str()); // kimeneti adatfolyam
ostream_iterator<string> oo(os,"\n"); // kimeneti bejr az adatfolyam szmra
unique_copy(b.begin(),b.end(),oo); // b tartalmnak a kimenetre msolsa,
// a kettztt rtkek elvetse
return !is.eof() || !os; // hiballapot visszaadsa (3.2, 21.3.3)
}
Az ifstream egy istream, mely egy fjlhoz kapcsolhat, az ofstream pedig egy ostream,
mely szintn egy fjlhoz kapcsolhat. Az ostream_iterator msodik paramtere a kimeneti
rtkeket elvlaszt jel.
Bevezets 78
3.8.4. Bejrsok s prediktumok
A bejrk lehetv teszik, hogy ciklusokat rjunk egy sorozat bejrsra. A ciklusok meg-
rsa azonban fradsgos lehet, ezrt a standard knyvtr mdot ad arra, hogy egy adott
fggvnyt a sorozat minden egyes elemre meghvjunk. Tegyk fel, hogy runk egy progra-
mot, mely a bemenetrl szavakat olvas s feljegyzi elfordulsuk gyakorisgt. A karakter-
lncok s a hozzjuk tartoz gyakorisgok kzenfekv brzolsa egy map-pel trtnhet:
map<string,int> histogram;
Az egyes karakterlncok gyakorisgnak feljegyzsre termszetes mvelet a kvetkez:
void record(const string& s)
{
histogram[s]++; // "s'' gyakorisgnak rgztse
}
Ha beolvastuk a bemenetet, szeretnnk az sszegyjttt adatokat a kimenetre kldeni.
A map (string,int) prokbl ll sorozat. Kvetkezskppen szeretnnk meghvni az alb-
bi fggvnyt
void print(const pair<const string,int>& r)
{
cout << r.first << ' ' << r.second << '\n';
}
a map minden elemre (a prok (pair) els elemt first-nek, msodik elemt second-nak
nevezzk). A pair els eleme const string, nem sima string, mert minden map kulcs
konstans.
A fprogram teht a kvetkez:
int main()
{
istream_iterator<string> ii(cin);
istream_iterator<string> eos;
for_each(ii,eos,record);
for_each(histogram.begin(),histogram.end(),print);
}
3. Kirnduls a standard knyvtrban 79
Vegyk szre, hogy nem kell rendeznnk a map-et ahhoz, hogy a kimenet rendezett le-
gyen. A map rendezve trolja az elemeket s a ciklus is (nvekv) sorrendben jrja vgig
a map-et.
Sok programozsi feladat szl arrl, hogy meg kell keresni valamit egy trolban, ahelyett,
hogy minden elemen vgrehajtannk egy feladatot. A find algoritmus (18.5.2) knyelmes
mdot ad egy adott rtk megkeressre. Ennek az tletnek egy ltalnosabb vltozata
olyan elemet keres, mely egy bizonyos kvetelmnynek felel meg. Pldul meg akarjuk ke-
resni egy map els 42-nl nagyobb rtkt:
bool gt_42(const pair<const string,int>& r)
{
return r.second>42;
}
void f(map<string,int>& m)
{
typedef map<string,int>::const_iterator MI;
MI i = find_if(m.begin(),m.end(),gt_42);
// ...
}
Mskor megszmllhatnnk azon szavakat, melyek gyakorisga nagyobb, mint 42:
void g(const map<string,int>& m)
{
int c42 = count_if(m.begin(),m.end(),gt_42);
// ...
}
Az olyan fggvnyeket, mint a gt_42(), melyet az algoritmus vezrlsre hasznlunk, pre-
diktumnak (lltmny, vezrlfggvny, predicate) nevezzk. Ezek minden elemre
meghvdnak s logikai rtket adnak vissza, melyet az algoritmus szndkolt tevkenys-
gnek elvgzshez felhasznl. A find_if() pldul addig keres, amg a prediktuma true-t
nem ad vissza, jelezvn, hogy a krt elemet megtallta. Hasonl mdon a count_if() annyit
szmll, ahnyszor a prediktuma true.
A standard knyvtr nhny hasznos prediktumot is biztost, valamint olyan sablonokat,
melyek tovbbiak alkotsra hasznlhatk (18.4.2).
Bevezets 80
3.8.5. Tagfggvnyeket hasznl algoritmusok
Sok algoritmus alkalmaz fggvnyt egy sorozat elemeire. Pldul 3.8.4-ben a
for_each(ii,eos,record);
meghvja a record()-ot minden egyes, a bemenetrl beolvasott karakterlncra.
Gyakran mutatk trolival van dolgunk, s sokkal inkbb a hivatkozott objektum egy tag-
fggvnyt szeretnnk meghvni, nem pedig egy globlis fggvnyt, a mutatt paramter-
knt tadva. Tegyk fel, hogy a Shape::draw() tagfggvnyt akarjuk meghvni egy
list<Shape>* elemeire. A plda kezelsre egyszeren egy nem tag fggvnyt runk, mely
meghvja a tagfggvnyt:
void draw(Shape* p)
{
p->draw();
}
void f(list<Shape*>& sh)
{
for_each(sh.begin(),sh.end(),draw);
}
A mdszert gy ltalnosthatjuk:
void g(list<Shape*>& sh)
{
for_each(sh.begin(),sh.end(),mem_fun(&Shape::draw));
}
A standard knyvtri mem_fun() sablon (18.4.4.2) paramterknt egy tagfggvny muta-
tjt kapja (15.5) s valami olyasmit hoz ltre, amit a tag osztlyra hivatkoz mutatn ke-
resztl hvhatunk meg. A mem_fun(&Shape::draw) eredmnye egy Shape* paramtert kap
s visszaadja, amit a Shape::draw() visszaad.
A mem_fun() azrt fontos, mert megengedi, hogy a szabvnyos algoritmusokat tbbalak
(polimorf) objektumok trolira hasznljuk.
3. Kirnduls a standard knyvtrban 81
3.8.6. A standard knyvtr algoritmusai
Mi az algoritmus? ltalnos meghatrozsa szerint szablyok vges halmaza, mely adott
problmahalmaz megoldshoz mveletek sorozatt hatrozza meg s t fontos jellemzje
van: vgessg, meghatrozottsg, bemenet, kimenet, hatkonysg [Knuth,1968, 1.1].
A C++ standard knyvtrnak viszonylatban az algoritmus elemek sorozatn mveleteket
vgz sablonok (template-ek) halmaza.
A standard knyvtr tbb tucat algoritmust tartalmaz. Az algoritmusok az std nvtrhez tar-
toznak, lersuk az <algorithm> fejllomnyban szerepel. me nhny, melyeket klnsen
hasznosnak talltam:
3.9. Matematika
A C-hez hasonlan a C++ nyelvet sem elssorban szmokkal vgzett mveletekre tervez-
tk. Mindemellett rengeteg numerikus munkt vgeztek C++-ban s ez tkrzdik a stan-
dard knyvtrban is.
Bevezets 82
Vlogatott szabvnyos algoritmusok
for_each() Hvd meg a fggvnyt minden elemre (18.5.1)
find() Keresd meg a paramterek els elfordulst
(18.5.2)
find_if() Keresd meg a prediktumra az els illeszkedst
(18.5.2)
count() Szmlld meg az elem elfordulsait (18.5.3)
count_if() Szmlld meg az illeszkedseket a prediktumra
(18.5.3)
replace() Helyettestsd be az elemet j rtkkel (18.6.4)
replace_if() Helyettestsd be a prediktumra illeszked elemet
j rtkkel (18.6.4)
copy() Msold az elemeket (18.6.1)
unique_copy() Msold a csak egyszer szerepl elemeket (18.6.1)
sort() Rendezd az elemeket (18.7.1)
equal_range() Keresd meg az sszes egyez rtk elemet
(18.7.2)
merge() Fsld ssze a rendezett sorozatokat (18.7.3)
3.9.1. Komplex szmok
A standard knyvtr a komplex szmok egy tpuscsaldjt tartalmazza, a 2.5.2-ben lert
complex osztly alakjban. Az egyszeres pontossg lebegpontos (float), a ktszeres pon-
tossg (double) stb. skalrokat tartalmaz komplex szmok tmogatsra a standard
knyvtrbeli complex egy sablon:
template<class scalar> class complex {
public:
complex(scalar re, scalar im);
// ...
};
A szoksos aritmetikai mveletek s a leggyakrabban hasznlt matematikai fggvnyek
komplex szmokkal is mkdnek:
// szabvnyos exponencilis fggvny a <complex> sablonbl:
template<class C> complex<C> pow(const complex<C>&, int);
void f(complex<float> fl, complex<double> db)
{
complex<long double> ld = fl+sqrt(db);
db += fl*3;
fl = pow(1/fl,2);
// ...
}
Rszletesebben lsd 22.5.
3.9.2. Vektoraritmetika
A 3.7.1-ben lert vector-t ltalnos rtktrolsra terveztk; kellen rugalmas s illeszkedik
a trolk, bejrk s algoritmusok szerkezetbe, ugyanakkor nem tmogatja a matematikai
vektormveleteket. Ilyen mveleteket knnyen be lehetett volna pteni a vector-ba, de az
ltalnossg s rugalmassg eleve kizr olyan optimalizlsokat, melyeket komolyabb, sz-
mokkal vgzett munknl gyakran lnyegesnek tekintnk. Emiatt a standard knyvtrban
megtalljuk a valarray nev vektort is, mely kevsb ltalnos s a szmmveletekhez job-
ban megfelel:
template<class T> class valarray {
// ...
T& operator[ ](size_t);
// ...
};
3. Kirnduls a standard knyvtrban 83
A size_t eljel nlkli egsz tpus, melyet a nyelv tmbk indexelsre hasznl. A szoksos arit-
metikai mveleteket s a leggyakoribb matematikai fggvnyeket megrtk a valarray-kre is:
// szabvnyos abszoltrtk-fggvny a <valarray> sablonbl:
template<class T> valarray<T> abs(const valarray<T>&);
void f(valarray<double>& a1, valarray<double>& a2)
{
valarray<double> a = a1*3.14+a2/a1;
a2 += a1*3.14;
a = abs(a);
double d = a2[7];
// ...
}
Rszletesebben lsd: 22.4
3.9.3. Alapszint numerikus tmogats
A standard knyvtr a lebegpontos tpusokhoz termszetesen tartalmazza a leggyakoribb
matematikai fggvnyeket (log(), pow() s cos(), lsd 2.2.3). Ezenkvl azonban tartalmaz
olyan osztlyokat is, melyek beptett tpusok tulajdonsgait pldul egy float kitevjnek
lehetsges legnagyobb rtkt rjk le (lsd 22.2).
3.10. A standard knyvtr szolgltatsai
A standard knyvtr szolgltatsait az albbi mdon osztlyozhatjuk:
1. Alapvet futsi idej tmogats (pl. trlefoglals s futsi idej tpusinformci),
lsd 16.1.3.
2. A szabvnyos C knyvtr (nagyon csekly mdostsokkal, a tpusrendszer
megsrtsnek elkerlsre), lsd 16.1.2.
3. Karakterlncok s bemeneti/kimeneti adatfolyamok (nemzetkzi karakterksz-
let s nyelvi tmogatssal), lsd 20. s 21. fejezet.
4. Trolk (vector, list s map) s trolkat hasznl algoritmusok (ltalnos bej-
rsok, rendezsek s sszefslsek) rendszere, lsd 16., 17., 18. s 19.fejezet.
Bevezets 84
5. Szmokkal vgzett mveletek tmogatsa (komplex szmok s vektorok aritme-
tikai mveletekkel), BLAS-szer s ltalnostott szeletek, valamint az optimali-
zlst megknnyt szerkezetek, lsd 22. fejezet.
Annak f felttele, hogy egy osztly bekerlhet-e a knyvtrba, az volt, hogy valamilyen
mdon hasznlta-e mr majdnem minden C++ programoz (kezdk s szakrtk egy-
arnt), hogy ltalnos alakban megadhat-e, hogy nem jelent-e jelents tbbletterhelst
ugyanennek a szolgltatsnak valamely egyszerbb vltozathoz viszonytva, s hogy
knnyen megtanulhat-e a hasznlata. A C++ standard knyvtra teht az alapvet adat-
szerkezeteket s az azokon alkalmazhat alapvet algoritmusokat tartalmazza.
Minden algoritmus talakts nlkl mkdik minden trolra. Ez az egyezmnyesen STL-
nek (Standard Template Library, szabvnyos sablonknyvtr) [Stepanov, 1994] nevezett vz
bvthet, abban az rtelemben, hogy a felhasznlk a knyvtr rszeknt megadottakon
kvl knnyen kszthetnek sajt trolkat s algoritmusokat, s ezeket azonnal mkdtet-
hetik is a szabvnyos trolkkal s algoritmusokkal egytt.
3.11. Tancsok
[1] Ne talljunk fel a melegvizet hasznljunk knyvtrakat.
[2] Ne higgynk a csodkban. rtsk meg, mit tesznek knyvtraink, hogyan
teszik, s milyen ron teszik.
[3] Amikor vlaszthatunk, rszestsk elnyben a standard knyvtrat ms knyvt-
rakkal szemben.
[4] Ne gondoljuk, hogy a standard knyvtr mindenre idelis.
[5] Ne felejtsk el bepteni (#include) a felhasznlt szolgltatsok fejllomnyait.
3.3.
[6] Ne felejtsk el, hogy a standard knyvtr szolgltatsai az std nvtrhez tartoz-
nak. 3.3.
[7] Hasznljunk string-et char* helyett. 3.5, 3.6.
[8] Ha ktsgeink vannak, hasznljunk tartomnyellenrz vektort (mint a Vec).
3.7.2.
[9] Rszestsk elnyben a vector<T>-t, a list<T>-t s a map<key,value>-t a T[ ]-vel
szemben. 3.7.1, 3.7.3, 3.7.4.
3. Kirnduls a standard knyvtrban 85
[10] Amikor elemeket tesznk egy trolba, hasznljunk push_back()-et vagy
back_inserter()-t. 3.7.3, 3.8.
[11] Hasznljunk vektoron push_back()-et a realloc() tmbre val alkalmazsa
helyett. 3.8.
[12] Az ltalnos kivteleket a main()-ben kapjuk el. 3.7.2.
Bevezets 86
Els rsz
Alapok
Ez a rsz a C++ beptett tpusait s azokat az alapvet lehetsgeket rja le, amelyekkel
programokat hozhatunk ltre. A C++-nak a C nyelvre visszautal rsze a hagyomnyos
programozsi stlusok tmogatsval egytt kerl bemutatsra, valamint ez a rsz trgyalja
azokat az alapvet eszkzket is, amelyekkel C++ programot hozhatunk ltre logikai s fi-
zikai elemekbl.
Fejezetek
4. Tpusok s deklarcik
5. Mutatk, tmbk s struktrk
6. Kifejezsek s utastsok
7. Fggvnyek
8. Nvterek s kivtelek
9. Forrsfjlok s programok
Tpusok s deklarcik
Ne fogadj el semmit, ami
nem tkletes!
(ismeretlen szerz)
A tkletessg csak az ssze-
omls pontjn rhet el.
(C.N. Parkinson)
Tpusok Alaptpusok Logikai tpusok Karakterek Karakterliterlok Egszek
Egsz literlok Lebegpontos tpusok Lebegpontos literlok Mretek void Felso-
rol tpusok Deklarcik Nevek Hatkrk Kezdeti rtkads Objektumok
typedef-ek Tancsok Gyakorlatok
4
4.1. Tpusok
Vegyk az
x = y+f(2);
kifejezst. Hogy ez rtelmes legyen valamely C++ programban, az x, y s f neveket megfe-
lelen definilni kell, azaz a programoznak meg kell adnia, hogy ezek az x, y, s f nev
egyedek lteznek s olyan tpusak, amelyekre az = (rtkads), a + (sszeads) s a ()
(fggvnyhvs) rendre rtelmezettek.
A C++ programokban minden nvnek (azonostnak) van tpusa. Ez a tpus hatrozza meg,
milyen mveleteket lehet vgrehajtani a nven (azaz az egyeden, amelyre a nv hivatko-
zik) s ezek a mveletek mit jelentenek. Pldul a
float x; // x lebegpontos vltoz
int y = 7; // y egsz tpus vltoz, kezdrtke 7
float f(int); // f egsz paramtert vr s lebegpontos szmot visszaad fggvny
deklarcik mr rtelmess teszik a fenti pldt. Mivel y-t int-knt adtuk meg, rtkl lehet
adni, hasznlni lehet aritmetikai kifejezsekben stb. Msfell f-et olyan fggvnyknt hatroz-
tuk meg, amelynek egy int paramtere van, gy meg lehet hvni a megfelel paramterrel.
Ez a fejezet az alapvet tpusokat (4.1.1) s deklarcikat (4.9) mutatja be. A pldk csak
a nyelv tulajdonsgait szemlltetik, nem felttlenl vgeznek hasznos dolgokat. A terjedel-
mesebb s valsghbb pldk a ksbbi fejezetekben kerlnek sorra, amikor mr tbbet
ismertettnk a C++-bl. Ez a fejezet egyszeren csak azokat az alapelemeket rja le, ame-
lyekbl a C++ programok ltrehozhatk. Ismernnk kell ezeket az elemeket, a velk jr
elnevezseket s formai kvetelmnyeket, ahhoz is, hogy valdi C++ programot kszthes-
snk, de fleg azrt, hogy el tudjuk olvasni a msok ltal rt kdot. A tbbi fejezet megr-
tshez azonban nem szksges teljesen tltni ennek a fejezetnek minden apr rszlett.
Kvetkezskpp az olvas jobban teszi, ha csak tnzi, hogy megrtse a fontosabb fogal-
makat, s ksbb visszatr, hogy megrtse a rszleteket, amint szksges.
Alapok 90
4.1.1. Alaptpusok
A C++ rendelkezik azokkal az alaptpusokkal, amelyek megfelelnek a szmtgp leggya-
koribb trolsi egysgeinek s adattrolsi mdszereinek:
4.2 Logikai tpus (bool)
4.3 Karaktertpusok (mint a char)
4.4 Egsz tpusok (mint az int)
4.5 Lebegpontos tpusok (mint a double)
Tovbb a felhasznl megadhat:
4.8 felsorol tpusokat adott rtkhalmazok jellsre (enum)
s ltezik a
4.7 void tpus is, melyet az informci hinynak jelzsre hasznlunk.
Ezekbl a tpusokbl ms tpusokat is ltrehozhatunk. Ezek a kvetkezk:
5.1 Mutattpusok (mint az int*)
5.2 Tmbtpusok (mint a char[ ])
5.5 Referencia-tpusok (mint a double&)
5.7 Adatszerkezetek s osztlyok (10. fejezet)
A logikai, karakter- s egsz tpusokat egytt integrlis tpusoknak nevezzk, az integrlis
s lebegpontos tpusokat pedig kzsen aritmetikai tpusoknak. A felsorol tpusokat s
az osztlyokat (10. fejezet) felhasznli adattpusokknt emlegetjk, mert a felhasznlnak
kell azokat meghatroznia; elzetes bevezets nlkl nem llnak rendelkezsre, mint az
alaptpusok. A tbbi tpust beptett tpusnak nevezzk.
Az integrlis s lebegpontos tpusok tbbfajta mrettel adottak, lehetv tve a programo-
znak, hogy kivlaszthassa a felhasznlt tr nagysgt, a pontossgot, s a szmtsi rtk-
tartomnyt (4.6). Azt felttelezzk, hogy a szmtgp bjtokat biztost a karakterek tro-
lshoz, gpi szt az egszek trolsra s az azokkal val szmolsra, lteznek alkalmas
egyedek lebegpontos szmtsokhoz s cmek szmra, hogy hivatkozhassunk ezekre az
egyedekre. A C++ alaptpusai a mutatkkal s tmbkkel egytt az adott nyelvi megvals-
tstl fggetlenl biztostjk ezeket a gpszint fogalmakat a programoz szmra.
4. Tpusok s deklarcik 91
A legtbb programban a logikai rtkekhez egyszeren bool-t hasznlhatunk, karakterek-
hez char-t, egszekhez int-et, lebegpontos rtkekhez pedig double-t. A tbbi alaptpus
hatkonysgi s ms egyedi clokra hasznlatos, gy legjobb azokat elkerlni addig, amg
ilyen ignyek fel nem merlnek, de a rgi C s C++ kdok olvasshoz ismernnk kell ket.
4.2. Logikai tpusok
A logikai tpusoknak (bool), kt rtke lehet: true vagy false (igaz, illetve hamis). A logikai
tpusokat logikai mveletek eredmnynek kifejezsre hasznljuk:
void f(int a, int b)
{
bool b1 = a==b; // = rtkads, == egyenlsgvizsglat
// ...
}
Ha a s b rtke ugyanaz, akkor b1 igaz lesz, msklnben hamis.
A bool gyakran hasznlatos olyan fggvny visszatrsi rtkeknt, amely valamilyen felt-
telt ellenriz (prediktum):
bool is_open(File*);
bool greater(int a, int b) { return a>b; }
Egsz szmra alaktva a true rtke 1 lesz, a false- pedig 0. Ugyangy az egszek is logikai
tpusv alakthatk: a nem nulla egszek true, a 0 false logikai rtkk alakulnak:
bool b = 7; // bool(7) igaz, gy b igaz
int i = true; // int(true) rtke 1, gy i rtke 1
Aritmetikai s logikai kifejezsekben a logikai tpusok egssz alakulnak; az aritmetikai s
logikai egsz-mveletek az talaktott rtkeken hajtdnak vgre. Ha az eredmny vissza-
alakul logikai tpusv, a 0 false lesz, a nem nulla egszek pedig true rtket kapnak.
Alapok 92
void g()
{
bool a = true;
bool b = true;
bool x = a+b; // a+b rtke 2, gy x igaz
bool y = a|b; // a|b rtke 1, gy y igaz
}
Logikai tpusv mutatkat is alakthatunk (C.6.2.5). A nem nulla mutatk true, a nulla
mutatk false rtkek lesznek.
4.3. Karaktertpusok
A char tpus vltozk egy karaktert trolhatnak az adott nyelvi megvalsts karakterksz-
letbl:
char ch = 'a';
A char tpus ltalban 8 bites, gy 256 klnbz rtket trolhat. A karakterkszlet jellem-
zen az ISO-646 egy vltozata, pldul ASCII, gy a billentyzeten megjelen karaktereket
tartalmazza. Sok problma szrmazik abbl, hogy ezeket a karakterkszleteket csak rsz-
ben szabvnyostottk (C.3).
Jelentsen eltrnek azok a karakterkszletek, amelyek termszetes nyelveket tmogatnak,
s azok is, amelyek msflekppen tmogatjk ugyanazt a termszetes nyelvet. Itt azonban
csak az rdekel minket, hogy ezek a klnbsgek hogyan befolysoljk a C++ szablyait.
Ennl fontosabb krds, hogyan programozzunk tbbnyelv, tbb karakterkszletes kr-
nyezetben, ez azonban a knyv keretein tlmutat, br szmos helyen emltsre kerl (20.2,
21.7, C3.3, D).
Biztosan feltehet, hogy az adott C++-vltozat karakterkszlete tartalmazza a decimlis
szmjegyeket, az angol bc 26 betjt s nhny ltalnos rsjelet. Nem biztos, hogy egy
8 bites karakterkszletben nincs 127-nl tbb karakter (nhny karakterkszlet 255 karak-
tert biztost), hogy nincs tbb alfabetikus karakter, mint az angolban (a legtbb eurpai
nyelvben tbb van), hogy az bc beti sszefggek (az EBCDIC lyukat hagy az i s
a j kztt), vagy hogy minden karakter rendelkezsre ll, ami a C++ kd rshoz szks-
4. Tpusok s deklarcik 93
ges (nhny nemzeti karakterkszlet nem biztostja a { } [ ] | \ karaktereket, C.3.1). Ami-
kor csak lehetsges, nem szabad semmit feltteleznnk az objektumok brzolsrl s ez
az ltalnos szably a karakterekre is vonatkozik.
Minden karakternek van egy egsz rtke, a b- pldul az ASCII karakterkszletben 98.
me egy kis program, amely a begpelt karakter egsz rtkt mutatja meg:
#include <iostream>
int main()
{
char c;
std::cin >> c;
std::cout << "A(z) ' " << c << " ' rtke " << int(c) << '\n';
}
Az int(c) jells a c karakter egsz rtkt adja. Az a lehetsg, hogy karaktert egssz le-
het alaktani, felvet egy krdst: a char eljeles vagy eljel nlkli? A 8 bites bjton brzolt
256 rtket gy lehet rtelmezni, mint 0-tl 255-ig vagy -127-tl 127-ig terjed rtkeket.
Sajnos az adott fordtprogram dnti el, melyiket vlasztja egy sima char esetben (C.1,
C.3.4). A C++ azonban ad kt olyan tpust, amelyekre a krds biztosan megvlaszolhat:
a signed char-t (eljeles karakter), amely legalbb a -127 s127 kzti rtkeket kpes t-
rolni s az unsigned char (eljel nlkli karakter) tpust, amely legalbb 0-tl 255-ig tud r-
tkeket trolni. Szerencsre csak a 0-127 tartomnyon kvli rtkekben lehet klnbsg
s a leggyakoribb karakterek a tartomnyon bell vannak.
Azok a 0-127 tartomnyon tli rtkek, amelyeket egy sima char trol, nehezen felderthe-
t hordozhatsgi problmkat okozhatnak. Lsd mg a C.3.4-et arra az esetre, ha tbb-
fle char tpus szksges, vagy ha char tpus vltozkban szeretnnk egszeket trolni.
A nagyobb karakterkszletek pldul a Unicode karaktereinek trolsra a wchar_t ll
rendelkezsnkre, amely nll tpus. Mrete az adott C++-vltozattl fgg, de elg nagy
ahhoz, hogy a szksges legnagyobb karakterkszletet trolhassa (lsd 21.7 s C.3.3).
A klns nv mg a C-bl maradt meg. A C-ben a wchar_t egy typedef (4.9.7), vagyis t-
pus-lnv, nem pedig beptett tpus. Az _t toldalk a szabvnyos typedef-ektl val meg-
klnbztetst segti.
Jegyezzk meg, hogy a karaktertpusok integrlis tpusok (4.1.1), gy alkalmazhatak rjuk
az aritmetikai s logikai mveletek (6.2) is.
Alapok 94
4.3.1. Karakterliterlok
A karakterliterl, melyet gyakran karakterkonstansnak is hvnak, egy egyszeres idzjelek
kz zrt karakter, pldul 'a' s '0'. A karakterliterlok tpusa char. Valjban szimbolikus
konstansok (jelkpes llandk), melyek rtke annak a szmtgpnek a karakterkszlet-
ben lv karakter egsz rtke, amin a C++ program fut. Ha pldul ASCII karakterkszlet-
tel rendelkez szmtgpet hasznlunk, a '0' rtke 48 lesz. A program hordozhatsgt
javtja, ha decimlis jells helyett karakterliterlokat hasznlunk. Nhny karakternek szin-
tn van szabvnyos neve, ezek a \ fordtott perjelt hasznljk n. escape karakterknt. Pl-
dul a \n az j sort, a \t pedig a vzszintes tabultort (behzst) jelenti. Az escape karakte-
rekrl rszletesebben lsd C.3.2-t.
A szles karakterliterlok Lab alakak, ahol az egyszeres idzjelek kztt lv karakte-
rek szmt s jelentst az adott C++-megvalsts a wchar_t tpushoz igaztja, mivel a sz-
les karakterliterlok tpusa wchar_t.
4.4. Egsz tpusok
A char-hoz hasonlan az egsz tpusok is hromflk: sima int, signed int, s unsigned
int. Az egszek hrom mretben adottak: short int, sima int , illetve long int. Egy long int-
re lehet sima long-knt hivatkozni. Hasonlan, a short a short int, az unsigned az unsigned
int, a signed pedig a signed int szinonimja.
Az eljel nlkli (unsigned) egsz tpusok olyan felhasznlsra idelisak, amely gy kezeli
a trat, mint egy bittmbt. Szinte soha nem j tlet az eljel nlkli tpust hasznlni int he-
lyett, hogy egy vagy tbb bitet nyerjnk pozitv egszek trolshoz. Azok a prblkoz-
sok, amelyek gy ksrlik meg biztostani valamilyen rtk nem negatv voltt, hogy a vl-
tozt unsigned-knt adjk meg, ltalban meghisulnak a mgttes konverzis szablyok
miatt (C.6.1, C.6.2.1).
A sima char-ral ellenttben a sima int-ek mindig eljelesek. A signed int tpusok csak vil-
gosabban kifejezett szinonimi a nekik megfelel sima int tpusoknak.
4. Tpusok s deklarcik 95
4.4.1. Egsz literlok
Az egsz literlok ngyfle alakban fordulnak el: decimlis, oktlis, hexadecimlis s
karakterliterlknt. A decimlis literlok a leginkbb hasznlatosak s gy nznek ki, ahogy
elvrjuk tlk:
7 1234 976 12345678901234567890
A fordtprogramnak figyelmeztetnie kell olyan literlok esetben, amelyek tl hosszak az
brzolshoz. A nullval kezdd s x-szel folytatd (0x) literlok hexadecimlis (16-os
szmrendszerbeli) szmok. Ha a literl nullval kezddik s szmjeggyel folytatdik, okt-
lis (8-as szmrendszerbeli) szmrl van sz:
decimlis: 2 63 83
oktlis: 0 02 077 0123
hexadecimlis: 0x0 0x2 0x3f 0x53
Az a, b, c, d, e s f betk, illetve nagybets megfelelik rendre 10-et, 11-et, 12-t, 13-at, 14-
et s 15-t jelentenek. Az oktlis s hexadecimlis jells leginkbb bitmintk kifejezsnl
hasznos. Meglepetseket okozhat, ha ezekkel a jellsekkel valdi szmokat fejeznk ki.
Egy olyan gpen pldul, ahol az int egy kettes komplemens 16 bites egszknt van b-
rzolva, 0xffff a -1 negatv decimlis szm lesz. Ha tbb bitet hasznltunk volna az egsz
brzolsra, akkor ez 65 535 lett volna.
Az U uttag hasznlatval eljel nlkli (unsigned) literlokat adhatunk meg. Hasonlan, az
L uttag hasznlatos a long literlokhoz. Pldul 3 egy int, 3U egy unsigned int s 3L egy
long int. Ha nincs megadva uttag, a fordt egy olyan egsz literlt ad, amelynek tpusa
megfelel az rtknek s a megvalsts egsz-mreteinek (C.4).
J tlet korltozni a nem maguktl rtetd llandk hasznlatt nhny, megjegyzsekkel
megfelelen elltott const (5.4) vagy felsorol tpus (4.8) kezdeti rtkadsra.
4.5. Lebegpontos tpusok
A lebegpontos tpusok lebegpontos (vals) szmokat brzolnak. Az egszekhez hason-
lan ezek is hromfajta mretek lehetnek: float (egyszeres pontossg), double (ktszeres
pontossg), s long double (kiterjesztett pontossg).
Alapok 96
Az egyszeres, ktszeres s kiterjesztett pontossg pontos jelentse az adott C++-vltozattl
fgg. A megfelel pontossg kivlasztsa egy olyan problmnl, ahol fontos a vlaszts,
a lebegpontos szmtsok mly megrtst kveteli meg. Ha nem rtnk a lebegpontos
aritmetikhoz, krjnk tancsot, sznjunk idt a megtanulsra, vagy hasznljunk double-t
s remljk a legjobbakat.
4.5.1. Lebegpontos literlok
Alaprtelmezs szerint a lebegpontos literlok double tpusak. A fordtnak itt is figyel-
meztetnie kell, ha a lebegpontos literlok az brzolshoz kpest tl nagyok. me nhny
lebegpontos literl:
1.23 .23 0.23 1. 1.0 1.2e10 1.23e-15
Jegyezzk meg, hogy szkz nem fordulhat el egy lebegpontos literl kzepn.
A 65.43 e-21 pldul nem lebegpontos literl, hanem ngy klnll nyelvi egysg (ami
formai hibt okoz):
65.43 e - 21
Ha float tpus lebegpontos literlt akarunk megadni, akkor azt az f vagy F uttag haszn-
latval tehetjk meg:
3.14159265f 2.0f 2.997925F 2.9e-3f
Ha long double tpus lebegpontos literlt szeretnnk megadni, hasznljuk az l vagy L ut-
tagot:
3.14159265L 2.0L 2.997925L 2.9e-3L
4.6. Mretek
A C++ alaptpusainak nhny jellemzje, mint pldul az int mrete, a C++ adott megval-
ststl fgg (C.2). Rmutatok ezekre a fggsgekre s gyakran ajnlom, hogy kerljk
ket vagy tegynk lpseket annak rdekben, hogy hatsukat cskkentsk. Mirt kellene
ezzel foglalkozni? Azok, akik klnbz rendszereken programoznak vagy tbbfle ford-
4. Tpusok s deklarcik 97
tt hasznlnak, knytelenek trdni ezzel, mert ha nem tennk, rknyszerlnnek arra,
hogy idt pazaroljanak nehezen megfoghat programhibk megtallsra s kijavtsra.
Azok, akik azt lltjk, hogy nem rdekli ket a hordozhatsg, ltalban azrt teszik ezt,
mert csak egy rendszert hasznlnak s gy rzik, megengedhetik maguknak azt a hozzl-
lst, miszerint a nyelv az, amit a fordtm megvalst. Ez beszklt ltsmd. Ha egy prog-
ram sikeres, akkor valszn, hogy tviszik ms rendszerre, s valakinek meg kell tallnia
s ki kell javtania a megvalsts sajtossgaibl add problmkat. A programokat to-
vbb gyakran jra kell fordtani ms fordtkkal ugyanarra a rendszerre s mg a kedvenc
fordtnk ksbbi vltozata is mskppen csinlhat nhny dolgot, mint a mostani. Sokkal
egyszerbb ismerni s korltozni az adott fordt hasznlatnak hatst, amikor egy prog-
ramot megrnunk, mint megprblni ksbb kibogozni a problmt.
A megvalstsbl ered nyelvi sajtossgok hatst viszonylag knny korltozni, a rend-
szerfgg knyvtrakt azonban sokkal nehezebb. Az egyik mdszer az lehet, hogy lehe-
tleg csak a standard knyvtr elemeit hasznljuk.
Annak, hogy tbb egsz, tbb eljel nlkli, s tbb lebegpontos tpus van, az az oka,
hogy ez lehetsget ad a programoznak, hogy a hardver jellemzit megfelelen kihasz-
nlhassa. Sok gpen jelents klnbsgek vannak a memriaignyben, a memria hozz-
frsi idejben, s a tbbfajta alaptpussal val szmolsi sebessgben. Ha ismerjk a g-
pet, ltalban knnyen kivlaszthatjuk pldul a megfelel egsz tpust egy adott vltoz
szmra, igazn hordozhat kdot rni azonban sokkal nehezebb.
A C++ objektumainak mrete mindig a char mretnek tbbszrse, gy a char mrete 1.
Egy objektum mrett a sizeof opertorral kaphatjuk meg (6.2). Az alaptpusok mretre
vonatkozan a kvetkezk garantltak:
1 sizeof(char) sizeof(short) sizeof(int) sizeof(long)
1 sizeof(bool) sizeof(long)
sizeof(char) sizeof(wchar_t) sizeof(long)
sizeof(float) sizeof(double) sizeof(long double)
sizeof(N) sizeof(signed N) sizeof(unsigned N)
A fentiekben N lehet char, short int, int vagy long int, tovbb biztostott, hogy a char leg-
albb 8, a short legalbb 16, a long pedig legalbb 32 bites. A char a gp karakterkszlet-
bl egy karaktert trolhat. Az albbi bra az alaptpusok egy lehetsges halmazt s egy
minta-karakterlncot mutat:
Alapok 98
Ugyanilyen mretarnyban (0,5 cm egy bjt) egy megabjt memria krlbell t kilom-
ternyire lgna ki a jobb oldalon.
A char tpust az adott nyelvi vltozatnak gy kell megvlasztania, hogy a karakterek trol-
sra s kezelsre egy adott szmtgpen a legmegfelelbb legyen; ez jellemzen egy 8 bi-
tes bjt. Hasonlan, az int tpusnak a legmegfelelbbnek kell lennie az egszek trolsra s
kezelsre; ez ltalban egy 4 bjtos (32 bites) gpi sz. Nem blcs dolog tbbet felttelez-
ni. Pldul vannak olyan gpek, ahol a char 32 bites. Ha szksgnk van r, az adott C++-
vltozat egyedi tulajdonsgait megtallhatjuk a <limits> fejllomnyban (22.2). Pldul:
#include <limits>
#include <iostream>
int main()
{
std::cout << "A legnagyobb lebegpontos szm == " << std::numeric_limits<float>::max()
<< ", a char eljeles == " << std::numeric_limits<char>::is_signed << '\n';
}
Az alaptpusok rtkadsokban s kifejezsekben szabadon prosthatk. Ahol lehetsges,
az rtkek gy alaktdnak t, hogy ne legyen adatveszts (C.6).
Ha v rtk pontosan brzolhat egy T tpus vltozban, akkor v rtk T tpusv alakt-
sa megrzi az rtket s nincs problma. Legjobb, ha elkerljk azokat az eseteket, amikor
a konverzik nem rtkrzk (C.6.2.6).
Nagyobb programok ksztshez az automatikus konverzikat rszletesebben meg kell r-
tennk, fleg azrt, hogy kpesek legynk rtelmezni a msok ltal rt kdot, a kvetkez
fejezetek olvasshoz ugyanakkor ez nem szksges.
4. Tpusok s deklarcik 99
char:
bool:
short:
int:
int*:
double:
char[14]:
'a'
1
756
100000000
&c1
1234567e34
Hello, world!\0
4.7. Void
A void formja alapjn alaptpus, de csak egy bonyolultabb tpus rszeknt lehet hasznlni;
nincsenek void tpus objektumok. Vagy arra hasznljuk, hogy meghatrozzuk, hogy egy
fggvny nem ad vissza rtket, vagy akkor, amikor egy mutat ismeretlen tpus objek-
tumra mutat:
void x; // hiba: nincsenek void objektumok
void f(); // az f fggvny nem ad vissza rtket (7.3)
void* pv; // ismeretlen tpus objektumra hivatkoz mutat (5.6)
Amikor egy fggvnyt bevezetnk, meg kell hatrozni visszatrsi rtknek tpust is. Lo-
gikailag elvrhat lenne, hogy a visszatrsi tpus elhagysval jelezzk, a fggvny nem
ad vissza rtket. Ez viszont a nyelvtant (A fggelk) kevsb szablyoss tenn s tkz-
ne a C-beli gyakorlattal. Kvetkezskppen a void ltszlagos visszatrsi tpus-knt
hasznlatos, annak jellsre, hogy a fggvny nem ad vissza rtket.
4.8. Felsorol tpusok
A felsorol tpus (enumeration) olyan tpus, amely felhasznl ltal meghatrozott rtke-
ket tartalmaz. Meghatrozsa utn az egsz tpushoz hasonlan hasznlhat. A felsorol t-
pusok tagjaiknt nvvel rendelkez egsz konstansokat adhatunk meg. Az albbi kd pl-
dul hrom egsz llandt ad meg ezeket felsorol konstansoknak nevezzk s rtke-
ket rendel hozzjuk:
enum { ASM, AUTO, BREAK };
Alaprtelmezs szerint az llandk 0-tl nvekven kapnak rtkeket, gy ASM==0,
AUTO==1, BREAK==2. A felsorol tpusnak lehet neve is:
enum keyword { ASM, AUTO, BREAK };
Minden felsorols nll tpus. A felsorol konstansok tpusa a felsorolsi tpus lesz.
Az AUTO pldul keyword tpus.
Alapok 100
Ha keyword tpus vltozt adunk meg sima int helyett, mind a felhasznlnak, mind a for-
dtnak utalunk a vltoz tervezett hasznlatra:
void f(keyword key)
{
switch (key) {
case ASM:
// valamit csinlunk
break;
case BREAK:
// valamit csinlunk
break;
}
}
A fordt figyelmeztetst adhat, mert a hrom keyword tpus rtkbl csak kettt kezeltnk.
A felsorol konstans a kezdeti rtkadskor integrlis tpus (4.1.1) konstans kifejezssel
(C.5.) is megadhat. A felsorol tpus rtkhalmaza sszes tagjnak rtkt tartalmazza,
felkerektve a 2 legkzelebbi, azoknl nagyobb hatvnynl eggyel kisebb rtkig. Ha
a legkisebb felsorol konstans nem negatv, az rtkhalmaz 0-val kezddik, ha negatv, a 2
legkzelebbi, a tagoknl kisebb negatv hatvnyval. Ez a szably azt a legkisebb bitmezt
adja meg, amely tartalmazhatja a felsorol konstansok rtkt. Pldul:
enum e1 { dark, light }; // tartomny: 0:1
enum e2 { a = 3, b = 9 }; // tartomny: 0:15
enum e3 { min = -10, max = 1000000 }; // tartomny: -1048576:1048575
Egy integrlis tpus rtkt meghatrozott mdon felsorol tpusv alakthatjuk. Ha az r-
tk nem esik a felsorol tpus rtkhalmazba, a konverzi eredmnye nem meghatrozott:
enum flag { x=1, y=2, z=4, e=8 }; // tartomny: 0:15
flag f1 = 5; // tpushiba: 5 nem flag tpus
flag f2 = flag(5); // rendben: flag(5) flag tpus s annak tartomnyn belli
flag f3 = flag(z|e); // rendben: flag(12) flag tpus s annak tartomnyn belli
flag f4 = flag(99); // meghatrozatlan: 99 nem esik a flag tartomnyn bellre
Az utols rtkads mutatja, mirt nincs automatikus konverzi egszrl felsorol tpusra;
a legtbb egsz rtk ugyanis nem brzolhat egy adott felsorol tpusban.
4. Tpusok s deklarcik 101
A felsorol tpusok rtkhalmaznak fogalma klnbzik a Pascal nyelvcsaldba tartoz
nyelvek felsorol tpusainak fogalmtl. A C-ben s a C++-ban azonban hossz hagyom-
nya van azoknak a bitkezel mveleteknek, amelyek arra ptenek, hogy a felsorol tpu-
sok tagjain kvli rtkek jl krlhatroltak.
A felsorol tpusok sizeof-ja egy olyan integrlis tpus sizeof-ja, amely kpes a felsorol t-
pus rtkhalmazt trolni s nem nagyobb sizeof(int)-nl, hiszen akkor nem lehetne int-
knt vagy unsigned int-knt brzolni. Pldul sizeof(e1) lehet 1 vagy taln 4, de nem lehet
8 egy olyan gpen, ahol sizeof(int)==4.
Alaprtelmezs szerint a felsorol tpusok aritmetikai mveletek esetben egssz alaktd-
nak (6.2). A felsorol tpus felhasznli tpus, gy a felhasznlk a felsorolsra sajt mve-
leteket adhatnak meg, pldul a ++ s << opertorokkal (11.2.3).
4.9. Deklarcik
A C++ programokban a neveket (azonostkat) hasznlat eltt be kell vezetnnk, azaz meg
kell hatroznunk tpusukat, hogy megmondjuk a fordtnak, a nv mifle egyedre hivatko-
zik. A deklarcik sokflesgt a kvetkez pldk szemlltetik:
char ch;
string s;
int count = 1;
const double pi = 3.1415926535897932385;
extern int error_number;
char* name = "Natasa";
char* season[ ] = { "tavasz", "nyr", "sz", "tl" };
struct Date { int d, m, y; };
int day(Date* p) { return p->d; }
double sqrt(double);
template<class T> T abs(T a) { return a<0 ? -a : a; }
typedef complex<short> Point;
struct User;
enum Beer { Carlsberg, Tuborg, Thor };
namespace NS { int a; }
Alapok 102
Amint a pldkbl lthat, a deklarci tbbet is jelenthet annl, mint hogy egyszeren
egy nevet kapcsol ssze a nv tpusval. A fenti deklarcik tbbsge definci is, azaz
meg is hatrozza azt az egyedet, amelyre a nv hivatkozik. A ch esetben pldul ez az
egyed a megfelel memriaterlet, amelyet vltozknt hasznlunk (vagyis ezt a mem-
riaterletet fogjuk lefoglalni), a day-nl a meghatrozott fggvny, a pi llandnl
a 3.1415926535897932385 rtk, a Date-nl egy j tpus. A Point esetben az egyed
a complex<short> tpus, gy a Point a complex<short> szinonimja lesz. A fenti deklarci-
k kzl csak a
double sqrt(double);
extern int error_number;
struct User;
deklarcik nem defincik is egyben: azaz mshol kell definilni (meghatrozni) azokat az
egyedeket, amelyekre hivatkoznak. Az sqrt fggvny kdjt (trzst) ms deklarcikkal
kell meghatrozni, az int tpus error_number vltoz szmra az error_number egy m-
sik deklarcijnak kell lefoglalnia a memrit, s a User tpus egy msik deklarcijnak
kell megadnia, hogy a tpus hogy nzzen ki. Pldul:
double sqrt(double d) { /* ... */ }
int error_number = 1;
struct User { /* ... */ };
A C++ programokban minden nv szmra mindig pontosan egy definci (meghatrozs)
ltezhet (az #include hatsait lsd 9.2.3-ban). Ugyanakkor a nevet tbbszr is
deklarlhatunk (bevezethetjk). Egy egyed minden deklarcija meg kell, hogy egyezzen
a hivatkozott egyed tpusban. gy a kvetkez rszletben kt hiba van:
int count;
int count; // hiba: jbli definci
extern int error_number;
extern short error_number; // hiba: nem megfelel tpus
A kvetkezben viszont egy sincs (az extern hasznlatrl lsd 9.2):
extern int error_number;
extern int error_number;
4. Tpusok s deklarcik 103
Nhny definci valamilyen rtket is meghatroz a megadott egyedeknek:
struct Date { int d, m, y; };
typedef complex<short> Point;
int day(Date* p) { return p->d; }
const double pi = 3.1415926535897932385;
Tpusok, sablonok, fggvnyek s llandk esetben ez az rtk nem vltozik. Nem
konstans adattpusok esetben a kezdeti rtket ksbb mdosthatjuk:
void f()
{
int count = 1;
char* name = "Bjarne";
// ...
count = 2;
name = "Marian";
}
A defincik kzl csak az albbi nem hatroz meg rtket:
char ch;
string s;
(Arrl, hogy hogyan s mikor kap egy vltoz alaprtelmezett rtket, lsd 4.9.5-t s
10.4.2-t.) Minden deklarci, amely rtket hatroz meg, egyben defincinak is minsl.
4.9.1. A deklarcik szerkezete
A deklarcik ngy rszbl llnak: egy nem ktelez minstbl, egy alaptpusbl, egy
deklartorbl, s egy szintn nem ktelez kezdrtk-ad kifejezsbl. A fggvny- s
nvtr-meghatrozsokat kivve a deklarci pontosvesszre vgzdik:
char* kings[ ] = { "Antignusz", "Szeleukusz", "Ptolemaiosz" };
Itt az alaptpus char, a deklartor a *kings[ ], a kezdrtk-ad rsz pedig az ={}.
A minst (specifier) egy kulcssz, mint a virtual (2.5.5, 12.2.6) s az extern (9.2), s
a bevezetett elem nhny, nem a tpusra jellemz tulajdonsgt hatrozza meg. A deklar-
tor (declarator) egy nvbl s nhny nem ktelez opertorbl ll. A leggyakoribb dekla-
rtor-opertorok a kvetkezk (A.7.1):
Alapok 104
* mutat eltag
*const konstans mutat eltag
& referencia eltag
[ ] tmb uttag
() fggvny uttag
Hasznlatuk egyszer lenne, ha mindegyikk eltagknt (prefix) vagy mindegyikk uttag-
knt (postfix) hasznlt opertor volna. A *, a [ ] s a () opertorokat azonban arra tervez-
tk, hogy kifejezsekben is hasznlhatk legyenek (6.2), gy a * eltag-, a [ ] s a () pedig
uttag opertorok. Az uttagknt hasznlt opertorok tbb megktssel jrnak, mint az el-
tagknt hasznltak. Kvetkezskppen a *kings[ ] egy valamire hivatkoz mutatkbl ll
vektor, s zrjeleket kell hasznlnunk, ha olyasmit akarunk kifejezni, mint fggvnyre
hivatkoz mutat (lsd az 5.1 pldit). Teljes rszletessggel lsd a nyelvtant az A fg-
gelkben.
Jegyezzk meg, hogy a tpus nem hagyhat el a deklarcibl:
const c = 7; // hiba: nincs tpus
gt(int a, int b) { return (a>b) ? a : b; } // hiba: nincs visszatrsi tpus
unsigned ui; // rendben: 'unsigned' jelentse 'unsigned int'
long li; // rendben: 'long' jelentse 'long int'
A szabvnyos C++ ebben eltr a C s a C++ rgebbi vltozataitl, amelyek megengedtk az
els kt pldt, azt felttelezve, hogy a tpus int, ha nincs tpus megadva (B.2). Ez az imp-
licit int szably sok flrerts s nehezen megfoghat hiba forrsa volt.
4.9.2. Tbb nv bevezetse
Egyetlen deklarciban tbb nevet is megadhatunk. A deklarci ekkor vesszvel elvlasz-
tott deklarcik listjt tartalmazza. Kt egszet pldul gy vezethetnk be:
int x, y; // int x s int y;
Jegyezzk meg, hogy az opertorok csak egyes nevekre vonatkoznak, az ugyanabban
a deklarciban szerepl tovbbi nevekre nem:
int* p, y; // int* p s int y, NEM int* y
int x, *q; // int x s int* q
int v[10], *pv; // int v[10] s int* pv
A fentihez hasonl szerkezetek rontjk a program olvashatsgt, ezrt kerlendk.
4. Tpusok s deklarcik 105
4.9.3. Nevek
A nv (azonost) betk s szmok sorozatbl ll. Az els karakternek betnek kell len-
nie. Az _ (alhzs) karaktert betnek tekintjk. A C++ nem korltozza a nvben hasznl-
hat karakterek szmt. A fordtprogram rja azonban a megvalsts egyes rszeire nincs
befolyssal (konkrtan a szerkesztprogramra, a linker-re), ez pedig hatrokat szab.
Nhny futsi idej krnyezet ugyancsak szksgess teszi, hogy kibvtsk vagy megszo-
rtsuk az azonostban elfogadhat karakterkszletet. A bvtsek (pldul a $ engedlye-
zse egy nvben) nem hordozhat programokat eredmnyeznek. A C++ kulcsszavai (A
fggelk), mint a new s az int, nem hasznlhatk felhasznli egyedek neveknt. Pldk
a nevekre:
hello ez_egy_szokatlanul_hossz_nv
DEFINED foO bAr u_name LoPatko
var0 var1 CLASS _class ___
s nhny plda olyan karaktersorozatokra, amelyek nem hasznlhatk azonostknt:
012 egy bolond $sys class 3var
fizetes.esedekes foo~bar .name if
Az alhzssal kezdd nevek a nyelvi megvalsts s a futsi idej krnyezet egyedi esz-
kzei szmra vannak fenntartva, gy ezeket nem szabadna hasznlni alkalmazi progra-
mokban.
Amikor a fordt olvassa a programot, mindig a leghosszabb olyan sorozatot keresi, amely
kiadhat egy nevet. gy a var10 s nem a var nv (amit a 10-es szm kvet). Hasonlan, az
elseif is egy nv, nem pedig az else, amit az if kulcssz kvet.
A kis- s nagybetket a nyelv megklnbzteti, gy a Count s a count klnbz nevek,
de nem blcs dolog olyan neveket vlasztani, amelyek csak a kezdbetben trnek el.
A legjobb elkerlni azokat a neveket, amelyek csak kicsit klnbznek. Pldul a nagybe-
ts o (O) s a nulla (0) nehezen megklnbztethet, a kis L (l) s az egyes (1) szintn. K-
vetkezskppen azonostnak a l0, lO, l1 s ll nem szerencss vlaszts.
A nagy hatkr nevek lehetleg hosszak s rthetek legyenek, mint vector,
Window_with_border, s Department_number. A kd viszont rthetbb lesz, ha a kis hat-
krben hasznlt neveknek rvid, hagyomnyos stlus nevk van, mint x, i s p. Az osztlyok
(10. fejezet) s a nvterek (8.2) hasznlhatk arra, hogy a hatkrk kicsik maradjanak.
Hasznos dolog viszonylag rvidnek hagyni a gyakran hasznlt neveket, az igazn hosszakat
Alapok 106
pedig megtartani a kevsb gyakran hasznlatos egyedeknek. Vlasszuk meg gy a neveket,
hogy az egyed jelentsre s ne annak megvalstsra utaljanak. A phone_book (telefon-
knyv) pldul jobb, mint a number_list (szmok listja), mg akkor is, ha a telefonszmokat
listban (3.7) troljuk. A j nevek megvlasztsa is egyfajta mvszet.
Prbljunk kvetkezetes elnevezsi stlust fenntartani. Pldul rjuk nagy kezdbetvel
a nem standard knyvtrbeli felhasznli tpusokat s kisbetvel azokat a neveket, amelyek
nem tpusnevek (pldul Shape s current_token). Tovbb hasznljunk csupa nagybett
makrk esetben (ha makrkat kell hasznlnunk, pldul HACK) s hasznljunk alhzst,
ha az azonostban szt akarjuk vlasztani a szavakat. Akrhogy is, nehz elrni a kvetke-
zetessget, mivel a programokat ltalban klnbz forrsokbl vett rszletek alkotjk s
szmos klnbz sszer stlus hasznlatos bennk. Legynk kvetkezetesek a rvidt-
sek s betszavak hasznlatban is.
4.9.4. Hatkrk
A deklarci a megadott nevet egy hatkrbe (scope) vezeti be, azaz a nevet csak a prog-
ramszveg meghatrozott rszben lehet hasznlni. A fggvnyeken bell megadott nevek
esetben (ezeket gyakran loklis vagy helyi nvnek hvjuk) ez a hatkr a deklarci hely-
tl annak a blokknak a vgig tart, amelyben a deklarci szerepel. A blokk olyan kdrsz,
amelyet a { } kapcsos zrjelek hatrolnak.
Egy nevet globlisnak neveznk, ha fggvnyen, osztlyon (10. fejezet) vagy nvtren
(8.2) kvl bevezetett. A globlis nevek hatkre a bevezets pontjtl annak a fjlnak
a vgig terjed, amelyben a deklarci szerepel. A blokkokban szerepl nvdeklarcik
a krlvev blokkban lv deklarcikat s a globlis neveket elfedhetik, azaz egy nevet
jra meg lehet adni gy, hogy egy msik egyedre hivatkozzon egy blokkon bell. A blokk-
bl val kilps utn a nv visszanyeri elz jelentst:
int x; // globlis x
void f()
{
int x; // a loklis x elfedi a globlis x-et
x = 1; // rtkads a loklis x-nek
{
int x; // elfedi az els loklis x-et
x = 2; // rtkads a msodik loklis x-nek
}
4. Tpusok s deklarcik 107
x = 3; // rtkads az els loklis x-nek
}
int* p = &x; // a globlis x cmnek felhasznlsa
A nevek elfedse elkerlhetetlen nagy programok rsakor. A kdot olvasnak azonban
knnyen elkerli a figyelmt, hogy egy nv tbbszr szerepel. Mivel az ilyen hibk viszony-
lag ritkn fordulnak el, nagyon nehezen lehet azokat megtallni. Kvetkezskppen
a nvelfedsek szmt a lehet legkisebbre kell cskkenteni. Ha valaki olyan neveket hasz-
nl globlis vltozknt vagy egy hosszabb fggvny loklis vltozjaknt, mint i s x, ak-
kor maga keresi a bajt.
Az elfedett globlis nevekre a :: hatkrjelz hasznlatval hivatkozhatunk:
int x;
void f2()
{
int x = 1; // a globlis x elfedse
::x = 2; // rtkads a globlis x-nek
x = 2; // rtkads a loklis x-nek
// ...
}
Elfedett loklis nv hasznlatra nincs md.
A nv hatkre a nv deklarcijnak pontjtl kezddik; azaz a teljes deklartor utn s
a kezdeti rtk(ek)et megad rsz eltt. Ez azt jelenti, hogy egy nevet sajt kezdrtknek
meghatrozsra is hasznlhatunk:
int x;
void f3()
{
int x = x; // perverz: kezdeti rtkads x-nek sajt maga (nem meghatrozott) rtkvel
}
Ez nem tiltott, csak butasg. Egy j fordtprogram figyelmeztetst ad, ha egy vltozt az-
eltt hasznlunk, mieltt rtkt belltottuk volna (lsd 5.9[9]).
Alapok 108
Egy nvvel egy blokkban kt klnbz objektumra a :: opertor hasznlata nlkl is hi-
vatkozhatunk:
int x = 11;
void f4() // perverz:
{
int y = x; // a globlis x felhasznlsa, y = 11
int x = 22;
y = x; // a loklis x felhasznlsa, y = 22
}
A fggvnyparamterek neveit gy tekintjk, mint ha a fggvny legkls blokkjban len-
nnek megadva, gy az albbi hibs, mert x-et ugyanabban a hatkrben ktszer adtuk
meg:
void f5(int x)
{
int x; // hiba
}
Ilyen hiba gyakran elfordul, rdemes figyelnnk r.
4.9.5. Kezdeti rtkads
Ha egy objektumhoz kezdrtk-ad kifejezst adunk meg, akkor ez hatrozza meg az ob-
jektum kezdeti rtkt. Ha nincs megadva ilyen, a globlis (4.9.4), nvtr (8.2), vagy helyi
statikus objektumok (7.1.2, 10.2.4) (melyeket egyttesen statikus objektumoknak neve-
znk) a megfelel tpus 0 rtkt kapjk kezdrtkl:
int a; // jelentse "int a = 0;''
double d; // jelentse "double d = 0.0;''
A loklis vltozknak (ezeket nha automatikus objektumoknak nevezzk) s a szabad tr-
ban ltrehozott objektumoknak (dinamikus vagy heap objektumok) alaprtelmezs sze-
rint nincs kezdrtkk:
void f()
{
int x; // x rtke nem meghatrozott
// ...
}
4. Tpusok s deklarcik 109
A tmbk s struktrk tagjai alaprtelmezs szerint kapnak kezdrtket, fggetlenl at-
tl, hogy az adott szerkezet statikus-e vagy sem. A felhasznli tpusokhoz magunk is meg-
adhatunk alaprtelmezett kezdrtket (10.4.2).
A bonyolultabb objektumoknak egynl tbb rtkre van szksgk a kezdeti rtkadshoz.
A tmbk s struktrk C tpus feltltsekor (5.2.1, 5.7) ezt egy { } zrjelek ltal hat-
rolt listval rhetjk el. A konstruktorral rendelkez felhasznli tpusoknl a fggvny st-
lus paramterlistk hasznlatosak (2.5.2, 10.2.3). Jegyezzk meg, hogy a deklarcikban
szerepl () res zrjelek jelentse mindig fggvny:
int a[ ] = { 1, 2 }; // tmb kezdeti rtkadsa
Point z(1,2); // fggvny stlus kezdeti rtkads (konstruktorral)
int f(); // fggvny-deklarci
4.9.6. Objektumok s balrtkek
Nvvel nem rendelkez vltozkat is lefoglalhatunk s hasznlhatunk, s rtket is adha-
tunk nekik furcsa kifejezsekkel (pl. *p[a+10]=7). Kvetkezskpp el kellene neveznnk
azt, hogy valami a memriban. Ez az objektum legegyszerbb s legalapvetbb fogalma.
Azaz, az objektum egy folytonos trterlet; a bal oldali rtk (balrtk) pedig egy olyan
kifejezs, amely egy objektumra hivatkozik. A balrtk (lvalue) szt eredetileg arra alkot-
tk, hogy a kvetkezt jelentse: valami, ami egy rtkads bal oldaln szerepelhet. Nem
minden balrtk lehet azonban az rtkads bal oldaln, egy balrtk hivatkozhat llandra
(const) is (5.5). A nem const-knt megadott balrtket szoks mdosthat balrtknek
(modifiable lvalue) is nevezni. Az objektumnak ezt az egyszer s alacsony szint fogalmt
nem szabad sszetveszteni az osztlyobjektumok s tbbalak (polimorf tpus) objektu-
mok (15.4.3) fogalmval.
Hacsak a programoz mskpp nem rendelkezik (7.1.2, 10.4.8), egy fggvnyben beve-
zetett vltoz akkor jn ltre, amikor defincijhoz rkeznk, s akkor sznik meg, ami-
kor a neve a hatkrn kvlre kerl (10.4.4). Az ilyen objektumokat automatikus objek-
tumoknak nevezzk. A globlis s nvtr-hatkrben bevezetett objektumok s a fggv-
nyekben vagy osztlyokban megadott static objektumok (csak) egyszer jnnek ltre s
kapnak kezdeti rtket, s a program befejeztig lnek(10.4.9). Az ilyen objektumokat
statikus objektumoknak nevezzk. A tmbelemeknek s a nem statikus struktrk vagy
osztlyok tagjainak az lettartamt az az objektum hatrozza meg, amelynek rszei. A new
s delete opertorokkal olyan objektumok hozhatk ltre, amelyek lettartama kzvetlenl
szablyozhat (6.2.6).
Alapok 110
4.9.7. Typedef
Az a deklarci, amit a typedef kulcssz elz meg, a tpus szmra j nevet hoz ltre, nem
egy adott tpus vltozt:
typedef char* Pchar;
Pchar p1, p2; // p1 s p2 tpusa char*
char* p3 = p1;
Az gy megadott, gyakran typedef-nek (ltpus) nevezett nv knyelmes rvidts lehet egy
nehezen hasznlhat nv helyett. Pldul az unsigned char tlsgosan hossz az igazn
gyakori hasznlatra, ezrt megadhatjuk a szinonimjt, az uchar-t:
typedef unsigned char uchar;
A typedef msik hasznlata az, hogy egy tpushoz val kzvetlen hozzfrst egy helyre
korltozunk:
typedef int int32;
typedef short int16;
Ha most az int32-t hasznljuk, amikor egy viszonylag nagy egszre van szksgnk, prog-
ramunkat tvihetjk egy olyan gpre, ahol a sizeof(int) 2-vel egyenl, gy, hogy a kdban
egyszer szerepl int32-t most mskpp hatrozzuk meg:
typedef long int32;
Vgezetl, a typedef-ek inkbb ms tpusok szinonimi, mint nll tpusok. Kvetkezs-
kppen a typedef-ek szabadon felcserlhetk azokkal a tpusokkal, melyeknek szinonimi.
Azok, akik ugyanolyan jelentssel vagy brzolssal rendelkez nll tpusokat szeretn-
nek, hasznljk a felsorol tpusokat (4.8) vagy az osztlyokat (10. fejezet).
4. Tpusok s deklarcik 111
4.10. Tancsok
[1] A hatkrk legyenek kicsik. 4.9.4.
[2] Ne hasznljuk ugyanazt a nevet egy hatkrben s az azt krlvev hatkrben
is. 4.9.2.
[3] Deklarcinknt (csak) egy nevet adjunk meg. 4.9.3.
[4] A gyakori s helyi nevek legyenek rvidek, a nem helyi s ritkn hasznlt ne-
vek hosszabbak. 4.9.3.
[5] Kerljk a hasonlnak ltsz neveket. 4.9.3.
[6] Elnevezsi stlusunk legyen kvetkezetes. 4.9.3.
[7] Figyeljnk arra, hogy a nvvlaszts inkbb a jelentsre, mintsem a megvals-
tsra utaljon. 4.9.3.
[8] Ha a beptett tpus, amelyet egy rtk brzolsra hasznlunk, megvltozhat,
hasznljunk typedef-et, gy a tpus szmra beszdes nevet adhatunk. 4.9.7
[9] A typedef-ekkel tpusok szinonimit adjuk meg; j tpusok definilsra hasznl-
junk felsorol tpusokat s osztlyokat. 4.9.7.
[10] Emlkezznk arra, hogy minden deklarciban szksges a tpus megadsa
(nincs implicit int). 4.9.1.
[11] Kerljk a karakterek szmrtkvel kapcsolatos szksgtelen felttelezseket.
4.3.1, C.6.2.1.
[12] Kerljk az egszek mretvel kapcsolatos szksgtelen felttelezseket. 4.6.
[13] Kerljk a szksgtelen felttelezseket a lebegpontos tpusok rtkkszlet-
vel kapcsolatban is. 4.6.
[14] Rszestsk elnyben a sima int-et a short int-tel vagy a long int-tel szemben.
4.6.
[15] Rszestsk elnyben a double-t a float-tal vagy a long double-lal szemben. 4.5.
[16] Rszestsk elnyben a sima char-t a signed char-ral s az unsigned char-ral
szemben. C.3.4.
[17] Kerljk az objektumok mretvel kapcsolatos szksgtelen felttelezseket.
4.6.
[18] Kerljk az eljel nlkli aritmetikt. 4.4.
[19] Legynk vatosak az eljelesrl eljel nlklire s unsigned-rl signed-ra val
talaktssal. C.6.2.6.
[20] Legynk vatosak a lebegpontos tpusrl egszre val talaktssal. C.6.2.6.
[21] Legynk vatosak a kisebb tpusokra val talaktsokkal (pldul int-rl char-ra).
C.6.2.6.
Alapok 112
4.11. Gyakorlatok
1. (*2) Futtassuk le a Hell, vilg! programot (3.2). Ha a program fordtsa nem
gy sikerl, ahogy kellene, olvassuk el B.3.1-et.
2. (*1) 4.9 minden deklarcijra vgezzk el a kvetkezket: ha a deklarci
nem definci, rjunk hozz defincit. Ha a deklarci definci is, rjunk olyan
deklarcit, ami nem az.
3. (*1.5) rjunk programot, amely kirja az alaptpusok, nhny szabadon vlasztott
mutattpus s nhny szabadon vlasztott felsorol tpus mrett. Hasznljuk
a sizeof opertort.
4. (*1.5) rjunk programot, amely kirja az 'a''z' betket s a '0''9' szmjegye-
ket, valamint a hozzjuk tartoz egsz rtkeket. Vgezzk el ugyanezt a tbbi
kirhat karakterre is. Csinljuk meg ugyanezt hexadecimlis jellssel.
5. (*2) Mi a rendszernkn a legnagyobb s legkisebb rtke a kvetkez tpu-
soknak: char, short, int, long, float, double, long double s unsigned?
6. (*1) Mi a leghosszabb loklis nv, amit a C++ programokban hasznlhatunk
a rendszernkben? Mi a leghosszabb kls nv, amit a C++ programokban
hasznlhatunk a rendszernkben? Van-e megszorts a nevekben hasznlhat
karakterekre?
7. (*2) Rajzoljunk brt az egsz s alaptpusokrl, ahol egy tpus egy msik tpus-
ra mutat, ha az els minden rtke minden szabvnyos megvalstsban br-
zolhat a msik tpus rtkeknt. Rajzoljuk meg az brt kedvenc C++-
vltozatunk tpusaira is.
4. Tpusok s deklarcik 113
Mutatk, tmbk s struktrk
A fensges s a nevetsges
gyakran annyira sszefggnek,
hogy nehz ket sztvlasztani.
(Tom Paine)
Mutatk Nulla Tmbk Karakterliterlok Tmbre hivatkoz mutatk Konstansok
Mutatk s konstansok Referencik void* Struktrk Tancsok Gyakorlatok
5.1. Mutatk
Ha T egy tpus, T* a T-re hivatkoz mutat tpus lesz, azaz egy T* tpus vltoz egy T t-
pus objektum cmt tartalmazhatja. Pldul:
char c = 'a';
char* p = &c; // a p a c cmt trolja
5
brval:
Sajnos a tmbkre s fggvnyekre hivatkoz mutatk esetben bonyolultabb jells
szksges:
int* pi; // mutat egszre
char** ppc; // mutat char-ra hivatkoz mutatra
int* ap[15]; // egszre hivatkoz mutatk 15 elem tmbje
int (*fp)(char*); // char* paramter fggvnyre hivatkoz mutat; egszet ad vissza
int* f(char*); // char* paramter fggvny; egszre hivatkoz mutatt ad vissza
Lsd 4.9.1-et a deklarcik formai kvetelmnyeire vonatkozan, s az A fggelket
a teljes nyelvtannal kapcsolatban.
A mutatn vgezhet alapvet mvelet a dereferencia, azaz a mutat ltal mutatott objek-
tumra val hivatkozs. E mveletet indirekcinak (kzvetett hasznlatnak, hivatkozsnak)
is hvjk. Az indirekci jele az eltagknt hasznlt egyoperandus * :
char c = 'a';
char* p = &c; // a p a c cmt trolja
char c2 = *p; // c2 == 'a'
A p ltal mutatott vltoz c, a c-ben trolt rtk 'a', gy c2 rtke 'a' lesz.A tmbelemekre hi-
vatkoz mutatkon aritmetikai mveleteket is vgezhetnk (5.3), a fggvnyekre hivatko-
z mutatk pedig vgtelenl hasznosak, ezeket a 7.7 pontban trgyaljuk.
A mutatk clja, hogy kzvetlen kapcsolatot teremtsenek annak a gpnek a cmzsi elj-
rsaival, amin a program fut. A legtbb gp bjtokat cmez meg. Azok, amelyek erre nem
kpesek, olyan hardverrel rendelkeznek, amellyel a bjtokat gpi szavakbl nyerik ki. Ms-
rszrl kevs gp tud kzvetlenl egy bitet megcmezni, kvetkezskpp a legkisebb ob-
jektum, amely szmra nllan memrit foglalhatunk s amelyre beptett tpus muta-
tval hivatkozhatunk, a char. Jegyezzk meg, hogy a bool legalbb annyi helyet foglal, mint
a char (4.6). Ahhoz, hogy a kisebb rtkeket tmrebben lehessen trolni, logikai oper-
torokat (6.2.4) vagy struktrkban lev bitmezket (C.8.1) hasznlhatunk.
Alapok 116
&c
'a'
p:
c:
5.1.1. Nulla
A nulla (0) az int tpusba tartozik. A szabvnyos konverziknak (C.6.2.3) ksznheten a 0
integrlis (4.1.1), lebegpontos, mutat, vagy tagra hivatkoz mutat tpus konstans-
knt is hasznlhat. A tpust a krnyezet dnti el. A nullt ltalban (de nem szksgszer-
en) a megfelel mret csupa nulla bitminta jelli.Nincs olyan objektum, amely szmra
a 0 cmmel foglalnnk helyet. Kvetkezskppen a 0 mutat-literlknt viselkedik, azt je-
llve, hogy a mutat nem hivatkozik objektumra.A C-ben a nulla mutatt (nullpointer) szo-
ks a NULL makrval jellni. A C++ szigorbb tpusellenrzse miatt az ilyen NULL makrk
helyett hasznljuk a sima 0-t, ez kevesebb problmhoz vezet. Ha gy rezzk, muszj
a NULL-t megadnunk, tegyk azt az albbi mdon:
const int NULL = 0 ;
A const minst megakadlyozza, hogy a NULL-t vletlenl jra definiljuk s biztostja,
hogy a NULLt ott is hasznlni lehessen, ahol llandra van szksg.
5.2. Tmbk
Ha T egy tpus, a T[size] a size darab T tpus elembl ll tmb tpus lesz. Az elemek sor-
szmozsa 0-tl size-1-ig terjed:
float v[3]; // hrom lebegpontos szmbl ll tmb: v[0], v[1], v[2]
char* a[32]; // karakterre hivatkoz mutatk 32 elem tmbje: a[0] .. a[31]
A tmb elemeinek szma, mrete vagy dimenzija, konstans kifejezs kell, hogy legyen
(C.5). Ha vltoz mretre van szksgnk, hasznljunk vektort (3.7.1, 16.3):
void f(int i)
{
int v1[i]; // hiba: a tmb mrete nem konstans kifejezs
vector<int> v2(i); // rendben
}
A tbbdimenzis tmbk gy brzoldnak, mint tmbkbl ll tmbk:
int d2[10][20]; // d2 olyan tmb, amely 10 darab, 20 egszbl ll tmbt tartalmaz
5. Mutatk, tmbk s struktrk 117
A ms nyelvekben tmbk mretnek meghatrozsra hasznlt vessz jells fordtsi
hibkat eredmnyez, mert a , (vessz) mveletsorozatot jelz opertor (6.2.2) s nem meg-
engedett konstans kifejezsekben (C.5). Pldul prbljuk ki ezt:
int bad[5,2]; // hiba: konstans kifejezsben nem lehet vessz
A tbbdimenzis tmbket a C.7 pontban trgyaljuk. Alacsonyszint kdon kvl a leg-
jobb, ha kerljk ket.
5.2.1. Tmbk feltltse
A tmbknek rtkekbl ll listkkal adhatunk kezdrtket:
int v1[ ] = { 1, 2, 3, 4 };
char v2[ ] = { 'a', 'b', 'c', 0 };
Amikor egy tmbt gy adunk meg, hogy a mrett nem hatrozzuk meg, de kezdrtke-
ket biztostunk, a fordtprogram a tmb mrett a kezdrtk-lista elemeinek megszml-
lsval szmtja ki. Kvetkezskpp v1 s v2 tpusa rendre int[4] s char[4] lesz. Ha a m-
retet megadjuk, a kezdrtk-listban nem szerepelhet annl tbb elem, mert ez hibnak
szmt:
char v3[2] = { 'a', 'b', 0 }; // hiba: tl sok kezdrtk
char v4[3] = { 'a', 'b', 0 }; // rendben
Ha a kezdrtk tl kevs elemet ad meg, a tmb maradk elemeire 0 lesz felttelezve:
int v5[8] = { 1, 2, 3, 4 };
Az elz kd egyenrtk a kvetkezvel:
int v5[ ] = { 1, 2, 3, 4 , 0, 0, 0, 0 };
Jegyezzk meg, hogy a kezdrtk-lista nem helyettesthet s nem brlhat fell tmb-
rtkadssal:
void f()
{
v4 = { 'c', 'd', 0 }; // hiba: tmbt nem lehet rtkl adni
}
Alapok 118
Ha ilyen rtkadsra van szksgnk, tmb helyett hasznljunk vector-t (16.3) vagy
valarray-t (22.4).
A karaktertmbket knyelmi okokbl karakterliterlokkal (5.2.2) is feltlthetjk.
5.2.2. Karakterliterlok
A karakterliterl egy macskakrmkkel hatrolt karaktersorozat:
"Ez egy karakterlnc"
Egy karakterliterl a ltszlagosnl eggyel tbb karaktert tartalmaz; a '\0' null karakterre
vgzdik, melynek rtke 0:
sizeof("Bohr")==5
A karakterliterlok tpusa megfelel szm const (lland) karakterbl ll tmb, gy
a "Bohr" tpusa const char[5] lesz.
A karakterliterlokat egy char*-nak is rtkl adhatjuk. Ez azrt megengedett, mert
a karakterliterl tpusa a C s a C++ korbbi vltozataiban char* volt, gy ez szksges ah-
hoz, hogy milli sornyi C s C++ kd rvnyes maradjon. Az ilyen karakterliterlokat azon-
ban hiba ilyen mutatn keresztl mdostani.
void f()
{
char* p = "Platn";
p[4] = 'e'; // hiba: rtkads konstansnak; az eredmny nem meghatrozott
}
Az effajta hibt ltalban nem lehet a futsi idig kiderteni, s a nyelv egyes megvalst-
sai is klnbznek abban, hogy mennyire szereznek rvnyt ennek a szablynak. (Lsd
mg B.2.3-at.) Az, hogy a karakterliterlok llandk, nemcsak magtl rtetd, hanem azt
is lehetv teszi, hogy a nyelv adott vltozata jelentsen optimalizlhassa a karakterliterlok
trolsnak s hozzfrsnek mdjt.
5. Mutatk, tmbk s struktrk 119
Ha olyan karakterlncot szeretnnk, amit biztosan mdosthatunk, karaktereit egy tmbbe
kell msolnunk:
void f()
{
char p[ ] = "Znn"; // p egy 6 karakterbl ll tmb
p[0] = 'R'; // rendben
}
A karakterliterl trolsi helye nem vltozik (statikus), gy egy fggvny visszatrsi rtke-
knt biztonsgosan megadhat:
const char* error_message(int i)
{
// ...
return "tartomnyhiba";
}
A range_error-t tartalmaz memriaterlet tartalma nem trldik az error_message() meg-
hvsa utn.
Az, hogy kt egyforma karakterliterl egyetlen memriaterleten troldik-e, az adott nyel-
vi vltozattl fgg (C.1):
const char* p = "Herakleitosz";
const char* q = "Herakleitosz";
void g()
{
if (p == q) cout << "Egyezik!\n"; // az eredmny az adott C++-vltozattl fgg
// ...
}
Jegyezzk meg, hogy a mutatkra alkalmazott == a cmeket (a mutat rtkeket) hasonlt-
ja ssze, nem azokat az rtkeket, melyekre a mutatk hivatkoznak.
res karakterlncot a "" szomszdos macskakrm-prral rhatunk le (tpusa const char[1]).
A nem grafikus karakterek jellsre hasznlt fordtott perjel (C.3.2) szintn hasznlhat
egy karakterlnc belsejben. Ez lehetv teszi az idzjel (" ) s a fordtott perjel escape
karakter (\) karakterlncon belli brzolst is. Az '\n' (j sor) karakter ezek kzl messze
a leggyakrabban hasznlt:
cout<<"cseng az zenet vgn\a\n";
Alapok 120
Az '\a' karakter az ASCII BEL (cseng), amely alert-knt is ismert, kirsa pedig valamilyen
hangjelzst eredmnyez.
A karakterlncokban igazi sortrs nem szerepelhet:
"Ez nem karakterlnc
hanem formai hiba"
A hossz lncok reshely (whitespace) karakterekkel szttrdelhetk, hogy a program-
szveg szebb legyen:
char alpha[ ] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
A fordtprogram sszefzi a szomszdos lncokat, gy az alpha egyetlen karakterlnccal is
megadhat lett volna:
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
A null karaktert elvileg a karakterlncok belsejben is hasznlhatnnk, de a legtbb prog-
ram nem felttelezi, hogy utna is vannak karakterek. A "Jens\000Munk" karakterlncot
pldul az olyan standard knyvtrbeli fggvnyek, mint a strcpy() s a strlen(), "Jens"-knt
fogjk kezelni (20.4.1).
Az L eltag karakterlncok mint amilyen az L"angst" szles karakterekbl (wide char)
llnak (4.3, C3.3), tpusuk const wchar_t[ ].
5.3. Tmbkre hivatkoz mutatk
A C++-ban a tmbk s mutatk kztt szoros kapcsolat ll fenn. Egy tmb nevt gy is
hasznlhatjuk, mint egy mutatt, amely a tmb els elemre mutat:
int v[ ] = { 1, 2, 3, 4 };
int* p1 = v; // mutat a kezdelemre (automatikus konverzi)
int* p2 = &v[0]; // mutat a kezdelemre
int* p3 = &v[4]; // mutat az "utols utni" elemre
5. Mutatk, tmbk s struktrk 121
brval:
Az biztosan mkdik, ha a mutatt eggyel a tmb vge utni elemre lltjuk. Ez sok algorit-
mus szmra fontos (2.7.2, 18.3). Mivel azonban egy ilyen mutat tnylegesen mr nem
mutat egy tmb elemre, nem szabad rsra vagy olvassra hasznlni. Nem meghatrozott,
hogy mi trtnik, amikor egy tmb kezdeleme eltt lev elem cmt vesszk, ezrt az
ilyesmi kerlend. Egyes szmtgpeken a tmbk gyakran a gp cmzsi hatrain kerl-
nek lefoglalsra, gy az eggyel a kezdelem eltti elem egyszeren rtelmetlen lesz.
A tmbnevek automatikus (implicit) talaktsa mutatv szleskren hasznlatos a C st-
lus kdokban szerepl fggvnyhvsoknl:
extern "C" int strlen(const char*); // a <string.h> fejllomnybl
void f()
{
char v[ ] = "Annemarie";
char* p = v; // char[ ] automatikus talaktsa char*-g
strlen(p);
strlen(v); // char[ ] automatikus talaktsa char*-g
v = p; // hiba: a tmbnek nem adhat rtk
}
A standard knyvtr strlen() fggvnynek mindkt hvskor ugyanaz az rtk addik t. Az
a bkken, hogy az automatikus konverzit lehetetlen elkerlni, vagyis nincs md olyan
fggvny bevezetsre, amelynek meghvsakor a v tmb tmsoldik. Szerencsre mutat-
rl tmbre val talakts nem vgezhet sem automatikusan, sem definilt mdon.
A tmbparamter automatikus mutatv alaktsa azt jelenti, hogy a tmb mrete elvsz
a fggvny szmra. A fggvnynek azonban valahogy meg kell hatroznia a tmb mre-
tt, hogy rtelmes mveleteket hajthasson vgre rajta. A C standard knyvtrban lev ms
fggvnyekhez hasonlan amelyek karakterre hivatkoz mutatkat kapnak paramter-
knt az strlen() is arra szmt, hogy a null karakter jelzi a karakterlnc vgt, gy a strlen(p)
Alapok 122
p1 p2 p3
v: 1 2 3 4
a p karaktereinek a 0 null karakter vgzdsig szmolt mennyisgt jelenti, nem belertve
a null karakter vgzdst . Ez meglehetsen alacsony szint megolds. A standard knyv-
trban lv vector (16.3.) s string (20. fejezet) esetben nincs ilyen problma.
5.3.1. Tmbk bejrsa
Sok algoritmus lnyege a tmbkhz s ms hasonl adattpusokhoz val hatkony s ele-
gns hozzfrs (lsd 3.8, 18. fejezet). A hozzfrs egy tmbre hivatkoz mutatval, il-
letve egy indexszel vagy egy elemre hivatkoz mutatval valsthat meg. me egy plda
egy karakterlnc bejrsra index hasznlatval:
void fi(char v[ ])
{
for (int i = 0; v[i]!=0; i++) use(v[i]);
}
Ez egyenrtk a mutatval trtn bejrssal:
void fp(char v[ ])
{
for (char* p = v; *p!=0; p++) use(*p);
}
Az eltagknt hasznlt * indirekci opertor egy mutat-hivatkozst old fel, gy *p a p ltal
mutatott karakter lesz, a ++ pedig gy nveli a mutatt, hogy az a tmb kvetkez elem-
re hivatkozzon. Nincs olyan eredend ok, amirt az egyik vltozat gyorsabb lenne a msik-
nl. A modern fordtprogramoknak ugyanazt a kdot kell ltrehozniuk mindkt plda
esetben (lsd 5.9[8]-at). A programozk logikai s eszttikai alapon vlaszthatnak a vlto-
zatok kztt.
Ha a +, -, ++ vagy -- aritmetikai mveleti jeleket mutatkra alkalmazzuk, az eredmny a mu-
tatk ltal hivatkozott objektumok tpustl fgg. Amikor egy T* tpus p mutatra alkal-
mazunk egy aritmetikai opertort, akkor p-rl felttelezzk, hogy egy T tpus objektumok-
bl ll tmb elemre mutat, gy p+1 a tmb kvetkez elemt jelzi, p-1 pedig az elz
elemre mutat. Ez arra utal, hogy p+1 egsz rtke sizeof(T)-vel nagyobb lesz, mint p egsz
rtke. Hajtsuk vgre a kvetkezt:
#include <iostream>
int main ()
{
int vi[10];
short vs[10];
5. Mutatk, tmbk s struktrk 123
std::cout << &vi[0] << ' ' << &vi[1] << '\n';
std::cout << &vs[0] << ' ' << &vs[1] << '\n';
}
Ekkor a kvetkezt kapjuk (a mutatk rtknek alaprtelmezs szerinti, hexadecimlis je-
llst hasznlva):
0x7fffaef0 0x7fffaef4
0x7fffaedc 0x7fffaede
Ez azt mutatja, hogy sizeof(short) az adott megvalstsban 2, sizeof(int) pedig 4.
Mutatkat csak akkor vonhatunk ki egymsbl definilt mdon, ha mindkt mutat ugyan-
annak a tmbnek az elemeire mutat (br a nyelvben nincs gyors md annak ellenrzsre,
hogy valban arra mutatnak). Amikor kivonunk egy mutatt egy msikbl, az eredmny
a kt mutat kztt lv tmbelemek szma (egy egsz tpus rtk) lesz. A mutatkhoz
egsz rtket is adhatunk s ki is vonhatunk belle egszet, az eredmny mindkt esetben
egy mutat rtk lesz. Ha ez az rtk nem ugyanannak a tmbnek egy elemre mutat,
amelyre az eredeti mutat, vagy nem eggyel a tmb mg, az eredmnyl kapott mutat
rtk felhasznlsa kiszmthatatlan eredmnyhez vezethet:
void f()
{
int v1[10];
int v2[10];
int i1 = &v1[5]-&v1[3]; // i1 = 2
int i2 = &v1[5]-&v2[3]; // meghatrozhatatlan eredmny
int* p1 = v2+2; // p1 = &v2[2]
int* p2 = v2-2; // *p2 nem meghatrozott
}
A bonyolult mutataritmetika rendszerint szksgtelen, ezrt legjobb elkerlni. Nincs rtel-
me mutatkat sszeadni, s ez nem is megengedett.
A tmbk nem nlerk, mert nem biztos, hogy a tmb elemeinek szma is troldik
a tmbbel egytt. Ez azt jelenti, hogy ahhoz, hogy bejrjunk egy tmbt, amely nem tartal-
maz a karakterlncokhoz hasonl vgzdst, valahogy meg kell adnunk a tmb elemei-
nek szmt:
Alapok 124
void fp(char v[ ], unsigned int size)
{
for (int i=0; i<size; i++) use(v[i]);
const int N = 7;
char v2[N];
for (int i=0; i<N; i++) use(v2[i]);
}
Jegyezzk meg, hogy a legtbb C++-vltozat a tmbk esetben nem vgez indexhatr-
ellenrzst. A tmb ezen fogalma eredenden alacsony szint. Fejlettebb tmbfogalmat
osztlyok hasznlatval valsthatunk meg (3.7.1).
5.4. Konstansok
A C++ felknlja a const, azaz a felhasznli lland fogalmt, hogy lehetsgnk legyen
annak kifejezsre, hogy egy rtk nem vltozik meg kzvetlenl. Ez szmos esetben hasz-
nos lehet. Sok objektumnak a ltrehozs utn mr nem vltozik meg az rtke. A szimboli-
kus konstansok (jelkpes llandk) knnyebben mdosthat kdhoz vezetnek, mint
a kdban kzvetlenl elhelyezett literlok. Gyakori, hogy egy rtket mutatn keresztl
rnk el, de az rtket nem vltoztatjuk meg. A legtbb fggvnyparamtert csak olvassuk,
nem rjuk.
A const kulcssz hozzadhat egy objektum deklarcijhoz, jelezve, hogy az objektumot
llandknt hatrozzuk meg. Mivel egy llandnak ksbb nem lehet rtket adni, kezde-
ti rtkadst kell vgeznnk:
const int model = 90; // a model lland
const int v[ ] = { 1, 2, 3, 4 }; // a v[i] lland
const int x; // hiba: nincs kezdeti rtkads
Ha valamit const-knt hatrozunk meg, az biztostk arra, hogy hatkrn bell rtke nem
fog megvltozni:
void f()
{
model = 200; // hiba
v[2]++; // hiba
}
5. Mutatk, tmbk s struktrk 125
Jegyezzk meg, hogy a const kulcssz mdostja a tpust s megszortst ad arra, hogyan
hasznlhatunk egy objektumot, de nem hatrozza meg, hogyan kell az lland szmra he-
lyet foglalni:
void g(const X* p)
{
// itt *p nem mdosthat
}
void h()
{
X val; // a val mdosthat
g(&val);
// ...
}
Attl fggen, hogy a fordtprogram mennyire okos, szmos mdon kihasznlhatja egy
objektum lland mivoltt. Az llandk kezdeti rtke pldul gyakran (de nem mindig)
egy konstans kifejezs (C.5), ami fordtsi idben kirtkelhet. Tovbb, ha a fordt-
program tud az lland minden hasznlatrl, nem kell trhelyet sem lefoglalnia szmra:
const int c1 = 1;
const int c2 = 2;
const int c3 = my_f(3); // c3 rtke fordtskor nem ismert
extern const int c4; // c4 rtke fordtskor nem ismert
const int* p = &c2; // c2 szmra trterletet kell foglalni
Ekkor a fordtprogram ismeri c1 s c2 rtkt, gy azokat konstans kifejezsekben felhasz-
nlhatjuk. Mivel a c3 s c4 rtkek fordtsi idben nem ismertek (ha csak ebben a fordt-
si egysgben lev informcikat hasznljuk fel, lsd 9.1), c3-nak s c4-nek trhelyet kell
foglalni. Mivel c2 cmt hasznljuk, c2-nek is helyet kell foglalni. A c1 konstans plda arra
az egyszer s gyakori esetre, amikor az lland rtke fordtsi idben ismert s szmra
nem szksges trat foglalni. Az extern kulcssz azt jelli, hogy a c4-et mshol definiltuk
(9.2).
A konstansokbl ll tmbknek ltalban szksges helyet foglalni, mert a fordtprog-
ram nem tudja eldnteni, mely tmbelemekre hivatkoznak a kifejezsek. Sok gpen azon-
ban mg ebben az esetben is nvelhetjk a hatkonysgot, gy, hogy a konstansokbl ll
tmbt csak olvashat memriba tesszk.
Alapok 126
A const-okat gyakran hasznljuk tmbk indexhatraknt s case cmkknl is:
const int a = 42;
const int b = 99;
const int max = 128;
int v[max];
void f(int i)
{
switch (i) {
case a:
// ...
case b:
// ...
}
}
Ilyen esetekben gyakori, hogy const helyett felsorol konstansokat (4.8) hasznlunk. Azt,
hogy a const milyen mdon hasznlhat osztlyok tagfggvnyeivel, a 10.2.6 s 10.2.7
pontokban trgyaljuk.
A szimbolikus konstansokat rendszeresen hasznlnunk kellene arra, hogy elkerljk a kd-
ban a mgikus szmokat. Ha egy numerikus lland, pldul egy tmb mrete, a kdban
ismtldik, a programot nehz lesz tnzni, hogy a megfelel mdostskor az lland min-
den egyes elfordulst kicserljk. A szimbolikus konstansok hasznlata viszont lokliss
teszi az informcit. A numerikus konstansok rendszerint valamilyen, a programmal kap-
csolatos felttelezst jellnek. A 4 pldul egy egszben lv bjtok szmt, a 128 a beme-
net tmeneti trba helyezshez (pufferelshez) szksges karakterek szmt, a 6.24
pedig a dn korona s az amerikai dollr kztti keresztrfolyamot jellheti. Ha ezeket az
rtkeket numerikus llandknt hagyjuk a kdban, akkor az, aki a programot karbantart-
ja, nagyon nehezen tudja megtallni s megrteni azokat. Ezeket az llandkat gyakran
nem veszik szre, s rvnytelenn vlnak, amikor a programot tviszik ms rendszerre
vagy ha ms vltozsok alssk az ltaluk kifejezett felttelezseket. Ha a feltevseket
megjegyzsekkel megfelelen elltott szimbolikus konstansokknt valstjuk meg, minim-
lisra cskkenthetjk az ilyen jelleg karbantartsi problmkat.
5. Mutatk, tmbk s struktrk 127
5.4.1. Mutatk s konstansok
A mutatk hasznlatakor kt objektummal kapcsolatos dologrl van sz: magrl a muta-
trl s az ltala mutatott objektumrl. Ha a mutat deklarcijt a const sz elzi meg, ak-
kor az az objektumot, s nem a mutatt hatrozza meg llandknt. Ahhoz, hogy lland-
knt egy mutatt, s ne az ltala mutatott objektumot vezessk be, a *const deklartort kell
hasznlnunk a sima * helyett:
void f1(char* p)
{
char s[ ] = "Gorm";
const char* pc = s; // mutat llandra
pc[3] = 'g'; // hiba: pc llandra mutat
pc = p; // rendben
char *const cp = s; // konstans mutat
cp[3] = 'a'; // rendben
cp = p; // hiba: cp konstans
const char *const cpc = s; // konstans mutat llandra
cpc[3] = 'a'; // hiba: cpc llandra mutat
cpc = p; // hiba: cpc konstans
}
A *const deklartorjelz teszi llandv a mutatt. Nincs azonban const* deklartor-oper-
tor, gy a * eltt szerepl const kulcsszt az alaptpus rsznek tekintjk:
char *const cp; // konstans mutat karakterre
char const* pc; // mutat kostans karakterre
const char* pc2; // mutat kostans karakterre
ltalban segtsget jelent, ha az ilyen deklarcikat jobbrl balra olvassuk ki. Pldul: cp
egy konstans (const) mutat, amely egy karakterre (char) mutat s pc2 egy mutat, amely
egy karakter-konstansra (char const) mutat.
Egy objektum, amely lland akkor, amikor mutatn keresztl frnk hozz, lehet, hogy
mdosthat lesz akkor, ha ms mdon frnk hozz. Ez klnsen hasznos a fggvny-
paramterek esetben. Azzal, hogy egy mutat-paramtert const-knt adunk meg, a fgg-
vnynek megtiltjuk, hogy mdostsa a mutat ltal mutatott objektumot:
char* strcpy(char* p, const char* q); // *q nem mdosthat
Alapok 128
Egy vltoz cmt rtkl adhatjuk egy konstansra hivatkoz mutatnak, mert ebbl mg
semmi rossz nem kvetkezik. Konstans cmt azonban nem lehet rtkl adni egy nem
konstans mutatnak, mert ezzel megengednnk, hogy az objektum rtke megvltozzon:
void f4()
{
int a = 1;
const int c = 2;
const int* p1 = &c; // rendben
const int* p2 = &a; // rendben
int* p3 = &c; // hiba: kezdeti rtkads int*-nak const int*-gal
*p3 = 7; // ksrlet c rtknek mdostsra
}
A const-ra hivatkoz mutatkkal kapcsolatos megszortsokat meghatrozott (explicit)
tpuskonverzival kszblhetjk ki (10.2.7.1 s 15.4.2.1).
5.5. Referencik
A referencia (hivatkozs) egy objektum lneve (alias). Az ilyen hivatkozsokat ltalban
fggvnyek s klnsen tlterhelt opertorok (11. fejezet) paramtereinek s visszatrsi
rtkeinek megadsra hasznljuk. Az X& jells jelentse referencia X-re. Lssunk egy
pldt:
void f()
{
int i = 1;
int& r = i; // r s i itt ugyanarra az int-re hivatkoznak
int x = r; // x = 1
r = 2; // i = 2
}
Azt biztostand, hogy a referencia valaminek a neve legyen (azaz tartozzon hozz objek-
tum), a hivatkozs clpontjt mr ltrehozskor meg kell hatroznunk:
int i = 1;
int& r1 = i; // rendben: r1 kapott kezdrtket
int& r2; // hiba: kezdeti rtkads hinyzik
extern int& r3; // rendben: r3 mshol kap kezdrtket
5. Mutatk, tmbk s struktrk 129
A referencia kezdeti rtkadsa nagyban klnbzik a ksbbi rtkadstl. A ltszat elle-
nre a referencin nem hajtdik vgre egyetlen mvelet sem. Pldul:
void g()
{
int ii = 0;
int& rr = ii;
rr++; // ii nvelse eggyel
int* pp = &rr; // pp az ii-re mutat
}
Ez nem helytelen, de rr++ nem az rr rtkt nveli; a ++ egy int-re hajtdik vgre (ami itt
ii). Kvetkezskppen a referencik rtke mr nem mdosthat a kezdeti rtkads utn;
mindig arra az objektumra fognak hivatkozni, amelyre kezdetben belltottuk azokat. Az rr
ltal jellt objektumra hivatkoz mutatt &rr-rel kaphatjuk meg.
A referencia magtl rtetd mdon megvalsthat (konstans) mutatknt is, amely min-
den egyes hasznlatakor automatikusan feloldja a mutat-hivatkozst. Nem szrmazhat baj
abbl, ha gy gondolunk a referencikra, mindaddig, mg el nem felejtjk, hogy nem olyan
objektumok, amelyet mutatknt kezelhetnnk:
Egyes fordtprogramok olyan optimalizcit alkalmazhatnak, amely a referencia szmra
futsi idben szksgtelenn teszi trterlet lefoglalst.
A referencia kezdeti rtkadsa magtl rtetd, ha a kezdrtk egy balrtk (vagyis egy
olyan objektum, amelynek cmre hivatkozhatunk, lsd 4.9.6). Egy sima T& kezdrtke
T tpus balrtk kell, hogy legyen. Egy const T& esetben ez nem szksges (sem balr-
tknek, sem T tpusnak nem kell lennie), helyette az albbiak trtnnek:
Alapok 130
&ii
1 ii:
rr:
pp:
1. Elszr T-re trtn automatikus tpuskonverzi megy vgbe, ha szksges
(lsd C.6-ot),
2. aztn a kapott rtk egy T tpus ideiglenes vltozba kerl,
3. vgl ez az ideiglenes vltoz lesz a kezdrtk.
Vegyk a kvetkez pldt:
double& dr = 1; // hiba: balrtkre van szksg
const double& cdr = 1; // rendben
A msodik a kvetkezkppen rtelmezhet:
double temp = double(1); // elszr ltrehozunk egy ideiglenes vltozt a jobb oldali
// rtkkel
const double& cdr = temp; // majd ezt hasznljuk a cdr kezdeti rtkadsra
A referencia kezdrtkt trol ideiglenes vltoz a referencia hatkrnek vgig marad
fenn. A konstansok s vltozk hivatkozsait azrt klnbztetjk meg, mert a vltozk
esetben nagy hibalehetsgeket rejt magban egy ideiglenes vltoz bevezetse, a vlto-
znak val rtkads ugyanis a nemsokra megszn ideiglenes trterletnek adna r-
tket. A konstansok hivatkozsaival nincs ilyen problma, ami szerencss, mert ezek gyak-
ran fggvnyparamterknt jtszanak fontos szerepet (11.6).
A referencikat olyan fggvnyparamterek megadsra is hasznlhatjuk, melyeken ke-
resztl a fggvny mdosthatja a neki tadott objektum rtkt:
void increment(int& aa) { aa++; }
void f()
{
int x = 1;
increment(x); // x = 2
}
A paramtertads a kezdeti rtkadshoz hasonl, gy az increment meghvsakor az aa
paramter az x msik neve lesz. Ha azt szeretnnk, hogy a program olvashat maradjon,
legjobb, ha elkerljk az olyan fggvnyeket, amelyek mdostjk paramtereiket. Ehelyett
meghatrozhatjuk a fggvny ltal visszaadand rtket vagy mutat paramtert adhatunk
neki:
int next(int p) { return p+1; }
void incr(int* p) { (*p)++; }
5. Mutatk, tmbk s struktrk 131
void g()
{
int x = 1;
increment(x); // x = 2
x = next(x); // x = 3
incr(&x); // x = 4
}
Az increment(x) jells az olvasnak semmit sem rul el arrl, hogy az x rtke mdosul,
ellenttben az x=next(x) s incr(&x) jellsekkel. Kvetkezskppen a sima referencia-
paramtereket csak olyan esetekben hasznljuk, amikor a fggvny neve hatrozottan utal
arra, hogy ezek mdosulnak.
A referencikat olyan fggvnyek megadsra is hasznlhatjuk, amelyek egy rtkads bal
s jobb oldaln egyarnt szerepelhetnek. Ez a bonyolultabb felhasznli tpusok tervezse-
kor lehet igazn hasznos. Adjunk meg pldul egy egyszer asszociatv tmbt. Elszr ha-
trozzuk meg a Pair adatszerkezetet:
struct Pair {
string name;
double val;
};
Az alaptlet az, hogy a string-hez tartozik egy lebegpontos rtk. Knny elkszteni
a value() fggvnyt, amely egy Pair-bl ll adatszerkezetet vet ssze klnbz karakter-
lncokkal. Rvidtsk le a pldt s hasznljunk egy nagyon egyszer (persze nem tl ha-
tkony) megvalstst:
vector<Pair> pairs;
double& value(const string& s)
/*
Vesszk Pair-ek egy halmazt,
megkeressk s-t, ha megtalltuk, visszaadjuk az rtkt; ha nem, j Pair-t ksztnk s
visszaadjuk az alaprtelmezett 0-t.
*/
{
for (int i = 0; i < pairs.size(); i++)
if (s == pairs[i].name) return pairs[i].val;
Pair p = { s, 0 };
pairs.push_back(p); // Pair hozzadsa a vghez (3.7.3)
return pairs[pairs.size()-1].val;
}
Alapok 132
Ezt a fggvnyt gy foghatjuk fel, mint egy lebegpontos rtkekbl ll tmbt, amit ka-
rakterlncok indexelnek. Adott karakterlnccal sszevetve, a value() a megfelel lebeg-
pontos objektumot (nem pedig annak rtkt) tallja meg, s az erre vonatkoz referencit
adja vissza:
int main() // az egyes szavak elfordulsnak megszmllsa a bemeneten
{
string buf;
while (cin>>buf) value(buf)++;
for (vector<Pair>::const_iterator p = pairs.begin(); p!=pairs.end(); ++p)
cout << p->name << ": " << p->val << '\n';
}
A while ciklus minden esetben beolvas egy szt a cin szabvnyos bemenetrl s a buf
karakterlncba helyezi (3.6.), aztn nveli a hozz tartoz szmllt. Vgl kirja az ered-
mnyl kapott tblzatot, amelyben a bemenetrl kapott karakterlncok s azok elfordu-
lsnak szma szerepel. Ha a bemenet pldul a kvetkez:
aa bb bb aa aa bb aa aa
akkor a program eredmnye az albbi:
aa: 5
bb: 3
Ezt mr knny gy tovbb finomtani, hogy valdi asszociatv tmbt kapjunk; ehhez egy
sablon osztlyt kell hasznlnunk a tlterhelt (11.8.) [ ] indexel opertorral. Mg knnyebb
a dolgunk, ha a standard knyvtr map (17.4.1.) tpust hasznljuk.
5.6. Void-ra hivatkoz mutatk
Brmilyen tpus objektumra hivatkoz mutatt rtkl lehet adni egy void* tpus vlto-
znak, egy void* tpus vltozt rtkl lehet adni egy msik void* tpusnak, a void* t-
pus vltozkat ssze lehet hasonltani, hogy egyenlek-e vagy sem, egy void* tpus vl-
tozt pedig meghatrozott mdon ms tpusv lehet alaktani. A tbbi mvelet nem lenne
5. Mutatk, tmbk s struktrk 133
biztonsgos, mert a fordtprogram nem tudja, hogy valjban mifle objektumra hivatko-
zik egy ilyen mutat, ezrt a tbbi mvelet fordtsi idej hibt eredmnyez. Ahhoz, hogy
egy void* tpus vltozt hasznlhassunk, t kell konvertlnunk azt adott tpus mutatv:
void f(int* pi)
{
void* pv = pi; // rendben: int* automatikus konvertlsa void*-g
*pv; // hiba: nem lehet void*-ra hivatkozni
pv++; // hiba: void* nem nvelhet (a mutatott objektum mrete ismeretlen)
int* pi2 = static_cast<int*>(pv); // explicit visszaalakts int*-ra
double* pd1 = pv; // hiba
double* pd2 = pi; // hiba
double* pd3 = static_cast<double*>(pv); // nem biztonsgos
}
ltalban nem biztonsgos olyan mutatt hasznlni, amely olyan tpusra konvertldik
(cast), amely klnbzik a mutat ltal elzleg hivatkozott tpustl. A gp pldul feltte-
lezheti, hogy minden double 8 bjtos memriahatron jn ltre. Ha gy van, akkor furcsa m-
kds szrmazhat abbl, ha a pi egy olyan int-re mutatott, amely nem gy helyezkedett el
a memriban. Az ilyen jelleg explicit tpusknyszerts eredenden csnya s nem bizton-
sgos, kvetkezskpp a hasznlt static_cast jellst is szndkosan csnynak terveztk.
A void* elsdlegesen arra hasznlatos, hogy mutatkat adjunk t olyan fggvnyeknek,
amelyek nem feltteleznek semmit az objektumok tpusrl, valamint arra, hogy fggv-
nyek nem tpusos objektumokat adjanak vissza. Ahhoz, hogy ilyen objektumokat hasznl-
junk, explicit tpuskonverzit kell alkalmaznunk. Azok a fggvnyek, amelyek void* tpu-
s mutatkat hasznlnak, jellemzen a rendszer legals szintjn helyezkednek el, ahol az
igazi hardver-erforrsokat kezelik. Pldul:
void* my_alloc(size_t n); // n bjt lefoglalsa sajt trterleten
A rendszer magasabb szintjein lv void* tpus mutatkat gyanakvssal kell figyelnnk,
mert tervezsi hibt jelezhetnek. Ha a void*-ot optimalizlsra hasznljuk, rejtsk tpus-
biztos fellet mg (13.5, 24.4.2).
A fggvnyekre hivatkoz mutatkat (7.7.) s a tagokra hivatkoz mutatkat (15.5) nem
adhatjuk rtkl void* tpus vltoznak.
Alapok 134
5.7. Struktrk
A tmbk azonos tpus elemekbl llnak, a struct-ok (adatszerkezetek, struktrk) majd-
nem tetszleges tpusakbl:
struct address {
char* name; // "Jim Dandy"
long int number; // 61
char* street; // "South St"
char* town; // "New Providence"
char state[2]; // 'N' 'J'
long zip; // 7974
};
A fenti kd egy address (cm) nev j tpust hoz ltre, amely levelek kldshez szksges
cmzsi adatokat tartalmaz. Vegyk szre a pontosvesszt a definci vgn. Ez egyike azon
kevs helyeknek a C++-ban, ahol pontosvesszt kell tenni a kapcsos zrjel utn, ezrt so-
kan hajlamosak elfelejteni.
Az address tpus vltozkat pontosan gy adhatjuk meg, mint ms vltozkat, s az egyes
tagokra a . (pont, tagkivlaszt) opertorral hivatkozhatunk:
void f()
{
address jd;
jd.name = "Jim Dandy";
jd.number = 61;
}
A tmbk kezdeti rtkadsra hasznlt jells a struktra-tpus vltozk feltltsre is
hasznlhat:
address jd = {
"Jim Dandy",
61, "South St",
"New Providence", {'N','J'}, 7974
};
Ennl azonban rendszerint jobb megolds konstruktorokat (10.2.3) hasznlni. Vegyk sz-
re, hogy a jd.state-et nem lehetett volna az "NJ" karakterlnccal feltlteni. Mivel a karakter-
lncok a '\0' karakterre vgzdnek, az "NJ" hrom karakterbl ll, ami eggyel tbb, mint
ami a jd.state-be belefr.
5. Mutatk, tmbk s struktrk 135
A struktrk objektumaira gyakran hivatkozunk mutatkon keresztl a -> (struktra-muta-
t) opertorral:
void print_addr(address* p)
{
cout << p->name << '\n'
<< p->number << ' ' << p->street << '\n'
<< p->town << '\n'
<< p->state[0] << p->state[1] << ' ' << p->zip << '\n';
}
Ha p egy mutat, akkor p->m egyenrtk (*p).m-mel.
A struktra-tpus objektumokat rtkl adhatjuk, tadhatjuk fggvnyparamterknt, s
visszaadhatjuk fggvnyek visszatrsi rtkeknt is:
address current;
address set_current(address next)
{
address prev = current;
current = next;
return prev;
}
Ms lehetsges mveletek, mint az sszehasonlts (== s !=), nem meghatrozottak, de
a felhasznl megadhat ilyeneket (11. fejezet).
A struktra-tpus objektumok mrete nem felttlenl a tagok mretnek sszege. Ennek
az az oka, hogy sok gp ignyli bizonyos tpus objektumok elhelyezst a felptstl fg-
g memriahatrokra, vagy eleve hatkonyabban kezeli az gy ltrehozott objektumokat.
Az egszek pldul gyakran gpi szhatrokon jnnek ltre. Ezt gy mondjuk, hogy az
ilyen gpeken az objektumok jl illesztettek. Ez a struktrkon bell lyukakat eredm-
nyez. Szmos gpen a sizeof(address) pldul 24, nem pedig 22, ahogy az elvrhat len-
ne. Az elpazarolt helyet a lehet legkevesebbre cskkenthetjk, ha egyszeren mret sze-
rint rendezzk a struktra tagjait (a legnagyobb tag lesz az els). A legjobb azonban az, ha
olvashatsg szerint rendezzk sorba a tagokat, s csak akkor mret szerint, ha bizonytot-
tan szksg van optimalizlsra.
Alapok 136
Egy tpus neve rgtn felhasznlhat attl a ponttl, ahol elszr megjelenik, nem csak
a teljes deklarci utn:
struct Link {
Link* previous;
Link* successor;
};
A struktra teljes deklarcijnak vgig viszont nem adhatunk meg jabb ilyen tpus ob-
jektumokat:
struct No_good {
No_good member; // hiba: rekurzv definci
};
Ez azrt hibs, mert a fordtprogram nem kpes eldnteni a No_good mrett. Kt (vagy
tbb) struktra-tpus klcsns hivatkozshoz adjunk meg pldul egy nevet, amely a t-
pus neve:
struct List; // ksbb meghatrozand
struct Link {
Link* pre;
Link* suc;
Link* member_of;
};
struct List {
Link* head;
};
A List els deklarcija nlkl a List hasznlata a Link deklarcijban formai hibt okozott
volna. A struktra-tpus neve a tpus meghatrozsa eltt is felhasznlhat, feltve, hogy ez
a hasznlat nem ignyli egy tag nevnek vagy a struktra mretnek ismerett:
class S; // 'S' valamilyen tpus neve
extern S a;
S f();
void g(S);
S* h(S*);
5. Mutatk, tmbk s struktrk 137
A fenti deklarcik kzl azonban sok nem hasznlhat, hacsak meg nem adjuk az S tpust:
void k(S* p)
{
S a; // hiba: S nem definilt; a helyfoglalshoz mret kell
f(); // hiba: S nem definilt; rtk visszaadshoz mret kell
g(a); // hiba: S nem definilt; paramter tadshoz mret kell
p->m = 7; // hiba: S nem definilt; a tag neve nem ismert
S* q = h(p); // rendben: a mutatk szmra foglalhat hely s t is adhatk
q->m = 7; // hiba: S nem definilt; a tag neve nem ismert
}
A struct az osztly (10. fejezet) egyszer formja.
A C trtnetre visszanyl okok miatt ugyanazzal a nvvel s ugyanabban a hatkrben
megadhatunk egy struct-ot s egy nem struktra jelleg tpust is:
struct stat { /* ... */ };
int stat(char* name, struct stat* buf);
Ebben az esetben a sima stat nv a nem-struktra neve, az adatszerkezetre pedig a struct
eltaggal kell hivatkoznunk. Eltagknt a class, union (C.8.2) s enum (4.8) kulcsszavak
is hasznlhatk, ezekkel elkerlhetjk a ktrtelmsget. A legjobb azonban, ha nem ter-
heljk tl a neveket.
5.7.1. Egyenrtk tpusok
Kt struktra mindig klnbz tpus, akkor is, ha tagjaik ugyanazok:
struct S1 { int a; };
struct S2 { int a; };
A fenti kt tpus klnbz, gy
S1 x;
S2 y = x; // hiba: nem megfelel tpus
Alapok 138
A struktra-tpusok az alaptpusoktl is klnbznek, ezrt
S1 x;
int i = x; // hiba: nem megfelel tpus
Minden struct-nak egyrtelm meghatrozsa kell, hogy legyen a programban (9.2.3.).
5.8. Tancsok
[1] Kerljk a nem magtl rtetd mutat-aritmetikt. 5.3.
[2] gyeljnk arra, hogy ne rjunk egy tmb indexhatrn tlra. 5.3.1.
[3] Hasznljunk 0-t NULL helyett. 5.1.1.
[4] Hasznljuk a vector-t s a valarray-t a beptett (C stlus) tmbk helyett.
5.3.1.
[5] Hasznljunk string-et nulla vgzds karaktertmbk helyett. 5.3.
[6] Hasznljunk a lehet legkevesebb egyszer referencia-paramtert. 5.5.
[7] Az alacsonyszint kdot kivve kerljk a void*-ot. 5.6.
[8] Kerljk a kdban a nem magtl rtetd literlokat (mgikus szmokat).
Hasznljunk helyettk jelkpes llandkat. 4.8, 5.4.
5.9. Gyakorlatok
1. (*1) Vezessk be a kvetkezket: karakterre hivatkoz mutat, 10 egszbl ll
tmb, 10 egszbl ll tmb referencija, karakterlncokbl ll tmbre hivat-
koz mutat, karakterre hivatkoz mutatra hivatkoz mutat, konstans egsz,
konstans egszre hivatkoz mutat, egszre hivatkoz konstans mutat. Mind-
egyiknek adjunk kezdeti rtket.
2. (*1,5) Mik a char*, int*, s void* mutattpusokra vonatkoz megszortsok
a mi rendszernkn? Lehetne-e pldul egy int*-nak furcsa rtke? Segtsg:
illeszts.
5. Mutatk, tmbk s struktrk 139
3. (*1) Hasznljunk typedef-et a kvetkezk meghatrozsra: unsigned char,
const unsigned char, egszre hivatkoz mutat, karakterre hivatkoz mutatra
hivatkoz mutat, karaktertmbkre hivatkoz mutat; 7 elem, egszre hivat-
koz mutatkbl ll tmb; 7 elem, egszre hivatkoz mutatkbl ll tmbre
hivatkoz mutat; egszre hivatkoz mutatkat tartalmaz 7 elem tmbkbl
ll 8 elem tmb.
4. (*1) rjunk egy swap nev fggvnyt, amely kt egszt cserl fel. Hasznljunk
int* tpust a paramterek tpusaknt. rjunk egy msik swap-et is, melynek para-
mterei int& tpusak.
5. (*1,5) Mi az str tmb mrete a kvetkez pldban?
char str[ ] = "rvid karakterlnc";
Mi a "rvid karakterlnc" hossza?
6. (*1) Ksztsk el az f(char), g(char&) s h(const char&) fggvnyeket. Hvjuk
meg ket az 'a', 49, 3300, c, uc s sc paramterekkel, ahol c char, uc unsigned
char s sc signed char tpus. Mely hvsok megengedettek? Mely hvsoknl
vezet be a fordtprogram ideiglenes vltozt?
7. (*1,5) Ksztsnk egy tblzatot, amely a hnapok neveibl s napjaik szm-
bl ll. rjuk ki a tblzatot. Csinljuk meg mindezt ktszer: egyszer hasznljunk
karaktertmbt a nevek s egy tmbt a napok szmra, msodszor hasznl-
junk struktrkbl ll tmbt, ahol az egyes adatszerkezetek a hnap nevt s
a benne lev napok szmt troljk.
8. (*2) Futtassunk le nhny tesztet, hogy megnzzk, a fordtprogram tnyleg
egyenrtk kdot hoz-e ltre a mutatk hasznlatval s az indexelssel val
tmbbejrshoz (5.3.1). Ha klnbz mrtk optimalizlst lehet hasznlni,
nzzk meg, hat-e s hogyan hat ez a ltrehozott kd minsgre.
9. (*1,5) Talljunk pldt, hol lenne rtelme egy nevet a sajt kezdrtkben
hasznlni.
10. (*1) Adjunk meg egy karakterlncokbl ll tmbt, ahol a karakterlncok
a hnapok neveit tartalmazzk. rjuk ki ezeket. Adjuk t a tmbt egy fgg-
vnynek, amely kirja a karakterlncokat.
11. (*2) Olvassuk be a bemenetrl szavak egy sorozatt. A bemenetet lezr sz-
knt hasznljuk a Quit-et. rjuk ki a beolvasott szavakat. Ne rjuk ki ktszer
ugyanazt a szt. Mdostsuk a programot, hogy rendezze a szavakat, mieltt
kirn azokat.
12. (*2) rjunk olyan fggvnyt, amely megszmolja egy betpr elfordulsait egy
karakterlncban, s egy msikat, ami ugyanezt csinlja egy nulla vg karakter-
tmbben (vagyis egy C stlus karakterlncban). Az "ab" pr pldul ktszer
szerepel az "xabaacbaxabb"-ben.
13. (*1,5) Adjunk meg egy Date struktrt dtumok brzolshoz. rjunk olyan
fggvnyt, ami Date-eket olvas be a bemenetrl, olyat, ami Date-eket
r a kimenetre, s olyat, ami egy dtummal ad kezdrtket a Date-nek.
Alapok 140
Kifejezsek s utastsok
Az id eltti optimalizls
minden rossz gykere.
(D. Knuth)
Msrszrl, nem hagyhatjuk
figyelmen kvl
a hatkonysgot.
(John Bentley)
Asztali szmolgp plda Bemenet Parancssori paramterek Kifejezsek (ttekin-
ts) Logikai s sszehasonlt opertorok Nvels s cskkents Szabad tr Meg-
hatrozott tpuskonverzik Utastsok (ttekints) Deklarcik Elgaz utastsok
Deklarcik a felttelekben Ciklusutastsok A hrhedt goto Megjegyzsek s beh-
zs Tancsok Gyakorlatok
6
6.1. Egy asztali szmolgp
A kifejezseket s utastsokat egy asztali szmolgp pldjn keresztl mutatjuk be.
A szmolgp a ngy aritmetikai alapmveletet hajtja vgre lebegpontos rtkeken. A m-
veleti jeleket a szmok kztt (infix opertorknt) kell megadni. A felhasznl vltozkat is
megadhat. A bemenet legyen a kvetkez:
r = 2.5
area = pi * r * r
A szmolgp program az albbiakat fogja kirni (pi elre meghatrozott):
2.5
19.635
A 2.5 a bemenet els sornak, a 19.635 a bemenet msodik sornak eredmnye.
A szmolgp ngy f rszbl ll: egy elemzbl (parser), egy adatbeviteli fggvnybl,
egy szimblumtblbl s egy vezrlbl. Valjban ez egy miniatr fordtprogram,
amelyben az elemz vgzi a szintaktikai elemzst (vagyis a nyelvi utastsok formai elem-
zst), az adatbeviteli fggvny kezeli a bemenetet s vgzi a lexikai elemzst (vagyis
a nyelvi elemek rtelmezst), a szimblumtbla tartalmazza a nem vltoz adatokat s
a vezrl kezeli a kezdeti rtkadst, a kimenetet s a hibkat. A szmolgpet szmos szol-
gltatssal bvthetjk, hogy mg hasznosabb tegyk (6.6[20]), de a kd gy is elg hossz
lesz, s a legtbb szolgltats csak a kdot nveln, anlkl, hogy tovbbi betekintst nyj-
tana a C++ hasznlatba.
6.1.1. Az elemz
me a szmolgp ltal elfogadott nyelvtan:
program:
END // END a bevitel vge
expr_list END
expr_list:
expression PRINT // PRINT a pontosvessz
expression PRINT expr_list
Alapok 142
expression:
expression + term
expression - term
term
term:
term / primary
term * primary
primary
primary:
NUMBER
NAME
NAME = expression
- primary
( expression )
Ms szval, a program kifejezsek sorozata, amelyeket pontosvesszk vlasztanak el egy-
mstl. A kifejezsek alapelemei a szmok, nevek s a *, /, +, - (akr egy, akr kt
operandus) opertorok, s az = . A neveket nem kell hasznlat eltt definilni.
Az ltalunk hasznlt szintaktikai elemzs mdszert rendszerint rekurzv leszllsnak
(recursive descent) nevezik; npszer s lnyegretr, fellrl lefel halad eljrs. Egy
olyan nyelvben, mint a C++, amelyben a fggvnyhvsok viszonylag kis kltsgek",
a mdszer hatkony is. A nyelvtan minden szablyra adunk egy fggvnyt, amely ms
fggvnyeket hv meg. A lezr szimblumokat (pldul az END, NUMBER, + s -)
a get_token() lexikai elemz, a nem lezr szimblumokat pedig az expr(), term() s prim()
szintaktikai elemz fggvnyek ismerik fel. Ha egy (rsz)kifejezs minkt operandusa is-
mert, a kifejezs kirtkeldik egy igazi fordtprogram esetben ezen a ponton trtn-
hetne a kd ltrehozsa. Az elemz a get_token() fggvnyt hasznlja arra, hogy bemene-
tet kapjon. Az utols get_token() hvs eredmnye a curr_tok globlis vltozban tallhat.
A curr_tok vltoz tpusa Token_value felsorol tpus:
enum Token_value {
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
Token_value curr_tok = PRINT;
6. Kifejezsek s utastsok 143
Az, hogy minden szimblumot (token) a karakternek megfelel egsz rtkkel jellnk,
knyelmes s hatkony megolds, s segtheti azokat, akik hibakerest (debugger) hasz-
nlnak. Ez a mdszer addig mkdik, amg bemenetknt olyan karaktert nem adunk meg,
melynek rtkt felsorol konstansknt mr hasznljuk. n pedig nem tudok olyan karak-
terkszletrl, amelyben van olyan kirhat karakter, melynek egsz rtke egy szmjegy.
Azrt vlasztottam a curr_tok kezdeti rtkeknt a PRINT-et, mert a curr_tok ezt az rtket
fogja felvenni, miutn a szmolgp kirtkelt egy kifejezst s kirta annak rtkt. gy
alapllapotban indtjuk el a rendszert, a lehet legkisebbre cskkentjk annak az eslyt,
hogy hibk forduljanak el s egyedi indtkdra sincs szksgnk.
Minden elemz fggvnynek van egy logikai (bool) (4.2) paramtere, amely jelzi, hogy
meg kell-e hvnia a get_token()-t a kvetkez szimblum beolvasshoz. A fggvny kir-
tkeli a sajt kifejezst s visszaadja az rtkt. Az expr() fggvny kezeli az sszeadst
s kivonst. A fggvny egyetlen ciklusbl ll, amely elemeket (term) keres az sszeads-
hoz vagy kivonshoz:
double expr(bool get) // sszeads s kivons
{
double left = term(get);
for (;;) // "rkk" (vgtelen ciklus)
switch (curr_tok) {
case PLUS:
left += term(true);
break;
case MINUS:
left -= term(true);
break;
default:
return left;
}
}
Ez a fggvny nmagban nem csinl tl sokat. Egy nagyobb program magasabb szint
fggvnyeihez hasonl mdon ms fggvnyeket hv meg a feladat elvgzshez.
A switch utasts azt vizsglja meg, hogy a switch kulcssz utn zrjelben megadott felttel
rtke megegyezik-e a konstansok valamelyikvel. A break-kel a switch utastsbl lphe-
tnk ki. A case cmkket kvet konstansoknak klnbznik kell egymstl. Ha a vizsglt
rtk nem egyezik egyik case cmkvel sem, a default cmke vlasztdik ki. A programoz-
nak nem ktelez megadnia a default rszt.
Alapok 144
Figyeljk meg, hogy a 2-3+4-hez hasonl kifejezsek (2-3)+4-knt rtkeldnek ki, ahogy
azt a nyelvtanban meghatroztuk.
A furcsa for( ; ; ) jells megszokott mdja annak, hogy vgtelen ciklust rjunk; gy mond-
hatjuk ki, hogy rkk (forever). Ez a for utasts (6.3.3) vgletes formja; helyette hasz-
nlhatjuk a while(true) szerkezetet is. A switch utasts vgrehajtsa addig ismtldik, amg
nem tall a +-tl s - -tl klnbz jelet, amikor is a default cmke utni return utasts
hajtdik vgre.
Az sszeads s kivons kezelsre a += s -= opertorokat hasznljuk. Hasznlhatnnk
a left=left+term(true) s left=left-term(true) formt is, a program jelentse nem vltozna.
A left+=term(true) s a left-=term(true) azonban nemcsak rvidebbek, hanem kzvetleneb-
bl is fejezik ki a kvnt mveletet. Minden rtkad opertor nll nyelvi egysg, gy a +
= 1 nyelvtanilag hibs a + s az = kztti szkz miatt.
A kvetkez ktoperandus mveletekhez lteznek rtkad opertorok:
+ - * / % & | ^ << >>
gy a kvetkez rtkad opertorok lehetsgesek:
= += -= *= /= %= &= |= ^= <<= >>=
A % a modul vagy maradkkpz opertor; &, |, s ^ a bitenknti S, VAGY, illetve kiz-
r VAGY opertorok; << s >> pedig a balra s jobbra lptet opertorok. A mveleti jele-
ket s jelentsket 6.2 foglalja ssze. Ha @ egy binris (ktoperandus) opertor, akkor
x@=y jelentse x=x@y, azzal a klnbsggel, hogy x csak egyszer rtkeldik ki.
A 8. s a 9. fejezet trgyalja, hogyan ptsnk fel programot modulokbl. A szmolgp pl-
da deklarciit egy kivtellel gy rendezhetjk sorba, hogy mindent csak egyszer s
hasznlat eltt adunk meg. A kivtel az expr(), ami meghvja a term()-et, ami meghvja
a prim()-et, ami pedig ismt meghvja az expr()-et. Ezt a krt valahol meg kell szaktanunk.
A prim() meghatrozsa eltti deklarci erre val.
double expr(bool);
A term() fggvny ugyanolyan mdon kezeli a szorzst s osztst, mint ahogy az expr() ke-
zeli az sszeadst s kivonst:
6. Kifejezsek s utastsok 145
double term(bool get) // szorzs s oszts
{
double left = prim(get);
for (;;)
switch (curr_tok) {
case MUL:
left *= prim(true);
break;
case DIV:
if (double d = prim(true)) {
left /= d;
break;
}
return error("Nullval nem lehet osztani");
default:
return left;
}
}
A nullval val oszts nem meghatrozott s rendszerint vgzetes hibt okoz. Ezrt oszts
eltt megnzzk, hogy a nevez 0 -e, s ha igen, meghvjuk az error()-t. Az error() fgg-
vnyt a 6.1.4-ben ismertetjk. A d vltozt pontosan azon a ponton vezetjk be a program-
ba, ahol az szksges, s rgtn kezdeti rtket is adunk neki. Egy felttelben bevezetett
nv hatkre a felttel ltal vezrelt utasts, az eredmnyezett rtk pedig a felttel rtke
(6.3.2.1). Kvetkezskppen a left/=d oszts s rtkads csak akkor megy vgbe, ha d
nem nulla.
A prim() fggvny, amely az elemi szimblumokat kezeli, nagyban hasonlt az expr()-re s
a term()-re, kivve azt, hogy mivel mr lejjebb rtnk a hvsi hierarchiban, nmi valdi
munkt kell vgezni s nincs szksg ciklusra:
double number_value;
string string_value;
double prim(bool get) // elemi szimblumok kezelse
{
if (get) get_token();
switch (curr_tok) {
case NUMBER: // lebegpontos konstans
{ double v = number_value;
get_token();
return v;
}
Alapok 146
case NAME:
{ double& v = table[string_value];
if (get_token() == ASSIGN) v = expr(true);
return v;
}
case MINUS: // egyoperandus mnusz
return -prim(true);
case LP:
{ double e = expr(true);
if (curr_tok != RP) return error(") szksges");
get_token(); // ')' lenyelse
return e;
}
default:
return error("elemi szimblum szksges");
}
}
Amikor egy NUMBER-t (azaz egy egsz vagy lebegpontos literlt) tallunk, visszaadjuk az
rtkt. A get_token() bemeneti eljrs elhelyezi az rtket a number_value globlis vlto-
zban. Globlis vltoz hasznlata a kdban gyakran jelenti, hogy a program szerkezete
nem kristlytiszta valamifle optimalizcit alkalmaztak r. Itt is ez trtnt. Idelis esetben
egy nyelvi egysg (lexikai szimblum) kt rszbl ll: egy rtkbl, amely meghatrozza
a szimblum fajtjt (ebben a programban ez a Token_value) s (ha szksges) a token r-
tkbl. Itt csak egy egyszer curr_tok vltoz szerepel, gy a number_value globlis vl-
toz szksges ahhoz, hogy az utols beolvasott NUMBER rtkt trolja. E ktes szerep
globlis vltoz kikszblst is a feladatok kz tzzk ki (6.6[21]). A number_value r-
tkt nem felttlenl szksges a v loklis vltozba menteni a get_token() meghvsa eltt.
A szmolgp a szmtshoz minden helyes bemenetnl hasznlatba veszi az els szmot,
mieltt egy msikat olvasna be, hiba esetn viszont segtheti a felhasznlt, ha mentjk az
rtket s helyesen kirjuk. Hasonlan ahhoz, ahogy az utols beolvasott NUMBER-t
a number_value trolja, az utols beolvasott NAME karakterlncot a string_value tartalmaz-
za. Mieltt a szmolgp brmit kezdene egy nvvel, meg kell nznie, hogy a nevet rt-
kl kell-e adnia vagy csak egyszeren be kell olvasnia. Mindkt esetben a szimblumtbl-
hoz fordul. A szimblumtbla egy map (3.7.4, 17.4.1):
map<string,double> table;
Azaz, amikor a table-t egy karakterlnccal indexeljk, az eredmnyl kapott rtk az
a double lesz, ami a karakterlnchoz tartozik. Tegyk fel, hogy a felhasznl a kvetkez-
ket rja be:
radius = 6378.388;
6. Kifejezsek s utastsok 147
Ekkor a szmolgp az albbiakat hajtja vgre:
double& v = table["radius"];
// ... expr() kiszmolja az tadand rtket
v = 6378.388;
A v referencit hasznljuk arra, hogy a radius-hoz tartoz double rtkre hivatkozzunk,
amg az expr() a bemeneti karakterekbl kiszmtja a 6378.388 rtket.
6.1.2. A bemeneti fggvny
A bemenet beolvassa gyakran a program legrendezetlenebb rsze. Ez azrt van gy, mert
a programnak egy emberrel kell trsalognia s meg kell birkznia annak szeszlyeivel, szo-
ksaival s viszonylag vletlenszer hibival. Kellemetlen dolog (jogosan), ha megprbl-
juk a felhasznlt rknyszerteni, hogy gy viselkedjen, hogy az a gp szmra megfele-
lbb legyen. Egy alacsonyszint beolvas eljrs feladata az, hogy karaktereket olvasson be
s magasabb szint szimblumokat hozzon ltre bellk. Ezek a szimblumok ksbb
a magasabb szint eljrsok bemeneti egysgei lesznek. Itt az alacsonyszint beolvasst
a get_token() vgzi. Nem felttlenl mindennapi feladat alacsonyszint bemeneti eljrso-
kat rni. Sok rendszer erre a clra szabvnyos fggvnyeket nyjt.
Kt lpsben ptem fel a get_token()-t. Elszr egy megtveszten egyszer vltozatot k-
sztek, amely komoly terhet r a felhasznlra. Ezutn ezt mdostom egy kevsb elegns,
de jval hasznlhatbb vltozatra.
Az tlet az, hogy beolvasunk egy karaktert, felhasznljuk arra, hogy eldntsk, milyen
szimblumot kell ltrehozni, majd visszaadjuk a beolvasott token-t brzol Token_value
rtket. A kezdeti utastsok beolvassk az els nem reshely (whitespace, azaz szkz,
tabultor, j sor stb.) karaktert ch-ba, s ellenrzik, hogy az olvassi mvelet sikerlt-e:
Token_value get_token()
{
char ch = 0;
cin>>ch;
switch (ch) {
case 0:
return curr_tok=END; // rtkads s visszatrs
Alapok 148
Alaprtelmezs szerint a >> opertor tugorja az reshely karaktereket s ch rtkt vlto-
zatlanul hagyja, ha a bemeneti mvelet nem sikerl. Kvetkezskppen ch==0 a bemenet
vgt jelzi.
Az rtkads egy opertor, az rtkads eredmnye pedig annak a vltoznak az rtke,
melynek rtket adunk. Ez megengedi, hogy a curr_tok vltoznak az END-et adjam rt-
kl, majd a vltozt ugyanabban az utastsban adjam vissza. Az, hogy egy utastst hasz-
nlunk kett helyett, megknnyti a kd ksbbi mdostst. Ha az rtkadst s a vissza-
adott rtket klnvlasztannk a kdban, lehet, hogy a programoz megvltoztatn az
egyiket, de elfelejten mdostani a msikat.
Nzznk meg nhny esetet kln-kln, mieltt a teljes fggvnnyel foglalkoznnk. A ki-
fejezsek ; vgzdst, a zrjeleket s az opertorokat gy kezeljk, hogy egyszeren
visszaadjuk az rtkket:
case ';':
case '*':
case '/':
case '+':
case '-':
case '(':
case ')':
case '=':
return curr_tok=Token_value(ch);
A szmokat gy kezeljk:
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.':
cin.putback(ch);
cin >> number_value;
return curr_tok=NUMBER;
A case cmkket fggleges helyett vzszintesen egy kupacba tenni ltalban nem j tlet,
mert ez az elrendezs nehezebben olvashat. Fraszt lenne azonban minden szmjegyet
kln sorba rni. Mivel a >> mveleti jel a lebegpontos konstansokat szablyszeren egy
double tpus vltozba olvassa, a kd magtl rtetd. Elszr a kezd karaktert (szm-
jegyet vagy pontot) visszatesszk a cin-be, majd a konstanst a number_value vltozba
helyezzk.
6. Kifejezsek s utastsok 149
A neveket hasonlan kezeljk:
default: // NAME, NAME =, vagy hiba
if (isalpha(ch)) {
cin.putback(ch);
cin>>string_value;
return curr_tok=NAME;
}
error("rossz szimblum");
return curr_tok=PRINT;
A standard knyvtrban lev isalpha() fggvnyt (20.4.2) hasznljuk arra, hogy ne kelljen
minden karaktert felsorolnunk, mint klnbz case cmkket. A karakterlncok (ebben az
esetben a string_value) >> mvelete addig olvassa a lncot, amg reshelyet nem tall. K-
vetkezskppen a felhasznlnak szkzzel kell befejeznie az adott nevet azon opertorok
eltt, melyek a nevet operandusknt hasznljk. Ez nem idelis megolds, ezrt erre
a problmra mg visszatrnk 6.1.3-ban.
me a teljes bemeneti fggvny:
Token_value get_token()
{
char ch = 0;
cin>>ch;
switch (ch) {
case 0:
return curr_tok=END;
case ';':
case '*':
case '/':
case '+':
case '-':
case '(':
case ')':
case '=':
return curr_tok=Token_value(ch);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case '.':
cin.putback(ch);
cin >> number_value;
return curr_tok=NUMBER;
Alapok 150
default: // NAME, NAME =, vagy hiba
if (isalpha(ch)) {
cin.putback(ch);
cin>>string_value;
return curr_tok=NAME;
}
error("rossz szimblum");
return curr_tok=PRINT;
}
}
Egy opertor talaktsa az opertornak megfelel szimblumra magtl rtetd, mivel az
opertorok token_value rtkt az opertor egsz rtkeknt hatroztuk meg (4.8).
6.1.3. Alacsonyszint bemenet
Ha a szmolgpet gy hasznljuk, ahogy az eddigiekben lertuk, fny derl nhny k-
nyelmetlen dologra. Fraszt emlkezni arra, hogy pontosvesszt kell tennnk egy kifeje-
zs utn, ha ki akarjuk ratni az rtkt, s nagyon bosszant tud lenni, hogy csak
reshellyel lehet egy nevet befejezni. Pldul x=7 egy azonost, s nem x, amit az = ope-
rtor s a 7-es szm kvet. Mindkt problmt gy oldjuk meg, hogy a get_token()-ben a t-
pussal kapcsolatos alaprtelmezett bemeneti mveleteket olyan kdra cserljk, amely
egyenknt olvassa be a karaktereket. Elszr is, az j sor karaktert azonosknt kezeljk
a kifejezs vgt jelz pontosvesszvel:
Token_value get_token()
{
char ch;
do { // reshelyek tugrsa az '\n' kivtelvel
if(!cin.get(ch)) return curr_tok = END;
} while (ch!='\n' && isspace(ch));
switch (ch) {
case ';':
case '\n':
return curr_tok=PRINT;
A do utastst hasznljuk, amely egyenrtk a while utastssal, kivve, hogy a ciklusmag
mindig legalbb egyszer vgrehajtdik. A cin.get(ch) beolvas egy karaktert a szabvnyos be-
meneti adatfolyambl ch-ba. Alaprtelmezs szerint a get() nem ugorja t az reshelyeket
6. Kifejezsek s utastsok 151
gy, ahogy a >> mvelet teszi. Az if(!cin.get(ch)) ellenrzs sikertelen, ha nem olvashat be
karakter a cin-bl; ebben az esetben END-et adunk vissza, hogy befejezzk a szmolgp
mkdst. A ! (NEM) opertort azrt hasznljuk, mert a get() igazat ad vissza, ha sikeres.
A standard knyvtr isspace() fggvnye vgzi az reshelyek (20.4.2) szabvnyos vizsg-
latt. Ha c reshely, az isspace(c) nem nulla rtket ad vissza, ms esetben nullt. A vizsg-
latot tblzatban val keressknt valstjuk meg, gy az isspace() hasznlata sokkal gyor-
sabb, mint az egyes reshely karakterek vizsglata. Hasonl fggvnyekkel nzhetjk meg,
hogy egy karakter szmjegy (isdigit()), bet (isalpha()), esetleg bet vagy szm-e
(isalnum()).
Miutn tugrottuk az reshelyeket, a kvetkez karaktert arra hasznljuk, hogy eldntsk,
mifle nyelvi egysg jn. A problmt, amit az okoz, hogy a >> addig olvassa a karakter-
lncot, amg reshelyeket nem tall, gy oldjuk meg, hogy egyszerre egy karaktert olva-
sunk be, amg olyan karaktert nem tallunk, ami nem szm s nem bet:
default: // NAME, NAME=, vagy hiba
if (isalpha(ch)) {
string_value = ch;
while (cin.get(ch) && isalnum(ch)) string_value.push_back(ch);
cin.putback(ch);
return curr_tok=NAME;
}
error("rossz szimblum");
return curr_tok=PRINT;
Szerencsre mindkt javts elvgezhet gy, hogy a kdnak csak egyes helyi rvnyess-
g rszeit mdostjuk. Fontos tervezsi cl, hogy olyan programokat hozzunk ltre, melyek
javtst, fejlesztst helyi mdostsokkal intzhetjk.
6.1.4. Hibakezels
Mivel a program ennyire egyszer, a hibakezelssel nem kell komolyabban trdnnk.
Az error fggvny egyszeren megszmolja a hibkat, kir egy hibazenetet, s visszatr:
int no_of_errors;
double error(const string& s)
{
no_of_errors++;
cerr << "hiba: " << s << '\n';
return 1;
}
Alapok 152
A cerr egy tmeneti trba nem helyezett (nem pufferelt) kimeneti adatfolyam, amely rend-
szerint hibajelzsre hasznlatos (21.2.1).
Azrt adunk vissza rtket, mert a hibk jellemzen valamilyen kifejezs kirtkelse kz-
ben trtnnek, gy vagy teljesen abba kellene hagynunk a kirtkelst, vagy olyan rtket
kellene visszaadnunk, amely nem valszn, hogy tovbbi hibkat okozna. Ezen egyszer
szmolgp esetben az utbbi megolds megfelel. Ha a get_token() nyomon kvette vol-
na a sorok szmt, az error() tjkoztathatta volna a felhasznlt a hiba pontos helyrl,
ami akkor lenne hasznos, ha a szmolgpet nem interaktvan hasznlnnk (6.6.[19]).
A program futsnak gyakran be kell fejezdne, miutn hiba trtnt, mert nincs megadva,
milyen sszer mdon folytathatn mkdst. Ezt tehetjk meg az exit() meghvsval,
amely elszr rendbe rakja az adatfolyamokat s hasonl dolgokat, majd befejezi a progra-
mot, melynek visszatrsi rtke az exit() paramtere lesz (9.4.1.1).
Kivtelek hasznlatval elegnsabb hibakezel eljrsok kszthetk (lsd 8.3 s 14. feje-
zet), de amit most csinltunk, egy 150 soros szmolgpnek ppen megfelel.
6.1.5. A vezrl
Miutn a program minden rszlete a helyre kerlt, mr csak a vezrl kdra van szksgnk
ahhoz, hogy elindtsuk a mkdst. Ebben az egyszer pldban ezt a main() vgzi el:
int main()
{
table["pi"] = 3.1415926535897932385; // elre megadott nevek beillesztse
table["e"] = 2.7182818284590452354;
while (cin) {
get_token();
if (curr_tok == END) break;
if (curr_tok == PRINT) continue;
cout << expr(false) << '\n';
}
return no_of_errors;
}
Hagyomny szerint a main() 0-t kell, hogy visszaadjon, ha a program hiba nlkl r v-
get, ms esetben nem nullt (3.2). A hibk szmnak visszaadsval ezt szpen megold-
6. Kifejezsek s utastsok 153
juk. Ebben az esetben az egyetlen szksges elkszts az, hogy a szimblumtblba be-
le kell tennnk az elre megadott neveket. A f ciklus feladata, hogy beolvassa a kifejez-
seket s kirja a vlaszt. Ezt a kvetkez sor oldja meg:
cout << expr(false) << '\n';
A false paramter mondja meg az expr()-nek, hogy nem kell meghvnia a get_token()-t ah-
hoz, hogy egy jabb szimblumot kapjon, amellyel dolgozhat.
A cin ciklusonknt egyszeri ellenrzse biztostja, hogy a program befejezdik, ha hiba tr-
tnik a bemeneti adatfolyammal, az END vizsglata pedig arrl gondoskodik, hogy a ciklus-
bl megfelelen lpjnk ki, ha a get_token() a fjl vghez r. A break utasts a legkze-
lebbi krlvev switch utastsbl vagy ciklusbl (azaz for, while vagy do utastsbl) lp
ki. A PRINT (azaz '\n' s ';') vizsglata megknnyti az expr() dolgt az res kifejezsek ke-
zelsben. A continue utasts egyenrtk azzal, hogy a ciklus legvgre ugrunk, gy eb-
ben az esetben
while (cin) {
// ...
if (curr_tok == PRINT) continue;
cout << expr(false) << '\n';
}
megegyezik a kvetkezvel:
while (cin) {
// ...
if (curr_tok != PRINT)
cout << expr(false) << '\n';
}
6.1.6. Fejllomnyok
A szmolgp a standard knyvtr eszkzeit hasznlja. Ezrt a megfelel fejllomnyokat
(header) be kell ptennk (#include), hogy befejezzk a programot:
#include<iostream> // bemenet/kimenet
#include<string> // karakterlncok
#include<map> // asszociatv tmb
#include<cctype> // isalpha(), stb.
Alapok 154
Ezen fejllomnyok mindegyike az std nvtrben nyjt szolgltatsokat, gy ahhoz, hogy az
ltaluk nyjtott neveket felhasznlhassuk, vagy az std:: minstt kell hasznlnunk, vagy
a globlis nvtrbe kell helyeznnk a neveket a kvetkezkppen:
using namespace std;
n az utbbit vlasztottam, hogy ne keverjem ssze a kifejezsek trgyalst a modularits
krdskrvel. A 8. s a 9. fejezet trgyalja, milyen mdon lehet ezt a szmolgpet a nv-
terek hasznlatval modulokba szervezni s hogyan lehet forrsfjlokra bontani. A szabv-
nyos fejllomnyoknak szmos rendszeren .h kiterjeszts fjl megfeleljk van, melyek le-
rjk az osztlyokat, fggvnyeket stb. s a globlis nvtrbe is behelyezik azokat (9.2.1,
9.2.4, B.3.1).
6.1.7. Parancssori paramterek
Miutn a programot megrtam s kiprbltam, knyelmetlennek talltam, hogy elszr el
kell indtani a programot, aztn be kell gpelni a kifejezseket, vgl ki kell lpni. A leg-
gyakrabban egyetlen kifejezs kirtkelsre hasznltam a programot. Ha egy kifejezst
meg lehetne adni parancssori paramterknt, jnhny billentyletst megtakarthatnnk.
A program a main() (3.2., 9.4.) meghvsval kezddik, amely kt paramtert kap: az
egyik, melyet ltalban argc-nek neveznek, a paramterek (argumentumok) szmt adja
meg, a msik a paramterekbl ll tmb, ezt rendszerint argv-nek hvjk. A paramterek
karakterlncok, ezrt argv tpusa char*[argc+1] lesz. A program neve (ahogy az a parancs-
sorban elfordul) argc[0]-knt addik t, gy argc rtke mindig legalbb 1. A paramterek
listjt a null karakter zrja le, gy argv[argc]==0. Vegyk az albbi parancsot:
dc 150/1.1934
Ekkor a paramterek rtke a kvetkez:
6. Kifejezsek s utastsok 155
argc:
argv:
2
"dc" "150/1.1934"
0
Mivel a main() meghvsra vonatkoz szablyok a C nyelv kvetelmnyeivel azonosak,
a hvskor C tpus tmbk s karakterlncok hasznlatosak.
A parancssori paramterek beolvassa egyszer, a problma csak az, hogyan hasznljuk
azokat gy, hogy minl kevesebbet kelljen programoznunk. Az tlet a kvetkez: olvas-
sunk ugyangy a parancssori karakterlncbl, mint a bemeneti adatfolyamokbl. A karak-
terlncbl olvas adatfolyam neve micsoda meglepets istringstream. Sajnos nincs
elegns mdja annak, hogy cin-knt az istringstream-re hivatkozhassunk, ezrt ki kell ta-
llnunk, hogy a szmolgp bemeneti fggvnyei hogyan hivatkozzanak vagy az
istringstream-re vagy a cin-re, attl fggen, milyen parancssori paramtereket adunk meg.
Egyszer megolds, ha bevezetnk egy input nev globlis mutatt, amely a hasznland
bemeneti adatfolyamra mutat; minden bemeneti eljrsban ezt fogjuk felhasznlni:
istream* input; // mutat bemeneti adatfolyamra
int main(int argc, char* argv[ ])
{
switch (argc) {
case 1: // olvass a szabvnyos bemenetrl
input = &cin;
break;
case 2: // a karakterlnc paramter beolvassa
input = new istringstream(argv[1]);
break;
default:
error("tl sok paramter");
return 1;
}
table["pi"] = 3.1415926535897932385; // elre megadott nevek beillesztse
table["e"] = 2.7182818284590452354;
while (*input) {
get_token();
if (curr_tok == END) break;
if (curr_tok == PRINT) continue;
cout << expr(false) << '\n';
}
if (input != &cin) delete input;
return no_of_errors;
}
Alapok 156
Az istringstream olyan istream, amely karakterlnc paramterbl olvas (21.5.3). Amikor
elri a lnc vgt, pontosan ugyangy jelzi azt, mint a tbbi adatfolyam a bemenet vgt
(3.6, 21.3.3). Az istringstream hasznlathoz be kell pteni az <sstream> fejllomnyt.
Knny lenne gy mdostani a main()-t, hogy tbb parancssori paramtert is elfogadjon,
de erre nincs szksg, mert egyetlen paramterknt tbb kifejezst is tadhatunk:
dc "rate=1.1934;150/rate;19.75/rate;217/rate"
Azrt hasznlok idzjeleket, mert a ; a UNIX rendszerekben parancs-elvlasztknt hasz-
nlatos. Ms rendszerek szablyai a program indtsakor paramterek megadsra vonatko-
zan eltrek.
Nem volt elegns dolog gy mdostani a bemeneti eljrsokat, hogy cin helyett *input-ot
hasznljanak, hogy ezzel rugalmasabbak legyenek s klnbz bemeneti forrsokkal m-
kdhessenek. A vltoztats elkerlhet lett volna, ha kell elreltssal mr a kezdetektl
bevezetnk valamilyen, az input-hoz hasonl dolgot. ltalnosabb s hasznosabb megol-
dst kszthetnk, ha szrevesszk, hogy a bemenet forrsa valjban a szmolgp modul
paramtere kell, hogy legyen. Az alapvet problma, amit ezzel a szmolgppel rzkel-
tetni akartam, az, hogy a szmolgp csak fggvnyek s adatok gyjtemnye. Nincs
olyan modul (2.4) vagy objektum (2.5.2), amely kifejezett s egyrtelm mdon brzol-
ja a szmolgpet. Ha egy szmolgp modul vagy szmolgp tpus tervezse lett volna
a clom, akkor termszetesen meggondoltam volna, milyen paramterei lehetnek a modul-
nak/tpusnak (8.5[3], 10.6[16]).
6.1.8. Megjegyzs a stlussal kapcsolatban
A standard knyvtrbeli map szimblumtblaknt val hasznlata majdnem csalsnak
tnhet azoknak a programozknak a szemben, akik nem ismerik az asszociatv tmbket.
De nem az. A standard knyvtr s ms knyvtrak arra valk, hogy hasznljk azokat.
A knyvtrak tervezskor s megvalstskor ltalban nagyobb figyelmet kapnak, mint
amennyit egy programoz megengedhet magnak, amikor sajt kezleg olyan kdot r,
amit csak egyetlen program hasznl fel.
Ha megnzzk a szmolgp kdjt (klnsen az els vltozatot), lthatjuk, hogy nem
sok hagyomnyos C stlus, alacsonyszint kd tallhat benne. Szmos hagyomnyos
trkkt helyettestettnk azzal, hogy olyan standard knyvtrbeli osztlyokat hasznltunk,
mint az ostream, string, s map (3.4, 3.5, 3.7.4, 17.fejezet).
6. Kifejezsek s utastsok 157
Vegyk szre, hogy az aritmetika, a ciklusok, st az rtkadsok is viszonylag ritkn fordul-
nak el. ltalban ilyennek kellene lennie egy olyan kdnak, amely nem kezeli a hardvert
kzvetlenl s nem l alacsonyszint elvont adatbrzolsokkal.
6.2. Opertorok ttekints
Ez a rsz sszefoglalja a kifejezseket s bemutat nhny pldt. Minden opertort egy vagy
tbb nv kvet, amely pldaknt szolgl az ltalnosan hasznlt megnevezsekre s a szo-
ksos hasznlatra. A tblzatokban az osztlynv egy osztly neve, a tag egy tag neve, az
objektum egy olyan kifejezs, amelynek az eredmnye osztlyobjektum, a mutat egy mu-
tat eredmny kifejezs, a kif egy kifejezs, s a balrtk egy olyan kifejezs, amely nem
konstans objektumot jell.
A tpus csak akkor lehet egy teljesen ltalnos tpusnv (*-gal, ()-lel stb.), ha zrjelek k-
z van zrva; mshol megszortsok vonatkoznak r (A.5).
A kifejezsek formja fggetlen az operandusok tpustl. Az itt bemutatott jelentsek arra
az esetre vonatkoznak, amikor az operandusok beptett tpusak (4.1.1). A felhasznli
tpus operandusokra alkalmazott opertorok jelentst magunk hatrozhatjuk meg (2.5.2,
11. fejezet).
A tblzat minden celljban azonos erssg (precedencij) opertorok tallhatk. A fel-
sbb cellkban lev opertorok az als cellkban levkkel szemben elnyt lveznek. Pl-
dul a+b*c jelentse a+(b*c), nem pedig (a+b)*c, mert a * magasabb precedencij, mint
a +.
Az egyoperandus (unris) s az rtkad opertorok jobbrl balra, az sszes tbbi balrl
jobbra rtelmezend. Pldul a=b=c jelentse a=(b=c), a+b+c jelentse (a+b)+c, *p++ je-
lentse pedig *(p++), nem (*p)++.
Nhny nyelvtani szablyt nem lehet kifejezni a precedencival s az asszociativitssal
(ktssel). Pldul a=b<c?d=e:f=g jelentse a=((b<c)?(d=e):(f=g)), de ahhoz, hogy ezt el-
dnthessk, meg kell nznnk a nyelvtant (A.5).
Alapok 158
6. Kifejezsek s utastsok 159
Opertor ttekints
hatkr-felolds osztlynv :: tag
hatkr-felolds nvtr_nv :: tag
globlis hatkr :: nv
globlis hatkr :: minstett_nv
tagkivlaszts objektum . tag
tagkivlaszts mutat -> tag
indexels mutat [kif]
fggvnyhvs kif (kif_lista)
rtk ltrehozsa tpus (kif_lista)
nvels uttaggal balrtk ++
cskkents uttaggal balrtk --
tpusazonosts typeid (tpus)
futsi idej tpusazonosts typeid (kif)
futsi idben ellenrztt
tpusknyszerts dynamic_cast <tpus> (kif)
fordtsi idben ellenrztt
tpusknyszerts static_cast <tpus> (kif)
nem ellenrztt tpusknyszerts reinterpret_cast <tpus> (kif)
konstans tpusknyszerts const_cast <tpus> (kif)
objektum mrete sizeof kif
tpus mrete sizeof (tpus)
nvels eltaggal ++ balrtk
cskkents eltaggal -- balrtk
komplemenskpzs ~ kif
(logikai) nem ! kif
mnusz eljel - kif
plusz eljel + kif
cm opertor & balrtk
indirekci * kif
ltrehozs (memriafoglals) new tpus
ltrehozs (memriafoglals
s kezdeti rtkads) new (kif_lista)
ltrehozs (elhelyezs) new (kif_lista) tpus
ltrehozs (elhelyezs
s kezdeti rtkads) new (kif_lista) tpus (kif_lista)
felszmols (felszabadts) delete mutat
tmb felszmolsa delete [ ] mutat
tpuskonverzi (tpus) kif
Alapok 160
Opertor ttekints (folytats)
tagkivlaszts objektum .*tagra_hivatkoz_mutat
tagkivlaszts mutat -> *tagra_hivatkoz_mutat
szorzs kif * kif
oszts kif / kif
modul (maradkkpzs) kif % kif
sszeads (plusz) kif + kif
kivons (mnusz) kif - kif
balra lptets kif << kif
jobbra lptets kif >> kif
kisebb kif < kif
kisebb vagy egyenl kif <= kif
nagyobb kif > kif
nagyobb vagy egyenl kif >= kif
egyenl kif == kif
nem egyenl kif != kif
bitenknti S kif & kif
bitenknti kizr VAGY kif ^ kif
bitenknti megenged VAGY kif | kif
logikai S kif && kif
logikai megenged VAGY kif || kif
feltteles kifejezs kif ? kif : kif
6.2.1. Eredmnyek
Az aritmetikai mveletek eredmnynek tpust az a szablyhalmaz dnti el, amelyet lta-
lnos aritmetikai talaktsok-nak neveznk (C.6.3). A f cl az, hogy a legtgabb
operandustpussal megegyez eredmny jjjn ltre. Ha egy binris opertor operandusa
pldul lebegpontos, a szmtst lebegpontos aritmetikval vgezzk s az eredmny egy
lebegpontos rtk lesz. Ha long tpus operandusa van, a szmts hossz egsz (long) arit-
metikval trtnik, az eredmny pedig long rtk lesz. Az int-nl kisebb operandusok (mint
a bool s a char) int-t alakulnak, mieltt az opertort alkalmazzuk rjuk.
Az ==, <= stb. relcis (sszehasonlt) opertorok logikai rtkeket adnak vissza. A fel-
hasznl ltal megadott opertorok jelentst s eredmnyt deklarcijuk hatrozza meg
(11.2).
Ha egy opertornak balrtk operandusa van, akkor ha ez logikailag lehetsges az ope-
rtor eredmnye egy olyan balrtk lesz, amely a balrtk operandust jelli:
6. Kifejezsek s utastsok 161
Opertor ttekints (folytats)
egyszer rtkads balrtk = kif
szorzs s rtkads balrtk *= kif
oszts s rtkads balrtk /= kif
maradkkpzs s rtkads balrtk %= kif
sszeads s rtkads balrtk += kif
kivons s rtkads balrtk -= kif
balra lptets s rtkads balrtk <<= kif
jobbra lptets s rtkads balrtk >>= kif
S s rtkads balrtk &= kif
megenged VAGY s rtkads balrtk |= kif
kizr VAGY s rtkads balrtk ^= kif
kivtel kivltsa throw kif
vessz (mveletsor) kif , kif
void f(int x, int y)
{
int j = x = y; // x=y rtke az x rtkads utni rtke
int* p = &++x; // p x-re mutat
int* q = &(x++); // hiba: x++ nem balrtk
int* pp = &(x>y?x:y); // a nagyobb rtk int cme
}
Ha a ? : msodik s harmadik operandusa is balrtk s ugyanolyan tpusak, az eredmny
a megfelel tpus balrtk lesz. Az, hogy ilyen mdon megrizzk a balrtkeket, nagy ru-
galmassgot ad az opertorok hasznlatban. Ez klnsen akkor fontos, ha olyan kdot
runk, amelynek egyformn s hatkonyan kell mkdnie beptett s felhasznli tpusok
esetben is (pldul ha olyan sablonokat vagy programokat runk, amelyek C++ kdot hoz-
nak ltre).
A sizeof eredmnye a size_t nev eljel nlkli integrlis tpus, melynek meghatrozsa
a <cstddef> fejllomnyban szerepel, a mutat-kivons pedig egy eljeles integrlis tpus,
amit ptrdiff_t-nek hvnak s szintn a <cstddef> fejllomny rja le.
A fordtnak nem kell ellenriznie az aritmetikai tlcsordulst s ltalban nem is teszi meg.
Pldul:
void f()
{
int i = 1;
while (0 < i) i++;
cout << "Az i negatv lett!" << i << '\n';
}
A ciklus elbb-utbb az i rtkt a legnagyobb egsz rtken tl nveli. Ami ekkor trt-
nik, nem meghatrozott; az rtk jellemzen egy negatv szmig r krbe (az n gpemen
ez -2147483648). Hasonlan, a nullval oszts eredmnye sem meghatrozott, ez viszont
rendszerint a program hirtelen befejezdst eredmnyezi. Az alulcsorduls, a tlcsorduls
s a nullval val oszts nem vlt ki szabvnyos kivteleket (14.10).
Alapok 162
6.2.2. Kirtkelsi sorrend
A kifejezseken belli rszkifejezsek kirtkelsi sorrendje nem meghatrozott, gy nem
ttelezhetjk fel pldul azt sem, hogy a kifejezs kirtkelse balrl jobbra trtnik:
int x = f(2)+g(3); // nem meghatrozott, hogy f() vagy g() hvdik meg elszr
Jobb kdot kszthetnk, ha a kifejezsek kirtkelsi sorrendje nem kttt, de a kirtke-
lsi sorrendre vonatkoz megszortsok hinya elre nem meghatrozott eredmnyekhez
vezethet:
int i = 1;
v[i] = i++; // nem meghatrozott eredmny
A fenti kifejezs vagy v[1]=1-knt, vagy v[2]=1-knt rtkeldik ki, esetleg mg furcsbban
viselkedik. A fordtprogramok figyelmeztethetnek az ilyen ktrtelmsgekre, sajnos,
a legtbb ezt nem teszi meg.
A , (vessz), a && (logikai S), s a || (logikai VAGY) opertorok esetben biztostott,
hogy a bal oldali operandus a jobb oldali eltt rtkeldik ki. A b=(a=2,a+1) pldul a b-
nek 3-at ad rtkl. A || s a && hasznlatra vonatkoz pldk a 6.2.3-ban tallhatk.
Beptett tpusoknl a && msodik operandusa csak akkor rtkeldik ki, ha az els
operandus true, a || msodik operandusa pedig csak akkor, ha az els operandus rtke
false; ezt nha rvid vagy rvidzras kirtkelsnek (short-circuit evaluation) nevezik. Je-
gyezzk meg, hogy a , (vessz) mveletsor-jelz logikailag klnbzik attl a vessztl,
amit arra hasznlunk, hogy a fggvnyhvsoknl elvlasszuk a paramtereket. Nzzk az
albbi pldt:
f1(v[i],i++); // kt paramter
f2( (v[i],i++) ); // egy paramter
Az f1 meghvsnak kt paramtere van, v[i] s i++, a paramter-kifejezsek kirtkelsi
sorrendje pedig nem meghatrozott. Az olyan megolds, amely fgg a paramter-kifejez-
sek sorrendjtl, nagyon rossz stlusrl rulkodik s eredmnye nem meghatrozott. Az f2
meghvshoz egy paramtert adtunk meg; a (v[i], i++) vesszs kifejezs, amely i++-szal
egyenrtk.
6. Kifejezsek s utastsok 163
A csoportosts kiknyszertsre zrjeleket hasznlhatunk. Pldul a*b/c jelentse (a*b)/c,
ezrt zrjeleket kell hasznlnunk, ha a*(b/c)-t akarunk kapni. Az a*(b/c) kifejezs csak ak-
kor rtkeldhet ki (a*b)/c-knt, ha a felhasznl nem tud klnbsget tenni kztk. Az
a*(b/c) s az (a*b)/c szmos lebegpontos szmtsnl jelentsen klnbzik, gy a fordt-
program pontosan gy fogja az ilyen kifejezseket kirtkelni, ahogy azokat lertuk.
6.2.3. Az opertorok sorrendje
A precedencia s a ktsi (asszociativitsi) szablyok a leggyakoribb hasznlatot tkrzik.
Pldul
if (i<=0 || max<i) // ...
azt jelenti, hogy ha i kisebb vagy egyenl 0-nl VAGY max kisebb i-nl. Ez egyenrtk
az albbival:
if ( (i<=0) || (max<i) ) // ...
Az albbi rtelmetlen, de szablyos kifejezssel viszont nem:
if (i <= (0||max) < i) // ...
Zrjeleket hasznlni azonban mindig hasznos, ha a programoznak ktsgei vannak ezek-
kel a szablyokkal kapcsolatban. A zrjelek hasznlata mg gyakoribb, ha a rszkifejez-
sek bonyolultabbak. A bonyolult rszkifejezsek mindig hiba forrsai lehetnek, ezrt ha
gy rezzk, hogy szksgnk van zrjelekre fontoljuk meg, hogy nem kellene-e egy
kln vltoz hasznlatval sztbontanunk a kifejezst. Vannak olyan esetek, amikor az
opertorok sorrendje nem a magtl rtetd rtelmezst eredmnyezi:
if (i&mask == 0) // hopp! == kifejezs & operandusaknt
Ekkor nem az trtnik, hogy alkalmazzuk a mask-ot az i-re, majd megnzzk, hogy az ered-
mny 0-e. Mivel az == elnyt lvez az & (ktoperandus) mvelettel szemben, a kifejezs
i&(mask==0)-knt lesz rtelmezve. Szerencsre a fordtprogram knnyen figyelmeztethet
az ilyen hibkra. Ebben az esetben a zrjelek fontosak:
if ((i&mask) == 0) // ...
Alapok 164
rdemes megjegyezni, hogy a kvetkez nem gy mkdik, ahogy egy matematikus elvrn:
if (0 <= x <= 99) // ...
Ez megengedett, de rtelmezse (0<=x)<=99, ahol az els sszehasonlts eredmnye vagy
true vagy false. A logikai rtket a fordtprogram aztn automatikusan 1-re vagy 0-ra ala-
ktja, amit aztn sszehasonltva 99-cel true-t kapunk. A kvetkezkppen vizsglhatjuk
meg, hogy x a 0..99 tartomnyban van-e:
if (0<=x && x<=99) // ...
Gyakori hiba kezdknl, hogy a felttelekben =-t (rtkadst) hasznlnak == (egyenl) helyett:
if (a = 7) // hopp! konstans rtkads a felttelben
Ez termszetes, mert az = jelentse sok nyelvben egyenl. A fordtprogramok ltalban
figyelmeztetnek is erre.
6.2.4. Bitenknti logikai mveletek
Az &, |, ^, -, >> s << bitenknti logikai opertorokat integrlis (egsz tpus) objektumok-
ra s felsorolsokra alkalmazzuk azaz a bool, char, short, int, long tpusokra, ezek eljel
nlkli (unsigned) megfelelire s az enumtpusokra. Az eredmny tpust a szoksos arit-
metikai talaktsok (C.6.3.) dntik el.
A bitenknti logikai opertorok jellemz felhasznlsa a kis halmazok (bitvektorok) fogal-
mnak megvalstsa. Ebben az esetben egy eljel nlkli egsz minden bitje a halmaz egy
elemt jelli, s a bitek szma korltozza a halmaz elemeinek szmt. Az & binris oper-
tort metszetknt, a | opertort uniknt, a ^-ot szimmetrikus differenciaknt, a ~-t pedig
komplemensknt rtelmezzk. Felsorol tpust arra hasznlhatunk, hogy megnevezzk
egy ilyen halmaz elemeit. me egy rvid plda, melyet az ostreammegvalstsbl vettnk
klcsn:
enum ios_base::iostate {
goodbit=0, eofbit=1, failbit=2, badbit=4
};
6. Kifejezsek s utastsok 165
Az adatfolyam az llapotot gy llthatja be s ellenrizheti:
state = goodbit;
// ...
if (state&(badbit|failbit)) // nem megfelel adatfolyam
A kln zrjelek azrt szksgesek, mert az & elnyt lvez a | mveleti jellel szemben.
Egy fggvny gy jelezheti, hogy elrte a bemenet vgt:
state |= eofbit;
A |= opertort arra hasznljuk, hogy az llapothoz hozzadjunk valamilyen j informcit.
Az egyszer state=eofbit rtkads kitrlt volna minden ms bitet.
Ezek az adatfolyam-llapotjelzk megfigyelhetk a folyam megvalstsn kvl is. Pldul
gy nzhetjk meg, hogyan klnbzik kt adatfolyam llapota:
int diff = cin.rdstate()^cout.rdstate(); // rdstate() az llapotot adja vissza
Az adatfolyam-llapotok klnbsgeinek kiszmtsa nem tl gyakori, ms hasonl tpu-
soknl viszont alapvet mvelet. Vegyk pldul azt az esetet, amikor ssze kell hasonlta-
nunk azt a bitvektort, amely a kezelt megszaktsok halmazt jelli, egy msik bitvektorral,
amely olyan megszaktsok halmazt brzolja, melyek arra vrnak, hogy kezeljk ket.
Jegyezzk meg, hogy ezt a zsonglrkdst a bitekkel az iostream megvalstsbl vet-
tk s nem a felhasznli felletbl. A knyelmes bitkezels nagyon fontos lehet, de a meg-
bzhatsg, a mdosthatsg, vagy a hordozhatsg rdekben a rendszer alacsonyabb
szintjein kell tartanunk. ltalnosabb halmazfogalomra nzzk meg a standard knyvtrbe-
li set-et (17.4.3), bitset-et (17.5.3), s a vector<bool>-t (16.3.11).
A mezk (C.8.1) hasznlata igazn knyelmes mdon rvidti le azt a mveletet, amikor
lptetssel s maszkolssal vesznk ki bitmezket egy szbl. Ezt termszetesen megtehet-
jk a bitenknti logikai opertorokkal is. Egy 32 bites long kzps 16 bitjt pldul gy ve-
hetjk ki:
unsigned short middle(long a) { return (a>>8)&0xffff; }
A bitenknti logikai opertorokat ne keverjk ssze az &&, || s ! logikai opertorokkal. Az
utbbiak vagy true-t, vagy false-t adnak vissza, s elsdlegesen akkor hasznosak, amikor egy
Alapok 166
if, while, vagy for utastsban (6.3.2, 6.3.3) felttelt runk. Pldul az !0 (nem nulla) true
rtk, mg a ~0 (a nulla komplemense) egy csupa egyesbl ll bitminta, amely a -1 rtk
kettes komplemensbeli brzolsa.
6.2.5. Nvels s cskkents
A ++ opertort arra hasznljuk, hogy egy rtk nvelst kzvetlenl, s nem az sszeads
s rtkads prostsval fejezzk ki. Definci szerint a ++lvalue jelentse lvalue+=1, ez
pedig lvalue=lvalue+1-et jelent, feltve, hogy a balrtknek nincs mellkhatsa". A nve-
lend objektumot jell kifejezs (csak) egyszer rtkeldik ki. A cskkentst ugyangy
a -- opertor fejezi ki. A ++ s -- opertorokat hasznlhatjuk eltagknt s uttagknt is.
A ++x rtke az x j (megnvelt) rtke lesz, pldul az y=++x egyenrtk az y=(x+=1)-
gyel. Az x++ rtke azonban az x rgi rtke: az y=x++ egyenrtk az y=(t=x,x+=1,t)-vel,
ahol t egy x-szel azonos tpus vltoz.
A mutatk sszeadshoz s kivonshoz hasonlan a mutatkra alkalmazott ++ s -- m-
kdst azok a tmbelemek hatrozzk meg, amelyekre a mutat hivatkozik; p++ a p-t
a kvetkez tmbelemre lltja (5.3.1).
A nvel opertorok klnsen a ciklusokban hasznlatosak, vltozk nvelsre vagy csk-
kentsre. Egy nulla vgzds karakterlncot pldul a kvetkezkppen msolhatunk t:
void cpy(char* p, const char* q)
{
while (*p++ = *q++) ;
}
A C-hez hasonlan a C++-t is szeretik s gyllik azrt, mert megengedi az ilyen tmr, ki-
fejezskzpont kdolst. Mivel a
while (*p++ = *q++) ;
kifejezs meglehetsen zavaros a nem C programozk szmra, ez a kdolsi stlus viszont
nem ritka a C-ben s a C++-ban, megri kzelebbrl megvizsglnunk. Vegyk elszr a ka-
raktertmbk msolsnak egy hagyomnyosabb mdjt:
int length = strlen(q);
for (int i = 0; i<=length; i++) p[i] = q[i];
6. Kifejezsek s utastsok 167
Ez pazarls. A nulla vgzds karakterlnc hosszt gy hatrozzuk meg, hogy a nulla vg-
zdst keresve vgigolvassuk azt. gy ktszer olvassuk vgig a teljes lncot: egyszer azrt,
hogy meghatrozzuk a hosszt, egyszer pedig azrt, hogy tmsoljuk. Ezrt inkbb prbl-
juk ezt:
int i;
for (i = 0; q[i]!=0 ; i++) p[i] = q[i];
p[i] = 0; // lezr nulla
Az i vltozt indexelsre hasznljuk, de ki lehet kszblni, mert p s q mutatk:
while (*q != 0) {
*p = *q;
p++; // lptets a kvetkez karakterre
q++; // lptets a kvetkez karakterre
}
*p = 0; // lezr nulla
Mivel az uttagknt hasznlt nvel opertor megengedi, hogy elszr felhasznljuk az r-
tket, s csak azutn nveljk meg, a kvetkezkppen rhatjuk jra a ciklust:
while (*q != 0) {
*p++ = *q++;
}
*p = 0; // lezr nulla
A *p++ = *q++ rtke *q, ezrt a pldt gy mdosthatjuk:
while ((*p++ = *q++) != 0) { }
Ebben az esetben addig nem vesszk szre, hogy *q nulla, amg be nem msoljuk *p-be s
meg nem nveljk p-t. Kvetkezskppen elhagyhatjuk az utols rtkadst, amiben a nul-
la vgzdst adjuk rtkl. Vgl tovbb rvidthetjk a pldt azzal, hogy szrevesszk,
nincs szksgnk az res blokkra s hogy felesleges a !=0 vizsglat, mert egy mutat vagy
integrlis felttel mindig sszehasonltdik 0-val. gy megkapjuk azt a vltozatot, amelyet
clul tztnk ki.
while (*p++ = *q++) ;
Ez a vltozat vajon kevsb olvashat, mint az elz? Egy tapasztalt C vagy C++ programo-
z szmra nem. Hatkonyabb idben s trterletben, mint az elz? Az els vltozatot ki-
Alapok 168
vve, ami meghvta az strlen()-t, nem igazn. Az, hogy melyik vltozat a leghatkonyabb,
a gp felptstl s a fordtprogramtl fgg, a nulla vgzds karakterlncok msol-
snak leghatkonyabb mdja viszont ltalban a standard knyvtrbeli msol fggvny.
char* strcpy(char*, const char*); // a <string.h> fejllomnybl
ltalnosabb msolsra a szabvnyos copy algoritmust (2.7.2, 18.6.1) hasznlhatjuk. Ahol
lehetsges, rszestsk elnyben a standard knyvtr lehetsgeit a mutatkkal s bjtok-
kal val gyeskedssel szemben. A standard knyvtr fggvnyei lehetnek helyben kifej-
tett fggvnyek (7.1.1) vagy egyedi gpi utastsokkal megvalstottak. Ezrt gondosan
fontoljuk meg, mieltt elhinnnk, hogy valamilyen kzzel rt kdrszlet fellmlja a knyv-
tri fggvnyek teljestmnyt.
6.2.6. Szabad tr
A nvvel rendelkez objektumok lettartamt (lifetime) hatkrk (4.9.4) dnti el, gyak-
ran azonban hasznos, ha olyan objektumot hozunk ltre, amely fggetlenl ltezik attl
a hatkrtl, ahol ltrehoztuk. Nevezetesen gyakori, hogy olyan objektumokat hozunk lt-
re, amelyek akkor is felhasznlhatk, miutn visszatrtnk abbl a fggvnybl, ahol ltre-
hoztuk azokat. Az ilyen objektumokat a new opertor hozza ltre s a delete opertort hasz-
nlhatjuk felszmolsukra. A new ltal ltrehozott objektumokra azt mondjuk, hogy a sza-
bad trban vannak (free store), vagy azt, hogy kupac-objektumok (heap), vagyis a dina-
mikus memriban vannak.
Nzzk meg, hogyan rnnk meg egy fordtprogramot olyan stlusban, ahogy az asztali
szmolgpnl tettk (6.1). A szintaktikai elemz fggvnyek felpthetnek egy kifejezs-
ft a kdkszt szmra:
struct Enode {
Token_value oper;
Enode* left;
Enode* right;
// ...
};
Enode* expr(bool get)
{
Enode* left = term(get);
6. Kifejezsek s utastsok 169
for (;;)
switch(curr_tok) {
case PLUS:
case MINUS:
{ Enode* n = new Enode; // Enode ltrehozsa a szabad trban
n->oper = curr_tok;
n->left = left;
n->right = term(true);
left = n;
break;
}
default:
return left; // csompont visszaadsa
}
}
A kdkszt aztn felhasznln az eredmnyl kapott csompontokat (node) s trln
azokat:
void generate(Enode* n)
{
switch (n->oper) {
case PLUS:
// ...
delete n; // Enode trlse a szabad trbl
}
}
A new ltal ltrehozott objektum addig ltezik, amg kifejezetten meg nem semmistjk
a delete-tel. Ezutn a new jra felhasznlhatja az objektum ltal lefoglalt trhelyet. A C++-
vltozatok nem garantljk, hogy van szemtgyjt (garbage collector), amely megkeresi
azokat az objektumokat, amelyekre nincs mr hivatkozs s jra felhasznlhatv teszi
azok helyt. Kvetkezskppen felttelezzk, hogy a new ltal ltrehozott objektumokat
magunknak kell megsemmistennk, a delete-et hasznlva. Ha van szemtgyjt, a delete-
ek a legtbb esetben elhagyhatk (C.9.1).
A delete opertort csak a new ltal visszaadott mutatra vagy nullra lehet alkalmazni. Ha
a delete-et nullra alkalmazzuk, nem lesz hatsa.
A new opertornak egyedi vltozatait is meghatrozhatjuk (15.6).
Alapok 170
6.2.6.1. Tmbk
A new hasznlatval ltrehozhatunk objektumokbl ll tmbt is:
char* save_string(const char* p)
{
char* s = new char[strlen(p)+1];
strcpy(s,p); // msols p-bl s-be
return s;
}
int main(int argc, char* argv[ ])
{
if (argc < 2) exit(1);
char* p = save_string(argv[1]);
// ...
delete[ ] p;
}
A sima delete opertort arra hasznlhatjuk, hogy egyes objektumokat felszmoljuk,
a delete[ ] tmbk felszmolsra hasznlatos.
Ha vissza akarjuk nyerni a new ltal lefoglalt trhelyet, a delete-nek vagy a delete[ ]-nek meg
kell tudni llaptani, mekkora a lefoglalt objektum mrete. Ebbl az kvetkezik, hogy
a szabvnyos new opertorral ltrehozott objektumok valamivel tbb helyet foglalnak, mint
a statikus objektumok. Az objektum mrett ltalban egy gpi sz trolja.
Jegyezzk meg, hogy a vector (3.7.1, 16.3) valdi objektum, ezrt ltrehozsra s felsz-
molsra a sima new-t s delete-et hasznlhatjuk:
void f(int n)
{
vector<int>* p = new vector<int>(n); // nll objektum
int* q = new int[n]; // tmb
// ...
delete p;
delete[ ] q;
}
A delete[ ] opertort csak a new ltal visszaadott mutatra vagy nullra alkalmazhatjuk. Ha
a delete[ ]-et nullra alkalmazzuk, nem lesz hatsa.
6. Kifejezsek s utastsok 171
6.2.6.2. Memria-kimerls
A szabad tr new, delete, new[ ] s delete[ ] opertorai fggvnyekknt vannak megvalstva:
void* operator new(size_t); // hely az nll objektum szmra
void operator delete(void*);
void* operator new[ ](size_t); // hely a tmb szmra
void operator delete[ ](void*);
Ha a new opertornak egy objektum szmra kell helyet foglalnia, az operator new()-t hv-
ja meg, hogy az megfelel szm bjtot foglaljon le. Ha tmb szmra foglal helyet, az ope-
rator new[ ]() meghvsra kerl sor. Az operator new() s az operator new [ ]() szabvnyos
megvalstsa a visszaadott memrit nem tlti fel kezdrtkkel.
Mi trtnik, ha a new nem tall lefoglalhat helyet? Alaprtelmezs szerint a lefoglal
bad_alloc kivtelt vlt ki (a msik lehetsget illeten lsd 19.4.5-t):
void f()
{
try {
for(;;) new char[10000];
}
catch(bad_alloc) {
cerr << "Elfogyott a memria!\n";
}
}
Akrmennyi memria ll a rendelkezsnkre, a kd vgl meg fogja hvni a bad_alloc ese-
mnykezeljt.
Magunk is meghatrozhatjuk, mit csinljon a new, amikor kifogy a memria. Ha a new nem
jr sikerrel, elszr azt a fggvnyt hvja meg, amelyet a <new> fejllomnyban bevezetett
set_new_handler() fggvnnyel elzleg belltottunk (amennyiben ezt megtettk):
void out_of_store()
{
cerr << "Az operator new nem jrt sikerrel: nincs trhely\n";
throw bad_alloc();
}
Alapok 172
int main()
{
set_new_handler(out_of_store); // out_of_store lesz a new_handler
for (;;) new char[10000];
cout << "ksz\n";
}
Ez azonban soha nem fog elrni addig, hogy kirja a ksz-t. Ehelyett a kvetkezt fogja
kirni:
Az operator new nem jrt sikerrel: nincs trhely
Lsd 14.4.5-t az operator new() egy olyan lehetsges megvalstsrl, amely megvizs-
glja, ltezik-e meghvhat kezelfggvny, s ha nem tall ilyet, bad_alloc-ot vlt ki. Egy
new_handler azonban valami okosabbat is tehet, mint hogy egyszeren befejezi a progra-
mot. Ha tudjuk, hogyan mkdik a new s a delete pldul azrt, mert sajt operator
new()-t s operator delete()-et rtunk ,a kezelfggvny megprblhat valamennyi mem-
rit keresni, hogy a new visszatrhessen, vagyis a felhasznl gondoskodhat szemtgyjt-
rl, gy elhagyhatv teheti a delete-et (br ez ktsgtelenl nem kezdknek val feladat).
Majdnem mindenkinek, akinek automatikus szemtgyjtre van szksge, a leghaszno-
sabb, ha szerez egy mr megrt s ellenrztt termket (C.9.1).
Azzal, hogy j new_handler-t lltunk be, felvllaljuk, hogy neknk kell trdnnk a me-
mria kimerlsvel kapcsolatos problmkkal a new minden hasznlatakor. A memria-
foglalsnak ktfle tja ltezik: vagy gondoskodunk nem szabvnyos lefoglal s felszaba-
dt fggvnyekrl (15.6) a new szablyos hasznlata szmra, vagy a felhasznl ltal
adott tovbbi foglalsi adatokra tmaszkodunk (10.4.11, 19.4.5.).
6.2.7. Meghatrozott tpuskonverzik
Nha nyers memrival kell dolgoznunk, azaz a tr olyan objektumot tartalmaz vagy fog
tartalmazni, melynek tpusa ismeretlen a fordtprogram szmra. Ilyen eset, amikor a me-
mriafoglal (alloktor) egy jonnan lefoglalt memriaterletre hivatkoz void* tpus mu-
tatt ad vissza, vagy ha azt akarjuk kifejezni, hogy egy adott egsz rtket gy kell kezelni,
mint egy I/O eszkz cmt:
void* malloc(size_t);
void f()
{
int* p = static_cast<int*>(malloc(100)); // a new ltal lefoglalt helyet int-knt hasznljuk
IO_device* d1 = reinterpret_cast<IO_device*>(0Xff00); // eszkz a 0Xff00 cmen
// ...
}
6. Kifejezsek s utastsok 173
A fordtprogram nem ismeri a void* ltal mutatott objektum tpust. Azt sem tudja, vajon
a 0Xff00 rvnyes cm-e. Kvetkezskppen az talaktsok helyessge teljes mrtkben
a programoz kezben van. Az explicit (pontosan meghatrozott) tpusknyszertsek
(casting) nha szksgesek, de hagyomnyosan tl sokszor hasznljk azokat, s jelents
hibaforrsok.
A static_cast opertor egymssal kapcsolatban lev tpusok kztti konverzit vgez, pl-
dul kt, ugyanazon osztlyhierarchiban lv mutattpus, integrlis tpus s felsorol t-
pus, vagy lebegpontos tpus s integrlis tpus kzttit. A reinterpret_class olyan tpusok
talaktst hajtja vgre, amelyek nincsenek kapcsolatban, pldul egszrl mutatra vagy
mutattpusrl egy msik, nem rokon mutatra konvertl. Ez a megklnbztets lehetv
teszi, hogy a fordtprogram elvgezzen bizonyos minimlis tpusellenrzst a static_cast
esetben, s megknnyti, hogy a programoz megtallja a veszlyesebb talaktsokat,
melyeket a reinterpret_cast jell. Nhny static_cast hordozhat, a reinterpret_cast-ok
kzl viszont csak kevs. A reinterpret_cast esetben nem sok dolog biztos; ltalban j t-
pust hoz ltre, amelynek ugyanaz a bitmintja, mint a paramter. Ha a cl legalbb annyi
bites, mint az eredeti rtk, az eredmnyt a reinterpret_cast-tal az eredeti tpusra alakthat-
juk s hasznlhatjuk azt. A reinterpret_cast eredmnyt csak akkor lehet biztosan felhasz-
nlni, ha annak tpusa pontosan az a tpus, amelyet az rtk meghatrozsra hasznltunk.
Ha ksrtst rznk, hogy pontosan meghatrozott tpusknyszertst alkalmazzunk, szn-
junk idt arra, hogy meggondoljuk, vajon tnyleg szksges-e. A C++-ban az explicit t-
pusknyszerts a legtbb esetben szksgtelen olyankor, amikor a C-ben szksg lenne r
(1.6), s sok olyan esetben is, ahol a C++ korai vltozataiban szksges volt (1.6.2, B.2.3).
Szmos programban az ilyen tpuskonverzi teljesen elkerlhet; mshol nhny eljrsra
korltozhatjuk a hasznlatt. Ebben a knyvben explicit tpusknyszertst valsgh hely-
zetekben csak a 6.2.7, 7.7, 13.5, 15.4, s 25.4.1 pontokban hasznlunk.
A futsi idben ellenrztt konverzik egyik formja a dynamic_cast (15.4.1). A const
minstt eltvolt konstanstalant const_cast (15.4.2.1) opertort szintn hasznlhatjuk.
A C++ a C-bl rklte a (T)e jellst, amely brmilyen talaktst elvgez, amit ki lehet fe-
jezni a static_cast, reinterpret_cast s a const_cast kombincijaknt. Eredmnyl T tpus
rtk jn ltre (B.2.3). Ez a C stlus konverzi sokkal veszlyesebb, mint a fent emltettek,
mert a jellst nehezebben lehet szrevenni egy nagy programban s a programoz szn-
dka szerinti talakts fajtja nem nyilvnval. Azaz a (T)e lehet, hogy hordozhat tala-
ktst vgez egymssal kapcsolatban lev tpusok kztt, de nem hordozhatt a nem rokon
tpusok kztt, esetleg egy mutattpusrl eltvoltja a const minstt. Ha nem tudjuk T s
e pontos tpust, ezt nem tudjuk eldnteni.
Alapok 174
6.2.8. Konstruktorok
Egy T tpus rtk ltrehozsa egy e rtkbl a T(e) fggvnyjellssel fejezhet ki:
void f(double d)
{
int i = int(d); // d csonkolsa
complex z = complex(d); // complex ltrehozsa d-bl
// ...
}
A T(e) szerkezetet nha fggvny stlus konverzinak nevezik. Sajnos, beptett T tpu-
sokra T(e) egyenrtk (T)e-vel, ami azt vonja maga utn, hogy a T(e) hasznlata nem min-
dig biztonsgos. Aritmetikai tpusok esetben az rtkek csonkulhatnak, s mg egy
hosszabb egsz tpusrl egy rvidebbre (pldul long-rl char-ra) val talakts is nem
meghatrozott viselkedst eredmnyezhet. A jellst megprblom kizrlag ott hasznlni,
ahol az rtk ltrehozsa pontosan meghatrozott, azaz a szkt aritmetikai talaktsok-
nl (C.6), az egszekrl felsorol tpusra val talaktsoknl (4.8), s a felhasznli tpu-
sok objektumainak ltrehozsnl (2.5.2, 10.2.3).
A mutat-konverzikat a T(e) jellst hasznlva nem fejezhetjk ki kzvetlenl. A char*(2)
pldul formai hibnak szmt. Sajnos az a vdelem, amit a konstruktor jells nyjt az ilyen
veszlyes talaktsok ellen, kikerlhet ha a mutattpusokra typedef neveket (4.9.7)
hasznlunk.
A T alaprtelmezett rtknek kifejezsre a T() konstruktor jells hasznlatos:
void f(double d)
{
int j = int(); // alaprtelmezett int rtk
complex z = complex(); // alaprtelmezett complex rtk
// ...
}
A beptett tpusok konstruktornak rtke a 0, amit a fordt az adott tpusra konvertl
(4.9.5). Ezrt az int() egy msfajta mdja a 0 rsnak. A T felhasznli tpusra T()-t az alap-
rtelmezett konstruktor (10.4.2) hatrozza meg, ha ltezik ilyen.
A konstruktor jells hasznlata beptett tpusokra sablonok rsakor klnsen fontos.
Ekkor a programoz nem tudhatja, hogy a sablon (template) paramtere beptett vagy fel-
hasznli tpusra vonatkozik-e majd (16.3.4, 17.4.1.2).
6. Kifejezsek s utastsok 175
6.3. Utastsok ttekints
me a C++ utastsok sszefoglalsa, nhny pldval:
Alapok 176
Az utastsok formai szablyai
utasts:
deklarci
{ utasts_lista
nem ktelez
}
try { utasts_lista
nem ktelez
} kezel_lista
kif
nem ktelez
;
if (felttel) utasts
if (felttel) utasts else utasts
switch (felttel) utasts
while (felttel) utasts
do utasts while (kif);
for (kezdrtk_meghatroz felttel
nem ktelez
;kif
nem ktelez
) utasts
case konstans_kif : utasts
default : utasts
break ;
continue ;
return kif
nem ktelez
;
goto azonost;
azonost : utasts
utasts_lista:
utasts utasts_lista
nem ktelez
felttel:
kif
tpusazonost deklartor = kif
kezel_lista:
catch (kif_deklarci) { utasts_lista
nem ktelez
}
kezel_lista kezel_lista
nem ktelez
Jegyezzk meg, hogy a deklarci egy utasts, rtkad s eljrshv utastsok pedig
nincsenek: az rtkadsok s a fggvnyhvsok kifejezsek. A kivtelek kezelsre vonat-
koz utastsokat a try blokkokat a 8.3.1 pontban trgyaljuk.
6.3.1. Deklarcik mint utastsok
A deklarci utasts. Hacsak egy vltozt static-knt nem adunk meg, minden esetben kez-
drtket fog kapni, amikor a vezrls thalad a deklarcijn (lsd mg 10.4.8). A dekla-
rcikat azrt engedjk meg minden olyan helyen, ahol utastst hasznlhatunk (s mg pr
tovbbi helyen, 6.3.2.1, 6.3.3.1), hogy lehetv tegyk a programoznak a kezdrtk
nlkli vltozkbl szrmaz hibk cskkentst s a vltozk hatkrnek lehet legna-
gyobb szktst a kdban. Ritkn van ok j vltoz bevezetsre, mieltt lenne egy olyan
rtk, amit a vltoznak tartalmaznia kell:
void f(vector<string>& v, int i, const char* p)
{
if (p==0) return;
if (i<0 || v.size()<=i) error("rossz index");
string s = v[i];
if (s == p) {
// ...
}
// ...
}
A lehetsg, hogy a deklarcikat vgrehajthat kd utn is elhelyezhetjk, alapvet fon-
tossg sok konstans esetben, illetve az olyan egyszeri rtkadsos programozsi stlus-
nl, ahol egy objektum rtke nem vltozik meg annak ltrehozsa s kezdeti rtkadsa
utn. Felhasznli tpusoknl a vltoz meghatrozsnak elhalasztsa addig, amg egy
megfelel kezdeti rtk rendelkezsre nem ll jobb teljestmnyhez is vezethet:
string s; /* ... */ s = "A legjobb a j ellensge.";
A fenti knnyen elfordulhat, hogy sokkal lassabb, mint a kvetkez:
string s = "Voltaire";
Kezdeti rtk nlkl ltalban akkor adunk meg egy vltozt, ha a vltoznak utastsra van
szksge a kezdeti rtkadshoz. Ilyenek pldul a bemeneti vltozk s a tmbk.
6. Kifejezsek s utastsok 177
6.3.2. Kivlaszt utastsok
Egy rtket az if vagy a switch utastssal vizsglhatunk meg:
if (felttel) utasts
if (felttel) utasts else utasts
switch (felttel) utasts
Az albbi sszehasonlt opertorok a logikai (bool) tpus true rtket adjk vissza, ha az
sszehasonlts igaz, s false-t, ha hamis:
== != < <= > >=
Az if utastsban az els (s egyetlen) utasts akkor hajtdik vgre, ha a kifejezs nem nul-
la. Ha nulla, a msodik utastsra ugrunk (ha megadtunk ilyet). Ebbl az kvetkezik, hogy
brmilyen aritmetikai vagy mutat kifejezst lehet felttelknt hasznlni. Pldul ha x egy
egsz, akkor
if (x) // ...
azt jelenti, hogy
if (x != 0) // ...
A p mutat esetben az albbi egy kzvetlen utasts, ami azt a vizsglatot fejezi ki, hogy p
egy rvnyes objektumra mutat:
if (p) // ...
A kvetkez kzvetett mdon ugyanezt a krdst fogalmazza, gy, hogy sszehasonltja
egy olyan rtkkel, amelyrl tudjuk, hogy nem mutat objektumra:
if (p != 0) // ...
Jegyezzk meg, hogy a 0 mutatt nem minden gp brzolja csupa nullval (5.1.1). Min-
den fordtprogram, amivel tallkoztam, ugyanazt a kdot ksztette mindkt vizsglatra.
Alapok 178
A
&& || !
logikai opertorok leggyakrabban felttelekben hasznlatosak. Az && s a || mveletek
nem rtkelik ki a msodik paramtert, csak ha szksg van r:
if (p && 1<p->count) // ...
A fenti utasts pldul elszr megvizsglja, hogy p nem nulla-e, s csak akkor nzi meg,
hogy l<p->count teljesl-e, ha p nem nulla.
Nhny if utastst knyelmesen feltteles kifejezsekre cserlhetnk. Pldul az
if (a <= b)
max = b;
else
max = a;
jobban kifejezhet gy:
max = (a<=b) ? b : a;
A felttel krl lv zrjelek nem szksgesek, n azonban gy gondolom, a kd
knnyebben olvashat lesz tlk.
A switch utasts if utastsok sorozataknt is lerhat. Pldul a
switch (val) {
case 1:
f();
break;
case 2:
g();
break;
default:
h();
break;
}
6. Kifejezsek s utastsok 179
gy is kifejezhet:
if (val == 1)
f();
else if (val == 2)
g();
else
h();
A jelents ugyanaz, de az els (switch) vltozatot rszestjk elnyben, mert a mvelet ter-
mszete (egy rtket llandk halmazval hasonltunk ssze) gy vilgosabb. A switch uta-
sts olvashatbb olyan pldknl, amelyek nem maguktl rtetdek, s jobb kdot is
hozhatunk ltre vele.
Vigyzzunk arra, hogy a switch case-t mindig fejezzk be valahogy, hacsak nem akarjuk
a vgrehajtst a kvetkez case-nl folytatni. Vegyk a kvetkezt:
switch (val) { // vigyzat!
case 1:
cout << "1. eset\n";
case 2:
cout << "2.eset\n";
default:
cout << "Alaprtelmezs: nincs ilyen eset\n";
}
Ha val==1-gyel hvjuk meg, a kvetkezket rja ki:
1. eset
2. eset
Alaprtelmezs: nincs ilyen eset
Ez az avatatlanokat nagy meglepetsknt rheti. J tlet, ha megjegyzsekkel ltjuk el azon
(ritka) eseteket, amikor a case-ek kztti tovbblps szndkos, gy egy nem magyarzott
tovbblpsrl felttelezhetjk, hogy programhiba. A case befejezsnek leggyakoribb
mdja a break hasznlata, de a return is hasznos lehet (6.1.1).
Alapok 180
6.3.2.1. Deklarcik felttelekben
A vletlen hibs mkds elkerlsre ltalban j tlet a vltozkat a legkisebb lehetsges
hatkrben bevezetni. Nevezetesen, rendszerint legjobb elhalasztani egy idelis vltoz be-
vezetst addig, amg kezdeti rtket nem tudunk adni neki. gy nem kerlhetnk olyan
helyzetbe, hogy a vltozt mg azeltt hasznljuk, mieltt kezdeti rtkt belltottuk volna.
Az emltett kt elv egyik legelegnsabb felhasznlsa, ha a vltozt egy felttelben adjuk
meg. Vegyk a kvetkez pldt:
if (double d = prim(true)) {
left /= d;
break;
}
Itt d deklarlt s kezdrtket is kap, amit a felttel rtkvel hasonltunk ssze. A d hat-
kre a deklarci pontjtl annak az utastsnak a vgig terjed, amit a felttel vezrel. Ha
volna egy else g az if utastsban, a d hatkre mindkt gra kiterjedne.
A msik hagyomnyos s kzenfekv megolds, ha a d-t a felttel eltt vezetjk be. gy vi-
szont nagyobb lesz a d hasznlatnak hatkre; kiterjedhet a kezdeti rtkads el vagy a d
szndkolt hasznos lettartama utn is:
double d;
// ...
d2 = d; // hopp!
// ...
if (d = prim(true)) {
left /= d;
break;
}
// ...
d = 2.0; // d kt, egymstl fggetlen hasznlata
A vltozk felttelekben trtn megadsnak nemcsak logikai haszna van, tmrebb for-
rskdot is eredmnyez.
A felttelben lv deklarcinak egyetlen vltozt vagy konstanst kell megadnia s feltl-
tenie kezdrtkkel.
6. Kifejezsek s utastsok 181
6.3.3. Ciklusutastsok
A ciklusokat for, while vagy do utastssal fejezhetjk ki:
while ( felttel ) utasts
do utasts while ( kifejezs ) ;
for ( kezdrtk_meghatroz felttel
nem ktelez
; kifejezs
nem ktelez
) utasts
Ezen utastsok mindegyike ismtelten vgrehajt egy utastst (amit vezrelt (controlled)
utastsnak vagy ciklusmagnak neveznk), amg a felttel hamiss nem vlik vagy a prog-
ramoz ms mdon ki nem lp a ciklusbl.
A for utasts szablyos ciklusok kifejezsre val. A ciklusvltozt, a ciklusfelttelt, s
a ciklusvltozt mdost kifejezst egyetlen sorban rhatjuk le, ami nagyon megnvelheti
az olvashatsgot s ezzel cskkentheti a hibk gyakorisgt. Ha nem szksges kezdeti r-
tkads, a kezdrtk_meghatroz (inicializl) utasts res is lehet. Ha a felttelt elhagy-
juk, a for utasts rkk a ciklusban marad, hacsak a felhasznl kifejezetten kilpsre
nem knyszerti egy break, return, goto, vagy throw utastssal, vagy valami kevsb egy-
szer mdon, pldul az exit() (9.4.1.1) meghvsval. Ha a kifejezst elhagyjuk, a ciklus-
magban kell mdostanunk egy ciklusvltozt. Ha a ciklus nem az egyszer bevezetnk
egy ciklusvltozt, megvizsgljuk a felttelt, mdostjuk a ciklusvltozt fajtbl val, lta-
lban jobb, ha while utastssal fejezzk ki, de a for is segthet olyan ciklusok rsnl,
melyeknek nincs meghatrozott lellsi felttele:
for(;;) { // "rkk" (vgtelen ciklus)
// ...
}
A while utasts egyszeren vgrehajtja a ciklusmagot, amg felttele hamiss nem vlik. Ak-
kor hajlok arra, hogy a while-t rszestsem elnyben a for-ral szemben, amikor nincs mag-
tl rtetd ciklusvltoz vagy amikor a ciklusvltoz mdostsa termszetes mdon
a ciklusmag kzepn trtnik. A bemeneti ciklus egy olyan ciklusra plda, amelyben nincs
magtl rtetd ciklusvltoz:
while(cin>>ch) // ...
Tapasztalatom szerint a do utasts knnyen hibk s tvedsek forrsa lehet. Ennek az az
oka, hogy a ciklusmag mindig vgrehajtdik egyszer, mieltt a felttel kirtkeldik. Ahhoz
azonban, hogy a ciklusmag megfelelen mkdjn, valamilyen felttelnek mr az els al-
kalommal is teljeslnie kell. A vrtnl sokkal gyakrabban vettem szre azt, hogy egy felt-
Alapok 182
tel nem gy teljeslt, ahogy az elvrhat lett volna; vagy amikor a programot elszr meg-
rtk s teszteltk, vagy ksbb, amikor a kdot mdostottk. Ezenkvl jobban szeretem
a felttelt ell, ahol jl lthatom. Kvetkezskppen n magam prblom elkerlni a do
utastsokat.
6.3.3.1. Deklarcik a for utastsban
Vltozkat a for utasts kezdrtk-ad rszben adhatunk meg. Ha ez deklarci, akkor
az ltala bevezetett vltoz (vagy vltozk) hatkre a for utasts vgig terjed:
void f(int v[ ], int max)
{
for (int i = 0; i<max; i++) v[i] = i*i;
}
Ha az index vgs rtkt tudni kell a for ciklusbl val kilps utn, a ciklusvltozt a cik-
luson kvl kell megadni (pl. 6.3.4.).
6.3.4. Goto
A C++-ban megtallhat a hrhedt goto :
goto azonost ;
azonost : utasts
A goto az ltalnos magasszint programozsban kevs dologra hasznlhat, de nagyon
hasznos lehet, amikor a C++ kdot program s nem kzvetlenl egy szemly kszti; hasz-
nlhatjuk pldul olyan elemzben, melyet egy kdkszt program (kdgenertor) hozott
ltre valamilyen nyelvtan alapjn. A goto akkor is hasznos lehet, ha a hatkonysg alapve-
t kvetelmny, pldul valamilyen vals idej alkalmazs bels ciklusban.
A goto kevs rtelmes hasznlatnak egyike a mindennapi kdban az, hogy kilpnk egy
begyazott ciklusbl vagy switch utastsbl (a break csak a legbels ciklusbl vagy switch
utastsbl lp ki):
void f()
{
int i;
int j;
6. Kifejezsek s utastsok 183
for (i = 0; i<n; i++)
for (j = 0; j<m; j++) if (nm[i][j] == a) goto found;
// nem tallhat
// ...
found:
// nm[i][j] == a
}
A ciklus vgre ugr continue utasts mkdsvel a 6.1.5-ben foglalkoztunk.
6.4. Megjegyzsek s behzs
A program olvasst s megrtst sokkal kellemesebb teheti, ha okosan hasznljuk
a megjegyzseket s a behzst. Szmos behzsi stlus hasznlatos s nem ltok alapvet
okot arra, hogy egyiket a msikkal szemben elnyben rszestsk (br a legtbb programo-
zhoz hasonlan nekem is van vlasztott stlusom a knyv nyilvn tkrzi is azt). Ugyan-
ez vonatkozik a megjegyzsek stlusra is.
A megjegyzseket szmos mdon lehet rosszul hasznlni, ami gy nagymrtkben rontja
a program olvashatsgt. A fordtprogram nem rti a megjegyzsek tartalmt, ezrt nincs
md arra, hogy biztostsa azt, hogy egy megjegyzs
[1] rtelmes,
[2] a programmal sszhangban ll s
[3] idszer legyen.
Szmos program olyan megjegyzseket tartalmaz, melyek rthetetlenek, flrerthetek,
vagy egyszeren hibsak. A rossz megjegyzsek rosszabbak, mint ha egyltaln nem hasz-
nlnnk megjegyzst.
Ha valamit lerhatunk magval a programnyelvvel, akkor tegyk azt, ne megjegyzsben
emltsk meg. Ez az szrevtel az ilyenfajta megjegyzsekre vonatkozik:
// a "v" vltznak kezdrtket kell adni
// a "v" vltozt csak az "f()" fggvny hasznlhatja
// az "init()" fggvnyt minden ms fggvny eltt meg kell hvni ebben a fjlban
Alapok 184
// a "cleanup()" fggvnyt meg kell hvni a program vgn
// a "weird()" fggvnyt ne hasznljuk
// az "f()" fggvnynek kt paramtert kell adni
A C++ megfelel hasznlata az ilyen megjegyzseket ltalban szksgtelenn teszi. A fen-
tieket pldul kivlthatjuk, ha alkalmazzuk az sszeszerkesztsi (9.2) vagy az osztlyokra
vonatkoz lthatsgi, kezdrtk-adsi s felszmolsi szablyokat (10.4.1).
Mihelyt valamit vilgosan lertunk a nyelvvel, msodszor mr nem kell megemltennk egy
megjegyzsben:
a = b+c; // a-bl b+c lesz
count++; // nveljk a szmllt
Az ilyen megjegyzsek mg az egyszeren feleslegeseknl is rosszabbak, mert nvelik az
elolvasand szveg hosszt, gyakran sszezavarjk a program szerkezett, s lehet, hogy
hibsak. Meg kell azonban jegyeznnk, hogy az ilyen megjegyzsek szleskren haszn-
latosak tantsi clokra az olyan programozsi nyelvekrl szl knyvekben, mint amilyen
ez is. Ez az egyik, amiben egy knyvben lv program klnbzik egy igazi programtl.
n a kvetkezket szeretem:
1. Minden forrsfjlban van megjegyzs, amely lerja, mi a kzs a fjlban lev
deklarcikban, utal a segdanyagokra, ltalnos tleteket ad a kd mdost-
sval kapcsolatban stb.
2. Minden osztlyhoz, sablonhoz s nvtrhez tartozik megjegyzs.
3. Minden nem magtl rtetd fggvnyhez van olyan megjegyzs, amely lerja
a fggvny cljt, a felhasznlt algoritmust (ha az nem nyilvnval), s esetleg
azt, hogy mit felttelez krnyezetrl.
4. Minden globlis s nvtr-vltozhoz, illetve konstanshoz van megjegyzs.
5. Van nhny megjegyzs ott, ahol a kd nem nyilvnval s/vagy ms rendszer-
re nem tltethet.
6. A fentieken kvl kevs megjegyzs van.
6. Kifejezsek s utastsok 185
Pldul:
// tbl.c: Implementation of the symbol table.
/*
Gaussian elimination with partial pivoting.
See Ralston: "A first course ..." pg 411.
*/
// swap() assumes the stack layout of an SGI R6000.
/*******************************
Copyright (c) 1997 AT&T, Inc.
All rights reserved
*******************************/
A jl megvlasztott s jl megrt megjegyzsek alapvet rszt kpezik a j programnak. J
megjegyzseket rni legalbb olyan nehz, mint megrni magt a programot. Olyan mv-
szet, melyet rdemes mvelni.
Jegyezzk meg azt is, hogy ha kizrlag a // megjegyzseket hasznljuk egy fggvnyben,
akkor ennek a fggvnynek brmely rszt megjegyzsbe tehetjk a /* */ jellssel (ez for-
dtva is igaz).
6.5. Tancsok
[1] Rszestsk elnyben a standard knyvtrat a tbbi knyvtrral s a kzzel rt
kddal szemben. 6.1.8.
[2] Kerljk a bonyolult kifejezseket. 6.2.3.
[3] Ha ktsgeink vannak az opertorok precedencijval kapcsolatban, zrjelez-
znk. 6.2.3.
[4] Kerljk a tpusknyszertst (cast). 6.2.7.
[5] Ha explicit tpuskonverzi szksges, rszestsk elnyben a jobban definilt
konverzis opertorokat a C stlus talaktssal szemben. 6.2.7.
[6] Kizrlag jl meghatrozott szerkezeteknl hasznljuk a T(e) jellst. 6.2.8.
[7] Kerljk az olyan kifejezseket, melyek kirtkelsi sorrendje nem meghatro-
Alapok 186
zott. 6.2.2.
[8] Kerljk a goto-t. 6.3.4.
[9] Kerljk a do utastst. 6.3.3.
[10] Ne adjunk meg vltozt addig, amg nincs rtk, amivel feltlthetnnk. 6.3.1,
6.3.2.1, 6.3.3.1.
[11] A megjegyzseket frisstsk rendszeresen. 6.4.
[12] Tartsunk fenn kvetkezetes stlust. 6.4.
[13] A globlis operator new() helyettestsre adjunk meg inkbb egy operator
new() tagot (15.6). 6.2.6.2.
[14] Bemenet beolvassakor mindig vegyk szmtsba a rosszul megadott bemene-
tet is. 6.1.3.
6.6. Gyakorlatok
1. (*1) rjuk meg a kvetkez for utastssal egyenrtk while utastst:
for (i=0; i<max_length; i++) if (input_line[i] == '?') quest_count++;
Ezt rjuk t gy, hogy ciklusvltozknt mutatt hasznlunk, azaz gy, hogy
a vizsglat *p=='?' alak legyen.
2. (*1) Zrjelezzk teljesen a kvetkez kifejezseket:
a = b + c * d << 2 & 8
a & 077 != 3
a == b || a == c && c < 5
c = x != 0
0 <= i < 7
f(1,2)+3
a = - 1 + + b -- - 5
a = b == c ++
a = b = c = 0
a[4][2] *= * b ? c : * d * 2
a-b,c=d
3. (*2) Olvassuk be reshellyel elvlasztott (nv- s rtk-) prok sorozatt, ahol
a nv egyetlen reshellyel elvlasztott sz, az rtk pedig egy egsz vagy lebe-
gpontos rtk. Szmtsuk ki s rjuk ki minden nvre az rtkek sszegt s
szmtani kzept, valamint az sszes nvre vonatkoz sszeget s szmtani
kzepet. Tipp: 6.1.8.
4. (*1) rjuk meg a bitenknti logikai opertorok (6.2.4) rtktblzatt a 0 s 1
6. Kifejezsek s utastsok 187
operandusok sszes lehetsges prostsra.
5. (*1,5) Talljunk 5 klnbz C++ szerkezetet, melynek jelentse nem meghat-
rozott (C.2). (*1,5.) Talljunk 5 klnbz C++ szerkezetet, melynek jelentse
a nyelvi megvalststl fgg (C.2).
6. (*1) Adjunk 10 klnbz pldt nem hordozhat C++ kdra.
7. (*2) rjunk 5 kifejezst, melyek kirtkelsi sorrendje nem meghatrozott. Hajt-
suk ket vgre s nzzk meg, mit csinl velk egy de lehetleg tbb C++-
vltozat.
8. (*1,5) Mi trtnik a rendszernkben, ha nullval osztunk? Mi trtnik tlcsordu-
ls s alulcsorduls esetn?
9. (*1) Zrjelezzk teljesen a kvetkez kifejezseket:
*p++
*--p
++a--
(int*)p->m
*p.m
*a[i]
10. (*2) rjuk meg a kvetkez fggvnyeket: strlen(), ami egy C stlus karakter-
lnc hosszt adja vissza, strcpy(), ami egy karakterlncot msol egy msikba, s
strcmp(), ami kt karakterlncot hasonlt ssze. Gondoljuk meg, mi legyen a pa-
ramterek s a visszatrsi rtk tpusa. Ezutn hasonltsuk ssze a fggvnye-
ket a standard knyvtrban lv vltozatokkal, ahogy azok a <cstring>-ben
(<string.h>-ban) szerepelnek s ahogy a 20.4.1 pontban lertuk azokat.
11. (*1) Nzzk meg, hogyan reagl a fordtprogramunk ezekre a hibkra:
void f(int a, int b)
{
if (a = 3) // ...
if (a&077 == 0) // ...
a := b+1;
}
Ksztsnk tbb egyszer hibt s nzzk meg, hogyan reagl
a fordtprogram.
12. (*2) Mdostsuk gy a 6.6[3] programot, hogy a kzps rtket (medin) is ki-
szmtsa.
13. (*2) rjuk meg a cat() fggvnyt, amelynek kt C stlus karakterlnc paramte-
re van s egy olyan karakterlncot ad vissza, amely a paramterek sszefz-
sbl ll el. Az eredmnyeknek foglaljunk helyet a new-val.
14. (*2) rjuk meg a rev() fggvnyt, amelynek egy karakterlnc paramtere van s
Alapok 188
megfordtja a benne lv karaktereket. Azaz a rev(p) lefutsa utn p utols ka-
raktere az els lesz s gy tovbb.
15. (*1,5) Mit csinl a kvetkez plda s mirt rna valaki ilyesmit?
void send(int* to, int* from, int count)
// Duff programja. A megjegyzseket szndkosan trltem.
{
int n = (count+7)/8;
switch (count%8) {
case 0: do { *to++ = *from++;
case 7: *to++ = *from++;
case 6: *to++ = *from++;
case 5: *to++ = *from++;
case 4: *to++ = *from++;
case 3: *to++ = *from++;
case 2: *to++ = *from++;
case 1: *to++ = *from++;
} while (--n>0);
}
}
16. (*2) rjuk meg az atoi(const char*) fggvnyt, amely egy szmokat tartalmaz
karakterlncot kap s visszaadja a megfelel egszet. Pldul atoi("123") 123
lesz. Mdostsuk az atoi()-t gy, hogy kezelje a C++ oktlis s hexadecimlis je-
llst is, az egyszer, tzes szmrendszerbeli szmokkal egytt. Mdostsuk
a fggvnyt gy is, hogy kezelje a C++ karakterkonstans jellst is.
17. (*2) rjunk egy olyan itoa(int i, char b[ ]) fggvnyt, amely ltrehozza b-ben i
karakterlnc brzolst s visszaadja b-t.
18. (*2) Gpeljk be teljes egszben a szmolgp pldt s hozzuk mkdsbe.
Ne takartsunk meg idt azzal, hogy mr begpelt szvegrszeket hasznlunk.
A legtbbet a kis buta hibk kijavtsbl fogunk tanulni.
19. (*2) Mdostsuk a szmolgpet, hogy kirja a hibk sornak szmt is.
20. (*3) Tegyk lehetv, hogy a felhasznl fggvnyeket adhasson meg a szmo-
lgphez. Tipp: adjunk meg egy fggvnyt mveletek sorozataknt, gy, mint-
ha a felhasznl gpelte volna be azokat. Az ilyen sorozatokat karakterlncknt
vagy szimblumok (tokenek) listjaknt trolhatjuk. Ezutn olvassuk be s hajt-
suk vgre ezeket a mveleteket, amikor a fggvny meghvdik. Ha azt akar-
juk, hogy egy felhasznli fggvnynek paramterei legyenek, akkor arra kln
jellst kell kitallnunk.
21. (*1,5) Alaktsuk t a szmolgpet, hogy a symbol szerkezetet hasznlja s ne
a statikus number_value s string_value vltozkat.
22. (*2,5) rjunk olyan programot, amely kiveszi a megjegyzseket a C++ progra-
6. Kifejezsek s utastsok 189
mokbl. Azaz, olvassunk a cin-rl, tvoltsuk el mind a //, mind a /* */ megjegy-
zseket, majd rjuk ki az eredmnyt a cout-ra. Ne trdjnk azzal, hogy a kime-
net szp legyen (az egy msik, sokkal nehezebb feladat lenne). Ne trdjnk
a hibs programokkal. vakodjunk a //, /* s */ hasznlattl a megjegyzsek-
ben, karakterlncokban s karakterkonstansokban.
23. (*2) Nzznk meg nhny programot, hogy elkpzelsnk lehessen a mostansg
hasznlatos stlusok (behzsok, elnevezsek s megjegyzsek) vltozatossgrl.
Alapok 190
Fggvnyek
Ismtelni emberi dolog.
Rekurzit rni isteni.
(L. Peter Deutsch)
Fggvnydeklarcik s -defincik Paramtertads Visszatrsi rtkek Fggvny-
tlterhels A tbbrtelmsg feloldsa Alaprtelmezett paramterek stdargs Fgg-
vnyekre hivatkoz mutatk Makrk Tancsok Gyakorlatok
7.1. Fggvnydeklarcik
A C++-ban ltalban gy vgznk el valamit, hogy meghvunk r egy fggvnyt, s a fgg-
vny definicijval rjuk le, hogyan kell azt elvgezni. A fggvnyt azonban nem hvhatjuk
meg gy, hogy elzetesen nem deklarltuk. A fggvny deklarcija megadja a fggvny
nevt, visszatrsi rtknek tpust (ha van ilyen), s azon paramterek szmt s tpust,
amelyeket t kell adni a fggvny meghvsakor:
Elem* next_elem();
char* strcpy(char* to, const char* from);
void exit(int);
7
A paramtertads ugyanazt a szerepet tlti be, mint a kezdeti rtkads. A fordtprogram
ellenrzi a paramterek tpust s automatikus tpuskonverzit vgez, ha szksges:
double sqrt(double);
double sr2 = sqrt(2); // sqrt() meghvsa double(2) paramterrel
double sq3 = sqrt("three"); // hiba: sqrt() double tpus paramtert ignyel
Az ilyen ellenrzs s konverzi jelentsgt nem szabad albecslni.
A fggvnydeklarci paramterneveket is tartalmazhat, melyek segtik a program olvas-
jt. A fordtprogram ezeket egyszeren nem veszi figyelembe. Amint 4.7-ben emltettk,
a void visszatrsi tpus azt jelenti, hogy a fggvny nem ad vissza rtket.
7.1.1. Fggvnydefincik
Minden fggvnyt, amit meghvunk a programban, valahol (de csak egyszer) meg kell ha-
troznunk. A fggvnydefinci (fggvny-meghatrozs) olyan deklarci, amelyben
megadjuk a fggvny trzst:
extern void swap(int*, int*); // deklarci
void swap(int* p, int* q) // definci
{
int t = *p;
*p = *q;
*q = t;
}
A fggvnyek definciinak s deklarciinak ugyanazt a tpust kell meghatrozniuk. A pa-
ramterek nevei azonban nem rszei a tpusnak, gy nem kell azonosaknak lennik.
Nem ritka az olyan fggvnydefinci, amely nem hasznlja fel az sszes paramtert:
void search(table* t, const char* key, const char*)
{
// a harmadik paramter nem hasznlatos
}
Alapok 192
Amint ltjuk, azt a tnyt, hogy egy paramtert nem hasznlunk fel, gy jellhetjk, hogy
nem nevezzk meg a paramtert. A nvtelen paramterek jellemzen a program egyszer-
stse folytn vagy annak ksbbi bvthetsgt biztostand kerlnek a kdba. Mindkt
esetben azzal, hogy br nem hasznljuk fel, de a helykn hagyjuk a paramtereket, bizto-
stjuk, hogy a fggvnyt meghvkat nem rintik a mdostsok.
A fggvnyeket a fordt ltal a hvs sorban kifejtendknt (inline-knt) is megadhatjuk:
inline int fac(int n)
{
return (n<2) ? 1 : n*fac(n-1);
}
Az inline kulcssz javaslat a fordtprogram szmra, hogy a fac() meghvsnl prblja
meg annak kdjt a hvs sorban ltrehozni, ahelyett, hogy elbb ltrehozn azt, majd
a szoksos fggvnyhv eljrs szerint hvn meg. Egy okos fordtprogram a fac(6)
meghvsakor ltre tudja hozni a720 konstanst. Az egymst klcsnsen meghv (klcs-
nsen rekurzv) helyben kifejtett fggvnyek, illetve a bemenettl fggen magukat jrah-
v vagy nem jrahv helyben kifejtett fggvnyek lehetsge miatt nem biztos, hogy egy
inline fggvny minden hvsa a hvs sorban jn ltre. A fordtprogramok intelligenci-
jnak mrtke nem rhat el, gy elfordulhat, hogy az egyik 720-at, a msik 6*fac(5)-t,
a harmadik pedig a fac(6) nem helyben kifejtett hvst hozza ltre.
Ha nem rendelkeznk kivtelesen intelligens fordt- s szerkeszt-programmal, a hvs so-
rban trtn ltrehozst akkor biztosthatjuk, ha a fggvny kifejtse s nem csak dek-
larcija is a hatkrben szerepel (9.2). Az inline kulcssz nem befolysolja a fggvny
rtelmezst. Nevezetesen az ilyen fggvnyeknek s static vltoziknak (7.1.2.) is
ugyangy egyedi cmk van.
7.1.2. Statikus vltozk
A loklis (helyi) vltozk akkor kapnak kezdrtket, amikor a vgrehajts elr a definicijuk-
hoz. Alaprtelmezs szerint ez a fggvny minden meghvsakor megtrtnik, az egyes fgg-
vnyhvsoknak pedig sajt msolatuk van a vltozrl. Ha egy loklis vltozt static-knt ve-
zetnk be, akkor azt a fggvny minden meghvsakor egyetlen, lland cm objektum jel-
li majd. A vltoz csak egyszer kap rtket, amikor a vgrehajts elri annak els defincijt:
7. Fggvnyek 193
void f(int a)
{
while (a--) {
static int n = 0; // egyszer kap kezdrtket
int x = 0; // 'a' alkalommal kap kezdrtket (az f() minden meghvsakor)
cout << "n == " << n++ << ", x == " << x++ << '\n';
}
}
int main()
{
f(3);
}
A fenti a kvetkezket rja ki:
n == 0, x == 0
n == 1, x == 0
n == 2, x == 0
A statikus vltozk anlkl biztostanak emlkezetet a fggvnynek, hogy globlis vlto-
zt vezetnnek be, amelyet ms fggvnyek is elrhetnek s mdostssal hasznlhatatlan-
n tehetnek (lsd mg 10.2.4).
7.2. Paramtertads
Amikor egy fggvny meghvdik, a fordtprogram a formlis paramterek szmra trte-
rletet foglal le, az egyes formlis paramterek pedig a megfelel valdi (aktulis) param-
ter-rtkkel tltdnek fel. A paramtertads szerepe azonos a kezdeti rtktadsval.
A fordtprogram ellenrzi, hogy az aktulis paramterek tpusa megegyezik-e a formlis
paramterek tpusval, s vgrehajt minden szabvnyos s felhasznli tpuskonverzit.
A tmbk tadsra egyedi szablyok vonatkoznak (7.2.1), de van lehetsg nem ellenr-
ztt (7.6) s alaprtelmezett paramterek (7.5) tadsra is. Vegyk a kvetkez pldt:
void f(int val, int& ref)
{
val++;
ref++;
}
Alapok 194
Amikor f() meghvdik, val++ az els aktulis paramter helyi msolatt nveli, ref++ pe-
dig a msodik aktulis paramtert. Az albbi
void g()
{
int i = 1;
int j = 1;
f(i,j);
}
j-t fogja nvelni, de i-t nem. Az els paramter (i) rtk szerint addik t, a msodik (j) re-
ferencia szerint. Amint 5.5-ben emltettk, azok a fggvnyek, melyek mdostjk a refe-
rencia szerint tadott paramtereiket, nehezen olvashatv teszik a programot s ltalban
kerlendk (ellenben lsd 21.3.2-et). szreveheten hatkonyabb lehet, ha egy nagy ob-
jektumot referencia, nem pedig rtk szerint adunk t. Ebben az esetben a paramtert
megadhatjuk const-knt, hogy jelezzk, csak hatkonysgi okokbl hasznlunk referencit
s nem szeretnnk lehetv tenni, hogy a hvott fggvny mdosthassa az objektum rt-
kt:
void f(const Large& arg)
{
// "arg" rtke nem mdosthat, csak explicit tpuskonverzival
}
A referencia-paramter deklarcijban a const elhagysa azt fejezi ki, hogy szndkunk-
ban ll a vltozt mdostani:
void g(Large& arg); // ttelezzk fel, hogy g() mdostja arg-ot
Hasonlan, a const-knt megadott mutat paramter azt jelzi az olvasnak, hogy a param-
ter ltal mutatott objektum rtkt a fggvny nem vltoztatja meg:
int strlen(const char*); // karakterek szma egy C stlus
// karakterlncban
char* strcpy(char* to, const char* from); // C stlus karakterlnc msolsa
int strcmp(const char*, const char*); // C stlus karakterlncok sszehasonltsa
A const paramterek hasznlatnak fontossga a program mretvel egytt n.
Jegyezzk meg, hogy a paramtertads szerepe klnbzik a (nem kezdeti) rtkadstl.
Ez a const paramterek, a referencia-paramterek, s nhny felhasznli tpus paramter
esetben lnyeges (10.4.4.1).
7. Fggvnyek 195
Literlt, llandt s olyan paramtert, amely talaktst ignyel, t lehet adni const& para-
mterknt, de nem const&-knt nem. Mivel a const T& paramterek konverzija megenge-
dett, biztostott, hogy egy ilyen paramternek pontosan ugyanazokat az rtkeket lehet
adni, mint egy T tpus rtknek, azltal, hogy az rtket ideiglenes vltozban adjuk t
(amennyiben ez szksges):
float fsqrt(const float&); // Fortran stlus sqrt referencia-paramterrel
void g(double d)
{
float r = fsqrt(2.0f); // a 2.0f-et tartalmaz ideiglenes vltozra hivatkoz
// referencia tadsa
r = fsqrt(r); // r-re hivatkoz referencia tadsa
r = fsqrt(d); // a float(d)-t tartalmaz ideiglenes vltozra hivatkoz referencia
// tadsa
}
Mivel a nem const referencia tpus paramterek konverzija nem megengedett, az ideig-
lenes vltozk bevezetsbl add buta hibk elkerlhetk:
void update(float& i);
void g(double d, float r)
{
update(2.0f); // hiba: konstans paramter
update(r); // r-re hivatkoz referencia tadsa
update(d); // hiba: tpuskonverzi szksges
}
Ha ezek a hvsok szablyosak lennnek, az update() csendben mdostotta volna azokat
az ideiglenes vltozkat, amelyek azonnal trldtek. Ez rendszerint kellemetlen meglepe-
tsknt rn a programozt.
7.2.1. Tmb paramterek
Ha fggvnyparamterknt tmbt hasznlunk, a tmb els elemre hivatkoz mutat
addik t:
int strlen(const char*);
void f()
{
char v[ ] = "egy tmb";
int i = strlen(v);
int j = strlen("Nicholas");
}
Alapok 196
Azaz egy T[ ] tpus paramter T* tpusv lesz alaktva, ha paramterknt addik t. Ebbl
az kvetkezik, hogy ha egy paramterknt alkalmazott tmb egy elemhez rtket rende-
lnk, a tmb paramter is mdosul, vagyis a tmbk abban klnbznek a tbbi tpustl,
hogy nem rtk szerint addnak t (ez nem is lehetsges).
A tmb mrete nem hozzfrhet a hvott fggvny szmra. Ez bosszant lehet, de tbb
md van r, hogy a problmt megkerljk. A C stlus karakterlncok nulla vgzdsek,
gy mretk knnyen kiszmthat. Ms tmbknl a mretet egy tovbbi paramterrel ad-
hatjuk meg:
void compute1(int* vec_ptr, int vec_size); // egyik mdszer
struct Vec {
int* ptr;
int size;
};
void compute2(const Vec& v); // msik mdszer
Vlaszthatjuk azt is, hogy tmbk helyett olyan tpusokat hasznlunk, mint a vector (3.7.1,
16.3). A tbbdimenzis tmbk nmileg bonyolultabbak (lsd C.7), de helyettk gyakran
hasznlhatunk mutatkbl ll tmbket, melyek nem ignyelnek klnleges bnsmdot:
char* day[ ] = {
"htf", "kedd", "szerda", "cstrtk", "pntek", "szombat", "vasrnap"
};
A vector s a hozz hasonl tpusok a beptett, alacsonyszint tmbk s mutatk helyett
is hasznlhatk.
7.3. Visszatrsi rtk
A main() kivtelvel(3.2) minden nem void-knt megadott fggvnynek rtket kell
visszaadnia. Megfordtva, a void fggvnyek nem adhatnak vissza rtket:
int f1() { } // hiba: nincs visszatrsi rtk
void f2() { } // rendben
7. Fggvnyek 197
int f3() { return 1; } // rendben
void f4() { return 1; } // hiba: visszatrsi rtk void fggvnyben
int f5() { return; } // hiba: visszatrsi rtk hinyzik
void f6() { return; } // rendben
A visszatrsi rtket a return utasts hatrozza meg:
int fac(int n) { return (n>1) ? n*fac(n-1) : 1; }
Az nmagukat meghv fggvnyeket rekurzv (jrahv) fggvnyeknek nevezzk.
Egy fggvnyben tbb return utasts is lehet:
int fac2(int n)
{
if (n > 1) return n*fac2(n-1);
return 1;
}
A paramtertadshoz hasonlan a fggvnyrtk visszaadsnak szerepe is azonos a kez-
deti rtkadsval. A return utastst gy tekintjk, hogy az egy visszatrsi tpus, nv nl-
kli vltoznak ad kezdrtket. A fordtprogram sszehasonltja a return kifejezs tpu-
st a visszatrsi tpussal s minden szabvnyos s felhasznli tpustalaktst vgrehajt:
double f() { return 1; } // 1 automatikusan double(1)-gy alakul
Minden egyes alkalommal, amikor egy fggvny meghvdik, paramtereinek s loklis
(automatikus) vltozinak egy j msolata jn ltre. A tr a fggvny visszatrse utn is-
mt felhasznlsra kerl, ezrt loklis vltozra hivatkoz mutatt soha nem szabad vissza-
adni, mert a mutatott hely tartalma kiszmthatatlan mdon megvltozhat:
int* fp() { int local = 1; /* ... */ return &local; } // rossz
Ez a hiba kevsb gyakori, mint referencit hasznl megfelelje:
int& fr() { int local = 1; /* ... */ return local; } // rossz
Szerencsre a fordtprogram ltalban figyelmeztet, hogy loklis vltozra vonatkoz hi-
vatkozst adtunk vissza.
Alapok 198
A void fggvnyek nem adhatnak vissza rtket, de meghvsuk nem is eredmnyez ilyet,
kvetkezskppen egy void fggvny return utastsban szerepl kifejezsknt hasznl-
hatunk void fggvnyt:
void g(int* p);
void h(int* p) { /* ... */ return g(p); } // rendben: res visszatrsi rtk
Ez a fajta visszatrs olyan sablon (template) fggvnyek rsnl fontos, ahol a visszatr-
si tpus egy sablonparamter (lsd 18.4.4.2).
7.4. Tlterhelt fggvnynevek
A legtbb esetben j tlet klnbz fggvnyeknek klnbz neveket adni, de amikor
egyes fggvnyek lnyegben ugyanazt a mveletet vgzik klnbz tpus objektumo-
kon, knyelmesebb lehet ugyangy elnevezni azokat. Azt, hogy klnbz tpusokra vo-
natkoz mveletekre ugyanazt a nevet hasznljuk, tlterhelsnek (overloading) nevezzk.
Ez a mdszer a C++ alapmveleteinl is hasznlatos. Azaz, csak egyetlen nv van az
sszeadsra (+), az mgis hasznlhat egsz, lebegpontos, s mutat tpus rtkek
sszeadsra is. A gondolatot a programoz ltal ksztett fggvnyekre is kiterjeszthetjk:
void print(int); // egsz kirsa
void print(const char*); // C stlus karakterlnc kirsa
Ami a fordtprogramot illeti, az egyetlen, ami kzs az azonos nev fggvnyekben,
a nv. A fggvnyek felteheten hasonlak valamilyen rtelemben, de a nyelv nem korl-
tozza s nem is segti a programozt. Ezrt a tlterhelt fggvnynevek elsdlegesen jells-
beli knyelmet adnak. Ez a knyelem az olyan hagyomnyos nev fggvnyeknl igazn
lnyeges, mint az sqrt, print, s open. Amikor a nv jelentse fontos, ez a knyelem alapve-
tv vlik. Ez trtnik pldul a +, * s << opertorok, a konstruktorok (11.7), valamint az
ltalnostott (generikus) programozs (2.7.2, 18. fejezet) esetben. Amikor egy f fggvny
meghvdik, a fordtprogramnak ki kell tallnia, melyik f nev fggvnyt hvja meg. Ezt
gy teszi, hogy minden egyes f nev fggvny formlis paramtereinek tpust sszehaso-
ntja az aktulis paramterek tpusval, majd azt a fggvnyt hvja meg, amelynek param-
terei a legjobban illeszkednek, s fordtsi idej hibt ad, ha nincs jl illeszked fggvny:
7. Fggvnyek 199
void print(double);
void print(long);
void f()
{
print(1L); // print(long)
print(1.0); // print(double)
print(1); // hiba, tbbrtelm: print(long(1)) vagy print(double(1))?
}
A fordtprogram a tlterhelt fggvnyek halmazbl gy vlasztja ki a megfelel vltoza-
tot, hogy megkeresi azt a fggvnyt, amelyiknl a hvs paramter-kifejezsnek tpusa
a legjobban illeszkedik a fggvny formlis paramtereire. Ahhoz, hogy mindez elvrsa-
inknak (kzelten) megfelel mdon trtnjen, az albbiakat kell megksrelni (ebben
a sorrendben):
1. Pontos illeszkeds: nincs szksg konverzira vagy csak egyszer konverzikat
kell vgezni (pldul tmb nevt mutatra, fggvny nevt fggvnyre hivatko-
z mutatra, vagy T-t const T-re).
2. Kiterjesztst hasznl illeszkeds: csak egsz rtkre kiterjeszts (integral
promotion) szksges (bool-rl int-re, char-rl int-re, short-rl int-re, illetve
ezek unsigned megfelelirl int-re C.6.1), valamint float-rl double-ra.
3. Szabvnyos konverzikat hasznl illeszkeds: int-rl double-ra, double-rl int-
re, Derived*-rl Base*-ra (12.2), T*-rl void*-ra (5.6), vagy int-rl unsigned
int-re (C.6).
4. Felhasznli konverzikat hasznl illeszkeds (11.4).
5. Fggvnydeklarciban hrom pontot () hasznl illeszkeds (7.6).
Ha azon a szinten, ahol elszr tallunk megfelel illeszkedst, kt illeszkedst is tallunk,
a hvst a fordt tbbrtelmknt elutastja. A tlterhelst felold szablyok elssorban
azrt ennyire alaposan kidolgozottak, mert figyelembe kellett venni a C s a C++ beptett
numerikus tpusokra vonatkoz bonyolult szablyait (C.6). Vegyk a kvetkez pldt:
void print(int);
void print(const char*);
void print(double);
void print(long);
void print(char);
void h(char c, int i, short s, float f)
{
print(c); // pontos illeszkeds: print(char) meghvsa
print(i); // pontos illeszkeds: print(int) meghvsa
Alapok 200
print(s); // kiterjeszts egssz: print(int) meghvsa
print(f); // float kiterjesztse double-l: print(double)
print('a'); // pontos illeszkeds: print(char) meghvsa
print(49); // pontos illeszkeds: print(int) meghvsa
print(0); // pontos illeszkeds: print(int) meghvsa
print("a"); // pontos illeszkeds: print(const char*) meghvsa
}
A print(0) hvs a print(int)-et hvja meg, mert 0 egy int. A print('a') hvs a print(char)-t,
mivel 'a' egy char (4.3.1). Az talaktsok (konverzik) s a kiterjesztsek kztt azrt
tesznk klnbsget, mert elnyben akarjuk rszesteni a biztonsgos kiterjesztseket (pl-
dul char-rl int-re) az olyan, nem biztonsgos mveletekkel szemben, mint az int-rl
char-ra trtn talakts.
A tlterhels feloldsa fggetlen a szba jhet fggvnyek deklarcis sorrendjtl.
A tlterhels viszonylag bonyolult szablyrendszeren alapul, gy a programoz nha meg-
lepdhet azon, melyik fggvny hvdik meg. Ez azonban mg mindig a kisebbik rossz. Ve-
gyk figyelembe a tlterhels alternatvjt: gyakran hasonl mveleteket kell vgrehajta-
nunk tbbfle tpus objektumon. Tlterhels nlkl tbb fggvnyt kellene megadnunk,
klnbz nevekkel:
void print_int(int);
void print_char(char);
void print_string(const char*); // C stlus karakterlnc
void g(int i, char c, const char* p, double d)
{
print_int(i); // rendben
print_char(c); // rendben
print_string(p); // rendben
print_int(c); // rendben? print_int(int(c)) meghvsa
print_char(i); // rendben? print_char(char(i)) meghvsa
print_string(i); // hiba
print_int(d); // rendben? print_int(int(d)) meghvsa
}
A tlterhelt print()-hez kpest tbb nvre s arra is emlkeznnk kell, hogy a neveket he-
lyesen hasznljuk. Ez fraszt lehet, meghistja az ltalnostott programozsra (2.7.2) ir-
nyul erfesztseket, s ltalban arra sztnzi a programozt, hogy viszonylag alacsony
7. Fggvnyek 201
szint tpusokra irnytsa figyelmt. Mivel nincs tlterhels, ezen fggvnyek paramterein
brmilyen szabvnyos konverzit elvgezhetnk, ami szintn hibkhoz vezethet. Ebbl az
kvetkezik, hogy a fenti pldban a ngy hibs paramterrel val hvs kzl csak egyet
vesz szre a fordtprogram. A tlterhels nveli annak az eslyt, hogy egy nem megfele-
l paramtert a fordtprogram elutast.
7.4.1. A tlterhels s a visszatrsi tpus
A tlterhels feloldsnl a visszatrsi tpust nem vesszk figyelembe. Ez azrt szksges,
hogy az egyes opertorokra (11.2.1, 11.2.4) vagy fggvnyhvsra vonatkoz felolds kr-
nyezetfggetlen maradjon. Vegyk a kvetkezt:
float sqrt(float);
double sqrt(double);
void f(double da, float fla)
{
float fl = sqrt(da); // sqrt(double) meghvsa
double d = sqrt(da); // sqrt(double) meghvsa
fl = sqrt(fla); // sqrt(float) meghvsa
d = sqrt(fla); // sqrt(float) meghvsa
}
Ha a visszatrsi tpust a fordtprogram figyelembe venn, tbb nem lenne lehetsges,
hogy elszigetelten nzzk az sqrt() egy hvst s eldntsk, azzal melyik fggvnyt hvtk
meg.
7.4.2. A tlterhels s a hatkr
A klnbz, nem nvtr hatkrben megadott fggvnyek nem tlterheltek:
void f(int);
void g()
{
void f(double);
f(1); // f(double) meghvsa
}
Alapok 202
Vilgos, hogy f(int) lett volna a legjobb illeszkeds f(1)-re, de a hatkrben csak f(double)
lthat. Az ilyen esetekben helyi deklarcikat adhatunk a kdhoz vagy trlhetjk azokat,
hogy megkapjuk a kvnt viselkedst. Mint mindig, a szndkos elfeds hasznos mdszer
lehet, a nem szndkos azonban meglepetseket okozhat. Ha osztly-hatkrk (15.2.2)
vagy nvtr-hatkrk (8.2.9.2) kztt tnyl tlterhelst szeretnnk, using deklarci-
kat vagy using utastsokat hasznlhatunk (8.2.2). Lsd mg 8.2.6-ot.
7.4.3. A tbbrtelmsg kzi feloldsa
Tbbrtelmsghez vezethet, ha egy fggvnynek tl sok (vagy tl kevs) tlterhelt vlto-
zatt adjuk meg:
void f1(char);
void f1(long);
void f2(char*);
void f2(int*);
void k(int i)
{
f1(i); // tbbrtelm: f1(char) vagy f1(long)
f2(0); // tbbrtelm: f2(char*) vagy f2(int*)
}
Ahol lehetsges, az ilyen esetekben gy kell tekintsk egy fggvny tlterhelt vltozatainak
halmazt, mint egszet, s gondoskodnunk kell rla, hogy a vltozatok azonos rtelmek
legyenek. Ez tbbnyire gy oldhat meg, ha a fggvnynek egy olyan j vltozatt adjuk
hozz az eddigiekhez, amely feloldja a tbbrtelmsget:
inline void f1(int n) { f1(long(n)); }
A fenti fggvnyvltozat hozzadsa feloldja az sszes f1(i)-hez hasonl tbbrtelmsget
a szlesebb long int tpus javra.
A hvsok feloldsra tpusknyszertst is hasznlhatunk:
f2(static_cast<int*>(0));
Ez azonban ltalban csnya s ideiglenes szksgmegolds, hiszen a kvetkez hama-
rosan bekvetkez hasonl fggvnyhvssal ismt foglalkoznunk kell majd.
7. Fggvnyek 203
Nhny kezd C++ programozt bosszantanak a fordtprogram ltal kirt tbbrtelmsgi
hibk, a tapasztaltabbak viszont becslik ezeket az zeneteket, mert hasznos jelzi a terve-
zsi hibknak.
7.4.4. Tbb paramter feloldsa
A tlterhelst felold szablyok alapjn biztosthatjuk, hogy a legegyszerbb algoritmus
(fggvny) lesz felhasznlva, amikor a szmtsok hatkonysga s pontossga jelentsen
klnbzik a szban forg tpusoknl:
int pow(int, int);
double pow(double, double);
complex pow(double, complex);
complex pow(complex, int);
complex pow(complex, double);
complex pow(complex, complex);
void k(complex z)
{
int i = pow(2,2); // pow(int,int) meghvsa
double d = pow(2.0,2.0); // pow(double,double) meghvsa
complex z2 = pow(2,z); // pow(double,complex) meghvsa
complex z3 = pow(z,2); // pow(complex,int) meghvsa
complex z4 = pow(z,z); // pow(complex,complex) meghvsa
}
A kett vagy tbb paramter tlterhelt fggvnyek kztti vlaszts folyamn minden pa-
ramterre kivlasztdik a legjobban illeszked fggvny, a 7.4 szablyai alapjn. Az
a fggvny hvdik meg, amely az adott paramterre a legjobban, a tbbire pedig jobban
vagy ugyangy illeszkedik. Ha nem ltezik ilyen fggvny, a hvst a fordt tbbrtelm-
knt elutastja:
void g()
{
double d = pow(2.0,2); // hiba: pow(int(2.0),2) vagy pow(2.0,double(2))?
}
A fggvnyhvs azrt tbbrtelm, mert a pow (double,double) els paramterre 2.0,
a pow(int,int) msodik paramterre pedig 2 a legjobb illeszkeds.
Alapok 204
7.5. Alaprtelmezett paramterek
Egy ltalnos fggvnynek ltalban tbb paramterre van szksge, mint amennyi az egy-
szer esetek kezelshez kell. Nevezetesen az objektumokat (10.2.3) ltrehoz fggv-
nyek gyakran szmos lehetsget nyjtanak a rugalmassg rdekben. Vegynk egy fgg-
vnyt, amely egy egszt r ki. sszernek ltszik megadni a felhasznlnak a lehetsget,
hogy meghatrozza, milyen szmrendszerben rja ki a fggvny az egszt, a legtbb prog-
ram azonban az egszeket tzes szmrendszer szerint rja ki. Pldul a
void print(int value, int base =10); // az alaprtelmezett alap 10
void f()
{
print(31);
print(31,10);
print(31,16);
print(31,2);
}
ezt a kimenetet eredmnyezheti:
31 31 1f 11111
Az alaprtelmezett (default) paramter hatst elrhetjk tlterhelssel is:
void print(int value, int base);
inline void print(int value) { print(value,10); }
A tlterhels viszont kevsb teszi nyilvnvalv az olvas szmra, hogy az a szndkunk,
hogy legyen egy egyszer print fggvnynk s egy rvidtsnk.
Az alaprtelmezett paramter tpusnak ellenrzse a fggvny deklarcijakor trtnik s
a paramter a fggvny hvsakor rtkeldik ki. Alaprtelmezett rtkeket csak a zr pa-
ramtereknek adhatunk:
int f(int, int =0, char* =0); // rendben
int g(int =0, int =0, char*); // hiba
int h(int =0, int, char* =0); // hiba
7. Fggvnyek 205
Jegyezzk meg, hogy a * s az = kztti szkz lnyeges (a *= rtkad opertor, 6.2):
int nasty(char*=0); // szintaktikus hiba
Az alaprtelmezett paramterek ugyanabban a hatkrben egy ksbbi deklarcival nem
ismtelhetk meg s nem mdosthatk:
void f(int x = 7);
void f(int = 7); // hiba: az alaprtelmezett paramter nem adhat meg ktszer
void f(int = 8); // hiba: klnbz alaprtelmezett paramterek
void g()
{
void f(int x = 9); // rendben: ez a deklarci elfedi a klst
// ...
}
Hibalehetsget rejt magban, ha egy nevet gy adunk meg egy begyazott hatkrben,
hogy a nv elfedi ugyanannak a nvnek egy kls hatkrben lev deklarcijt.
7.6. Nem meghatrozott szm paramter
Nhny fggvny esetben nem hatrozhat meg a hvsban elvrt paramterek szma s
tpusa. Az ilyen fggvnyeket gy adhatjuk meg, hogy a paramter-deklarcik listjt a
jellssel zrjuk le, melynek jelentse s taln nhny tovbbi paramter:
int printf(const char* ...);
A fenti azt hatrozza meg, hogy a C standard knyvtrnak printf() fggvnye (21.8)
meghvsakor legalbb egy char* tpus paramtert vr, de lehet, hogy van ms paramte-
re is:
printf("Hell, vilg!\n");
printf("A nevem %s %s\n", vezetek_nev, kereszt_nev);
printf("%d + %d = %d\n",2,3,5);
Alapok 206
Az ilyen fggvnyek olyan adatokra tmaszkodnak, amelyek nem elrhetk a fordtprog-
ram szmra, amikor az a paramterek listjt rtelmezi. A printf() esetben az els para-
mter egy formtum-vezrl, amely egyedi karaktersorozatokat tartalmaz, lehetv tve,
hogy a printf() helyesen kezelje a tbbi paramtert: a %s pldul azt jelenti, hogy vrj egy
char* paramtert, a %d pedig azt, hogy vrj egy int paramtert. A fordtprogram viszont
ltalban nem tudhatja (s nem is biztosthatja), hogy a vrt paramterek tnyleg ott van-
nak s megfelel tpusak-e:
#include <stdio.h>
int main()
{
printf("A nevem %s %s\n",2);
}
A fenti kdot a fordt lefordtja s (a legjobb esetben) furcsnak ltsz kimenetet hoz lt-
re. (Prbljuk ki!)
Termszetesen ha egy paramter nem deklarlt, a fordtprogram nem fog elegend infor-
mcival rendelkezni a szabvnyos tpusellenrzs s -konverzi elvgzshez. Ebben az
esetben egy short vagy egy char int-knt addik t, egy float pedig double-knt, a progra-
moz pedig nem felttlenl ezt vrja.
Egy jl megtervezett programban legfeljebb nhny olyan fggvnyre van szksg, mely-
nek paramterei nem teljesen meghatrozottak. A tlterhelt vagy alaprtelmezett paramte-
reket hasznl fggvnyek arra hasznlhatk, hogy megoldjk a tpusellenrzst a legtbb
olyan esetben, amikor a paramterek tpust szksgbl meghatrozatlanul hagyjuk. A h-
rom pont csak akkor szksges, ha a paramterek szma s tpusa is vltozik. Leggyakrab-
ban akkor hasznljuk, amikor olyan C knyvtri fggvnyekhez ksztnk felletet,
amelyek mg nem hasznljk ki a C++ ltal nyjtott jabb lehetsgeket:
int fprintf(FILE*, const char* ...); // a <cstdio> fejllomnybl
int execl(const char* ...); // UNIX fejllomnybl
A <cstdarg> fejllomnyban szabvnyos makrkat tallunk, melyekkel hozzfrhetnk az
ilyen fggvnyek nem meghatrozott paramtereihez. Kpzeljk el, hogy egy olyan hiba-
fggvnyt runk, amelynek van egy egsz paramtere, ami a hiba slyossgt jelzi, s ezt
tetszleges hosszsg (tbb karakterlncbl ll) szveg kveti. Elkpzelsnk az, hogy
gy hozzuk ltre a hibazenetet, hogy minden szt kln karakterlnc paramterknt
adunk t. Ezen paramterek listjt egy char-ra hivatkoz nullpointer kell, hogy lezrja:
7. Fggvnyek 207
extern void error(int ...);
extern char* itoa(int, char[ ]); // lsd 6.6.[17]
const char* Null_cp = 0;
int main(int argc, char* argv[ ])
{
switch (argc) {
case 1:
error(0,argv[0],Null_cp);
break;
case 2:
error(0,argv[0],argv[1],Null_cp);
break;
default:
char buffer[8];
error(1,argv[0], "with",itoa(argc-1,buffer),"arguments", Null_cp);
}
// ...
}
Az itoa() azt a karakterlncot adja vissza, amelyik a fggvny egsz tpus paramternek
felel meg.
Vegyk szre, hogy ha a 0 egsz rtket hasznltuk volna befejezsknt, a kd nem lett vol-
na hordozhat: nhny nyelvi megvalsts a nulla egszt s a nulla mutatt (nullpointer)
nem azonos mdon brzolja. Ez a plda szemllteti a nehzsgeket s azt a tbbletmun-
kt, amellyel a programoz szembenz, amikor a tpusellenrzst elnyomja a hrom pont.
A hibafggvnyt gy adhatjuk meg:
void error(int severity ...) // a "severity" (slyossg) utn nullval lezrt char*-ok
// kvetkeznek
{
va_list ap;
va_start(ap,severity); // kezdeti paramterek
for (;;) {
char* p = va_arg(ap,char*);
if (p == 0) break;
cerr << p << ' ';
}
va_end(ap); // paramterek visszalltsa
cerr << '\n';
if (severity) exit(severity);
}
Alapok 208
Elszr meghatrozzuk a va_list-et s a va_start() meghvsval rtket adunk neki.
A va_start makr paramterei a va_list neve s az utols formlis paramter neve.
A va_arg() makrt arra hasznljuk, hogy sorba rendezzk a nem megnevezett paramtere-
ket. A programoznak minden egyes hvskor meg kell adnia egy tpust; a va_arg() felt-
telezi, hogy ilyen tpus aktulis paramter kerlt tadsra, de ltalban nincs md arra,
hogy ezt biztostani is tudja. Mieltt mg visszatrnnk egy olyan fggvnybl, ahol
a va_start()-ot hasznltuk, meg kell hvnunk a va_end()-et. Ennek az az oka, hogy
a va_start() gy mdosthatja a vermet, hogy a visszatrst nem lehet sikeresen vghezvin-
ni. A va_end() helyrelltja ezeket a mdostsokat.
7.7. Fggvnyre hivatkoz mutatk
A fggvnyekkel csak kt dolgot csinlhatunk: meghvhatjuk ket s felhasznlhatjuk a c-
mket. Amikor a fggvny cmt vesszk, az gy kapott mutatt hasznlhatjuk arra, hogy
meghvjuk a fggvnyt:
void error(string s) { /* ... */ }
void (*efct)(string); // mutat fggvnyre
void f()
{
efct = &error; // efct az error fggvnyre mutat
efct("error"); // error meghvsa efct-n keresztl
}
A fordtprogram r fog jnni, hogy efct egy mutat s meghvja az ltala mutatott fgg-
vnyt. Azaz, egy fggvnyre hivatkoz mutatt nem ktelez a * opertorral feloldanunk.
Ugyangy nem ktelez a & hasznlata sem a fggvny cmnek lekrdezsre:
void (*f1)(string) = &error; // rendben
void (*f2)(string) = error; // ez is j; jelentse ugyanaz, mint az &error-nak
void g()
{
f1("Vasa"); // rendben
(*f1)("Mary Rose"); // ez is j
}
A fggvnyekre hivatkoz mutatk paramtereinek tpust ugyangy meg kell adnunk,
mint a fggvnyeknl. A mutatkat hasznl rtkadsokban gyelni kell a teljes fggvny
tpusra:
7. Fggvnyek 209
void (*pf)(string); // mutat void(string)-re
void f1(string); // void(string)
int f2(string); // int(string)
void f3(int*); // void(int*)
void f()
{
pf = &f1; // rendben
pf = &f2; // hiba: rossz visszatrsi tpus
pf = &f3; // hiba: rossz paramtertpus
pf("Hra"); // rendben
pf(1); // hiba: rossz paramtertpus
int i = pf("Zeusz"); // hiba: void rtkads int-nek
}
A paramtertads szablyai a kzvetlen fggvnyhvsok s a fggvnyek mutatn keresz-
tl trtn meghvsa esetben ugyanazok. Gyakran knyelmes, ha nevet adunk egy fgg-
vnyre hivatkoz mutat tpusnak, hogy ne mindig a meglehetsen nehezen rthet dek-
larciformt hasznljuk. me egy plda egy UNIX-os rendszer-fejllomnybl:
typedef void (*SIG_TYP)(int); // a <signal.h> fejllomnybl
typedef void (*SIG_ARG_TYP)(int);
SIG_TYP signal(int, SIG_ARG_TYP);
A fggvnyekre hivatkoz mutatkbl ll tmbk gyakran hasznosak. Pldul az egeret
hasznl szvegszerkesztm menrendszere az egyes mveleteket jell fggvnyekre hi-
vatkoz mutatkbl sszelltott tmbkkel van megvalstva. Itt nincs lehetsgnk, hogy
a rendszert rszletesen ismertessk, de az alapvet tlet ez:
typedef void (*PF)();
PF edit_ops[ ] = { // szerkesztmveletek
&cut, &paste, &copy, &search
};
PF file_ops[ ] = { // fjlkezels
&open, &append, &close, &write
};
Az egr gombjaival kivlasztott menpontokhoz kapcsold mveleteket vezrl mutat-
kat gy hatrozhatjuk meg s tlthetjk fel rtkkel:
PF* button2 = edit_ops;
PF* button3 = file_ops;
Alapok 210
A teljes megvalstshoz tbb informcira van szksg ahhoz, hogy minden menelemet
meghatrozhassunk. Pldul trolnunk kell valahol azt a karakterlncot, amelyik meghat-
rozza a kirand szveget. Ahogy a rendszert hasznljuk, az egrgombok jelentse gyakran
megvltozik a krnyezettel egytt. Az ilyen vltozsokat (rszben) gy hajtjuk vgre, hogy
mdostjuk a gombokhoz kapcsolt mutatk rtkt. Amikor a felhasznl kivlaszt egy me-
npontot (pldul a 3-as elemet a 2-es gomb szmra), a megfelel mvelet hajtdik vgre:
button2[2](); // button2 harmadik fggvnynek meghvsa
Akkor tudnnk igazn nagyra rtkelni a fggvnyekre hivatkoz mutatk kifejezerejt,
ha nlklk prblnnk ilyen kdot rni s mg jobban viselked rokonaik, a virtulis
fggvnyek (12.2.6) nlkl. Egy ment futsi idben gy mdosthatunk, hogy j fggv-
nyeket tesznk a mvelettblba, de j menket is knnyen ltrehozhatunk.
A fggvnyekre hivatkoz mutatk arra is hasznlhatk, hogy a tbbalak (polimorf) elj-
rsok azaz amelyeket tbb, klnbz tpus objektumra lehet alkalmazni egyszer for-
mjt adjk:
typedef int (*CFT)(const void*, const void*);
void ssort(void* base, size_t n, size_t sz, CFT cmp)
/*
A "base" vektor "n" elemnek rendezse nvekv sorrendbe
a "cmp" ltal mutatott sszehasonlt fggvny segtsgvel.
Az elemek "sz" mretek.
Shell rendezs (Knuth, 3. ktet, 84.o.)
*/
{
for (int gap=n/2; 0<gap; gap/=2)
for (int i=gap; i<n; i++)
for (int j=i-gap; 0<=j; j-=gap) {
char* b = static_cast<char*>(base); // szksges tpusknyszerts
char* pj = b+j*sz; // &base[j]
char* pjg = b+(j+gap)*sz; // &base[j+gap]
if (cmp(pjg,pj)<0) { // base[j] s base[j+gap] felcserlse
for (int k=0; k<sz; k++) {
char temp = pj[k];
pj[k] = pjg[k];
pjg[k] = temp;
}
}
}
}
7. Fggvnyek 211
Az ssort() nem ismeri azoknak az objektumoknak a tpust, amelyeket rendez, csak az ele-
mek szmt (a tmb mrett), az egyes elemek mrett, s azt a fggvnyt, melyet meg kell
hvnia, hogy elvgezze az sszehasonltst. Az ssort() tpust gy vlasztottuk meg, hogy
megegyezzen a szabvnyos C knyvtri qsort() rendez eljrs tpusval. A valdi progra-
mok a qsort()-ot, a C++ standard knyvtrnak sort algoritmust (18.7.1), vagy egyedi ren-
dez eljrst hasznlnak. Ez a kdolsi stlus gyakori C-ben, de nem a legelegnsabb md-
ja, hogy ezt az algoritmust C++-ban rjuk le (lsd 13.3, 13.5.2).
Egy ilyen rendez fggvnyt a kvetkezkppen lehetne egy tblzat rendezsre hasznlni:
struct User {
char* name;
char* id;
int dept;
};
User heads[ ] = {
"Ritchie D.M.", "dmr", 11271,
"Sethi R.", "ravi", 11272,
"Szymanski T.G.", "tgs", 11273,
"Schryer N.L.", "nls", 11274,
"Schryer N.L.", "nls", 11275,
"Kernighan B.W.", "bwk", 11276
};
void print_id(User* v, int n)
{
for (int i=0; i<n; i++)
cout << v[i].name << '\t' << v[i].id << '\t' << v[i].dept << '\n';
}
Elszr meg kell hatroznunk a megfelel sszehasonlt fggvnyeket, hogy rendezni
tudjunk. Az sszehasonlt fggvnynek negatv rtket kell visszaadnia, ha az els para-
mtere kisebb, mint a msodik, nullt, ha paramterei egyenlek, egyb esetben pedig po-
zitv szmot:
int cmp1(const void* p, const void* q) // nevek (name) sszehasonltsa
{
return strcmp(static_cast<const User*>(p)->name,static_cast<const User*>(q)->name);
}
int cmp2(const void* p, const void* q) // osztlyok (dept) sszehasonltsa
{
return static_cast<const User*>(p)->dept - static_cast<const User*>(q)->dept;
}
Alapok 212
Ez a program rendez s kir:
int main()
{
cout << "Fnkk bcsorrendben:\n";
ssort(heads,6,sizeof(User),cmp1);
print_id(heads,6);
cout << '\n';
cout << "Fnkk osztlyok szerint:\n";
ssort(heads,6,sizeof(User),cmp2);
print_id(heads,6);
}
Egy tlterhelt fggvny cmt gy hasznlhatjuk fel, hogy egy fggvnyre hivatkoz muta-
thoz rendeljk vagy annak kezdrtkl adjuk. Ebben az esetben a cl tpusa alapjn v-
lasztunk a tlterhelt fggvnyek halmazbl:
void f(int);
int f(char);
void (*pf1)(int) = &f; // void f(int)
int (*pf2)(char) = &f; // int f(char)
void (*pf3)(char) = &f; // hiba: nincs void f(char)
Egy fggvnyt egy fggvnyre hivatkoz mutatn keresztl pontosan a megfelel param-
ter- s visszatrsi tpusokkal kell meghvni. Ezen tpusokra vonatkozan nincs automatikus
konverzi, ha fggvnyekre hivatkoz mutatkat adunk rtkl vagy tltnk fel kezdrtk-
kel. Ez azt jelenti, hogy
int cmp3(const mytype*,const mytype*);
nem megfelel paramter az ssort() szmra. Ha cmp3-at elfogadnnk az ssort paramtere-
knt, megszegnnk azt a vllalst, hogy a cmp3-at mytype* tpus paramterekkel fogjuk
meghvni (lsd mg 9.2.5-t).
7. Fggvnyek 213
7.8. Makrk
A makrk nagyon fontosak a C-ben, de kevesebb a hasznuk a C++-ban. Az els makrkra
vonatkoz szably: ne hasznljuk ket, ha nem szksgesek. Majdnem minden makr
a programozsi nyelv, a program, vagy a programoz gyenge pontjt mutatja. Mivel tren-
dezik a programkdot, mieltt a fordtprogram ltn azt, szmos programozsi eszkz sz-
mra komoly problmt jelentenek. gy ha makrt hasznlunk, szmthatunk arra, hogy az
olyan eszkzk, mint a hibakeresk, kereszthivatkozs-vizsglk s hatkonysgvizsglk
gyengbb szolgltatst fognak nyjtani. Ha makrt kell hasznlnunk, olvassuk el figyelme-
sen C++-vltozatunk elfordtjnak (preprocessor) hivatkozsi kziknyvt s ne prbl-
junk tl okosak lenni. Kvessk azt a szokst, hogy a makrkat gy nevezzk el, hogy sok
nagybet legyen bennk. A makrk formai kvetelmnyeit az A.11 mutatja be.
Egy egyszer makrt gy adhatunk meg:
#define NAME a sor maradk rsze
ahol a NAME szimblum elfordul, ott kicserldik a sor maradk rszre. Pldul a
named = NAME
kifejezst a kvetkez vltja fel:
named = a sor maradk rsze
Megadhatunk paramterekkel rendelkez makrt is:
#define MAC(x,y) argument1: x argument2: y
Amikor MAC-ot hasznljuk, paramterknt meg kell adnunk kt karakterlncot. Ezek x-et
s y-t fogjk helyettesteni, amikor MAC behelyettestdik. Pldul a
expanded = MAC(foo bar, yuk yuk)
gy alakul t:
expanded = argument1: foo bar argument2: yuk yuk
Alapok 214
A makrneveket nem terhelhetjk tl s a makr-elfordt rekurzv hvsokat sem tud
kezelni:
#define PRINT(a,b) cout<<(a)<<(b)
#define PRINT(a,b,c) cout<<(a)<<(b)<<(c) /* problms?: jbli definci, nem tlterhels */
#define FAC(n) (n>1)?n*FAC(n-1):1 /* problms: rekurzv makr */
A makrk karakterlncokat kezelnek, keveset tudnak a C++ nyelvtanrl s semmit sem
a C++ tpusairl, illetve a hatkrk szablyairl. A fordtprogram csak a makr behelyet-
testett formjt ltja, gy akkor jelzi a makrban lv esetleges hibt, amikor a makr behe-
lyettestdik, s nem akkor, amikor a makrt kifejtjk, ami nagyon homlyos hibazenetek-
hez vezet.
me nhny lehetsges makr:
#define CASE break;case
#define FOREVER for(;;)
Nhny teljesen flsleges makr:
#define PI 3.141593
#define BEGIN {
#define END }
s nhny veszlyes makr:
#define SQUARE(a) a*a
#define INCR_xx (xx)++
Hogy lssuk, mirt veszlyesek, prbljuk meg behelyettesteni ezt:
int xx = 0; // globlis szmll
void f()
{
int xx = 0; // loklis vltoz
int y = SQUARE(xx+2); // y=xx+2*xx+2 vagyis y=xx+(2*xx)+2
INCR_xx; // a loklis xx nvelse
}
7. Fggvnyek 215
Ha makrt kell hasznlnunk, hasznljuk a :: hatkr-jelzt, amikor globlis nevekre (4.9.4)
hivatkozunk, s a makr paramterek elfordulsait tegyk zrjelbe, ahol csak lehetsges:
#define MIN(a,b) (((a)<(b))?(a):(b))
Ha bonyolult makrkat kell rnunk, amelyek megjegyzsekre szorulnak, blcs dolog /* */
megjegyzseket hasznlnunk, mert a C++ eszkzk rszeknt nha C elfordtkat hasznl-
nak, ezek viszont nem ismerik a // jellst:
#define M2(a) something(a) /* rtelmes megjegyzs */
Makrk hasznlatval megtervezhetjk sajt, egyni nyelvnket. Ha azonban ezt a kibv-
tett nyelvet rszestjk elnyben a sima C++-szal szemben, az a legtbb C++ programoz
szmra rthetetlen lesz. Tovbb a C elfordt egy nagyon egyszer makr-feldolgoz.
Ha valami nem magtl rtetdt akarunk csinlni, akkor az vagy lehetetlennek, vagy szk-
sgtelenl nehznek bizonyulhat. A const, inline, template, enum s namespace
megoldsokat arra szntk, hogy a hagyomnyos elfordtott szerkezeteket kivltsk:
const int answer = 42;
template<class T> inline T min(T a, T b) { return (a<b)?a:b; }
Amikor makrt runk, nem ritka, hogy egy j nvre van szksgnk valami szmra. Kt ka-
rakterlncot a ## makropertorral sszefzve pldul j karakterlncot hozhatunk ltre:
#define NAME2(a,b) a##b
int NAME2(hack,cah)();
Ez a kvetkezt eredmnyezi a fordtprogram szmra:
int hackcah();
A
#undef X
utasts biztostja, hogy X nev makr nem lesz definilva akkor sem, ha az utasts eltt
szerepelt ilyen. Ez bizonyos vdelmet ad a nem kvnt makrk ellen, de nem tudhatjuk,
hogy egy kdrszletben mit felttelezznk X hatsairl.
Alapok 216
7.8.1. Feltteles fordts
A makrk egy bizonyos hasznlatt majdnem lehetetlen elkerlni. Az #ifdef azonost
direktva arra utastja a fordtprogramot, hogy felttelesen minden bemenetet figyelmen
kvl hagyjon, amg az #endif utastssal nem tallkozik. Pldul az
int f(int a
#ifdef arg_two
,int b
#endif
);
kdrszletbl a fordtprogram ennyit lt (kivve ha az arg_two nev makrt a #define
elfordt direktvval korbban definiltuk):
int f(int a
);
Ez megzavarja azokat az eszkzket, amelyek sszer viselkedst tteleznek fel a progra-
mozrl.
Az #ifdef legtbb felhasznlsa kevsb bizarr, s ha mrsklettel hasznljk, kevs krt
okoz. Lsd mg 9.3.3-at.
Az #ifdef-et vezrl makrk neveit figyelmesen kell megvlasztani, hogy ne tkzzenek
a szoksos azonostkkal:
struct Call_info {
Node* arg_one;
Node* arg_two;
// ...
};
Ez az rtatlannak ltsz forrsszveg zavart fog okozni, ha valaki a kvetkezt rja:
#define arg_two x
Sajnos a szokvnyos s elkerlhetetlenl beptend fejllomnyok sok veszlyes s szk-
sgtelen makrt tartalmaznak.
7. Fggvnyek 217
7.9. Tancsok
[1] Legynk gyanakvak a nem const referencia paramterekkel kapcsolatban; ha
azt akarjuk, hogy a fggvny mdostsa paramtert, hasznljunk inkbb muta-
tkat s rtk szerinti visszaadst. 5.5.
[2] Hasznljunk const referencia paramtereket, ha a lehet legritkbbra kell csk-
kentennk a paramterek msolst. 5.5.
[3] Hasznljuk a const-ot szleskren, de kvetkezesen. 7.2.
[4] Kerljk a makrkat. 7.8.
[5] Kerljk a nem meghatrozott szm paramterek hasznlatt. 7.6.
[6] Ne adjunk vissza loklis vltozkra hivatkoz mutatkat vagy ilyen
referencikat. 7.3.
[7] Akkor hasznljuk a tlterhelst, ha a fggvnyek elvben ugyanazt a mveletet
hajtjk vgre klnbz tpusokon. 7.4.
[8] Amikor egszekre vonatkozik a tlterhels, hasznljunk fggvnyeket, hogy
megszntessk a tbbrtelmsget. 7.4.3.
[9] Ha fggvnyre hivatkoz mutat hasznlatt fontolgatjuk, vizsgljuk meg, hogy
egy virtulis fggvny (2.5.5) vagy sablon (2.7.2) hasznlata nem jobb megol-
ds-e. 7.7.
[10] Ha makrkat kell hasznlnunk, hasznljunk csnya neveket, sok nagybetvel.
7.8.
7.10. Gyakorlatok
1. (*1) Deklarljuk a kvetkezket: fggvny, amelynek egy karakterre hivatkoz
mutat s egy egszre mutat referencia paramtere van s nem ad vissza rt-
ket; ilyen fggvnyre hivatkoz mutat; fggvny, amelynek ilyen mutat para-
mtere van; fggvny, amely ilyen mutatt ad vissza. rjuk meg azt a fggvnyt,
amelynek egy ilyen mutatj paramtere van s visszatrsi rtkknt param-
tert adja vissza. Tipp: hasznljunk typedef-et.
2. (*1) Mit jelent a kvetkez sor? Mire lehet j?
typedef int (&rifii) (int, int);
Alapok 218
3. (*1,5) rjunk egy Hell, vilg!-szer programot, ami parancssori paramterknt
vesz egy nevet s kirja, hogy Hell, nv!. Mdostsuk ezt a programot gy,
hogy tetszleges szm nv paramtere lehessen s mondjon hellt minden
egyes nvvel.
4. (*1,5) rjunk olyan programot, amely tetszleges szm fjlt olvas be, melyek
nevei parancssori paramterknt vannak megadva, s kirja azokat egyms utn
a cout-ra. Mivel ez a program sszefzi a paramtereit, hogy megkapja a kime-
netet, elnevezhetjk cat-nek.
5. (*2) Alaktsunk egy kis C programot C++ programm. Mdostsuk a fejllom-
nyokat gy, hogy minden meghvott fggvny deklarlva legyen s hatrozzuk
meg minden paramter tpust. Ahol lehetsges, cserljk ki a #define utastso-
kat enum-ra, const-ra vagy inline-ra. Tvoltsuk el az extern deklarcikat a .c
fjlokbl, s ha szksges, alaktsunk t minden fggvnyt a C++ fggvnyek
formai kvetelmnyeinek megfelelen. Cserljk ki a malloc() s free() hvso-
kat new-ra, illetve delete-re. Tvoltsuk el a szksgtelen konverzikat.
6. (*2) rjuk jra az ssort()-ot (7.7) egy hatkonyabb rendezsi algoritmus felhasz-
nlsval. Tipp: qsort().
7. (*2,5) Vegyk a kvetkezt:
struct Tnode {
string word;
int count;
Tnode* left;
Tnode* right;
};
rjunk fggvnyt, amellyel j szavakat tehetnk egy Tnode-okbl ll fba.
rjunk fggvnyt, amely kir egy Tnode-okbl ll ft. rjunk olyan fggvnyt,
amely egy Tnode-okbl ll ft gy r ki, hogy a szavak bcsorrendben van-
nak. Mdostsuk a Tnode-ot, hogy (csak) egy mutatt troljon, ami egy tetszle-
gesen hossz szra mutat, amit a szabad tr karaktertmbknt trol, a new
segtsgvel. Mdostsuk a fggvnyeket, hogy a Tnode j definicijt
hasznljk.
8. (*2,5) rjunk fggvnyt, amely ktdimenzis tmbt invertl. Tipp: C.7.
9. (*2) rjunk titkost programot, ami a cin-rl olvas s a kdolt karaktereket kir-
ja a cout-ra. Hasznlhatjuk a kvetkez, egyszer titkost smt: c karakter tit-
kostott formja legyen c^key[i], ahol key egy karakterlnc, amely parancssori
paramterknt adott. A program ciklikus mdon hasznlja a key-ben lv karak-
tereket, amg a teljes bemenetet el nem olvasta. Ha nincs megadva key (vagy
a paramter null-karakterlnc), a program ne vgezzen titkostst.
7. Fggvnyek 219
10. (*3,5) rjunk programot, ami segt megfejteni a 7.10[9]-ben lert mdszerrel tit-
kostott zeneteket, anlkl, hogy tudn a kulcsot. Tipp: lsd David Kahn: The
Codebreakers, Macmillan, 1967, New York; 207-213. o.
11. (*3) rjunk egy error nev fggvnyt, amely %s, %c s %d kifejezseket tartal-
maz, printf stlus, formzott karakterlncokat vesz paramterknt s ezen k-
vl tetszleges szm paramtere lehet. Ne hasznljuk a printf()-et. Nzzk
meg a 21.8-at, ha nem tudjuk, mit jelent a %s, %c s %d. Hasznljuk
a <cstdarg>-ot.
12. (*1) Hogyan vlasztannk meg a typedef hasznlatval meghatrozott fgg-
vnyekre hivatkoz mutattpusok neveit?
13. (*2) Nzznk meg nhny programot, hogy elkpzelsnk lehessen a mostan-
sg hasznlatos nevek stlusnak vltozatossgrl. Hogyan hasznljk a nagy-
betket? Hogyan hasznljk az alhzst? Mikor hasznlnak rvid neveket, mint
amilyen az i s x?
14. (*1) Mi a hiba ezekben a makrkban?
#define PI = 3.141593;
#define MAX(a,b) a>b?a:b
#define fac(a) (a)*fac((a)-1)
15. (*3) rjunk makrfeldolgozt, amely egyszer makrkat definil s cserl ki
(ahogy a C elfordt teszi). Olvassunk a cin-rl s rjunk a cout-ra. Elszr ne
prbljunk paramterekkel rendelkez makrkat kezelni. Tipp: az asztali sz-
molgp (6.1) tartalmaz egy szimblumtblt s egy lexikai elemzt, amit m-
dosthatunk.
16. (*2) rjuk meg magunk a print() fggvnyt a 7.5-bl.
17. (*2) Adjunk hozz a 6.1 pontban lv asztali szmolgphez olyan fggvnye-
ket, mint az sqrt(), log(), s sin(). Tipp: adjuk meg elre a neveket, a fggv-
nyeket pedig fggvnyre hivatkoz mutatkbl ll tmbn keresztl hvjuk
meg. Ne felejtsk el ellenrizni a fggvnyhvsok paramtereit.
18. (*1) rjunk olyan faktorilis fggvnyt, amely nem hvja meg nmagt. Lsd
mg 11.14[6]-ot.
19. (*2) rjunk fggvnyeket, amelyek egy napot, egy hnapot, s egy vet adnak
hozz egy Date-hez, ahogy azt a 6.6[13]-ban lertuk. rjunk fggvnyt, ami
megadja, hogy egy adott Date a ht melyik napjra esik. rjunk olyan fggvnyt,
ami megadja egy adott Date-re kvetkez els htf Date-jt.
Alapok 220
Nvterek s kivtelek
Ez a 787-es v!
I.sz.?
(Monty Python)
Nincs olyan ltalnos szably, ami
all ne lenne valamilyen kivtel.
(Robert Burton)
Modulok, felletek s kivtelek Nvterek using using namespace Nvtkzsek fel-
oldsa Nevek keresse Nvterek sszefzse Nvtr-lnevek Nvterek s C kd Ki-
vtelek throw s catch A kivtelek s a programok szerkezete Tancsok Gyakorlatok
8.1. Modulok s felletek
Minden valsgos program klnll rszekbl ll. Mg az egyszer Hell, vilg! prog-
ram is legalbb kt rszre oszthat: a felhasznli kdra, ami a Hell, vilg! kirst kri, s
a kirst vgz I/O rendszerre.
8
Vegyk a szmolgp pldjt a 6.1-bl. Lthatjuk, hogy 5 rszbl ll:
1. A (szintaktikai) elemzbl (parser), ami a szintaktikai elemzst vgzi,
2. az adatbeviteli fggvnybl vagy lexikai elemzbl (lexer), ami a karakterekbl
szimblumokat hoz ltre
3. a (karakterlnc, rtk) prokat trol szimblumtblbl
4. a main() vezrlbl
5. s a hibakezelbl
brval:
A fenti brban a nyl jelentse: felhasznlja. Az egyszersts kedvrt nem jelltem,
hogy mindegyik rsz tmaszkodik a hibakezelsre. Az igazat megvallva a szmolgpet h-
rom rszbl llra terveztem, a vezrlt s a hibakezelt a teljessg miatt adtam hozz.
Amikor egy modul felhasznl egy msikat, nem szksges, hogy mindent tudjon a felhasz-
nlt modulrl. Idelis esetben a modulok legnagyobb rsze nem ismert a felhasznl elem
szmra. Kvetkezskppen klnbsget tesznk a modul s a modul fellete (interfsz)
kztt. A szintaktikai elemz pldul kzvetlenl csak az adatbeviteli fggvny felletre,
nem pedig a teljes lexikai elemzre tmaszkodik. Az adatbeviteli fggvny csak megval-
stja a felletben kzztett szolgltatsokat. Ezt brval gy mutathatjuk be:
Alapok 222
vezrl
elemz
adatbeviteli fggvny
szimblumtbla
hibakezel
A szaggatott vonalak jelentse: megvalstja. Ez tekinthet a program valdi felpts-
nek. Neknk, programozknak, az a feladatunk, hogy ezt h mdon adjuk vissza a kd-
ban. Ha ezt tesszk, a kd egyszer, hatkony, rthet, s knnyen mdosthat lesz, mert
kzvetlenl fogja tkrzni eredeti elkpzelsnket.
A kvetkez rszben bemutatjuk, hogyan lehet a szmolgp program logikai felptst
vilgosan kifejezni, a 9.3 pontban pedig azt, hogyan rendezhetjk el gy a program forrs-
szvegt, hogy abbl elnynk szrmazzon. A szmolgp kis program; a valdi letben
nem hasznlnm olyan mrtkben a nvtereket s a kln fordtst (2.4.1, 9.1), mint itt.
Most csak azrt hasznljuk ezeket, hogy nagyobb programok esetben is hasznos mdsze-
reket mutassunk be, anlkl, hogy belefulladnnk a kdba. A valdi programokban min-
den modul, amelyet nll nvtr jell, gyakran fggvnyek, osztlyok, sablonok stb. sz-
zait tartalmazza.
A nyelvi eszkzk b vlasztknak bemutatshoz tbb lpsben bontom modulokra
a szmolgpet. Az igazi programoknl nem valszn, hogy ezen lpsek mindegyikt
vgrehajtannk. A tapasztalt programoz mr az elejn kivlaszthat egy krlbell megfe-
lel tervet. Ahogy azonban a program az vek sorn fejldik, nem ritkk a drasztikus szer-
kezeti vltoztatsok.
A hibakezels mindentt fontos szerepet tlt be a program szerkezetben. Amikor egy
programot modulokra bontunk vagy egy programot modulokbl hozunk ltre, gyelnnk
kell arra, hogy a hibakezels okozta modulok kztti fggsgekbl minl kevesebb le-
gyen. A C++ kivteleket nyjt arra a clra, hogy elklntsk a hibk szlelst s jelzst
azok kezelstl. Ezrt miutn trgyaltuk, hogyan brzolhatjuk a modulokat nvterek-
knt (8.2), bemutatjuk, hogyan hasznlhatjuk a kivteleket arra, hogy a modularitst to-
vbb javtsuk (8.3).
8. Nvterek s kivtelek 223
vezrl
hibakezel
szintaktikai elemz megvalstsa
lexikai elemz megvalstsa
szimblumtbla megvalstsa
szintaktikai elemz fellete
lexikai elemz fellete
szimblumtbla fellete
A modularits fogalma sokkal tbb mdon rtelmezhet, mint ahogy ebben s a kvetke-
z fejezetben tesszk. Programjainkat pldul rszekre bonthatjuk prhuzamosan vgrehaj-
tott s egymssal kapcsolatot tart folyamatok segtsgvel is. Ugyangy az nll cmterek
(address spaces) s a cmterek kztti informcis kapcsolat is olyan fontos tmakrk,
amelyeket itt nem trgyalunk. gy gondolom, a modularits ezen megkzeltsei nagyrszt
egymstl fggetlenek s ellenttesek. rdekes mdon minden rendszer knnyen modu-
lokra bonthat. A nehzsget a modulok kztti biztonsgos, knyelmes s hatkony kap-
csolattarts biztostsa jelenti.
8.2. Nvterek
A nvterek (namespace) mindig valamilyen logikai csoportostst fejeznek ki. Azaz, ha
egyes deklarcik valamilyen jellemz alapjn sszetartoznak, akkor ezt a tnyt kifejezhet-
jk gy is, hogy kzs nvtrbe helyezzk azokat. A szmolgp elemzjnek (6.1.1) dek-
larciit pldul a Parser nvtrbe tehetjk:
namespace Parser {
double expr(bool);
double prim(bool get) { /* ... */ }
double term(bool get) { /* ... */ }
double expr(bool get) { /* ... */ }
}
Az expr() fggvnyt elszr deklarljuk s csak ksbb fejtjk ki, hogy megtrjk a 6.1.1-
ben lert fggsgi krt.
A szmolgp bemeneti rszt szintn nll nvtrbe helyezhetjk:
namespace Lexer {
enum Token_value {
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
Token_value curr_tok;
double number_value;
string string_value;
Token_value get_token() { /* ... */ }
}
Alapok 224
A nvterek ilyen hasznlata elg nyilvnvalv teszi, mit nyjt a lexikai s a szintaktikai
elemz a felhasznl programelemnek. Ha azonban a fggvnyek forrskdjt is a nvte-
rekbe helyeztem volna, a szerkezet zavaross vlt volna. Ha egy valsgos mret nvtr
deklarcijba beletesszk a fggvnytrzseket is, ltalban tbb oldalas (kpernys) in-
formcin kell trgnunk magunkat, mire megtalljuk, milyen szolgltatsok vannak felk-
nlva, azaz, hogy megtalljuk a felletet.
Kln meghatrozott felletek helyett olyan eszkzket is biztosthatunk, amelyek kinye-
rik a felletet egy modulbl, amely a megvalstst tartalmazza. Ezt nem tekintem j meg-
oldsnak. A felletek meghatrozsa alapvet tervezsi tevkenysg (lsd 23.4.3.4-et),
hiszen egy modul a klnbz programelemek szmra klnbz felleteket nyjthat, r-
adsul a felletet sokszor mr a megvalsts rszleteinek kidolgozsa eltt megtervezik.
me a Parser egy olyan vltozata, ahol a felletet (interfsz) elklntjk a megvalststl
(implementci):
namespace Parser {
double prim(bool);
double term(bool);
double expr(bool);
}
double Parser::prim(bool get) { /* ... */ }
double Parser::term(bool get) { /* ... */ }
double Parser::expr(bool get) { /* ... */ }
Vegyk szre, hogy a fellet s a lnyegi programrsz sztvlasztsnak eredmnyeknt
most minden fggvnynek pontosan egy deklarcija s egy defincija van. A felhasznl
programelemek csak a deklarcikat tartalmaz felletet fogjk ltni. A program megval-
stst ebben az esetben a fggvnytrzseket a felhasznl elem ltkrn kvl he-
lyezzk el.
Lthattuk, hogy egy tagot megadhatunk a nvtr meghatrozsn bell, s kifejthetjk k-
sbb, a nvtr_neve::tag_neve jellst hasznlva.
A nvtr tagjait a kvetkez jells hasznlatval kell bevezetni:
namespace nvtr_nv {
// deklarci s defincik
}
8. Nvterek s kivtelek 225
A nvtrdefincin kvl j tagot nem adhatunk meg minst formban:
void Parser::logical(bool); // hiba: nincs logical() a Parser nvtrben
A cl az, hogy knnyen meg lehessen tallni minden nevet a nvtrdeklarciban, s hogy
a gpelsi, illetve az eltr tpusokbl add hibkat szrevegyk:
double Parser::trem(bool); // hiba: nincs trem() a Parser nvtrben
double Parser::prim(int); // hiba: Parser::prim() logikai paramter
A nvtr (namespace) egyben hatkr (scope), vagyis nagyon alapvet s viszonylag egy-
szer fogalom. Minl nagyobb egy program, annl hasznosabbak a nvterek, hogy kifejez-
zk a program rszeinek logikai elklntst. A kznsges loklis hatkrk, a globlis
hatkrk s az osztlyok maguk is nvterek (C.10.3). Idelis esetben egy program min-
den eleme valamilyen felismerhet logikai egysghez (modulhoz) tartozik. Ezrt elmle-
tileg egy bonyolultabb program minden deklarcijt nll nvterekbe kellene helyez-
ni, melyek neve a programban betlttt logikai szerepet jelzi. A kivtel a main(), amelynek
globlisnak kell lennie, hogy a futsi idej krnyezet felismerje (8.3.3).
8.2.1. Minstett nevek
A nvterek kln hatkrt alkotnak. Az ltalnos hatkr-szablyok termszetesen rjuk is
vonatkoznak, gy ha egy nevet elzetesen a nvtrben vagy egy krlvev blokkban ad-
tunk meg, minden tovbbi nehzsg nlkl hasznlhatjuk. Msik nvtrbl szrmaz nevet
viszont csak akkor hasznlhatunk, ha minstjk nvternek nevvel:
double Parser::term(bool get) // figyeljk meg a Parser:: minstt
{
double left = prim(get); // nem kell minst
for (;;)
switch (Lexer::curr_tok) { // figyeljk meg a Lexer:: minstt
case Lexer::MUL: // figyeljk meg a Lexer:: minstt
left *= prim(true); // nem kell minst
// ...
}
// ...
}
A Parser minstre itt azrt van szksg, hogy kifejezzk, hogy ez a term() az, amelyet
a Parser-ben bevezettnk, s nem valamilyen ms globlis fggvny. Mivel a term()
Alapok 226
a Parser tagja, nem kell minstenie a prim()-et. Ha azonban a Lexer minstt nem tesszk
ki, a fordtprogram a curr_tok vltozt gy tekinti, mintha az nem deklarlt lenne, mivel
a Lexer nvtr tagjai nem tartoznak a Parser nvtr hatkrbe.
8.2.2. Using deklarcik
Ha egy nv gyakran hasznlatos sajt nvtern kvl, bosszant lehet llandan minsteni
nvternek nevvel. Vegyk a kvetkezt:
double Parser::prim(bool get) // elemi szimblumok kezelse
{
if (get) Lexer::get_token();
switch (Lexer::curr_tok) {
case Lexer::NUMBER: // lebegpontos konstans
Lexer::get_token();
return Lexer::number_value;
case Lexer::NAME:
{ double& v = table[Lexer::string_value];
if (Lexer::get_token() == Lexer::ASSIGN) v = expr(true);
return v;
}
case Lexer::MINUS: // mnusz eljel (egyoperandus mnusz)
return -prim(true);
case Lexer::LP:
{ double e = expr(true);
if (Lexer::curr_tok != Lexer::RP) return Error::error(") szksges");
Lexer::get_token(); // ')' lenyelse
return e;
}
case Lexer::END:
return 1;
default:
return Error::error("elemi szimblum szksges");
}
}
A Lexer minsts ismtelgetse igen fraszt, de ki lehet kszblni egy using deklarci-
val, amellyel egy adott helyen kijelentjk, hogy az ebben a hatkrben hasznlt get_token
a Lexer get_token-je:
double Parser::prim(bool get) // elemi szimblumok kezelse
{
using Lexer::get_token; // a Lexer get_token-jnek hasznlata
using Lexer::curr_tok; // a Lexer curr_tok-jnak hasznlata
using Error::error; // az Error error-jnak hasznlata
8. Nvterek s kivtelek 227
if (get) get_token();
switch (curr_tok) {
case Lexer::NUMBER: // lebegpontos konstans
get_token();
return Lexer::number_value;
case Lexer::NAME:
{ double& v = table[Lexer::string_value];
if (get_token() == Lexer::ASSIGN) v = expr(true);
return v;
}
case Lexer::MINUS: // mnusz eljel
return -prim(true);
case Lexer::LP:
{ double e = expr(true);
if (curr_tok != Lexer::RP) return error(") szksges");
get_token(); // ')' lenyelse
return e;
}
case Lexer::END:
return 1;
default:
return error("elemi szimblum szksges");
}
}
A using direktva egy loklis szinonmt vezet be.
A loklis szinonmkat ltalban clszer a lehet legszkebb hatkrrel hasznlni, hogy
elkerljk a tvedseket. A mi esetnkben azonban az elemz minden fggvnye ugyan-
azokat a neveket hasznlja a tbbi modulbl, gy a using deklarcikat elhelyezhetjk
a Parser nvtr meghatrozsban is:
namespace Parser {
double prim(bool);
double term(bool);
double expr(bool);
using Lexer::get_token; // a Lexer get_token-jnek hasznlata
using Lexer::curr_tok; // a Lexer curr_tok-jnak hasznlata
using Error::error; // az Error error-jnak hasznlata
}
Alapok 228
gy a Parser fggvnyeit majdnem az eredeti vltozatukhoz (6.1.1) hasonlra egyszerst-
hetjk:
double Parser::term(bool get) // szorzs s oszts
{
double left = prim(get);
for (;;)
switch (curr_tok) {
case Lexer::MUL:
left *= prim(true);
break;
case Lexer::DIV:
if (double d = prim(true)) {
left /= d;
break;
}
return error("oszts 0-val");
default:
return left;
}
}
Azt is megtehetnnk, hogy a lexikai szimblumok (token, nyelvi egysg) neveit a Parser
nvtrbe is bevezetjk. Azrt hagyjuk ket minstett alakban, hogy emlkeztessenek,
a Parser a Lexer-re tmaszkodik.
8.2.3. Using direktvk
Mit tehetnk, ha clunk az, hogy a Parser fggvnyeit annyira leegyszerstsk, hogy pon-
tosan olyanok legyenek, mint eredeti vltozataik? Egy nagy program esetben sszernek
tnik, hogy egy elz, kevsb modulris vltozatt nvtereket hasznlva alaktsuk t.
A using direktva majdnem ugyangy teszi elrhetv egy nvtr neveit, mintha azokat
a nvterkn kvl vezettk volna be (8.2.8):
namespace Parser {
double prim(bool);
double term(bool);
double expr(bool);
using namespace Lexer; // a Lexer sszes nevt elrhetv teszi
using namespace Error; // az Error sszes nevt elrhetv teszi
}
8. Nvterek s kivtelek 229
Ez lehetv teszi szmunkra, hogy a Parser fggvnyeit pontosan gy rjuk meg, ahogy azt
eredetileg tettk (6.1.1):
double Parser::term(bool get) // szorzs s oszts
{
double left = prim(get);
for (;;)
switch (curr_tok) { // a Lexer-beli curr_tok
case MUL: // a Lexer-beli MUL
left *= prim(true);
break;
case DIV: // a Lexer-beli DIV
if (double d = prim(true)) {
left /= d;
break;
}
return error("oszts 0-val"); // az Error-beli error
default:
return left;
}
}
A using direktvk a nvterekben ms nvterek beptsre hasznlhatk (8.2.8), fggv-
nyekben jellsbeli segtsgknt vehetk biztonsgosan ignybe (8.3.3.1). A globlis using
direktvk a nyelv rgebbi vltozatairl val tllsra szolglnak (8.2.9), egybknt jobb,
ha kerljk ket.
8.2.4. Tbb fellet hasznlata
Vilgos, hogy a Parser szmra ltrehozott nvtr nem a fellet, amit a Parser a felhaszn-
l programelem szmra nyjt. Inkbb olyan deklarcihalmaznak tekinthetjk, ami az
egyes elemz fggvnyek knyelmes megrshoz szksges. A Parser fellete a felhasz-
nl elemek szmra sokkal egyszerbb kellene, hogy legyen:
namespace Parser {
double expr(bool);
}
Alapok 230
Szerencsre a kt nvtr-meghatrozs egyttesen ltezhet, gy mindkett felhasznlhat
ott, ahol az a legmegfelelbb. Lthatjuk, hogy a Parser nvtr kt dolgot nyjt:
[1] Kzs krnyezetet az elemzt megvalst fggvnyek szmra
[2] Kls felletet, amit az elemz a felhasznl programelem rendelkezsre bocst
Ennek rtelmben a main() vezrlkd csak a kvetkezt kell, hogy lssa:
namespace Parser { // felhasznli fellet
double expr(bool);
}
Brmelyik felletet is talltuk a legjobbnak az elemz fggvnyek kzs krnyezetnek b-
rzolsra, a fggvnyeknek ltniuk kell azt:
namespace Parser { // fellet a megvalstshoz
double prim(bool);
double term(bool);
double expr(bool);
using Lexer::get_token; // a Lexer get_token-jnek hasznlata
using Lexer::curr_tok; // a Lexer curr_tok-jnak hasznlata
using Error::error; // az Error error-jnak hasznlata
}
brval:
A nyilak a ltal nyjtott felleten alapul viszonyokat fejezik ki.
8. Nvterek s kivtelek 231
Parser' Parser
Parser megvalsts Driver
A Parser (Parser prime) a felhasznl programelemek szmra nyjtott szk fellet; nem
C++ azonost. Szndkosan vlasztottam, hogy jelljem, ennek a felletnek nincs kln
neve a programban. A kln nevek hinya nem okozhat zavart, mert a programozk az
egyes felletek szmra klnbz s maguktl rtetd neveket tallnak ki, s mert
a program fizikai elrendezse (lsd 9.3-at) termszetesen klnbz (fjl)neveket ad.
A programozi fellet nagyobb a felhasznlknak nyjtottnl. Ha ez a fellet egy valdi
rendszer valsgos mret moduljnak fellete lenne, sokkal gyakrabban vltozna, mint
a felhasznlk ltal lthat fellet. Fontos, hogy a modulokat hasznl fggvnyeket (eb-
ben az esetben a Parser-t hasznl main()-t) elklntsk az ilyen mdostsoktl.
A kt fellet brzolsra nem kell nll nvtereket hasznlnunk, de ha akarnnk, megte-
hetnnk. A felletek megtervezse az egyik legalapvetbb tevkenysg, de ktl fegyver.
Kvetkezskppen rdemes vgiggondolni, valjban mit prblunk megvalstani, s
tbb megoldst is kiprblni.
Az itt bemutatott megolds az ltalunk megtekintettek kzl a legegyszerbb s gyakran
a legjobb. Legfbb gyengje, hogy a kt fellet neve nem klnbzik, valamint hogy a for-
dtprogram szmra nem ll rendelkezsre elegend informci, hogy ellenrizze a nv-
tr kt definicijnak kvetkezetessgt. A fordtprogram azonban rendszerint akkor is
megprblja ellenrizni az sszefggseket, ha erre nincs mindig lehetsge, a szerkeszt-
program pedig szreveszi a legtbb olyan hibt, amin a fordtprogram tsiklott.
Az itt bemutatott megoldst hasznlom a fizikai modularits (9.3) trgyalsra is, s ezt ajn-
lom arra az esetre is, amikor nincsenek tovbbi logikai megszortsok (lsd mg 8.2.7-et).
8.2.4.1. Fellettervezsi mdszerek
A felletek clja az, hogy a lehetsges mrtkig cskkentsk a programok klnbz rszei
kztt fennll fggsgeket. A kisebb fellet knnyebben rthet rendszerhez vezet, mely-
nek adatrejtsi tulajdonsgai jobbak, knnyebben mdosthat s gyorsabban lefordthat.
Amikor a fggsgeket nzzk, fontos emlkeznnk arra, hogy a fordtprogramok s
a programozk az albbi egyszer hozzllssal viszonyulnak hozzjuk: ha egy definci
az X pontrl lthat (a hatkrben van), akkor brmi, ami az X pontban van lerva, brmi-
tl fgghet, ami abban a definciban lett meghatrozva. Persze a helyzet ltalban nem
ennyire rossz, mert a legtbb definci a legtbb kd szmra nem br jelentsggel. Ko-
rbbi definciinkat adottnak vve vegyk a kvetkezt:
Alapok 232
namespace Parser { // fellet a megvalstshoz
// ...
double expr(bool);
// ...
}
int main()
{
// ...
Parser::expr(false);
// ...
}
A main() fggvny csak a Parser::expr() fggvnytl fgg, de idre, gondolkodsra, sz-
molgatsra stb. van szksg ahhoz, hogy erre rjjjnk. Kvetkezskppen a valsgos m-
ret programok esetben a programozk s a fordtsi rendszerek tbbnyire biztosra men-
nek s felttelezik, hogy ahol elfordulhat fggsg, ott el is fordul, ami teljesen sszer
megkzelts. Clunk ezrt az, hogy gy fejezzk ki programunkat, hogy a lehetsges fg-
gsgek halmazt a valban rvnyben lev fggsgek halmazra szktjk.
Elszr megprbljuk a magtl rtetdt: a mr meglv megvalstsi fellet segtsg-
vel az elemz szmra felhasznli felletet hatrozunk meg:
namespace Parser { // fellet a megvalstshoz
// ...
double expr(bool);
// ...
}
namespace Parser_interface { // fellet a felhasznlknak
using Parser::expr;
}
Nyilvnval, hogy a Parser_interface-t hasznl programelemek kizrlag s csupn kz-
vetett mdon a Parser::expr() fggvnytl fggnek. Mgis, ha egy pillantst vetnk a fg-
gsgek brjra, a kvetkezt ltjuk:
8. Nvterek s kivtelek 233
Most a Driver (a vezrl) tnik sebezhetnek a Parser fellet vltozsaival szemben, pedig
azt hittk, jl elszigeteltk tle. Mg a fggsg ilyen megjelense sem kvnatos, gy meg-
szortjuk a Parser_interface fggsgt a Parser-tl, gy, hogy a megvalstsi felletnek
csak az elemz szmra lnyeges rszt (ezt korbban Parser'-nek neveztk) tesszk ltha-
tv ott, ahol a Parser_interface-t meghatrozzuk:
namespace Parser { // fellet a felhasznlknak
double expr(bool);
}
namespace Parser_interface { // eltr nev fellet a felhasznlknak
using Parser::expr;
}
brval:
Alapok 234
Parser_interface
Parser
Driver Parser megvalsts
Parser_interface
Parser Parser'
Driver Parser megvalsts
A Parser s a Parser' egysgessgt biztostand, az egyetlen fordtsi egysgen dolgoz
fordtprogram helyett ismt a fordtsi rendszer egszre tmaszkodunk. Ez a megolds
csak abban klnbzik a 8.2.4-ben szerepltl, hogy kiegszl a Parser_interface nv-
trrel. Ha akarnnk, a Parser_interface-t egy sajt expr() fggvnnyel konkrtan is brzol-
hatnnk:
namespace Parser_interface {
double expr(bool);
}
Most a Parser-nek nem kell a hatkrben lennie, hogy meghatrozhassuk
a Parser_interface-t. Csak ott kell lthatnak lennie, ahol a Parser_interface::expr() fgg-
vnyt kifejtjk:
double Parser_interface::expr(bool get)
{
return Parser::expr(get);
}
Az utbbi vltozatot brval gy szemlltethetjk:
A fggsgeket ezzel a lehet legkevesebbre cskkentettnk. Mindent kifejtettnk s meg-
felelen elneveztnk. Mgis, ezt a megoldst a legtbb esetben tlznak tallhatjuk.
8. Nvterek s kivtelek 235
Parser_interface
Parser_interface
megvalsts
Parser
Driver Parser megvalsts
8.2.5. A nvtkzsek elkerlse
A nvterek logikai szerkezetek kifejezsre valk. A legegyszerbb eset, amikor kt sze-
mly ltal rt kdot kell megklnbztetnnk. Ez gyakran fontos gyakorlati jelentsggel
br. Ha csak egyetlen globlis hatkrt hasznlunk, igen nehz lesz a programot klnll
rszekbl ltrehozni. Az a problma merlhet fel, hogy az nllnak felttelezett rszek
mindegyike ugyanazokat a neveket hasznlja, gy amikor egyetlen programban egyestjk
azokat, a nevek tkzni fognak. Vegyk a kvetkezt:
// my.h:
char f(char);
int f(int);
class String { /* ... */ };
// your.h:
char f(char);
double f(double);
class String { /* ... */ };
Ha a fentieket meghatrozzuk, egy harmadik szemly csak nehezen hasznlhatja egyszer-
re a my.h-t s a your.h-t is. A kzenfekv megolds, hogy mindkt deklarcihalmazt sajt,
kln nvtrbe helyezzk:
namespace My {
char f(char);
int f(int);
class String { /* ... */ };
}
namespace Your {
char f(char);
double f(double);
class String { /* ... */ };
}
Most mr alkalmazhatjuk a My s a Your deklarciit, ha minstket (8.2.1), using dekla-
rcikat (8.2.2) vagy using direktvkat (8.2.3) hasznlunk.
8.2.5.1. Nvtelen nvterek
Gyakran hasznos deklarcik halmazt nvtrbe helyezni, pusztn azrt, hogy vdekez-
znk a lehetsges nvtkzsekkel szemben. A clunk az, hogy a kd helyileg maradjon
rvnyes, nem pedig az, hogy felletet nyjtsunk a felhasznlknak:
Alapok 236
#include "header.h"
namespace Mine {
int a;
void f() { /* ... */ }
int g() { /* ... */ }
}
Mivel nem akarjuk, hogy a Mine nv ismert legyen az adott krnyezeten kvl is, nem r-
demes olyan felesleges globlis nevet kitallni, amely vletlenl tkzhet valaki ms neve-
ivel. Ilyen esetben a nvteret nvtelenl hagyhatjuk:
#include "header.h"
namespace {
int a;
void f() { /* ... */ }
int g() { /* ... */ }
}
Vilgos, hogy kell lennie valamilyen mdszernek arra is, hogy kvlrl frhessnk hozz
egy nvtelen nvtr (unnamed namespace) tagjaihoz. A nvtelen nvtrhez tartozik egy rej-
tett using direktva is. Az elz deklarci egyenrtk a kvetkezvel:
namespace $$$ {
int a;
void f() { /* ... */ }
int g() { /* ... */ }
}
using namespace $$$;
Itt $$$ valamilyen nv, amely egyedi abban a hatkrben, ahol a nvteret meghatroztuk.
A klnbz fordtsi egysgekben lv nvtelen nvterek mindig klnbzek. Ahogy azt
szerettk volna, nincs md arra, hogy egy nvtelen nvtr egy tagjt egy msik fordtsi
egysgbl megnevezhessk.
8.2.6. Nevek keresse
Egy T tpus paramterrel rendelkez fggvnyt ltalban a T-vel azonos nvtrben szoks
megadni. Kvetkezskppen ha egy fggvnyt nem tallunk meg hasznlati krnyezet-
ben, akkor paramtereinek nvterben fogjuk keresni:
namespace Chrono {
class Date { /* ... */ };
8. Nvterek s kivtelek 237
bool operator==(const Date&, const std::string&);
std::string format(const Date&); // string brzols
// ...
}
void f(Chrono::Date d, int i)
{
std::string s = format(d); // Chrono::format()
std::string t = format(i); // hiba: a hatkrben nincs format()
}
Ez a keressi szably a minstk hasznlatval ellenttben sok gpelstl kmli meg
a programozt, s nem is szennyezi gy a nvteret, mint a using direktva (8.2.3). Alkal-
mazsa klnsen fontos az opertorok operandusai (11.2.4) s a sablonparamterek
(C.13.8.4) esetben, ahol a minstk hasznlata nagyon fraszt lehet.
Vegyk szre, hogy maga a nvtr a hatkrben kell, hogy legyen, a fggvnyt pedig csak
akkor tallhatjuk meg s hasznlhatjuk fel, ha elbb bevezettk.
Termszetesen egy fggvny tbb nvtrbl is kaphat paramtereket:
void f(Chrono::Date d, std::string s)
{
if (d == s) {
// ...
}
else if (d == "1914 augusztus 4") {
// ...
}
}
Az ilyen esetekben a fggvnyt a fordtprogram a szoksos mdon, a hvs hatkrben,
illetve az egyes paramterek nvterben (belertve a paramterek osztlyt s alaposztlyt
is) keresi, s minden tallt fggvnyre elvgzi a tlterhels feloldst (7.4). Nevezetesen,
a fordt a d==s hvsnl az operator==-t az f()-et krlvev hatkrben, az (==-t string-
ekre meghatroz) std nvtrben, s a Chrono nvtrben keresi. Ltezik egy
std::operator==(), de ennek nincs Date paramtere, ezrt a Chrono::operator==()-t hasznl-
ja, amelynek viszont van. Lsd mg 11.2.4-et.
Alapok 238
Amikor egy osztlytag meghv egy nvvel rendelkez fggvnyt, az osztly s bzisoszt-
lynak tagjai elnyben rszeslnek azokkal a fggvnyekkel szemben, melyeket a fordt-
program a paramterek tpusa alapjn tallt. Az opertoroknl ms a helyzet (11.2.1,
11.2.4).
8.2.7. Nvtr-lnevek
Ha a felhasznlk nvtereiknek rvid neveket adnak, a klnbz nvterek nevei kny-
nyebben tkzhetnek:
namespace A { // rvid nv, (elbb-utbb) tkzni fog
// ...
}
A::String s1 = "Grieg";
A::String s2 = "Nielsen";
Valdi kdban viszont ltalban nem clszer hossz nvtrneveket hasznlni:
namespace American_Telephone_and_Telegraph { // tl hossz
// ...
}
American_Telephone_and_Telegraph::String s3 = "Grieg";
American_Telephone_and_Telegraph::String s4 = "Nielsen";
A dilemmt gy oldhatjuk fel, ha a hosszabb nvtrneveknek rvid lneveket (alias) adunk:
// hasznljunk nvtr-lneveket a nevek rvidtsre:
namespace ATT = American_Telephone_and_Telegraph;
ATT::String s3 = "Grieg";
ATT::String s4 = "Nielsen";
A nvtr-lnevek azt is lehetv teszik a felhasznlnak, hogy a knyvtrra hivatkozzon
s egyetlen deklarciban hatrozza meg, valjban melyik knyvtrra gondol:
namespace Lib = Foundation_library_v2r11;
// ...
Lib::set s;
Lib::String s5 = "Sibelius";
8. Nvterek s kivtelek 239
Ez nagymrtkben egyszerstheti a knyvtrak msik vltozatra trtn cserjt. Azltal,
hogy kzvetlenl Lib-et hasznlunk a Foundation_library_v2r11 helyett, a Lib lnv rt-
knek mdostsval s a program jrafordtsval a v3r02 vltozatra frissthetjk
a knyvtrat. Az jrafordts szre fogja venni a forrsszint sszefrhetetlensgeket. Ms-
rszrl, a (brmilyen tpus) lnevek tlzott hasznlata zavart is okozhat.
8.2.8. Nvterek sszefzse
Egy felletet gyakran mr ltez felletekbl akarunk ltrehozni:
namespace His_string {
class String { /* ... */ };
String operator+(const String&, const String&);
String operator+(const String&, const char*);
void fill(char);
// ...
}
namespace Her_vector {
template<class T> class Vector { /* ... */ };
// ...
}
namespace My_lib {
using namespace His_string;
using namespace Her_vector;
void my_fct(String&);
}
Ennek alapjn a My_lib nvteret hasznlva mr megrhatjuk a programot:
void f()
{
My_lib::String s = "Byron"; // megtallja a My_lib::His_string::String nevet
// ...
}
using namespace My_lib;
void g(Vector<String>& vs)
{
// ...
my_fct(vs[5]);
// ...
}
Alapok 240
Ha az emltett nvtrben egy explicit mdon minstett nv (mint a My_lib::String) nem be-
vezetett, a fordt a nevet a using direktvkban szerepl nvterekben (pldul His_string)
fogja keresni.
Egy elem valdi nvtert csak akkor kell tudnunk, ha valamit megakarunk hatrozni:
void My_lib::fill(char c) // hiba: a My_lib-ben nincs megadva fill()
{
// ...
}
void His_string::fill(char c) // rendben: fill() szerepel a His_string-ben
{
// ...
}
void My_lib::my_fct(String& v) // rendben; a String jelentse My_lib::String, ami
// His_string::String
{
// ...
}
Idelis esetben egy nvtr
1. logikailag sszetartoz szolgltatsok halmazt fejezi ki,
2. nem ad hozzfrst a nem kapcsold szolgltatsokhoz,
3. s nem r nagy jellsbeli terhet a felhasznlra.
Az itt s a kvetkez rszekben bemutatott sszefzsi, beptsi mdszerek az #include-
dal (9.2.1) egytt komoly tmogatst nyjtanak ehhez.
8.2.8.1. Kivlaszts
Alkalmanknt elfordul, hogy egy nvtrbl csak nhny nvhez akarunk hozzfrni. Ezt
meg tudnnk tenni gy is, hogy olyan nvtr-deklarcit runk, amely csak azokat a neve-
ket tartalmazza, melyeket szeretnnk. Pldul megadhatnnk a His_string azon vltozatt,
amely csak magt a String-et s az sszefz opertort nyjtja:
namespace His_string { // csak egy rsze a His_string-nek
class String { /* ... */ };
String operator+(const String&, const String&);
String operator+(const String&, const char*);
}
8. Nvterek s kivtelek 241
Ez azonban knnyen zavaross vlhat, hacsak nem mi vagyunk a His_string tervezi vagy
karbantarti. A His_string valdi meghatrozsnak mdostsa ebben a deklarciban
nem fog tkrzdni. Az adott nvtrben szerepl szolgltatsok kivlasztst jobban ki le-
het fejezni using deklarcikkal:
namespace My_string {
using His_string::String;
using His_string::operator+; // brmelyik His_string-beli + hasznlhat
}
A using deklarci az adott nv minden deklarcijt a hatkrbe helyezi, gy pldul
egyetlen using deklarcival egy tlterhelt fggvny sszes vltozatt bevezethetjk.
gy ha a His_string-et gy mdostjk, hogy egy tagfggvnyt vagy az sszefz mvelet
egy tlterhelt vltozatt adjk a String-hez, akkor ez a vltoztats automatikusan hozzfr-
het lesz a My_string-et hasznl elemek szmra. Fordtva is igaz: ha a His_string-bl el-
tvoltunk egy szolgltatst vagy megvltozatjuk a His_string fellett, a fordtprogram fel
fogja ismerni a My_string minden olyan hasznlatt, amelyre ez hatssal van (lsd mg
15.2.2).
8.2.8.2. sszefzs s kivlaszts
A (using direktvkkal trtn) sszefzs s a (using deklarcikkal trtn) kivlaszts
sszekapcsolsa azt a rugalmassgot eredmnyezi, amelyre a legtbb valdi programban
szksgnk van. Ezek rvn gy adhatunk hozzfrst klnfle eszkzkhz, hogy fel-
oldjuk az egybeptskbl add nvtkzseket s tbbrtelmsgeket:
namespace His_lib {
class String { /* ... */ };
template<class T> class Vector { /* ... */ };
// ...
}
namespace Her_lib {
template<class T> class Vector { /* ... */ };
class String { /* ... */ };
// ...
}
namespace My_lib {
using namespace His_lib; // minden a His_lib-bl
using namespace Her_lib; // minden a Her_lib-bl
Alapok 242
using His_lib::String; // az esetleges tkzsek feloldsa a His_lib javra
using Her_lib::Vector; // az esetleges tkzsek feloldsa a Her_lib javra
template<class T> class List { /* ... */ }; // tovbbiak
// ...
}
Amikor megvizsglunk egy nvteret, a nvtrben lv, kifejezetten megadott nevek (bele-
rtve a using deklarcikkal megadottakat is) elnyben rszeslnek azokkal a nevekkel
szemben, melyeket ms hatkrkbl tettnk hozzfrhetv a using direktvval (lsd
mg C.10.1-et). Kvetkezskppen a My_libet hasznl elemek szmra a String s
Vector nevek tkzst a fordtprogram a His_lib::String s Her_lib::Vector javra fogja fel-
oldani. Tovbb a My_lib::List lesz hasznlatos alaprtelmezs szerint, fggetlenl attl,
hogy szerepel-e List a His_lib vagy Her_lib nvtrben.
Rendszerint jobban szeretem vltozatlanul hagyni a neveket, amikor j nvtrbe teszem
azokat. Ily mdon nem kell ugyanannak az elemnek kt klnbz nevre emlkeznem.
Nha azonban j nvre van szksg, vagy egyszeren j, ha van egy j nevnk:
namespace Lib2 {
using namespace His_lib; // minden a His_lib-bl
using namespace Her_lib; // minden a Her_lib-bl
using His_lib::String; // az esetleges tkzsek feloldsa a His_lib javra
using Her_lib::Vector; // az esetleges tkzsek feloldsa a Her_lib javra
typedef Her_lib::String Her_string; // tnevezs
template<class T> class His_vec // "tnevezs"
: public His_lib::Vector<T> { /* ... */ };
template<class T> class List { /* ... */ }; // tovbbiak
// ...
}
Az tnevezsre nincs kln nyelvi eljrs. Ehelyett az j elemek meghatrozsra val lta-
lnos mdszerek hasznlatosak.
8.2.9. Nvterek s rgi kdok
Sok milli sor C s C++ kd tmaszkodik globlis nevekre s ltez knyvtrakra. Hogyan
hasznlhatjuk a nvtereket arra, hogy cskkentsk az ilyen kdokban lv problmkat?
A mr ltez kdok jrarsa nem mindig jrhat t. Szerencsre a C knyvtrakat gy is
8. Nvterek s kivtelek 243
hasznlhatjuk, mintha azokat egy nvtrben deklarltk volna. A C++-ban rt knyvtrak
esetben ez nem gy van (9.2.4), msrszrl viszont a nvtereket gy terveztk, hogy a le-
het legcseklyebb krokozssal be lehessen azokat pteni a rgebbi C++ programokba is.
8.2.9.1. Nvterek s a C
Vegyk a hagyomnyosan els C programot:
#include <stdio.h>
int main()
{
printf("Hell, vilg!\n");
}
Ezt a programot nem lenne j tlet szttrdelni. Az sem sszer, ha a szabvnyos knyvt-
rakat egyedi megoldsoknak tekintjk. Emiatt a nvterekre vonatkoz nyelvi szablyokat
gy hatroztk meg, hogy viszonylag knnyedn lehessen egy nvterek nlkl megrt
program szerkezett nvterek hasznlatval vilgosabban kifejezni. Tulajdonkppen erre
plda a szmolgp program (6.1). Ennek megvalstshoz a kulcs a using direktva.
A stdio.h C fejllomnyban lv szabvnyos bemeneti/kimeneti szolgltatsok deklarcii
pldul egy nvtrbe kerltek, a kvetkezkppen:
// stdio.h:
namespace std {
// ...
int printf(const char* ... );
// ...
}
using namespace std;
Ez megrzi a visszirny kompatibilitst. Azoknak viszont, akik nem akarjk, hogy a nevek
automatikusan hozzfrhetk legyenek, ksztettnk egy j fejllomnyt is, a cstdio-t:
// cstdio:
namespace std {
// ...
int printf(const char* ... );
// ...
}
Alapok 244
A C++ standard knyvtrnak azon felhasznli, akik aggdnak a deklarcik msolsa mi-
att, a stdio.h-t termszetesen gy fogjk meghatrozni, hogy beleveszik a cstdio-t:
// stdio.h:
#include<cstdio>
using namespace std;
A using direktvkat elsdlegesen a nyelv rgebbi vltozatairl val tllst segt eszkz-
knek tekintem. A legtbb olyan kdot, amely ms nvtrben lv nevekre hivatkozik,
sokkal vilgosabban ki lehet fejezni minstsekkel s using deklarcikkal.
A nvterek s az sszeszerkeszts kztti kapcsolatot a 9.2.4 rszben trgyaljuk.
8.2.9.2. Nvterek s tlterhels
A tlterhels (7.4) nvtereken keresztl mkdik. Ez alapvet ahhoz, hogy a mr megl-
v knyvtrakat a forrskd lehet legkisebb mdostsval fejleszthessk nvtereket hasz-
nlv. Pldul:
// old A.h:
void f(int);
// ...
// old B.h:
void f(char);
// ...
// old user.c:
#include "A.h"
#include "B.h"
void g()
{
f('a'); // f()-et hvja B.h-bl
}
Ezt a programot anlkl alakthatjuk nvtereket hasznl vltozatra, hogy a tnyleges
programkdot megvltoztatnnk:
8. Nvterek s kivtelek 245
// new A.h:
namespace A {
void f(int);
// ...
}
// new B.h:
namespace B {
void f(char);
// ...
}
// new user.c:
#include "A.h"
#include "B.h"
using namespace A;
using namespace B;
void g()
{
f('a'); // f()-et hvja B.h-bl
}
Ha teljesen vltozatlanul akartuk volna hagyni a user.c-t, a using direktvkat a fejllom-
nyokba tettk volna.
8.2.9.3. A nvterek nyitottak
A nvterek nyitottak; azaz szmos nvtr deklarcijbl adhatunk hozzjuk neveket:
namespace A {
int f(); // most f() az A tagja
}
namespace A {
int g(); // most A kt tagja f() s g()
}
Ezltal gy hozhatunk ltre egyetlen nvtren bell lv nagy programrszeket, ahogy egy
rgebbi knyvtr vagy alkalmazs lt az egyetlen globlis nvtren bell. Hogy ezt megte-
hessk, a nvtr-meghatrozsokat szt kell osztanunk szmos fejllomny s forrsfjl k-
Alapok 246
ztt. Ahogy azt a szmolgp pldjban (8.2.4.) mutattuk, a nvterek nyitottsga lehet-
v teszi szmunkra, hogy a klnbz programelemeknek klnbz felleteket nyjt-
sunk azltal, hogy egy adott nvtr klnbz rszeit mutatjuk meg nekik. Ez a nyitottsg
szintn a nyelv rgebbi vltozatairl val tllst segti. Pldul a
// sajt fejllomny:
void f(); // sajt fggvny
// ...
#include<stdio.h>
int g(); // sajt fggvny
// ...
jrarhat anlkl, hogy a deklarcik sorrendjt megvltoztatnnk:
// sajt fejllomny:
namespace Mine {
void f(); // sajt fggvny
// ...
}
#include<stdio.h>
namespace Mine {
int g(); // sajt fggvny
// ...
}
Amikor j kdot rok, jobban szeretek sok kisebb nvteret hasznlni (lsd 8.2.8), mint iga-
zn nagy programrszeket egyetlen nvtrbe rakni. Ez azonban gyakran kivitelezhetetlen,
ha nagyobb programrszeket alaktunk t nvtereket hasznl vltozatra.
Amikor egy nvtr elzetesen bevezetett tagjt kifejtjk, biztonsgosabb a Mine:: utasts-
formt hasznlni ahelyett, hogy jra megnyitnnk a Mine-t:
void Mine::ff() // hiba: nincs ff() megadva Mine-ban
{
// ...
}
A fordtprogram ezt a hibt szreveszi. Mivel azonban egy nvtren bell j fggvnyeket
is meghatrozhatunk, a fordtprogram a fentivel azonos jelleg hibt az jra megnyitott
nvterekben mr nem rzkeli:
8. Nvterek s kivtelek 247
namespace Mine { // Mine jra megnyitsa fggvnyek meghatrozshoz
void ff() // hopp! nincs ff() megadva Mine-ban; ezzel a defincival adjuk hozz
{
// ...
}
// ...
}
A fordtprogram nem tudhatja, hogy nem egy j ff() fggvnyt akartunk meghatrozni.
A meghatrozsokban szerepl nevek minstsre hasznlhatunk nvtr-lneveket
(8.2.7), de az adott nvtr jbli megnyitsra nem.
8.3. Kivtelek
Ha egy program klnll modulokbl ll klnsen ha ezek kln fejlesztett knyvt-
rakbl szrmaznak , a hibakezelst kt klnll rszre kell sztvlasztanunk:
1. az olyan hibaesemnyek jelzsre, melyeket nem lehet helyben megszntetni,
2. illetve a mshol szlelt hibk kezelsre.
A knyvtr ltrehozja felismerheti a futsi idej hibkat, de ltalban nem tud mit kezde-
ni velk. A knyvtrt felhasznl programelem tudhatn, hogyan birkzzon meg a hibk-
kal, de nem kpes szlelni azokat msklnben a felhasznl kdjban szerepelnnek
a hibkat kezel eljrsok s nem a knyvtr talln meg azokat.
A szmolgp pldjban ezt a problmt azzal kerltk ki, hogy a program egszt egy-
szerre terveztk meg, ezltal beilleszthettk a hibakezelst a teljes szerkezetbe. Amikor
azonban a szmolgp logikai rszeit klnbz nvterekre bontjuk szt, ltjuk, hogy min-
den nvtr fgg az Error nvtrtl (8.2.2), az Error-ban lv hibakezel pedig arra tmasz-
kodik, hogy minden modul megfelelen viselkedik, miutn hiba trtnt. Tegyk fel, hogy
nincs lehetsgnk a szmolgp egszt megtervezni s nem akarjuk, hogy az Error s
a tbbi modul kztt szoros legyen a kapcsolat. Ehelyett tegyk fel, hogy a elemzt s
a tbbi rszt gy rtk meg, hogy nem tudtk, hogyan szeretn a vezrl kezelni a hibkat.
Alapok 248
Br az error() nagyon egyszer volt, magban foglalt egy hibakezelsi mdszert:
namespace Error {
int no_of_errors;
double error(const char* s)
{
std::cerr << "hiba: " << s << '\n';
no_of_errors++;
return 1;
}
}
Az error() fggvny egy hibazenetet r ki, olyan alaprtelmezett rtket ad, mely lehetv
teszi a hv szmra, hogy folytassa a szmolst, s egy egyszer hiballapotot kvet nyo-
mon. Fontos, hogy a program minden rsze tudjon az error() ltezsrl s arrl, hogyan
lehet meghvni, illetve mit vrhat tle. Ez tl sok felttel lenne egy olyan program esetben,
amit kln fejlesztett knyvtrakbl hoztunk ltre.
A hibajelzs s a hibakezels sztvlasztsra sznt C++ eszkz a kivtel. Ebben a rszben
rviden lerjuk a kivteleket, abban a krnyezetben, ahogy a szmolgp pldjban len-
nnek hasznlatosak. A 14. fejezet tfogbban trgyalja a kivteleket s azok hasznlatt.
8.3.1. Dobs s elkaps
A kivteleket (exception) arra talltk ki, hogy segtsenek megoldani a hibk jelzst:
struct Range_error {
int i;
Range_error(int ii) { i = ii; } // konstruktor (2.5.2, 10.2.3)
};
char to_char(int i)
{
if (i<numeric_limits<char>::min() || numeric_limits<char>::max()<i) // lsd 22.2
throw Range_error(i);
return i;
}
A to_char() fggvny vagy az i szmrtkt adja vissza karakterknt, vagy Range_error ki-
vtelt vlt ki. Az alapgondolat az, hogy ha egy fggvny olyan problmt tall, amellyel
nem kpes megbirkzni, kivtelt vlt ki (kivtelt dob, throw), azt remlve, hogy (kzve-
tett vagy kzvetlen) meghvja kpes kezelni a problmt. Ha egy fggvny kpes erre, je-
8. Nvterek s kivtelek 249
lezheti, hogy el akarja kapni (catch) azokat a kivteleket, melyek tpusa megegyezik
a problma jelzsre hasznlt tpussal. Ahhoz pldul, hogy meghvjuk a to_char()-t s el-
kapjuk azt a kivtelt, amit esetleg kivlthat, a kvetkezt rhatjuk:
void g(int i)
{
try {
char c = to_char(i);
// ...
}
catch (Range_error) {
cerr << "hopp\n";
}
}
A
catch ( /* ... */ ) {
// ...
}
szerkezetet kivtelkezelnek (exception handler) nevezzk. Csak kzvetlenl olyan blokk
utn hasznlhat, amit a try kulcssz elz meg, vagy kzvetlenl egy msik kivtelkezel
utn. A catch szintn kulcssz. A zrjelek olyan deklarcit tartalmaznak, amely a fgg-
vnyparamterek deklarcijhoz hasonl mdon hasznlatos. A deklarci hatrozza meg
azon objektum tpust, melyet a kezel elkaphat. Nem ktelez, de megnevezheti az elka-
pott objektumot is. Ha pldul meg akarjuk tudni a kivltott Range_error rtkt, akkor
pontosan gy adhatunk nevet a catch paramternek, ahogy a fggvnyparamtereket ne-
vezzk meg:
void h(int i)
{
try {
char c = to_char(i);
// ...
}
catch (Range_error x) {
cerr << "hopp: to_char(" << x.i << ")\n";
}
}
Ha brmilyen try blokkban szerepl vagy onnan meghvott kd kivtelt vlt ki, a try blokk
kezelit kell megvizsglni. Ha a kivtel tpusa megegyezik a kezelnek megadott tpussal,
Alapok 250
a kezel vgrehajtja a megfelel mveletet. Ha nem, a kivtelkezelket figyelmen kvl
hagyjuk s a try blokk gy viselkedik, mint egy kznsges blokk. Ha a kivtelt nem kap-
ja el egyetlen try blokk sem, a program befejezdik (14.7).
A C++ kivtelkezelse alapveten nem ms, mint a vezrls tadsa a hv fggvny meg-
felel rsznek. Ahol szksges, a hibrl informcit adhatunk a hvnak. A C programo-
zk gy gondolhatnak a kivtelkezelsre, mint egy olyan, jl viselked eljrsra, amely
a setjmp/longjmp (16.1.2) hasznlatt vltja fel. Az osztlyok s a kivtelkezels kztti kl-
csnhatst a 14. fejezetben trgyaljuk.
8.3.2. A kivtelek megklnbztetse
Egy program futsakor ltalban szmos hiba lphet fel, melyeket klnbz nev kivte-
leknek feleltethetnk meg. n a kivtelkezels cljra kln tpusokat szoktam megadni. Ez
a lehet legkisebbre cskkenti a cljukkal kapcsolatos zavart. Beptett tpusokat, mint ami-
lyen az int, viszont sohasem hasznlok kivtelknt. Egy nagy programban nem lenne hat-
kony md arra, hogy megtalljam a ms clra hasznlt int kivteleket, ezrt sosem lehetnk
biztos abban, hogy az int egy effle eltr hasznlata nem okoz-e zavart az n kdomban.
Szmolgpnknek (6.1) ktfajta futsi idej hibt kell kezelnie: a formai kvetelmnyek
megsrtst s a nullval val oszts ksrlett. A kezelnek nem kell rtket tadni abbl
a kdbl, amelyik felismerte a nullval val oszts ksrlett, gy a nullval val osztst egy
egyszer res tpussal brzolhatjuk:
struct Zero_divide { };
Msrszt a kezel a nyelvi hibkrl bizonyra szeretne jelzst kapni. Itt egy karakterlncot
adunk t:
struct Syntax_error {
const char* p;
Syntax_error(const char* q) { p = q; }
};
A knyelmesebb jells vgett a szerkezethez hozzadtam egy konstruktort (2.5.2,
10.2.3).
Az elemzt hasznl programrszben megklnbztethetjk a kt kivtelt, ha mindkett-
jk szmra hozzadunk egy-egy kezelt a try blokkhoz, gy szksg esetn a megfelel
kezelbe lphetnk. Ha az egyik kezel aljn kiesnk, a vgrehajts a kezelk listjnak
vgtl folytatdik:
8. Nvterek s kivtelek 251
try {
// ...
expr(false);
// kizrlag akkor jutunk ide, ha expr() nem okozott kivtelt
// ...
}
catch (Syntax_error) {
// szintaktikus hiba kezelse
}
catch (Zero_divide) {
// nullval oszts kezelse
}
// akkor jutunk ide, ha expr() nem okozott kivtelt vagy ha egy Syntax_error
// vagy Zero_divide kivtelt elkaptunk (s kezeljk nem trt vissza,
// nem vltott ki kivtelt, s ms mdon sem vltoztatta meg a vezrlst).
A kezelk listja nmileg egy switch utastshoz hasonlt, de itt nincs szksg break utas-
tsokra. E listk formai kvetelmnyei rszben ezrt klnbznek a case-tl, rszben pe-
dig azrt, hogy jelljk, minden kezel kln hatkrt (4.9.4) alkot.
A fggvnyeknek nem kell az sszes lehetsges kivtelt elkapniuk. Az elz try blokk pl-
dul nem prblta elkapni az elemz bemeneti mveletei ltal kivltott kivteleket, azok
csupn keresztlmennek a fggvnyen, megfelel kezelvel rendelkez hvt keresve.
A nyelv szempontjbl a kivteleket rgtn kezeltnek tekintjk, amint belpnek a keze-
ljkbe, ezrt a try blokkot meghv programrsznek kell foglalkoznia azokkal a kivtelek-
kel, melyek a kezel vgrehajtsa kzben lpnek fel. A kvetkez pldul nem okoz vg-
telen ciklust:
class Input_overflow { /* ... */ };
void f()
{
try {
// ...
}
catch (Input_overflow) {
// ...
throw Input_overflow();
}
}
Alapok 252
A kivtelkezelk egymsba is gyazhatk:
class XXII { /* ... */ };
void f()
{
// ...
try {
// ...
}
catch (XXII) {
try {
// valami bonyolult
}
catch (XXII) {
// a bonyolult kezel nem jrt sikerrel
}
}
// ...
}
Ilyen gyakran rossz stlusra utal egymsba gyazott kivtelkezelket azonban ritkn
runk.
8.3.3. Kivtelek a szmolgpben
Az alapvet kivtelkezel eljrsokbl kiindulva jrarhatjuk a 6.1 rszben szerepl szmo-
lgpet, hogy klnvlasszuk a futsi idben tallt hibk kezelst a szmolgp f prog-
ramrsztl. Ez a program olyan elrendezst eredmnyezi, amely jobban hasonlt a kln-
ll, lazn kapcsold rszekbl ltrehozott programokra.
Elszr kikszblhetjk az error() fggvnyt. Helyette az elemz fggvnyek csak a hi-
bk jelzsre hasznlatos tpusokrl fognak tudni:
namespace Error {
struct Zero_divide { };
struct Syntax_error {
const char* p;
Syntax_error(const char* q) { p = q; }
};
}
8. Nvterek s kivtelek 253
Az elemz hrom szintaktikus hibt ismer fel:
Lexer::Token_value Lexer::get_token()
{
using namespace std; // az input, isalpha(), stb. hasznlata miatt (6.1.7)
// ...
default: // NAME, NAME =, vagy hiba
if (isalpha(ch)) {
input->putback(ch);
*input >> string_value;
return curr_tok=NAME;
string_value = ch;
while (input->get(ch) && isalnum(ch))
string_value.push_back(ch);
input->putback(ch);
return curr_tok=NAME;
}
throw Error::Syntax_error("rossz szimblum");
}
}
double Parser::prim(bool get) // elemi szimblumok kezelse
{
// ...
case Lexer::LP:
{ double e = expr(true);
if (curr_tok != Lexer::RP) throw Error::Syntax_error("')' szksges");
get_token(); // ')' lenyelse
return e;
}
case Lexer::END:
return 1;
default:
throw Error::Syntax_error("elemi szimblum szksges");
}
}
Ha az elemz ilyen hibt tall, a throw-t hasznlja arra, hogy tadja a vezrlst egy kezel-
nek, amelyet valamilyen (kzvetett vagy kzvetlen) hv fggvny hatroz meg. A throw
opertor egy rtket is tad a kezelnek. Pldul a
throw Syntax_error("elemi szimblum szksges");
a kezelnek egy Syntax_error objektumot ad t, amely a primary expected karakterlncra
hivatkoz mutatt tartalmazza.
Alapok 254
A nullval val oszts hibjnak jelzshez nem szksges semmilyen adatot tadni:
double Parser::term(bool get) // szorzs s oszts
{
// ...
case Lexer::DIV:
if (double d = prim(true)) {
left /= d;
break;
}
throw Error::Zero_divide();
// ...
}
Most mr elkszthetjk a vezrlt, hogy az kezelje a Zero_divide s Syntax_error kivtele-
ket:
int main(int argc, char* argv[ ])
{
// ...
while (*input) {
try {
Lexer::get_token();
if (Lexer::curr_tok == Lexer::END) break;
if (Lexer::curr_tok == Lexer::PRINT) continue;
cout << Parser::expr(false) << '\n';
}
catch(Error::Zero_divide) {
cerr << "nullval oszts ksrlete\n";
if (Lexer::curr_tok != Lexer::PRINT) skip();
}
catch(Error::Syntax_error e) {
cerr << "formai hiba:" << e.p << "\n";
if (Lexer::curr_tok != Lexer::PRINT) skip();
}
}
if (input != &cin) delete input;
return no_of_errors;
}
Ha nem trtnt hiba a PRINT (azaz sorvge vagy pontosvessz) szimblummal lezrt kife-
jezs vgn, a main() meghvja a skip() helyrellt fggvnyt. A skip() az elemzt egy
meghatrozott llapotba prblja lltani, azltal, hogy eldobja a karaktereket addig, amg
sorvgt vagy pontosvesszt nem tall. A skip() fggvny, a no_of_errors s az input k-
zenfekv vlaszts a Driver nvtr szmra:
8. Nvterek s kivtelek 255
namespace Driver {
int no_of_errors;
std::istream* input;
void skip();
}
void Driver::skip()
{
no_of_errors++;
while (*input) { // karakterek elvetse sortrsig vagy pontosvesszig
char ch;
input->get(ch);
switch (ch) {
case '\n':
case ';':
return;
}
}
}
A skip() kdjt szndkosan rtuk az elemz kdjnl alacsonyabb elvonatkoztatsi szinten.
gy az elemzben lv kivtelek nem kapjk el, mikzben ppen az elemz kivteleinek
kezelst vgzik. Megtartottam azt az tletet, hogy megszmoljuk a hibkat, s ez a szm
lesz a program visszatrsi rtke. Gyakran hasznos tudni a hibkrl, mg akkor is, ha
a program kpes volt helyrellni a hiba utn.
A main()-t nem tesszk a Driver nvtrbe. A globlis main() a program indt fggvnye
(3.2), gy a main() egy nvtren bell rtelmetlen. Egy valsgos mret programban
a main() kdjnak legnagyobb rszt a Driver egy kln fggvnybe tennm t.
8.3.3.1. Ms hibakezel mdszerek
Az eredeti hibakezel kd rvidebb s elegnsabb volt, mint a kivteleket hasznl vlto-
zat. Ezt azonban gy rte el, hogy a program rszeit szorosan sszekapcsolta. Ez a megk-
zelts nem felel meg olyan programok esetben, melyeket kln fejlesztett knyvtrakbl
hoztak ltre. Felvetdhet, hogy a klnll skip() hibakezel fggvnyt a main()-ben, egy
llapotvltoz bevezetsvel kszbljk ki:
int main(int argc, char* argv[ ]) // rossz stlus
{
// ...
bool in_error = false;
Alapok 256
while (*Driver::input) {
try {
Lexer::get_token();
if (Lexer::curr_tok == Lexer::END) break;
if (Lexer::curr_tok == Lexer::PRINT) {
in_error = false;
continue;
}
if (in_error == false) cout << Parser::expr(false) << '\n';
}
catch(Error::Zero_divide) {
cerr << "nullval oszts ksrlete\n";
++ Driver::no_of_errors;
in_error = true;
}
catch(Error::Syntax_error e) {
cerr << "formai hiba:" << e.p << "\n";
++ Driver::no_of_errors;
in_error = true;
}
}
if (Driver::input != &std::cin) delete Driver::input;
return Driver::no_of_errors;
}
Ezt szmos okbl rossz tletnek tartom:
1. Az llapotvltozk gyakran zavart okoznak s hibk forrsai lehetnek, kl-
nsen akkor, ha lehetsget adunk r, hogy elszaporodjanak s hatsuk nagy
programrszekre terjedjen ki. Nevezetesen az in_error-t hasznl main()-t
kevsb olvashatnak tartom, mint a skip() fggvnyt hasznl vltozatot.
2. ltalban jobb kln tartani a hibakezelst s a kznsges kdot.
3. Veszlyes, ha a hibakezels elvonatkoztatsi szintje megegyezik annak a kd-
nak az absztrakcis szintjvel, ami a hibt okozta; a hibakezel kd ugyanis
megismtelheti azt a hibt, amely a hibakezelst elszr kivltotta. (A gyakorla-
tok kztt szerepel, hogy mi trtnik, ha a main() in_error-t hasznl. 8.5[7]).
4. Tbb munkval jr az egsz kdot mdostani a hibakezels hozzadsval,
mint kln hibakezel fggvnyeket adni a kdhoz.
A kivtelkezels nem helyi problmk megoldsra val. Ha egy hiba helyben kezelhet,
akkor majdnem mindig ezt is kell tennnk. Pldul nincs ok arra, hogy kivtelt hasznljunk
a tl sok paramter hiba fellpsekor:
8. Nvterek s kivtelek 257
int main(int argc, char* argv[])
{
using namespace std;
using namespace Driver;
switch (argc) {
case 1: // olvass szabvnyos bemenetrl
input = &cin;
break;
case 2: // karakterlnc paramter beolvassa
input = new istringstream(argv[1]);
break;
default:
cerr << "tl sok paramter\n";
return 1;
}
// mint korbban
}
A kivtelek tovbbi trgyalsa a 14. fejezetben trtnik.
8.4. Tancsok
[1] Hasznljunk nvtereket a logikai felpts kifejezsre. 8.2.
[2] A main() kivtelvel minden nem loklis nevet helyezznk valamilyen nvtr-
be. 8.2.
[3] A nvtereket gy tervezzk meg, hogy utna knyelmesen hasznlhassuk, anl-
kl, hogy vletlenl hozzfrhetnnk ms, fggetlen nvterekhez. 8.2.4.
[4] Lehetleg ne adjunk a nvtereknek rvid neveket. 8.2.7.
[5] Ha szksges, hasznljunk nvtr-lneveket a hossz nvtrnevek rvidtsre.
8.2.7.
[6] Lehetleg ne rjunk nehz jellsbeli terheket nvtereink felhasznlira. 8.2.2.,
8.2.3.
[7] Hasznljuk a Nvtr::tag jellst, amikor a nvtr tagjait meghatrozzuk. 8.2.8.
[8] A using namespacet csak a C-rl vagy rgebbi C++-vltozatokrl val tlls-
kor, illetve helyi hatkrben hasznljuk. 8.2.9.
[9] Hasznljunk kivteleket arra, hogy a szoksos feldolgozst vgz kdrszt elv-
lasszuk attl a rsztl, amelyben a hibkkal foglalkozunk. 8.3.2.
Alapok 258
[10] Inkbb felhasznli tpusokat hasznljunk kivtelekknt, mint beptett tpuso-
kat. 8.3.2.
[11] Ne hasznljunk kivteleket, amikor a helyi vezrlsi szerkezetek is megfelelek.
8.3.3.1.
8.5. Gyakorlatok
1. (*2,5) rjunk string elemeket tartalmaz ktirny lncolt lista modult a 2.4-ben
tallhat Stack modul stlusban. Prbljuk ki gy, hogy ltrehozunk egy prog-
ramnyelvekbl ll listt. Adjunk erre listra egy sort() fggvnyt s egy olyat,
ami megfordtja a listban szerepl karakterlncok sorrendjt.
2. (*2) Vegynk egy nem tl nagy programot, amely legalbb egy olyan knyv-
trat hasznl, ami nem hasznl nvtereket. Mdostsuk gy, hogy a knyvtr
nvtereket hasznljon. Tipp: 8.2.9.
3. (*2) Ksztsnk modult a szmolgp programbl nvterek felhasznlsval
a 2.4 pont stlusban. Ne hasznljunk globlis using direktvkat. Jegyezzk fel,
milyen hibkat vtettnk. Tegynk javaslatokat arra, miknt kerlhetnnk el az
ilyen hibkat a jvben.
4. (*1) rjunk programot, amelyben egy fggvny kivtelt dob, egy msik pedig
elkapja.
5. (*2) rjunk programot, amely olyan egymst hv fggvnyekbl ll, ahol a h-
vs mlysge 10. Minden fggvnynek adjunk egy paramtert, amely eldnti,
melyik szinten lpett fel a kivtel. A main()-nel kapjuk el a kivteleket s rjuk
ki, melyiket kaptuk el. Ne felejtsk el azt az esetet, amikor a kivtelt a kivlt
fggvnyben kapjuk el.
6. (*2) Mdostsuk a 8.5[5] programjt gy, hogy megmrjk, van-e klnbsg
a kivtelek elkapsnak nehzsgben attl fggen, hogy a stack osztlyon
bell hol jtt ltre kivtel. Adjunk minden fggvnyhez egy karakterlnc objek-
tumot s mrjk meg jra a klnbsget.
7. (*1) Talljuk meg a hibt a 8.3.3.1-ben szerepl main() els vltozatban.
8. (*2) rjunk fggvnyt, amely vagy visszaad egy rtket, vagy egy paramter
alapjn eldobja azt. Mrjk meg a kt mdszer futsi idejnek klnbsgt.
9. (*2) Mdostsuk a 8.5[3]-ban lv szmolgpet kivtelek hasznlatval.
Jegyezzk fel, milyen hibkat vtettnk. Tegynk javaslatokat arra, miknt
kerlhetnnk el az ilyen hibkat a jvben.
10. (*2,5) rjuk meg a plus(), minus(), multiply() s divide() fggvnyeket, amelyek
ellenrzik a tlcsordulst s az alulcsordulst, s kivteleket vltanak ki, ha
ilyen hibk trtnnek.
11. (*2) Mdostsuk a szmolgpet, hogy a 8.5[10] fggvnyeit hasznlja.
8. Nvterek s kivtelek 259
Forrsfjlok s programok
A formnak a rendeltetshez kell igazodnia.
(Le Corbusier)
Kln fordts sszeszerkeszts Fejllomnyok A standard knyvtr fejllomnyai
Az egyszeri definils szablya sszeszerkeszts nem C++ kddal Az sszeszerkeszts
s a fggvnyekre hivatkoz mutatk Fejllomnyok hasznlata a modularits kifejezs-
re Egyetlen fejllomnyos elrendezs Tbb fejllomnyos elrendezs llomny-
rszemek Programok Tancsok Gyakorlatok
9.1. Kln fordts
A fjl (az egyes fjlrendszerekben) a trols s fordts hagyomnyos egysge. Vannak
olyan rendszerek, amelyek a C++ programokat nem fjlok halmazaknt troljk s fordt-
jk, s a programok sem fjlok formjban jelennek meg a programoz szmra. Ez a lers
azonban csak azokra a rendszerekre sszpontost, amelyek a fjlok hagyomnyos haszn-
latra tmaszkodnak.
9
Egy teljes programot rendszerint lehetetlen egy fjlban trolni, mr csak azrt sem, mert
a szabvnyos knyvtrak s az opercis rendszer forrskdja ltalban nem szerepel
a program forrsban. A valsgos mret alkalmazsokban az sem knyelmes s clsze-
r, ha a felhasznl sajt kdjt egyetlen fjl trolja. A program elrendezsi mdja segthet
kihangslyozni a program logikai felptst, segtheti az olvast a program megrtsben
s segthet abban is, hogy a fordtprogram kiknyszertse ezt a logikai szerkezetet. Amikor
a fordtsi egysg a fjl, akkor a teljes fjlt jra kell fordtani, ha (brmilyen kis) vltoztatst
hajtottak vgre rajta, vagy egy msik fjlon, amelytl az elz fgg. Az jrafordtsra hasz-
nlt id mg egy kzepes mret program esetben is jelentsen cskkenthet, ha a prog-
ramot megfelel mret fjlokra bontjuk.
A felhasznl a fordtprogramnak egy forrsfjlt (source file) ad t. Ezutn a fjl elford-
tsa trtnik: azaz vgrehajtdik a makrfeldolgozs (7.8), az #include utastsok pedig
beptik a fejllomnyokat (2.4.1, 9.2.1). Az elfeldolgozs eredmnyt fordtsi egysg-
nek (translation unit) hvjk. A fordtprogram valjban csak ezekkel dolgozik s a C++
szablyai is ezek formjt rjk le. Ebben a knyvben csak ott teszek klnbsget a forrs-
fjl s a fordtsi egysg kztt, ahol meg kell klnbztetni azt, amit a programoz lt, s
amit a fordtprogram figyelembe vesz. Ahhoz, hogy a programoz lehetv tegye az elk-
lntett fordtst, olyan deklarcikat kell megadnia, amelyek biztostjk mindazt az infor-
mcit, ami ahhoz szksges, hogy a fordtsi egysget a program tbbi rsztl elklnt-
ve lehessen elemezni. A tbb fordtsi egysgbl ll programok deklarciinak ugyangy
kvetkezetesnek kell lennik, mint az egyetlen forrsfjlbl ll programoknak. A rend-
szernkben vannak olyan eszkzk, amelyek segtenek ezt biztostani; nevezetesen a szer-
kesztprogram (linker), amely szmos kvetkezetlensget kpes szrevenni. Ez az a prog-
ram, ami sszekapcsolja a kln fordtott rszeket. A szerkesztt nha (zavar mdon) be-
tltnek (loader) is szoktk nevezni. A teljes sszeszerkesztst el lehet vgezni a program
futsa eltt. Emellett lehetsg van arra is, hogy ksbb j kdot adjunk a programhoz (di-
namikus szerkeszts).
A program fizikai szerkezetn ltalban a forrsfjlokba szervezett programot rtik. A prog-
ram forrsfjlokra val fizikai sztvlasztst a program logikai felptse kell, hogy irnyt-
sa. A programok forrsfjlokba rendezst is ugyanaz a fggsgi kapcsolat vezrli, mint
azok nvterekbl val sszelltst. A program logikai s fizikai szerkezetnek azonban
nem kell megegyeznie. Hasznos lehet pldul tbb forrsfjlt hasznlni egyetlen nvtr
fggvnyeinek trolsra, nvtr-meghatrozsok egy gyjtemnyt egyetlen fjlban trol-
ni, vagy egy nvtr definciit tbb fjl kztt sztosztani (8.2.4).
Elszr ttekintnk nhny, az sszeszerkesztshez kapcsold rszletet s szakkifejezst,
majd ktfle mdjt ismertetjk annak, hogyan lehet fjlokra sztvlasztani a szmolgpet
(6.1, 8.2).
Alapok 262
9.2. sszeszerkeszts
A fggvnyek, osztlyok, sablonok, vltozk, nvterek, felsorolsok s felsorolk neveit
kvetkezetesen kell hasznlni az sszes fordtsi egysgben, kivve, ha kifejezetten loklis-
knt nem hatroztuk meg azokat.
A programoz feladata biztostani, hogy minden nvtr, osztly, fggvny stb. megfelelen
legyen deklarlva minden olyan fordtsi egysgben, amelyben szerepel, s hogy minden
deklarci, amely ugyanarra az egyedre vonatkozik, egysges legyen. Vegyk pldul a k-
vetkez kt fjlt:
// file1.c:
int x = 1;
int f() { /* csinlunk valamit */ }
// file2.c:
extern int x;
int f();
void g() { x = f(); }
A file2.c-ben lv g() ltal hasznlt x s f() meghatrozsa a file1.c-ben szerepel. Az extern
kulcssz jelzi, hogy a file2.c-ben az x deklarcija (csak) deklarci s nem definci (4.9).
Ha x mr rendelkezne kezdrtkkel, a fordtprogram az extern kulcsszt egyszeren fi-
gyelmen kvl hagyn, mert a kezdrtket is meghatroz deklarcik egyben defin-
cinak is minslnek. Egy objektumot a programban csak pontosan egyszer hatrozhatunk
meg. Deklarlni tbbszr is lehet, de a tpusoknak pontosan meg kell egyeznik:
// file1.c:
int x = 1;
int b = 1;
extern int c;
// file2.c:
int x; // jelentse int x = 0;
extern double b;
extern int c;
Itt hrom hiba van: x-et ktszer definiltuk, b-t ktszer deklarltuk klnbz tpusokkal,
c-t pedig ktszer deklarltuk, de egyszer sem definiltuk. Az effajta hibkat (szerkesztsi hi-
ba, linkage error) a fordtprogram ami egyszerre csak egy fjlt nz nem ismeri fel, a
szerkeszt azonban a legtbbet igen. Jegyezzk meg, hogy a globlis vagy nvtr-hatkr-
9. Forrsfjlok s programok 263
ben kezdrtk nlkl megadott vltozk alaprtelmezs szerint kapnak kezdrtket. Ez
nem vonatkozik a loklis vltozkra (4.9.5, 10.4.2) vagy a szabad trban ltrehozott ob-
jektumokra (6.2.6).
A kvetkez programrszlet kt hibt tartalmaz:
// file1.c:
int x;
int f() { return x; }
// file2.c:
int x;
int g() { return f(); }
A file2.c-ben az f() meghvsa hiba, mert f()-et a file2.c nem deklarlja. Ezenkvl a szer-
keszt nem fogja sszeszerkeszteni a programot, mert x-et ktszer definiltuk. Jegyezzk
meg, hogy az f() meghvsa a C nyelvben nem lenne hiba (B.2.2).
Az olyan neveket, amelyeket a nevet meghatroz fordtsi egysgtl klnbz fordtsi
egysgben is hasznlhatunk, kls szerkesztsnek (external linkage) nevezzk. Az elz
pldkban szerepl sszes nv kls nv. Az olyan neveket, amelyekre csak abban a for-
dtsi egysgben lehet hivatkozni, ahol meghatrozsuk szerepel, bels szerkeszts nv-
nek nevezzk.
A helyben kifejtett (inline) fggvnyeket (7.1.1, 10.2.9) minden olyan fordtsi egysgben
definilni kell azonos mdon (9.2.3) , amelyben hasznlatosak. Ezrt a kvetkez pl-
da nem csak rossz stlusra vall, hanem szablytalan is:
// file1.c:
inline int f(int i) { return i; }
// file2.c:
inline int f(int i) { return i+1; }
Sajnos, ezt a hibt a C++ egyes vltozatai nehezen veszik szre, ezrt a helyben kifejtett kd
s a kls szerkeszts kvetkez klnben teljesen logikus prostsa tiltott, hogy a for-
dtprogram-rk lete knnyebb legyen:
// file1.c:
extern inline int g(int i);
int h(int i) { return g(i); } // hiba: g() nincs definilva ebben a fordtsi egysgben
// file2.c:
extern inline int g(int i) { return i+1; }
Alapok 264
Alaprtelmezs szerint a const-ok (5.4) s a typedef-ek (4.9.7) bels szerkesztsek. K-
vetkezskppen ez a plda szablyos (br zavar lehet):
// file1.c:
typedef int T;
const int x = 7;
// file2.c:
typedef void T;
const int x = 8;
Az olyan globlis vltozk, amelyek egy adott fordtsi egysgben loklisnak szmtanak,
gyakran okoznak zavart, ezrt legjobb elkerlni ket. A globlis konstansokat s a helyben
kifejtett fggvnyeket rendszerint csak fejllomnyokba (9.2.1) szabadna tennnk, hogy
biztostsuk a kvetkezetessget. A konstansokat kifejezett utastssal tehetjk kls szer-
kesztsv:
// file1.c:
extern const int a = 77;
// file2.c:
extern const int a;
void g()
{
cout << a << '\n';
}
Itt g() 77-et fog kirni.
A nvtelen nvtereket (8.2.5) arra hasznlhatjuk, hogy a neveket egy adott fordtsi egysg-
re nzve lokliss tegyk. A nvtelen nvterek s a bels szerkeszts hatsa nagyon hasonl:
// file 1.c:
namespace {
class X { /* ... */ };
void f();
int i;
// ...
}
// file2.c:
class X { /* ... */ };
void f();
int i;
// ...
9. Forrsfjlok s programok 265
A file1.c-ben lv f() fggvny nem azonos a file2.c-ben lv f() fggvnnyel. Ha van egy
adott fordtsi egysgre nzve loklis nevnk s ugyanazt a nevet hasznljuk mshol egy
kls szerkeszts egyed szmra is, akkor magunk keressk a bajt.
A C nyelv s a rgebbi C++ programokban a static kulcsszt hasznltk (zavaran) annak
a kifejezsre, hogy hasznlj bels szerkesztst (B.2.3). A static kulcsszt lehetleg csak
fggvnyeken (7.2.1) s osztlyokon (10.2.4) bell hasznljuk.
9.2.1. Fejllomnyok
A tpusoknak ugyanannak az objektumnak, fggvnynek, osztlynak stb. minden deklar-
cijban egysgesnek kell lennik, kvetkezskppen a fordtnak tadott s ksbb
sszeszerkesztett forrskdnak is. A klnbz fordtsi egysgekben lv deklarcik
egysgessgnek elrsre nem tkletes, de egyszer mdszer, hogy a vgrehajthat k-
dot s/vagy adatlersokat tartalmaz forrsfjlokba beptjk (#include) a felletre vonat-
koz informcikat tartalmaz fejllomnyokat (header).
Az #include szvegkezel eszkz, ami arra val, hogy a forrskd-rszeket egyetlen egy-
sgbe (fjlba) gyjtsk ssze a fordtshoz. Az
#include "beptend"
utasts a beptend fjl tartalmra cserli azt a sort, amelyben az #include elfordul. A fjl
tartalmnak C++ forrsszvegnek kell lennie, mert a fordtprogram ennek olvassval ha-
lad tovbb.
A standard knyvtrbeli fejllomnyok beptshez a fjl nevt idzjelek helyett a < >
zrjelprok kz kell foglalni:
#include <iostream> // a szabvnyos include knyvtrbl
#include "myheader.h" // az aktulis knyvtrbl
Sajnos a bept utastsban a szkzk mind a < >, mind a " " belsejben fontosak:
#include < iostream > // nem fogja megtallni az <iostream>-et
Furcsnak tnhet, hogy egy fjlt minden egyes alkalommal jra kell fordtani, ha valahov
mshov beptjk, de a beptett fjlok jellemzen csak deklarcikat tartalmaznak, s
nem olyan kdot, amelyet a fordtprogramnak alaposan elemeznie kellene. Tovbb a leg-
Alapok 266
tbb modern C++-vltozat valamilyen formban tmogatja az elfordtott fejllomnyokat,
hogy cskkentse a munkt, amit ugyanannak a fejllomnynak az ismtelt fordtsa jelent.
Alapszablyknt fogadjuk el, hogy egy fejllomnyban a kvetkezk szerepelhetnek:
Mindez nem nyelvi kvetelmny, csak sszer mdja az #include hasznlatnak a logikai szer-
kezet kifejezsre. Ezzel ellenttben egy fejllomny sohasem tartalmazhatja a kvetkezket:
A fejllomnyok hagyomny szerint .h kiterjesztsek, a fggvny- s adatdefincikat tar-
talmaz fjlok kiterjesztse pedig .c, ezrt gyakran hvjk ezeket .h fjlok-nak s .c fj-
lok-nak. Ms szoksos jellseket is tallhatunk, mint a .C, .cxx, .cpp s .cc. Fordtprog-
ramunk dokumentcija ezt jl meghatrozza.
9. Forrsfjlok s programok 267
Nevestett nvterek namespace N { /* */ }
Tpusdefincik struct Point { int x, y; };
Sablondeklarcik template<class T> class Z;
Sablondefincik template<class T> class V { /* */ };
Fggvnydeklarcik extern int strlen(const char*);
Helyben kifejtett fggvnyek
defincii inline char get(char* p) { return *p++; }
Adatdeklarcik extern int a;
Konstansdefincik const float pi = 3.141593;
Felsorolsok enum Light { red, yellow, green };
Nvdeklarcik class Matrix;
Bept utastsok #include <algorithm>
Makrdefincik #define VERSION 12
Feltteles fordtsi utastsok #ifdef __cplusplus
Megjegyzsek /* check for end of file */
Kznsges
fggvnydefincik char get(char *p) { return *p++; }
Adatdefincik int a;
Agregtum-defincik short tbl[ ] = { 1, 2, 3 };
Nvtelen nvterek namespace { /* */ }
Exportlt
sablondefincik export template<class T>f(T t) { /* */ }
Az egyszer llandkat ajnlatos fejllomnyokba tenni. Az agregtumokat azonban nem,
mert az egyes C++-vltozatok nehezen tudjk elkerlni, hogy a tbb fordtsi egysgben
elfordul egyedeibl msodpldnyt ksztsenek. Ezenkvl az egyszer esetek sokkal
gyakoribbak, ezrt j kd ksztshez fontosabbak. Blcs dolog nem tl okosnak lenni az
#include hasznlatnl. Azt ajnlom, csak teljes deklarcikat s defincikat ptsnk be
s csak globlis hatkrben, szerkesztsi blokkokban, vagy olyan nvtrdefinciknl, ami-
kor rgi kdot alaktunk t (9.2.2) tegyk ezt. Clszer elkerlni a makrkkal val gyes-
kedst is. Az egyik legkevsb kedvelt foglalatossgom olyan hibt nyomon kvetni, amit
egy olyan nv okoz, amelyet egy kzvetetten beptett, szmomra teljesen ismeretlen fejl-
lomnyban szerepl makr helyettest.
9.2.2. A standard knyvtr fejllomnyai
A standard knyvtr eszkzeit szabvnyos fejllomnyok halmazn keresztl mutatjuk be
(16.1.2). A standard knyvtrbeli fejllomnyokat nem kell uttaggal elltnunk; tudjuk r-
luk, hogy fejllomnyok, mert beillesztskhz az #include <> formt hasznljuk az
#include "" helyett. A .h kiterjeszts hinya nem utal semmire a fejllomny trolsval
kapcsolatban. Egy olyan fejllomny, mint a <map>, valsznleg a map.h nev szvegfjl-
ban troldik a szoksos knyvtrban. Msfell a szabvnyos fejllomnyokat nem muszj
hagyomnyos mdon trolni. Az egyes C++-vltozatok szmra megengedett, hogy kihasz-
nljk a standard knyvtr definciinak ismerett s ezltal optimalizljk annak megval-
stst, illetve a szabvnyos fejllomnyok kezelsnek mdjt. A nyelv adott megvalst-
sa ismerheti a beptett szabvnyos matematikai knyvtrat (22.3) s gy kezelheti az
#include <cmath> utastst, mint egy kapcsolt, ami anlkl teszi elrhetv a szabvnyos
matematikai fggvnyeket, hogy brmilyen fjlt beolvasnnk. A C standard knyvtrnak
minden <X.h> fejllomnyhoz ltezik megfelel szabvnyos <cX> C++ fejllomny.
Az #include <cstdio> pldul azt nyjtja, amit az #include <stdio.h>. A stdio.h fjl ltalban
valahogy gy nz ki:
#ifdef __cplusplus // csak C++ fordtk szmra (9.2.4)
namespace std { // a standard knyvtrat az std nvtr rja le (8.2.9)
extern "C" { // az stdio fggvnyek C szerkesztsek (9.2.4)
#endif
// ...
int printf(const char* ...);
// ...
#ifdef __cplusplus
}
}
using namespace std; // az stdio elrhetv ttele a globlis nvtrben
#endif
Alapok 268
Azaz a deklarcik (nagy valsznsggel) kzsek, de az sszeszerkesztssel s nvterek-
kel kapcsolatos dolgokra oda kell figyelnnk, hogy lehetv tegyk, hogy a C s C++ osz-
tozzanak a fejllomnyon.
9.2.3. Az egyszeri definils szablya
Egy adott osztlyt, felsorolst, sablont stb. mindig csak egyszer definilhatunk egy program-
ban. Gyakorlati szempontbl ez azt jelenti, hogy pldul egy osztlynak, amelyet valahol
egy fjlban trolunk, pontosan egy kifejtssel kell rendelkeznie. Sajnos, a nyelvi szably nem
lehet ennyire egyszer. Egy osztly defincijt pldul ssze lehet lltani makrk behelyet-
testsvel is, de #include utastsokkal (9.2.1) szveges formban kt forrsfjlban is el
lehet helyezni. Mg ennl is nagyobb baj, hogy a fjl fogalma nem rsze a C s C++ nyelv-
nek, gy vannak olyan vltozatok, amelyek a programokat nem forrsfjlokban troljk.
Kvetkezskppen a szabvnyban lv szablyt amely azt mondja, hogy egy osztly, sab-
lon stb. defincijnak egyedinek kell lennie valamelyest bonyolultabb s ravaszabb m-
don fogalmaztuk meg. Ezt a szablyt gyakran az egyszeri definils szablynak (ODR,
one-definition rule) nevezik. Azaz egy osztly, sablon, vagy helyben kifejtett fggvny kt-
fle definilsa kizrlag akkor fogadhat el ugyanazon egyed kt pldnyaknt, ha
1. klnbz fordtsi egysgben szerepelnek s
2. szimblumrl szimblumra megegyeznek s
3. ezen szimblumok jelentse mindkt fordtsi egysgben ugyanaz.
Pldul:
// file1.c:
struct S { int a; char b; };
void f(S*);
// file2.c:
struct S { int a; char b; };
void f(S* p) { /* ... */ }
Az ODR rtelmben a fenti plda helyes s S ugyanarra az osztlyra vonatkozik mindkt
forrsfjlban. Nem blcs dolog azonban egy defincit ilyen mdon ktszer lerni. Ha vala-
ki mdostja a file2.c-t, azt felttelezheti, hogy az ott szerepl S az S egyetlen definilsa s
szabadon megvltoztathatja azt, ami nehezen felfedezhet hibt okozhat.
9. Forrsfjlok s programok 269
Alapok 270
Az ODR szndka az, hogy megengedje egy osztlydefinci beillesztst klnbz for-
rsfjlokba egy kzs forrsfjlbl:
// file s.h:
struct S { int a; char b; };
void f(S*);
// file1.c:
#include "s.h"
// f() hasznlata itt
// file2.c:
#include "s.h"
void f(S* p) { /* ... */ }
brval:
Nzznk pldkat az ODR szably megsrtsnek mindhrom mdjra:
// file1.c:
struct S1 { int a; char b; };
struct S1 { int a; char b; }; // hiba: kt definci
Ez azrt hiba, mert egy struct-ot egyetlen fordtsi egysgben nem lehet ktszer definilni.
// file1.c:
struct S2 { int a; char b; };
// file2.c:
struct S2 { int a; char bb; }; // hiba
s.h:
file1.c: file2.c:
struct S {int a; char b};
void f(S*);
#include "s.h"
// f() hasznlata itt
#include "s.h"
void f(S*p) {/*...*/}
Ez azrt hiba, mert S2 olyan osztlyokat nevez meg, amelyek egy tag nevben klnbznek.
// file1.c:
typedef int X;
struct S3 { X a; char b; };
// file2.c:
typedef char X;
struct S3 { X a; char b; }; // hiba
Itt az S3 kt definicija szimblumrl szimblumra megegyezik, de a plda hibs, mert az
X nv (trkks mdon) mst jelent a kt fjlban.
A legtbb C++-vltozat nem kpes a klnbz fordtsi egysgekben lv osztly-
defincik kvetkezetessgt ellenrizni, ezrt az ODR-t megsrt deklarcik nehezen
szrevehet hibkat okozhatnak. Sajnos az a mdszer sem kpes az ODR utolsknt bemu-
tatott megszegse ellen vdelmet nyjtani, amikor a kzs definicikat fejllomnyokba
tesszk s aztn azokat ptjk be. A helyi typedef-ek s makrk ugyanis mdosthatjk
a beptett deklarcik jelentst:
// file s.h:
struct S { Point a; char b; };
// file1.c:
#define Point int
#include "s.h"
// ...
// file2.c:
class Point { /* ... */ };
#include "s.h"
// ...
Az ilyen kdmdosuls ellen gy vdekezhetnk a legjobban, ha a fejllomnyokat annyi-
ra klnllv tesszk, amennyire csak lehetsges. Pldul ha a Point osztlyt az s.h llo-
mnyban vezettk volna be, a fordtprogram felismerte volna a hibt.
A sablondefincikat tbb fordtsi egysgbe is bepthetjk, amg ez nem srti az ODR-t,
az exportlt sablonokat pedig gy is hasznlhatjuk, hogy csak a deklarcijukat adjuk meg:
// file1.c:
export template<class T> T twice(T t) { return t+t; }
9. Forrsfjlok s programok 271
// file2.c:
template<class T> T twice(T t); // deklarci
int g(int i) { return twice(i); }
Az export kulcssz azt jelenti, hogy ms fordtsi egysgbl elrhet (13.7).
9.2.4. sszeszerkeszts nem C++ kddal
A C++ programok ltalban ms nyelven megrt rszleteket is tartalmaznak. Hasonlan gya-
kori az is, hogy C++ kdrszletet hasznlnak ms nyelven megrt programok rszeknt.
Az egyttmkds a klnbz nyelven megrt programrszek kztt nem mindig
knny st mg az azonos nyelven rt, de klnbz fordtprogrammal lefordtott kd-
rszletek kztt sem. A klnbz nyelvek s ugyanazon nyelv klnbz megvalstsai
pldul klnbzkppen hasznlhatjk a gpi regisztereket a paramterek trolsra,
mskppen helyezhetik azokat a verembe, klnbz lehet a beptett tpusok, pldul
a karakterlncok s egszek szerkezete, illetve azon nevek formja, melyeket a fordtprog-
ram a szerkesztnek tad, s a szerkeszttl megkvetelt tpusellenrzsek. Hogy segt-
snk, sszeszerkesztsi szablyt hatrozhatunk meg az extern deklarcikra. A kvetkez
plda bevezeti a C s a C++ standard knyvtraiban lev strcpy() fggvnyt, s meghat-
rozza, hogy a C sszeszerkesztsi szablyainak megfelelen kell azt hozzszerkeszteni
a kdhoz:
extern "C" char* strcpy(char*, const char*);
A deklarci hatsa a sima deklarciktl csak az strcpy() hvsra hasznlt sszeszer-
kesztsi szablyban tr el.
extern char* strcpy(char*, const char*);
Az extern "C" utasts klnsen fontos a C s a C++ kztti szoros kapcsolat miatt. Jegyez-
zk meg, hogy az extern "C"-ben szerepl "C" az sszeszerkesztsi szablyt, nem pedig
a programnyelvet jelli. Az extern "C"-t gyakran hasznljk olyan Fortran vagy assembler
eljrsokkal val sszeszerkesztshez, melyek vletlenl ppen megfelelnek a C kvetel-
mnyeinek.
Az extern "C" utasts (csak) az sszeszerkesztsi szablyt hatrozza meg, a fggvnyh-
vsok szerept nem befolysolja. Az extern "C"-knt megadott fggvnyekre is a C++ t-
pusellenrzsi s paramter-talaktsi szablyai vonatkoznak, nem pedig a gyengbb C
szablyok.
Alapok 272
Pldul:
extern "C" int f();
int g()
{
return f(1); // hiba: nem vr paramtert
}
Knyelmetlen lehet, ha sok deklarcihoz kell hozzadnunk az extern "C"-t, ezrt bevezet-
tnk egy eljrst, mellyel deklarcik egy csoportjnak sszeszerkesztst hatrozhatjuk
meg:
extern "C" {
char* strcpy(char*, const char*);
int strcmp(const char*, const char*);
int strlen(const char*);
// ...
}
Ezt a szerkezetet, melyet gyakran szerkesztsi blokknak (linkage block) neveznek, gy is
hasznlhatjuk, hogy belefoglalunk egy teljes C fejllomnyt s gy alkalmass tesszk azt
a C++-ban val hasznlatra:
extern "C" {
#include <string.h>
}
A fenti mdszer gyakran hasznlatos arra, hogy C fejllomnyokbl C++ fejllomnyokat
hozzanak ltre. Egy msik lehetsg, ha feltteles fordtst (7.8.1) hasznlunk, hogy kzs
C s C++ fejllomnyt ksztsnk:
#ifdef __cplusplus
extern "C" {
#endif
char* strcpy(char*, const char*);
int strcmp(const char*, const char*);
int strlen(const char*);
// ...
#ifdef __cplusplus
}
#endif
9. Forrsfjlok s programok 273
A kszen kapott __cplusplus makr hasznlatval azt biztosthatjuk, hogy a C++ szerkeze-
tek eltnjenek, amikor a fjlt C fejllomnyknt hasznljuk.
Egy szerkesztsi blokkon bell brmilyen deklarci szerepelhet:
extern "C" { // brmilyen deklarci jhet ide, pl:
int g1; // definci
extern int g2; // deklarci, nem definci
}
Ez a vltozk hatkrt s trolsi osztlyt nem rinti, gy g1 globlis vltoz marad, s
definicija is lesz, nem csak deklarcija. Ha egy vltozt csak deklarlni, nem pedig
definilni akarunk, az extern kulcsszt kzvetlenl a deklarci eltt kell megadnunk:
extern "C" int g3; // deklarci, nem definci
Ez els ltsra furcsnak tnik, pedig clja csak annyi, hogy a deklarci jelentse vltozat-
lan maradjon, amikor egy extern deklarcihoz "C"-t adunk (s a fjl jelentse is, amikor
majd a szerkesztsi blokkba foglaljuk).
Egy C szerkeszts nevet nvtrben is megadhatunk. A nvtr azt befolysolni fogja, ho-
gyan lehet a nvhez hozzfrni C++ programokbl, de azt nem, hogy a szerkeszt hogyan
fogja ltni a nevet. Egy jellemz plda erre az std nvtr printf() fggvnye:
#include<cstdio>
void f()
{
std::printf("Hell, "); // rendben
printf("vilg!\n"); // hiba: nincs globlis printf()
}
Mg ha std::printf()-nek nevezzk is, ez mg mindig ugyanaz a rgi C printf() (21.8). Ez
lehetv teszi szmunkra, hogy C szerkeszts knyvtrakat ptsnk be egy ltalunk v-
lasztott nvtrbe, ahelyett, hogy a globlis nvteret szennyeznnk". Sajnos ugyanez a ru-
galmassg nem ll rendelkezsnkre az olyan fejllomnyok esetben, amelyek C++ szer-
keszts fggvnyeket hatroznak meg a globlis nvtrben. Ennek az az oka, hogy a C++
egyedek sszeszerkesztsnl figyelembe kell venni a nvtereket is, gy a ltrehozott trgy-
kd (object fjl) tkrzni fogja a nvterek hasznlatt vagy annak hinyt.
Alapok 274
9.2.5. Az sszeszerkeszts s a fggvnyekre hivatkoz mutatk
Ha egy programban a C s C++ kdrszleteket keverjk, elfordulhat, hogy az egyik nyelven
megrt fggvnyekre hivatkoz mutatkat a msik nyelven definilt fggvnyeknek szeret-
nnk tadni. Ha a kt nyelv adott vltozatainak sszeszerkesztsi szablyai, illetve a fgg-
vnyhvsi eljrsok kzsek, a fggvnyekre hivatkoz mutatk tadsa egyszer. Ennyi k-
zs tulajdonsg azonban ltalban nem ttelezhet fel, gy figyelnnk kell arra, hogy biztost-
suk a fggvnyek oly mdon trtn meghvst, ahogy azt a fggvny elvrja. Ha egy dek-
larci szmra meghatrozzuk az sszeszerkesztsi mdot, akkor az minden olyan fgg-
vnytpusra, fggvnynvre, s vltoznvre vonatkozni fog, amit a deklarci(k)
bevezet(nek). Ez mindenfle furcsa de nha alapvet sszeszerkesztsi mdot lehet-
v tesz. Pldul:
typedef int (*FT)(const void*, const void*); // FT C++ szerkeszts
extern "C" {
typedef int (*CFT)(const void*, const void*); // CFT C szerkeszts
void qsort(void* p, size_t n, size_t sz, CFT cmp); // cmp C szerkeszts
}
void isort(void* p, size_t n, size_t sz, FT cmp); // cmp C++ szerkeszts
void xsort(void* p, size_t n, size_t sz, CFT cmp); // cmp C szerkeszts
extern "C" void ysort(void* p, size_t n, size_t sz, FT cmp); // cmp C++ szerkeszts
int compare(const void*, const void*); // compare() C++ szerkeszts
extern "C" int ccmp(const void*, const void*); // ccmp() C szerkeszts
void f(char* v, int sz)
{
qsort(v,sz,1,&compare); // hiba
qsort(v,sz,1,&ccmp); // rendben
isort(v,sz,1,&compare); // rendben
isort(v,sz,1,&ccmp); // hiba
}
Egy olyan nyelvi vltozat, amelyben a C s C++ ugyanazt a fggvnyhvsi mdot hasznl-
ja, nyelvi kiterjesztsknt elfogadhatja a hibaknt megjellt eseteket.
9. Forrsfjlok s programok 275
9.3. Fejllomnyok hasznlata
A fejllomnyok hasznlatnak illusztrlsra most bemutatjuk a szmolgp program
(6.1, 8.2) nhny lehetsges fizikai elrendezst.
9.3.1. Egyetlen fejllomnyos elrendezs
Egy programot gy bonthatunk a legegyszerbben tbb fjlra, hogy a defincikat megfe-
lel szm .c fjlba, a .c fjlok kztti kapcsolatot biztost tpusok deklarciit pedig
egyetlen .h fjlba tesszk, melyet minden .c fjl bept (#include). A szmolgp program
esetben t .c fjlt lexer.c, parser.c, table.c, error.c s main.c hasznlhatnnk a fggv-
nyek s adatlersok trolsra, s a dc.h fejllomnyban trolhatnnk azoknak a neveknek
a deklarciit, amelyek egynl tbb fjlban hasznlatosak.
A dc.h fejllomny gy nzne ki:
// dc.h:
namespace Error {
struct Zero_divide { };
struct Syntax_error {
const char* p;
Syntax_error(const char* q) { p = q; }
};
}
#include <string>
namespace Lexer {
enum Token_value {
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
extern Token_value curr_tok;
extern double number_value;
extern std::string string_value;
Token_value get_token();
}
Alapok 276
namespace Parser {
double prim(bool get); // elemi szimblumok kezelse
double term(bool get); // szorzs s oszts
double expr(bool get); // sszeads s kivons
using Lexer::get_token;
using Lexer::curr_tok;
}
#include <map>
extern std::map<std::string,double> table;
namespace Driver {
extern int no_of_errors;
extern std::istream* input;
void skip();
}
Minden vltozdeklarciban az extern kulcsszt hasznljuk annak biztostsra, hogy egy
meghatrozs ne forduljon el tbbszr is, amikor a dc.h-t a fjlokba beptjk. Az egyes
defincik a megfelel .c fjlban szerepelnek.
A lnyegi kd elhagysval a lexer.c valahogy gy nz ki:
// lexer.c:
#include "dc.h"
#include <iostream>
#include <cctype>
Lexer::Token_value Lexer::curr_tok;
double Lexer::number_value;
std::string Lexer::string_value;
Lexer::Token_value Lexer::get_token() { /* ... */ }
A fejllomny ilyen hasznlata biztostja, hogy a benne lv minden deklarci valamilyen
ponton be legyen ptve abba a fjlba, amely a hozz tartoz kifejtst tartalmazza. A lexer.c
fordtsakor pldul a fordtprogramnak a kvetkez kd addik t:
namespace Lexer { // a dc.h-bl
// ...
Token_value get_token();
}
// ...
Lexer::Token_value Lexer::get_token() { /* ... */ }
9. Forrsfjlok s programok 277
gy a fordtprogram biztosan szreveszi, ha egy nvhez megadott tpusok nem egysge-
sek. Ha a get_token()-t pldul Token_value tpus visszatrsi rtkkel vezettk volna be,
de int visszatrsi rtkkel definiltuk volna, a lexer.c fordtsa tpustkzsi vagy nem
megfelel tpus (type mismatch) hiba miatt nem sikerlt volna. Ha egy definci hinyzik,
a szerkeszt fogja szrevenni a problmt, ha egy deklarci, akkor valamelyik .c fjl for-
dtsa hisul meg.
A parser.c fjl gy fog kinzni:
// parser.c:
#include "dc.h"
double Parser::prim(bool get) { /* ... */ }
double Parser::term(bool get) { /* ... */ }
double Parser::expr(bool get) { /* ... */ }
A table.c pedig gy:
// table.c:
#include "dc.h"
std::map<std::string,double> table;
A szimblumtbla nem ms, mint egy standard knyvtrbeli map tpus vltoz. A fenti de-
finci a table-t globlisknt hatrozza meg. Egy valsgos mret programban a globlis
nvtr effle kis szennyezdsei felhalmozdnak s vgl problmkat okoznak. Csak
azrt voltam itt ilyen hanyag, hogy lehetsgem legyen figyelmeztetni r.
A main.c fjl vgl gy fog kinzni:
// main.c:
#include "dc.h"
#include <sstream>
int Driver::no_of_errors = 0;
std::istream* Driver::input = 0;
void Driver::skip() { /* ... */ }
int main(int argc, char* argv[ ]) { /* ... */ }
Alapok 278
Ahhoz, hogy a program main() fggvnynek ismerjk fel, a main()-nek globlis fgg-
vnynek kell lennie, ezrt itt nem hasznltunk nvteret.
A program fizikai elrendezst valahogy gy lehet bemutatni:
szrevehetjk, hogy a fell lv fejllomnyok mind a standard knyvtr fjljai. A program
elemzsekor ezek a knyvtrak szmos esetben kihagyhatk, mert szleskren ismertek
s stabilak. A kis programoknl az elrendezs egyszersthet, ha minden #include utas-
tst kzs fejllomnyba tesznk.
Az egyetlen fejllomnyos fizikai rszekre bonts akkor a leghasznosabb, ha a program ki-
csi s rszeit nem ll szndkunkban kln hasznlni. Jegyezzk meg, hogy amikor nvte-
reket hasznlunk, a dc.h-ban egyben a program logikai felptst is brzoljuk. Ha nem
hasznlunk nvtereket, a szerkezet homlyos lesz, br ezen a megjegyzsek segthetnek.
A nagyobb programok egyetlen fejllomnyos elrendezse nem mkdik a hagyomnyos,
fjl alap fejlesztkrnyezetekben. A kzs fejllomny mdostsa maga utn vonja az egsz
program jrafordtst s nagy a hibalehetsg, ha tbb programoz is mdostja az egyetlen
fejllomnyt. Hacsak nem fektetnek hangslyt a nvterekkel s osztlyokkal kapcsolatos
programozsi stlusra, a logikai felpts a program nvekedsvel egytt romlani fog.
9. Forrsfjlok s programok 279
<sstream> <map> <string>
dc.h
<cctype>
main.c parser.c table.c lexer.c
<iostream>
9.3.2. Tbb fejllomnyos elrendezs
Egy msik fizikai elrendezs szerint minden logikai modulnak sajt fejllomnya lenne,
amely lerja a modul ltal nyjtott szolgltatsokat. Ekkor minden .c fjlhoz tartozik egy
megfelel .h fjl, ami meghatrozza a .c szolgltatsait (fellett). Minden .c fjl bepti a sa-
jt .h fjljt, s rendszerint tovbbi olyan .h fjlokat is, amelyek meghatrozzk, mire van
szksge ms modulokbl ahhoz, hogy megvalstsa a felletben kzztett szolgltatso-
kat. Ez a fizikai elrendezs megegyezik a modul logikai felptsvel. A felhasznlknak
sznt felletet a .h fjl tartalmazza, a programozi fellet egy _impl.h vgzds fjlban sze-
repel, a modul fggvny- s vltozdefincii stb. pedig a .c fjlokban vannak elhelyezve.
Ily mdon az elemzt hrom fjl kpviseli, felhasznli fellett pedig a parser.h nyjtja:
// parser.h:
namespace Parser { // fellet a felhasznlknak
double expr(bool get);
}
Az elemzt megvalst fggvnyek kzs krnyezett a parser_impl.h adja:
// parser_impl.h:
#include "parser.h"
#include "error.h"
#include "lexer.h"
namespace Parser { // fellet a megvalstshoz
double prim(bool get);
double term(bool get);
double expr(bool get);
using Lexer::get_token;
using Lexer::curr_tok;
}
A parser.h felhasznli fejllomnyt azrt ptjk be, hogy a fordtprogram ellenrizhesse
a kvetkezetessget (9.3.1). Az elemz fggvnyeket a parser.c fjlban egytt troljuk
azokra a fejllomnyokra vonatkoz #include utastsokkal, melyekre a Parser fggvnyei-
nek szksge van:
// parser.c:
#include "parser_impl.h"
#include "table.h"
double Parser::prim(bool get) { /* ... */ }
double Parser::term(bool get) { /* ... */ }
double Parser::expr(bool get) { /* ... */ }
Alapok 280
Az elemz s annak a vezrl ltali hasznlata brval gy mutathat be:
Ahogy vrtuk, ez elg jl egyezik a 8.3.3-ban lert logikai szerkezettel. Ha a table.h-t
a parser_impl.h-ba ptettk volna be a parser.c helyett, a szerkezetet mg tovbb egysze-
rsthettk volna. A table.h azonban valami olyasmire plda, ami nem szksges az elem-
z fggvnyek kzs krnyezetnek kifejezshez, csak a fggvnyek megvalstsainak
van szksge r. Tulajdonkppen egyetlen fggvny, a prim() hasznlja, gy ha a fggs-
geket valban a lehet legkevesebbre szeretnnk cskkenteni, a prim()-et tegyk kln .c
fjlba s csak oda ptsk be a table.h-t:
9. Forrsfjlok s programok 281
parser.h lexer.h
parser_impl.h
parser.c main.c
error.h table.h
parser.h lexer.h
parser_impl.h
parser.c prim.c
error.h table.h
Ilyen alapossgra a nagyobb modulokat kivve nincs szksg. A valsgos mret modu-
lok esetben gyakori, hogy tovbbi fjlokat ptenek be ott, ahol egyes fggvnyek szm-
ra azok szksgesek. Tovbb nem ritka, hogy egynl tbb _impl.h fjl van, mivel a modul
fggvnyeinek rszhalmazai klnbz kzs krnyezetet ignyelnek.
Meg kell jegyeznnk, hogy az _impl.h hasznlata nem szabvnyos s mg csak nem is gya-
kori megolds n egyszeren gy szeretek elnevezni dolgokat.
Mirt trdnk ezzel a bonyolultabb tbb fejllomnyos elrendezssel? Nyilvn sokkal ke-
vesebb gondolkodst ignyel, ha egyszeren minden deklarcit bedobunk egy fejllo-
mnyba, mint ahogy azt a dc.h-nl tettk.
A tbb fejllomnyos elrendezs olyan modulok s programok esetben hatsos, amelyek
nagysgrendekkel nagyobbak, mint a mi apr elemznk s szmolgpnk. Alapveten
azrt hasznltuk ezt az elrendezstpust, mert jobban azonostja a kapcsolatokat. Egy nagy
program elemzsekor vagy mdostsakor alapvet, hogy a programoz viszonylag kis
kdrszletre sszpontosthasson. A tbb fejllomnyos elrendezs segt, hogy pontosan el-
dnthessk, mitl fgg az elemz kd, s hogy figyelmen kvl hagyhassuk a program tb-
bi rszt. Az egyetlen fejllomnyos elrendezs rknyszert minket, hogy minden olyan
deklarcit megnzznk, amelyet valamelyik modul hasznl, s eldntsk, hogy odaill-e.
A lnyeg, hogy a kd mdostsa mindig hinyos informcik s helyi nzpont alapjn
trtnik. A tbb fejllomnyos elrendezs megengedi, hogy sikeresen dolgozzunk bell-
rl kifel, csak helyi szemszgbl. Az egyetlen fejllomnyos elrendezs mint minden
ms elrendezs, ahol egy globlis informcitr van a kzppontban fellrl lefel hala-
d megkzeltst ignyel, gy rkk gondolkodnunk kell azon, hogy pontosan mi fgg
egy msik dologtl.
A jobb sszpontosts azt eredmnyezi, hogy kevesebb informci kell a modul lefordt-
shoz, gy az gyorsabban trtnik. A hats drmai lehet. Elfordul, hogy a fordtsi id ti-
zedrszre cskken, pusztn azrt, mert egy egyszer fggsgelemzs a fejllomnyok
jobb hasznlathoz vezet.
9.3.2.1. A szmolgp egyb moduljai
A szmolgp tbbi moduljt az elemzhz hasonlan rendezhetjk el. Ezek a modulok
azonban olyan kicsik, hogy nem ignyelnek sajt _impl.h fjlokat. Az ilyen fjlok csak ott
kellenek, ahol egy logikai modul sok fggvnybl ll, amelyeknek kzs krnyezetre van
szksgk.
Alapok 282
A hibakezelt a kivteltpusok halmazra egyszerstettk, gy nincs szksgnk az error.c-re:
// error.h:
namespace Error {
struct Zero_divide { };
struct Syntax_error {
const char* p;
Syntax_error(const char* q) { p = q; }
};
}
Az adatbeviteli kd (lexikai elemz, lexer) meglehetsen nagy s rendezetlen felletet
nyjt:
// lexer.h:
#include <string>
namespace Lexer {
enum Token_value {
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
extern Token_value curr_tok;
extern double number_value;
extern std::string string_value;
Token_value get_token();
}
A lexer.h-n kvl a lexikai elemz az error.h-ra, az <iostream>-re s a <ctype>-ban meg-
adott, a karakterek fajtit eldnt fggvnyekre tmaszkodik:
// lexer.c:
#include "lexer.h"
#include "error.h"
#include <iostream>
#include <cctype>
9. Forrsfjlok s programok 283
Lexer::Token_value Lexer::curr_tok;
double Lexer::number_value;
std::string Lexer::string_value;
Lexer::Token_value Lexer::get_token() { /* ... */ }
Az error.h #include utastsait kln tehettk volna, a Lexer-hez tartoz _impl.h fjlba, ez
azonban tlzs egy ilyen kis program esetben.
A modul megvalstsban szoksos mdon ptjk be (#include) a modul ltal nyjtott fe-
lletet ebben az esetben a lexer.h-t , hogy a fordtprogram ellenrizhesse a kvetkeze-
tessget.
A szimblumtbla alapveten nll, br a standard knyvtrbeli <map> fejllomny hasz-
nlatval szmos rdekes dolog kerlhet bele, hogy hatkonyan valsthassa meg a map
sablonosztlyt:
// table.h:
#include <map>
#include <string>
extern std::map<std::string,double> table;
Mivel feltesszk, hogy az egyes fejllomnyok tbb .c fjlba is bele lehetnek ptve, a table
deklarcijt kln kell vlasztanunk annak kifejtstl, mg akkor is, ha a table.c s
a table.h kztti klnbsg csak az extern kulcssz:
// table.c:
#include "table.h"
std::map<std::string,double> table;
A vezrl tulajdonkppen mindenre tmaszkodik:
// main.c:
#include "parser.h"
#include "lexer.h"
#include "error.h"
#include "table.h"
Alapok 284
namespace Driver {
int no_of_errors;
std::istream* input;
void skip();
}
#include <sstream>
int main(int argc, char* argv[ ]) { /* ... */ }
Mivel a Driver nvteret kizrlag a main() hasznlja, a main.c-be tesszk azt. Kln is sze-
repelhetne driver.h fjlknt, amit az #include utastssal beptnk.
A nagyobb programokat rendszerint megri gy elrendezni, hogy a vezrlnek kevesebb
kzvetlen fggsge legyen. Gyakran sszer az olyan mveletekbl is minl kevesebbet
alkalmazni, amit a main() tesz, nevezetesen hogy meghv egy kln forrsfjlban lv ve-
zrl fggvnyt. Ez klnsen fontos olyan kdok esetben, amelyeket knyvtrknt aka-
runk hasznlni. Ekkor ugyanis nem tmaszkodhatunk a main()-ben megrt kdra s fel kell
kszlnnk arra is, hogy klnbz fggvnyekbl hvjk meg kdunkat (9.6[8]).
9.3.2.2. A fejllomnyok hasznlata
A programban hasznlt fejllomnyok (header file) szma tbb tnyeztl fgg. Ezen t-
nyezk kzl sok inkbb a rendszer fjlkezelsbl addik s nem a C++-bl. Pldul, ha
szvegszerkeszt programunk nem kpes arra, hogy tbb fjlt nzznk vele egyszerre, ak-
kor nem elnys sok fejllomnyt hasznlnunk. Hasonlan, ha 20 darab 50 soros fjl olva-
ssa szreveheten tbb idt ignyel, mint egyetlen 1000 soros fjl, akkor ktszer is gon-
doljuk meg, mieltt egy kis projektben a tbb fejllomnyos stlust hasznljuk.
Nhny figyelmeztets: egy tucatnyi fejllomny (termszetesen a szoksos fejllom-
nyokkal egytt, melyeket gyakran szzas nagysgrendben szmolhatunk) a program vg-
rehajtsi krnyezete szmra rendszerint mg kezelhet. Ha azonban egy nagy program
deklarciit logikailag a lehet legkisebb fejllomnyokra bontjuk (pldul gy, hogy min-
den szerkezet deklarcijt kln fjlba tesszk), knnyen egy tbb szz fjlbl ll, kezel-
hetetlen zrzavar lehet az eredmny.
Nagy projekteknl persze elkerlhetetlen a sok fejllomny. Az ilyeneknl tbb szz fjl
(nem szmolva a szoksos fejllomnyokat) az ltalnos. Az igazi bonyodalom ott kezd-
dik, amikor elrik az ezres nagysgrendet. A fent trgyalt alapvet mdszerek ekkor is al-
kalmazhatk, de az llomnyok kezelse sziszifuszi feladatt vlik. Emlkezznk, hogy
9. Forrsfjlok s programok 285
a valdi mret programoknl az egyetlen fejllomnyos elrendezst ltalban nem vlaszt-
hatjuk, mert az ilyen programok rendszerint eleve tbb fejllomnyt tartalmaznak. A ktfaj-
ta elrendezsi mdszer kztt a program alkotrszeinek ltrehozsakor kell (nha tbb-
szr is) vlasztanunk.
Igazn nem is a mi zlsnkre van bzva, hogy az egyetlen s a tbb fejllomnyos elrende-
zs kzl vlasszunk. Ezek olyan egymst kiegszt mdszerek, melyeket mindig figye-
lembe kell vennnk a lnyegi modulok tervezsekor, s jra kell gondolnunk azokat,
ahogy a rendszer fejldik. Rendkvl fontos emlkeznnk arra, hogy egy fellet nem szol-
glhat minden clra ugyanolyan jl. Rendszerint megri klnbsget tenni a fejleszti s
a felhasznli fellet kztt. Ezenkvl sok nagyobb program szerkezete olyan, hogy cl-
szer a felhasznlk tbbsgnek egyszer, a tapasztaltabb felhasznlknak pedig terjedel-
mesebb felletet nyjtani. A tapasztalt felhasznlk felletei (a teljes felletek) sokkal
tbb szolgltatst ptenek be, mint amennyirl egy tlagos felhasznlnak tudnia kell. Va-
ljban az tlagos felhasznl fellett gy hatrozhatjuk meg, hogy nem ptjk be azokat
a fejllomnyokat, amelyek olyan szolgltatsokat rnak le, amelyek ismeretlenek lennnek
az tlagos felhasznl szmra. Az tlagos felhasznl kifejezs nem lekicsinyl. Ahol
nem muszj szakrtnek lennem, jobban szeretek tlagos felhasznl lenni. gy ugyanis
kevesebb a veszekeds.
9.3.3. llomny-rszemek
A tbb fejllomnyos megkzelts gondolata az, hogy minden logikai modult kvetkeze-
tes, nll egysgknt brzoljunk. A program egsznek szempontjbl nzve viszont
azon deklarcik tbbsge, melyek ahhoz kellenek, hogy minden logikai egysg teljes le-
gyen, felesleges. Nagyobb programoknl az ilyen flsleg (redundancia) hibkhoz vezet-
het, amint egy osztlylerst vagy helyben kifejtett fggvnyeket tartalmaz fejllomnyt
ugyanabban a fordtsi egysgben (9.2.3) ktszer ptnk be az #include-dal.
Kt vlasztsunk lehet.
1. tszervezhetjk a programunkat, hogy eltvoltsuk a flsleget, vagy
2. tallunk valamilyen mdot arra, hogy a fejllomnyok tbbszri beptse
megengedett legyen.
Az els megkzelts ami a szmolgp vgs vltozathoz vezetett fraszt s vals-
gos mret programoknl gyakorlatilag kivitelezhetetlen. A flslegre azrt is szksgnk
van, hogy a program egyes egysgei elklnlten is rthetek legyenek. A fls #include-
ok kiszrse s az ennek eredmnyekppen ltrejtt egyszerstett program nagyon el-
nys lehet mind logikai szempontbl, mind azltal, hogy cskken a fordtsi id. Az sszes
Alapok 286
elforduls megtallsa azonban ritkn sikerl, gy alkalmaznunk kell valamilyen eszkzt,
ami megengedi a fls #include-ok jelenltt. Lehetleg szisztematikusan kell hasznl-
nunk, mert nem tudhatjuk, hogy a felhasznl mennyire alapos elemzst tart rdemesnek.
A hagyomnyos megolds az, hogy a fejllomnyokba llomny-rszemeket (bepts-
figyelket, include-guards) illesztnk:
// error.h:
#ifndef CALC_ERROR_H
#define CALC_ERROR_H
namespace Error {
// ...
}
#endif // CALC_ERROR_H
A fjlnak az #ifndef s az #endif kztti tartalmt a fordtprogram nem veszi figyelembe,
ha a CALC_ERROR_H mr definilt. Ezrt amikor a fordtprogram az error.h-val elszr ta-
llkozik, beolvassa annak tartalmt, a CALC_ERROR_H pedig rtket kap. Ha ismt tall-
kozna vele a fordts sorn, msodszor mr nem fogja figyelembe venni. Ez makrkkal va-
l gyeskeds, de mkdik s mindentt jelen van a C s C++ vilgban. A standard knyv-
tr fejllomnyainak mindegyike tartalmaz llomny-rszemeket.
A fejllomnyokat mindenfle krnyezetben hasznljk, a makrnevek tkzse ellen pe-
dig nincs nvtr vdelem. Az llomny-rszemeknek ezrt hossz s csnya neveket szok-
tam vlasztani.
Amint a programoz hozzszokik a fejllomnyokhoz s az llomny-rszemekhez, hajla-
mos kzvetlenl vagy kzvetve sok fejllomnyt bepteni. Ez nem kvnatos, mg azoknl
a C++-vltozatoknl sem, melyek optimalizljk a fejllomnyok feldolgozst. Szksgte-
lenl hossz fordtsi idt okozhatnak s szmos deklarcit s makrt elrhetv te-
hetnek, ami kiszmthatatlanul s kedveztlenl befolysolhatja a program jelentst. Csak
akkor ptsnk be fejllomnyokat, amikor tnyleg szksg van r.
9. Forrsfjlok s programok 287
9.4. Programok
A program kln fordtott egysgek gyjtemnye, melyet a szerkesztprogram egyest.
Minden, ebben a gyjtemnyben hasznlt fggvnynek, objektumnak, tpusnak stb. egye-
di meghatrozssal (defincival) kell rendelkeznie (4.9, 9.2.3) s pontosan egy main()
nev fggvnyt kell tartalmaznia (3.2). A program ltal vgzett f tevkenysg a main()
meghvsval kezddik s az abbl val visszatrssel r vget. A main() ltal visszaadott
int rtk lesz a program visszatrsi rtke, amit a main()-t meghv rendszer megkap.
Ezen az egyszer meghatrozson a globlis vltozkat tartalmaz (10.4.9) vagy el nem
kapott kivtelt (14.7) kivlt programok esetben finomtanunk kell.
9.4.1. Kezdeti rtkads nem loklis vltozknak
Elvileg a fggvnyeken kvl megadott, nem loklisnak szmt vltozk (azaz a globlis,
nvtr-, vagy static osztlyvltozk) a main() meghvsa eltt, a fordtsi egysgben
definicijuk sorrendjben kapnak kezdrtket (10.4.9). Ha egy ilyen vltoznak nincs
pontosan meghatrozott (explicit) kezdrtke, akkor a tpusnak megfelel alaprtelme-
zett rtkkel tltdik fel (10.4.2). A beptett tpusok s felsorolsok esetben az alaprtel-
mezett kezdrtk a 0:
double x = 2; // nem loklis vltozk
double y;
double sqx = sqrt(x+y);
Itt az x s az y az sqx eltt kap kezdrtket, gy az sqrt(2) hvdik meg.
A klnbz fordtsi egysgekben lv globlis vltozk kezdrtkkel val elltsnak
sorrendje nem kttt, kvetkezskppen nem blcs dolog ezeknl a kezdrtkek kztt
sorrendi fggsgeket ltrehozni. Tovbb nem lehetsges olyan kivtelt sem elkapni, amit
egy globlis vltoz kezdeti rtkadsa vltott ki (14.7). ltalban az a legjobb, ha minl
kevesebb globlis vltozt hasznlunk; fleg a bonyolult kezdeti rtkadst ignyl glob-
lis vltozk hasznlatt kell korltoznunk.
A klnbz fordtsi egysgekben lv globlis vltozk kezdeti rtkkel val feltltsnek
sorrendjt szmos mdon knyszerthetjk ki, de nincs kztk olyan, amely egyszerre hor-
dozhat s hatkony is lenne. Fleg a dinamikus csatols knyvtrak (DLL) nem kpesek
Alapok 288
a bonyolult fggsgekkel rendelkez globlis vltozkkal boldogan egytt lni. Globlis
vltozk helyett gyakran hasznlhatunk referencit visszaad fggvnyeket:
int& use_count()
{
static int uc = 0;
return uc;
}
A use_count() hvs most globlis vltozknt mkdik, kivve, hogy els hasznlatakor
kap kezdrtket (5.5):
void f()
{
cout << ++use_count(); // nvels s kirs
// ...
}
A nem loklis statikus vltozk kezdeti rtkadst brmilyen eljrs vezrelheti, amit az
adott nyelvi vltozat arra hasznl, hogy elindtsa a C++ programot. Csak akkor garantlt,
hogy a mdszer megfelelen mkdik, ha a main() vgrehajtsra sor kerl, ezrt el kell
kerlnnk azon nem loklis vltozk hasznlatt, melyek futsi idej kezdeti rtkadst
ignyelnek olyan C++ kdban, amit nem C++ program hasznl.
Jegyezzk meg, hogy a kezdrtket konstans kifejezsektl kap vltozk (C.5) nem
fgghetnek ms fordtsi egysgben lev objektumok rtktl s nem ignyelnek futsi
idej kezdeti rtkadst, gy minden esetben biztonsgosan hasznlhatk.
9.4.1.1. A program befejezse
A programok futsa szmos mdon rhet vget:
A main()-bl val visszatrssel
Az exit() meghvsval
Az abort() meghvsval
El nem kapott kivtel kivltsval
Tovbb tbbfle hibs felpts s nyelvi vltozattl fgg mdszer ltezik arra, hogy egy
program sszeomoljon. Ha a program befejezsre a standard knyvtrbeli exit() fggvnyt
hasznljuk, akkor meghvdnak a ltrehozott statikus objektumok destruktorai (10.4.9,
10.2.4). Ha azonban a program a standard knyvtr abort() fggvnyt hasznlja,
9. Forrsfjlok s programok 289
a destruktorok meghvsra nem kerl sor. Jegyezzk meg: ez azt is jelenti, hogy az exit()
nem fejezi be rgtn a programot; destruktorban val meghvsa vgtelen ciklust eredm-
nyezhet. Az exit() fggvny tpusa
void exit(int);
A main() visszatrsi rtkhez (3.2) hasonlan az exit() paramtere is visszaaddik
a rendszernek a program visszatrsi rtkeknt. A nulla sikeres befejezst jelent.
Az exit() meghvsa azt jelenti, hogy a hv fggvny loklis vltozinak s az azt hv fgg-
vnyek hasonl vltozinak destruktorai nem hvdnak meg. A loklis objektumok megfe-
lel megsemmistst (14.4.7) egy kivtel dobsa s elkapsa biztostja. Emellett az exit()
meghvsa gy fejezi be a programot, hogy az exit() hvjnak nem ad lehetsget arra,
hogy megoldja a problmt. Ezrt gyakran az a legjobb, ha a krnyezetet egy kivtel kivl-
tsval elhagyjuk, s megengedjk egy kivtelkezelnek, hogy eldntse, mi legyen a tovb-
biakban. A C (s C++) standard knyvtrnak atexit() fggvnye lehetsget ad arra, hogy
kdot hajthassunk vgre a program befejezdsekor:
void my_cleanup();
void somewhere()
{
if (atexit(&my_cleanup)==0) {
// norml programbefejezskor a my_cleanup hvdik meg
}
else {
// hopp: tl sok atexit fggvny
}
}
Ez nagyban hasonlt a globlis vltozk destruktorainak a program befejezdsekor trt-
n automatikus meghvshoz (10.4.9, 10.2.4). Jegyezzk meg, hogy az atexit() param-
ternek nem lehet paramtere s nem adhat vissza rtket. Az atexit fggvnyek szmt az
adott nyelvi vltozat korltozza; a fggvny nem nulla rtk visszaadsval jelzi, ha ezt
a korltot elrtk. Ezek a korltozsok az atexit()-et kevsb hasznlhatv teszik, mint
amilyennek els pillantsra ltszik.
Az atexit(f) meghvsa eltt ltrehozott objektum destruktora az f meghvsa utn fog meg-
hvdni, az atexit(f) meghvsa utn ltrehozott objektum destruktora pedig az f meghv-
sa eltt.
Az exit(), abort(), s atexit() fggvnyek deklarcijt a <cstdlib> fejllomny tartalmazza.
Alapok 290
9.5. Tancsok
[1] Hasznljuk fejllomnyokat a felletek brzolsra s a logikai szerkezet
kihangslyozsra. 9.1, 9.3.2.
[2] Abban a forrsfjlban ptsk be ket (#include), amelyben fggvnyeiket
kifejtjk. 9.3.1.
[3] Ne adjunk meg globlis egyedeket ugyanazzal a nvvel s hasonl, de klnb-
z jelentssel klnbz fordtsi egysgekben. 9.2.
[4] Kerljk a fejllomnyokban a nem helyben kifejtend fggvnyeket. 9.2.1.
[5] Csak globlis hatkrben s nvterekben hasznljuk az #include-ot. 9.2.1.
[6] Csak teljes deklarcikat ptsnk be. 9.2.1
[7] Hasznljunk llomny-rszemeket. 9.3.3.
[8] A C fejllomnyokat nvterekben ptsk be, hogy elkerljk a globlis neve-
ket. 9.3.2.
[9] Tegyk a fejllomnyokat klnllv. 9.2.3.
[10] Klnbztessk meg a fejleszti s a felhasznli felletet. 9.3.2.
[11] Klnbztessk meg az tlagos s a tapasztalt felhasznlk fellett. 9.3.2.
[12] Kerljk az olyan nem loklis objektumok hasznlatt, amelyek futsi idej kez-
deti rtkadst ignyelnek olyan kdban, amit nem C++ program rszeknt
szndkozunk felhasznlni. 9.4.1.
9.6. Gyakorlatok
1. (*2) Talljuk meg, hol trolja rendszernk a szabvnyos fejllomnyokat. ras-
suk ki neveiket. Van-e olyan nem szabvnyos fejllomny, amely ezekkel egytt
troldik? Be lehet-e pteni nem szabvnyos fejllomnyokat a <> jellst
hasznlva?
2. (*2) Hol troldnak a nem szabvnyos foundation knyvtrak fejllomnyai?
3. (*2,5) rjunk programot, amely beolvas egy forrsfjlt s kirja a beptett fjlok
neveit. Hasznljunk behzst a beptett fjlok ltal beptett fjlok kirsakor,
a befoglals mlysgnek jellsre. Prbljuk ki a programot nhny valdi
forrsfjlon (hogy elkpzelsnk legyen a beptett informci nagysgrl).
4. (*3) Mdostsuk az elbbi programot, hogy minden beptett fjlra kirja a meg-
jegyzsek s a nem megjegyzsek sorainak szmt, illetve a nem megjegy-
zsknt szerepl, reshelyekkel elvlasztott szavak szmt.
9. Forrsfjlok s programok 291
5. (*2,5) A kls beptsfigyel olyan programelem, amely a megfigyelt fjlon k-
vl vgzi az ellenrzst, s fordtsonknt csak egyszer vgez beptst. Kszt-
snk egy ilyen szerkezeti elemet, tervezznk mdszert a tesztelsre, s fejtsk
ki elnyeit s htrnyait a 9.3.3-ban lert llomny-rszemekkel szemben.
Van-e a kls beptsfigyelknek brmilyen jelents futsi idbeli elnye
a rendszernkben?
6. (*3) Hogyan valsul meg a dinamikus csatols (szerkeszts) a rendszernkben?
Milyen megszortsok vonatkoznak a dinamikusan szerkesztett kdra? Milyen
kvetelmnyeknek kell, hogy megfeleljen a kd, hogy dinamikusan csatolhat
legyen?
7. (*3) Nyissunk meg s olvassunk be 100 fjlt, melyek mindegyike 1500 karaktert
tartalmaz. Nyissunk meg s olvassunk be egy 150 000 karakterbl ll fjlt.
Tipp: nzzk meg a pldt a 21.5.1 pontban. Van-e eltrs a teljestmnyben?
Hny fjl lehet egyszerre megnyitva rendszernkben? Vlaszoljuk meg ezeket
a krdseket a beptett fjlok hasznlatval kapcsolatban is.
8. (*2) Mdostsuk a szmolgpet, hogy meg lehessen hvni a main()-bl vagy
ms fggvnybl is, egy egyszer fggvnyhvssal.
9. (*2) Rajzoljuk meg a modulfggsgi diagramokat (9.3.2) a szmolgp azon
vltozataira, melyek az error()-t hasznltk kivtelek helyett. (8.2.2).
Alapok 292
Msodik rsz
Absztrakcis mdszerek
Ebben a rszben azzal foglalkozunk, milyen lehetsgeket nyjt a C++ nyelv j tpusok
meghatrozsra s hasznlatra, illetve bemutatjuk az sszefoglal nven objektumorien-
tlt programozsnak s ltalnostott (generikus) programozsnak nevezett eljrsokat.
Fejezetek
10. Osztlyok
11. Opertorok tlterhelse
12. Szrmaztatott osztlyok
13. Sablonok
14. Kivtelkezels
15. Osztlyhierarchik
... nincs nehezebb, ktesebb kimenetel, veszlyesebb dolog, mint j trvnyek bevezet-
srt skraszllni. Mert ellensgei azok, akiknek a rgi trvnyek hasznra vannak, azok pe-
dig, akiknek az j rendelkezsek szolglnak hasznukra, pusztn lagymatag vdelmezi...
Niccolo Machiavelli
(A fejedelem (vi), Lutter va fordtsa)
Osztlyok
Ezek a tpusok nem elvontak;
ugyanannyira valsgosak,
mint az int s a float.
(Doug McIlroy)
Fogalmak s osztlyok Osztlytagok Az elrhetsg szablyozsa Konstruktorok Sta-
tikus tagok Alaprtelmezett msols const tagfggvnyek this struct-ok Osztlyon
belli fggvnydefincik Konkrt osztlyok Tagfggvnyek s segdfggvnyek Ope-
rtorok tlterhelse A konkrt osztlyok hasznlata Destruktorok Alaprtelmezett
konstruktorok Loklis vltozk Felhasznli msols new s delete Tagobjektu-
mok Tmbk Statikus trols Ideiglenes vltozk Unik Tancsok Gyakorlatok
10.1. Bevezets
A C++ nyelv osztlyai azt a clt szolgljk, hogy a programoz a beptett adattpusokkal
azonos knyelmi szinten hasznlhat j adattpusokat hozhasson ltre. Ezenkvl az rk-
lds (12. fejezet) s a sablonok (13. fejezet) segtsgvel gy szervezhetjk az egymssal
kapcsolatban ll osztlyokat, hogy kapcsolataikat hatkonyan hasznlhassuk ki.
10
A tpus egy fogalom konkrt brzolsa. A C++ beptett float tpusa pldul a +, -, * stb.
mveleteivel egytt a vals szm matematikai fogalmnak egy megkzeltse. Az osztly
egy felhasznli tpus. Azrt terveznk j tpust, hogy meghatrozzunk egy fogalmat,
amelynek nincs kzvetlen megfelelje a nyelv beptett tpusai kztt. Lehet pldul
Trunk_line tpusunk egy telefonos kapcsolatokat kezel programban, Explosion tpusunk
egy videjtk szmra, vagy list<Paragraph> tpusunk egy szvegszerkeszt programban.
Egy programot knnyebb megrteni s mdostani, ha abban az ltala kezelt fogalmaknak
megfelel tpusok szerepelnek. Ha a programoz alkalmas osztlyokat hasznl, a program
tmrebb lesz, radsul sokfle kdelemz eljrs hasznlata vlik lehetv. A fordtprog-
ram pldul feldertheti az objektumok nem szablyos hasznlatt, amit msknt csak egy
alapos ellenrzs sorn fedezhetnnk fel.
j tpus definilsakor az alapvet szempont a megvalsts vletlenszer, esetleges rsz-
leteinek (pldul a troland adatok elrendezsnek) elvlasztsa a tpus helyes hasznla-
thoz alapveten szksges tulajdonsgoktl, pldul az adatokat elr fggvnyek teljes
listjtl. Ez az elvlaszts legjobban gy fejezhet ki, ha az adott tpus adatszerkezett rin-
t sszes kls hasznlatot s bels rendrak fggvnyt csak az adott tpusra vonatkoz
programozsi felleten keresztl tesszk elrhetv. Ez a fejezet a viszonylag egyszer,
konkrt felhasznli tpusokkal foglalkozik. Idelis esetben ezek csak ltrehozsuk md-
jukban klnbznek a beptett tpusoktl, a hasznlat mdjban nem.
10.2. Osztlyok
Az osztly (class) a programoz ltal meghatrozott, ms nven felhasznli tpus. Az alb-
biakban az osztlyok meghatrozsnak, illetve az osztlyba tartoz objektumok ltrehoz-
snak s hasznlatnak fbb eszkzeit mutatjuk be.
10.2.1. Tagfggvnyek
Vizsgljuk meg, hogyan brzolnnk a dtum fogalmt egy Date adatszerkezettel (struk-
trval, struct) s egy sor, ilyen vltozkat kezel fggvnnyel:
struct Date { // brzols
int d, m, y;
};
Absztrakcis mdszerek 296
void init_date(Date& d, int, int, int); // kezdeti rtkads d-nek
void add_year(Date& d, int n); // n vet ad d-hez
void add_month(Date& d, int n); // n hnapot ad d-hez
void add_day(Date& d, int n); // n napot ad d-hez
Az adattpus s ezen fggvnyek kztt nincs kifejezett kapcsolat. Ilyen kapcsolatot azltal
hozhatunk ltre, hogy a fggvnyeket tagfggvnyekknt adjuk meg:
struct Date {
int d, m, y;
void init(int dd, int mm, int yy); // kezdeti rtkads
void add_year(int n); // n v hozzadsa
void add_month(int n); // n hnap hozzadsa
void add_day(int n); // n nap hozzadsa
};
Az osztlydefincin bell bevezetett fggvnyeket (a struct is osztly, 10.2.8) tagfggv-
nyeknek hvjuk. A tagfggvnyeket az adatszerkezetek tagjainak elrsre vonatkoz szo-
ksos formban alkalmazhatjuk s csak megfelel tpus objektumra:
Date my_birthday;
void f()
{
Date today;
today.init(16,10,1996);
my_birthday.init(30,12,1950);
Date tomorrow = today;
tomorrow.add_day(1);
// ...
}
Minthogy a klnbz adatszerkezeteknek azonos nev fggvnyeik is lehetnek, a tag-
fggvnyek meghatrozsakor meg kell adnunk az adatszerkezet nevt is:
void Date::init(int dd, int mm, int yy)
{
d = dd;
m = mm;
y = yy;
}
10. Osztlyok 297
A tagfggvnyekben a tagokat az objektum kifejezett megadsa nlkl is hasznlhatjuk. Ek-
kor a nv azon objektum megfelel tagjra vonatkozik, amelyre a tagfggvnyt meghvtuk.
Amikor pldul a Date::init() tagfggvnyt alkalmazzuk a today vltozra, az m=mmrtk-
ads a today.m vltozra vonatkozik. Ha ugyanezt a tagfggvnyt a my_birthday vltoz-
ra alkalmaznnk, az m=mm rtkads a my_birthday.m vltozra vonatkozna. A tagfgg-
vny mindig tudja, hogy milyen objektumra hvtk meg.
A
class X { ... };
kifejezst osztlydefincinak hvjuk, mert egy j tpust hatroz meg. Trtneti okokbl az
osztlydefincit nha osztlydeklarciknt emltik. Azon deklarcikhoz hasonlatosan,
amelyek nem defincik, az osztlydefincik az #include utasts felhasznlsval tbb for-
rsllomnyban is szerepeltethetk, feltve, hogy nem srtjk meg az egyszeri definils
szablyt (9.2.3).
10.2.2. Az elrhetsg szablyozsa
A Date elz pontbeli deklarcija azon fggvnyek halmazt adja meg, melyekkel a Date
tpus objektumot kezelhetjk. Ebbl azonban nem derl ki, hogy kizrlag ezek lehetnek
mindazok a fggvnyek, amelyek kzvetlenl fggnek a Date tpus brzolstl s kz-
vetlenl elrhetik az ilyen tpus objektumokat. A megszortst gy fejezhetjk ki, ha struct
helyett class-t hasznlunk:
class Date {
int d, m, y;
public:
void init(int dd, int mm, int yy); // kezdeti rtkads
void add_year(int n); // n v hozzadsa
void add_month(int n); // n hnap hozzadsa
void add_day(int n); // n nap hozzadsa
};
A public cmke kt rszre osztja az osztly trzst. Az els, privt (private) rszbeli neveket
csak a tagfggvnyek hasznlhatjk. A msodik, nyilvnos (public) rsz az osztly nyilv-
nos fellete. A struct szerkezetek egyszeren olyan osztlyok, melyekben a tagok alapr-
telmezett elrhetsge nyilvnos (10.2.8). A tagfggvnyeket a megszokott mdon
definilhatjuk s hasznlhatjuk:
Absztrakcis mdszerek 298
inline void Date::add_year(int n)
{
y += n;
}
Mindazonltal a nem tag fggvnyek a privt tagokat nem hasznlhatjk:
void timewarp(Date& d)
{
d.y -= 200; // hiba: Date::y privt
}
Szmos elnnyel jr, ha a tagok elrhetsgt egy pontosan megadott lista fggvnyeire
korltozzuk. Pldul, ha egy hiba miatt a Date rvnytelen rtket kap (mondjuk 1985. de-
cember 36.-t), akkor biztosak lehetnk abban, hogy ez a hiba csak valamelyik tagfgg-
vnyben lehet. Ebbl kvetkezik, hogy a hibakeress els szakasza, a hiba helynek beha-
trolsa mr azeltt megtrtnik, hogy a program egyltaln lefutna. Ez egyik esete annak
az ltalnos megfigyelsnek, hogy az osztly viselkedsnek brmilyen mdostsa csakis
a tagfggvnyek megvltoztatsval rhet el. Pldul ha megvltoztatjuk egy osztly adat-
brzolst, akkor elg a tagfggvnyeket ennek megfelelen mdostanunk. Az osztlyt
hasznl kd kzvetlenl csak az osztly nyilvnos fellettl fgg, ezrt nem kell jrarni
(br lehet, hogy jra kell fordtani). A msik elny, hogy a leend felhasznlnak elg a tag-
fggvnyek meghatrozst tanulmnyoznia ahhoz, hogy megtudja, hogyan lehet hasznl-
ni az osztlyt.
A privt tagok vdelme az osztlytagok nv szerinti elrhetsgnek korltozsn mlik,
ezrt a cmek megfelel kezelsvel vagy pontosan meghatrozott tpuskonverzival
megkerlhet. Ez persze csals. A C++ a vletlen hibk ellen vd, nem a vdelmi rendszer
tudatos megkerlse, a csals ellen. Egy ltalnos cl nyelvben csak hardverszinten lehet-
ne a rosszindulat hasznlat ellen vdekezni, s igazi rendszerekben mg ez is nehezen ki-
vitelezhet.
Az init() fggvnyt rszben azrt vettk fel, mert ltalban clszer, ha van egy, az objek-
tumnak rtket ad fggvnynk, rszben pedig azrt, mert az adattagok privtt ttele mi-
att erre knyszerltnk.
10.2.3. Konstruktorok
Az init()-hez hasonl fggvnyek hasznlata az objektumok kezdeti rtkadsra nem ele-
gns s hibk forrsa lehet. Minthogy sehol sincs lefektetve, hogy egy objektumnak kezd-
rtket kell adni, a programoz elfelejtheti azt vagy ppen tbbszr is megteheti (mind-
10. Osztlyok 299
kt esetben egyformn vgzetes kvetkezmnyekkel). Jobb megolds, ha lehetv tesszk
a programoznak, hogy megadjon egy olyan fggvnyt, melynek clja kifejezetten az ob-
jektumok elksztse. Mivel az ilyen fggvny ltrehozza az adott tpus rtkeket,
konstruktornak (vagyis ltrehoznak, constructor) hvjuk. A konstruktort arrl ismerjk
meg, hogy ugyanaz a neve, mint magnak az osztlynak:
class Date {
// ...
Date(int, int, int); // konstruktor
};
Ha egy osztly rendelkezik konstruktorral, akkor minden, ebbe az osztlyba tartoz objek-
tum kap kezdrtket. Ha a konstruktornak paramterekre van szksge, azokat meg kell
adni:
Date today = Date(23,6,1983);
Date xmas(25,12,1990); // rvidtett forma
Date my_birthday; // hiba: nincs kezdrtk
Date release1_0(10,12); // hiba: a harmadik paramter hinyzik
Gyakran clszer, ha a kezdeti rtkads tbbflekppen is lehetsges. Ezt gy rhetjk el,
ha tbbfle konstruktor ll rendelkezsre:
class Date {
int d, m, y;
public:
// ...
Date(int, int, int); // nap, hnap, nap
Date(int, int); // nap, hnap, aktulis v
Date(int); // nap, aktulis hnap s v
Date(); // alaprtelmezett Date: mai dtum
Date(const char*); // a dtum karakterlnccal brzolva
};
A konstruktorokra ugyanazok a tlterhelsi szablyok vonatkoznak, mint ms fggvnyek-
re (7.4). Amg a konstruktorok kellen klnbznek a paramterek tpusaiban, a fordt-
program ki fogja tudni vlasztani, melyiket kell az egyes meghvsokkor alkalmazni:
Date today(4);
Date july4("July 4, 1983");
Date guy("5 Nov");
Date now; // alaprtelmezett kezdeti rtkads az aktulis dtummal
A Date plda esetben megfigyelhetjk a konstuktorok elburjnzst, ami ltalnos jelen-
sg. A programoz egy osztly tervezsekor mindig ksrtst rez j s j fggvnyekkel
bvteni azt, mondvn, valakinek gyis szksge lesz rjuk. Tbb gondot ignyel ugyanis
Absztrakcis mdszerek 300
mrlegelni, mire van igazn szksg s arra szortkozni. Ez az odafigyels ugyanakkor lta-
lban kisebb s rthetbb programokhoz vezet. Az egymssal rokon fggvnyek szmnak
cskkentsre az egyik md az alaprtelmezett paramter-rtkek hasznlata (7.5). A Date
osztly paramtereinek pldul egy olyan alaprtelmezett rtket adhatunk, melynek jelen-
tse: vegyk az alaprtelmezett today-t.
class Date {
int d, m, y;
public:
Date(int dd =0, int mm =0, int yy =0);
// ...
};
Date::Date(int dd, int mm, int yy)
{
d = dd ? dd : today.d;
m = mm ? mm : today.m;
y = yy ? yy : today.y;
// ellenrizzk, hogy Date rvnyes dtum-e
}
Ha egy paramter-rtket az alaprtelmezett rtk jelzsre hasznlunk, annak kvl kell es-
nie a lehetsges rtkek halmazn. A day (nap) s month (hnap) paramterek esetben ez
a halmaz vilgosan meghatrozhat, a year (v) meznl azonban a zr rtk nem esik
nyilvnvalan a halmazon kvlre. Szerencsre az eurpai naptrban nincs 0-dik v; az id-
szmtsunk utni els v (year==1) kzvetlenl az idszmtsunk eltti els v (year==-1)
utn kvetkezik.
10.2.4. Statikus tagok
A Date tpushoz tartoz knyelmes alaprtelmezett rtket egy jelents rejtett problma
rn hoztuk ltre: Date osztlyunk a today nev globlis vltoztl fgg. gy az osztly csak
akkor hasznlhat, ha a today vltozt definiltuk s minden kdrszletben megfelelen
hasznljuk. Ez a fajta megszorts az osztlyt az eredeti krnyezeten kvl hasznlhatatlan-
n teszi. A felhasznlknak tl sok kellemetlen meglepetsben lesz rszk, amikor ilyen
krnyezetfgg osztlyokat prblnak hasznlni s a kd mdostsa is problematikus lesz.
Ezzel az egy kis globlis vltozval mg taln megbirkzunk, de ez a stlus vezet az ere-
deti programozn kvl ms szmra hasznlhatatlan kdhoz. Kerljk el!
Szerencsre a kvnt clt elrhetjk a nyilvnosan elrhet globlis vltoz jelentette teher-
ttel nlkl is. Az olyan vltozkat, melyek egy osztlyhoz tartoznak, de annak objektu-
maihoz nem, statikus tagnak nevezzk. A statikus tagokbl mindig pontosan egy pldny
10. Osztlyok 301
ltezik, nem pedig objektumonknt egy, mint a kznsges, nem statikus adattagokbl. Eh-
hez hasonlan az olyan fggvnyeket, melyek egy adott osztlytaghoz hozzfrnek, de
nem szksges objektumra meghvni azokat, statikus tagfggvnynek hvjuk. Tervezzk t
az osztlyt gy, hogy megrizzk az alaprtelmezett konstruktor-rtkek szerept, de kz-
ben elkerljk a globlis vltoz hasznlatnak htrnyt:
class Date {
int d, m, y;
static Date default_date;
public:
Date(int dd =0, int mm =0, int yy =0);
// ...
static void set_default(int, int, int);
};
A Date konstruktort immr gy hatrozhatjuk meg:
Date::Date(int dd, int mm, int yy)
{
d = dd ? dd : default_date.d;
m = mm ? mm : default_date.m;
y = yy ? yy : default_date.y;
// ellenrizzk, hogy Date rvnyes dtum-e
}
Amikor szksges, mdosthatjuk az alaprtelmezett rtket. Egy statikus tagra ugyangy
hivatkozhatunk, mint brmilyen ms tagra, st, akr egy objektum megnevezse nlkl is;
ekkor az osztly nevvel minsthetjk:
void f()
{
Date::set_default(4,5,1945);
}
A statikus tagokat mind az adattagokat, mind a fggvnyeket definilni kell valahol:
Date Date::default_date(16,12,1770);
void Date::set_default(int d, int m, int y)
{
Date::default_date = Date(d,m,y);
}
Absztrakcis mdszerek 302
Az alaprtelmezett rtk itt Beethoven szletsi dtuma, amg valaki t nem lltja valami
msra. Vegyk szre, hogy a Date() jells a Date::default_date rtket szolgltatja:
Date copy_of_default_date = Date();
Kvetkezskppen nincs szksg kln fggvnyre az alaprtelmezett dtum lekrdezshez.
10.2.5. Osztly tpus objektumok msolsa
Alaprtelmezs szerint az osztly tpus objektumok msolhatk s kezdrtkknt egy
azonos tpus osztly egy objektumnak msolatt is kaphatjk, mg akkor is, ha konstruk-
torokat is megadtunk:
Date d = today; // kezdeti rtkads msolssal
Alaprtelmezs szerint az osztly objektum msolata minden tag msolatbl ll. Ha nem
ez a megfelel viselkeds egy X osztly szmra, az X::X(const X&) msol konstruktorral
megvltoztathatjuk azt. (Erre a 10.4.4.1 pontban rszletesebben is visszatrnk.) Ennek
megfelelen az osztly objektumokat alaprtelmezs szerint rtkadssal is msolhatjuk:
void f(Date& d)
{
d = today;
}
Az alaprtelmezett viselkeds itt is a tagonknti msols. Ha ez nem megfelel egy osztly
szmra, a programoz megadhatja a megfelel rtkad opertort (10.4.4.1).
10.2.6. Konstans tagfggvnyek
A Date osztlyhoz eddig olyan tagfggvnyeket adtunk, melyek rtket adnak egy Date
objektumnak vagy megvltoztatjk azt, de az rtk lekrdezsre sajnos nem adtunk lehe-
tsget. Ezen knnyen segthetnk, ha ksztnk nhny fggvnyt, amelyekkel kiolvas-
hatjuk az vet, a hnapot s a napot:
class Date {
int d, m, y;
public:
int day() const { return d; }
int month() const { return m; }
int year() const;
// ...
};
10. Osztlyok 303
Vegyk szre a const minstt a fggvnydeklarcikban az (res) paramterlista utn. Ez
azt jelenti, hogy ezek a fggvnyek nem vltoztatjk meg az objektum llapott. Term-
szetesen a fordtprogram megakadlyozza, hogy vletlenl megszegjk ezt az gretet:
inline int Date::year() const
{
return y++; // hiba: ksrlet tag rtknek mdostsra konstans fggvnyben
}
Ha egy konstans tagfggvnyt osztlyn kvl hatrozzuk meg, a const uttagot ki kell
rnunk:
inline int Date::year() const // helyes
{
return y;
}
inline int Date::year() // hiba: a const minst hinyzik a tagfggvny tpusbl
{
return y;
}
Vagyis a const minsts rsze a Date::day() s Date::year() fggvnyek tpusnak.
Egy konstans tagfggvnyt alkalmazhatunk lland (konstans) s vltoz (nem konstans) ob-
jektumokra is, a nem konstans tagfggvnyeket viszont csak nem konstans objektumokra:
void f(Date& d, const Date& cd)
{
int i = d.year(); // rendben
d.add_year(1); // rendben
int j = cd.year(); // rendben
cd.add_year(1); // hiba: cd konstans, rtke nem mdosthat
}
10.2.7. nhivatkozs
Az add_year(), add_month(), s add_year() llapotfrisst fggvnyeket gy hatroztuk
meg, hogy azok nem adnak vissza rtket. Az ilyen, egymssal kapcsolatban lev frisst
fggvnyek esetben sokszor hasznos, ha visszaadunk egy, a frisstett objektumra mutat
referencit, mert a mveleteket ekkor lncba kapcsolhatjuk (lncolhatjuk).
Absztrakcis mdszerek 304
Tegyk fel, hogy a kvetkezt szeretnnk rni:
void f(Date& d)
{
// ...
d.add_day(1).add_month(1).add_year(1);
// ...
}
Ezzel egy napot, egy hnapot s egy vet adunk d-hez. Ehhez viszont minden fggvnyt
gy kell megadnunk, hogy azok egy Date tpus referencit adjanak vissza:
class Date {
// ...
Date& add_year(int n); // n v hozzadsa
Date& add_month(int n); // n hnap hozzadsa
Date& add_day(int n); // n nap hozzadsa
};
Minden (nem statikus) tagfggvny tudja, melyik objektumra hvtk meg, gy pontosan hi-
vatkozhat r:
Date& Date::add_year(int n)
{
if (d==29 && m==2 && !leapyear(y+n)) { // figyeljnk februr 29-re!
d = 1;
m = 3;
}
y += n;
return *this;
}
A *this kifejezs azt az objektumot jelenti, amelyre a tagfggvnyt meghvtk. (Egyenrt-
k a Simula nyelv THIS s a Smalltalk self kifejezsvel.)
Egy nem statikus tagfggvnyben a this kulcssz egy mutatt jelent arra az objektumra,
amelyre a fggvnyt meghvtk. Az X osztly egy nem const tagfggvnyben a this tpusa
X*. Mindazonltal a this nem kznsges vltoz, gy nem lehet a cmt felhasznlni vagy
rtket adni neki. Az X osztly egy konstans tagfggvnyben a this tpusa const X* lesz,
hogy ne lehessen megvltoztatni magt az objektumot (lsd mg 5.4.1).
10. Osztlyok 305
A this hasznlata legtbbszr automatikus. Pldul minden nem statikus tagra val hivatko-
zs tulajdonkppen a this-t hasznlja, hogy a megfelel objektum tagjt rje el. Az add_year
fggvnyt pldul egyenrtk, m fradsgos mdon gy is megadhattuk volna:
Date& Date::add_year(int n)
{
if (this->d==29 && this->m==2 && !leapyear(this->y+n)) {
this->d = 1;
this->m = 3;
}
this->y += n;
return *this;
}
A this-t meghatrozott (explicit) mdon gyakran lncolt listk kezelsre hasznljuk (pl-
dul 24.3.7.4).
10.2.7.1. Fizikai s logikai konstansok
Esetenknt elfordulhat, hogy egy tagfggvny logikailag lland, mgis meg kell vltoz-
tatnia egy tag rtkt. A felhasznl szmra a fggvny nem mdostja az objektum lla-
pott, de valamilyen, a felhasznl ltal kzvetlenl nem lthat rszlet megvltozik.
Az ilyen helyzetet gyakran hvjk logikai konstans mivoltnak. A Date osztlyt pldul egy
fggvny visszatrsi rtke egy karakterlnccal brzolhatja, melyet a felhasznl a kime-
netben felhasznlhat. Egy ilyen brzols felptse idignyes feladat, ezrt rdemes egy
pldnyt trolni belle, amit az egymst kvet lekrdezsek mind felhasznlhatnak, amg
a Date rtke meg nem vltozik. Ilyen bels gyorsttr (gyorstr, cache) inkbb bonyolul-
tabb adatszerkezeteknl hasznlatos, de nzzk meg, hogyan mkdhetne ez a Date osz-
tly esetben:
class Date {
bool cache_valid;
string cache;
void compute_cache_value(); // gyorstr feltltse
// ...
public:
// ...
string string_rep() const; // brzols karakterlnccal
};
Absztrakcis mdszerek 306
A felhasznl szemszgbl a string_rep fggvny nem vltoztatja meg az objektum lla-
pott, ezrt vilgos, hogy konstans tagfggvnynek kell lennie. Msrszt a gyorsttrat fel
kell tlteni a hasznlat eltt. Ezt elrhetjk tpusknyszerts alkalmazsval is:
string Date::string_rep() const
{
if (cache_valid == false) {
Date* th = const_cast<Date*>(this); // konstans elvetse
th->compute_cache_value();
th->cache_valid = true;
}
return cache;
}
Vagyis a const_cast opertort (15.4.2.1) hasznltuk, hogy egy Date* tpus mutatt kap-
junk a this-re. Ez aligha elegns megolds, s nem biztos, hogy egy eredetileg is lland-
knt megadott objektum esetben is mkdik:
Date d1;
const Date d2;
string s1 = d1.string_rep();
string s2 = d2.string_rep(); // nem meghatrozott viselkeds
A d1 vltoz esetben a string_rep() egyszeren az eredeti tpusra alakt vissza, gy a dolog
mkdik. m d2-t konstansknt adtuk meg s az adott nyelvi vltozat esetleg valamilyen
memria-vdelmet alkalmaz az lland rtkek megrzsre. Ezrt a d2.string_rep() hvs
nem biztos, hogy pontosan meghatrozhat, az adott nyelvi vltozattl fggetlen ered-
mnnyel fog jrni.
10.2.7.2. A mutable minst
Az elbb lert tpusknyszerts (a const minst talaktsa) s a vele jr, megvalststl
fgg viselkeds elkerlhet, ha a gyorsttrba kerl adatokat vltozkony-knt
(mutable) adjuk meg:
class Date {
mutable bool cache_valid;
mutable string cache;
void compute_cache_value() const; // (vltozkony) gyorstr feltltse
// ...
public:
// ...
string string_rep() const; // brzols karakterlnccal
};
10. Osztlyok 307
A mutable trolsi minsts azt jelenti, hogy a tagot gy kell trolni, hogy akkor is mdo-
sthat legyen, ha konstans objektum. Vagyis a mutable azt jelenti, hogy soha nem llan-
d. Ezt felhasznlva egyszersthetnk a string_rep() meghatrozsn:
string Date::string_rep() const
{
if (!cache_valid) {
compute_cache_value();
cache_valid = true;
}
return cache;
}
Ezltal a string_rep()-et megfelelen hasznlatba vehetjk:
Date d3;
const Date d4;
string s3 = d3.string_rep();
string s4 = d4.string_rep(); // rendben!
A tagok vltozkonyknt val megadsa akkor alkalmas leginkbb, ha az brzolsnak csak
egy rsze vltozhat. Ha az objektum logikailag vltozatlan marad, de a tagok tbbsge m-
dosulhat, jobb a vltoz adatrszt kln objektumba tenni s kzvetett ton elrni. Ezzel
a mdszerrel a gyorsttrba helyezett karakterlncot tartalmaz program gy rhat meg:
struct cache {
bool valid;
string rep;
};
class Date {
cache* c; // kezdeti rtkads a konstruktorban (10.4.6)
void compute_cache_value() const; // a gyorstr ltal mutatott elem feltltse
// ...
public:
// ...
string string_rep() const; // brzols karakterlnccal
};
string Date::string_rep() const
{
if (!c->valid) {
compute_cache_value();
c->valid = true;
}
return c->rep;
}
Absztrakcis mdszerek 308
A gyorsttrat tmogat eljrsok az n. lusta vagy takaros kirtkels (lazy evaluation)
klnfle formira is tvihetk.
10.2.8. Struktrk s osztlyok
Definci szerint a struktra (struct), olyan osztly, melynek tagjai alaprtelmezs szerint
nyilvnosak. Vagyis a
struct s { ...
egyszeren rvidtse az albbinak:
class s { public: ...
A private: elrhetsgi minsts annak jelzsre hasznlhat, hogy a kvetkez tagok pri-
vt elrsek, a public: pedig azt mondja, hogy a kvetkez tagok nyilvnosak. Attl elte-
kintve, hogy a nevek klnbznek, az albbi deklarcik egyenrtkek:
class Date1 {
int d, m, y;
public:
Date1(int dd, int mm, int yy);
void add_year(int n); // n v hozzadsa
};
struct Date2 {
private:
int d, m, y;
public:
Date2(int dd, int mm, int yy);
void add_year(int n); // n v hozzadsa
};
A vlasztott stlust csak a krlmnyek s az egyni zls hatrozza meg. n ltalban
azokat az osztlyokat adom meg struct-knt, amelyekben minden tag nyilvnos. Ezekre az
osztlyokra gy gondolok, mint amik nem igazi tpusok, csak adatszerkezetek. A kon-
struktorok s lekrdez fggvnyek nagyon hasznosak lehetnek a struktrk szmra is, de
inkbb csak jellsbeli knnyebbsget jelentenek, mintsem a tpus tulajdonsgait garantl-
jk (mint az invarinsok, lsd 24.3.7.1).
10. Osztlyok 309
Az osztlyokban nem szksges elszr az adattagokat megadni, st, sokszor jobb azokat
a deklarci vgre tenni, hogy kihangslyozzuk a nyilvnos felhasznli felletet alkot
fggvnyeket:
class Date3 {
public:
Date3(int dd, int mm, int yy);
void add_year(int n); // n v hozzadsa
private:
int d, m, y;
};
Valdi kdban, ahol ltalban mind a nyilvnos fellet, mind a tnyleges megvalsts terje-
delmesebb, mint a tanknyvi pldkban, rendszerint a Date3 stlust rszestem elnyben.
Az elrhetsgi minstseket az osztlydeklarcikon bell tbbszr is hasznlhatjuk:
class Date4 {
public:
Date4(int dd, int mm, int yy);
private:
int d, m, y;
public:
void add_year(int n); // n v hozzadsa
};
Ha azonban a deklarci tbb nyilvnos rszt is tartalmaz (mint a Date4 osztlynl), akkor
a kd zavaross vlhat. Tbb privt rsz hasznlata szintn ezt eredmnyezi. Mindazonl-
tal a szmtgp ltal elksztett kdok szmra kedvez, hogy az elrhetsgi minstsek
ismtldhetnek.
10.2.9. Osztlyon belli fggvnydefincik
Az osztlyon bell definilt (nem csak deklarlt) fggvnyek helyben kifejtett (inline) tag-
fggvnynek szmtanak, azaz a fordtprogram a fggvny meghvsa helyett kzvetlenl
beilleszti a kdot. Vagyis az osztly meghatrozsn belli kifejts kicsi, de gyakran hasz-
nlt fggvnyek szmra hasznos. Ahhoz az osztly-definicihoz hasonlan, amelyben sze-
repel, az osztlyon bell kifejtett fggvny is szerepelhet tbb fordtsi egysgben (az
#include utastssal beptve). Persze az osztlyhoz hasonlan jelentsnek minden fel-
hasznlsakor azonosnak kell lennie (9.2.3).
Absztrakcis mdszerek 310
Az a stlus, mely szerint az adattagokat az osztly definicijnak vgre helyezzk, kisebb
gondhoz vezet az adatbrzolst felhasznl nyilvnos inline fggvnyek tekintetben.
Vegyk ezt a pldt:
class Date { // zavar lehet
public:
int day() const { return d; } // return Date::d
// ...
private:
int d, m, y;
};
Ez szablyos C++-kd, mivel egy osztly egy tagfggvnye az osztly minden tagjra hivat-
kozhat, mintha az osztly definicija mr a tagfggvny-trzsek beolvassa eltt teljes lett
volna. A kdot olvas programozt azonban ez megzavarhatja, ezrt n vagy elreveszem
az adatokat, vagy az inline tagfggvnyeket az osztly utn fejtem ki:
class Date {
public:
int day() const;
// ...
private:
int d, m, y;
};
inline int Date::day() const { return d; }
10.3. Hatkony felhasznli tpusok
Az elz Date osztly pldjn bemutattuk az osztlyok meghatrozshoz szksges alap-
vet nyelvi elemeket. Most az egyszer s hatkony tervezsre helyezzk a hangslyt s azt
mutatjuk be, hogy az egyes nyelvi elemek hogyan tmogatjk ezt.
Szmos program hasznl egyszer, de srn elfordul elvont fogalmakat, konkrt tpu-
sokkal brzolva: latin vagy knai karaktereket, lebegpontos szmokat, komplex sz-
mokat, pontokat, mutatkat, koordintkat, (mutateltols (offset)) prokat, dtumokat,
idpontokat, rtkkszleteket, kapcsolatokat, csompontokat, (rtkegysg) prokat, le-
mezcmeket, forrskd-helyeket, BCD karaktereket, pnznemeket, vonalakat, tglalapokat,
rgztett pontos szmokat, trtrsszel br szmokat, karakterlncokat, vektorokat s tm-
bket. Gyakran elfordul, hogy egy program kzvetetten tmaszkodik ezen tpusok n-
melyikre s mg tbbre kzvetlenl, knyvtrak kzvettsvel.
10. Osztlyok 311
A C++ ms programozsi nyelvekkel egyetemben kzvetlenl tmogat nhnyat a fenti t-
pusok kzl, m szmuk miatt nem lehetsges az sszeset kzvetlenl tmogatni. Egy lta-
lnos cl programozsi nyelv tervezje nem is lthatja elre az egyes alkalmazsok ig-
nyeit. Teht szksg van olyan eljrsokra, melyekkel a felhasznl adott cl tpusokat
adhat meg. Az ilyen tpusokat konkrt tpusoknak vagy konkrt osztlyoknak hvjuk, hogy
megklnbztessk ket az absztrakt (elvont) osztlyoktl (12.3), illetve az osztlyhierar-
chik osztlyaitl (12.2.4 s 12.4).
A C++ nyelv egyik kifejezett clja volt, hogy az ilyen felhasznli tpusok megadst s ha-
tkony hasznlatt is tmogassa, mert ezek az elegns programozs alapkvei. Mint lta-
lban, itt is rvnyes, hogy az egyszer s fldhzragadt sokkal jelentsebb, mint a bonyo-
lult s krmnfont.
Ennek fnyben ksztsnk egy jobb dtumosztlyt:
class Date {
public: // nyilvnos fellet
enum Month { jan=1, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec };
class Bad_date { }; // kivtelosztly
Date(int dd =0, Month mm =Month(0), int yy =0); // 0 jelentse "vedd az
// alaprtelmezettet"
// fggvnyek a Date vizsglathoz
int day() const;
Month month() const;
int year() const;
string string_rep() const; // brzols karakterlnccal
void char_rep(char s[ ]) const; // brzols C stlus karakterlnccal
static void set_default(int, Month, int);
// fggvnyek a Date mdostshoz
Date& add_year(int n); // n v hozzadsa
Date& add_month(int n); // n hnap hozzadsa
Date& add_day(int n); // n nap hozzadsa
private:
int d, m, y; // brzols
static Date default_date;
};
Absztrakcis mdszerek 312
A vgezhet mveletek ilyen halmaza meglehetsen jellemz a felhasznli adattpusokra.
A kvetkezk szerepelnek benne:
1. Egy konstruktor, amely kezdrtket ad az objektumoknak s vltozknak
2. Lekrdez fggvnyek, melyekkel egy Date-et megvizsglhatunk. Ezek const
minstse jelzi, hogy nem mdostjk annak az objektumnak vagy vltoznak
az llapott, amelyre meghvtk ket.
3. A Date objektumokat s vltozkat kezel fggvnyek, melyek az brzols
vagy a konkrt megvalsts ismerete, illetve az egyes elemek szerepvel val
bajlds nlkl is meghvhatk.
4. Automatikusan definilt mveletek, melyek segtsgvel a Date-ek szabadon
msolhatk.
5. A Bad_date osztly, mellyel a hibk mint kivtelek jelezhetk.
A Month (hnap) tpust azrt vezettem be, hogy kezeljem azt a problmt, amit az okoz,
hogy emlkeznnk kell r: vajon jnius 7-t amerikai stlusban Date(6,7)-nek vagy eurpai
stlusban Date(7,6)-nak kell-e rnunk. Az alaprtelmezett paramter-rtkek kezelsre is
gondoltam, ezzel kln eljrs foglalkozik.
Gondolkodtam azon, hogy a napok s vek brzolsra a Day-t s a Year-t, mint nll t-
pusokat bevezessem, hogy a Date(1995,jul,27) s a Date(27,jul,1995) sszekevereds-
nek veszlyt elkerljem. Ezek a tpusok azonban nem lennnek annyira hasznosak, mint
a Month. Majdnem minden ilyen hiba amgy is kiderl futsi idben nemigen dolgozom
olyan dtumokkal, mint a 27-ik v jlius 26-ika. Az 1800 eltti trtnelmi dtumok kezel-
se annyira bonyolult, hogy jobb trtnsz szakrtkre bzni. Ezenkvl pedig egy
valahanyadikt nem lehet rendesen ellenrizni a hnap s az v ismerete nlkl. (Egy al-
kalmas Year tpus meghatrozsra nzve lsd: 11.7.1.)
Az alaprtelmezett dtumot mint rvnyes Date objektumot definilni kell valahol:
Date Date::default_date(22,jan,1901);
A 10.2.7.1-ben emltett gyorsttras (cache) mdszer egy ilyen egyszer tpusnl felesle-
ges, gy kihagytam. Ha mgis szksges, kiegszthetjk vele az osztlyt, mint a felhaszn-
li felletet nem rint megvalstsi rszlettel.
10. Osztlyok 313
me egy kicsi elmleti plda arra, hogy lehet Date-eket hasznlni:
void f(Date& d)
{
Date lvb_day = Date(16,Date::dec,d.year());
if (d.day()==29 && d.month()==Date::feb) {
// ...
}
if (midnight()) d.add_day(1);
cout << "A kvetkez nap:" << d+1 << '\n';
}
Felttelezzk, hogy a << kimeneti s a + sszead mvelet a Date-ekre definilt; (ezt
a 10.3.3-ban valban meg is tesszk).
Figyeljk meg a Date::feb jellst. Az f() nem tagfggvnye Date-nek, gy meg kell adni,
hogy a Date-nek s nem valami msnak a feb-jrl van sz.
Mirt ri meg egy kln tpust megadni egy olyan egyszer dolog szmra, mint egy dtum?
Vgl is berhetnnk egy egyszer adatszerkezettel...
struct Date {
int day, month, year;
};
...s hagynnk, hogy a programozk dntsk el, mit csinlnak vele. De ha ezt tennnk, ak-
kor minden felhasznlnak magnak kellene a Date-ek sszetevit kezelnie: vagy kzvet-
lenl, vagy kln fggvnyekben. Ez pedig azzal jrna, hogy a dtum fogalma sztsz-
rdna, gy azt nehezebb lenne megrteni, dokumentlni s mdostani. Ha egy fogalmat
egyszer adatszerkezetknt bocstunk a felhasznlk rendelkezsre, az szksgszeren
kln munkt ignyel tlk.
Ezenkvl br a Date tpus ltszlag egyszer, mgis gondot ignyel gy megrni, hogy he-
lyesen mkdjk. Pldul egy Date objektum nvelshez szkvekkel kell trdni, azzal
a tnnyel, hogy a hnapok klnbz hosszsgak s gy tovbb (lsd a 10.6[1]-es fel-
adatot). Az v-hnap-nap adatbrzols radsul sok program szmra szegnyes. Ha vi-
szont gy dntnk, hogy megvltoztatjuk, csak a kijellt fggvnyeket kell mdostanunk.
Ha a Date-et pldul az 1970. janur elseje utni vagy eltti napok szmval akarnnk b-
rzolni, csak a Date tagfggvnyeit kellene megvltoztatnunk (10.6.[2]).
Absztrakcis mdszerek 314
10.3.1. Tagfggvnyek
Termszetesen minden tagfggvnyt ki kell fejteni valahol. me a Date konstruktornak
defincija:
Date::Date(int dd, Month mm, int yy)
{
if (yy == 0) yy = default_date.year();
if (mm == 0) mm = default_date.month();
if (dd == 0) dd = default_date.day();
int max;
switch (mm) {
case feb:
max = 28+leapyear(yy);
break;
case apr: case jun: case sep: case nov:
max = 30;
break;
case jan: case mar: case may: case jul: case aug: case oct: case dec:
max = 31;
break;
default:
throw Bad_date(); // valaki csalt
}
if (dd<1 || max<dd) throw Bad_date();
y = yy;
m = mm;
d = dd;
}
A konstruktor ellenrzi, hogy a kapott adatok rvnyes dtumot adnak-e. Ha nem, mint
pldul a Date(30,Date::Feb,1994) esetben, kivtelt vlt ki (8.3, 14. fejezet), amely jelzi,
hogy olyan jelleg hiba trtnt, amit nem lehet figyelmen kvl hagyni. Ha a kapott adatok
elfogadhatak, a kezdeti rtkads megtrtnik. Ez meglehetsen jellemz eljrsmd.
Msfell ha a Date objektum mr ltrejtt, akkor az tovbbi ellenrzs nlkl felhasznlha-
t s msolhat. Ms szval a konstruktor fellltja az osztlyra jellemz invarinst (ebben
az esetben azt, hogy egy rvnyes dtumrl van sz). A tbbi tagfggvny szmthat erre
az llapotra s ktelessge fenntartani azt. Ez a tervezsi mdszer risi mrtkben leegy-
szerstheti a kdot (lsd a 24.3.7.1-es pontot).
10. Osztlyok 315
A Month(0) rtket (amely nem jelent igazi hnapot) a vegyk az alaprtelmezett hna-
pot jelzsre hasznljuk. A Month felsorolsban megadhatnnk egy rtket kifejezetten en-
nek jelzsre, de jobb egy nyilvnvalan rvnytelen rtket hasznlni erre a clra, mint
hogy olyan ltszatot keltsnk, hogy 13 hnap van egy vben. Vegyk szre, hogy a 0 rt-
ket azrt hasznlhatjuk, mert az a Month felsorols biztostott garantlt rtktartomnyba
esik (4.8).
Gondolkodtam azon, hogy az adatellenrzst kln, egy is_date() fggvnybe teszem, de
ez olyan kdhoz vezetne, amely bonyolultabb s kevsb hatkony, mint a kivtelek elka-
psn alapul. Tegyk fel pldul, hogy a >> mvelet rtelmezett a Date osztlyra:
void fill(vector<Date>& aa)
{
while (cin) {
Date d;
try {
cin >> d;
}
catch (Date::Bad_date) {
// sajt hibakezel
continue;
}
aa.push_back(d); // lsd 3.7.3
}
}
Mint az ilyen egyszer konkrt osztlyok esetben szoksos, a tagfggvnyek meghatro-
zsa a trivilis s a nem tl bonyolult kztt mozog. Pldul:
inline int Date::day() const
{
return d;
}
Date& Date::add_month(int n)
{
if (n==0) return *this;
if (n>0) {
int delta_y = n/12;
int mm = m+n%12;
if (12 < mm) { // megjegyzs: int(dec)==12
delta_y++;
mm -= 12;
}
Absztrakcis mdszerek 316
// most azok az esetek jnnek, amikor Month(mm)-nek nincs d napja
y += delta_y;
m = Month(mm);
return *this;
}
// negatv n kezelse
return *this;
}
10.3.2. Segdfggvnyek
Egy osztlyhoz ltalban szmos olyan fggvny tartozhat, melyeket nem szksges mag-
ban az osztlyban tagknt megadni, mert nincs szksgk a bels adatbrzols kzvetlen
elrsre:
int diff(Date a, Date b); // napok szma az [a,b] vagy [b,a] tartomnyban
bool leapyear(int y);
Date next_weekday(Date d);
Date next_saturday(Date d);
Ha ezeket a fggvnyeket magban az osztlyban fejtennk ki, az bonyolultabb tenn az
osztly fellett s a bels adatbrzols esetleges mdostsakor tbb fggvnyt kellene
ellenrizni.
Hogyan kapcsoldnak az ilyen segdfggvnyek a Date osztlyhoz? Hagyomnyosan
a deklarcijukat az osztly deklarcijval azonos fjlba tennnk, gy azon felhasznlk
szmra, akiknek szksgk van a Date osztlyra, rgtn ezek is rendelkezsre llnnak
a felletet ler fejllomny beptse utn (9.2.1):
#include "Date.h"
A Date.h fejllomny hasznlata mellett vagy helyett a segdfggvnyek s az osztly kap-
csolatt gy tehetjk nyilvnvalv, hogy az osztlyt s segdfggvnyeit egy nvtrbe fog-
laljuk (8.2):
namespace Chrono { // dtumkezel szolgltatsok
class Date { /* ... */};
int diff(Date a, Date b);
bool leapyear(int y);
10. Osztlyok 317
Date next_weekday(Date d);
Date next_saturday(Date d);
// ...
}
A Chrono nvtr termszetesen a tbbi kapcsold osztlyt is tartalmazn, pldul a Time
(Id) s Stopwatch (Stopper) osztlyokat s azok segdfggvnyeit is. Egy egyetlen osztlyt
tartalmaz nvtr hasznlata ltalban csak tlbonyoltott, knyelmetlen kdhoz vezet.
10.3.3. Opertorok tlterhelse
Gyakran hasznos lehet olyan fggvnyeket felvenni, amelyek a hagyomnyos jellsmd
hasznlatt biztostjk. Az operator== fggvny pldul lehetv teszi az == egyenlsgi
opertor hasznlatt a Date objektumokra:
inline bool operator==(Date a, Date b) // egyenlsg
{
return a.day()==b.day() && a.month()==b.month() && a.year()==b.year();
}
Egyb kzenfekv jelltek:
bool operator!=(Date, Date); // egyenltlensg
bool operator<(Date, Date); // kisebb
bool operator>(Date, Date); // nagyobb
// ...
Date& operator++(Date& d); // Date nvelse egy nappal
Date& operator--(Date& d); // Date cskkentse egy nappal
Date& operator+=(Date& d, int n); // n nap hozzadsa
Date& operator-=(Date& d, int n); // n nap kivonsa
Date operator+(Date d, int n); // n nap hozzadsa
Date operator-(Date d, int n); // n nap kivonsa
ostream& operator<<(ostream&, Date d); // d kirsa
istream& operator>>(istream&, Date& d); // beolvass d-be
A Date osztly szmra ezen opertorok hasznlhatsga pusztn knyelmi szempontnak
tnik. m sok tpus pldul a komplex szmok (11.3), a vektorok (3.7.1) s a fgg-
vnyszer objektumok (18.4) esetben ezek hasznlata annyira beidegzdtt a felhasz-
nlknl, hogy szinte ktelez megadni ket. Az opertorok tlterhelsvel a 11. fejezet
foglalkozik.
Absztrakcis mdszerek 318
10.3.4. A konkrt osztlyok jelentsge
Azrt hvjuk a Date s ms egyszer felhasznli tpusokat konkrt tpusoknak, hogy meg-
klnbztessem azokat az absztrakt osztlyoktl (2.5.4) s az osztlyhierarchiktl (12.3),
illetve hogy hangslyozzam az olyan beptett tpusokkal val hasonlsgukat, mint az int
vagy a float. Ezeket rtktpusoknak (value types) is nevezik, hasznlatukat pedig r-
tkkzpont programozsnak (value-oriented programming). Hasznlati modelljk s m-
gtte lev filozfia nagyon klnbzik attl, amit gyakran objektum-orientlt programo-
zsnak hvnak (2.6.2).
A konkrt osztlyok dolga az, hogy egyetlen, viszonylag egyszer dolgot jl s hatkonyan
csinljanak. ltalban nem cl, hogy a felhasznlnak eszkzt adjunk a kezbe egy konk-
rt osztly viselkedsnek megvltoztatsra. gy a konkrt osztlyokat nem sznjuk arra
sem, hogy tbbalak (polimorf) viselkedst tanstsanak (2.5.5, 12.2.6).
Ha nem tetszik egy konkrt tpus viselkedse, akkor rhatunk egy msikat, ami a kvnal-
maknak megfelelen mkdik. Ez az adott tpus jrahasznostsval is elrhetjk; a tpust
pontosan gy hasznlhatjuk fel az j tpus megvalstshoz, mint egy int-et:
class Date_and_time {
private:
Date d;
Time t;
public:
Date_and_time(Date d, Time t);
Date_and_time(int d, Date::Month m, int y, Time t);
// ...
};
A 12. fejezetben trgyalt rkldsi eljrst gy hasznlhatjuk fel egy j tpus meghatroz-
sra, hogy csak az eltrseket kell lernunk. A Vec osztlyt pldul a vector alapjn kszt-
hetjk el (3.7.2).
Egy valamireval fordtprogrammal egy, a Date-hez hasonl konkrt osztly hasznlata
nem jr a szksges trolhely vagy a futsi id rejtett nvekedsvel. A konkrt osztlyok
mrete fordtsi idben ismert, ezrt az objektumok szmra helyet foglalhatunk a futsi
veremben is, azaz a szabad trat rint mveletek nlkl. A memriakioszts is ismert, gy
a helyben fordts egyszer feladat. A memriakiosztsnak ms nyelvekkel, pldul a C-vel
vagy a Fortrannal val sszeegyeztetse is hasonlan knnyen, kln erfeszts nlkl
megoldhat.
10. Osztlyok 319
Az ilyen egyszer tpusok megfelel halmaza teljes programok alapjul szolglhat. Ha egy
alkalmazsban nincsenek meg a megfelel kicsi, de hatkony tpusok, akkor a tl ltalnos
s kltsges osztlyok hasznlata komoly futsi idbeli s trfelhasznls-beli pazarls-
hoz vezethet. A konkrt tpusok hinya msfell zavaros programokat eredmnyez, illetve
azt, hogy minden programoz megrja az egyszer s srn hasznlt adatszerkezeteket
kzvetlenl kezel kdot.
10.4. Objektumok
Objektumok tbbflekppen jhetnek ltre: lehetnek automatikus vagy globlis vltozk,
osztlyok tagjai stb. Az albbiakban ezeket a lehetsgeket, a rjuk vonatkoz szablyokat,
az objektumok kezdllapott bellt konstruktorokat s a hasznlatbl kikerl objektu-
mok eltakartsra szolgl destruktorokat trgyaljuk.
10.4.1. Destruktorok
Az objektumok kezdllapott a konstruktorok lltjk be, vagyis a konstruktorok hozzk
ltre azt a krnyezetet, amelyben a tagfggvnyek mkdnek. Esetenknt az ilyen krnye-
zet ltrehozsa valamilyen erforrs fjl, zr, memriaterlet lefoglalsval jr, amit
a hasznlat utn fel kell szabadtani (14.4.7). Kvetkezskppen nmelyik osztlynak
szksge van egy olyan fggvnyre, amely biztosan meghvdik, amikor egy objektum
megsemmisl, hasonlan ahhoz, ahogy a konstruktor meghvsra is biztosan sor kerl,
amikor egy objektum ltrejn: ezek a destruktor (megsemmist, destructor) fggvnyek.
Feladatuk ltalban a rendbettel s az erforrsok felszabadtsa. A destruktorok automa-
tikusan meghvdnak, amikor egy automatikus vltozt tartalmaz blokk lefut, egy dinami-
kusan ltrehozott objektumot trlnek s gy tovbb. Nagyon klnleges esetben van csak
szksg arra, hogy a programoz kifejezetten meghvja a destruktort (10.4.11).
A destruktor legjellemzbb feladata, hogy felszabadtsa a konstruktorban lefoglalt mem-
riaterletet. Vegynk pldul egy valamilyen Name tpus elemek tblzatt tartalmaz
Table osztlyt. A konstruktornak le kell foglalnia az elemek trolshoz szksges mem-
rit. Ha a Table objektum brmilyen mdon trldik, a memrit fel kell szabadtani, hogy
mshol fel lehessen majd hasznlni. Ezt gy rhetjk el, hogy megrjuk a konstruktort ki-
egszt fggvnyt:
class Name {
const char* s;
// ...
};
Absztrakcis mdszerek 320
class Table {
Name* p;
size_t sz;
public:
Table(size_t s = 15) { p = new Name[sz = s]; } // konstruktor
~Table() { delete[ ] p; } // destruktor
Name* lookup(const char *);
bool insert(Name*);
};
A destruktort jelent ~Table() jells a komplemenskpzst jell ~ szimblumot hasznl-
va utal a destruktornak a Table() konstruktorhoz val viszonyra. Az sszetartoz kon-
struktordestruktor pr meghatrozsa a C++-ban szoksos eljrs vltoz mret objek-
tumok megvalstsra. A standard knyvtr troli, pldul a map, ennek a mdszernek
valamelyik vltozatt hasznljk, hogy az elemeik szmra trolhelyet biztostsanak, ezrt
a programoz a kvetkezkben lertakra tmaszkodik, amikor valamelyik standard knyv-
trbeli trolt hasznlja.(gy viselkedik pldul a szabvnyos string osztly is.) A lertak al-
kalmazhatak a destruktor nlkli osztlyokra is. Ezekre gy tekinthetnk, mint amelyek-
nl egy olyan destruktorunk van, amely nem csinl semmit.
10.4.2. Alaprtelmezett konstruktorok
Hasonlkppen a legtbb tpust gy tekinthetjk, mint amelynek van alaprtelmezett
konstruktora. Az alaprtelmezett konstruktor az, amelyiket paramter nlkl hvhatjuk
meg. Minthogy a fenti pldban a 15 mint alaprtelmezett rtk adott, a Table::Table(size_t)
fggvny alaprtelmezett konstruktor. Ha a programoz megadott alaprtelmezett
konstruktort, akkor a fordtprogram azt fogja hasznlni, msklnben szksg esetn
megprbl ltrehozni egyet. A fordtprogram ltal ltrehozott alaprtelmezett konstruktor
automatikusan meghvja az osztly tpus tagok s a bzisosztlyok (12.2.2) alaprtelme-
zett konstruktort:
struct Tables {
int i;
int vi[10];
Table t1;
Table vt[10];
};
Tables tt;
10. Osztlyok 321
Itt tt kezdrtkkel val feltltse fordts kzben ltrehozott alaprtelmezett konstruktor
segtsgvel trtnik, amely a Table(15)-t hvja meg tt.t1-re s tt.vt minden egyes elem-
re. Msrszt tt.i s tt.vi elemei nem kapnak kezdrtket, mert ezek az objektumok nem
osztly tpusak. Az osztlyok s a beptett tpusok egymstl eltr kezelsmdjnak a C-
vel val egyeztets s a futsi id nvelstl val tartzkods az oka.
Mivel a const-ok s a referencik ktelezen kezdrtket kell, hogy kapjanak (5.5, 5.4),
az ilyeneket tartalmaz tagoknak nem lehet alaprtelmezett konstruktora, hacsak a progra-
moz kifejezetten nem gondoskodik konstruktorrl (10.4.6.1):
struct X {
const int a;
const int& r;
};
X x; // hiba: nincs alaprtelmezett konstruktor X szmra
Az alaprtelmezett konstruktorok kzvetlen mdon is hvhatk (10.4.10). A beptett tpu-
soknak szintn van alaprtelmezett konstruktoruk (6.2.8).
10.4.3. Ltrehozs s megsemmists
Tekintsk t a klnbz mdokat: hogyan hozhatunk ltre objektumot s ksbb az ho-
gyan semmisl meg. Objektum a kvetkez mdokon hozhat ltre:
10.4.4 Nvvel elltott automatikus objektumknt, amely akkor keletkezik, ami-
kor a program vgrehajtsa sorn deklarcija kirtkeldik, s akkor
semmisl meg, amikor a program kilp abbl a blokkbl, amelyen bell
a deklarci szerepelt.
10.4.5 Szabad trbeli objektumknt, amely a new opertor hasznlatval jn lt-
re s a delete opertor hasznlatval semmisl meg.
10.4.6 Nem statikus tagobjektumknt, amely egy msik osztly objektum tagja-
knt jn ltre s azzal egytt keletkezik, illetve semmisl meg.
10.4.7 Tmbelemknt, amely akkor keletkezik s semmisl meg, amikor
a tmb, melynek eleme.
10.4.8 Loklis statikus objektumknt, amely akkor jn ltre, amikor a program
vgrehajtsa sorn elszr tallkozik a deklarcijval s egyszer semmi-
sl meg: a program befejezsekor.
10.4.9 Globlis, nvtrbeli vagy statikus osztly-objektumknt, amely egyszer,
a program indulsakor jn ltre s a program befejezsekor semmisl meg.
Absztrakcis mdszerek 322
10.4.10 Ideiglenes objektumknt, amely egy kifejezs kirtkelsekor jn ltre s
a teljes kifejezs vgn, melyben elfordult, semmisl meg.
10.4.11 Felhasznl ltal rt fggvnnyel vgzett, paramterekkel vezrelt lefog-
lalsi mvelet segtsgvel nyert, a memriba helyezett objektumknt.
10.4.12 Uni tagjaknt, amelynek nem lehet sem konstruktora, sem destruktora.
Ez a felsorols nagyjbl a fontossg sorrendjben kszlt. A kvetkez alpontokban rsz-
letesen elmagyarzzuk az objektumok ltrehozsnak ezen vltozatait s hasznlatukat.
10.4.4. Loklis vltozk
A loklis vltozk konstruktora minden alkalommal vgrehajtdik, valahnyszor a vezrls
fonala keresztlhalad a vltoz deklarcijn, a destruktor vgrehajtsra pedig akkor ke-
rl sor, amikor kilpnk a vltoz blokkjbl. A loklis vltozk destruktorai konstruk-
toraik sorrendjhez viszonytva fordtott sorrendben hajtdnak vgre:
void f(int i)
{
Table aa;
Table bb;
if (i>0) {
Table cc;
// ...
}
Table dd;
// ...
}
Itt aa, bb s dd ebben a sorrendben keletkeznek az f() meghvsakor s a dd, bb, aa sor-
rendben semmislnek meg, amikor a vezrls kilp az f()-bl. Ha egy hvsnl i>0, a cc
a bb utn jn ltre, s dd ltrejtte eltt semmisl meg.
10.4.4.1. Objektumok msolsa
Ha t1 s t2 a Table osztlyba tartoz objektumok, t2=t1 alaprtelmezs szerint t1-nek ta-
gonknti tmsolst jelenti t2-be (10.2.5). Ha nem brljuk fell ezt az alaprtelmezett vi-
selkedst, meglep (s rendszerint nemkvnatos) hats lphet fel, ha olyan osztly objek-
tumaira alkalmazzuk, melynek mutat tagjai vannak. A tagonknti msols rendszerint nem
megfelel olyan objektumok szmra, amelyek egy konstruktordestruktor pr ltal kezelt
erforrsokat tartalmaznak:
10. Osztlyok 323
void h()
{
Table t1;
Table t2 = t1; // kezdeti rtkads msolssal: problms
Table t3;
t3 = t2; // rtkads msolssal: problms
}
Itt a Table alaprtelmezett konstruktora ktszer hvdik meg: egyszer t1-re s egyszer t3-ra.
A t2-re nem hvdik meg, mert ez a vltoz a t1-bl val msolssal kapott kezdrtket.
A Table destruktor viszont hromszor hvdik meg: t1-re, t2-re s t3-ra is. Alaprtelmezs sze-
rint az rtkads tagonknti msolst jelent, gy a h() fggvny vgn t1, t2 s t3 mindegyike
arra a nvtmbre hivatkoz mutatt fogja tartalmazni, amely t1 ltrejttekor kapott helyet
a szabad trban. A mutat, mely a t3 ltrejttekor kijellt nvtmbre mutat, nem marad meg,
mert a t3=t2 rtkads kvetkeztben fellrdik, gy az ltala elfoglalt trterlet a program
szmra rkre elvsz, hacsak nincs automatikus szemtgyjts (10.4.5). Msrszt a t1 rsz-
re ltrehozott tmb t1-ben, t2-ben s t3-ban egyarnt megjelenik, teht hromszor is trldik.
Ez nem meghatrozott s valsznleg katasztroflis eredmnyhez vezet.
Az ilyen anomlik elkerlhetk, ha megadjuk, mit jelent egy Table objektum msolsa:
class Table {
// ...
Table(const Table&); // msol konstruktor
Table& operator=(const Table&); // msol rtkads
};
A programoz brmilyen alkalmas jelentst meghatrozhat ezen msol mveletek szm-
ra, de az ilyen tpus trolk esetben a msol mvelet hagyomnyos feladata az, hogy le-
msolja a tartalmazott elemeket (vagy legalbbis a felhasznl szmra gy tesz, mintha ez
a msols megtrtnt volna, lsd 11.12):
Table::Table(const Table& t) // msol konstruktor
{
p = new Name[sz=t.sz];
for (int i = 0; i<sz; i++) p[i] = t.p[i];
}
Table& Table::operator=(const Table& t) // rtkads
{
if (this != &t) { // vakodjunk az n-rtkadstl: t = t
delete[ ] p;
Absztrakcis mdszerek 324
p = new Name[sz=t.sz];
for (int i = 0; i<sz; i++) p[i] = t.p[i];
}
return *this;
}
Mint majdnem mindig, a msol konstruktor s az rtkad mvelet itt is jelentsen eltr.
Ennek alapvet oka az, hogy a msol konstruktor le nem foglalt memrit kszt fel a fel-
hasznlsra, mg az rtkad mveletnek egy mr ltrehozott objektumot kell helyesen
kezelnie.
Az rtkadst bizonyos esetekben optimalizlni lehet, de az rtkad opertor ltalnos
clja egyszer: vdekezni kell a sajt magval val rtkads ellen, trlni kell a rgi ele-
meket, elkszteni s bemsolni az j elemeket. ltalban minden nem statikus tagot m-
solni kell (10.4.6.3.)
10.4.5. A szabad tr
A dinamikusan kezelt memriaterleten, a szabad trban ltrehozott objektumok kon-
struktort a new opertor hvja meg, s ezek az objektumok addig lteznek, amg a rjuk
hivatkoz mutatra nem alkalmazzuk a delete opertort:
int main()
{
Table* p = new Table;
Table* q = new Table;
delete p;
delete p; // valsznleg futsi idej hibt okoz
}
A Table::Table() konstruktort ktszer hvjuk meg, csakgy, mint a Table::~Table()
destruktort. Sajnos azonban ebben a pldban a new-k s delete-ek nem felelnek meg egy-
msnak: a p ltal hivatkozott objektumot ktszer trltk, mg a q ltal mutatottat egyszer
sem. Nyelvi szempontbl egy objektum nem trlse nem hiba, mindssze a memria pa-
zarlsa, mindazonltal egy hosszan fut programnl az ilyen memriaszivrgs vagy me-
mrialyuk (memory leak) slyos s nehezen felderthet hiba. Szerencsre lteznek az
ilyesfajta memriaszivrgst keres eszkzk is. A p ltal mutatott objektum ktszeri trl-
se slyos hiba; a program viselkedse nem meghatrozott s nagy valsznsggel kataszt-
roflis lesz.
10. Osztlyok 325
Bizonyos C++-vltozatok automatikusan jrahasznostjk az elrhetetlen objektumok ltal
elfoglalt memrit (ezek a szemtgyjtst alkalmaz megvalstsok), de viselkedsk nem
szabvnyostott. Ha van is szemtgyjts, a delete opertor ktszeri meghvsa egyben
a destruktor (ha van ilyen) ktszeri meghvst fogja eredmnyezi, gy az objektum ktszer
trldik, ami ilyenkor is slyos hiba. A legtbb esetben az objektumok ezen viselkedse
csak aprbb knyelmetlensget jelent. Jelesl, ahol van szemtgyjts, ott is a csak mem-
ria-felszabadtst vgz destruktorokat lehet megtakartani. Ennek az egyszerstsnek
a hordozhatsg elvesztse az ra, st bizonyos programoknl a futsi id nvekedse s
a viselkeds megjsolhatatlansga is (C.9.1).
Miutn egy objektumot a delete mvelettel trltnk, brmilyen hozzfrsi ksrlet az ob-
jektumhoz hibnak szmt. Sajnos az egyes nyelvi vltozatok nem kpesek megbzhat m-
don jelezni az ilyen hibkat.
A programoz megszabhatja, hogyan trtnjk a new hasznlata esetn a memria lefogla-
lsa, illetve annak a delete-tel val felszabadtsa (6.2.6.2 s 15.6). Lehetsges a lefoglals,
a konstruktorok s a kivtelek egyttmkdsnek a megadsa is (14.4.5 s 19.4.5). A sza-
bad trban lev tmbket a 10.4.7. trgyalja.
10.4.6. Osztly tpus tagok
Nzznk egy osztlyt, amely egy kisebb cgrl trolhat adatokat:
class Club {
string name;
Table members;
Table officers;
Date founded;
// ...
Club(const string& n, Date fd);
};
A Club osztly konstruktornl paramterknt meg kell adni a nevet s az alapts dtumt.
Az osztlytagok konstruktorainak paramtereit a tartalmaz osztly konstruktor-
definicijnak tag-kezdrtk listjban (member initializer) adjuk meg:
Club::Club(const string& n, Date fd)
: name(n), members(), officers(), founded(fd)
{
// ...
}
Absztrakcis mdszerek 326
A tagok kezdrtk-listjt kettspont elzi meg s az egyes tagoknak kezdrtket ad ki-
fejezseket vesszk vlasztjk el.
A tagok konstruktorainak vgrehajtsa megelzi a tartalmaz osztly sajt konstruktora
trzsnek vgrehajtst. A konstruktorok a tagoknak az osztly deklarcijban elfoglalt
sorrendjben s nem a kezdrtket ad kifejezseknek a listban val felsorolsi sorrend-
jben hajtdnak vgre. Az esetleges zavarok elkerlse rdekben nyilvn clszer a tago-
kat a deklarciban elfoglalt sorrendjkben felvenni a kezdrtk-ad kifejezsek listjra.
A tagok destruktorai a konstruktorok sorrendjvel ellenkez sorrendben hvdnak meg.
Ha egy tag konstruktornak nincs szksge paramterre, nem szksges felvenni a listra,
gy a kvetkez kdrszlet egyenrtk az elz pldabelivel:
Club::Club(const string& n, Date fd)
: name(n), founded(fd)
{
// ...
}
A Table::Table konstruktor a Club::officers tagot mindkt esetben a 15-tel, mint alaprtel-
mezett paramterrel hozza ltre.
Ha egy osztlynak osztly tpus tagjai vannak, az osztly megsemmistsekor elszr sajt
destruktor fggvnynek (ha van ilyen) trzse hvdik meg, majd a tagok destruktorai
a deklarcival ellenttes sorrendben. A konstruktor alulrl felfel haladva (a tagokat el-
szr) pti fel a tagfggvnyek vgrehajtsi krnyezett, a destruktor pedig fellrl lefel
(a tagokat utoljra) bontja le azt.
10.4.6.1. A tagok szksgszer kezdeti rtkadsa
Azon tagok feltltse kezdrtkkel szksgszer, amelyeknl a kezdeti rtkads kln-
bzik az egyszer rtkadstl azaz az alaprtelmezett konstruktor nlkli osztlyba tar-
toz, a const s a referencia tpus tagok:
class X {
const int i;
Club c;
Club& pc;
// ...
X(int ii, const string& n, Date d, Club& c) : i(ii), c(n,d), pc(c) { }
};
10. Osztlyok 327
Ezen tagok kezdeti rtkadsra nincs egyb lehetsg, s hiba azt nem megtenni is. A leg-
tbb tpus esetben azonban a programoz vlaszthat a kezdeti s a sima rtkads k-
zl. Ilyenkor n ltalban a tag-kezdrtk lists megoldst vlasztom, hogy egyrtelm le-
gyen a kezdeti rtkads tnye. Ez a mdszer radsul hatkonyabb is:
class Person {
string name;
string address;
// ...
Person(const Person&);
Person(const string& n, const string& a);
};
Person::Person(const string& n, const string& a)
: name(n)
{
address = a;
}
Itt a name az n egy msolatval kap kezdrtket. Msfell az address elszr egy res ka-
rakterlnccal tltdik fel, majd rtkl az a egy msolatt kapja.
10.4.6.2. Konstans tagok
Egy statikus, egsz tpus konstans tagot lehetsges a deklarciban egy kezdrtk-ad
konstans kifejezssel is feltlteni:
class Curious {
public:
static const int c1 = 7; // rendben, de ne felejtsk el a meghatrozst
static int c2 = 11; // hiba: nem lland
const int c3 = 13; // hiba: nem statikus
static const int c4 = f(17); // hiba: a kezdrtk-ad nem lland
static const float c5 = 7.0; // hiba: a kezdrtk-ad nem egsz rtk
// ...
};
Akkor s csak akkor, ha a kezdrtket kapott tagot memriban trolt objektumknt hasz-
nljuk, szksges, hogy az ilyen tag (de csak egy helyen) definilt legyen, de ott nem sza-
bad megismtelni a kezdrtk-ad kifejezst:
const int Curious::c1; // szksges, de a kezdrtk-ad nem szerepelhet itt mg egyszer
const int* p = &Curious::c1; // rendben: Curious::c1 meghatrozott
Absztrakcis mdszerek 328
Msik megoldsknt, jelkpes llandknt hasznlhatunk felsorol konstanst (4.8, 14.4.6,
15.3) is az osztly deklarcijn bell, ha szksges:
class X {
enum { c1 = 7, c2 = 11, c3 = 13, c4 = 17 };
// ...
};
gy a programoz nem fog ksrtsbe esni, hogy az osztlyban vltozknak, lebegpontos
szmoknak stb. adjon kezdrtket.
10.4.6.3. Tagok msolsa
Az alaprtelmezett msol konstruktor s az alaprtelmezett msol rtkads (10.4.4.1)
az osztly sszes tagjt msolja. Ha ez nem lehetsges, az ilyen osztly objektum msol-
si ksrlete hiba:
class Unique_handle {
private: // a msol mveleteket privtt tesszk, megelzend az
// alaprtelmezett msolst (11.2.2)
Unique_handle(const Unique_handle&);
Unique_handle& operator=(const Unique_handle&);
public:
// ...
};
struct Y {
// ...
Unique_handle a; // explicit kezdrtket ignyel
};
Y y1;
Y y2 = y1; // hiba: Y::a nem msolhat
Ezenkvl az alaprtelmezett rtkads nem jhet ltre a fordtskor, ha az osztly egy nem
statikus tagja: referencia, konstans, vagy olyan felhasznli tpus melynek nincsen msol
rtkadsa.
10. Osztlyok 329
Jegyezzk meg, hogy a referencia tpus tagok ugyanarra az objektumra hivatkoznak az ere-
deti objektumban s a msolatban is. Ez gond lehet, ha a hivatkozott objektumot trlni kell.
Ha msol konstruktort runk, gyeljnk arra, hogy minden tagot msoljunk, amelyet szk-
sges. Alaprtelmezs szerint az elemek alaprtelmezett mdon kapnak kezdrtket, de
sokszor nem erre van szksg egy msol konstruktorban:
Person::Person(const Person& a) : name(a.name) { } // vigyzat!
Itt elfelejtettem az address tagot msolni, gy az alaprtelmezs szerinti res karakterlncot
kapja kezdrtkknt. Ha j taggal bvtnk egy osztlyt, ne felejtsk el ellenrizni, hogy
vannak-e olyan felhasznl ltal megadott konstruktorok, amelyeket az j tagok kezdeti r-
tkadsra s msolsra val tekintettel meg kell vltoztatni.
10.4.7. Tmbk
Ha egy osztly egy tagjnak van alaprtelmezett, azaz paramter nlkl hvhat kon-
struktora, akkor ilyen osztly objektumok tmbjt is meghatrozhatjuk:
Table tbl[10];
A fenti egy 10 Table elembl ll tmbt hoz ltre s minden elemet a Table::Table()
konstruktorral, a 15 rtk alaprtelmezett paramterrel tlt fel.
A kezdrtk-lista (5.2.1, 18.6.7) alkalmazsn kvl nincs ms md egy tmb elemeinek
konstruktorai szmra (nem alaprtelmezett) paramtereket megadni. Ha felttlenl szk-
sges, hogy egy tmb tagjai klnbz kezdrtket kapjanak, rjunk olyan alaprtelmezett
konstruktort, amely ellltja a kvnt rtkeket:
class Ibuffer {
string buf;
public:
Ibuffer() { cin>>buf; }
// ...
};
void f()
{
Ibuffer words[100]; // minden elem a cin-rl kap kezdrtket
// ...
}
Az ilyen trkkket azonban ltalban jobb elkerlni.
Absztrakcis mdszerek 330
Amikor egy tmb megsemmisl, az sszes elemre meghvdik a destruktor. Ha nem new
mvelettel ltrehozott tmbrl van sz, akkor ez automatikusan trtnik. A C nyelvhez ha-
sonlan a C++ sem klnbzteti meg az egyedi elemre s a tmb kezdelemre hivatkoz
mutatt (5.3), ezrt a programoznak meg kell adnia, hogy egyedi elemet vagy tmbt
kell-e trlni:
void f(int sz)
{
Table* t1 = new Table;
Table* t2 = new Table[sz];
Table* t3 = new Table;
Table* t4 = new Table[sz];
delete t1; // helyes
delete[ ] t2; // helyes
delete[ ] t3; // helytelen; problma
delete t4; // helytelen; problma
}
A tmbk s egyedi elemek dinamikus trterleten val elhelyezse az adott nyelvi vlto-
zattl fgg. Ezrt a klnbz vltozatok klnbzkppen fognak viselkedni, ha hibsan
hasznljuk a delete s delete[ ] opertorokat. Egyszer s rdektelen esetekben, mint az el-
z plda, a fordt szreveheti a hibt, de ltalban futtatskor fog valami csnya dolog
trtnni.
A kifejezetten tmbk trlsre szolgl delete[ ] logikailag nem szksges. Elkpzelhet
lenne, hogy a szabad trtl megkveteljk, hogy minden objektumrl tartsa nyilvn, hogy
egyedi objektum avagy tmb. Ekkor a nyilvntarts terht levennnk a programoz vll-
rl, de ez a ktelezettsg egyes C++-vltozatokban jelents memria- s futsi id-tbbletet
jelentene. Ha az olvas tl nehzkesnek tallja a C stlus tmbk hasznlatt, itt is hasz-
nlhat helyettk olyan osztlyokat, mint a vector (3.7.1, 16.3):
void g()
{
vector<Table>* p1 = new vector<Table>(10);
Table* p2 = new Table;
delete p1;
delete p2;
}
10. Osztlyok 331
10.4.8. Loklis statikus adatok
A loklis statikus objektumok (7.1.2) konstruktora akkor hajtdik vgre, amikor a vgre-
hajtsi szl elszr halad keresztl az objektum meghatrozsn:
void f(int i)
{
static Table tbl;
// ...
if (i) {
static Table tbl2;
// ...
}
}
int main()
{
f(0);
f(1);
f(2);
// ...
}
Itt tbl konstruktora f() els meghvsakor hvdik meg. Mivel tbl-t statikusknt adtuk meg,
gy nem semmisl meg, amikor f()-bl visszatr a vezrls s nem jn jra ltre f() mso-
dik meghvsakor. Mivel a tbl2 vltoz deklarcijt tartalmaz blokk nem hajtdik vgre
az f(0) meghvskor, tbl2 is csak f(1) vgrehajtsakor jn ltre, a blokk jbli vgrehajtsa-
kor nem.
A loklis statikus objektumok destruktorai akkor hvdnak meg, amikor a program lell
(9.4.1.1). Hogy pontosan mikor, az nincs meghatrozva.
10.4.9. Nem loklis adatok
A fggvnyeken kvl meghatrozott (azaz globlis, nvtrbeli s osztlyhoz tartoz stati-
kus) vltozk a main() fggvny meghvsa eltt jnnek ltre (s kapnak kezdrtket), s
minden ltrehozott objektum destruktora a main() fggvnybl val kilps utn vgre fog
hajtdni. A dinamikus knyvtrak hasznlata (dinamikus csatols) kiss bonyolultabb te-
szi ezt, hiszen ilyenkor a kezdeti rtkadsra akkor kerl sor, amikor a dinamikus kd a fu-
t programhoz kapcsoldik.
Absztrakcis mdszerek 332
A fordtsi egysgeken bell a nem loklis objektumok konstruktorainak vgrehajtsa
a definicijuk sorrendjben trtnik:
class X {
// ...
static Table memtbl;
};
Table tbl;
Table X::memtbl;
namespace Z {
Table tbl2;
}
A konstruktorok vgrehajtsi sorrendje a kvetkez: tbl, X::memtbl, Z::tbl2. Vegyk szre,
hogy a definci s nem a deklarci sorrendje szmt. A destruktorok a konstruktorokkal
ellenttes sorrendben hajtdnak vgre: Z::tbl2, X::memtbl, tbl.
Nincs nyelvi vltozattl fggetlen meghatrozsa annak, hogy az egyes fordtsi egysgek
nem loklis objektumai milyen sorrendben jnnek ltre:
// file1.c:
Table tbl1;
// file2.c:
Table tbl2;
Az, hogy tbl1 vagy tbl2 fog elbb ltrejnni, a C++ adott vltozattl fgg, de a sorrend
azon bell is vltozhat. Dinamikus csatols hasznlata vagy akr a fordtsi folyamat kis
mdostsa is megvltoztathatja a sorrendet. A destruktorok vgrehajtsi sorrendje is hason-
lan vltozatfgg.
Knyvtrak tervezsekor szksges vagy egyszeren knyelmes lehet egy olyan,
konstruktorral s destruktorral br tpus elksztse, amely kizrlag a kezdeti rtkads s
rendraks cljt szolglja. Ilyen tpus adatot csak arra clra fogunk hasznlni, hogy egy sta-
tikus objektum szmra memriaterletet foglaljunk le azrt, hogy lefusson a konstruktora
s a destruktora:
class Zlib_init {
Zlib_init(); // Zlib elksztse hasznlatra
~Zlib_init(); // Zlib utni takarts
};
10. Osztlyok 333
class Zlib {
static Zlib_init x;
// ...
};
Sajnos egy tbb fordtsi egysgbl ll program esetben nincs garancia arra, hogy egy
ilyen objektum kezdeti rtkadsa az els hasznlat eltt megtrtnik s a destruktor az
utols hasznlat utn fut le. Egyes C++-vltozatok biztosthatjk ezt, de a legtbb nem.
Programozi szinten azonban lehetsges azt a megoldst alkalmazni, amit a nyelvi vltoza-
tok ltalban a loklis statikus objektumokra alkalmaznak: egy-egy, az els hasznlatot fi-
gyel kapcsolt:
class Zlib {
static bool initialized;
static void initialize() { /* kezdeti rtkads */ initialized = true; }
public:
// nincs konstruktor
void f()
{
if (initialized == false) initialize();
// ...
}
// ...
};
Ha sok fggvnyben kell lekrdezni az els hasznlatot figyel kapcsolt, az fraszt fel-
adat lehet, de megoldhat. Ez a mdszer azon alapul, hogy a konstruktor nlkli statikus
objektumok 0 kezdrtket kapnak. A dolog akkor vlik igazn problematikuss, ha az ob-
jektum els hasznlata egy vgrehajtsi idre rzkeny fggvnyben trtnik, ahol az ellen-
rzs s szksg esetn a kezdeti rtkads tl sok idt vehet ignybe. Ilyenkor tovbbi
trkkkre van szksg (21.5.2).
Egy lehetsges msik megkzelts, hogy az egyes objektumokat fggvnyekkel helyette-
stjk (9.4.1):
int& obj() { static int x = 0; return x; } // kezdeti rtkads els hasznlatkor
Az els hasznlatot figyel kapcsolk nem kezelnek minden elkpzelhet helyzetet. Lehet-
sges pldul olyan objektumokat megadni, amelyek a kezdeti rtkads alatt egymsra hi-
vatkoznak az ilyesmit jobb elkerlni. Ha mgis ilyen objektumokra van szksg, akkor
vatosan, fokozatosan kell ltrehozni azokat. Egy msik problma, hogy az utols haszn-
latot nem tudjuk egy jelzvel jelezni. Ehelyett lsd 9.4.1.1 s 21.5.2.
Absztrakcis mdszerek 334
10.4.10. Ideiglenes objektumok
Ideiglenes objektumok legtbbszr aritmetikai kifejezsekbl jnnek ltre. Pldul az
x*y+z kifejezs kirtkelse sorn egy ponton az x*y rszeredmnyt valahol trolni kell.
Hacsak nem a program gyorstsn dolgozik (11.6), a programoz ritkn kell, hogy az ide-
iglenes objektumokkal trdjk, habr ez is elfordul (11.6, 22.4.7).
Egy ideiglenes objektum, hacsak nincs referencihoz ktve vagy nem egy nevestett objek-
tumnak ad kezdrtket, trldik a tartalmaz teljes kifejezs kirtkelse vgn. A teljes
kifejezs olyan kifejezs, amely nem rszkifejezse ms kifejezsnek.
A szabvnyos string osztly c_str() nev tagfggvnye egy C stlus, nullkarakterrel lezrt
karaktertmbt ad vissza (3.5.1, 20.4.1). A + opertor karakterlncok esetben sszefzst
jell. Ezek nagyon hasznos dolgok a karakterlncok kezelsekor, de egyttes hasznlatuk
furcsa problmkhoz vezethet:
void f(string& s1, string& s2, string& s3)
{
const char* cs = (s1+s2).c_str();
cout << cs;
if (strlen(cs=(s2+s3).c_str())<8 && cs[0]=='a') {
// cs hasznlata
}
}
Az olvas valsznleg azt mondja erre, hogy nem kell ilyet csinlni, s egyetrtek vele,
de ilyen kdot szoktak rni, gy rdemes tudni, hogyan kell azt rtelmezni.
Elszr egy ideiglenes, string osztly objektum jn ltre, amely az s1+s2 mvelet eredm-
nyt trolja. Ettl az objektumtl aztn elkrjk a C stlus karaktertmbt, majd a kifejezs
vgn az ideiglenes objektum trldik. Vajon hol foglalt helyet a fordt a C stlus karak-
tertmb szmra? Valsznleg az s1+s2-t tartalmaz ideiglenes objektumban, s annak
megsemmislse utn nem biztos, hogy nem semmisl meg az a terlet is, kvetkezskp-
pen cs felszabadtott memriaterletre mutat. A cout << cs kimeneti mvelet mkdhet
a vrt mdon, de ez puszta szerencse krdse. A fordtprogram esetleg feldertheti az ilyen
problmt s figyelmeztethet r.
Az if utastsos plda egy kicsit ravaszabb. Maga a felttel a vrakozsnak megfelelen fog
mkdni, mert a teljes kifejezs, amelyben az s2+s3-at tartalmaz ideiglenes objektum lt-
rejn, maga az if felttele. Mindazonltal az ideiglenes objektum a felttelesen vgrehajtan-
d utasts vgrehajtsnak megkezdse eltt megsemmisl, gy a cs vltoz brmifle ot-
tani hasznlata nem biztos, hogy mkdik.
10. Osztlyok 335
Vegyk szre, hogy ebben az esetben, mint sok ms esetben is, az ideiglenes objektumok-
kal kapcsolatos problma abbl addik, hogy egy magasabb szint adatot alacsony szinten
hasznltunk. Egy tisztbb programozsi stlus nem csak jobban olvashat programrszletet
eredmnyezett volna, de az ideiglenes objektumokkal kapcsolatos problmkat is teljesen
elkerlte volna:
void f(string& s1, string& s2, string& s3)
{
cout << s1+s2;
string s = s2+s3;
if (s.length()<8 && s[0]=='a') {
// s hasznlata
}
}
Ideiglenes vltozt hasznlhatunk konstans referencia vagy nevestett objektum kezdrt-
keknt is:
void g(const string&, const string&);
void h(string& s1, string& s2)
{
const string& s = s1+s2;
string ss = s1+s2;
g(s,ss); // s s ss itt hasznlhat
}
Ez a kdrszlet jl mkdik. Az ideiglenes vltoz megsemmisl, amikor az hivatkoz-
st vagy nevestett objektumt tartalmaz kdblokk lefut. Emlkezznk arra, hogy hiba egy
loklis vltozra mutat referencit visszaadni egy fggvnybl (7.3) s hogy ideiglenes
objektumot nem adhatunk egy nem konstans referencia kezdrtkl (5.5). Ideiglenes
vltozt ltrehozhatunk kifejezett konstruktorhvssal is:
void f(Shape& s, int x, int y)
{
s.move(Point(x,y)); // Point ltrehozsa a Shape::move() szmra
// ...
}
Az ilyen mdon ltrehozott ideiglenes vltozk is ugyanolyan szablyok szerint semmisl-
nek meg, mint az automatikusan ltrehozottak.
Absztrakcis mdszerek 336
10.4.11. Az objektumok elhelyezse
A new opertor alaprtelmezs szerint a szabad trban hozza ltre az objektumokat. Mit te-
gynk, ha mshol szeretnnk, hogy egy objektum ltrejjjn? Vegynk pldaknt egy egy-
szer osztlyt:
class X {
public:
X(int);
// ...
};
Az objektumokat tetszs szerinti helyre tehetjk, ha megadunk egy memria-lefoglal fgg-
vnyt, amelynek tovbbi paramterei vannak, s a new opertor hasznlatakor megadjuk
ezeket a paramtereket:
void* operator new(size_t, void* p) { return p; } // explicit elhelyez opertor
void* buf = reinterpret_cast<void*>(0xF00F); // fontos cm
X* p2 = new(buf)X; // X ltrehozsa a 'buf-ban', az operator
// new(sizeof(X),buf) meghvsval
Ezen hasznlat miatt a new (buf) X utastsforma, amely az operator new-nak tovbbi pa-
ramtereket ad, elhelyez utastsknt (placement syntax) ismert. Jegyezzk meg, hogy
minden new opertor a mretet vrja els paramterknt, s ezt, mint a ltrehozand ob-
jektum mrett, automatikusan megkapja (15.6). Hogy melyik opertort fogja egy adott h-
vs elrni, azt a szoksos paramter-egyeztetsi szablyok fogjk eldnteni (7.4); minden
new() opertornak egy size_t tpus els paramtere van.
Az elhelyez operator new() a legegyszerbb ilyen lefoglal fggvny, s definicija
a <new> szabvnyos fejllomnyban szerepel.
A reinterpret_cast a legdurvbb s a legnagyobb krokozsra kpes a tpuskonverzis
opertorok kzl (6.2.7). Legtbbszr egyszeren a paramternek megfelel bitsorozat
rtket, mint a kvnt tpust adja vissza, gy aztn a lnyegbl fakadan nyelvi vltozattl
fgg, veszlyes s esetenknt felttlenl szksges egszek s mutatk kztti talakts-
ra hasznlhat.
Az elhelyez new opertor felhasznlhat arra is, hogy egy bizonyos helyrl (Arena objek-
tumtl) foglaljunk memrit:
10. Osztlyok 337
class Arena {
public:
virtual void* alloc(size_t) =0;
virtual void free(void*) =0;
// ...
};
void* operator new(size_t sz, Arena* a)
{
return a->alloc(sz);
}
A klnbz Arena objektumokban szksg szerint tetszleges tpus objektumokat hoz-
hatunk ltre:
extern Arena* Persistent;
extern Arena* Shared;
void g(int i)
{
X* p = new(Persistent) X(i); // X lland trterleten
X* q = new(Shared) X(i); // X megosztott memriban
// ...
}
Ha egy objektumot olyan helyre helyeznk, amelyet nem (kzvetlenl) a szabvnyos sza-
badtr-kezel kezel, nmi vatossgra van szksg annak megsemmistsekor. Ennek alap-
vet mdja az, hogy kzvetlenl meghvjuk a destruktort:
void destroy(X* p, Arena* a)
{
p->~X(); // destruktor meghvsa
a->free(p); // memria felszabadtsa
}
Jegyezzk meg, hogy a destruktorok kzvetlen meghvst csakgy, mint az egyedi ig-
nyeket kielgt globlis memria-lefoglalk hasznlatt inkbb kerljk el, ha lehet. Ese-
tenknt mgis alapvet szksgnk van rjuk: pldul nehz lenne egy hatkonyan mk-
d ltalnos trolosztlyt kszteni a standard knyvtr vector (3.7.1, 16.3.8) tpusa nyo-
mn, kzvetlen destruktorhvs nlkl. Mindazonltal egy kezd C++-programoz inkbb
hromszor gondolja meg, mieltt kzvetlenl
Absztrakcis mdszerek 338
meghvna egy destruktort s akkor is inkbb krje eltte tapasztalt kollgjnak tancst.
Az elhelyez opertor s a kivtelkezels kapcsolatrl lsd a 14.4.4-es pontot.
A tmbknl nincs megfelelje az elhelyez opertornak, de nincs is szksg r, mert az
elhelyez opertort tetszleges tpusokra alkalmazhatjuk. Tmbkre vonatkozan azonban
megadhatunk pldul egyedi operator delete()-et (19.4.5).
10.4.12. Unik
Egy nevestett uni (union) olyan adatszerkezet (struct), amelyben minden tag cme azo-
nos (lsd C.8.2). Egy uninak lehetnek tagfggvnyei, de nem lehetnek statikus tagjai.
A fordtprogram ltalban nem tudhatja, hogy az uni melyik tagja van hasznlatban, va-
gyis nem ismert, hogy milyen tpus objektum van az uniban. Ezrt egy uninak nem le-
het olyan tagja, amelynek konstruktorral vagy destruktorral rendelkezik, mert akkor nem
lehetne a helyes memriakezelst biztostani, illetve azt, hogy az uni megsemmislsvel
a megfelel destruktor hvdik meg.
Az unik felhasznlsa leginkbb alacsony szinten vagy olyan osztlyok belsejben trt-
nik, amelyek nyilvntartjk, hogy mi van az uniban (10.6[20]).
10.5. Tancsok
[1] A fogalmakat osztlyokra kpezzk le. 10.1.
[2] Csak akkor hasznljunk nyilvnos adatokat (struct-okat), amikor tnyleg csak
adatok vannak s nincs rjuk nzve invarinst ignyl felttel. 10.2.8.
[3] A konkrt tpusok a legegyszerbb osztlyok. Hacsak lehet, hasznljunk inkbb
konkrt tpust, mint bonyolultabb osztlyokat vagy egyszer adatszerkezeteket.
10.3.
[4] Egy fggvny csak akkor legyen tagfggvny, ha kzvetlenl kell hozzfrnie
az osztly brzolshoz. 10.3.2.
[5] Hasznljunk nvteret arra, hogy nyilvnvalv tegyk egy osztlynak s segd-
fggvnyeinek sszetartozst. 10.3.2.
[6] Egy tagfggvny, ha nem vltozatja meg az objektumnak az rtkt, legyen
const tagfggvny. 10.2.6.
[7] Egy fggvny, amelynek hozz kell frnie az osztly brzolshoz, de nem
10. Osztlyok 339
szksges, hogy egy objektumon keresztl hvjuk meg, legyen statikus tagfgg-
vny. 10.2.4.
[8] Az osztlyra llapotbiztostit (invarins) a konstruktorban lltsunk be. 10.3.1.
[9] Ha egy konstruktor lefoglal valamilyen erforrst, akkor legyen destruktora az
osztlynak, amelyik felszabadtja azt. 10.4.1.
[10] Ha egy osztlynak van mutat tagja, akkor legyenek msol mveletei (msol
konstruktora s msol rtkadsa). 10.4.4.1.
[11] Ha egy osztlynak van referencia tagja, valsznleg szksge lesz msol m-
veletekre (msol konstruktorra s msol rtkadsra) is. 10.4.6.3.
[12] Ha egy osztlynak szksge van msol mveletre vagy destruktorra, valsz-
nleg szksge lesz konstruktorra, destruktorra, msol konstruktorra s mso-
l rtkadsra is. 10.4.4.1.
[13] A msol rtkadsnl gyeljnk az nmagval val rtkadsra. 10.4.4.1.
[14] Msol konstruktor rsakor gyeljnk arra, hogy minden szksges elemet
msoljunk (gyeljnk az alaprtelmezett kezdeti rtkadsra). 10.4.4.1.
[15] Ha j taggal bvtnk egy osztlyt, ellenrizzk, nincsenek-e felhasznli
konstruktorok, amelyekben kezdrtket kell adni az j tagnak. 10.4.6.3.
[16] Hasznljunk felsorol konstansokat, ha egsz konstansokra van szksg egy
osztly deklarcijban. 10.4.6.2.
[17] Globlis vagy nvtrhez tartoz objektumok hasznlatakor kerljk a vgrehaj-
tsi sorrendtl val fggst. 10.4.9.
[18] Hasznljunk els hasznlatot jelz kapcsolkat, hogy a vgrehajtsi sorrendtl
val fggst a lehet legkisebbre cskkentsk. 10.4.9.
[19] Gondoljunk arra, hogy az ideiglenes objektumok annak a teljes kifejezsnek
a vgn megsemmislnek, amelyben ltrejttek. 10.4.10.
10.6. Gyakorlatok
1. (*1) Talljuk meg a hibt a 10.2.2-beli Date::add_year() fggvnyben. Aztn
talljunk mg kt tovbbi hibt a 10.2.7-beli vltozatban.
2. (*2.5) Fejezzk be s prbljuk ki a Date osztlyt. rjuk jra gy, hogy az adat-
brzolsra az 1970.01.01. ta eltelt napokat hasznljuk.
3. (*2) Keressnk egy kereskedelmi hasznlatban lev Date osztlyt. Elemezzk
az ltala nyjtott szolgltatsokat. Ha lehetsges, vitassuk meg az osztlyt egy
tnyleges felhasznlval.
4. (*1) Hogyan rjk el a Chrono nvtr Date osztlynak set_default fggvnyt
(10.3.2)? Adjunk meg legalbb hrom vltozatot.
5. (*2) Hatrozzuk meg a Histogram osztlyt, amely a konstruktorban paramter-
Absztrakcis mdszerek 340
knt megadott idtartomnyokra vonatkoz gyakorisgokat tartja nyilvn. Biz-
tostsunk mveletet a grafikon kiratsra s kezeljk az rtelmezsi tartom-
nyon kvl es rtkeket is.
6. (*2) Hatrozzunk meg osztlyokat, amelyek bizonyos (pldul egyenletes vagy
exponencilis) eloszlsok szerinti vletlen szmokat adnak. Mindegyik osztly-
nak legyen egy konstruktora, amely az eloszlst megadja, s egy draw fggv-
nye, amely a kvetkez rtket adja vissza.
7. (*2.5) Ksztsk el a Table osztlyt, amely (nvrtk) prokat trol. Ezutn m-
dostsuk a szmolgp programot (6.1), hogy az a map helyett a Table osztlyt
hasznlja. Hasonltsuk ssze a kt vltozatot.
8. (*2) rjuk jra a 7.10[7]-beli Tnode-ot, mint olyan osztlyt, amelynek konstruk-
torai, destruktorai stb. vannak. Adjuk meg a Tnode-ok egy fjt, mint osztlyt
(konstruktorokkal s destruktorokkal).
9. (*3) Hatrozzuk meg, ksztsk el s ellenrizzk az Intset osztlyt, amely eg-
szek halmazt brzolja. Legyen meg az uni, a metszet, s a szimmetrikus dif-
ferencia mvelet is.
10. (*1.5) Mdostsuk az Intset osztlyt, hogy csompontok (Node objektumok) hal-
mazt jelentse, ahol a Node egy meghatrozott adatszerkezet.
11. (*3) Hozzunk ltre egy olyan osztlyt, amely egsz konstansokbl s a +, -, * s
/ mveletekbl ll egyszer aritmetikai kifejezseket kpes elemezni, kirt-
kelni, trolni s kirni. A nyilvnos fellet ilyesmi legyen:
class Expr {
// ...
public:
Expr(const char*);
int eval();
void print();
};
Az Expr::Expr() konstruktor karakterlnc paramtere a kifejezs. Az Expr::eval()
fggvny visszaadja a kifejezs rtkt, az Expr::print() pedig brzolja azt
a cout-on. A program gy nzhet ki:
Expr x("123/4+123*4-3");
cout << "x = " << x.eval() << "\n";
x.print();
Hatrozzuk meg az Expr osztlyt ktflekppen: egyszer mint csompontok
lncolt listjt, msszor egy karakterlnccal brzolva. Ksrletezznk a kifejezs
klnbz kiratsaival: teljesen zrjelezve, a mveleti jelet uttagknt hasz-
nlva, assembly kddal stb.
10. Osztlyok 341
12. (*2) Hatrozzuk meg a Char_queue osztlyt, hogy a nyilvnos fellet ne fgg-
jn az brzolstl. Ksztsk el a Char_queue-t mint (a) lncolt listt, illetve (b)
vektort.
13. (*3) Tervezznk egy szimblumtbla s egy szimblumtbla-elem osztlyt vala-
mely nyelv szmra. Nzzk meg az adott nyelv egy fordtprogramjban, ho-
gyan nznek ki ott az igazi szimblumtblk.
14. (*2) Mdostsuk a 10.6[11]-beli kifejezsosztlyt, hogy vltozkat is kezelni tud-
jon, valamint a = rtkad mveletet is. Hasznljuk a 10.6[131]-beli szimblum-
tbla osztlyt.
15. (*1) Adott a kvetkez program:
#include <iostream>
int main()
{
std::cout << "Hell, vilg!\n";
}
Mdostsuk gy, hogy a kvetkez kimenetet adja:
Kezdeti rtkads
Hell, vilg!
Takarts
A main() fggvnyt semmilyen mdon nem vltoztathatjuk meg.
16. (*2) Hatrozzunk meg egy olyan Calculator osztlyt, amilyet a 6.1-beli fggv-
nyek nagyrszt megvalstanak. Hozzunk ltre Calculator objektumokat s al-
kalmazzuk azokat a cin-bl szrmaz bemenetre, a parancssori paramterekre
s a programban trolt karakterlncokra. Tegyk lehetv a kimenetnek a be-
menethez hasonl mdon tbbfle helyre val irnytst.
17. (*2) Hatrozzunk meg kt osztlyt, mindegyikben egy-egy statikus taggal, gy,
hogy mindegyik ltrehozshoz a msikra hivatkozunk. Hol fordulhat el ilyes-
mi igazi kdban? Hogyan lehet mdostani az osztlyokat, hogy kikszbljk
a vgrehajtsi sorrendtl val fggst?
18. (*2.5) Hasonltsuk ssze a Date osztlyt (10.3) az 5.9[13] s a 7.10[19] feladat-
ra adott megoldssal. rtkeljk a megtallt hibkat s gondoljuk meg, milyen
klnbsgekkel kell szmolni a kt osztly mdostsakor.
19. (*3) rjunk olyan fggvnyt, amely egy istream-bl s egy vector<string>-bl ki-
indulva elkszt egy map<string,vector<int> > objektumot, amely minden ka-
rakterlncot s azok elfordulsnak sorszmt tartalmazza. Futtassuk a progra-
mot egy olyan szvegfjllal, amely legalbb 1000 sort tartalmaz, s legalbb 10
Absztrakcis mdszerek 342
Opertorok tlterhelse
Amikor n hasznlok egy
szt, azt rtem alatta,
amit n akarok se tbbet,
se kevesebbet.
(Humpty Dumpty)
Jells Opertor fggvnyek Egy- s ktoperandus mveleti jelek Az opertorok
elre meghatrozott jelentse Az opertorok felhasznli jelentse Opertorok s nv-
terek Komplex szm tpusok Tag s nem tag opertorok Vegyes md aritmetika
Kezdeti rtkads Msols Konverzik Literlok Segdfggvnyek Konverzis
opertorok A tbbrtelmsg feloldsa Bart fggvnyek s osztlyok Tagok s ba-
rt fggvnyek Nagy objektumok rtkads s kezdeti rtkads Indexels Fgg-
vnyhvs Indirekci Nvels s cskkents Egy karakterlnc osztly Tancsok
Gyakorlatok
11
11.1. Bevezets
Minden mszaki szakterletnek s a legtbb nem mszakinak is kialakultak a maga
megszokott rvidtsei, amelyek knyelmess teszik a gyakran hasznlt fogalmak kifejez-
st, trgyalst. Az albbi pldul
x+y*z
vilgosabb szmunkra, mint a
vegyk y-t z-szer s az eredmnyt adjuk x-hez
Nem lehet elgg megbecslni a szoksos mveletek tmr jellsnek fontossgt.
A legtbb nyelvvel egytt a C++ is tmogat egy sor, a beptett tpusokra vonatkoz mve-
letet. A legtbb fogalomnak, amelyre mveleteket szoktak alkalmazni, azonban nincs meg-
felelje a beptett tpusok kztt, gy felhasznli tpussal kell azokat brzolni. Pldul ha
komplex szmokkal akarunk szmolni, ha mtrix-mveletekre, logikai jellsekre vagy ka-
rakterlncokra van szksgnk a C++-ban, osztlyokat hasznlunk, hogy ezeket a fogalma-
kat brzoljuk. Ha ezekre az osztlyokra vonatkoz mveleteket definilunk, megszokot-
tabb s knyelmesebb jells felhasznlsval kezelhetjk az objektumokat, mintha csak az
alapvet fggvny-jellst hasznlnnk.
class complex { // nagyon leegyszerstett complex tpus
double re, im;
public:
complex(double r, double i) : re(r), im(i) { }
complex operator+(complex);
complex operator*(complex);
};
Itt pldul a komplex szm fogalmnak egy egyszer megvalstst lthatjuk. Egy complex
rtket egy ktszeres pontossg lebegpontos szmpr brzol, melyet a + s a * mve-
letek kezelnek. A felhasznl adja meg a complex::operator+() s complex::operator*()
opertorokat, hogy rtelmezze a + s * mveleteket. Ha pldul b s c complex tpusak,
akkor a b+c a b.operator(c)-t jelenti. Ezek utn kzeltleg meghatrozhatjuk a complex sz-
mokat tartalmaz kifejezsek megszokott jelentst:
Absztrakcis mdszerek 344
void f()
{
complex a = complex(1, 3.1);
complex b = complex(1.2, 2);
complex c = b;
a = b+c;
b = b+c*a;
c = a*b+complex(1,2);
}
A szoksos kirtkelsi szablyok rvnyesek, gy a msodik kifejezs azt jelenti, hogy
b=b+(c*a), s nem azt, hogy b=(b+c)*a.
Az opertorok tlterhelsnek legnyilvnvalbb alkalmazsai kzl sok konkrt tpusokra
vonatkozik (10.3). Az opertorok tlterhelse azonban nemcsak konkrt tpusoknl hasz-
nos. ltalnos s absztrakt felletek felptsnl pldul gyakran hasznlunk olyan oper-
torokat, mint a ->, a [ ] s a ().
11.2. Opertor fggvnyek
A kvetkez opertorok (6.2) jelentst meghatroz fggvnyeket megadhatjuk:
+ - * / % ^ &
| ~ ! = < > +=
-= *= /= %= ^= &= |=
<< >> >>= <<= == != <=
>= && || ++ -- ->* ,
-> [ ] () new new[ ] delete delete[ ]
A kvetkezknek viszont nem lehet felhasznli jelentst tulajdontani:
:: (hatkr-felolds, 4.9.4, 10.2.4)
. (tagkivlaszts, 5.7)
.* (tagkivlaszts a tagra hivatkoz mutatn keresztl, 15.5)
Ezek olyan opertorok (mveleti jelek), amelyek msodik operandusknt nem rtket, ha-
nem nevet vrnak s a tagokra val hivatkozs alapvet mdjai. Ha tl lehetne terhelni eze-
ket azaz ha a felhasznl hatrozhatn meg jelentsket akkor ez rdekes mellkhat-
sokkal jrhatna [Stroustrup, 1994]. A hromparamter feltteles-kifezs opertor, a ?:
(6.3.2) sem terhelhet tl, mint ahogy a sizeof (4.6) s a typeid (15.4.4) sem.
11. Opertorok tlterhelse 345
j mveleti jeleket sem adhatunk meg; ehelyett a fggvnyhvsi jells hasznlhat, ha
a rendelkezsre llkon kvl tovbbi opertorokra is szksg van. gy pldul ne **-ot
hasznljunk, hanem azt, hogy pow(). Ezek a megszortsok tl szigornak tnhetnek, de ru-
galmasabb szablyok knnyen az egyrtelmsg elvesztshez vezetnnek. Els pillants-
ra nyilvnvalnak s egyszernek tnhet a ** opertort hasznlni a hatvnyozsra, de gon-
doljunk csak meg: a ** mveleti jel balrl kssn, mint a Fortranban, vagy jobbrl, mint az
Algolban? Az a**p kifejezst hogyan rtelmezzk: mint a*(*p)-t vagy mint (a)**(p)-t?
Az opertor fggvnyek neve az operator kulcsszbl s azt kveten magbl az oper-
torbl ll; pldul operator <<. Az opertor fggvnyeket ugyangy deklarlhatjuk s hv-
hatjuk meg, mint a tbbi fggvnyt. Az opertorral val jells csak az opertor fggvny
kzvetlen meghvsnak rvidtse:
void f(complex a, complex b)
{
complex c = a + b; // rvid forma
complex d = a.operator+(b); // explicit hvs
}
A complex elz definicijt adottnak vve a fenti kt kezdeti rtkads jelentse azonos.
11.2.1. Egy- s ktoperandus mveletek
Ktoperandus mveleti jelet egyparamter nem statikus tagfggvnyknt vagy ktpara-
mter nem tag fggvnyknt definilhatunk. Ha @ ktoperandus mveletet jell, akkor
aa@bb vagy aa.operator@(bb)-t, vagy operator@(aa,bb)-t jelli. Ha mindkett rtelmezett,
a tlterhels-feloldsi szablyok (7.4) dntik el, melyik alkalmazhat, illetve hogy egylta-
ln brmelyik alkalmazhat-e:
class X {
public:
void operator+(int);
X(int);
};
void operator+(X,X);
void operator+(X,double);
void f(X a)
{
a+1; // a.operator+(1)
1+a; // ::operator+(X(1),a)
a+1.0; // ::operator+(a,1.0)
}
Absztrakcis mdszerek 346
Az egyoperandus (akr el-, akr uttagknt hasznlt) mveleti jelek paramter nlkli
nem statikus tagfggvnyknt vagy egyparamter nem tag fggvnyknt definilhatk.
Ha @ eltag s egyoperandus mveletet jell, akkor @aa vagy aa.operator@()-t, vagy
operator@(aa)-t jelli. Ha mindkett rtelmezett, a tlterhels-feloldsi szablyok (7.4)
dntik el, melyik alkalmazhat, illetve hogy egyltaln brmelyik alkalmazhat-e. Ha @
uttag s egyoperandus mveletet ad meg, akkor aa@ vagy aa.operator@(int)-et, vagy
operator@(aa,int)-et jelli. (Ezt rszletesebben a 11.11 pont rja le.) Ha mindkett rtel-
mezett, ismt csak a tlterhels-feloldsi szablyok (7.4) dntik el, melyik alkalmazhat, il-
letve hogy egyltaln brmelyik alkalmazhat-e. Opertort csak a nyelvi szablyoknak
megfelelen definilhatunk (A.5), gy nem lehet pldul egyoperandus % vagy
hromoperandus + mveletnk:
class X {
// tagok (a 'this' mutat automatikus):
X* operator&(); // eltagknt hasznlt egyoperandus & (cm)
X operator&(X); // ktoperandus & (s)
X operator++(int); // uttagknt hasznlt nvel opertor (lsd 11.1)
X operator&(X,X); // hiba: hromoperandus
X operator/(); // hiba: egyoperandus /
};
// nem tag fggvnyek :
X operator-(X); // eltagknt hasznlt egyoperandus mnusz (mnusz eljel)
X operator-(X,X); // ktoperandus mnusz (kivons)
X operator--(X&,int); // uttagknt hasznlt cskkent opertor
X operator-(); // hiba: nincs operandus
X operator-(X,X,X); // hiba: hromoperandus
X operator%(X); // hiba: egyoperandus %
A [ ] opertort a 11.8, a () opertort a 11.9, a -> opertort a 11.10, a ++ s -- opertorokat
a 11.11, a memriafoglal s felszabadt opertorokat a 6.2.6.2, a 10.4.11 s a 15.6 pon-
tokban rjuk le.
11.2.2. Az opertorok elre meghatrozott jelentse
A felhasznli opertorok jelentsnek csak nhny elrsnak kell megfelelnik.
Az operator=, operator[ ], operator() s az operator-> nem statikus tagfggvny kell, hogy
legyen; ez biztostja, hogy els operandusuk balrtk (lvalue) lesz (4.9.6).
11. Opertorok tlterhelse 347
Bizonyos beptett opertorok jelentse megegyezik ms opertoroknak ugyanazon para-
mterre sszetetten gyakorolt hatsval. Pldul ha a egy int, akkor ++a jelentse meg-
egyezik a+=1-gyel, ami pedig azt jelenti, hogy a=a+1. Hacsak a felhasznl nem gondos-
kodik rla, ilyen sszefggsek nem llnak fenn a felhasznli opertorokra, gy a fordt-
program pldul nem fogja kitallni a Z::operator+=() mvelet jelentst pusztn abbl,
hogy megadtuk a Z::operator+() s Z::operator=() mveleteket.
Hagyomnyosan az = (rtkad), a & (cmkpz) s a , (vessz; 6.2.2) opertorok elre
definiltak, ha osztlyba tartoz objektumra alkalmazzuk azokat. Ezeket az elre meghat-
rozott jelentseket az ltalnos felhasznl ell elrejthetjk, ha privtknt adjuk meg azokat:
class X {
private:
void operator=(const X&);
void operator&();
void operator,(const X&);
// ...
};
void f(X a, X b)
{
a = b; // hiba: az rtkad opertor privt
&a; // hiba: a cm opertor (&) privt
a,b; // hiba: a vessz opertor (,) privt
}
Alkalmas mdon definilva azonban j jelents is tulajdonthat nekik.
11.2.3. Opertorok s felhasznli tpusok
Az opertoroknak tagfggvnynek kell lennik vagy paramtereik kztt legalbb egy fel-
hasznli tpusnak kell szerepelnie (kivtelek ez all a new s delete opertorok jelentst
fellbrl fggvnyek.) Ez a szably biztostja, hogy a programoz egy kifejezs rtelmt
csak akkor mdosthassa, ha legalbb egy felhasznli tpus elfordul benne. Ebbl ad-
dan nem definilhat olyan opertor, amely kizrlag mutatkkal mkdik. A C++ teht
bvthet, de nem vltoztathat meg, (az osztlyba tartoz objektumokra vonatkoz =, &
s , opertorokat kivve).
Az olyan opertorok, melyeket arra sznunk, hogy els paramterknt valamilyen alapt-
pust fogadjanak el, nem lehetnek tagfggvnyek. Vegyk pldul azt az esetet, amikor egy
complex vltozt akarunk a 2 egszhez hozzadni: az aa+2 kifejezst alkalmas tagfggvny
Absztrakcis mdszerek 348
meglte esetn rtelmezhetjk aa.operator+(2)-knt, de a 2+aa kifejezst nem, mert nincs
int osztly, amelynek + olyan tagfggvnye lehetne, hogy a 2.operator(aa) eredmnyre jus-
sunk. De ha lenne is, akkor is kt tagfggvny kellene ahhoz, hogy 2+aa-val s aa+2-vel
is megbirkzzunk. Minthogy a fordtprogram nem ismeri a felhasznli + mvelet jelent-
st, nem ttelezheti fel rla a felcserlhetsget (kommutativitst), hogy annak alapjn
2+aa-t mint aa+2-t kezelje. Az ilyesmit rendszerint nem tag fggvnyekkel kezelhetjk
(11.3.2, 11.5).
A felsorolsok felhasznli tpusok, gy rjuk is rtelmezhetnk opertorokat:
enum Day { sun, mon, tue, wed, thu, fri, sat };
Day& operator++(Day& d)
{
return d = (sat==d) ? sun : Day(d+1);
}
A fordtprogram minden kifejezst ellenriz, hogy nem lp-e fel tbbrtelmsg. Ha egy
felhasznli opertor is biztost lehetsges rtelmezst, a kifejezs ellenrzse a 7.4 pont-
ban lertak szerint trtnik.
11.2.4. Nvterek opertorai
Az opertor mindig valamilyen osztly tagja vagy valamilyen nvtrben (esetleg a globlis-
ban) definilt. Vegyk pldul a standard knyvtr karakterlnc-kirsi mveletnek egy-
szerstett vltozatt:
namespace std { // egyszerstett std
class ostream {
// ...
ostream& operator<<(const char*);
};
extern ostream cout;
class string {
// ...
};
ostream& operator<<(ostream&, const string&);
}
11. Opertorok tlterhelse 349
int main()
{
char* p = "Hell";
std::string s = "vilg";
std::cout << p << ", " << s << "!\n";
}
Ez termszetesen azt rja ki, hogy Hell, vilg!. De mirt? Vegyk szre, hogy nem tettem
mindent elrhetv az std nvtrbl azltal, hogy azt rtam volna:
using namespace std;
Ehelyett az std:: eltagot alkalmaztam a string s a cout eltt. Vagyis a legrendesebben vi-
selkedve nem szennyeztem be a globlis nvteret s egyb mdon sem vezettem be szk-
sgtelen fggseket.
A C stlus karakterlncok (char*) kimeneti mvelete az std::ostream egy tagja, gy
std::cout << p
jelentse definci szerint:
std::cout.operator<<(p)
Mivel azonban az std::ostream-nek nincs olyan tagja, amelyet az std::string-re alkalmazhat-
nnk, gy
std::cout << s
jelentse:
operator<<(std::cout,s)
A nvtrben definilt opertorokat ugyangy operandusuk tpusa szerint tallhatjuk meg,
mint ahogy a fggvnyeket paramtereik tpusa szerint (8.2.6). Minthogy a cout az std nv-
trben van, gy az std is szba kerl, amikor a << szmra alkalmas defincit keresnk. gy
aztn a fordtprogram megtallja s felhasznlja a kvetkez fggvnyt:
std::operator<<(std::ostream&, const std::string&)
Absztrakcis mdszerek 350
Jelljn @ egy ktoperandus mveletet. Ha x az X tpusba, az y pedig az Y tpusba tarto-
zik, akkor x@y feloldsa, azaz a paramterek tpusnak megfelel fggvny megkeresse
a kvetkezkppen trtnik:
Ha X egy osztly, keresnk egy operator@-t, amely az X osztlynak vagy vala-
melyik bzisosztlynak tagfggvnye.
Keresnk egy operator@ deklarcit az x@y kifejezst krlvev krnyezetben.
Ha X az N nvtr tagja, az operator@-t az N nvtrben keressk.
Ha Y az M nvtr tagja, az operator@-t az M nvtrben keressk.
Ha az operator@ tbbfle deklarcijt is megtalltuk, a feloldsi szablyokat kell alkalmaz-
ni (7.4), hogy a legjobb egyezst megtalljuk, ha egyltaln van ilyen. Ez a keressi eljrs
csak akkor alkalmazand, ha legalbb egy felhasznli tpus szerepel az operandusok k-
ztt, ami azt is jelenti, hogy a felhasznli konverzikat (11.3.2, 11.4) is figyelembe
vesszk. (A typedef-fel megadott nevek csak szinonimk, nem felhasznli tpusok
(4.9.7).) Az egyoperandus mveletek feloldsa hasonlan trtnik.
Jegyezzk meg, hogy az opertorok feloldsban a tagfggvnyek nem lveznek elnyt
a nem tag fggvnyekkel szemben. Ez eltr a nvvel megadott fggvnyek keresstl
(8.2.6). Az opertorok el nem rejtse biztostja, hogy a beptett opertorok nem vlnak el-
rhetetlenn, s hogy a felhasznl a meglev osztlydeklarcik mdostsa nlkl adhat
meg j jelentseket. A szabvnyos iostream knyvtr pldul definilja a << tagfggvnye-
ket a beptett tpusokra, a felhasznl viszont a felhasznli tpusoknak a << mvelettel va-
l kimenetre kldst az ostream osztly (21.2.1) mdostsa nlkl definilhatja.
11.3. Komplex szm tpusok
A komplex szmoknak a bevezetben emltett megvalstsa tl keveset nyjt ahhoz, hogy
brkinek is tessk. Egy matematika tanknyvet olvasva azt vrnnk, hogy a kvetkez
fggvny mkdik:
void f()
{
complex a = complex(1,2);
complex b = 3;
complex c = a+2.3;
complex d = 2+b;
complex e = -b-c;
b = c*2*c;
}
11. Opertorok tlterhelse 351
Radsul elvrnnk, hogy ltezzk nhny tovbbi mvelet is, pldul a == az sszehason-
ltsra s a << a kimenetre, s mg a matematikai fggvnyek (mint a sin() s a sqrt()) meg-
felel kszlett is ignyelnnk.
A complex osztly egy konkrt tpus, gy felptse megfelel a 10.3-beli elveknek. Rad-
sul a komplex aritmetika felhasznli olyan nagy mrtkben ptenek az opertorokra,
hogy a complex osztly definilsa az opertor-tlterhelsre vonatkoz szinte valamennyi
szably alkalmazst ignyli.
11.3.1. Tag s nem tag opertorok
Elnys lenne, ha minl kevesebb fggvny frne hozz kzvetlenl egy adott objektum
bels adatbrzolshoz. Ezt gy rhetjk el, ha csak azokat az opertorokat adjuk meg
magban az osztlyban, amelyek rtelmknl fogva mdostjk els paramterket, mint
pldul a +=. Azokat az opertorokat, amelyek csak egy j rtket lltanak el paramte-
reik alapjn, mint pldul a +, az osztlyon kvl definilom s az alapvet opertorok se-
gtsgvel valstom meg:
class complex {
double re, im;
public:
complex& operator+=(complex a); // hozz kell frni az brzolshoz
// ...
};
complex operator+(complex a, complex b)
{
complex r = a;
return r += b; // az brzols elrse a += opertoron keresztl
}
Ezen deklarcik alapjn mr lerhatjuk a kvetkezt:
void f(complex x, complex y, complex z)
{
complex r1 = x+y+z; // r1 = operator+(operator+(x,y),z)
complex r2 = x; // r2 = x
r2 += y; // r2.operator+=(y)
r2 += z; // r2.operator+=(z)
}
Esetleges hatkonysgi klnbsgektl eltekintve r1 s r2 kiszmtsa egyenrtk.
Absztrakcis mdszerek 352
Az sszetett rtkad opertorokat, pldul a +=-t s a *=-t ltalban knnyebb definilni,
mint egyszer megfeleliket, a + s * opertorokat. Ez tbbnyire meglepst kelt, pedig
pusztn abbl kvetkezik, hogy az sszeadsnl 3 objektum jtszik szerepet (a kt ssze-
adand s az eredmny), mg a += opertornl csak kett. Az utbbi esetben hatkonyabb
a megvalsts, ha nem hasznlunk ideiglenes vltozkat:
inline complex& complex::operator+=(complex a)
{
re += a.re;
im += a.im;
return *this;
}
A fenti megoldsnl nincs szksg ideiglenes vltozra az eredmny trolsra s a ford-
tprogram szmra is knnyebb feladat a teljes helyben kifejts.
Egy j fordtprogram az optimlishoz kzeli kdot kszt a sima + opertor hasznlata ese-
tn is. De nincs mindig j optimalizlnk s nem minden tpus olyan egyszer, mint
a complex, ezrt a 11.5 pont trgyalja, hogyan adhatunk meg olyan opertorokat, amelyek
hozzfrhetnek az osztly brzolshoz.
11.3.2. Vegyes md aritmetika
Ahhoz, hogy a
complex d = 2+b;
kdot kezelni tudjuk, olyan + opertorra van szksgnk, amely klnbz tpus param-
tereket is elfogad. A Fortran kifejezsvel lve teht vegyes md aritmetikra (mixed-
mode arithmetic) van szksg. Ezt knnyen megvalsthatjuk, ha megadjuk az opertor
megfelel vltozatait:
class complex {
double re, im;
public:
complex& operator+=(complex a) {
re += a.re;
im += a.im;
return *this;
}
11. Opertorok tlterhelse 353
complex& operator+=(double a) {
re += a;
return *this;
}
// ...
};
complex operator+(complex a, complex b)
{
complex r = a;
return r += b; // complex::operator+=(complex)-et hvja meg
}
complex operator+(complex a, double b)
{
complex r = a;
return r += b; // complex::operator+=(double)-t hvja meg
}
complex operator+(double a, complex b)
{
complex r = b;
return r += a; // complex::operator+=(double)-t hvja meg
}
Egy double hozzadsa egy komplex szmhoz egyszerbb mvelet, mint egy komplex
szm hozzadsa; ezt tkrzik a fenti defincik is. A double operandust kezel mveletek
nem rintik a komplex szm kpzetes rszt, gy hatkonyabbak lesznek.
A fenti deklarcik mellett most mr lerhatjuk a kvetkezt:
void f(complex x, complex y)
{
complex r1 = x+y; // operator+(complex,complex)-et hvja meg
complex r2 = x+2; // operator+(complex,double)-t hvja meg
complex r3 = 2+x; // operator+(double,complex)-et hvja meg
}
11.3.3. Kezdeti rtkads
A complex vltozknak skalrokkal val kezdeti s egyszer rtkads kezelshez szks-
gnk van a skalrok (egsz vagy lebegpontos (vals) rtkek) complex-sz talaktsra:
complex b = 3; // b.re=3, b.im=0-t kell jelentenie
Absztrakcis mdszerek 354
Az olyan konstruktor, amely egyetlen paramtert vr, konverzit jelent a paramter tpus-
rl a konstruktor tpusra:
class complex {
double re, im;
public:
complex(double r) : re(r), im(0) { }
// ...
};
Ez a konstruktor a vals szmegyenesnek a komplex skba val szoksos begyazst
jelenti.
Egy konstruktor mindig azt rja el, hogyan hozhatunk ltre egy adott tpus rtket. Ha egy
adott tpus rtket kell ltrehozni egy (kezdeti vagy egyszer) rtkad kifejezs rtk-
bl s ebbl egy konstruktor ltre tudja hozni a kvnt tpus rtket, akkor konstruktort al-
kalmazunk. Ezrt az egyparamter konstruktorokat nem kell explicit meghvnunk:
complex b = 3;
A fenti egyenrtk a kvetkezvel:
complex b = complex(3);
Felhasznli konverzira csak akkor kerl sor automatikusan, ha az egyrtelm (7.4). Ar-
ra nzve, hogyan adhatunk meg csak explicite meghvhat konstruktorokat, lsd a 11.7.1
pontot.
Termszetesen szksgnk lesz egy olyan konstruktorra is, amelynek kt double tpus pa-
ramtere van, s a (0,0) kezdrtket ad alaprtelmezett konstruktor is hasznos:
class complex {
double re, im;
public:
complex() : re(0), im(0) { }
complex(double r) : re(r), im(0) { }
complex(double r, double i) : re(r), im(i) { }
// ...
};
11. Opertorok tlterhelse 355
Alaprtelmezett paramter-rtkeket hasznlva gy rvidthetnk:
class complex {
double re, im;
public:
complex(double r =0, double i =0) : re(r), im(i) { }
// ...
};
Ha egy tpusnak van konstruktora, a kezdeti rtkadsra nem hasznlhatunk kezdrtk-
listt (5.7, 4.9.5):
complex z1 = { 3 }; // hiba: complex rendelkezik konstruktorral
complex z2 = { 3, 4 }; // hiba: complex rendelkezik konstruktorral
11.3.4. Msols
A megadott konstruktorokon kvl a complex osztlynak lesz egy alaprtelmezett msol
konstruktora (10.2.5) is. Az alaprtelmezett msol konstruktor egyszeren lemsolja a ta-
gokat. A mkdst pontosan gy hatrozhatnnk meg:
class complex {
double re, im;
public:
complex(const complex& c) : re(c.re), im(c.im) { }
// ...
};
n elnyben rszestem az alaprtelmezett msol konstruktort azon osztlyok esetben,
amelyeknl ez megfelel. Rvidebb lesz a kd, mintha brmi mst rnk, s a kd olvas-
jrl felttelezem, hogy ismeri az alaprtelmezett mkdst. A fordtprogram is ismeri s
azt is, hogyan lehet azt optimalizlni. Ezenkvl pedig sok tag esetn fraszt dolog kzzel
kirni a tagonknti msolst s knny kzben hibzni (10.4.6.3).
A msol konstruktor paramtereknt referencit kell hasznlnom. A msol konstruktor ha-
trozza meg a msols jelentst belertve a paramter msolst is gy a
complex::complex(complex c) : re(c.re), im(c.im) { } // hiba
hibs, mert a fggvny meghvsa vgtelen rekurzihoz vezet.
Absztrakcis mdszerek 356
Ms, complex paramter fggvnyek esetben n rtk s nem referencia szerinti para-
mter-tadst hasznlok. Mindig az osztly ksztje dnt. A felhasznl szemszgbl nz-
ve nincs sok klnbsg egy complex s egy const complex& paramtert kap fggvny
kztt. Errl bvebben r a 11.6 pont.
Elvileg a msol konstruktort az ilyen egyszer kezdeti rtkadsoknl hasznljuk:
complex x = 2; // complex(2) ltrehozsa; ezzel adunk kezdrtket x-nek
complex y = complex(2,0); // complex(2,0) ltrehozsa; ezzel adunk kezdrtket y-nak
A fordtprogram azonban optimalizl s elhagyja a msol konstruktor meghvst. rhat-
tuk volna gy is:
complex x(2); // x kezdrtke 2
complex y(2,0); // y kezdrtke (2,0)
A complex-hez hasonl aritmetikai tpusok esetben jobban kedvelem az = jel hasznlatt.
Ha a msol konstruktort privtt tesszk (11.2.2) vagy ha egy konstruktort explicit-knt
adunk meg (11.7.1), az = stlus rtkads ltal elfogadhat rtkek krt a () stlus r-
tkads ltal elfogadotthoz kpest korltozhatjuk.
A kezdeti rtkadshoz hasonlan a kt azonos osztlyba tartoz objektum kztti rtk-
ads alaprtelmezs szerint tagonknti rtkadst jelent (10.2.5). A complex osztlynl er-
re a clra megadhatnnk kifejezetten a complex::operator= mveletet, de ilyen egyszer
osztly esetben erre nincs ok, mert az alaprtelmezett mkds pont megfelel.
A msol konstruktor akr a fordtprogram hozta ltre, akr a programoz rta nem-
csak a vltozk kezdrtknek belltsra hasznlatos, hanem paramter-tadskor, rtk
visszaadsakor s a kivtelkezelskor is (lsd 11.7). Ezek szerept a nyelv a kezdeti rtk-
adsval azonosknt hatrozza meg (7.1, 7.3, 14.2.1).
11.3.5. Konstruktorok s konverzik
A ngy alapvet aritmetikai mveletnek eddig hrom-hrom vltozatt hatroztuk meg:
complex operator+(complex,complex);
complex operator+(complex,double);
complex operator+(double,complex);
// ...
11. Opertorok tlterhelse 357
Ez frasztv vlhat, s ami fraszt, ott knnyen elfordulhatnak hibk. Mi lenne, ha min-
den paramter hromfle tpus lehetne? Minden egyparamter mveletbl hrom vlto-
zat kellene, a ktparamterekbl kilenc, a hromparamterekbl huszonht s gy to-
vbb. Ezek a vltozatok gyakran nagyon hasonlak. Valjban majdnem mindegyik gy
mkdik, hogy a paramtereket egy kzs tpusra alaktja, majd egy szabvnyos algorit-
must hajt vgre.
Ahelyett, hogy a paramterek minden lehetsges prostsra megadnnk egy fggvnyt,
tpuskonverzikra hagyatkozhatunk. Tegyk fel, hogy complex osztlyunknak van egy
olyan konstruktora, amely egy double rtket alakt complex-sz, gy a complex osztly sz-
mra elg egyetlen egyenlsg-vizsgl mveletet megadnunk:
bool operator==(complex,complex);
void f(complex x, complex y)
{
x==y; // jelentse operator==(x,y)
x==3; // jelentse operator==(x,complex(3))
3==y; // jelentse operator==(complex(3),y)
}
Lehetnek azonban okok, melyek miatt jobb kln fggvnyeket megadni. Egyes esetekben
pldul a konverzi tl bonyolult mvelet lehet, mskor bizonyos paramtertpusokra egy-
szerbb algoritmusok alkalmazhatk. Ahol ilyen okok nem lpnek fel jelents mrtkben,
ott a fggvny legltalnosabb formjt megadva (esetleg nhny kritikus vltozattal kieg-
sztve) s a konverzikra hagyatkozva elkerlhetjk, hogy a vegyes md aritmetikbl
addan nagyon sokfle fggvnyt kelljen megrnunk.
Ha egy fggvny vagy opertor tbb vltozattal rendelkezik, a fordtprogram feladata
a legalkalmasabb vltozat kivlasztsa, a paramtertpusok s a lehetsges (szabvnyos
vagy felhasznli) konverzik alapjn. Ha nincs legjobb vltozat, a kifejezs tbbrtelm s
hibs (lsd 7.4).
Az olyan objektumok, melyeket a konstruktor kzvetlen meghvsa vagy automatikus hasz-
nlata hozott ltre, ideiglenes vltoznak szmtanak s amint lehetsges, megsemmislnek
(lsd 10.4.10). A . s -> opertorok bal oldaln nem trtnik automatikus felhasznli
konverzi. Ez akkor is gy van, ha maga a . implicit (a kifejezsbe belertett):
void g(complex z)
{
3+z; // rendben: complex(3)+z
3.operator+=(z); // hiba: 3 nem egy osztly objektuma
3+=z; // hiba: 3 nem egy osztly objektuma
}
Absztrakcis mdszerek 358
Ezt kihasznlva egy mveletet tagfggvnny tve kifejezhetjk, hogy a mvelet bal oldali
operandusknt balrtket vr.
11.3.6. Literlok
Osztly tpus literlokat nem definilhatunk abban az rtelemben, ahogyan 1.2 s 1.2e3
double tpus literlok. Az alapvet tpusokba tartoz literlokat viszont gyakran hasznl-
hatjuk, ha a tagfggvnyek fel vannak ksztve a kezelskre. Az egyparamter
konstruktorok ltalnos eljrst biztostanak erre a clra. Egyszer s helyben kifejtett
(inline) konstruktorok esetben sszer a literl paramter konstruktorhvsokra mint li-
terlokra gondolni. A complex(3) kifejezst pldul n gy tekintem, mint egy complex r-
tk literlt, noha a sz technikai rtelmben vve nem az.
11.3.7. Kiegszt tagfggvnyek
Eddig csak konstruktorokat s aritmetikai mveleteket adtunk a complex osztlyhoz.
A tnyleges hasznlathoz ez kevs. A vals s a kpzetes rsz lekrdezse pldul srn
hasznlatos:
class complex {
double re, im;
public:
double real() const { return re; }
double imag() const { return im; }
// ...
};
A complex osztly tbbi tagfggvnyvel ellenttben a real() s az imag() nem vltoztatja
meg egy complex objektum rtkt, gy const-knt adhat meg.
A real() s imag() fggvnyek alapjn egy sor hasznos fggvnyt definilhatunk anlkl,
hogy azoknak hozzfrst kellene adnunk a complex osztly adatbrzolshoz:
inline bool operator==(complex a, complex b)
{
return a.real()==b.real() && a.imag()==b.imag();
}
Vegyk szre, hogy a vals s a kpzetes rszt elg olvasnunk, rnunk sokkal ritkbban kell.
11. Opertorok tlterhelse 359
Ha rszleges frisstsre van szksgnk, a kvetkezt rhatjuk:
void f(complex& z, double d)
{
// ...
z = complex(z.real(),d); // d hozzrendelse z.im-hez
}
Egy jl optimalizl fordt ebbl egyetlen rtkadst kszt.
11.3.8. Segdfggvnyek
Ha mindent sszerakunk, complex osztlyunk gy alakul:
class complex {
double re, im;
public:
complex(double r =0, double i =0) : re(r), im(i) { }
double real() const { return re; }
double imag() const { return im; }
complex& operator+=(complex);
complex& operator+=(double);
// -=, *=, s /=
};
Kiegsztsknt egy sor segdfggvnyt kell biztostanunk:
complex operator+(complex,complex);
complex operator+(complex,double);
complex operator+(double,complex);
// -, *, s /
complex operator-(complex); // egyoperandus mnusz
complex operator+(complex); // egyoperandus plusz
bool operator==(complex,complex);
bool operator!=(complex,complex);
istream& operator>>(istream&,complex&); // bemenet
ostream& operator<<(ostream&,complex); // kimenet
Absztrakcis mdszerek 360
Vegyk szre, hogy a real() s imag() fggvnyek szerepe alapvet az sszehasonlt fgg-
vnyek definilsban. A kvetkez segdfggvnyek is nagyrszt ezekre ptenek.
Megadhatnnk olyan fggvnyeket is, amelyek a polr-koordints jellst tmogatjk:
complex polar(double rho, double theta);
complex conj(complex);
double abs(complex);
double arg(complex);
double norm(complex);
double real(complex); // a knyelmesebb jellsrt
double imag(complex); // a knyelmesebb jellsrt
Vgl szksgnk lesz a tovbbi alapvet matematikai fggvnyekre:
complex acos(complex);
complex asin(complex);
complex atan(complex);
// ...
Felhasznli szemszgbl nzve az itt bemutatott complex osztly szinte azonos
a complex<double>-lal (lsd a standard knyvtrbeli <complex>-et, 22.5).
11.4. Konverzis opertorok
Konstruktorok hasznlata tpuskonverzi cljra knyelmes lehet, de nemkvnatos kvet-
kezmnyei vannak. Egy konstruktor nem tud
1. automatikus talaktst megadni felhasznli adattpusrl beptett adattpusra
(mert a beptett adattpusok nem osztlyok)
2. talaktst megadni egy jabban megadott osztlyrl egy rgebbire, a rgebbi
osztly deklarcijnak megvltoztatsa nlkl.
Ezeket a feladatokat az talaktand osztly konverzis (talakt) opertornak
definilsval oldhatjuk meg. Ha T egy tpus neve, akkor az X::operator T() fggvny hat-
11. Opertorok tlterhelse 361
rozza meg az X tpus T-re val konverzijt. Definilhatunk pldul a 6 bites, nem negatv
egszeket brzol Tiny osztlyt, melynek objektumait aritmetikai kifejezsekben szaba-
don keverhetjk egszekkel:
class Tiny {
char v;
void assign(int i) { if (i&~077) throw Bad_range(); v=i; }
public:
class Bad_range { };
Tiny(int i) { assign(i); }
Tiny& operator=(int i) { assign(i); return *this; }
operator int() const { return v; } // konverzi int tpusra
};
Amikor egy Tiny egy egsztl kap rtket vagy kezdrtket, ellenrizzk, hogy az rtk
a megengedett tartomnyba esik-e. Minthogy egy Tiny msolsakor nincs szksg az rtk-
ellenrzsre, az alaprtelmezett msol konstruktor s rtkads ppen megfelel.
Ahhoz, hogy a Tiny vltozkra is lehetv tegyk az egszeknl szoksos mveleteket, ha-
trozzuk meg a Tiny-rl int-re val automatikus konverzit, a Tiny::operator int()-et. Je-
gyezzk meg, hogy a konverzi cltpusa az opertor nevnek rsze s nem szabad kirni,
mint a konverzis fggvny visszatrsi rtkt:
Tiny::operator int() const { return v; } // helyes
int Tiny::operator int() const { return v; } // hiba
Ilyen tekintetben a konverzis opertor a konstruktorra hasonlt.
Ha egy int helyn egy Tiny szerepel, akkor arra a helyre a megfelel int rtk fog kerlni:
int main()
{
Tiny c1 = 2;
Tiny c2 = 62;
Tiny c3 = c2-c1; // c3 = 60
Tiny c4 = c3; // nincs tartomnyellenrzs (nem szksges)
int i = c1+c2; // i = 64
c1 = c1+c2; // tartomnyhiba: c1 nem lehet 64
i = c3-64; // i = -4
c2 = c3-64; // tartomnyhiba: c2 nem lehet -4
c3 = c4; // nincs tartomnyellenrzs (nem szksges)
}
Absztrakcis mdszerek 362
A konverzis fggvnyek klnsen hasznosak olyan adatszerkezetek kezelsekor, ame-
lyeknl az adatoknak (a konverzis opertor ltal definilt) kiolvassa egyszer feladat, el-
lenttben az rtkadssal s a kezdrtk-adssal.
Az istream s ostream tpusok egy konverzi segtsgvel tmogatjk az albbihoz hason-
l vezrlsi szerkezeteket:
while (cin>>x) cout<<x;
A cin>>x bemeneti mvelet egy istream& referencit ad vissza, amely automatikusan a cin
objektum llapott tkrz rtkre alaktdik. Ezt azutn a while utasts ellenrzi
(21.3.3.). ltalban azonban nem j tlet adatvesztssel jr automatikus konverzit meg-
hatrozni kt tpus kztt.
Clszer takarkoskodni a konverzis opertorok bevezetsvel. Ha tl sok van bellk,
az a kifejezsek tbbrtelmsghez vezethet. A tbbrtelmsget mint hibt jelzi ugyan
a fordtprogram, de kikszblni fradsgos lehet. Taln a legjobb eljrs az, ha kezdet-
ben nevestett fggvnyekkel vgeztetjk az talaktst (pldul X::make_int()). Ha ksbb
valamelyik ilyen fggvny annyira npszer lesz, hogy alkalmazsa nem elegns tbb,
akkor kicserlhetjk az X::operator int() konverzis opertorra.
Ha vannak felhasznli konverzik s felhasznli opertorok is, lehetsges, hogy tbbr-
telmsg lp fel a felhasznli s a beptett opertorok kztt:
int operator+(Tiny,Tiny);
void f(Tiny t, int i)
{
t+i; // hiba, tbbrtelm: operator+(t,Tiny(i)) vagy int(t)+i ?
}
Ezrt vagy felhasznli konverzikra ptsnk, vagy felhasznli opertorokra, de ne mind-
kettre.
11.4.1. Tbbrtelmsg
Egy X osztly objektum rtkadsa egy V tpus rtkkel akkor megengedett, ha van olyan
X::operator=(Z) rtkad opertor, amely szerint V tpus egyben Z is, vagy ha van egy egye-
di konverzi V-rl Z-re. A kezdeti rtkadsnl hasonl a helyzet.
11. Opertorok tlterhelse 363
Bizonyos esetekben a kvnt tpus rtket konstruktorok s konverzis opertorok ism-
telt alkalmazsval llthatjuk el. Ezt a helyzetet kzvetlen konverzival kell megoldani; az
automatikus felhasznli konverziknak csak egy szintje megengedett. Nha a kvnt tpu-
s rtk tbbflekppen is ltrehozhat, ez pedig hiba:
class X { /* ... */ X(int); X(char*); };
class Y { /* ... */ Y(int); };
class Z { /* ... */ Z(X); };
X f(X);
Y f(Y);
Z g(Z);
void k1()
{
f(1); // hiba: tbbrtelm f(X(1)) vagy f(Y(1))?
f(X(1)); // rendben
f(Y(1)); // rendben
g("Mack"); // hiba: kt felhasznli konverzi szksges; g(Z(X("Mack")))-et
// nem prbltuk
g(X("Doc")); // rendben: g(Z(X("Doc")))
g(Z("Suzy")); // rendben: g(Z(X("Suzy")))
}
A felhasznli konverzikat a fordtprogram csak akkor veszi figyelembe, ha szksgesek
egy hvs feloldshoz:
class XX { /* ... */ XX(int); };
void h(double);
void h(XX);
void k2()
{
h(1); // h(double(1)) vagy h(XX(1))? h(double(1))!
}
A h(1) hvs a h(double(1)) hvst jelenti, mert ehhez csak egy szabvnyos (s nem felhasz-
nli) konverzira van szksg (7.4). A konverzis szablyok se nem a legegyszerbben
megvalsthat, se nem a legegyszerbben lerhat, de nem is az elkpzelhet legltalno-
sabb szablyok. Viszont viszonylag biztonsgosak s alkalmazsukkal kevsb fordulnak
el meglep eredmnyek. A programoznak sokkal knnyebb egy tbbrtelmsget felol-
dani, mint megtallni egy hibt, amit egy nem sejtett konverzi alkalmazsa okoz.
Absztrakcis mdszerek 364
Az elemzs sorn alkalmazott szigoran alulrl felfel val halads elvbl az is kvetke-
zik, hogy a visszatrsi rtket nem vesszk figyelembe a tlterhelsek feloldsakor:
class Quad {
public:
Quad(double);
// ...
};
Quad operator+(Quad,Quad);
void f(double a1, double a2)
{
Quad r1 = a1+a2; // ktszeres pontossg sszeads
Quad r2 = Quad(a1)+a2; // Quad aritmetika kiknyszertse
}
Ezen tervezsi md vlasztsnak egyik oka, hogy a szigor alulrl felfel val halads el-
ve rthetbb, a msik pedig az, hogy nem a fordtprogram dolga eldnteni, milyen fok
pontossgot akar a programoz egy sszeadsnl.
Ha egy kezdeti vagy egyszer rtkads mindkt oldalnak eldlt a tpusa, akkor az rtk-
ads feloldsa ezen tpusok figyelembe vtelvel trtnik:
class Real {
public:
operator double();
operator int();
// ...
};
void g(Real a)
{
double d = a; // d = a.double();
int i = a; // i = a.int();
d = a; // d = a.double();
i = a; // i = a.int();
}
Az elemzs itt is alulrl felfel trtnik, egyszerre csak egy opertornak s paramtereinek
figyelembe vtelvel.
11. Opertorok tlterhelse 365
11.5. Bart fggvnyek
Amikor egy fggvnyt egy osztly tagjaknt adunk meg, hrom, logikailag klnbz dol-
got jelznk:
1. A fggvny hozzfrhet az osztly deklarcijnak privt rszeihez.
2. A fggvny az osztly hatkrbe tartozik.
3. A fggvnyt az osztly egy objektumra kell meghvni (egy this mutat ll
a rendelkezsre).
Ha egy tagfggvnyt static-knt hatrozunk meg (10.2.4), akkor ez csak az els kt tulaj-
donsgot jelenti; ha friend-knt (bartknt), csak az elst.
Definiljunk pldul egy Matrix-ot egy Vector-ral szorz opertort. Termszetesen mind
a Matrix, mind a Vector osztly alkalmazza az adatrejts elvt, s csak tagfggvnyeiken ke-
resztl kezelhetjk ket. A szorzst megvalst fggvny azonban nem lehet mindkt osz-
tly tagja. Nem is akarunk ltalnos, alacsonyszint hozzfrst megengedni, hogy minden
felhasznl rhassa s olvashassa a Matrix s Vector osztlyok teljes adatbrzolst. Ahhoz,
hogy ezt elkerljk, a * opertort mindkt osztlyban friend (bart) fggvnyknt hat-
rozzuk meg:
class Matrix;
class Vector {
float v[4];
// ...
friend Vector operator*(const Matrix&, const Vector&);
};
class Matrix {
Vector v[4];
// ...
friend Vector operator*(const Matrix&, const Vector&);
};
Vector operator*(const Matrix& m, const Vector& v)
{
Vector r;
for (int i = 0; i<4; i++) { // r[i] = m[i] * v;
r.v[i] = 0;
for (int j = 0; j<4; j++) r.v[i] += m.v[i].v[j] * v.v[j];
}
return r;
}
Absztrakcis mdszerek 366
A friend deklarcit az osztly privt s nyilvnos rszbe is tehetjk. A tagfggvnyekhez
hasonlan a bart fggvnyeket is az osztly deklarcijban adjuk meg, gy ugyanolyan
mrtkben hozztartoznak az osztly fellethez, mint a tagfggvnyek.
Egy osztly tagfggvnye lehet egy msik osztly bart fggvnye:
class List_iterator {
// ...
int* next();
};
class List {
friend int* List_iterator::next();
// ...
};
Nem szokatlan helyzet, hogy egy osztly sszes tagfggvnye egy msik osztly bartja.
Ennek jelzsre egy rvidts szolgl:
class List {
friend class List_iterator;
// ...
};
E deklarci hatsra a List_iterator osztly sszes tagfggvnye a List osztly bart fggv-
nye lesz.
Vilgos, hogy friend osztlyokat csak szorosan sszetartoz fogalmak kifejezsre szabad
hasznlnunk. Szmos esetben viszont vlaszthatunk, hogy egy osztlyt tag (begyazott osz-
tly) vagy nem tag bartknt adunk meg (24.4).
11.5.1. A bart fggvnyek elrse
A tagfggvnyek deklarcijhoz hasonlan a friend deklarcik sem vezetnek be j nevet
a tartalmaz hatkrbe:
class Matrix {
friend class Xform;
friend Matrix invert(const Matrix&);
// ...
};
Xform x; // hiba: a hatkrben nincs Xform
Matrix (*p)(const Matrix&) = &invert; // hiba: a hatkrben nincs invert()
11. Opertorok tlterhelse 367
Nagy programok s osztlyok esetben elnys, ha egy osztly nem ad hozz titokban j
neveket a tartalmaz hatkrhz, azoknl a sablon osztlyoknl pedig, amelyek tbb k-
lnbz krnyezetben pldnyosthatk (13. fejezet), ez kifejezetten fontos.
A bart (friend) osztlyt elzleg meg kell adnunk a tartalmaz hatkrben vagy ki kell fej-
tennk az osztlyt kzvetlenl tartalmaz nem osztly tpus hatkrben. A kzvetlenl
tartalmaz nvtr hatkrn kvli neveket nem vesznk figyelembe:
class AE { /* ... */ }; // nem "bartja" Y-nak
namespace N {
class X { /* ... */ }; // Y "bartja"
class Y {
friend class X;
friend class Z;
friend class AE;
};
class Z { /* ... */ }; // Y "bartja"
}
A bart fggvnyeket ugyangy megadhatjuk pontosan, mint a bart osztlyokat, de elr-
hetjk paramtereik alapjn is (8.2.6), mg akkor is, ha nem a kzvetlenl tartalmaz
hatkrben adtuk meg:
void f(Matrix& m)
{
invert(m); // a Matrix "bart" invert()-je
}
Ebbl kvetkezik, hogy egy bart fggvnyt vagy egy tartalmaz hatkrben kell kzvet-
lenl megadnunk, vagy az osztlynak megfelel paramterrel kell rendelkeznie, msk-
lnben nem hvhatjuk meg:
// a hatkrben nincs f()
class X {
friend void f(); // rtelmetlen
friend void h(const X&); // paramtere alapjn megtallhat
};
void g(const X& x)
{
f(); // a hatkrben nincs f()
h(x); // X h() "bartja"
}
Absztrakcis mdszerek 368
11.5.2. Bartok s tagfggvnyek
Mikor hasznljunk bart fggvnyt s mikor jobb vlaszts egy tagfggvny egy mvelet sz-
mra? Az els szempont egy osztlynl az, hogy minl kevesebb fggvny rje el kzvetle-
nl az adatbrzolst s hogy az adatlekrdezst a segdfggvnyek megfelel krvel t-
mogassuk. Ezrt az elsdleges krds nem az, hogy ez a fggvny tag legyen, statikus tag
vagy bart?, hanem az, hogy tnyleg szksge van-e az brzols elrsre? ltalban ke-
vesebb fggvnynek van erre tnylegesen szksge, mint els rnzsre gondolnnk.
Bizonyos mveleteknek tagoknak kell lennik: pldul a konstruktoroknak, destruk-
toroknak s a virtulis fggvnyeknek (12.2.6). Sokszor azonban van mrlegelsi lehet-
sg. Mivel egy tagfggvny neve az osztlyra nzve loklisnak szmt, a fggvnyt inkbb
tagfggvnyknt adjuk meg, hacsak nem szl valamilyen rv amellett, hogy nem tag fgg-
vny legyen.
Vegynk egy X osztlyt, amely egy mvelet klnfle mdozatait jelenti meg:
class X {
// ...
X(int);
int m1();
int m2() const;
friend int f1(X&);
friend int f2(const X&);
friend int f3(X);
};
A tagfggvnyeket csak az adott osztly objektumaira alkalmazhatjuk; felhasznli talak-
tst a fordt nem vgez:
void g()
{
99.m1(); // hiba: nem prbltuk X(99).m1()-et
99.m2(); // hiba: nem prbltuk X(99).m2()-t
}
Az X(int) talaktst a fordt nem alkalmazza, hogy a 99-bl X tpus objektumot csinljon.
Az f1() globlis fggvny hasonl tulajdonsggal rendelkezik, mert nem const referencia
paramterekre a fordt nem alkalmaz felhasznli talaktst (5.5, 11.3.5). Az f2() s f3()
paramtereire azonban alkalmazhat ilyen:
11. Opertorok tlterhelse 369
void h()
{
f1(99); // hiba: nem prbltuk f1(X(99))-et
f2(99); // rendben: f2(X(99));
f3(99); // rendben: f3(X(99));
}
Ezrt egy, az objektum llapott megvltoztat mvelet vagy tag legyen, vagy pedig nem
const referencia (vagy nem const mutat) paramter globlis fggvny. Olyan mveletet,
amelynek balrtkre van szksge, ha alapvet adattpusra alkalmazzuk (=, *=, ++ stb.),
a legtermszetesebb mdon felhasznli tpus tagfggvnyeknt definilhatunk.
Megfordtva: ha egy mvelet sszes operandusa automatikusan konvertlhat, akkor
a megvalst fggvny csak olyan nem tag fggvny lehet, amely paramterknt const
referencia vagy nem referencia tpust vr. Ez gyakori eset olyan mveleteket megvalst
fggvnyeknl, melyeknek nincs szksgk balrtkre, ha alapvet adattpusra alkalmaz-
zuk azokat (+, -, || stb.). Az ilyen mveleteknek gyakran az operandus-osztly brzols-
nak elrsre van szksgk, ezrt aztn a ktoperandus opertorok a friend fggvnyek
leggyakoribb forrsai.
Ha nincs tpuskonverzi, akkor nincs knyszert ok arra sem, hogy vlasszunk a tagfgg-
vny s a referencia paramtert vr bart fggvny kzl. Ilyenkor a programoz aszerint
dnthet, hogy melyik formt rszesti elnyben. A legtbb embernek pldul jobban tetszik
az inv(m) jells, ha egy m Matrix inverzrl van sz, mint a msik lehetsges m.inv() je-
lls. Ha azonban az inv() azt a Matrix-ot invertlja, amelyikre alkalmaztuk s nem egy j
Matrix-knt adja vissza az inverzt, akkor persze csak tagfggvny lehet.
Ha ms szempontok nem jtszanak kzre, vlasszunk tagfggvnyt. Nem tudhatjuk, hogy
valaki nem ad-e majd meg valamikor egy konverzis opertort, s azt sem lthatjuk elre,
hogy egy jvbeli mdosts nem vltoztatja-e meg az objektum llapott. A tagfggvny-
hvsi forma vilgoss teszi a felhasznl szmra, hogy az objektum llapota megvltoz-
hat; referencia paramter hasznlata esetn ez sokkal kevsb nyilvnval. Tovbb sokkal
rvidebbek a kifejezsek egy tagfggvny trzsben, mint a kls fggvnybeli megfelel-
ik; egy nem tag fggvnynek meghatrozott paramterre van szksge, mg a tagfggvny
automatikusan hasznlhatja a this mutatt. Ezenkvl, mivel a tagfggvnyek neve az osz-
tlyra nzve loklisnak szmt, a kls fggvnyek neve hosszabb szokott lenni.
Absztrakcis mdszerek 370
11.6. Nagy objektumok
A complex osztly mveleteinek paramtereit complex tpusknt hatroztuk meg. Ez azt
jelenti, hogy a paramterek minden mveletnl lemsoldnak. Kt double msolsa klt-
sges mvelet lehet ugyan, de valsznleg olcsbb, mint egy pr mutat. Nem minden
osztlynak van azonban knyelmesen kicsi brzolsa. A nagymrv msolsokat elke-
rlend, megadhatunk referencia tpus paramtereket kezel fggvnyeket:
class Matrix {
double m[4][4];
public:
Matrix();
friend Matrix operator+(const Matrix&, const Matrix&);
friend Matrix operator*(const Matrix&, const Matrix&);
};
A referencik alkalmazsa nagy objektumokra is lehetv teszi a szoksos aritmetikai m-
veletek hasznlatt, nagymrv msolsok nlkl is. Mutatkat nem hasznlhatunk, mert
a mutatra alkalmazott opertorok jelentst nem vltoztathatjuk meg. Az sszeadst gy
definilhatnnk:
Matrix operator+(const Matrix& arg1, const Matrix& arg2)
{
Matrix sum;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
sum.m[i][j] = arg1.m[i][j] + arg2.m[i][j];
return sum;
}
Ez az operator+() az operandusokat referencikon keresztl ri el, de objektum-rtket ad
vissza. Referencit visszaadni hatkonyabbnak tnhet:
class Matrix {
// ...
friend Matrix& operator+(const Matrix&, const Matrix&);
friend Matrix& operator*(const Matrix&, const Matrix&);
};
Ez szablyos kd, de egy memria-lefoglalsi problmt okoz. Minthogy a fggvnybl az
eredmnyre vonatkoz referencit adjuk vissza, az eredmny maga nem lehet automatikus
vltoz (7.3). Mivel egy mveletet tbbszr is alkalmazhatunk egy kifejezsen bell, az
11. Opertorok tlterhelse 371
eredmny nem lehet loklis statikus vltoz sem. Ezrt aztn az eredmnynek jellemzen
a szabad trban foglalnnk helyet. A visszatrsi rtk msolsa (vgrehajtsi idben, kd-
s adatmretben mrve) gyakran olcsbb, mint az objektum szabad trba helyezse s on-
nan eltvoltsa, s programozni is sokkal egyszerbb.
Az eredmny msolsnak elkerlsre vannak mdszerek. A legegyszerbb ezek kzl
egy statikus objektumokbl ll tmeneti tr hasznlata:
const max_matrix_temp = 7;
Matrix& get_matrix_temp()
{
static int nbuf = 0;
static Matrix buf[max_matrix_temp];
if (nbuf == max_matrix_temp) nbuf = 0;
return buf[nbuf++];
}
Matrix& operator+(const Matrix& arg1, const Matrix& arg2)
{
Matrix& res = get_matrix_temp();
// ...
return res;
}
gy egy Matrix msolsa csak egy kifejezs rtkn alapul rtkadskor trtnik meg. De
az g legyen irgalmas, ha olyan kifejezst tallnnk rni, amelyhez max_matrix_temp-nl
tbb ideiglenes rtk kell!
Hibkra kevesebb lehetsget ad mdszer, ha a mtrix tpust csak a tnyleges adatot tro-
l tpus lerjaknt (handle, 25.7) hatrozzuk meg. gy aztn a mtrixlerk gy kpvi-
selhetik az objektumokat, hogy kzben a lehet legkevesebb helyfoglals s msols trt-
nik (11.12 s 11.14[18]). Ez az eljrs azonban a visszatrsi rtkknt objektumot s nem
referencit vagy mutatt hasznl opertorokon alapul. Egy msik mdszer hromvltozs
mveletek meghatrozsra s azok olyankor automatikusan trtn meghvsra tmasz-
kodik, amikor olyan kifejezsek kirtkelse trtnik, mint a=b+c vagy a+b*i (21.4.6.3 s
22.4.7).
Absztrakcis mdszerek 372
11.7. Alapvet opertorok
ltalnossgban, ha X egy tpus, akkor az X(const X&) msol konstruktor kezeli azt az
esetet, amikor egy X tpus objektumnak egy ugyanilyen objektumot adunk kezdrtkl.
Nem lehet elgg hangslyozni, hogy a kezdeti s az egyszer rtkads klnbz mve-
letek (10.4.4.1). Ez klnsen fontos akkor, amikor a destruktorral is szmolnunk kell. Ha
az X osztlynak van valamilyen nem magtl rtetd feladatot pldul a szabad trban
lefoglalt memria felszabadtst vgz destruktora, akkor az osztlynak valsznleg
szksge lesz az objektum ltrehozst, megsemmistst s msolst vgz sszes fgg-
vnyre:
class X {
// ...
X(Sometype); // konstruktor: objektumok ltrehozsa
X(const X&); // msol konstruktor
X& operator=(const X&); // msol rtkads: takarts s msols
~X(); // destruktor: takarts
};
Ezenkvl mg hromfle helyzetben msoldik egy objektum: tadott fggvny-
paramterknt, fggvny visszatrsi rtkeknt, illetve kivtelknt. Ha paramterknt ke-
rl tadsra, egy addig kezdrtk nlkli vltoz, a formlis paramter kap kezdrtket.
Ennek szerepe azonos az egyb kezdeti rtkadsokval. Ugyanez igaz a visszatrsi rtk-
re s a kivtelre is, mg ha kevsb nyilvnval is. Ilyen esetben a msol konstruktor vg-
zi a munkt:
string g(string arg) // string rtk szerint tadva (msol konstruktor hasznlatval)
{
return arg; // string visszaadsa (msol konstruktor hasznlatval)
}
int main ()
{
string s = "Newton"; // string kezdrtket kap (msol konstruktor hasznlatval)
s = g(s);
}
Vilgos, hogy az s vltoz rtknek "Newton"-nak kell lennie a g() meghvsa utn. Nem
nehz feladat az s rtknek egy msolatt az arg formlis paramterbe msolni; a string
osztly msol konstruktornak hvsa ezt megteszi. Amikor g() visszaadja a visszatrsi r-
tket, a string(const string&) jabb hvsa kvetkezik, amikor egy olyan ideiglenes
11. Opertorok tlterhelse 373
vltoz kap rtket, amely aztn az s-nek ad rtket. Hatkonysgi okokbl az egyik (de
csak az egyik) msolst gyakran elhagyhatjuk. Az ideiglenes vltozk aztn persze
a string::~string() destruktor segtsgvel megsemmislnek (10.4.10).
Ha a programoz nem ad meg msol konstruktort vagy msol rtkadst egy osztly sz-
mra, a fordtprogram hozza ltre a hinyz fggvnyt vagy fggvnyeket (10.2.5).
Ez egyben azt is jelenti, hogy a msol mveletek nem rkldnek (12.2.3).
11.7.1. Explicit konstruktorok
Alaprtelmezs szerint az egyparamter konstruktor egyben automatikus konverzit is je-
lent. Bizonyos tpusok szmra ez idelis:
complex z = 2; // z kezdeti rtkadsa complex(2)-vel
Mskor viszont nem kvnatos s hibk forrsa lehet:
string s = 'a'; // s karakterlnc, int('a') szm elemmel
Nagyon valszntlen, hogy az s-et megad programoz ezt akarta volna.
Az automatikus konverzikat az explicit kulcssz alkalmazsval akadlyozhatjuk meg. Va-
gyis egy explicit-knt megadott konstruktort csak kzvetlen mdon lehet meghvni. gy
ahol elvileg egy msol konstruktorra van szksg (11.3.4), ott az explicit konstruktor nem
hvdik meg automatikusan:
class String {
// ...
explicit String(int n); // n bjt lefoglalsa
String(const char* p);// a kezdrtk (p) egy C stlus karakterlnc
};
String s1 = 'a'; // hiba: nincs automatikus char->String talakts
String s2(10); // rendben: String 10 karakternyi hellyel
String s3 = String(10); // rendben: String 10 karakternyi hellyel
String s4 = "Brian"; // rendben: s4 = String("Brian")
String s5("Fawlty");
void f(String);
String g()
{
Absztrakcis mdszerek 374
f(10); // hiba: nincs automatikus int ->String talakts
f(String(10));
f("Arthur"); // rendben: f(String("Arthur"))
f(s1);
String* p1 = new String("Eric");
String* p2 = new String(10);
return 10; // hiba: nincs automatikus int ->String talakts
}
A klnbsg akztt, hogy
String s1 = 'a'; // hiba: nincs automatikus char ->String talakts
s akztt, hogy
String s2(10); // rendben: karakterlnc 10 karakternyi hellyel
cseklynek tnhet, de igazi kdban kevsb az, mint kitallt pldkban.
A Date osztlyban egy sima int-et hasznltunk az v brzolsra (10.3). Ha a Date osztly
ltfontossg szerepet jtszott volna, akkor bevezethettk volna a Year osztlyt, hogy for-
dtsi idben szigorbb ellenrzsek trtnjenek:
class Year {
int y;
public:
explicit Year(int i) : y(i) { } // Year ltrehozsa int-bl
operator int() const { return y; } // talakts Year-rl int-re
};
class Date {
public:
Date(int d, Month m, Year y);
// ...
};
Date d3(1978,feb,21); // hiba: a 21 nem Year tpus
Date d4(21,feb,Year(1978)); // rendben
A Year egy egyszer csomagol (beburkol, wrapper) osztly az int krl. Az operator
int()-nek ksznheten a Year automatikusan mindenhol int-t alakul, ahol szksges. Az-
ltal, hogy a konstruktort explicit-knt adtuk meg, biztostottuk, hogy az int-nek Year-r va-
11. Opertorok tlterhelse 375
l alaktsa csak ott trtnik meg, ahol ezt krjk s a vletlen rtkadsok a fordtskor
kiderlnek. Minthogy a Year tagfggvnyeit knny helyben kifejtve (inline) fordtani, a fu-
tsi id s a szksges trhely nvekedstl sem kell tartanunk.
Hasonl mdszer tartomny (intervallum) tpusokra (25.6.1) is alkalmazhat.
11.8. Indexels
Osztly tpus objektumoknak az operator [ ] (subscripting) fggvny segtsgvel adha-
tunk sorszmot (indexet). Az operator[ ] msodik paramtere (az index) brmilyen tpus
lehet, gy aztn vektorokat, asszociatv tmbket stb. is definilhatunk.
Pldaknt rjuk most jra a 5.5-beli pldt, amelyben egy asszociatv tmb segtsgvel r-
tunk egy fjlban a szavak elfordulst megszmol kis programot. Akkor egy fggvnyt
hasznltunk, most egy asszociatv tmb tpust:
class Assoc {
struct Pair {
string name;
double val;
Pair(string n ="", double v =0) :name(n), val(v) { }
};
vector<Pair> vec;
Assoc(const Assoc&); // a msolst megakadlyozand privt
Assoc& operator=(const Assoc&); // a msolst megakadlyozand privt
public:
Assoc() {}
const double& operator[ ](const string&);
double& operator[ ](string&);
void print_all() const;
};
Az Assoc tpus objektumok Pair-ek vektort tartalmazzk. A megvalstsban ugyanazt az
egyszer s nem tl hatkony keressi mdszert hasznljuk, mint az 5.5 pontban:
double& Assoc::operator[ ](string& s)
// megkeressk s-t; ha megtalltuk, visszaadjuk az rtkt; ha nem, j Pair-t hozunk
// ltre s az alaprtelmezett 0 rtket adjuk vissza
{
Absztrakcis mdszerek 376
for (vector<Pair>::iterator p = vec.begin(); p!=vec.end(); ++p)
if (s == p->name) return p->val;
vec.push_back(Pair(s,0)); // kezdrtk: 0
return vec.back().val; // az utols elem visszaadsa (16.3.3)
}
Minthogy az Assoc objektum brzolsa kvlrl nem rhet el, szksg van egy kimeneti
fggvnyre:
void Assoc::print_all() const
{
for (vector<Pair>::const_iterator p = vec.begin(); p!=vec.end(); ++p)
cout << p->name << ": " << p->val << '\n';
}
Vgl megrhatjuk a fprogram egyszer vltozatt:
int main() // szavak elfordulsnak megszmllsa a bemeneten
{
string buf;
Assoc vec;
while (cin>>buf) vec[buf]++;
vec.print_all();
}
Az asszociatv tmb tlett tovbbfejleszti a 17.4.1 pont.
Az operator[ ]() fggvnyeknek tagfggvnynek kell lennik.
11.9. Fggvnyhvs
A fggvnyhvs (function call), vagyis a kifejezs(kifejezs-lista) jells gy tekinthet,
mint egy ktoperandus mvelet, ahol a kifejezs a bal oldali, a kifejezs-lista pedig a jobb
oldali operandus. A () hv opertor a tbbi opertorhoz hasonl mdon tlterhelhet.
Az operator()() paramterlistjnak kirtkelse s ellenrzse a szoksos paramter-tad-
si szablyok szerint trtnik. A fggvnyhv opertor tlterhelse elssorban olyan tpu-
sok ltrehozsakor hasznos, amelyeknek csak egy mveletk van vagy ltalban csak egy
mveletk hasznlatos.
11. Opertorok tlterhelse 377
A ( ) hv mvelet legnyilvnvalbb s taln legfontosabb alkalmazsa az, hogy a valami-
kppen fggvnyknt viselked objektumokat fggvnyknt hvhassuk meg. Egy fgg-
vnyknt viselked objektumot fggvnyszer vagy egyszeren fggvnyobjektumnak h-
vunk (18.4). Az ilyen fggvnyobjektumok fontosak, mert lehetv teszik, hogy olyan k-
dot rjunk, amelyben valamilyen nem magtl rtetd mveletet paramterknt adunk t.
A standard knyvtrban pldul sok olyan algoritmus tallhat, melyek egy fggvnyt hv-
nak meg egy trol minden elemre. Vegyk az albbi pldt:
void negate(complex& c) { c = -c; }
void f(vector<complex>& aa, list<complex>& ll)
{
for_each(aa.begin(),aa.end(),negate); // a vektor sszes elemnek neglsa
for_each(ll.begin(),ll.end(),negate); // a lista sszes elemnek neglsa
}
Ez a vektor s a lista minden elemt neglja.
Mi lenne, ha a lista minden elemhez complex(2,3)-at akarnnk hozzadni? Ezt knnyen
megtehetjk:
void add23(complex& c)
{
c += complex(2,3);
}
void g(vector<complex>& aa, list<complex>& ll)
{
for_each(aa.begin(),aa.end(),add23);
for_each(ll.begin(),ll.end(),add23);
}
Hogyan tudnnk egy olyan fggvnyt rni, melyet tbbszr meghvva egy-egy tetszleges
rtket adhatunk az elemekhez? Olyasmire van szksgnk, aminek megadhatjuk a kvnt
rtket s utna ezt az rtket hasznlja fel minden hvsnl. Ez a fggvnyeknek nem ter-
mszetes tulajdonsga. Jellemz megoldsknt valahova a fggvnyt krlvev krnyezet-
be helyezve adjuk t az rtket, ami nem tiszta megolds. Viszont rhatunk egy osztlyt,
amely a megfelel mdon mkdik:
class Add {
complex val;
public:
Add(complex c) { val = c; } // az rtk mentse
Add(double r, double i) { val = complex(r,i); }
void operator()(complex& c) const { c += val; } // a paramter nvelse az rtkkel
};
Absztrakcis mdszerek 378
Egy Add osztly objektum kezdrtknek egy komplex szmot adunk, majd a ( ) mve-
letet vgrehajtatva ezt a szmot hozzadjuk a paramterhez:
void h(vector<complex>& aa, list<complex>& ll, complex z)
{
for_each(aa.begin(),aa.end(),Add(2,3));
for_each(ll.begin(),ll.end(),Add(z));
}
Ez a tmb minden elemhez complex(2,3)-at fog adni, a lista elemeihez pedig z-t. Vegyk
szre, hogy Add(z) egy olyan objektumot hoz ltre, amelyet aztn a for_each ismtelten fel-
hasznl. Nem egyszeren egy egyszer vagy tbbszr meghvott fggvnyrl van sz.
A tbbszr meghvott fggvny az Add(z) operator()() fggvnye.
Mindez azrt mkdik, mert a for_each egy sablon (template), amely a ( ) mveletet alkal-
mazza a harmadik paramterre, anlkl, hogy trdne vele, mi is igazbl a harmadik
paramter:
template<class Iter, class Fct> Fct for_each(Iter b, Iter e, Fct f)
{
while (b != e) f(*b++);
return f;
}
Els pillantsra ez a mdszer furcsnak tnhet, de egyszer, hatkony, s nagyon hasznos
(lsd 3.8.5, 18.4).
Az operator()() tovbbi npszer alkalmazsai a rszlncok kpzsre vagy tbb dimenzi-
s tmbk indexelsre (22.4.5) val hasznlat.
Az operator()()-nak tagfggvnynek kell lennie.
11.10. Indirekci
A -> indirekci (hivatkozstalants, dereferencing) opertort egyparamter, uttagknt
hasznlt opertorknt definilhatjuk. Legyen adott egy osztly:
class Ptr {
// ...
X* operator->();
};
11. Opertorok tlterhelse 379
Ekkor a Ptr osztly objektumokat az X osztly tagjainak elrsre hasznlhatjuk, a muta-
tkhoz nagyon hasonl mdon:
void f(Ptr p)
{
p->m = 7; // (p.operator->())->m = 7
}
A p objektumnak a p.operator->() mutatv val talaktsa nem fgg attl, hogy milyen m
tagra mutat. Az operator->() ebben az rtelemben egyoperandus uttag-opertor, formai
kvetelmnyei viszont nem jak, gy a tagnevet ki kell rni utna:
void g(Ptr p)
{
X* q1 = p->; // szintaktikus hiba
X* q2 = p.operator->(); // rendben
}
A ->() opertor tlterhelsnek f alkalmazsa az okos vagy intelligens mutat (smart po-
inter) tpusok ltrehozsa, azaz olyan objektumok, amelyek mutatknt viselkednek, de
radsul valamilyen tennivalt vgeznek, valahnyszor egy objektumot rnek el rajtuk ke-
resztl. Pldul ltrehozhatunk egy Rec_ptr osztlyt, amellyel a lemezen trolt Rec osztly
objektumok rhetek el. A Rec_ptr konstruktora egy nevet vr, melynek segtsgvel a ke-
resett objektum a lemezen megkereshet, a Rec_ptr::operator->() fggvny a memriba
tlti az objektumot, amikor azt a Rec_ptr-en keresztl el akarjuk rni, a Rec_ptr destruktora
pedig szksg esetn a megvltozott objektumot a lemezre rja:
class Rec_ptr {
const char* identifier;
Rec* in_core_address;
// ...
public:
Rec_ptr(const char* p) : identifier(p), in_core_address(0) { }
~Rec_ptr() { write_to_disk(in_core_address,identifier); }
Rec* operator->();
};
Rec* Rec_ptr::operator->()
{
if (in_core_address == 0) in_core_address = read_from_disk(identifier);
return in_core_address;
}
Absztrakcis mdszerek 380
A Rec_ptr-t gy hasznlhatjuk:
struct Rec { // a Rec tpus, amire Rec_ptr mutat
string name;
// ...
};
void update(const char* s)
{
Rec_ptr p(s); // Rec_ptr ellltsa s-bl
p->name = "Roscoe"; // s mdostsa; ha szksges, elszr beolvassa a lemezrl
// ...
}
Termszetesen az igazi Rec_ptr egy sablon lenne, a Rec tpus pedig paramter. Egy vals-
gos program hibakezelst is tartalmazna s kevsb nav mdon kezeln a lemezt.
Kznsges mutatk esetben a -> hasznlata egyenrtk az egyvltozs * s [ ] haszn-
latval. Ha adott egy tpus:
Y* p;
akkor teljesl a kvetkez:
p->m == (*p).m == p[0].m
Ahogy mr megszokhattuk, a felhasznli opertorokra nzve ez nem biztostott. Szksg
esetn persze gondoskodhatunk errl:
class Ptr_to_Y {
Y* p;
public:
Y* operator->() { return p; }
Y& operator*() { return *p; }
Y& operator[ ](int i) { return p[i]; }
};
Ha egy osztlyban tbb ilyen opertort hatrozunk meg, akkor tancsos lehet ezt gy ten-
ni, hogy a fenti egyenrtksg teljesljn, ugyangy, mint ahogy ++x s x+=1 is j, ha
x=x+1-gyel azonos hatssal jr, ha x egy olyan osztly vltoz, amelyben a ++, +=, + m-
veletek rtelmezettek.
11. Opertorok tlterhelse 381
A -> opertor tlterhelhetsge nem csak kis klnlegessg, hanem rdekes programok
egy osztlya szmra fontos is, azon oknl fogva, hogy az indirekci (dereferencing) kulcs-
fogalom, a -> opertor tlterhelse pedig tiszta, kzvetlen s hatkony mdja annak egy
programban val megjelentsre. A bejrk (itertorok) (19. fejezet) jellemz s lnyegi
pldt adnak erre. A -> opertor msik haszna, hogy korltozott, de hasznos mdon lehe-
tv teszi a C++ nyelvben a delegcit (24.2.4).
Az operator-> tagfggvny kell, hogy legyen. Csak gy hasznlhat, ha mutatt vagy olyan
tpust ad vissza, amelyre a -> alkalmazhat. Ha egy sablon osztly szmra adjuk meg, sok-
szor elfordul, hogy nem is kerl tnyleges felhasznlsra, ezrt sszer e megszorts el-
lenrzst a tnyleges hasznlatig elhalasztani.
11.11. Nvels s cskkents
Amint a programoz kitall egy intelligens mutatt, sokszor dnt gy, hogy ehhez a ++
nvel (increment) s -- cskkent (decrement) mvelet is hozztartozik, a beptett tpu-
sokra rtelmezett nvels s cskkents mintjra. Ez klnsen nyilvnval s szksges
olyankor, amikor a cl egy kznsges mutatnak egy okosra val kicserlse, amely azo-
nos jelents mellett csak nmi futsi idej hiba-ellenrzssel van kiegsztve. Vegyk pl-
dul az albbi egybknt problematikus hagyomnyos programot:
void f1(T a) // hagyomnyos hasznlat
{
T v[200];
T* p = &v[0];
p--;
*p = a; // hopp: 'p' tartomnyon kvli s nem kaptuk el
++p;
*p = a; // rendben
}
A p mutatt ki szeretnnk cserlni valamilyen Ptr_to_T osztly objektumra, amelyre csak ak-
kor tudjuk alkalmazni az indirekci opertort, ha tnyleg egy objektumra mutat. Azt is el sze-
retnnk rni, hogy p-t csak gy lehessen nvelni vagy cskkenteni, ha tmbn belli objek-
tumra mutat, mg a nvels vagy cskkents hatsra is. Valami ilyesmit szeretnnk teht:
class Ptr_to_T {
// ...
};
Absztrakcis mdszerek 382
void f2(T a) // ellenrztt
{
T v[200];
Ptr_to_T p(&v[0],v,200);
p--;
*p = a; // futsi idej hiba: 'p' tartomnyon kvli
++p;
*p = a; // rendben
}
A nvel s cskkent opertorok az egyetlenek a C++ nyelv opertorai kztt, amelyek
eltagknt (prefix) s uttagknt (postfix) egyarnt hasznlhatk. Ezrt a Ptr_to_T tpus
szmra mindkt fajta nvel s cskkent opertort definilnunk kell:
class Ptr_to_T {
T* p;
T* array;
int size;
public:
Ptr_to_T(T* p, T* v, int s); // csatols s mret v tmbhz, a kezdrtk p
Ptr_to_T(T* p); // csatols nll objektumhoz, a kezdrtk p
Ptr_to_T& operator++(); // eltag
Ptr_to_T operator++(int); // uttag
Ptr_to_T& operator--(); // eltag
Ptr_to_T operator--(int); // uttag
T& operator*(); // eltag
};
Az int paramterrel jelezzk a ++ uttagknt val alkalmazst. Magt az int-et nem hasz-
nljuk, csak l-paramter, amely az el- s uttagknt val hasznlat kztt tesz klnbs-
get. Knnyen megjegyezhetjk, melyik melyik, ha arra gondolunk, hogy a tbbi (aritmeti-
kai s logikai) egy paramter opertorhoz hasonlan az l-paramter nlkli ++ s -- az
eltagknt, a paramteres vltozat a furcsa uttagknt val mkdshez kell.
A Prt_to_T osztlyt hasznlva a plda egyenrtk az albbival:
void f3(T a) // ellenrztt
{
T v[200];
Ptr_to_T p(&v[0],v,200);
p.operator--(0);
11. Opertorok tlterhelse 383
p.operator*() = a; // futsi idej hiba: 'p' tartomnyon kvli
p.operator++();
p.operator*() = a; // rendben
}
A Prt_to_T osztly kiegsztse gyakorlatnak marad (11.14[19]). tdolgozsa olyan sablon-
n, amely kivteleket is hasznl a futsi idben fellp hibk jelzsre, egy msik gyakor-
lat (14.12[12]). A 13.6.3 egy mutatsablont mutat be, amely rklds hasznlata mellett
is jl mkdik.
11.12. Egy karakterlnc osztly
me a String osztly egy valsgosabb vltozata, amely a cljainknak mg ppen megfelel.
Ez a karakterlnc-osztly tmogatja az rtk szerinti mkdst (value semantics, rtk-sze-
mantika), a karakterr s -olvas mveleteket, az ellenrztt s ellenrizetlen elrst, az
adatfolyam ki- s bemenetet, a karakterliterlokat, az egyenlsgvizsgl s sszefz m-
veleteket. A karakterlncokat C stlus, nullval lezrt karaktertmbknt trolja, a msolsok
szmnak cskkentsre pedig hivatkozsszmllt hasznl. Egy tbbet tud s/vagy tbb
szolgltatst nyjt string osztly rsa j gyakorlat (11.14[7-12]). Ha megvagyunk vele, el-
dobhatjuk a gyakorlatainkat s hasznlhatjuk a standard knyvtrbeli string-et (20. fejezet).
Az n majdnem valsgos String osztlyom hrom segdosztlyt hasznl: az Srep-et, hogy
tbb azonos rtk String is hasznlhassa ugyanazt az eltrolt adatot, ha azonos az rtkk;
a Range-et az rtktartomny-megsrtsi hibkat jelz kivtelek kivltshoz; s a Cref-et,
hogy egy rs s olvass kztt klnbsget tev index-opertort tmogasson:
class String {
struct Srep; // adatbrzols
Srep *rep;
public:
class Cref; // referencia char-ra
class Range { }; // kivtelkezelshez
// ...
};
Absztrakcis mdszerek 384
A tbbi taghoz hasonlan a tagosztlyokat (member class, amit gyakran hvnak begyazott
osztlynak, nested class-nak is) deklarlhatjuk az osztlyban, majd ksbb kifejthetjk:
struct String::Srep {
char* s; // mutat az elemekre
int sz; // karakterek szma
int n; // hivatkozsszmll
Srep(int nsz, const char* p)
{
n = 1;
sz = nsz;
s = new char[sz+1]; // hely a lezr nulla szmra is
strcpy(s,p);
}
~Srep() { delete[ ] s; }
Srep* get_own_copy() // msols, ha szksges
{
if (n==1) return this;
n--;
return new Srep(sz,s);
}
void assign(int nsz, const char* p)
{
if (sz != nsz) {
delete[ ] s;
sz = nsz;
s = new char[sz+1];
}
strcpy(s,p);
}
private: // a msols megakadlyozsa
Srep(const Srep&);
Srep& operator=(const Srep&);
};
A String osztlynak megvannak a szoksos konstruktorai, destruktora s rtkad mve-
letei is:
class String {
// ...
11. Opertorok tlterhelse 385
String(); // x = ""
String(const char*); // x = "abc"
String(const String&); // x = msik_karakterlnc
String& operator=(const char *);
String& operator=(const String&);
~String();
// ...
};
A String osztly rtk szerint mkdik, azaz egy s1=s2 rtkads utn s1 s s2 kt teljesen
klnbz karakterlnc lesz, vagyis ha ksbb az egyiket mdostjuk, akkor annak nem
lesz hatsa a msikra. A msik megolds az lenne, ha a String osztly mutatkkal dolgoz-
na. Ekkor az s1=s2 rtkads utn s2 megvltoztatsa s1-et is rinten. Ha egy osztlynak
megvannak a hagyomnyos aritmetikai mveletei, mint a komplex szmokkal, vek-
torokkal, mtrixokkal, karakterlncokkal vgzettek, n elnyben rszestem az rtk szerin-
ti mkdst. Ahhoz viszont, hogy ennek tmogatsa ne kerljn tl sokba, a String-et le-
rknt brzolom, amely az adatbrzolsra mutat, amit csak szksg esetn kell msolni:
String::String() // az alaprtelmezett rtk egy res karakterlnc
{
rep = new Srep(0,"");
}
String::String(const String& x) // msol konstruktor
{
x.rep->n++;
rep = x.rep; // az brzols megosztsa
}
String::~String()
{
if (--rep->n == 0) delete rep;
}
String& String::operator=(const String& x) // msol rtkads
{
x.rep->n++; // vdelem az "st = st" ellen
if (--rep->n == 0) delete rep;
rep = x.rep; // az brzols megosztsa
return *this;
}
A const char* paramter l-msol mveletek bevezetsvel a karakterliterlokat is meg-
engedjk:
Absztrakcis mdszerek 386
String::String(const char* s)
{
rep = new Srep(strlen(s),s);
}
String& String::operator=(const char* s)
{
if (rep->n == 1) // Srep jrahasznostsa
rep->assign(strlen(s),s);
else { // j Srep hasznlata
rep->n--;
rep = new Srep(strlen(s),s);
}
return *this;
}
Az egyes karakterlncokat elr opertorok megtervezse nehz, mert az idelis megolds
az lenne, ha ezek a szoksos jellst (azaz a [ ]-t) hasznlnk, a lehet leghatkonyabbak
lennnek s a paramter rtkt is ellenriznk. Sajnos, ez a hrom kvetelmny nem tel-
jesthet egyszerre. n gy ksztettem el az osztlyt, hogy hatkony ellenrizetlen mve-
leteket adtam meg (egy kicsit knyelmetlenebb jellssel), illetve kevsb hatkony ellen-
rztt eljrsokat (a hagyomnyos jellssel):
class String {
// ...
void check(int i) const { if (i<0 || rep->sz<=i) throw Range(); }
char read(int i) const { return rep->s[i]; }
void write(int i, char c) { rep=rep->get_own_copy(); rep->s[i]=c; }
Cref operator[ ](int i) { check(i); return Cref(*this,i); }
char operator[ ](int i) const { check(i); return rep->s[i]; }
int size() const { return rep->sz; }
// ...
};
Az tlet az, hogy a hagyomnyos [ ] jellssel az ellenrztt elrs legyen biztostott a k-
znsges felhasznls szmra, de a felhasznlnak legyen mdja egyszerre vgignzni
a teljes tartomnyt s a gyorsabb, ellenrizetlen elrst hasznlni:
11. Opertorok tlterhelse 387
int hash(const String& s)
{
int h = s.read(0);
const int max = s.size();
for (int i = 1; i<max; i++) h ^= s.read(i)>>1; // ellenrzs nlkli hozzfrs s-hez
return h;
}
Nehz dolog egy opertort, pldul a [ ]-t gy meghatrozni, hogy az az r s az olvas jel-
leg hozzfrst is tmogassa, ha nem fogadhat el az a megolds, hogy egyszeren egy
referencit adunk vissza, amit aztn a felhasznl kedve szerint felhasznlhat. Itt pldul ez
nem lehetsges, mert a String-et gy hatroztam meg, hogy az egyes rtkadssal,
paramter-tadssal stb. megadott rtk String-ek ugyanazt a bels brzolst hasznljk,
mg az egyik String-et tnylegesen nem rjk: az rtk msolsa csak ekkor trtnik meg.
Ezt a mdszert ltalban rskori msolsnak vagy msols rskor-nak (copy-on-write)
hvjk. A tnyleges msolst a String::get_own_copy() vgzi.
Abbl a clbl, hogy az elr fggvnyeket helyben kifejtve (inline) lehessen fordttatni,
olyan helyre kell elhelyezni definicijukat, ahonnan az Srep osztly elrhet. Teht vagy
az Srep-et kell a String osztlyon bell megadni, vagy pedig az elr fggvnyeket kell
inline-knt meghatrozni a String-en kvl s az String::Srep utn (11.14[2]).
Megklnbztetend az rst s az olvasst, a String::operator[ ]() egy Cref-et ad vissza, ha
nem const objektumra hvtk meg. A Cref gy viselkedik, mint a char&, azzal a klnbsg-
gel, hogy rsakor meghvja a String::Sref::get_own_copy()-t:
class String::Cref { // hivatkozs s[i]-re
friend class String;
String& s;
int i;
Cref(String& ss, int ii) : s(ss), i(ii) { }
public:
operator char() const { return s.read(i); } // rtk kijellse
void operator=(char c) { s.write(i,c); } // rtk mdostsa
};
Pldul:
void f(String s, const String& r)
{
char c1 = s[1]; // c1 = s.operator[ ](1).operator char()
s[1] = 'c'; // s.operator[ ](1).operator=('c')
Absztrakcis mdszerek 388
char c2 = r[1]; // c2 = r.operator[ ](1)
r[1] = 'd'; // hiba: char rtkads, r.operator[ ](1) = 'd'
}
Vegyk szre, hogy egy nem const objektumra az s.operator[ ](1) rtke Cref(s,1) lesz.
Ahhoz, hogy teljess tegyk a String osztlyt, meghatrozunk mg egy sor hasznos fggvnyt:
class String {
// ...
String& operator+=(const String&);
String& operator+=(const char*);
friend ostream& operator<<(ostream&, const String&);
friend istream& operator>>(istream&, String&);
friend bool operator==(const String& x, const char* s)
{ return strcmp(x.rep->s, s) == 0; }
friend bool operator==(const String& x, const String& y)
{ return strcmp(x.rep->s, y.rep->s) == 0; }
friend bool operator!=(const String& x, const char* s)
{ return strcmp(x.rep->s, s) != 0; }
friend bool operator!=(const String& x, const String& y)
{ return strcmp(x.rep->s, y.rep->s) != 0; }
};
String operator+(const String&, const String&);
String operator+(const String&, const char*);
Hely-megtakarts cljbl a ki- s bemeneti opertorokat, illetve az sszefzst meghagy-
tam gyakorlatnak.
A fprogram pusztn egy kiss megdolgoztatja a String opertorokat:
String f(String a, String b)
{
a[2] = 'x';
char c = b[3];
cout << "Az f-ben: " << a << ' ' << b << ' ' << c << '\n';
return b;
}
11. Opertorok tlterhelse 389
int main()
{
String x, y;
cout << "Adjon meg kt karakterlncot!\n";
cin >> x >> y;
cout << "Bemenet: " << x << ' ' << y << '\n';
String z = x;
y = f(x,y);
if (x != z) cout << "Az x srlt!\n";
x[0] = '!';
if (x == z) cout << "Az rs nem sikerlt!\n";
cout << "Kilps: " << x << ' ' << y << ' ' << z << '\n';
}
Ebbl a String osztlybl mg hinyoznak fontosnak vagy akr alapvetnek tekinthet dol-
gok, pldul nincs az rtket C stlus adatknt visszaad mvelet (11.14[10], 20. fejezet).
11.13. Tancsok
[1] Opertorokat elssorban azrt adjunk meg, hogy a szoksos hasznlati mdot
tmogassuk. 11.1.
[2] Nagy operandusok esetben hasznljunk const referencia paramtereket. 11.6.
[3] Nagy rtkeket ad eredmnyeknl fontoljuk meg az eredmny-visszaads opti-
malizlst. 11.6.
[4] Hagyatkozzunk az alaprtelmezett msol mveletekre, ha azok megfelelek az
osztlyunk szmra. 11.3.4.
[5] Brljuk fell vagy tiltsuk meg az alaprtelmezett msolst, ha az egy adott tpus
szmra nem megfelel. 11.2.2.
[6] Ha egy fggvnynek szksge van az adatbrzols elrsre, inkbb tagfgg-
vny legyen. 11.5.2.
[7] Ha egy fggvnynek nincs szksge az adatbrzols elrsre, inkbb ne
legyen tagfggvny. 11.5.2.
[8] Hasznljunk nvteret, hogy a segdfggvnyeket osztlyukhoz rendeljk.
11.2.4.
[9] Szimmetrikus mveletekre hasznljunk nem tag fggvnyeket. 11.3.2.
[10] Tbbdimenzis tmb indexelsre hasznljunk ()-t. 11.9.
[11] Az egyetlen mret paramter konstruktorok legyenek explicit-ek. 11.7.1.
Absztrakcis mdszerek 390
[12] ltalnos clra hasznljuk a szabvnyos string-et (20. fejezet), ne a gyakorlatok
megoldsa rvn kapott sajt vltozatot. 11.12.
[13] Legynk vatosak az automatikus konverzik bevezetsekor. 11.4.
[14] Bal oldali operandusknt balrtket vr mveleteket tagfggvnyekkel valst-
sunk meg. 11.3.5.
11.14. Gyakorlatok
1. (*2) Milyen konverzikat hasznlunk az albbi program egyes kifejezseiben:
struct X {
int i;
X X(int);
operator+(int);
};
struct Y {
int i;
Y(X);
Y operator+(X);
operator int();
};
extern X operator*(X, Y);
extern int f(X);
X x = 1;
Y y = x;
int i = 2;
int main()
{
i + 10; y + 10; y + 10 * y;
x + y + i; x * x + i; f(7);
f(y); y + y; 106 + y;
}
Mdostsuk a programot gy, hogy futs kzben kirjon minden megengedett
kifejezst.
2. (*2) Fejezzk be s teszteljk a 11.12-beli String osztlyt.
11. Opertorok tlterhelse 391
3. (*2) Definiljuk az INT osztlyt, amely pontosan gy viselkedik, mint egy int.
(Segtsg: definiljuk az INT::operator int()-et).
4. (*1) Definiljuk a RINT osztlyt, amely pontosan gy viselkedik, mint egy int,
azzal a klnbsggel, hogy csak a kvetkez mveletek engedlyezettek: (egy
s kt paramter) +, (egy s kt paramter) -, *, / s %. (Segtsg: ne
definiljuk a RINT::operator int()-et).
5. (*3) Definiljuk a LINT osztlyt, amely pontosan gy viselkedik, mint a RINT, de
legalbb 64 bites pontossg.
6. (*4) Definiljunk egy tetszleges pontossg aritmetikt tmogat osztlyt.
Teszteljk az 1000 faktorilisnak kiszmttatsval. (Segtsg: a String osztly-
hoz hasonl trszervezsre lesz szksg.)
7. (*2) Hatrozzunk meg a String osztly szmra egy kls bejrt (itertort):
class String_iter {
// hivatkozs karakterlncra s annak elemre
public:
String_iter(String& s); // bejr s szmra
char& next(); // hivatkozs a kvetkez elemre
// igny szerint tovbbi mveletek
};
Hasonltsuk ezt ssze hasznossg, stlus s hatkonysg szempontjbl a String
egy bels bejrjval. (Vagyis adott egy kurrens elemnk a String-ben, s a vele
kapcsolatos mveletek.)
8. (*1.5) A () opertor tlterhelsvel hatrozzunk meg egy rszlnc-mveletet
egy karakterlnc-osztly szmra. Milyen ms mveletet szeretnnk egy karak-
terlncon vgezni?
9. (*3) Tervezzk meg gy a String osztlyt, hogy a rszlnc-opertort egy rtk-
ads bal oldaln is fel lehessen hasznlni. Elszr egy olyan vltozatot rjunk,
amelyiknl egy rszlncot egy azonos hosszsg teljes karakterlncra lehet
cserlni, aztn egy olyat, amelyiknl eltrre.
10. (*2) Definiljuk a String osztly szmra az rtket C stlus adatknt visszaad
mveletet. Vitassuk meg annak elnyeit s htrnyait, hogy ez egy konverzis
opertor. Vitassuk meg a C stlus adat szmra szksges memria lefoglals-
nak klnfle mdjait.
11. (*2.5) Tervezznk s ksztsnk egy egyszer regulris kifejezs illeszt eszkzt
a String osztly szmra.
12. (*1.5) Mdostsuk gy a 11.14[11]-beli eszkzt, hogy az mkdjn a standard
knyvtrbeli string-gel. A string defincijt nem vltoztathatjuk meg.
Absztrakcis mdszerek 392
13. (*2) rjunk egy programot, amit opertor-tlterhelssel s a makrk hasznlat-
val olvashatatlann tesznk. Egy tlet: hatrozzuk meg +-t gy az INT-ekre,
hogy - -t jelentsen s fordtva. Ezutn egy makrval hatrozzuk meg gy az int-
et, hogy INT-et jelentsen. Brljuk fell a npszer fggvnyeket referencia
tpus paramtereket hasznl fggvnyekknt. Nhny flrevezet megjegy-
zssel is nagy zavart lehet kelteni.
14. (*3) Cserljk ki egy bartunkkal a 11.14[13] feladatra adott megoldsunkat, s
prbljuk meg futtatsa nlkl kiderteni, mit csinl. A gyakorlat vgre meg
fogjuk tanulni, hogy mit ne tegynk tbb.
15. (*2) Definiljuk a Vec4 tpust, mint ngy float-bl ll vektort. Definiljuk a [ ]
mveletet. Adjuk meg a +, -, *, /, =, +, +=, -=, *= s /= mveleteket a vektorok
s float-ok egyttes hasznlatra.
16. (*3) Definiljuk a Mat4 tpust, mint ngy Vec4-bl ll vektort. Definiljuk a [ ]
mveletet, mint ami Vec4-et ad vissza, ha a Mat4-re alkalmazzuk. Adjuk meg
a szoksos mtrix-mveleteket. Ksztsnk fggvnyt, amely a Gauss-fle kik-
szbls mdszert alkalmazza egy Mat4-re.
17. (*2) Definiljunk egy Vector tpust, amely a Vec4-hez hasonlt, de
Vector::Vector(int) konstruktornak paramterknt megadhatjuk a mretet.
18. (*3) Definiljunk egy Matrix tpust, amely a Mat4-hez hasonlt, de
Matrix::Matrix(int,int) konstruktornak paramterknt megadhatjuk
a dimenzikat.
19. (*2) Fejezzk be a 11.11 pont Ptr_to_T osztlyt s ellenrizzk. Legyenek
meg legalbb a *, ->, =, ++ s -- mveletek. Ne okozzunk futsi idej hibt,
csak ha egy hibs mutatt tnylegesen felhasznlnak.
20. (*1) Adott kt struktra:
struct S { int x, y; };
struct T { char* p; char* q; };
rjunk egy C osztlyt, melynek segtsgvel majdnem gy hasznlhatjuk vala-
mely S s T x-t s p-jt, mint ha x s p C tagja lenne.
21. (*1.5) Definiljuk az Index osztlyt a mypow(double,Index) hatvnyoz fgg-
vny kitevje szmra. Talljunk mdot arra, hogy 2**I meghvja mypow(2,I)-t.
22. (*2) Definiljuk az Imaginary osztlyt kpzetes szmok brzolsra.
Definiljuk a Complex osztlyt ennek alapjn. Ksztsk el az alapvet
aritmetikai mveleteket.
11. Opertorok tlterhelse 393
Szrmaztatott osztlyok
Ne szaportsuk az
objektumokat,
ha nem szksges.
(W. Occam)
Fogalmak s osztlyok Szrmaztatott osztlyok Tagfggvnyek Ltrehozs s meg-
semmists Osztlyhierarchik Tpusmezk Virtulis fggvnyek Absztrakt oszt-
lyok Hagyomnyos osztlyhierarchik Absztrakt osztlyok mint felletek Az objektu-
mok ltrehozsnak adott helyre korltozsa Absztrakt osztlyok s osztlyhierarchik
Tancsok Gyakorlatok
12.1. Bevezets
A C++ a Simula nyelvtl klcsnzte az osztly, mint felhasznli tpus, illetve az osztlyhi-
erarchia fogalmt, valamint a rendszertervezs azon elvt, hogy a programban hasznlt fo-
galmak modellezsre osztlyokat hasznljon. A C++ nyjtotta nyelvi szerkezetek kzvet-
lenl tmogatjk ezeket a tervezsi elveket. Megfordtva is igaz: akkor hasznljuk a C++
nyelvet hatkonyan, ha a nyelvi lehetsgeket a tervezsi elvek tmogatsra hasznljuk.
Aki a nyelvi elemeket csak a hagyomnyosabb programozs jellsbeli altmasztsra
hasznlja, a C++ valdi erssgnek hasznlatrl mond le.
12
Egy fogalom sohasem nmagban ltezik, hanem ms fogalmakkal egytt s erejnek egy
rszt is a rokon fogalmakkal val kapcsolatbl merti. Prbljuk csak megmagyarzni, mi
az, hogy aut. Hamarosan bevezetjk a kvetkez fogalmakat: kerk, motor, vezet, gya-
logos, teheraut, mentk, utak, olaj, gyorshajts, brsg, motel stb. Minthogy a fogalmakat
osztlyokknt brzoljuk, felmerl a krds: hogyan brzoljuk a fogalmak kztti kapcso-
latokat? Persze tetszleges kapcsolatot egy programozsi nyelvben nem tudunk kzvetle-
nl kifejezni. De ha tudnnk, akkor sem akarnnk, hiszen osztlyainkat a mindennapi
letben hasznlt fogalmaknl szkebben s preczebben akarjuk meghatrozni. A szrmaz-
tatott osztly fogalma s a vele kapcsolatos nyelvi eljrsok clja a viszonyok kifejezse,
azaz hogy kifejezzk, mi a kzs az osztlyokban. A kr s a hromszg fogalmban pl-
dul kzs, hogy mindkett skgrbe-alakzat, gy a Circle (Kr) s Triangle (Hromszg)
osztlyokat gy rhatjuk le, hogy pontosan meghatrozott (explicit) mdon megmondjuk,
hogy a Shape (Alakzat) osztly a kzs bennk. Ha egy programban gy szerepeltetnk k-
rket s hromszgeket, hogy nem vonjuk be a skidom fogalmt, akkor valami lnyegeset
mulasztunk el. Ez a fejezet azt feszegeti, mi kvetkezik ebbl az egyszer elvbl ami va-
ljban az ltalban objektumorientltnak nevezett programozsi elv alapja.
A nyelvi lehetsgek s mdszerek bemutatsa az egyszertl s konkrttl a bonyolul-
tabb, kifinomultabb, elvontabb fel halad. Sokak szmra ez a megszokottl a kevsb is-
mert fel val haladst is fogja jelenti. De ez nem csupn egyszer utazs a rgi, rossz md-
szerektl az egyedli igaz t fel. Amikor rmutatok egy megkzelts korltaira, hogy
a programozt az j fel tereljem, mindig adott problmk kapcsn teszem; ms problmk
kapcsn vagy ms sszefggsben lehetsges, hogy a korbbi mdszer alkalmazsa a jobb
vlaszts. Hasznlhat programokat az itt trgyalt mdszerek mindegyiknek felhasznl-
sval rtak mr. A cl az, hogy az olvas megismerje az sszes eljrst, hogy aztn okos s
kiegyenslyozott mdon tudjon majd vlasztani kzlk, amikor igazi feladatokat kell
megoldania.
A fejezetben elszr az objektumorientlt programozst tmogat alapvet nyelvi eszkz-
ket mutatom be, majd egy hosszabb plda kapcsn azt trgyalom, hogyan lehet ezek alkal-
mazsval jl szerkesztett programot rni. Az objektumorientlt programozst tmogat to-
vbbi nyelvi eszkzket, pldul a tbbszrs rkldst vagy a futsi idej tpusazonos-
tst a 15. fejezet trgyalja.
Absztrakcis mdszerek 396
12.2. Szrmaztatott osztlyok
Vegynk egy programot, amely egy cg dolgozit kezeli. Egy effle programban lehet egy
ilyen adatszerkezet:
struct Employee { // alkalmazott
string first_name, family_name;
char middle_initial;
Date hiring_date;
short department;
// ...
};
Ezutn meghatrozhatunk egy fnkt is:
struct Manager { // fnk
Employee emp; // a fnk mint alkalmazott
set<Employee*> group; // beosztottak
short level;
// ...
};
A fnk egyben alkalmazott is, ezrt az Employee (Alkalmazott) adatokat a Manager (F-
nk, vezet) objektum emp tagjban troljuk. Ez nyilvnval lehet a programoz (kln-
sen egy figyelmes programoz) szmra, de a fordtprogram s az egyb eszkzk sehon-
nan nem fogjk tudni, hogy a Manager egyben Employee is. Egy Manager* nem Employee*
is egyben, gy a kett nem cserlhet fel. Egy Employee-ket tartalmaz listra nem vehetnk
fel egy Manager-t a megfelel kd megrsa nlkl. Vagy tpusknyszertst kellene alkal-
maznunk a Manager*-ra, vagy az emp tag cmt kellene az alkalmazottak listjra tennnk.
Egyik megolds sem elegns s zavar is lehet. A helyes megkzelts az, hogy kifejezetten
megmondjuk, a Manager-ek egyben Employee-k is, csak tovbbi adatokat is tartalmaznak:
struct Manager : public Employee {
set<Employee*> group;
short level;
// ...
};
A Manager az Employee-bl szrmazik, s fordtva, az Employee a Manager bzisosztlya.
A Manager osztlynak megvannak azok a tagjai, amelyek az Employee-nek is (first_name,
department stb.) s ezekhez jnnek hozz a sajt tagok (group, level stb.).
12. Szrmaztatott osztlyok 397
A szrmaztatst gyakran gy brzoljk grafikusan, hogy a szrmaztatott osztlybl egy nyi-
lat rajzolnak a bzisosztly fel, jelezve, hogy a szrmaztatott osztly a bzisosztlyra hivat-
kozik (s nem fordtva):
ltalban gy mondjk, a szrmaztatott osztly tulajdonsgokat rkl a bzisosztlytl.
Ennek alapjn ezt a kapcsolatot rkldsnek (rkls, inheritance) is hvjk. Az angol ki-
fejezsek a bzisposztlyra s a szrmaztatott osztlyra: base class (vagy superclass), illet-
ve derived class (subclass). Az utbbi szhasznlat (superclass fosztly, subclass alosz-
tly) azonban zavar lehet, hiszen a szrmaztatott osztly bizonyos rtelemben szlesebb
a bzisosztlynl, mivel annl tbb adatot trol s tbb fggvnyt biztost.
A szrmaztats npszer s hatkony megvalstsa a szrmaztatott osztly objektumot
a bzisosztly olyan objektumaknt brzolja, amely kiegszl a szrmaztatott osztlyra
egyedileg jellemz adatokkal is:
Absztrakcis mdszerek 398
Manager
Employee
first_name
family_name
...
first_name
family_name
...
group
level
...
Emplyee: Manager:
A Manager osztlynak az Employee-bl ilyen mdon val szrmaztatsa a Manager tpust
az Employee altpusv teszi, gy teht mindenhol, ahol Employee objektum hasznlhat,
egy Manager is megfelel. Most mr kszthetnk egy listt az alkalmazottakrl (Employee),
akiknek egy rsze vezet beoszts (Manager):
void f(Manager m1, Employee e1)
{
list<Employee*> elist;
elist.push_front(&m1);
elist.push_front(&e1);
// ...
}
Ne feledjk, egy Manager egyben Employee is, gy egy Manager*-ot hasznlhatunk
Employee*-knt is. Az Employee viszont nem felttlenl Manager, gy Employee*-ot nem
hasznlhatunk Manager*-knt. ltalban, ha egy Derived (szrmaztatott) osztlynak egy
Base (bzis) osztly nyilvnos bzisosztlya (15.3), akkor egy Base* vltoz
tpusknyszerts nlkl kaphat Derived* tpus rtket. A msik irnyban (Base*-rl
Derived*-ra) explicit konverzi szksges:
void g(Manager mm, Employee ee)
{
Employee* pe = &mm; // rendben: minden Manager egyben Employee is
Manager* pm = &ee; // hiba: nem minden Employee fnk
pm->level = 2; // katasztrfa: ee nem rendelkezik 'level' taggal
pm = static_cast<Manager*>(pe); // "nyers ervel": mkdik, mert pe
// a Manager tpus mm-re mutat
pm->level = 2; // ez is j: pm a Manager tpus mm-re mutat,
// amelynek van 'level' tagja
}
Vagyis mutatk s referencik hasznlatakor a szrmaztatott osztly objektumait gy kezel-
hetjk, mint a bzisosztly objektumait. A msik irnyban ez nem ll. A static_cast s
a dynamic_cast hasznlatt a 15.4.2 pont rja le.
Egy osztly bzisosztlyknt val hasznlata egyenrtk az osztly egy (nvtelen) objek-
tumnak deklarlsval. Kvetkezskppen egy osztlyt csak akkor hasznlhatunk bzis-
osztlyknt, ha definiltuk (5.7):
12. Szrmaztatott osztlyok 399
class Employee; // csak deklarci, nem definci
class Manager : public Employee { // hiba: Employee nem definilt
// ...
};
12.2.1. Tagfggvnyek
Az egyszer adatszerkezetek mint a Manager s az Employee nem tl rdekesek s sok-
szor nem is klnsebben hasznosak. Az informcit megfelel tpusknt kell megadnunk,
melyhez az elvgezhet mveletek is hozztartoznak, s ezt gy kell megtennnk, hogy
kzben nem ktdnk az adott brzolshoz:
class Employee {
string first_name, family_name;
char middle_initial;
// ...
public:
void print() const;
string full_name() const
{ return first_name + ' ' + middle_initial + ' ' + family_name; }
// ...
};
class Manager : public Employee {
// ...
public:
void print() const;
// ...
};
A szrmaztatott osztlyok tagfggvnyei ugyangy elrhetik a bzisosztly nyilvnos
(public) s vdett (protected, 15.3) tagjait, mintha maguk vezettk volna be azokat:
void Manager::print() const
{
cout << "A keresett nv " << full_name() << '\n';
// ...
}
A szrmaztatott osztly azonban nem ri el a bzisosztly privt (private) tagjait:
void Manager::print() const
{
cout << "A keresett nv " << family_name << '\n'; // hiba!
// ...
}
Absztrakcis mdszerek 400
A Manager::print() msodik vltozatt a fordt nem fogja lefordtani. A szrmaztatott osz-
tlynak nincs klnleges engedlye a bzisosztly privt tagjainak elrsre, gy
a Manager::print() szmra a family_name nem rhet el.
Nmelyek meglepdnek ezen, de gondoljuk el, mi lenne fordtott esetben: ha a szrmazta-
tott osztly tagfggvnye elrhetn a bzisosztly privt tagjait. A privt tag fogalma rtel-
metlenn vlna azltal, hogy a programoz hozzfrhetne az osztly privt rszhez egy
osztlyt szrmaztatva belle. Tovbb nem lehetne tbb egy privt tag hasznlatt a tag-
s bart (friend) fggvnyek tnzsvel megkeresni. Az egsz program minden forrsllo-
mnyt t kne nzni: a szrmaztatott osztlyokat s azok fggvnyeit keresni, majd a szr-
maztatott osztlyokbl szrmaztatott tovbbi osztlyokat s azok fggvnyeit s gy tovbb.
Ez legjobb esetben is fraszt s sokszor kivitelezhetetlen is. Ott, ahol ez elfogadhat, in-
kbb vdett (protected) s ne privt (private) tagokat hasznljunk. Egy vdett tag a szr-
maztatott osztlyok szmra olyan, mint egy nyilvnos (public), a tbbiek szmra azonban
privtnak minsl (15.3).
A legtisztbb megolds ltalban az, ha a szrmaztatott osztly a bzisosztlynak csak
a nyilvnos tagjait hasznlja:
void Manager::print() const
{
Employee::print(); // alkalmazottak adatainak kirsa
cout << level; // a fnkkre vonatkoz adatok kirsa
// ...
}
Vegyk szre, hogy a :: hatkr opertort kellett hasznlni, mert a print() fggvnyt
a Manager osztly jradefinilja. A fggvnynevek ilyen mdon val jrafelhasznlsa igen
ltalnos. Ha vatlanul ilyet runk:
void Manager::print() const
{
print(); // hopp!
// a fnkkre vonatkoz adatok kirsa
}
a program vratlan mdon jra s jra meg fogja hvni nmagt.
12. Szrmaztatott osztlyok 401
12.2.2. Konstruktorok s destruktorok
Egyes szrmaztatott osztlyoknak konstruktorokra van szksgk. Ha a bzisosztlynak
vannak ilyen fggvnyei, akkor az egyiket meg is kell hvni. Az alaprtelmezett konstruk-
torokat automatikusan is meghvhatjuk, de ha a bzisosztly minden konstruktora param-
tert ignyel, akkor a megfelel konstruktort csak explicit mdon lehet meghvni. Vegyk
a kvetkez pldt:
class Employee {
string first_name, family_name;
short department;
// ...
public:
Employee(const string& n, int d);
// ...
};
class Manager : public Employee {
set<Employee*> group; // beosztottak
short level;
// ...
public:
Manager(const string& n, int d, int lvl);
// ...
};
A bzisosztly konstruktora a szrmaztatott osztly konstruktornak definicijban kap pa-
ramtereket. Ebbl a szempontbl a bzisosztly konstruktora gy viselkedik, mintha
a szrmaztatott osztly tagja lenne (10.4.6):
Employee::Employee(const string& n, int d)
: family_name(n), department(d) // tagok kezdeti rtkadsa
{
// ...
}
Manager::Manager(const string& n, int d, int lvl)
: Employee(n,d), // a bzisosztly kezdeti rtkadsa
level(lvl) // tagok kezdeti rtkadsa
{
// ...
}
Absztrakcis mdszerek 402
Egy szrmaztatott osztly konstruktora csak a sajt tagjai s a bzisosztly konstruktora sz-
mra adhat meg kezdrtket; a bzisosztly tagjainak nem:
Manager::Manager(const string& n, int d, int lvl)
: family_name(n), // hiba: family_name nem deklarlt a Manager osztlyban
department(d), // hiba: department nem deklarlt a Manager osztlyban
level(lvl)
{
// ...
}
A fenti definci hrom hibt tartalmaz: nem hvja meg az Employee konstruktort, s kt z-
ben is megprbl kzvetlenl kezdrtket adni az Employee tagjainak.
Az osztlyba tartoz objektumok alulrl felfel plnek fel; elszr a bzisosztly, aztn
a tagok, vgl maga a szrmaztatott osztly. A megsemmists fordtott sorrendben trtnik:
elszr a szrmaztatott osztly, aztn a tagok, vgl a bzisosztly. A tagok a deklarci sor-
rendjben jnnek ltre s fordtott sorrendben semmislnek meg (10.4.6 s 15.2.4.1).
12.2.3. Msols
Egy osztlyba tartoz objektum msolst a msol konstruktor s az rtkadsok hatroz-
zk meg (10.4.4.1):
class Employee {
// ...
Employee& operator=(const Employee&);
Employee(const Employee&);
};
void f(const Manager& m)
{
Employee e = m; // e ltrehozsa m Employee rszbl
e = m; // m Employee rsznek msolsa e-be
}
Minthogy az Employee osztly msol fggvnyei nem tudnak a Manager osztlyrl, a Ma-
nager objektumnak csak az Employee rsze fog lemsoldni. Az objektumnak ez a felsze-
leteldse (slicing), azaz a tny, hogy ekkor az objektumnak csak egy szelete msoldik
le, meglep lehet s hibkhoz vezethet. A felszeletelds megakadlyozsa az egyik oka
annak, hogy osztlyhierarchiba tartoz objektumok esetben clszerbb mutatkat s
referencikat hasznlnunk. A hatkonysgi megfontolsok mellett tovbbi ok, hogy meg-
rizzk a tbbalak (polimorfikus) viselkedst (2.5.4 s 12.2.6).
12. Szrmaztatott osztlyok 403
Jegyezzk meg, hogy ha nem hatrozunk meg msol rtkad opertort, akkor a fordt-
program fog ltrehozni egyet. Ebbl kvetkezik, hogy az rtkad opertorok nem rk-
ldnek. (A konstruktorok soha.)
12.2.4. Osztlyhierarchik
Egy szrmaztatott osztly lehet maga is bzisosztly:
class Employee { /* ... */ };
class Manager : public Employee { /* ... */ };
class Director : public Manager { /* ... */ };
Az egymssal kapcsolatban ll osztlyok ilyen halmazt hagyomnyosan osztlyhierarchi-
nak hvjuk. A hierarchia leggyakrabban fa szokott lenni, de lehet ennl ltalnosabb grf is.:
class Temporary { /* ... */ };
class Secretary : public Employee { /* ... */ };
class Tsec : public Temporary, public Secretary { /* ... */ };
class Consultant : public Temporary, public Manager { /* ... */ };
brval:
Vagyis ahogy a 15.2 pont rszletesen elmagyarzza, a C++ nyelv kpes az osztlyoknak
egy irnytott krmentes grfjt kifejezni.
Absztrakcis mdszerek 404
Employee
Manager Secretary
Consultant
Tsec
Director
Temporary
12.2.5. Tpusmezk
Ha a deklarcikban alkalmazott knyelmes rvidtsnl tbbre akarjuk hasznlni az oszt-
lyok szrmaztatst, meg kell vlaszolnunk a kvetkez krdst: ha adott egy Base* muta-
t, akkor milyen szrmaztatott osztlyba tartozik az objektum, amelyre mutat? A problm-
nak ngy alapvet megoldsa van:
1. rjk el, hogy csak egyfle objektum jhessen szba (2.7, 13. fejezet).
2. Helyezznk egy tpusmezt a bzisosztlyba s a fggvnyek ezt krdezzk le.
3. Hasznljuk a dynamic_cast-ot (dinamikus tpuskonverzi, 15.4.2, 15.4.5).
4. Hasznljunk virtulis fggvnyeket (2.5.5, 12.2.6).
Bzisosztlyra hivatkoz mutatkat gyakran hasznlunk olyan trol osztlyokban
(container class), mint a halmaz (set), a vektor (vector) s a lista (list). Ekkor az els megol-
ds homogn listkat, azaz azonos tpus objektumokat eredmnyez. A tbbi megolds le-
hetv tesz heterogn listkat is, azaz olyanokat, ahol klnbz tpus objektumok (vagy
ilyenekre hivatkoz mutatk) lehetnek. A 3. megolds a 2. megoldsnak a nyelv ltal tmo-
gatott vltozata, a 4. pedig a 2.-nak egyedi, tpusbiztos talaktsa. Az 1. s a 4. megolds
prostsai klnsen rdekesek s ersek; majdnem mindig vilgosabb kdot eredm-
nyeznek, mint a 2. vagy a 3. megolds.
Nzznk meg elszr egy tpusmezs megoldst, hogy lssuk, legtbbszr mirt kerlen-
d. A fnk alkalmazott pldt gy rhatnnk t:
struct Employee {
enum Empl_type { M, E };
Empl_type type;
Employee() : type(E) { }
string first_name, family_name;
char middle_initial;
Date hiring_date;
short department;
// ...
};
struct Manager : public Employee {
Manager() { type = M; }
set<Employee*> group; // beosztottak
short level;
// ...
};
12. Szrmaztatott osztlyok 405
Most mr rhatunk egy fggvnyt, amely minden Employee-rl ki tudja rni az informcit:
void print_employee(const Employee* e)
{
switch (e->type) {
case Employee::E:
cout << e->family_name << '\t' << e->department << '\n';
// ...
break;
case Employee::M:
{ cout << e->family_name << '\t' << e->department << '\n';
// ...
const Manager* p = static_cast<const Manager*>(e);
cout << " szint " << p->level << '\n';
// ...
break;
}
}
}
Az alkalmazottak listja gy rhat ki:
void print_list(const list<Employee*>& elist)
{
for (list<Employee*>::const_iterator p = elist.begin(); p!=elist.end(); ++p)
print_employee(*p);
}
Ez jl mkdik, klnsen az egyetlen programoz ltal fenntartott kis programokban.
Alapvet gyengje, hogy az a felttele, hogy a programoz a megfelel (s a fordtprog-
ram ltal nem ellenrizhet mdon) kell, hogy kezelje a tpusokat. A problmt ltalban
slyosbtja, hogy az olyan fggvnyek, mint a print_employee, a szba jhet osztlyok k-
zs vonsait hasznljk ki:
void print_employee(const Employee* e)
{
cout << e->family_name << '\t' << e->department << '\n';
// ...
if (e->type == Employee::M) {
const Manager* p = static_cast<const Manager*>(e);
cout << " szint " << p->level << '\n';
// ...
}
}
Absztrakcis mdszerek 406
Egy nagy fggvnyben, ahol sok szrmaztatott tpust kell kezelni, nehz lehet az sszes
ilyen tpusmez-ellenrzst megtallni. De ha megtalltuk is, nehz lehet megrteni, hogy
mi is trtnik. Tovbb, ha a rendszer j Employee-vel bvl, az sszes fontos fggvnyt
mdostani kell vagyis az sszes olyat, amelyik ellenrzi a tpusmezt. Minden olyan
fggvnyt meg kell vizsglni, amelyiknek egy vltoztats utn szksge lehet a tpusmez
ellenrzsre. Ez azt jelenti, hogy hozz kell frni a kritikus forrskdhoz s kln munkt
jelent a vltoztats utni teszt is. A tpusknyszerts rulkod jele annak, hogy javtani le-
hetne a kdon.
Vagyis a tpusmezs megolds hibkra ad lehetsget s nehezen mdosthat kdot ered-
mnyez. Ahogy a rendszer mrete n, a problma slyosbodik, mert a tpusmez alkal-
mazsa ellentmond a modularits s az adatrejts elvnek. Minden tpusmezt hasznl
fggvnynek ismernie kell az sszes olyan osztly brzolst (s megvalstsuk egyb
rszleteit), amely a tpusmezs osztly leszrmazottja.
Ezenkvl ha van egy olyan adat (pldul egy tpusmez), amely minden szrmaztatott osz-
tlybl elrhet, akkor a programoz hajlamos arra, hogy tovbbi ilyen mezket hozzon
ltre. A kzs bzisosztly gy mindenfle hasznos informcik gyjtemnyv vlik.
Ez aztn a bzisosztly s a szrmaztatott osztlyok megvalstsnak legkevsb kvnatos
sszefondshoz vezet. A tisztbb felpts s knnyebb mdosthatsg kedvrt a k-
lnll dolgokat kezeljk kln, a klcsns fggseket pedig kerljk el.
12.2.6. Virtulis fggvnyek
A virtulis fggvnyek azltal kerlik el a tpusmezs megolds problmit, hogy segts-
gkkel a programoz olyan fggvnyeket deklarlhat a bzisosztlyban, amelyeket a szr-
maztatott osztlyok fellbrlhatnak. A fordt- s betltprogram gondoskodik az objek-
tumtpusok s az alkalmazott fggvnyek sszhangjrl:
class Employee {
string first_name, family_name;
short department;
// ...
public:
Employee(const string& name, int dept);
virtual void print() const;
// ...
};
12. Szrmaztatott osztlyok 407
A virtual kulcssz azt jelenti, hogy a print() fggvny felletknt szolgl az ebben az osz-
tlyban definilt print() fggvnyhez, illetve a szrmaztatott osztlyokban definilt print()
fggvnyekhez. Ha a szrmaztatott osztlyokban szerepelnek ilyenek, a fordtprogram
mindig az adott Employee objektumnak megfelel fggvnyt fogja meghvni.
Egy virtulis fggvny akkor szolglhat felletknt a szrmaztatott osztlyokban definilt
fggvnyekhez, ha azoknak ugyanolyan tpus paramtereik vannak, mint a bzisosztly-
belinek, s a visszatrsi rtk is csak nagyon csekly mrtkben klnbzik (15.6.2).
A virtulis tagfggvnyeket nha metdusoknak (method) is hvjk.
A virtulis fggvnyt mindig definilnunk kell abban az osztlyban, amelyben elszr
deklarltuk, hacsak nem tisztn virtulis (pure virtual) fggvnyknt adtuk meg (12.3):
void Employee::print() const
{
cout << family_name << '\t' << department << '\n';
// ...
}
Virtulis fggvnyt akkor is hasznlhatunk, ha osztlybl nem is szrmaztatunk tovbbi
osztlyt; ha pedig szrmaztatunk, annak a fggvnybl nem kell felttlenl sajt vltozat.
Osztly szrmaztatsakor csak akkor rjunk egy megfelel vltozatot a fggvnybl, ha va-
lban szksges:
class Manager : public Employee {
set<Employee*> group;
short level;
// ...
public:
Manager(const string& name, int dept, int lvl);
void print() const;
// ...
};
void Manager::print() const
{
Employee::print();
cout << "\tszint " << level << '\n';
// ...
}
Absztrakcis mdszerek 408
A szrmaztatott osztly azonos nev s azonos tpus paramterekkel br fggvnye fell-
rja vagy fellbrlja (override) a virtulis fggvny bzisosztlybeli vltozatt. Hacsak
kzvetlen mdon meg nem mondjuk, hogy a virtulis fggvny melyik vltozatt akarjuk
hasznlni mint az Employee::print() hvsnl , az objektumhoz leginkbb ill fellr
fggvny lesz meghvva.
A globlis print_employee() fggvny (12.2.5) szksgtelen, mert a helybe a print() tag-
fggvnyek lptek. Az alkalmazottak (Employee) listjt gy rathatjuk ki:
void print_list(const list<Employee*>& s)
{
for (list<Employee*>::const_iterator p = s.begin(); p!=s.end(); ++p) // lsd 2.7.2
(*p)->print();
}
De akr gy is:
void print_list(const list<Employee*>& s)
{
for_each(s.begin(),s.end(),mem_fun(&Employee::print)); // lsd 3.8.5
}
Minden Employee a tpusnak megfelelen rdik ki. A
int main()
{
Employee e("Brown",1234);
Manager m("Smith",1234,2);
list<Employee*> empl;
empl.push_front(&e); // 2.5.4
empl.push_front(&m);
print_list(empl);
}
pldul az albbi kimenetet eredmnyezi:
Smith 1234
szint 2
Brown 1234
Ez akkor is mkdik, ha a print_list() fggvnyt azeltt rtuk meg s fordtottuk le, mieltt
a Manager osztlyt egyltaln kitalltuk volna! Ez az osztlyoknak egy kulcsfontossg tu-
lajdonsga. Ha helyesen alkalmazzuk, az objektumorientlt tervezs sarokkve lesz s
a programok fejlesztsnl bizonyos fok stabilitst ad.
12. Szrmaztatott osztlyok 409
Azt, hogy az Employee fggvnyei attl fggetlenl helyesen viselkednek, hogy pontosan
milyen fajta Employee-re hvtuk meg azokat, tbbalaksgnak (polimorfizmus,
polymorphism) nevezzk. A virtulis fggvnyekkel br tpus neve tbbalak tpus
(polimorfikus tpus). A C++ nyelvben a tbbalak viselkedst virtulis fggvnyek haszn-
latval vagy az objektumoknak mutatkon vagy referencikon t val kezelsvel rhetjk
el. Ha kzvetlenl kezelnk egy objektumot s nem mutat vagy referencia segtsgvel,
a fordtprogram felismeri annak pontos tpust, gy a futsi idej tbbalaksgra nincs
szksg.
Vilgos, hogy a tbbalaksg tmogatsa rdekben a fordtprogramnak minden Employee
tpus objektumban valamilyen, a tpusra vonatkoz informcit (tpusinformcit) kell
nyilvntartania, melynek segtsgvel kpes a megfelel print() fggvnyt meghvni. Ehhez
rendszerint egyetlen mutatnyi hely is elg, s erre is csak azon osztlyokban van szksg,
amelyeknek van virtulis fggvnyk; teht nem minden osztlyban s mg csak nem is
minden szrmaztatott osztlyban. A tpusmezs megolds vlasztsa esetn ehhez kpest je-
lents mennyisg trterletet kellett volna a tpusmez szmra biztostanunk.
Ha egy fggvnyt (miknt a Manager::print()-et is) a :: hatkr-felold opertor segtsg-
vel hvunk meg, akkor ezltal kikapcsoljuk a virtualitst. Msklnben a Manager::print()
vgtelen rekurzit idzne el. A minstett nv hasznlatnak van mg egy elnye: ha egy
virtulis fggvny inline (ami el szokott fordulni), akkor a fordtprogram a :: minstvel
jelzett hvsokat kpes helyben kifejteni. Ennek segtsgvel a programoz hatkonyan k-
pes azokat az egyedi eseteket kezelni, amikor mondjuk egy virtulis fggvny ugyanarra az
objektumra egy msik fggvnyt is meghv. A Manager::print() fggvny ennek pldja.
Minthogy a Manager::print() meghvsakor meghatrozzuk az objektum tpust, az
Employee::print() ezt kvet meghvsakor a tpusrl mr nem kell jra dnteni.
rdemes megemlteni, hogy a virtulis fggvnyhvs hagyomnyos s nyilvnval megva-
lstsa az egyszer kzvetett fggvnyhvs (indirekci) (2.5.5), gy a hatkonysg elvesz-
tstl val flelem ne riasszon vissza senkit a virtulis fggvnyek hasznlattl ott, ahol
egy kznsges fggvnyhvs elfogadhatan hatkony.
Absztrakcis mdszerek 410
12.3. Absztrakt osztlyok
Sok osztly hasonlt az Employee osztlyra annyiban, hogy nmagban s szrmaztatott
osztlyok bzisosztlyaknt is hasznos. Az ilyen osztlyok szmra elegendek az elz
pontban bemutatott mdszerek. De nem minden osztly ilyen. Bizonyos osztlyok, pld-
ul a Shape (Alakzat), olyan elvont fogalmakat jelentenek meg, amelyekhez nem ltezhet-
nek objektumok. A Shape-nek csak mint bzisosztlynak van rtelme. Ez abbl is lthat,
hogy nem tudunk hozz virtulis fggvnyeket rtelmesen definilni:
class Shape {
public:
virtual void rotate(int) { error("Shape::rotate"); } // nem "elegns"
virtual void draw() { error("Shape::draw"); }
// ...
};
Egy ilyen meghatrozatlan alakzatot meg tudunk ugyan adni (a nyelv megengedi), de nem
sok rtelme van:
Shape s; // butasg: "alak nlkli alakzat"
A dolog azrt rtelmetlen, mert az s minden mvelete hibt fog eredmnyezni.
Jobb megolds, ha a Shape osztly virtulis fggvnyeit tisztn virtulis (pure virtual) fgg-
vnyknt deklarljuk. A virtulis fggvnyek az =0 kezdeti rtkadstl lesznek tisztn
virtulisak:
class Shape { // absztrakt osztly
public:
virtual void rotate(int) = 0; // tisztn virtulis fggvny
virtual void draw() = 0; // tisztn virtulis fggvny
virtual bool is_closed() = 0; // tisztn virtulis fggvny
// ...
};
Ha egy osztly legalbb egy tisztn virtulis fggvnnyel rendelkezik, akkor absztrakt osz-
tlynak (elvont osztly, abstract class) hvjuk, ilyen osztlyba tartoz objektumot pedig nem
hozhatunk ltre:
Shape s; // hiba: s az absztrakt Shape osztly vltozja lenne
12. Szrmaztatott osztlyok 411
Az absztrakt osztlyokat csak felletknt (interface), illetve ms osztlyok bzisosztlyaknt
hasznlhatjuk:
class Point { /* ... */ };
class Circle : public Shape {
public:
void rotate(int) { } // a Shape::rotate fellrsa
void draw(); // a Shape::draw fellrsa
bool is_closed() { return true; } // a Shape::is_closed fellrsa
Circle(Point p, int r);
private:
Point center;
int radius;
};
Ha egy tisztn virtulis fggvnyt a szrmaztatott osztlyban nem definilunk, akkor az tisz-
tn virtulis fggvny marad, st, a szrmaztatott osztly is absztrakt osztly lesz. Ez a meg-
valsts lpcszetes felptst teszi lehetv:
class Polygon : public Shape { // absztrakt osztly
public:
bool is_closed() { return true; } // a Shape::is_closed fellrsa
// ... a draw s a rotate nincs fellrva ...
};
Polygon b; // hiba: a Polygon osztlynak nem lehet objektuma
class Irregular_polygon : public Polygon {
list<Point> lp;
public:
void draw(); // a Shape::draw fellrsa
void rotate(int); // a Shape::rotate fellrsa
// ...
};
Irregular_polygon poly(some_points); // j (megfelel konstrukort felttelezve)
Az absztrakt osztlyok fontos kpessge, hogy segtsgkkel a megvalsts egyb rszei-
nek elrhetv ttele nlkl biztosthatunk felletet. Egy opercis rendszer pldul egy
absztrakt osztly mg rejtheti eszkzmeghajtinak tulajdonsgait:
class Character_device {
public:
virtual int open(int opt) = 0;
virtual int close(int opt) = 0;
Absztrakcis mdszerek 412
virtual int read(char* p, int n) = 0;
virtual int write(const char* p, int n) = 0;
virtual int ioctl(int ...) = 0;
virtual ~Character_device() { } // virtulis destruktor
};
Az egyes eszkzmeghajtkat a Character_device-bl szrmaztatott osztlyknt
definilhatjuk, s sokfle eszkzmeghajtt kezelhetnk ezen felleten keresztl. A virtulis
destruktorok fontossgt a 12.4.2 pont magyarzza el.
Az absztrakt osztlyok bevezetsvel immr minden eszkz a keznkben van arra, hogy
modulris mdon, ptkvekknt osztlyokat hasznlva egy teljes programot rjunk.
12.4. Osztlyhierarchik tervezse
Vegyk a kvetkez egyszer tervezsi problmt: hogyan lehet egy program szmra lehe-
tv tenni egy egsz rtk bekrst a felhasznli felletrl? Zavarbaejten sokfle mdon.
Ahhoz, hogy elszigeteljk programunkat ettl a sokflesgtl s felderthessk a klnbz
tervezsi mdokat, kezdjk a munkt ezen egyszer adatbeviteli mvelet modelljnek fell-
ltsval. A tnyleges felhasznli fellet elksztsnek rszleteit ksbbre halasztjuk.
Az alaptlet az, hogy lesz egy Ival_box (rtkmez) osztlyunk, amely tudja, hogy milyen
rtkeket fogadhat el. A program elkrheti egy Ival_box objektum rtkt s felszlthatja
arra is, hogy krje be ezt az rtket a felhasznltl, ha mg nem ll rendelkezsre. Azt is
megkrdezheti, hogy az rtk megvltozott-e a legutbbi krs ta.
Minthogy ez az alaptlet sokflekppen megvalsthat, abbl kell kiindulnunk, hogy sok-
fle klnbz Ival_box lesz: csszkk, szveges adatbeviteli mezk, ahov a felhasznl
berhatja az rtket, szmtrcsk, hanggal vezrelhet eszkzk.
Azt az ltalnos megkzeltst alkalmazzuk, hogy egy virtulis felhasznli felletet bocs-
tunk az alkalmazs rendelkezsre, amely a ltez felhasznli felletek szolgltatsainak
egy rszt biztostja. E fellet szmos rendszeren elkszthet, gy kdja hordozhat lesz.
Termszetesen vannak ms mdok is arra, hogy egy alkalmazst elvlasszunk a felhaszn-
li fellettl. Azrt vlasztottam ezt, mert ltalnos, mert a kapcsn egy sor eljrst s terve-
zsi szempontot lehet bemutatni, mert ezeket a mdszereket alkalmazzk a valdi felhasz-
12. Szrmaztatott osztlyok 413
nli felleteteket kezel rendszerekben, s vgl a leglnyegesebb ok , mert ezek
a mdszerek a felhasznli felletetek szk tartomnynl jval szlesebb krben is alkal-
mazhatk.
12.4.1. Hagyomnyos osztlyhierarchia
Els megoldsunk egy hagyomnyos osztlyhierarchia; ilyennel a Simula, Smalltalk s r-
gebbi C++-programokban tallkozhatunk.
Az Ival_box osztly az sszes Ival_box ltal hasznlatos felletet rja le s egy olyan alapr-
telmezett megvalstst ad, melyet az egyes Ival_box-ok sajtjaikkal fellbrlhatnak. Ezen-
kvl megadjuk az alapmegoldshoz szksges adatokat is:
class Ival_box {
protected:
int val;
int low, high;
bool changed;
public:
Ival_box(int ll, int hh) { changed = false; val = low = ll; high = hh; }
virtual int get_value() { changed = false; return val; }
virtual void set_value(int i) { changed = true; val = i; } // felhasznlk szmra
virtual void reset_value(int i) { changed = false; val = i; } // alkalmazsok szmra
virtual void prompt() { }
virtual bool was_changed() const { return changed; }
};
A fggvnyek alaprtelmezett vltozatai meglehetsen vzlatosak, pongyolk; cljuk leg-
inkbb az, hogy illusztrljk a megkzeltst. (Egy valdi osztly pldul rtkellenrzst
is vgezne.)
Az ival osztlyokat egy programoz gy hasznlhatn fel:
void interact(Ival_box* pb)
{
pb->prompt(); // jelzs a felhasznlnak
// ...
int i = pb->get_value();
if (pb->was_changed()) {
// j rtk; valamit csinlunk vele
}
Absztrakcis mdszerek 414
else {
// a rgi rtk j volt; ezt is felhasznljuk valahogy
}
// ...
}
void some_fct()
{
Ival_box* p1 = new Ival_slider(0,5); // az Ival_slider az Ival_box osztlybl szrmazik
interact(p1);
Ival_box* p2 = new Ival_dial(1,12);
interact(p2);
}
A programkd legnagyobb rsze az interact() fggvny stlusban rdna, s egyszer
Ival_box-okat, illetve azokra hivatkoz mutatkat hasznlna. gy a programnak nem kelle-
ne tudnia az esetleg nagy szm klnbz Ival_box-vltozatokrl, csak annak a viszony-
lag kis szm fggvnynek kellene ismernie azokat, amelyek ilyen objektumokat ltrehoz-
nak. Ez a felhasznlkat elszigeteli a szrmaztatott osztlyok esetleges mdostsaitl.
A kd legnagyobb rsznek mg arrl sem kell tudnia, hogy egyltaln klnbz
Ival_box-ok lteznek.
Hogy egyszerstsem a trgyalst, eltekintek attl a krdstl, hogyan vr a program beme-
netre. Lehetsges megolds, hogy a program a get_value() fggvnyben tnylegesen vr
a felhasznli bemenetre, megoldhat gy is, hogy az Ival_box-ot egy esemnyhez kap-
csoljuk s egy visszahvs (callback) segtsgvel vlaszolunk, esetleg a programmal kln
vgrehajtsi szlat indttatunk el az Ival_box szmra, majd a szl llapott krdezzk le.
Az ilyen dntsek alapvet fontossgak a felhasznli felletet kezel rendszerek terve-
zsekor, de ha itt a valsgot akr csak megkzelt rszletessggel trgyalnnk ezeket, el-
vonnnk a figyelmet a programozsi eljrsok s nyelvi eszkzk trgyalstl. Az itt be-
mutatott tervezsi mdszerek s az azokat tmogat nyelvi eszkzk nem ktdnek adott
felhasznli fellethez; jval szlesebb krben is alkalmazhatk.
A klnbz Ival_box-okat az Ival_box-bl szrmaztatott osztlyokknt hatrozhatjuk meg:
class Ival_slider : public Ival_box {
// a csszka kinzett, viselkedst meghatroz grafikai elemek
public:
Ival_slider(int, int);
int get_value();
void prompt();
};
Az Ival_box adattagjait vdettknt (protected) vezettk be, hogy a szrmaztatott osztlyok-
12. Szrmaztatott osztlyok 415
bl elrhetek legyenek. gy aztn az Ival_slider::get_value() fggvny elhelyezheti az r-
tket az Ival_box::val adattagban. A vdett tagok elrhetk az osztly s a szrmaztatott osz-
tlyok fggvnyei szmra is, de az ltalnos felhasznl szmra nem (15.3).
Az Ival_box-bl az Ival_slider mellett ms vltozatokat is szrmaztathatunk. Ezek kztt ott
lehet az Ival_dial, amelynl egy gomb forgatsval adhatunk meg egy rtket,
a Flashing_ival_slider, amely felvillan, ha a prompt() fggvnnyel erre krjk, s
a Popup_ival_slider, amely a prompt() hatsra valamilyen feltn helyen jelenik meg,
a felhasznltl szinte kikvetelve egy rtk megadst.
De vajon honnan vegyk a grafikus elemeket? A legtbb felhasznli felletet kezel rend-
szer biztost egy osztlyt, amely lerja a kpernyn lev objektumok alapvet tulajdonsga-
it. Ha pldul a Big Bucks Inc. (Sok Pnz Rt.) rendszert hasznljuk, akkor az
Ival_slider, az Ival_dial stb. osztlyok mindegyike egy-egy fajta BBwindow (Big Bucks
window) kell, hogy legyen. Ezt a legegyszerbben gy rhetjk el, ha Ival_box-unkat gy
rjuk t, hogy a BBwindow-bl szrmaztatott osztly legyen. gy aztn az sszes osztlyunk
a BBwindow-bl szrmaztatott lesz, teht elhelyezhet lesz a kpernyn, megjelense iga-
zodik majd a rendszer tbbi grafikus elemnek megjelenshez, tmretezhet, thelyez-
het lesz stb., a BBwindow rendszer szablyainak megfelelen. Osztlyhierarchink teht
gy fog kinzni:
class Ival_box : public BBwindow { /* ... */ }; // jrarva a BBwindow hasznlatra
class Ival_slider : public Ival_box { /* ... */ };
class Ival_dial : public Ival_box { /* ... */ };
class Flashing_ival_slider : public Ival_slider { /* ... */ };
class Popup_ival_slider : public Ival_slider { /* ... */ };
brval:
Absztrakcis mdszerek 416
BBwindow
Ival_box
Ival_dial Ival_slider
Flashing_ival_slider Popup_ival_slider
12.4.1.1. Brlat
Ez gy sok tekintetben jl fog mkdni s az ilyesfajta osztlyfelpts szmos problmra
j megolds. m van nhny htultje, melyek miatt ms tervezsi lehetsgek utn fo-
gunk nzni.
A BBwindow osztlyt utlag tettk az Ival_box bzisosztlyv, ami nem egszen helyes.
A BBwindow osztly nem alapvet rsze az Ival_box-ra ptett rendszernek, meglte csu-
pn rszletkrds. Az, hogy az Ival_box a BBwindow osztly leszrmazottja, ezt a rszlet-
krdst elsrend tervezsi dntss emeli. Ez abban az esetben helyes, ha cgnk kulcs-
fontossg zleti dntse, hogy a Big Bucks Inc. ltal biztostott krnyezetet hasznljuk.
De mi trtnik, ha Ival_box-unkat olyan rendszerekre is t szeretnnk ltetni, melyek az
Imperial Bananas, a Liberated Software vagy a Compiler Whizzles-tl szrmaznak? Ek-
kor programunkbl ngy vltozatot kellene ksztennk:
class Ival_box : public BBwindow { /* ... */ }; // BB vltozat
class Ival_box : public CWwindow { /* ... */ }; // CW vltozat
class Ival_box : public IBwindow { /* ... */ }; // IB vltozat
class Ival_box : public LSwindow { /* ... */ }; // LS vltozat
Ha ennyi vltozatunk van, mdostsuk, vltozatkvetsk rmlomm vlhat.
Egy msik problma, hogy az Ival_box-ban deklarlt adatok minden szrmaztatott osztly
rendelkezsre llnak. Ezek az adatok megint csak egy apr rszletet jelentenek, mgis
bekerltek az Ival_box felletbe. Ez gyakorlati szempontbl azt is jelenti, hogy nem bizto-
stott, hogy mindig a megfelel adatot kapjuk. Az Ival_slider esetben pldul nem szks-
ges az adat kln trolsa, minthogy ez a csszka llsbl meghatrozhat, valahnyszor
vgrehajtjk a get_value()-t. ltalban is problematikus kt rokon, de eltr adathalmaz t-
rolsa. Elbb-utbb valaki elri, hogy ne legyenek tbb sszhangban. A tapasztalat is azt
mutatja, hogy kezd programozk szksgtelen s nehezebb mdosthatsgot eredm-
nyez mdon szeretnek a vdett (protected) adattagokkal gyeskedni. Jobb, ha az adatta-
gok privtok, mert gy a szrmaztatott osztlyok ri nem zavarhatjk ssze azokat. Mg
jobb, ha az adatok a szrmaztatott osztlyokban vannak, mert akkor pontosan meg tudnak
felelni a kvetelmnyeknek s nem keserthetik meg az egymssal nem rokon szrmazta-
tott osztlyok lett. A vdett fellet szinte mindig csak fggvnyeket, tpusokat s konstan-
sokat tartalmazzon.
Ha az Ival_box a BBwindow-bl szrmazik, ez azzal az elnnyel jr, hogy az Ival_box fel-
hasznli a BBwindow minden szolgltatst ignybe vehetik, ami sajnos azt is jelenti, hogy
a BBwindow osztly vltozsakor az Ival_box felhasznlinak jra kell fordtaniuk, esetleg
jra kell rniuk a kdjukat. A legtbb C++-vltozat gy mkdik, hogy ha egy bzisosztly
mrete megvltozik, akkor az sszes szrmaztatott osztlyt jra kell fordtani.
12. Szrmaztatott osztlyok 417
Vgl lehetsges, hogy programunknak olyan vegyes krnyezetben kell futnia, ahol kln-
bz felhasznli felletek ablakai lteznek egyidejleg. Vagy azrt, mert valahogy ezek
egy kpernyn tudnak osztozni, vagy mert programunknak klnbz rendszerek felhasz-
nlival kell kapcsolatot tartania. Ehhez pedig nem elg rugalmas megolds, ha a felhasz-
nli felletet az egyetlen Ival_box felletnk bzisosztlyaknt bedrtozzuk.
12.4.2. Absztrakt osztlyok
Nos, kezdjk jra a tervezst, hogy megoldjuk a hagyomnyos felpts brlatban felve-
tett problmkat:
1. A felhasznli fellet valban olyan rszletkrds legyen, amelyrl nem kell
tudomst vennik azon felhasznlknak, akiket nem rdekel.
2. Az Ival_box osztly ne tartalmazzon adatokat.
3. Ha megvltozik a felhasznli felletet kezel rendszer, ne legyen szksges
az Ival_box csaldot felhasznl kd jrafordtsa.
4. Klnbz felhasznli felletekhez tartoz Ival_box-ok tudjanak egyszerre
ltezni a programban.
Tbbfle megolds knlkozik erre; most egy olyat mutatok be, amely tisztn megvalst-
hat a C++ nyelvvel.
Elszr is, az Ival_box osztlyt puszta felletknt (pure interface) hatrozzuk meg:
class Ival_box {
public:
virtual int get_value() = 0;
virtual void set_value(int i) = 0;
virtual void reset_value(int i) = 0;
virtual void prompt() = 0;
virtual bool was_changed() const = 0;
virtual ~Ival_box() { }
};
Ez sokkal vilgosabb, mint az Ival_box osztly eredeti deklarcija volt. Elhagytuk az adat-
tagokat s a tagfggvnyek egyszerstett kifejtst is. Elmaradt a konstruktor is, mivel
nincs kezdrtkre vr adat. Ehelyett egy virtulis destruktorunk van, amely biztostja az
rkl osztlyok adatainak helyes eltakartst.
Absztrakcis mdszerek 418
Az Ival_slider defincija gy alakulhat:
class Ival_slider : public Ival_box, protected BBwindow {
public:
Ival_slider(int,int);
~Ival_slider();
int get_value();
void set_value(int i);
// ...
protected:
// a BBwindow virtulis fggvnyeit fellr fggvnyek
// pl. BBwindow::draw(), BBwindow::mouse1hit()
private:
// a csszka adatai
};
Mivel az Ival_slider osztly az absztrakt Ival_box osztlybl szrmazik, meg kell valstania
annak tisztn virtulis (pure virtual) fggvnyeit. A BBwindow osztlybl is szrmazik, ezrt
onnan valk az eszkzei, melyekkel ezt megteheti. Az Ival_box adja a szrmaztatott osztly
fellett, ezrt nyilvnos (public) mdon szrmazik onnan. Mivel a BBwindow osztlybl
val szrmazsa mindssze segtsget nyjt a megvalstshoz, onnan vdett (protected)
mdon szrmazik (15.3.2). Ebbl kvetkezik, hogy az Ival_slider-t felhasznl programoz
nem hasznlhatja kzvetlenl a BBwindow ltal nyjtott eszkzket. Az Ival_slider fellete
az Ival_box-tl rklt rszbl ll, illetve abbl, amit maga az Ival_slider kifejezetten
deklarl. Azrt hasznlunk vdett szrmaztatst a szigorbb megktst jelent (s ltalban
biztonsgosabb) privt helyett, hogy az Ival_slider-bl szrmaztatott osztlyok szmra
a BBwindow-t elrhetv tegyk.
A tbb osztlybl val kzvetlen rkldst ltalban tbbszrs rkldsnek (multiple
inheritance) hvjk (15.2). Vegyk szre, hogy Ival_slider-nek mind az Ival_box, mind
a BBwindow fggvnyei kzl fell kell rnia nhnyat, ezrt kzvetve vagy kzvetlenl
mindkt osztlybl szrmaznia kell. Mint a 12.4.1.1 pontban lttuk, lehetsges ugyan az
Ival_slider kzvetett szrmaztatsa a BBwindow-bl (azltal, hogy az Ival_box
a BBwindow-bl szrmazik), de ez nemkvnatos mellkhatsokkal jr. Hasonlan, az az
t, hogy a BBwindow megvalstsi osztly tagja legyen az Ival_box-nak, nem jrhat,
mert egy osztly nem rhatja fell tagjainak virtulis fggvnyeit (24.3.4). Az ablaknak az
Ival_box osztly egy BBwindow* tpus tagjaknt val brzolsa teljesen eltr szerkezet-
hez vezet, melynek megvannak a maga elnyei s htrnyai (12.7[14], 25.7).
rdekes mdon az Ival_slider ilyen mdon val deklarlsa esetn ugyanolyan kdot rha-
tunk, mint azeltt. Csak azrt vltoztattunk, hogy a szerkezet logikusabb mdon tkrzze
a megvalstst.
12. Szrmaztatott osztlyok 419
Szmos osztlynak szksge van valamilyen rendraksra, mieltt egy objektuma meg-
semmisl. Mivel az absztrakt Ival_box osztly nem tudhatja, hogy egy szrmaztatott osztly-
nak nincs-e szksge erre, fel kell tteleznie, hogy igenis szksg van r. A rendrakst gy
biztostjuk, hogy a bzisosztlyban definiljuk az Ival_box::~Ival_box() virtulis destruktort
s a szrmaztatott osztlyokban megfelel mdon fellrjuk azt:
void f(Ival_box* p)
{
// ...
delete p;
}
A delete opertor megsemmisti az objektumot, amelyre p mutat. Nem tudhatjuk, hogy pon-
tosan milyen osztly objektumrl van sz, de mivel az Ival_box-nak virtulis destruktora
van, a megfelel destruktor fog meghvdni, (ha az adott osztlynak van ilyen).
Az Ival_box hierarchit most gy rhatjuk le:
class Ival_box { /* ... */ };
class Ival_slider : public Ival_box, protected BBwindow { /* ... */ };
class Ival_dial : public Ival_box, protected BBwindow { /* ... */ };
class Flashing_ival_slider : public Ival_slider { /* ... */ };
class Popup_ival_slider : public Ival_slider { /* ... */ };
Egyszer rvidtsekkel pedig gy brzolhatjuk:
A szaggatott nyilak a vdett (protected) md rkldst jellik. Az ltalnos felhasznlk
szmra ezek csak rszletkrdsek.
Absztrakcis mdszerek 420
BBwindow
Ival_slider Ival_dial
Flashing_slider Popup_slider
Ival_box BBwindow
12.4.3. Egyb megvalstsok
Ez a szerkezet tisztbb s knnyebben mdosthat, mint a hagyomnyos, de nem kevs-
b hatkony. A vltozatkvetsi problmt azonban nem oldja meg:
class Ival_box { /* ... */ }; // kzs
class Ival_slider : public Ival_box, protected BBwindow { /* ... */ }; // BB
class Ival_slider : public Ival_box, protected CWwindow { /* ... */ }; // CW
// ...
Radsul a BBwindow-hoz s a CWwindow-hoz rt Ival_slider-ek nem ltezhetnek egytt,
mg akkor sem, ha egybknt maguk a BBwindow s CWwindow felhasznli felletek
igen.
A nyilvnval megolds az, hogy klnbz nev Ival_slider osztlyokat hozunk ltre:
class Ival_box { /* ... */ };
class BB_ival_slider : public Ival_box, protected BBwindow { /* ... */ };
class CW_ival_slider : public Ival_box, protected CWwindow { /* ... */ };
// ...
brval:
Hogy programunk Ival_box osztlyait jobban elszigeteljk a megvalsts egyb rszletei-
tl, szrmaztathatunk egy absztrakt Ival_slider osztlyt az Ival_box-bl, majd ebbl rkl-
tethetjk az egyes rendszerfgg Ival_slider-eket:
class Ival_box { /* ... */ };
class Ival_slider : public Ival_box { /* ... */ };
class BB_ival_slider : public Ival_slider, protected BBwindow { /* ... */ };
class CW_ival_slider : public Ival_slider, protected CWwindow { /* ... */ };
// ...
12. Szrmaztatott osztlyok 421
BBwindow
BB_ival_slider CW_ival_slider
Ival_box CWwindow
brval:
ltalban mg ennl is jobban jrunk, ha a hierarchiban egyedibb osztlyokat hasznlunk.
Ha pldul a Big Bucks Inc. rendszerben van egy csszka (slider) osztly, akkor a mi
Ival_slider-nket kzvetlenl a BBslider-bl szrmaztathatjuk:
class BB_ival_slider : public Ival_slider, protected BBslider { /* ... */ };
class CW_ival_slider : public Ival_slider, protected CWslider { /* ... */ };
brval:
Ez a javts jelents lehet abban a (srn elfordul) esetben, ha a mi fogalmaink nem es-
nek tvol a megvalsts cljbl felhasznlt rendszer fogalmaitl. Ekkor a programozs tu-
lajdonkppen a rokon fogalmak kztti lekpezsre egyszersdik, s a BBwindow-hoz
hasonl ltalnos bzisosztlyokbl val rklds ritkn fordul el. A teljes hierarchia
egyrszt az eredeti, alkalmazskzpont rendszer szrmaztatott osztlyokknt megvals-
tott felleteinek viszonyrendszerbl fog llni:
class Ival_box { /* ... */ };
class Ival_slider : public Ival_box { /* ... */ };
class Ival_dial : public Ival_box { /* ... */ };
class Flashing_ival_slider : public Ival_slider { /* ... */ };
class Popup_ival_slider : public Ival_slider { /* ... */ };
Absztrakcis mdszerek 422
BBwindow
BB_ival_slider CW_ival_slider
Ival_slider
Ival_box
CWwindow
BBslider
BB_ival_slider CW_ival_slider
Ival_slider CWslider
BBwindow Ival_box CWwindow
Illetve a hierarchit szintn az rklds segtsgvel tbbfle grafikus felhasznli fel-
letre lekpez szrmaztatott osztlyokbl:
class BB_ival_slider : public Ival_slider, protected BBslider { /* ... */ };
class BB_flashing_ival_slider : public Flashing_ival_slider,
protected BBwindow_with_bells_and_whistles { /* ... */ };
class BB_popup_ival_slider : public Popup_ival_slider, protected BBslider { /* ... */ };
class CW_ival_slider : public Ival_slider, protected CWslider { /* ... */ };
// ...
A kapott felptmnyt egyszer rvidtsek segtsgvel gy brzolhatjuk:
Az eredeti Ival_box hierarchia vltozatlan marad, csak a konkrt megvalstst vgz osz-
tlyok veszik krl.
12.4.3.1. Brlat
Az absztrakt osztlyokat hasznl osztlyszerkezet rugalmas s majdnem ugyanolyan egy-
szeren kezelhet, mint a konkrt felhasznli felletet bzisosztlyknt szerepeltet.
Az utbbiban a fa gykere a megfelel ablakosztly, az elbbiben viszont vltozatlanul az
alkalmazs osztlyhierarchija marad a tnyleges megvalstst vgz osztlyok alapja.
A program szempontjbl ezek a szerkezetek egyenrtkek abban az rtelemben, hogy
majdnem az egsz kd vltoztats nlkl s ugyangy mkdik mindkt esetben, s mind-
kettnl az alkalmazott felhasznli fellettl fgg elemekre val tekintet nlkl vizsgl-
hatjuk az Ival_box csald osztlyait. A 12.4.1-beli interact() fggvnyt pldul nem kell j-
rarnunk, ha az egyik szerkezetrl a msikra vltunk.
12. Szrmaztatott osztlyok 423
Ival_box
Ival_slider Ival_dial
ipopup iflash
BBislider CWipop
BBslider
CWislider
BBslider CWsl CWsl BBb&w CWsl
BBipop BBifl CWifl
Mindkt esetben jra kell rnunk az egyes Ival_box osztlyokat, ha a felhasznli fellet
nyilvnos fellete megvltozik, de az absztrakt osztlyokat hasznl szerkezet esetben
szinte az egsz kd vdett a megvalsts vltozstl s egy ilyen vltozs utn nem kell
jrafordtani. Ez klnsen akkor fontos, ha a megvalstst vgz elemek ksztje egy j,
majdnem kompatibilis vltozatot bocst ki. Radsul az absztrakt osztlyos megoldst v-
lasztk a klasszikus hierarchia hveinl kevsb vannak kitve az egyedi, mshol nem hasz-
nlhat megvalsts csapdjba val bezrds veszlynek. Az elvont Ival_box oszt-
lyokra ptett programot vlasztva nem hasznlhatjuk vletlenl a megvalst osztlyok
nyjtotta lehetsgeket, mert csak az Ival_box hierarchiban kifejezetten megadott lehet-
sgek rhetk el, semmi sem rkldik automatikusan egy rendszerfgg bzisosztlytl.
12.4.4. Az objektumok ltrehozsnak adott helyre korltozsa
A program legnagyobb rsze megrhat az Ival_box fellet felhasznlsval. Ha a szrmaz-
tatott felletek tovbbfejldnek s tbb szolgltatst nyjtanak, mint a sima Ival_box, akkor
nagyrszt hasznlhatjuk az Ival_box, Ival_slider stb. felleteket. Az objektumokat azonban
az adott rendszerre jellemz nevek (pldul CW_ival_dial s BB_flashing_ival_slider) fel-
hasznlsval kell ltrehozni. J lenne, ha az ilyen rendszerfgg nevek minl kevesebb
helyen fordulnnak el, mivel az objektumok ltrehozsa nehezen kthet helyhez, hacsak
nem szisztematikusan jrunk el.
Szoks szerint az indirekci (kzvetett hivatkozs) bevezetse a megolds. Ezt tbbflekp-
pen is megtehetjk. Egyszer megolds lehet pldul egy olyan osztly bevezetse, amely
az objektumokat ltrehoz mveletekrt felels:
class Ival_maker {
public:
virtual Ival_dial* dial(int, int) =0; // trcsa (dial) ksztse
virtual Popup_ival_slider* popup_slider(int, int) =0; // elugr csszka (popup slider)
// ksztse
// ...
};
Az Ival_maker osztly az Ival_box hierarchia minden olyan fellete szmra rendelkezik az
adott tpus objektumot ltrehoz fggvnnyel, mely felletrl a felhasznlk tudhatnak.
Az ilyen osztlyokat gyrnak (factory) hvjk, fggvnyeiket pedig nmikpp flreveze-
t mdon virtulis konstruktoroknak (15.6.2).
Absztrakcis mdszerek 424
Az egyes klnbz felhasznli felleteket kezel rendszereket most az Ival_maker osz-
tlybl szrmaztatott osztlyokknt brzoljuk:
class BB_maker : public Ival_maker { // BB-vltozatok ksztse
public:
Ival_dial* dial(int, int);
Popup_ival_slider* popup_slider(int, int);
// ...
};
class LS_maker : public Ival_maker { // LS-vltozatok ksztse
public:
Ival_dial* dial(int, int);
Popup_ival_slider* popup_slider(int, int);
// ...
};
Minden fggvny a kvnt fellet s megvalstsi tpus objektumot hozza ltre:
Ival_dial* BB_maker::dial(int a, int b)
{
return new BB_ival_dial(a,b);
}
Ival_dial* LS_maker::dial(int a, int b)
{
return new LS_ival_dial(a,b);
}
Ha adott egy mutat egy Ival_maker objektumra, akkor a programoz ennek segtsgvel
gy hozhat ltre objektumokat, hogy nem kell tudnia, pontosan milyen rendszer felhasz-
nli fellet van hasznlatban:
void user(Ival_maker* pim)
{
Ival_box* pb = pim->dial(0,99); // megfelel trcsa ltrehozsa
// ...
}
BB_maker BB_impl; // BB-felhasznlknak
LS_maker LS_impl; // LS-felhasznlknak
void driver()
{
user(&BB_impl); // BB hasznlata
user(&LS_impl); // LS hasznlata
}
12. Szrmaztatott osztlyok 425
12.5. Osztlyhierarchik s absztrakt osztlyok
Az absztrakt osztlyok felletek (interface). Az osztlyhierarchia annak eszkze, hogy foko-
zatosan ptsnk fel osztlyokat. Termszetesen minden osztly ad egy felletet a progra-
moz szmra, nmely absztrakt osztly pedig jelents szolgltatsokat knl, amelyekre
pthetnk, de fellet s ptk szerepk alapveten az absztrakt osztlyoknak s az
osztlyhierarchiknak van.
Klasszikus hierarchinak azt a felptst nevezzk, amelynek egyes osztlyai hasznos szol-
gltatsokat knlnak a felhasznlknak, illetve egyben a fejlettebb vagy egyedi feladatot
vgz osztlyok szmra ptkl szolglnak. Az ilyen felpts idelisan tmogatja a l-
psenknti finomtssal val fejlesztst, illetve az j osztlyok ltrehozst, amennyiben
ezek megfelelen illeszkednek a hierarchiba.
A klasszikus felpts a tnyleges megvalstst sokszor sszekapcsolja a felhasznlknak
nyjtott fellettel. Ez gyben az absztrakt osztlyok segthetnek. Az absztrakt osztlyok se-
gtsgvel felptett rendszer tisztbb s hatkonyabb mdot ad a fogalmak kifejezsre,
anlkl hogy a megvalsts rszleteivel keveredne vagy jelentsen nveln a program fu-
tsi idejt. A virtulis fggvnyek meghvsa egyszer s fggetlen attl, hogy mifle elvo-
natkoztatsi rteg hatrt lpi t. Egy absztrakt osztly virtulis fggvnyt meghvni sem-
mivel sem kerl tbbe, mint brmely ms virtulis fggvnyt.
A fentiekbl add vgkvetkeztets az, hogy egy rendszert a felhasznlk fel mindig
absztrakt osztlyok hierarchijaknt mutassunk, de klasszikus hierarchiaknt ptsnk fel.
12.6. Tancsok
[1] Kerljk a tpusmezk alkalmazst. 12.2.5.
[2] Az objektumok felszeleteldst (slicing) elkerlend hasznljunk mutatkat s
referencikat. 12.2.3.
[3] Hasznljunk absztrakt osztlyokat, hogy a vilgos felletek elksztsre ssz-
pontosthassunk. 12.3.
[4] Hasznljunk absztrakt osztlyokat, hogy minl kisebb felleteket hasznlhas-
sunk. 12.4.2.
[5] Hasznljunk absztrakt osztlyokat, hogy a felleteket elvlasszuk a megvalst-
si rszletektl. 12.4.2.
Absztrakcis mdszerek 426
[6] Hasznljunk virtulis fggvnyeket, hogy ksbb j megvalstst kszthessnk
a meglev felhasznli kd befolysolsa nlkl. 12.4.1.
[7] Hasznljunk absztrakt osztlyokat, hogy minl kevesebbszer kelljen a felhaszn-
li kdot jrafordtani. 12.4.2.
[8] Hasznljunk absztrakt osztlyokat, hogy a program tbbfle rendszeren is m-
kdjn. 12.4.3.
[9] Ha egy osztlynak van virtulis fggvnye, akkor legyen virtulis destruktora is.
12.4.2.
[10] Az absztrakt osztlyoknak ltalban nincs szksgk konstruktorra. 12.4.2.
[11] Az nll fogalmakat kln brzoljuk. 12.4.1.1.
12.7. Gyakorlatok
1. (*1) Ha adott a kvetkez:
class Base {
public:
virtual void iam() { cout << "Bzisosztly\n"; }
};
Szrmaztassunk kt osztlyt a Base-bl, mindegyiknek legyen egy iam() fgg-
vnye, amely kirja az osztly nevt. Hozzunk ltre egy-egy ilyen osztly ob-
jektumot s hvjuk meg rjuk az iam() fggvnyt. Rendeljnk a szrmaztatott
osztlyok objektumaira hivatkoz mutatkat Base* tpus mutatkhoz s hvjuk
meg ezeken keresztl az iam() fggvnyt.
2. (*3.5) Ksztsnk egy egyszer grafikus rendszert a rendelkezsnk ll grafi-
kus felhasznli fellet felett. (Ha nincs ilyen vagy nincs tapasztalatunk ilyesmi-
vel, akkor kszthetnk egy egyszer, ASCII karakterekbl felptett megvals-
tst, ahol egy pont egy karakterpozcinak felel meg, s az rs a megfelel
karakter, mondjuk a * megfelel pozcira val helyezst jelenti.)
Ebben a feladatban s a tovbbiakban a kvetkez osztlyokat hasznljuk:
Window (Ablak), Point (Pont), Line (Vonal), Dot (Kpernypont), Rectangle
(Tglalap), Circle (Kr), Shape (Alakzat, Idom), Square (Ngyzet) s Triangle
(Hromszg).
A Window(n,m) hozzon ltre egy n-szer m mret terletet a kpernyn.
A kperny pontjait az (x,y) derkszg (descartes-i) koordintk segtsgvel
cmezzk meg. A Window osztlyba tartoz w aktulis helye w.current()
12. Szrmaztatott osztlyok 427
kezdetben Point(0,0). A pozcit a w.current(p) hvssal llthatjuk be, ahol p
egy Point. A Point objektumokat egy koordinta-pr adja meg: Point(x,y);
a Line objektumokat egy Point pr Line(w.current(),p2) ; a Shape osztly
a Dot, a Line, a Rectangle, a Circle stb. kzs fellete. Egy Point nem Shape is
egyben. A Dot(p)-knt ltrehozott Dot egy Point p-t jelent a kpernyn.
A Shape-ek nem lthatk, amg a draw() fggvnyt meg nem hvjuk; pldul:
w.draw(Circle(w.current(),10)). Minden Shape-nek 9 rintkezsi pontja van: e
(east kelet), w (west nyugat), n (north szak), s (south dl), ne (szakke-
let), nw (szaknyugat), se (dlkelet), sw (dlnyugat) s c (center kzppont).
A Line(x.c(),y.nw()) pldul egy vonalat hz az x kzeptl az y bal fels sar-
khoz. Ha egy Shape-re alkalmaztuk a draw() fggvnyt, az aktulis pozci
a Shape se()-je lesz. Egy Rectangle-t a bal als s a jobb fels cscsval adunk
meg; Rectangle(w.current(),Point(10,10)). Egyszer tesztknt jelentsnk meg
egy gyermekrajzot, amely egy hzat brzol tetvel, kt ablakkal, s egy ajtval.
3. (*2) Egy Shape fontos rszei szakaszokknt jelennek meg a kpernyn. Adjunk
meg olyan mveleteket, amelyek segtsgvel meg tudjuk vltoztatni ezen sza-
kaszok kinzett. Az s.thickness(n) a 0,1,2,3 rtkek valamelyikre lltsa be
a vonalszlessget, ahol a 2 az alaprtelmezett rtk s a 0 rtk azt jelenti,
hogy a vonal lthatatlan. A vonal lehessen tmr, szaggatott vagy pontokbl
ll is. Ezt a Shape::outline() fggvny lltsa be.
4. (*2.5) rjuk meg a Line::arrowhead() fggvnyt, amely egy vonal vgre egy
nyilat rajzol. Minthogy egy vonalnak kt vge van s a nyl a vonalhoz kpest
ktfle irnyba mutathat, gy az arrowhead() fggvny paramtere vagy para-
mterei ki kell, hogy tudjk fejezni ezt a ngyfle lehetsget.
5. (*3.5) Gondoskodjunk arrl, hogy azon pontok s vonalszakaszok, amelyek
kvl esnek egy Window-n, ne jelenjenek meg. Ezt a jelensget gyakran hvjk
levgsnak (clipping). E clbl gyakorlatknt ne hagyatkozzunk a felhasz-
nlt grafikus felhasznli felletre.
6. (*2.5) Egsztsk ki grafikai rendszernket a Text tpussal. A Text legyen egy
tglalap alak Shape, amely karaktereket tud megjelenteni. Alaprtelmezs sze-
rint egy karakter a koordinta-tengelyen minden irnyban egy egysgnyi helyet
foglaljon el.
7. (*2) Hatrozzunk meg egy fggvnyt, amely megtallja kt Shape egymshoz
legkzelebbi pontjait s sszekti azokat.
8. (*3) Vezessk be grafikai rendszernkbe a szn fogalmt. Hromfle dolog lehet
sznes: a httr, egy zrt Shape belseje s egy Shape hatra.
Absztrakcis mdszerek 428
9. (*2) Vegyk az albbi osztlyt:
class Char_vec {
int sz;
char element[1];
public:
static Char_vec* new_char_vec(int s);
char& operator[ ](int i) { return element[i]; }
// ...
};
Definiljuk a new_char_vec()-t, hogy egybefgg memriaterletet foglalhas-
sunk le egy Char_vec objektum szmra, gy elemeit az element() fggvnnyel
indexelhetjk. Milyen krlmnyek kztt okoz ez a trkk komoly problm-
kat?
10. (*2.5) Ha adottak a Shape osztlybl szrmaz Circle, Square s Triangle oszt-
lyok, hatrozzuk meg az intersect() fggvnyt, amely kt Shape* paramtert
vesz s a megfelel fggvnyek meghvsval megllaptja, hogy a kt Shape t-
fed-e, metszi-e egymst. Ehhez szksges lesz az osztlyok megfelel (virtu-
lis) fggvnyekkel val bvtse. Ne rjuk meg az tfeds tnyleges megllapt-
sra szolgl kdot, csak arra gyeljnk, hogy a megfelel fggvnyeket hvjuk
meg. Ezt az eljrst angolul ltalban double dispatch vagy multi-method
nven emlegetik.
11. (*5) Tervezznk s rjunk meg egy esemnyvezrelt szimulcikat vgz
knyvtrat. Segtsg: nzzk meg a <task.h> fejllomnyt. Ez egy rgi program,
az olvas jobbat tud rni. Legyen egy task nev osztly. A task osztly objektu-
mok legyenek kpesek llapotuk mentsre s visszalltsra (mondjuk
a task::save() s a task::restore() fggvnyekkel), hogy kiegszt eljrsknt
(co-routine) mkdhessenek. Az egyes elvgzend feladatokat a task osztlybl
rkl osztlyok objektumaiknt adhassuk meg. A task-ok ltal vgrehajtand
programokat virtulis fggvnyekkel hatrozzuk meg. Egy j task szmra le-
gyen lehetsges paramtereket megadni konstruktora(i)nak paramtereknt.
Legyen egy temez, amely megvalstja a virtulis id fogalmt. Legyen egy
task::delay(long) fggvny, amely fogyasztja ezt a virtulis idt. Az, hogy ez
az temez a task rsze vagy nll osztly lesz-e, a f tervezsi dntsek egyi-
ke. A task-oknak kapcsolatot kell tartaniuk egymssal. Erre a clra tervezznk
egy queue osztlyt. Egy task-nak legyen lehetsge tbb forrs fell rkez be-
menetre vrakozni. Kezeljk a futsi idej hibkat azonos mdon. Hogyan le-
hetne egy ilyen knyvtrat hasznl programban hibakeresst vgezni?
12. (*2) Hatrozzuk meg egy kalandjtk szmra a Warrior (harcos), Monster
(szrny) s Object (trgy; olyasmi, amit fel lehet kapni, el lehet dobni, hasznlni
lehet stb.) osztlyok fellett.
12. Szrmaztatott osztlyok 429
13. (*1.5) Mirt van a 12.7[2]-ben Point s Dot osztly is? Milyen krlmnyek k-
ztt lenne j tlet a Shape osztlyokat a kulcsosztlyok, pldul a Line konkrt
vltozataival bvteni?
14. (*3) Vzoljuk az Ival_box plda (12.4) egy eltr megvalstsi mdjt: minden,
a program ltal elrhet osztly egyszeren egy mutatt tartalmazzon a megval-
st osztlyra. Ilyen mdon minden felletosztly egy megvalst osztly le-
rja (handle) lesz, s kt hierarchival fogunk rendelkezni: egy fellet- s egy
megvalstsi hierarchival. rjunk olyan rszkdokat, amelyek elg rszletesek
ahhoz, hogy bemutassk a tpuskonverzikbl add lehetsges problmkat.
Gondoljuk t a kvetkez szempontokat: a hasznlat knnysge; a programo-
zs knnysge; mennyire knny a megvalst osztlyok s a felletek jra-
hasznostsa, ha j fogalmat vezetnk be a hierarchiba; mennyire knny
vltoztatsokat eszkzlni a felletekben vagy a megvalstsban; s szksg
van-e jrafordtsra, ha vltozott a rendszerfgg elemek megvalstsa.
Absztrakcis mdszerek 430
Sablonok
Az Olvas idzetnek helye.
(B. Stroustrup)
Sablonok Egy karakterlnc sablon Pldnyosts Sablonparamterek Tpusellenr-
zs Fggvnysablonok Sablonparamterek levezetse Sablonparamterek meghat-
rozsa Fggvnysablonok tlterhelse Eljrsmd megadsa sablonparamterek-
kel Alaprtelmezett sablonparamterek Specializci rklds s sablonok Tag
sablonok Konverzik A forrskd szerkezete Tancsok Gyakorlatok
13.1. Bevezets
Fggetlen fogalmakat fggetlenl brzoljunk s csak szksg esetn hasznljunk egytt.
Ha megsrtjk ezt az elvet, akkor vagy nem rokon fogalmakat kapcsolunk ssze vagy szk-
sgtelen fggseket teremtnk, gy kevsb rugalmas rszekbl vagy sszetevkbl kell
majd a programokat sszelltanunk. A sablonok (template) egyszer mdot adnak arra,
hogy ltalnos fogalmak szles krt brzoljuk s egyszer mdon hasznljuk egytt.
Az gy ltrejv osztlyok futsi id s trigny tekintetben felveszik a versenyt a kzzel
rott s egyedibb feladatot vgz kddal.
13
A sablonok kzvetlenl tmogatjk az ltalnostott (generikus) programozst (2.7.), azaz
a tpusoknak paramterknt val hasznlatt. A C++ sablonjai lehetv teszik, hogy egy
osztly vagy fggvny definilsakor egy tpust paramterknt adjunk meg. A sablon a fel-
hasznlt tpusnak csak azon tulajdonsgaitl fgg, amelyeket tnylegesen ki is hasznl.
A sablon ltal felhasznlt paramtertpusok nem kell, hogy rokonsgban lljanak egyms-
sal, gy nem szksges az sem, hogy egyazon rkldsi hierarchia tagjai legyenek.
Ebben a fejezetben a sablonokat gy mutatjuk be, hogy az elsdleges hangsly a standard
knyvtr tervezshez, megvalstshoz s hasznlathoz szksges mdszerekre esik.
A standard knyvtr nagyobb mrtk ltalnossgot, rugalmassgot s hatkonysgot k-
vetel, mint a legtbb program. Kvetkezskppen a trgyaland eljrsok szles krben
hasznlhatak s igen sokfle problma megoldshoz biztostanak hatkony segtsget.
Lehetv teszik, hogy egyszer felletek mg kifinomult megvalstsokat rejtsnk s
csak akkor mutassuk be a bonyolult rszleteket a felhasznlnak, ha valban szksge
van rjuk. A sort(v) pldul sokfle trol objektum tartalmazta sokfle tpus elemnek sok-
fle rendez algoritmushoz adhat felletet. Egy adott v-hez a fordtprogram automatiku-
san vlasztja ki a legalkalmasabb rendez fggvnyt.
A standard knyvtr minden fbb fogalmat egy-egy sablonknt brzol (pldul string,
ostream, complex, list s map), de a legfbb mveleteket is, pldul a karakterlncok
(string-ek) sszehasonltst, a << kimeneti mveletet, a komplex szmok (complex) ssze-
adst, egy lista (list) kvetkez elemnek vtelt, vagy a rendezst (sort()). Ezrt aztn e
knyvnek az emltett knyvtrral foglalkoz fejezetei (a III. rsz) gazdag forrsai a sablo-
nokra s az azokra pt programozsi mdszerekre vonatkoz pldknak. Kvetkezs-
kppen ez a fejezet a sablonok fogalmt jrja krl s csupn a hasznlatuk alapvet md-
jait bemutat kisebb pldkra sszpontost:
13.2 Az osztlysablonok ltrehozsra s hasznlatra szolgl alapvet eljrsok
13.3 Fggvnysablonok, fggvnyek tlterhelse, tpusok levezetse
13.4 ltalnostott algoritmusok eljrsmdjnak megadsa sablonparamterekkel
13.5 Sablon tbbfle megvalstsa klnbz definicikkal
13.6 rklds s sablonok (futsi s fordtsi idej tbbalaksg)
13.7 A forrskd szerkezete
A sablon (template) fogalmt a 2.7.1 s a 3.8 pont vezette be. A sablonnevek feloldsra,
illetve a sablonok formai kvetelmnyeire vonatkoz rszletes szablyok a C.13 pontban
vannak.
Absztrakcis mdszerek 432
13.2. Egy egyszer karakterlnc sablon
Vegynk egy karakterlncot. A string (karakterlnc) olyan osztly, amely karaktereket trol
s olyan indexelsi, sszefzsi s sszehasonltsi mveleteket nyjt, amelyeket rendesen
a karakterlnc fogalmhoz ktnk. Ezeket klnfle karakterkszletek szmra szeret-
nnk biztostani. Pldul az eljeles s eljel nlkli, knai vagy grg stb. karakterekbl l-
l lncok szmos sszefggsben hasznosak lehetnek. Ezrt gy szeretnnk a karakterlnc
fogalmt brzolni, hogy minl kevsb fggjnk egy adott karakterkszlettl. A karakter-
lnc definicija arra pt, hogy egy karaktert le lehet msolni, ezen kvl nem sok egybre.
Ezrt ha a 11.2-beli char-okbl felpl string osztlyban a karakterek tpust paramter-
r tesszk, ltalnosabb karakterlnc-osztlyt kapunk:
template<class C> class String {
struct Srep;
Srep *rep;
public:
String();
String(const C*);
String(const String&);
C read(int i) const;
// ...
};
A template<class C> eltag azt jelenti, hogy egy sablon deklarcija kvetkezik s abban
a C tpusparamtert fogjuk hasznlni. Bevezetse utn a C-t ugyangy hasznlhatjuk, mint
brmely ms tpusnevet. A C hatkre a template<class C> eltaggal bevezetett deklarci
vgig terjed. Jegyezzk meg, hogy a template<class C> eltag azt jelenti, hogy C egy tpus-
nv; nem felttlenl kell osztlynvnek lennie. Az osztlysablon neve a <> jelpr kz rott
tpusnvvel egytt egy, a sablon ltal meghatrozott osztly nevt adja s ugyangy hasz-
nlhat, mint brmely ms osztlynv:
String<char> cs;
String<unsigned char> us;
String<wchar_t> ws;
class Jchar {
// japn karakter
};
String<Jchar> js;
13. Sablonok 433
A nvre vonatkoz sajtos formai kvetelmnyektl eltekintve a String<char> pontosan
ugyangy mkdik, mintha a 11.12-beli String-defincival definiltuk volna. A String sab-
lonn ttele lehetv teszi, hogy a char-okbl ll karakterlncok szolgltatsait ms tpu-
s karakterekbl ll String-ek szmra is elrhetv tegyk. Pldul ha a standard knyv-
trbeli map s String sablonokat hasznljuk, a 11.8 pont szszmll pldja gy rhat t:
int main() // szavak elfordulsnak megszmllsa a bemeneten
{
String<char> buf;
map<String<char>,int> m;
while (cin>>buf) m[buf]++;
// eredmny kirsa
}
A Jchar japn karaktereket hasznl vltozat ez lenne:
int main() // szavak elfordulsnak megszmllsa a bemeneten
{
String<Jchar> buf;
map<String<Jchar>,int> m;
while (cin>>buf) m[buf]++;
// eredmny kirsa
}
A standard knyvtrban szerepel a sablonn alaktott String-hez hasonl basic_string sab-
lon is (11.12, 20.3). A standard knyvtrban a string mint a basic_string<char> szinoni-
mja szerepel:
typedef basic_string<char> string;
Ez lehetv teszi, hogy a szszmll pldt gy rjuk t:
int main() // szavak elfordulsnak megszmllsa a bemeneten
{
string buf;
map<string,int> m;
while (cin>>buf) m[buf]++;
// eredmny kirsa
}
A typedef-ek ltalban is hasznosak a sablonokbl ltrehozott osztlyok hossz neveinek
lervidtsre. Radsul, ha nem rdekel bennnket egy tpus pontos definicija, akkor egy
typedef elrejti ellnk, hogy sablonbl ltrehozott tpusrl van sz.
Absztrakcis mdszerek 434
13.2.1. Sablonok meghatrozsa
A sablonbl ltrehozott osztlyok teljesen kznsges osztlyok, ezrt a sablonok haszn-
lata semmivel sem ignyel hosszabb futsi idt, mint egy egyenrtk kzzel rott oszt-
ly, de nem felttlenl jelenti a ltrehozott kd mennyisgnek cskkenst sem.
ltalban j tlet hibakeresssel ellenrizni egy osztlyt, pldul a String-et, mieltt sablont
ksztnk belle (String<C>). Ezltal szmos tervezsi hibt, a kdhibknak pedig a leg-
tbbjt egy adott osztly sszefggsben kezelhetnk. Ezt a fajta hibakeresst (debugg-
ing) a legtbb programoz jl ismeri, s a legtbben jobban boldogulnak egy konkrt pl-
dval, mint egy elvonttal. Ksbb aztn anlkl foglalkozhatunk a tpus ltalnostsbl
esetleg add problmkkal, hogy a hagyomnyosabb hibk elvonnk a figyelmnket. Ha-
sonlan, ha meg akarunk rteni egy sablont, akkor hasznos annak viselkedst elszr egy
konkrt tpus paramterrel (pldul a char-ral) elkpzelni, mieltt megprbljuk a visel-
kedst teljes ltalnossgban megrteni.
Egy sablon osztly (template class) tagjait ugyangy deklarljuk s definiljuk, mint a k-
znsges osztlyokt. Egy tagot nem szksges magban az osztlyban definilni; valahol
mshol is elg, ugyangy, mint egy nem sablon osztlytag esetben (C.13.7.). A sablon
osztlyok tagjai maguk is sablonok, paramtereik pedig ugyanazok, mint a sablon osztlyi.
Ha egy ilyen tagot az osztlyn kvl runk le, kifejezetten sablonknt kell megadnunk:
template<class C> struct String<C>::Srep {
C* s; // mutat az elemekre
int sz; // elemek szma
int n; // hivatkozsszmll
// ...
};
template<class C> C String<C>::read(int i) const { return rep->s[i]; }
template<class C> String<C>::String()
{
rep = new Srep(0,C());
}
A sablonparamterek mint a C inkbb paramterek, mint a sablonon kvl definilt t-
pusok, de ez nem rinti azt a mdot, ahogyan az azokat hasznl sablonkdot rjuk.
A String<C> hatkrn bell a <C>-vel val minsts felesleges, hiszen a sablon neve mr
tartalmazza azt, gy a konstruktor neve String<C>::String lesz. De ha jobban tetszik, meg is
adhatjuk a minstst:
13. Sablonok 435
template<class C> String<C>::String<C>()
{
rep = new Srep(0,C());
}
Egy programban egy tagfggvnyt csak egyetlen fggvny definilhat. Ugyangy a sablon osz-
tlyok tagfggvnyeit is csak egy fggvnysablon definilhatja. De amg a fggvnyeket csak
tlterhelni lehet (13.3.2), addig a specializcik (13.5) hasznlata lehetv teszi, hogy egy
sablonnak tbb vltozatt is elkszthessk.
Az osztlysablonok neve nem terhelhet tl, gy ha egy hatkrben mr megadtunk egy
osztlysablont, ott nem lehet ugyanolyan nven msik egyedet bevezetni (lsd mg 13.5):
template<class T> class String { /* ... */ };
class String { /* ... */ }; // hiba: kt meghatrozs
A sablonparamterknt hasznlt tpusnak biztostania kell a sablon ltal vrt felletet.
A String sablon paramtereknt hasznlt tpusnak pldul tmogatnia kell a szoksos m-
sol mveleteket (10.4.4.1, 20.2.1). Jegyezzk meg: az nem kvetelmny, hogy egy sab-
lon klnbz paramterei rkldsi viszonyban lljanak egymssal.
13.2.2. Sablonok pldnyostsa
Az eljrst, melynek sorn egy sablon osztlybl s egy sablonparamterbl egy osztly-
deklarci keletkezik, gyakran sablon-pldnyostsnak (template instantiation) hvjk
(C.13.7.). Ha fggvnyt hozunk ltre egy sablon fggvnybl s egy sablonparamterbl,
az a fggvny-pldnyosts. A sablon adott paramtertpus szmra megadott vltozatt
specializcinak (specialization) nevezzk.
ltalban az adott C++ fordt s nem a programoz dolga, hogy minden felhasznlt para-
mtertpus szmra ltrehozza a megfelel sablon fggvnyt (C.13.7):
String<char> cs;
void f()
{
String<Jchar> js;
cs = "Az adott nyelvi vltozat feladata, hogy kitallja, milyen kdot kell ltrehozni.";
}
Absztrakcis mdszerek 436
A fenti esetben a String<char> s a String<Jchar>, a megfelel Srep tpusok, a destruktorok
s az alaprtelmezett konstruktorok, illetve a String<char>::operator=(char *) deklarciit
a fordt hozza ltre. Ms tagfggvnyeket nem hasznlunk, gy ilyeneket nem kell ksz-
tenie (remlhetleg nem is teszi). A ltrehozott osztlyok kznsges osztlyok, gy az osz-
tlyokra vonatkoz szoksos szablyok rvnyesek rjuk. Ugyangy a ltrehozott fggv-
nyek is kznsges fggvnyek s a fggvnyekre vonatkoz szoksos szablyok szerint
viselkednek.
Nyilvnval, hogy a sablonok hatkony eszkzt adnak arra, hogy viszonylag rvid defin-
cikbl hozzunk ltre kdot. Ezrt aztn nem rt nmi vatossg, hogy elkerljk a mem-
rinak csaknem azonos fggvny-definicikkal val elrasztst (13.5).
13.2.3. Sablonparamterek
A sablonoknak lehetnek tpust meghatroz, kznsges tpus (pl. int), s sablon tpus
paramtereik (C.13.3). Termszetesen egy sablonnak tbb paramtere is lehet:
template<class T, T def_val> class Cont { /* ... */ };
Ahogy a plda mutatja, egy sablonparamtert felhasznlhatunk a tovbbi sablonparamte-
rek meghatrozsban is.
Az egsz tpus paramterek mretek s korltok megadsnl hasznosak:
template<class T, int i> class Buffer {
T v[i];
int sz;
public:
Buffer() : sz(i) {}
// ...
};
Buffer<char,127> cbuf;
Buffer<Record,8> rbuf;
A Buffer-hez hasonl egyszer s korltozott trolk ott lehetnek fontosak, ahol a futsi
idej hatkonysg s a program tmrsge elsdleges szempont, s ahol ezrt nem lehet
az ltalnosabb string-et vagy vector-t hasznlni. Mivel a sablon a mretet paramterknt
megkapja, a kifejtsben el lehet kerlni a szabad tr hasznlatt. Egy msik plda erre
a Range osztly a 25.6.1-ben.
13. Sablonok 437
A sablon paramtere lehet konstans kifejezs (C.5), kls szerkeszts objektum vagy
fggvny cme (9.2), illetve egy tagra hivatkoz, tl nem terhelt mutat (15.5). A mutat,
ha sablon paramtereknt akarjuk hasznlni, &of alak kell, hogy legyen, ahol of egy ob-
jektum vagy fggvny neve, illetve f alak, ahol f egy fggvny neve. A tagra hivatkoz mu-
tatkat &X::of alakban kell megadni, ahol of a tag neve. Karakterlnc literlt nem hasznl-
hatunk sablonparamterknt.
Az egsz tpus paramtereknek konstansnak kell lennik:
void f(int i)
{
Buffer<int,i> bx; // hiba: konstans kifejezs szksges
}
Megfordtva, a nem tpusba tartoz paramterek a sablonon bell llandk, gy a param-
ter rtknek mdostsra tett ksrlet hibnak szmt.
13.2.4. Tpusok egyenrtksge
Ha adott egy sablon, akkor klnfle paramtertpusok megadsval klnfle tpusokat
hozhatunk ltre belle:
String<char> s1;
String<unsigned char> s2;
String<int> s3;
typedef unsigned char Uchar;
String<Uchar> s4;
String<char> s5;
Buffer<String<char>,10> b1;
Buffer<char,10> b2;
Buffer<char,20-10> b3;
Ha azonos paramterekkel adunk meg sablonokat, azok ugyanarra a ltrehozott tpusra
fognak hivatkozni. De mit is jelent itt az, hogy azonos? Szoks szerint, a typedef-ek nem
vezetnek be j tpust, gy a String<Uchar> ugyanaz, mint a String<unsigned char>. Megfor-
dtva, mivel a char s az unsigned char klnbz tpusok (4.3), a String<char> s
a String<unsigned char> is klnbzek lesznek.
A fordtprogram ki tudja rtkelni a konstans kifejezseket is (C.5), gy a Buffer<char,20-
10>-rl felismeri, hogy a Buffer<char,10>-zel azonos tpus.
Absztrakcis mdszerek 438
13.2.5. Tpusellenrzs
A sablonokat paramterekkel definiljuk s ksbb gy is hasznljuk. A sablondefinciban
a fordtprogram ellenrzi a formai hibkat, illetve az olyanokat, amelyek a konkrt para-
mterek ismerete nlkl felderthetek:
template<class T> class List {
struct Link {
Link* pre;
Link* suc;
T val;
Link(Link* p, Link* s,const T& v) : pre(p), suc(s), val(v) { }
} // szintaktikus hiba: hinyzik a pontosvessz
Link* head;
public:
List() : head(7) { } // hiba: kezdeti rtkads mutatnak int-tel
List(const T& t) : head(new Link(0,o,t)) { } // hiba: 'o' nem definilt azonost
// ...
void print_all() const { for (Link* p = head; p; p=p->suc) cout << p->val << '\n'; }
};
A fordtprogram az egyszer nyelvi hibkat mr a defincinl kiszrheti, nha azonban
csak ksbb, a hasznlatnl. A felhasznlk jobban szeretik, ha a hibk hamar kiderlnek,
de nem minden egyszer hibt knny felderteni. Ebben a pldban hrom hibt vtet-
tem (szndkosan). A sablon paramtertl fggetlenl egy T* tpus mutatnak nem ad-
hatjuk a 7 kezdrtket. Hasonlan, az o vltoz (amely persze egy hibsan rt nulla) nem
lehet a List<T>::Link konstruktor paramtere, mert ilyen nv az adott pontrl nem elrhet.
A sablon definicijban hasznlt nvnek vagy ismertnek kell lennie, vagy valamilyen ssze-
r s nyilvnval mdon kell fggnie valamelyik sablonparamtertl (C.13.8.1). A T sab-
lonparamtertl val legkznsgesebb s legkzenfekvbb fggs egy T tpus tag vagy
T tpus paramter hasznlata. A List<T>::print_all() pldban a cout << p->val kifejezs
hasznlata nmileg kifinomultabb plda.
A sablonparamterek hasznlatval sszefgg hibk csak a sablon hasznlatnak helyn
derthetk fel:
class Rec { /* ... */ };
void f(const List<int>& li, const List<Rec>& lr)
{
li.print_all();
lr.print_all();
}
13. Sablonok 439
Itt a li.print_all() rendben van, de a lr.print_all() tpushibs, mert a Rec tpusnak nincs <<
kimeneti mvelete. A legels pont, ahol a sablonparamterek hasznlatval sszefgg hi-
ba kiderlhet, a sablonnak az adott paramterrel val els hasznlata. Ezt a pontot rendsze-
rint els pldnyostsi pontnak (first point of instantiation) vagy egyszeren pldnyostsi
pontnak hvjk (C.13.7). Az adott C++-vltozat megengedett mdon ezt az ellenrzst
a program sszeszerkesztsig elhalaszthatja. Ha ebben a fordtsi egysgben a print_all()-
nak csak a deklarcija s nem a defincija ismert, lehetsges, hogy az adott fordtnak el
is kell halasztania a tpusellenrzst a program sszeszerkesztsig (13.7). A tpusellenr-
zs azonos szablyok szerint trtnik, fggetlenl attl, hogy melyik ponton megy vgbe.
A felhasznlk itt is a minl korbbi ellenrzst szeretik. A sablonparamterekre vonatko-
z megszortsokat a tagfggvnyek segtsgvel is kifejezhetjk (13.9[16]).
13.3. Fggvnysablonok
A legtbb programoz szmra a sablonok els szm s legnyilvnvalbb felhasznlsa
olyasfle trol osztlyok ltrehozsa s hasznlata, mint a basic_string (20.3), a vector
(16.3), a list (17.2.2) vagy a map (17.4.1). Ksbb azonban felmerl a sablonknt hasz-
nlt fggvnyek szksgessge. Nzzk pldul egy tmb rendezst:
template<class T> void sort(vector<T>&); // deklarci
void f(vector<int>& vi, vector<string>& vs)
{
sort(vi); // sort(vector<int>&);
sort(vs); // sort(vector<string>&);
}
A sablon fggvnyek (template function) meghvsakor a fggvny paramterei hatrozzk
meg, hogy a sablon melyik pldnyt hasznljuk, vagyis a sablonparamtereket a fgg-
vnyparamterekbl vezetjk le (deduce) (13.3.1).
Termszetesen a sablon fggvnyt valahol definilnunk kell (C.13.7):
template<class T> void sort(vector<T>& v) // definci
// Shell rendezs (Knuth, III. ktet, 84. o.
2
)
{
const size_t n = v.size();
Absztrakcis mdszerek 440
2
Magyarul: D. E. Knuth: A szmtgp-programozs mvszete III. ktet, Keress s rendezs; Mszaki
knyvkiad, Budapest, 1988; 95. oldal
for (int gap=n/2; 0<gap; gap/=2)
for (int i=gap; i<n; i++)
for (int j=i-gap; 0<=j; j-=gap)
if (v[j+gap]<v[j]) { // v[j] s v[j+gap] felcserlse
T temp = v[j];
v[j] = v[j+gap];
v[j+gap] = temp;
}
}
Hasonltsuk ssze a sort() ezen definicijt a 7.7-belivel. Ez a sablonn alaktott vltozat vi-
lgosabb s rvidebb, mert a rendezend elemek tpusra vonatkozan tbb informcira
tmaszkodhat. Valsznleg gyorsabb is, mert nincs szksge az sszehasonlt fggvny-
re hivatkoz mutatra. Ebbl kvetkezik, hogy nincs szksg kzvetett fggvnyhvsra,
a < sszehasonltst pedig knnyen lehet helyben kifejtve (inline) fordtani.
Tovbbi egyszerstst jelenthet a standard knyvtrbeli swap() sablon hasznlata (18.6.8),
mellyel az rtkcsert termszetes formra alakthatjuk:
if (v[j+gap]<v[j]) swap(v[j],v[j+gap]);
Ez a kd hatkonysgt semmilyen mdon nem rontja. Ebben a pldban a < mveletet
hasznltuk sszehasonltsra. Nem minden tpusnak van azonban < opertora, ami korl-
tozza a sort() ezen vltozatnak hasznlhatsgt; de ez a korltozs knnyen megkerlhe-
t (13.4).
13.3.1. A fggvnysablonok paramterei
A fggvnysablonok alapvet fontossgak a trol tpusok (2.7.2, 3.8, 18. fejezet) szles
krre alkalmazhat ltalnos algoritmusok rshoz. Alapvet jelentsg, hogy egy fgg-
vnyhvskor a sablonparamtereket le lehet vezetni, ki lehet kvetkeztetni (deduce)
a fggvny paramtereibl.
A fordtprogram akkor tudja levezetni egy hvs tpusos s nem tpusba tartoz paramte-
reit, ha a fggvny paramterlistja egyrtelmen azonostja a sablonparamterek halmazt
(C.13.4):
template<class T, int i> T& lookup(Buffer<T,i>& b, const char* p);
class Record {
const char[12];
// ...
};
13. Sablonok 441
Record& f(Buffer<Record,128>& buf, const char* p)
{
return lookup(buf,p); // lookup() hasznlata, ahol T egy Record s i rtke 128
}
Itt T-rl azt llaptja meg a fordtprogram, hogy Record, az i-rl pedig azt, hogy rtke128.
Megjegyzend, hogy a fordtprogram az osztlysablonok paramtereit soha nem vezeti le
(C.13.4). Ennek az az oka, hogy az osztlyok tbbfle konstruktora nyjtotta rugalmassg
ezt sok esetben megakadlyozn, esetleg ttekinthetetlenn tenn. Egy osztly klnfle
vltozatai kztti vlasztsra a specializlt vltozatok hasznlata ad eszkzt (13.5). Ha egy
levezett tpus objektumot kell ltrehoznunk, ezt sokszor megtehetjk gy, hogy a ltreho-
zst egy fggvny meghvsval hajtatjuk vgre (lsd a 17.4.1.2 pontbeli make_pair()-t).
Ha egy paramtert nem lehet levezetni a sablon fggvny paramtereibl (C.13.4), akkor
kzvetlenl meg kell adnunk. Ezt ugyangy tehetjk meg, mint ahogy egy sablon osztly
szmra kzvetlenl megadjuk a sablonparamtereket:
template<class T> class vector { /* ... */ };
template<class T> T* create(); // T ltrehozsa s r hivatkoz mutat visszaadsa
void f()
{
vector<int> v; // osztly, sablonparamtere 'int'
int* p = create<int>(); // fggvny, sablonparamtere 'int'
}
A kzvetlen meghatrozs (explicit specification) egyik szoksos hasznlata a sablon fgg-
vny visszatrsirtk-tpusnak megadsa:
template<class T, class U> T implicit_cast(U u) { return u; }
void g(int i)
{
implicit_cast(i); // hiba: T nem vezethet le
implicit_cast<double>(i); // T tpusa double, U tpusa int
implicit_cast<char,double>(i); // T tpusa char, U tpusa double
implicit_cast<char*,int>(i); // T tpusa char*, U tpusa int; hiba: int
// nem alakthat char*-ra
}
Az alaprtelmezett fggvnyparamter-rtkekhez hasonlan (7.5), az explicit megadott
sablonparamterek kzl is csak az utolskat lehet elhagyni.
Absztrakcis mdszerek 442
A sablonparamterek kzvetlen megadsa talakt fggvnycsaldok s objektum-ltre-
hoz fggvnyek definicijt teszi lehetv (13.3.2, C.13.1, C.13.5). Az automatikus
(implicit) konverzik (C.6) explicit vltozatai, pldul az implicit_cast() idnknt igen
hasznosak lehetnek. A dynamic_cast, static_cast stb. formai kvetelmnyei megfelelnek az
explicit minsts sablon fggvnyekinek. A beptett tpuskonverzis opertorok azon-
ban olyan mveleteket tmogatnak, amelyeket nem fejezhetnk ki ms nyelvi elemmel.
13.3.2. Fggvnysablonok tlterhelse
Azonos nven tbb fggvnysablon is szerepelhet, st ugyanolyan nven tbb kznsges
fggvny is. A tlterhelt (vagyis azonos nvvel mst s mst jelent) fggvnyek meghv-
sakor a megfelel meghvand fggvny vagy fggvnysablon kivlasztshoz a tlterhe-
ls (overloading) feloldsa szksges:
template<class T> T sqrt(T);
template<class T> complex<T> sqrt(complex<T>);
double sqrt(double);
void f(complex<double> z)
{
sqrt(2); // sqrt<int>(int)
sqrt(2.0); // sqrt(double)
sqrt(z); // sqrt<double>(complex<double>)
}
Ahogy a sablon fggvny fogalma a fggvny fogalmnak ltalnostsa, ugyangy a sab-
lon fggvnyekre alkalmazand tlterhels-feloldsi szablyok is a fggvnyekre alkalma-
zand tlterhels-feloldsi szablyok ltalnostsai. A mdszer alapveten a kvetkez:
megkeressk minden sablonhoz azt a specializlt vltozatot, amelyik a paramtereknek
a legjobban megfelel. Ezutn ezekre a pldnyokra s az sszes kznsges fggvnyre is
a szoksos tlterhels-feloldsi szablyokat alkalmazzuk:
1. Meg kell keresni azokat a specializlt sablon fggvny vltozatokat (13.2.2),
amelyek rszt fognak venni a tlterhels feloldsban. Ehhez az sszes fgg-
vnysablont megvizsgljuk, hogy ha ms ugyanilyen nev fggvny vagy sab-
lon fggvny nem lenne elrhet, akkor lehetne-e valamilyen sablonparamter-
rel alkalmazni. Az sqrt(z) hvs esetben pldul a kvetkez jelltek addnak:
sqrt<double>(complex<double>) s sqrt<complex<double>>(complex<double>).
2. Ha kt sablon fggvny is meghvhat lenne s az egyik specializltabb a m-
siknl (13.5.1), akkor a kvetkez lpsekben csak azt vesszk figyelembe.
13. Sablonok 443
Az sqrt(z) hvs esetben az sqrt<double>(complex<double>)-t vlasztjuk az
sqrt<complex<double>>(complex<double>) helyett: minden hvs, ami megfelel
sqrt<T>(complex<T>)-nek, megfelel sqrt<T>(T)-nek is.
3. Ezek utn vgezzk el a kznsges tlterhels-feloldst ezen fggvnyekre s
a kznsges fggvnyekre (7.4.). Ha egy sablon fggvny paramtert a sab-
lonparamterekbl vezettk le (13.3.1), akkor arra nem alkalmazhatunk kiter-
jesztst (promotion), illetve szabvnyos vagy felhasznli konverzit. Az sqrt(2)
hvs pontosan megfelel az sqrt<int>(int)-nek, gy azt vlasztjuk a sqrt(double)
helyett.
4. Ha egy fggvny s egy specializlt vltozata ugyanolyan mrtkben megfelel,
akkor a fggvnyt vlasztjuk. Emiatt a sqrt(2.0)-hoz a sqrt(double)-t vlasztjuk,
s nem a sqrt<double>(double)-t.
5. Ha nem tallunk megfelel fggvnyt, akkor a hvs hibs. Ha tbb ugyanolyan
mrtkben megfelel fggvnyt is tallunk, akkor a hvs tbbrtelm s ezrt
hibs:
template<class T> T max(T,T);
const int s = 7;
void k()
{
max(1,2); // max<int>(1,2)
max('a','b'); // max<char>('a','b')
max(2.7,4.9); // max<double>(2.7,4.9)
max(s,7); // max<int>(int(s),7) (egyszer konverzi)
max('a',1); // hiba: tbbrtelm (nincs szabvnyos konverzi)
max(2.7,4); // hiba: tbbrtelm (nincs szabvnyos konverzi)
}
A fenti plda kt nem egyrtelm hvst explicit minstssel oldhatjuk fel:
void f()
{
max<int>('a',1); // max<int>(int('a'),1)
max<double>(2.7,4); // max<double>(2.7,double(4))
}
Vagy megfelel deklarcik alkalmazsval:
inline int max(int i, int j) { return max<int>(i,j); }
inline double max(int i, double d) { return max<double>(i,d); }
inline double max(double d, int i) { return max<double>(d,i); }
inline double max(double d1, double d2) { return max<double>(d1,d2); }
Absztrakcis mdszerek 444
void g()
{
max('a',1); // max(int('a'),1)
max(2.7,4); // max(2.7,double(4))
}
Kznsges fggvnyekre a kznsges tlterhels-feloldsi szablyok rvnyesek (7.4),
s a helyben kifejts (inline) biztostja, hogy a hvs nem jr kln kltsggel.
A max() fggvny igen egyszer, gy explicit mdon is rhattuk volna, de a sablon
specializlt hasznlata knny s ltalnosan hasznlhat mdja az ilyen tlterhels-felol-
d fggvnyek rsnak. A tlterhels-feloldsi szablyok biztostjk, hogy a sablon fgg-
vnyek helyesen mkdnek egytt az rkldssel:
template<class T> class B { /* ... */ };
template<class T> class D : public B<T> { /* ... */ };
template<class T> void f(B<T>*);
void g(B<int>* pb, D<int>* pd)
{
f(pb); // f<int>(pb)
f(pd); // f<int>(static_cast<B<int>*>(pd)); szabvnyos talakts D<int>*-rl B<int>*-ra
}
Ebben a pldban az f() sablon fggvny minden T tpusra elfogadja B<T>*-ot. Egy
D<int>* tpus paramternk van, gy a fordtprogram knnyen jut arra a kvetkeztets-
re, hogy T-t int-nek vve a hvs egyrtelmen feloldhat, f(B<int>*)-knt. Az olyan
fggvnyparamtereket, amelyek nem vesznek rszt a sablonparamter levezetsben,
pontosan gy kezelhetjk, mint egy nem sablon fggvny paramtert, gy a szoksos t-
konverzik megengedettek:
template<class T, class C> T get_nth(C& p, int n); // az n-edik elem
Ez a fggvny felttelezheten a C tpus trol n-edik elemt adja vissza. Minthogy C-t
a hvs aktulis paramterbl kell levezetni, az els paramterre nem alkalmazhat
konverzi, a msodik paramter azonban teljesen kznsges, gy a szoksos konverzik
mindegyike tekintetbe vehet:
class Index {
public:
operator int();
// ...
};
13. Sablonok 445
void f(vector<int>& v, short s, Index i)
{
int i1 = get_nth<int>(v,2); // pontos illeszkeds
int i2 = get_nth<int>(v,s); // szabvnyos konverzi: short-rl int-re
int i3 = get_nth<int>(v,i); // felhasznli konverzi: Index-rl int-re
}
13.4. Eljrsmd megadsa sablonparamterekkel
Gondoljuk meg, hogyan rendezhetjk a karakterlncokat. Hrom dolog jtszik szerepet: a ka-
rakterlnc, az elemek tpusa s a lnc elemeinek sszehasonltsakor alkalmazott szempont.
Nem betonozhatjuk be a rendezsi elvet a trolba, mert az ltalban nem szabhatja meg,
mire van szksge az elemek tpusval kapcsolatban, de az elemek tpusba sem, mert az
elemeket sokfle mdon rendezhetjk. Ehelyett a megfelel mvelet vgrehajtsakor kell
megadni az alkalmazand feltteleket. Milyen rendezsi elvet alkalmazzunk, ha pldul
svd neveket tartalmaz karakterlncokat akarunk rendezni? A svd nevek rendezse sz-
mra a karakterek kt klnbz numerikus megfeleltetsi mdja (collating sequence)
hasznlatos. Termszetesen sem egy ltalnos string tpus, sem egy ltalnos rendez algo-
ritmus nem tudhat a nevek rendezsnek svd szoksairl, ezrt brmely ltalnos meg-
olds megkveteli, hogy a rendez eljrst ne csak egy adott tpusra adhassuk meg, hanem
adott tpusra val adott alkalmazskor is. ltalnostsuk pldul a C standard knyvtrnak
strcmp() fggvnyt tetszleges T tpusbl ll String-ekre (13.2):
template<class T, class C>
int compare(const String<T>& str1, const String<T>& str2)
{
for(int i=0; i<str1.length() && i< str2.length(); i++)
if (!C::eq(str1[i],str2[i])) return C::lt(str1[i],str2[i]) ? -1 : 1;
return str1.length()-str2.length();
}
Ha valaki azt szeretn, hogy a compare() eltekintsen a kis- s nagybetk kztti klnb-
sgtl vagy figyelembe vegye a program nyelvi krnyezett (locale, helyi sajtossgok), ak-
kor ezt a C::eq() s a C::lt() fggvnyek megfelel definilsval teheti meg. Ezzel minden
(sszehasonlt, rendez stb.) eljrst lerhatunk, ha az a trol s a C-mveletek nyelvn
megfogalmazhat:
Absztrakcis mdszerek 446
template<class T> class Cmp { // szoksos, alaprtelmezett sszehasonlts
public:
static int eq(T a, T b) { return a==b; }
static int lt(T a, T b) { return a<b; }
};
class Literate { // svd nevek sszehasonltsa
public:
static int eq(char a, char b) { return a==b; }
static int lt(char,char); // kikeress tblzatbl karakterrtk alapjn (13.9[14])
};
A sablonparamterek megadsakor most mr pontosan megadhatjuk az sszehasonltsi
szablyokat:
void f(String<char> swede1, String<char> swede2)
{
compare< char,Cmp<char> >(swede1,swede2);
compare< char,Literate >(swede1,swede2);
}
Az sszehasonlt mveletek sablonparamterknt val megadsnak kt jelents elnye
van az egyb lehetsgekhez, pldul a fggvnymutatk alkalmazshoz kpest. Egyrszt
tbb mvelet megadhat egyetlen paramterknt, a futsi id nvekedse nlkl. Msrszt,
az eq() s az lt() sszehasonlt mveleteket knny helyben kifejtve (inline) fordtani, mg
egy fggvnymutatn keresztli hvs ilyen md fordtsa klnleges mrtk figyelmet
kvetel a fordtprogramtl.
Termszetesen sszehasonlt mveleteket nemcsak a beptett, hanem a felhasznli tpu-
sokra is megadhatunk. Ez alapvet fontossg felttele annak, hogy ltalnos algoritmuso-
kat olyan tpusokra alkalmazhassunk, amelyeknek nem maguktl rtetd sszehasonltsi
feltteleik vannak (18.4).
Minden osztlysablonbl ltrehozott osztly sajt pldnyokat kap a sablon statikus vlto-
zibl (C.13.1).
13.4.1. Alaprtelmezett sablonparamterek
Fradsgos dolog minden egyes hvsnl kzvetlenl meghatrozni az sszehasonltsi fel-
tteleket. Tlterhelssel szerencsre knnyen megadhatunk olyan alaprtelmezst, hogy
csak a szoksostl eltr sszehasonltsi szempontot kelljen megadni:
13. Sablonok 447
template<class T, class C>
int compare(const String<T>& str1, const String<T>& str2); // sszehasonlts C
// hasznlatval
template<class T>
int compare(const String<T>& str1, const String<T>& str2); // sszehasonlts
// Cmp<T> hasznlatval
De a szoksos rendezst megadhatjuk, mint alaprtelmezett sablonparamter-rtket is:
template<class T, class C = Cmp<T> >
int compare(const String<T>& str1, const String<T>& str2)
{
for(int i=0; i<str1.length() && i< str2.length(); i++)
if (!C::eq(str1[i],str2[i])) return C::lt(str1[i],str2[i]) ? -1 : 1;
return str1.length()-str2.length();
}
gy mr lerhatjuk a kvetkezt:
void f(String<char> swede1, String<char> swede2)
{
compare(swede1,swede2); // Cmp<char> hasznlata
compare<char,Literate>(swede1,swede2); // Literate hasznlata
}
Egy (nem svdek szmra) kevsb ezoterikus plda a kis- s nagybetk kztti klnbs-
get figyelembe vev, illetve elhanyagol rendezs:
class No_case { /* ... */ };
void f(String<char> s1, String<char> s2)
{
compare(s1,s2); // kisbet-nagybet klnbzik
compare<char,No_case>(s1,s2); // kisbet-nagybet nem klnbzik
}
A standard knyvtr szles krben alkalmazza azt a mdszert, hogy az alkalmazand elj-
rsmdot (policy) egy sablonparamter adja meg, s ennek a legltalnosabb eljrsmd az
alaprtelmezett rtke (pldul 18.4). Elgg furcsa mdon azonban a basic_string (13.2,
20. fejezet) sszehasonltsaira ez nem ll. Az eljrsmdot kifejez sablonparamtereket
gyakran nevezik jellemvonsoknak (traits) is. Pldul a standard knyvtrbeli string
a char_traits-re pl (20.2.1), a szabvnyos algoritmusok a bejrk (itertorok) jellemvo-
nsait (19.2.2), a standard knyvtrbeli trolk pedig a memriafoglalkt (alloktor,
19.4.) hasznljk fel.
Absztrakcis mdszerek 448
Egy alaprtelmezett sablonparamter rtelmi ellenrzse ott s csak akkor trtnik meg,
ahol s amikor az alaprtelmezett paramtert tnylegesen felhasznljuk. gy ha nem hasz-
nljuk fel az alaprtelmezett Cmp<T> paramtert, akkor olyan X tpusokra is hasznlhatjuk
a compare()-t, amelyekre a fordt nem fordtan le aCmp<X>-et, mert mondjuk az X-re a <
nem rtelmezett. Ez dnt jelentsg a szabvnyos trolk tervezsnl, hiszen ezek sab-
lonparamtert hasznlnak az alaprtelmezett rtkek megadsra (16.3.4).
13.5. Specializci
Alaprtelmezs szerint egy sablon (template) egyetlen defincit ad a felhasznlhat ltal el-
kpzelhet sszes paramterrtk (vagy paramterrtkek) szmra. Ez azonban nem min-
den sablon rsakor kedvez. Elfordulhat, hogy olyasmit szeretnnk kifejezni, hogy ha
a sablonparamter egy mutat, hasznld ezt, ha nem, hasznld azt, vagy hogy hiba, ha
a sablonparamter nem a My_base osztly egy leszrmazottjra hivatkoz mutat. Sok ha-
sonl tervezsi szempontot figyelembe lehet gy venni, hogy a sablonnak tbbfle
defincit adunk s a fordtprogram az alkalmazott paramtertpusok szerint vlaszt kz-
lk. A sablon ilyenfle tbbszrs meghatrozst specializcinak (specialization, egyedi
cl felhasznli vltozatok hasznlata, szakosts) hvjuk.
Vegyk egy Vector sablon valszn felhasznlsait:
template<class T> class Vector { // ltalnos vektortpus
T* v;
int sz;
public:
Vector();
Vector(int);
T& elem(int i) { return v[i]; }
T& operator[ ](int i);
void swap(Vector&);
// ...
};
Vector<int> vi;
Vector<Shape*> vps;
Vector<string> vs;
Vector<char*> vpc;
Vector<Node*> vpn;
13. Sablonok 449
A legtbb Vector valamilyen mutattpus Vector-a lesz. Tbb okbl is, de fleg azrt, mert
a tbbalak (polymorph) viselkeds megrzse cljbl mutatkat kell hasznlnunk
(2.5.4, 12.2.6). Ezrt aki objektumorientlt programozst folytat s tpusbiztos, pldul
standard knyvtrbeli trolkat hasznl, az biztosan szmos mutatt tartalmaz trolt fog
hasznlni.
A legtbb C++-vltozat alaprtelmezs szerint lemsolja a sablon fggvnyek kdjt. Ez j
a vgrehajtsi sebessg szempontjbl, de kritikus esetekben (mint az imnti Vector-nl)
a kd felfvdsval jr.
Szerencsre ltezik egyszer megolds. A mutatkat tartalmaz trolknak elg egyetlen
megvalsts. Ezt specializlt vltozat ksztsvel rhetjk el. Elszr definiljuk a Vector-
nak a void mutatkra vonatkoz vltozatt (specializcijt):
template<> class Vector<void*> {
void** p;
// ...
void*& operator[ ](int i);
};
Ezt a vltozatot aztn az sszes, mutatt tartalmaz vektor kzs megvalstsaknt hasz-
nlhatjuk. A template<> eltag azt jelenti, hogy ennl a specializlt vltozatnl nem kell sab-
lonparamtert megadnunk. Azt, hogy milyen tpus sablonparamterre hasznljuk, a nv
utni <> jelpr kztt adjuk meg: vagyis a <void*> azt jelenti, hogy ezt a defincit kell min-
den olyan Vector esetben hasznlni, amelyikre a T tpusa void*.
A Vector<void*> egy teljes specializci, azaz ezen vltozat hasznlatakor nincs sablonpa-
ramter, amit meg kellene adni vagy le kellene vezetni; a Vector<void*>-ot a kvetkez m-
don deklarlt Vector-ok szmra hasznljuk:
Vector<void*> vpv;
Ha olyan vltozatot akarunk megadni, ami mutatkat tartalmaz Vector-ok, s csak azok
esetn hasznland, rszleges specializcira van szksgnk:
template<class T> class Vector<T*> : private Vector<void*> {
public:
typedef Vector<void*> Base;
Vector() : Base() {}
explicit Vector(int i) : Base(i) {}
Absztrakcis mdszerek 450
T*& elem(int i) { return reinterpret_cast<T*&>(Base::elem(i)); }
T*& operator[ ](int i) { return reinterpret_cast<T*&>(Base::operator[ ](i)); }
// ...
};
A nv utni <T*> specializl minta azt jelzi, hogy ezt a vltozatot kell minden mutattpus
esetben hasznlni; azaz minden olyan sablonparamternl, ami T* alakba rhat:
Vector<Shape*> vps; // <T*> most <Shape*>, gy T is Shape
Vector<int**> vppi; // <T*> most <int**>, gy T is int*
Jegyezzk meg, hogy rszleges specializci hasznlata esetn a sablonparamter
a specializcira hasznlt mintbl addik; a sablonparamter nem egyszeren az aktulis
sablonparamter. gy pldul a Vector<Shape*> esetben T tpusa Shape s nem Shape*.
Ha adott a Vector ezen rszlegesen specializlt vltozata, akkor ez az sszes mutattpusra
vonatkoz Vector kzs megvalstsa. A Vector<T*> osztly egyszeren egy fellet
a void*-os vltozathoz, melyet kizrlag az rklds s a helyben kifejts eszkzvel va-
lstottuk meg.
Fontos, hogy a Vector ezen finomtsa a felhasznli fellet megvltoztatsa nlkl trtnt.
A specializci a kzs fellet tbbfle meghatrozsnak eszkze. Termszetesen az lta-
lnos Vector-t s a mutatkra vonatkoz vltozatot hvhattuk volna klnbzkppen is.
Amikor ezt kiprbltam, kiderlt, hogy sok felhasznl, akinek tudnia kellett volna rla,
mgsem a mutats vltozatot hasznlta s a kapott kd a vrtnl sokkal nagyobb lett. Eb-
ben az esetben sokkal jobb a fontos rszleteket egy kzs fellet mg rejteni.
Ez a mdszer a gyakorlatban a kd felfvdsnak megakadlyozsban volt sikeres. Akik
nem alkalmaznak ilyen mdszereket (akr a C++-ban, akr egyb tpus-paramterezsi le-
hetsgeket tartalmaz nyelvekben), knnyen azt vehetik szre, hogy az ismtld kd k-
zepes mret programok esetben is megabjtokra rghat. A vektormveletek klnbz
vltozatainak lefordtshoz szksges id megtakartsval ez a mdszer a fordtsi s
szerkesztsi idt is drmai mdon cskkenti. Az sszes mutatt tartalmaz lista egyetlen
specializlt vltozattal val megvalstsa j plda arra, hogyan lehet a kdfelfvdst meg-
akadlyozni gy, hogy a kzs kdot a lehet legjobban nveljk.
Az ltalnos sablont az sszes specializlt vltozat eltt kell megadni:
template<class T> class List<T*> { /* ... */ };
template<class T> class List { /* ... */ }; // hiba: ltalnos sablon a specializlt utn
13. Sablonok 451
Az ltalnos sablon ltal adott ltfontossg informci az, hogy milyen paramtereket kell
a felhasznls vagy a specializci sorn megadni. Ezrt elg az ltalnos sablont a specia-
lizlt vltozat deklarcija vagy definicija eltt megadni:
template<class T> class List;
template<class T> class List<T*> { /* ... */ };
Ha hasznljuk is, akkor az ltalnos sablont valahol definilnunk kell (13.7).
Ha valahol szerepel egy felhasznli specializlt vltozat, akkor annak deklarcija
a specializlt hasznlat minden helyrl elrhet kell, hogy legyen:
template<class T> class List { /* ... */ };
List<int*> li;
template<class T> class List<T*> { /* ... */ }; // hiba
Itt a List-et az int*-ra a List<int*> hasznlata utn specializlunk.
Egy sablon minden specializlt vltozatt ugyanabban a nvtrben kell megadni, mint ma-
gt a sablont. Ha hasznljk, akkor egy explicit deklarlt (azaz nem egy ltalnosabbl lt-
rehozott) sablonnak valahol szintn explicit definiltnak kell lennie (13.7). Vagyis a specia-
lizlt vltozat explicit megadsbl kvetkezik, hogy szmra a fordt nem hoz ltre
defincit.
13.5.1. Specializcik sorrendje
Az egyik vltozat specializltabb egy msiknl, ha minden olyan paramterlista, amely il-
leszkedik az egyikre, illeszkedik a msikra is. Ez fordtva nem ll fenn:
template<class T> class Vector; // ltalnos
template<class T> class Vector<T*>; // mutatkhoz
template<> class Vector<void*>; // void*-okhoz
Minden tpust hasznlhatunk a legltalnosabb Vector paramtereknt, de a Vector<T*> pa-
ramtereknt csak mutatt, a Vector<void*> paramtereknt pedig csak void* mutatt.
Az objektumok s mutatk stb. (13.5) deklarcijban, illetve a tlterhels feloldsakor
(13.3.2) a leginkbb specializlt vltozat rszesl elnyben.
Absztrakcis mdszerek 452
A specializl minta megadsnl a sablonparamterek levezetsnl (13.3.1) hasznlt t-
pusokbl sszelltott tpusok hasznlhatk fel.
13.5.2. Fggvnysablon specializci
A specializci termszetesen a sablon fggvnyeknl (template function) is hasznos. Ve-
gyk a 7.7 s 13.3 pldabeli Shell rendezst. Ez az elemeket a < segtsgvel hasonltja
ssze s a rszletezett kd segtsgvel cserli fel. Jobb definci lenne a kvetkez:
template<class T> bool less(T a, T b) { return a<b; }
template<class T> void sort(Vector<T>& v)
{
const size_t n = v.size();
for (int gap=n/2; 0<gap; gap/=2)
for (int i=gap; i<n; i++)
for (int j=i-gap; 0<=j; j-=gap)
if (less(v[j+gap],v[j])) swap(v[j],v[j+gap]);
}
Ez nem javtja magt az algoritmust, de lehetsget nyjt a megvalsts javtsra. Eredeti
formjban egy Vector<char*>-ot nem rendez jl, mert kt char*-ot a < segtsgvel hason-
lt ssze, azaz az els karakter cmt fogja az sszehasonlts alapjul venni. Ehelyett a mu-
tatott karakterek szerinti sszehasonltst szeretnnk. Erre a less()-nek egy egyszer, const
char*-ra vonatkoz specializcija fog gyelni:
template<> bool less<const char*>(const char* a, const char* b)
{
return strcmp(a,b)<0;
}
Mint az osztlyoknl is (13.5), a <> sablon-eltag azt jelzi, hogy ez egy sablonparamter
megadsa nlkli specializlt vltozat. A sablon fggvny neve utni <const char*> azt je-
lenti, hogy a fggvnyt azokban az esetekben kell alkalmazni, amikor a sablonparamter
const char*. Minthogy a sablonparamter kzvetlenl levezethet a fggvny paramterlis-
tjbl, nem kell explicit megadnunk. gy egyszersthetnk a specializci megadsn:
template<> bool less<>(const char* a, const char* b)
{
return strcmp(a,b)<0;
}
13. Sablonok 453
Minthogy adott a template<> eltag, a msodik, res <> felesleges, hiszen nem hordoz j
informcit. gy aztn ltalban gy rnnk:
template<> bool less(const char* a, const char* b)
{
return strcmp(a,b)<0;
}
n jobban kedvelem ezt a rvidebb deklarcis formt.
Vegyk a swap() kzenfekv meghatrozst:
template<class T> void swap(T& x, T& y)
{
T t = x; // x msolsa az ideiglenes vltozba
x = y; // y msolsa x-be
y = t; // ideiglenes vltoz msolsa y-ba
}
Ez nem tl hatkony, ha Vector-ok vektoraira hvjuk meg; az sszes elem msolsval cse-
rli fel a Vector-okat. De ezt is megoldhatjuk megfelel specializcival. Maga a Vector ob-
jektum csak annyi adatot tartalmaz, hogy az elemeihez val kzvetett hozzfrst tmogas-
sa (mint a string 11.12, 13.2). gy aztn a csert a megfelel brzol adatok cserjvel le-
het megoldani. Hogy lehetv tegyk az brzol adatok kezelst, adjunk meg egy swap()
fggvnyt a Vector osztly szmra (13.5):
template<class T> void Vector<T>::swap(Vector & a) // brzolsok cserje
{
swap(v,a.v);
swap(sz,a.sz);
}
A swap() tagot felhasznlhatjuk az ltalnos swap() specializlt definicijban:
template<class T> void swap(Vector<T>& a, Vector<T>& b)
{
a.swap(b);
}
A less() s a swap() ezen vltozatait hasznlja a standard knyvtr (16.3.9, 20.3.16) is. R-
adsul ezek a pldk szles krben hasznlt mdszereket mutatnak be. A specializci ak-
kor hasznos, ha a sablonparamterek egy adott halmazra egy ltalnos algoritmusnl lte-
zik hatkonyabb megvalsts (itt a swap()). Ezenkvl akkor is hasznos, ha a paramtert-
pus valamilyen szablytalansga miatt a szabvnyos algoritmus valamilyen nem kvnt m-
don mkdne (mint a less() esetben). Ezek a szablytalan tpusok tbbnyire a beptett
mutat- s tmbtpusok.
Absztrakcis mdszerek 454
13.6. rklds s sablonok
Az rklds s a sablonok olyan eszkzk, amelyekkel meglevk alapjn j tpusok pt-
hetk, s amelyekkel ltalnossgban a kzs vonsok klnfle kihasznlsval hasznos
kd rhat. Mint a 3.7.1, 3.8.5 s 13.5 pontokban lttuk, ezen eszkzk prostsa sok
hasznos mdszer alapja.
Egy sablon osztlynak (template class) egy nem sablon osztlybl val szrmaztatsa m-
dot nyjt arra, hogy sablonok egy halmaza kzs megvalstson osztozzk. A 13.5 pont-
beli vektor j plda erre:
template<class T> class List<T*> : private List<void*> { /* ... */ };
Msknt nzve a plda azt mutatja, hogy a sablon elegns s tpusbiztos felletet ad egy
msknt nem biztonsgosan s nem elegnsan hasznlhat eszkzhz.
Termszetesen a sablon osztlyok kztti rklds is hasznos. A bzisosztly egyik hasz-
na az, hogy tovbbi osztlyok szmra ptkl szolgl. Ha a bzisosztly adatai vagy m-
veletei valamely szrmaztatott osztly sablonparamtertl fggenek, akkor magnak
a bzisosztlynak is paramtereket kell adni (lsd pldul a 3.7.2 pontbeli Vec osztlyt):
template<class T> class vector { /* ... */ };
template<class T> class Vec : public vector<T> { /* ... */ };
A sablon fggvnyek tlterhels-feloldsi szablyai biztostjk a fggvnyek helyes mk-
dst az ilyen szrmaztatott tpusokra (13.3.2).
Az az eset a leggyakoribb, amikor ugyanaz a sablonparamter szerepel a bzis- s a szr-
maztatott osztlyban is, de ez nem szksgszer. rdekes, br ritkbban alkalmazott md-
szerek alapulnak azon, hogy magt a szrmaztatott osztlyt adjuk t a bzisosztlynak:
template <class C> class Basic_ops { // trolk alapmveletei
public:
bool operator==(const C&) const; // minden elem sszehasonltsa
bool operator!=(const C&) const;
// ...
// hozzfrs biztostsa C mveleteihez
const C& derived() const { return static_cast<const C&>(*this); }
};
13. Sablonok 455
template<class T> class Math_container : public Basic_ops< Math_container<T> > {
public:
size_t size() const;
T& operator[ ](size_t);
const T& operator[ ](size_t) const;
// ...
};
Ezltal lehetv vlik a trolk alapvet mveleteinek az egyes trolktl elklntett, egy-
szeri meghatrozsa. m mivel az olyan mveletek defincijhoz, mint az == s a != mind
a trolnak, mind annak elemeinek tpusra szksg van, a bzisosztlyt t kell adni
a trolsablonnak.
Ha feltesszk, hogy a Math_container egy hagyomnyos vektorra hasonlt, akkor
a Basic_ops egy tagja valahogy gy nzhet ki:
template <class C> bool Basic_ops<C>::operator==(const C& a) const
{
if (derived().size() != a.size()) return false;
for (int i = 0; i<derived().size(); ++i)
if (derived()[i] != a[i]) return false;
return true;
}
A trolk s a mveletek elvlasztsra szolgl msik mdszer rklds helyett sablon-
paramterekkel kapcsolja ssze azokat:
template<class T, class C> class Mcontainer {
C elements;
public:
T& operator[ ](size_t i) { return elements[i]; }
.. // ...
friend bool operator==(const Mcontainer&, const Mcontainer&); // elemek
// sszehasonltsa
friend bool operator!=(const Mcontainer&, const Mcontainer&);
// ...
};
template<class T> class My_array { /* ... */ };
Mcontainer< double,My_array<double> > mc;
Absztrakcis mdszerek 456
Egy sablonbl ltrehozott osztly teljesen kznsges osztly, ezrt lehetnek bart (friend)
fggvnyei (C.13.2) is. Ebben a pldban azrt hasznltam bartokat, hogy a == s a != sz-
mra a kt paramter szoksos felcserlhetsgt biztostsam (11.3.2). Ilyen esetekben
meg lehetne fontolni azt is, hogy a C paramterknt trol helyett sablont hasznljunk.
13.6.1. Paramterezs s rklds
A sablonok egy tpust vagy fggvnyt egy msik tpussal paramtereznek. A sablon kdja
minden paramtertpus esetben azonos, csakgy, mint a sablont hasznl legtbb kd-
rsz. Az absztrakt osztlyok felleteket rnak le, klnfle megvalstsaik kdrszeit az
osztlyhierarchik kzsen hasznlhatjk, s az absztrakt osztlyt hasznl kd legnagyobb
rsze nem is fgg a megvalststl. A tervezs szempontjbl a kt megkzelts annyira
kzeli, hogy kzs nevet rdemel. Minthogy mindkett azt teszi lehetv, hogy egy algo-
ritmust egyszer rjunk le s aztn klnfle tpusokra alkalmazzuk, nha mindkettt tbb-
alaksgnak (polimorfizmus, polymorphism) hvjk. Hogy megklnbztessk ket, a vir-
tulis fggvnyek ltal biztostottat futsi idej (run-time) tbbalaksgnak, a sablonok l-
tal nyjtottat pedig fordtsi idej (compile-time) tbbalaksgnak vagy paramteres
(parametric) tbbalaksgnak hvjk.
Vgl is mikor vlasszunk absztrakt osztlyt s mikor alkalmazzunk sablont? Mindkt eset-
ben olyan objektumokat kezelnk, amelyeknek azonos mveleteik vannak. Ha nem szk-
sges egymssal al- s flrendeltsgi viszonyban llniuk, akkor legyenek sablonparam-
terek. Ha az objektumok aktulis tpusa a fordtsi idben nem ismert, akkor legjobban egy
kzs absztrakt osztlybl rkld osztlyknt brzolhatjuk azokat. Ha a futsi id na-
gyon fontos, azaz a mveletek helyben kifejtve trtn fordthatsga alapvet szempont,
hasznljunk sablont. Errl rszletesebben a 24.4.1 pont r.
13.6.2. Tag sablonok
Egy osztlynak vagy osztlysablonnak lehetnek olyan tagjai is, amelyek maguk is sablonok:
template<class Scalar> class complex {
Scalar re, im;
public:
template<class T>
complex(const complex<T>& c) : re(c.real()), im(c.imag()) { }
// ...
};
13. Sablonok 457
complex<float> cf(0,0);
complex<double> cd = cf; // rendben: float-rl double-ra alakts hasznlata
class Quad {
// nincs talakts int-re
};
complex<Quad> cq;
complex<int> ci = cq; // hiba: nincs talakts Quad-rl int-re
Vagyis kizrlag akkor tudunk complex<T2>-bl complex<T1>-et pteni, ha T1-nek kez-
drtkl adhatjuk T2-t, ami sszer megszortsnak tnik.
Sajnos azonban a C++ nyelv elfogad a beptett tpusok kztti bizonyos sszertlen
konverzikat is, pldul double-rl int-re. A vgrehajtsi idben a csonktsbl ered hibkat
el lehetne kapni, implicit_cast (13.3.1) vagy checked (C.6.2.6.2) stlus ellenrztt
konverzival:
template<class Scalar> class complex {
Scalar re, im;
public:
complex() : re(0), im(0) { }
complex(const complex<Scalar>& c) : re(c.real()), im(c.imag()) { }
template<class T2> complex(const complex<T2>& c)
: re(checked_cast<Scalar>(c.real())), im(checked_cast<Scalar>(c.imag())) { }
// ...
};
A teljessg kedvrt alaprtelmezett s msol konstruktort is megadtam. Elg klns
mdon egy sablon konstruktorbl a fordt soha nem hoz ltre msol konstruktort, gy
kzvetlenl deklarlt msol konstruktor hjn a fordt alaprtelmezett konstruktort hozott
volna ltre, ami ebben az esetben azonos lett volna az ltalam megadottal.
Egy sablon tag nem lehet virtulis:
class Shape {
// ...
template<class T> virtual bool intersect(const T&) const =0; // hiba: virtulis sablon
};
Ez szablytalan. Ha megengedett lenne, akkor a virtulis fggvnyek megvalstsnak ha-
gyomnyos virtulisfggvny-tbls mdja (2.5.5) nem lenne alkalmazhat. A szerkeszt-
nek az intersect() fggvny minden j paramtertpussal trtn meghvsa esetn egy
jabb elemmel kellene bvtenie a Shape osztly virtulisfggvny-tbljt.
Absztrakcis mdszerek 458
13.6.3. rkldsi viszonyok
ltalban gy gondolnunk egy sablonra, mint j tpusok ltrehozst segt ltalnostsra.
Ms szval a sablon egy olyan eszkz, amely szksg esetn a felhasznl elrsai szerin-
ti tpusokat hoz ltre. Ezrt az osztlysablonokat nha tpusksztknek vagy tpusgenerto-
roknak hvjk.
A C++ nyelv szablyai szerint kt, azonos osztlysablonbl ltrehozott osztly nem ll ro-
konsgban egymssal:
class Shape { /* ... */ };
class Circle : public Shape { /* ... */ };
Ilyenkor egyesek megprbljk a set<Circle*>-ot set<Shape*>-knt kezelni, ami hibs rve-
lsen nyugv slyos logikai hiba: az egy Circle egyben Shape is, gy a Circle-k halmaza
egyben Shape-ek halmaza is; teht a Circle-k halmazt Shape-ek halmazaknt is kezelhe-
tem rvels teht pontja hibs. Oka, hogy a Circle-k halmaza kizrlag Circle tpus ob-
jektumokat tartalmaz, mg a Shape-ek halmaza esetben ez egyltaln nem biztos:
class Triangle : public Shape { /* ... */ };
void f(set<Shape*>& s)
{
// ...
s.insert(new Triangle());
// ...
}
void g(set<Circle*>& s)
{
f(s); // tpushiba: s tpusa set<Circle*>, nem set<Shape*>
}
A fenti pldt a fordtprogram nem fogja lefordtani, mert nincs beptett konverzi
set<Circle*>&-rl set<Shape*>&-re. Nagyon helyesen. Az a garancia, hogy a set<Circle*>
elemei Circle-k, lehetv teszi, hogy az elemekre biztonsgosan s hatkonyan vgez-
znk Circle-kre jellemz mveleteket, pldul a sugr lekrdezst. Ha megengednnk
a set<Circle*>-knek set<Shape*>-knt val kezelst, akkor ez mr nem lenne biztostott.
Pldul az f() fggvny egy Triangle* elemet tesz set<Shape*> paramterbe; ha
a set<Shape*> egy set<Circle*> lehetne, akkor nem lenne tbb igaz, hogy egy set<Circle*>
csak Circle*-okat tartalmaz.
13. Sablonok 459
13.6.3.1. Sablonok konverzija
Az elz plda azt mutatta be, mirt nem lehet semmilyen alaprtelmezett kapcsolat kt,
azonos osztlysablonbl ltrehozott osztly kztt. Egyes osztlyoknl azonban mgiscsak
szeretnnk ilyen kapcsolatot kifejezni. Pldul ha egy mutat sablont ksztnk, szeretnnk
tkrzni a mutatott objektumok kztti rkldsi viszonyokat. Tag sablonok (13.6.2)
igny esetn sokfle hasonl kapcsolatot kifejezhetnek:
template<class T> class Ptr { // mutat T-re
T* p;
public:
Ptr(T*);
template<class T2> operator Ptr<T2> (); // Ptr<T> konverzija Ptr<T2>-re
// ...
};
Ezutn szeretnnk a konverzis opertorokat gy definilni, hogy Ptr-jeinkre fennlljanak
a beptett mutatknl megszokott rkldsi kapcsolatok:
void f(Ptr<Circle> pc)
{
Ptr<Shape> ps = pc; // mkdnie kell
Ptr<Circle> pc2 = ps; // elvileg hibt eredmnyez
}
Azt szeretnnk, hogy az els kezdrtk-ads csak akkor legyen engedlyezett, ha a Shape
valban kzvetett vagy kzvetlen nyilvnos bzisosztlya a Circle-nek. ltalban gy sze-
retnnk a konverzis opertorokat megadni, hogy a Ptr<T>-rl Ptr<T2>-re trtn
konverzi csak akkor legyen engedlyezett, ha egy T2* tpus mutat rtkl kaphat egy
T* tpus mutatt. Ezt gy tehetjk meg:
template<class T>
template<class T2>
Ptr<T>::operator Ptr<T2> () { return Ptr<T2>(p); }
A return utastst a fordt csak akkor fogadja el, ha p (ami T* tpus) a Ptr<T2>(T2*)
konstruktor paramtere lehet. Ezrt ha a T* automatikusan T2*-ra konvertlhat, a Ptr<T>-
rl Ptr<T2>-re trtn konverzi mkdni fog:
void f(Ptr<Circle> pc)
{
Ptr<Shape> ps = pc; // rendben: Circle* talakthat Shape*-ra
Ptr<Circle> pc2 = ps; // hiba: Shape* nem alakthat Circle*-g
}
Absztrakcis mdszerek 460
Legynk vatosak s csak logikailag rtelmes konverzikat definiljunk.
Jegyezzk meg, hogy nem lehetsges egy sablonnak, illetve annak szintn sablon tagjnak
sablonparamter-listit egyesteni:
template<class T, class T2> // hiba
Ptr<T>::operator Ptr<T2> () { return Ptr<T2>(p); }
13.7. A forrskd szerkezete
A sablonokat hasznl kd szerkezete alapveten ktfle lehet:
1. A sablonok defincijt beptjk (#include) egy fordtsi egysgbe, mieltt hasz-
nlnnk azokat.
2. A hasznlat eltt a sablonoknak csak a deklarcijt emeljk be, definicijukat
kln fordtjuk.
Ezenkvl lehetsges, hogy a sablonfggvnyeket egy fordtsi egysgen bell elszr csak
deklarljuk, majd hasznljuk s csak vgl definiljuk.
Hogy lssuk a ktfle megkzelts kztti klnbsget, vegynk egy egyszer pldt:
#include<iostream>
template<class T> void out(const T& t) { std::cerr << t; }
Hvjuk ezt a fjlt out.c-nek s ptsk be, valahnyszor szksgnk van az out()-ra:
// user1.c:
#include "out.c"
// out() hasznlata
// user2.c:
#include "out.c"
// out() hasznlata
Vagyis az out()-ot s a hozz szksges valamennyi deklarcit tbb klnbz fordtsi
egysgbe is beptjk. A fordtprogram dolga, hogy csak akkor hozzon ltre kdot, ha
szksges, s hogy a felesleges informcik feldolgozst optimalizlja, ami azt is jelenti,
hogy a sablon fggvnyeket ugyangy kezeli, mint a helyben kifejtett (inline) fggvnyeket.
13. Sablonok 461
Ezzel az a nyilvnval gond, hogy minden olyan informci, amelyre az out()-nak szks-
ge van, az out()-ot felhasznl valamennyi fordtsi egysgbe belekerl, s ilyen mdon
megn a fordtprogram ltal feldolgozand adat mennyisge. Egy msik gond, hogy a fel-
hasznlk esetleg vletlenl rkapnak az eredetileg csak az out() definicijhoz szks-
ges deklarcik hasznlatra. Ezt a veszlyt nvterek hasznlatval, a makrk hasznlat-
nak elkerlsvel s ltalban a beptend informci mennyisgnek cskkentsvel h-
rthatjuk el.
E gondolatmenet logikus folyomnya a kln fordts: ha a sablon nem pl be a felhasz-
nli kdba, azok az elemek, melyektl fgg, nem is befolysolhatjk azt. gy az eredeti
out.c llomnyt kt rszre bontjuk:
// out.h:
template<class T> void out(const T& t);
// out.c:
#include<iostream>
#include "out.h"
export template<class T> void out(const T& t) { std::cerr << t; }
Az out.c fjl most az out() definilshoz szksges sszes informcit tartalmazza, az out.h
csak a meghvshoz szksgeset. A felhasznl csak a deklarcit (vagyis a felletet) p-
ti be:
// user1.c:
#include "out.h"
// out() hasznlata
// user2.c:
#include "out.h"
// out() hasznlata
Ezen a mdon a sablon fggvnyeket a nem helyben kifejtett fggvnyekhez hasonlan ke-
zeljk. Az out.c-beli defincit kln fordtjuk, s az adott fordt dolga szksg esetn az
out() lersnak megkeresse. Ez nmi terhet r a fordtra, hiszen a sablon-meghatrozs
fls pldnyainak kiszrse helyett szksg esetn meg kell tallnia az egyetlen defincit.
Jegyezzk meg, hogy a sablon defincija csak akkor rhet el ms fordtsi egysgbl, ha
kifejezetten export-knt adjuk meg (9.2.3). (Ez gy trtnik, hogy a defincihoz vagy egy
megelz deklarcihoz hozzadjuk az export szt.) Ha nem gy tesznk, a defincinak
minden hasznlat helyrl elrhetnek kell lennie.
Absztrakcis mdszerek 462
A fordt- s szerkesztprogramtl, a fejlesztett program fajtjtl, illetve a fejleszts kls
feltteleitl fgg, hogy melyik mdszer vagy azok milyen prostsa a legjobb. ltalban
a helyben kifejtett fggvnyeket s az egyb, alapveten ms sablon fggvnyeket hv
fggvnyeket rdemes minden olyan fordtsi egysgben elhelyezni, ahol felhasznljk
azokat. Egy, a sablon-pldnyosts tern tlagos tmogatst nyjt szerkesztprogram ese-
tben ez meggyorstja a fordtst s pontosabb hibazenetekhez vezet.
Ha a defincit beptjk, sebezhetv tesszk azt, mert gy rtelmt a bepts helyn r-
vnyes makrk s deklarcik befolysolhatjk. Ezrt a nagyobb vagy bonyolultabb fgg-
seket ignyl sablonokat jobb kln fordttatni, de akkor is ez az eljrs kvetend, ha
a sablon definicija sok deklarcit ignyel, mert ezek nem kvnatos mellkhatsokkal jr-
hatnak a sablon felhasznlsnak helyn.
n azt tekintem idelis megoldsnak, ha a sablondefincikat kln fordtjuk, a felhaszn-
li kdban pedig csak deklarcijukat szerepeltetjk. Ezen elvek alkalmazst azonban
mindig az adott helyzethez kell igaztanunk, a sablonok kln fordtsa pedig egyes nyelvi
vltozatok esetben kltsges mulatsg lehet.
Brmelyik megkzeltst vlasszuk is, a nem helyben kifejtett statikus tagoknak (C.13.1)
csak egyetlen definicija lehet, valamelyik fordtsi egysgben. Ebbl kvetkezen ilyen ta-
gokat lehetleg ne hasznljunk olyan sablonoknl, amelyek sok fordtsi egysgben szere-
pelnek.
Fontos cl, hogy a kd ugyangy mkdjk, akr egyetlen egysgben szerepel, akr kln
fordtott egysgekbe elosztva. Ezt inkbb gy rhetjk el, hogy cskkentjk a definci fg-
gst a krnyezettl, nem pedig gy, hogy a krnyezetbl minl tbbet tvisznk a pl-
dnyosts folyamatba.
13.8. Tancsok
[1] Olyan algoritmusok lersra, amelyek sokfle paramtertpusra alkalmazhatk,
sablont hasznljunk. 13.3.
[2] A trolkat sablonknt ksztsk el. 13.2.
[3] A trolkat specializljuk mutattpusokra, hogy cskkentsk a kd mrett.
13.5.
[4] A specializci eltt mindig adjuk meg a sablon ltalnos formjt. 13.5.
13. Sablonok 463
[5] A specializcit deklarljuk, mieltt hasznlnnk. 13.5.
[6] A sablonok fggst a pldnyosts mdjtl cskkentsk a lehet legkisebb-
re. 13.2.5, C.13.8.
[7] Definiljunk minden deklarlt specializcit. 13.5.
[8] Gondoljuk meg, hogy a sablonnak nincs-e szksge C stlus karakterlncokra
s tmbkre vonatkoz specializcikra. 13.5.2.
[9] Paramterezznk eljrsmd objektummal. 13.4.
[10] Specializci s tlterhels segtsgvel adjunk azonos felletet ugyanazon fo-
galom klnbz tpusokra vonatkoz megvalstsnak. 13.5.
[11] Egyszer esetekre vonatkozan egyszer felletet adjunk; a ritkbb eseteket
tlterhelssel s alaprtelmezett paramter-rtkekkel kezeljk. 13.5, 13.4.
[12] Mieltt sablonn ltalnostannk valamit, vgezznk hibakeresst egy konkrt
pldn. 13.2.1.
[13] Ne felejtsk el kitenni az export kulcsszt azoknl a definciknl, amelyeket
ms fordtsi egysgbl is el kell rni. 13.7.
[14] A nagy vagy nem magtl rtetd krnyezeti fggsg sablonokat kln
fordtsi egysgben helyezzk el. 13.7.
[15] Konverzikat sablonokkal fejezznk ki, de ezeket nagyon vatosan definiljuk.
13.6.3.1.
[16] Szksg esetn egy constraint() fggvny segtsgvel korltozzuk, milyen pa-
ramterei lehetnek a sablonnak. 13.9[16], C.13.10.
[17] A fordtsi s sszeszerkesztsi idvel val takarkoskods cljbl explicit pl-
dnyostst hasznljunk. C.13.10.
[18] Ha a futsi id dnt szempont, rklds helyett hasznljunk sablonokat.
13.6.1.
[19] Ha fontos szempont, hogy j vltozatokat jrafordts nlkl vezethessnk be,
sablonok helyett hasznljunk rkldst. 13.6.1.
[20] Ha nem lehet kzs bzisosztlyt megadni, rklds helyett hasznljunk sab-
lonokat. 13.6.1.
[21] Ha olyan beptett tpusokat s adatszerkezeteket kell hasznlnunk, amelyek-
nek a korbbi vltozatokkal sszeegyeztethetnek kell maradniuk, rklds
helyett hasznljunk sablonokat. 13.6.1.
Absztrakcis mdszerek 464
13.9. Gyakorlatok
1. (*2) Javtsuk ki a hibkat a List 13.2.5 pontbeli definicijban s rjuk meg
a fordtprogram ltal az f() fggvny s a List szmra ltrehozott kddal
egyenrtk kdot. Futtassunk le egy egyszer programot a sajt, illetve a sab-
lonbl ltrehozott vltozat ellenrzsre. Amennyiben mdunk van r, hasonlt-
suk ssze a ktfle kdot.
2. (*3) rjunk osztlysablont egy egyszeresen lncolt lista szmra, amely egy Link
tpusbl szrmaztatott tpus elemeket tud trolni. A Link tpus tartalmazza az
elemek sszekapcsolshoz szksges informcikat. Az ilyen listt tolakod
(intrusive) listnak nevezik. Ezen lista felhasznlsval rjunk egy brmilyen t-
pus elemeket tartalmazni kpes (azaz nem tolakod) egyszeresen lncolt listt.
Hasonltsuk ssze a kt lista hatkonysgt, elnyeiket s htrnyaikat.
3. (*2.5) rjunk tolakod s nem tolakod ktszeresen lncolt listkat. Milyen m-
veletek szksgesek az egyszeresen lncolt lista mveletein fell?
4. (*2) Fejezzk be a 13.2 pontbeli String sablont a 11.12 pontbeli String osztly
alapjn.
5. (*2) Hatrozzunk meg egy olyan sort()-ot, amely az sszehasonltsi felttelt
sablonparamterknt veszi t. Hatrozzunk meg egy Record osztlyt, melynek
kt tagja van, a count s a price. Rendezznk egy set<Record> halmazt mindkt
tag szerint.
6. (*2) Ksztsnk egy qsort() sablont.
7. (*2) rjunk egy programot, amely (key,value) prokat olvas be s az egyes kul-
csokhoz (key) tartoz rtkek (value) sszegt rja ki. Adjuk meg, milyen tpu-
sok lehetnek kulcsok, illetve rtkek.
8. (*2.5) A 11.8 pontbeli Assoc osztly alapjn ksztsnk egy egyszer Map
osztlyt. A Map mkdjk helyesen kulcsknt hasznlt C stlus karakterlncok-
kal s string-ekkel is, valamint akkor is, ha az alkalmazott tpusnak van alapr-
telmezett konstruktora, s akkor is, ha nincs.
9. (*3) Hasonltsuk ssze a 11.8 pontbeli szszmll program hatkonysgt egy
asszociatv tmbt nem hasznl programval. Azonos stlus be- s kimeneti
mveleteket hasznljunk mindkt esetben.
10. (*3) rjuk t a 13.9[8]-beli Map-et valamilyen alkalmasabb adatszerkezet pl-
dul vrs-fekete fa (red-black tree) vagy S-fa (splay tree) felhasznlsval.
11. (*2.5) Hasznljuk fel a Map-et topologikus rendezst megvalst fggvny r-
sra. (A topologikus rendezst a [Knuth,1987] els ktete rja le, a 280. oldaltl
kezdden.)
12. (*1.5) A 13.9[7]-beli sszegz programot javtsuk ki, hogy szkzket is tartal-
maz nevekre is helyesen mkdjn.
13. Sablonok 465
13. (*2) rjunk readline() sablonokat klnfle sorfajtk szmra (pldul (cikk,
szm, r)).
14. (*2) Hasznljuk a 13.4 pontbeli Literate-ben vzolt mdszert karakterlncok
fordtott bcsorrend rendezsre. Gondoskodjunk rla, hogy a mdszer
olyan C++-vltozatokkal is mkdjn, ahol a char eljeles (signed), s ott is,
ahol unsigned. Adjunk egy, a kis- s nagybetk kztti klnbsget elhanyago-
l rendezst tmogat vltozatot is.
15. (*1.5) Szerkessznk egy pldt, amely legalbb hromfle eltrst mutat be egy
fggvnysablon s egy makr kztt (a formai kvetelmnyek klnbsgn
fell).
16. (*2) Tervezznk egy mdszert, amellyel biztosthat, hogy a fordtprogram
minden olyan sablon minden paramterre ellenrizzen bizonyos megszortso-
kat, amelyeknek megfelel tpus objektum ltrejn a programban. Csak a T
paramternek egy My_base-bl szrmaztatott osztlynak kell lennie tpus
megszortsok ellenrzse nem elg!
Absztrakcis mdszerek 466
Kivtelkezels
Ne szljon kzbe,
amikor ppen kzbeszlok.
(Winston S. Churchill)
Hibakezels A kivtelek csoportostsa A kivtelek elkapsa Minden kivtel elkap-
sa Tovbbdobs Az erforrsok kezelse auto_ptr A kivtelek s a new oper-
tor Az erforrsok kimerlse Kivtelek konstruktorokban Kivtelek destruk-
torokban Olyan kivtelek, amelyek nem hibk Kivtelek specifikcija Vratlan kiv-
telek El nem kapott kivtelek A kivtelek s a hatkonysg A hibakezels egyb md-
jai Szabvnyos kivtelek Tancsok Gyakorlatok
14.1. Hibakezels
Ahogy a 8.3 pontban rmutattunk, egy knyvtr szerzje felfedezheti a futsi idej hib-
kat, de ltalban fogalma sincs rla, mit kezdjen velk. A knyvtr felhasznlja tudhatja,
hogyan kell az ilyen hibkat kezelni, de nem tudja felderteni azokat msklnben a fel-
hasznli kdban kezeln s nem a knyvtrnak kellene megtallnia ket. A kivtelek
(exception) az ilyen problmk kezelst segtik. Az alaptlet az, hogy ha egy fggvny
olyan hibt tall, amelyet nem tud kezelni, akkor egy kivtelt vlt ki (kivtelt dob, throw)
14
abban a remnyben, hogy a (kzvetett vagy kzvetlen) hv kpes kezelni a problmt.
Az adott problmt kezelni tud fggvnyek jelezhetik, hogy el akarjk kapni (catch) a ki-
vtelt (2.4.2, 8.3).
A hibakezels ezen mdja felveszi a versenyt a hagyomnyosabb mdszerekkel. Tekintsk
t a tbbi lehetsget: ha a program szreveszi, hogy olyan problma lpett fel, amelyet
nem lehet helyben megoldani, akkor
1. befejezheti a program futst,
2. egy hiba jelents rtket adhat vissza,
3. egy normlis rtket adhat vissza s a programot szablytalan llapotban
hagyhatja,
4. meghvhat egy, hiba esetn meghvand fggvnyt.
Alaprtelmezs szerint az els eset, azaz a program futsnak befejezse trtnik, ha olyan
kivtel lp fel, amelyet nem kap el a program. A legtbb hiba kezelsre ennl jobb meg-
olds szksges s lehetsges. Azok a knyvtrak, amelyek nem ismerik a befoglal prog-
ram cljt vagy annak ltalnos mkdst, nem hajthatnak vgre egyszeren egy abort()-
ot vagy exit()-et. Olyan knyvtrat, amely felttel nlkl befejezi a program futst, nem
hasznlhatunk olyan programban, amelynek nem szabad elszllnia. A kivtelek szerept
teht gy is megfogalmazhatnnk, hogy lehetsget adnak arra, hogy a vezrls visszake-
rljn a hvhoz, ha a megfelel mvelet helyben nem vgezhet el.
A msodik eset (hiba jelents rtk visszaadsa) nem mindig kivitelezhet, mert nem
mindig ltezik elfogadhat hibt jelent rtk. Ha egy fggvny pldul int tpussal tr
vissza, akkor minden int rtk hihet visszatrsi rtk lehet. De ha alkalmazhat is ez
a mdszer, sokszor akkor is knyelmetlen, mert minden hvst ellenrizni kell, ami a prog-
ram mrett akr ktszeresre is nvelheti (14.8). Ezrt aztn ezt a mdszert ritkn alkal-
mazzk annyira kvetkezetesen, hogy minden hibt szleljenek vele.
A harmadik mdszer (normlis rtk visszaadsa s a program szablytalan llapotban va-
l hagysa) azzal a gonddal jr, hogy a hv esetleg nem veszi szre, hogy a program nem
megengedett llapotba kerlt. A C standard knyvtrnak szmos fggvnye pldul az
errno globlis vltozt lltja be, hogy hibt jelezzen (14.8), a programok azonban jellem-
zen elmulasztjk az errno vltoz kellen kvetkezetes vizsglatt, ami megakadlyozn
a hibs hvsok halmozdst. Ezenkvl az egyidej hozzfrsnl (konkurrencia,
concurrency) a globlis vltozk hibajelzsre val hasznlata nem mkdik jl.
Absztrakcis mdszerek 468
A kivtelkezels nem az olyan esetek kezelsre szolgl, mint amelyekre a negyedik, a hi-
bakezel fggvny meghvsa md alkalmas, kivtelek hjn viszont a hibakezel fgg-
vnynek csak a tbbi hrom lehetsge van a hiba kezelsre. A hibakezel fggvnyek s
a kivtelek tmjt a 14.4.5 pont trgyalja.
A kivtelkezelst akkor clszer hasznlnunk a hagyomnyos mdszerek helyett, ha azok
nem elgsgesek, nem elegnsak vagy hibkat okozhatnak. A kivtelek hasznlata lehe-
tv teszi a hibakezel kdnak a kznsges kdtl val elvlasztst, ezltal a progra-
mot olvashatbb s a kdelemz eszkzk szmra kezelhetbb teszi. A kivtelkezel
eljrs szablyosabb hibakezelst tesz lehetv s megknnyti a kln megrt kdrszek
kztti egyttmkdst.
A C++ kivtelkezelsnek a Pascal- s C-programozk szmra j vonsa, hogy a hibk
(klnsen a knyvtrakban fellpett hibk) alaprtelmezett kezelse a program lelltsa.
A hagyomnyos kezels az volt, hogy valahogy tevickltnk a hibn, aztn remnyked-
tnk. A kivtelkezels trkenyebb teszi a programot abban az rtelemben, hogy tbb
gondot s figyelmet kell fordtani arra, hogy a program elfogadhatan fusson. Mindazonl-
tal ez elnysebbnek tnik, mintha ksbb a fejleszts sorn lpnnek fel hibs eredm-
nyek, vagy akr a fejleszts lezrulta utn, amikor a program mr a mit sem sejt felhaszn-
lk kezben van. Ha a lells az adott programnl elfogadhatatlan, akkor el lehet kapni az
sszes kivtelt (14.3.2) vagy az sszes adott fajtjt (14.6.2), gy a kivtel csak akkor llt-
ja le a programot, ha a programoz ezt hagyja. Ez pedig jobb, mint ha a hiba hagyomnyos
mdon trtnt nem teljes kezelst kveten vgzetes hiba, majd felttel nlkli lells
trtnne.
Egyesek a hibkon val tevickls nem vonz tulajdonsgait hibazenetek kiratsval,
a felhasznl segtsgt kr ablakokkal stb. prbltk enyhteni. Az ilyesmi fleg a prog-
ram hibakeressnl (debugging, a program belvse) hasznos, amikor a felhasznl
a program szerkezett ismer programoz. Nem fejlesztk szmra az esetleg jelen sem le-
v felhasznl/kezel segtsgt kr knyvtr elfogadhatatlan. Ezenkvl sokszor nincs is
hov rtestst kldeni a hibrl (pldul ha a program olyan krnyezetben fut, ahol a cerr
nem vezet a felhasznl ltal elrhet helyre), s a hibazenetek a vgfelhasznl szmra
gysem mondannak semmit. Az a legkevesebb, hogy a hibazenet esetleg nem is a meg-
felel nyelven jelenne meg, mondjuk finnl egy angol felhasznl szmra. Ennl rosszabb,
hogy a hibazenet jellemzen a knyvtr fogalmaival lenne megfogalmazva, mondjuk egy
grafikus felhasznli felletrl rkezett rossz adat hatsra bad argument to atan2. Egy j
knyvtr nem halandzszik gy. A kivtelek lehetv teszik az adott kdrszlet szmra,
hogy a hibt, amit nem tud kezelni, a kd olyan rsze szmra tovbbtsa, amely taln bol-
dogul vele. A programnak csak olyan rsze lehet kpes rtelmes hibazenet kldsre,
amelynek van fogalma a program sszefggseirl, krnyezetrl.
14. Kivtelkezels 469
A kivtelkezelsre gy is tekinthetnk, mint a fordtsi idej tpusellenrzs s tbbrtel-
msg-kiszrs futsi idej megfeleljre. A tervezsi folyamatra nagyobb hangslyt helyez
s megnvelheti egy kezdeti (mg hibs) vltozat ellltshoz szksges munka mennyi-
sgt. m az eredmny egy olyan kd, amelynek sokkal nagyobb az eslye arra, hogy az
elvrt mdon fusson, hogy egy nagyobb program rsze lehessen, hogy ms programozk
szmra is rthet legyen, hogy eszkzkkel lehessen kezelni. Ennek megfelelen a kiv-
telek nyelvileg tmogatott kezelse kifejezetten tmogatja a j stlus programozst, mint
ahogy a C++ nyelv ms eszkzei tmogatjk azt. Ms nyelveken (C vagy Pascal) a j stlus
csak egyes szablyok megkerlsvel s nem is tkletesen rhet el.
Meg kell azonban jegyeznnk, hogy a hibakezels ezutn is nehz feladat marad s a kiv-
telkezel eljrs jllehet rendszerezettebb, mint azok a mdszerek, amelyeket helyette-
st a vezrlst kizrlag helyben szablyoz nyelvi elemekhez kpest kevsb hatkony.
A C++ nyelv kivtelkezelse a programoznak a hibk azon helyen val kezelsre ad le-
hetsget, ahol ez a rendszer szerkezetbl addan a legtermszetesebb. A kivtelek nyil-
vnvalv teszik a hibakezels bonyolultsgt, de vigyzzunk, hogy a rossz hrrt ne annak
hozjt hibztassuk. Ezen a ponton clszer jraolvasni a 8.3 pontot, amely a kivtelkeze-
ls alapvet vonsait mutatja be.
14.1.1. A kivtelek ms megkzeltsei
A kivtel (exception) azon szavak egyike, amelyek klnbz emberek szmra kln-
bz jelentssel brnak. A C++ nyelv kivtelkezel rendszert gy terveztk, hogy hibk s
ms kivteles jelensgek kezelst tmogassa innen a neve s hogy tmogassa a hibk
kezelst fggetlenl fejlesztett sszetevkbl ll programokban.
A kivtelkezels csak a szinkron kivtelek, pldul a tmbindex-hibk vagy ki- s bemene-
ti hibk kezelsre szolgl. Az aszinkron esemnyek, mint a billentyzet fell rkez meg-
szaktsok vagy bizonyos aritmetikai hibk nem felttlenl kivtelek s nem kzvetlenl
ezzel az eljrssal kezelendk. Az aszinkron esemnyek vilgos s hatkony kezelshez
a kivtelkezels itt lert mdjtl alapveten klnbz eljrsokra van szksg. Sok rend-
szernek vannak az aszinkronits kezelsre szolgl eljrsai (pldul szignlok (jelzsek,
signal) hasznlata), de mivel ezek rendszerfggek szoktak lenni, lersuk itt nem szerepel.
A kivtelkezels egy nem loklis, a (vgrehajtsi) verem visszatekersn (stack
unwinding, 14.4) alapul vezrlsi szerkezet, amelyet alternatv visszatrsi eljrsknt is
tekinthetnk. Ezrt kivteleket olyan esetekben is szablyosan alkalmazhatunk, amelyek-
nek semmi kzk a hibkhoz (14.5). A kivtelkezelsnek azonban a hibakezels s a hi-
batr viselkeds tmogatsa az elsdleges clja s ez a fejezet is ezekre sszpontost.
Absztrakcis mdszerek 470
A szabvnyos C++ nem ismeri a vgrehajtsi szl (thread) s a folyamat (process, processz)
fogalmt, ezrt az egyidej hozzfrssel sszefgg kivteles helyzeteket itt nem trgyal-
juk. A hasznlt rendszer dokumentcija lerja az ezeket kezel eszkzket; itt csak azt
jegyzem meg, hogy a C++ nyelv kivtelkezel rendszert gy terveztk, hogy konkurens
programban is hatkony legyen, feltve, hogy a programoz vagy a rendszer betart bizo-
nyos alapvet szablyokat, pldul azt, hogy szablyosan zrolja (lock) a megosztott adat-
szerkezeteket a hasznlat eltt.
A C++ kivtelkezel eljrsainak a hibk s kivteles esemnyek jelentse s kezelse a cl-
juk, de a programoznak kell eldntenie, hogy egy adott programban mi szmt kivteles-
nek. Ez nem mindig knny (14.5). Tekintsnk-e kivtelesnek egy olyan esemnyt, amely
a program legtbb futsakor fellp? Lehet-e egy tervezett s kezelt esemny hiba? Mindkt
krdsre igen a vlasz. A kivteles nem azt jelenti, hogy szinte soha nem trtnhet meg
vagy hogy vgzetes. Jobb gy rtelmezni a kivtelt, hogy a rendszer valamely rsze nem
tudta megtenni, amire krtk. Szoks szerint ilyenkor valami mssal prblkozunk. Kiv-
telek kivltsa (dobsa) a fggvnyhvsokhoz kpest ritkn forduljon el, klnben
a rendszer szerkezete ttekinthetetlen lesz. A legtbb nagy program normlis s sikeres fut-
tatsa sorn azonban nhny kivtel kivltsa s elkapsa biztosan el fog fordulni.
14.2. A kivtelek csoportostsa
A kivtel olyan objektum, melynek osztlya valamilyen kivtel elfordulst rja le. A hibt
szlel kd (ltalban egy knyvtr) eldobja (throw) az objektumot (8.3). A hibt kezel-
ni kpes kdrszlet beavatkozsi szndkt egy elkap (catch) zradkkal jelzi. A kivtel
eldobsa visszatekeri a vgrehajtsi vermet, egszen addig, amg egy megfelel (vagyis
a kivtelt kivlt fggvnyt kzvetve vagy kzvetlenl meghv) fggvnyben catch-et
nem tallunk.
A kivtelek gyakran termszetes mdon csaldokra oszthatk. Ebbl kvetkezleg a kiv-
telek csoportostshoz s kezelshez az rklds hasznos segtsget nyjthat. Egy ma-
tematikai knyvtr kivteleit pldul gy csoportosthatjuk:
class Matherr { };
class Overflow: public Matherr { };
class Underflow: public Matherr { };
class Zerodivide: public Matherr { };
// ...
14. Kivtelkezels 471
Ez a szerkezet lehetv teszi, hogy a pontos tpusra val tekintet nlkl kezeljnk brmi-
lyen Matherr-t:
void f()
{
try {
// ...
}
catch (Overflow) {
// az Overflow (tlcsorduls) vagy ms onnan szrmaz hiba kezelse
}
catch (Matherr) {
// nem Overflow matematikai (Matherr) hibk kezelse
}
}
Itt az Overflow-t kln kezeltk. Az sszes tbbi Matherr kivtelt az ltalnos zradk fog-
ja kezelni. A kivteleknek hierarchiba val szervezse fontos lehet a kd tmrsge clj-
bl. Gondoljuk meg pldul, hogyan lehetne a matematikai knyvtr sszes kivtelt
kezelni, ha nem volnnak csoportostva. Az sszes kivtelt fel kellene sorolni:
void g()
{
try {
// ...
}
catch (Overflow) { /* ... */ }
catch (Underflow) { /* ... */ }
catch (Zerodivide) { /* ... */ }
}
Ez nemcsak fraszt, de egy kivtel knnyen ki is maradhat. Gondoljuk meg, mi lenne, ha
nem csoportostannk a matematikai kivteleket. Ha a matematikai knyvtr egy j kivtel-
lel bvlne, minden, az sszes kivtelt kezelni kvn kdrszletet mdostani kellene. l-
talban a knyvtr els vltozatnak kibocstsa utn ilyen teljes kr mdostsokra nincs
md. De ha van is, nem biztos, hogy minden kd a rendelkezsnkre ll, vagy ha igen, nem
biztos, hogy tnyleg hajlandak vagyunk tdolgozni. Ezek az jrafordtsi s mdosthat-
sgi szempontok ahhoz az irnyelvhez vezetnnek, hogy az els vltozat kibocstsa utn
a knyvtr nem bvlhetne tovbbi kivtelekkel; ez pedig a legtbb knyvtr szmra el-
fogadhatatlan. Ezrt teht a kivteleket clszer knyvtranknti vagy alrendszerenknti
csoportokban megadni (14.6.2).
Absztrakcis mdszerek 472
Jegyezzk meg, hogy sem a beptett matematikai mveletek, sem a (C-vel kzs) alapve-
t matematikai knyvtr nem kivtelek formjban jelzik az aritmetikai hibkat. Ennek
egyik oka, hogy szmos utastscsvet alkalmaz (pipelined) rendszer aszinkron mdon
szlel bizonyos aritmetikai hibkat, pldul a nullval val osztst. A Matherr hierarchia
ezrt itt csak illusztrciul szolgl. A standard knyvtrbeli kivteleket a 14.10 rja le.
14.2.1. Szrmaztatott kivtelek
Az osztlyhierarchik kivtelkezelsre val hasznlata termszetesen vezet olyan kivtelke-
zelkhz, amelyeket a kivtelek hordozta informcinak csak egy rsze rdekel. Vagyis egy
kivtelt ltalban egy bzisosztlynak a kezelje kap el, nem sajt osztlynak kezelje.
A kivtel elkapsnak s megnevezsnek mkdse a paramteres fggvnyekvel azo-
nos. Vagyis a formlis paramter a paramter-rtkkel kap kezdrtket (7.2). Ebbl k-
vetkezik, hogy a kivtelnek csak az elkapott osztlynak megfelel rsze msoldik le (fel-
szeletelds, slicing, 12.2.3):
class Matherr {
// ...
virtual void debug_print() const { cerr << "Matematikai hiba"; }
};
class Int_overflow: public Matherr {
const char* op;
int a1, a2;
public:
Int_overflow(const char* p, int a, int b) { op = p; a1 = a; a2 = b; }
virtual void debug_print() const { cerr << op << '(' << a1 << ',' << a2 << ')'; }
// ...
};
void f()
{
try {
g();
}
catch (Matherr m) {
// ...
}
}
A Matherr kezelbe val belpskor az m egy Matherr objektum, mg ha g() egy
Int_overflow-t vltott is ki. Kvetkezskppen az Int_overflow-ban lev tbblet informci
elrhetetlen.
14. Kivtelkezels 473
Mint mindig, mutatk vagy referencik hasznlatval megakadlyozhatjuk az informci-
vesztst:
int add(int x, int y)
{
if ((x>0 && y>0 && x>INT_MAX-y) || (x<0 && y<0 && x<INT_MIN-y))
throw Int_overflow("+",x,y);
return x+y; // x+y nem fog tlcsordulni
}
void f()
{
try {
int i1 = add(1,2);
int i2 = add(INT_MAX,-2);
int i3 = add(INT_MAX,2); // ez az!
}
catch (Matherr& m) {
// ...
m.debug_print();
}
}
Az utols add() hvs kivltotta kivtel hatsra Int_overflow::debug_print() fog vgrehaj-
tdni. Ha a kivtelt rtk s nem referencia szerint kaptuk volna el, akkor ehelyett
Matherr::debug_print()-re kerlt volna sor.
14.2.2. sszetett kivtelek
Nem minden kivtelcsoport fa szerkezet. Egy kivtel gyakran kt csoportba is tartozik:
class Netfile_err : public Network_err, public File_system_err { /* ... */ };
Egy ilyen Netfile_err -t el lehet kapni a hlzati kivtelekkel trd fggvnyekben:
void f()
{
try {
// valami
}
catch(Network_err& e) {
// ...
}
}
Absztrakcis mdszerek 474
De akr a fjlrendszer-kivtelekkel foglalkoz fggvnyekben is:
void g()
{
try {
// valami ms
}
catch(File_system_err& e) {
// ...
}
}
A hibakezels ilyen nem hierarchikus szervezse akkor fontos, amikor a szolgltatsok
(pldul a hlzati szolgltatsok) a felhasznl szmra lthatatlanok. Ebben a pldban
a g() rja esetleg nem is tudott arrl, hogy hlzat is szerepet jtszik (lsd mg 14.6).
14.3. A kivtelek elkapsa
Vegyk az albbi kdrszletet:
void f()
{
try {
throw E();
}
catch(H) {
// mikor jutunk ide?
}
}
A vezrls akkor kerl a kezelhz, ha
1. H ugyanaz a tpus, mint E,
2. H egyrtelm bzisosztlya E-nek,
3. H s E mutattpusok s a mutatott tpusokra teljesl 1. vagy 2.,
4. H referencia s tpusra teljesl 1. vagy 2.
14. Kivtelkezels 475
Ezenkvl egy kivtel elkapsnl ugyangy alkalmazhatjuk a const minstt, mint egy
fggvny paramternl. Ettl az elkaphat kivtelek tpusa nem vltozik meg, csak meg-
akadlyozza, hogy az elkapott kivtelt mdosthassuk.
Az az elv, hogy a kivtelrl kivltsakor msolat kszl, a kezel pedig az eredeti kivtel
msolatt kapja meg. Lehetsges, hogy egy kivtelrl elkapsa eltt tbb msolat is kszl,
ezrt nem lehet olyan kivtelt kivltani, ami nem msolhat. Az adott nyelvi vltozat na-
gyon sokfle mdszert alkalmazhat a kivtelek trolsra s tovbbtsra, de az biztostott,
hogy a memria elfogysakor szabvnyos mdon kivltand kivtel, a bad_alloc (14.4.5)
szmra van hely.
14.3.1. A kivtelek tovbbdobsa
Gyakran elfordul, hogy miutn egy kezel elkapott egy kivtelt, gy dnt, hogy nem tudja
teljes egszben kezelni azt. Ilyenkor a kezel ltalban megteszi helyben, amit lehet, majd
tovbbdobja a kivtelt. gy a hibt vgl is a legalkalmasabb helyen lehet kezelni. Ez akkor
is igaz, ha a kivtel kezelshez szksges informci nem egyetlen helyen ll rendelkezs-
re s a hiba kvetkezmnyeit legjobban tbb kezel kztt elosztva kszblhetjk ki:
void h()
{
try {
// esetleg matematikai hibkat kivlt kd
}
catch (Matherr) {
if (teljesen_le_tudjuk_kezelni) {
// Matherr kezelse
return;
}
else {
// megtesszk, amit lehet
throw; // a kivtel tovbbdobsa
}
}
}
A kivtel tovbbdobst az operandus nlkli throw jelzi. Ha akkor prblunk meg kivtelt
tovbbdobni, ha nincs is kivtel, terminate() hvs fog bekvetkezni (14.7). A fordtprog-
ram az ilyen esetek egy rszt de nem mindet feldertheti s figyelmeztethet rjuk.
Absztrakcis mdszerek 476
A tovbbdobs az eredeti kivtelre vonatkozik, nem csak annak elkapott rszre, ami
Matherr-knt rendelkezsre ll. Ms szval, ha a program Int_overflow-t vltott ki, amelyet
h() egy Matherr-knt kapott el s gy dnttt, hogy tovbbdobja, akkor a h() hvja is
Int_overflow-t kap.
14.3.2. Minden kivtel elkapsa
Az elkapsi s tovbbdobsi mdszer egy vgletes esetvel is rdemes megismerkednnk.
Mint ahogy fggvnyekre a ... tetszleges paramtert jelent (7.6), a catch(...) jelentse is
a tetszleges kivtel elkapsa:
void m()
{
try {
// valami
}
catch (...) { // minden kivtelt elkapunk
// takarts
throw;
}
}
gy ha m() f rsznek vgrehajtsa sorn brmilyen kivtel lp fel, sor kerl a kezel fgg-
vny rendrak tevkenysgre. Amint a helyi rendraks megtrtnt, az arra okot ad kiv-
tel tovbbdobdik a tovbbi hibakezels kivltsa cljbl. A 14.6.3.2 pont r arrl, hogyan
lehet informcihoz jutni a ... kezel ltal elkapott kivtelrl.
A hibakezelsnek ltalban (a kivtelkezelsnek pedig klnsen) fontos szempontja
a program ltal felttelezett llapot rvnyessgnek megrzse (invarins, 24.3.7.1). Pl-
dul ha m() bizonyos mutatkat olyan llapotban hagy htra, ahogy azokat tallta, akkor
a kivtelkezelbe berhatjuk a nekik elfogadhat rtket ad kdot. gy a minden kivtelt
elkap kezel tetszleges invarinsok kezelsre alkalmas hely lehet. Sok fontos esetben
azonban egy ilyen kivtelkezel nem a legelegnsabb megolds (lsd 14.4).
14. Kivtelkezels 477
14.3.2.1. A kezelk sorrendje
Mivel egy szrmaztatott osztly kivtelt tbb tpus kivtel kezelje is elkaphat, a try utas-
tsban a kezelk sorrendje lnyeges. A kezelk kiprblsra ebben a sorrendben kerl sor:
void f()
{
try {
// ...
}
catch (std::ios_base::failure) {
// i/o adatfolyam hibk kezelse (14.10)
}
catch (std::exception& e) {
// standard knyvtrbeli kivtelek kezelse (14.10)
}
catch (...) {
// egyb kivtelek kezelse (14.3.2)
}
}
Mivel a fordtprogram ismeri az osztlyhierarchit, sok logikai hibt kiszrhet:
void g()
{
try {
// ...
}
catch (...) {
// minden kivtel kezelse (14.3.2)
}
catch (std::exception& e) {
// standard knyvtrbeli kivtelek kezelse (14.10)
}
catch (std::bad_cast) {
// dynamic_cast hibk kezelse (15.4.2)
}
}
Itt az exception soha nem jut szerephez. Mg ha el is tvoltjuk a mindent elkap kezelt,
a bad_cast akkor sem kerl szba, mert az exception leszrmazottja.
Absztrakcis mdszerek 478
14.4. Erforrsok kezelse
Amikor egy fggvny lefoglal valamilyen erforrst pldul megnyit egy fjlt, lefoglal va-
lamennyi memrit a szabad trban, zrol valamit stb. , gyakran fontos felttel a rendszer
tovbbi mkdse szempontjbl, hogy az erforrst rendben fel is szabadtsa a hvhoz
val visszatrs eltt:
void use_file(const char* fn)
{
FILE* f = fopen(fn,"r");
// f hasznlata
fclose(f);
}
Ez mkdkpesnek tnik, amg szre nem vesszk, hogy ha valami hiba trtnik az
fopen() meghvsa utn, de az fclose() meghvsa eltt, akkor egy kivtel miatt a use_file()
fggvny az fclose() vgrehajtsa nlkl trhet vissza. Ugyanez a problma kivtelkezelst
nem tmogat nyelvek esetben is fellphet. Pldul a C standard knyvtrnak longjmp()
fggvnye ugyanilyen problmt okozhat. Mg egy kznsges return utasts miatt is
visszatrhet a use_file() az fclose() vgrehajtsa nlkl.
Egy els ksrlet a use_file() hibatrv ttelre gy nzhet ki:
void use_file(const char* fn)
{
FILE* f = fopen(fn,"r");
try {
// f hasznlata
}
catch (...) {
fclose(f);
throw;
}
fclose(f);
}
A fjlt hasznl kd egy try blokkban van, amely minden hibt elkap, bezrja a fjlt, majd
tovbbdobja a kivtelt.
14. Kivtelkezels 479
Ezzel a megoldssal az a gond, hogy feleslegesen hossz, fradsgos s esetleg lass is
lehet. Radsul minden tl hossz s fradsgos megolds nyomban ott jrnak a hibk, hi-
szen a programozk elfradnak. Szerencsre van jobb megolds. A megoldand helyzet
ltalnos formjban gy nz ki:
void acquire()
{
// els erforrs lektse
// ...
// n-edik erforrs lektse
// erforrsok felhasznlsa
// n-edik erforrs felszabadtsa
// ...
// els erforrs felszabadtsa
}
ltalban fontos szempont, hogy az erforrsokat megszerzskkel ellenttes sorrendben
szabadtsuk fel. Ez erteljesen emlkeztet a konstruktorok ltal felptett s destruktorok l-
tal megsemmistett helyi objektumok viselkedsre. gy aztn az ilyen erforrs-lefoglalsi
s -felszabadtsi problmkat konstruktorokkal s destruktorokkal br osztlyok objektu-
mainak hasznlatval kezelhetjk. Pldul megadhatjuk a File_ptr osztlyt, amely egy
FILE*-hoz hasonlan viselkedik:
class File_ptr {
FILE* p;
public:
File_ptr(const char* n, const char* a) { p = fopen(n,a); }
File_ptr(FILE* pp) { p = pp; }
~File_ptr() { fclose(p); }
operator FILE*() { return p; }
};
Egy File_ptr objektumot vagy egy FILE* mutatval, vagy az fopen()-hez szksges param-
terekkel hozhatunk ltre; brmelyik esetben lettartama vgn a File_ptr objektum meg-
semmisl, destruktora pedig bezrja a fjlt. Programunk mrete gy minimlisra cskken:
void use_file(const char* fn)
{
File_ptr f(fn,"r");
// f hasznlata
}
Absztrakcis mdszerek 480
A destruktor attl fggetlenl meghvdik, hogy a fggvnybl normlisan vagy kivtel
kivltsa folytn lptnk-e ki. gy a kivtelkezel eljrs lehetv teszi, hogy a hibakezelst
eltvoltsuk a f algoritmusbl. Az gy add kd egyszerbb s kevesebb hibt okoz, mint
hagyomnyos megfelelje.
Azt a mveletet, amikor a kivtelek kezelsekor a hvsi lncban megkeressk a megfelel
kezelt, rendszerint a verem visszatekersnek hvjk. Ahogy a vermet visszatekerik,
gy hvjk meg a ltrehozott loklis objektumok destruktorait.
14.4.1. Konstruktorok s destruktorok hasznlata
Az erforrsok loklis objektumok hasznlatval val kezelst szoks szerint gy hvjk,
hogy kezdeti rtkads az erforrs megszerzsvel (resource acquisition is initialization).
Ez az ltalnos eljrs a konstruktorok s destruktorok tulajdonsgain, valamint a kivtel-
kezel rendszerrel val egyttmkdskn alapul.
Egy objektumot addig nem tekintnk lteznek, amg konstruktora le nem fut. Destruktora
csak ebben az esetben fog a verem visszatekersekor meghvdni. Egy rszobjektumokbl
ll objektumot olyan mrtkben tekintnk ltrehozottnak, amilyen mrtkben rszobjek-
tumainak konstruktorai lefutottak, egy tmbt pedig addig az elemig, amelynek konstruk-
tora mr lefutott. A destruktor a verem visszatekersekor csak a teljes egszben ltrehozott
elemekre fut le.
A konstruktor azt prblja elrni, hogy az objektum teljesen s szablyosan ltrejjjn. Ha
ez nem rhet el, egy jl megrt konstruktor amennyire csak lehetsges olyan llapot-
ban hagyja a rendszert, mint meghvsa eltt volt. Idelis esetben a nav mdon megrt
konstruktorok teljestik ezt s nem hagyjk az objektumot valamilyen flig ltrehozott l-
lapotban. Ezt a kezdeti rtkads az erforrs megszerzsvel mdszernek a tagokra va-
l alkalmazsval rhetjk el.
Vegynk egy X osztlyt, amelynek konstruktora kt erforrst ignyel: egy x llomnyt s
egy y zrolst. Ezek megszerzse nem biztos, hogy sikerl, s kivtelt vlthat ki. Az X osz-
tly konstruktornak semmilyen krlmnyek kztt nem szabad gy visszatrnie, hogy az
llomnyt lefoglalta, de a zrat nem. Ezenkvl ezt anlkl kell elrni, hogy a dolog bonyo-
lultsgval a programoznak trdnie kelljen. A megszerzett erforrsok brzolsra kt
osztlyt hasznlunk, a File_ptr-t s a Lock_ptr-t. Az adott erforrs megszerzst az azt jel-
l loklis objektum kezdeti rtkadsa brzolja:
14. Kivtelkezels 481
class X {
File_ptr aa;
Lock_ptr bb;
public:
X(const char* x, const char* y)
: aa(x,"rw"), // 'x' lefoglalsa
bb(y) // 'y' lefoglalsa
{ }
// ...
};
Csakgy mint a loklis objektumos pldban, itt is az adott fordt dolga a szksges nyil-
vntartsok vezetse. A felhasznlnak nem kell ezzel trdnie. Pldul ha az aa ltreho-
zsa utn, de a bb- eltt kivtel vltdik ki, akkor az aa destruktornak meghvsra sor
kerl, de a bb-re nem.
Ebbl kvetkezleg ahol az erforrsok megszerzse ezen egyszer modell szerint trt-
nik, ott a konstruktor rjnak nem kell kifejezett kivtelkezel kdot rnia.
Az alkalmi mdon kezelt erforrsok kztt a leggyakoribb a memria:
class Y {
int* p;
void init();
public:
Y(int s) { p = new int[s]; init(); }
~Y() { delete[ ] p; }
// ...
};
A fenti mdszer hasznlata ltalnos gyakorlat s a memria elszivrgshoz (memory
leak) vezethet. Ha az init()-ben kivtel keletkezik, a lefoglalt memria nem fog felszabadul-
ni, mivel az objektum nem jtt ltre teljesen, gy destruktora nem fut le. me egy biztons-
gos vltozat:
class Z {
vector<int> p;
void init();
public:
Z(int s) : p(s) { init(); }
// ...
};
Absztrakcis mdszerek 482
A p ltal hasznlt memrit a vector kezeli. Ha az init() kivtelt vlt ki, a lefoglalt memri-
t p (automatikusan meghvott) destruktora fogja felszabadtani.
14.4.2. Auto_ptr
A standard knyvtr auto_ptr sablon osztlya tmogatja a kezdeti rtkads az erforrs
megszerzsvel mdszert. Egy auto_ptr-nek alapveten egy mutatval adhatunk kezdr-
tket s ugyangy hivatkozhatunk a mutatott objektumra, mint egy hagyomnyos mutat-
nl. Ezenkvl amikor az auto_ptr megsemmisl, az ltala mutatott objektum is automatiku-
san trldik:
void f(Point p1, Point p2, auto_ptr<Circle> pc, Shape* pb) // kilpskor ne felejtsk
// el trlni pb-t
{
auto_ptr<Shape> p(new Rectangle(p1,p2)); // p tglalapra mutat
auto_ptr<Shape> pbox(pb);
p->rotate(45); // az auto_ptr<Shape> pont gy hasznlhat, mint a Shape*
// ...
if (in_a_mess) throw Mess();
// ...
}
Itt a Rectangle, a pb ltal mutatott Shape s a pc ltal mutatott Circle egyarnt trldni fog,
akr vltdott ki kivtel, akr nem.
Ezen tulajdonos szerinti kezels (vagy destruktv msols) tmogatsa cljbl az auto_ptr-
eknek a kznsges mutatktl gykeresen eltr msolsi mdszerk van: egy auto_ptr-
nek egy msikba val msolsa utn a forrs nem mutat semmire. Minthogy az auto_ptr-
eket a msols megvltoztatja, konstans (const) auto_ptr-eket nem lehet msolni.
Az auto_ptr sablont a <memory> fejllomny deklarlja. me egy lehetsges kifejtse:
template<class X> class std::auto_ptr {
template <class Y> struct auto_ptr_ref { /* ... */ }; // segdosztly
X* ptr;
public:
typedef X element_type;
explicit auto_ptr(X* p =0) throw() { ptr=p; } // throw() jelentse "nem vlt ki kivtelt",
// lsd 14.6
~auto_ptr() throw() { delete ptr; }
14. Kivtelkezels 483
// figyeljk meg: msols s rtkads nem konstans paramterekkel
auto_ptr(auto_ptr& a) throw(); // msol, majd a.ptr=0
template<class Y> auto_ptr(auto_ptr<Y>& a) throw(); // msol, majd a.ptr=0
auto_ptr& operator=(auto_ptr& a) throw(); // msol, majd a.ptr=0
template<class Y> auto_ptr& operator=(auto_ptr<Y>& a) throw(); // msol, majd a.ptr=0
X& operator*() const throw() { return *ptr; }
X* operator->() const throw() { return ptr; }
X* get() const throw() { return ptr; } // mutat kinyerse
X* release() throw() { X* t = ptr; ptr=0; return t; } // tulajdonosvlts
void reset(X* p =0) throw() { if (p!=ptr) { delete ptr; ptr=p; } }
auto_ptr(auto_ptr_ref<X>) throw(); // msols auto_ptr_ref-bl
template<class Y> operator auto_ptr_ref<Y>() throw(); // msols auto_ptr_ref-be
template<class Y> operator auto_ptr<Y>() throw(); // destruktv msols auto_ptr-bl
};
Az auto_ptr_ref clja, hogy megvalstsa a kznsges auto_ptr szmra a destruktv m-
solst, ugyanakkor megakadlyozza egy konstans auto_ptr msolst. Ha egy D*-ot B*-g
lehet alaktani, akkor a sablonkonstruktor s a sablon-rtkads (meghatrozott mdon
vagy automatikusan) egy auto_ptr<D>-t auto_ptr<B>-v tud alaktani:
void g(Circle* pc)
{
auto_ptr<Circle> p2 = pc; // most p2 felel a trlsrt
auto_ptr<Circle> p3 = p2; // most p3 felel a trlsrt (p2 mr nem)
p2->m = 7; // programozi hiba: p2.get()==0
Shape* ps = p3.get(); // mutat kinyerse auto_ptr-bl
auto_ptr<Shape> aps = p3; // tulajdonosvlts s tpuskonverzi
auto_ptr<Circle> p4 = pc; // programozi hiba: most p4 is felel a
// trlsrt
}
Az nem meghatrozott, mi trtnik, ha tbb auto_ptr is mutat egy objektumra, de az a leg-
valsznbb, hogy az objektum ktszer trldik ami hiba.
Jegyezzk meg, hogy az auto_ptr destruktv msolsi mdszere miatt nem teljesti a szab-
vnyos trolk elemeire vagy a sort()-hoz hasonl szabvnyos eljrsokra vonatkoz kve-
telmnyeket:
vector< auto_ptr<Shape> >& v; // veszlyes: auto_ptr hasznlata trolban
// ...
sort(v.begin(),v.end()); // Ezt ne tegyk: a rendezstl v srlhet
Absztrakcis mdszerek 484
Vilgos, hogy az auto_ptr nem egy ltalnos okos vagy intelligens mutat (smart poin-
ter). De amire terveztk az automatikus mutatk kivtelbiztos kezelsre arra lnyeges
kltsg nlkl megfelel.
14.4.3. Figyelmeztets
Nem minden programnak kell mindenfajta hibval szemben immunisnak lennie s nem
minden erforrs annyira ltfontossg, hogy a vdelme megrje a kezdeti rtkads az
erforrs megszerzsvel mdszernek, az auto_ptr-nek, illetve a catch(...) alkalmazsnak
fradsgt. Pldul sok, egyszeren a bemenetet olvas s azzal le is fut programnl a s-
lyosabb futsi idej hibkat gy is kezelhetjk, hogy egy alkalmas hibazenet kiadsa utn
a programot lelltjuk. Ezzel a rendszerre bzzuk a program ltal lefoglalt sszes erforrs
felszabadtst, gy a felhasznl jrafuttathatja a programot egy jobb bemenettel. Az itt le-
rt mdszer olyan alkalmazsok szmra hasznos, amelyeknl a hibk ilyesfle egyszers-
tett kezelse elfogadhatatlan. Egy knyvtr tervezje pldul ltalban nem lhet feltev-
sekkel a knyvtrat hasznl program hibatrsi kvetelmnyeit illeten, gy aztn el kell
kerlnie minden felttel nlkli futsi idej hibt s minden erforrst fel kell szabadtania,
mieltt egy knyvtri fggvny visszatr. A kezdeti rtkads az erforrs megszerzsvel
mdszer a kivteleknek a hibk jelzsre val felhasznlsval egytt sok ilyen knyvtr
szmra megfelel.
14.4.4. A kivtelek s a new opertor
Vegyk a kvetkezt:
void f(Arena& a, X* buffer)
{
X* p1 = new X;
X* p2 = new X[10];
X* p3 = new(buffer[10]) X; // X tmeneti trba helyezse (nem
// szksges felszabadts)
X* p4 = new(buffer[11]) X[10];
X* p5 = new(a) X; // trfoglals az 'a' Arena-tl (a-t kell
// felszabadtani)
X* p6 = new(a) X[10];
}
14. Kivtelkezels 485
Mi trtnik, ha X konstruktora kivtelt vlt ki? Felszabadul-e a new() opertor ltal lefoglalt
memria? Kznsges esetben a vlasz igen, gy p1 s p2 kezdeti rtkadsa nem okoz me-
mria-elszivrgst.
Ha az elhelyez utastst (placement syntax, 10.4.11) hasznljuk, a vlasz nem ennyire
egyszer. Az elhelyez utasts nmely felhasznlsa lefoglal nmi memrit, amit aztn fel
kellene szabadtani, de nmelyik nem. Ezenkvl az elhelyez utasts alkalmazsnak
ppen a memria nem szabvnyos lefoglalsa a lnyege, gy aztn jellemzen nem szabv-
nyos mdon is kell felszabadtani azt. Kvetkezskppen a tovbbiak a felhasznlt mem-
riafoglaltl (alloktortl) fggnek. Ha egy Z::operator new() memriafoglal szerepelt, ak-
kor a rendszer meghvja a Z::operator delete()-et, feltve, hogy van ilyen; ms mdon nem
prbl felszabadtst vgezni. A tmbk kezelse ezzel azonos mdon trtnik (15.6.1). Ez
az eljrs helyesen kezeli a standard knyvtrbeli elhelyez new opertort (10.4.11), csak-
gy, mint minden olyan esetet, amikor a programoz sszeill lefoglal s felszabadt
fggvnyprt rt.
14.4.5. Az erforrsok kimerlse
Visszatr programozsi dilemma, hogy mi trtnjk, ha nem sikerl egy erforrs lefogla-
lsi ksrlete, pldul mert korbban meggondolatlanul nyitogattunk fjlokat (az fopen()-
nel) s foglaltunk le memrit a szabad trban (a new opertorral), anlkl, hogy trdtnk
volna vele, mi van, ha nincs meg a fjl vagy hogy kifogytunk-e a szabad trbl. Ilyen prob-
lmval szembeslve a programozk ktfle megoldst szoktak alkalmazni:
1. jrakezds: krjnk segtsget valamelyik hv fggvnytl s folytassuk a prog-
ram futst.
2. Befejezs: hagyjuk abba a szmtst s trjnk vissza a hvhoz.
Az els esetben a hvnak fel kell kszlnie arra, hogy segtsget adjon egy ismeretlen kd-
rszletnek annak erforrs-lefoglal problmjban. A msodik esetben a hvnak arra kell
felkszlnie, hogy kezelje az erforrs-lefoglals sikertelensgbl add problmt. A m-
sodik eset a legtbbszr sokkal egyszerbb s a rendszer elvonatkoztatsi szintjeinek jobb
sztvlasztst teszi lehetv. Jegyezzk meg, hogy a befejezs vlasztsakor nem a prog-
ram futsa fejezdik be, hanem csak az adott szmts. A befejezs (termination) azon el-
jrs hagyomnyos neve, amely egy sikertelen szmtsbl a hv ltal adott hibakezel-
be tr vissza (ami aztn jra megprblhatja a szmtst elvgeztetni), ahelyett, hogy
megprbln maga orvosolni a helyzetet s a hiba fellptnek helyrl folytatni a szmtst.
Absztrakcis mdszerek 486
A C++-ban az jrakezd modellt a fggvnyhvsi eljrs, a befejez modellt a kivtelkeze-
l eljrs tmogatja. Mindkettt szemllteti a standard knyvtrbeli new() opertor egy egy-
szer megvalstsa s felhasznlsa:
void* operator new(size_t size)
{
for (;;) {
if (void* p = malloc(size)) return p; // megprblunk memrit tallni
if (_new_handler == 0) throw bad_alloc(); // nincs kezel: feladjuk
_new_handler(); // segtsget krnk
}
}
Itt a C standard knyvtrnak malloc() fggvnyt hasznltam a szabad memriahely tny-
leges megkeressre; az operator new() ms vltozatai ms mdokat vlaszthatnak. Ha si-
kerlt memrit tallni, a new() opertor visszaadhatja az arra hivatkoz mutatt; ha nem,
a new() meghvja a _new_handler-t. Ha az tall a malloc() szmra lefoglalhat memrit,
akkor minden rendben. Ha nem, akkor a kezel nem trhet vissza a new() opertorba vg-
telen ciklus okozsa nlkl. A _new_handler ekkor kivtelt vlthat ki, ilyen mdon valame-
lyik hvra hagyva a helyzet tisztzst:
void my_new_handler()
{
int no_of_bytes_found = find_some_memory();
if (no_of_bytes_found < min_allocation) throw bad_alloc(); // feladjuk
}
Valahol lennie kell egy try blokknak is, a megfelel kivtelkezelvel:
try {
// ...
}
catch (bad_alloc) {
// valahogy reaglunk a memria kifogysra
}
A new() opertor ezen vltozatban hasznlt _new_handler egy, a szabvnyos
set_new_handler() fggvny ltal fenntartott fggvnymutat. Ha sajt my_new_handler()-
nket szeretnnk _new_handler-knt hasznlni, ezt rhatjuk:
set_new_handler(&my_new_handler);
14. Kivtelkezels 487
Ha el akarjuk kapni a bad_alloc kivtelt is, ezt rhatjuk:
void f()
{
void(*oldnh)() = set_new_handler(&my_new_handler);
try {
// ...
}
catch (bad_alloc) {
// ...
}
catch (...) {
set_new_handler(oldnh); // kezel jbli belltsa
throw; // tovbbdobs
}
set_new_handler(oldnh); // kezel jbli belltsa
}
Mg jobb, ha a catch(...) kezelt a kezdeti rtkads az erforrs megszerzsvel md-
szernek (14.4) a _new_handler-re val alkalmazsval elkerljk (14.12[1]).
A _new_handler alkalmazsval a hiba szlelsnek helytl semmifle tovbbi informci
nem jut el a segdfggvnyig. Knny tbb informcit kzvetteni, m minl tbb jut el
belle egy futsi idej hiba szlelsnek helytl a kijavtst segt helyig, a kt kd annl
inkbb fgg egymstl. Ebbl addan az egyik kdrszleten csak a msikat rtve s eset-
leg azt mdostva lehet vltoztatni. A klnbz helyen lev programrszek elszigetels-
re j mdszer, ha az ilyen fggseket a legkisebb mrtkre szortjuk vissza. A kivtelkeze-
l eljrs jobban tmogatja az elszigetelst, mint a hv ltal biztostott segdfggvnyek
meghvsa.
ltalban clszer az erforrsok megszerzst rtegekbe, elvonatkoztatsi szintekbe szer-
vezni, s elkerlni, hogy egy rteg a hv rteg segtsgre szoruljon. Nagyobb rendszerek
esetben a tapasztalat azt mutatja, hogy a sikeres rendszerek ezt az utat kvetik.
A kivtelek kivltshoz szksg van egy eldoband objektumra. Az egyes C++-
vltozatoktl elvrjuk, hogy a memria elfogysakor is maradjon annyi tartalk, amennyi
egy bad_alloc kivltshoz kell, de lehetsges, hogy valamilyen ms kivtel dobsa a me-
mria elfogyshoz vezet.
Absztrakcis mdszerek 488
14.4.6. Kivtelek konstruktorokban
A kivtelek adnak megoldst arra a problmra, hogyan jelentsnk hibt egy konstruktorbl.
Minthogy a konstruktorok nem adnak vissza kln visszatrsi rtket, amelyet a hv meg-
vizsglhatna, a hagyomnyos (azaz kivteleket nem alkalmaz) lehetsgek a kvetkezk:
1. Adjunk vissza egy hibs llapot objektumot, megbzva a hvban, hogy az majd
ellenrzi az llapotot.
2. lltsunk be egy nem loklis vltozt (pldul az errno-t), hogy jelezze a ltreho-
zs sikertelensgt, s bzzunk a felhasznlban, hogy ellenrzi a vltozt.
3. Ne vgezznk kezdeti rtkadst a konstruktorban s bzzunk meg a felhaszn-
lban, hogy az els hasznlat eltt elvgzi azt.
4. Jelljk meg az objektumot kezdrtk nlkli-knt, s vgezze az els megh-
vott tagfggvny a kezdeti rtkadst. Ez a fggvny jelenti majd a hibt, ha
a kezdeti rtkads nem sikerlt.
A kivtelkezels lehetsget ad arra, hogy a ltrehozs sikertelensgre vonatkoz infor-
mcit a konstruktorbl tadjuk. Egy egyszer Vector osztly pldul gy vdekezhet a tl-
zott ignyek ellen:
class Vector {
public:
class Size { };
enum { max = 32000 };
Vector(int sz)
{
if (sz<0 || max<sz) throw Size();
// ...
}
// ...
};
A Vector-okat ltrehoz kd most elkaphatja a Vector::Size hibkat, s megprblhatunk
valami rtelmeset kezdeni velk:
Vector* f(int i)
{
try {
Vector* p = new Vector(i);
// ...
return p;
}
14. Kivtelkezels 489
catch(Vector::Size) {
// mrethiba kezelse
}
}
Mint mindig, maga a hibakezel az alapvet hibakezel s -helyrellt mdszerek szok-
sos kszlett alkalmazhatja. Valahnyszor a hv egy kivtelt kap, megvltozik annak rtel-
mezse, hogy mi volt a hiba. Ha a kivtellel egytt a megfelel informci is tovbbtdik,
a problma kezelshez rendelkezsre ll ismeretek halmaza akr bvlhet is. Ms sz-
val, a hibakezel mdszerek alapvet clja az, hogy a hiba felfedezsnek eredeti helyrl
olyan helyre jutassunk el informcit, ahol elegend ismeret ll rendelkezsre a hiba k-
vetkezmnyeinek elhrtshoz, s ezt radsul megbzhatan s knyelmesen tegyk.
A kezdeti rtkads az erforrs megszerzsvel eljrs az egynl tbb erforrst igny-
be vev konstruktorok kezelsnek legbiztosabb s legelegnsabb mdja (14.4). Ez lnye-
gben a sok erforrs kezelsnek problmjt az egy erforrst kezel egyszerbb eljrs
ismtelt alkalmazsra vezeti vissza.
14.4.6.1. Kivtelek s a tagok kezdeti rtkadsa
Mi trtnik, ha egy tag kezdeti rtkadsa kzvetlenl vagy kzvetve kivtelt vlt ki? Alap-
rtelmezs szerint a kivtelt az a hv fggvny kapja meg, amelyik a tag osztlynak
konstruktornak meghvta, de maga a konstruktor is elkaphatja azt, ha a teljes fggvnytr-
zset a tag kezdrtk-listjval egytt egy try blokkba zrjuk:
class X {
Vector v;
// ...
public:
X(int);
// ...
};
X::X(int s)
try
:v(s) // v kezdrtke s
{
// ...
}
catch (Vector::Size) { // a v ltal kivltott kivtelt itt kapjuk el
// ...
}
Absztrakcis mdszerek 490
14.4.6.2. Kivtelek s msols
Ms konstruktorokhoz hasonlan a msol konstruktor is jelezheti a hibs futst kivtel ki-
vltsval. Ebben az esetben az objektum nem jn ltre. A vector msol konstruktora pl-
dul memrit foglal le s tmsolja az elemi objektumokat (16,3,4,, E.3.2.), ami kivtelt
vlthat ki. A kivtel kivltsa eltt a msol konstruktor fel kell szabadtsa a lefoglalt er-
forrsokat. Az E.2. s E.3. fggelkekben rszletesen trgyaljuk a kivtelkezels s a tro-
lk erforrs-gazdlkodsnak kapcsolatait.
A msol rtkad opertor ugyangy lefoglalhat erforrsokat s kivlthat kivtelt. Mieltt
ez utbbit tenn, az rtkadsnak biztostania kell, hogy mindkt operandust azonos lla-
potban hagyja. Ms esetben megszegjk a standard knyvtrbeli elrsokat, melynek k-
vetkezmnye nem meghatrozott viselkeds lehet.
14.4.7. Kivtelek destruktorokban
A kivtelkezel eljrs szemszgbl nzve egy destruktort ktflekppen lehet meghvni:
1. Norml (szablyos) meghvs: a hatkrbl val szablyos kilpskor ((10.4.3)
vagy egy delete hvs folytn (10.4.5) stb.
2. Kivtelkezels kzbeni meghvs: a verem visszatekerse kzben (14.4) a kivtel-
kezel eljrs elhagy egy blokkot, amely destruktorral br objektumot tartalmaz.
Az utbbi esetben a kivtel nem lphet ki a destruktorbl. Ha megteszi, az a kivtelkezel
eljrs hibjnak szmt, s meghvdik az std::terminate() (14.7). Vglis a kivtelkezel
eljrsnak s a destruktornak ltalban nincs mdja eldnteni, hogy elfogadhat-e az egyik
kivtelnek a msik kedvrt val elhanyagolsa.
Ha a destruktor olyan fggvnyt hv meg, amely kivtelt vlthat ki, akkor ez ellen vdekezhet:
X::~X()
try {
f(); // kivtelt vlthat ki
}
catch (...) {
// valamit csinlunk
}
A standard knyvtrbeli uncaught_exception() fggvny true-val tr vissza, ha van olyan
eldobott kivtel, amelyet mg nem kaptak el. Ez lehetv teszi a destruktor attl fgg
programozst, hogy az objektum szablyos vagy a verem visszatekerse kzbeni megsem-
mistsrl van-e sz.
14. Kivtelkezels 491
14.5. Kivtelek, amelyek nem hibk
Ha egy kivtelre szmtunk s elkapjuk s gy annak nincs a program mkdsre nzve
rossz kvetkezmnye, akkor mirt lenne hiba? Csak mert a programoz a kivtelre mint hi-
bra s a kivtelkezel eljrsokra pedig mint hibakezel eszkzkre gondol? Nos, a kiv-
teleket gy is tekinthetjk, mintha vezrlszerkezetek lennnek:
void f(Queue<X>& q)
{
try {
for (;;) {
X m = q.get(); // 'Empty' kivtelt vlt ki, ha a sor res
// ...
}
}
catch (Queue<X>::Empty) {
return;
}
}
Ez egszen jnak tnik, gy ebben az esetben tnyleg nem teljesen vilgos, mi szmt hib-
nak s mi nem.
A kivtelkezels kevsb rendezett eljrs, mint az ifhez vagy a for-hoz hasonl helyi ve-
zrlsi szerkezetek s azoknl gyakran kevsb hatkony is, ha a kivtelre tnylegesen sor
kerl. Ezrt kivteleket csak ott hasznljunk, ahol a hagyomnyosabb vezrlsi szerkezetek
nem elegnsak vagy nem hasznlhatak. A standard knyvtr pldul tartalmaz egy tetsz-
leges elemekbl ll queue-t (sor) is, kivtelek alkalmazsa nlkl (17.3.2).
A keresfggvnyeknl klnsen a rekurzv hvsokat nagymrtkben alkalmaz fgg-
vnyeknl (pldul egy fban keres fggvnynl) j tlet befejezsknt kivtelt
alkalmazni:
void fnd(Tree* p, const string& s)
{
if (s == p->str) throw p; // megtallta s-t
if (p->left) fnd(p->left,s);
if (p->right) fnd(p->right,s);
}
Tree* find(Tree* p, const string& s)
{
Absztrakcis mdszerek 492
try {
fnd(p,s);
}
catch (Tree* q) { // q->str==s
return q;
}
return 0;
}
Ugyanakkor a kivtelek ilyen hasznlata knnyen tlzsba vihet s ttekinthetetlen kd-
hoz vezethet. Ha sszer, ragaszkodjunk a kivtelkezels hibakezels elvhez. Ekkor
a kd vilgosan kt rszre klnl: kznsges s hibakezel kdra. Ez rthetbb teszi
a kdot. Sajnos a val vilg nem ilyen tiszta, a program szerkezete pedig ezt (bizonyos fo-
kig kvnatos mdon) tkrzni fogja.
A hibakezels lnyegnl fogva nehz. Becsljnk meg mindent, ami segt egy vilgos mo-
dellt kialaktani arrl, mi szmt hibnak s hogyan kezeljk.
14.6. Kivtelek specifikcija
A kivtel kivltsa vagy elkapsa befolysolja a fggvnynek ms fggvnyekhez val vi-
szonyt. Ezrt rdemes lehet a fggvny deklarcijval egytt megadni azon kivteleket
is, amelyeket a fggvny kivlthat.
void f(int a) throw (x2, x3);
Ha egy fggvny megadja, milyen kivtelek lphetnek fel vgrehajtsa kzben, akkor ez-
zel valjban garancit nyjt a hasznlinak. Ha a fggvny futsa kzben valamit olyasmit
prbl tenni, ami ezt a garancit rvnytelenn tenn, akkor ez a ksrlet az
std::unexpected() hvss fog talakulni. Az unexpected() alaprtelmezett jelentse
std::terminate(), ami szokvnyos esetben meghvja az abort()-ot. (Rszletesen lsd
a 9.4.1.1 pontban.) Valjban a
void f() throw (x2, x3)
{
// trzs
}
14. Kivtelkezels 493
egyenrtk a kvetkezvel:
void f()
try
{
// trzs
}
catch (x2) { throw; } // tovbbdobs
catch (x3) { throw; } // tovbbdobs
catch (...) {
std::unexpected(); // az unexpected() nem fog visszatrni
}
Ennek legfontosabb elnye, hogy a fggvny deklarcija a hvk ltal elrhet fellet r-
sze. A fggvnydefincik viszont nem ltalnosan elrhetek. Ha hozz is frnk az sszes
knyvtr forrshoz, hatrozottan nem szeretnk srn beljk nzegetni, radsul a meg-
kivtel-specifikcikkal (exception specification) elltott fggvnydeklarcik sokkal rvi-
debbek s vilgosabbak, mint az egyenrtk kzzel rott vltozat.
A kivtel-specifikcik nlkli fggvnyekrl azt kell feltteleznnk, hogy brmilyen kiv-
telt kivlthatnak:
int f(); // brmilyen kivtelt kivlthat
A kivtelt ki nem vlt fggvnyeket res listval adhatjuk meg:
int g() throw (); // nem vlt ki kivtelt
Azt gondolhatnnk, az lenne a j alaprtelmezs, hogy a fggvny nem vltana ki kivtelt.
Ekkor azonban alapveten minden fggvny rszre szksges lenne kivteleket meghat-
rozni, emiatt pedig sokszor kellene jrafordtani s meg is akadlyozn a ms nyelveken rt
programokkal val egyttmkdst. Ez aztn arra sztnzn a programozkat, hogy ak-
nzzk al a kivtelkezel rendszert s hogy hibs kdot rjanak a kivtelek elfojtsra, ez
pedig az aknamunkt szre nem vevknek hamis biztonsgrzetet adna.
14.6.1. A kivtelek ellenrzse
Egy felletnek fordtsi idben nem lehet minden megsrtsi ksrlett ellenrizni, de azrt
fordtskor sok ellenrzs trtnik. A specifiklt (teht engedlyezett) kivteleket a ford-
t gy rtelmezi, hogy a fggvny azok mindegyikt ki fogja vltani. A kivtel-specifikcik
fordtsi idej ellenrzsnek szablyai a knnyen felderthet hibkat tiltjk.
Absztrakcis mdszerek 494
Ha egy fggvny valamely deklarcija megad kivteleket is, akkor mindegyik deklarci-
jnak (s definicijnak is) tartalmaznia kell pontosan ugyanazokat a kivteleket:
int f() throw (std::bad_alloc);
int f() // hiba: a kivtel-meghatrozs hinyzik
{
// ...
}
Fontos szempont, hogy a kivtelek fordtsi egysgek hatrain tnyl ellenrzse nem k-
telez. Termszetesen az adott nyelvi vltozat ellenrizhet gy, de a nagyobb rendszerek leg-
tbbje szmra fontos, hogy ez ne trtnjen meg, vagy ha mgis, csak akkor jelezzen vgze-
tes hibt, ha a kivtel-specifikcik megsrtst nem lehet majd futsi idben elkapni.
A lnyeg az, hogy j kivtellel val bvts ne knyszertse ki a kapcsold kivtel-
specifikcik kijavtst s az esetleg rintett sszes kd jrafordtst. A rendszer gy egy
flig feljtott llapotban tovbb mkdhet s a vratlan kivtelek dinamikus (futsi idbe-
li) szlelsre hagyatkozhat, ami alapvet az olyan nagy rendszereknl, ahol a nagyobb
frisstsek kltsgesek s nincs is meg az sszes forrs.
Egy virtulis fggvnyt csak olyan fggvnnyel lehet fellrni, amely legalbb annyira szi-
goran hatrozza meg a kivteleket, mint maga az eredeti fggvny:
class B {
public:
virtual void f(); // brmit kivlthat
virtual void g() throw(X,Y);
virtual void h() throw(X);
};
class D : public B {
public:
void f() throw(X); // rendben
void g() throw(X); // rendben: D::g() szigorbb, mint B::g()
void h() throw(X,Y); // hiba: D::h() megengedbb, mint B::h()
};
Ezt a szablyt a jzan sz diktlja. Ha egy szrmaztatott osztly olyan kivtelt vltana ki,
amit az eredeti fggvny nem adott meg lehetsgesknt, annak elkapst nem vrhatnnk
el a hvtl. Msrszt egy fellr fggvny, amely kevesebb kivtelt vlt ki, betartja a fell-
rt fggvny kivtel-specifikcijban fellltott szablyokat.
14. Kivtelkezels 495
Ugyangy egy, a kivteleket szigorbban meghatroz fggvnyt hozzrendelhetnk egy
megengedbb fggvnymutathoz, de fordtva nem:
void f() throw(X);
void (*pf1)() throw(X,Y) = &f; // rendben
void (*pf2)() throw() = &f; // hiba: f() megengedbb, mint pf2
Kivtel-specifikci nlkli fggvnyre hivatkoz mutatt kivteleket meghatroz fgg-
vnymutathoz klnskppen nem rendelhetnk:
void g(); // brmit kivlthat
void (*pf3)() throw(X) = &g; // hiba: g() megengedbb, mint pf3
A kivtel-specifikci nem rsze a fggvny tpusnak, a typedef-ek pedig nem is tartalmaz-
hatnak ilyet:
typedef void (*PF)() throw(X); // hiba
14.6.2. Vratlan kivtelek
Ha a kivtelek kre specifiklt, az ott nem szerepl kivtelek az unexpected() meghvst
vlthatjk ki. Az ilyen hvsok a tesztelsen kvl ltalban nem kvnatosak. A kivtelek
gondos csoportostsval s a felletek megfelel meghatrozsval viszont elkerlhetk,
de az unexpected() hvsokat is el lehet fogni s rtalmatlann tenni.
Egy megfelelen kidolgozott Y alrendszer gyakran az Yerr osztlybl szrmaztatja a kivteleit:
class Some_Yerr : public Yerr { /* ... */ };
A fenti esetben a
void f() throw (Xerr, Yerr, exception);
minden Yerr-t tovbbtani fog a hvjnak. gy teht f() a Some_Yerr osztly hibt is tovb-
btani fogja s f()-ben semmilyen Yerr nem fog unexpected() hvst kivltani.
A standard knyvtr ltal kivltott sszes kivtel az exception osztly (14.10) leszrmazottja.
Absztrakcis mdszerek 496
14.6.3. Kivtelek lekpezse
A programot lelltani kezeletlen kivtel fellpse esetn nha tl knyrtelen eljrs.
Ilyenkor az unexpected() viselkedst kell elfogadhatbb tenni.
Ennek az a legegyszerbb mdja, hogy a standard knyvtrbeli std::bad_exception-t fel-
vesszk a specifiklt kivtelek kz. Ekkor az unexpected() egyszeren egy bad_exception-
t fog kivltani egy kezelfggvny meghvsa helyett:
class X { };
class Y { };
void f() throw(X,std::bad_exception)
{
// ...
throw Y(); // bad_exception-t vlt ki
}
A kivtelkezel ekkor elkapja az elfogadhatatlan Y kivtelt s bad_exception tpust vlt ki
helyette. A bad_exception-nel semmi baj, a terminate()-nl kevsb drasztikus de azrt
gy is meglehetsen durva beavatkozs, radsul az informci, hogy milyen kivtel okoz-
ta a problmt, elvsz.
14.6.3.1. Kivtelek felhasznli lekpezse
Vegynk egy g() fggvnyt, amely nem hlzati krnyezethez kszlt. Ttelezzk fel, hogy
g() csak a sajt Y alrendszervel kapcsolatos kivtelek kivltst engedlyezi. Tegyk fel,
hogy g()-t hlzati krnyezetben kell meghvnunk.
void g() throw(Yerr);
Termszetesen g() nem fog tudni semmit a hlzati kivtelekrl s az unexpected()-et fog-
ja meghvni, ha ilyennel tallkozik. A g() hlzati krnyezetben val hasznlathoz olyan
kdra van szksgnk, amely hlzati kivteleket kezel, vagy t kell rnunk g()-t. Tegyk
fel, hogy az jrars nem lehetsges vagy nem kvnatos. Ekkor a problmt az
unexpected() jelentsnek fellbrlsval oldhatjuk meg.
A memria elfogyst a _new_handler kezeli, amelyet a set_new_handler() llt be. Ugyan-
gy a vratlan kivtelre adott vlaszt egy _unexpected_handler hatrozza meg, amelyet az
<exception> fejllomnyban megadott std::set_unexpected() llt be:
14. Kivtelkezels 497
typedef void(*unexpected_handler)();
unexpected_handler set_unexpected(unexpected_handler);
A vratlan kivtelek megfelel kezelshez ltre kell hoznunk egy osztlyt, hogy a kezde-
ti rtkads az erforrs megszerzsvel mdszert hasznlhassuk az unexpected() fggv-
nyekben:
class STC { // trol s visszallt
unexpected_handler old;
public:
STC(unexpected_handler f) { old = set_unexpected(f); }
~STC() { set_unexpected(old); }
};
Ezutn definilunk egy fggvnyt az unexpected() erre az esetre kvnt jelentsvel:
class Yunexpected : public Yerr { };
void throwY() throw(Yunexpected) { throw Yunexpected(); }
A throwY()-t unexpected()-knt hasznlva brmilyen kivtelbl Yunexpected lesz.
Vgl elkszthetjk g()-nek egy hlzati krnyezetben alkalmazhat vltozatt:
void networked_g() throw(Yerr)
{
STC xx(&throwY); // az unexpected() most Yunexpected-et vlt ki
g();
}
Mivel az Yunexpected az Yerr-bl szrmazik, a kivtel-specifikci nem srl. Ha
a throwY() olyan kivtelt vltott volna ki, amit a specifikci nem engedlyez, akkor
a terminate() hajtdott volna vgre.
Azltal, hogy mentettk s visszalltottuk az _unexpected_handler-t, tbb alrendszer sz-
mra tettk lehetv egymsra val hats nlkl a vratlan kivtelek kezelst. A vrat-
lan kivtelek engedlyezett alaktsnak e mdszere alapveten a rendszer ltal
a bad_exception-nal nyjtott szolgltats rugalmasabb vltozata.
Absztrakcis mdszerek 498
14.6.3.2. Kivtelek tpusnak visszalltsa
A vratlan kivtelek Yunexpected-d alaktsa lehetv tenn a networked_g() felhasznl-
jnak, hogy megtudja: egy vratlan kivtelt kpeztnk le az Yunexpected-re. Azt azonban
nem fogja tudni, hogy melyik kivtel lekpezse trtnt meg. Egy egyszer eljrssal lehe-
tv tehetjk ennek megjegyzst s tovbbtst. Pldul gy tudnnk informcit gyjte-
ni a Network_exception-krl:
class Yunexpected : public Yerr {
public:
Network_exception* pe;
Yunexpected(Network_exception* p) :pe(p?p->clone():0) { }
~Yunexpected() { delete p; }
};
void throwY() throw(Yunexpected)
{
try {
throw; // A tovbbdobott kivtelt azonnal el kell kapni!
}
catch(Network_exception& p) {
throw Yunexpected(&p); // lekpezett kivtel kivltsa
}
catch(...) {
throw Yunexpected(0);
}
}
A kivtelek tovbbdobsa s elkapsa lehetv teszi, hogy azon tpusok sszes kivtelt
kezeljk, amelyeket meg tudunk nevezni. A throwY() fggvnyt az unexpected() hvja meg,
azt pedig elvileg egy catch(...) kezel. gy teht biztosan van tovbbdobhat kivtel. Egy
unexpected() fggvny nem tekinthet el a hibtl s nem trhet vissza. Ha megprbl gy
tenni, az unexpected() maga fog bad_expection-t kivltani (14.6.3). A clone() fggvnyt
arra hasznljuk, hogy a kivtel egy msolata szmra helyet foglaljon a szabad memri-
ban. Ez a msolat tlli a verem visszatekerst.
14.7. El nem kapott kivtelek
Ha egy kivtelt nem kapnak el, akkor az std::terminate() meghvsra kerl sor.
A terminate() fog meghvdni akkor is, ha a kivtelkezel eljrs a vermet srltnek tall-
ja, vagy ha egy, a verem visszatekerse sorn meghvott destruktor kivtel kivltsval pr-
bl vget rni.
14. Kivtelkezels 499
A vratlan kivteleket az _unexpected_handler kezeli, amelyet az std::set_unexpected() l-
lt be. Ehhez hasonlan, az el nem kapott kivtelek kezelst az _uncaught_handler vg-
zi, amelyet az <exception> fejllomnyban megadott std::set_uncaught() llt be:
typedef void(*terminate_handler)();
terminate_handler set_terminate(terminate_handler);
A visszatrsi rtk a set_terminate()-nek elzleg adott fggvny.
A terminate() meghvsnak oka, hogy idnknt a kivtelkezelssel fel kell hagyni kevs-
b kifinomult hibakezelsi mdszerek javra. A terminate()-et pldul hasznlhatjuk arra,
hogy megszaktsunk egy folyamatot vagy esetleg jraindtsunk egy rendszert (j kezdeti r-
tkadssal). A terminate() meghvsa drasztikus intzkeds: akkor kell hasznlni, amikor
a kivtelkezel eljrs ltal megvalstott hibakezel stratgia csdt mondott s ideje a hi-
batrs ms szintjre ttrni.
A terminate() alaprtelmezs szerint az abort()-ot hvja meg (9.4.1.1). Ez az alaprtelme-
zs a legtbb felhasznl szmra megfelel vlaszts, klnsen a program hibakeresse
(debugging) alatt.
Az _uncaught_handler fggvnyekrl a rendszer felttelezi, hogy nem fognak visszatrni
a hvjukhoz. Ha az adott fggvny megprblja, a terminate() meg fogja hvni az abort()-ot.
Jegyezzk meg, hogy az abort() a programbl val nem szablyos kilpst jelent. Az exit()
fggvny visszatrsi rtkvel jelezhetjk a rendszernek, hogy a programbl szablyosan
vagy nem szablyosan lptnk-e ki (9.4.1.1).
Az adott nyelvi vltozat hatrozza meg, hogy a program futsnak el nem kapott kivtel mi-
atti befejezdsnl a destruktorok meghvdnak-e. Bizonyos rendszereknl alapvet,
hogy a destruktorok ne hvdjanak meg, hogy a program futtatst folytatni lehessen a hi-
bakeresbl. Ms rendszerek szmra felptskbl addan szinte lehetetlen nem meg-
hvni a destruktorokat a kivtel kezeljnek keressekor.
Ha biztostani akarjuk a rendrakst egy el nem kapott kivtel esetben, a main() fggvny-
ben a tnylegesen elkapni kvnt kivtelek mell rhatunk egy minden kivtelt elkap ke-
zelt is (14.3.2):
int main()
try {
// ...
}
Absztrakcis mdszerek 500
catch (std::range_error)
{
cerr << "Tartomnyhiba: mr megint!\n";
}
catch (std::bad_alloc)
{
cerr << "A new kifogyott a memribl.\n";
}
catch (...) {
// ...
}
Ez a globlis vltozk konstruktorai s destruktorai ltal kivltottak kivtelvel minden ki-
vtelt elkap. A globlis vltozk kezdeti rtkadsa alatt fellp kivteleket nem lehet el-
kapni. Egy nem loklis statikus objektum kezdeti rtkadsa kzbeni throw esetben csak
a set_unexpected() (14.6.2) segtsgvel kaphatjuk meg a vezrlst, ami jabb ok arra,
hogy lehetsg szerint kerljk a globlis vltozkat.
A kivtelek kivltsnl a kivtel fellpsnek pontos helye ltalban nem ismert, vagyis
kevesebb informcit kapunk, mint amit egy hibakeres (debugger) tudhat a program l-
lapotrl. Ezrt bizonyos C++ fejlesztkrnyezetek, programok vagy fejlesztk szmra
elnysebb lehet nem elkapni azokat a kivteleket, amelyek kvetkezmnyeit a program-
ban nem kszbljk ki.
14.8. A kivtelek s a hatkonysg
Elvileg lehetsges a kivtelkezelst gy megtervezni, hogy az ne jrjon a futsi id nveke-
dsvel, ha nem kerl sor kivtel kivltsra. Radsul ezt gy is meg lehet tenni, hogy egy
kivtel kivltsa ne legyen klnsebben kltsges egy fggvnyhvshoz kpest. Ha azon-
ban a memriaignyt is cskkenteni szeretnnk, illetve a kivtelkezelst a C hvsi sorrend-
jvel s a hibakeresk szabvnyos eljrsaival is ssze szeretnnk egyeztetni, ha nem is
lehetetlen, de nehz feladatra vllalkozunk de emlkezznk arra, hogy a kivtelek hasz-
nlatnak alternatvi sincsenek ingyen. Nem szokatlan olyan hagyomnyos rendszerekkel
tallkozni, amelyeknl a kd felt a hibakeressre szntk.
14. Kivtelkezels 501
Vegynk egy egyszer f() fggvnyt, amelynek ltszlag semmi kze nincs a kivtelkezelshez:
void g(int);
void f()
{
string s;
// ...
g(1);
g(2);
}
m g() kivtelt vlthat ki, gy f()-nek tartalmaznia kell a kivtel fellpte esetn s-et megsem-
mist kdot. Ha g() nem vltott volna ki kivtelt, valamilyen ms mdon kellett volna a hi-
bt jeleznie. gy a fentivel sszehasonlthat hagyomnyos kd, amely kivtelek helyett
hibkat kezel, nem a fenti egyszer kd lenne, hanem valami ilyesmi:
bool g(int);
bool f()
{
string s;
// ...
if (g(1))
if (g(2))
return true;
else
return false;
else
return false;
}
A programozk szoks szerint persze nem kezelik ennyire mdszeresen a hibkat, s az
nem is mindig ltfontossg. De ha gondos s mdszeres hibakezels szksges, akkor
jobb azt a szmtgpre, vagyis a kivtelkezel rendszerre hagyni.
A kivtelek specifikcija (14.6) nagyon hasznos lehet a fordt ltal ltrehozott kd jav-
tsra. Ha az albbi mdon kijelentettk volna, hogy g() nem vlt ki kivtelt, akkor az f()
szmra ltrehozott kdon javthattunk volna:
void g(int) throw();
rdemes megjegyezni, hogy a hagyomnyos C fggvnyek nem vltanak ki kivtelt, gy a leg-
tbb programban az sszes C fggvny az res throw() kivtel-meghatrozssal adhat meg.
Az adott nyelvi vltozat tudhatja, hogy a C-nek csak nhny standard knyvtrbeli fggvnye
vlt ki kivtelt, pldul az atexit() s a qsort(), s ennek ismeretben jobb kdot kszthet.
Absztrakcis mdszerek 502
Mieltt egy C fggvnyt elltnnk az res kivtel-meghatrozssal (a throw()-val), gon-
doljuk meg, nem vlthat-e ki kivtelt. Pldul trhattk, hogy a new opertort hasznlja,
amely viszont bad_alloc-ot vlthat ki vagy olyan C++-knyvtrat hvhat meg, amely ugyan-
ezt teheti.
14.9. A hibakezels egyb mdjai
A kivtelkezel eljrs clja, hogy lehetv tegye egy programrsz szmra, hogy a prog-
ram ms rszeit rtestse egy kivteles krlmny szlelsrl. A feltevs az, hogy a kt rsz
fggetlenl kszlt s a kivtelt kezel rsz tud valami rtelmeset kezdeni a hibval.
A kivtelkezelk hatkony hasznlathoz tfog stratgira van szksgnk. Vagyis a prog-
ram klnbz rszeinek egyet kell rtenik abban, hogyan hasznljk a kivteleket s hol
kezelik a hibkat. A kivtelkezel eljrs lnyegnl fogva nem loklis, gy alapvet jelen-
tsg, hogy tfog stratgia rvnyesljn. Ebbl kvetkezik, hogy a hibakezels mdj-
ra legjobb a rendszer tervezsnek legkorbbi szakaszban gondolni, s hogy az alkalma-
zott mdszernek (a teljes program sszetettsghez kpest) egyszernek s pontosan meg-
hatrozottnak kell lennie. Egy, a lnyegnl fogva annyira knyes terleten, mint a hibake-
zels, egy bonyolult mdszerhez nem tudnnk kvetkezetesen alkalmazkodni.
Elszr is fel kell adni azt a tvhitet, hogy az sszes hibt egyetlen eljrssal kezelhetjk; ez
csak bonyoltan a helyzetet. A sikeres hibatr rendszerek tbbszintek. Mindegyik szint
annyi hibval birkzik meg, amennyivel csak tud, anlkl, hogy tlsgosan megszenvedne
vele, a maradk kezelst viszont a magasabb szintekre hagyja. A terminate() ezt a kezel-
si mdot azzal tmogatja, hogy meneklsi utat hagy arra az esetre, ha maga a kivtelkeze-
l rendszer srlne vagy nem tkletes hasznlata miatt maradnnak nem elkapott kivte-
lek. Az unexpected() clja ugyangy az, hogy meneklsi utat hagyjon arra az esetre, ha
a kivtelek specifikcijra pl hibakezel rendszer mgsem jelentene a kivtelek sz-
mra thatolhatatlan falat.
Nem minden fggvny kell, hogy tzfalknt viselkedjen. A legtbb rendszerben nem le-
het minden fggvnyt gy megrni, hogy vagy teljes sikerrel jrjon vagy egy pontosan
meghatrozott mdon legyen sikertelen. Hogy ez mirt nem lehetsges, az programrl
programra s programozrl programozra vltozik. Nagyobb programok esetben a k-
vetkez okokat sorolhatjuk fel:
14. Kivtelkezels 503
1. Tl sok munka kellene ennek a megbzhatsgnak olyan biztostshoz, hogy
kvetkezetesen mindenhol rvnyesljn.
2. A szksges trterlet s futsi id nvekedse nagyobb lenne az elfogadhatnl
(mert rendszeresen ugyanazon hibk pldul rvnytelen paramterek ism-
telt ellenrzsre kerl sor).
3. Ms nyelven rt fggvnyek nem alkalmazkodnnak a szablyokhoz.
4. Ez a pusztn helyi rtelemben vett megbzhatsg olyan bonyolultsghoz ve-
zetne, amely vgl alsn a teljes rendszer megbzhatsgt.
Ugyanakkor egy program olyan klnll rszrendszerekre bontsa, amelyek vagy teljes si-
kerrel jrnak, vagy meghatrozott mdon lesznek sikertelenek, alapvet, megtehet s gaz-
dasgos. Egy fbb knyvtrat, rszrendszert vagy kulcsfontossg fggvnyt gy kell meg-
tervezni. A kivteleket ilyen knyvtrak vagy rszrendszerek felletei szmra clszer
meghatrozni.
Rendszerint nem adatik meg az a luxus, hogy egy rendszer sszes kdjt a nullrl kszt-
hessk el. Ezrt egy ltalnos, minden programrszre kiterjed hibakezelsi stratgia meg-
alkotsakor figyelembe kell vennnk az egyes, a minktl eltr mdszert alkalmaz prog-
ramrszleteket is. Ehhez pedig tekintetbe kell vennnk olyan krdseket, mint hogy a prog-
ramrszlet hogyan kezeli az erforrsokat, vagy milyen llapotba kerl egy hibja utn
a rendszer. Az a cl, hogy a programrszlet ltszlag akkor is az ltalnos hibakezelsi md-
szer szerint kezelje a hibkat, ha valjban eltr bels eljrst alkalmaz.
Alkalmasint szksges lehet az egyik stlus hibajelzsrl a msikra ttrni. Pldul egy C
knyvtri fggvny meghvsa utn ellenrizhetjk az errno-t s kivtelt vlthatunk ki,
vagy fordtva, elkaphatunk egy kivtelt s az errno-t bellthatjuk, mieltt a C++-knyv-
trbl visszatrnk egy C programba:
void callC() throw(C_blewit)
{
errno = 0;
c_function();
if (errno) {
// takarts (ha lehetsges s szksges)
throw C_blewit(errno);
}
}
extern "C" void call_from_C() throw()
{
Absztrakcis mdszerek 504
try {
c_plus_plus_function();
}
catch (...) {
// takarts (ha lehetsges s szksges)
errno = E_CPLPLFCTBLEWIT;
}
}
Ilyenkor fontos, hogy kvetkezetesen jrjunk el, hogy a hibajelent stlusok talaktsa
teljes legyen.
A hibakezels a lehetsgekhez mrten hierarchikus legyen. Ha egy fggvny futsi ide-
j hibt szlel, ne krjen segtsget vagy erforrst a hvjtl. Az ilyen krsek az egyms-
tl fgg elemek kztt krbe-krbe jr vgtelen ciklusokat okoznak, ami a programot t-
tekinthetetlenn teszi, a vgtelen ciklusok lehetsgvel pedig a hibkat kezel kdba egy
nem kvnatos lehetsget pt.
Alkalmazzunk olyan egyszerst mdszereket, mint a kezdeti rtkads az erforrs meg-
szerzsvel, illetve olyan egyszerst feltevseket, mint a kivtelek hibkat jelentenek.
Ezzel a hibakezel kdot szablyosabb tehetjk. Lsd mg a 24.3.7.1 pontot, ahol elma-
gyarzzuk, hogyan lehet llapotbiztostkat (invarinsokat) s feltevseket hasznlni, hogy
a kivtelek kivltsa szablyosabb legyen.
14.10. Szabvnyos kivtelek
A kvetkez tblzat a szabvnyos kivteleket s az azokat kivlt fggvnyeket, operto-
rokat, ltalnos eszkzket mutatja be:
14. Kivtelkezels 505
Szabvnyos (a nyelvbe beptett) kivtelek
Nv Kivlt Hivatkozs Fejllomny
bad_alloc new 6.2.6.2, 19.4.5 <new>
bad_cast dynamic_cast 15.4.1.1 <typeinfo>
bad_typeid typeid 15.4.4 <typeinfo>
bad_exception kivtel_specifikci 14.6.3 <exception>
A knyvtri kivtelek a standard knyvtr exception nev az <exception> fejllomnyban
megadott kivtelosztlybl kiindul osztlyhierarchia tagjai:
class exception {
public:
exception() throw();
exception(const exception&) throw();
exception& operator=(const exception&) throw();
virtual ~exception() throw();
virtual const char* what() const throw();
private:
// ...
};
A hierarchia gy nz ki:
Absztrakcis mdszerek 506
Szabvnyos (a standard knyvtr ltal kivltott) kivtelek
Nv Ki dobja Hivatkozs Fejllomny
out_of_range at() 16.3.3,20.3.3 <stdexcept>
bitset<>::operator[ ]() 17.5.3 <stdexcept>
invalid_argument bitset konstruktor 17.5.3.1 <stdexcept>
overflow_error bitset<>::to_ulong() 17.5.3.3 <stdexcept>
ios_base::failure ios_base::clear() 21.3.6 <ios>
exception
runtime_error logic_error
lenght_error
domain_error
out_of_range
invalid_argument
ios_base::failure
range_error
bad_alloc
bad_exception
bad_cast
bad_typeid
overflow_error
underflow_error
Ez meglehetsen krlmnyesnek tnik ahhoz kpest, hogy nyolc szabvnyos kivtelt lel
fel. Oka, hogy a hierarchia megprbl a standard knyvtrban meghatrozott kivteleken
kvli kivtelek szmra is hasznlhat keretrendszert adni. A logikai hibk (logic error)
azok, amelyeket elvileg a program indulsa eltt, fggvny- vagy konstruktorparamterek
ellenrzsvel el lehetne kapni. A tbbi futsi idej hiba (run-time error). Nmelyek ezt az
sszes hiba s kivtel szmra hasznos keretrendszernek ltjk n nem.
A standard knyvtrbeli kivtelek az exception ltal meghatrozott fggvnyeket nem b-
vtik jakkal, csak megfelelen definiljk a megkvnt virtulis fggvnyeket. gy az alb-
bit rhatjuk:
void f()
try {
// a standard knyvtr hasznlata
}
catch (exception& e) {
cout << "standard knyvtri kivtel " << e.what() << '\n'; // taln
// ...
}
catch (...) {
cout << "msik kivtel\n";
// ...
}
A standard knyvtrbeli kivtelek az exception-bl szrmaznak, a tbbi viszont nem feltt-
lenl, gy hiba lenne az sszes kivtelt az exception elkapsval megprblni lekezelni. Ha-
sonlan hiba lenne felttelezni, hogy az sszes, az exception-bl szrmaz kivtel standard
knyvtrbeli kivtel, a felhasznlk ugyanis hozzadhatjk sajt kivteleiket az exception
hierarchihoz.
Jegyezzk meg, hogy az expection-mveletek maguk nem vltanak ki kivtelt. Ebbl k-
vetkezen egy standard knyvtrbeli kivtel kivltsa nem vlt ki bad_alloc kivtelt. A ki-
vtelkezel rendszer fenntart a sajt cljaira egy kis memrit (pldul a veremben), hogy
ott trolhassa a kivteleket. Termszetesen rhatunk olyan kdot, amely a rendszer sszes
memrijt elfogyasztja s gy hibt knyszert ki.
me egy fggvny, amit ha meghvunk, kiprblja, hogy a fggvnyhvsnl vagy a kivtel-
kezelsnl fogy-e ki elszr a memria:
void perverted()
{
try {
throw exception(); // ismtld kivtel
}
14. Kivtelkezels 507
catch (exception& e) {
perverted(); // ismtld fggvnyhvs
cout << e.what();
}
}
A kimeneti utasts egyedl azt a clt szolglja, hogy megakadlyozza a fordtprogramot
az e nev kivtel ltal felhasznlt memria jrahasznostsban.
14.11. Tancsok
[1] Hibakezelsre hasznljunk kivteleket. 14.1, 14.5, 14.9.
[2] Ne hasznljunk kivteleket, ha a helyi vezrlsi szerkezetek elgsgesek. 14.1.
[3] Az erforrsok kezelsre a kezdeti rtkads az erforrs megszerzsvel
mdszert hasznljuk. 14.4.
[4] Nem minden programnak kell kivtelbiztosnak lennie. 14.4.3.
[5] Az invarinsok rvnyessgnek megrzsre hasznljuk a kezdeti rtkads
az erforrs megszerzsvel mdszert s a kivtelkezelket. 14.3.2.
[6] Minl kevesebb try blokkot hasznljunk. Meghatrozott kezelkd helyett
a kezdeti rtkads az erforrs megszerzsvel mdszert hasznljuk. 14.4.
[7] Nem minden fggvnynek kell minden lehetsges hibt kezelnie. 14.9.
[8] A konstruktorhibk jelzsre vltsunk ki kivtelt. 14.9.
[9] Ha egy rtkads vlt ki kivtelt, eltte gondoskodjon paramtereinek kvetke-
zetes llapotba hozsrl. 14.4.6.2.
[10] A destruktorokban kerljk a kivtelek kivltst. 14.4.7.
[11] A main() fggvny kapja el s jelentse az sszes hibt. 14.7.
[12] Klntsk el a kznsges kdot a hibakezelstl. 14.4.5 s 14.5.
[13] Gondoskodjunk arrl, hogy egy konstruktorban minden lefoglalt erforrst fel-
szabadtsunk, ha a konstruktor kivtelt vlt ki. 14.4.
[14] Az erforrsok kezelse hierarchikus legyen. 14.4.
[15] A fbb felletekben hatrozzuk meg az engedlyezett kivteleket. 14.9.
[16] Legynk rsen, nehogy a new ltal lefoglalt s a kivtelek fellpte esetn fel
nem szabadtott memria elszivrogjon. 14.4.1, 14.4.2, 14.4.4.
[17] A fggvnyekrl ttelezzk fel, hogy minden kivtelt kivlthatnak, amit megen-
gedtek nekik. 14.6.
[18] Ne ttelezzk fel, hogy minden kivtel az exception osztly leszrmazottja.
14.10.
[19] Egy knyvtr ne fejezze be egyoldalan a program futst. Ehelyett vltson ki
kivtelt s hadd dntsn a hv. 14.1.
Absztrakcis mdszerek 508
[20] Egy knyvtr ne bocssson ki a vgfelhasznlnak sznt diagnosztikai zenete-
ket. Ehelyett vltson ki kivtelt s hadd dntsn a hv. 14.1.
[21] A tervezs sorn minl hamarabb alaktsuk ki a hibakezelsi stratgit. 14.9
14.12. Gyakorlatok
1. (*2) ltalnostsuk a 14.6.3.1 pontbeli STC osztlyt sablonn, amely a kezdeti
rtkads az erforrs megszerzsvel mdszert hasznlja klnfle tpus
fggvnyek trolsra s visszalltsra.
2. (*3) Egsztsk ki a 11.11 pontbeli Ptr_to_T osztlyt mint egy olyan sablont,
amely kivtelekkel jelzi a futsi idej hibkat.
3. (*3) rjunk fggvnyt, amely egy binris fa cscsaiban keres egy char* tpus
mez alapjn. A hello-t tartalmaz cscs megtallsakor a find(hello) a cscsra
hivatkoz mutatval trjen vissza; a keress sikertelensgt kivtellel jelezzk.
4. (*3.) Hozzunk ltre egy Int osztlyt, amely pontosan gy viselkedik, mint az
elemi int tpus, csak tl- s alulcsorduls esetn kivtelt vlt ki.
5. (*2.5) Vegyk a felhasznlt opercis rendszer C felletnek llomnyok meg-
nyitsra, lezrsra, olvassra, rsra val alapvet mveleteit, s rjunk hoz-
zjuk egyenrtk C++ fggvnyeket, amelyek a megfelel C fggvnyeket hv-
jk meg, de hiba esetn kivtelt vltanak ki.
6. (*2.5) rjunk egy teljes Vector sablont Range s Size kivtelekkel.
7. (*1) rjunk egy ciklust, ami kiszmtja a 14.12[6]-beli Vector sszegt a Vector
mretnek lekrdezse nlkl. Ez mirt nem j tlet?
8. (*2.5) Gondoljuk meg, mi lenne, ha egy Exception osztlyt hasznlnnk az
sszes kivtelknt hasznlt osztly seknt (bzisosztlyaknt). Hogyan nzne
ki? Hogyan lehetne hasznlni? Mire lenne j? Milyen htrnyok szrmaznnak
abbl a megktsbl, hogy ezt az osztlyt kell hasznlni?
9. (*1) Adott a kvetkez fggvny:
int main() { /* ... */ }
Vltoztassuk meg gy, hogy minden kivtelt elkapjon, hibazenett alaktsa
azokat, majd meghvja az abort()-ot. Vigyzat: a 14.9 pontbeli call_from_C()
fggvny nem teljesen kezel minden esetet.
10. (*2) rjunk visszahvsok (callback) megvalstsra alkalmas osztlyt vagy
sablont.
11. (*2.5) rjunk egy Lock osztlyt, amely valamely rendszer szmra a konkurens
hozzfrst tmogatja.
14. Kivtelkezels 509
Osztlyhierarchik
Az absztrakci szelektv tudatlansg.
(Andrew Koenig)
Tbbszrs rklds A tbbrtelmsg feloldsa rklds s using deklarci-
k Ismtld bzisosztlyok Virtulis bzisosztlyok A tbbszrs rklds haszn-
lata Hozzfrs-szablyozs Vdett tagok Bzisosztlyok elrse Futsi idej tpus-
informci dynamic_cast Statikus s dinamikus konverzi typeid Kiterjesztett tpus-
informci A futsi idej tpusinformci helyes s helytelen hasznlata Tagra hivatko-
z mutatk Szabad tr Virtulis konstruktorok Tancsok Gyakorlatok
15.1. Bevezets s ttekints
Ez a fejezet a szrmaztatott osztlyoknak s a virtulis fggvnyeknek a ms nyelvi elemek-
kel, pldul a hozzfrs-szablyozssal, a nvfeloldssal, a szabad tr kezelsvel,
a konstruktorokkal, a mutatkkal s a tpuskonverzikkal val klcsnhatst trgyalja. t
f rszbl ll:
15.2 Tbbszrs rklds
15.3 Hozzfrs-szablyozs
15
15.4 Futsi idej tpusazonosts
15.5 Tagokra hivatkoz mutatk
15.6 A szabad tr hasznlata
Az osztlyokat ltalban bzisosztlyok hljbl hozzuk ltre. Mivel a legtbb ilyen h-
l hagyomnyosan fa szerkezet, az osztlyokbl ll hlkat vagy osztlyhlkat (class
lattice) gyakran nevezik osztlyhierarchinak (class hierarchy) is. Az osztlyokat clszer
gy megtervezni, hogy a felhasznlknak ne kelljen indokolatlan mrtkben azzal trdni-
k, hogy egy osztly milyen mdon pl fel ms osztlyokbl. gy pldul a virtulis fgg-
vnyek meghvsi mdja biztostja, hogy ha egy f() fggvnyt meghvunk egy objektumra,
akkor mindig ugyanaz a fggvny hajtdik vgre, fggetlenl attl, hogy a hierarchia me-
lyik osztlya deklarlta a fggvnyt a meghvshoz. Ez a fejezet az osztlyhlk sszellt-
snak s az osztlytagok elrsnek mdjairl, illetve az osztlyhlk fordtsi s futsi ide-
j bejrsnak eszkzeirl szl.
15.2. Tbbszrs rklds
Ahogy a 2.5.4 s 12.3 pontokban lttuk, egy osztlynak tbb kzvetlen bzisosztlya is le-
het, azaz tbb osztlyt is megadhatunk a : jel utn az osztly deklarcijban. Vegynk egy
szimulcis programot, ahol a prhuzamos tevkenysgeket a Task (Feladat) osztllyal, az
adatgyjtst s -megjelentst pedig a Displayed (Megjelents) osztllyal brzoljuk. Ekkor
olyan szimullt egyedeket hatrozhatunk meg, mint a Satellite (Mhold):
class Satellite : public Task, public Displayed {
// ...
};
Tbb kzvetlen bzisosztly hasznlatt szoks szerint tbbszrs rkldsnek (multiple
inheritance) nevezik. Az egyszeres rkldsnl (single inheritance) csak egy kzvetlen
bzisosztly van.
A Satellite-okra sajt mveleteiken kvl a Task-ok s Displayed-ek mveleteinek unija is
alkalmazhat:
void f(Satellite& s)
{
s.draw(); // Displayed::draw()
s.delay(10); // Task::delay()
s.transmit(); // Satellite::transmit()
}
Absztrakcis mdszerek 512
Hasonlan, ha egy fggvny Task vagy Displayed paramtert vr, akkor adhatunk neki egy
Satellite-ot is:
void highlight(Displayed*);
void suspend(Task*);
void g(Satellite* p)
{
highlight(p); // mutat tadsa a Satellite Displayed rszre
suspend(p); // mutat tadsa a Satellite Task rszre
}
A program ltrehozsa nyilvn valamilyen egyszer eljrs alkalmazst kveteli meg a for-
dtprogramtl, hogy a Task-ot vr fggvnyek ms rszt lssk egy Satellite-nak, mint
a Displayed-et vrk. A virtulis fggvnyek a megszokott mdon mkdnek:
class Task {
// ...
virtual void pending() = 0;
};
class Displayed {
// ...
virtual void draw() = 0;
};
class Satellite : public Task, public Displayed {
// ...
void pending(); // fellrja a Task::pending() fggvnyt
void draw(); // fellrja a Displayed::draw() fggvnyt
};
Ez biztostja, hogy Satellite::draw(), illetve Satellite::pending() fog meghvdni, ha egy
Satellite-ot Displayed-knt, illetve Task-knt kezelnk.
Jegyezzk meg, hogy ha csak egyszeres rkldst hasznlhatnnk, akkor ez a krlmny
korltozn a programozt a Satellite, Displayed s Task osztlyok megvalstsnak megv-
lasztsban. Egy Satellite vagy Task, vagy Displayed lehetne, de nem mindkett (hacsak
a Task nem a Displayed-bl szrmazik vagy fordtva). Ezen lehetsgek mindegyike csk-
kenti a rugalmassgot.
Mi szksge lehet brkinek egy Satellite osztlyra? Nos, brmilyen meglep, a Satellite pl-
dt a valsgbl mertettk. Volt s taln mg mindig van egy olyan program, amely
a tbbszrs rklds lersra itt hasznlt minta szerint plt fel. A program mholdakat,
15. Osztlyhierarchik 513
fldi llomsokat stb. magba foglal hrkzlsi rendszerek szerkezetnek tanulmnyoz-
sra szolglt. Egy ilyen szimulci birtokban meg tudjuk vlaszolni a forgalmi adatokra vo-
natkoz krdseket, meg tudjuk hatrozni, mi trtnik, ha egy fldi llomst vihar akad-
lyoz, mholdas s fldi kapcsolatok elnyeit-htrnyait tudjuk elemezni stb. A klnbz
krlmnyek utnzsa szmos megjelentsi s hibakeressi mveletet ignyel, s szksg
van a Satellite-okhoz hasonl objektumok, illetve rszegysgeik llapotnak trolsra is,
az elemzs, illetve a hibakeress s -elhrts cljbl.
15.2.1. A tbbrtelmsg feloldsa
Kt bzisosztlynak lehetnek azonos nev tagfggvnyei is:
class Task {
// ...
virtual debug_info* get_debug();
};
class Displayed {
// ...
virtual debug_info* get_debug();
};
Ha egy Satellite-ot hasznlunk, akkor ezeket a fggvnyeket egyrtelmen kell meg-
neveznnk:
void f(Satellite* sp)
{
debug_info* dip = sp->get_debug(); // hiba: tbbrtelm
dip = sp->Task::get_debug(); // rendben
dip = sp->Displayed::get_debug(); // rendben
}
Az explicit megnevezs azonban zavar, ezrt ltalban legjobb elkerlni az ilyen problm-
kat. Ennek legegyszerbb mdja, ha a szrmaztatott osztlyban ksztnk egy j fggvnyt:
class Satellite : public Task, public Displayed {
// ...
debug_info* get_debug() // fellrja a Task::get_debug() s
// Displayed::get_debug() fggvnyeket
{
Absztrakcis mdszerek 514
debug_info* dip1 = Task::get_debug();
debug_info* dip2 = Displayed::get_debug();
return dip1->merge(dip2);
}
};
Ezltal a Satellite bzisosztlyaira vonatkoz informcik felhasznlst helyhez ktttk.
Mivel a Satellite::get_debug() elfedi mindkt bzisosztlynak get_debug() fggvnyt,
a Satellite::get_debug() hvdik meg, valahnyszor get_debug()-ot hvunk meg egy Satellite
objektumra.
A Telstar::draw minstett nv a Telstar-ban vagy valamelyik bzisosztlyban megadott
draw-ra vonatkozhat:
class Telstar : public Satellite {
// ...
void draw()
{
draw(); // hopp!: rekurzv hvs
Satellite::draw(); // megtallja a Displayed::draw-t
Displayed::draw();
Satellite::Displayed::draw(); // felesleges ktszeri minsts
}
};
Vagyis ha a Satellite::draw nem a Satellite osztlyban bevezetett draw-t jelenti, akkor a for-
dtprogram vgignzi a bzisosztlyokat, vagyis Task::draw-t s Displayed::draw-t keres.
Ha csak egyet tall, akkor azt fogja hasznlni, ha tbbet vagy egyet sem, a Satellite::draw
ismeretlen vagy tbbrtelm lesz.
15.2.2. rklds s using deklarcik
A tlterhels feloldsra nem kerl sor klnbz osztlyok hatkrn keresztl (7.4),
a klnbz bzisosztlyok fggvnyei kztti tbbrtelmsgek feloldsa pedig nem tr-
tnik meg a paramtertpus alapjn.
Egymssal alapveten nem rokon osztlyok egyestsekor, pldul a Task s Displayed osz-
tlyok Satellite-t val sszegyrsnl az elnevezsekben megmutatkoz hasonlsg l-
talban nem jelent kzs clt. Amikor az ilyen nevek tkznek, ez gyakran meglepetsknt
ri a felhasznlt:
15. Osztlyhierarchik 515
class Task {
// ...
void debug(double p); // informci kirsa csak akkor, ha a priorits
// alacsonyabb p-nl
};
class Displayed {
// ...
void debug(int v); // minl nagyobb 'v,' annl tbb hibakeressi informci rdik ki
};
class Satellite : public Task, public Displayed {
// ...
};
void g(Satellite* p)
{
p->debug(1); // hiba, tbbrtelm: Displayed::debug(int) vagy
// Task::debug(double) ?
p->Task::debug(1); // rendben
p->Displayed::debug(1); // rendben
}
De mi van akkor, ha a klnbz bzisosztlyokban tudatos tervezsi dnts kvetkezt-
ben szerepelnek azonos nevek, s a felhasznl szmra kvnatos lenne a paramtertpus
alapjn vlasztani kzlk? Ebben az esetben a fggvnyeket a using deklarcival (8.2.2)
hozhatjuk kzs hatkrbe:
class A {
public:
int f(int);
char f(char);
// ...
};
class B {
public:
double f(double);
// ...
};
class AB: public A, public B {
public:
using A::f;
using B::f;
char f(char); // elfedi A::f(char)-t
AB f(AB);
};
Absztrakcis mdszerek 516
void g(AB& ab)
{
ab.f(1); // A::f(int)
ab.f('a'); // AB::f(char)
ab.f(2.0); // B::f(double)
ab.f(ab); // AB::f(AB)
}
A using deklarcik lehetv teszik, hogy a bzis- s szrmaztatott osztlyok fggvnyei-
bl tlterhelt fggvnyek halmazt lltsuk el. A szrmaztatott osztlyban megadott fgg-
vnyek elfedik (hide) a bzisosztly fggvnyeit, amelyek egybknt elrhetek lennnek.
A bzisosztly virtulis fggvnyei ugyangy fellrhatk (override), mint egybknt
(15.2.3.1).
Egy osztlydeklarci using deklarcijnak (8.2.2) egy bzisosztly tagjra kell vonatkoz-
nia. Egy osztly tagjra vonatkoz using deklarci nem szerepelhet az osztlyon, annak
szrmaztatott osztlyn, illetve annak tagfggvnyein kvl, a using direktvk (8.2.3) pe-
dig nem szerepelhetnek egy osztly definicijban s nem vonatkozhatnak osztlyra.
A using deklarcik nem szolglhatnak kiegszt informci elrsre sem, csak az egyb-
knt is hozzfrhet informcik knyelmesebb hasznlatt teszik lehetv (15.3.2.2).
15.2.3. Ismtld bzisosztlyok
Azltal, hogy tbb bzisosztly lehet, elfordulhat, hogy egy osztly ktszer fordul el a
bzisosztlyok kztt. Pldul ha mind a Task, mind a Displayed a Link (Kapcsolat) osztly-
bl szrmazott volna, a Satellite-oknak kt Link-je lenne:
struct Link {
Link* next;
};
class Task : public Link {
// a Link-et a Task-ok listjhoz (az temez listhoz) hasznljuk
// ...
};
class Displayed : public Link {
// a Link-et a Displayed objektumok listjhoz (a megjelentsi listhoz) hasznljuk
// ...
};
15. Osztlyhierarchik 517
Ez nem gond. Kt kln Link objektum szolgl a listk brzolsra s a kt lista nem za-
varja egymst. Termszetesen a Link osztly tagjaira nem hivatkozhatunk a ktrtelmsg
veszlye nlkl (15.2.3.1). Egy Satellite objektumot gy rajzolhatunk le:
Ha a kzs bzisosztlyt nem szabad kt kln objektummal brzolni, virtulis bzisosz-
tlyokat (15.2.4) alkalmazhatunk.
A Link-hez hasonlan tbbszr szerepl bzisosztlyok olyan elemek, amelyeket nem sza-
bad a kzvetlenl rkl osztlyon kvl hasznlni. Ha egy ilyen osztlyra olyan pontrl
kell hivatkozni, ahonnt annak tbb pldnya is lthat, a tbbrtelmsg elkerlse rde-
kben a hivatkozst minsteni kell:
void mess_with_links(Satellite* p)
{
p->next = 0; // hiba: tbbrtelm (melyik Link?)
p->Link::next = 0; // hiba: tbbrtelm (melyik Link?)
p->Task::Link::next = 0; // rendben
p->Displayed::Link::next = 0; // rendben
// ...
}
Ez pontosan ugyanaz az eljrs, mint amit a tagokra val tbbrtelm hivatkozsok felol-
dsra hasznlunk (15.2.1).
15.2.3.1. Fellrs
A tbbszr szerepl bzisosztlyok valamely virtulis fggvnyt a szrmaztatott osztly
(egyetlen) fggvnye fellrhatja (fellbrlhatja, override). Egy objektumnak sajt magt
egy fjlbl kiolvasni vagy oda visszarni val kpessgt pldul gy brzolhatjuk:
class Storable {
public:
virtual const char* get_file() = 0;
virtual void read() = 0;
Absztrakcis mdszerek 518
Link Link
Task Displayed
Satellite
virtual void write() = 0;
virtual ~Storable() { }
};
Termszetesen tbb felhasznl pthet erre, hogy olyan osztlyokat rjon, amelyek fgget-
lenl vagy egytt hasznlva jobban kidolgozott osztlyokat adnak. Pldul lellthatunk s
jraindthatunk egy szimulcit, ha mentjk az alkotelemeket s ksbb visszalltjuk azo-
kat. Ezt az tletet gy valsthatjuk meg:
class Transmitter : public Storable {
public:
void write();
// ...
};
class Receiver : public Storable {
public:
void write();
// ...
};
class Radio : public Transmitter, public Receiver {
public:
const char* get_file();
void read();
void write();
// ...
};
A fellr fggvny ltalban meghvja a bzisosztlybeli vltozatokat s a szrmaztatott
osztlyra jellemz tennivalkat vgzi el:
void Radio::write()
{
Transmitter::write();
Receiver::write();
// kirja a Radio-ra jellemz adatokat
}
Az ismtld bzisosztlyokrl szrmaztatott osztlyokra val tpuskonverzit a 15.4.2
pont rja le. Arrl, hogyan lehet az egyes write() fggvnyeket a szrmaztatott osztlyok k-
ln fggvnyeivel fellrni, a 25.6 pont szl.
15. Osztlyhierarchik 519
15.2.4. Virtulis bzisosztlyok
Az elz pont Radio pldja azrt mkdik, mert a Storable osztlyt biztonsgosan, knyel-
mesen s hatkonyan lehet tbbszrzni. Ez azonban az olyan osztlyok esetben rendsze-
rint nem igaz, amelyek j ptkvei ms osztlyoknak. A Storable osztlyt pldul gy is
meghatrozhatnnk, mint ami tartalmazza az objektum mentsre hasznlt fjl nevt:
class Storable {
public:
Storable(const char* s);
virtual void read() = 0;
virtual void write() = 0;
virtual ~Storable();
private:
const char* store;
Storable(const Storable&);
Storable& operator=(const Storable&);
};
A Storable ezen ltszlag csekly mdostsa utn meg kell vltoztatnunk a Radio szerke-
zett is. Az objektum sszes rsze a Storable azonos pldnyn kell, hogy osztozzk; kln-
ben szksgtelenl nehz feladat lenne az objektum tbbszri trolsnak megakadlyoz-
sa. A virtulis bzisosztlyok (virtual base class) ezt a megosztst segtik. A szrmaztatott
osztly minden virtulis bzisosztlyt ugyanaz a (megosztott) objektum brzolja:
class Transmitter : public virtual Storable {
public:
void write();
// ...
};
class Receiver : public virtual Storable {
public:
void write();
// ...
};
class Radio : public Transmitter, public Receiver {
public:
void write();
// ...
};
Absztrakcis mdszerek 520
brval:
Hasonltsuk ssze ezt az brt a Satellite objektum 15.2.3-beli rajzval, hogy lssuk a k-
lnbsget a kznsges s a virtulis rklds kztt. Az rkldsi grfban egy adott ne-
v osztly minden virtulisknt megadott bzisosztlyt az osztly egyetlen objektuma b-
rzolja, a nem virtulis bzisosztlyokat viszont sajt rszobjektumuk.
15.2.4.1. Virtulis bzisosztlyok programozsa
Amikor a programoz fggvnyeket kszt egy olyan osztly szmra, amelynek virtulis
bzisosztlya van, nem tudhatja, hogy a bzisosztlyt meg kell-e osztani egyb szrmazta-
tott osztlyokkal, ami gondot jelenthet, ha egy szolgltatst gy kell megvalstani, hogy a
bzisosztly egy adott fggvnynek meghvsra pontosan egyszer kerljn sor, pldul
mert a nyelv elrja, hogy egy virtulis bzisosztly konstruktora csak egyszer futhat le.
A virtulis bzisosztly konstruktort a teljes objektum, azaz a legtvolabbi szrmaztatott
osztly konstruktora hvja meg (automatikusan vagy kzvetlenl):
class A { // nincs konstruktor
// ...
};
class B {
public:
B(); // alaprtelmezett konstruktor
// ...
};
class C {
public:
C(int); // nincs alaprtelmezett konstruktor
};
15. Osztlyhierarchik 521
Storable
Receiver Transmitter
Radio
class D : virtual public A, virtual public B, virtual public C
{
D() { /* ... */ } // hiba: C-nek nincs alaprtelmezett konstruktora
D(int i) : C(i) { /* ... */ }; // rendben
// ...
};
A virtulis bzisosztly konstruktora a szrmaztatott osztlyok konstruktora eltt hvdik
meg. Szksg esetn a programoz ezt a mkdst utnozhatja is, ha a virtulis bzisosz-
tly fggvnyt csak a legtvolabbi szrmaztatott osztlybl hvja meg. Tegyk fel, hogy
van egy alapvet Window osztlyunk, amely ki tudja rajzolni tartalmt:
class Window {
// alapkd
virtual void draw();
};
Az ablakokat emellett tbbfle mdon dszthetjk s szolgltatsokkal egszthetjk ki:
class Window_with_border : public virtual Window {
// a szegly kdja
void own_draw(); // a szegly megjelentse
void draw();
};
class Window_with_menu : public virtual Window {
// a men kdja
void own_draw(); // a men megjelentse
void draw();
};
Az own_draw() fggvnyeknek nem kell virtulisaknak lennik, mert egy virtulis draw()
fggvnybl akarjuk meghvni azokat, ami pontosan ismeri az objektum tpust, amelyre
meghvtk.
Ebbl egy mkdkpes Clock osztlyt llthatunk ssze:
class Clock : public Window_with_border, public Window_with_menu {
// az ra kdja
void own_draw(); // az ralap s a mutatk megjelentse
void draw();
};
Absztrakcis mdszerek 522
brval:
A draw() fggvnyeket most mr gy rhatjuk meg az own_draw() fggvnyek felhasznl-
sval, hogy brmelyik draw() meghvsa pontosan egyszer hvja meg a Window::draw()-t,
fggetlenl attl, milyen fajta Window-ra hvtk meg:
void Window_with_border::draw()
{
Window::draw();
own_draw(); // a szegly megjelentse
}
void Window_with_menu::draw()
{
Window::draw();
own_draw(); // a men megjelentse
}
void Clock::draw()
{
Window::draw();
Window_with_border::own_draw();
Window_with_menu::own_draw();
own_draw(); // az ralap s a mutatk megjelentse
}
A virtulis bzisosztlyokrl szrmaztatott osztlyokra val konverzit a 15.4.2 pont rja le.
15.2.5. A tbbszrs rklds hasznlata
A tbbszrs rklds legegyszerbb s legnyilvnvalbb felhasznlsa kt egybknt
egymssal rokonsgban nem ll osztly sszeragasztsa egy harmadik osztly rszeknt.
A Task s Displayed osztlyokbl a 15.2 pontban sszerakott Satellite osztly is ilyen.
15. Osztlyhierarchik 523
Window
Window_with_border Window_with_menu
Clock
A tbbszrs rklds ilyen mdon val hasznlata egyszer, hatkony s fontos de nem
tl rdekes, hiszen alapjban vve csak a tovbbt fggvnyek megrstl kmli meg
a programozt. Az eljrs nem befolysolja szmotteven a program ltalnos szerkezett s
alkalmasint tkzhet azzal a kvnalommal, hogy a megvalsts rszletei maradjanak rejtve.
Egy mdszernek azonban nem kell okosnak lennie ahhoz, hogy hasznos legyen.
A tbbszrs rklds hasznlata absztrakt osztlyok ksztsre mr nagyobb jelents-
g, annyiban, hogy befolysolja a program tervezsnek mdjt. A 12.4.3-beli
BB_ival_slider osztly egy plda erre:
class BB_ival_slider
: public Ival_slider // fellet
, protected BBslider // megvalsts
{
// az 'Ival_slider' s a 'BBslider' ltal ignyelt fggvnyek megvalstsa
// a 'BBslider' szolgltatsainak hasznlata
};
Ebben a pldban a kt bzisosztly logikailag klnbz szerepet jtszik. Az egyik egy
nyilvnos absztrakt osztly, amely a felletet nyjtja, a msik pedig egy vdett (protected)
konkrt osztly, amely a megvalstsrl gondoskodik. A ktfle szerep az osztlyok stlu-
sban s az alkalmazott hozzfrsi szablyokban tkrzdik. A tbbszrs rklds sze-
repe itt lnyegbevg, mert a szrmaztatott osztlynak mind a fellet, mind a megvalsts
virtulis fggvnyeit fell kell rnia.
A tbbszrs rklds lehetv teszi a testvrosztlyok szmra, hogy egyetlen kzs s
jelentette fggs bevezetse nlkl osztozhassanak adatokon. Ez az eset, amikor az gyne-
vezett kr alak rklds (diamond-shaped inheritance) lp fel. (Lsd a Radio (15.2.4)
s a Clock (15.2.4.1) pldkat.) Ha a bzisosztly nem ismtelhet, akkor virtulis (s nem
kznsges) bzisosztlyra van szksg.
Az n vlemnyem az, hogy a kr alak rklsi hl akkor kezelhet a legjobban, ha
vagy a virtulis bzisosztly vagy a belle kzvetlenl szrmaz osztlyok absztraktak. Ve-
gyk pldul jra a 12.4 pont Ival_box osztlyait. Ott vgl az sszes Ival_box osztlyt
absztraktt tettk, hogy kifejezzk szerepket, vagyis hogy kizrlag felletek. Ez lehetv
tette, hogy a lnyegi programkd minden rszt a megfelel megvalst osztlyokba rejt-
sk, s az egyes rszeken val osztozs is csak a megvalsts cljra hasznlt ablakoz
rendszer klasszikus hierarchijban trtnt.
Persze lenne rtelme annak, hogy a Popup_ival_slider-t megvalst osztly nagy rsze k-
zs legyen a sima Ival_slider-t megvalst osztlyval, gy az adatbekr mezk kezelsn
Absztrakcis mdszerek 524
kvl azonosak lennnek. Ekkor az is termszetes lenne, hogy az elll csszka-osztly-
ban elkerljk az Ival_slider objektumok ismtldst. Ehhez az Ival_slider-t virtuliss
tesszk:
class BB_ival_slider : public virtual Ival_slider, protected BBslider { /* ... */ };
class Popup_ival_slider : public virtual Ival_slider { /* ... */ };
class BB_popup_ival_slider
: public virtual Popup_ival_slider, protected BB_ival_slider { /* ... */ };
brval:
Knnyen elkpzelhetjk a Popup_ival_slider-bl szrmaz tovbbi felleteket s az azok-
bl s a BB_popup_ival_slider-bl szrmaz tovbbi megvalst osztlyokat.
Ha az tletet vgigvisszk, akkor a program fellett kpez absztrakt osztlyokbl val min-
den szrmaztatst virtuliss tesznk. Ez valban a leglogikusabb, legltalnosabb, s legru-
galmasabb megoldsnak tnik. Hogy mirt nem tettem ezt, annak egyrszt a hagyomnyok
figyelembe vtele az oka, msrszt a virtulis bzisosztlyok megvalstsnak legnyilvnva-
lbb s leggyakoribb mdszerei annyira hely- s idignyesek, hogy kiterjedt hasznlatuk
egy osztlyon bell nem vonz. Mieltt ez a trignyben s futsi idben mrt kltsg vissza-
riasztana bennnket egy ms szemszgbl vonz szerkezet vlasztstl, gondoljuk meg,
hogy az Ival_box-ot brzol objektum ltalban csak egy mutatt tartalmaz a virtulis
tblra. Ahogy a 15.2.4 pontban lttuk, egy vltozkat nem tartalmaz absztrakt osztly ve-
szly nlkl ismtelhet. gy a virtulis bzisosztly helyett kznsgeset alkalmazhatunk:
class BB_ival_slider : public Ival_slider, protected BBslider { /* ... */ };
class Popup_ival_slider : public Ival_slider { /* ... */ };
class BB_popup_ival_slider
: public Popup_ival_slider, protected BB_ival_slider { /* ... */ };
15. Osztlyhierarchik 525
Ival_slider BBslider
Popup_ival_slider BB_ival_slider
BB_popup_ival_slider
brval:
Ez valsznleg megvalsthat, optimalizlt vltozata az elzekben bemutatott, bevallot-
tan vilgosabb szerkezetnek.
15.2.5.1. A virtulis bzisosztlyok fggvnyeinek fellrsa
A szrmaztatott osztlyok fellrhatjk (override) kzvetlen vagy kzvetett virtulis bzis-
osztlyaik virtulis fggvnyeit. Kt klnbz osztly akr a virtulis bzisosztly kln-
bz fggvnyeit is fellrhatja, gy tbb szrmaztatott osztly jrulhat hozz a virtulis
bzisosztly ltal adott fellet megvalstshoz. A Window osztlynak lehetnek pldul
set_color() s prompt() fggvnyei. Ekkor a Window_with_border fellbrlhatja
a set_color()-t, mint a sznellenrz sma rszt, a Window_with_menu pedig a prompt()-
ot, mint a felhasznli fellet kezelsnek rszt:
class Window {
// ...
virtual void set_color(Color) = 0; // httrszn belltsa
virtual void prompt() = 0;
};
class Window_with_border : public virtual Window {
// ...
void set_color(Color); // httrszn kezelse
};
class Window_with_menu : public virtual Window {
// ...
void prompt(); // felhasznli tevkenysgek kezelse
};
Absztrakcis mdszerek 526
Ival_slider Ival_slider BBslider
Popup_ival_slider BB_ival_slider
BB_popup_ival_slider
class My_window : public Window_with_menu, public Window_with_border {
// ...
};
Mi trtnik, ha klnbz szrmaztatott osztlyok ugyanazt a fggvnyt rjk fell? Ez csak
akkor megengedett, ha az egyik szrmaztatott osztly minden olyan osztly rkse, amely
fellrja a fggvnyt, vagyis egy fggvnynek az sszeset fell kell rnia. A My_window pl-
dul fellrhatja a prompt()-t, hogy a Window_with_menu-belinl jobb vltozatot adjon:
class My_window : public Window_with_menu, public Window_with_border {
// ...
void prompt();// a felhasznli tevkenysgek kezelst nem hagyjuk a bzisosztlyra
};
brval:
Ha kt osztly fellr egy bzisosztlybeli fggvnyt, de a kt fggvny egyike nem rja fell
a msikat, akkor az osztlyhierarchia hibs. Ilyenkor nem lehet virtulis fggvnytblt p-
teni, mert a teljes objektumra vonatkoz fggvnyhvs ktrtelm lenne. Pldul ha
a 15.2.4. pontbeli Radio nem adta volna meg a write() fggvnyt, akkor a Receiver s
Transmitter osztlyokbeli write() deklarcik a Radio-ban hibt okoztak volna. Mint
a Radio esetben is, az ilyen konfliktust a fellr fggvnynek a legtvolabbi szrmaztatott
osztlybl val meghvsval oldhatjuk meg.
Az olyan osztlyokat, amelyek egy virtulis bzisosztly nmelyik (de nem mindegyik)
fggvnynek megvalstst tartalmazzk, gyakran mixin-nek nevezik.
15. Osztlyhierarchik 527
Window { set_color(), prompt() }
Window_with_border { set_color() } Window_with_menu { prompt() }
My_window { prompt() }
15.3. Hozzfrs-szablyozs
Egy osztlytag lehet privt (private), vdett (protected) vagy nyilvnos (public):
Ha privt, a nevt csak a tagfggvnyekben s a deklarl osztly bartaiban
(friend-jeiben) lehet felhasznlni.
Ha vdett, a nevt csak a deklarl osztly s bartai tagfggvnyeiben, vala-
mint az osztlybl szrmaztatott osztlyok s bartaik tagfggvnyeiben lehet
felhasznlni.
Ha nyilvnos, a nevt mindenhol fel lehet hasznlni.
Ez azt a nzetet tkrzi, hogy egy osztlyt hromfle fggvny rhet el: az osztlyt megva-
lst fggvnyek (azaz a bartok s a tagok), egy szrmaztatott osztlyt megvalst fgg-
vnyek (azaz a szrmaztatott osztlyok bartai s a tagok), s az egyb fggvnyek. Ezt gy
brzolhatjuk:
A hozzfrsi szablyok egynteten vonatkoznak a nevekre. Hogy a nv mit jell, az
rdektelen a hozzfrs szempontjbl. Ez azt jelenti, hogy ugyangy lehetnek privt tag-
fggvnyek, tpusok, llandk stb., mint privt adattagok. Pldul egy hatkony nem tola-
kod (non-intrusive, 16.2.1) listaosztlynak valsznleg szksge van az elemeket nyil-
vntart adatszerkezetekre. Az ilyen informci legjobb, ha privt:
Absztrakcis mdszerek 528
ltalnos felhasznlk
szrmaztatott osztly tagfggvnyei s bartai
sajt tagfggvnyek s bartok
nyilvnos:
vdett:
privt:
template<class T> class List {
private:
struct Link { T val; Link* next; };
struct Chunk {
enum { chunk_size = 15 };
Link v[chunk_size];
Chunk* next;
};
Chunk* allocated;
Link* free;
Link* get_free();
Link* head;
public:
class Underflow { }; // kivtelosztly
void insert(T);
T get();
// ...
};
template<class T> void List<T>::insert(T val)
{
Link* lnk = get_free();
lnk->val = val;
lnk->next = head;
head = lnk;
}
template<class T> List<T>::Link* List<T>::get_free()
{
if (free == 0) {
// j Chunk lefoglalsa s Link-jeinek a szabad listra helyezse
}
Link* p = free;
free = free->next;
return p;
}
template<class T> T List<T>::get()
{
if (head == 0) throw Underflow();
Link* p= head;
head = p->next;
p->next = free;
free = p;
return p->val;
}
15. Osztlyhierarchik 529
A List<T> hatkrbe azltal lpnk be, hogy a tagfggvnyben List<T>::-t runk. Mivel
a get_free() visszatrsi rtkt elbb emltjk, mint a List<T>::get_free() nevet, a Link rvi-
dts helyett a teljes List<T>::Link nevet kell hasznlnunk. A nem tag fggvnyeknek a ba-
rt (friend) fggvnyek kivtelvel nincs ilyen hozzfrsk:
void would_be_meddler(List<T>* p)
{
List<T>::Link* q = 0; // hiba: List<T>::Link privt
// ...
q = p->free; // hiba: List<T>::free privt
// ...
if (List<T>::Chunk::chunk_size > 31) { // hiba: List<T>::Chunk::chunk_size privt
// ...
}
}
Az osztlyok (class) tagjai alaprtelmezs szerint privtok (private), a struktrk (struct) tag-
jai nyilvnosak (public, 10.2.8).
15.3.1. Vdett tagok
A vdett (protected) tagok hasznlatnak bemutatsra vegyk a 15.2.4.1 pontbeli
Window pldt. Az own_draw() fggvnyek (akarattal) nem teljes kr szolgltatst ad-
nak. Arra a clra terveztk ket, hogy csak a szrmaztatott osztlyok szmra szolgljanak
ptkvekl, az ltalnos felhasznls szmra nem biztonsgosak, nem knyelmesek.
Msfell a draw() mveletek az ltalnos felhasznlst szolgljk. Ezt a klnbsget
a Window osztly felletnek kt, egy vdett s egy nyilvnos felletre val sztvlaszts-
val lehet kifejezni:
class Window_with_border {
public:
virtual void draw();
// ...
protected:
void own_draw();
// egyb kirajzol kd
private:
// brzols stb.
};
Absztrakcis mdszerek 530
A szrmaztatott osztly a bzisosztly vdett tagjai kzl csak a sajt osztlyba tartoz ob-
jektumokat tudja elrni:
class Buffer {
protected:
char a[128];
// ...
};
class Linked_buffer : public Buffer { /* ... */ };
class Cyclic_buffer : public Buffer {
// ...
void f(Linked_buffer* p) {
a[0] = 0; // rendben: Cyclic_buffer sajt vdett tagjt ri el
p->a[0] = 0; // hiba: ms tpus vdett tagjt prbltuk elrni
}
};
Ez megakadlyozza az olyan hibkat, amelyek azltal lphetnnek fel, hogy az egyik szr-
maztatott osztly sszezavarja a msik szrmaztatott osztly adatait.
15.3.1.1. A vdett tagok hasznlata
Az adatok elrejtsnek egyszer privt/nyilvnos modellje jl szolglja a konkrt tpusokat
(10.3). De ha szrmaztatott osztlyokat hasznlunk, akkor ktfle felhasznlja lesz egy
osztlynak: a szrmaztatott osztlyok s a nagykznsg. A mveleteket megvalst ta-
gok s bartok ezen felhasznlk rdekben kezelik az objektumokat. A privt/nyilvnos
modell lehetv teszi a megvalsts s az ltalnos felhasznls pontos megklnbztet-
st, de a szrmaztatott osztlyok megfelel kezelst nem.
A protected-knt deklarlt tagokkal sokkal knnyebb visszalni, mint a privtknt beveze-
tettekkel. Ezrt a tagok vdettknt val megadsa ltalban tervezsi hiba. Ha jelents
mennyisg adatot gy helyeznk el egy kzs osztlyban, hogy az sszes szrmaztatott
osztly hasznlhatja azokat, az adatok srlhetnek. Mg rosszabb, hogy a vdett tago-
kat csakgy, mint a nyilvnosakat nem knny tszervezni, mert nincs j mdszer az
sszes hasznlat feldertsre; gy a vdett tagok megneheztik a program mdostst.
Szerencsre nem kell felttlenl vdett tagokat hasznlni; az osztlyokra a privt az alapr-
telmezett hozzfrsi kategria s ltalban ez a jobb vlaszts. Az n tapasztalatom az,
hogy az sszes szrmaztatott osztly ltal kzvetlenl hasznlhat, jelents mennyisg
adat kzs osztlyban val elhelyezse helyett mindig addik ms megolds.
15. Osztlyhierarchik 531
Jegyezzk meg, hogy ezek a kifogsok nem rvnyesek a vdett tagfggvnyekre;
a protected minstvel remekl adhatunk meg a szrmaztatott osztlyokban hasznlhat
mveleteket. A 12.4.2 pontbeli Ival_slider is plda erre. Ha ebben a pldban a megval-
st osztly privt lett volna, a tovbbi rkls lehetetlenn vlt volna. A tagok elrhets-
gre pldkat a C.11.1 tartalmaz.
15.3.2. Bzisosztlyok elrse
A tagokhoz hasonlan egy bzisosztly is lehet nyilvnos, vdett vagy privt:
class X : public B { /* ... */ };
class Y : protected B { /* ... */ };
class Z : private B { /* ... */ };
A nyilvnos rkls a szrmaztatott osztlyt a bzisosztly egy altpusv (subtype) teszi; ez
az rkls legltalnosabb formja. A vdett s a privt rkls a megvalsts mdjnak je-
llsre hasznlatos. A vdett rkls olyan osztlyhierarchikban a leghasznosabb, ahol jel-
lemzen tovbbi rkls trtnik (a 12.4.2 pontbeli Ival_slider j plda erre). A privt bzis-
osztlyok akkor a leghasznosabbak, amikor egy osztlyt a fellet korltozsval hatrozunk
meg, ami ltal ersebb garancik adhatk. A mutatkra vonatkoz Vector pldul rtk-el-
lenrzssel bvti ki Vector<void*> bzisosztlyt (13.5). Ha azt is biztostani szeretnnk,
hogy a Vec-hez (3.7.2) val minden hozzfrs ellenrztt legyen, bzisosztlyt privtknt
kell megadnunk, gy a Vec-ek nem konvertldnak nem ellenrztt vector-r:
template <class T > class Vec : private Vector <T> { /* ... */ };
Az osztly hozzfrsi szintjt nem muszj explicit megadni. Ilyenkor az alaprtelmezett
hozzfrse class esetn privt, struct esetn nyilvnos lesz:
class XX : B { /* ... */ }; // B privt bzisosztly
struct YY : B { /* ... */ }; // B nyilvnos bzisosztly
Az olvashatsg szempontjbl azonban legjobb kirni a hozzfrsi szintet megad kulcsszt.
A bzisosztly hozzfrsi szintje az osztly tagjainak elrhetsge mellett a szrmaztatott
osztlyra hivatkoz mutatknak vagy referenciknak a bzisosztly tpusra val konvertl-
hatsgt is jelzi. Vegynk egy B bzisosztlybl szrmaz D osztlyt:
Ha B privt bzisosztly, akkor nyilvnos s vdett tagjai csak D tagfggvnyeibl
s bartaibl rhetk el. Csak D bartai s tagjai konvertlhatnak egy D* mutatt
B*-g.
Absztrakcis mdszerek 532
Ha B vdett bzisosztly, akkor nyilvnos s vdett tagjai csak D tagfggvnyeibl
s bartaibl, valamint a D-bl szrmaz osztlyok tagfggvnyeibl s bartaibl
rhetk el. Csak D tagfggvnyei s bartai, valamint a D-bl szrmaz osztlyok
tagfggvnyei s bartai vgezhetnek konverzit D*-rl B*-ra.
Ha B nyilvnos bzisosztly, nyilvnos tagjai brhol hasznlhatk. Ezenkvl vdett
tagjai D tagfggvnyeibl s bartaibl, valamint a D-bl szrmaz osztlyok tagfgg-
vnyeibl s bartaibl rhetk el. Brmely fggvny vgezhet D*-rl B*-ra val
konverzit.
Ez alapveten azonos a tagok elrhetsgi szablyaival (15.3). A bzisosztlyok elrhet-
sgt ugyanazon szempontok szerint adjuk meg, mint a tagokt. A BBwindow-t pldul
azrt adtam meg az Ival_slider vdett bzisosztlyaknt (12.4.2), mert a BBwindow inkbb
az Ival_slider megvalstsnak, mint felletnek rsze. A BBwindow-t azonban nem rejt-
hettem el teljesen gy, hogy privt bzisosztlly teszem, mert az Ival_slider-bl tovbbi
osztlyokat akartam szrmaztatni s azoknak el kellett rnik a megvalstst.
A bzisosztlyok elrhetsgre a C.11.2 mutat pldkat.
15.3.2.1. A tbbszrs rklds s az elrhetsg
Ha egy nevet vagy bzisosztlyt egy tbbszrs rkldsi hl tbb tvonaln is elrhe-
tnk, akkor abban az esetben lesz elrhet, ha valamelyik t mentn elrhet:
struct B {
int m;
static int sm;
// ...
};
class D1 : public virtual B { /* ... */ } ;
class D2 : public virtual B { /* ... */ } ;
class DD : public D1, private D2 { /* ... */ };
DD* pd = new DD;
B* pb = pd; // rendben: elrhet D1-en keresztl
int i1 = pd->m; // rendben: elrhet D1-en keresztl
Ha egy bizonyos elemet tbb t mentn is elrhetnk, attl mg egyrtelmen, azaz tb-
brtelmsgi hiba nlkl hivatkozhatunk r:
class X1 : public B { /* ... */ } ;
class X2 : public B { /* ... */ } ;
class XX : public X1, public X2 { /* ... */ };
15. Osztlyhierarchik 533
XX* pxx = new XX;
int i1 = pxx->m; // hiba, tbbrtelm: XX::X1::B::m vagy XX::X2::B::m
int i2 = pxx->sm; // rendben: csak egy B::sm van egy XX-ben
15.3.2.2. A using deklarcik s az elrhetsg
A using deklarcik nem szolglhatnak tbb informci elrsre, csak az egybknt is
hozzfrhet adatok knyelmesebb hasznlatt teszik lehetv. Msrszt viszont, ha egy in-
formci elrhet, akkor a hozzfrsi jog ms felhasznlk fel tovbbadhat:
class B {
private:
int a;
protected:
int b;
public:
int c;
};
class D : public B {
public:
using B::a; // hiba: B::a privt
using B::b; // B::b nyilvnosan elrhet D-n keresztl
};
Ha egy using deklarci privt vagy vdett rkldssel jr egytt, akkor a bzisosztly l-
tal rendesen felknlt szolgltatsok egy rszhez felletet adhat:
class BB : private B { // hozzfrst ad a B::b s B::c nevekhez, de a B::a-hoz nem
public:
using B::b;
using B::c;
};
Lsd mg a 15.2.2 pontot.
Absztrakcis mdszerek 534
15.4. Futsi idej tpusinformci
A 12.4 pontban lert Ival_box-ok valszer hasznlata lenne, ha tadnnk azokat egy kp-
ernykezel rendszernek, majd az visszaadn ket a programnak, valahnyszor valamilyen
tevkenysg trtnt. Sok felhasznli fellet mkdik gy. Egy felhasznli felletet kezel
rendszer azonban nem felttlenl tud a mi Ival_box-ainkrl. A rendszer fellett a rendszer
sajt osztlyai s objektumai nyelvn adjk meg, nem a mi alkalmazsunk osztlyainak
nyelvn. Ez szksgszer s rendjn is val, de azzal a kellemetlen kvetkezmnnyel jr,
hogy informcit vesztnk a rendszernek tadott s ksbb neknk visszaadott objektu-
mok tpusrl.
Az elveszett adatok visszanyershez valahogy meg kell tudnunk krdezni az objektum-
tl a tpust. Brmilyen mveletet akarunk is vgezni az objektummal, alkalmas tpus, az
objektumra hivatkoz mutatra vagy referencira van szksgnk. Kvetkezskppen egy
objektum tpusnak futsi idben val lekrdezshez a legnyilvnvalbb s leghaszno-
sabb mvelet az, amely rvnyes mutatt ad vissza, ha az objektum a vrt tpus, illetve
nullpointert, ha nem. Pontosan ezt teszi a dynamic_cast opertor. Pldul tegyk fel,
hogy a rendszer a my_event_handler()-t arra a BBwindow-ra hivatkoz mutatval hvja
meg, amellyel egy tevkenysg trtnt. Ekkor az Ival_box osztly do_something() fggv-
nyt hasznlva meghvhatnnk programunkat:
void my_event_handler(BBwindow* pw)
{
if (Ival_box* pb = dynamic_cast<Ival_box*>(pw)) // Vajon pw egy Ival_box-ra mutat?
pb->do_something();
else {
// hopp! nem vrt esemny
}
}
A folyamatot gy is magyarzhatjuk, hogy a dynamic_cast fordt a felhasznli felletet ke-
zel rendszer sajtos nyelvrl az alkalmazs nyelvre. Fontos szrevenni, hogy mi nem
nyert emltst ebben a pldban: az objektum tnyleges tpusa. Az objektum az Ival_box
egy bizonyos fajtja lesz, mondjuk Ival_slider, amelyet a BBwindow egy bizonyos tpusa va-
lst meg, mondjuk a BBslider. Az objektum tnyleges tpusnak kidertse s megemltse
se nem szksges, se nem kvnatos a rendszer s a program kztti ezen prbeszdben.
Ltezik fellet a prbeszd lnyegnek lersra, egy jl megtervezett fellet pedig elrejti
a lnyegtelen rszleteket.
15. Osztlyhierarchik 535
Rajzban a
pb = dynamic_cast<Ival_box*>(pw)
utasts hatst gy brzolhatjuk:
A pw-bl s pb-bl kiindul nyilak az tadott objektumra hivatkoz mutatkat jellik, mg
a tbbi nyl az tadott objektum klnbz rszei kztti rkldsi viszonyokat brzolja.
A tpusinformcik futsi idben val hasznlatt hagyomnyosan futsi idej tpusinfor-
mcinak (run-time type information) hvjk s gyakran RTTI-nek rvidtik.
A bzisosztlyrl szrmaztatott osztlyra trtn konverzit gyakran lefel trtn vagy
szrmaztatott irny konverzinak (downcast) hvjk, mert az rklsi fk a hagyomnyos
brzols szerint a gykrtl lefel nnek. Ehhez hasonlan a szrmaztatott osztlyrl
bzisosztlyra trtn konverzi neve felfel trtn konverzi, vagy bzisirny kon-
verzi (upcast). A bzisosztlyrl testvrre pldul BBwindow-rl Ival_box-ra val
konverzit keresztbe trtn konverzinak (crosscast) hvjk.
15.4.1. Dynamic_cast
A dynamic_cast (dinamikus tpusknyszerts) opertor kt paramtert vr, egy <> kz rt
tpust s egy () kz rt mutatt vagy referencit.
Vegyk elszr a mutat esett:
dynamic_cast<T *>(p)
Absztrakcis mdszerek 536
BBwindow pw pb Ival_box
BBslider Ival_slider
BB_ival_slider
Ha a p a T * tpusba, vagy olyan D * tpusba tartozik, ahol T bzisosztlya D-nek, akkor az
eredmny ugyanaz, mintha a p-t egy T * vltoznak adtuk volna rtkl:
class BB_ival_slider : public Ival_slider, protected BBslider {
// ...
};
void f(BB_ival_slider* p)
{
Ival_slider* pi1 = p; // rendben
Ival_slider* pi2 = dynamic_cast<Ival_slider*>(p); // rendben
BBslider* pbb1 = p; // hiba: BBslider vdett bzisosztly
BBslider* pbb2 = dynamic_cast<BBslider*>(p); // rendben: pbb2 rtke 0 lesz
}
Ez nem tl rdekes. Azt azonban j tudni, hogy a dynamic_cast nem engedi meg a vdett
vagy privt bzisosztlyok vdelmnek vletlen megsrtst.
A dynamic_cast clja azon esetek kezelse, amelyeknl a fordtprogram nem tudja a
konverzi helyessgt megtlni:
dynamic_cast<T *>(p)
A fenti kd megvizsglja a p ltal mutatott objektumot (ha van ilyen). Ha az objektum T osz-
tly vagy van egy egyrtelm T tpus se, akkor a dynamic_cast egy, az objektumra hivat-
koz T * tpus mutatt ad vissza, ms esetben 0-t. Ha p rtke 0, a dynamic_cast<T *>(p)
eredmnye 0 lesz. Jegyezzk meg, hogy a konverzi csak egyrtelmen azonostott objektu-
moknl mkdik. Lehet olyan pldkat hozni, ahol a konverzi nem sikerl s 0-t ad, mert
a p ltal mutatott objektumnak tbb T tpus bzisosztlyt kpvisel rszobjektuma van
(15.4.2).
A dynamic_cast-nak a lefel vagy keresztbe trtn konvertlshoz tbbalak (polimorf)
mutatra vagy hivatkozsra van szksge:
class My_slider: public Ival_slider { // tbbalak bzisosztly (Ival_slider rendelkezik
// virtulis fggvnyekkel)
// ...
};
class My_date : public Date { // nem tbbalak bzisosztly (Date nem rendelkezik
// virtulis fggvnyekkel)
// ...
};
15. Osztlyhierarchik 537
void g(Ival_box* pb, Date* pd)
{
My_slider* pd1 = dynamic_cast<My_slider*>(pb); // rendben
My_date* pd2 = dynamic_cast<My_date*>(pd); // hiba: Date nem tbbalak
}
Az a megkts, hogy a mutatnak tbbalaknak kell lennie, egyszersti a dynamic_cast
megvalstst, mert megknnyti az objektum tpusnak trolshoz szksges informci
helynek megtallst. ltalnos megolds, hogy a tpust jelz mutatt az objektum virtu-
lis fggvnytbljba (2.5.5) helyezik, ezltal egy tpus-informci objektumot fznek az
objektumhoz:
A szaggatott nyl az eltolst (offset) jelli, amelynek segtsgvel a teljes objektum kezdete
megtallhat, ha csak egy tbbalak rszobjektumra hivatkoz mutat adott. Vilgos, hogy
a dynamic_cast hatkonyan felhasznlhat; csak nhny, a bzisosztlyt ler type_info ob-
jektumot kell sszehasonltani, nincs szksg hosszadalmas keressre vagy karakterlncok
sszehasonltsra.
A dynamic_cast-nak tbbalak tpusokra val korltozsa logikai szempontbl nzve is r-
telmes. Ha egy objektumnak nincs virtulis fggvnye, akkor pontos tpusnak ismerete
nlkl nem kezelhet biztonsgosan, ezrt gyelni kell, hogy az ilyen objektum ne kerl-
jn olyan krnyezetbe, ahol nem ismeretes a pontos tpusa. Ha azonban a tpusa ismert,
nincs szksg dynamic_cast-ra.
A dynamic_cast cltpusa nem kell, hogy tbbalak legyen, ezrt egy konkrt tpust tbb-
alakba csomagolhatunk, mondjuk, hogy egy objektumokat kezel ki- s bemeneti rend-
szeren keresztl tovbbtsuk (25.4.1), majd ksbb kicsomagoljuk belle a konkrt tpust:
Absztrakcis mdszerek 538
My_slider:
...
vptr
...
"My_slider"
bzisosztlyok
"Ival_slider"
vtbl:
type_info:
type_info:
class Io_obj { // bzisosztly-objektum az I/O rendszer szmra
virtual Io_obj* clone() = 0;
};
class Io_date : public Date, public Io_obj { };
void f(Io_obj* pio)
{
Date* pd = dynamic_cast<Date*>(pio);
// ...
}
Egy tbbalak objektum kezdcmt egy void*-ra val dinamikus tpusknyszertssel ha-
trozhatjuk meg:
void g(Ival_box* pb, Date* pd)
{
void* pd1 = dynamic_cast<void*>(pb); // rendben
void* pd2 = dynamic_cast<void*>(pd); // hiba: Date nem tbbalak
}
Ez azonban csak nagyon alacsony szint fggvnyekkel val egyttmkds cljra hasznos.
15.4.1.1. Referencik dinamikus tpusknyszertse
Egy objektum tbbalak (polymorph) viselkedshez akkor frnk hozz, ha mutatn
vagy referencin t kezeljk. A dynamic_cast mvelet sikertelensgt 0-val jelzi. Ez
referencikra se nem kivitelezhet, se nem kvnatos.
Ha egy mutatrl, mint eredmnyrl van sz, akkor fel kell kszlnnk arra a lehetsgre,
hogy az eredmny 0 lesz, azaz a mutat nem mutat semmilyen objektumra. Ezrt egy
dynamic_cast mvelet eredmnyt mindig ellenriznnk kell. A p mutatn vgzett
dynamic_cast<T*>(p) egy krdsknt foghat fel: a p ltal mutatott objektum T tpus?
Msrszt viszont jogosan ttelezhetjk fel, hogy egy referencia mindig egy objektumra vo-
natkozik. Kvetkezskppen egy r referencia esetn a dynamic_cast<T&>(r) nem krds,
hanem llts: a r ltal mutatott objektum T tpus. A dynamic_cast mvelet eredmnyt
maga a dynamic_cast-ot megvalst kd ellenrzi automatikusan, s ha a dynamic_cast
operandusa nem a vrt tpus hivatkozs, akkor bad_cast kivtelt vlt ki:
15. Osztlyhierarchik 539
void f(Ival_box* p, Ival_box& r)
{
if (Ival_slider* is = dynamic_cast<Ival_slider*>(p)) { // Vajon p egy Ival_slider-re mutat?
// 'is' hasznlata
}
else {
// *p nem slider
}
Ival_slider& is = dynamic_cast<Ival_slider&>(r); // az r egy Ival_slider-re hivatkozik!
// 'is' hasznlata
}
A sikertelen dinamikus mutat- illetve referencia-talaktsok eredmnynek eltrsben
a mutatk, illetve a referencik kztti alapvet klnbsg tkrzdik. Ha egy felhasznl
vdekezni akar a referencia-talakts sikertelensge ellen, akkor egy megfelel kivtelke-
zelre van szksge:
void g()
{
try {
f(new BB_ival_slider,*new BB_ival_slider); // a paramterek Ival_box-knt
// addnak t
f(new BBdial,*new BBdial); // a paramterek Ival_box-knt addnak t
}
catch (bad_cast) { // 14.10
// ...
}
}
Az f() fggvny els meghvsa sikeresen fog visszatrni, mg a msodik bad_cast kivtelt
vlt ki, amit g() elkap.
A 0 rtk ellenrzse elhagyhat, gy alkalmasint vletlenl el is fog maradni. Ha ez ag-
gasztja az olvast, akkor rhat egy olyan konverzis fggvnyt, amely sikertelensg esetn
a 0 rtk visszaadsa helyett kivtelt vlt ki (15.8[1]).
15.4.2. Osztlyhierarchik bejrsa
Ha csak egyszeres rkldst hasznlunk, az osztly s bzisosztlyai egyetlen bzisosz-
tlyban gykerez ft alkotnak. Ez egyszer, de gyakran tl ers megszortst jelent. Tbb-
szrs rklds hasznlata esetn nincs egyetlen gykr. Ez nmagban nem bonyoltja
nagyon a helyzetet, de ha egy osztly tbbszr fordul el a hierarchiban, akkor nmi va-
tossggal kell az adott osztly objektumra vagy objektumokra hivatkoznunk.
Absztrakcis mdszerek 540
Termszetesen a hierarchikat igyeksznk annyira egyszernek venni de nem egysze-
rbbnek , amennyire programunk megengedi. De ha mr kialakult egy bonyolultabb hie-
rarchia, hamarosan szksgnk lesz annak bejrsra (vagyis vgignzsre), hogy alkal-
mas osztlyt talljunk, amelyet felletknt hasznlhatunk. Ez az igny kt esetben merlhet
fel. Nha kifejezetten meg akarunk nevezni egy bzisosztlyt vagy annak egy tagjt (pld-
ul 15.2.3 s 15.2.4.1). Mskor egy, a bzisosztlyt vagy annak egy szrmaztatott osztlyt
megjelent objektumra hivatkoz mutatra van szksgnk, ha adott egy mutat a teljes
objektumra vagy annak valamely rszobjektumra (15.4 s 15.4.1).
Itt azt tekintjk t, hogyan lehet tpusknyszertst (cast) hasznlva a kvnt tpus mutat-
hoz jutni. Hogy szemlltessk az elrhet mdszereket s a rjuk vonatkoz szablyokat,
vegynk egy tbbszr szerepl s virtulis bzisosztlyt egyarnt tartalmaz hlt:
class Component : public virtual Storable { /* ... */ };
class Receiver : public Component { /* ... */ };
class Transmitter : public Component { /* ... */ };
class Radio : public Receiver, public Transmitter { /* ... */ };
brval:
Itt a Radio objektumnak kt Component osztly rszobjektuma van. Ezrt a Radio-n bel-
li, Storable-rl Component-re val dinamikus tpusknyszerts tbbrtelm lesz s nullt
ad. Ilyenkor egyszeren nem lehet tudni, melyik Component-re gondolt a programoz:
void h1(Radio& r)
{
Storable* ps = &r;
// ...
Component* pc = dynamic_cast<Component*>(ps); // pc = 0
}
15. Osztlyhierarchik 541
Storable
Receiver Transmitter
Component Component
Radio
Ez a tbbrtelmsg fordtsi idben ltalban nem derthet fel:
void h2(Storable* ps) // ps-rl nem tudjuk, hogy Component-re mutat-e
{
Component* pc = dynamic_cast<Component*>(ps);
// ...
}
A tbbrtelmsgnek ez a fajta feldertse csak virtulis bzisosztlyoknl szksges. K-
znsges bzisosztlyok s lefel (azaz a szrmaztatott osztly fel trtn; 15.4)
konverzi esetn a kvnt tpus rszobjektum mindig egyrtelm (ha ltezik). Ezzel
egyenrtk tbbrtelmsg lp fel felfel (azaz a bzisosztly fel trtn) konverzi ese-
tn s ezek a tbbrtelmsgek fordtsi idben kiderlnek.
15.4.2.1. Statikus s dinamikus konverzi
A dynamic_cast mvelet tbbalak bzisosztlyrl szrmaztatott osztlyra vagy testvrosz-
tlyra val talaktst tud vgezni (15.4.1). A static_cast (6.2.7) nem vizsglja a kiindul
objektum tpust, gy nem kpes ezekre:
void g(Radio& r)
{
Receiver* prec = &r; // a Receiver a Radio kznsges bzisosztlya
Radio* pr = static_cast<Radio*>(prec); // rendben, nincs ellenrzs
pr = dynamic_cast<Radio*>(prec); // rendben, futsi idej ellenrzs
Storable* ps = &r; // a Storable a Radio virtulis bzisosztlya
pr = static_cast<Radio*>(ps); // hiba: virtulis bzisosztlyrl nem lehet talaktani
pr = dynamic_cast<Radio*>(ps); // rendben, futsi idej ellenrzs
}
A dynamic_cast-nak tbbalak operandusra van szksge, mert egy nem tbbalak objek-
tum nem trol olyan informcit, amelynek alapjn meg lehetne keresni azon objektumo-
kat, amelyeknek se (bzisa). Olyan objektum is lehet pldul virtulis bzisosztly, amely-
nek a memriban val elhelyezkedst egy msik nyelv, pldul a Fortran vagy a C hat-
rozza meg. Ezekre vonatkozan csak statikus adatok llnak rendelkezsre, de
a dynamic_cast megvalstshoz szksges informcit a futsi idej tpusinformci tar-
talmazza.
Mirt akarna valaki static_cast-ot hasznlni egy osztlyhierarchia bejrsra?
A dynamic_cast nmileg nveli a futsi idt (15.4.1), de ennl jelentsebb ok, hogy milli
sornyi kd van a dynamic_cast bevezetse eltti idkbl. Az ilyen kdok ms mdokon
Absztrakcis mdszerek 542
biztostjk az alkalmazott tpustalaktsok helyessgt, gy a dynamic_cast-tal vgeztetett
ellenrzs feleslegesnek tnik. Az ilyen ltalban C stlus tpuskonverzival (6.2.7) r-
dott kdban azonban gyakran maradhatnak rejtett hibk, gy, hacsak lehet, hasznljuk
a biztonsgosabb dynamic_cast-ot.
A fordtprogram nem ttelezhet fel semmit egy void* mutat ltal mutatott memriater-
letrl. Ebbl kvetkezik, hogy az objektum tpusa fell rdekld dynamic_cast nem k-
pes void*-rl konvertlni. Ehhez static_cast kell:
Radio* f(void* p)
{
Storable* ps = static_cast<Storable*>(p); // Bzzunk a programozban!
return dynamic_cast<Radio*>(ps);
}
Mind a dynamic_cast, mind a static_cast tiszteletben tartja a const minstst s a hozzf-
rsi korltozsokat:
class Users : private set<Person> { /* ... */ };
void f(Users* pu, const Receiver* pcr)
{
static_cast<set<Person>*>(pu); // hiba: nem frhet hozz
dynamic_cast<set<Person>*>(pu); // hiba: nem frhet hozz
static_cast<Receiver*>(pcr); // hiba: const minsts nem vsz el
dynamic_cast<Receiver*>(pcr); // hiba: const minsts nem vsz el
Receiver* pr = const_cast<Receiver*>(pcr); // rendben
// ...
}
Privt bzisosztlyra nem lehet konvertlni, const-ot nem konstanss konvertlni pedig
csak const_cast-tal lehet (6.2.7), s mg akkor is csak gy kapunk helyes eredmnyt, ha az
objektumot eredetileg nem const-knt deklarltuk (10.2.7.1).
15.4.3. Osztlyobjektumok felptse s megsemmistse
Egy valamilyen osztlyba tartoz objektum tbb, mint egyszeren a memria egy rsze
(4.9.6). Az osztlyobjektumokat konstruktoraik ptik fel a nyers memribl s
destruktoraik lefutsval vlnak jra nyers memriv. Az objektum felptse alulrl fel-
fel, megsemmistse fellrl lefel trtnik, s az osztlyobjektum olyan mrtkben ltez
15. Osztlyhierarchik 543
objektum, amennyire felptse, illetve megsemmistse megtrtnt. Ez tkrzdik a futsi
idej tpusazonostsra (RTTI), a kivtelkezelsre (14.4.7) s a virtulis fggvnyekre vo-
natkoz szablyokban.
Nem blcs dolog az objektumfelpts vagy -megsemmists sorrendjre tmaszkodni, de
a sorrendet megfigyelhetjk, ha virtulis fggvnyeket, dynamic_cast-ot vagy typeid-t
(15.4.4) hvunk akkor, amikor az objektum mg nincs kszen. Pldul ha a 15.4.2 pont-
beli hierarchia Component konstruktora egy virtulis fggvnyt hv, akkor a Storable vagy
Component-beli vltozatot fogja meghvni, nem a Receiver, Transmitter vagy Radio-belit.
Az objektum ltrehozsnak ezen pontjn az objektum mg nem Radio; csak egy rszben
felptett objektum. Ennek fnyben legjobb elkerlni a virtulis fggvnyeknek
konstruktorbl vagy destruktorbl val meghvst.
15.4.4. Typeid s kiterjesztett tpusinformci
A dynamic_cast opertor az objektumok tpusra vonatkoz, futsi idben jelentkez in-
formciigny legnagyobb rszt kielgti. Fontos tulajdonsga, hogy biztostja a felhaszn-
l kd helyes mkdst a programoz ltal hasznlt osztlyokbl szrmaz osztlyokkal
is. gy a dynamic_cast a virtulis fggvnyekhez hasonlan megrzi a rugalmassgot s b-
vthetsget.
Nha azonban alapvet fontossg tudni az objektum pontos tpust. Pldul tudni szeret-
nnk az objektum osztlynak nevt vagy memriakiosztst. A typeid opertor ezt az
operandusa tpust jelz objektum visszaadsval tmogatja. Ha a typeid() fggvny lenne,
valahogy gy adhatnnk meg:
class type_info;
const type_info& typeid(type_name) throw(); // l-deklarci
const type_info& typeid(expression) throw(bad_typeid); // l-deklarci
Azaz a typeid() egy standard knyvtrbeli, a <typeinfo> fejllomnyban definilt type_info
nev tpusra val referencit ad vissza. Ha operandusknt egy tpusnevet kap, a typeid() az
azt brzol type_info-ra val referencival tr vissza, ha kifejezst, a kifejezs ltal jellt
objektumot brzol type_info-ra fog hivatkozni. A typeid() leginkbb egy referencival
vagy mutatval jellt objektum tpusnak lekrdezsre hasznlatos:
void f(Shape& r, Shape* p)
{
typeid(r); // az r ltal hivatkozott objektum tpusa
typeid(*p); // a p ltal mutatott objektum tpusa
typeid(p); // a mutat tpusa, vagyis Shape* (nem gyakori, leginkbb tveds)
}
Absztrakcis mdszerek 544
Ha a mutat vagy referencia operandus rtke 0, a typeid() bad_typeid kivtelt vlt ki.
A type_info megvalsts-fggetlen rsze gy nz ki:
class type_info {
public:
virtual ~type_info(); // tbbalak
bool operator==(const type_info&) const; // sszehasonlthat
bool operator!=(const type_info&) const;
bool before(const type_info&) const; // rendezs
const char* name() const; // a tpus neve
private:
type_info(const type_info&); // msols megakadlyozsa
type_info& operator=(const type_info&); // msols megakadlyozsa
// ...
};
A before() fggvny lehetv teszi a type_info objektumok rendezst. A meghatrozott
rendezsi sorrendnek nincs kze az rklsi viszonyokhoz.
Nem biztos, hogy a rendszer minden egyes tpust egyetlen type_info objektum kpviseli.
Dinamikus csatols knyvtrak hasznlata esetn pldul valban nehz a tbb type_info
objektumot elkerl megvalsts elksztse. Ezrt a == mvelettel az egyenlsget
a type_info objektumok s nem az azokra hivatkoz mutatk esetben vizsgljuk.
Nha tudni akarjuk egy objektum pontos tpust, hogy valamilyen szabvnyos mveletet
vgezznk az egsz objektumon (s nem csak annak egy sn). Idelis esetben az ilyen
mveletek virtulis fggvnyek formjban llnak rendelkezsre, gy nem szksges a pon-
tos tpus ismerete. Egyes esetekben azonban nem ttelezhet minden egyes kezelt objek-
tumra vonatkoz kzs fellet, gy a megolds tja a pontos tpuson keresztl vezet
(15.4.4.1). Egy msik, sokkal egyszerbb hasznlat az osztly nevnek diagnosztikai kime-
net cljra val lekrdezse:
#include<typeinfo>
void g(Component* p)
{
cout << typeid(*p).name();
}
15. Osztlyhierarchik 545
Az osztlyok nevnek szveges brzolsa az adott nyelvi vltozattl fgg. C stlus karak-
terlncokkal trtnik, amelyek a rendszerhez tartoz memriarszben vannak, ezrt ne
prbljuk meg a delete[ ] mveletet alkalmazni rjuk.
15.4.4.1. Kiterjesztett tpusinformci
Egy objektum pontos tpusnak meghatrozsa ltalban csak az els lps a r vonatkoz
rszletesebb informcik megszerzse s hasznlata fel.
Gondoljuk meg, hogy egy program vagy programozst segt eszkz hogyan tudna futsi
idben tpusokrl szl informcit adni a felhasznlknak. Tegyk fel, hogy van egy esz-
kznk, amely minden felhasznlt osztlyrl megmondja az objektum memriakiosztst.
Ezeket a lerkat egy map-be tehetem, hogy annak alapjn a felhasznli kd megtallhas-
sa a memriakiosztsi informcit:
map<string, Layout> layout_table;
void f(B* p)
{
Layout& x = layout_table[typeid(*p).name()];
// x hasznlata
}
Valaki ms egszen eltr informcit adhat:
struct TI_eq {
bool operator()(const type_info* p, const type_info* q) { return *p==*q; }
};
struct TI_hash {
int operator()(const type_info* p); // hastrtk kiszmtsa (17.6.2.2)
};
hash_map<const type_info*,Icon,hash_fct,TI_hash,TI_eq> icon_table; // 17.6
void g(B* p)
{
Icon& i = icon_table[&typeid(*p)];
// i hasznlata
}
Absztrakcis mdszerek 546
A typeid-ekhez informci rendelsnek ez a mdja tbb programoz vagy eszkz szmra
teszi lehetv, hogy a tpusokhoz egymstl teljesen fggetlen informcikat rendeljenek:
Ez nagyon fontos, mert annak valsznsge, hogy valaki informciknak olyan halmazval
tud elllni, amely egymagban minden felhasznl ignyeit kielgti, a nullval egyenl.
15.4.5. Az RTTI helyes s helytelen hasznlata
Csak szksg esetn hasznljunk explicit futsi idej tpusinformcit. A statikus (fordtsi
idben trtn) ellenrzs biztonsgosabb, olcsbb s alkalmasint jobban szerkesz-
tett programokhoz vezet. A futsi idej tpusinformcit pldul arra hasznlhatjuk, hogy
egy rosszul lczott switch utastst rjunk:
// a futsi idej tpusinformci helytelen hasznlata
void rotate(const Shape& r)
{
if (typeid(r) == typeid(Circle)) {
// nem csinlunk semmit
}
else if (typeid(r) == typeid(Triangle)) {
// hromszg forgatsa
}
else if (typeid(r) == typeid(Square)) {
// ngyzet forgatsa
}
// ...
}
A dynamic_cast-nak a typeid helyett val hasznlata alig javtana ezen a kdon.
15. Osztlyhierarchik 547
layout_table:
icon_table:
"T"
...
...
&typeid(T)
...
Az objektum
memriakiosztsa
A tpus ikonos
brzolsa
Sajnos ez nem egy lgbl kapott plda; ilyen kdot tnyleg rnak. A C-hez, a Pascalhoz,
a Modula-2-hz, vagy az Adhoz hasonl nyelveken nevelkedett programoz majdnem el-
lenllhatatlan ksrtst rez, hogy a programot switch utastsok halmazaknt ptse fel. En-
nek a ksrtsnek ltalban ellen kell llni. Futsi idej tpusazonosts helyett inkbb vir-
tulis fggvnyeket (2.5.5, 12.2.6) hasznljunk a legtbb olyan eset kezelsre, amikor
a tpuson alapul futsi idej megklnbztets szksges.
Az RTTI helyes hasznlata sokszor merl fel akkor, amikor a kdban valamilyen szolglta-
tst egy bizonyos osztlyhoz kapcsolunk s a felhasznl rkldssel akar tovbbi szol-
gltatsokat hozzadni. A 15.4 pontbeli Ival_box hasznlata ennek egy pldja. Ha a fel-
hasznl hajland s kpes a knyvtri osztlyok pldul a BBwindow mdostsra,
akkor az RTTI hasznlata elkerlhet; klnben viszont szksges. De ha a felhasznl haj-
land is a knyvtri osztlyok mdostsra, az ilyen mdosts problmkhoz vezethet.
Pldul szksgess vlhat a virtulis fggvnyek l-megvalstsa olyan osztlyok eset-
ben, amelyeknl azok nem szksgesek vagy nem rtelmesek. Ezt a problmt a 24.4.3 n-
mileg rszletesebben trgyalja. Az RTTI-nek egy egyszer, objektumokat kezel ki- s be-
meneti rendszer elksztsre szolgl hasznlatt a 25.4.1 rja le.
Azok szmra, akik a Smalltalkon vagy a Lispen, esetleg ms, nagymrtkben a dinamikus
tpusellenrzsre pt nyelveken nevelkedtek, csbt dolog az RTTI-t tlsgosan ltal-
nos tpusokkal egytt hasznlni. Vegyk ezt a pldt:
// a futsi idej tpusinformci helytelen hasznlata
class Object { /* ... */ }; // tbbalak
class Container : public Object {
public:
void put(Object*);
Object* get();
// ...
};
class Ship : public Object { /* ... */ };
Ship* f(Ship* ps, Container* c)
{
c->put(ps);
// ...
Object* p = c->get();
if (Ship* q = dynamic_cast<Ship*>(p)) { // futsi idej ellenrzs
return q;
}
Absztrakcis mdszerek 548
else {
// valami mst csinlunk (ltalban hibakezelst vgznk)
}
}
Itt az Object osztly szksgtelen s mesterklt. Tlsgosan ltalnos, mert az adott alkalma-
zsban nem felel meg semmilyen elvonatkoztatsi szintnek s a programozt egy megval-
sts-szint fogalom hasznlatra knyszerti. Az ilyen jelleg problmkat gyakran jobban
oldja meg, ha kizrlag egy adott tpus mutatt tartalmaz trol sablonokat hasznlunk:
Ship* f(Ship* ps, list<Ship*>& c)
{
c.push_front(ps);
// ...
return c.pop_front();
}
Virtulis fggvnyekkel egytt hasznlva gy majdnem minden esetet megoldhatunk.
15.5. Tagra hivatkoz mutatk
Sok osztlynak van egyszer, nagyon ltalnos fellete, amelyet sokfle hasznlatra szn-
tak. Pldul sok objektumorientlt felhasznli fellet hatroz meg egy sor krst, amely-
re minden, a kpernyn megjelentett objektumnak tudnia kell vlaszolni. Radsul az ilyen
krsek kzvetett vagy kzvetlen mdon programoktl rkezhetnek. Vegyk ezen elv egy
egyszer vltozatt:
class Std_interface {
public:
virtual void start() = 0;
virtual void suspend() = 0;
virtual void resume() = 0;
virtual void quit() = 0;
virtual void full_size() = 0;
virtual void small() = 0;
virtual ~Std_interface() {}
};
15. Osztlyhierarchik 549
Minden mvelet pontos jelentst az az objektum definilja, amelyre alkalmazzk. Gyakran
a krst kiad szemly vagy program s a fogad objektum kztt egy szoftverrteg van.
Idelis esetben az ilyen kztes rtegeknek nem kell semmit tudniuk az egyes mveletekrl
(resume(), full_size() stb.). Ha tudnnak, a kztes rtegeket fel kellene jtani, valahny-
szor a mveletek halmaza megvltozik. Kvetkezskppen az ilyen kztes rtegek csupn
tovbbtanak valamely, az alkalmazand mveletre vonatkoz adatot a krs forrstl
a fogadhoz.
Ennek egyszer mdja az alkalmazand mveletet jell karakterlnc kldse. Pldul
a suspend() meghvsa cljbl a suspend (felfggeszts) szveget kldhetnnk. Valaki-
nek azonban ltre kell hoznia a karakterlncot, valakinek pedig meg kell fejtenie, melyik
mvelethez tartozik, ha egyltaln tartozik valamelyikhez. Ez gyakran tlsgosan kzvetett-
nek s fradsgosnak tnik. Ehelyett kldhetnnk csak egy, a mveletet jell egsz rt-
ket. Mondjuk a 2 jelenthetn a suspend-et. De amg egy egsz szmot a szmtgpek k-
nyelmesen kezelnek, az emberek szmra ez meglehetsen zavar lehet, radsul mg
mindig meg kell rnunk a kdot, amely meghatrozza, hogy a 2 a suspend()-et jelenti s
meg kell hvnunk a suspend()-et.
A C++ nyelv lehetv teszi az osztlyok tagjainak kzvetett elrst. A tagra hivatkoz mu-
tat olyan rtk, amely az osztly egy tagjt azonostja. Gondolhatunk r gy, mint egy, az
adott osztlyhoz tartoz objektumban lev tag helyre, de a fordt termszetesen figye-
lembe veszi az adattagok, a virtulis s nem virtulis fggvnyek stb. kztti klnbsget.
Vegyk az Std_interface-t. Ha meg akarjuk hvni a suspend()-et valamely objektumra anl-
kl, hogy kzvetlenl megneveznnk, akkor egy olyan mutatra lesz szksgnk, ami az
Std_interface::suspend() tagra mutat. Ugyancsak szksgnk lesz a suspend() rvn felfg-
gesztend objektumra hivatkoz mutatra vagy referencira. Vegynk egy igen egyszer
pldt:
typedef void (Std_interface::* Pstd_mem)(); // tagra hivatkoz mutat
void f(Std_interface* p)
{
Pstd_mem s = &Std_interface::suspend;
p->suspend(); // kzvetlen hvs
(p->*s)(); // hvs tagra hivatkoz mutatn keresztl
}
Absztrakcis mdszerek 550
Egy tagra hivatkoz mutatt (pointer to member) gy kapunk, hogy a cmkpz & oper-
tort egy teljes nev (teljesen minstett, fully qualified) osztlytagra alkalmazzuk (pldul
&Std_interface::suspend()). Az X osztly egy tagjra hivatkoz mutat tpus vltozkat
az X::* formban deklarlhatjuk.
A C szintaxis ttekinthetsgnek hinyt ltalban a typedef alkalmazsval ellenslyozzk,
az X::* forma viszont lthatan nagyon szpen megfelel a hagyomnyos * deklartornak.
Egy m tagra hivatkoz mutatt egy objektummal kapcsolatban hasznlhatunk. A kapcsola-
tot a ->* s .* opertorokkal fejezhetjk ki. Pldul a p->*maz m-et a p ltal mutatott objek-
tumhoz kti, az obj.*m pedig az obj objektumhoz. Az eredmny az m tpusnak megfele-
len hasznlhat, de a ->* vagy .* mvelet eredmnyt nem lehet ksbbi hasznlatra
flretenni.
Termszetesen ha tudnnk, melyik tagot akarjuk meghvni, kzvetlenl meghvnnk azt,
a tagra hivatkoz mutatkkal val bajlds helyett. A kznsges fggvnymutatkhoz ha-
sonlan a tagfggvnyekre hivatkoz mutatkat akkor hasznljuk, amikor a tag nevnek is-
merete nlkl akarunk egy fggvnyre hivatkozni. A tagra hivatkoz mutat azonban nem
egyszeren egy mutat egy memriaterletre, mint egy vltoz cme vagy egy fggvny-
mutat, inkbb egy adatszerkezeten belli eltolsra (offszetre) vagy egy tmbbeli indexre
hasonlt. Amikor egy tagra hivatkoz mutatt a megfelel tpus objektumra hivatkoz mu-
tatval prostjuk, olyasvalami keletkezik, ami egy bizonyos objektum egy bizonyos tagjt
azonostja. Ezt rajzzal gy brzolhatjuk:
Mivel egy virtulis tagra hivatkoz mutat (a fenti pldban s) egyfajta eltols, nem fgg az
objektum helytl a memriban, ezrt biztonsgosan tadhat egy msik cmtrnek
(address space), feltve, hogy az objektum elhelyezkedse a kettben azonos. A kzns-
ges fggvnyekre hivatkoz mutatkhoz hasonlan a nem virtulis tagfggvnyekre hivat-
koz mutatkat sem lehet msik cmtrnek tadni.
15. Osztlyhierarchik 551
X::start
X::suspend
p
s
vtbl:
Jegyezzk meg, hogy a fggvnymutatn keresztl meghvott fggvny lehet virtulis is.
Pldul amikor egy fggvnymutatn keresztl meghvjuk a suspend()-et, akkor azon ob-
jektum tpusnak megfelel suspend()-et kapjuk, amelyre a fggvnymutatt alkalmaztuk.
Ez a fggvnymutatknak igen lnyeges tulajdonsga.
Egy tolmcsol (tovbbt, interpreter) fggvny egy tagra hivatkoz mutat segtsgvel
meghvhat egy karakterlnccal (vagyis szveggel) megadott fggvnyt:
map<string,Std_interface*> variable;
map<string,Pstd_mem> operation;
void call_member(string var, string oper)
{
(variable[var]->*operation[oper])(); // var.oper()
}
A tagfggvnyekre hivatkoz mutatk legfontosabb hasznlatt a 3.8.5 s 18.4 pontbeli
mem_fun() fggvnyben vizsglhatjuk meg.
A statikus tagok nem tartoznak egy bizonyos objektumhoz, gy egy statikus tagra hivatko-
z mutat csupn egy kznsges mutat:
class Task {
// ...
static void schedule();
};
void (*p)() = &Task::schedule; // rendben
void (Task::* pm)() = &Task::schedule; // hiba: egyszer mutatt adtunk rtkl
// tagra hivatkoz mutatnak
Az adattagokra hivatkoz mutatkat a C.12 pont rja le.
15.5.1. Bzis- s szrmaztatott osztlyok
Egy szrmaztatott osztly legalbb azokkal a tagokkal rendelkezik, amelyeket a bzisosz-
tlybl rkl, de gyakran tbbel. Ez azt jelenti, hogy egy bzisosztly egy tagjra hivatko-
z mutatt biztonsgosan rtkl adhatunk egy szrmaztatott osztlytagra hivatkoz
mutatnak, de fordtva nem. Ezt a tulajdonsgot gyakran kontravariancinak
(contravariance) hvjk:
Absztrakcis mdszerek 552
class text : public Std_interface {
public:
void start();
void suspend();
// ...
virtual void print();
private:
vector s;
};
void (Std_interface::* pmi)() = &text::print; // hiba
void (text::*pmt)() = &Std_interface::start; // rendben
A fel nem cserlhetsg ezen szablya ltszlag ellenkezik azzal a szabllyal, hogy egy szr-
maztatott osztlyra hivatkoz mutatt rtkl adhatunk a bzisosztlyra hivatkoz muta-
tnak. Valjban azonban mindkt szably azrt van, hogy biztostsa az alapvet garancit
arra, hogy egy mutat soha nem mutat olyan objektumra, amelynek nincsenek meg a mu-
tat tpusa ltal grt tulajdonsgai. Ebben az esetben az Std_interface::* brmilyen
Std_interface-re alkalmazhat, s a legtbb ilyen objektum vlhetleg nem text tpus lesz.
Ezrt aztn nincs meg a text::print tagjuk, amellyel pmi-nek kezdrtket prbltunk adni.
A kezdeti rtkads megtagadsval a fordtprogram egy futsi idej hibtl ment meg
bennnket.
15.6. A szabad tr
Az operator new() s az operator delete() definilsval t lehet venni a memriakezelst
egy osztlytl (6.2.6.2). Ezen globlis fggvnyek kicserlse azonban nem a flnkeknek
val, hiszen elfordulhat, hogy valaki az alaprtelmezett viselkedsre szmt vagy ezen
fggvnyek valamilyen ms vltozatt mr meg is rta.
Gyakran jobb megkzelts ezeket a mveleteket egy bizonyos osztlyra megrni. Ez az
osztly tbb szrmaztatott osztly alapja is lehet. Tegyk fel, hogy a 12.2.6-beli Employee
osztlyt s annak minden leszrmazottjt szeretnnk egyedi memriafoglalval (alloktor-
ral) s -felszabadtval (dealloktorral) elltni:
class Employee {
// ...
public:
// ...
15. Osztlyhierarchik 553
void* operator new(size_t);
void operator delete(void*, size_t);
};
Az operator new() s az operator delete() tagfggvnyek automatikusan statikusak lesznek,
ezrt nincs this mutatjuk s nem vltoztatnak meg egy objektumot, csak olyan trterletet
adnak, amelyet a konstruktorok feltlthetnek s a destruktorok kitakarthatnak.
void* Employee::operator new(size_t s)
{
// lefoglal 's' bjtnyi memrit s r hivatkoz mutatt ad vissza
}
void Employee::operator delete(void* p, size_t s)
{
if (p) { // trls csak ha p!=0; lsd 6.2.6, 6.2.6.2
// feltesszk, hogy 'p' 's' bjt memrira mutat, amit az Employee::operator new() foglalt le
// felszabadtjuk a memrit
}
}
Az eddig rejtlyes size_t paramter haszna most vilgoss vlik. Ez ugyanis a tnylegesen
trlend objektum mrete. Ha egy sima Employee-t trlnk, akkor paramterrtkknt
sizeof(Employee)-t; ha egy Manager-t, akkor sizeof(Manager)-t kapunk. Termszetesen az
adott osztly egyedi memriafoglalja trolhatja az ilyen informcit (mint ahogy az ltal-
nos clnak is meg kell ezt tennie) s nem trdhet az operator delete() fggvny size_t pa-
ramtervel. Ez azonban nehezebb teszi egy ltalnos cl memriafoglal sebessgnek
s memriafogyasztsnak javtst.
Honnan tudja a fordtprogram a delete() opertort a megfelel mretettel elltni? Knnyen,
amg a delete mveletnek tadott tpus azonos az objektum tnyleges tpusval. Ez azonban
nincs mindig gy:
class Manager : public Employee {
int level;
// ...
};
void f()
{
Employee* p = new Manager; // problms (a pontos tpus ismerete elvsz)
delete p;
}
Ekkor a fordtprogram nem fogja tudni a pontos mretet. A tmb trlshez hasonlan itt
is a programoz segtsgre van szksg; az Employee bzisosztlyban meg kell adni egy
virtulis destruktort:
Absztrakcis mdszerek 554
class Employee {
public:
void* operator new(size_t);
void operator delete(void*, size_t);
virtual ~Employee();
// ...
};
Akr egy res destruktor is megteszi:
Employee::~Employee() { }
A memria felszabadtst a (mretet ismer) destruktor vgzi. St ha az Employee-ben van
destruktor, akkor ez biztostja, hogy minden belle szrmaztatott osztlynak lesz
destruktora (s gy tudja a mretet), akkor is, ha a szrmaztatott osztlyban nem szerepel
felhasznli destruktor:
void f()
{
Employee* p = new Manager;
delete p; // most mr j (az Employee tbbalak)
}
A memria lefoglalsa egy (a fordtprogram ltal ltrehozott) hvssal trtnik:
Employee::operator new(sizeof(Manager))
A felszabadtsrl szintn egy (a fordtprogram ltal ltrehozott) hvs gondoskodik:
Employee::operator delete(p,sizeof(Manager))
Vagyis ha olyan memriafoglal/felszabadt prt akarunk rni, amely szrmaztatott oszt-
lyokkal is jl mkdik, akkor vagy virtulis destruktort kell rni a bzisosztlyban, vagy nem
szabad felhasznlni a felszabadtban a size_t paramtert. Termszetesen meg lehetett vol-
na gy tervezni a nyelvet, hogy ne legyen szksg ilyen megfontolsokra, de ez csak a ke-
vsb biztonsgos rendszerekben lehetsges optimalizlsok elnyeirl val lemonds
rn trtnhetett volna.
15. Osztlyhierarchik 555
15.6.1. Memriafoglals tmbk szmra
Az operator new() s az operator delete() fggvnyek lehetv teszik, hogy a programoz
vgezze az egyes objektumok szmra a memriafoglalst s -felszabadtst. Az operator
new[ ]() s az operator delete[ ]() pontosan ugyanezt a szerepet jtssza a tmbk esetben:
class Employee {
public:
void* operator new[ ](size_t);
void operator delete[ ](void*);
// ...
};
void f(int s)
{
Employee* p = new Employee[s];
// ...
delete[ ] p;
}
Itt a szksges memrit a
Employee::operator new[ ](sizeof(Employee)*s+delta)
hvs fogja biztostani, ahol a delta az adott fordttl fgg minimlis tbblet. A memrit
az albbi hvs szabadtja fel:
Employee::operator delete[ ](p); // felszabadt s*sizeof(Employee)+delta bjtot
Az elemek s szmt (illetve a delta-t) a rendszer megjegyzi. Ha a delete[ ]()-et egy- helyett
ktparamteres formban adtuk volna meg, a hvsban a msodik paramter
s*sizeof(Employee) +delta lett volna.
15.6.2. Virtulis konstruktorok
Miutn virtulis destruktorokrl hallottunk, nyilvnval a krds: lehetnek-e
a konstruktorok is virtulisak?. A rvid vlasz az, hogy nem. A kicsit hosszabb: nem, de
a kvnt hats knnyen elrhet. Egy objektum ltrehozshoz a konstruktornak tudnia kell
a ltrehozand objektum pontos tpust. Ezrt a konstruktor nem lehet virtulis. Radsul
a konstruktor nem egszen olyan, mint a kznsges fggvnyek. Pldul olyan mdokon
mkdik egytt a memriakezel eljrsokkal, ahogy a kznsges fggvnyek nem. Ezrt
nincs konstruktorra hivatkoz mutat.
Absztrakcis mdszerek 556
Mindkt megszorts megkerlhet egy konstruktort meghv s a ltrehozott objektumot
visszaad fggvny ksztsvel. Ez kedvez, mert gyakran hasznos lehet, ha a pontos t-
pus ismerete nlkl tudunk j objektumot ltrehozni. Az Ival_box_maker osztly (12.4.4)
pontosan erre a clra szolglt. Itt az tlet egy msik vltozatt mutatom be, ahol egy osztly
objektumai a felhasznliknak kpesek sajt maguk msolatt tadni:
class Expr {
public:
Expr(); // alaprtelmezett konstruktor
Expr(const Expr&); // msol konstruktor
virtual Expr* new_expr() { return new Expr(); }
virtual Expr* clone() { return new Expr(*this); }
// ...
};
Mivel a new_expr()-hez s a clone()-hoz hasonl fggvnyek virtulisak s kzvetett ton
objektumokat hoznak ltre, gyakran hvjk ket virtulis konstruktornak, br az elneve-
zs nmileg flrevezet. Mindegyik egy konstruktort hasznl, hogy megfelel objektumot
hozzon ltre.
Egy szrmaztatott osztly fellrhatja a new_expr() s/vagy clone() fggvnyt, hogy egy sa-
jt tpus objektumot adjon vissza:
class Cond : public Expr {
public:
Cond();
Cond(const Cond&);
Cond* new_expr() { return new Cond(); }
Cond* clone() { return new Cond(*this); }
// ...
};
Ez azt jelenti, hogy egy Expr osztly objektumhoz a felhasznl pontosan ugyanolyan t-
pus objektumot tud ltrehozni:
void user(Expr* p)
{
Expr* p2 = p->new_expr();
// ...
}
A p2-hz rendelt mutat megfelel, br ismeretlen tpus.
15. Osztlyhierarchik 557
A Cond::new_expr() s a Cond::clone() visszatrsi tpusa Cond* s nem Expr* , ezrt egy
Cond informciveszts nlkl lemsolhat (klnozhat):
void user2(Cond* pc, Expr* pe)
{
Cond* p2 = pc->clone();
Cond* p3 = pe->clone(); // hiba
// ...
}
A fellr fggvnyek tpusa ugyanaz kell, hogy legyen, mint a fellrt virtulis fggvny,
de a visszatrsi rtk tpusa kevsb szigoran kttt. Vagyis ha az eredeti visszatrsi r-
tk B* volt, akkor a fellr fggvny visszatrsi rtke lehet D* is, feltve, hogy B nyilv-
nos bzisosztlya D-nek. Ugyangy egy B& visszatrsi rtk D&-ra mdosthat.
Jegyezzk meg, hogy a paramtertpusok hasonl mdostsa tpushibhoz vezetne (lsd
15.8[12]).
15.7. Tancsok
[1] Ha tulajdonsgok unijt akarjuk kifejezni, hasznljunk kznsges tbbszrs
rkldst. 15.2, 15.2.4.
[2] A tulajdonsgoknak a megvalst kdtl val elvlasztsra hasznljunk tbb-
szrs rkldst. 15.2.5.
[3] Hasznljunk virtulis bzisosztlyt, ha egy hierarchia nmely (de nem mindegyik)
osztlyra nzve kzs dolgot akarunk kifejezni. 15.2.5.
[4] Kerljk a tpusknyszertst (cast). 15.4.5.
[5] Ha az adott osztlyhierarchia bejrsa elkerlhetetlen, hasznljuk
a dynamic_cast-ot. 15.4.1.
[6] A typeid helyett rszestsk elnyben a dynamic_cast-ot. 15.4.4.
[7] A protected-del szemben rszestsk elnyben a private-et. 15.3.1.1.
[8] Adattagokat ne adjunk meg vdettknt. 15.3.1.1.
[9] Ha egy osztly definilja az operator delete()-et, akkor legyen virtulis
destruktora. 15.6.
[10] A konstruktor vagy destruktor futsa alatt ne hvjunk virtulis fggvnyt. 15.4.3.
[11] Ritkn s lehetleg csak fellr virtulis fggvnyekben hasznljuk a tagne-
vek explicit minstst felolds cljra. 15.2.1.
Absztrakcis mdszerek 558
15.8. Gyakorlatok
1. (*1) rjunk olyan ptr_cast sablont, amely ugyangy mkdik, mint
a dynamic_cast, de a 0-val val visszatrs helyett bad_cast kivtelt vlt ki.
2. (*2) rjunk olyan programot, amely egy objektum ltrehozsa kzben az RTTI-
hez viszonytva mutatja be a konstruktorhvsok sorrendjt. Hasonlan mutassuk
be az objektum lebontst.
3. (*3.5) rjuk meg a Reversi/Othello trsasjtk egy vltozatt. Minden jtkos le-
hessen l szemly vagy szmtgp. Elszr a program helyes mkdsre
sszpontostsunk, s csak azutn arra, hogy a program annyira okos legyen,
hogy rdemes legyen ellene jtszani.
4. (*3) Javtsunk a 15.8[3]-beli jtk felhasznli felletn.
5. (*3) Ksztsnk egy grafikus objektumosztlyt egy mvelethalmazzal, amely
alapjn grafikus objektumok egy knyvtra szmra kzs bzisosztly lehet.
Nzznk bele egy grafikus knyvtrba, hogy ott milyen mveletek vannak. Hoz-
zunk ltre egy adatbzis-objektum osztlyt egy mvelethalmazzal, amely alapjn
elhihet, hogy kzs bzisosztlya az adatbzisban mezk soraknt trolt objek-
tumoknak. Vizsgljunk meg egy adatbzis-knyvtrat, hogy ott milyen mveletek
vannak. Hatrozzunk meg egy grafikus adatbzis-objektumot tbbszrs rkl-
dssel s anlkl, s hasonltsuk ssze a kt megolds elnyeit s htrnyait.
6. (*2) rjuk meg a 15.6.2-beli clone() mvelet egy olyan vltozatt, amely a para-
mterben megkapott Arena-ba (10.4.11) teszi a lemsolt objektumot. Ksztsnk
egy egyszer Arena-t mint az Arena-bl szrmaz osztlyt.
7. (*2) Anlkl, hogy belenznnk a knyvbe, rjunk le annyi C++-kulcsszt,
amennyit csak tudunk.
8. (*2) rjunk szabvnyos C++-programot, amelyben legalbb tz klnbz kulcs-
sz szerepel egyms utn, gy, hogy nincs azonostkkal, opertorokkal vagy
rsjelekkel elvlasztva.
9. (*2.5) Rajzoljuk le a 15.2.4-beli Radio objektum memriakiosztsnak egy lehets-
ges vltozatt. Magyarzzuk el, hogyan lehetne egy virtulis fggvnyt meghvni.
11. (*3) Gondoljuk meg, hogyan lehet a dynamic_cast-ot megvalstani. Ksztsnk
egy dcast sablont, amely gy viselkedik, mint a dynamic_cast, de csak az lta-
lunk meghatrozott fggvnyekre s adatokra tmaszkodik. Gondoskodjunk ar-
rl, hogy a rendszert a dcast vagy az elzleg megadott osztlyok megvltoztat-
sa nlkl lehessen j osztlyokkal bvteni.
12. (*2) Tegyk fel, hogy a fggvnyparamterek tpus-ellenrzsi szablyai a vissza-
trsi rtkre vonatkozakhoz hasonlan enyhtettek, teht egy Derived* para-
mter fggvny fellrhat egy Base*-ot. rjunk egy programot, ami konverzi
nlkl elrontana egy Derived tpus objektumot. rjuk le a paramtertpusokra
vonatkoz fellrsi szablyok egy biztonsgos enyhtst.
15. Osztlyhierarchik 559
Harmadik rsz
A standard knyvtr
Ebben a rszben a C++ standard knyvtrt mutatjuk be. Megvizsgljuk a knyvtr szerke-
zett s azokat az alapvet mdszereket, amelyeket az egyes elemek megvalstshoz
hasznltak. Clunk az, hogy megrtsk, hogyan kell hasznlni ezt a knyvtrat, illetve
szemlltessk azokat az ltalnos mdszereket, amelyeket tervezskor s programozskor
hasznlhatunk. Ezenkvl bemutatjuk azt is, hogyan bvthetjk a knyvtrat, pontosabban
a rendszer fejleszti hogyan kpzeltk el a tovbbfejlesztst.
Fejezetek
16. A knyvtr szerkezete s a trolk
17. Szabvnyos trolk
18. Algoritmusok s fggvnyobjektumok
19. Bejrk s memriafoglalk
20. Karakterlncok
21. Adatfolyamok
22. Szmok
...s te, Marcus, oly sok mindent adtl nekem, m n adok neked most egy jtancsot. Sok
ember lgy. Hagyd el a rgi jtkot, hogy mindig csak ugyanaz a Marcus Cocoza vagy.
Tlontl sokat vesszdtl mr Marcus Cocozval, mg vgl valsggal a rabszolgja lettl.
Szinte semmit sem teszel anlkl, hogy ne mrlegelnd, vajon milyen hatssal lesz Marcus
Cocoza boldogsgra s tekintlyre. Szntelen flelemben lsz, hogy Marcus Cocoza
esetleg valami ostobasgot kvet el, vagy elunja magt. Deht mit szmt mindez valjban?
Az emberek az egsz vilgon ostobasgokat mvelnek... Szeretnm ha felszabadulnl, ha
szved jra megtalln bkjt. Mostantl fogva ne csak egy, hanem tbb ember lgy,
annyi, amennyit csak el tudsz gondolni...
(Karen Blixen: lmodozk; Kertsz Judit fordtsa)
A knyvtr szerkezete s a trolk
jdonsg volt.
Egyedi volt. Egyszer volt.
Biztos, hogy sikeres lesz!
(H. Nelson)
Tervezsi szempontok a standard knyvtrhoz A knyvtr szerkezete Szabvnyos fejllo-
mnyok Nyelvi tmogats A trolk szerkezete Bejrk Bzisosztllyal rendelkez
trolk Az STL troli vector Bejrk Elemek elrse Konstruktorok Mdostk
Listamveletek Mret s kapacits vector<bool> Tancsok Gyakorlatok
16.1. A standard knyvtr
Minek kell szerepelnie a C++ standard knyvtrban? A programoz szempontjbl az t-
nik idelisnak, ha egy knyvtrban megtall minden olyan osztlyt, fggvnyt, s sablont,
ami rdekes, jelents s elgg ltalnos. A krds azonban most nem az, hogy egy knyv-
trban mi legyen benne, hanem az, hogy a standard knyvtr milyen elemeket tartalmaz-
zon. Az elbbi krds esetben sszer megkzelts, hogy minden elrhet legyen, az
utbbi esetben azonban ez nem alkalmazhat. A standard knyvtr olyan eszkz, amelyet
minden C++-vltozat tartalmaz, gy minden programoz szmthat r.
16
A C++ standard knyvtra a kvetkez szolgltatsokat nyjtja:
1. Tmogatja a nyelv lehetsgeinek hasznlatt, pldul a memriakezelst
(6.2.6) s a futsi idej tpusinformci (RTTI, 15.4) kezelst.
2. Informcikat ad az adott nyelvi vltozat egyedi tulajdonsgairl, pldul
megadja a legnagyobb float rtket (22.2).
3. Elrhetv teszi azokat a fggvnyeket, amelyeket nem lehet a nyelven bell
optimlisan elkszteni minden rendszer szmra (pldul sqrt(), 22.3
vagy memmove(), 19.4.6).
4. Olyan magas szint szolgltatsokat nyjt, amelyekre a programoz ms rend-
szerre tvihet (hordozhat) programok ksztsekor tmaszkodhat, pldul
listkat (17.2.2), asszociatv tmbket (map) (17.4.1), rendez fggvnyeket
(18.7.1) s bemeneti/kimeneti adatfolyamokat (21. fejezet).
5. Keretet biztost a knyvtr elemeinek tovbbfejlesztsre, pldul szablyokkal
s eszkzkkel segti, hogy a felhasznl ugyanolyan bemeneti/kimeneti felle-
tet biztosthasson sajt tpusaihoz, mint a knyvtr a beptett tpusokhoz.
6. Tovbbi knyvtrak kzs alapjul szolgl.
Nhny tovbbi eszkzt pldul a vletlenszm-ellltkat (22.7) csak azrt helyeztek
a standard knyvtrba, mert gy hasznlatuk knyelmes s hagyomnyosan itt a helyk.
A knyvtr tervezsekor leginkbb az utols hrom szerepkrt vettk figyelembe. Ezek
a szerepek ersen sszefggnek. A hordozhatsg pldul olyan ltalnos fogalom, amely
fontos tervezsi szempont minden egyedi cl knyvtr esetben. A kzs troltpusok
(pldul a listk vagy asszociatv tmbk) pedig igen jelentsek a kln fejlesztett knyv-
trak knyelmes egyttmkdsnek biztostsban.
Az utols pont klnsen fontos a tervezs szempontjbl, mert ezzel korltozhat a stan-
dard knyvtr hatkre s gtat szabhatunk a szolgltatsok znnek. A karakterlnc- s
listakezel lehetsgek pldul a standard knyvtrban kaptak helyet. Ha ez nem gy tr-
tnt volna, akkor a kln fejlesztett knyvtrak csak a beptett tpusok segtsgvel m-
kdhetnnek egytt egymssal. Ugyanakkor a mintailleszt s a grafikai lehetsgek nem
szerepelnek itt, mert br tagadhatatlanul szles krben hasznlatosak nem kimondottan
a kln fejlesztett knyvtrak kztti egyttmkdst szolgljk.
Ha egy lehetsg nem felttlenl szksges a fenti szerepkrk teljestshez, akkor azt
megvalsthatjuk kln, a standard knyvtron kvl is. Azzal, hogy kihagyunk egy szolgl-
tatst a standard knyvtrbl, azt a lehetsget is nyitva hagyjuk a tovbbi knyvtrak sz-
mra, hogy ugyanazt az tletet ms-ms formban valstsk meg.
A standard knyvtr 564
16.1.1. Tervezsi korltozsok
A standard knyvtrak szerepkreibl szmos korltozs kvetkezik a knyvtr szerkeze-
tre nzve. A C++ standard knyvtra ltal knlt szolgltatsok tervezsekor az albbi
szempontokat tartottk szem eltt:
1. Komoly segtsget jelentsen lnyegben minden tanul s profi programoz
szmra, kzjk rtve a tovbbi knyvtrak fejlesztit is.
2. Kzvetve vagy kzvetlenl minden programoz felhasznlhassa az sszes olyan
clra, ami a knyvtr hatskrbe esik.
3. Elg hatkony legyen ahhoz, hogy a ksbbi knyvtrak ellltsban komoly
vetlytrsa legyen a sajt kezleg ellltott fggvnyeknek, osztlyoknak s
sablonoknak.
4. Mentes legyen az eljrsmd szablyozstl, vagy lehetsget adjon r, hogy
a felhasznl paramterknt hatrozza meg az eljrsmdot.
5. Legyen primitv, a matematikai rtelemben. Egy olyan sszetev, amely kt,
gyengn sszefgg feladatkrt tlt be, kevsb hatkony, mint kt nll
komponens, amelyeket kimondottan az adott szerep betltsre fejlesztettek ki.
6. Legyen knyelmes, hatkony s elg biztonsgos a szoksos felhasznlsi ter-
leteken.
7. Nyjtson teljeskr szolgltatsokat ahhoz, amit vllal. A standard knyvtr fon-
tos feladatok megvalstst is rhagyhatja ms knyvtrakra, de ha egy feladat
teljestst vllalja, akkor elegend eszkzt kell biztostania ahhoz, hogy az
egyes felhasznlknak s fejlesztknek ne kelljen azokat ms mdszerekkel
helyettestenik.
8. Legyen sszhangban a beptett tpusokkal s mveletekkel s biztasson azok
hasznlatra.
9. Alaprtelmezs szerint legyen tpusbiztos (mindig helyesen kezelje a klnbz
tpusokat).
10. Tmogassa az ltalnosan elfogadott programozsi stlusokat.
11. Legyen bvthet gy, hogy a felhasznl ltal ltrehozott tpusokat hasonl
formban kezelhessk, mint a beptett s a standard knyvtrban meghatro-
zottakat.
Rossz megolds pldul, ha egy rendez fggvnybe beptjk az sszehasonltsi md-
szert, hiszen ugyanazok az adatok ms-ms szempont szerint is rendezhetk. Ezrt a C stan-
dard knyvtrnak qsort() fggvnye paramterknt veszi t az sszehasonltst vgz
fggvnyt, s nem valamilyen rgztett mveletre (pldul a < opertorra) hivatkozik (7.7).
Msrszrl ebben a megoldsban minden egyes sszehasonlts alkalmval meg kell hv-
16. A knyvtr szerkezete s a trolk 565
nunk egy fggvnyt, ami tbbletterhet jelent a qsort() eljrst felhasznl tovbbi eljrsok-
ban. Szinte minden adattpus esetben knnyen elvgezhetjk az sszehasonltst, anlkl,
hogy fggvnyhvssal rontannk a rendszer teljestmnyt.
Jelents ez a teljestmnyromls? A legtbb esetben valsznleg nem. Egyes algoritmusok
esetben azonban ezek a fggvnyhvsok adjk a vgrehajtsi id jelents rszt, ezrt
ilyenkor a felhasznlk ms megoldsokat fognak keresni. Ez a problmt a 13.4 pontban
oldottuk meg, ahol bemutattuk, hogyan adhatunk meg sszehasonltsi felttelt egy sablon-
paramterrel. A plda szemlltette, hogy a hatkonysg s az ltalnossg kt egymssal
szemben ll kvetelmny. Egy szabvny knyvtrnak azonban nem csak meg kell vals-
tania feladatait, hanem elg hatkonyan kell azokat elvgeznie ahhoz, hogy a felhasznlk-
nak eszbe se jusson sajt eljrsokat rni az adott clra. Ellenkez esetben a bonyolultabb
eszkzk fejleszti knytelenek kikerlni a standard knyvtr szolgltatsait, hiszen csak
gy maradhatnak versenykpesek. Ez pedig nem csak a knyvtrak fejlesztinek okoz sok
tbbletmunkt, hanem azon felhasznlk lett is megnehezti, akik platformfggetlen
programokat szeretnnek kszteni vagy tbb, kln fejlesztett knyvtrat szeretnnek
hasznlni.
A primitvsg s a knyelmessg a szoksos felhasznlsi terleteken kvetelmnyek is
szemben llnak egymssal. Egy szabvny knyvtrban az els kvetelmny azonnal kizr
minden optimalizlsi lehetsget arra nzve, hogy felkszljnk a gyakori esetekre.
Az olyan sszetevk azonban, amelyek ltalnos, de nem primitv szolgltatsokat nyjta-
nak, szerepelhetnek a standard knyvtrban a primitvek mellett, de nem helyettk.
A klcsns kizrs nem akadlyozhat bennnket abban, hogy mind a profi, mind az alkal-
mi programoz lett egyszerbb tegyk. Azt sem engedhetjk meg, hogy egy sszetev
alaprtelmezett viselkedse homlyos vagy veszlyes legyen.
16.1.2. A standard knyvtr szerkezete
A standard knyvtr szolgltatsait az std nvtrben definiltk s fejllomnyok segtsg-
vel rhetjk el azokat. Ezek a fejllomnyok jelzik a knyvtr legfontosabb rszeit, gy fel-
sorolsukbl ttekintst kaphatunk a knlt eszkzkrl. Ezek szerint nzzk vgig
a knyvtrat a most kvetkez fejezetekben is.
Ezen alfejezet tovbbi rszben a fejllomnyokat soroljuk fel, szerepeik szerint csoporto-
stva. Mindegyikrl adunk egy rvid lerst is s megemltjk, hol tallhat rszletes elem-
zsk. A csoportostst gy vlasztottuk meg, hogy illeszkedjen a standard knyvtr szer-
kezethez. Ha a szabvnyra vonatkoz hivatkozst adunk meg (pldul s.18.1), akkor az
adott lehetsget itt nem vizsgljuk rszletesen.
A standard knyvtr 566
Ha egy szabvnyos fejllomny neve c betvel kezddik, akkor az egy szabvnyos C
knyvtrbeli fejllomny megfelelje. Minden <X.h> fejllomnyhoz, amely a C standard
knyvtrnak rszeknt neveket ad meg a globlis nvtrben, ltezik egy <cX> megfelel,
amely ugyanazon neveket az std nvtrbe helyezi (9.2.2).
A multimap s multiset asszociatv trolkat sorrendben a <map>, illetve a <set> llomny-
ban tallhatjuk meg. A priority_queue osztly a <queue> fjlban szerepel.
A <memory> fejllomny tartalmazza az auto_ptr sablont is, amely elssorban arra hasznl-
hat, hogy simbb tegyk a mutatk s kivtelek egyttmkdst (14.4.2).
16. A knyvtr szerkezete s a trolk 567
Trolk
<vector> T tpus elemek egydimenzis tmbje 16.3
<list> T tpus elemek ktirny listja 17.2.2
<deque> T tpus elemek ktvg sora 17.2.3
<queue> T tpus elemekbl kpzett sor 17.3.2
<stack> T tpus elemekbl kpzett verem 17.3.1
<map> T tpus elemek asszociatv tmbje 17.4.1
<set> T tpus elemek halmaza 17.4.3
<bitset> logikai rtkek tmbje 17.5.3
ltalnos eszkzk
<utility> opertorok s prok 17.1.4, 17.4.1.2
<functional> fggvnyobjektumok 18.4
<memory> memriafoglalk a trolkhoz 19.4.4
<ctime> C stlus dtum- s idkezels s.20.5
Bejrk
<iterator> bejrk s kezelsk 19. fejezet
A bejrk (iterator) lehetv teszik, hogy a szabvnyos algoritmusokat ltalnosan hasznl-
hassuk a szabvnyos trolkban s ms hasonl tpusokban (2.7.2, 19.2.1).
Egy szokvnyos ltalnos algoritmus egyformn alkalmazhat brmilyen tpus elemek
brmilyen sorozatra (3.8, 18.3). A C standard knyvtrban szerepl bsearch() s qsort()
fggvnyek csak a beptett tmbkre hasznlhatk, melyek elemeihez a felhasznl nem
hatrozhat meg sem msol konstruktort, sem destruktort (7.7).
A kivtelkezelsre tmaszkod hibaellenrzst a 24.3.7.1 pontban vizsgljuk meg.
A standard knyvtr 568
Algoritmusok
<algorithm> ltalnos algoritmusok 18.fejezet
<cstdlib> bsearch() qsort() 18.11
Ellenrzsek, diagnosztika
<exception> kivtelosztly 14.10
<stdexcept> szabvnyos kivtelek 14.10
<cassert> hibaellenrz makr 24.3.7.2
(felttelezett rtk biztostsa)
<cerrno> C stlus hibakezels 20.4.1
Karakterlncok
<string> T tpus elemekbl ll karakterlnc 20. fejezet
<cctype> karakterek osztlyozsa 20.4.2
<cwtype> szles karakterek osztlyozsa 20.4.2
<cstring> C stlus karakterlncokat 20.4.1
kezel fggvnyek
<cwchar> C stlus szles karakterlncok kezelse 20.4
<cstdlib> C stlus karakterlncokat 20.4.1
kezel fggvnyek
A <cstring> fejllomnyban szerepelnek az strlen(), strcpy() stb. fggvnyek. A <cstdlib>
adja meg az atof() s atoi() fggvnyeket, amelyek a C stlus karakterlncokat alaktjk
szmrtkekre.
Az adatfolyam-mdostk (manipulator) olyan objektumok, melyek segtsgvel az adatfo-
lyamok llapott megvltoztathatjuk (21.4.6). (Pldul mdosthatjuk a lebegpontos sz-
mok kimeneti formtumt.)
A locale az olyan kulturlis eltrsek meghatrozsra szolgl, mint a dtumok rsmdja,
a pnzegysgek jellsre hasznlt szimblumok vagy a karakterlncok rendezsre vonat-
koz szablyok, melyek a klnbz termszetes nyelvekben s kultrkban jelentsen
eltrhetnek.
16. A knyvtr szerkezete s a trolk 569
Ki- s bemenet
<iosfwd> elzetes deklarcik 21.1
az I/O szolgltatsokhoz
<iostream> szabvnyos bemeneti adatfolyamok 21.2.1
objektumai s mveletei
<ios> bemeneti adatfolyamok bzisosztlyai 21.2.1
<streambuf> tmeneti tr adatfolyamokhoz 21.6
<istream> bemeneti adatfolyam sablon 21.3.1
<ostream> kimeneti adatfolyam sablon 21.2.1
<iomanip> adatfolyam-mdostk (manipultorok) 21.4.6.2
<sstream> adatfolyamok 21.5.3
karakterlncbl/karakterlncba
<cstdlib> karakterosztlyoz fggvnyek 20.4.2
<fstream> adatfolyamok fjlokbl/fjlokba 21.5.1
<cstdio> a printf() fggvnycsald 21.8
<cwchar> printf() szolgltatsok 21.8
szles karakterekre
Nemzetkzi szolgltatsok
<locale> kulturlis eltrsek brzolsa 21.7
<clocale> kulturlis eltrsek brzolsa C stlusban 21.7
A <cstddef> fejllomny hatrozza meg azt a tpust, amit a sizeof() fggvny visszaad
(size_t), a mutatknak egymsbl val kivonsakor keletkez rtk tpust (ptrdiff_t) s
a hrhedt NULL makrt (5.1.1).
A hagyomnyt kvetve az abs(), s div() fggvny a <cstdlib> fejllomnyban tallhat, br
matematikai fggvnyek lvn jobban illennek a <cmath> fjlba. (22.3)
A felhasznlk s a knyvtrak fejleszti nem bvthetik vagy szkthetik a szabvnyos fej-
llomnyokban szerepl deklarcik krt. A fejllomnyok jelentst gy sem mdost-
hatjuk, hogy megprblunk makrkat megadni az llomnyok beszerkesztse eltt vagy
megvltoztatjuk a deklarcik jelentst azzal, hogy sajt deklarcikat ksztnk a fejllo-
A standard knyvtr 570
A programnyelvi elemek tmogatsa
<limits> numerikus rtkhatrok 22.2
<climits> C stlus, numerikus, skalr rtk- 22.2.1
hatrokat megad makrk
<cfloat> C stlus, numerikus, lebegpontos 22.2.1
rtkhatrokat megad makrk
<new> dinamikus memriakezels 16.1.3
<typeinfo> futsi idej tpusazonosts tmogatsa 15.4.1
<exception> kivtelkezels tmogatsa 14.10
<cstddef> C knyvtrak nyelvi tmogatsa 6.2.1
<cstdarg> vltoz hosszsg paramterlistval 7.6
rendelkez fggvnyek kezelse
<csetjmp> C stlus verem-visszatekers s.18.7
<cstdlib> programbefejezs 9.4.1.1
<ctime> rendszerra D.4.4.1
<csignal> C stlus szignlkezels D.4.4.1
Numerikus rtkek
<complex> komplex szmok s mveletek 22.5
<valarray> numerikus vektorok s mveletek 22.4
<numeric> ltalnostott numerikus mveletek 22.6
<cmath> ltalnos matematikai mveletek 22.3
<cstdlib> C stlus vletlenszmok 22.7
mny krnyezetben (9.2.3). Brmely program, amely nem tartja be ezeket a jtkszab-
lyokat, nem nevezheti magt a szabvnyhoz igazodnak, s az ilyen trkkket alkalmaz
programok ltalban nem vihetk t ms rendszerre. Lehet, hogy ma mkdnek, de az
adott krnyezet legkisebb vltozsa hasznlhatatlann teheti ket. Tartzkodjunk az ilyen
szemfnyvesztstl.
Ahhoz, hogy a standard knyvtr valamely lehetsget hasznlhassuk, a megfelel fejllo-
mnyt be kell ptennk (#include). Nem jelent a szabvnynak megfelel megoldst, ha
a megfelel deklarcikat sajt kezleg adjuk meg programunkban. Ennek oka az, hogy bi-
zonyos nyelvi vltozatok a beptett szabvnyos fejllomnyok alapjn optimalizljk a for-
dtst, msok pedig a standard knyvtr szolgltatsait optimalizljk, attl fggen, milyen
fejllomnyokat hasznltunk. ltalban igaz, hogy a fejlesztk olyan formban hasznlhat-
jk a szabvnyos fejllomnyokat, amelyre a programozk nem kszlhetnek fel s prog-
ramjaik ksztsekor nem is kell tudniuk ezek szerkezetrl.
Ezzel szemben a programoz specializlhatja a szolgltats-sablonokat, pldul a swap()
sablon fggvnyt (16.3.9), gy ezek jobban igazodhatnak a nem szabvnyos knyvtrak-
hoz s a felhasznli tpusokhoz.
16.1.3. Nyelvi elemek tmogatsa
A standard knyvtrnak egy kis rsze a nyelvi elemek tmogatsval foglalkozik, azaz
olyan szolgltatsokat nyjt, amelyekre a program futtatshoz azrt van szksg, mert
a nyelv eszkzei ezektl fggen mkdnek.
Azon knyvtri fggvnyeket, amelyek a new s delete opertorok kezelst segtik,
a 6.2.6, a 10.4.11, a 14.4.4 s a 15.6 pontban mutattuk be. Ezek a <new> fejllomnyban
szerepelnek.
A futsi idej tpusazonosts elssorban a type_info osztlyt jelenti, amely a <typeinfo> fej-
llomnyban tallhat s a 15.4.4 pont rt le.
A szabvnyos kivtelek osztlyait a 14.10 pontban vizsgltuk, helyk a <new>,
a <typeinfo>, az <ios>, az <exception>, illetve az <stdexcept> fejllomnyban van.
A program elindtsrl s befejezsrl a 3.2, a 9.4 s a 10.4.9 pontban volt sz.
16. A knyvtr szerkezete s a trolk 571
16.2. A trolk szerkezete
A trol (container) olyan objektum, amely ms objektumokat tartalmaz. Pldakppen meg-
emlthetjk a listkat (list), a vektorokat (vector) s az asszociatv tmbket (map). ltalban
lehetsgnk van r, hogy a trolkban objektumokat helyezznk el vagy eltvoltsuk azo-
kat onnan. Termszetesen ez az tlet szmtalan formban megvalsthat. A C++ standard
knyvtrnak trolit kt felttel szem eltt tartsval fejlesztettk ki: biztostsk a lehet leg-
nagyobb szabadsgot a felhasznlnak j, egyni trolk fejlesztsben, ugyanakkor nyjt-
sanak hasonl kezeli felletet. Ez a megkzelts lehetv teszi, hogy a trolk hatkony-
sga a lehet legnagyobb legyen s a felhasznlk olyan programot rhassanak, amelyek
fggetlenek az ppen hasznlt trol tpustl.
A trolk tervezi ltalban vagy csak az egyik, vagy csak a msik felttellel foglalkoznak.
A standard knyvtrban szerepl trolk s algoritmusok bemutatjk, hogy lehetsges egy-
szerre ltalnos s hatkony eszkzket ltrehozni. A kvetkezkben kt hagyomnyos
troltpus erssgeit s gyengesgeit mutatjuk be, gy megismerkedhetnk a szabvnyos
trolk szerkezetvel.
16.2.1. Egyedi cl trolk s bejrk
A legkzenfekvbb megkzelts egy vektor s egy lista megvalstsra az, hogy mindket-
tt olyan formban ksztjk el, ami legjobban megfelel a tervezett cloknak:
template<class T> class Vector { // optimlis
public:
explicit Vector(size_t n); // kezdetben n darab, T() rtk elemet trol
T& operator[ ](size_t); // indexels
// ...
};
template<class T> class List { // optimlis
public:
class Link { /* ... */ };
List(); // kezdetben res
void put(T*); // az aktulis elem el helyezs
T* get(); // az aktulis elem megszerzse
// ...
};
A standard knyvtr 572
Mindkt osztly olyan mveleteket knl, amely idelis felhasznlsukhoz, s mindkettben
szabadon kivlaszthatjuk a megfelel brzolst, anlkl, hogy ms troltpusokkal foglal-
koznnk. Ez lehetv teszi, hogy a mveletek megvalstsa kzel optimlis legyen. Kl-
nsen fontos, hogy a leggyakrabban hasznlt mveletek (teht a put() a List esetben, illet-
ve az operator[ ]() a Vector esetben) egszen rvidek s knnyen optimalizlhatak (pl.
helyben (inline) kifejthetk) legyenek.
A trolk egyik leggyakoribb felhasznlsi mdja, hogy egyms utn vgiglpkednk a t-
rolban trolt elemeken. Ezt nevezzk a trol bejrsnak, a feladat megvalstshoz
pedig ltalban ltrehozunk egy bejr (iterator) osztlyt, amely megfelel az adott trol
tpusnak. (11.5 s 11.14[7])
Bizonyos esetekben, amikor a felhasznl bejr egy trolt, nem is akarja tudni, hogy az
adatokat tnylegesen egy listban vagy egy vektorban troljuk. Ezekben a helyzetekben
a bejrsnak nem szabad attl fggnie, hogy a List vagy a Vector osztlyt hasznltuk. Az ide-
lis az, ha mindkt esetben pontosan ugyanazt a programrszletet hasznlhatjuk.
A megoldst a bejr (iterator) osztly jelenti, amely biztost egy krem a kvetkezt m-
veletet, amely minden trol szmra megvalsthat. Pldul:
template<class T> class Itor { // kzs fellet (absztrakt osztly 2.5.4, 12.3)
public:
// 0 visszaadsval jelezzk, hogy nincs tbb elem
virtual T* first() = 0; // mutat az els elemre
virtual T* next() = 0; // mutat a kvetkez elemre
};
Ezt az osztlyt ksbb elkszthetjk kln a Vector s kln a List osztlyhoz:
template<class T> class Vector_itor : public Itor<T> { // vektor-megvalsts
Vector<T>& v;
size_t index; // az aktulis elem indexrtke
public:
Vector_itor(Vector<T>& vv) : v(vv), index(0) { }
T* first() { return (v.size()) ? &v[index=0] : 0; }
T* next() { return (++index<v.size()) ? &v[index] : 0; }
};
template<class T> class List_itor : public Itor<T> { // lista-megvalsts
List<T>& lst;
List<T>::Link p; // az aktulis elemre mutat
16. A knyvtr szerkezete s a trolk 573
public:
List_itor(List<T>&);
T* first();
T* next();
};
Grafikus formban (szaggatott vonallal jelezve a megvalsts a felhasznlsval
kapcsolatot):
A kt bejr (iterator) bels szerkezete jelentsen eltr, de ez a felhasznl szmra ltha-
tatlan marad. Ezek utn brmit bejrhatunk, amire az Itor osztlyt meg tudjuk valstani.
Pldul:
int count(Itor<char>& ii, char term)
{
int c = 0;
for (char* p = ii.first(); p; p=ii.next()) if (*p==term) c++;
return c;
}
Van azonban egy kis problma. A bejrn elvgzend mvelet lehet rendkvl egyszer,
mgis mindig vgre kell hajtanunk egy (virtulis) fggvnyhvst. A legtbb esetben ez
a teljestmnyromls nem jelent nagy vesztesget az egyb tevkenysgek mellett. A nagy-
teljestmny rendszerekben azonban gyakran ppen egy egyszer trol gyors bejrsa je-
lenti a ltfontossg feladatot, s a fggvnyek meghvsa sokkal kltsgesebb lehet,
mint egy egsz szmokkal vgzett sszeads vagy egy mutatrtk-szmts (ami a Vector
s a List esetben megvalstja a next() fggvnyt). Ezrt a fenti modell nem hasznlhat,
vagy legalbbis nem tkletes egy szabvnyos knyvtr szolgltatsaknt.
A standard knyvtr 574
Vector List
Itor
Vector_itor List_itor
Ennek ellenre a trol-bejr szemllet nagyon jl hasznlhat sok rendszer esetben.
vekig ez volt programjaim kedvenc megoldsa. Az elnyket s a htrnyokat az albbi-
akkal foglalhatjuk ssze:
+ Az nll trolk (container) egyszerek s hatkonyak.
+ A trolknak nem kell hasonltaniuk egymsra. A bejr (iterator) s a beburko-
l (wrapper) osztlyok (25.7.1) segtsgvel a kln fejlesztett trolkat is egy-
sges formban rhetjk el.
+ A kzs felhasznli felletet a bejrk biztostjk (nem egy ltalnos
troltpus, 16.2.2).
+ Ugyanahhoz a trolhoz tbb bejrt is definilhatunk a klnbz ignyeknek
megfelelen.
+ A trolk alaprtelmezs szerint tpusbiztosak s homognek (azaz a trol
minden eleme ugyanolyan tpus). Heterogn trolkat gy hozhatunk ltre,
hogy olyan mutatkbl hozunk ltre egy homogn trolt, amelyek azonos
ssel rendelkez objektumokra mutatnak.
+ A trolk nem tolakodk (non-intrusive), (azaz nem ignylik, hogy a trol tag-
jai egy kzs stl szrmazzanak, vagy valamilyen hivatkoz mezvel rendel-
kezzenek). A nem tolakod trolk jl hasznlhatak a beptett tpusok, vagy
a mi hatskrnkn kvl ltrehozott adatszerkezetek esetben.
- Minden bejrn keresztli hozzfrs egy virtulis fggvnyhvssal rontja
a rendszer hatkonysgt. Az egyszer hozzfrsi eljrsokhoz kpest ez
a mdszer komoly idvesztesget is jelenthet.
- A bejr-osztlyok hierarchija knnyen ttekinthetetlenn vlhat.
- Semmilyen kzs alapot nem tallhatunk a klnbz trolk kztt, mg ke-
vsb a trolkban trolt objektumok kztt. Ez megnehezti az olyan ltalnos
feladatokra val felkszlst, mint a perzisztencia (persistence) biztostsa vagy
az objektum be- s kivitel.
A + jellel az elnyket, - jellel a htrnyokat soroltuk fel.
A bejrk ltal biztostott rugalmassgra kln felhvnnk a figyelmet. Egy olyan kzs fel-
hasznli fellet, mint az Itor, akkor is megvalsthat, amikor a trol (esetnkben
a Vector s a List) megtervezse s elksztse mr rg megtrtnt. Amikor a trolt tervez-
zk, ltalban konkrt formban gondolkodunk, pldul egy tmbt vagy egy listt ksz-
tnk. Csak ksbb ltjuk meg az elvont brzols azon lehetsgeit, amelyek egy adott
krnyezetben egyarnt alkalmazhatak a tmbkre s a listkra is.
Ezt a ksei elvonatkoztatst tulajdonkppen tbbszr is elvgezhetjk. Pldul kpzeljk
el, hogy egy halmazt szeretnnk ltrehozni. A halmaz teljesen ms jelleg elvont brzols,
16. A knyvtr szerkezete s a trolk 575
mint az Itor, de ugyanazzal a mdszerrel, amelyet az Itor esetben alkalmaztunk, kszthe-
tnk egy halmaz jelleg felletet is a Vector s a List osztlyhoz:
Ezrt a ksei elvonatkoztats az absztrakt osztlyok segtsgvel lehetv teszi, hogy
ugyanazokat a fogalmakat tbb, klnbz formban is brzolhassuk, s ez akkor is igaz,
ha az egyes megvalstsokban semmilyen hasonlsg sincs. A listk s a vektorok eset-
ben mg tallhatunk nhny nyilvnval hasonlsgot, de az Itor felletet akr egy istream
szmra is knnyen elkszthetnnk.
Elmletileg a listban szerepl utols kt pont jelenti a szemllet igazi htrnyt. Ez azt je-
lenti, hogy ez a megkzelts mg akkor sem lehet idelis megolds egy szabvnyos knyv-
trban, ha a bejrk fggvnyhvsaibl illetve a hasonl trol-felletekbl ered teljest-
mnyromlst sikerlt is kikszblnnk (amire bizonyos helyzetekben lehetsg van).
A nem tolakod (non-intrusive) trolk nhny esetben egy kicsit lassabbak s kicsit tbb
helyet ignyelnek, mint a tolakod trolk. n magam ezt nem tartom gondnak; ha mgis
az lenne, az Itor-hoz hasonl bejrkat tolakod trolkhoz is elkszthetjk (16.5[11]).
16.2.2. Trolk kzs ssel
Egy tolakod trol elkszthet anlkl is, hogy sablonokat (template) hasznlnnk vagy
brmely ms mdon paramtereket adnnk meg a tpushoz:
struct Link {
Link* pre;
Link* suc;
// ...
};
A standard knyvtr 576
Vektor List
Set Itor
Vector_set Vector_itor List_set List_itor
class List {
Link* head;
Link* curr; // az aktulis elem
public:
Link* get(); // az aktulis elem eltvoltsa s visszaadsa
void put(Link*); // beszrs az aktulis elem el
// ...
};
A List itt nem ms, mint Link tpus objektumok listja, azaz olyan objektumok trolsra
kpes, amelyek a Link adatszerkezetbl szrmaznak:
class Ship : public Link { /* ... */ };
void f(List* lst)
{
while (Link* po = lst->get()) {
if (Ship* ps = dynamic_cast<Ship*>(po)) { // a Ship-nek tbbalaknak kell lennie
// (15.4.1)
// a ship hasznlata
}
else {
// hopp, valami mst csinlunk
}
}
}
A Simula ebben a stlusban hatrozta meg szabvnyos trolit, teht ezt a megoldst tekint-
hetjk az objektumorientlt programozst tmogat nyelvek eredeti szemlletnek. Napja-
inkban minden objektum kzs bzisosztlynak neve Object vagy valami hasonl.
Az Object osztly ltalban szmos ms szolgltatst is nyjt azon kvl, hogy sszekapcsol-
ja a trolkat.
Ez a szemllet gyakran (br nem szksgszeren) kiegszl egy ltalnos troltpussal is:
class Container : public Object {
public:
virtual Object* get(); // az aktulis elem eltvoltsa s visszaadsa
virtual void put(Object*); // beszrs az aktulis elem el
virtual Object*& operator[ ](size_t); // indexels
// ...
};
16. A knyvtr szerkezete s a trolk 577
Figyeljk meg, hogy a Container osztlyban szerepl mveletek virtulisak, gy a klnb-
z trolk sajt ignyeiknek megfelelen fellrhatjk azokat:
class List : public Container {
public:
Object* get();
void put(Object*);
// ...
};
class Vector : public Container {
public:
Object*& operator[ ](size_t);
// ...
};
Sajnos azonnal jelentkezik egy problma. Milyen mveleteket kell biztostania a Container
osztlynak? Csak azok a fggvnyek szerepelhetnek, amelyeket minden trol meg tud va-
lstani, de az sszes trol mvelethalmaznak metszete nevetsgesen szk fellet. St, az
az igazsg, hogy az igazn rdekes esetekben ez a metszet teljesen res. Teht gyakorlati-
lag a tmogatni kvnt troltpusokban szerepl, lnyeges mveletek unijt kell szerepel-
tetnnk. A szolgltatsokhoz biztostott felletek ilyen unijt kvr vagy bsges felletnek
nevezzk. (fat interface, 24.4.3)
Vagy e fellet lersban ksztnk valamilyen alaprtelmezett vltozatot a fggvnyekhez,
vagy azokat tisztn virtulisakk (pure virtual) tve arra ktelezzk a szrmaztatott oszt-
lyokat, hogy k fejtsk ki a fggvnyeket. Mindkt esetben sok-sok olyan fggvnyt
kapunk, amelyek egyszeren futsi idej hibt vltanak ki:
class Container : public Object {
public:
struct Bad_op { // kivtelosztly
const char* p;
Bad_op(const char* pp) :p(pp) { }
};
virtual void put(Object*) { throw Bad_op("put-hiba"); }
virtual Object* get() { throw Bad_op("get-hiba"); }
virtual Object*& operator[ ](int) { throw Bad_op("[ ]"); }
// ...
};
A standard knyvtr 578
Ha vdekezni szeretnnk azon lehetsggel szemben, hogy egy trol nem tmogatja
a get() fggvny hasznlatt, el kell kapnunk valahol a Container::Bad_op kivtelt. Ezek
utn a korbbi Ship pldt a kvetkez formban valsthatjuk meg:
class Ship : public Object { /* ... */ };
void f1(Container* pc)
{
try {
while (Object* po = pc->get()) {
if (Ship* ps = dynamic_cast<Ship*>(po)) {
// a ship hasznlata
}
else {
// hopp, valami mst csinlunk
}
}
}
catch (Container::Bad_op& bad) {
// hopp, valami mst csinlunk
}
}
Ez gy tlsgosan bonyolult, ezrt a Bad_op kivtel ellenrzst rdemes mshova helyez-
nnk. Ha szmthatunk r, hogy a kivteleket mshol kezelik, akkor a pldt az albbi for-
mra rvidthetjk:
void f2(Container* pc)
{
while (Object* po = pc->get()) {
Ship& s = dynamic_cast<Ship&>(*po);
// a Ship hasznlata
}
}
Ennek ellenre az az rzsnk, hogy a futsi idej tpusellenrzs stlustalan s nem is tl
hatkony. Ezrt inkbb a statikus tpusellenrzs mellett maradunk:
void f3(Itor<Ship>* i)
{
while (Ship* ps = i->next()) {
// a Ship hasznlata
}
}
16. A knyvtr szerkezete s a trolk 579
A troltervezs objektumok kzs bzisosztllyal megkzeltsnek elnyeit s htr-
nyait az albbiakkal foglalhatjuk ssze (nzzk meg a 16.5[10] feladatot is):
- A klnbz trolkon vgzett mveletek virtulis fggvnyhvst eredmnyez-
nek.
- Minden trolt a Container osztlybl kell szrmaztatnunk. Ennek kvetkezt-
ben kvr felletet kell ksztennk, s nagy mrtk elreltsra, illetve futsi
idej tpusellenrzsre van szksg. Egy kln fejlesztett trolt egy ltalnos
keretbe szortani a legjobb esetben is krlmnyes (16.5[12]).
+ A kzs Container bzisosztly egyszer megoldst knl olyan trolk fejlesz-
tshez, amelyek hasonl mveleteket biztostanak.
- A trolk heterognek s alaprtelmezs szerint nem tpusbiztosak. (Mindssze
arra szmthatunk, hogy az elemek tpusa Object *.) Ha szksg van r, sablo-
nok (template) segtsgvel hozhatunk ltre tpusbiztos s homogn trolkat.
- A trolk tolakodak (ami esetnkben azt jelenti, hogy minden elemnek az
Object osztlybl kell szrmaznia). Beptett tpusokba tartoz objektumokat,
illetve a mi hatskrnkn kvl meghatrozott adatszerkezeteket kzvetlenl
nem helyezhetnk el bennk.
- A trolbl kiemelt elemekre megfelel tpuskonverzit kell alkalmaznunk,
mieltt hasznlhatnnk azokat.
+ A Container s az Object osztly olyan szolgltatsok kialaktshoz hasznlha-
t, amelyek minden trolra vagy minden objektumra alkalmazhatk. Ez nagy-
mrtkben leegyszersti az olyan ltalnos szolgltatsok megvalstst, mint
a perzisztencia biztostsa vagy az objektumok ki- s bevitele.
Ugyangy, mint korbban (16.2.1), a + az elnyket, a - a htrnyokat jelli.
Az egymstl fggetlen trolkhoz s bejrkhoz viszonytva a kzs bzisosztllyal ren-
delkez objektumok tlete feleslegesen sok munkt hrt a felhasznlra, jelents futsi
idej terhelst jelent s korltozza a trolban elhelyezhet objektumok krt. Radsul
sok osztly esetben az Object osztlybl val szrmaztats a megvalsts rszleteinek fel-
fedst jelenti, ezrt ez a megkzelts nagyon tvol ll az idelis megoldstl egy szabv-
nyos knyvtr esetben.
Ennek ellenre az ezen megolds ltal knlt ltalnossgot s rugalmassgot nem szabad
albecslnnk. Szmtalan vltozatt, szmtalan programban sikeresen felhasznltk mr.
Ennek a megkzeltsnek azokon a terleteken van jelentsge, ahol a hatkonysg kevs-
b fontos, mint az egyszersg, amelyet az egyszer Container fellet s az objektum ki-
s bevitelhez hasonl szolgltatsok biztostanak.
A standard knyvtr 580
16.2.3. A standard knyvtr troli
A standard knyvtr troli (container) s bejri (iterator) amelyet gyakran neveznk
STL (Standard Template Library) keretrendszernek, (3.10) olyan megkzeltsknt fogha-
tk fel, amely a korbban bemutatott kt hagyomnyos modell elnyeit a lehet legjobban
kiaknzza. Az STL azonban egyik mdszert sem alkalmazza kzvetlenl; clja az, hogy egy-
szerre hatkony s ltalnos algoritmusokat knljon, mindenfle megalkuvs nlkl. A ha-
tkonysg rdekben az alkotk a gyakran hasznlt adatelr fggvnyek esetben elve-
tettk a nehezen optimalizlhat virtulis fggvnyeket, gy nem adhattak szabvnyos
felletet a trolk s a bejrk szmra absztrakt osztly formjban. Ehelyett mindegyik t-
roltpus tmogatja az alapvet mveleteknek egy szabvnyos halmazt. A kvr felletek
problmjnak (16.2.2, 24.4.3) elkerlse rdekben az olyan mveletek, amelyek nem
minden trolban valsthatk meg hatkonyan, nem szerepelnek ebben a kzs halmaz-
ban. Az indexels pldul alkalmazhat a vector esetben, de nem hasznlhat a list tro-
lkra. Ezenkvl minden troltpus sajt bejrkat biztost, amelyek a szoksos bejrm-
veleteket teszik elrhetv.
A szabvnyos trolk nem kzs bzisosztlybl szrmaznak, hanem minden trol nl-
lan tartalmazza a szabvnyos trolfelletet. Ugyangy nincs kzs bejr-s sem. A szab-
vnyos trolk s bejrk hasznlatakor nem trtnik futsi idej tpusellenrzs, sem
kzvetlen (explicit), sem automatikus (implicit) formban.
A minden trolra kiterjed kzs szolgltatsok biztostsa igen fontos s bonyolult fel-
adat. Az STL ezt a memriafoglalk (alloktor, allocator) segtsgvel valstja meg, amelye-
ket sablonparamterknt adunk meg (19.4.3), ezrt nincs szksg kzs bzisosztlyra.
Mieltt a rszleteket megvizsglnnk s konkrt pldkat mutatnnk be, foglaljuk ssze az
STL szemllete ltal biztostott elnyket s htrnyokat:
+ Az nll trolk egyszerek s hatkonyak (nem egszen olyan egyszerek,
mint a tnyleg teljesen fggetlen trolk, de ugyanolyan hatkonyak).
+ Mindegyik trol biztostja a szabvnyos mveleteket szabvnyos nven s je-
lentssel. Ha szksg van r, az adott troltpusnak megfelel tovbbi mvele-
tek is megtallhatk. Ezenkvl, a becsomagol vagy beburkol (wrapper) osz-
tlyok (25.7.1) segtsgvel a kln fejlesztett trolk is beilleszthetk a kzs
keretrendszerbe. (16.5[14])
+ A tovbbi kzs hasznlati formkat a szabvnyos bejrk biztostjk. Minden
trol biztost bejrkat, amelyek lehetv teszik bizonyos mveletek vgrehaj-
tst szabvnyos nven s jelentssel. Minden bejr-tpus kln ltezik min-
den troltpushoz, gy ezek a bejrk a lehet legegyszerbbek s a lehet
leghatkonyabbak.
16. A knyvtr szerkezete s a trolk 581
+ A klnbz szksgletek kielgtsre minden trolhoz ltrehozhatunk sajt
bejrkat vagy ltalnos felleteket, a szabvnyos bejrk mellett.
+ A trolk alaprtelmezs szerint tpusbiztosak s homognek (azaz az adott
trol minden eleme ugyanolyan tpus). Heterogn trolkat gy kszthe-
tnk, hogy egy olyan homogn trolt hozunk ltre, amely kzs bzisosztlyra
hivatkoz mutatkat tartalmaz.
+ A trolk nem tolakodak (azaz a trol tagjainak nem kell egy adott bzisosz-
tlybl szrmazniuk, vagy hivatkoz mezket tartalmazniuk). A nem tolakod
trolk a beptett tpusok esetben hasznlhatk jl, illetve az olyan adatszer-
kezetekhez, melyeket a mi hatskrnkn kvl hatroztak meg.
+ Az ltalnos keretrendszer lehetv teszi tolakod trolk hasznlatt is. Term-
szetesen ez az elemek tpusra nzve komoly megktseket jelenthet.
+ Minden trol hasznl egy paramtert, az gynevezett memriafoglalt
(allocator), amely olyan szolgltatsok kezelshez nyjt segtsget, amelyek
minden trolban megjelennek. Ez nagymrtkben megknnyti az olyan ltal-
nos feladatok megvalstst, mint a perzisztencia biztostsa vagy az objektum
ki- s bevitel.(19.4.3)
- A trolk s bejrk nem rendelkeznek olyan szabvnyos, futsi idej brzo-
lssal, amelyet pldul fggvnyparamterknt tadhatnnk (br a szabvnyos
trolk s bejrk esetben knnyen ltrehozhatnnk ilyet, ha erre az adott
programban szksgnk van, 19.3).
Ugyangy, mint eddig (16.2.1) a + az elnyket, a - a htrnyokat jelli.
A trolknak (container) s a bejrknak (iterator) teht nincs rgztett, ltalnos brzol-
sa. Ehelyett minden trol ugyanazt a szabvnyos felletet biztostja szabvnyos mveletek
formjban. Ennek kvetkeztben a trolkat egyformn kezelhetjk s fel is cserlhetjk.
A bejrkat is hasonlan hasznlhatjuk. Ez az id- s trhasznlati hatkonysgot csak ke-
vss rontja, a felhasznl viszont kihasznlhatja az egysgessget, mind a trolk szintjn
(a kzs bzisosztlybl szrmaz trolk esetben), mind a bejrk szintjn (a specializlt
trolk esetben).
Az STL megkzeltse ersen pt a sablonok (template) hasznlatra. Ahhoz, hogy elke-
rljk a felesleges kdismtlseket, gyakran van szksg arra, hogy mutatkat tartalmaz
trolkban rszlegesen specializlt vltozatok ksztsvel kzsen hasznlhat sszetev-
ket hozunk ltre (13.5).
A standard knyvtr 582
16.3. A vektor
Itt a vector-t gy rjuk le, mint a teljes szabvnyos trolk egyik pldjt. Ha mst nem mon-
dunk, a vector-ra vonatkoz lltsok vltoztats nlkl alkalmazhatk az sszes tbbi szab-
vnyos trolra is. A 17. fejezet foglalkozik azokkal a lehetsgekkel, amelyek kizrlag
a list, a set, a map vagy valamelyik msik trolra vonatkoznak. Azokat a lehetsgeket,
amelyeket kifejezetten a vector vagy egy hasonl trol valst meg, csak bizonyos mr-
tkig rszletezzk. Clunk az, hogy megismerjk a vector lehetsges felhasznlsi terlete-
it s megrtsk a standard knyvtr globlis szerkezetben elfoglalt szerept.
A 17.1 pontban ttekintjk a standard trolk tulajdonsgait s az ltaluk knlt lehets-
geket. Az albbiakban a vector trolt klnbz szempontok szerint mutatjuk be: a tagt-
pusok, a bejrk, az elemek elrse, a konstruktorok, a veremmveletek, a listamveletek,
a mret s kapacits, a segdfggvnyek, illetve a vector<bool> szempontjbl.
16.3.1. Tpusok
A szabvnyos vector egy sablon (template), amely az std nvtrhez tartozik s a <vector>
fejllomnyban tallhat. Elszr is nhny szabvnyos tpusnevet definil:
template <class T, class A = allocator<T> > class std::vector {
public:
// tpusok
typedef T value_type; // elemtpus
typedef A allocator_type; // memriakezel-tpus
typedef typename A::size_type size_type;
typedef typename A::difference_type difference_type;
typedef megvalsts_fgg1 iterator; // T*
typedef megvalsts_fgg2 const_iterator; // const T*
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef typename A::pointer pointer; // elemmutat
typedef typename A::const_pointer const_pointer;
typedef typename A::reference reference; // hivatkozs elemre
typedef typename A::const_reference const_reference;
// ...
};
16. A knyvtr szerkezete s a trolk 583
Minden szabvnyos trol definilja ezeket a tpusokat, mint sajt tagjait, de a sajt megva-
lstsnak legmegfelelbb formban.
A trol elemeinek tpust az els sablonparamter hatrozza meg, amit gyakran neveznk
rtktpusnak (value_type) is. A memriafoglal tpusa (allocator_type) amelyet (nem k-
telezen) a sablon msodik paramterben adhatunk meg azt hatrozza meg, hogy
a value_type hogyan tart kapcsolatot a klnbz memriakezel eljrsokkal. Ezen bell,
a memriafoglal adja meg azokat a fggvnyeket, amelyeket a trol az elemek trols-
ra szolgl memria lefoglalsra s felszabadtsra hasznl. A memriafoglalkrl a 19.4
pontban lesz sz rszletesen. ltalban a size_type hatrozza meg azt a tpust, amelyet a t-
rol indexelshez hasznlunk, mg a difference_type annak az rtknek a tpust jelli,
amelyet kt bejr klnbsgnek kpzsekor kapunk. A legtbb trol esetben ezek
a size_t, illetve a ptrdiff_t tpust jelentik (6.2.1)
A bejrkat a 2.7.2 pontban mutattuk be s a 19. fejezetben foglalkozunk velk rszlete-
sen. gy kpzelhetjk el ket, mint egy trol valamelyik elemre hivatkoz mutatt.
Minden trol ler egy iterator nev tpust, amellyel az elemekre mutathatunk. Rendelkez-
snkre ll egy const_iterator tpus is, amelyet akkor hasznlunk, ha nincs szksg az ele-
mek mdostsra. Ugyangy, mint a mutatk esetben, itt is mindig hasznljuk a bizton-
sgosabb const vltozatot, hacsak nem felttlenl a msik lehetsgre van szksgnk.
A vector bejrinak konkrt tpusa a megvalststl fgg, a legnyilvnvalbb megolds egy
hagyomnyosan definilt vector esetben a T*, illetve a const T*.
A visszafel halad bejrk tpust a vector szmra a szabvnyos reverse_iterator sablon
segtsgvel hatroztk meg. (19.2.5) Ez az elemek sorozatt fordtott sorrendben szolgl-
tatja.
A 3.8.1 pontban bemutattuk, hogy ezen tpusok segtsgvel a felhasznl gy rhat tro-
lkat hasznl programrszleteket, hogy a tnylegesen hasznlt tpusokrl semmit sem tud,
st, olyan kdot is rhat, amely minden szabvnyos trol esetben hasznlhat:
template<class C> typename C::value_type sum(const C& c)
{
typename C::value_type s = 0;
typename C::const_iterator p = c.begin(); // kezds az elejn
while (p!=c.end()) { // folytats a vgig
s += *p; // elem rtknek megszerzse
++p; // p a kvetkez elemre fog mutatni
}
return s;
}
A standard knyvtr 584
A typename hasznlata egy sablonparamter tagjnak neve eltt elg furcsn nz ki, de
a fordtprogram nem akad fenn rajta, ugyanis nincs ltalnos mdszer arra, hogy egy sab-
lonparamter valamelyik tagjrl eldntsk, hogy az tpusnv-e. (C.13.5)
Ugyangy, mint a mutatk esetben, az eltagknt hasznlt * a bejr indirekcijt jelenti
(2.7.2, 19.2.1), mg a ++ a bejr nvelst vgzi.
16.3.2. Bejrk
Ahogy az elz alfejezetben mr bemutattuk, a programozk a bejrkat arra hasznlhat-
jk, hogy bejrjk a trolt az elemek tpusnak pontos ismerete nlkl. Nhny tagfgg-
vny teszi lehetv, hogy elrjk az elemek sorozatnak valamelyik vgt:
template <class T, class A = allocator<T> > class vector {
public:
// ...
// iterators:
iterator begin(); // az els elemre mutat
const_iterator begin() const;
iterator end(); // az "utols utni" elemre mutat
const_iterator end() const;
reverse_iterator rbegin(); // htulrl az els elemre mutat
const_reverse_iterator rbegin() const;
reverse_iterator rend(); // htulrl az "utols utni" elemre mutat
const_reverse_iterator rend() const;
// ...
};
A begin()/end() pr a trol elemeit a szoksos sorrendben adja meg, azaz elsknt a nul-
ladik elemet (vagyis az elst) kapjuk, majd sorban a kvetkezket; az rbegin()/rend() pr-
nl viszont fordtott sorrendben, azaz az n-1. elem utn kvetkezik az n-2., majd az n-3.,
s gy tovbb. Pldul ha egy iterator hasznlatval ilyen sorozatot kapunk:
16. A knyvtr szerkezete s a trolk 585
A B C
begin() end()
akkor a reverse_iterator a kvetkez elemsorozatot adja (19.2.5):
gy lehetsgnk van olyan algoritmusok ksztsre, amelyeknek fordtott sorrendben van
szksge az elemek sorozatra. Pldul:
template<class C> typename C::iterator find_last(C& c, typename C::value_type v)
{
typename C::reverse_iterator ri = find(c.rbegin(),c.rend(),v);
if (ri == c.rend()) return c.end(); // a c.end() jelzi, hogy "nem tallhat"
typename C::iterator i = ri.base();
return --i;
}
Egy reverse_iterator esetben az ri.base() fggvny egy olyan bejrt ad vissza, amely az ri
ltal kijellt hely utn kvetkez elemre mutat. (19.2.5) A visszafel halad bejrk nlkl
egy ciklust kellett volna ksztennk:
template<class C> typename C::iterator find_last(C& c, typename C::value_type v)
{
typename C::iterator p = c.end(); // keress a vgtl visszafel
while (p!=c.begin())
if (*--p==v) return p;
return c.end(); // a c.end() jelzi, hogy "nem tallhat"
}
A visszafel halad bejr is kznsges bejr, teht rhattuk volna ezt is:
template<class C> typename C::iterator find_last(C& c, typename C::value_type v)
{
typename C::reverse_iterator p = c.rbegin(); // a sorozat tnzse visszafel
while (p!=c.rend()) {
if (*p==v) {
typename C::iterator i = p.base();
return --i;
}
++p; // vigyzzunk: nvels, nem cskkents (--)
}
return c.end(); // a c.end() jelzi, hogy "nem tallhat"
}
A standard knyvtr 586
C B A
rbegin() rend()
Figyeljnk r, hogy a C::reverse_iterator tpus nem ugyanaz, mint a C::iterator.
16.3.3. Az elemek elrse
A vector igen fontos tulajdonsga a tbbi trolval sszehasonltva, hogy az egyes eleme-
ket brmilyen sorrendben knnyen s hatkonyan elrhetjk:
template <class T, class A = allocator<T> > class vector {
public:
// ...
// hozzfrs az elemekhez
reference operator[ ](size_type n); // nem ellenrztt hozzfrs
const_reference operator[ ](size_type n) const;
reference at(size_type n); // ellenrztt hozzfrs
const_reference at(size_type n) const;
reference front(); // els elem
const_reference front() const;
reference back(); // utols elem
const_reference back() const;
// ...
};
Az indexelshez az operator[ ]() vagy az at() fggvnyt hasznljuk. A [ ] opertor ellenri-
zetlen hozzfrst biztost, mg az at() indexhatr-ellenrzst is vgez s out_of_range ki-
vtelt vlt ki, ha a megadott index nem a megfelel tartomnyba esik:
void f(vector<int>& v, int i1, int i2)
try {
for(int i = 0; i < v.size(); i++) {
// a tartomny mr ellenrztt, itt a nem ellenrztt v[i]-t kell hasznlnunk
}
v.at(i1) = v.at(i2); // hozzfrskor a tartomny ellenrzse
// ...
}
catch(out_of_range) {
// hopp, kicssztunk a tartomnyon kvlre
}
16. A knyvtr szerkezete s a trolk 587
Ez a plda bemutat egy hasznos tletet is: ha az indexhatrokat mr valamilyen mdon el-
lenriztk, akkor az ellenrizetlen indexel opertort teljes biztonsggal hasznlhatjuk. El-
lenkez esetben viszont rdemes az at() fggvnyt alkalmaznunk. Ez a klnbsg fontos
lehet olyan programoknl, ahol a hatkonysgra is figyelnnk kell. Ha a hatkonysg nem
jelents szempont vagy nem tudjuk egyrtelmen eldnteni, hogy a tartomny ellenrztt-
e, akkor biztonsgosabb ellenrztt [ ] opertorral rendelkez vektort hasznljunk (pld-
ul a Vec-et, 3.7.2), vagy legalbbis ellenrztt bejrt (19.3).
A tmbk alaprtelmezett hozzfrsi mdja ellenrizetlen. Biztonsgos (ellenrztt) szol-
gltatsokat megvalsthatunk egy gyors alaprendszer felett, de gyors szolgltatst lass
alaprendszer felett nem.
A hozzfrsi mveletek reference vagy const_reference tpus rtket adnak vissza, attl
fggen, hogy konstans objektumra alkalmaztuk-e azokat. Az elemek elrsre a referencia
megfelel tpus. A vector<X> legegyszerbb s legkzenfekvbb megvalstsban
a reference egyszeren X&, mg a const_reference megfelelje a const X&. Ha egy indexha-
tron kvli elemre prblunk hivatkozni, az eredmny meghatrozhatatlan lesz:
void f(vector<double>& v)
{
double d = v[v.size()]; // nem meghatrozhat: rossz indexrtk
list<char> lst;
char c = lst.front(); // nem meghatrozhat: a lista res
}
A szabvnyos sorozatok kzl csak a vector s a deque (17.2.3) tmogatja az indexelst.
Ennek oka az, hogy a rendszer tervezi nem akartk a felhasznlkat alapveten rossz ha-
tsfok mveletek bevezetsvel megzavarni. Az indexels pldul nem alkalmazhat a list
tpusra (17.2.2), mert veszlyesen rossz hatsfok eljrs lenne (konkrtabban: O(n)).
A front() s back() tagfggvnyek sorrendben az els, illetve az utols elemre hivatkoz
referencikat adnak vissza. Akkor igazn hasznosak, ha biztosan tudjuk, hogy lteznek
ezek az elemek s programunkban valamirt klnsen fontosak. Ennek gyakori esete,
amikor egy vektort veremknt (stack, 16.3.5) akarunk hasznlni. Jegyezzk meg, hogy
a front() arra az elemre ad hivatkozst, amelyre a begin() egy bejrt. A front() gy is el-
kpzelhet, mint maga az els elem, mg a begin() inkbb az els elemre hivatkoz muta-
t. A back() s az end() kztti kapcsolat egy kicsit bonyolultabb: a back() az utols elem,
mg az end() egy mutat az utols utni pozcira.
A standard knyvtr 588
16.3.4. Konstruktorok
Termszetesen a vector a konstruktoroknak, destruktoroknak s msol mveleteknek is
teljes trhzt knlja:
template <class T, class A = allocator<T> > class vector {
public:
// ...
// konstruktorok, stb.
explicit vector(const A& = A());
explicit vector(size_type n, const T& val = T(), const A& = A()); // n darab val
template <class In> // az In-nek bemeneti bejrnak kell lennie (19.2.1)
vector(In first, In last, const A& = A()); // msols [first:last[-bl
vector(const vector& x);
~vector();
vector& operator=(const vector& x);
template <class In> // az In-nek bemeneti bejrnak kell lennie (19.2.1)
void assign(In first, In last); // msols [first:last[-bl
void assign(size_type n, const T& val); // n darab val
// ...
};
A vector gyors hozzfrst tesz lehetv tetszleges elemhez, de mretnek mdostsa vi-
szonylag bonyolult. Ezrt ltalban a vector ltrehozsakor megadjuk annak mrett is:
vector<Record> vr(10000);
void f(int s1, int s2)
{
vector<int> vi(s1);
vector<double>* p = new vector<double>(s2);
}
Az ilyen mdon ltrehozott vektorelemeknek az elemek tpusnak megfelel alaprtelme-
zett konstruktorok adnak kezdrtket, teht a vr vektornak mind a 10 000 elemre lefut
a Record() fggvny, a vi si darab elemnek pedig egy-egy int() hvs ad kezdrtket.
Gondoljunk r, hogy a beptett tpusok alaprtelmezett konstruktora az adott tpusnak
megfelel 0 rtket adja kezdrtkl. (4.9.5, 10.4.2)
16. A knyvtr szerkezete s a trolk 589
Ha egy tpus nem rendelkezik alaprtelmezett konstruktorral, akkor belle nem hozhatunk
ltre vektort, hacsak nem adjuk meg az sszes elem kezdrtkt. Pldul:
class Num { // vgtelen pontossg
public:
Num(long);
// nincs alaprtelmezett konstruktor
// ...
};
vector<Num> v1(1000); // hiba: nincs alaprtelmezett Num
vector<Num> v2(1000,Num(0)); // rendben
Mivel egy vector nem trolhat negatv szm elemet, gy mretnek nemnegatv szmnak
kell lennie. Ez azon kvetelmnyben jut kifejezsre, hogy a vektor size_type tpusnak
unsigned-nak kell lennie. Ez bizonyos rendszerekben nagyobb vektormret hasznlatt te-
szi lehetv, egyes esetekben viszont meglepetsekhez is vezethet:
void f(int i)
{
vector<char> vc0(-1); // a fordt erre knnyen figyelmeztethet
vector<char> vc1(i);
}
void g()
{
f(-1); // becsapjuk f()-et, hogy elfogadjon egy igen nagy pozitv szmot
}
Az f(-1) hvsban a -1 rtket egy igen nagy pozitv szmra alaktja a rendszer (C.6.3). Ha
szerencsnk van, fordtnk figyelmeztet erre.
A vector mrett kzvetve is megadhatjuk, gy, hogy felsoroljuk a kezdeti elemhalmazt. Ezt
gy valsthatjuk meg, hogy a konstruktornak azon rtkek sorozatt adjuk t, amelyekbl
a vektort fel kell pteni:
void f(const list<X>& lst)
{
vector<X> v1(lst.begin(),lst.end()); // elemek msolsa listbl
char p[ ] = "despair";
vector<char> v2(p,&p[sizeof(p)-1]); // karakterek msolsa C stlus karakterlncbl
}
A standard knyvtr 590
Minden esetben a vector konstruktora szmtja ki a vektor mrett, mikzben a bemeneti
sorozatbl tmsolja az elemeket.
A vector azon konstruktorait, melyek egyetlen paramtert ignyelnek, az explicit kulcssz-
val deklarljuk, gy vletlen konverzik nem fordulhatnak el (11.7.1):
vector<int> v1(10); // rendben: 10 egszbl ll vektor
vector<int> v2 = vector<int>(10); // rendben: 10 egszbl ll vektor
vector<int> v3 = v2; // rendben: v3 v2 msolata
vector<int> v4 = 10; // hiba: automatikus konvertls ksrlete 10-rl vector<int>-re
A msol konstruktor s a msol rtkads a vector elemeit msoljk le. Egy sok elembl
ll vektor esetben ez hosszadalmas mvelet lehet, ezrt a vektorokat ltalban referen-
ciaknt adjuk t:
void f1(vector<int>&); // szoksos stlus
void f2(const vector<int>&); // szoksos stlus
void f3(vector<int>); // szokatlan stlus
void h()
{
vector<int> v(10000);
// ...
f1(v); // hivatkozs tadsa
f2(v); // hivatkozs tadsa
f3(v); // a 10 000 elem j vektorba msolsa az f3() szmra
}
Az assign fggvnyek a tbbparamteres konstruktorok prjainak tekinthetk. Azrt van
rjuk szksg, mert az = egyetlen, jobb oldali operandust vr, gy ha alaprtelmezett para-
mterrtket akarunk hasznlni vagy rtkek teljes sorozatt akarjuk tadni, az assign fgg-
vnyre van szksgnk:
class Book {
// ...
};
void f(vector<Num>& vn, vector<char>& vc, vector<Book>& vb, list<Book>& lb)
{
vn.assign(10,Num(0)); // Num(0) tz pldnyt trol vektor rtkl adsa vn-nek
16. A knyvtr szerkezete s a trolk 591
char s[ ] = "literl";
vc.assign(s,&s[sizeof(s)-1]); // a "literl" rtkl adsa vc-nek
vb.assign(lb.begin(),lb.end()); // listaelemek rtkl adsa
// ...
}
Ezek utn egy vector objektumot feltlthetnk tetszleges elemsorozattal, ami tpus szerint
megfelel, s ksbb is hozzrendelhetnk az objektumhoz ilyen sorozatokat. Fontos,
hogy ezt anlkl rtk el, hogy nagyszm konstruktort illetve konverzis opertort kellett
volna definilnunk. Figyeljk meg, hogy az rtkads teljes egszben lecserli a vektor
elemeit. Elmletileg az sszes rgi elemet trljk, majd az j elemeket beillesztjk. Az r-
tkads utn a vector mrete az rtkl adott elemek szmval egyezik meg:
void f()
{
vector<char> v(10,'x'); // v.size()==10, minden elem rtke 'x'
v.assign(5,'a'); // v.size()==5, minden elem rtke 'a'
// ...
}
Termszetesen, amit az assign() csinl, az megvalsthat kzvetetten gy is, hogy elszr
ltrehozzuk a kvnt vektort, majd ezt adjuk rtkl:
void f2(vector<Book>& vh, list<Book>& lb)
{
vector<Book> vt(lb.begin(),lb.end());
vh = vt;
// ...
}
Ez a megolds azonban nem csak csnya, de rossz hatsfok is lehet.
Ha egy vektor konstruktorban kt ugyanolyan tpus paramtert hasznlunk, akkor azt
ktflekppen is rtelmezhetnnk:
vector<int> v(10,50); // vector(mret,rtk) vagy vector(bejr1,bejr2)?
// vector(mret,rtk)!
Az int nem bejr, gy a megvalstsoknak biztostaniuk kell, hogy a megfelel konstruk-
tor fusson le:
vector(vector<int>::size_type, const int&, const vector<int>::allocator_type&);
A standard knyvtr 592
A msik lehetsges konstruktor a kvetkez:
vector(vector<int>::iterator, vector<int>::iterator, const vector<int>::allocator_type&);
A knyvtr ezt a problmt gy oldja meg, hogy megfelel mdon tlterheli a konstruk-
torokat, de hasonlan kezeli az assign() s az insert() (16.3.6) esetben elfordul tbb-
rtelmsget is.
16.3.5. Veremmveletek
A vektort ltalban gy kpzeljk el, mint egy egysges adatszerkezetet, melyben az ele-
meket indexelssel rhetjk el. Ezt a szemlletet azonban el is felejthetjk s a vektort a leg-
elvontabb (legltalnosabb) sorozat megtestestjnek tekinthetjk. Ha erre gondolunk s
megfigyeljk, hogy a tmbknek, vektoroknak milyen ltalnos felhasznlsi terletei van-
nak, nyilvnvalv vlik, hogy a vector osztlyban a veremmveletekre is szksg lehet:
template <class T, class A = allocator<T> > class vector {
public:
// ...
// veremmveletek
void push_back(const T& x); // hozzads a vghez
void pop_back(); // az utols elem eltvoltsa
// ...
};
Ezek a fggvnyek a vektort veremnek tekintik s annak vgn vgeznek mveleteket:
void f(vector<char>& s)
{
s.push_back('a');
s.push_back('b');
s.push_back('c');
s.pop_back();
if (s[s.size()-1] != 'b') error("Lehetetlen!");
s.pop_back();
if (s.back() != 'a') error("Ennek soha nem szabad megtrtnnie!");
}
16. A knyvtr szerkezete s a trolk 593
Amikor meghvjuk a push_back() fggvnyt, az s vektor mrete mindig n egy elemmel s
a paramterknt megadott elem a vektor vgre kerl. gy az s[s.size()-1] elem (ami ugyan-
az, mint az s.back() ltal visszaadott rtk, 16.3.3) a verembe legutoljra helyezett elem lesz.
Attl eltekintve, hogy a vector szt hasznljuk a stack helyett, semmi szokatlant nem mvel-
tnk. A _back uttag azt hangslyozza ki, hogy az elemet a vektor vgre helyezzk, nem
pedig az elejre. Egy j elem elhelyezse a vektor vgn nagyon kltsges mvelet is lehet,
hiszen annak trolshoz tovbbi memrit kell lefoglalnunk. Az adott nyelvi vltozatnak
azonban biztostania kell, hogy az ismtld veremmveletek csak ritkn okozzanak m-
retnvekedsbl ered teljestmnycskkenst.
Figyeljk meg, hogy a pop_back() fggvny sem ad vissza rtket. Egyszeren trli a legutol-
s elemet s ha azt szeretnnk megtudni, hogy a kiemels (pop) eltt milyen rtk szerepelt
a verem tetejn, akkor azt kln meg kell nznnk. Ezt a tpus vermet sokan nem kedve-
lik (2.5.3, 2.5.4), de ez a megolds hatkonyabb lehet s ez tekinthet a szabvnynak is.
Mirt lehet szksg veremszer mveletekre egy vektor esetben? Egy nyilvnval indok,
hogy a verem megvalstsra ez az egyik lehetsg (17.3.1), de ennl gyakoribb, hogy
a tmbt folyamatosan nvekedve akarjuk ltrehozni. Elkpzelhet pldul, hogy ponto-
kat akarunk beolvasni egy tmbbe, de nem tudjuk, sszesen hny pontot kapunk. Ez eset-
ben arra nincs lehetsgnk, hogy mr kezdetben a megfelel mret tmbt hozzuk lt-
re s utna csak egyszeren belerjuk a pontokat. Ehelyett a kvetkez eljrst kell vgre-
hajtanunk:
vector<Point> cities;
void add_points(Point sentinel)
{
Point buf;
while (cin >> buf) {
if (buf == sentinel) return;
// j pont ellenrzse
cities.push_back(buf);
}
}
Ez a megolds biztostja, hogy a vector igny szerint nvekedhessen. Ha mindssze annyi
a teendnk, hogy a pontokat elhelyezzk a vektorban, akkor a cities adatszerkezetet feltlt-
hetjk kzvetlenl egy konstruktor segtsgvel is (16.3.4), ltalban azonban valamilyen
mdon mg fel kell dolgoznunk a berkez adatokat s csak fokozatosan tlthetjk fel
a vektort a program elrehaladtval. Ilyenkor hasznlhatjuk a push_back() fggvnyt.
A standard knyvtr 594
A C programokban ez a leggyakoribb felhasznlsi terlete a realloc() fggvnynek, amely
a C standard knyvtrban szerepel. Ezrt a vektor s ltalban minden szabvnyos tro-
l biztost egy ltalnosabb, elegnsabb, de nem kevsb hatkony vltozatot a realloc()
helyett.
A vektor mrett (size()) a push_back() automatikusan nveli, gy nem fordulhat el, hogy
a vektor tlcsordul (mindaddig, amg memria ignyelhet a rendszertl, 19.4.1). Alulcsor-
duls ellenben elfordulhat:
void f()
{
vector<int> v;
v.pop_back(); // nem meghatrozhat hats: v llapota nem
// meghatrozhat lesz
v.push_back(7); // nem meghatrozhat hats (v llapota nem
// meghatrozhat), valsznleg rossz
}
Az alulcsorduls kvetkezmnye nem meghatrozhat s a legtbb C++-vltozat esetben
olyan memriaterlet fellrst eredmnyezi, amely nem tartozik a vektorhoz. Az alulcsor-
dulst ugyangy el kell kerlnnk, mint a tlcsordulst.
16.3.6. Listamveletek
A push_back(), a pop_back() s a back() mvelet (16.3.5) lehetv teszi, hogy a vector osz-
tlyt veremknt hasznljuk. Bizonyos helyzetekben azonban arra is szksgnk lehet, hogy
a verem kzepre illessznk be, vagy onnan tvoltsunk el egy elemet:
template <class T, class A = allocator<T> > class vector {
public:
// ...
// listamveletek
iterator insert(iterator pos, const T& x); // x felvtele pos el
void insert(iterator pos, size_type n, const T& x); // n darab x felvtele pos el
template <class In> // az In-nek bemeneti bejrnak kell lennie (19.2.1)
void insert(iterator pos, In first, In last); // elemek beillesztse sorozatbl
iterator erase(iterator pos); // a pos pozciban lev elem eltvoltsa
iterator erase(iterator first, iterator last); // a sorozat trlse
void clear(); // minden elem trlse
// ...
};
16. A knyvtr szerkezete s a trolk 595
Ezen mveletek mkdsnek bemutatshoz egy gymlcsk (fruit) neveit tartalmaz
vektort hozunk ltre. Elszr is meghatrozzuk a vektort, majd feltltjk nhny nvvel:
vector<string> fruit;
fruit.push_back("szibarack");
fruit.push_back("alma");
fruit.push_back("kivi");
fruit.push_back("krte");
fruit.push_back("csillaggymlcs");
fruit.push_back("szl");
Ha hirtelen elegnk lesz azokbl a gymlcskbl, melyek neve k betvel kezddik, ak-
kor ezeket a kvetkez eljrssal trlhetjk ki:
sort(fruit.begin(),fruit.end());
vector<string>::iterator p1 = find_if(fruit.begin(),fruit.end(),initial('k'));
vector<string>::iterator p2 = find_if(p1,fruit.end(),initial_not('k'));
fruit.erase(p1,p2);
Teht rendezzk a vektort, megkeressk az els s az utols gymlcst, melynek neve k
betvel kezddik, vgl ezeket trljk a fruit objektumbl. Hogyan kszthetnk olyan
fggvnyeket, mint az initial(x) amely eldnti, hogy a kezd karakter x bet-e vagy az
initial_not(x), amely akkor ad igaz rtket, ha a kezdbet nem x? Ezzel a krdssel k-
sbb, a 18.4.2 pontban foglalkozunk rszletesen.
Az erase(p1,p2) mvelet p1-tl p2-ig trli az elemeket (a p2 elem marad). Ezt a kvetkez-
kppen szemlltethetjk:
fruit[ ]:
p1 p2
alma csillaggymlcs kivi krte szibarack szl
Az erase(p1,p2) trli a kivi s a krte elemet, teht a vgeredmny a kvetkez lesz:
fruit[ ]:
alma csillaggymlcs szibarack szl
A standard knyvtr 596
Szoks szerint, a programoz ltal megadott sorozat az els feldolgozand elemtl az utol-
s feldolgozott elem utni pozciig tart.
Esetleg esznkbe jut az albbi megoldssal prblkozni:
vector<string>::iterator p1 = find_if(fruit.begin(),fruit.end(),initial('k'));
vector<string>::reverse_iterator p2 = find_if(fruit.rbegin(),fruit.rend(),initial('k'));
fruit.erase(p1,p2+1); // hopp! tpushiba
Azonban a vector<fruit>::iterator s a vector<fruit>::reverse_iterator nem felttlenl azonos
tpus, gy nem hasznlhatjuk ket egytt az erase() fggvny hvsakor. Ha egy
reverse_iterator rtket egy iterator rtkkel egytt akarunk hasznlni, akkor az elbbit t
kell alaktanunk:
fruit.erase(p1,p2.base()); // iterator kinyerse reverse_iterator-bl (19.2.5)
Ha egy vektorbl elemeket trlnk, akkor megvltozik annak mrete s a trlt elem utn
szerepl rtkeket a felszabadult terletre kell msolnunk. Pldnkban a fruit.size() rtke
4-re vltozik, s az szibarack, amire eddig fruit[5] nven hivatkozhattunk, most mr
fruit[3]-knt lesz elrhet.
Termszetesen arra is lehetsgnk van, hogy egyetlen elemet trljnk. Ebben az esetben
csak az erre az elemre mutat bejrra van szksg (s nem egy bejr-prra):
fruit.erase(find(fruit.begin(),fruit.end(),"szibarack"));
fruit.erase(fruit.begin()+1);
Ez a kt sor trli az szibarack-ot s a csillaggymlcs-t, gy a fruit vektorban mr csak kt
elem marad:
fruit[ ]:
alma szl
Arra is lehetsgnk van, hogy egy vektorba elemeket illessznk be. Pldul:
fruit.insert(fruit.begin()+1,"cseresznye");
fruit.insert(fruit.end(),"ribizli");
16. A knyvtr szerkezete s a trolk 597
Az j elem a megadott elem el kerl, az utna kvetkez elemek pedig elmozdulnak, hogy
az j elemet beszrhassuk. Az eredmny:
fruit[ ]:
alma cseresznye szl ribizli
Figyeljk meg, hogy az f.insert(f.end(),x) egyenrtk az f.push_back(x) mvelettel.
Teljes sorozatokat is beilleszthetnk egy vektorba:
fruit.insert(fruit.begin()+2,citrus.begin(),citrus.end());
Ha a citrus egy msik trol, amely gy nz ki:
citrus[ ]:
citrom grapefruit narancs lime
akkor a kvetkez eredmnyt kapjuk:
fruit[ ]:
alma cseresznye citrom grapefruit narancs lime szl ribizli
Az insert() fggvny a citrus elemeit tmsolja a fruit vektorba. A citrus trol vltozatlan
marad.
Ktsgtelen, hogy az insert() s az erase() ltalnosabbak, mint azok a mveletek, melyek
csak a vektor vgnek mdostst teszik lehetv (16.3.5). ppen emiatt azonban sokkal
tbb gonddal is jrhatnak. Pldul ahhoz, hogy az insert() egy j elemet beillesszen, eset-
leg az sszes korbbi elemet t kell helyeznie a memriban. Ha sokszor hasznlunk telje-
sen ltalnos beszr s trl mveleteket egy troln, akkor ennek a trolnak esetleg
nem is vector-nak, hanem inkbb list-nek kne lennie. A list trol hatkonyan kpes
egyttmkdni az insert() s az erase() mveletekkel, de az indexels ez esetben nehzkes
(16.3.3).
A beszrs s a trls a vector esetben esetleg sok-sok elem thelyezsvel jr, (mg a list
vagy az asszociatv trolk pldul a map esetben ez elkerlhet). Ennek kvetkezt-
ben elfordulhat, hogy a vector egyik elemre mutat iterator egy insert() vagy egy erase()
mvelet vgrehajtsa utn egy msik, vagy akr egy egyltaln nem ltez elemre mutat.
Soha ne prbljunk meg elrni elemeket rvnytelen bejrn keresztl, mert az eredmny
A standard knyvtr 598
meghatrozhatatlan lesz s ltalban vgzetes kvetkezmnyekkel jr. Klnsen vesz-
lyes egy olyan bejr hasznlata, amely a beszrs helyt jellte ki, mert az insert() az els
paramtert rvnytelenn teszi:
void duplicate_elements(vector<string>& f)
{
for(vector<string>::iterator p = f.begin(); p!=f.end(); ++p) f.insert(p,*p); // Nem!
}
Erre kell gondolnunk majd a 16.5[15] feladatnl is. A vector adott vltozata az j p elem be-
szrshoz esetleg az sszes elemet thelyezn, de a p utniakat biztosan.
A clear() mvelet a trol sszes elemt trli. Ezrt a c.clear() a c.erase(c.begin(),c.end())
rvidtsnek tekinthet. A c.clear() vgrehajtsa utn a c.size() rtke 0 lesz.
16.3.7. Az elemek kivlasztsa
A legtbb esetben az erase() s az insert() clpontja egy jl meghatrozott hely, pldul
a begin() vagy az end() eredmnye, valamilyen keressi eljrs (pldul a find()) ltal
visszaadott rtk vagy egy bejrssal megtallt elempozci. Ezekben az esetekben rendel-
kezsnkre ll egy bejr, amely a kvnt elemet jelli ki. A vector (s a vektorszer tro-
lk) elemeit azonban meghatrozhatjuk indexelssel is. Hogyan llthatunk el egy olyan
bejrt, amely az insert() vagy az erase() utastsban megfelel paramter a 7-es sorszm
elem kijellshez? Mivel ez a hetedik elem a vektor elejtl szmtva, gy a c.begin()+7 ki-
fejezs jelenti a megoldst. A tovbbi lehetsgek, amelyek a tmbkhz val hasonlsg
miatt felmerlhetnek bennnk, gyakran nem mkdnek. Pldul gondoljuk vgig a kvet-
kez eseteket:
template<class C> void f(C& c)
{
c.erase(c.begin()+7); // rendben (ha c bejri tmogatjk a + mveletet
// (19.2.1))
c.erase(&c[7]); // nem ltalnos
c.erase(c+7); // hiba: 7 hozzadsa egy trolhoz nem rtelmezhet
c.erase(c.back()); // hiba: c.back() referencia, nem bejr
c.erase(c.end()-2); // rendben (utols eltti eltti elem)
c.erase(c.rbegin()+2); // hiba: vector::reverse_iterator s vector::iterator
// klnbz tpusok
c.erase((c.rbegin()+2).base()); // zavaros, de j (19.2.5)
}
16. A knyvtr szerkezete s a trolk 599
A legcsalogatbb lehetsg a &c[7], amely a vector legnyilvnvalbb megvalstsban
hasznlhat is, hiszen a c[7] egy ltez elem, s ennek cme hasznlhat bejrknt. A c vi-
szont lehet egy olyan trol is, ahol a bejr nem egy egyszer mutat valamelyik elemre.
A map index-opertora (17.4.1.3) pldul egy mapped_type& tpus referencit, s nem
egy elemre hivatkoz referencit (value_type&) ad vissza.
A bejrra nem minden trol esetben hasznlhat a + mvelet. A list pldul mg
a c.begin()+7 forma hasznlatt sem tmogatja. Ha felttlenl hetet kell adnunk egy
list::iterator objektumhoz, akkor a ++ opertort kell ismtelgetnnk.
A c+7 s a c.back() forma egyszeren tpushibt eredmnyez. A trol nem numerikus vl-
toz, amelyhez hetet hozzadhatnnk, a c.back() pedig egy konkrt elem pldul a kr-
te rtkkel, amely nem hatrozza meg a krte helyt a c trolban.
16.3.8. Mret s kapacits
A vector osztlyt eddig gy prbltuk meg bemutatni, hogy a lehet legkevesebbet szljunk
a memriakezelsrl. A vector szksg szerint nvekszik. ltalban ennyit ppen elg tud-
nunk. Ennek ellenre lehetsgnk van r, hogy kzvetlenl a vector memriahasznlat-
rl krdezznk, s bizonyos helyzetekben rdemes is lnnk ezzel a lehetsggel. Az ilyen
cl mveletek a kvetkezk:
template <class T, class A = allocator<T> > class vector {
public:
// ...
// kapacits
size_type size() const; // elemek szma
bool empty() const { return size()==0; }
size_type max_size() const; // a lehetsges legnagyobb vektor mrete
void resize(size_type sz, T val = T()); // a hozzadott elemeknek val ad kezdrtket
size_type capacity() const; // az elemek szmnak megfelel lefoglalt memria mrete
void reserve(size_type n); // hely biztostsa sszesen n elem szmra; nem adunk
// kezdrtket
// length_error kivltsa, ha n>max_size()
// ...
};
A standard knyvtr 600
A vector minden pillanatban valahny elemet trol. Az ppen trolt elemek szmt a size()
fggvnnyel krdezhetjk le s a resize() segtsgvel mdosthatjuk. Teht a programoz
meg tudja llaptani a vektor mrett s meg tudja azt vltoztatni, ha a vektor szknek vagy
tl nagynak bizonyul:
class Histogram {
vector<int> count;
public:
Histogram(int h) : count(max(h,8)) {}
void record(int i);
// ...
};
void Histogram::record(int i)
{
if (i<0) i = 0;
if (count.size()<=i) count.resize(i+i); // sok hely kell
count[i]++;
}
A resize() hasznlata egy vector esetben nagyon hasonlt arra, amikor a C standard knyv-
trnak realloc() fggvnyt hasznljuk egy dinamikusan lefoglalt C tmb esetben.
Amikor egy vektort tmreteznk, hogy tbb (vagy kevesebb) elemet troljunk benne, el-
kpzelhet, hogy az sszes elem j helyre kerl a memriban. Ezrt tmretezhet vekto-
rok elemeire hivatkoz mutatkat nem trolhatunk akrmeddig, egy resize() utasts utn
ugyanis ezek mr felszabadtott memriaterletre mutatnak. Ehelyett nyilvntarthatunk in-
dexrtkeket. Soha ne felejtsk el, hogy a push_back(), az insert() s az erase() is tmre-
tezi a vektort.
Ha a programoz tudja, hogy a vektor mrete a ksbbiekben mennyire nhet, esetleg r-
demes a reserve() fggvny segtsgvel elre lefoglalnia a megfelel mret terletet a k-
sbbi felhasznlshoz:
struct Link {
Link* next;
Link(Link* n =0) : next(n) {}
// ...
};
vector<Link> v;
void chain(size_t n) // v feltltse n szm Link-kel, melyek az elz Link-re mutatnak
{
16. A knyvtr szerkezete s a trolk 601
v.reserve(n);
v.push_back(Link(0));
for (int i = 1; i<n; i++) v.push_back(Link(&v[i-1]));
// ...
}
A v.reserve(n) fggvnyhvs biztostja, hogy a v mretnek nvelsekor mindaddig nem
lesz szksg memriafoglalsra, amg v.size() meg nem haladja n rtkt.
A szksges memriaterlet elzetes lefoglalsnak kt elnye van. Az egyik, hogy mg
a legegyszerbb nyelvi vltozat is egyetlen mvelettel lefoglalhat elegend memriaterle-
tet, s nem kell minden lpsben lass memriaignylst vgrehajtania. Ezenkvl a leg-
tbb esetben szmolhatunk egy logikai elnnyel is, amely taln mg fontosabb, mint a ha-
tkonysgi szempont. Amikor egy vector mrete megn, akkor elmletileg minden elem
helye megvltozhat a memriban. Ennek kvetkeztben minden olyan jelleg lncols,
amilyet az elbbi pldban ltrehoztunk az elemek kztt, csak akkor hasznlhat, ha
a reserve() garantlja, hogy az elemek memriabeli helye nem vltozik meg a vektor felp-
tse kzben. Teht bizonyos esetekben a reserve() biztostja programunk helyessgt,
amellett, hogy hatkonysgi elnyket is jelent.
Ugyanezt a biztonsgot nyjtja az is, ha csak kiszmthat idkznknt fordulhat el, hogy
a vector szmra lefoglalt terlet elfogy s egy bonyolult mvelettel t kell helyeznnk az
addig trolt elemeket. Ez nagyon fontos lehet olyan programok esetben, melyeknek szi-
gor futsi idej korltozsoknak kell megfelelnik.
Fontos megjegyeznnk, hogy a reserve() nem vltoztatja meg a vektor (logikai) mrett,
ezrt az j elemeknek sem kell kezdrtket adnia. Teht mindkt szempontbl ellentte-
sen viselkedik, mint a resize().
Ugyangy, ahogy a size() az ppen trolt elemek szmt adja meg, a capacity() fggvny
a lefoglalt memria-egysgek szmrl tjkoztat. gy a c.capacity()-c.size() kifejezs azt
adja meg, hogy mg hny elemet hozhatunk ltre jbli memriafoglals nlkl.
A vektor mretnek cskkentsvel nem cskkentjk annak kapacitst. Az gy felszaba-
dult terletek teht megmaradnak a vector-ban a ksbbi nvekedst segtend.
Ha a felszabadult terletet vissza szeretnnk adni a rendszernek, egy kis trkkhz kell
folyamodnunk:
vector <Link> tmp = v; // v msolata alaprtelmezett kapacitssal
v.swap(tmp); // most v rendelkezik az alaprtelmezett kapacitssal (16.3.9)
A standard knyvtr 602
A vector az elemek trolshoz szksges memriaterletet gy foglalja le, hogy meghvja
(a sablonparamterknt megadott) memriafoglal tagfggvnyeit. Az alaprtelmezett me-
mriafoglal, melynek neve allocator (19.4.1), a new opertort hasznlja a memriafogla-
lshoz, gy egy bad_alloc kivtelt vlt ki, ha mr nincs elg szabad memria. Ms memria-
foglalk ms mdszert is kvethetnek (19.4.2).
A reserve() s capacity() fggvnyek csak az olyan egysges, tmr trolk esetben
hasznlhatk, mint a vector. A list-hez pldul ilyen szolgltats nem ll rendelkezsnkre.
16.3.9. Tovbbi tagfggvnyek
Nagyon sok algoritmus kztk a fontos rendez eljrsok elemek felcserlsvel dolgo-
zik. A csere (13.5.2) legegyszerbb megvalstsa, hogy egyszeren tmsolgatjuk az ele-
meket. A vector osztlyt azonban ltalban olyan szerkezet brzolja, amely az elemek le-
rjt (handle) trolja (13.5, 17.1.3). gy kt vector sokkal hatkonyabban is felcserlhet,
ha a lerkat cserljk fel. A vector::swap() fggvny ezt valstja meg. Az alaprtelmezett
swap() nagysgrendekkel lassabb, mint a fenti eljrs:
template <class T, class A = allocator<T> > class vector {
public:
// ...
void swap(vector&);
allocator_type get_allocator() const;
};
A get_allocator() fggvny lehetsget ad a programoznak arra, hogy elrje a vector me-
mriafoglaljt (16.3.1, 16.3.4). Erre a szolgltatsra ltalban akkor van szksgnk, ami-
kor egy, a vektorhoz kapcsold adatnak a programban ugyangy akarunk memrit fog-
lalni, mint a vector-nak magnak (19.4.1)
16.3.10. Segdfggvnyek
Kt vector objektumot az == s a < opertor segtsgvel hasonlthatunk ssze:
template <class T, class A>
bool std::operator==(const vector<T,A>& x, const vector<T,A>& y);
16. A knyvtr szerkezete s a trolk 603
template <class T, class A>
bool std::operator<(const vector<T,A>& x, const vector<T,A>& y);
A v1 s a v2 vector akkor egyenl, ha v1.size()==v2.size() s v1[n]==v2[n] minden rtelmes
n index esetn. Hasonlan, a < a nyelvi elemek szerinti rendezst jelenti, teht a < mvele-
tet a vector esetben a kvetkezkppen hatrozhatjuk meg:
template <class T, class A>
inline bool std::operator<(const vector<T,A>& x, const vector<T,A>& y)
{
return lexicographical_compare (x.begin(),x.end(),y.begin(),y.end()); // lsd 18.9
}
Ez azt jelenti, hogy x akkor kisebb, mint y, ha az els olyan x[i] elem, amely nem egyezik
meg a megfelel y[i] elemmel, kisebb, mint y[i], vagy x.size()<y.size() s minden x[i] meg-
egyezik a megfelel y[i] rtkkel.
A standard knyvtr az == s a < defincijnak megfelelen meghatrozza a !=, a <=, a >,
s a >= opertort is.
Mivel a swap() egy tagfggvny, v1.swap(v2) formban hvhatjuk meg. Nem minden tpus-
ban szerepel azonban a swap() tagfggvny, gy az ltalnos algoritmusok a swap(a,b) for-
mt hasznljk. Ahhoz, hogy ez a forma a vector-ok esetben is mkdjn, a standard
knyvtr a kvetkez specializcit adja meg:
template <class T, class A> void std::swap(vector<T,A>& x, vector<T,A>& y)
{
x.swap(y);
}
16.3.11. Vector<bool>
A vector<bool> specializlt osztly (13.5) logikai rtkeknek egy tmrtett vektort val-
stja meg. Egy bool tpus vltozt is meg kell tudnunk cmezni, gy az legalbb egy bjtot
foglal. A vector<bool> osztlyt azonban knnyen elkszthetjk gy is, hogy minden elem
csak egy bitet foglaljon.
A szoksos vector mveletek ugyanolyan jelentssel hasznlhatk a vector<bool> esetben is.
Klnsen fontos, hogy az indexels s a bejrs is elvrsainknak megfelelen mkdnek:
A standard knyvtr 604
void f(vector<bool>& v)
{
for (int i = 0; i<v.size(); ++i) cin >> v[i]; // ciklus index hasznlatval
typedef vector<bool>::const_iterator VI;
for (VI p = v.begin(); p!=v.end(); ++p) cout<<*p; // ciklus bejrk
// hasznlatval
}
Ennek elrshez utnozni kell a bitenknti cmzst. Mivel egy mutat az egy bjtnl kisebb
memriaegysgeket nem kpes azonostani, a vector<bool>::iterator nem lehet mutat.
A bool* pldul biztosan nem hasznlhat bejrknt a vector<bool> osztlyban:
void f(vector<bool>& v)
{
bool* p = v.begin(); // hiba: nem megfelel tpus
// ...
}
A klnll bitek megcmzsnek mdjt a 17.5.3 pontban mutatjuk be.
A knyvtrban szerepel a bitset tpus is, amely logikai rtkek halmazt trolja, a logikai hal-
mazok szoksos mveleteivel (17.5.3).
16.4. Tancsok
[1] Hordozhat programok ksztshez hasznljuk a standard knyvtr szolglta-
tsait. 16.1.
[2] Ne prbljuk jraalkotni a standard knyvtr szolgltatsait. 16.1.2.
[3] Ne higgyk, hogy minden esetben a standard knyvtr jelenti a legjobb
megoldst.
[4] Amikor j szolgltatsokat hozunk ltre, gondoljuk vgig, hogy nem valstha-
tk-e meg a standard knyvtr nyjtotta kereteken bell. 16.3.
[5] Ne felejtsk, hogy a standard knyvtr szolgltatsai az std nvtrhez tartoznak.
16.1.2.
[6] A standard knyvtr szolgltatsait a megfelel fejllomny segtsgvel ptsk
be, ne hasznljunk kzvetlen deklarcit. 16.1.2.
16. A knyvtr szerkezete s a trolk 605
[7] Hasznljuk ki a ksei elvonatkoztats elnyeit. 16.2.1.
[8] Kerljk a kvr felletek hasznlatt. 16.2.2.
[9] Ha az elemeken visszafel akarunk haladni, hasznljunk reverse_iterator-okat
iterator helyett. 16.3.2.
[10] Ha iterator-t szeretnnk csinlni egy reverse_iterator-bl, hasznljuk a base()
fggvnyt. 16.3.2.
[11] Trolkat referencia szerint adjunk t. 16.3.4.
[12] Ha egy trol elemeire akarunk hivatkozni, mutatk helyett hasznljunk bejr-
tpusokat (pldul list<char>::iterator). 16.3.1.
[13] Hasznljunk const bejrkat, ha a trol elemeit nem akarjuk megvltoztatni.
16.3.1.
[14] Ha tartomnyellenrzst akarunk vgezni akr kzvetlenl, akr kzvetve ,
hasznljuk az at() fggvnyt. 16.3.3.
[15] Hasznljuk inkbb a push_back() vagy a resize() fggvnyt egy trolban, mint
a realloc() fggvnyt egy tmbben. 16.3.5.
[16] Ne hasznljuk egy tmretezett vector bejrit. 16.3.8.
[17] A bejrk rvnytelenn vlst elkerlhetjk a reserve() fggvny hasznlat-
val. 16.3.8.
[18] Ha szksges, a reserve() fggvnyt felhasznlhatjuk a teljestmny kiszmtha-
tv ttelre is. 16.3.8.
16.5. Feladatok
A fejezet legtbb feladatnak megoldst knnyen megtallhatjuk, ha megnzzk a stan-
dard knyvtr adott vltozatt. Mieltt azonban megnznnk, hogy a knyvtr kszti ho-
gyan kzeltettk meg az adott problmt, rdemes sajt megoldst ksztennk.
1. (*1.5.) Ksztsnk egy vector<char> tpus objektumot, amely az bc betit
tartalmazza, sorrendben. rassuk ki ennek a tmbnek a tartalmt elre, majd
visszafel.
2. (*1.5.) Ksztsnk egy vector<string> objektumot, majd olvassuk be gyml-
csk neveit a cin eszkzrl. Rendezzk a listt, majd rassuk ki elemeit.
3. (*1.5.) A 16.5[2] feladat vektornak felhasznlsval rjunk olyan ciklust, amely
az sszes 'a' betvel kezdd nev gymlcst jelenti meg.
4. (*1.) A 16.5[2] feladat vektornak felhasznlsval rjunk olyan ciklust, amellyel
trlhetjk az sszes 'a' betvel kezdd nev gymlcst.
5. (*1.) A 16.5[2] feladat vektornak felhasznlsval rjunk olyan ciklust, amellyel
az sszes citrusflt trlhetjk.
A standard knyvtr 606
6. (*1.5.) A 16.5[2] feladat vektornak felhasznlsval rjunk olyan ciklust, amely
azokat a gymlcsket trli, amelyeket nem szeretnk.
7. (*2.) Fejezzk be a 16.2.1 pontban elkezdett Vector, List s Itor osztlyt.
8. (*2.5.) Az Itor osztlybl kiindulva gondoljuk vgig, hogyan kszthetnk
bejrkat elre halad bejrsokhoz, visszafel halad bejrsokhoz, olyan
trolhoz, amely a bejrs alatt megvltozhat, illetve megvltoztathatatlan tro-
lhoz. Szervezzk gy ezeket a trolkat , hogy a programoz mindig azt
a bejrt hasznlhassa, amelyik a leghatkonyabb megoldst knlja az adott al-
goritmus megvalstsra. A trolkban kerljnk minden kdismtlst. Milyen
ms bejr-fajtra lehet szksge egy programoznak? Gyjtsk ssze az lta-
lunk hasznlt megkzelts elnyeit s htrnyait.
9. (*2.) Fejezzk be a 16.2.2 pontban elkezdett Container, Vector s List osztly
megvalstst.
10. (*2.5.) lltsunk el 10 000 darab egyenletes eloszls vletlenszmot a 0-1023
tartomnyban s troljuk azokat a.) a standard knyvtr vector osztlyval, b.)
a 16.5[7] feladat Vector osztlyval, c.) a 16.5[9] feladat Vector osztlyval.
Mindegyik esetben szmoljuk ki a vektor elemeinek szmtani kzept (mintha
mg nem tudnnk). Mrjk a ciklusok lefutsnak idejt. Becsljk meg vagy
szmoljuk ki a hromfle vektor memria-felhasznlst, s hasonltsuk ssze
az rtkeket.
11. (*1.5.) rjunk egy bejrt, amely lehetv teszi, hogy a 16.2.2 pontban bemuta-
tott Vector osztlyt a 16.2.1 pontban hasznlt trol stlusban kezeljk.
12. (*1.5.) Szrmaztassunk egy osztlyt a Container-bl, amely lehetv teszi, hogy
a 16.2.1 vektort a 16.2.2 pontban bemutatott trolstlusban hasznljuk.
13. (*2.) Ksztsnk olyan osztlyokat, amelyek lehetv teszik, hogy a 16.2.1 s
a 16.2.2 Vector osztlyait szabvnyos trolkknt hasznljuk.
14. (*2) rjunk egy ltez (nem szabvnyos, nem tanknyvi plda) troltpushoz
egy olyan sablont, amely ugyanazokkal a tagfggvnyekkel s tpus tagokkal
rendelkezik, mint a szabvnyos vector. Az eredeti troltpust ne vltoztassuk
meg. Hogyan tudjuk felhasznlni azokat a szolgltatsokat, amelyeket a nem
szabvnyos vektor megvalst, de a vector nem?
15. (*1.5) Gondoljuk vgig, hogyan viselkedik a 16.3.6 pontban bemutatott
duplicate_elements() fggvny egy olyan vector<string> esetben, melynek
hrom eleme: ne tedd ezt.
16. A knyvtr szerkezete s a trolk 607
Szabvnyos trolk
Itt az ideje, hogy munknkat
vgre szilrd elmleti
alapokra helyezzk.
(Sam Morgan)
Szabvnyos trolk Trolk s mveletek - ttekints Hatkonysg brzols Meg-
ktsek az elemekre Sorozatok vector list deque talaktk stack
queue priority_queue Asszociatv trolk map sszehasonltsok multimap
set multiset Majdnem-trolk bitset Tmbk Hast tblk A hash_map egy
megvalstsa Tancsok Gyakorlatok
17.1. A szabvnyos trolk
A standard knyvtr ktfle trolt tartalmaz: sorozatokat (sequences) s asszociatv trol-
kat (associative container). A sorozatok mindegyike nagyon hasonlt a vector trolra
(16.3). Ha kln nem emltjk, ugyanazok a tagtpusok s fggvnyek hasznlhatk ezek-
re is, mint a vektorokra, s eredmnyk is ugyanaz lesz. Az asszociatv trolk ezen kvl
lehetsget adnak az elemek kulcsokkal trtn elrsre is (3.7.4).
17
A beptett tmbk (5.2), a karakterlncok (20. fejezet), a valarray osztly (22.4) s
a bitset (bithalmaz) tpusok szintn elemek trolsra szolglnak, gy ezeket is trolknak
tekinthetjk. E tpusok azonban mgsem tkletesen kidolgozott, szabvnyos trolk. Ha
a standard knyvtr elvrsainak megfelelen prblnnk meg elkszteni ket, akkor el-
sdleges cljukat nem tudnk maradktalanul elrni. A beptett tmbktl pldul nem
vrhatjuk el, hogy egyszerre tartsk nyilvn sajt mretket s ugyanakkor teljesen ssze-
egyeztethetek maradjanak a C tmbkkel.
A szabvnyos trolk alaptlete, hogy logikailag felcserlhetek legyenek minden rtelmes
helyzetben. gy a felhasznl mindig szabadon vlaszthat kzlk, az ppen hasznlni k-
vnt mveletek, illetve a hatkonysgi szempontok figyelembe vtelvel. Ha pldul gyak-
ran kell elrnnk elemeket valamilyen kulcsrtk segtsgvel, akkor a map (17.4.1) tro-
lt hasznljuk. Ha legtbbszr a szoksos listamveletekre van szksgnk, akkor a list
(17.2.2) a megfelel osztly. Ha sokszor kell hozzfznnk elemeket a trol vghez,
vagy ppen el kell onnan tvoltanunk elemeket, akkor a deque (ktvg sor, 17.2.3),
a stack (17.3.1) vagy a queue (17.3.2) nyjtja a legtbb segtsget. Ezeken kvl maga
a programoz is kifejleszthet olyan trolkat, melyek pontosan illeszkednek a szabvnyos
trolk ltal knlt keretrendszerbe (17.6). Leggyakrabban a vector osztlyt fogjuk hasznl-
ni, mert ennek elnyeit rengeteg felhasznlsi terleten kiaknzhatjuk.
Az tlet, hogy a klnbz jelleg trolkat vagy mg ltalnosabban, az sszes inform-
ciforrst egysges formban kezeljk, elvezet minket az ltalnostott (generikus) prog-
ramozs fogalmhoz (2.7.2, 3.8). Ezt a szemlletmdot kvetve a standard knyvtr na-
gyon sok ltalnos algoritmust biztost (18. fejezet), melyek megkmlik a programozt
attl, hogy kzvetlenl az egyes trolk rszleteivel foglalkozzon.
17.1.1. Mveletek ttekints
Ebben a pontban felsoroljuk azokat az osztlytagokat, amelyek minden, vagy majdnem
minden trolban megtallhatk. Ha rszleteket szeretnnk megtudni, nzzk meg a meg-
felel fejllomnyt (<vector>, <list>, <map> stb, 16.1.2).
A standard knyvtr 610
A trolt tekinthetjk elemek sorozatnak, akr abban a sorrendben, amelyet a trol bejr-
ja meghatroz, akr ellenkez irnyban. Egy asszociatv trol esetben a sorrendet a trol
sszehasonltsi szempontja hatrozza meg (ami alaprtelmezs szerint a < mvelet).
17. Szabvnyos trolk 611
Tpusok (16.3.1)
value_type Az elemek tpusa.
allocator_type A memriakezel tpusa.
size_type Tpus az indexelshez, elemszmllshoz stb.
difference_type A bejrk kztti klnbsg tpusa.
iterator gy viselkedik, mint a value_type* tpus.
const_iterator A const value_type* megfelelje.
reverse_iterator A trol elemeit fordtott sorrendben ltjuk,
egybknt a value_type* megfelelje.
const_reverse_iterator A trol elemeit fordtott sorrendben ltjuk;
a const value_type* tpushoz hasonlt.
reference Olyan, mint a value_type&.
const_reference Olyan, mint a const value_type&.
key_type A kulcs tpusa (csak asszociatv trolkhoz).
mapped_type A mapped_value tpusa (csak asszociatv
trolkhoz).
key_compare Az sszehasonltsi szempont tpusa (csak asszoci-
atv trolkhoz).
Bejrk (16.3.2)
begin() Az els elemre mutat.
end() Az utols utni elemre mutat.
rbegin() Visszafel halad felsorols esetn az els elemre
mutat.
rend() Visszafel halad felsorols esetn az utols utni
elemre mutat.
Nhny elemet kzvetlenl is elrhetnk:
A vektorok (vector) s a ktvg sorok (deque) olyan hatkony mveleteket is biztostanak,
amelyek az elemek sorozatnak vgn (back) vgeznek mdostsokat. A listk (list) s
a ktvg sorok (deque) az ezekkel egyenrtk mveleteket az elemsorozatok elejn
(front) is kpesek elvgezni:
A trolk lehetv teszik a listamveletek hasznlatt is:
A standard knyvtr 612
Hozzfrs elemekhez (16.3.3)
front() Az els elem.
back() Az utols elem.
[ ] Indexels, ellenrzs nlkli hozzfrs. (Listk-
nl nem hasznlhat.)
at() Indexels, ellenrztt vltozat. (Listknl nem
hasznlhat.)
Verem- s sormveletek (16.3.5, 17.2.2.2)
push_back() Elem beszrsa a trol vgre.
pop_back() Elem eltvoltsa a trol vgrl.
push_front() j els elem beillesztse (csak listkhoz s ktv-
g sorokhoz).
pop_front() Az els elem eltvoltsa (csak listkhoz s ktv-
g sorokhoz).
Listamveletek (16.3.6)
insert(p,x) x beillesztse p el.
insert(p,n,x) x elem n darab msolatnak beillesztse p el.
insert(p,first,last) Elemek beszrsa a [first:last[ tartomnybl a p el.
erase(p) A p helyen lv elem eltvoltsa.
erase(first,last) A [first:last[ tartomny trlse.
clear() Az sszes elem trlse.
Minden trolban tallhatunk az elemek szmval kapcsolatos mveleteket, illetve nhny
egyb fggvnyt is:
A trolk szmos konstruktort, illetve rtkad opertor biztostanak:
17. Szabvnyos trolk 613
Tovbbi mveletek (16.3.8, 16.3.9, 16.3.10)
size() Az elemek szma.
empty() res a trol?
max_size() A legnagyobb lehetsges trol mrete.
capacity() A vector szmra lefoglalt terlet mrete (csak
vektorokhoz).
reserve() Memriafoglals a ksbbi nvelsek gyorsts-
hoz (csak vektorokhoz).
resize() A trol mretnek mdostsa (csak vektorok-
hoz, listkhoz s ktvg sorokhoz).
swap() Kt trol elemeinek felcserlse.
get_allocator() A trol memriafoglaljnak lemsolsa.
== Megegyezik a kt trol tartalma?
!= Klnbzik a kt trol tartalma?
< Az egyik trol bcsorrendben megelzi
a msikat?
Konstruktorok stb. (16.3.4)
container() res trol.
container(n) n darab elem, alaprtelmezett rtkkel (asszociatv
trolkhoz nem hasznlhat).
container(n,x) x elem n pldnybl ksztett trol (asszociatv
trolkhoz nem hasznlhat).
container(first,last) A kezdelemek a [first:last[ tartomnybl
szrmaznak.
container(x) Msol konstruktor. A kezdeti elemeket az x tro-
l elemei adjk.
~container() A trol s sszes elemnek megsemmistse.
Az asszociatv trolk lehetv teszik az elemek kulcs alapjn trtn elrst:
Ezen ltalnos mveleteken kvl a legtbb trol nhny egyedi mveletet is biztost.
A standard knyvtr 614
rtkadsok (16.3.4)
operator=(x) Msol rtkads. Az elemek az x trolbl
szrmaznak.
assign(n,x) x elem n pldnynak beillesztse a trolba
(asszociatv trolknl nem hasznlhat).
assign(first,last) rtkads a [first:last[ tartomnybl.
Asszociatv mveletek (17.4.1)
operator[ ](k) A k kulcs elem elrse (egyedi kulccsal rendel-
kez trolkhoz).
find(k) k kulccsal rendelkez elem keresse.
lower_bound(k) Az els k kulcs elem megkeresse.
upper_bound(k) Az els olyan elem megkeresse, melynek kulcsa
nagyobb, mint k.
equal_range(k) A k kulcs elemek als s fels hatrnak
megkeresse.
key_comp() A kulcs-sszehasonlt objektum msolata.
value_comp() A mapped_value rtkeket sszehasonlt objek-
tum msolata.
17.1.2. Trolk ttekints
A szabvnyos trolkat (container) az albbi tblzat foglalja ssze:
Az Elrs oszlopban a Kzvetlen azt jelenti, hogy az elemek tetszleges sorrendben elrhe-
tk (kzvetlen elrs, random access), a Ktirny esetben pedig ktirny (bidirectional)
soros hozzfrs bejrkat hasznlhatunk. A ktirny bejrk mveletei rszhalmazt
kpezik a kzvetlen elrsek mveleteinek (19.2.1).
A tblzat tovbbi elemeibl az adott mvelet hatkonysgra kvetkeztethetnk. A kons-
tans rtk azt fejezi ki, hogy az adott mvelet vgrehajtsi ideje nem fgg a trolban trolt
elemek szmtl. A konstans idigny egy msik szoksos jellse O(1). Az O(n) rtk azt
fejezi ki, hogy a szmtsi id arnyos a feldolgozott elemek szmval. A + jellst azokban
17. Szabvnyos trolk 615
A szabvnyos trolk mveletei
[ ] Lista- Mveletek Mveletek Bejrk
mveletek a trol a trol v-
elejn gn (verem
mveletek)
16.3.3 16.3.6 17.2.2.2 16.3.5 19.2.1
17.4.1.3 20.3.9 20.3.9 20.3.12
vector konstans O(n)+ konstans+ Kzvetlen
list konstans konstans konstans Ktirny
deque konstans O(n) konstans konstans Kzvetlen
stack konstans
queue konstans konstans
priority_queue O(log(n)) O(log(n))
map O(log(n)) O(log(n))+ Ktirny
multimap O(log(n))+ Ktirny
set O(log(n))+ Ktirny
multiset O(log(n))+ Ktirny
string konstans O(n)+ O(n)+ konstans+ Kzvetlen
array konstans Kzvetlen
valarray konstans Kzvetlen
bitset konstans
az esetekben hasznltuk, ahol idnknt jelents mennyisg tbbletterhels is elfordul-
hat. Egy elem beszrsa egy listba pldul mindig ugyanannyi ideig tart, gy a megfelel
helyen a konstans rtket olvashatjuk. Ugyanez a mvelet a vector esetben a beszrsi
pont utn ll sszes elem thelyezsvel jr, gy ott az O(n) rtk szerepel. St, idnknt
az sszes elemet (teht a beszrsi pont elttieket is) t kell helyezni, ezrt a + jelet is meg-
adtuk. A nagy O (ordo) egy hagyomnyos jellsmd, a + jelet csak azrt hasznltam,
hogy kiegszt informcit adjak azoknak a programozknak, akik az tlagos teljestmny
mellett a mvelet idejnek megjsolhatsgra is hangslyt helyeznek. Az O(n)+ jells ha-
gyomnyos jelentse amortizlt lineris id (amortized linear time, vagyis kb. tbblet-
terhet jelent, egyenes arnyossgban nvekv id).
Termszetesen, ha a konstans egy nagyon hossz idtartamot jelent, akkor ez nagyobb
lehet, mint az elemek szmval egyenesen arnyos idigny. Nagy adatszerkezetek eset-
ben azonban a konstans jelentse mgis olcs, az O(n) jelentse pedig drga, mg az
O(log(n)) idt tekinthetjk elg olcsnak. Mg viszonylag nagy n rtkek esetn is, az
O(log(n)) kzelebb ll a konstanshoz, mint az O(n)-hez. Azoknak a programozknak,
akiknek programjaik kltsgeivel komolyan foglalkozniuk kell, ennl alaposabb ismere-
tekre is szksgk van. Tisztban kell lennik azzal, mely elemek figyelembe vtelvel ala-
kul ki n rtke. Olyan alapmvelet szerencsre nincs, melyet nagyon drgnak nevezhet-
nnk, ami O(n*n) vagy mg jelentsebb idignyt jelentene.
A string kivtelvel az itt kzlt kltsgrtkek megfelelnek a C++ szabvny elvrsainak.
A string tpusnl szerepl becslsek sajt felttelezseim.
A bonyolultsgnak, illetve az idignynek ezen mrszmai a fels hatrt jelentik. Szere-
pk abban ll, hogy a programoz megtudhatja bellk, mit vrhat el az adott szolgltats-
tl. Termszetesen a konkrt megvalstsok kszti igyekeznek a lnyeges helyeken
ezeknl jobb megoldst biztostani.
17.1.3. brzols
A szabvny nem r el semmilyen egyedi brzolsi mdot egyik trol esetben sem.
A szabvny csak a trol fellett rja le, illetve nhny bonyolultsgi kvetelmnyt hat-
roz meg. Az egyes nyelvi vltozatok kszti maguk vlaszthatjk meg a legmegfelelbb s
gyakran nagyon jl optimalizlt megvalstsi mdot, amely kielgti az ltalnos kvetel-
mnyeket. A trolkat ltalban olyan adatszerkezet segtsgvel hozzk ltre, amely az
elemek elrst biztost azonost mellett a mretre s kapacitsra vonatkoz informci-
kat is nyilvntartja. A vector esetben az elemek adatszerkezete leggyakrabban egy tmb:
A standard knyvtr 616
A list megvalstsnak legegyszerbb eszkze a lncols, ahol az elemek egymsra mutatnak:
A map leggyakoribb megvalstsa a (kiegyenslyozott) binris fa, ahol a cscsok vagy ms
nven csompontok (kulcs, rtk) prokat jellnek ki:
A string megvalstsra a 11.12 pontban adtunk egy tletet, de elkpzelhetjk tmbk so-
rozataknt is, ahol minden tmb nhny karaktert trol:
17. Szabvnyos trolk 617
mret
brzols
tartalk hely elemek
vektor:
list:
map:
(kulcs, rtk) prok:
rszlncok:
string:
brzols
brzols
cscs
rszlnc-lerk
cscs
brzols
...
elemek:
17.1.4. Megktsek az elemekre
A trolban trolt elemek a beillesztett objektumok msolatai, gy ahhoz, hogy egy objek-
tum bekerlhessen a trolba, olyan tpusnak kell lennie, amely lehetv teszi, hogy a t-
rol msolatot ksztsen rla. A trol az elemeket egy msol konstruktor vagy egy rtk-
ads segtsgvel msolhatja le. A msols eredmnynek mindkt esetben egyenrtknek
kell lennie az eredeti objektummal. Ez nagyjbl azt jelenti, hogy minden elkpzelhet
egyenlsgvizsglat azonosnak kell, hogy minstse az eredeti s a msolt objektumot.
Az elemek msolsnak teht gy kell mkdnie, mint a beptett tpusok (kztk a muta-
tk) szoksos msolsnak. Az albbi rtkads pldul nagy lpst jelent afel, hogy az X
tpus egy szabvnyos trol elemeinek tpusa legyen:
X& X::operator=(const X& a) // helyes rtkad opertor
{
// 'a' minden tagjnak msolsa *this-be
return *this;
}
Az albbi kdrszlet Y tpusa viszont helytelen, mert a visszatrsi rtk nem a szoksos s
a jelents sem megfelel.
void Y::operator=(const Y& a) // helytelen rtkad opertor
{
// 'a' minden tagjnak trlse
}
A szabvnyos trolk szablyaitl val eltrsek egy rszt mr a fordt kpes jelezni, de
sok esetben ezt a segtsget sem kapjuk meg, csak teljesen vratlan esemnyekkel kerlnk
szembe. Egy olyan msolsi mvelet pldul, amely kivtelt vlthat ki, rszlegesen tmsolt
elemet is eredmnyezhet. Ennek kvetkezmnyekppen a trol olyan llapotba kerl,
amely csak jval ksbb okoz problmkat. Az ilyen msolsi mveletek teht mr nma-
gukban is rossz tervezs kvetkezmnyei (14.4.6.1).
Ha az elemek msolsa nem lehetsges, megoldst az jelenthet, ha a trolban konkrt ob-
jektumok helyett azokra hivatkoz mutatkat helyeznk el. Ennek legnyilvnvalbb pl-
dja a tbbalak (polimorf) tpusok esete (2.5.4, 12.2.6). Ha a tbbalaksg lehetsgt
meg akarjuk rizni, akkor a vector<Shape> helyett pldul a vector<Shape*> osztlyra lesz
szksgnk.
A standard knyvtr 618
17.1.4.1. sszehasonltsok
Az asszociatv trolk megkvetelik, hogy elemeiket ssze lehessen hasonltani. Ugyanez
elmondhat ms trolk nhny mveletrl is (pldul a rendez eljrsokrl, mint
a sort()). Alaprtelmezs szerint a sorrend meghatrozsra a < opertort hasznljuk. Ha ez
az adott esetben nem hasznlhat, akkor a programoznak valamilyen ms megoldst kell
tallnia (17.4.1.5, 18.4.2). A rendezsi szempontnak szigoran megengednek kell lennie
(strict weak ordering), ami lnyegben azt jelenti, hogy a kisebb mint s egyenl mve-
letnek is tranzitvnak kell lennie. Vegyk pldul a kvetkez cmp rendezst:
1. cmp(x,x) mindig hamis.
2. Ha cmp(x,y) s cmp(y,z), akkor cmp(x,z).
3. Hatrozzuk meg az egyenlsg equiv(x,y) mvelett a kvetkez kifejezssel:
!(cmp(x,y)||cmp(y,x)). gy, ha equiv(x,y) s equiv(y,z), akkor equiv(x,z).
Ennek megfelelen a kvetkezket rhatjuk:
template<class Ran> void sort(Ran first, Ran last); // a < hasznlata az
// sszehasonltsra
template<class Ran, class Cmp> void sort(Ran first, Ran last, Cmp cmp);
// a cmp hasznlata
Az els vltozat a < opertort hasznlja, mg a msodik a programoz ltal megadott cmp
rendezst. Az elz fejezet gymlcsket tartalmaz troljnak esetben pldul szks-
gnk lehet egy olyan rendez eljrsra, amely nem klnbzteti meg a kis- s nagybetket.
Ezt gy rhetjk el, hogy ltrehozunk egy fggvnyobjektumot (function object) (11.9,
18.4), amely egy string-prra elvgzi az sszehasonltst:
class Nocase { // kis- s nagybetket nem megklnbztet karakterlnc-
// sszehasonlts
public:
bool operator()(const string&, const string&) const;
};
bool Nocase::operator()(const string& x, const string& y) const
// igazat ad vissza, ha x bcsorrendben megelzi y-t; a kis- s nagybetk kztti
// klnbsg nem szmt
{
string::const_iterator p = x.begin();
string::const_iterator q = y.begin();
17. Szabvnyos trolk 619
while (p!=x.end() && q!=y.end() && toupper(*p)==toupper(*q)) {
++p;
++q;
}
if (p == x.end()) return q != y.end();
if (q == y.end()) return false;
return toupper(*p) < toupper(*q);
}
A sort fggvnyt ezutn meghvhatjuk ezzel a rendezsi szemponttal. Pldul az albbi so-
rozatbl kiindulva:
fruit:
alma krte Alma Krte citrom
A sort(fruit.begin(), fruit.end, Nocase()) rendezs eredmnye a kvetkez lesz:
fruit:
Alma alma citrom Krte krte
Ugyanakkor az egyszer sort(fruit.begin(), fruit.end()) a kvetkez eredmnyt adn (felt-
telezve, hogy olyan karakterkszletet hasznlunk, ahol a nagybetk megelzik a kisbetket):
fruit:
Alma Krte alma citrom krte
Vigyzzunk r, hogy a < opertor a C stlus karakterlncoknl (teht a char* tpusnl) nem
bcsorrend szerinti rendezst jelent (13.5.2). Ennek pldul az a kvetkezmnye, hogy
az olyan asszociatv trolk, melyeknek kulcsa C stlus karakterlnc, nem gy mkdnek,
mint ahogy azt els rnzsre gondolnnk. Ezen kellemetlensg kijavtshoz egy olyan <
opertort kell hasznlnunk, amely tnyleg bcsorrend szerinti sszehasonltst vgez:
struct Cstring_less {
bool operator()(const char* p, const char* q) const { return strcmp(p,q)<0; }
};
map<char*,int,Cstring_less> m; // const char* kulcsok sszehasonltsra a strcmp()
// fggvnyt hasznl asszociatv tmb
A standard knyvtr 620
17.1.4.2. Tovbbi relcis mveletek
A trolk s az algoritmusok alaprtelmezs szerint a < mveletet hasznljk, amikor az
elemek kztt sorrendet kell megllaptaniuk. Ha ez az alaprtelmezs nem felel meg
a programoznak, akkor j sszehasonltsi szempontot is megadhat. Az egyenlsgvizsg-
lat mvelett viszont nem adhatjuk meg kzvetlenl. Ha van egy sorrend-meghatroz fgg-
vnynk (pldul a cmp), akkor az egyenlsget kt sorrendvizsglattal llapthatjuk meg:
if (x == y) // megadott felhasznli sszehasonlts esetn nem hasznlatos
if (!cmp(x,y) && !cmp(y,x)) // a felhasznli cmp sszehasonlts esetn hasznlatos
Ez a lehetsg megkml minket attl, hogy minden asszociatv trol s szmos algoritmus
esetben kln paramterknt adjuk t az egyenlsgvizsgl eljrst. Els rnzsre azt
mondhatnnk, hogy ez a megolds nem tl hatkony, de a trolk rendkvl ritkn vizsgl-
jk elemeik egyenlsgt, s ilyenkor is ltalban elegend egyetlen cmp() hvs.
A kisebb mint (alaprtelmezs szerint < ) mvelettel vgzett egyenrtksg-vizsglat
gyakorlati okokbl is hasznosabb lehet, mint az egyenl (==) hasznlata. Az asszociatv
trolk (17.4) pldul a ! (cmp(x,y)||cmp(y,x)) egyenrtksg-vizsglattal hasonltjk
ssze a kulcsaikat. Az egyenrtk kulcsok nem felttlenl egyenlk. Egy olyan multimap
(17.4.2) pldul, amelynek sorrend-meghatroz fggvnye nem klnbzteti meg a kis-
s nagybetket, a Last, last, lAst, s lasT karakterlncokat egyenrtknek fogja minsteni,
annak ellenre, hogy az == opertor klnbznek tartja azokat. gy teht lehetsgnk
nylik arra, hogy a szmunkra lnyegtelen klnbsgektl a rendezsben eltekintsnk.
A < s az == opertor segtsgvel a tbbi szoksos sszehasonlt mveletet knnyen
definilhatjuk. A standard knyvtr az std::rel_ops nvtrben adja meg ezeket s a <utility>
fejllomny segtsgvel hasznlhatk:
template<class T> bool rel_ops::operator!=(const T& x, const T& y) { return !(x==y); }
template<class T> bool rel_ops::operator>(const T& x, const T& y) { return y<x; }
template<class T> bool rel_ops::operator<=(const T& x, const T& y) { return !(y<x); }
template<class T> bool rel_ops::operator>=(const T& x, const T& y) { return !(x<y); }
A rel_ops nvtr alkalmazsa biztostja, hogy az opertorokat knnyen elrhetjk, amikor
szksg van rjuk, viszont meghatrozsuk nem trtnik meg titokban, ha explicit nem
krjk a nvtr hasznlatt.
void f()
{
using namespace std;
// a !=, > stb. alaprtelmezs szerint nem jn ltre
}
17. Szabvnyos trolk 621
void g()
{
using namespace std;
using namespace std::rel_ops;
// a !=, > stb. alaprtelmezs szerint ltrejn
}
A !=, stb. opertorokat nem az std nvtr rja le, mert nem mindig van rjuk szksg, s bizo-
nyos esetekben meghatrozsuk meg is vltoztatn a felhasznl programjnak mkdst.
Pldul, ha egy ltalnos matematikai knyvtrat akarunk kszteni, akkor valsznleg a sa-
jt relcis mveleteinkre lesz szksgnk s nem a beptett fggvnyekre.
17.2. Sorozatok
A sorozatok krlbell gy nznek ki, mint a korbban (16.3) bemutatott vector. A stan-
dard knyvtr ltal biztostott alapvet sorozatok a kvetkezk:
vector list deque
Ezekbl szrmaznak a megfelel fellet kialaktsval az albbi trolk:
stack queue priority_queue
Ezeket a sorozatokat trol-talaktknak (container adapter), sorozat-talaktknak
(sequence adapter) vagy egyszeren talaktknak (adapter, 17.3) nevezzk.
17.2.1. A vector
A szabvnyos vector osztllyal a 16.3 pontban mr rszletesen foglalkoztunk. Az elzetes
memriafoglals (reserve) lehetsge kizrlag a vektorokra jellemz. Az indexels a [ ]
opertorral ellenrizetlen adatelrst jelent. Ha ellenrztt hozzfrst szeretnnk, hasznl-
juk az at() fggvnyt (16.3.3), egy ellenrztt vektort (3.7.2) vagy egy ellenrztt bejrt
(19.3). A vector osztlyokban kzvetlen elrs (random-access) bejrkat (19.2.1) hasz-
nlhatunk.
A standard knyvtr 622
17.2.2. A list
A lista egy olyan sorozat, amely elemek beszrsra s trlsre a legalkalmasabb. A vector
(s a deque, 17.2.3) osztllyal sszehasonltva az indexels fjdalmasan lass lenne, gy
meg sem valstottk a list trolban. Ennek kvetkezmnye, hogy a list csak ktirny
(bidirectional) bejrkat (19.2.1) biztost, kzvetlen elrseket nem hasznlhatunk. A list
osztlyt ezek alapjn valamilyen ktirny lncolt listval szoks megvalstani (17.8[16]).
A lista mindazon tagtpusok s tagfggvnyek hasznlatt lehetv teszi, melyet a vektor
(16.3) esetben megtallunk, kivve az indexelst, a capacity() s a reserve() fggvnyeket:
template <class T, class A = allocator<T> > class std::list {
public:
// a vector-hoz hasonl tpusok s mveletek, kivve: [ ], at(), capacity(), s reserve()
// ...
};
17.2.2.1. thelyezs, rendezs, sszefsls
Az ltalnos sorozat-mveletek mellett a list szmos, kifejezetten a listkhoz ksztett m-
veleteket knl:
template <class T, class A = allocator<T> > class list {
public:
// ...
// kifejezetten listkhoz ksztett mveletek
void splice(iterator pos, list& x); // x minden elemnek thelyezse
// a listban lev pos el, msols nlkl
void splice(iterator pos, list& x, iterator p); // *p thelyezse x-bl
// a listban lev pos el, msols nlkl
void splice(iterator pos, list& x, iterator first, iterator last);
void merge(list&); // rendezett listk sszefslse
template <class Cmp> void merge(list&, Cmp);
void sort();
template <class Cmp> void sort(Cmp);
// ...
};
17. Szabvnyos trolk 623
Ezek a listamveletek stabilak (stable), ami azt jelenti, hogy az egyenrtk rtkkel ren-
delkez elemek egymshoz viszonytott (relatv) sorrendje nem vltozik meg.
A 16.3.6 pontban pldakppen a fruit troln vgeztnk mveleteket. Azok a feladatok
vltoztats nlkl elvgezhetk akkor is, ha a fruit trtnetesen egy lista. Ezenkvl az
egyik lista elemeit egyetlen splice mvelettel t is helyezhetjk egy msik listba. Induljunk
ki az albbi listkbl:
fruit:
alma krte
citrus:
narancs grapefruit citrom
A kvetkez mvelettel tudjuk a narancs elemet thelyezni a citrus listbl a fruit listba:
list<string>::iterator p = find_if(fruit.begin(),fruit.end(),initial('k'));
fruit.splice(p,citrus,citrus.begin());
A mvelet kiveszi a citrus els elemt, majd beilleszti ezt az elemet a fruit trol els k be-
tvel kezdd eleme el. Az eredmny teht a kvetkez:
fruit:
alma narancs krte
citrus:
grapefruit citrom
Jegyezzk meg, hogy a splice() nem kszt msolatokat az elemekrl gy, mint az insert()
(16.3.6), csak a listk adatszerkezett rendezi t a feladatnak megfelelen.
A splice() segtsgvel nem csak egyetlen elemet, hanem tartomnyokat, vagy akr teljes lis-
tkat is thelyezhetnk:
fruit.splice(fruit.begin(),citrus);
Eredmnye:
fruit:
grapefruit citrom alma narancs krte
citrus:
<res>
A standard knyvtr 624
A splice fggvny minden vltozatnak a msodik paramterben meg kell adnunk azt a list
objektumot, amelybl az elemeket t kell helyezni. Ez teszi lehetv, hogy az eredeti list-
bl trljk az elemet. Egy bejr erre nmagban nem lenne alkalmas, hiszen egy konk-
rt elemre mutat bejrbl semmilyen ltalnos mdszerrel nem lehet megtudni, hogy az
adott elem ppen melyik trolban tallhat (18.6).
Termszetesen, a bejr-paramternek olyan rvnyes bejrt kell tartalmaznia, amely
a megfelel listhoz kapcsoldik. Teht vagy a list egyik elemre kell mutatnia, vagy a lis-
ta end() fggvnynek rtkt kell tartalmaznia. Ha ez nem gy van, akkor az eredmny
nem meghatrozhat s ltalban vgzetes problmkat okoz:
list<string>::iterator p = find_if(fruit.begin(),fruit.end(),initial('k'));
fruit.splice(p,citrus,citrus.begin()); // rendben
fruit.splice(p,citrus,fruit.begin()); // hiba: fruit.begin() nem mutat citrus-ban lev elemre
citrus.splice(p,fruit,fruit.begin()); // hiba: p nem mutat citrus-ban lev elemre
Az els splice() helyes mg akkor is, ha a citrus trol res.
A merge() kt rendezett listt egyest (fsl ssze). Az egyik listbl trli az elemeket, mi-
kzben a msikba beszrja azokat, a rendezs megrzsvel.
f1:
alma birsalma krte
f2:
citrom grapefruit narancs lime
Ez a kt lista az albbi programrszlettel rendezhet s fzhet ssze:
f1.sort();
f2.sort();
f1.merge(f2);
Az eredmny a kvetkez lesz:
f1:
alma birsalma citrom grapefruit krte lime narancs
f2:
<res>
Ha az sszefslt listk valamelyike nem volt rendezett, a merge() akkor is olyan listt ered-
mnyez, amelyben a kt lista elemeinek unija szerepel, de az elemek helyes sorrendje
nem biztostott.
17. Szabvnyos trolk 625
A splice() fggvnyhez hasonlan a merge() sem msolja az elemeket, hanem a forrslist-
bl kiveszi, majd a cllistba beilleszti azokat. Az x.merge(y) fggvny meghvsa utn az
y lista res lesz.
17.2.2.2. Mveletek a lista elejn
A lista els elemre vonatkoz mveletek azoknak a minden sorozatban megtallhat elj-
rsoknak a prjai, melyek az utols elemre hivatkoznak (16.3.6):
template <class T, class A = allocator<T> > class list {
public:
// ...
// hozzfrs elemekhez
reference front(); // hivatkozs az els elemre
const_reference front() const;
void push_front(const T&); // j els elem hozzadsa
void pop_front(); // els elem eltvoltsa
// ...
};
A trol els elemnek neve front. A list esetben a front fejelem mveletei ugyanolyan ha-
tkonyak s knyelmesek, mint a sorozat vgt kezel fggvnyek (16.3.5). Ha mi dnt-
hetnk, akkor rdemesebb az utols elemet hasznlni, mert az gy megrt programrszletek
a vector s a list szmra is megfelelek. gy ha csak egy kicsi esly is van arra, hogy az l-
talunk listra hasznlt algoritmust valamikor ltalnos eljrsknt ms troltpusokra is
alkalmazzuk, akkor mindenkppen az utols elem mveleteit hasznljuk, hiszen ezek jval
szlesebb krben mkdkpesek. Itt is ahhoz a szablyhoz kell igazodnunk, miszerint
a lehet legnagyobb rugalmassg elrse rdekben rdemes a lehet legkisebb mvelet-
halmazt hasznlnunk egy feladat elvgzshez (17.1.4.1).
17.2.2.3. Tovbbi mveletek
Az elemek beszrsa s trlse rendkvl hatkony mvelet a list trolban. Ez termszete-
sen arra sztnzi a programozkat, hogy listt hasznljanak, ha programjaikban az ilyen
mveletek gyakoriak. Ezrt fontos, hogy az elemek kzvetlen trlsre ltalnos, szabv-
nyos mdszereket adjunk:
A standard knyvtr 626
template <class T, class A = allocator<T> > class list {
public:
// ...
void remove(const T& val);
template <class Pred> void remove_if(Pred p);
void unique(); // ktszer szerepl elemek eltvoltsa == hasznlatval
template <class BinPred> void unique(BinPred b); // ktszer szerepl elemek
// eltvoltsa b hasznlatval
void reverse(); // az elemek sorrendjnek megfordtsa
};
Pldul tegyk fel, hogy adott az albbi lista:
fruit:
alma narancs grapefruit citrom narancs grgdinnye krte birsalma
Ekkor a narancs rtkkel rendelkez elemeket a kvetkez paranccsal tvolthatjuk el:
fruit.remove("narancs");
Az eredmny teht:
fruit:
alma grapefruit citrom grgdinnye krte birsalma
Gyakran bizonyos felttelt kielgt elemeket szeretnnk trlni, nem csak egy adott rtk-
kel rendelkezket. A remove_if fggvny pontosan ezt a feladatot hajtja vgre. A kvetke-
z utasts pldul az sszes g betvel kezdd gymlcsnevet trli a fruit listbl:
fruit.remove_if(initial('g'));
A fggvny lefutsa utn a fruit a kvetkezkppen nz ki:
fruit:
alma citrom krte birsalma
A trlseknek gyakran az az oka, hogy meg szeretnnk szntetni az ismtldseket.
A unique() fggvny ezzel a cllal szerepel a list osztlyban:
fruit.sort();
fruit.unique();
17. Szabvnyos trolk 627
A rendezsre azrt van szksg, mert a unique() csak azokat az ismtldseket szri ki,
amelyeknl kzvetlenl egyms utn szerepel kt (vagy tbb) azonos rtk. Pldul ha
a fruit lista tartalma a kvetkez:
alma krte alma alma krte
akkor a fruit.unique() hvs nmagban az albbi eredmnyt adn:
alma krte alma krte
Ha elszr rendeznk, akkor a vgeredmny:
alma krte
Ha csak bizonyos ismtldseket akarunk megszntetni, akkor megadhatunk egy predik-
tumot (felttelt), amely meghatrozza, mely ismtldsek nem kellenek. Pldul meghat-
rozhatjuk a ktoperandus (binris) initial2(x) prediktumot (18.4.2), amely karakterln-
cokat vizsgl, s csak akkor ad igaz rtket, ha a karakterlnc x betvel kezddik. Teht ha
a kvetkez listbl indulunk ki:
krte krte alma alma
akkor a kvetkez utastssal el tudjuk tntetni az sszes k betvel kezdd, egyms utn
kvetkez nevet:
fruit.unique(initial2('k'));
Az eredmny a kvetkez lesz:
krte alma alma
A 16.3.2 pontban mr volt rla sz, hogy a trol elemeit nha fordtott sorrendben akar-
juk elrni. A list esetben lehetsg van arra, hogy az elemek sorrendjt teljesen megfordt-
suk, azaz az utols elem vljon elsv, az els pedig utolsv. A reverse() fggvny ezt
a mveletet az elemek msolsa nlkl valstja meg. Vegyk a kvetkez listt:
fruit:
bann cseresznye eper krte
A standard knyvtr 628
Ekkor a fruit.reverse() hvs eredmnye a kvetkez lesz:
fruit:
krte eper cseresznye bann
A listbl eltvoltott elemek teljesen trldnek. Egy mutat trlse azonban nem jelenti azt,
hogy maga a mutatott objektum is trldik. Ha olyan mutatkbl ll trolt akarunk hasz-
nlni, amely automatikusan trli a belle kivett mutatk ltal mutatott objektumokat, akkor
azt neknk kell megrnunk (17.8[13]).
17.2.3. A deque
A deque egy ktvg sort (double-ended queue) valst meg. Ez azt jelenti, hogy a deque
egy olyan sorozat, amit arra optimalizltak, hogy egyrszt mindkt vgn ugyanolyan hat-
kony mveleteket hasznlhassunk, mint egy list-nl, msrszt az indexels ugyanolyan ha-
tkony legyen, mint a vector esetben:
template <class T, class A = allocator<T> > class std::deque {
// a vector-hoz hasonl tpusok s mveletek (16.3.3, 16.3.5, 16.3.6), kivve:
// capacity() s reserve()
// a list-hez hasonl fejelem-mveletek (17.2.2.2)
};
Az adatszerkezet belsejben az elemek trlse s beszrsa ugyanolyan (rossz) hatkony-
sg, mint a vector-nl. Ennek kvetkeztben a deque trolt akkor hasznljuk, ha besz-
rsok s trlsek ltalban csak a vgeken fordulnak el, pldul egy vastszakasz model-
lezshez vagy egy krtyapakli brzolshoz:
deque<car> siding_no_3;
deque<Card> bonus;
17.3. Sorozat-talaktk
A vector, a list s a deque sorozatok nem pthetk fel egymsbl komoly hatkonysgrom-
ls nlkl. Ugyanakkor a verem (stack) s a sor (queue) elegnsan s hatkonyan megva-
lsthat e hrom alap-sorozattpus segtsgvel. gy ezt a kt osztlyt nem teljesen nll
trolknt hatroztk meg, hanem az alaptrolk talaktiknt.
17. Szabvnyos trolk 629
Egy trol talaktja (adapter) egy leszktett (korltozott) felletet ad az adott trolhoz.
A legszembetnbb, hogy az talaktk nem biztostanak bejrkat, mert a cljuknak meg-
felel felhasznlskor a leszktett fellet elegend szolgltatst nyjt. Azokat a mdszere-
ket, melyekkel egy trolbl ltrehozhatunk egy talaktt, ltalnosan hasznlhatjuk olyan
esetekben, amikor egy osztly szolgltatsait a felhasznlk ignyeihez szeretnnk igazta-
ni, az eredeti osztly megvltoztatsa nlkl.
17.3.1. A stack
A stack (verem) trolt a <stack> fejllomny rja le. Annyira egyszer szerkezet, hogy le-
rsnak legegyszerbb mdja, ha bemutatunk egy lehetsges megvalstst:
template <class T, class C = deque<T> > class std::stack {
protected:
C c;
public:
typedef typename C::value_type value_type;
typedef typename C::size_type size_type;
typedef C container_type;
explicit stack(const C& a = C()) : c(a) { }
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
value_type& top() { return c.back(); }
const value_type& top() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
Vagyis a stack egyszeren egy fellet egy olyan trolhoz, melynek tpust sablonparam-
terknt adjuk meg. A stack mindssze annyit tesz, hogy elrejti troljnak nem verem stlu-
s mveleteit, valamint a back(), push_back(), pop_back() fggvnyeket hagyomnyos
nevkn (top(), push(), pop()) teszi elrhetv.
Alaprtelmezs szerint a stack egy deque trolt hasznl elemeinek trolsra, de brmi-
lyen sorozatot hasznlhatunk, melyben elrhet a back(), a push_back() s a pop_back()
fggvny:
stack<char> s1; // char tpus elemek trolsa deque<char> segtsgvel
stack< int,vector<int> > s2; // int tpus elemek trolsa vector<int> segtsgvel
A standard knyvtr 630
Egy verem kezdeti feltltshez felhasznlhatunk egy mr ltez trolt is:
void print_backwards(vector<int>& v)
{
stack< int,vector<int> > state(v); // kezdllapot belltsa v segtsgvel
while (state.size()) {
cout << state.top();
state.pop();
}
}
De gondoljunk r, hogy ez a mvelet a paramterknt megadott trol elemeinek msol-
sval jr, gy rendkvl hosszadalmas lehet.
A veremhez az elemek trolshoz hasznlt trol push_back() mveletvel adunk eleme-
ket. gy a stack nem telhet meg mindaddig, amg a trol kpes memrit lefoglalni (me-
mriafoglaljnak segtsgvel, 19.4).
Ugyanakkor a verem alulcsordulhat:
void f()
{
stack<int> s;
s.push(2);
if (s.empty()) { // az alulcsorduls megakadlyozhat
// nincs kiemels
}
else { // de nem elkpzelhetetlen
s.pop(); // j: s.size() rtke 0 lesz
s.pop(); // nem meghatrozott hats, valsznleg rossz
}
}
Jegyezzk meg, hogy a fels elem hasznlathoz nincs szksgnk a pop() fggvnyre.
Erre a top() utasts szolgl, a pop() mveletre pedig akkor van szksg, ha el akarjuk tvo-
ltani a legfels elemet. Ez a megolds nem tlsgosan knyelmetlen, s sokkal hatko-
nyabb, amikor a pop() mveletre nincs szksg:
void f(stack<char>& s)
{
if (s.top()=='c') s.pop(); // nem ktelez kezd 'c' eltvoltsa
// ...
}
17. Szabvnyos trolk 631
Az nllan, teljesen kifejlesztett trolkkal ellenttben a veremnek (s a tbbi trol-tala-
ktnak) nem lehet memriafoglalt megadni sablonparamterknt, azt a stack megvals-
tshoz hasznlt trol sajt memriafoglalja helyettesti.
17.3.2. A queue
A <queue> fejllomnyban lert queue (sor) olyan fellet egy trolhoz, amely lehetv te-
szi az elemek beszrst az adatszerkezet vgre, illetve kivtelt a trol elejrl:
template <class T, class C = deque<T> > class std::queue {
protected:
C c;
public:
typedef typename C::value_type value_type;
typedef typename C::size_type size_type;
typedef C container_type;
explicit queue(const C& a = C()) : c(a) { }
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
value_type& front() { return c.front(); }
const value_type& front() const { return c.front(); }
value_type& back() { return c.back(); }
const value_type& back() const { return c.back(); }
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
Alaprtelmezs szerint a queue a deque trolt hasznlja elemeinek trolshoz, de brmely
sorozat betltheti ezt a szerepet, amely rendelkezik a front(), back(), push_back() s
pop_front() fggvnyekkel. Mivel a vector nem teszi lehetv a pop_front() hasznlatt,
a vektor nem lehet a sor bels trolja.
A standard knyvtr 632
A sor szerkezet szinte minden rendszerben elfordul valamilyen formban. Egy egyszer
zenetalap kiszolglt pldul a kvetkezkppen hatrozhatunk meg:
struct Message {
// ...
};
void server(queue<Message>& q)
{
while(!q.empty()) {
Message& m = q.front(); // zenet elfogsa
m.service(); // a krst kiszolgl fggvny meghvsa
q.pop(); // zenet trlse
}
}
Az zeneteket a push() utasts segtsgvel helyezhetjk a sorba.
Ha az gyfl (kliens) s a kiszolgl (szerver) kln-kln folyamatknt vagy szlknt fut,
akkor a sor-hozzfrseket valamilyen mdon ssze kell hangolnunk (szinkronizls):
void server2(queue<Message>& q, Lock& lck)
{
while(!q.empty()) {
Message m;
{ LockPtr h(lck); // zrols az zenet kinyerse kzben (14.4.1)
if (q.empty()) return; // valaki ms mr megszerezte az zenetet
m = q.front();
q.pop();
}
m.service(); // a krst kiszolgl fggvny meghvsa
}
}
Az egyidej hozzfrsnek (konkurrencia, prhuzamossg), illetve a zrolsnak (lock) mg
nincs szabvnyos defincija sem a C++ nyelvben, sem a szmtstechnika vilgban. Ha
ezeket a lehetsgeket szeretnnk hasznlni, akkor jrjunk utna, hogy sajt rendszernk
milyen lehetsgeket biztost, s azokat hogyan rhetjk el a C++ nyelvbl (17.8[8]).
17.3.3. A priority_queue
A priority_queue (prioritsos sor) egy olyan sor, melyben az elemek fontossgi rtket kap-
nak, s ez az rtk szablyozza, hogy az elemek milyen sorrendben vehetk ki:
17. Szabvnyos trolk 633
template <class T, class C = vector<T>, class Cmp = less<typename C::value_type> >
class std::priority_queue {
protected:
C c;
Cmp cmp;
public:
typedef typename C::value_type value_type;
typedef typename C::size_type size_type;
typedef C container_type;
explicit priority_queue(const Cmp& a1 = Cmp(), const C& a2 = C())
: c(a2), cmp(a1) { make_heap(c.begin(),c.end(),cmp); } // lsd 18.8
template <class In>
priority_queue(In first, In last, const Cmp& = Cmp(), const C& = C());
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
const value_type& top() const { return c.front(); }
void push(const value_type&);
void pop();
};
A priority_queue-t a <queue> fejllomny deklarlja.
Alaprtelmezs szerint a priority_queue az elemeket egyszeren a < opertor segtsgvel
hasonltja ssze, s a top() mindig a legnagyobb elemet adja vissza:
struct Message {
int priority;
bool operator<(const Message& x) const { return priority < x.priority; }
// ...
};
void server(priority_queue<Message>& q, Lock& lck)
{
while(!q.empty()) {
Message m;
{ LockPtr h(lck); // zrols az zenet kinyerse kzben (14.4.1)
if (q.empty()) return; // valaki ms mr megszerezte az zenetet
m = q.top();
q.pop();
}
m.service(); // a krst kiszolgl fggvny meghvsa
}
}
A standard knyvtr 634
Ez a programrszlet abban klnbzik a sornl (queue, 17.3.2) bemutatott pldtl, hogy
itt az zenetek kzl a legfontosabb (legnagyobb priorits) kerl elszr feldolgozsra.
Az nem meghatrozott, hogy az azonos fontossgi rtkkel rendelkez elemek kzl me-
lyik jelenik meg elszr a sor elejn. Kt elem fontossga (prioritsa) akkor egyezik meg,
ha egyik sem fontosabb a msiknl (17.4.1.5).
Ha a < opertor nem felel meg cljainknak, akkor egy sablonparamter segtsgvel meg-
adhatjuk az sszehasonlt mveletet. Pldul karakterlncokat rendezhetnk a kis- s
nagybetk megklnbztetse nlkl, ha az albbi sorban helyezzk el azokat:
priority_queue<string, vector<string>, Nocase> pq; // Nocase hasznlata az
// sszehasonltsokhoz (17.1.4.1)
A karakterlncokat a pq.push() utastssal tesszk be a sorba s a pq.top(), pq.pop() mve-
letekkel dolgozhatjuk fel.
Az olyan sablonokbl ltrehozott objektumok, melyeknl klnbz sablonparamtereket
hasznltunk, klnbz tpusak (13.6.3.1):
priority_queue<string>& pq1 = pq; // hiba: nem megfelel tpus
sszehasonltsi szempontot megadhatunk gy is, hogy kzben nem vltoztatjuk meg
a prioritsos sor tpust. Ehhez egy megfelel tpus sszehasonlt objektumot kell ltre-
hoznunk, melynek konstruktor-paramterben megadjuk az rvnyes sszehasonltsi
szempontot:
struct String_cmp { // futsi idej sszehasonltsi felttelt kifejez tpus
String_cmp(int n = 0); // az n sszehasonltsi felttel hasznlata
// ...
};
typedef priority_queue<string, vector<string>, String_cmp> Pqueue;
void g(Pqueue& pq) // a pq a String_cmp()-et hasznlja az sszehasonltsokhoz
{
Pqueue pq2(String_cmp(nocase));
pq = pq2; // rendben: pq s pq2 azonos tpus, gy pq most
// a String_cmp(nocase)-t shasznlja
}
Az elemek rendezse nmi teljestmnyromlssal jr, de ez egyltaln nem jelents.
A priority_queue egyik hatkony megvalstsi mdja a faszerkezet, mellyel az elemek egy-
17. Szabvnyos trolk 635
mshoz viszonytott helyt knnyedn bellthatjuk. Ezzel a megoldssal a push() s
a pop() mvelet kltsge is O(log(n)).
Alaprtelmezs szerint a priority_queue egy vector trolt hasznl elemeinek nyilvntarts-
ra, de brmely sorozattpus megfelel e clra, amely rendelkezik a front(), push_back() s
pop_back() fggvnyekkel, valamint lehetv teszi kzvetlen elrs bejrk hasznlatt.
A prioritsos sorok leggyakrabban hasznlt megvalstsi eszkze a heap (kupac vagy ha-
lom, 18.8)
17.4. Asszociatv trolk
Az asszociatv tmb az egyik leghasznosabb felhasznli tpus. Ennek kvetkeztben az el-
ssorban szveg- vagy szimblum-feldolgozsra kifejlesztett nyelvekben gyakran beptett
tpusknt szerepel. Az asszociatv tmb, melynek gyakori elnevezse a map (hozzrende-
ls, lekpezs) s a dictionary (sztr) is, rtkprokat trol. Az egyik rtk a kulcs (key),
melyet a msik rtk (a hozzrendelt rtk, mapped value) elrshez hasznlunk.
Az asszociatv tmbt gy kpzelhetjk el, mint egy tmbt, melynek indexe nem felttle-
nl egy egsz szm:
template<class K, class V> class Assoc {
public:
V& operator[ ](const K&); // hivatkozs visszaadsa a K-nak megfelel V-re
// ...
};
Itt a K tpus kulcs segtsgvel a V tpus hozzrendelt rtket vlasztjuk ki.
Az asszociatv trol az asszociatv tmb fogalmnak ltalnostsa. A map sablon a hagyo-
mnyos asszociatv tmbnek felel meg, ahol minden kulcsrtkhez egyetlen rtket rende-
lnk. A multimap egy olyan asszociatv tmb, amely megengedi, hogy ugyanolyan kulcs-
rtkkel tbb elem is szerepeljen, mg a set s a multiset olyan egyedi asszociatv tmbk,
melyekben nincs hozzrendelt rtk.
A standard knyvtr 636
17.4.1. A map
A map (kulcs,rtk) prok sorozata, melyben a bejegyzseket a kulcs (key) alapjn gyorsan
elrhetjk. A map trolban a kulcsok egyediek, azaz minden kulcshoz legfeljebb egy r-
tket rendelnk hozz. A map szerkezetben ktirny bejrkat hasznlhatunk (19.2.1).
A map megkveteli, hogy a kulcsknt hasznlt tpusokban a kisebb mint mvelet hasz-
nlhat legyen (17.1.4.1), s ez alapjn az elemeket rendezve trolja. Ennek kvetkezt-
ben a map bejrsakor az elemeket rendezve kapjuk meg. Ha olyan elemeink vannak, me-
lyek kztt nem lehet egyrtelm sorrendet megllaptani, vagy nincs szksg arra, hogy
az elemeket rendezve troljuk, akkor hasznljuk inkbb a hash_map szerkezetet (17.6).
17.4.1.1. Tpusok
A map a trolk szoksos tpus tagjain (16.3.1) kvl nhny, az osztly egyedi cljnak
megfelel tpust is meghatroz:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class std::map {
public:
// tpusok
typedef Key key_type;
typedef T mapped_type;
typedef pair<const Key, T> value_type;
typedef Cmp key_compare;
typedef A allocator_type;
typedef typename A::reference reference;
typedef typename A::const_reference const_reference;
typedef megvalsts_fgg1 iterator;
typedef megvalsts_fgg2 const_iterator;
typedef typename A::size_type size_type;
typedef typename A::difference_type difference_type;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
// ...
};
17. Szabvnyos trolk 637
Jegyezzk meg, hogy a value_type a map esetben a (kulcs, rtk) prt (pair) jelenti. A hoz-
zrendelt rtkek tpust a mapped_type adja meg. Teht a map nem ms, mint pair<const
Key, mapped_type> tpus elemek sorozata. Szoks szerint a konkrt bejr-tpusok az adott
megvalststl fggek. Mivel a map trolt leggyakrabban valamilyen faszerkezet segts-
gvel valstjk meg, a bejrk ltalban biztostanak valamilyen fabejrst.
A visszafel halad bejrkat a szabvnyos reverse_iterator sablonok (19.2.5) segtsgvel
hatrozhatjuk meg.
17.4.1.2. Bejrk s prok
A map a szoksos fggvnyeket biztostja a bejrk elrshez (16.3.2):
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > > class map {
public:
// ...
// bejrk
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
// ...
};
A map bejrsakor pair<const Key, mapped_type> tpus elemek sorozatn haladunk v-
gig. Pldul egy telefonknyv bejegyzseit az albbi eljrssal rathatjuk ki:
void f(map<string,number>& phone_book)
{
typedef map<string,number>::const_iterator CI;
for (CI p = phone_book.begin(); p!=phone_book.end(); ++p)
cout << p->first << '\t' << p->second << '\n';
}
A standard knyvtr 638
A map bejri az elemeket kulcs szerint nvekv sorrendben adjk vissza (17.1.4.5), gy
a phone_book bejegyzsei bcsorrendben jelennek meg. Egy pair els elemre a first, m-
sodik elemre a second nvvel hivatkozhatunk, fggetlenl attl, hogy ppen milyen tpu-
sak ezek az elemek:
template <class T1, class T2> struct std::pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() :first(T1()), second(T2()) { }
pair(const T1& x, const T2& y) :first(x), second(y) { }
template<class U, class V>
pair(const pair<U, V>& p) :first(p.first), second(p.second) { }
};
Az utols konstruktorra azrt van szksg, hogy a prokon tpuskonverzit is vgezhessnk
(13.6.2):
pair<int,double> f(char c, int i)
{
pair<char,int> x(c,i);
// ...
return x; // pair<char,int>-rl pair<int,double>-ra val konverzi szksges
.. return pair<int,double>(c,i); // konverzi szksges
}
A map esetben a pr els tagja a kulcs, a msodik tagja a hozzrendelt rtk.
A pair adatszerkezet nem csak a map megvalstsban hasznlhat, gy ez is egy nll
osztly a standard knyvtrban. A pair lerst a <utility> fejllomnyban tallhatjuk meg.
A pair tpus vltozk ltrehozst knnyti meg a kvetkez fggvny:
template <class T1, class T2> pair<T1,T2> std::make_pair(T1 t1, T2 t2)
{
return pair<T1,T2>(t1,t2);
}
A pair alaprtelmezett kezdrtkeit kt elemtpusnak alaprtelmezett rtke adja. Fontos,
hogy ha az elemek tpusa beptett, akkor a kezdrtk 0 (5.1.1), karakterlncok esetben
pedig egy res karakterlnc (20.3.4). Olyan tpus, melynek nincs alaprtelmezett
konstruktora, csak akkor lehet egy pair eleme, ha a pair kezdrtkt kifejezetten megadjuk.
17. Szabvnyos trolk 639
17.4.1.3. Indexels
A legjellemzbb map-mvelet a hozzrendelses (asszocicis) keress, amit az indexel
opertor valst meg:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class map {
public:
// ...
mapped_type& operator[ ](const key_type& k); // a k kulcs elem elrse
// ...
};
Az indexel opertor a megadott kulcsot indexnek tekintve egy keresst hajt vgre s
visszaadja a kulcshoz rendelt rtket. Ha a kulcs nem tallhat meg a trolban, akkor ez-
zel a kulccsal s a mapped_type alaprtelmezett rtkvel egy j elem kerl az asszociatv
tmbbe:
void f()
{
map<string,int> m; // a map kezdetben res
int x = m["Henry"]; // j bejegyzs ksztse ("Henry"); a kezdrtk 0, a
// visszaadott rtk 0
m["Harry"] = 7; // j bejegyzs ksztse ("Harry"), a kezdrtk 0, kapott rtk 7
int y = m["Henry"]; // a "Henry" bejegyzs rtknek visszaadsa
m["Harry"] = 9; // "Harry" rtknek mdostsa 9-re
}
Egy kicsit valsgszerbb plda: kpzeljnk el egy programot, amely kiszmtja a bemene-
tn megadott trgyak darabszmt. A listban (nv, mennyisg) bejegyzsek szerepelnek.
Az sszestst el szeretnnk vgezni trgyanknt s az egsz listra is. A lista lehet pldul
a kvetkez:
szg 100 kalapcs 2 frsz 3 frsz 4 kalapcs 7 szg 1000 szg 250
A feladat nagy rszt elvgezhetjk, mikzben a (nv, mennyisg) prokat beolvassuk egy
map trolba:
void readitems(map<string,int>& m)
{
string word;
int val = 0;
while (cin >> word >> val) m[word] += val;
}
A standard knyvtr 640
Az m[word] indexels azonostja a megfelel (string, int) prt, s visszaadja az int rtket.
A fenti programrszletben kihasznljuk azt is, hogy az j elemek int rtke alaprtelmezs
szerint 0.
A readitems() fggvnnyel felptett map egy szoksos ciklus segtsgvel jelenthet meg:
int main()
{
map<string,int> tbl;
readitems(tbl);
int total = 0;
typedef map<string,int>::const_iterator CI;
for (CI p = tbl.begin(); p!=tbl.end(); ++p) {
total += p->second;
cout << p->first << '\t' << p->second << '\n';
}
cout << "----------------\nsszesen\t" << total << '\n';
return !cin;
}
A fenti bemenettel a fggvny eredmnye a kvetkez:
frsz 7
kalapcs 9
szg 1350
------------------------
sszesen 1366
Figyeljk meg, hogy a nevek bcsorrendben jelennek meg (17.4.1, 17.4.1.5).
Az indexel opertornak meg kell tallnia a megadott kulcsrtket a trolban. Ez termsze-
tesen nem olyan egyszer mvelet, mint a tmbk esetben az egsz rtkkel val indexe-
ls. A pontos kltsg: O(log(a_map_mrete)). Ez sok alkalmazs esetben mg elfogadhat,
ha azonban a mi cljainknak tl drga, rdemesebb hast trolt (17.6) hasznlnunk. Ha
a map trolban nem tallhat meg egy kulcs, akkor az erre vonatkoz indexelssel ltre-
hozunk egy alaprtelmezett elemet. Ennek kvetkeztben a const map osztlyok esetben
nem hasznlhatjuk az operator[ ]() mveletet. Radsul az indexels csak akkor hasznlha-
t, ha a mapped_type tpusnak van alaprtelmezett rtke. Ha csak arra vagyunk kvncsi-
ak, hogy egy adott kulcs megtallhat-e a trolban, akkor hasznljuk a find() mveletet
(17.4.1.6), ami a map megvltoztatsa nlkl keresi meg a kulcsot.
17. Szabvnyos trolk 641
17.4.1.4. Konstruktorok
A map a szoksos konstruktorokat s egyb fggvnyeket biztostja (16.3.4):
template <class Key, class T, class Cmp =less<Key>,
class A =allocator<pair<const Key,T> > >
class map {
public:
// ...
// ltrehozs/msols/megsemmists:
explicit map(const Cmp& = Cmp(), const A& = A());
template <class In> map(In first, In last, const Cmp& = Cmp(), const A& = A());
map(const map&);
~map();
map& operator=(const map&);
// ...
};
A trol lemsolsa azt jelenti, hogy helyet foglalunk az elemeknek, majd mindegyikrl m-
solatot ksztnk (16.3.4). Ez nagyon kltsges mvelet is lehet, ezrt csak akkor hasznl-
juk, ha elkerlhetetlen. Ebbl kvetkezik, hogy az olyan trolkat, mint a map, csak
referencia szerint rdemes tadni.
A sablon konstruktor pair<const Key, T> elemek sorozatt kapja paramterknt, amelyet
egy bemeneti bejr-prral adunk meg. A fggvny a sorozat elemeit az insert() mvelet
segtsgvel szrja be az asszociatv tmbbe.
17.4.1.5. sszehasonltsok
Ahhoz, hogy egy adott kulcs elemet megtalljunk egy map-ben, a map mveleteinek
ssze kell tudnia hasonltani a kulcsokat. A bejrk is a kulcsok nvekv rtkei szerint ha-
ladnak vgig a troln, gy a beszrsok is kulcs-sszehasonltsokat vgeznek (ahhoz,
hogy az elemeket elhelyezzk a map trolt brzol faszerkezetben).
Alaprtelmezs szerint a kulcsok sszehasonltshoz hasznlt mvelet a < (kisebb mint)
opertor, de egy sablon- vagy konstruktor-paramterben ms fggvnyt is megadhatunk
(17.3.3). Az itt megadott rendezsi szempont a kulcsokat hasonltja ssze, mg a map ese-
A standard knyvtr 642
tben a value_type (kulcs, rtk) prokat jelent. Ezrt van szksg a value_comp() fgg-
vnyre, amely a kulcsokat sszehasonlt eljrs alapjn a prokat hasonltja ssze:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class map {
public:
// ...
typedef Cmp key_compare;
class value_compare : public binary_function<value_type,value_type,bool> {
friend class map;
protected:
Cmp cmp;
value_compare(Cmp c) : cmp(c) {}
public:
bool operator()(const value_type& x, const value_type& y) const
{ return cmp(x.first, y.first); }
};
key_compare key_comp() const;
value_compare value_comp() const;
// ...
};
Pldul:
map<string,int> m1;
map<string,int,Nocase> m2; // sszehasonlts tpusnak megadsa (17.1.4.1)
map<string,int,String_cmp> m3; // sszehasonlts tpusnak megadsa (17.1.4.1)
map<string,int,String_cmp> m4(String_cmp(literary)); // sszehasonltand objektum
// tadsa
A key_comp() s a value_comp() tagfggvny lehetv teszi, hogy az asszociatv tmbben
az egsz elemekre a csak kulcsokra, illetve a csak rtkekre vonatkoz sszehasonlt m-
veleteket hasznljuk. Erre legtbbszr akkor van szksg, ha ugyanazt az sszehasonltst
szeretnnk hasznlni egy msik trolban vagy algoritmusban is:
void f(map<string,int>& m)
{
map<string,int> mm; // sszehasonlts alaprtelmezs szerint < opertorral
map<string,int> mmm(m.key_comp()); // m szerinti sszehasonlts
// ...
}
17. Szabvnyos trolk 643
A 17.1.4.1 pontban pldt lthatunk arra, hogyan adhatunk meg egyedi sszehasonltso-
kat, a 18.4 pontban pedig a fggvnyobjektumok ltalnos bemutatsval foglalkozunk.
17.4.1.6. Mveletek asszociatv tmbkkel
A map s termszetesen az sszes asszociatv trol legfontosabb tulajdonsga, hogy egy
kulcs alapjn frhetnk hozz az informcikhoz. Ezen cl megvalstshoz szmos egye-
di fggvny ll rendelkezsnkre:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class map {
public:
// ...
// map-mveletek
iterator find(const key_type& k); // a k kulcs elem megkeresse
const_iterator find(const key_type& k) const;
size_type count(const key_type& k) const; // a k kulcs elemek szmnak
// meghatrozsa
iterator lower_bound(const key_type& k); // az els k kulcs elem megkeresse
const_iterator lower_bound(const key_type& k) const;
iterator upper_bound(const key_type& k); // az els k-nl nagyobb kulcs elem
// megkeresse
const_iterator upper_bound(const key_type& k) const;
pair<iterator,iterator> equal_range(const key_type& k);
pair<const_iterator,const_iterator> equal_range(const key_type& k) const;
// ...
};
Az m.find(k) mvelet egyszeren visszaad egy k kulcs elemre hivatkoz bejrt. Ha ilyen
elem nincs, akkor a visszaadott bejr az m.end(). Egy egyedi kulcsokkal rendelkez tro-
l esetben (mint a map s a set) az eredmny az egyetlen k kulcs elemre mutat bejr
lesz. Ha a trol nem teszi ktelezv egyedi kulcsok hasznlatt (mint a multimap s
a multiset), akkor a visszaadott bejr az els megfelel kulcs elem lesz:
void f(map<string,int>& m)
{
map<string,int>::iterator p = m.find("Arany");
A standard knyvtr 644
if (p!=m.end()) { // ha "Arany"-at talltunk
// ...
}
else if (m.find("Ezst")!=m.end()) { // "Ezst" keresse
// ...
}
// ...
}
Egy multimap (17.4.2) esetben az els k kulcs elem megkeresse helyett ltalban az
sszes ilyen elemre szksgnk van. Az m.lower_bound(k) s az m.upper_bound(k) fgg-
vnyekkel az m k kulcs elemeibl ll rszsorozat elejt, illetve vgt krdezhetjk le.
Szoks szerint, a sorozat vgt jelz bejr az utols utni elemre mutat:
void f(multimap<string,int>& m)
{
multimap<string,int>::iterator lb = m.lower_bound("Arany");
multimap<string,int>::iterator ub = m.upper_bound("Arany");
for (multimap<string,int>::iterator p = lb; p!=ub; ++p) {
// ...
}
}
Az als s a fels hatr meghatrozsa kt kln mvelettel nem tl elegns s nem is ha-
tkony. Ezrt ll rendelkezsnkre az equal_range() fggvny, amely mindkt rtket
visszaadja:
void f(multimap<string,int>& m)
{
typedef multimap<string,int>::iterator MI;
pair<MI,MI> g = m.equal_range("Arany");
for (MI p = g.first; p!=g.second; ++p) {
// ...
}
}
Ha a lower_bound(k) nem tallja meg a k kulcsot, akkor az els olyan elemre hivatkoz
bejrt adja vissza, melynek kulcsa nagyobb, mint k, illetve az end() bejrt, ha nem lte-
zik k-nl nagyobb kulcs. Ugyanezt a hibajelzsi mdot hasznlja az upper_bound() s az
equal_range() is.
17. Szabvnyos trolk 645
17.4.1.7. Listamveletek
Ha egy asszociatv tmbbe j rtket szeretnnk bevinni, akkor a hagyomnyos megolds
az, hogy egyszer indexelssel rtket adunk az adott kulcs elemnek:
phone_book["Rendelsi osztly"] = 8226339;
Ez a sor biztostja, hogy a phone_book trolban meglesz a Rendelsi osztly bejegyzs
a megfelel rtkkel, fggetlenl attl, hogy korbban ltezett-e mr ilyen kulcs. Elemeket
beilleszthetnk kzvetlenl az insert() fggvny segtsgvel is, s az erase() szolgl az ele-
mek trlsre:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class map {
public:
// ...
// listamveletek
pair<iterator, bool> insert(const value_type& val); // (kulcs,rtk) pr beszrsa
iterator insert(iterator pos, const value_type& val); // a pos csak javaslat
template <class In> void insert(In first, In last); // elemek beszrsa sorozatbl
void erase(iterator pos); // a mutatott elem trlse
size_type erase(const key_type& k); // a k kulcs elem trlse (ha van ilyen)
void erase(iterator first, iterator last); // tartomny trlse
void clear(); // minden elem trlse
// ...
};
Az m.insert(val) fggvny beszrja a val rtket, amely egy (Key, T) prt ad meg. Mivel
a map egyedi kulcsokkal foglalkozik, a beszrsra csak akkor kerl sor, ha az m trolban
mg nincs ilyen kulccsal rendelkez elem. Az m.insert(val) visszatrsi rtke egy
pair<iterator,bool>. A pr msodik, logikai tagja akkor igaz, ha a val rtk tnylegesen be-
kerlt a trolba. A bejr az mazon elemt jelli ki, melynek kulcsa megegyezik a val kul-
csval (val.first):
void f(map<string,int>& m)
{
pair<string,int> p99("Pali",99);
pair<map<string,int>::iterator,bool> p = m.insert(p99);
if (p.second) {
A standard knyvtr 646
// "Pali"-t beszrtuk
}
else {
// "Pali" mr szerepelt
}
map<string,int>::iterator i = p.first; // points to m["Pali"]
// ...
}
ltalban nem rdekel minket, hogy a kulcs korbban mr szerepelt-e a map-ben vagy j
elemknt kerlt be. Ha mgis erre vagyunk kvncsiak, annak oka ltalban az, hogy a k-
vnt kulcs egy msik map trolba is kerlhet az ltalunk vizsglt helyett, s ezt szre kell
vennnk. Az insert() msik kt vltozata nem jelzi, hogy az j rtk tnyleg bekerlt-e
a trolba.
Ha az insert(pos,val) forma szerint egy pozcit is megadunk, akkor csak egy ajnlst
adunk, hogy a rendszer a val kulcsnak keresst a pos pozcitl kezdje. Ha az ajnls he-
lyes, jelents teljestmnynvekedst rhetnk el. Ha nem tudunk j ajnlst adni, hasznl-
juk inkbb az elz vltozatot, mind az olvashatsg, mind a hatkonysg rdekben:
void f(map<string,int>& m)
{
m["Dilbert"] = 3; // elegns, de valsznleg kevsb hatkony
m.insert(m.begin(),make_pair(const string("Dogbert"),99)); // csnya
}
Valjban a [ ] egy kicsit tbb, mint egyszeren az insert() knyelmesebb alakja. Az m[k]
eredmnye a kvetkez kifejezssel egyenrtk:
(*(m.insert(make_pair(k,V())).first)).second
ahol a V() a hozzrendelt rtk alaprtelmezett rtkt jelli. Ha ezt az egyenrtksget
megrtettk, akkor valsznleg mr rtjk az asszociatv trolkat is. Mivel a [ ] mindenkp-
pen hasznlja a V() rtket, nem hasznlhatjuk az indexelst olyan map esetben, melynek
rtktpusa nem rendelkezik alaprtelmezett rtkkel. Ez egy sajnlatos hinyossga az
asszociatv trolknak, annak ellenre, hogy az alaprtelmezett rtk meglte nem alapvet
kvetelmnyk (ld. 17.6.2). Az elemek trlst is kulcs szerint vgezhetjk el:
void f(map<string,int>& m)
{
int count = m.erase("Ratbert");
// ...
}
17. Szabvnyos trolk 647
A visszaadott egsz rtk a trlt elemek szmt adja meg. Teht a count tartalma 0, ha
nincs Ratbert kulcs elem a trolban. A multimap s a multiset esetben a visszaadott
rtk egynl nagyobb is lehet. Egy elemet egy r mutat bejr segtsgvel trlhetnk,
elemek sorozatt pedig a megfelel tartomny kijellsvel:
void g(map<string,int>& m)
{
m.erase(m.find("Catbert"));
m.erase(m.find("Alice"),m.find("Wally"));
}
Termszetesen gyorsabban lehet trlni egy olyan elemet, melynek mr megtalltuk
a bejrjt, mint egy olyat, melyet elszr a kulcs alapjn meg kell keresnnk. Az erase()
utn a bejrt tovbb mr nem hasznlhatjuk, mivel az ltala mutatott elem megsemmislt.
Az m.erase(b,e) hvsa, ahol e rtke m.end() veszlytelen (b vagy m valamelyik elemre
hivatkozik, vagy m.end()). Ugyanakkor az m.erase(p) meghvsa, amennyiben p rtke
m.end(), slyos hiba s tnkreteheti a trolt.
17.4.1.8. Tovbbi mveletek
A map biztostja azokat a szoksos fggvnyeket, melyek az elemek szmval foglalkoz-
nak, illetve egy specializlt swap() fggvnyt:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class map {
public:
// ...
// kapacits:
size_type size() const; // elemek szma
size_type max_size() const; // a lehetsges legnagyobb map mrete
bool empty() const { return size()==0; }
void swap(map&);
};
Szoks szerint a size() s a max_size() ltal visszaadott rtk valamilyen elemszm.
Ezenkvl a map hasznlatakor rendelkezsnkre llnak az sszehasonlt opertorok (==,
!=, <, >, <=, >=), valamint a swap() eljrs is, ezek azonban nem tagfggvnyei a map
osztlynak:
A standard knyvtr 648
template <class Key, class T, class Cmp, class A>
bool operator==(const map<Key,T,Cmp,A>&, const map<Key,T,Cmp,A>&);
// ugyangy !=, <, >, <=, and >=
template <class Key, class T, class Cmp, class A>
void swap(map<Key,T,Cmp,A>&, map<Key,T,Cmp,A>&);
Mirt lehet szksg arra, hogy kt asszociatv tmbt sszehasonltsunk? Ha kt map objek-
tumot hasonltunk ssze, ltalban nem csak azt akarjuk megtudni, hogy egyenlek-e, ha-
nem azt is, hogy miben klnbznek, ha klnbznek. Ilyenkor teht az == s a != nem
hasznlhat. Az ==, a < s a swap() megvalstsval azonban lehetv vlik, hogy olyan
algoritmusokat ksztsnk, melyek minden trolra alkalmazhatk. Ezekre a fggvnyekre
pldul akkor lehet szksgnk, ha asszociatv tmbkbl ll vektort szeretnnk rendez-
ni vagy map trolk halmazra van szksgnk.
17.4.2. A multimap
A multimap nagyon hasonlt a map trolra, azzal a kivtellel, hogy ugyanaz a kulcs tbb-
szr is szerepelhet:
template <class Key, class T, class Cmp = less<Key>,
class A = allocator< pair<const Key,T> > >
class std::multimap {
public:
// mint a map, kivve a kvetkezt:
iterator insert(const value_type&); // bejrt ad vissza, nem prt
// nincs indexel opertor ([ ])
};
Pldul (felhasznlva a 17.1.4.1 pontban C stlus karakterlncok sszehasonltshoz be-
mutatott CString_less osztlyt):
void f(map<char*,int,Cstring_less>& m, multimap<char*,int,Cstring_less>& mm)
{
m.insert(make_pair("x",4));
m.insert(make_pair("x",5));// nincs hatsa: "x" mr szerepel (17.4.1.7)
// most m["x"] == 4
17. Szabvnyos trolk 649
mm.insert(make_pair("x",4));
mm.insert(make_pair("x",5));
// mm most ("x",4)-et s ("x",5)-t is tartalmazza
}
Teht a multimap trolban nem lehet olyan indexelst megvalstani, mint a map eset-
ben. Itt az equal_range(), a lower_bound() s az upper_bound() mvelet (17.4.1.6) jelen-
ti az azonos kulcs elemek elrsre szolgl legfbb eszkzt.
Termszetesen ha ugyanahhoz a kulcshoz tbb rtket is hozzrendelhetnk, akkor a map
helyett felttlenl a multimap szerkezetet hasznljuk. Ez pedig sokkal gyakrabban elfor-
dul, mint gondolnnk. Bizonyos tekintetben a multimap sokkal tisztbb s elegnsabb
megoldst ad, mint a map.
Mivel egy embernek tbb telefonszma is lehet, a telefonknyvet is rdemesebb multimap
formban elkszteni. Sajt telefonszmaimat pldul a kvetkez programrszlettel jelent-
hetem meg:
void print_numbers(const multimap<string,int>& phone_book)
{
typedef multimap<string,int>::const_iterator I;
pair<I,I> b = phone_book.equal_range("Stroustrup");
for (I i = b.first; i != b.second; ++i) cout << i->second << '\n';
}
A multimap esetben az insert() mindig beilleszti a paramtereknt megadott elemet, gy
nincs szksg a pair<iterator, bool> tpus visszatrsi rtkre; a multimap::insert() egyet-
len bejrt ad vissza. Az egysgessg rdekben a knyvtr tartalmazhatn az insert() lta-
lnos formjt, mind a map-hez, mind a multimap-hez, annak ellenre, hogy a bool rsz fe-
lesleges a multimap osztlyban. Egy msik tervezsi szemllet szerint elkszthettk volna
az egyszer insert() fggvnyt mindkt trolhoz, amely nem ad vissza logikai rtket. Ez
esetben viszont a map felhasznlinak valamilyen ms lehetsget kellett volna biztostani
annak megllaptshoz, hogy j kulcs kerlt-e a trolba. Ebben a krdsben a klnb-
z fellettervezsi elmletek tkztek, s nem szletett megegyezs.
17.4.3. A set
A set (halmaz) olyan egyedi map (17.4.1) trolnak tekinthet, ahol a hozzrendelt rt-
kek rdektelenek, gy csak a kulcsokat tartjuk meg. Ez a felletnek csupn apr mdost-
st jelenti:
A standard knyvtr 650
template <class Key, class Cmp = less<Key>, class A = allocator<Key> >
class std::set {
public:
// mint a map, kivve a kvetkezt
typedef Key value_type; // a kulcs maga az rtk
typedef Cmp value_compare;
// nincs indexel opertor ([ ])
};
A value_type tpust teht itt a key_type tpussal azonosknt hatrozzuk meg. Ez a trkk le-
hetv teszi, hogy nagyon sok esetben ugyanazzal a programmal kezeljnk egy map s egy
set trolt.
Figyeljk meg, hogy a set egy sorrendvizsgl opertort hasznl (alaprtelmezs szerint <),
s nem egyenlsget (==) ellenriz. Ennek kvetkeztben az elemek egyenrtksgt
a nem-egyenlsg hatrozza meg (17.1.4.1) s a halmaz bejrsakor az elemeket megha-
trozott sorrendben kapjuk meg.
Ugyangy, mint a map esetben, itt is rendelkezsnkre llnak a halmazokat sszehason-
lt opertorok (==, !=,< , >, <=, >=) s a swap() fggvny is.
17.4.4. A multiset
A multiset egy olyan halmaz, amely megengedi, hogy ugyanazok a kulcsok tbbszr is el-
forduljanak:
template <class Key, class Cmp = less<Key>, class A = allocator<Key> >
class std::multiset {
public:
// mint a set, kivve a kvetkezt:
iterator insert(const value_type&); // bejrt ad vissza, nem prt
};
A tbbszr elfordul kulcsok elrshez elssorban az equal_range(), a lower_bound()
s az upper_bound() fggvnyeket hasznlhatjuk.
17. Szabvnyos trolk 651
17.5. Majdnem-trolk
A beptett tmbk (5.2), a karakterlncok (20. fejezet), valamint a valarray (22.4) s
a bitset (17.5.3) osztlyok is elemeket trolnak, gy sok szempontbl trolnak tekinthet-
jk ket. Mindegyik hordoz azonban nhny olyan vonst, amely ellentmond a szabvnyos
trolk elveinek, ezrt ezek a majdnem-trolk nem mindig cserlhetk fel a teljesen ki-
fejlesztett trolkkal, pldul a vector vagy a list osztlyokkal.
17.5.1. Karakterlncok
A basic_string osztly lehetsget ad indexelsre, hasznlhatunk kzvetlen elrs bejr-
kat s a trolknl megszokott knyelmi lehetsgek tbbsge is rendelkezsnkre ll (20.
fejezet). A basic_string azonban nem teszi lehetv, hogy annyifle tpust hasznljunk
elemknt, mint a trolkban. Leginkbb karakterlncokhoz felel meg s ltalban a trolk-
tl jelentsen eltr formban hasznljuk.
17.5.2. A valarray
A valarray kifejezetten a szmokkal vgzett mveletekhez kszlt vektor, gy nem is akar
ltalnos trol lenni. Ehelyett szmos hasznos matematikai mveletet biztost, mg a szab-
vnyos trolk mveletei (17.1.1) kzl csak a size() s az indexels ll a rendelkezsnk-
re (22.4.2). A valarray elemeit kzvetlen elrs bejrval rhetjk el (19.2.1).
17.5.3. Bithalmazok
Egy rendszer szempontjbl gyakran van szksg arra, hogy pldul egy bemeneti adatfo-
lyamot (21.3.3) egy sor ktrtk (pldul j/rossz, igaz/hamis, kikapcsolt/bekapcsolt) l-
lapotjelzvel rjunk le. A C++ egsz rtkekre megvalstott bitszint mveletek segtsg-
vel tmogatja az ilyen llapotjelzk hatkony kezelst (amennyiben csak nhny van
bellk). Ilyen mvelet az & (s), a | (vagy), a ^ (kizr vagy), a << (lptets balra) s a >>
(lptets jobbra). A bitset<N> osztly ezt a fogalmat ltalnostja. Segtsgvel egyszeren
vgezhetnk mveleteket N darab biten, melyeket 0-tl N1-ig indexelnk. Az N rtkt
fordtsi idben rgztjk. Egy olyan bitsorozat esetben, amely nem fr el egy long int vl-
tozban, a bitset hasznlata sokkal knyelmesebb, mint az int rtkek kzvetlen kezelse.
Kevesebb jelzbit esetben hatkonysgi szempontok alapjn kell dntennk. Ha a biteket
sorszmozs helyett el szeretnnk nevezni, halmazt (set, 17.4.3), felsorolsi tpust (4.8),
vagy bitmezt (bitfield, C.8.1) hasznlhatunk.
A standard knyvtr 652
A bitset<N> N darab bit tmbjnek tekinthet. A bitset a vector<bool> osztlytl abban k-
lnbzik, hogy rgztett mret, a set troltl abban, hogy egszekkel s nem asszociatv
rtkkel indexelhet, illetve mindketttl eltr abban, hogy kzvetlen bitmveleteket knl.
Egy beptett mutat (5.1) segtsgvel nem tudunk biteket kijellni, ezrt a bitset osztly-
nak meg kell hatroznia egy hivatkozs bitre tpust. Ez a mdszer ltalnosan hasznlha-
t olyan objektumok esetben, melyekhez a beptett mutatk valamilyen okbl nem
hasznlhatk:
template<size_t N> class std::bitset {
public:
class reference { // hivatkozs egyetlen bitre
friend class bitset;
reference();
public: // b[i] az (i+1)-edik bitre hivatkozik
~reference();
reference& operator=(bool x); // b[i] = x;
reference& operator=(const reference&); // b[i] = b[j];
bool operator~() const; // return ~b[i]
operator bool() const; // x = b[i];
reference& flip(); // b[i].flip();
};
// ...
};
A bitset sablon az std nvtrhez tartozik s a <bitset> fejllomny segtsgvel rhetjk el.
A C++ trtnetre visszanyl okokbl a bitset nhny szempontbl eltr a standard knyv-
tr osztlyainak stlustl. Pldul, ha egy index (amit gyakran bitpozcinak neveznk) k-
vl esik a megengedett tartomnyon, akkor egy out_of_range kivtel keletkezik. Nem ll-
nak rendelkezsnkre bejrk. A bitpozcikat jobbrl balra szmozzuk, ugyangy, ahogy
a biteket egy gpi szban. gy a b[i] bit rtke pow(2,i). A bitset teht egy N bites binris
szmnak tekinthet:
17. Szabvnyos trolk 653
1 1 1 1 0 1 1 1 0 1
9 8 7 6 5 4 3 2 1 0
pozici:
bitset<10>(989)
17.5.3.1. Konstruktorok
Egy bitset objektumot ltrehozhatunk alaprtelmezett rtkkel, valamint egy unsigned long
int vagy egy string bitjeibl is:
template<size_t N> class bitset {
public:
// ...
// konstruktorok
bitset(); // N nulla bit
bitset(unsigned long val); // bitek a val-bl
template<class Ch, class Tr, class A> // Tr karakter-jellemz (20.2)
explicit bitset(const basic_string<Ch,Tr,A>& str, // bitek az str karakterlncbl
basic_string<Ch,Tr,A>::size_type pos = 0,
basic_string<Ch,Tr,A>::size_type n = basic_string<Ch,Tr,A>::npos);
// ...
};
A bitek alaprtelmezett rtke 0. Ha unsigned long int paramtert adunk meg, akkor
a szm minden egyes bitje a neki megfelel bit kezdrtkt hatrozza meg. A basic_string
(20. fejezet) ugyangy mkdik; a 0 karakter jelenti a 0 bitrtket, az 1 karakter az 1 bit-
rtket. Minden ms karakter invalid_argument kivtelt vlt ki. Alaprtelmezs szerint
a rendszer a bitek belltshoz teljes karakterlncot hasznl, de a basic_string
konstruktornak (20.3.4) megfelel stlusban megadhatjuk, hogy a karaktereknek csak egy
rsztartomnyt hasznljuk, a pos pozcitl kezdve a lnc vgig vagy a pos+n pozciig:
void f()
{
bitset<10> b1; // all 0
bitset<16> b2 = 0xaaaa; // 1010101010101010
bitset<32> b3 = 0xaaaa; // 00000000000000001010101010101010
bitset<10> b4("1010101010"); // 1010101010
bitset<10> b5("10110111011110",4); // 0111011110
bitset<10> b6("10110111011110",2,8); // 0011011101
bitset<10> b7("n0g00d"); // invalid_argument vltdott ki
bitset<10> b8 = "n0g00d"; // hiba: nincs char*-rl bitset-re val talakts
}
A bitset osztly alapvet clja az, hogy egyetlen gpi sz helyigny bitsorozathoz hat-
kony megoldst adhassunk. A felletet ennek a szemlletnek megfelelen alaktottk ki.
A standard knyvtr 654
17.5.3.2. Bitkezel mveletek
A bitset szmos olyan mveletet biztost, melyekkel egyes biteket rhetnk el vagy az
sszes bitet egyszerre kezelhetjk:
template<size_t N> class std::bitset {
public:
// ...
// bithalmaz-mveletek
reference operator[ ](size_t pos); // b[i]
bitset& operator&=(const bitset& s); // s
bitset& operator|=(const bitset& s); // vagy
bitset& operator^=(const bitset& s); // kizr vagy
bitset& operator<<=(size_t n); // logikai eltols balra (feltlts nullkkal)
bitset& operator>>=(size_t n); // logikai eltols jobbra (feltlts nullkkal)
bitset& set(); // minden bit 1-re lltsa
bitset& set(size_t pos, int val = 1); // b[pos]=val
bitset& reset(); // minden bit 0-ra lltsa
bitset& reset(size_t pos); // b[pos]=0
bitset& flip(); // minden bit rtknek mdostsa
bitset& flip(size_t pos); // b[pos] rtknek mdostsa
bitset operator~() const { return bitset<N>(*this).flip(); } // komplemens halmaz
// ltrehozsa
bitset operator<<(size_t n) const { return bitset<N>(*this)<<=n; } // eltolt halmaz
// ltrehozsa
bitset operator>>(size_t n) const { return bitset<N>(*this)>>=n; } // eltolt halmaz
// ltrehozsa
// ...
};
Az indexel opertor out_of_range kivtelt vlt ki, ha a megadott index kvl esik a meg-
engedett tartomnyon. Nem ellenrztt indexelsre nincs lehetsg.
A mveletek ltal visszaadott bitset& rtk a *this. Azok a mveletek, melyek a bitset& he-
lyett bitset rtket adnak vissza, egy msolatot ksztenek a *this objektumrl, ezen a m-
solaton vgzik el a kvnt mveletet, s ennek eredmnyt adjk vissza. Fontos, hogy a <<
s a >> mveletek itt tnyleg eltolst jelentenek s nem be- vagy kimeneti mveleteket.
17. Szabvnyos trolk 655
A bitset kimeneti opertora egy olyan << fggvny, melynek kt paramtere egy ostream
s egy bitset (17.5.3.3).
Amikor a biteket eltoljuk, logikai eltols trtnik (teht nem ciklikus). Ez azt jelenti, hogy
nhny bit kiesik, msok pedig az alaprtelmezett 0 rtket kapjk meg. Mivel a size_t t-
pus eljel nlkli, arra nincs lehetsgnk, hogy negatv szmmal toljuk el a bitsorozatot.
Ennek kvetkeztben a b<<-1 utasts egy igen nagy pozitv szmmal tolja el a biteket, gy
a b bitset minden bitjnek rtke 0 lesz. A fordtk ltalban figyelmeztetnek erre a hibra.
17.5.3.3. Tovbbi mveletek
A bitset is tmogatja az olyan szoksos mveleteket, mint a size(), az ==, a ki- s bemenet stb.:
template<size_t N> class bitset {
public:
// ...
unsigned long to_ulong() const;
template <class Ch, class Tr, class A> basic_string<Ch,Tr,A> to_string() const;
size_t count() const; // 1 rtk bitek szma
size_t size() const { return N; } // bitek szma
bool operator==(const bitset& s) const;
bool operator!=(const bitset& s) const;
bool test(size_t pos) const; // igaz, ha b[pos] rtke 1
bool any() const; // igaz, ha brmelyik bit rtke 1
bool none() const; // igaz, ha egyik bit rtke sem 1
};
A to_ulong() s a to_string() fggvny a megfelel konstruktor fordtott (inverz) mvelete.
A flrerthet talaktsok elkerlse rdekben a szoksos konverzis opertorok helyett
a fenti mveletek llnak rendelkezsnkre. Ha a bitset objektumnak olyan bitjei is rtket
tartalmaznak, melyek nem brzolhatk unsigned long int formban, a to_ulong() fgg-
vny overflow_error kivtelt vlt ki.
A to_string() mvelet a megfelel tpus karakterlncot lltja el, amely '0' s '1' karakte-
rekbl pl fel. A basic_string a karakterlncok brzolshoz hasznlt sablon (20. fejezet).
A standard knyvtr 656
A to_string() fggvnyt hasznlhatjuk egy int binris alakjnak kiratshoz is:
void binary(int i)
{
bitset<8*sizeof(int)> b = i; // 8 bites bjtot tteleznk fel (lsd mg 22.2)
cout << b.template to_string< char,char_traits<char>,allocator<char> >() << '\n';
}
Sajnos egy minstett sablon tag meghvsa igen bonyolult s ritkn hasznlt utastsformt
kvetel (C.13.6). A tagfggvnyeken kvl a bitset lehetv teszi a logikai & (s), a | (vagy),
a ^ (kizr vagy), valamint a szoksos ki- s bemeneti opertorok hasznlatt is:
template<size_t N> bitset<N> std::operator&(const bitset<N>&, const bitset<N>&);
template<size_t N> bitset<N> std::operator|(const bitset<N>&, const bitset<N>&);
template<size_t N> bitset<N> std::operator^(const bitset<N>&, const bitset<N>&);
template <class charT, class Tr, size_t N>
basic_istream<charT,Tr>& std::operator>>(basic_istream<charT,Tr>&, bitset<N>&);
template <class charT, class Tr, size_t N>
basic_ostream<charT,Tr>& std::operator<<(basic_ostream<charT,Tr>&, const bitset<N>&);
Teht egy bitset objektumot kirathatunk anlkl is, hogy elbb karakterlncc alaktannk:
void binary(int i)
{
bitset<8*sizeof(int)> b = i; // 8 bites bjtot tteleznk fel (lsd mg 22.2)
cout << b << '\n';
}
Ez a programrszlet nullk s egyesek formjban rja ki a biteket s a legnagyobb
helyirtk bit lesz a bal oldalon.
17.5.4. Beptett tmbk
A beptett tmbk tmogatjk az indexelst s a szoksos mutatk formjban
a kzvetlen elrs bejrk hasznlatt is (2.7.2). A tmb azonban nem tudja a sajt mre-
tt, gy a programoznak kell azt nyilvntartania. A tmbk ltalban nem biztostjk
a szabvnyos tagmveleteket s -tpusokat.
Nha nagyon hasznos az a lehetsg, hogy egy beptett tmbt elrejthetnk a szabvnyos
trolk knyelmes jellsrendszere mg, gy, hogy kzben megtartjuk alacsonyszint ter-
mszetnek elnyeit:
17. Szabvnyos trolk 657
template<class T, int max> struct c_array {
typedef T value_type;
typedef T* iterator;
typedef const T* const_iterator;
typedef T& reference;
typedef const T& const_reference;
T v[max];
operator T*() { return v; }
reference operator[ ](size_t i) { return v[i]; }
const_reference operator[ ](ptrdiff_t i) const { return v[i]; }
iterator begin() { return v; }
const_iterator begin() const { return v; }
iterator end() { return v+max; }
const_iterator end() const { return v+max; }
ptrdiff_t size() const { return max; }
};
A tmbkkel val kompatibilits miatt az eljeles ptrdiff_t tpust (16.2.2) hasznlom index-
knt az eljel nlkli size_t helyett.
A c_array sablon nem rsze a standard knyvtrnak. Mindssze egyszer pldaknt szere-
pel itt arra, hogy egy idegen trolt hogyan igazthatunk a szabvnyos trolk keretrend-
szerhez. Ez az osztly olyan szabvnyos algoritmusokhoz (18. fejezet) hasznlhat, me-
lyeknek a begin(), end() stb. mveletekre van szksgk. A verembe anlkl helyezhet,
hogy kzvetve dinamikus memrit hasznlnnk, s ezenkvl tadhat olyan C stlus
fggvnyeknek is, melyek mutatt vrnak paramterknt:
void f(int* p, int sz); // C stlus
void g()
{
c_array<int,10> a;
f(a,a.size()); // C stlus hasznlat
c_array<int,10>::iterator p = find(a.begin(),a.end(),777); // C++/STL stlus
// hasznlat
// ...
}
A standard knyvtr 658
17.6. j trolk ltrehozsa
A szabvnyos trolk olyan keretrendszert biztostanak, melyet a programoz szabadon
bvthet. Az albbiakban azt mutatom be, hogyan kszthetnk olyan trolkat, melyek fel-
cserlhetk a szabvnyos trolkkal, ha indokolt. A bemutatott plda a gyakorlatban is m-
kdik, de nem optimlis. A felletet gy vlasztottam meg, hogy az nagyon kzel lljon
a hash_map ltez, szles krben elrhet, magas szint megvalstsaihoz. Az itt bemuta-
tott hash_map arra hasznlhat, hogy megtanuljuk az ltalnos alapelveket. Komoly mun-
khoz hasznljunk inkbb a tmogatott hash_map tpust.
17.6.1. A hash_map
A map olyan asszociatv trol, melynek szinte brmilyen tpus eleme lehet, hiszen az
egyetlen kvetelmny egy kisebb mint mvelet az elemek sszehasonltshoz
(17.4.1.5). Ha azonban tbbet tudunk a kulcs tpusrl, ltalban lervidthetjk az elemek
megtallshoz szksges idt, azzal, hogy hast fggvnyt (hash function) ksztnk s
a trolt hast tblaknt (hash table) valstjuk meg.
A hast fggvny egy olyan eljrs, amely egy rtkhez gyorsan hozz tud rendelni egy in-
dexet, mgpedig gy, hogy kt klnbz rtk ritkn kapja ugyanazt. A hast tbla ez-
utn lnyegben gy mkdik, hogy az rtket a kiszmtott indexre helyezi, ha az mg
res, ellenkez esetben az index kzelbe. Ha egy elem a hozz rendelt indexen tallha-
t, akkor annak megtallsa nagyon gyors lehet, de az index kzelben elhelyezett elem
keresse sem tl lass, ha az elemek egyenlsgnek vizsglata megfelelen gyors. Ezrt
aztn nem ritka, hogy olyan nagyobb trolk esetben, ahol a keress a legjelentsebb m-
velet, a hash_map akr 510-szer gyorsabban vgzi el ezt a feladatot, mint a map. Ugyan-
akkor az is igaz, hogy a hash_map egy rosszul megvlasztott hast fggvnnyel mg
a map-nl is sokkal lassabb lehet.
Hast tblt sokflekppen kszthetnk. A hash_table felletnek csak ott kell klnbz-
nie a szabvnyos trolk fellettl, ahol a hasts hatkonysga ezt megkveteli. A leg-
alapvetbb klnbsg a map s a hash_map kztt, hogy a map a < mvelettel hasonltja
ssze az elemeit, mg a hash_map az == mveletet hasznlja s szksge van egy hast
fggvnyre is. Teht ha a hash_map objektumot nem alaprtelmezett belltsokkal akar-
juk ltrehozni, akkor a paramterek jelentsen eltrnek a map trolnl hasznltaktl:
map<string,int> m1; // karakterlncok sszehasonltsa < hasznlatval
map<string,int,Nocase> m2; // karakterlncok sszehasonltsa Nocase()
// hasznlatval (17.1.4.1)
17. Szabvnyos trolk 659
hash_map<string,int> hm1; // hasts Hash<string>() hasznlatval (17.6.2.3),
// sszehasonlts == hasznlatval
hash_map<string,int,hfct> hm2; // hasts hfct() hasznlatval, sszehasonlts ==
// hasznlatval
hash_map<string,int,hfct,eql> hm3; // hasts hfct() hasznlatval, sszehasonlts eql
// hasznlatval
A hastsos keresst hasznl trolt egy vagy tbb tblzat segtsgvel hozhatjuk ltre.
Az elemek trolsn kvl a trolnak nyilvn kell tartania azt is, milyen rtket milyen ha-
stott rtkhez (az elzekben index) rendelt hozz. Erre szolgl a hast tbla. A has-
t tblk teljestmnye ltalban jelentsen romlik, ha tlsgosan teltett vlnak, teht
mondjuk 75 szzalkig megteltek. Ezrt az albbiakban lert hash_map automatikusan n-
veli mrett, ha tl teltett vlik. Az tmretezs azonban rendkvl kltsges mvelet le-
het, gy mindenkppen lehetv kell tennnk egy kezdeti mret megadst.
Ezek alapjn a hash_map els vltozata a kvetkez lehet:
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
class hash_map {
// mint a map, kivve a kvetkezket:
typedef H Hasher;
typedef EQ key_equal;
hash_map(const T& dv =T(), size_type n =101, const H& hf =H(), const EQ& =EQ());
template<class In> hash_map(In first, In last,
const T& dv =T(), size_type n =101, const H& hf =H(), const EQ& =EQ());
};
Ez lnyegben megegyezik a map felletvel (17.4.1.4), csak a < mveletet az == oper-
torral helyettestettk s megadtunk egy hast fggvnyt is.
A knyvben eddig hasznlt map objektumok (3.7.4, 6.1, 17.4.1) knnyedn helyettesthetk
a hash_map szerkezettel. Nem kell mst tennnk, csak a map nevet t kell rnunk hash_map-
re. A map cserjt hash_map-re ltalban leegyszersthetjk egy typedef utastssal:
typedef hash_map<string,record> Map;
Map dictionary;
A typedef arra is hasznlhat, hogy a sztr (dictionary) tnyleges tpust elrejtsk a felhasz-
nlk ell.
A standard knyvtr 660
Br a meghatrozs nem egszen pontos, a map s a hash_map kztti ellenttet tekinthet-
jk egyszeren tr-id ellenttnek. Ha a hatkonysg nem fontos, nem rdemes idt veszte-
getni a kzttk val vlasztsra: mindkett jl hasznlhat. Nagy s nehzkesen hasznl-
hat tblk esetben a hash_map jelents elnye a sebessg, s ezt rdemes hasznlnunk,
hacsak nem fontos a kis trigny. Mg ha takarkoskodnunk is kell a memrival, akkor is
rdemes megvizsglnunk nhny egyb lehetsget a trigny cskkentsre, mieltt az
egyszer map szerkezetet vlasztjuk. Az aktulis mret kiszmtsa nagyon fontos ahhoz,
hogy ne egy alapjaiban rossz kdot prbljunk optimalizlni.
A hatkony hasts megvalstsnak legfontosabb rsze a megfelel hast fggvny meg-
vlasztsa. Ha nem tallunk j hast fggvnyt, akkor a map knnyen tlszrnyalhatja
a hash_map hatkonysgt. A C stlus karakterlncokra vagy egsz szmokra pl has-
ts ltalban elg hatkony tud lenni, de mindig rdemes arra gondolnunk, hogy egy has-
t fggvny hatkonysga nagymrtkben fgg az aktulis rtkektl, melyeket hastanunk
kell (17.8[35]). A hash_map trolt kell hasznlnunk akkor, ha a kulcshoz nem adhatunk
meg < mveletet vagy az nem felel meg elvrsainknak. Megfordtva: a hast fggvny
nem hatroz meg az elemek kztt olyan sorrendet, mint amilyet a < opertor, gy a map
osztlyra van szksgnk, ha az elemek sorrendje fontos. A map osztlyhoz hasonlan
a hash_map is biztost egy find() fggvnyt, amely megllaptja, hogy egy kulcs szerepel-e
mr a trolban.
17.6.2. brzols s ltrehozs
A hash_map sokflekppen megvalsthat. Itt egy olyan formt mutatok be, amely vi-
szonylag gyors, de legfontosabb mveletei elg egyszerek. Ezek a mveletek
a konstruktorok, a keress (a [ ] opertor), az tmretezs s az egy elem trlst vgz
fggvny (erase()).
A hast tbla itt bemutatott egyszer megvalstsa egy olyan vektort hasznl, amely a be-
jegyzsekre hivatkoz mutatkat trolja. Minden bejegyzsben (Entry) szerepel egy kulcs
(key), egy rtk (value) , egy mutat a kvetkez ugyanilyen hastkddal rendelkez
Entry bejegyzsre (ha van ilyen), illetve egy trltsget jelz (erased) bit:
17. Szabvnyos trolk 661
kulcs rtk trlt kvetkez
kulcs rtk trlt kvetkez
...
Ez deklarci formjban a kvetkezkppen nz ki.
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
class hash_map {
// ...
private: // brzols
struct Entry {
key_type key;
mapped_type val;
bool erased;
Entry* next; // hash-tlcsorduls esetre
Entry(key_type k, mapped_type v, Entry* n)
: key(k), val(v), next(n), erased(false) { }
};
vector<Entry> v; // az aktulis bejegyzsek
vector<Entry*> b; // a hast tbla: mutat v-be
// ...
};
Vizsgljuk meg az erased bit szerept. Az ugyanolyan hastkddal rendelkez elemek ke-
zelsnek mdja miatt nagyon nehz lenne egy bizonyos elemet trlni. Ezrt a tnyleges
trls helyett az erase() meghvsakor csak az erased bitet jelljk be, s az elemet mind-
addig figyelmen kvl hagyjuk, amg a tblt t nem mretezzk.
A f adatszerkezet mellett a hash_map osztlynak egyb adatokra is szksge van. Term-
szetesen minden konstruktornak az sszes adattagot be kell lltania:
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
class hash_map {
// ...
hash_map(const T& dv =T(), size_type n =101, const H& h =H(), const EQ& e =EQ())
: default_value(dv), b(n), no_of_erased(0), hash(h), eq(e)
{
set_load(); // alaprtelmezs
v.reserve(max_load*b.size()); // hely biztostsa a nvekedshez
}
void set_load(float m = 0.7, float g = 1.6) { max_load = m; grow = g; }
// ...
A standard knyvtr 662
private:
float max_load; // v.size()<=b.size()*max_load megtartsa
float grow; // ha szksges, resize(bucket_count()*grow)
size_type no_of_erased; // v azon bejegyzseinek szma, amelyeket trlt elem foglal el
Hasher hash; // hast fggvny
key_equal eq; // egyenlsg
const T default_value; // a [ ] ltal hasznlt alaprtelmezett rtk
};
A szabvnyos asszociatv trolk megkvetelik, hogy a hozzrendelt rtk tpusa alaprtel-
mezett rtkkel rendelkezzen (17.4.1.7). Ez a kvetelmny valjban nem elengedhetetlen
s bizonyos esetekben nagyon knyelmetlen is lehet. Ezrt az alaprtelmezett rtket para-
mterknt vesszk t, ami lehetv teszi a kvetkez sorok lerst:
hash_map<string,Number> phone_book1; // alaprtelmezs: Number()
hash_map<string,Number> phone_book2(Number(411)); // alaprtelmezs: Number(411)
17.6.2.1. Keressek
Vgl elrkeztnk a kritikus keressi fggvnyekhez:
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
class hash_map {
// ...
mapped_type& operator[ ](const key_type&);
iterator find(const key_type&);
const_iterator find(const key_type&) const;
// ...
};
Az rtkek megtallshoz az operator[ ]() a hast fggvnyt hasznlja, mellyel kiszmtja
a kulcshoz tartoz indexet a hast tblban. Ezutn vgignzi az adott index alatt tallha-
t bejegyzseket, amg meg nem tallja a kvnt kulcsot. Az gy megtallt Entry objektum-
ban tallhat az a value rtk, amelyet kerestnk. Ha nem talltuk meg a kulcsot, akkor
alaprtelmezett rtkkel felvesznk egy j elemet:
17. Szabvnyos trolk 663
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
hash_map<Key,T,H,EQ,A>::mapped_type& hash_map<Key,T,H,EQ,A>::operator[ ](const
key_type& k)
{
size_type i = hash(k)%b.size(); // hasts
for(Entry* p = b[i]; p; p = p->next) // keress a hasts eredmnyekppen i-be kerlt
// elemek kztt
if (eq(k,p->key)) { // megvan
if (p->erased) { // jbli beszrs
p->erased = false;
no_of_erased--;
return p->val = default_value;
}
return p->val;
}
// ha nincs meg
if (size_type(b.size()*max_load) <= v.size()) { //ha "teltett"
resize(b.size()*grow); // nvekeds
return operator[ ](k); // jrahasts
}
v.push_back(Entry(k,default_value,b[i])); // Entry hozzadsa
b[i] = &v.back(); // az j elemre mutat
return b[i]->val;
}
A map megoldstl eltren a hash_map nem a kisebb mint mveletbl szrmaztathat
egyenlsgvizsglatot (17.1.4.1) hasznlja, hiszen az azonos hastkddal rendelkez ele-
meket vgignz ciklusban az eq() fggvny meghvsa pontosan ezt a feladatot ltja el. Ez
a ciklus nagyon fontos a keress hatkonysga szempontjbl, a leggyakoribb kulcstpusok
(pldul a string, vagy a C stlus karakterlncok) szempontjbl pedig egy felesleges
sszehasonlts akr jelents teljestmnyromlst is eredmnyezhet.
Az azonos hastkddal rendelkez elemek trolshoz hasznlhattuk volna a set<Entry>
trolt is, de ha elg j hastfggvnynk (hash()) van s a hasttbla (b) mrete is meg-
felel, akkor ezen halmazok tbbsgben egyetlen elem lesz. Ezrt ezeket az elemeket egy-
szeren az Entry osztly next mezjvel kapcsoltam ssze (17.8[27]).
Figyeljk meg, hogy a b az elemekre hivatkoz mutatkat trolja s az elemek valjban
a v-be kerlnek. A push_back() ltalban megtehetn, hogy thelyezi az elemeket, s ezzel
A standard knyvtr 664
a mutatk rvnytelenn vlnnak (16.3.5), most azonban a konstruktorok (17.6.2) s
a resize() fggvny elre helyet foglalnak az elemek szmra a reserve() eljrssal, gy el-
kerljk a vratlan thelyezseket.
17.6.2.2. Trls s tmretezs
A hastfggvnyes keress nagyon rossz hatsfokv vlhat, ha a tbla tlsgosan teltett.
Az ilyen kellemetlensgek elkerlse rdekben az indexel opertor automatikusan tm-
retezheti a tblt. A set_load() (17.6.2) fggvny szablyozza, hogy ez az tmretezs mi-
kor s hogyan menjen vgbe, nhny tovbbi fggvnnyel pedig lehetv tesszk a prog-
ramoz szmra, hogy lekrdezze a hash_map llapott:
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
class hash_map {
// ...
void resize(size_type n); // a hast tbla mretnek n-re lltsa
void erase(iterator position); // a mutatott elem trlse
size_type size() const { return v.size()-no_of_erased; } // az elemek szma
size_type bucket_count() const { return b.size(); } // a hast tbla mrete
Hasher hash_fun() const { return hash; } // a hasznlt hast fggvny
key_equal key_eq() const { return eq; } // a hasznlt egyenlsgvizsglat
// ...
};
A resize() fggvny rendkvl fontos, viszonylag egyszer, de nha rendkvl drga
mvelet:
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
void hash_map<Key,T,H,EQ,A>::resize(size_type s)
{
size_type i = v.size();
while (no_of_erased) { // a "trlt" elemek tnyleges eltvoltsa
if (v[--i].erased) {
v.erase(&v[i]);
--no_of_erased;
}
}
17. Szabvnyos trolk 665
if (s <= b.size()) return;
b.resize(s); // s-b.size() szm mutat hozzadsa
fill(b.begin(),b.end(),0); // bejegyzsek trlse (18.6.6)
v.reserve(s*max_load); // ha v-nek jbli memriafoglalsra van szksge, most
// trtnjen meg
if (no_of_erased) { // a "trlt" elemek tnyleges eltvoltsa
for (size_type i = v.size()-1; 0<=i; i--)
if (v[i].erased) {
v.erase(&v[i]);
if (--no_of_erased == 0) break;
}
}
for (size_type i = 0; i<v.size(); i++) { // jrahasts
size_type ii = hash(v[i].key)%b.size(); // hasts
v[i].next = b[ii]; // lncols
b[ii] = &v[i];
}
}
Ha szksg van r, a programoz maga is meghvhatja a resize() eljrst, gy biztosthatja,
hogy az idvesztesg kiszmthat helyen kvetkezzen be. Tapasztalataim szerint bizonyos
alkalmazsokban a resize() mvelet rendkvl fontos, de a hasttblk szempontjbl nem
nlklzhetetlen. Egyes megvalstsi mdszerek egyltaln nem hasznljk.
Mivel a tnyleges munka nagy rsze mshol trtnik (s csak akkor, amikor tmretezzk
a hash_map trolt), az erase() fggvny nagyon egyszer:
template<class Key, class T, class H = Hash<Key>,
class EQ = equal_to<Key>, class A = allocator< pair<const Key,T> > >
void hash_map<Key,T,H,EQ,A>::erase(iterator p) // a mutatott elem trlse
{
if (p->erased == false) no_of_erased++;
p->erased = true;
}
17.6.2.3. Hasts
A hash_map::operator[ ]() teljess ttelhez mg meg kell hatroznunk a hash() s az eq()
fggvnyt. Bizonyos okokbl (amelyeket majd a 18.4 pontban rszleteznk) a hast fgg-
vnyt rdemesebb egy fggvnyobjektum operator()() mveleteknt elksztennk:
template <class T> struct Hash : unary_function<T, size_t> {
size_t operator()(const T& key) const;
};
A standard knyvtr 666
A j hast fggvny paramterben egy kulcsot kap s egy egsz rtket ad vissza, amely
klnbz kulcsok esetben nagy valsznsggel klnbz. A j hastfggvny kiv-
lasztsa nagyon nehz. Gyakran az vezet elfogadhat eredmnyhez, ha a kulcsot jell bi-
teken s egy elre meghatrozott egsz rtken kizr vagy mveletet hajtunk vgre:
template <class T> size_t Hash<T>::operator()(const T& key) const
{
size_t res = 0;
size_t len = sizeof(T);
const char* p = reinterpret_cast<const char*>(&key); //objektumok elrse bjtok
// sorozataknt
while (len--) res = (res<<1)^*p++; // a kulcsbrzols bjtjainak hasznlata
return res;
}
A reinterpret_cast (6.2.7) hasznlata jl jelzi, hogy valami csnya dolgot mveltnk, s
ha tbbet tudunk a hastott rtkrl, akkor ennl hatkonyabb megoldst is tallhatunk.
Pldul, ha az objektum mutatt tartalmaz, ha nagy objektumrl van sz, vagy ha az adat-
tagok igaztsa miatt az objektum brzolsban felhasznlatlan terletek (lyukak, holes)
vannak, mindenkppen jobb hastfggvnyt kszthetnk (17.8[29]).
A C stlus karakterlncok mutatk (a karakterekre) s a string is tartalmaz mutatt.
A specializcik ezekre az esetekre sorrendben a kvetkezk:
size_t Hash<char*>::operator()(const char* key) const
{
size_t res = 0;
while (*key) res = (res<<1)^*key++; // a karakterek egsz rtknek hasznlata
return res;
}
template <class C>
size_t Hash< basic_string<C> >::operator()(const basic_string<C>& key) const
{
size_t res = 0;
typedef basic_string<C>::const_iterator CI;
CI p = key.begin();
CI end = key.end();
while (p!=end) res = (res<<1)^*p++; // a karakterek egsz rtknek hasznlata
return res;
}
17. Szabvnyos trolk 667
A hash_map minden megvalstsban legalbb az egsz s a karakterlnc tpus kulcsok-
hoz szerepelnik kell hastfggvnynek. Komolyabb kulcsok esetben a programozt se-
gthetjk megfelel specializcikkal is. A j hastfggvny kivlasztsban sokat segthet
a megfelel mrszmokra tmaszkod ksrletezs. Megrzseinkre nagyon ritkn hagyat-
kozhatunk ezen a terleten.
A hash_map akkor vlik teljess, ha bejrkat is definilunk s nhny tovbbi egyszer
fggvnyt is ksztnk. Ez azonban maradjon meg feladatnak (17.8[34]).
17.6.3. Tovbbi hastott asszociatv trolk
Az egysgessg s a teljessg rdekben mindenkppen el kell ksztennk a hash_map
testvreit is: a hash_set, a hash_multimap s a hash_multiset trolt. Ezek ltrehozsa
a hash_map, a map, a multimap, a set s a multiset alapjn egyszer, gy ezeket is megha-
gyom feladatnak 17.8[34]. Ezeknek a hastott asszociatv trolknak komoly irodalma van
s ksz kereskedelmi vltozatok is elrhetk bellk. Tnyleges programokban ezek a vl-
tozatok jobban hasznlhatk, mint a hzilag sszeeszkbltak (kztk az ltalam bemuta-
tott is).
17.7. Tancsok
[1] Ha trolra van szksgnk, ltalban a vector osztlyt hasznljuk. 17.1.
[2] Ha gyakran hasznlunk egy mveletet, rdemes ismerni annak kltsgeit
(bonyolultsg, nagy O mrtk) 17.1.2.
[3] A trol fellete, megvalstsa s brzolsa kln-kln fogalom. Ne kever-
jk ssze ket. 17.1.3.
[4] Keresst vagy rendezst brmilyen szempont szerint megvalsthatunk.
17.1.4.1.
[5] Ne hasznljunk kulcsknt C stlus karakterlncokat, vagy biztostsunk megfele-
l sszehasonltsi mveletet. 17.1.4.1.
[6] Az sszehasonltsi szemponttal elemek egyenrtksgt hatrozhatjuk meg,
ami eltrhet attl, hogy a kulcsok teljesen egyenlk-e. 17.1.4.1.
[7] Lehetleg hasznljuk a sorozat vgt mdost fggvnyeket (back-mveletek),
ha elemek beszrsra vagy trlsre van szksgnk. 17.1.4.1.
A standard knyvtr 668
[8] Ha sok beszrsra, illetve trlsre van szksg a sorozat elejn vagy belsejben,
hasznljuk a list trolt.
[9] Ha az elemeket legtbbszr kulcs alapjn rjk el, hasznljuk a map vagy
a multimap szerkezetet. 17.4.1.
[10] A lehet legnagyobb rugalmassgot gy rhetjk el, ha a lehet legkevesebb
mveletet hasznljuk. 17.1.1.
[11] Ha fontos az elemek sorrendje, a hash_map helyett hasznljuk a map osztlyt.
17.6.1.
[12] Ha a keress sebessge a legfontosabb, a hash_map hasznosabb, mint a map.
17.6.1.
[13] Ha nem tudunk az elemekre kisebb mint mveletet megadni, a hash_map
trolt hasznlhatjuk. 17.6.1.
[14] Ha azt akarjuk ellenrizni, hogy egy kulcs megtallhat-e egy asszociatv trol-
ban, hasznljuk a find() fggvnyt. 17.4.1.6.
[15] Ha az adott kulccsal rendelkez sszes elemet meg akarjuk tallni, hasznljuk
az equal_range()-et. 17.4.1.6.
[16] Ha ugyanazzal a kulccsal tbb elem is szerepelhet, hasznljuk a multimap tro-
lt. 17.4.2.
[17] Ha csak a kulcsot kell nyilvntartanunk, hasznljuk a set vagy a multiset osz-
tlyt. 17.4.3.
17.8. Gyakorlatok
Az itt szerepl gyakorlatok tbbsgnek megoldsa kiderl a standard knyvtr brmely
vltozatnak forrskdjbl. Mieltt azonban megnznnk, hogy a knyvtr megalkoti
hogyan kzeltettk meg az adott problmt, rdemes sajt megoldst ksztennk. Vgl
nzzk t, hogy sajt rendszernkben milyen trolk s milyen mveletek llnak rendelke-
zsnkre.
1. (*2.5) rtsk meg az O( ) jellst (17.1.2). Vgezznk nhny mrst a szabv-
nyos trolk mveleteire s hatrozzuk meg a konstans szorzkat.
2. (*2) Sok telefonszm nem brzolhat egy long rtkkel. Ksztsnk egy
phone_number tpust s egy osztlyt, amely meghatrozza az sszes olyan m-
veletet, melyeknek egy telefonszmokat trol trolban hasznt vehetjk.
3. (*2) rjunk programot, amely kirja egy fjl szavait bcsorrendben. Ksztsnk
kt vltozatot: az egyikben a sz egyszeren reshely karakterekkel hatrolt ka-
raktersorozat legyen, a msikban olyan betsorozat, melyet nem bet karakte-
rek sorozata hatrol.
17. Szabvnyos trolk 669
4. (*2.5) rjunk egy egyszer Paszinsz krtyajtkot.
5. (*1.5) rjunk programot, amely eldnti, hogy egy sz palindrom-e (azaz brzo-
lsa szimmetrikus-e, pldul: ada, otto, tat). rjuk eljrst, amely egy egsz
szmrl dnti el ugyanezt, majd ksztsnk mondatokat vizsgl fggvnyt.
ltalnostsunk.
6. (*1.5) Ksztsnk egy sort (queue) kt verem segtsgvel.
7. (*1.5) Ksztsnk egy vermet, amely majdnem olyan, mint a stack, csak nem
msolja az elemeit s lehetv teszi bejrk hasznlatt.
8. (*3) Szmtgpnk minden bizonnyal tmogat valamilyen konkurens (prhu-
zamos vgrehajtsi) lehetsget: szlakat (thread), taszkokat (task) vagy folya-
matokat (process). Dertsk ki, hogyan mkdik ez. A konkurens hozzfrst
lehetv tev rendszer valamilyen mdot ad r, hogy megakadlyozzuk, hogy
kt folyamat ugyanazt a memriaterletet egyszerre hasznlja. Sajt rendszernk
zrolsi eljrsa alapjn ksztsnk egy osztlyt, amely a programoz szmra
egyszeren elrhetv teszi ezt a lehetsget.
9. (*2.5) Olvassuk be dtumok egy sorozatt (pldul: Dec85, Dec50, Jan76),
majd jelentsk meg azt gy, hogy a legksbbi idpont legyen az els a sor-
ban. A dtum formtuma a kvetkez legyen: hnapnv hrom karakteren,
majd vszm kt karakteren. Ttelezzk fel, hogy mindegyik dtum ugyanarra
az vszzadra vonatkozik.
10. (*2.5) ltalnostsuk a dtumok bemeneti formtumt gy, hogy felismerje az
albbi dtumformtumokat: Dec1985, 12/3/1990, (Dec,30,1950), 3/6/2001, stb.
Mdostsuk a 17.8[9] feladatot gy, hogy mkdjn ezekre a formtumokra is.
11. (*1.5) Hasznljuk a bitset trolt nhny szm binris alakjnak kiratshoz.
Pldul 0, 1, -1, 18, -18 s a legnagyobb pozitv int rtk.
12. (*1.5) A bitset segtsgvel brzoljuk, hogy egy osztly mely tanuli voltak
jelen egy adott napon. Olvassuk be 12 nap bitset objektumt s llaptsuk meg,
kik voltak jelen minden nap, s kik voltak legalbb 8 napot az iskolban.
13. (*1.5) Ksztsnk egy olyan mutatkbl ll listt, amely trli a mutatott objek-
tumokat is, ha egy mutatt trlnk belle vagy ha az egsz listt megszntetjk.
14. (*1.5) rassuk ki rendezve egy adott stack objektum elemeit anlkl, hogy az
eredeti vermet megvltoztatnnk.
15. (*2.5) Fejezzk be a hash_map (17.6.1) megvalstst. Ehhez meg kell rnunk
a find() s az equal_range() fggvnyt, valamint mdot kell adnunk a ksz sab-
lon ellenrzsre. Prbljuk ki a hash_map osztlyt legalbb egy olyan kulcst-
pusra, melyre az alaprtelmezett hastfggvny nem hasznlhat.
16. (*2.5) Ksztsnk el egy listt a szabvnyos list stlusban s teszteljk.
17. (*2) Bizonyos helyzetekben a list tlzott memria-felhasznlsa problmt
jelent. Ksztsnk egy egyirny lncolt listt a szabvnyos trolk stlusban.
A standard knyvtr 670
18. (*2.5) Ksztsnk el egy listt, amely olyan, mint a szabvnyos list, csak tmo-
gatja az indexelst is. Hasonltsunk ssze nhny listra az indexels kltsgt
egy ugyanilyen mret vector indexelsi kltsgvel.
19. (*2) Ksztsnk egy sablon fggvnyt, amely kt trolt sszefsl.
20. (*1.5) llaptsuk meg, hogy egy C stlus karakterlnc palindrom-e. Vizsgljuk
meg, hogy a karakterlnc (legalbb) els hrom szavbl ll sorozat palind-
rom-e.
21. (*2) Olvassuk be (name,value) (nv, rtk) prok egy sorozatt s ksztsnk
egy rendezett listt (name, total, mean, median) (nv, sszesen, tlag, kzp-
rtk) sorokbl.
22. (*2.5) Vizsgljuk meg, mekkora az ltalunk ksztett trolk trignye.
23. (*3.5) Gondolkozzunk el azon, milyen megvalstsi stratgit hasznlhatnnk
egy olyan hash_map trolhoz, melynl a lehet legkisebb trigny a legfbb
kvetelmny. Hogyan kszthetnnk olyan hash_map osztlyt, melynek keres-
si ideje minimlis? Nzzk t, mely mveleteket rdemes kihagyni a megvals-
tsbl az optimlishoz (felesleges memriafoglals, illetve idvesztesg nlkli)
kzeli megolds elrshez. Segtsg: a hasttblknak igen kiterjedt irodalma
van.
24. (*2) Dolgozzunk ki olyan elvet a hash_map tlcsordulsnak (klnbz rt-
kek ugyanazon hastkdra kerlsnek) kezelsre, amely az equal_range()
elksztst egyszerv teszi.
25. (*2.5) Becsljk meg a hash_map trignyt, majd mrjk is le azt. Hasonltsuk
ssze a becslt s a szmtott rtkeket. Hasonltsuk ssze az ltalunk megval-
stott hash_map s map trignyt.
26. (*2.5) Vizsgljuk meg sajt hash_map osztlyunkat abbl a szempontbl, hogy
melyik mvelettel telik el a legtbb id. Tegyk meg ugyanezt sajt map tro-
lnkra, illetve a hash_map kereskedelmi vltozataira is.
27. (*2.5) Ksztsk el a hash_map trolt egy vector<map<K,V>*> szerkezet segt-
sgvel. Minden map trolja az sszes olyan kulcsot, melyek hastkdja meg-
egyezik.
28. (*3) Ksztsk el a hash_map osztlyt Splay fk segtsgvel. (Lsd: D. Sleator,
R. E. Tarjan:Self-Adjusting Binary Search Trees, JACM, 32. ktet, 1985)
29. (*2) Adott egy struktra, amely egy karakterlnc-szer egyedet r le:
struct St {
int size;
char type_indicator;
char* buf; // mretre mutat
St(const char* p); // memriafoglals s a buf feltltse
};
17. Szabvnyos trolk 671
Ksztsnk 1000 St objektumot s hasznljuk ezeket egy hash_map kulcsaknt.
Ksztsnk programot, mellyel tesztelni lehet a hash_map hatkonysgt.
rjunk egy hastfggvnyt (Hash, 17.6.2.3) kifejezetten az St tpus kulcsok
kezelshez.
30. (*2) Adjunk legalbb ngyfle megoldst a trlsre kijellt (erased) elemek elt-
voltsra a hash_map trolbl. Ciklus helyett hasznljuk a standard knyvtr
algoritmusait. (3.8, 18. fejezet)
31. (*3) Ksztsnk olyan hash_map trolt, amely azonnal trli az elemeket.
32. (*2) A 17.6.2.3 pontban bemutatott hastfggvny nem mindig hasznlja
a kulcs teljes brzolst. Mikor hagy figyelmen kvl rszleteket ez a megolds?
rjunk olyan hastfggvnyt, amely mindig a kulcs teljes brzolst hasznlja.
Adjunk r pldt, mikor lehet jogos a kulcs egy rsznek elfelejtse s rjunk
olyan hastfggvnyt, amely a kulcsnak csak azt a rszt veszi figyelembe,
amelyet fontosnak nyilvntunk.
33. (*2.5) A hastfggvnyek forrskdja meglehetsen hasonl: egy ciklus sorra
veszi az adatokat, majd ellltja a hastrtket. Ksztsnk olyan Hash
(17.6.2.3) fggvnyt, amely az adatokat egy, a felhasznl ltal megadott fgg-
vny ismtelt meghvsval gyjti ssze. Pldul:
size_t res = 0;
while (size_t v = hash(key)) res = (res<<3)^v;
Itt a felhasznl a hash(K) fggvnyt minden olyan K tpusra megadhatja, amely
alapjn hastani akar.
34. (*3) A hash_map nhny megvalstsbl kiindulva ksztsk el
a hash_multimap, a hash_set s a hash_multiset trolt.
35. (*2.5) Ksztsnk olyan hastfggvnyt, amely egyenletes eloszls int rtke-
ket kpez le egy krlbell 1024 mret hasttblra. Ezen hastfggvny is-
meretben adjunk meg 1024 olyan kulcsrtket, amelyet a fggvny ugyanarra
a hastkdra kpez le.
A standard knyvtr 672
Algoritmusok s fggvnyobjektumok
A forma szabadd tesz.
(a mrnkk kzmondsa)
Bevezet A szabvnyos algoritmusok ttekintse Sorozatok Fggvnyobjektu-
mok Prediktumok Aritmetikai objektumok Lektk Tagfggvny-objektumok
for_each Elemek keresse count Sorozatok sszehasonltsa Keress Msols
transform Elemek lecserlse s eltvoltsa Sorozatok feltltse trendezs swap
Rendezett sorozatok binary_search merge Halmazmveletek min s max Kupac
Permutcik C stlus algoritmusok Tancsok Gyakorlatok
18.1. Bevezet
Egy trol nmagban nem tlsgosan rdekes dolog. Ahhoz, hogy tnyleg hasznoss vl-
jon, szmos alapvet mveletre is szksg van, melyekkel pldul lekrdezhetjk a trol
mrett, bejrhatjuk, msolhatjuk, rendezhetjk, vagy elemeket kereshetnk benne. Sze-
rencsre a standard knyvtr biztostja mindazokat a szolgltatsokat, melyekre a progra-
mozknak szksgk van a trolk hasznlathoz.
18
Ebben a fejezetben a szabvnyos algoritmusokat foglaljuk ssze s nhny pldt mutatunk
be hasznlatukra. Kiemeljk azokat a legfontosabb elveket s mdszereket, melyeket is-
mernnk kell az algoritmusok lehetsgeinek a C++-ban val kiaknzshoz. Nhny alap-
vet algoritmust rszletesen is megvizsglunk.
Azokat az eljrsokat, melyek segtsgvel a programozk sajt ignyeikhez alakthatjk
a szabvnyos algoritmusok viselkedst, a fggvnyobjektumok biztostjk. Ezek adjk
meg azokat az alapvet informcikat is, melyekre a felhasznlk adatainak kezelshez
szksg van.
ppen ezrt nagy hangslyt helyeznk arra, hogy bemutassuk a fggvnyobjektumok lt-
rehozsnak s hasznlatnak mdszereit.
18.2. A standard knyvtr algoritmusainak ttekintse
Els pillantsra gy tnhet, hogy a standard knyvtr algoritmusainak szma szinte vgte-
len, pedig mindssze 60 darab van bellk. Tallkoztam mr olyan osztllyal, melynek
nmagban tbb tagfggvnye volt. Radsul nagyon sok algoritmus ugyanazt az ltalnos
viselkedsformt, illetve felletstlust mutatja, s ez nagymrtkben leegyszersti megrt-
sket. Ugyangy, mint a programnyelvi lehetsgek esetben, a programoznak itt is csak
azokat az elemeket kell hasznlnia, amelyekre ppen szksge van s amelyek mkdst
ismeri. Semmilyen elnyt nem jelent, ha minden aprsghoz szabvnyos algoritmust kere-
snk, s azrt sem kapunk jutalmat, ha az algoritmusokat rendkvl okosan, de ttekint-
hetetlenl alkalmazzuk. Ne felejtsk el, hogy a programkd lersnak elsdleges clja,
hogy a ksbbi olvask szmra a program mkdse rthet legyen (A ksbbi olvask
valsznleg mi magunk lesznk nhny v mlva.) Msrszt, ha egy trol elemeivel va-
lamilyen feladatot kell elvgeznnk, gondoljuk vgig, hogy a mvelet nem fogalmazhat-e
meg a standard knyvtr algoritmusainak stlusban. Az is elkpzelhet, hogy az algoritmus
mr meg is van, csak olyan ltalnos formban, hogy els rnzsre r sem ismernk. Ha
megszokjuk az ltalnostott (generikus) algoritmusok vilgt, nagyon sok felesleges mun-
ktl kmlhetjk meg magunkat.
A standard knyvtr 674
Mindegyik algoritmus egy sablon fggvny (template function) (13.3) formjban jelenik
meg vagy sablon fggvnyek egy csoportjaknt. Ez a megolds lehetv teszi, hogy az al-
goritmusok sokfle elemsorozaton legyenek kpesek mkdni s termszetesen az elemek
tpusa is mdosthat legyen. Azok az algoritmusok, melyek eredmnykppen egy bejrt
(iterator) (19.1) adnak vissza, ltalban a bemeneti sorozat vgt hasznljk a sikertelen
vgrehajts jelzsre:
void f(list<string>& ls)
{
list<string>::const_iterator p = find(ls.begin(),ls.end(),"Frici");
if (p == ls.end()) {
// "Frici" nem tallhat
}
else {
// p "Frici"-re mutat
}
}
Az algoritmusok nem vgeznek tartomnyellenrzst a be- vagy kimenetkn. A rossz
tartomnybl ered hibkat ms mdszerekkel kell elkerlnnk (18.3.1, 19.3) Ha az algo-
ritmus egy bejrt ad vissza, annak tpusa ugyanolyan lesz, mint a bemeneti sorozat vala-
melyik bejrjnak. Teht pldul az algoritmus paramtere hatrozza meg, hogy a vissza-
trsi rtk const_iterator vagy nem konstans iterator lesz-e:
void f(list<int>& li, const list<string>& ls)
{
list<int>::iterator p = find(li.begin(),li.end(),42);
list<string>::const_iterator q = find(ls.begin(),ls.end(),"Ring");
}
A standard knyvtr algoritmusai kztt megtalljuk a trolk leggyakoribb ltalnos m-
veleteit, pldul a bejrsokat, keresseket, rendezseket, illetve az elemek beszrst s
trlst is. A szabvnyos algoritmusok kivtel nlkl az std nvtrben tallhatk s az
<algorithm> fejllomny deklarlja ket. rdekes, hogy az igazn ltalnos algoritmusok
nagy rsze annyira egyszer, hogy ltalban helyben kifejtett (inline) sablon fggvnyek
valstjk meg azokat. Ezrt az algoritmusok ciklusait a hatkony, fggvnyen belli
optimalizcis eljrsok jelentsen javthatjk.
A szabvnyos fggvnyobjektumok is az std nvtrben tallhatk, de ezek deklarcijt
a <functional> fejllomnyban rhetjk el. A fggvnyobjektumok felptse is olyan, hogy
knnyen hasznlhatk helyben kifejtett fggvnyknt.
18. Algoritmusok s fggvnyobjektumok 675
A nem mdost sorozatmveletek arra hasznlhatk, hogy adatokat nyerjnk ki egy soro-
zatbl vagy bizonyos elemek helyt meghatrozzuk bennk:
A legtbb algoritmus lehetv teszi, hogy a programoz hatrozza meg azt a feladatot, ame-
lyet minden elemen, illetve elempron el akar vgezni. Ezltal az algoritmusok sokkal lta-
lnosabbak s hasznosabbak, mint azt els rnzsre gondolhatnnk. A programoz hat-
rozhatja meg, mikor tekintnk kt elemet azonosnak s mikor klnbznek (18.4.2), s
a leggyakrabban vgrehajtott, leghasznosabb mveletet alaprtelmezettknt is kijellheti.
A sorozatmdost mveletek kztt igen kevs hasonlsgot tallhatunk azon kvl, hogy
megvltoztatjk a sorozat egyes elemeinek rtkt:
A standard knyvtr 676
Nem mdost sorozatmveletek (18.5) <algorithm>
for_each() Mvelet vgrehajtsa egy sorozat sszes elemre.
find() Egy rtk els elfordulsnak megkeresse egy
sorozatban.
find_if() Az els olyan elem megkeresse egy sorozatban, amire
egy llts teljesl.
find_first_of() Egy sorozat egy elemnek megkeresse egy msik
sorozatban.
adjacent_find() Kt szomszdos rtk keresse.
count() Egy rtk elfordulsainak szma egy sorozatban.
count_if() Azon elemek szma egy sorozatban, melyre teljesl egy
llts.
mismatch() Az els olyan elemek keresse, ahol kt sorozat
klnbzik.
equal() Igazat ad vissza, ha kt sorozat elemei pronknt meg-
egyeznek.
search() Egy sorozat els elfordulst keresi meg
rszsorozatknt.
find_end() Egy sorozat rszsorozatknt val utols elfordulst
keresi meg.
search_n() Egy rtk n-edik elfordulst keresi meg egy sorozatban.
Minden j rendszer magn viseli megalkotjnak rdekldsi krt, illetve szemlyes jel-
lemvonsait. A standard knyvtr troli s algoritmusai tkletesen tkrzik a klasszikus
adatszerkezetek mgtti elveket s az algoritmusok tervezsi szempontjait. A standard
knyvtr nem csak a trolk s algoritmusok legalapvetbb fajtit biztostja, melyekre min-
den programoznak szksge van, hanem olyan eszkzket is, melyekkel ezek az algorit-
musok megvalsthatk, s lehetsget ad a knyvtr bvtsre is.
18. Algoritmusok s fggvnyobjektumok 677
Sorozatmdost mveletek (18.6) <algorithm>
transform() Mvelet vgrehajtsa a sorozat minden elemn.
copy() Sorozat msolsa az els elemtl kezdve.
copy_backward() Sorozat msolsa az utols elemtl kezdve.
swap() Kt elem felcserlse.
iter_swap() Bejrk ltal kijellt kt elem felcserlse.
swap_ranges() Kt sorozat elemeinek felcserlse.
replace() Adott rtk elemek helyettestse.
replace_if() lltst kielgt elemek helyettestse.
replace_copy() Sorozat msolsa adott rtk elemek
helyettestsvel.
replace_copy_if() Sorozat msolsa lltst kielgt elemek
helyettestsvel.
fill() Az sszes elem helyettestse egy adott rtkre.
fill_n() Az els n elem helyettestse egy adott rtkkel.
generate() Az sszes elem helyettestse egy mvelettel elll-
tott rtkre.
generate_n() Az els n elem helyettestse egy mvelettel elll-
tott rtkre.
remove() Adott rtkkel rendelkez elemek trlse.
remove_if() lltst kielgt elemek trlse.
remove_copy() Sorozat msolsa adott rtk elemek trlsvel.
remove_copy_if() Sorozat msolsa lltst kielgt elemek trlsvel.
unique() Szomszdos egyenrtk elemek trlse.
unique_copy() Sorozat msolsa szomszdos egyenrtk elemek
trlsvel.
reverse() Az elemek sorrendjnek megfordtsa.
reverse_copy() Elemek msolsa fordtott sorrendben.
rotate() Elemek krbeforgatsa.
rotate_copy() Sorozat msolsa az elemek krbeforgatsval.
random_shuffle() Elemek trendezse egyenletes eloszls szerint.
A hangsly most nem igazn azon van, hogy ezek az algoritmusok hogyan valsthatk
meg, st a legegyszerbb algoritmusoktl eltekintve nem is azok hasznlatn. Ha az al-
goritmusok szerkezetrl s elksztsi mdjairl tbbet szeretnnk megtudni, ms kny-
veket kell fellapoznunk (pldul [Knuth, 1968] vagy [Tarjan, 1983]). Itt azzal foglalkozunk,
mely algoritmusok llnak rendelkezsnkre a standard knyvtrban s ezek hogyan jelen-
nek meg a C++ nyelvben. Ez a nzpont lehetv teszi, hogy amennyiben tisztban va-
gyunk az algoritmusokkal hatkonyan hasznljuk a standard knyvtrat, illetve olyan
szellemben fejlesszk azt tovbb, ahogy megszletett.
A standard knyvtr sok-sok olyan eljrst knl, melyekkel sorozatokat rendezhetnk, ke-
reshetnk bennk, vagy ms, sorrenden alapul mveleteket vgezhetnk velk:
A standard knyvtr 678
Rendezett sorozatok mveletei (18.7) <algorithm>
sort() tlagos hatkonysggal rendez.
stable_sort() Az egyenrtk elemek sorrendjnek megtartsval
rendez.
partial_sort() Egy sorozat elejt rendezi.
partial_sort_copy() Msol a sorozat elejnek rendezsvel.
nth_element() Az n-edik elemet a megfelel helyre teszi.
lower_bound() Egy rtk els elfordulst keresi meg.
upper_bound() Egy rtk utols elfordulst keresi meg.
equal_range() Egy adott rtket tartalmaz rszsorozatot ad meg.
binary_search() Igazat ad vissza, ha a megadott rtk szerepel
a sorozatban.
merge() Kt rendezett sorozatot fsl ssze.
inplace_merge() Kt egymst kvet rendezett rszsorozatot fsl
ssze.
partition() Elemeket helyez el egy lltsnak (felttelnek)
megfelelen.
stable_partition() Elemeket helyez el egy lltsnak megfelelen,
a relatv sorrend megtartsval.
A kupacmveletek (heap-mveletek) olyan llapotban tartjk a sorozatot, hogy az knnyen
rendezhet legyen, amikor arra szksg lesz:
A knyvtr biztost nhny olyan eljrst is, melyek sszehasonltssal kivlasztott eleme-
ket adnak meg:
18. Algoritmusok s fggvnyobjektumok 679
Halmazmveletek (18.7.5) <algorithm>
includes() Igazat ad vissza, ha egy sorozat rszsorozata
egy msiknak.
set_union() Rendezett unit llt el.
set_intersection() Rendezett metszetet llt el.
set_difference() Azon elemek rendezett sorozatt lltja el,
melyek az els sorozatban megtallhatk, de
a msodikban nem.
set_symmetric_difference() Azon elemek rendezett sorozatt lltja el, me-
lyek csak az egyik sorozatban tallhatk meg.
Kupacmveletek (18.8) <algorithm>
make_heap() Egy sorozatot felkszt kupacknt val hasznlatra.
push_heap() Elemet ad a kupachoz.
pop_heap() Elemet trl a kupacbl.
sort_heap() Rendezi a kupacot.
Minimum s maximum (18.9) <algorithm>
min() Kt rtk kzl a kisebb.
max() Kt rtk kzl a nagyobb.
min_element() A legkisebb rtk egy sorozatban.
max_element() A legnagyobb rtk egy sorozatban.
lexicographical_compare() Kt sorozat kzl az bcsorrend szerint
korbbi.
Vgl a knyvtr lehetv teszi azt is, hogy ellltsuk egy sorozat permutciit (az elemek
sszes lehetsges sorrendjt):
Ezeken kvl, a <numeric> (22.6) fejllomnyban nhny ltalnos matematikai algorit-
must is tallhatunk.
Az algoritmusok lersban a sablonparamterek neve nagyon fontos. Az In, Out, For, Bi s
Ran elnevezsek sorrendben bemeneti bejrt, kimeneti bejrt, elre halad bejrt, kt-
irny bejrt, illetve kzvetlen elrs (vletlen elrs) bejrt jelentenek (19.2.1).
A Pred egyparamter, a BinPred ktparamter prediktumot (lltst, logikai rtk
fggvnyt, 18.4.2) hatroz meg, mg a Cmp sszehasonlt fggvnyre utal. Az Op egy-
operandos mveletet, a BinOp ktoperandost vr. A hagyomnyok szerint sokkal
hosszabb neveket kellett volna hasznlnom a sablonparamterek megnevezsre, de gy
vettem szre, hogy ha mr egy kicsit is ismerjk a standard knyvtrat, a hossz nevek in-
kbb csak rontjk az olvashatsgot.
A kzvetlen elrs bejrk hasznlhatk ktirny bejrknt is, a ktirny bejrk elre
halad bejrknt, az elre halad bejrk pedig akr bemeneti, akr kimeneti bejrknt
(19.2.1). Ha a sablonnak olyan tpust adunk t, amely nem biztostja a szksges mvelete-
ket, a sablon pldnyostsakor hibazenetet kapunk (C.13.7). Ha olyan tpust hasznlunk,
amelyben megvannak a szksges mveletek, de jelentsk (szerepk) nem a megfelel,
akkor kiszmthatatlan futsi idej viselkedsre kell felkszlnnk (17.1.4).
A standard knyvtr 680
Permutcik (18.10) <algorithm>
next_permutation() Az bcsorrend szerinti rendezs alapjn kvet-
kez permutci.
prev_permutation() Az bcsorrend szerinti rendezs alapjn elz
permutci.
18.3. Sorozatok s trolk
ltalnos szably, hogy mindennek a leggyakoribb felhasznlsi mdja legyen a legrvi-
debb, a legegyszerbb s a legbiztonsgosabb. A standard knyvtr az ltalnossg rde-
kben itt-ott megsrti ezt a szablyt, de egy szabvnyos knyvtr esetben az ltalnossg
mindennl fontosabb. A 42 els kt elfordulst egy sorozatban pldul az albbi prog-
ramrszlettel kereshetjk meg
void f(list<int>& li)
{
list<int>::iterator p = find(li.begin(),li.end(),42); // els elforduls
if (p != li.end()) {
list<int>::iterator q = find(++p,li.end(),42); // msodik elforduls
// ...
}
// ...
}
Mivel a find() egy trolkon mkd mvelet, valamilyen tovbbi eszkz segtsgvel le-
hetv kell tennnk, hogy a msodik elfordulst is elrhessk. A tovbbi eszkz fogal-
mt ltalnostani minden trolra s algoritmusra nagyon nehz lenne, ezrt a standard
knyvtr algoritmusai csak sorozatokon mkdnek. Az algoritmusok bemenett gyakran
egy bejr-pr adja, amely egy sorozatot hatroz meg. Az els bejr az els elemet jelli
ki, mg a msodik az utols utni elemet (3.8, 19.2). Az ilyen sorozatot flig nyltnak ne-
vezzk, mivel az els megadott elemet tartalmazza, de a msodikat nem. A flig nylt soro-
zatok lehetv teszik, hogy a legtbb algoritmusnak ne kelljen egyedi esetknt kezelnie az
res sorozatot.
Egy sorozatot gyakran tekinthetnk tartomnynak (range) is, fleg ha elemeit kzvetlenl
elrhetjk. A flig nylt tartomnyok hagyomnyos matematikai jellse az [els,utols)
vagy az [els, utols[. Fontos, hogy egy ilyen sorozat lehet trol vagy annak rszsorozata
is, de bizonyos sorozatok, pldul a ki- s bemeneti adatfolyamok, egyltaln nem kapcso-
ldnak trolkhoz. A sorozatokkal kifejezett algoritmusok mindegyik esetben tkletesen
mkdnek.
18. Algoritmusok s fggvnyobjektumok 681
18.3.1. Bemeneti sorozatok
Az x.begin(), x.end() prral gyakran jelljk azt, hogy az x sszes elemre szksgnk van,
pedig ez a jells hosszadalmas, radsul sok hibalehetsget hordoz magban. Pldul ha
tbb bejrt is hasznlunk, nagyon knnyen hvunk meg egy algoritmust sorozatot nem al-
kot paramterprral:
void f(list<string>& fruit, list<string>& citrus)
{
typedef list<string>::const_iterator LI;
LI p1 = find(fruit.begin(),citrus.end(),"alma"); // helytelen! (klnbz sorozatok)
LI p2 = find(fruit.begin(),fruit.end(),"alma"); // rendben
LI p3 = find(citrus.begin(),citrus.end(),"krte"); // rendben
LI p4 = find(p2,p3,"peach"); // helytelen! (klnbz sorozatok)
// ...
}
A pldban kt hiba is szerepel. Az els mg elg nyilvnval (fleg ha vrjuk a hibt), de
a fordt mr ezt sem knnyen tallja meg. A msodik hibt viszont egy valdi programban
nagyon nehz felderteni, mg egy gyakorlott programoz szmra is. Ha a bejrk szmt
sikerl cskkentennk, akkor ezen hibk elfordulsnak valsznsgt is cskkenthet-
jk. Az albbiakban krvonalazunk egy megkzeltst, amely a bemeneti sorozatok fogal-
mnak bevezetsvel ezt a problmt prblja megoldani. Az algoritmusok ksbbi bemu-
tatsakor azonban nem hasznljuk majd a bemeneti sorozatokat, mert azok a standard
knyvtrban nem szerepelnek, s ebben a fejezetben kizrlag a standard knyvtrral aka-
runk foglalkozni.
Az alaptlet az, hogy paramterknt egy sorozatot adunk meg:
template<class In, class T> In find(In first, In last, const T& v) // szabvnyos
{
while (first!=last && *first!=v) ++first;
return first;
}
template<class In, class T> In find(Iseq<In> r, const T& v) // bvts
{
return find(r.first,r.second,v);
}
A tlterhels (overloading) (13.3.2) ltalban lehetv teszi, hogy az algoritmus bemeneti
sorozat formjt hasznljuk, ha Iseq paramtert adunk t.
A standard knyvtr 682
A bemeneti sorozatot termszetesen bejr-prknt (17.4.1.2) valstjuk meg:
template<class In> struct Iseq : public pair<In,In> {
Iseq(In i1, In i2) : pair<In,In>(i1,i2) { }
};
A find() fggvny msodik vltozatnak hasznlathoz kzvetlenl is elllthatunk Iseq
bemeneti sorozatot:
LI p = find(Iseq<LI>(fruit.begin(),fruit.end()),"alma");
Ez a megolds azonban mg krlmnyesebb, mint az eredeti find() fggvny. Ahhoz,
hogy ennek a megoldsnak hasznt vehessk, mg egy egyszer segdfggvnyre van
szksg. Egy trolhoz megadott Iseq valjban nem ms, mint az elemek sorozata az els-
tl (begin()) az utolsig (end()):
template<class C> Iseq<C::iterator> iseq(C& c) // trolra
{
return Iseq<C::iterator>(c.begin(),c.end());
}
Ez a fggvny lehetv teszi, hogy a teljes trolkra vonatkoz algoritmusokat tmren, is-
mtlsek nlkl rjuk le:
void f(list<string>& ls)
{
list<string>::iterator p = find(ls.begin(),ls.end(),"szabvnyos");
list<string>::iterator q = find (iseq(ls),"bvts");
// ..
}
Az iseq() fggvnynek knnyen elkszthetjk olyan vltozatait is, amelyek tmbkhz,
bemeneti adatfolyamokhoz, vagy brmely ms trolhoz (18.13[6]) nyjtanak ilyen hozz-
frst.
Az Iseq legfontosabb elnye, hogy a bemeneti sorozat lnyegt vilgoss teszi. Gyakorlati
haszna abban ll, hogy az iseq() megsznteti a knyelmetlen s hibalehetsget magban
hordoz ismtldst, amelyet a bemeneti sorozat kt bejrval val megadsa jelent.
A kimeneti sorozat fogalmnak meghatrozsa szintn hasznos lehet, br kevsb egysze-
r s kevsb kzvetlenl hasznlhat, mint a bemeneti sorozatok (18.13[7], lsd mg:
19.2.4).
18. Algoritmusok s fggvnyobjektumok 683
18.4. Fggvnyobjektumok
Nagyon sok olyan algoritmus van, amely a sorozatokat csak bejrk s rtkek segtsg-
vel kezeli. A 7 els elfordulst egy sorozatban pldul az albbi mdon tallhatjuk meg:
void f(list<int>& c)
{
list<int>::iterator p = find(c.begin(),c.end(),7);
// ...
}
Egy kicsit rdekesebb eset, ha mi adunk meg egy fggvnyt, amelyet az algoritmus hasznl
(3.8.4). Az els, htnl kisebb elemet pldul a kvetkez programrszlet keresi meg:
bool less_than_7(int v)
{
return v<7;
}
void f(list<int>& c)
{
list<int>::iterator p = find_if(c.begin(),c.end(),less_than_7);
// ...
}
Nagyon sok egyszer helyzetben nyjtanak segtsget a paramterknt tadott fggvnyek:
logikai felttelknt (prediktum), aritmetikai mveletknt, olyan eljrsknt, mellyel infor-
mcit nyerhetnk ki az elemekbl stb. Nem szoks s nem is hatkony minden felhaszn-
lsi terletre kln fggvnyt rni. Msrszt egyetlen fggvny nha nem is kpes megva-
lstani ignyeinket. Gyakran elfordul pldul, hogy a minden egyes elemre meghvott
fggvnynek a lefutsok kztt meg kell tartania valamilyen informcit. Az osztlyok tag-
fggvnyei az ilyen feladatokat jobban vgre tudjk hajtani, mint az nll eljrsok, hiszen
az objektumok kpesek adatok trolsra s lehetsget adnak azok kezdeti rtknek be-
lltsra is.
Nzzk, hogyan is kszthetnk egy fggvnyt vagy pontosabban egy fggvnyszer osz-
tlyt egy sszegzshez:
template<class T> class Sum {
T res;
public:
Sum(T i = 0) : res(i) { } // kezdeti rtkads
void operator()(T x) { res += x; } // sszegzs
A standard knyvtr 684
T result() const { return res; } // sszegzs visszaadsa
};
Lthat, hogy a Sum osztly olyan aritmetikai tpusokhoz hasznlhat, melyek kezdrt-
ke nulla lehet s alkalmazhat rjuk a += mvelet:
void f(list<double>& ld)
{
Sum<double> s;
s = for_each(ld.begin(),ld.end(),s); // s() meghvsa ld minden elemre
cout << "Az sszeg: " << s.result() << '\n';
}
Ebben a programrszletben a for_each() (18.5.1) fggvny az ld minden egyes elemre
meghvja a Sum<double>::operator()(double) tagfggvnyt, s eredmnyl a harmadik pa-
ramterben megadott objektumot adja vissza.
Annak, hogy ez az utasts egyltaln mkdik, az az oka, hogy a for_each() nem teszi k-
telezv, hogy harmadik paramtere tnyleg egy fggvny legyen. Mindssze annyit felt-
telez, hogy ez a valami meghvhat a megfelel paramterrel. Ezt a szerepet egy meghat-
rozott objektum is betltheti, sokszor hatkonyabban is, mint egy egyszer fggvny. Egy
osztly fggvnyhv opertort pldul knnyebb optimalizlni, mint egy fggvnyt, me-
lyet fggvnyre hivatkoz mutatknt adtunk t. Ezrt a fggvnyobjektumok gyakran
gyorsabban futnak, mint a szoksos fggvnyek. Azon osztlyok objektumait, melyekhez
elksztettk a fggvnyhv opertort (11.9), fggvnyszer objektumoknak, funktorok-
nak (functor) vagy egyszeren fggvnyobjektumoknak nevezzk.
18.4.1. Fggvnyobjektumok bzisosztlyai
A standard knyvtr szmos hasznos fggvnyobjektumot knl. A fggvnyobjektumok el-
ksztsnek megknnytshez a knyvtr kt bzisosztlyt tartalmaz:
template <class Arg, class Res> struct unary_function {
typedef Arg argument_type;
typedef Res result_type;
};
template <class Arg, class Arg2, class Res> struct binary_function {
typedef Arg first_argument_type;
typedef Arg2 second_argument_type;
typedef Res result_type;
};
18. Algoritmusok s fggvnyobjektumok 685
Ezen osztlyok clja, hogy szabvnyos neveket adjanak a paramtereknek s a visszatrsi
rtk tpusnak, gy a unary_function s a binary_function osztly leszrmazottait haszn-
l programozk elrhetik azokat. A standard knyvtr kvetkezetesen hasznlja ezeket az
osztlyokat, ami abban is segti a programozt, hogy megllaptsa, mire is j az adott fgg-
vnyobjektum (18.4.4.1).
18.4.2. Prediktumok
A prediktum (llts, felttel; predicate) egy olyan fggvnyobjektum (vagy fggvny),
amely logikai (bool) rtket ad vissza. A <functional> fejllomnyban pldul az albbi de-
fincikat tallhatjuk:
template <class T> struct logical_not : public unary_function<T,bool> {
bool operator()(const T& x) const { return !x; }
};
template <class T> struct less : public binary_function<T,T,bool> {
bool operator()(const T& x, const T& y) const { return x<y; }
};
Az egy- s ktoperandos (unris/binris) prediktumokra gyakran van szksg a szabv-
nyos algoritmusok esetben. Pldul sszehasonlthatunk kt sorozatot gy, hogy megke-
ressk az els elemet, amely az egyik sorozatban nem kisebb, mint a neki megfelel elem
a msikban:
void f(vector<int>& vi, list<int>& li)
{
typedef list<int>::iterator LI;
typedef vector<int>::iterator VI;
pair<VI,LI> p1 = mismatch(vi.begin(),vi.end(),li.begin(),less<int>());
// ...
}
A mismatch() fggvny a sorozatok sszetartoz elemeire alkalmazza a megadott
ktoperandus prediktumot, mindaddig, amg az igaz rtket nem ad vissza (18.5.4).
A visszatrsi rtk az a kt bejr, amelyeket az sszehasonlts nem sszetartoznak ta-
llt. Mivel nem tpust, hanem objektumot kell tadnunk, a less<int>() kifejezst hasznljuk
(zrjelekkel) a less<int> forma helyett.
A standard knyvtr 686
Elkpzelhet, hogy az els olyan elem helyett, amely nem kisebb a prjnl, pont arra van
szksgnk, amely kisebb annl. Ezt a feladatot az els olyan elempr megkeressvel vgez-
hetjk el, amelyre a nagyobb vagy egyenl (greater_equal) kiegszt felttel nem teljesl:
p1 = mismatch(vi.begin(),vi.end(),li.begin(),greater_equal<int>());
Egy msik lehetsg, hogy a sorozatokat fordtott sorrendben adjuk meg s a kisebb vagy
egyenl (less_equal) mveletet hasznljuk:
pair<LI,VI> p2 = mismatch(li.begin(),li.end(),vi.begin(),less_equal<int>());
A 18.4.4.4 pontban azt is megnzzk, hogyan rhatjuk le kzvetlenl a nem kisebb felttelt.
18.4.2.1. A prediktumok ttekintse
A <functional> fejllomnyban nhny ltalnos prediktum tallhat:
Az unris egyparamter, a binris ktparamter fggvnyt jelent, az arg (argumentum)
a paramtereket jelli. A less s a logical_not mveletet a 18.4.2 pont elejn rtuk le.
A knyvtr ltal knlt prediktumokon kvl a programoz sajt maga is ltrehozhat ilyen
fggvnyeket. Ezek a programoz ltal megadott prediktumok nagy szerepet jtszanak
a standard knyvtr algoritmusainak egyszer s elegns hasznlatban. A prediktumok
meghatrozsnak lehetsge klnsen fontos akkor, ha olyan osztlyra akarunk hasznl-
ni egy algoritmust, amely teljesen fggetlen a standard knyvtrtl s annak algoritmusai-
tl. Pldul kpzeljk el a 10.4.6 pontban bemutatott Club osztly kvetkez vltozatt:
18. Algoritmusok s fggvnyobjektumok 687
Prediktumok <functional>
equal_to binris arg1==arg2
not_equal_to binris arg1!=arg2
greater binris arg1>arg2
less binris arg1<arg2
greater_equal binris arg1>=arg2
less_equal binris arg1<=arg2
logical_and binris arg1&&arg2
logical_or binris arg1||arg2
logical_not unris !arg
class Person { /* ... */ };
struct Club {
string name;
list<Person*> members;
list<Person*> officers;
// ...
Club(const string& n);
};
Igen logikus feladat lenne, hogy megkeressnk egy adott nev klubot egy list<Club> tro-
lban. A standard knyvtr find_if() algoritmusa azonban egyltaln nem ismeri a Club
osztlyt. A knyvtri algoritmusok tudjk, hogyan lehet egyenlsget vizsglni, de a klubot
mi nem a teljes rtke alapjn akarjuk megtallni, mindssze a Club::name adattagot akar-
juk kulcsknt hasznlni. Teht runk egy olyan prediktumot, amely ezt a felttelt tkrzi:
class Club_eq : public unary_function<Club,bool> {
string s;
public:
explicit Club_eq(const string& ss) : s(ss) { }
bool operator()(const Club& c) const { return c.name==s; }
};
A megfelel prediktumok meghatrozsa ltalban nagyon egyszer, s ha az ltalunk lt-
rehozott tpusokhoz megadtuk a megfelel prediktumokat, akkor ezekre a szabvnyos al-
goritmusok ugyanolyan egyszeren s hatkonyan hasznlhatk lesznek, mint az egyszer
tpusokbl felptett trolk esetben:
void f(list<Club>& lc)
{
typedef list<Club>::iterator LCI;
LCI p = find_if(lc.begin(),lc.end(),Club_eq("tkez filozfusok"));
// ...
}
18.4.3. Aritmetikai fggvnyobjektumok
Amikor numerikus osztlyokat hasznlunk, gyakran van szksgnk a szoksos aritmetikai
mveletekre fggvnyobjektumok formjban. Ezrt a <functional> llomnyban a kvet-
kez mveletek deklarcija is szerepel:
A standard knyvtr 688
A multiplies mveletet hasznlhatjuk pldul arra, hogy kt vector elemeit sszeszorozzuk
s ezzel egy harmadik vektort hozzunk ltre:
void discount(vector<double>& a, vector<double>& b, vector<double>& res)
{
transform(a.begin(),a.end(),b.begin(),back_inserter(res),multiplies<double>());
}
A back_inserter() eljrsrl a 19.2.4. pontban lesz sz. Az aritmetikai algoritmusok nme-
lyikvel a 22.6 fejezetben rszletesen foglalkozunk.
18.4.4. Lektk, talaktk s tagadk
Hasznlhatunk olyan prediktumokat s aritmetikai fggvnyobjektumokat is, melyeket mi
magunk rtunk, de hivatkozunk bennk a standard knyvtr ltal knlt eljrsokra. Ha azon-
ban egy j prediktumra van szksgnk, elllthatjuk azt egy mr ltez prediktum apr
mdostsval is. A standard knyvtr tmogatja a fggvnyobjektumok ilyen felptst:
18.4.4.1 A lektk (binder) lehetv teszik, hogy egy ktparamter fggvnyob-
jektumot egyparamter fggvnyknt hasznljuk, azltal, hogy az egyik
paramterhez egy rgztett rtket ktnek.
18.4.4.2 A tagfggvny-talaktk (member function adapter) lehetv teszik,
hogy tagfggvnyeket hasznljunk az algoritmusok paramtereknt.
18.4.4.3 A fggvnyre hivatkoz mutatk talakti (pointer to function adapter)
lehetv teszik, hogy fggvnyre hivatkoz mutatkat hasznljunk algo-
ritmusok paramtereknt.
18.4.4.4 A tagadk (negater) segtsgvel egy llts (prediktum) tagadst,
ellenttt (negltjt) fejezhetjk ki.
Ezeket a fggvnyobjektumokat egytt talaktknak (adapter) nevezzk. Mindegyik ta-
lakt azonos felpts s a unary_function s binary_function fggvnyobjektum-bzis-
osztlyokon (18.4.1) alapul. Mindegyikhez rendelkezsnkre ll egy segdfggvny, mely
18. Algoritmusok s fggvnyobjektumok 689
Aritmetikai mveletek <functional>
plus binris arg1+arg2
minus binris arg1arg2
multiplies binris arg1*arg2
divides binris arg1/arg2
modulus binris arg1%arg2
negate unris arg
paramterenknt egy fggvnyobjektumot kap s a megfelel fggvnyobjektumot adja
vissza. Ha ezeket az osztlyokat az operator()() mvelettel hvjuk meg, akkor a kvnt fel-
adat kerl vgrehajtsra. Teht az talakt egyszeren egy magasabb szint fggvny: egy
fggvnyt kap paramterknt s ebbl egy msik fggvnyt llt el:
A standard knyvtr 690
Lektk, talaktk, tagadk <functional>
bind2nd(y) binder2nd Ktparamter fggvny meg-
hvsa gy, hogy y a msodik
paramter.
bind1st(x) binder1st Ktparamter fggvny meg-
hvsa gy, hogy x az els pa-
ramter.
mem_fun() mem_fun_t Paramter nlkli tagfggvny
meghvsa mutatn keresztl.
mem_fun1_t Egyparamter tagfggvny
meghvsa mutatn keresztl.
const_mem_fun_t Paramter nlkli konstans
tagfggvny meghvsa muta-
tn keresztl.
const_mem_fun1_t Egyparamter konstans tag-
fggvny meghvsa mutatn
keresztl.
mem_fun_ref() mem_fun-ref_t Paramter nlkli tagfggvny
meghvsa referencin keresz-
tl.
mem_fun1_ref_t Egyparamter tagfggvny
meghvsa referencin keresz-
tl.
const_mem_fun_ref_t Paramter nlkli konstans
tagfggvny meghvsa
referencin keresztl.
const_mem_fun1_ref_t Egyparamter konstans tag-
fggvny meghvsa referen-
cin keresztl.
ptr_fun() pointer_to_unary_function Egyparamter fggvnyre
hivatkoz mutat meghvsa.
ptr_fun() pointer_to_binary_function Ktparamter fggvnyre
hivatkoz mutat meghvsa.
not1() unary_negate Egyparamter prediktum
neglsa.
not2() binary_negate Ktparamter prediktum
neglsa.
18.4.4.1. Lektk
Az olyan ktparamter prediktumok, mint a less (18.4.2), igen hasznosak s rugalmasak.
Gyakran tapasztaljuk azonban, hogy a legknyelmesebb prediktum az lenne, amely
a trol minden elemt ugyanahhoz a rgztett elemhez hasonltan. A 18.4. pontban be-
mutatott less_than_7() egy jellemz plda erre. A less mveletnek mindenkppen kt para-
mtert kell megadnunk minden egyes hvskor, gy kzvetlenl nem hasznlhatjuk ezt az
eljrst. A megolds a kvetkez lehet:
template <class T> class less_than : public unary_function<T,bool> {
T arg2;
public:
explicit less_than(const T& x) : arg2(x) { }
bool operator()(const T& x) const { return x<arg2; }
};
Ezek utn mr lerhatjuk a kvetkezt:
void f(list<int>& c)
{
list<int>::const_iterator p = find_if(c.begin(),c.end(),less_than<int>(7));
// ...
}
A less_than(7) forma helyett a less_than<int>(7) alakot kell hasznlnunk, mert az <int>
sablonparamter nem vezethet le a konstruktor paramternek (7) tpusbl (13.3.1).
A less_than prediktum ltalban igen hasznos. A fenti mvelet viszont gy jtt ltre, hogy
rgztettk, lektttk (bind) a less fggvny msodik paramtert. Az ilyen szerkezet, mely-
ben teht egy paramtert rgztnk, minden helyzetben ugyangy megvalsthat, s annyi-
ra ltalnos s hasznos, hogy a standard knyvtr egy kln osztlyt knl erre a clra:
template <class BinOp>
class binder2nd : public unary_function<BinOp::first_argument_type, BinOp::result_type> {
protected:
BinOp op;
typename BinOp::second_argument_type arg2;
public:
binder2nd(const BinOp& x, const typename BinOp::second_argument_type& v)
: op(x), arg2(v) { }
result_type operator()(const argument_type& x) const { return op(x,arg2); }
};
18. Algoritmusok s fggvnyobjektumok 691
template <class BinOp, class T> binder2nd<BinOp> bind2nd(const BinOp& op, const T& v)
{
return binder2nd<BinOp>(op,v);
}
A bind2nd() fggvnyobjektumot hasznlhatjuk pldul arra, hogy meghatrozzuk a ki-
sebb, mint 7 egyparamter felttelt a less fggvny s a 7 rtk felhasznlsval:
void f(list<int>& c)
{
list<int>::const_iterator p = find_if(c.begin(),c.end(),bind2nd(less<int>(),7));
// ...
}
Elg olvashat ez a megolds? Elg hatkony? Egy tlagos C++-vltozat megvalstsval
sszehasonltva bizony hatkonyabb, mint az eredeti, melyben a 18.4. pont less_than_7()
fggvnyt hasznltuk akr idigny, akr trhasznlat szempontjbl! Az sszehasonl-
ts radsul knnyen fordthat helyben kifejtett fggvnyknt.
A jells logikus, de megszokshoz kell egy kis id, ezrt rdemes konkrt nvvel meg-
adni a kttt paramter mveletet is:
template <class T> struct less_than : public binder2nd< less<T> > {
explicit less_than(const T& x) : binder2nd(less<T>(),x) { }
};
void f(list<int>& c)
{
list<int>::const_iterator p = find_if(c.begin(),c.end(),less_than<int>(7));
// ...
}
Fontos, hogy a less_than eljrst a less mvelettel hatrozzuk meg, s nem kzvetlenl a <
opertorral, mert gy a less_than hasznlni tudja a less brmely elkpzelhet specializcijt
(13.5, 19.2.2).
A bind2nd() s a binder2nd mellett a <functional> fejllomnyban megtallhatjuk
a bind1st() s binder1st osztlyt is, melyekkel egy ktparamter fggvny els paramte-
rt rgzthetjk.
Egy paramter lektse, amit a bind1st() s a bind2nd() nyjt, nagyon hasonlt ahhoz az
ltalnos szolgltatshoz, amelyet Currying-nek neveznek.
A standard knyvtr 692
18.4.4.2. Tagfggvny-talaktk
A legtbb algoritmus felhasznl valamilyen szabvnyos vagy programoz ltal megadott
mveletet. Termszetesen gyakran tagfggvnyt szeretnnk meghvni. Pldul (3.8.5):
void draw_all(list<Shape*>& c)
{
for_each(c.begin(),c.end(),&Shape::draw); // hopp! hiba
}
A problmt az jelenti, hogy egy mf() tagfggvny meghvshoz az objektumot is meg kell
adnunk: p->mf(). Az olyan algoritmusok azonban, mint a for_each(), a paramterknt ka-
pott fggvnyeket az egyszer fggvnyhv utastssal hajtjk vgre: f(). Ezrt szksgnk
van egy olyan kvetkezetes s hatkony mdszerre, amivel ltrehozhatunk valamit, ami k-
pes rvenni az algoritmusokat, hogy tagfggvnyeket hvjanak meg. Egy lehetsges megol-
ds az lenne, hogy minden algoritmusnak kt pldnyt hozzuk ltre: az egyik a tagfgg-
vnyekkel lenne hasznlhat, a msik az egyszer fggvnyekkel. A helyzet mg ennl is
rosszabb, mert olyan vltozatokra is szksgnk lenne, amelyek objektumok trolin m-
kdnnek (nem objektumokra hivatkoz mutatkon). Ugyangy, mint a lektk (18.4.4.1)
esetben, a megoldst itt is egy j osztly s egy fggvny megrsa jelenti. Elszr vizsgl-
juk meg azt az ltalnos esetet, amikor egy tagfggvnyt paramterek nlkl szeretnnk
meghvni egy mutatkat tartalmaz trol minden elemre:
template<class R, class T> class mem_fun_t : public unary_function<T*,R> {
R (T::*pmf)();
public:
explicit mem_fun_t(R (T::*p)()) :pmf(p) {}
R operator()(T* p) const { return (p->*pmf)(); } // meghvs mutatn keresztl
};
template<class R, class T> mem_fun_t<R,T> mem_fun(R (T::*f)())
{
return mem_fun_t<R,T>(f);
}
Ez megoldja a pldban szerepl Shape::draw() hvst:
void draw_all(list<Shape*>& lsp) // paramter nlkli tag meghvsa objektumra
// hivatkoz mutatn keresztl
{
for_each(lsp.begin(),lsp.end(),mem_fun(&Shape::draw)); // minden alakzat
// kirajzolsa
}
18. Algoritmusok s fggvnyobjektumok 693
Ezenkvl szksgnk van egy olyan osztlyra s mem_fun() fggvnyre is, amelyek a pa-
ramteres tagfggvnyek kezelsre kpesek. Olyan vltozatok is kellenek, melyekkel kz-
vetlenl objektumokat hasznlhatunk, nem pedig objektumokra hivatkoz mutatkat. Ezek
neve mem_fun_ref(). Vgl szksg van a const tagfggvnyeket kezel vltozatokra is:
template<class R, class T> mem_fun_t<R,T> mem_fun(R (T::*f)());
// s az egyparamter tagokra, a const tagokra, s az egyparamter const tagokra
// vonatkoz vltozatok (lsd a 18.4.4 tblzatot)
template<class R, class T> mem_fun_ref_t<R,T> mem_fun_ref(R (T::*f)());
// s az egyparamter tagokra, a const tagokra, s az egyparamter const tagokra
// vonatkoz vltozatok (lsd a 18.4.4 tblzatot)
A <functional> fejllomny tagfggvny-talaktinak felhasznlsval a kvetkezket
rhatjuk:
void f(list<string>& ls) // paramter nlkli tagfggvny hasznlata objektumra
{
typedef list<string>::iterator LSI;
LSI p = find_if(ls.begin(),ls.end(),mem_fun_ref(&string::empty)); // "" keresse
}
void rotate_all(list<Shape*>& ls, int angle)
// egyparamter tagfggvny hasznlata objektumra hivatkoz mutatn keresztl
{
for_each(ls.begin(),ls.end(),bind2nd(mem_fun(&Shape::rotate),angle));
}
A standard knyvtrnak nem kell foglalkoznia azokkal a tagfggvnyekkel, melyek egynl
tbb paramtert vrnak, mert a standard knyvtrban nincs olyan algoritmus, amely kett-
nl tbb paramter fggvnyt vrna operandusknt.
18.4.4.3. Fggvnyre hivatkoz mutatk talakti
Egy algoritmus nem foglalkozik azzal, hogy a fggvnyparamter milyen formban adott:
fggvny, fggvnyre hivatkoz mutat vagy fggvnyobjektum. Ellenben a lektk
(18.4.4.1) szmra ez fontos, mert trolniuk kell egy msolatot a ksbbi felhasznlshoz.
A standard knyvtr kt talaktt knl a fggvnyekre hivatkoz mutatk szabvnyos al-
goritmusokban val felhasznlshoz. A definci s a megvalsts nagyon hasonlt a tag-
fggvny-talaktknl (18.4.4.2) hasznlt megoldsra. Most is kt fggvnyt s kt osz-
tlyt hasznlunk:
A standard knyvtr 694
template <class A, class R> pointer_to_unary_function<A,R> ptr_fun(R (*f)(A));
template <class A, class A2, class R>
pointer_to_binary_function<A,A2,R> ptr_fun(R (*f)(A, A2));
A fggvnyre hivatkoz mutatk ezen talakti lehetv teszik, hogy a szoksos fggv-
nyeket a lektkkel egytt hasznljuk:
class Record { /* ... */ };
bool name_key_eq(const Record&, const char*);// sszehasonlts nevek alapjn
bool ssn_key_eq(const Record&, long); // sszehasonlts szmok alapjn
void f(list<Record>& lr) // fggvnyre hivatkoz mutat hasznlata
{
typedef typename list<Record>::iterator LI;
LI p = find_if(lr.begin(),lr.end(),bind2nd(ptr_fun(name_key_eq),"John Brown"));
LI q = find_if(lr.begin(),lr.end(),bind2nd(ptr_fun(ssn_key_eq),1234567890));
// ...
}
A fenti utastsok azokat az elemeket keresik meg az lr listban, melyekben a John Brown,
illetve az 1234567890 kulcsrtk szerepel.
18.4.4.4. Tagadk
A prediktum-tagadk (negater) a lektkhz kapcsoldnak abbl a szempontbl, hogy
egy mveletet kapnak paramterknt s ebbl egy msik mveletet lltanak el. A taga-
dk defincija s megvalstsa kveti a tagfggvny-talaktknl (18.4.4.2) alkalmazott
formt. Meghatrozsuk rendkvl egyszer, de ezt az egyszersget kicsit elhomlyostja
a hossz szabvnyos nevek hasznlata:
template <class Pred>
class unary_negate : public unary_function<typename Pred::argument_type,bool> {
Pred op;
public:
explicit unary_negate(const Pred& p) : op(p) { }
bool operator()(const argument_type& x) const { return !op(x); }
};
template <class Pred>
class binary_negate : public binary_function<typename Pred::first_argument_type,
typename Pred::second_argument_type, bool> {
18. Algoritmusok s fggvnyobjektumok 695
typedef first_argument_type Arg;
typedef second_argument_type Arg2;
Pred op;
public:
explicit binary_negate(const Pred& p) : op(p) { }
bool operator()(const Arg& x, const Arg2& y) const { return !op(x,y); }
};
template<class Pred> unary_negate<Pred> not1(const Pred& p); // unris tagadsa
template<class Pred> binary_negate<Pred> not2(const Pred& p); // binris tagadsa
Ezek az osztlyok s fggvnyek is a <functional> fejllomnyban kaptak helyet.
A first_argument_type, second_argument_type stb. elnevezsek a unary_function, illetve
a binary_function szabvnyos bzisosztlyokbl erednek.
Ugyangy, mint a lektk, a tagadk is knyelmesen hasznlhatk segdfggvnyeiken
keresztl. Pldul a nem kisebb, mint ktparamter prediktumot is egyszeren lerhat-
juk, s megkereshetjk vele az els kt olyan szomszdos elemet, melyek kzl az els na-
gyobb vagy egyenl, mint a msodik:
void f(vector<int>& vi, list<int>& li) // a 18.4.2 pldjnak javtott vltozata
{
// ...
p1 = mismatch(vi.begin(),vi.end(),li.begin(),not2(less<int>()));
// ...
}
Teht a p1 kapja meg az els olyan elemprt, melyre a nem kisebb, mint mvelet hamis
rtket ad vissza.
A prediktumok logikai rtkekkel dolgoznak, gy a bitenknti opertoroknak (|, &, ^, ~)
nincs megfeleljk.
Termszetesen a lektk, az talaktk s a tagadk egytt is hasznlhatk:
extern "C" int strcmp(const char*,const char*); // a <cstdlib> fejllomnybl
void f(list<char*>& ls) // fggvnyre hivatkoz mutat hasznlata
{
typedef typename list<char*>::const_iterator LI;
LI p = find_if(ls.begin(),ls.end(),not1(bind2nd(ptr_fun(strcmp),"vicces")));
}
A standard knyvtr 696
Ez a kdrszlet az els olyan elemet keresi meg az ls listban, amely a vicces C stlus ka-
rakterlncot tartalmazza. A tagadra azrt van szksg, mert a strcmp() fggvny akkor ad
vissza 0 rtket, ha a kt karakterlnc egyenl.
18.5. Nem mdost algoritmusok sorozatokon
A sorozatok nem mdost algoritmusai elssorban arra szolglnak, hogy a sorozatokban
anlkl kereshessnk meg bizonyos elemeket, hogy ciklust rnnk. Ezen kvl lehetsget
adnak arra, hogy az elemekrl megtudjunk minden ltez informcit. Ezek az algoritmu-
sok csak konstans bejrkat (19.2.1) hasznlnak s a for_each() kivtelvel nem hasznl-
hatk olyan mveletek elvgzsre, melyek a sorozat elemeit megvltoztatnk.
18.5.1. A for_each
Knyvtrakat azrt hasznlunk, hogy ne neknk kelljen azzal fradozni, amit valaki ms
mr megvalstott. Egy knyvtr fggvnyeinek, osztlyainak, algoritmusainak stb. haszn-
lata megknnyti egy program megtervezst, megrst, tesztelst s dokumentlst is.
A standard knyvtr hasznlata ezenkvl olvashatbb is teszi programunkat olyanok sz-
mra, akik ismerik a knyvtrat, hiszen nem kell idt tltenik a hzilag sszeeszkblt
algoritmusok rtelmezsvel.
A standard knyvtr algoritmusainak legfbb elnye, hogy a programoznak nem kell
megrnia bizonyos ciklusokat. A ciklusok nehzkesek s knnyen kvethetnk el bennk
hibkat. A for_each() algoritmus a legegyszerbb algoritmus, abban az rtelemben, hogy
semmi mst nem csinl, minthogy egy ciklust helyettest, egy sorozat minden elemre vg-
rehajtva a paramterben megadott mveletet:
template<class In, class Op> Op for_each(In first, In last, Op f)
{
while (first != last) f(*first++);
return f;
}
Milyen fggvnyeket akarunk ilyen formban meghvni? Ha az elemekrl akarunk inform-
cikat sszegyjteni, az accumulate() fggvnyt (22.6) hasznlhatjuk. Ha meg akarunk ta-
llni valamit egy sorozatban, rendelkezsnkre ll a find() s a find_if() algoritmus. Ha bi-
18. Algoritmusok s fggvnyobjektumok 697
zonyos elemeket trlni vagy mdostani szeretnnk, a remove() (18.6.5), illetve
a replace() (18.6.4) jelent egyszerbb megoldst. Teht mieltt hasznlni kezdjk
a for_each() eljrst, gondoljuk vgig, nincs-e cljainknak jobban megfelel algoritmus.
A for_each() eredmnye az a fggvny vagy fggvnyobjektum, amelyet harmadik para-
mterknt megadtunk. A 18.4 pontban a Sum plda bemutatta, hogy ez a megolds lehe-
tv teszi az eredmnyek visszaadst a hvnak.
A for_each() gyakori felhasznlsi terlete az, hogy egy sorozat elemeibl bizonyos infor-
mcikat fejtnk ki. Pldul sszegyjthetnk neveket klubok egy listjbl:
void extract(const list<Club>& lc, list<Person*>& off)
// hivatalnokok thelyezse 'lc'-bl 'off'-ba
{
for_each(lc.begin(),lc.end(),Extract_officers(off));
}
A 18.4 s a 18.4.2. pontban szerepl pldknak megfelelen kszthetnk egy fggvny-
osztlyt, amely kikeresi a kvnt informcit. Ebben az esetben a kigyjtend neveket
a list<Person*> tartalmazza a Club objektumokon bell, ezrt az Extract_officers fggvny-
nek ki kell msolnia a hivatalnokokat (officer) a Club objektumok officers listjbl a gyj-
t listba:
class Extract_officers {
list<Person*>& lst;
public:
explicit Extract_officers(list<Person*>& x) : lst(x) { }
void operator()(const Club& c)
{ copy(c.officers.begin(),c.officers.end(),back_inserter(lst)); }
};
A nevek kiratst szintn a for_each() fggvnnyel vgezhetjk:
void extract_and_print(const list<Club>& lc)
{
list<Person*> off;
extract(lc,off);
for_each(off.begin(),off.end(),Print_name(cout));
}
A Print_name fggvny megrst meghagyjuk feladatnak (18.13[4]).
A standard knyvtr 698
A for_each() algoritmust a nem mdost eljrsok kz soroltuk, mert kzvetlenl nem
mdost. Ha azonban egy nem konstans sorozatra hvjuk meg, akkor a harmadik param-
terben megadott mvelet mdosthatja a sorozatot. (Nzzk meg pldul a negate() fgg-
vny hasznlatt a 11.9 pontban.)
18.5.2. A find fggvnycsald
A find() algoritmusok vgignznek egy sorozatot (vagy sorozatprt), s megkeresnek egy
konkrt rtket vagy egy olyan elemet, amelyre valamilyen llts (prediktum) teljesl.
A find() legegyszerbb vltozatai csak ezt a feladatot vgzik el:
template<class In, class T> In find(In first, In last, const T& val);
template<class In, class Pred> In find_if(In first, In last, Pred p);
A find() s a find_if() egy bejrt ad vissza, amely az els olyan elemre mutat, amely a ke-
ress felttelnek megfelel. Valjban a find() felfoghat a find_if() egy olyan vltozatnak
is, ahol a vizsglt prediktum az ==. Mirt nem lett mindkt fggvny neve find()? Azrt,
mert fggvny-tlterhelssel nem mindig tudunk klnbsget tenni kt azonos paramter-
szm sablon fggvny kztt:
bool pred(int);
void f(vector<bool(*f)(int)>& v1, vector<int>& v2)
{
find(v1.begin(),v1.end(),pred); // 'pred' keresse
find_if(v2.begin(),v2.end(),pred); // azon int keresse, amelyre pred() igazat ad vissza
}
Ha a find() s a find_if() fggvnynek ugyanaz lenne a neve, akkor igen meglep tbbr-
telmsggel tallkoztunk volna. ltalban az _if uttag azt jelzi, hogy az algoritmus egy pre-
diktumot vr paramterknt.
A find_first_of() algoritmus egy sorozat els olyan elemt keresi meg, amely megtallhat
a msodik sorozatban is:
template<class For, class For2>
For find_first_of(For first, For last, For2 first2, For2 last2);
template<class For, class For2, class BinPred>
For find_first_of(For first, For last, For2 first2, For2 last2, BinPred p);
18. Algoritmusok s fggvnyobjektumok 699
Pldul:
int x[ ] = { 1,3,4 };
int y[ ] = { 0,2,3,4,5};
void f()
{
int* p = find_first_of(x,x+3,y,y+5); // p = &x[1]
int* q = find_first_of(p+1,x+3,y,y+5); // q = &x[2]
}
A p mutat az x[1] elemre fog mutatni, mert a 3 az els olyan eleme az x-nek, amely meg-
tallhat az y-ban. Hasonlan a q az x[2]-re fog mutatni.
Az adjacent_find() algoritmus kt egyms utni, egyez elemet keres:
template<class For> For adjacent_find(For first, For last);
template<class For, class BinPred> For adjacent_find(For first, For last, BinPred p);
A visszatrsi rtk egy bejr, amely az els megfelel elemre mutat:
void f(vector<string>& text)
{
vector<string>::iterator p = adjacent_find(text.begin(),text.end());
if (p!=text.end() && *p=="az") { // Mr megint ktszer szerepel az "az"!
text.erase(p);
// ...
}
}
18.5.3. A count()
A count() s a count_if() fggvny egy rtk elfordulsainak szmt adja meg egy
sorozatban:
template<class In, class T>
typename iterator_traits<In>::difference_type count(In first, In last, const T& val);
template<class In, class Pred>
typename iterator_traits<In>::difference_type count_if(In first, In last, Pred p);
A standard knyvtr 700
A count() visszatrsi rtke nagyon rdekes. Kpzeljk el, hogy a count()-ot az albbi
egyszer fggvnnyel valstjuk meg:
template<class In, class T> int count(In first, In last, const T& val)
{
int res = 0;
while (first != last) if (*first++ == val) ++res;
return res;
}
A gond az, hogy egy int lehet, hogy nem felel meg visszatrsi rtknek. Egy olyan szm-
tgpen, ahol az int tpus elg kicsi, elkpzelhet, hogy tl sok elem van a sorozatban, gy
a count() azt nem tudja egy int-be helyezni. Egy nagyteljestmny programban, egyedi
rendszeren viszont rdemesebb a szmll ltal visszaadott rtket egy short-ban trolni.
Abban biztosak lehetnk, hogy egy sorozat elemeinek szma nem nagyobb, mint a kt
bejrja kztti legnagyobb klnbsg (19.2.1). Ezrt az els gondolatunk a problma
megoldsra az lehet, hogy a visszatrsi rtk tpust a kvetkezkppen hatrozzuk meg:
typename In::difference_type
Egy szabvnyos algoritmusnak azonban a beptett tmbkre ugyangy kell mkdnie,
mint a szabvnyos trolkra:
void f(const char* p, int size)
{
int n = count(p,p+size,'e'); // az 'e' bet elfordulsainak megszmllsa
}
Sajnos az int*::difference_type kifejezs a C++-ban nem rtelmezhet. Ez a problma az
iterator_traits tpus (19.2.2) rszleges specializcijval oldhat meg.
18.5.4. Egyenlsg s eltrs
Az equal() s a mismatch() fggvny kt sorozatot hasonlt ssze:
template<class In, class In2> bool equal(In first, In last, In2 first2);
template<class In, class In2, class BinPred>
bool equal(In first, In last, In2 first2, BinPred p);
18. Algoritmusok s fggvnyobjektumok 701
template<class In, class In2> pair<In, In2> mismatch(In first, In last, In2 first2);
template<class In, class In2, class BinPred>
pair<In, In2> mismatch(In first, In last, In2 first2, BinPred p);
Az equal() algoritmus egyszeren azt mondja meg, hogy a kt sorozat minden kt megfe-
lel eleme megegyezik-e, mg a mismatch() az els olyan elemprt keresi, melyek nem
egyenlek, s ezekre mutat bejrkat ad vissza. A msodik sorozat vgt nem kell meg-
adnunk (teht nincs last2), mert a rendszer azt felttelezi, hogy az legalbb olyan hossz,
mint az els sorozat s a last2 rtket a first2+(last-first) kifejezssel szmtja ki. Ezt a md-
szert gyakran lthatjuk a standard knyvtrban, amikor kt sorozat sszetartoz elemei k-
ztt vgznk valamilyen mveletet.
A 18.5.1 pontban mr emltettk, hogy ezek az algoritmusok sokkal hasznosabbak, mint
azt els rnzsre gondolnnk, mert a programoz hatrozhatja meg azt a prediktumot,
melyet az elemek egyenrtksgnek eldntshez akar hasznlni.
A sorozatoknak nem is kell ugyanolyan tpusaknak lennik:
void f(list<int>& li, vector<double>& vd)
{
bool b = equal(li.begin(),li.end(),vd.begin());
}
Az egyetlen kikts, hogy a prediktum paramtereiknt hasznlhassuk az elemeket.
A mismatch() kt vltozata csak a prediktumok hasznlatban klnbzik. Valjban
megvalsthatjuk ket egyetlen fggvnnyel is, amelynek van alaprtelmezett sablonpara-
mtere:
template<class In, class In2, class BinPred>
pair<In, In2> mismatch(In first, In last, In2 first2,
BinPred p = equal_to<In::value_type>()) // 18.4.2.1
{
while (first != last && p(*first,*first2)) {
++first;
++first2;
}
return pair<In,In2>(first,first2);
}
A standard knyvtr 702
A kt kln fggvny megadsa s az egyetlen, alaprtelmezett paramterrel meghatrozott
fggvny kztt akkor lthatjuk a klnbsget, ha mutatkat adunk t a fggvnyeknek. En-
nek ellenre, ha gy gondolunk a szabvnyos algoritmusok klnbz vltozataira, mint
egy alaprtelmezett prediktummal rendelkez fggvnyre, akkor krlbell feleannyi sab-
lon fggvnyre kell emlkeznnk.
18.5.5. Keress
A search(), a search_n() s a find_end() algoritmus egy rszsorozatot keres egy msik
sorozatban:
template<class For, class For2>
For search(For first, For last, For2 first2, For2 last2);
template<class For, class For2, class BinPred>
For search(For first, For last, For2 first2, For2 last2, BinPred p);
template<class For, class For2>
For find_end(For first, For last, For2 first2, For2 last2);
template<class For, class For2, class BinPred>
For find_end(For first, For last, For2 first2, For2 last2, BinPred p);
template<class For, class Size, class T>
For search_n(For first, For last, Size n, const T& val);
template<class For, class Size, class T, class BinPred>
For search_n(For first, For last, Size n, const T& val, BinPred p);
A search() fggvny a msodikknt megadott sorozatot keresi az elsben. Ha megtallha-
t ez a rszsorozat, egy bejrt kapunk eredmnyl, amely az els illeszked elemet jelli
ki az els sorozatban. Ha a keress sikertelen, akkor a sorozat vgt (last) kapjuk ered-
mnykppen. Teht a visszatrsi rtk mindig a [first,last] tartomnyban van:
string quote("Minek vesztegessk az idt tanulsra, mikor a tudatlansg azonnali?");
bool in_quote(const string& s)
{
typedef string::const_iterator I;
I p = search(quote.begin(),quote.end(),s.begin(),s.end()); // s keresse az idzetben (quote)
return p!=quote.end();
}
18. Algoritmusok s fggvnyobjektumok 703
void g()
{
bool b1 = in_quote("tanulsra"); // b1 = true
bool b2 = in_quote("tantsra"); // b2 = false
}
Teht a search() mvelettel egy rszsorozatot kereshetnk mindenfle sorozatra ltalno-
stva. Ebbl mr rezhetjk, hogy a search() egy nagyon hasznos algoritmus.
A find_end() is a msodikknt megadott sorozatot keresi rszsorozatknt az elsben. Ha si-
keres a keress, a find_end() az illeszked rsz utols elemre mutat bejrt adja vissza.
Mondhatjuk azt is, hogy a find_end() visszafel vgzi ugyanazt a keresst, mint a search().
Teht nem a rszsorozat els elfordulst kapjuk meg, hanem az utolst. A search_n() el-
jrs egy olyan rszsorozatot keres, amely legalbb n hosszon a value paramterben meg-
adott rtket tartalmazza. A visszatrsi rtk egy olyan bejr, amely az n darab illeszke-
ds els elemre mutat a sorozatban.
18.6. Mdost algoritmusok sorozatokra
Ha meg akarunk vltoztatni egy sorozatot, akkor megtehetjk, hogy egyesvel vgignzzk
az elemeket s kzben mdostjuk a megfelel rtkeket. De ha lehetsg van r, akkor ezt
a programozsi stlust rdemes elkerlnnk. Helyette egyszerbb s rendszerezettebb meg-
olds ll a rendelkezsnkre: hasznljuk a szabvnyos algoritmusokat a sorozatok bejr-
sra s a mdost mveletek elvgzsre. A nem mdost algoritmusok (18.5) ugyangy
mennek vgig az elemeken, de csak kiolvassk az informcikat. A mdost algoritmusok
segtsgvel a leggyakoribb frisstsi feladatokat vgezhetjk el. Ezek egy rsze az eredeti
sorozatot mdostja, mg msok j sorozatot hoznak ltre az ltalunk megadott sorozat
alapjn.
A szabvnyos algoritmusok a bejrk segtsgvel frnek hozz az adatszerkezetekhez. Eb-
bl kvetkezik, hogy egy j elem beszrsa egy trolba vagy egy elem trlse nem egy-
szer feladat. Pldul, ha csak egy bejr ll rendelkezsnkre, hogyan tallhatjuk meg azt
a trolt, amelybl a kijellt elemet trlni kell? Hacsak nem hasznlunk egyedi bejrkat
(pldul beszrkat, inserter, 3.8, 19.2.4), a bejrkon keresztl vgzett mveletek nem
vltoztathatjk meg a trol mrett. Az elemek beszrsa s trlse helyett az algoritmu-
sok csak az elemek rtkt vltoztatjk meg, illetve felcserlnek vagy msolnak elemeket.
Mg a remove() is gy mkdik, hogy csak fellrja a trlni kvnt elemeket (18.6.5).
A standard knyvtr 704
Az alapvet mdost algoritmusok ltalban egy mdostott msolatot hoznak ltre a meg-
adott sorozatbl. Azok az algoritmusok, melyek sorozatmdostknak tnnek, valjban
csak a msolk mdostott vltozatai.
18.6.1. Msols
A msols a legegyszerbb mdszer arra, hogy egy sorozatbl egy msikat lltsunk el.
Az alapvet msol mveletek rendkvl egyszerek:
template<class In, class Out> Out copy(In first, In last, Out res)
{
while (first != last) *res++ = *first++;
return res;
}
template<class Bi, class Bi2> Bi2 copy_backward(Bi first, Bi last, Bi2 res)
{
while (first != last) *--res = *--last;
return res;
}
A msol algoritmus kimenetnek nem kell felttlenl trolnak lennie. Brmit hasznlha-
tunk, amihez kimeneti bejr (19.2.6) megadhat:
void f(list<Club>& lc, ostream& os)
{
copy(lc.begin(),lc.end(),ostream_iterator<Club>(os));
}
Egy sorozat beolvasshoz meg kell adnunk, hogy hol kezddik s hol r vget. Az rshoz
csak az az egy bejr kell, amelyik megmondja, hov rjunk. Arra azonban ilyenkor is fi-
gyelnnk kell, hogy ne rjunk a fogad trol hatrain tlra. A problma egyik lehetsges
megoldsa a beszr (inserter) bejrk (19.2.4) hasznlata, mellyel a fogad adatszerkeze-
tet bvthetjk, ha arra szksg van:
void f(vector<char>& vs)
{
vector<char> v;
copy(vs.begin(),vs.end(),v.begin()); // tlrhat v vgn
copy(vs.begin(),vs.end(),back_inserter(v)); // elemek hozzadsa vs-bl v vghez
}
18. Algoritmusok s fggvnyobjektumok 705
A bemeneti s a kimeneti sorozat tfedhetik egymst. Ha a sorozatok kztt nincs tfeds
vagy a kimeneti sorozat vge a bemeneti sorozat belsejben van, akkor a copy() fggvnyt
hasznljuk. A copy_backward() utastsra akkor van szksg, ha a kimeneti sorozat eleje
van a bemeneti sorozatban. Ez a fggvny ilyen helyzetben addig nem rja fell az eleme-
ket, amg azokrl msolat nem kszlt (lsd mg 18.13[13]).
Termszetesen ahhoz, hogy valamit visszafel msoljunk le, egy ktirny bejrra
(19.2.1) van szksgnk, mind a bemeneti, mind a kimeneti sorozatban:
void f(vector<char>& vc)
{
vector<char> v(vc.size());
copy_backward(vc.begin(),vc.end(),ostream_iterator<char>(cout)); // hiba
copy_backward(vc.begin(),vc.end(),v.end()); // rendben
copy(v.begin(),v.end(),ostream_iterator<char>(cout)); // rendben
}
Gyakran olyan elemeket szeretnnk msolni, amelyek egy bizonyos felttelt teljestenek.
Sajnos a copy_if() fggvny valahogy kimaradt a standard knyvtr ltal nyjtott algoritmu-
sok sorbl (mea culpa). De ha szksgnk van r, pillanatok alatt megrhatjuk:
template<class In, class Out, class Pred> Out copy_if(In first, In last, Out res, Pred p)
{
while (first != last) {
if (p(*first)) *res++ = *first;
++first;
}
return res;
}
Ezutn ha az n rtknl nagyobb elemeket akarjuk megjelenteni, a kvetkez eljrst
hasznlhatjuk:
void f(list<int>&ld, int n, ostream& os)
{
copy_if(ld.begin(),ld.end(),ostream_iterator<int>(os),bind2nd(greater<int>(),n));
}
(Lsd mg a remove_copy_if()lerst is a 18.6.5 pontban.)
A standard knyvtr 706
18.6.2. Transzformcik
Egy kicsit flrevezet a neve, ugyanis a transform() nem felttlenl vltoztatja meg a beme-
nett. Ehelyett egy olyan kimenetet llt el, amely a bemeneten vgzett felhasznli mve-
let eredmnye:
template<class In, class Out, class Op>
Out transform(In first, In last, Out res, Op op)
{
while (first != last) *res++ = op(*first++);
return res;
}
template<class In, class In2, class Out, class BinOp>
Out transform(In first, In last, In2 first2, Out res, BinOp op)
{
while (first != last) *res++ = op(*first++,*first2++);
return res;
}
A transform() fggvny els vltozata, amely csak egy sorozatot olvas be, nagyon hasonlt
az egyszer msolsra. A klnbsg mindssze annyi, hogy a kzvetlen kirs helyett elbb
egy transzformcit (talaktst) is elvgez az elemen. gy a copy() eljrst a transform()
fggvnnyel is meghatrozhattuk volna, gy, hogy az elemmdost mvelet egyszeren
csak visszaadja a paramtert:
template<class T> T identity(const T& x) { return x; }
template<class In, class Out> Out copy(In first, In last, Out res)
{
return transform(first,last,res,identity);
}
Az identity explicit minstse ahhoz szksges, hogy a fggvnysablonbl konkrt fgg-
vnyt kapjunk, az iterator_traits sablont (19.2.2) pedig azrt hasznltuk, hogy az In elem-
tpushoz jussunk.
A transform() fggvnyt tekinthetjk a for_each() egy vltozatnak is, amely kzvetlenl
lltja el kimenett. Pldul klubok listjbl a transform() segtsgvel kszthetnk egy
olyan listt, amely csak a klubok neveit trolja:
string nameof(const Club& c) // nv kinyerse
{
return c.name;
}
18. Algoritmusok s fggvnyobjektumok 707
void f(list<Club>& lc)
{
transform(lc.begin(),lc.end(),ostream_iterator<string>(cout),nameof);
}
A transform() fggvny elnevezsnek egyik oka az, hogy az eredmnyt gyakran visszar-
juk oda, ahonnan a paramtert kaptuk. Pldul ha olyan objektumokat akarunk trlni, me-
lyekre mutatk hivatkoznak, a kvetkez eljrst hasznlhatjuk:
struct Delete_ptr { //fggvnyobjektum hasznlata helyben kifejtshez (inline fordtshoz)
template<class T> T* operator() (T* p) { delete p; return 0; }
};
void purge(deque<Shape*>& s)
{
transform(s.begin(),s.end(),s.begin(),Delete_ptr);
}
A transform() algoritmus eredmnye mindig egy kimeneti sorozat. A fenti pldban az
eredmnyt az eredeti bemeneti sorozatba irnytottuk vissza, gy a Delete_ptr()(p) jelent-
se p=Delete_ptr()(p) lesz. Ez indokolja a 0 visszatrsi rtket a Delete_ptr::operator()()
fggvnyben.
A transform() algoritmus msik vltozata kt bemeneti sorozattal dolgozik. Ez lehetv te-
szi, hogy kt sorozat adatait hasznljuk fel az j sorozat ltrehozshoz. Egy animcis
programban pldul szksg lehet egy olyan eljrsra, amely alakzatok sorozatnak helyt
frissti valamilyen talaktssal:
Shape* move_shape(Shape* s, Point p) // *s += p
{
s->move_to(s->center()+p);
return s;
}
void update_positions(list<Shape*>& ls, vector<Point>& oper)
{
// mvelet vgrehajtsa a megfelel objektumon
transform(ls.begin(),ls.end(),oper.begin(),ls.begin(),move_shape);
}
Valjban nem lenne szksgnk arra, hogy a move_shape() fggvnynek visszatrsi r-
tke legyen, de a transform() ragaszkodik a mvelet eredmnynek felhasznlshoz, gy
a move_shape() visszaadja az els operandust, amelyet visszarhatunk az eredeti helyre.
A standard knyvtr 708
Bizonyos helyzetekben ezt a problmt nem oldhatjuk meg gy. Ha a mveletet pldul
nem mi rtuk vagy nem akarjuk megvltoztatni, akkor nem ll rendelkezsnkre a megfe-
lel visszatrsi rtk. Mskor a bemeneti sorozat const. Ezekben az esetekben egy ktso-
rozatos for_each() fggvnyt kszthetnk, amely a ktsorozatos transform() prjnak
tekinthet:
template<class In, class In2, class BinOp>
BinOp for_each(In first, In last, In2 first2, BinOp op)
{
while (first != last) op(*first++,*first2++);
return op;
}
void update_positions(list<Shape*>& ls, vector<Point>& oper)
{
for_each(ls.begin(),ls.end(),oper.begin(),move_shape);
}
Esetenknt olyan kimeneti bejr is hasznos lehet, amely valjban semmit sem r
(19.6[2]).
A standard knyvtr nem tartalmaz olyan algoritmusokat, melyek hrom vagy ngy soro-
zatbl olvasnak, br ezek is knnyen elkszthetk. Helyettk hasznlhatjuk tbbszr egy-
ms utn a transform() fggvnyt.
18.6.3. Ismtld elemek trlse
Amikor informcikat gyjtnk, knnyen elfordulhatnak ismtldsek. A unique() s
a unique_copy() algoritmusokkal az egyms utn elfordul azonos rtkeket tvolthatjuk el:
template<class For> For unique(For first, For last);
template<class For, class BinPred> For unique(For first, For last, BinPred p);
template<class In, class Out> Out unique_copy(In first, In last, Out res);
template<class In, class Out, class BinPred>
Out unique_copy(In first, In last, Out res, BinPred p);
A unique() megsznteti a sorozatban egyms utn elfordul rtkismtldseket,
a unique_copy() pedig egy msolatot kszt az ismtldsek elhagysval:
18. Algoritmusok s fggvnyobjektumok 709
void f(list<string>& ls, vector<string>& vs)
{
ls.sort(); // listarendezs (17.2.2.1)
unique_copy(ls.begin(),ls.end(),back_inserter(vs));
}
Ezzel a programrszlettel az ls listt tmsolhatjuk a vs vektorba s menet kzben kiszrhet-
jk az ismtldseket. A sort() utastsra azrt van szksg, hogy az egyenrtk elemek
egyms mell kerljenek.
A tbbi szabvnyos algoritmushoz hasonlan a unique() is bejrkkal dolgozik. Azt nem
lehet megllaptani, hogy ezek a bejrk milyen tpus trolra mutatnak, gy a trolt nem
vltoztathatjuk meg, csak az annak elemeiben trolt rtkeket mdosthatjuk. Ebbl kvet-
kezik, hogy a unique() nem trli az ismtldseket a sorozatbl, ahogy azt navan reml-
nnk. Ehelyett az egyedi elemeket a sorozat elejre helyezi s visszaad egy bejrt, amely
az egyedi elemek rszsorozatnak vgre mutat:
template <class For> For unique(For first, For last)
{
first = adjacent_find(first,last); // 18.5.2
return unique_copy(first,last,first);
}
A rszsorozat utni elemek vltozatlanok maradnak, teht egy vektor esetben ez a megol-
ds nem sznteti meg az ismtldseket:
void f(vector<string>& vs) // vigyzat: rossz kd!
{
sort(vs.begin(),vs.end()); // vektorrendezs
unique(vs.begin(),vs.end()); // ismtldsek eltvoltsa (nem mkdik!)
}
St azzal, hogy a unique() a sorozat vgn ll elemeket elre helyezi az rtkismtld-
sek megszntetshez, akr j ismtldsek is keletkezhetnek:
int main()
{
char v[ ] = "abbcccde";
char* p = unique(v,v+strlen(v));
cout << v << ' ' << p-v << '\n';
}
A standard knyvtr 710
Az eredmny a kvetkez lesz:
abcdecde 5
Teht a p a msodik c betre mutat.
Azoknak az algoritmusoknak, melyeknek trlnie kellene elemeket (de ezt nem tudjk
megtenni), ltalban kt vltozata van. Az egyik a uniqe() mdszervel trendezi az eleme-
ket, a msik egy j sorozatot hoz ltre a unique_copy() mkdshez hasonlan. A _copy
uttag ezen kt vltozat megklnbztetsre szolgl.
Ahhoz hogy az ismtldseket tnylegesen kiszrjk egy trolbl, mg egy tovbbi utas-
tsra van szksg:
template<class C> void eliminate_duplicates(C& c)
{
sort(c.begin(),c.end()); // rendezs
typename C::iterator p = unique(c.begin(),c.end()); // tmrts
c.erase(p,c.end()); // zsugorts
}
Sajnos az eliminate_duplicates() fggvny a beptett tmbkhz nem hasznlhat, mg
a unique() azoknl is mkdik.
A unique_copy() hasznlatra a 3.8.3. pontban mutattunk pldt.
18.6.3.1. Rendezsi szempont
Az sszes ismtlds megszntetshez a bemeneti sorozatot rendeznnk kell (18.7.1).
Alaprtelmezs szerint a unique() s a unique_copy() is az == opertort hasznlja az egyen-
lsgvizsglathoz, de lehetv teszik, hogy a programoz ms mveletet adjon meg.
A 18.5.1 pontban szerepl programrszletet pldul talakthatjuk gy, hogy kiszrje az
ismtld neveket. Miutn a klubok hivatalnokainak nevt sszegyjtttk, az off listhoz
jutottunk, melynek tpusa list<Person*> (18.5.1). Ebbl a listbl az ismtldseket a k-
vetkez utastssal trlhetjk:
eliminate_duplicates(off);
Ez a megolds azonban mutatkat rendez, s csak akkor fog cljainknak megfelelen m-
kdni, ha felttelezzk, hogy minden Person objektumra egyetlen mutat hivatkozik.
18. Algoritmusok s fggvnyobjektumok 711
Az egyenrtksg eldntshez ltalban magukat a Person rekordokat kell megvizsgl-
nunk. Ehhez a kvetkez programrszletet kell megrnunk:
bool operator==(const Person& x, const Person& y) // "egyenlsg" objektumra
{
// x s y sszehasonltsa egyenlsgre
}
bool operator<(const Person& x, const Person& y) // "kisebb mint" objektumra
{
// x s y sszehasonltsa rendezettsgre
}
bool Person_eq(const Person* x, const Person* y) // "egyenlsg" mutatn keresztl
{
return *x == *y;
}
bool Person_lt(const Person* x, const Person* y) // "kisebb mint" mutatn keresztl
{
return *x < *y;
}
void extract_and_print(const list<Club>& lc)
{
list<Person*> off;
extract(lc,off);
off.sort(off,Person_lt);
list<Club>::iterator p = unique(off.begin(),off.end(),Person_eq);
for_each(off.begin(),p,Print_name(cout));
}
rdemes mindig gondoskodnunk arrl, hogy a rendezshez hasznlt felttel ugyanaz le-
gyen, mint az ismtldsek kiszrsre szolgl eljrs. A < s az == opertorok alaprtel-
mezett jelentse mutatk esetben ltalban nem felel meg szmunkra a mutatott objektu-
mok sszehasonltshoz.
18.6.4. Helyettests
A replace() algoritmusok vgighaladnak a megadott sorozaton, s egyes rtkeket jakra
cserlnek, a mi ignyeinknek megfelelen. Ugyanazt a mintt kvetik, mint a find/find_if
s a unique/unique_copy, gy sszesen ngy vltozatra van szksg. A fggvnyek megva-
lstsa most is elg egyszer ahhoz, hogy jl magyarzza szerepket:
A standard knyvtr 712
template<class For, class T>
void replace(For first, For last, const T& val, const T& new_val)
{
while (first != last) {
if (*first == val) *first = new_val;
++first;
}
}
template<class For, class Pred, class T>
void replace_if(For first, For last, Pred p, const T& new_val)
{
while (first != last) {
if (p(*first)) *first = new_val;
++first;
}
}
template<class In, class Out, class T>
Out replace_copy(In first, In last, Out res, const T& val, const T& new_val)
{
while (first != last) {
*res++ = (*first == val) ? new_val : *first;
++first;
}
return res;
}
template<class In, class Out, class Pred, class T>
Out replace_copy_if(In first, In last, Out res, Pred p, const T& new_val)
{
while (first != last) {
*res++ = p(*first) ? new_val : *first;
++first;
}
return res;
}
Srn elfordul, hogy karakterlncok egy listjn vgighaladva szlvrosom szoksos an-
gol trst (Aarhus) a helyes rhus vltozatra kell cserlnem:
void f(list<string>& towns)
{
replace(towns.begin(),towns.end(),"Aarhus","rhus");
}
Termszetesen ehhez egy kibvtett karakterkszletre van szksg (C.3.3)
18. Algoritmusok s fggvnyobjektumok 713
18.6.5. Trls
A remove() algoritmusok elemeket trlnek egy sorozatbl, rtk vagy felttel alapjn:
template<class For, class T> For remove(For first, For last, const T& val);
template<class For, class Pred> For remove_if(For first, For last, Pred p);
template<class In, class Out, class T>
Out remove_copy(In first, In last, Out res, const T& val);
template<class In, class Out, class Pred>
Out remove_copy_if(In first, In last, Out res, Pred p);
Tegyk fel pldul, hogy a Club osztlyban szerepel egy cmmez is s feladatunk az, hogy
sszegyjtsk a koppenhgai klubokat:
class located_in : public unary_function<Club,bool> {
string town;
public:
located_in(const string& ss) :town(ss) { }
bool operator()(const Club& c) const { return c.town == town; }
};
void f(list<Club>& lc)
{
remove_copy_if(lc.begin(),lc.end(),
ostream_iterator<Club>(cout),not1(located_in("Kbenhavn")));
}
A remove_copy_if() ugyanaz, mint a copy_if(), csak fordtott felttellel. Teht
a remove_copy_if() akkor helyez egy elemet a kimenetre, ha az nem elgti ki a felttelt.
A sima remove()a sorozat elejre gyjti a nem trlend elemeket s az gy kpzett rszso-
rozat vgre mutat bejrt ad vissza (lsd mg 18.6.3).
18.6.6. Feltlts s ltrehozs
A fill() s a generate() algoritmusok segtsgvel rendszerezetten tlthetnk fel egy soroza-
tot rtkekkel:
template<class For, class T> void fill(For first, For last, const T& val);
template<class Out, class Size, class T> void fill_n(Out res, Size n, const T& val);
A standard knyvtr 714
template<class For, class Gen> void generate(For first, For last, Gen g);
template<class Out, class Size, class Gen> void generate_n(Out res, Size n, Gen g);
A fill() fggvny megadott rtkeket ad a sorozat elemeinek, mg a generate() algoritmus
gy lltja el az rtkeket, hogy mindig meghvja a megadott fggvnyt. Teht a fill()
a generate() egyedi vltozata, amikor az rtkeket elllt (ltrehoz, genertor) fggvny
mindig ugyanazt az rtket adja vissza. Az _n vltozatok a sorozat els n elemnek adnak
rtket.
A 22.7 pont Randint s Urand vletlenszm-ellltjnak felhasznlsval pldul a k-
vetkezt rhatjuk:
int v1[900];
int v2[900];
vector v3;
void f()
{
fill(v1,&v1[900],99); // v1 minden elemnek 99-re lltsa
generate(v2,&v2[900],Randint()); // vletlen rtkekre llts (22.7)
// 200 vletlen egsz kldse a kimenetre a [0..99] tartomnybl
generate_n(ostream_iterator<int>(cout),200,Urand(100));
fill_n(back_inserter(v3),20,99); // 20 darab 99 rtk elem hozzadsa v3-hoz
}
A generate() s a fill() nem kezdeti, hanem egyszer rtkadst vgez. Ha nyers trolter-
letet akarunk felhasznlni (pldul egy memriaszeletet meghatrozott tpus s llapot
objektumm szeretnnk alaktani), hasznlhatjuk pldul az uninitialized_fill() fggvnyt,
a <memory> fejllomnybl (19.4.4). Az <algorithm> llomny algoritmusai ilyen clokra
nem felelnek meg.
18.6.7. Megfordts s elforgats
Idnknt szksgnk van r, hogy egy sorozat elemeit trendezzk:
template<class Bi> void reverse(Bi first, Bi last);
template<class Bi, class Out> Out reverse_copy(Bi first, Bi last, Out res);
template<class For> void rotate(For first, For middle, For last);
template<class For, class Out> Out rotate_copy(For first, For middle, For last, Out res);
18. Algoritmusok s fggvnyobjektumok 715
template<class Ran> void random_shuffle(Ran first, Ran last);
template<class Ran, class Gen> void random_shuffle(Ran first, Ran last, Gen& g);
A reverse() algoritmus megfordtja az elemek sorrendjt, teht az els elem lesz az utols
stb. A reverse_copy() szoks szerint egy msolatban lltja er bemeneti sorozatnak meg-
fordtst.
A rotate() algoritmus a [first, last[ sorozatot krknt kezeli, s addig forgatja az elemeket,
mg a korbbi middle elem a sorozat elejre nem kerl. Teht az az elem, amely eddig
a first+i pozcin volt, a mvelet utn a first+ (i+ (lastmiddle)) %(last-first) helyre kerl.
A % (modulus) opertor teszi a forgatst ciklikuss az egyszer balralptets helyett:
void f()
{
string v[ ] = { "Bka", "s","Barack" };
reverse(v,v+3); // Barack s Bka
rotate(v,v+1,v+3); // s Bka Barack
}
A rotate_copy() egy msolatban lltja el bemeneti sorozatnak elforgatott vltozatt.
Alaprtelmezs szerint a random_shuffle() megkeveri a paramterben megadott sorozatot
egy egyenletes eloszls vletlenszmokat elllt eljrs segtsgvel. Teht az elemek
sorozatnak egy permutcijt (lehetsges elemsorrendjt) vlasztja ki gy, hogy mind-
egyik permutcinak ugyanakkora eslye van. Ha ms eloszlst szeretnnk elrni vagy egy-
szeren csak jobb vletlenszm-ellltnk van, akkor azt megadhatjuk a random_shuffle()
fggvnynek. A 22.7. pont Urand eljrsval pldul a kvetkezkppen keverhetjk meg
egy krtyapakli lapjait:
void f(deque<Card>& dc, My_rand& r)
{
random_shuffle(dc.begin(),dc.end(),r);
// ...
}
Az elemek thelyezst a rotate() s ms fggvnyek a swap() fggvny (18.6.8) segts-
gvel hajtjk vgre.
A standard knyvtr 716
18.6.8. Elemek felcserlse
Ha brmi rdekeset szeretnnk vgrehajtani egy trol elemeivel, akkor mindig t kell he-
lyeznnk azokat. Ezt az thelyezst legjobban teht a legegyszerbben s a leghatko-
nyabban a swap() fggvnnyel fejezhetjk ki:
template<class T> void swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
template<class For, class For2> void iter_swap(For x, For2 y);
template<class For, class For2> For2 swap_ranges(For first, For last, For2 first2)
{
while (first != last) iter_swap(first++, first2++);
return first2;
}
Ahhoz, hogy felcserljnk elemeket, egy ideiglenes trolra van szksgnk. Egyes esetek-
ben lehetnek gyes trkkk, melyekkel ez elkerlhet, de az egyszersg s rthetsg r-
dekben rdemes elkerlni az ilyesmit. A swap() algoritmus rendelkezik egyedi cl vlto-
zatokkal azokhoz a tpusokhoz, ahol erre szksg lehet (16.3.9, 13.5.2).
Az iter_swap() algoritmus bejrkkal kijellt elemeket cserl fel, a swap_ranges() pedig kt
bemeneti paramtere ltal meghatrozott tartomnya elemeit cserli fel.
18.7. Rendezett sorozatok
Miutn sszegyjtttk az adatokat, ltalban szeretnnk rendezni azokat. Ha rendezett so-
rozat ll rendelkezsnkre, sokkal tbb lehetsgnk lesz arra, hogy adatainkat knyelme-
sen kezeljk.
Egy sorozat rendezshez valahogyan ssze kell hasonltanunk az elemeket. Ehhez egy
ktparamter prediktumot (18.4.2) hasznlhatunk. Az alaprtelmezett sszehasonlt
mvelet a less (18.4.2), amely viszont alaprtelmezs szerint a < opertort hasznlja.
18. Algoritmusok s fggvnyobjektumok 717
18.7.1. Rendezs
A sort() rendez algoritmusoknak kzvetlen elrs (vletlen elrs) bejrkra (19.2.1)
van szksgk. Ebbl kvetkezik, hogy a vektorok (16.3) s az ahhoz hasonl szerkeze-
tek esetben mkdnek a leghatkonyabban:
template<class Ran> void sort(Ran first, Ran last);
template<class Ran, class Cmp> void sort(Ran first, Ran last, Cmp cmp);
template<class Ran> void stable_sort(Ran first, Ran last);
template<class Ran, class Cmp> void stable_sort(Ran first, Ran last, Cmp cmp);
A szabvnyos list (17.2.2) osztly nem biztost kzvetlen hozzfrs bejrkat, gy azokat
megfelel listamveletekkel (17.2.2.1) kell rendeznnk.
Az egyszer sort() eljrs elg hatkony tlagosan N*log(N) de a legrosszabb esetre vett
hatkonysg elg rossz: O(N*N). Szerencsre a legrosszabb eset elg ritka. Ha a leg-
rosszabb esetben is garantlt mkdsre vagy stabil rendezsre van szksgnk, hasznl-
juk a stable_sort() fggvnyt. Ennek hatkonysga N*log(N)*log(N), ami N*log(N) rtkre
javul, ha a rendszerben elg memria ll rendelkezsnkre. A stable_sort() a sort() fgg-
vnnyel ellenttben megtartja az egyenrtknek minstett elemek sorrendjt.
Bizonyos helyzetekben a rendezett sorozatnak csak els nhny elemre van szksgnk.
Ebben az esetben van rtelme annak, hogy a sorozatot csak olyan hosszon rendezzk, ami-
lyenre ppen szksgnk van. Ezt nevezzk rszleges (parcilis) rendezsnek:
template<class Ran> void partial_sort(Ran first, Ran middle, Ran last);
template<class Ran, class Cmp>
void partial_sort(Ran first, Ran middle, Ran last, Cmp cmp);
template<class In, class Ran>
Ran partial_sort_copy(In first, In last, Ran first2, Ran last2);
template<class In, class Ran, class Cmp>
Ran partial_sort_copy(In first, In last, Ran first2, Ran last2, Cmp cmp);
A partial_sort() algoritmus alapvltozata a first s a middle kztti elemeket rendezi el.
A partial_sort_copy() algoritmusok egy N elem sorozatot hoznak ltre, ahol N a bemene-
ti s kimeneti sorozat elemei szmnak minimuma. Ebbl kvetkezik, hogy meg kell ad-
nunk az eredmnysorozat elejt s vgt is, hiszen ez hatrozza meg, hny elemet kell el-
rendezni:
A standard knyvtr 718
class Compare_copies_sold {
public:
int operator()(const Book& b1, const Book& b2) const
{ return b1.copies_sold()>b2.copies_sold(); } // rendezs cskken sorrendben
};
void f(const vector<Book>& sales) // a tz legjobban fogy knyv megkerse
{
vector<Book> bestsellers(10);
partial_sort_copy(sales.begin(),sales.end(),
bestsellers.begin(),bestsellers.end(),Compare_copies_sold());
copy(bestsellers.begin(),bestsellers.end(),ostream_iterator<Book>(cout,"\n"));
}
Mivel a partial_sort_copy() kimenetnek egy vletlen elrs bejrnak kell lennie, nem
rendezhetnk egy sorozatot egyenesen a cout adatfolyamra.
Vgl nzzk azokat az algoritmusokat, melyekkel pontosan csak annyi elemet rendezhe-
tnk el, amennyi az N-edik elem helynek megllaptshoz szksges. Ezek azonnal be-
fejezik mkdsket, ha a kvnt elemet megtalltk:
template<class Ran> void nth_element(Ran first, Ran nth, Ran last);
template<class Ran, class Cmp> void nth_element(Ran first, Ran nth, Ran last, Cmp cmp);
Ez a fggvny klnsen hasznos azoknak, akiknek kzprtkre, szzalkarnyra stb. van
szksgk (mrnkknek, szociolgusoknak, tanroknak).
18.7.2. Binris keress
A sorban trtn (soros, szekvencilis) keress, amit a find() is vgez, szrnyen rossz ha-
tsfok nagy sorozatok esetben, mgis ez a legjobb megolds, ha sem rendezs, sem ha-
sts (17.6) nem ll rendelkezsnkre. Ha azonban rendezett sorozatban keresnk, akkor
annak megllaptsra, hogy egy rtk szerepel-e a sorozatban, hasznlhatjuk a binris ke-
resst:
template<class For, class T> bool binary_search(For first, For last, const T& val);
template<class For, class T, class Cmp>
bool binary_search(For first, For last, const T& value, Cmp cmp);
18. Algoritmusok s fggvnyobjektumok 719
Pldul:
void f(list<int>& c)
{
if (binary_search(c.begin(),c.end(),7)) { // Van 7 a c-ben?
// ...
}
// ...
}
A binary_search() egy logikai rtket ad vissza, jelezvn, hogy az rtk szerepel-e a soro-
zatban. Ugyangy, mint a find() esetben, itt is gyakran tudni szeretnnk, hogy az adott r-
tkkel rendelkez elem hol tallhat. Egy sorozatban azonban tbb megfelel rtk is le-
het, s neknk ltalban vagy az elsre, vagy az sszesre van szksgnk. Ezrt szerepel-
nek a standard knyvtrban azok az eljrsok, melyekkel az els (lower_bound()), az utol-
s (upper_bound()) vagy az sszes (equal_range()) megfelel (egyenrtk) elemet kiv-
laszthatjuk:
template<class For, class T> For lower_bound(For first, For last, const T& val);
template<class For, class T, class Cmp>
For lower_bound(For first, For last, const T& val, Cmp cmp);
template<class For, class T> For upper_bound(For first, For last, const T& val);
template<class For, class T, class Cmp>
For upper_bound(For first, For last, const T& val, Cmp cmp);
template<class For, class T> pair<For, For> equal_range(For first, For last, const T& val);
template<class For, class T, class Cmp>
pair<For, For> equal_range(For first, For last, const T& val, Cmp cmp);
Ezek az algoritmusok a multimap (17.4.2) mveletei kz tartoznak. A lower_bound()
fggvnyt gy kpzelhetjk el, mint a find(), illetve a find_if() gyors vltozatt rendezett
sorozatokra:
void g(vector<int>& c)
{
typedef vector<int>::iterator VI;
VI p = find(c.begin(),c.end(),7); // valsznleg lass: O(N); c-t nem kell
// rendezni
VI q = lower_bound(c.begin(),c.end(),7); // valsznleg gyors: O(log(N)); c-t
// rendezni kell
// ...
}
A standard knyvtr 720
Ha a lower_bound(first, last, k) nem tallja meg a k rtket, akkor egy olyan bejrt ad
vissza, amely az els, k-nl nagyobb kulccsal rendelkez elemre mutat, vagy a last bejrt,
ha nincs k-nl nagyobb rtk. Az upper_bound() s az equal_range() szintn ezt a hibajel-
zsi mdszert hasznlja. Ezekkel az algoritmusokkal meghatrozhatjuk, hogy hov kell az
j elemet beszrnunk, ha a sorozat rendezettsgt nem akarjuk elrontani.
18.7.3. sszefsls
Ha van kt rendezett sorozatunk, akkor a merge() segtsgvel egy j rendezett sorozatot
hozhatunk ltre bellk, az inplace_merge() fggvny felhasznlsval pedig egy sorozat
kt rszt fslhetjk ssze:
template<class In, class In2, class Out>
Out merge(In first, In last, In2 first2, In2 last2, Out res);
template<class In, class In2, class Out, class Cmp>
Out merge(In first, In last, In2 first2, In2 last2, Out res, Cmp cmp);
template<class Bi> void inplace_merge(Bi first, Bi middle, Bi last);
template<class Bi, class Cmp> void inplace_merge(Bi first, Bi middle, Bi last, Cmp cmp);
Ezek az sszefsl algoritmusok abban trnek el jelentsen a list osztly hasonl eljrsa-
itl (17.2.2.1), hogy nem trlik az elemeket a bemeneti sorozatokbl. Minden elemrl k-
ln msolat kszl.
Az egyenrtk elemek sorrendjrl azt mondhatjuk el, hogy az els sorozatban lv ele-
mek mindig megelzik a msodik sorozat azonos elemeit.
Az inplace_merge() algoritmus elssorban akkor hasznos, ha egy sorozatot tbb szempont
szerint is rendezni akarunk. Kpzeljk el pldul halaknak egy vektort, amely fajok (he-
ring, tkehal stb.) szerint rendezett. Ha a halak minden fajon bell sly szerint rendezettek,
akkor az inplace_merge() segtsgvel knnyen rendezhetjk az egsz vektort a slyok
alapjn, hiszen csak az egyes fajtk rszsorozatait kell sszefslnnk (18.13[20]).
18.7.4. Feloszts
Egy sorozat felosztsa (partition) azt jelenti, hogy minden olyan elemet, amely kielgt egy
adott felttelt, a sorozat elejre helyeznk, a felttelt ki nem elgtket pedig a vgre.
A standard knyvtrban rendelkezsnkre ll a stable_partition() fggvny, amely megtart-
ja azon elemek egymshoz viszonytott sorrendjt, melyek egyformn megfelelnek vagy
18. Algoritmusok s fggvnyobjektumok 721
egyformn nem felelnek meg a prediktumnak. A knyvtrban szerepel a partition() elj-
rs is, amely ezt a sorrendet nem rzi meg, de egy kicsit gyorsabban fut le, ha kevesebb me-
mria ll rendelkezsnkre.
template<class Bi, class Pred> Bi partition(Bi first, Bi last, Pred p);
template<class Bi, class Pred> Bi stable_partition(Bi first, Bi last, Pred p);
A felosztst gy kpzelhetjk el, mint egy nagyon egyszer felttel szerinti rendezst:
void f(list<Club>& lc)
{
list<Club>::iterator p = partition(lc.begin(),lc.end(),located_in("Kbenhavn"));
// ...
}
Ez az eljrs gy rendezi a listt, hogy a koppenhgai klubok szerepeljenek elszr.
A visszatrsi rtk (esetnkben a p) az els olyan elemet hatrozza meg, amely nem el-
gti ki a felttelt, illetve ha ilyen nincs, akkor a sorozat vgre mutat.
18.7.5. Halmazmveletek sorozatokon
A sorozatokat tekinthetjk halmaznak is. Ebbl a szempontbl viszont illik a sorozatokhoz
is elksztennk az olyan alapvet halmazmveleteket, mint az uni vagy a metszet. Ms-
rszt viszont ezek a mveletek rendkvl kltsgesek, hacsak nem rendezett sorozatokkal
dolgozunk. Ezrt a standard knyvtr halmazkezel algoritmusai csak rendezett sorozatok-
kal hasznlhatk. Termszetesen nagyon jl mkdnek a set (17.4.3) s a multiset (17.4.4)
trol esetben is, melyek szintn rendezettek.
Ha ezeket az algoritmusokat nem rendezett sorozatokra alkalmazzuk, az eredmnysoroza-
tok nem fognak megfelelni a szoksos halmazelmleti szablyoknak. A fggvnyek nem
vltoztatjk meg bemeneti sorozataikat s a kimeneti sorozat rendezett lesz.
Az includes() algoritmus azt vizsglja, hogy a msodik sorozat minden eleme (a [first2,last2[
tartomnybl) megtallhat-e az els sorozat elemei kztt (a [first, last[ tartomnyban):
template<class In, class In2>
bool includes(In first, In last, In2 first2, In2 last2);
template<class In, class In2, class Cmp>
bool includes(In first, In last, In2 first2, In2 last2, Cmp cmp);
A standard knyvtr 722
A set_union() rendezett sorozatok unijt, a set_intersection() fggvny pedig metszetket
lltja el:
template<class In, class In2, class Out>
Out set_union(In first, In last, In2 first2, In2 last2, Out res);
template<class In, class In2, class Out, class Cmp>
Out set_union(In first, In last, In2 first2, In2 last2, Out res, Cmp cmp);
template<class In, class In2, class Out>
Out set_intersection(In first, In last, In2 first2, In2 last2, Out res);
template<class In, class In2, class Out, class Cmp>
Out set_intersection(In first, In last, In2 first2, In2 last2, Out res, Cmp cmp);
A set_difference() algoritmus olyan elemek sorozatt hozza ltre, melyek az els bemeneti
sorozatban megtallhatk, de a msodikban nem. A set_symmetric_difference() algoritmus
olyan sorozatot llt el, melynek elemei a kt bemeneti sorozat kzl csak az egyikben
szerepelnek:
template<class In, class In2, class Out>
Out set_difference(In first, In last, In2 first2, In2 last2, Out res);
template<class In, class In2, class Out, class Cmp>
Out set_difference(In first, In last, In2 first2, In2 last2, Out res, Cmp cmp);
template<class In, class In2, class Out>
Out set_symmetric_difference(In first, In last, In2 first2, In2 last2, Out res);
template<class In, class In2, class Out, class Cmp>
Out set_symmetric_difference(In first, In last, In2 first2, In2 last2, Out res, Cmp cmp);
Pldul:
char v1[ ] = "abcd";
char v2[ ] = "cdef";
void f(char v3[ ])
{
set_difference(v1,v1+4,v2,v2+4,v3); // v3 = "ab"
set_symmetric_difference(v1,v1+4,v2,v2+4,v3); // v3 = "abef"
}
18. Algoritmusok s fggvnyobjektumok 723
18.8. A kupac
A kupac (halom, heap) szt klnbz helyzetekben klnbz dolgokra hasznljuk
a szmtstechnikban. Amikor algoritmusokrl beszlnk, a kupac egy sorozat elemei-
nek olyan elrendezst jelenti, melyben az els elem a sorozat legnagyobb rtk eleme.
Ebben az adatszerkezetben viszonylag gyorsan lehet elvgezni az elemek beszrst (a
push_heap() fggvny segtsgvel), illetve trlst (a pop_heap() eljrssal). Mindkett
a legrosszabb esetben is O(log(N)) hatkonysggal mkdik, ahol N a sorozat elemeinek
szma. A rendezs (a sort_heap() felhasznlsval) szintn j hatkonysg: O(N*log(N)).
A kupacot ezek a fggvnyek valstjk meg:
template<class Ran> void push_heap(Ran first, Ran last);
template<class Ran, class Cmp> void push_heap(Ran first, Ran last, Cmp cmp);
template<class Ran> void pop_heap(Ran first, Ran last);
template<class Ran, class Cmp> void pop_heap(Ran first, Ran last, Cmp cmp);
template<class Ran> void make_heap(Ran first, Ran last); // sorozat kupacc
// alaktsa
template<class Ran, class Cmp> void make_heap(Ran first, Ran last, Cmp cmp);
template<class Ran> void sort_heap(Ran first, Ran last); // kupac sorozatt
// alaktsa
template<class Ran, class Cmp> void sort_heap(Ran first, Ran last, Cmp cmp);
A kupac-algoritmusok stlusa egy kiss meglep. Termszetes megolds lenne, hogy ezt
a ngy fggvnyt egy osztlyba foglaljuk ssze. Ha ezt tennnk, a priority_queue (17.3.3)
trolhoz nagyon hasonl szerkezetet kapnnk. Valjban a priority_queue-t szinte majd-
nem biztosan egy kupac kpviseli rendszernkben.
A push_heap(first, last) ltal beillesztett elem a *(last-1). Azt felttelezzk, hogy a [first, last-
1[ tartomny mr kupac, gy a push_heap() csak kiegszti a sorozatot a [first, last[ tarto-
mnyra a kvetkez elem beszrsval. Teht egy kupacot ltrehozhatunk egy sorozatbl
gy, hogy minden elemre meghvjuk a push_heap() mveletet. A pop_heap(first, last) gy
tvoltja el a kupac els elemt, hogy felcserli azt a (*(last-1)) elemmel, majd a [first, last-
1[ tartomnyt jra kupacc alaktja.
A standard knyvtr 724
18.9. Minimum s maximum
Az itt lert algoritmusok sszehasonlts alapjn vlasztanak ki rtkeket egy sorozatbl. Na-
gyon sokszor van szksgnk arra, hogy kt rtk kzl kivlasszuk a kisebbet vagy na-
gyobbat:
template<class T> const T& max(const T& a, const T& b)
{
return (a<b) ? b : a;
}
template<class T, class Cmp> const T& max(const T& a, const T& b, Cmp cmp)
{
return (cmp(a,b)) ? b : a;
}
template<class T> const T& min(const T& a, const T& b);
template<class T, class Cmp> const T& min(const T& a, const T& b, Cmp cmp);
A min() s a max() fggvny ltalnosthat gy, hogy teljes sorozatokbl vlasszk ki
a megfelel rtket:
template<class For> For max_element(For first, For last);
template<class For, class Cmp> For max_element(For first, For last, Cmp cmp);
template<class For> For min_element(For first, For last);
template<class For, class Cmp> For min_element(For first, For last, Cmp cmp);
Vgl az bcsorrend (lexikografikus) rendezs ltalnostst is bemutatjuk, amely ka-
rakterlncok helyett tetszleges rtksorozatra ad meg sszehasonltsi felttelt:
template<class In, class In2>
bool lexicographical_compare(In first, In last, In2 first2, In2 last2);
template<class In, class In2, class Cmp>
bool lexicographical_compare(In first, In last, In2 first2, In2 last2, Cmp cmp)
{
while (first != last && first2 != last2) {
if (cmp(*first,*first2)) return true;
if (cmp(*first2++,*first++)) return false;
}
return first == last && first2 != last2;
}
18. Algoritmusok s fggvnyobjektumok 725
Ez nagyon hasonlt az ltalnos karakterlncoknl (13.4.1) bemutatott fggvnyre, de
a lexicographical_compare() tetszleges kt sorozatot kpes sszehasonltani, nem csak
karakterlncokat. Egy msik klnbsg, hogy ez az algoritmus csak egy bool s nem egy int
rtket ad vissza, br az tbb informcit tartalmazhatna. Az eredmny kizrlag akkor lesz
true, ha az els sorozat kisebb (<), mint a msodik. Teht akkor is false visszatrsi rtket
kapunk, ha a kt sorozat egyenl.
A C stlus karakterlncok s a string osztly is sorozat, gy a lexicographical_compare()
hasznlhat ezekre is sszehasonlt felttelknt:
char v1[ ] = "igen";
char v2[ ] = "nem";
string s1 = "Igen";
string s2 = "Nem";
void f()
{
bool b1 = lexicographical_compare(v1,v1+strlen(v1),v2,v2+strlen(v2));
bool b2 = lexicographical_compare(s1.begin(),s1.end(),s2.begin(),s2.end());
bool b3 = lexicographical_compare(v1,v1+strlen(v1),s1.begin(),s1.end());
bool b4 = lexicographical_compare(s1.begin(),s1.end(),v1,v1+strlen(v1),Nocase());
}
A sorozatoknak nem kell azonos tpusaknak lennik, hiszen csak elemeket kell tudnunk
sszehasonltani, s az sszehasonlt prediktumot mi adhatjuk meg. Ez a lehetsg
a lexicographical_compare() eljrst sokkal ltalnosabb s egy kicsit lassabb teszi
a string osztly sszehasonlt mveleteinl. Lsd mg: 20.3.8.
18.10. Permutcik
Egy ngy elembl ll sorozatot sszesen 4*3*2 flekppen rendezhetnk el. Mindegyik
elrendezs egy-egy permutcija a ngy elemnek. Ngy karakterbl (abcd) pldul az
albbi 24 permutcit llthatjuk el:
abcd abdc acbd acdb adbc adcb bacd badc
bcad bcda bdac bdca cabd cadb cbad cbda
cdab cdba dabc dacb dbac dbca dcab dcba
A standard knyvtr 726
A next_permutation() s a prev_permutation() fggvny ilyen permutcikat llt el egy
sorozatbl:
template<class Bi> bool next_permutation(Bi first, Bi last);
template<class Bi, class Cmp> bool next_permutation(Bi first, Bi last, Cmp cmp);
template<class Bi> bool prev_permutation(Bi first, Bi last);
template<class Bi, class Cmp> bool prev_permutation(Bi first, Bi last, Cmp cmp);
Az abcd sorozat permutciit a kvetkezkppen rathatjuk ki:
int main()
{
char v[ ] = "abcd";
cout << v << '\t';
while(next_permutation(v,v+4)) cout << v << '\t';
}
A permutcikat bcsorrendben kapjuk meg (18.9). A next_permutation() fggvny
visszatrsi rtke azt hatrozza meg, hogy van-e mg tovbbi permutci. Ha nincs, false
rtket kapunk s azt a permutcit, melyben az elemek bcsorrendben llnak.
A prev_permutation() fggvny visszatrsi rtke azt adja meg, hogy van-e elz permu-
tci, s ha nincs, akkor az elemeket fordtott bcsorrendben tartalmaz permutcit
kapjuk.
18.11. C stlus algoritmusok
A C++ standard knyvtra rklt nhny algoritmust a C standard knyvtrtl is. Ezek a C
stlus karakterlncokat kezel fggvnyek (20.4.1), illetve a gyorsrendezs (quicksort) s
a binris keress, melyek csak tmbkre hasznlhatk.
A qsort() s a bsearch() fggvny a <cstdlib> s az <stdlib.h> fejllomnyban tallhat meg.
Mindkett tmbkn mkdik s n darab elem_size mret elemet dolgoznak fel egy fgg-
vnyre hivatkoz mutatval megadott kisebb, mint sszehasonlt mvelet segtsgvel.
Az elemek tpusnak nem lehet felhasznl ltal megadott msol konstruktora, msol r-
tkadsa, illetve destruktora:
18. Algoritmusok s fggvnyobjektumok 727
typedef int(*__cmp)(const void*, const void*); // a typedef-et csak a bemutatshoz
// hasznljuk
void qsort(void* p, size_t n, size_t elem_size, __cmp); // p rendezse
void* bsearch(const void* key, void* p, size_t n, size_t elem_size, __cmp); // kulcs keresse
// p-ben
A qsort() fggvny hasznlatrl a 7.7 pontban mr rszletesebben is sz volt.
Ezek az algoritmusok kizrlag a C-vel val kompatibilits miatt maradtak meg a nyelvben.
A sort() (18.7.1) s a search() (18.5.5) sokkal ltalnosabbak s ltalban sokkal hatko-
nyabbak is.
18.12. Tancsok
[1] Ciklusok helyett hasznljunk inkbb algoritmusokat (18.5.1).
[2] Ha ciklust runk, mindig gondoljuk vgig, hogy feladatunk nem fogalmazhat-e
meg egy ltalnos algoritmussal (18.2).
[3] Rendszeresen nzzk t az algoritmusokat, hogy felismerjk, ha valamilyen fel-
adat trivilis (18.2).
[4] Mindig ellenrizzk, hogy az ltalunk megadott bejr-pr tnyleg meghatroz-e
egy sorozatot (18.3.1).
[5] Tervezzk gy programjainkat, hogy a leggyakrabban hasznlt eljrsok legye-
nek a legegyszerbbek s leggyorsabbak (18.3, 18.3.1).
[6] Az rtkvizsglatokat gy fogalmazzuk meg, hogy prediktumknt is hasznl-
hassuk azokat (18.4.2).
[7] Mindig gondoljunk arra, hogy a prediktumok fggvnyek vagy objektumok,
de semmikppen sem tpusok (18.4.2).
[8] A lektk (binder) segtsgvel a ktparamter prediktumokbl egyparam-
ter prediktumokat llthatunk el (18.4.4.1).
[9] A mem_fun() s a mem_fun_ref() fggvny segt abban, hogy az algoritmuso-
kat trolkra alkalmazzuk (18.4.4.2).
[10] Ha egy fggvny valamelyik paramtert rgztennk kell, hasznljuk
a ptr_fun() fggvnyt.
[11] A strcmp() legnagyobb eltrse az == opertortl, hogy 0 visszatrsi rtk jelzi
az egyenlsget (18.4.4.4).
A standard knyvtr 728
[12] A for_each() s a transform() algoritmust csak akkor hasznljuk, ha nem tal-
lunk feladatunkhoz jobban illeszked eljrst (18.5.1).
[13] Hasznljunk prediktumokat, ha egy algoritmusban egyedi sszehasonltsi fel-
ttelre van szksgnk (184.2.1, 18.6.3.1).
[14] A prediktumok s ms fggvnyobjektumok segtsgvel a szabvnyos algorit-
musok sokkal tbb terleten hasznlhatk, mint els rnzsre gondolnnk
(18.4.2).
[15] A mutatk esetben az alaprtelmezett == s < opertor igen ritkn felel meg
ignyeinknek a szabvnyos algoritmusokban (18.6.3.1).
[16] Az algoritmusok nem tudnak kzvetlenl beilleszteni vagy kivenni elemet a pa-
ramterknt megadott sorozatokbl.
[17] Mindig ellenrizzk, hogy a sorozatok sszehasonltsakor a megfelel kisebb,
mint, illetve egyenlsg mveletet hasznljuk-e (18.6.3.1).
[18] A rendezett sorozatok gyakran hatkonyabb s elegnsabb teszik
programjainkat.
[19] A qsort() s a bsearch() fggvnyeket csak a C programokkal val sszeegyez-
tethetsg biztostsa rdekben hasznljuk (18.11).
18.13. Gyakorlatok
A fejezetben elfordul gyakorlatok tbbsgnek megoldst megtallhatjuk a standard
knyvtr brmely vltozatnak forrskdjban, de mieltt megnznnk, hogy a knyvtr al-
koti hogyan kzeltettk meg a problmt, prblkozzunk sajt megoldsok kidolgozsval.
1. (*2)Ismerkedjnk az O( ) jellssel. Adjunk megvalsthat pldt arra, amikor
N>10 rtkre egy O(N*N) algoritmus gyorsabb, mint egy O(N) algoritmus.
2. (*2)Ksztsk el s ellenrizzk a ngy mem_fun(), illetve mem_fun_ref() fgg-
vnyt (18.4.4.2)
3. (*1)rjuk meg a match() algoritmust, amely hasonlt a mismatch() fggvnyre,
csak ppen azoknak az elemeknek a bejrjt adja vissza, amelyek elsknt
elgtik ki a prediktumot.
4. (*1.5)rjuk meg s prbljuk ki a 18.5.1 pontban emltett Print_name()
fggvnyt.
5. (*1)Rendezznk egy listt a standard knyvtr algoritmusaival.
6. (*2.5)rjuk meg az iseq() fggvnynek (18.3.1) azon vltozatait, amelyek bep-
tett tmbkre, istream tpusokra, illetve bejr-prokra hasznlhatk. Adjuk
meg a szksges tlterhelseket is a standard knyvtr nem mdost algorit-
18. Algoritmusok s fggvnyobjektumok 729
musainak (18.5) szmra. Gondolkodjunk el rajta, hogyan kerlhetek el
a tbbrtelmsgek, s hogyan kerlhetjk el tl nagy szm sablon fggvny
ltrehozst.
7. (*2)Hatrozzuk meg az iseq() komplementert, az oseq() fggvnyt. A kimeneti
sorozatot, amelyet az oseq()paramterknt kap, t kell alaktanunk az algorit-
mus ltal visszaadott kimenetre. Adjuk meg a szksges tlterhelseket is, leg-
albb hrom, ltalunk kivlasztott szabvnyos algoritmusra.
8. (*1.5)Ksztsnk egy vektort, amely 1-tl 100-ig trolja a szmok ngyzeteit. Je-
lentsk meg a ngyzetszmokat egy tblzatban. Szmtsuk ki ezen vektor ele-
meinek ngyzetgykt s jelentsk meg az gy kapott eredmnyeket is.
9. (*2)rjuk meg azokat a fggvnyobjektumokat, melyek bitenknti logikai mve-
leteket vgeznek operandusaikon. Prbljuk ki ezeket az objektumokat olyan
vektorokon, melyek elemeinek tpusa char, int, illetve bitset<67>.
10. (*1)rjuk meg a binder3() eljrst, amely rgzti egy hromparamter fggvny
msodik s harmadik paramtert, s gy llt el belle egyparamter predi-
ktumot. Adjunk pldt olyan esetre, ahol a binder3() hasznos lehet.
11. (*1.5)rjunk egy rvid programot, amely amely eltvoltja egy fjlbl fjlbl
a szomszdos szomszdos ismtld szavakat. (A programnak az elz mondat-
bl pldul el kell tvoltania az amely, a fjlbl s a szomszdos sz egy-egy
elfordulst.)
12. (*2.5)Hozzunk ltre egy tpust, amellyel jsgokra s knyvekre mutat hivat-
kozsok rekordjait trolhatjuk egy fjlban. rjunk olyan programot, amely ki
tudja rni a fjlbl azokat a rekordokat, amelyeknek megadjuk a kiadsi vt,
a szerz nevt, egy, a cmben szerepl kulcsszavt, vagy a kiad nevt. Adjunk
lehetsget arra is, hogy a felhasznl az eredmnyeket klnbz szempontok
szerint rendezhesse.
13. (*2)Ksztsk el a move() algoritmust a copy() stlusban, gondolva arra, hogy
a bemeneti s kimeneti sorozat esetleg tfedik egymst. Gondoskodjunk az elj-
rs megfelel hatkonysgrl arra az esetre, ha paramterekknt kzvetlen
hozzfrs bejrkat kapnnk.
14. (*1.5)lltsuk el a food sz anagrammit, azaz az f, o, o s d betk ngybets
kombinciit. ltalnostsuk ezt a programot gy, hogy egy felhasznltl be-
krt sz anagrammit lltsa el.
15. (*1.5)Ksztsnk programot, amely mondatok anagrammit lltja el, azaz
a mondat szavainak minden permutcijt elkszti. (Ne a szavak betinek per-
mutciival foglalkozzunk!)
16. (*1.5)rjuk meg a find_if() (18.5.2) fggvnyt, majd ennek felhasznlsval
a find() algoritmust. Talljunk ki olyan megoldst, hogy a kt fggvnynek ne
kelljen klnbz nevet adnunk.
A standard knyvtr 730
17. (*2)Ksztsk el a search() (18.5.5) algoritmust. Ksztsnk egy kifejezetten
kzvetlen hozzfrs bejrkhoz igaztott vltozatot is.
18. (*2)Vlasszunk egy rendezsi eljrst (pldul a qsort()-ot a standard knyvtr-
bl vagy a 13.5.2 pontbl a Shell rendezst) s alaktsuk t gy, hogy minden
elemcsere utn jelenjen meg a sorozat aktulis llapota.
19. (*2)A ktirny bejrkhoz nem ll rendelkezsnkre rendez eljrs. Sejt-
snk az, hogy gyorsabb az elemeket tmsolni egy vektorba, s ott rendezni
azokat, mint ktirny bejrkkal kzvetlenl rendezni a sorozatot. Ksztsnk
egy ltalnos rendez eljrst ktirny bejrkkal s ellenrizzk a feltte-
lezst.
20. (*2.5)Kpzeljk el, hogy sporthorgszok egy csoportjnak rekordjait tartjuk
nyilvn. Minden fogs esetben troljuk a hal fajtjt, hosszt, slyt, a fogs
idpontjt, dtumt, a horgsz nevt stb. Rendezzk a rekordokat klnbz
szempontok szerint az inplace_merge() algoritmus felhasznlsval.
21. (*2)Ksztsnk listt azokrl a tanulkrl, akik matematikt, angolt, szmts-
technikt vagy biolgit tanulnak. Mindegyik trgyhoz vlasszunk ki 20 nevet
egy 40 fs osztlybl. Soroljuk fel azokat a tanulkat, akik matematikt s sz-
mtstechnikt is tanulnak. Vlasszuk ki azokat, akik tanulnak szmtstechni-
kt, de matematikt s biolgit nem. rassuk ki azokat, akik tanulnak szmts-
technikt s matematikt, de nem tanulnak sem angolt, sem biolgit.
22. (*1.5)Ksztsnk egy remove() fggvnyt, amely tnyleg eltvoltja a trlend
elemeket egy trolbl.
18. Algoritmusok s fggvnyobjektumok 731
Bejrk s memriafoglalk
Annak oka, hogy az adatszerkezetek s
az algoritmusok ilyen tkletesen egytt tudnak
mkdni az, hogy semmit sem tudnak egymsrl.
(Alex Stepanov)
Bejrk s sorozatok Mveletek bejrkon Bejr-jellemzk Bejr-kategrik Besz-
rk Visszafel halad bejrk Adatfolyam-bejrk Ellenrztt bejrk A kivtelek s
az algoritmusok Memriafoglalk A szabvnyos allocator Felhasznli memriafoglalk
Alacsonyszint memriamveletek Tancsok Gyakorlatok
19
19.1. Bevezets
A bejr (itertor, iterator) az a csavar, amely a trolkat s az algoritmusokat sszetartja.
Ezek segtsgvel az adatokat elvont formban rhetjk el, gy az algoritmusok ksztinek
nem kell rengeteg adatszerkezet konkrt rszleteivel foglalkozniuk. A msik oldalrl nzve
pedig a bejrk ltal knlt szabvnyos adatelrsi modell megkmli a trolkat attl, hogy
sokkal bvebb adatelrsi mvelethalmazt kelljen biztostaniuk. A memriafoglalk (allo-
ktor, allocator) ugyangy szigetelik el a trolk konkrt megvalstsait a memriaelrs
rszleteitl.
A bejrk elvont adatmodellje az objektumsorozat (19.2). A memriafoglalk azt teszik le-
hetv, hogy az alacsonyszint bjttmb adatmodell helyett a sokkal magasabb szint ob-
jektummodellt hasznljuk (19.4) A leggyakoribb alacsonyszint memriamodell hasznla-
tt nhny szabvnyos fggvny tmogatja (19.4.4).
A bejrk olyan fogalmat kpviselnek, amellyel bizonyra minden programoz tisztban
van. Ezzel ellenttben a memriafoglalk olyan eljrsokat biztostanak, amelyekkel egy
programoznak igen ritkn kell foglalkoznia, s igen kevs olyan programoz van, akinek
letben akr csak egyszer is memriafoglalt kellene rnia.
19.2. Bejrk s sorozatok
A bejr (itertor, iterator) tisztn elvont fogalom. Azt mondhatjuk, hogy minden, ami be-
jrknt viselkedik, bejrnak tekinthet (3.8.2). A bejr a sorozat elemeire hivatkoz
mutat fogalmnak elvont brzolsa. Legfontosabb fogalmai a kvetkezk:
az ppen kijellt elem (indirekci, jele a * s a ->)
a kvetkez elem kivlasztsa (nvels, jele a ++)
egyenlsg (jele az ==)
A beptett int* tpus pldul az int[ ]-nek, mg a list<int>::iterator egy listaosztlynak
a bejrja.
A standard knyvtr 734
A sorozat (sequence) is elvont fogalom: olyan valamit jell, amelyen vgighaladhatunk az
elejtl a vgig, a kvetkez elem mvelettel:
Ilyen sorozat a tmb (5.2), a vektor (16.3), az egyirny (lncolt) lista (17.8[17]), a ktir-
ny (lncolt) lista (17.2.2), a fa (17.4.1), a bemeneti sorozat (21.3.1) s a kimeneti soro-
zat (21.2.1). Mindegyiknek megvan a sajt, megfelel bejrja.
A bejr-osztlyok s -fggvnyek az std nvtrhez tartoznak s az <iterator> fejllomny-
ban tallhatk.
A bejr nem ltalnostott mutat. Sokkal inkbb a tmbre alkalmazott mutat elvont br-
zolsa. Nincs olyan fogalom, hogy null-bejr (teht elemre nem mutat bejr). Ha meg
akarjuk llaptani, hogy egy bejr ltez elemre mutat-e vagy sem, ltalban a sorozat end
elemhez kell hasonltanunk (teht nem valamilyen null elemhez). Ez a szemllet sok algo-
ritmust leegyszerst, mert nem kell az emltett egyedi esettel foglalkozniuk, radsul tk-
letesen megvalsthatk brmilyen tpus elemek sorozatra.
Ha egy bejr ltez elemre mutat, akkor rvnyesnek (valid) nevezzk s alkalmazhat r
az indirekci (a *, a [ ] vagy a -> opertor). Egy bejr rvnytelensgnek tbb oka is lehet:
mg nem adtunk neki kezdrtket; olyan trolba mutat, amelynek mrete megvltozott
(16.3.6., 16.3.8); teljes egszben trltk a trolt, amelybe mutat; vagy a sorozat vg-
re mutat (18.2). A sorozat vgt gy kpzelhetjk el, mint egy bejrt, amely egy olyan
kpzeletbeli elemre mutat, melynek helye a sorozatban az utols elem utn van.
19.2.1. A bejrk mveletei
Nem minden bejr (iterator) engedi meg pontosan ugyanannak a mvelethalmaznak
a hasznlatt. Az olvasshoz pldul msfajta mveletekre van szksg, mint az rshoz,
a vektorok pedig lehetv teszik, hogy brmikor brmelyik elemet knyelmesen s hatko-
nyan elrhessk, mg a listknl s adatfolyamoknl ez a mvelet rendkvl kltsges. Ezrt
a bejrkat t osztlyba soroljuk, aszerint, hogy milyen mveleteket kpesek hatkonyan
(azaz lland (konstans) id alatt, 17.1) megvalstani:
19. Bejrk s memriafoglalk 735
elem[0] elem[1] elem[2] ... elem[n-1]
begin() end()
Az rs s az olvass is a * indirekci opertorral lekpezett bejrn keresztl trtnik:
*p = x; // x rsa p-n keresztl
x = *p; // olvass p-n keresztl x-be
Ahhoz, hogy egy tpust bejrnak nevezznk, biztostania kell a megfelel mveleteket.
Ezeknek a mveleteknek pedig a hagyomnyos jelents szerint kell viselkednik, teht
ugyanazt az eredmnyt kell adniuk, mint a szoksos mutatknak.
Egy bejrnak kategrijtl fggetlenl lehetv kell tennie const s nem konstans el-
rst is ahhoz az objektumhoz, amelyre mutat. Egy elemet nem vltoztathatunk meg egy
const bejrn keresztl, legyen az brmilyen kategrij. A bejr knl bizonyos operto-
rokat, de a mutatott elem tpusa hatrozza meg, hogy mely mveleteket lehet tnylegesen
vgrehajtani.
Az olvass s az rs vgrehajtshoz az objektumok msolsra van szksg, gy ilyenkor
az elemek tpusnak a szoksos msolst kell biztostaniuk (17.1.4).
Csak kzvetlen elrs (random access) bejrkat nvelhetnk, illetve cskkenthetnk
tetszleges egsz rtkkel a relatv cmzs megvalstshoz. A kimeneti (output) bejrk
kivtelvel azonban kt bejr kztti tvolsg mindig megllapthat gy, hogy vgigha-
ladunk a kzttk lv elemeken, gy a distance() fggvny megvalsthat:
template<class In> typename iterator_traits<In>::difference_type distance(In first, In last)
{
typename iterator_traits<In>::difference_type d = 0;
while (first++!=last) d++;
return d;
}
A standard knyvtr 736
Bejr-mveletek s -kategrik
Kategria: r olvas elre ktirny kzvetlen
(kimeneti) (bemeneti) halad elrs
Rvidts: Out In For Bi Ran
Olvass: =*p =*p =*p =*p
Hozzfrs: -> -> -> -> [ ]
rs: *p= *p= *p= *p=
Halads: ++ ++ ++ ++ -- ++ -- + - += -=
sszehasonlts: == != == != == != == != < > <= >=
Az iterator_traits<In>::difference_type minden bemeneti (input) bejrhoz rendelkezsre
ll, hogy kt elem kztti tvolsgot trolhassunk (19.2.2).
A fggvny neve distance() s nem operator-(), mert elg kltsges mvelet lehet s
a bejrk esetben az opertorokat fenntartjuk azoknak az eljrsoknak, melyek konstans
id alatt elvgezhetk (17.1). Az elemek egyesvel trtn megszmllsa nem olyan m-
velet, amelyet nagy sorozatokon jhiszemen vgre szeretnnk hajtani. A kzvetlen elr-
s bejrkhoz a standard knyvtr a distance() fggvny sokkal hatkonyabb megvals-
tst biztostja.
Hasonlan, az advance() fggvny szolgl a += opertor lass, de mindenhol alkalmazha-
t vltozatnak megnevezsre:
template <class In, class Dist> void advance(In& i, Dist n); // i+=n
19.2.2. A bejr jellemzi
A bejrkat arra hasznljuk, hogy informcikat tudjunk meg az objektumrl, amelyre mu-
tatnak, illetve a sorozatrl, amelyhez kapcsoldnak. A bejr hivatkozsn keresztl kapott
objektumot pldul talakthatjuk, vagy megllapthatjuk a sorozatban szerepl elemek
szmt a sorozatot kijell bejrk segtsgvel. Az ilyen mveletek kifejezshez kpes-
nek kell lennnk megnevezni a bejrkhoz kapcsold tpusokat. Pldul azon objektum
tpusa, melyre a bejr mutat, vagy kt bejr kztti tvolsg tpusa. Ezeket a tpusokat
egy bejr szmra az iterator_traits (bejr jellemzk) sablon osztly rja le:
template<class Iter> struct iterator_traits {
typedef typename Iter::iterator_category iterator_category; // 19.2.3
typedef typename Iter::value_type value_type; // az elem tpusa
typedef typename Iter::difference_type difference_type;
typedef typename Iter::pointer pointer; // a ->() opertor visszatrsi tpusa
typedef typename Iter::reference reference; // a *() opertor visszatrsi tpusa
};
A difference_type kt bejr kztti tvolsg megjellsre szolgl tpus, az
iterator_category pedig olyan, amellyel lerhat, hogy a bejr milyen mveleteket tmo-
gat. A szoksos mutatk esetben egy-egy egyedi cl vltozat (specializci, 13.5) ll ren-
delkezsnkre a <T*> s a <const T*> tpushoz:
19. Bejrk s memriafoglalk 737
template<class T> struct iterator_traits<T*> { // specializci mutatkhoz
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
Teht a mutatk kzvetlen elrst (vletlen elrst, random-access, 19.2.3) tesznek lehet-
v, s a kzttk lv tvolsgot a standard knyvtr ptrdiff_t tpusval brzolhatjuk, ame-
lyet a <cstddef> (6.2.1) fejllomnyban tallhatunk meg. Az iterator_traits osztly segts-
gvel olyan eljrsokat rhatunk, amelyek a bejrk paramtereinek jellemzitl fggnek.
Erre klasszikus plda a count() fggvny:
template<class In, class T>
typename iterator_traits<In>::difference_type count(In first, In last, const T& val)
{
typename iterator_traits<In>::difference_type res = 0;
while (first != last) if (*first++ == val) ++res;
return res;
}
Itt az eredmny tpust az iterator_traits<In> osztlynak fogalmaival hatroztuk meg. Er-
re a megoldsra azrt van szksg, mert nincs olyan nyelvi elem, amellyel egy tetszleges
tpust egy msik tpus fogalmaival fejezhetnnk ki.
Mutatk esetben az iterator_traits osztly hasznlata helyett specializcit kszthetnnk
a count() fggvnybl:
template<class In, class T>
typename In::difference_type count(In first, In last, const T& val);
template<class In, class T> ptrdiff_t count<T*,T>(T* first, T* last, const T& val);
Ez a megolds azonban csak a count() problmjn segtene. Ha a mdszert egy tucat al-
goritmushoz hasznltuk volna, a tvolsgok tpusnak informciit tucatszor kellene meg-
ismtelnnk. ltalban sokkal jobb megkzelts, ha egy tervezsi dntst egyszer, egy he-
lyen runk le (23.4.2); gy ha ezt a dntst ksbb knytelenek vagyunk megvltoztatni,
csak erre az egy helyre kell figyelnnk.
A standard knyvtr 738
Mivel az itrator_traits<Iterator> minden bejr esetben definilt, j bejr ksztsekor min-
dig ltre kell hoznunk a httrben egy iterator_traits osztlyt is. Ha az alaprtelmezett osztly
amely az iterator_traits sablon pldnya nem felel meg ignyeinknek, szabadon kszthe-
tnk belle specializlt vltozatot, gy, ahogy a standard knyvtr teszi a mutatk esetben.
Az automatikusan ltrehozott iterator_traits osztly felttelezi, hogy a bejr-osztlyban kifej-
tettk a difference_type, value_type stb. tagtpusokat. Az <iterator> fejllomnyban a knyv-
tr megad egy tpust, melyet felhasznlhatunk a tagtpusok ltrehozshoz:
template<class Cat, class T, class Dist = ptrdiff_t, class Ptr = T*, class Ref = T&>
struct iterator {
typedef Cat iterator_category; // 19.2.3
typedef T value_type; // az elem tpusa
typedef Dist difference_type; // a bejr-klnbsg tpusa
typedef Ptr pointer; // a -> visszatrsi tpusa
typedef Ref reference; // a * visszatrsi tpusa
};
Jegyezzk meg, hogy itt a reference s a pointer nem bejrk, csak bizonyos bejrk ese-
tben egyszer visszatrsi rtkknt szolglnak az operator*(), illetve az operator->() m-
velethez.
Az iterator_traits osztly szmos olyan fellet elksztst leegyszersti, amely bejrkkal
dolgozik, s sok algoritmus esetben hatkony megoldsokat tesz lehetv.
19.2.3. Bejr-kategrik
A bejrk klnbz fajti (kategrii) hierarchikus rend szerint csoportostottak:
19. Bejrk s memriafoglalk 739
Bemeneti
(Input)
Kimeneti
(Output)
Elre halad
(Forward)
Ktirny
(Bidirectional)
Kzvetlen lrs
(Random-access)
Itt nem osztlyok leszrmazsi hierarchijrl van sz. A bejrk kategrii csak egy csopor-
tostst jelentenek a tpus ltal knlt mveletek alapjn. Nagyon sok, egymstl teljesen fg-
getlen tpus tartozhat ugyanahhoz a bejr-kategrihoz. A kznsges mutatk (19.2.2)
vagy a Checked_iters (19.3) pldul egyarnt kzvetlen elrs bejrk.
A 18. fejezetben mr felhvtuk r a figyelmet, hogy a klnbz algoritmusok klnbz
fajtj bejrkat hasznlnak paramterknt. St, mg ugyanannak az algoritmusnak is lehet
tbb vltozata, klnbz bejr-fajtkkal, hogy mindig a leghatkonyabb megoldst hasz-
nlhassuk. Annak rdekben, hogy a bejr-kategrik alapjn knnyen tlterhelhessnk
fggvnyeket, a standard knyvtr t osztlyt knl, amelyek az t bejr-kategrit br-
zoljk:
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
Ha megnzzk a bemeneti vagy az elre halad bejrk utastskszlett (19.2.1), akkor
azt vrhatnnk, hogy a forward_iterator_tag az output_iterator_tag osztly leszrmazottja
legyen, st az input_iterator_tag- is. A knyvtrban mgsem ez a megolds szerepel, ami-
nek az az oka, hogy ez tl zavaross s valsznleg hibss teheti a rendszert. Ennek elle-
nre mr lttam olyan megvalstst, ahol az ilyen szrmaztats tnylegesen leegyszers-
tette a programkdot.
Ezen osztlyok egymsbl val szrmaztatsnak egyetlen elnye, hogy fggvnyeinknek
nem kell tbb vltozatt elksztennk, ha ugyanazt az algoritmust szeretnnk alkalmazha-
tv tenni tbb, de nem az sszes bejrra. Pldul gondoljuk vgig, hogyan valsthat
meg a distance:
template<class In>
typename iterator_traits<In>::difference_type distance(In first, In last);
Kt nyilvnval lehetsgnk van:
1. Ha az In kzvetlen elrs bejr, egyszeren kivonhatjuk a first rtket a last
rtkbl.
2. Ellenkez esetben a tvolsg megllaptshoz kln bejrval vgig kell halad-
nunk a sorozaton a first elemtl a last rtkig.
Ezt a kt vltozatot kt segdfggvny hasznlatval fejezhetjk ki:
A standard knyvtr 740
template<class In>
typename iterator_traits<In>::difference_type
dist_helper(In first, In last, input_iterator_tag)
{
typename iterator_traits<In>::difference_type d = 0;
while (first++!=last) d++; // csak nvels
return d;
}
template<class Ran>
typename iterator_traits<Ran>::difference_type
dist_helper(Ran first, Ran last, random_access_iterator_tag)
{
return last-first; // vletlen elrsre tmaszkodik
}
A bejr kategrijt jelz paramter pontosan meghatrozza, milyen bejrra van szksg,
s msra nem is hasznljuk a fggvnyben, csak arra, hogy a tlterhelt fggvny megfele-
l vltozatt kivlasszuk. A paramterre a szmtsok elvgzshez nincs szksg. Ez
a megolds tisztn fordtsi idej vlasztst tesz lehetv, s amellett, hogy automatikusan
kivlasztja a megfelel segdfggvnyt, mg tpusellenrzst is vgez (13.2.5).
Ezutn a distance() fggvny megrsa mr nagyon egyszer, csak a megfelel segdfgg-
vnyt kell meghvnunk:
template<class In>
typename iterator_traits<In>::difference_type distance(In first, In last)
{
return dist_helper(first,last,iterator_traits<In>::iterator_category());
}
Egy dist_helper() fggvny meghvshoz a megadott iterator_traits<In>::iterator_category
osztlynak vagy input_iterator_tag, vagy random_access-iterator_tag tpusnak kell len-
nie. Ennek ellenre nincs szksg kln dist_helper() fggvnyre az elre halad (forward)
s a ktirny (bidirectional) bejrk esetben, mert a szrmaztatsnak ksznheten
a dist_helper() fggvny azon vltozata, amely input_iterator_tag paramtert vr, ezeket is
kezeli. Az output_iterator_tag tpust fogad vltozat hinya pedig pontosan azt fejezi ki,
hogy a distance() fggvny nem rtelmezhet kimeneti (output) bejrkra:
void f(vector<int>& vi,
list<double>& ld,
istream_iterator<string>& is1, istream_iterator<string>& is2,
ostream_iterator<char>& os1, ostream_iterator<char>& os2)
{
19. Bejrk s memriafoglalk 741
distance(vi.begin(),vi.end()); // kivon algoritmus hasznlata
distance(ld.begin(),ld.end()); // nvel algoritmus hasznlata
distance(is1,is2); // nvel algoritmus hasznlata
distance(os1,os2); // hiba: rossz bejr-kategria,
// a dist_helper() paramternek tpusa
// nem megfelel
}
A distance() fggvny meghvsa egy istream_iterator osztlyra mkdik, de valsznleg
teljesen rtelmetlen egy vals programban, hiszen vgigolvassuk vele a bemenetet, de min-
den elemet azonnal el is dobunk, s az eldobott elemek szmt adjuk vissza.
Az iterator_traits<T>::iterator_category lehetv teszi, hogy a programoz olyan vltozato-
kat rjon egy algoritmushoz, melyek kzl mindig automatikusan a megfelel kerl vgre-
hajtsra, amikor a megvalstssal egyltaln nem foglalkoz felhasznl valamilyen adat-
szerkezettel meghvja a fggvnyt. Ms szval elrejthetjk a megvalsts rszleteit egy egy-
sges fellet mgtt, a helyben kifejtett (inline) fggvnyek hasznlata pedig biztostja,
hogy ez az elegancia nem megy a futsi idej hatkonysg rovsra.
19.2.4. Beszr bejrk
Ha egy trolba egy bejr segtsgvel rni akarunk, fel kell kszlnnk arra, hogy a bejr
ltal kijellt elemtl kezdve a trol megvltozik. Ennek kvetkeztben tlcsorduls s gy
hibs memriars is bekvetkezhet:
void f(vector<int>& vi)
{
fill_n(vi.begin(),200,7); // 7 rtkl adsa vi[0]..[199]-nek
}
Ha a vi trolnak 200-nl kevesebb eleme van, ez az eljrs nagy bajokat okozhat.
Az <iterator> fejllomnyban a standard knyvtr ler hrom sablon osztlyt, amely ezzel
a problmval foglalkozik, knyelmes hasznlatukhoz pedig hrom fggvny is rendelke-
zsnkre ll:
template <class Cont> back_insert_iterator<Cont> back_inserter(Cont& c);
template <class Cont> front_insert_iterator<Cont> front_inserter(Cont& c);
template <class Cont, class Out> insert_iterator<Cont> inserter(Cont& c, Out p);
A standard knyvtr 742
A back_inserter() arra szolgl, hogy elemeket adjunk egy trol vghez, a front_inserter()
segtsgvel a sorozat elejre helyezhetnk elemeket, mg az egyszer inserter() a param-
terknt megadott bejr el szrja be az elemeket. ppen ezrt az inserter(c,p) utastsban
a p-nek rvnyes bejrnak kell lennie a c trolban. Termszetesen a trol mrete mindig
megn, amikor egy beszr bejr segtsgvel rtket runk bele.
A beszr bejrk (inserter) nem rnak fell ltez elemeket, hanem jakat szrnak be
a push_back(), a push_front(), illetve az insert() (16.3.6) utastssal:
void g(vector<int>& vi)
{
fill_n(back_inserter(vi),200,7); // 200 darab 7-es hozzadsa vi vghez
}
A beszr bejrk nem csak hasznosak, hanem egyszerek s hatkonyak is:
template <class Cont>
class insert_iterator : public iterator<output_iterator_tag,void,void,void,void> {
protected:
Cont& container; // a cltrol, amelybe beszrunk
typename Cont::iterator iter; // a trolba mutat
public:
explicit insert_iterator(Cont& x, typename Cont::iterator i)
: container(x), iter(i) {}
insert_iterator& operator=(const typename Cont::value_type& val)
{
iter = container.insert(iter,val);
++iter;
return *this;
}
insert_iterator& operator*() { return *this; }
insert_iterator& operator++() { return *this; } // prefix ++
insert_iterator operator++(int) { return *this; } // postfix ++
};
Teht a beszrk valjban kimeneti (output) bejrk.
Az insert_iterator a kimeneti sorozatok klnleges esete. A 18.3.1 pontban bevezetett iseq
fggvnyhez hasonlan megrhatjuk a kvetkezt:
19. Bejrk s memriafoglalk 743
template<class Cont>
insert_iterator<Cont>
oseq(Cont& c, typename Cont::iterator first, typename Cont::iterator last)
{
return insert_iterator<Cont>(c,c.erase(first,last)); // az erase magyarzatt lsd 16.3.6
}
Teht a kimeneti sorozat trli a rgi elemeket, majd az aktulis eredmnnyel helyettesti
azokat:
void f(list<int>& li,vector<int>& vi) // vi msodik felnek helyettestse li msolatval
{
copy(li.begin(),li.end(),oseq(vi,vi+vi.size()/2,vi.end()));
}
Az oseq() fggvnynek t kell adnunk paramterknt magt a trolt is, mert csak bejrk
segtsgvel nem lehet cskkenteni egy trol mrett (18.6., 18.6.3).
19.2.5. Visszafel halad bejrk
A szabvnyos trolkban megtallhat az rbegin() s az rend() fggvny, melyekkel ford-
tott sorrendben haladhatunk vgig az elemeken (16.3.2). Ezek az eljrsok reverse_iterator
rtket adnak vissza:
template <class Iter>
class reverse_iterator : public iterator<iterator_traits<Iter>::iterator_category,
iterator_traits<Iter>::value_type,
iterator_traits<Iter>::difference_type,
iterator_traits<Iter>::pointer,
iterator_traits<Iter>::reference> {
protected:
Iter current; // a current a *this ltal hivatkozott utni elemre mutat
public:
typedef Iter iterator_type;
reverse_iterator() : current() { }
explicit reverse_iterator(Iter x) : current(x) { }
template<class U> reverse_iterator(const reverse_iterator<U>& x) : current(x.base()) { }
Iter base() const { return current; } // a bejr aktulis rtke
A standard knyvtr 744
reference operator*() const { Iter tmp = current; return *--tmp; }
pointer operator->() const;
reference operator[ ](difference_type n) const;
reverse_iterator& operator++() { --current; return *this; } // vigyzzunk: nem ++
reverse_iterator operator++(int) { reverse_iterator t = current; --current; return t; }
reverse_iterator& operator--() { ++current; return *this; } // vigyzzunk: nem --
reverse_iterator operator--(int) { reverse_iterator t = current; ++current; return t; }
reverse_iterator operator+(difference_type n) const;
reverse_iterator& operator+=(difference_type n);
reverse_iterator operator-(difference_type n) const;
reverse_iterator& operator-=(difference_type n);
};
A reverse_iterator tpust egy iterator segtsgvel valstjuk meg, melynek neve current. Ez
a bejr csak a megfelel sorozat elemeire vagy az utols utni elemre mutathat, de neknk
a reverse_iterator utols utni eleme az eredeti sorozat els eltti eleme, amit nem rhe-
tnk el. gy a tiltott hozzfrsek elkerlse rdekben a current valjban
a reverse_iterator ltal kijellt elem utni rtkre mutat. Ezrt a * opertor a *(current-1)
elemet adja vissza. Az osztly jellegnek megfelelen a ++ mveletet a current rtkre al-
kalmazott -- eljrs valstja meg.
A reverse_iterator csak azokat a mveleteket teszi elrhetv, amelyeket a current bejr
tmogat:
void f(vector<int>& v, list<char>& lst)
{
v.rbegin()[3] = 7; // rendben: kzvetlen elrs bejr
lst.rbegin()[3] = '4'; // hiba: a ktirny bejrk nem tmogatjk
// a [ ] hasznlatt
*(++++++lst.rbegin()) = '4'; // rendben!
}
Ezeken kvl a knyvtr a reverse_iterator tpushoz az ==, a !=, a <, a >, a <=, a >=, a + s
a - mveleteket is tartalmazza.
19.2.6. Adatfolyamok bejri
Az adatok ki- s bevitelt hrom mdszer valamelyikvel valstjuk meg: vagy az adatfolya-
mok knyvtrval (21. fejezet), vagy a grafikus felhasznli fellet eszkzeivel (ezt a C++-
szabvny nem tartalmazza), vagy a C bemeneti/kimeneti (I/O) mveleteivel (21.8). Ezek
19. Bejrk s memriafoglalk 745
a ki- s bemeneti eszkzk elssorban arra szolglnak, hogy klnbz tpus rtkeket
egyesvel rjunk vagy olvassunk. A standard knyvtr ngy bejr-tpussal a ki- s beme-
neti adatfolyamokat is beilleszti a trolk s az algoritmusok ltal krvonalazott egysges
rendszerbe:
1. Az ostream_iterator segtsgvel egy ostream adatfolyamba rhatunk (3.4,
21.2.1).
2. Az istream_iterator segtsgvel egy istream adatfolyamba rhatunk (3.6,
21.3.1).
3. Az ostreambuf_iterator egy adatfolyam-puffer (tmeneti tr) rshoz hasznlha-
t (21.6.1).
4. Az istreambuf_iterator egy adatfolyam-puffer olvasshoz hasznlhat (21.6.2).
Az tlet csak annyi, hogy a gyjtemnyek ki- s bevitelt sorozatokknt jelentjk meg:
template <class T, class Ch = char, class Tr = char_traits<Ch> >
class ostream_iterator : public iterator<output_iterator_tag,void,void,void,void> {
public:
typedef Ch char_type;
typedef Tr traits_type;
typedef basic_ostream<Ch,Tr> ostream_type;
ostream_iterator(ostream_type& s);
ostream_iterator(ostream_type& s, const Ch* delim); // delim rsa minden kimeneti
// rtk utn
ostream_iterator(const ostream_iterator&);
~ostream_iterator();
ostream_iterator& operator=(const T& val); // val rsa a kimenetre
ostream_iterator& operator*();
ostream_iterator& operator++();
ostream_iterator& operator++(int);
};
Ez a bejr a kimeneti bejrk szoksos rs s tovbblps mvelett gy hajtja vgre,
hogy az ostream megfelel mveleteiv alaktja azokat:
void f()
{
ostream_iterator<int> os(cout); // int-ek rsa a cout-ra os-en keresztl
*os = 7; // a kimenet 7
++os; // felkszls a kvetkez kimenetre
*os = 79; // a kimenet 79
}
A standard knyvtr 746
A ++ opertor tnyleges kimeneti mveletet is vgrehajthat, de elkpzelhet olyan megol-
ds is, ahol semmilyen hatsa sincs. A klnbz nyelvi vltozatok klnbz mdszere-
ket alkalmazhatnak, ezrt ha hordozhat programot akarunk kszteni, mindenkppen
hasznljuk a ++ mveletet kt ostream_iterator-rtkads kztt. Termszetesen minden
szabvnyos algoritmus gy kszlt, hiszen ellenkez esetben mr vektorra sem lennnek
hasznlhatk, s ez az oka az ostream_iterator osztly ilyetn meghatrozsnak is.
Az ostream_iterator elksztse nagyon egyszer, gy feladatknt is szerepel a fejezet vgn
(19.6[4]). A szabvnyos ki- s bemenet tbbfle karaktertpust is tmogat. A char_traits
osztly (20.2) egy karaktertpus azon jellemzit rja le, melyek fontosak lehetnek a string
osztly vagy a ki- s bemenet szempontjbl.
Az istream bemeneti bejrjt hasonlan hatrozhatjuk meg:
template <class T, class Ch = char, class Tr = char_traits<Ch>, class Dist = ptrdiff_t>
class istream_iterator : public iterator<input_iterator_tag, T, Dist, const T*, const T&> {
public:
typedef Ch char_type;
typedef Tr traits_type;
typedef basic_istream<Ch,Tr> istream_type;
istream_iterator(); // a bemenet vge
istream_iterator(istream_type& s);
istream_iterator(const istream_iterator&);
~istream_iterator();
const T& operator*() const;
const T* operator->() const;
istream_iterator& operator++();
istream_iterator operator++(int);
};
Ez a bejr is olyan formj, hogy knyelmesen olvashassunk be adatokat (a >> segtsg-
vel) a bemeneti adatfolyambl egy trolba:
void f()
{
istream_iterator<int> is(cin); // int-ek beolvassa a cin-rl az is-en keresztl
int i1 = *is; // egy int beolvassa
++is; // felkszls a kvetkez bemenetre
int i2 = *is; // egy int beolvassa
}
19. Bejrk s memriafoglalk 747
Az alaprtelmezett istream_iterator a bemenet vgt brzolja, gy ennek felhasznlsval
megadhatunk bemeneti sorozatot is:
void f(vector<int>& v)
{
copy(istream_iterator<int>(cin),istream_iterator<int>(),back_inserter(v));
}
Ahhoz, hogy ez az eljrs mkdjn, a standard knyvtr az istream_iterator osztlyhoz
tartalmazza az == s a != mveletet is.
A fentiekbl lthat, hogy az istream_iterator megvalstsa nem annyira egyszer, mint az
ostream_iterator osztly, de azrt nem is tlsgosan bonyolult. Egy istream_iterator meg-
rsa szintn szerepel feladatknt (19.6[5]).
19.2.6.1. tmeneti trak adatfolyamokhoz
A 21.6 pontban rjuk le rszletesen, hogy az adatfolyam alap ki- s bemenet amelyet az
istreams az ostream megvalst valjban egy tmeneti trral (pufferrel) tartja a kapcso-
latot, amely az alacsonyszint, fizikai ki- s bemenetet kpviseli. Lehetsgnk van azon-
ban arra, hogy a szabvnyos adatfolyamok formzsi mveleteit elkerljk, s kzvetlenl
az tmeneti trakkal dolgozzunk (21.6.4). Ez a lehetsg a szabvnyos algoritmusok sz-
mra is adott az istrembuf_iterator s az ostreambuf_iterator segtsgvel:
template<class Ch, class Tr = char_traits<Ch> >
class istreambuf_iterator
: public iterator<input_iterator_tag,Ch,typename Tr::off_type,Ch*,Ch&> {
public:
typedef Ch char_type;
typedef Tr traits_type;
typedef typename Tr::int_type int_type;
typedef basic_streambuf<Ch,Tr> streambuf_type;
typedef basic_istream<Ch,Tr> istream_type;
class proxy; // segdtpus
istreambuf_iterator() throw(); // tmeneti tr vge
istreambuf_iterator(istream_type& is) throw(); // olvass is streambuf-jbl
istreambuf_iterator(streambuf_type*) throw();
istreambuf_iterator(const proxy& p) throw(); // olvass p streambuf-jbl
A standard knyvtr 748
Ch operator*() const;
istreambuf_iterator& operator++(); // eltag
proxy operator++(int); // uttag
bool equal(istreambuf_iterator&); // mindkt streambuf-nak vge (eof) vagy egyiknek
sem
};
A fentieken kvl rendelkezsnkre ll az == s a != opertor is.
Egy streambuf olvassa alacsonyabb szint mvelet, mint egy istream olvassa. Ennek k-
vetkeztben az istreambuf_iterator fellete kicsit bonyolultabb, mint az istream_iterator -.
Ennek ellenre, ha egyszer sikerlt hibtlanul kezdrtket adnunk egy istreambuf_iterator
objektumnak, akkor a *, a ++ s az = mveletek a szoksos helyeken, a szoksos jelentssel
hasznlhatk.
A proxy tpus egy megvalststl fgg segdtpus, amely lehetv teszi az uttagknt
hasznlt ++ opertor alkalmazst anlkl, hogy felesleges korltozsokat tenne
a streambuf-ra vonatkozan. A proxy trolja az eredmnyrtket addig, amg a bejrt to-
vbblptetjk:
template<class Ch, class Tr = char_traits<Ch> >
class istreambuf_iterator<Ch,Tr>::proxy {
Ch val;
basic_streambuf<Ch,Tr>* buf;
proxy(Ch v, basic_streambuf<Ch,Tr>* b) :val(v), buf(b) { }
public:
Ch operator*() { return val; }
};
Az ostreambuf_iterator osztlyt hasonlan definilhatjuk:
template <class Ch, class Tr = char_traits<Ch> >
class ostreambuf_iterator : public iterator<output_iterator_tag,void,void,void,void>{
public:
typedef Ch char_type;
typedef Tr traits_type;
typedef basic_streambuf<Ch,Tr> streambuf_type;
typedef basic_ostream<Ch,Tr> ostream_type;
ostreambuf_iterator(ostream_type& os) throw(); // rs os streambuf-jba
ostreambuf_iterator(streambuf_type*) throw();
ostreambuf_iterator& operator=(Ch);
19. Bejrk s memriafoglalk 749
ostreambuf_iterator& operator*();
ostreambuf_iterator& operator++();
ostreambuf_iterator& operator++(int);
bool failed() const throw(); // igaz, ha Tr::eof()-hoz rtnk
};
19.3. Ellenrztt bejrk
A standard knyvtr ltal biztostottak mellett a programoz sajt maga is ltrehozhat
bejrkat. Erre gyakran szksg is van, amikor egy j tpus trolt hozunk ltre. Pldakp-
pen bemutatunk egy olyan bejrt, amely ellenrzi, hogy rvnyes tartomnyban frnk-e
hozz a trolhoz.
A szabvnyos trolkat hasznlva sokkal ritkbban kell magunknak foglalkozni a memria-
kezelssel, a szabvnyos algoritmusok segtsgvel pedig a trolk elemeinek megcmz-
sre sem kell olyan gyakran gondolnunk. A standard knyvtr s a nyelvi eszkzk egyt-
tes hasznlatval fordtsi idben elvgezhetnk sok tpusellenrzsi feladatot, gy a futsi
idej hibk szma jelentsen kevesebb, mint a hagyomnyos, C stlus programokban. En-
nek ellenre a standard knyvtr mg mindig a programozra hrtja azt a feladatot, hogy
elkerlje egy trol memriahatrainak tlpst. Ha pldul vletlenl valamilyen x tro-
lnak az x[x.size()+7] elemt prbljuk meg elrni, akkor megjsolhatatlan de ltalban
rossz esemnyek kvetkezhetnek be. Egy tartomnyellenrztt vector hasznlata (pld-
ul a 3.7.1 pontban bemutatott Vec-) nha segthet ezen a problmn, sokkal ltalnosabb
megoldst jelent azonban, ha a bejrkon keresztl trtn minden hozzfrst ellenr-
znk.
Ahhoz, hogy ilyen szint ellenrzst rhessnk el anlkl, hogy jelents terhet helyeznnk
a programoz vllra, ellenrztt bejrkra van szksgnk, s valamilyen knyelmes md-
szerre, amellyel ezeket a trolkhoz kapcsolhatjuk. A Checked_iter megvalstshoz szk-
sgnk van egy trolra s egy bejrra, amely ebbe a trolba mutat. Ugyangy, mint a le-
ktk (binder, 18.4.4.1) vagy a beszr bejrk (inserter, 19.2.4) esetben, most is kln
fggvnyekkel segtjk az ellenrztt bejrk ltrehozst:
template<class Cont, class Iter> Checked_iter<Cont,Iter> make_checked(Cont& c, Iter i)
{
return Checked_iter<Cont,Iter>(c,i);
}
A standard knyvtr 750
template<class Cont> Checked_iter<Cont,typename Cont::iterator> make_checked(Cont& c)
{
return Checked_iter<Cont,typename Cont::iterator>(c,c.begin());
}
Ezek a fggvnyek sokat segtenek abban, hogy a tpusokat a paramterekbl llaptsuk
meg, s ne kln kelljen azokat megadni:
void f(vector<int>& v, const vector<int>& vc)
{
typedef Checked_iter<vector<int>,vector<int>::iterator> CI;
CI p1 = make_checked(v,v.begin()+3);
CI p2 = make_checked(v); // alaprtelmezs szerint az els elemre mutat
typedef Checked_iter<const vector<int>,vector<int>::const_iterator> CIC;
CIC p3 = make_checked(vc,vc.begin()+3);
CIC p4 = make_checked(vc);
const vector<int>& vv = v;
CIC p5 = make_checked(v,vv.begin());
}
Alaprtelmezs szerint a const trolknak minden bejrja is konstans, gy az ezekhez tar-
toz Checked_iter osztlyoknak is llandnak kell lennik. A p5 bejr arra mutat pldt,
hogyan hozhatunk ltre const bejrt nem konstans trolbl.
Ez magyarzza azt is, hogy mirt van szksge a Checked_iter tpusnak kt sablonparamter-
re: az egyik a trol tpust adja meg, a msik a konstans/nem konstans klnbsget fejezi ki.
Ezen Checked_iter tpusok nevei elg hosszak (s csnyk), de ez nem szmt, ha
a bejrkat ltalnostott (generikus) algoritmusok paramtereknt hasznljuk:
template<class Iter> void mysort(Iter first, Iter last);
void f(vector<int>& c)
{
try {
mysort(make_checked(c), make_checked(c,c.end());
}
catch (out_of_bounds) {
cerr<<"hopp: hiba a mysort()-ban\n";
abort();
}
}
19. Bejrk s memriafoglalk 751
Ez pont egy olyan algoritmus, melynek els vltozataiban knnyen elfordulhat, hogy ki-
lpnk a megengedett tartomnybl, gy az ellenrztt bejrk hasznlata nagyon is indo-
kolt.
A Checked_iter osztlyt egy trolra hivatkoz mutatval s egy olyan bejrval brzolhat-
juk, amely ebbe a trolba mutat:
template<class Cont, class Iter = typename Cont::iterator>
class Checked_iter : public iterator_traits<Iter> {
Iter curr; // az aktulis pozcira mutat bejr
Cont* c; // az aktulis trolra hivatkoz mutat
// ...
};
Az iterator_traits osztlybl val szrmaztats az egyik lehetsges mdszer a szksges
tpusok (typedef) meghatrozshoz. A msik egyszer megolds az iterator osztlybl
val szrmaztats ebben az esetben tlzott lenne (A reverse_iterator esetben ezt a meg-
oldst vlasztottuk a 19.2.5 pontban). Ugyangy, ahogy egy bejrnak nem kell felttlenl
osztlynak lennie, egy osztlyknt meghatrozott bejrnak sem kell felttlenl az iterator
osztly leszrmazottjnak lennie.
A Checked_iter osztly minden mvelete egyrtelm:
template<class Cont, class Iter = typename Cont::iterator>
class Checked_iter : public iterator_traits<Iter> {
// ...
public:
void valid(Iter p) const
{
if (c->end() == p) return;
for (Iter pp = c->begin(); pp!=c->end(); ++pp) if (pp == p) return;
throw out_of_bounds();
}
friend bool operator==(const Checked_iter& i, const Checked_iter& j)
{
return i.c==j.c && i.curr==j.curr;
}
// nincs alaprtelmezett kezdrtk-ad
// alaprtelmezett msol konstruktor s rtkads hasznlata
A standard knyvtr 752
Checked_iter(Cont& x, Iter p) : c(&x), curr(p) { valid(p); }
reference_type operator*() const
{
if (curr==c->end()) throw out_of_bounds();
return *curr;
}
pointer_type operator->() const
{
if (curr==c->end()) throw out_of_bounds();
return &*curr;
}
Checked_iter operator+(Dist d) const // csak kzvetlen elrs bejrkhoz
{
if (c->end()-curr<d || d<curr-c->begin()) throw out_of_bounds();
return Checked_iter(c,curr+d);
}
reference_type operator[ ](Dist d) const // csak kzvetlen elrs bejrkhoz
{
if (c->end()-curr<=d || d<curr-c->begin()) throw out_of_bounds();
return curr[d];
}
Checked_iter& operator++() // prefix ++
{
if (curr == c->end()) throw out_of_bounds();
++curr;
return *this;
}
Checked_iter operator++(int) // postfix ++
{
Checked_iter tmp = *this;
++*this; // prefix ++ ltal ellenrztt
return tmp;
}
Checked_iter& operator--() // prefix --
{
if (curr == c->begin()) throw out_of_bounds();
--curr;
return *this;
}
19. Bejrk s memriafoglalk 753
Checked_iter operator--(int) // postfix --
{
Checked_iter tmp = *this;
--*this; // prefix -- ltal ellenrztt
return tmp;
}
difference_type index() const { return curr-c.begin(); } // csak kzvetlen elrs esetn
Iter unchecked() const { return curr; }
// +, -, < stb. (19.6[6])
};
Egy Checked_iter objektumot mindig egy bizonyos trol egy bizonyos bejrjhoz ktnk.
Egy alaposabb megvalstsban a valid() fggvnynek hatkonyabb vltozatt kell ltre-
hoznunk a kzvetlen elrs bejrkhoz (19.6[6]). Miutn kezdrtket adtunk
a Checked_iter objektumnak, minden mveletet ellenrznk, amely elmozdthatja a bejrt,
gy mindig biztosak lehetnk abban, hogy az a trol egy ltez elemre mutat. Ha meg-
prblunk egy ilyen bejrt a trol rvnyes tartomnyn kvlre vinni, out_of_bounds ki-
vtelt kapunk:
void f(list<string>& ls)
{
int count = 0;
try {
Checked_iter< list<string> > p(ls,ls.begin());
while (true) {
++p; // elbb-utbb a vgre r
++count;
}
}
catch(out_of_bounds) {
cout << "Tllps a troln " << count << " prblkozs utn.\n";
}
}
A Checked_iter objektum pontosan tudja, melyik trolba mutat. Ez lehetv teszi, hogy az
olyan eseteket kezeljk, amikor egy trolba mutat bejr rvnytelenn vlik valamilyen
mvelet hatsra (16.3.8). Mg gy sem vesznk minden lehetsget figyelembe, teht ha
az sszes hibalehetsgre fel akarunk kszlni, akkor egy msik, bonyolultabb bejrt kell
ksztennk (lsd 19.6[7]).
A standard knyvtr 754
Figyeljk meg, hogy az uttagknt hasznlt ++ opertor megvalstshoz szksg van egy
ideiglenes trolelemre, mg az eltag-formnl erre nincs szksg. Ezrt bejrk esetben
mindig rdemes a p++ forma helyett a ++p alakot hasznlni, ha az ellenkezjre nincs k-
lnsebb okunk.
Mivel a Checked_iter egy trolra hivatkoz mutatt trol, kzvetlenl nem hasznlhat
a beptett tmbk kezelshez. Ha mgis ilyesmire van szksgnk, a c_array tpust
(17.5.4) hasznlhatjuk.
Ahhoz, hogy az ellenrztt bejrk megvalstst teljess tegyk, hasznlatukat egyszer-
v kell tennnk. Kt megkzeltssel prblkozhatunk:
1. Meghatrozunk egy ellenrztt trol tpust, amely ugyangy viselkedik, mint
az tlagos trolk, attl eltekintve, hogy kevesebb konstruktor ll benne rendel-
kezsnkre s a begin(), end() stb. eljrsok mind Check_iter objektumot adnak
vissza a szoksos bejrk helyett.
2. Ksztnk egy kezelosztlyt, amelynek valamilyen trolval adhatunk kezdr-
tket, s amely ehhez a trolhoz a ksbbiekben csak ellenrztt hozzfrse-
ket engedlyez (19.6[8]).
Az albbi sablon egy ellenrztt bejrt kt egy trolhoz:
template<class C> class Checked : public C {
public:
explicit Checked(size_t n) :C(n) { }
Checked() :C() { }
typedef Checked_iter<C> iterator;
typedef Checked_iter<C,C::const_iterator> const_iterator;
iterator begin() { return iterator(*this,C::begin()); }
iterator end() { return iterator(*this,C::end()); }
const_iterator begin() const { return const_iterator(*this,C::begin()); }
const_iterator end() const { return const_iterator(*this,C::end()); }
reference_type operator[ ](size_t n) { return Checked_iter<C>(*this)[n]; }
C& base() { return static_cast<C&>(*this); } // rgzts az alaptrolhoz
};
19. Bejrk s memriafoglalk 755
Ezutn hasznlhatjuk az albbi programrszletet:
Checked< vector<int> > vec(10);
Checked< list<double> > lst;
void f()
{
int v1 = vec[5]; // rendben
int v2 = vec[15]; // out_of_bounds kivtelt vlt ki
// ...
lst.push_back(v2);
mysort(vec.begin(),vec.end());
copy(vec.begin(),vec.end(),lst.begin());
}
A ltszlag felesleges base() fggvny szerepe az, hogy a Checked() fellett a trolk
kezelinek (handle) fellethez igaztsa, ezek ugyanis ltalban nem tesznek lehetv au-
tomatikus konverzikat.
Ha a trol mrete megvltozik, bejri rvnytelenn vlnak. Ez trtnik a Checked_iter
objektumokkal is. Ebben az esetben a Checked_iter-nek a kvetkez kdrszlettel adha-
tunk j kezdrtket:
void g(vector<int>& vi)
{
Checked_iter<int> p(vi,vi.begin());
// ..
int i = p.index(); // aktulis pozci lekrse
vi.resize(100); // p rvnytelen lesz
p = Checked_iter<int>(vi,vi.begin()+i); // az aktulis pozci visszalltsa
}
A rgi aktulis pozci rvnytelenn vlik, ezrt szksgnk van az index() fggvnyre,
amely egy Checked_iter trolsra s visszalltsra hasznlhat.
19.3.1. Kivtelek, trolk s algoritmusok
Knnyen gy tnhet, hogy a szabvnyos algoritmusok s az ellenrztt bejrk egyttes
hasznlata olyan felesleges, mint az v s a nadrgtart egyttes viselse: mindkett nma-
gban is megvd minket a balesetektl. Ennek ellenre a tapasztalat azt mutatja, hogy sok
ember s sok alkalmazs szmra indokolt ilyen szint paranoia, klnsen akkor, ha egy
gyakran vltoz programot sokan hasznlnak rendszeresen.
A standard knyvtr 756
A futsi idej ellenrzsek hasznlatnak egyik mdja, hogy csak a tesztels idejre hagy-
juk azokat programunkban. Teht ezek az ellenrzsek eltnnek a programbl, mieltt az
les krlmnyek kz kerlne. Ez az eljrs ahhoz hasonlthat, mint amikor a ment-
mellnyt addig hordjuk, amg partkzelben pancsolunk, s levesszk, amikor a nylt
tengerre merszkednk. Ugyanakkor igaz, hogy a futsi idej ellenrzsek jelents
mennyisg idt s memrit ignyelhetnek, gy folyamatosan ilyen felgyeletet biztosta-
ni nem lehet vals elvrs. Jelentktelen haszon rdekben optimalizlni mindig felesleges,
teht mieltt vglegesen trlnk bizonyos ellenrzseket, prbljuk ki, hogy jelents mr-
tk teljestmnyjavulst rnk-e el gy. Ahhoz, hogy a programoz tnyleg prblkozhas-
son ilyesmivel, knnyv kell tennnk szmra a futsi idej ellenrzsek eltvoltst
(24.3.7.1). Ha elvgeztk a szksges mrseket, a futsi idej ellenrzseket a ltfontos-
sg s remlhetleg a legalaposabban tesztelt helyekrl trlhetjk, mg mshol az el-
lenrzst egy viszonylag olcs biztostsnak tekinthetjk.
A Checked_iter hasznlata szmtalan hibra felhvhatja figyelmnket, de nem sokat segt
abban, hogy ezeket a hibkat kijavtsuk. A programozk ritkn rnak olyan programokat,
amelyek minden lehetsgre felkszlnek s ellenriznek minden mveletet, amely kiv-
telt okozhat (++, --, *, [ ], -> s =). gy kt nyilvnval stratgia kzl vlaszthatunk:
1. A kivteleket keletkezsi helyk kzelben kapjuk el, gy a kivtelkezel meg-
rja pontosabban megllapthatja a hiba okt s hatkony ellenlpseket tehet.
2. A kivteleket a programban viszonylag magas szinten kapjuk el, az eddigi sz-
mtsoknak egy jelents rszt eldobjuk, s minden adatszerkezetet gyansnak
minstnk, amelybe rs trtnt a hibt kivlt szmts kzben. (Lehet, hogy
ilyen adatszerkezetek nincsenek is, esetleg knnyen kizrhatk a hibaforrsok
krbl.)
Feleltlensg elkapni egy kivtelt, amelyrl azt sem tudjuk, hogy a program mely rszn
keletkezett, s tovbblpni azzal a felttelezssel, hogy egyetlen adatszerkezetnk sem
kerlt nemkvnatos llapotba, hacsak nincs egy tovbbi hibakezel, amely az ezutn ke-
letkez hibkat feldolgozza. Egy egyszer plda az ilyen helyzetre, amikor az eredmnyek
tovbbadsa eltt egy vgs ellenrzst vgznk (akr szmtgprl, akr emberi munka-
vgzsrl van sz). Ilyenkor egyszerbb s olcsbb rtatlanul tovbbhaladni, minthogy
alacsony szinten minden hibalehetsget megvizsgljunk. Ezzel egyben arra is pldt
mutattunk, hogy a tbbszint hibakezels (14.9) hogyan teszi lehetv a programok
egyszerstst.
19. Bejrk s memriafoglalk 757
19.4. Memriafoglalk
A memriafoglalk (alloktorok, allocator) szerepe az, hogy a fizikai memria kezelsnek
gondjt levegyk azon algoritmusok s trolk ksztinek vllrl, melyek szmra mem-
rit kell lefoglalnunk. A memriafoglalk szabvnyos felletet adnak a memriaterletek
lefoglalshoz s felszabadtshoz, valamint szabvnyos neveket adnak azoknak a tpu-
soknak, melyeket mutatknt vagy referenciaknt hasznlhatunk. A bejrhoz hasonlan
a memriafoglal fogalma is tisztn elvont, gy mindent memriafoglalnak neveznk, ami
memriafoglalknt viselkedik.
A standard knyvtr egy szabvnyos memriafoglalt biztost, amely a legtbb felhasznl
szmra megfelel, de ha szksg van r, a programozk maguk is ltrehozhatnak egyni
vltozatokat, melyekkel a memrit ms szerkezetnek tntethetik fel. Kszthetnk pld-
ul olyan memriafoglalt, amely osztott memrit hasznl, szemtgyjt algoritmust valst
meg, elre lefoglalt memriaszeletbl hozza ltre az objektumokat (19.4.2) s gy tovbb.
A szabvnyos trolk s algoritmusok a mkdskhz szksges memrit mindig me-
mriafoglal segtsgvel foglaljk le s rik el. gy ha j memriafoglalt ksztnk, a szab-
vnyos trolkat is felruhzzuk azzal a kpessggel, hogy a memrit megvltozott szem-
szgbl lssk.
19.4.1. A szabvnyos memriafoglal
A szabvnyos allocator sablon a <memory> fejllomnyban tallhat s a memriafoglalst
a new() opertor (6.2.6) segtsgvel vgzi. Alaprtelmezs szerint mindegyik szabvnyos
trol ezt hasznlja:
template <class T> class std::allocator {
public:
typedef T value_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
pointer address(reference r) const { return &r; }
A standard knyvtr 758
const_pointer address(const_reference r) const { return &r; }
allocator() throw();
template <class U> allocator(const allocator<U>&) throw();
~allocator() throw();
pointer allocate(size_type n, allocator<void>::const_pointer hint = 0); // hely n darab
// Ts szmra
void deallocate(pointer p, size_type n); // n darab Ts helynek felszabadtsa,
// megsemmists nlkl
void construct(pointer p, const T& val) { new(p) T(val); } // *p feltltse val-lal
void destroy(pointer p) { p->~T(); } // *p megsemmistse a hely felszabadtsa
// nlkl
size_type max_size() const throw();
template <class U>
struct rebind { typedef allocator<U> other; }; // valjban typedef allocator<U> other
};
template<class T> bool operator==(const allocator<T>&, const allocator<T>&) throw();
template<class T> bool operator!=(const allocator<T>&, const allocator<T>&) throw();
Az allocate(n) mvelettel n objektum szmra foglalhatunk helyet, prja pedig
a deallocate(p,n), mellyel felszabadthatjuk az gy lefoglalt terletet. Figyeljk meg, hogy
a deallocate() fggvnynek is tadjuk az n paramtert. Ezzel a megoldssal kzel optim-
lis memriafoglalkat kszthetnk, amelyek csak a felttlenl szksges informcikat t-
roljk a lefoglalt terletrl. Msrszt viszont ezek a memriafoglalk megkvetelik, hogy
a programoz mindig a megfelel n rtket adja meg a deallocate() hvsakor. Megjegyzen-
d, hogy a deallocate() klnbzik az operator delete()-tl, amennyiben mutat paramte-
re nem lehet nulla.
Az alaprtelmezett allocator a new(size_t) opertort hasznlja a memria lefoglalshoz s
a delete(void*) mveletet a felszabadtshoz. Ebbl kvetkezik, hogy szksg lehet
a new_handler() meghvsra s a std::bad_alloc kivtel kivltsra, ha elfogyott a mem-
ria (6.2.6.2).
Jegyezzk meg, hogy az allocate() fggvnynek nem kell felttlenl meghvnia egy ala-
csonyszint memriafoglalt. Gyakran jobb megoldst jelent egy memriafoglal szmra,
ha maga tartja nyilvn a kiosztsra ksz, szabad memriaszeleteket, s ezeket minimlis
idvesztesggel adja ki (19.4.2).
A nem ktelez hint paramter az allocate() fggvnyben mindig az adott megvalststl
fgg. Mindenkppen arra szolgl, hogy segtse a memriafoglalt azokban a rendszerek-
19. Bejrk s memriafoglalk 759
ben, ahol fontos a lokalits (vagyis az adott rendszerhez igazods). Egy memriafoglaltl
pldul elvrhatjuk, hogy egy lapozs rendszerben az sszefgg objektumoknak azonos
lapon foglaljon helyet. A hint paramter tpusa pointer, az albbi erteljesen egyszerstett
specializcinak megfelelen:
template <> class allocator<void> {
public:
typedef void* pointer;
typedef const void* const_pointer;
// megj.: nincs referencia
typedef void value_type;
template <class U>
struct rebind { typedef allocator<U> other; }; // valjban typedef allocator<U> other
};
Az allocator<void>::pointer tpus ltalnos mutattpusknt szolgl s minden szabvnyos
memriafoglal esetben a const void* tpusnak felel meg.
Ha a memriafoglal dokumentcija mst nem mond, a programoz kt lehetsg kzl
vlaszthat az allocate() fggvny hasznlatakor:
1. Nem adja meg a hint paramtert.
2. A hint paramterben egy olyan objektumra hivatkoz mutatt ad meg, amelyet
gyakran hasznl majd az j objektummal egytt. Egy sorozatban pldul meg-
adhatjuk az elz elem cmt.
A memriafoglalk arra szolglnak, hogy megkmljk a trolk ksztit a fizikai memria
kzvetlen kezelstl. Pldakppen vizsgljuk meg, hogy egy vector megvalstsban ho-
gyan hasznljuk a memrit:
template <class T, class A = allocator<T> > class vector {
public:
typedef typename A::pointer iterator;
// ...
private:
A alloc; // memriafoglal objektum
iterator v; // mutat az elemekre
// ...
public:
explicit vector(size_type n, const T& val = T(), const A& a = A())
: alloc(a)
{
A standard knyvtr 760
v = alloc.allocate(n);
for(iterator p = v; p<v+n; ++p) alloc.construct(p,val);
// ...
}
void reserve(size_type n)
{
if (n<=capacity()) return;
iterator p = alloc.allocate(n);
iterator q = v;
while (q<v+size()) { // ltez elemek msolsa
alloc.construct(p++,*q);
alloc.destroy(q++);
}
alloc.deallocate(v,capacity()); // rgi hely felszabadtsa
v = p-size();
// ...
}
// ...
};
A memriafoglalk mveleteit a pointer s reference typedef-ek segtsgvel fejezzk ki, gy
megadjuk a programoznak azt a lehetsget, hogy ms tpusokat hasznljon a memria
elrshez. Ezt ltalban nagyon nehz megoldani. A C++ nyelv segtsgvel pldul nincs
lehetsgnk arra, hogy egy tkletes hivatkozstpust hatrozzunk meg. A nyelv s
a knyvtrak alkoti viszont ezeket a typedef-eket olyan tpusok ltrehozshoz hasznlhat-
jk, amelyeket egy tlagos felhasznl nem tudna elkszteni. Pldaknt egy olyan mem-
riafoglalt emlthetnk, amely lland adatterlet elrsre szolgl, de gondolhatunk egy
nagy mutatra is, amellyel a memrinak azt a terlett is elrhetjk, amit a szoksos (l-
talban 32 bites) mutatk mr nem kpesek megcmezni.
Egy tlagos felhasznl a memriafoglalnak egyedi clra megadhat a szoksostl eltr
mutattpust is. Ugyanezt nem tehetjk meg referencikkal, de ez a korltozs ksrletezs-
nl vagy egyedi rendszerekben nem okoz nagy gondot.
Memriafoglalt azrt hozunk ltre, hogy knnyen kezelhessnk olyan objektumokat, me-
lyeknek tpust a memriafoglal sablonparamtereknt adtuk meg. A legtbb trol meg-
valstsban azonban szksg van ms tpus objektumok ltrehozsra is. Pldul a list
megvalstshoz szksg van Link objektumok ltrehozsra. Az ilyen Link jelleg objek-
tumokat a megfelel list osztly memriafoglaljval hozzuk ltre.
19. Bejrk s memriafoglalk 761
A furcsa rebind tpus arra szolgl, hogy memriafoglal kpes legyen brmilyen tpus ob-
jektum helynek lefoglalsra:
typedef typename A::template rebind<Link>::other Link_alloc; // "sablon", lsd C.13.6
Ha A egy allocator, akkor a rebind<Link>::other tpus-meghatrozs jelentse
allocator<Link>, teht a fenti typedef egy kzvetett megfogalmazsa az albbinak:
typedef allocator<Link> Link_alloc;
A kzvetett megfogalmazs azonban megkml minket attl, hogy az allocator kulcsszt
kzvetlenl hasznljuk. A Link_alloc tpust csak az A sablonparamteren keresztl hatroz-
zuk meg. Pldul:
template <class T, class A = allocator<T> > class list {
private:
class Link { /* ... */ };
typedef typename A::rebind<Link>::other Link_alloc; // allocator<Link>
Link_alloc a; // "link" memriafoglal
A alloc; // "list" memriafoglal
// ...
public:
typedef typename A::pointer iterator;
// ...
iterator insert(iterator pos, const T& x )
{
Link_alloc::pointer p = a.allocate(1); // egy Link megszerzse
// ...
}
// ...
};
Mivel a Link a list osztly tagja, paramtere egy memriafoglal. Ennek kvetkezben a k-
lnbz memriafoglalkkal rendelkez listk Link objektumai klnbz tpusak,
ugyangy, ahogy maguk a listk is klnbznek (17.3.3).
A standard knyvtr 762
19.4.2. Felhasznli memriafoglalk
A trol ksztjnek gyakran van szksge arra, hogy egyesvel hozzon ltre (allocate())
vagy semmistsen meg (deallocate()) objektumokat. Ha az allocate() fggvnyt meggondo-
latlanul ksztjk el, nagyon sokszor kell meghvnunk a new opertort, pedig a new oper-
tor ilyen hasznlatra gyakran kis hatkonysg. A felhasznli memriafoglalk pldja-
knt az albbiakban olyan mdszerrel lnk, mellyel egy kszletet hozunk ltre, amely
rgztett mret memriaszeleteket trol. A memriafoglal ennek felhasznlsval hatko-
nyabban hajtja vgre az allocate() eljrst, mint a hagyomnyos (br ltalnosabb) new()
opertor.
Vletlenl n mr korbban is ltrehoztam egy ilyen kszletes memriafoglalt, amely k-
rlbell azt csinlja, amire most szksgnk van, de nem a megfelel felletet nyjtja (hi-
szen vekkel azeltt kszlt, hogy a memriafoglalk tlete megszletett volna). Ez a Pool
osztly meghatrozza a rgztett mret elemek kszlett, amelybl a programoz gyorsan
foglalhat le terleteket s gyorsan fel is szabadthatja azokat. Ez egy alacsonyszint tpus,
amely kzvetlenl a memrit kezeli s az igaztsi (alignment) problmkkal is foglalkozik:
class Pool {
struct Link { Link* next; };
struct Chunk {
enum { size = 8*1024-16 }; // valamivel kevesebb 8K-nl, gy egy
// "Chunk" belefr 8K-ba
char mem[size]; // elszr a lefoglaland terlet az igazts
// miatt
Chunk* next;
};
Chunk* chunks;
const unsigned int esize;
Link* head;
Pool(Pool&); // msolsvdelem
void operator=(Pool&); // msolsvdelem
void grow(); // a kszlet nvelse
public:
Pool(unsigned int n); // n az elemek mrete
~Pool();
void* alloc(); // hely foglalsa egy elem szmra
void free(void* b); // elem visszahelyezse a kszletbe
};
19. Bejrk s memriafoglalk 763
inline void* Pool::alloc()
{
if (head==0) grow();
Link* p = head; // az els elem visszaadsa
head = p->next;
return p;
}
inline void Pool::free(void* b)
{
Link* p = static_cast<Link*>(b);
p->next = head; // b visszahelyezse els elemknt
head = p;
}
Pool::Pool(unsigned int sz)
: esize(sz<sizeof(Link)?sizeof(Link):sz)
{
head = 0;
chunks = 0;
}
Pool::~Pool() // minden 'chunk' felszabadtsa
{
Chunk* n = chunks;
while (n) {
Chunk* p = n;
n = n->next;
delete p;
}
}
void Pool::grow() // hely foglalsa j 'chunk' szmra, melyet 'esize' mret elemek
// lncolt listjaknt rendeznk
{
Chunk* n = new Chunk;
n->next = chunks;
chunks = n;
const int nelem = Chunk::size/esize;
char* start = n->mem;
char* last = &start[(nelem-1)*esize];
for (char* p = start; p<last; p+=esize)
reinterpret_cast<Link*>(p)->next = reinterpret_cast<Link*>(p+esize);
reinterpret_cast<Link*>(last)->next = 0;
head = reinterpret_cast<Link*>(start);
}
A standard knyvtr 764
A vals letben zajl munka szemlltetsre a Pool osztlyt vltozatlan formban hasznl-
juk fel az j memriafoglal megvalstshoz, nem csak trjuk, hogy a neknk megfelel
felletet nyjtsa. A kszletes memriafoglal clja, hogy objektumaink egyesvel trtn
ltrehozsa s megsemmistse gyors legyen. A Pool osztly mindezt biztostja. A megval-
stst azzal kell mg kiegsztennk, hogy egyszerre tetszleges szm, illetve ( a rebind()
ignyeinek megfelelen) tetszleges mret objektumokat is ltrehozhassunk. Ezt a kt
problmt feladatnak hagyjuk (19.6[9]).
A Pool osztly felhasznlsval a Pool_alloc megvalstsa mr egyszer:
template <class T> class Pool_alloc {
private:
static Pool mem; // elemkszlet sizeof(T) mrettel
public:
// mint a szabvnyos allocator (19.4.1)
};
template <class T> Pool Pool_alloc<T>::mem(sizeof(T));
template <class T> Pool_alloc<T>::Pool_alloc() { }
template <class T>
T* Pool_alloc<T>::allocate(size_type n, void* = 0)
{
if (n == 1) return static_cast<T*>(mem.alloc());
// ...
}
template <class T>
void Pool_alloc<T>::deallocate(pointer p, size_type n)
{
if (n == 1) {
mem.free(p);
return;
}
// ...
}
A memriafoglalt ezutn mr a megszokott formban hasznlhatjuk:
vector< int,Pool_alloc<int> > v;
map<string,number,Pool_alloc< pair<const string,number> > > m;
// ugyangy mint szoktuk
vector<int> v2 = v; // hiba: klnbz memriafoglal-paramterek
19. Bejrk s memriafoglalk 765
A Pool_Alloc megvalstshoz statikus Pool objektumot hasznlunk. Azrt dntttem gy,
mert a standard knyvtr a memriafoglalkra egy korltozst knyszert, mgpedig azzal,
hogy a szabvnyos trolk megvalstsnak megengedi, hogy minden objektumot egyen-
rtknek tekintsenek, amelynek tpusa az adott trol memriafoglalja. Ez valjban
igen jelents hatkonysgi elnyket jelent: ennek a korltozsnak ksznheten a Link
objektumok memriafoglalinak pldul nem kell kln memriaterletet flretennnk
(annak ellenre, hogy a Link osztly ltalban paramterknt egy memriafoglalt kap
ahhoz a trolhoz, amelyben szerepel, 19.4.1). Egy msik elny, hogy azokban a mvele-
tekben, ahol kt sorozat elemeit kell elrnnk (pldul a swap() fggvnyben), nem kell
megvizsglnunk, hogy a hasznlt elemek ugyanolyan memriafoglalkkal rendelkeznek-e.
A htrny, hogy a korltozs kvetkeztben az ilyen memriafoglalk nem hasznlhatnak
objektumszint adatokat.
Mieltt ilyen optimalizcit hasznlnnk, gondoljuk vgig, hogy szksg van-e r. n rem-
lem, hogy az alaprtelmezett allocator legtbb megvalstsban elvgzik ezt a klasszikus
C++ optimalizcit, gy megkmlnek minket ettl a problmtl.
19.4.3. ltalnostott memriafoglalk
Az allocator valjban nem ms, mint annak az tletnek az egyszerstett s optimalizlt vl-
tozata, miszerint egy trolnak egy sablonparamteren keresztl adjuk meg az informci-
kat (13.4.1., 16.2.3). Logikus elvrs pldul, hogy a trol minden elemnek helyt a t-
rol memriafoglaljval foglaljuk le. Ha azonban ilyenkor lehetv tesszk, hogy kt
ugyanolyan tpus list trolnak klnbz memriafoglalja legyen, akkor a splice()
(17.2.2.1) nem valsthat meg egyszer tlncolssal, hanem szablyos msolst kell meg-
hatroznunk, mert vdekeznnk kell azon (ritka) esetek ellen, amikor a kt listban a me-
mriafoglalk nem azonosak, annak ellenre, hogy tpusuk megegyezik. Hasonl problma,
hogy ha a memriafoglalk tkletesen ltalnosak, akkor a rebind() eljrsnak (amely tet-
szleges tpus elemek ltrehozst teszi lehetv a memriafoglal szmra) sokkal bo-
nyolultabbnak kell lennie. Ezrt a normlis memriafoglalkrl azt felttelezzk, hogy
nem trolnak objektumszint adatokat, az algoritmusok pedig kihasznlhatjk ezt.
Meglep mdon ez a drki korltozs az objektumszint informcikra nzve nem tls-
gosan veszlyes a memriafoglalk esetben. A legtbb memriafoglalnak nincs is szk-
sge objektumszint adatokra, st ilyen adatok nlkl mg gyorsabbak is lehetnek, rad-
sul a memriafoglalk azrt tudnak adatokat trolni, a memriafoglal-tpusok szintjn.
Ha kln adatelemekre van szksg, klnbz memriafoglal-tpusokat hasznlhatunk:
A standard knyvtr 766
template<class T, class D> class My_alloc { // T memriafoglaljt D hasznlatval
// hozzuk ltre
D d; // a My_alloc<T,D> szmra szksges adatok
// ...
};
typedef My_alloc<int,Persistent_info> Persistent;
typedef My_alloc<int,Shared_info> Shared;
typedef My_alloc<int,Default_info> Default;
list<int,Persistent> lst1;
list<int,Shared> lst2;
list<int,Default> lst3;
Az lst1, az lst2 s az lst3 listk klnbz tpusak, gy ha kzlk kettt akarunk felhasz-
nlni valamilyen mveletben, akkor ltalnos algoritmusokat kell hasznlnunk (18.fejezet),
nem pedig specializlt lista mveleteket (17.2.2.1). Ebbl kvetkezik, hogy az tlncols
helyett msolst kell hasznlnunk, gy a klnbz memriafoglalk hasznlata nem okoz
problmt.
Az objektumszint adatokra vonatkoz korltozsokra a memriafoglalkban azrt van
szksg, mert gy tudjuk kielgteni a standard knyvtr szigor hatkonysgi kvetelm-
nyeit, mind futsi id, mind trhasznlat szempontjbl. Egy lista memriafoglalja pld-
ul nem foglal jelentsen tbb memrit a szksgesnl, de ha minden listaelemnl jelent-
kezne egy kis felesleg, akkor ez jelents vesztesget okozna.
Gondoljuk vgig, hogyan hasznlhatnnk a memriafoglalkat akkor, ha a standard knyv-
tr hatkonysgi kvetelmnyeitl eltekintennk. Ez a helyzet olyan nem szabvnyos
knyvtr esetben fordulhat el, melyben nem volt fontos tervezsi szempont a nagy hat-
konysg az adatszerkezetek s tpusok ltrehozsakor, vagy akr a standard knyvtr bizo-
nyos egyedi cl megvalstsaiban. Ilyenkor a memriafoglal felhasznlhat olyan jelle-
g informcik trolsra is, melyek ltalban ltalnos bzisosztlyokban kapnak helyet
(16.2.2). Kszthetnk pldul olyan memriafoglalkat, melyek vlaszt tudnak adni arra
a krdsre, hogy az objektumok hol kaptak helyet, knlhatnak olyan informcikat, me-
lyek az objektumok elrendezsre vonatkoznak, vagy megkrdezhetjk tlk, hogy egy
adott elem benne van-e a trolban. Segtsgkkel elkszthet egy olyan trolfelgyel
is, amely gyorsttrknt szolgl a httrtrhoz vagy kapcsolatokat biztost a trol s ms
objektumok kztt s gy tovbb.
Ezzel a mdszerrel teht tetszleges szolgltatsokat biztosthatunk a szoksos
trolmveletek htterben. Ennek ellenre rdemes klnbsget tennnk az adatok tro-
19. Bejrk s memriafoglalk 767
lsnak s az adatok felhasznlsnak feladatai kztt. Ez utbbi nem tartozik egy ltalno-
stott memriafoglal hatskrbe, gy inkbb egy kln sablonparamter segtsgvel illik
megvalstanunk.
19.4.4. Elksztetlen memria
A szabvnyos allocator mellett a <memory> fejllomnyban tallhatunk nhny olyan fgg-
vnyt is, melyek az elksztetlen (rtkkel nem feltlttt) memria problmival foglal-
koznak. Azt a veszlyes, de gyakran nagyon fontos lehetsget biztostjk, hogy a T tpus-
nvvel hivatkozhatunk egy olyan memriaterletre, amely elg nagy egy T tpus objektum
trolsra, de nem teljesen szablyosan ltrehozott T objektumot tartalmaz.
A knyvtrban hrom fggvnyt tallunk, mellyel elksztetlen terletre rtkeket
msolhatunk:
template <class In, class For>
For uninitialized_copy(In first, In last, For res) // msols res-be
{
typedef typename iterator_traits<For>::value_type V;
while (first != last)
new (static_cast<void*>(&*res++)) V(*first++); // ltrehozs res-ben (10.4.11)
return res;
}
template <class For, class T>
void uninitialized_fill(For first, For last, const T& val) //msols [first,last)-ba
{
typedef typename iterator_traits<For>::value_type V;
while (first != last) new (static_cast<void*>(&*first++)) V(val); // ltrehozs first-ben
}
template <class For, class Size, class T>
void uninitialized_fill_n(For first, Size n, const T& val) //msols [first,first+n)-be
{
typedef typename iterator_traits<For>::value_type V;
while (n--) new (static_cast<void*>(&*first++)) V(val); // ltrehozs first-ben
}
Ezek a fggvnyek elssorban trolk s algoritmusok ksztsnl hasznosak. A reserve()
s a resize() (16.3.8) pldul legknnyebben ezen fggvnyek felhasznlsval valstha-
A standard knyvtr 768
t meg (19.6[10]). Igen nagy problma, ha egy ilyen kezdrtkkel nem rendelkez objek-
tum valahogy kiszabadul a trol bels megvalstsbl s tlagos felhasznlk kezbe
kerl (lsd mg E.4.4).
Az algoritmusoknak gyakran van szksgk ideiglenes trterletre feladataik helyes vgre-
hajtshoz. Ezeket a terleteket gyakran rdemes egyetlen mvelettel lefoglalni, annak el-
lenre, hogy rtkkel feltlteni csak akkor fogjuk azokat, amikor tnylegesen szksg lesz
rjuk. Ezrt a knyvtr tartalmaz egy fggvnyprt, mellyel elksztetlen memriaterletet
foglalhatunk le, illetve szabadthatunk fel:
template <class T> pair<T*,ptrdiff_t> get_temporary_buffer(ptrdiff_t); // lefoglals
// kezdeti rtkads
// nlkl
template <class T> void return_temporary_buffer(T*); // felszabadts
// megsemmists
// nlkl
A get_temporary_buffer<X>(n) mvelet megprbl helyet foglalni n vagy tbb X tpus ob-
jektum szmra. Ha ez sikerl, akkor az els kezdrtk nlkli objektumra hivatkoz muta-
tt s a lefoglalt terleten elfr, X tpus objektumok szmt adja vissza. Ha nincs elg me-
mria, a visszaadott pr msodik (second) tagja nulla lesz. Az tlet az, hogy a rendszer a gyors
helyfoglals rdekben rgztett mret tmeneti trat tart kszenltben. Ennek kvetkezt-
ben elfordulhat, hogy n objektum szmra ignylnk terletet, de az tmeneti trban ennl
tbb is elfr. A gond az, hogy az is elkpzelhet, hogy kevesebb terletet kapunk az ignyelt-
nl, gy a get_temporary_buffer() felhasznlsi mdja az, hogy kellen nagy trat ignylnk,
aztn ebbl annyit hasznlunk fel, amennyit megkaptunk. A get_temporary_buffer() ltal le-
foglalt terletet fel kell szabadtanunk a return_temporary_buffer() fggvny segtsgvel.
Ugyangy, ahogy a get_temporary_buffer() a konstruktor meghvsa nlkl foglal le terletet,
a return_temporary_buffer() a destruktor meghvsa nlkl szabadt fel. Mivel
a get_temporary_buffer() alacsonyszint mvelet s kifejezetten ideiglenes trak lefoglals-
ra szolgl, nem hasznlhatjuk a new vagy az allocator::allocate() helyett, hosszabb let ob-
jektumok ltrehozsra.
Azok a szabvnyos algoritmusok, melyek egy sorozatba rnak, felttelezik, hogy a sorozat
elemei korbban mr kaptak kezdrtket. Teht az algoritmusok az rshoz egyszer
rtkadst hasznlnak s nem msol konstruktort. Ennek kvetkeztben viszont nem
hasznlhatunk elksztetlen terletet egy algoritmus kimenete cljra. Ez sokszor igen kel-
lemetlen, mert az egyszer rtkads jval kltsgesebb, mint a kezdeti, radsul nem is
rdekel minket, milyen rtkeket fogunk fellrni (ha rdekelne, nem rnnk fell). A meg-
oldst a raw_storage_iterator jelenti, amely szintn a <memory> fejllomnyban tallhat
s egyszer helyett kezdeti rtkadst vgez:
19. Bejrk s memriafoglalk 769
template <class Out, class T>
class raw_storage_iterator : public iterator<output_iterator_tag,void,void,void,void> {
Out p;
public:
explicit raw_storage_iterator(Out pp) : p(pp) { }
raw_storage_iterator& operator*() { return *this; }
raw_storage_iterator& operator=(const T& val) {
T* pp = &*p;
new(pp) T(val); // val pp-be helyezse (10.4.11)
return *this;
}
raw_storage_iterator& operator++() {++p; return *this; }
raw_storage_iterator operator++(int) {
raw_storage_iterator t = *this;
++p;
return t;
}
};
Pldul kszthetnk egy sablon fggvnyt, amely egy vector elemeit egy tmeneti trba
msolja:
template<class T, class A> T* temporary_dup(vector<T,A>& v)
{
pair<T*,ptrdiff_t> p = get_temporary_buffer<T>(v.size());
if (p.second < v.size()) { // ellenrizzk, hogy elg volt-e az elrhet memria
if (p.first != 0) return_temporary_buffer(p.first);
return 0;
}
copy(v.begin(),v.end(),raw_storage_iterator<T*,T>(p.first));
return p.first;
}
Ha a get_temporary_buffer() helyett a new opertort hasznltuk volna, az tmeneti tr kez-
deti feltltsre sor kerlt volna. Mivel kikerltk az elksztst, a raw_storage_iterator
osztlyra van szksgnk a nem feltlttt terlet kezelshez. Ebben a pldban
a temporary_dump() fggvny meghvjnak feladata marad, hogy meghvja
a return_temporary_buffer() eljrst a visszaadott mutatra.
A standard knyvtr 770
19.4.5. Dinamikus memria
A <new> fejllomnyban olyan lehetsgek szerepelnek, melyekkel a new s delete oper-
tort valsthatjuk meg:
class bad_alloc : public exception { /* ... */ };
struct nothrow_t {};
extern const nothrow_t nothrow; // kivtelt ki nem vlt memriafoglal
typedef void (*new_handler)();
new_handler set_new_handler(new_handler new_p) throw();
void* operator new(size_t) throw(bad_alloc);
void operator delete(void*) throw();
void* operator new(size_t, const nothrow_t&) throw();
void operator delete(void*, const nothrow_t&) throw();
void* operator new[ ](size_t) throw(bad_alloc);
void operator delete[ ](void*) throw();
void* operator new[ ](size_t, const nothrow_t&) throw();
void operator delete[ ](void*, const nothrow_t&) throw();
void* operator new (size_t, void* p) throw() { return p; } // elhelyezs (10.4.11)
void operator delete (void* p, void*) throw() { } //nem csinl semmit
void* operator new[ ](size_t, void* p) throw() { return p; }
void operator delete[ ](void* p, void*) throw() { } //nem csinl semmit
Egy res kivtel-specifikcival (14.6) definilt operator new() vagy operator new[ ]() nem
jelezheti a memria elfogyst az std::bad_alloc kivtel kivltsval. Ehelyett, ha sikertelen
a helyfoglals, 0 rtket adnak vissza. A new kifejezs (6.2.6.2) az res kivtel-specifi-
kcival rendelkez memriafoglalk ltal visszaadott rtket mindig ellenrzi, s ha 0 r-
tket kap, nem hvja meg a konstruktort, hanem azonnal szintn 0 rtket ad vissza.
A nothrow memriafoglalk pldul 0 rtket adnak vissza a sikertelen helyfoglals jelz-
sre, s nem egy bad_alloc kivtelt vltanak ki:
void f()
{
int* p = new int[100000]; // bad_alloc-ot vlthat ki
if (int* q = new(nothrow) int[100000]) { // nem vlt ki kivtelt
// lefoglals sikeres
}
19. Bejrk s memriafoglalk 771
else {
// lefoglals sikertelen
}
}
Ez a mdszer lehetv teszi, hogy kivtel eltti hibakezelst hasznljunk a helyfoglals sorn.
19.4.6. C stlus helyfoglals
A C++ a C-tl rklt egy dinamikusmemria-kezel felletet, melyet a <cstdlib> fejllo-
mnyban tallhatunk meg:
void* malloc(size_t s); // s bjt lefoglalsa
void* calloc(size_t n, size_t s); // n-szer s bjt feltltse 0 kezdrtkkel
void free(void* p); // a malloc() vagy calloc() ltal lefoglalt szabad terlet felszabadtsa
void* realloc(void* p, size_t s); // a p ltal mutatott tmb mretnek mdostsa s-re;
Ezen fggvnyek helyett hasznljuk inkbb a new vagy a delete opertort, vagy a szabv-
nyos trolk mg magasabb szint szolgltatsait. A fenti eljrsok elksztetlen memri-
val foglalkoznak, gy a free() pldul nem hv meg semmilyen destruktort a memriater-
let felszabadtsra. A new s a delete megvalstsai hasznlhatjk ezeket a fggvnyeket,
de szmukra sem ktelez. Ha egy objektumot pldul a new opertorral hozunk ltre,
majd a free() fggvnnyel prblunk meg megsemmisteni, slyos problmkba tkzhe-
tnk. Ha a realloc() fggvny szolgltatsaira lenne szksgnk, hasznljunk inkbb szab-
vnyos trolkat, melyek az ilyen jelleg feladatokat ltalban sokkal egyszerbben s leg-
albb olyan hatkonyan hajtjk vgre (16.3.5).
A knyvtr tartalmaz nhny olyan fggvnyt is, melyekkel hatkonyan vgezhetnk bjt-
szint mveleteket. Mivel a C a tpus nlkli bjtokat eredetileg char* mutatkon keresztl
rte el, ezek a fggvnyek a <cstring> fejllomnyban tallhatk. Ezekben a fggvnyek-
ben a void* mutatkat char* mutatkknt kezeljk:
void* memcpy(void* p, const void* q, size_t n); // nem tfed terletek msolsa
void* memmove(void* p, const void* q, size_t n); // esetleg tfed terletek msolsa
A strcpy() (20.4.1) fggvnyhez hasonlan ezek a mveletek is n darab bjtot msolnak
a q cmrl a p cmre, majd a p mutatt adjk vissza. A memove() ltal kezelt memriater-
letek tfedhetik egymst, de a memcpy() felttelezi, hogy a kt terlet teljesen klnll, s
ltalban ki is hasznlja ezt a felttelezst.
A standard knyvtr 772
Hasonlan mkdnek az albbi fggvnyek is:
void* memchr(const void* p, int b, size_t n); // mint a strchr() (20.4.1):
// b keresse p[0]..p[n-1]-ben
int memcmp(const void* p, const void* q, size_t n); // mint a strcmp(): bjtsorozatok
// sszehasonltsa
void* memset(void* p, int b, size_t n); // n bjt b-re lltsa, p
// visszaadsa
Szmos C++-vltozatban ezeknek az eljrsoknak ersen optimalizlt vltozatai is
megtallhatk.
19.5. Tancsok
[1] Amikor egy algoritmust megrunk, dntsk el, milyen bejrra van szksgnk
az eljrs kellen hatkony megvalstshoz, majd folyamatosan figyeljnk,
hogy csak olyan mveleteket hasznljunk, melyek az adott bejr-kategria
esetben rendelkezsnkre llnak. 19.2.1
[2] Az algoritmusok hatkonyabb megvalstsra hasznljunk tlterhelst, ha a pa-
ramterknt megadott bejr a minimlis kvetelmnynl tbb szolgltatst
nyjt. 19.2.3.
[3] A klnbz bejr-kategrikra hasznlhat algoritmusok megrshoz hasz-
nljuk az iterator_traits osztlyokat. 19.2.2.
[4] Ne felejtsk el hasznlni a ++ opertort az istream_iterator s az
ostream_iterator objektumok esetben sem. 19.2.6.
[5] A trolk tlcsordulsnak elkerlse rdekben hasznljunk beszr (inserter)
bejrkat. 19.2.4.
[6] Tesztels alatt vgezznk minl tbb ellenrzst, s a vgleges vltozatbl is
csak azokat tvoltsuk el, melyeket felttlenl szksges. 19.3.1.
[7] Ha tehetjk, hasznljuk a ++p formt a p++ helyett. 19.3.
[8] Az adatszerkezeteket bvt algoritmusok hatkonysgt nvelhetjk, ha elk-
sztetlen memriaterleteket hasznlunk. 19.4.4.
[9] Ha egy algoritmus ideiglenes adatszerkezeteket hasznl, a hatkonysgot ideig-
lenes tr (puffer) hasznlatval nvelhetjk. 19.4.4.
[10] Ktszer is gondoljuk meg, mieltt sajt memriafoglal rsba kezdnk. 19.4.
[11] Kerljk a malloc(), a free(), a realloc() stb. fggvnyek hasznlatt. 19.4.6.
[12] A sablonok typedef-jeinek hasznlatt a rebind fggvnynl bemutatott mdszer
segtsgvel utnozhatjuk. 19.4.1.
19. Bejrk s memriafoglalk 773
19.6. Gyakorlatok
1. (*1.5) Ksztsk el a 18.6.7. reverse() fggvnyt. Segtsg: 19.2.3.
2. (*1.5) Ksztsnk egy kimeneti bejrt, amely valjban sehova sem r. Mikor
lehet rtelme egy ilyen bejrnak?
3. (*2) rjuk meg a reverse_iterator osztlyt (19.2.5).
4. (*1.5) Ksztsk el az ostream_iterator osztlyt (19.2.6).
5. (*2) Ksztsk el az istream_iterator osztlyt (19.2.6).
6. (*2.5) Fejezzk be a Checked_iter osztlyt (19.3).
7. (*2.5) Alaktsuk t a Checked_iter osztlyt gy, hogy ellenrizze az rvnytelen-
n vlt bejrkat is.
8. (*2) Tervezznk meg s ksztsnk el egy olyan kezelosztlyt, amely egy tro-
lt kpes helyettesteni gy, hogy annak teljes fellett biztostja. Az brzols-
ban trolnunk kell egy mutatt egy trolra, valamint meg kell rnunk a trol
mveleteit tartomnyellenrzssel.
9. (*2.5) Fejezzk be vagy rjuk jra a Pool_alloc (19.4.2) osztlyt gy, hogy az
a standard knyvtr allocator (19.4.1) osztlynak minden lehetsgt tmo-
gassa. Hasonltsuk ssze az allocator s a Pool_alloc hatkonysgt, s llapt-
suk meg, hogy sajt rendszernkben szksg van-e a Pool_alloc hasznlatra.
10. (*2.5) Ksztsnk el egy vector osztlyt gy, hogy memriafoglalkat haszn-
lunk a new s a delete opertor helyett.
A standard knyvtr 774
Karakterlncok
Jrt utat a jratlanrt el ne hagyj!
Karakterlncok Karakterek char_traits basic_string Bejrk Elemek elrse
Konstruktorok Hibakezels rtkads Koverzi sszehasonlts Beszrs
sszefzs Keress s csere Mret s kapacits Karakterlncok ki- s bevitele
C stlus karakterlncok Karakterek osztlyozsa A C knyvtr fggvnyei Tan-
csok Gyakorlatok
20.1. Bevezets
A karakterlnc (string) karakterek sorozata. A standard knyvtr string osztlya mindazokat
az eljrsokat elrhetv teszi, amelyekre a karakterlncokkal kapcsolatban szksgnk le-
het: indexels (20.3.3), rtkads (20.3.6), sszehasonlts (20.3.8), hozzfzs (20.3.9),
sszefzs (20.3.10), rszlncok keresse (20.3.11). Nincs viszont ltalnos rszlnc-keze-
l, ezrt a szabvnyos string hasznlatt is bemutatand ksztnk majd egyet
(20.3.11). Egy szabvnyos karakterlnc szinte brmilyen karakterek sorozata lehet (20.2).
20
A tapasztalatok azt mutatjk, hogy nem lehet tkletes string tpust megvalstani. Ehhez az
emberek zlse, elvrsaik, ignyeik tl nagy mrtkben klnbznek. gy a standard
knyvtr string osztlya sem tkletes. Bizonyos tervezsi krdsekben mshogy is dnt-
hettem volna az osztly ltrehozsakor. Ennek ellenre azt hiszem, sok elvrst kielgt s
knnyen elkszthetk azok a kiegszt fggvnyek, melyekkel a tovbbi feladatok meg-
oldhatk. Nagy elnyt jelent az is, hogy az std::string osztly ltalnosan ismert s minden-
hol elrhet. Ezek a jellemzk a legtbb esetben fontosabbak, mint azok tulajdonsgok,
amelyekkel az osztlyt esetleg mg kiegszthetnnk. A klnbz karakterlnc-osztlyok
elksztse gyakorlsnak nagyszer (11.12, 13.2), de ha szles krben hasznlhat vlto-
zatot akarunk, akkor a standard knyvtr string osztlyra lesz szksgnk.
A C++ a C-tl rklt, nullval lezrt karaktertmbknt rtelmezett karakterlncok (vagyis
a C stlus karakterlncok) kezelsre a standard knyvtrban szmos kln fggvnyt biz-
tost (20.4.1).
20.2. Karakterek
A karakter (character) mr nmagban is rdekes fogalom. Figyeljk meg pldul a C ka-
raktert. Ezt a C bett, amely valjban egy egyszer flkrv a papron (vagy a kpernyn),
hnapokkal ezeltt gpeltem be a szmtgpembe. Ott egy 8 bites bjtban a 67 szmr-
tkknt troldott. Elmondhatjuk rla, hogy a latin bc harmadik betje, a hatodik atom
(a szn, carbon) szoksos rvidtse s mellesleg egy programozsi nyelv neve is (1.6).
A karakterlncok programokban val felhasznlsakor csak az szmt, hogy e kacskarings
alakzathoz kapcsoldik egy hagyomnyos jelents, amit karakternek neveznk, s egy
szmrtk, amit a szmtgp hasznl. Hogy bonyoltsuk a dolgokat, ugyanahhoz a karak-
terhez a klnbz karakterkszletekben ms-ms szmrtk tartozhat, st, nem is minden
karakterkszletben tallunk szmrtket minden karakterhez, radsul sok klnbz ka-
rakterkszletet hasznlunk rendszeresen. A karakterkszlet nem ms, mint a karakterek
(a hagyomnyos szimblumok) egy lekpezse egsz rtkekre.
A C++ programozk ltalban felttelezik, hogy a szabvnyos amerikai karakterkszlet
(ASCII) rendelkezsnkre ll, de a C++ felkszlt arra az esetre is, ha bizonyos karakterek
hinyoznnak a programozsi krnyezetbl. Pldul ha olyan karakterek hinyoznak, mint
a [ vagy a {, hasznlhatunk helyettk kulcsszavakat vagy digrf kt tagbl ll jeleket
(C.3.1).
A standard knyvtr 776
Nagy kihvst jelentenek azok a karakterkszletek is, melyekben olyan karakterek szerepel-
nek, amelyek az ASCII-ban nem fordulnak el. Az olyan nyelvek karakterei, mint a knai,
a dn, a francia, az izlandi vagy a japn, nem rhatk le hibtlanul az ASCII karakterkszlet
segtsgvel. Mg nagyobb problma, hogy az e nyelvekhez hasznlt karakterkszletek is
klnbznek. A latin bct hasznl eurpai nyelvek karakterei pldul majdnem elfr-
nek egy 256 karakteres karakterkszletben, de sajnos a klnbz nyelvekhez klnbz
kszleteket hasznlunk s ezekben nha klnbz karaktereknek ugyanaz az egsz rtk
jutott. A francia karakterkszlet (amely Latin1-et hasznl) pldul nem teljesen egyeztethe-
t ssze az izlandi karakterekkel (gy azok hasznlathoz a Latin2 kszletre van szks-
gnk). Azok a nagyratr ksrletek, melyek sorn megprbltak minden, emberek ltal
ismert karaktert egyetlen karakterkszletben felsorolni, sok problmt megoldottak, de
mg a 16 bites kszletek (pldul a Unicode) sem elgtettek ki minden ignyt. A 32 bites
karakterkszletek, melyek ismereteim szerint az sszes karakter megjellsre alkalma-
sak lennnek, mg nem terjedtek el szles krben.
A C++ alapveten azt a megkzeltst kveti, hogy a programoznak megengedjk brme-
lyik karakterkszlet hasznlatt a karakterlncok karaktertpusnak megadshoz. Hasznl-
hatunk bvtett karakterkszleteket s ms rendszerre tltethet szmkdolst is (C.3.3)
20.2.1. Karakterjellemzk
A 13.2 pontban mr bemutattuk, hogy egy karakterlnc elmletileg tetszleges tpust k-
pes karakterknt hasznlni, ha az megfelel msolsi mveleteket biztost. Csak azokat
a tpusokat tudjuk azonban igazn hatkonyan s egyszeren kiaknzni, melyeknek nincs
felhasznli msol mvelete. Ezrt a szabvnyos string osztly megkveteli, hogy a ben-
ne karakterknt hasznlt tpusnak ne legyen felhasznli msol mvelete. Ez azt is lehe-
tv teszi, hogy a karakterlncok ki- s bevitele egyszer s hatkony legyen.
Egy karaktertpus jellemzit (traits) a hozz tartoz char_traits osztly rja le, amely az
albbi sablon specializcija:
template<class Ch> struct char_traits { };
Minden char_traits az std nvtrben szerepel s a szabvnyos vltozatok a <string> fejl-
lomnybl rhetk el. Az ltalnos char_traits osztly egyetlen jellemzt sem tartalmaz,
azokkal csak az egyes karaktertpusokhoz ksztett vltozatok rendelkeznek.
A char_traits<char> defincija pldul a kvetkez:
20. Karakterlncok 777
template<> struct char_traits<char> { // a char_traits mveleteknek nem szabad
// kivtelt kivltaniuk
typedef char char_type; // a karakter tpusa
static void assign(char_type&, const char_type&); // az = meghatrozsa a
// char_type szmra
// a karakterek egsz rtk brzolsa
typedef int int_type; // a karakter-rtkek egsz tpusa
static char_type to_char_type(const int_type&); // talakts int-rl char-ra
static int_type to_int_type(const char_type&); // talakts char-rl int-re
static bool eq_int_type(const int_type&, const int_type&); // ==
// char_type sszehasonltsok
static bool eq(const char_type&, const char_type&); // ==
static bool lt(const char_type&, const char_type&); // <
// mveletek s[n] tmbn
static char_type* move(char_type* s, const char_type* s2, size_t n);
static char_type* copy(char_type* s, const char_type* s2, size_t n);
static char_type* assign(char_type* s, size_t n, char_type a);
static int compare(const char_type* s, const char_type* s2, size_t n);
static size_t length(const char_type*);
static const char_type* find(const char_type* s, int n, const char_type&);
// bemeneti/kimeneti mveletek
typedef streamoff off_type; // eltols az adatfolyamban
typedef streampos pos_type; // pozci az adatfolyamban
typedef mbstate_t state_type; // tbb bjtos adatfolyam llapota
static int_type eof(); // fjl vge
static int_type not_eof(const int_type& i); // i, hacsak i rtke nem eof();
static state_type get_state(pos_type p); // p pozcin lev karakter
// llapota a tbb bjtos talakts sorn
};
A szabvnyos karakterlnc-sablon, a basic_string (20.3) megvalstsa e tpusokon s
fggvnyeken alapul. A basic_string-hez hasznlt karaktertpusnak rendelkeznie kell egy
olyan char_traits specializcival, amely mindezeket tmogatja.
A standard knyvtr 778
Ahhoz, hogy egy tpus char_type lehessen, kpes kell legyen arra, hogy minden karakter-
hez hozzrendeljen egy egsz rtket, melynek tpusa int_type. Az int_type s a char_type
tpus kztti talaktst a to_char_type() s a to_int_type() fggvny hajtja vgre. A char t-
pus esetben ez a talakts igen egyszer.
A move(s,s2,n) s a copy(s,s2,n) fggvnyek egyarnt az s2 cmrl msolnak t n karaktert
az s cmre, s mindketten az assign(s[i], s2[i]) utastst hasznljk. A klnbsg az, hogy
a move() akkor is helyesen mkdik, ha s2 az [s,s+n[ tartomnyba esik, a copy() viszont ki-
csit gyorsabb. Ez a mkdsi elv pontosan megegyezik a C standard knyvtr memcpy(),
illetve memmove() (19.4.6) fggvnyeinek mkdsvel. Az assign(s,n,x) fggvnyhvs
az x karakter n darab msolatt rja az s cmre az assign(s[i],x) utasts segtsgvel.
A compare() fggvny a lt(), illetve az eq() eljrsokat hasznlja a karakterek sszehason-
ltshoz. Visszatrsi rtke egy int, amely 0, ha a kt karakterlnc pontosan megegyezik;
negatv szm, ha az els paramter bcsorrendben elbb kvetkezik, mint a msodik;
fordtott esetben pozitv szm. A visszatrsi rtk ilyen hasznlata a C standard knyvtr
strcmp() fggvnynek mkdst kveti (20.4.1).
A ki- s bemenethez kapcsold fggvnyeket az alacsonyszint I/O mveletek hasznljk
(21.6.4).
A szles karakterek (amelyek a wchar_t osztly pldnyai, 4.3) nagyon hasonltanak az
egyszer char tpushoz, de kett vagy mg tbb bjtot hasznlnak. A wchar_t tpus tulaj-
donsgait a char_traits<wchar_t> osztly rja le:
template<> struct char_traits<wchar_t> {
typedef wchar_t char_type;
typedef wint_t int_type;
typedef wstreamoff off_type;
typedef wstreampos pos_type;
// mint a char_traits<char>
};
A wchar_t tpust elssorban a 16 bites karakterkszletek (pldul a Unicode) karaktereinek
trolshoz hasznljuk.
20. Karakterlncok 779
20.3. A basic_string osztly
A standard knyvtr karakterlncokhoz kapcsold szolgltatsai a basic_string sablonon
(template) alapulnak, amely hasonl tpusokat s mveleteket biztost, mint a szabvnyos
trolk (16.3):
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class std::basic_string {
public:
// ...
};
A sablon s a hozz kapcsold szolgltatsok az std nvtrhez tartoznak s a <string> fej-
llomnyon keresztl rhetk el.
A leggyakoribb karakterlnc-tpusok hasznlatt kt typedef knnyti meg:
typedef basic_string<char> string;
typedef basic_string<wchar_t> wstring;
A basic_string sok mindenben hasonlt a vector (16.3) osztlyra. A legfontosabb eltrs,
hogy a basic_string nem tartalmazza az sszes eljrst, amely a vector osztlyban megtall-
hat, helyettk nhny, jellegzetesen karakterlncokra vonatkoz mveletet (pldul rsz-
lnc-keresst) biztost. A string osztlyt nem rdemes egy egyszer tmbbel vagy a vector
tpussal megvalstani; a karakterlncok nagyon sok felhasznlsi mdja jobban biztostha-
t gy, ha a msolsok mennyisgt a lehet legkevesebbre cskkentjk, nem hasznlunk
dinamikus adatterletet a rvid karakterlncokhoz, a hosszabbaknl egyszer mdostha-
tsgot biztostunk s gy tovbb (20.6[12]). A string osztly fggvnyeinek szma jelzi
a karakterlncok kezelsnek fontossgt, s azt is, hogy bizonyos szmtgpek klnle-
ges hardverutastsokkal segtik ezeket a mveleteket. A knyvtrak kszti akkor hasznl-
hatjk ki legjobban az ilyen fggvnyek elnyeit, ha a standard knyvtrban tallnak
hasonlkat.
A standard knyvtr ms tpusaihoz hasonlan a basic_string<T> is egy konkrt tpus
(2.5.3, 10.3), virtulis fggvnyek nlkl. Btran felhasznlhatjuk tagknt egy magasabb
szint szvegfeldolgoz osztlyban, de arra nem val, hogy ms osztlyok bzisosztlya le-
gyen (25.2.1, lsd mg 20.6[10]).
A standard knyvtr 780
20.3.1. Tpusok
A vector osztlyhoz hasonlan a basic_string is tpusneveken keresztl teszi elrhetv
a vele kapcsolatban ll tpusokat:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// tpusok (mint a vector-nl, a list-nl stb., 16.3.1)
typedef Tr traits_type; // a basic_string-re jellemz
typedef typename Tr::char_type value_type;
typedef A allocator_type;
typedef typename A::size_type size_type;
typedef typename A::difference_type difference_type;
typedef typename A::reference reference;
typedef typename A::const_reference const_reference;
typedef typename A::pointer pointer;
typedef typename A::const_pointer const_pointer;
typedef megvalsts_fgg iterator;
typedef megvalsts_fgg const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
// ...
};
A basic_string az egyszer basic_string<char> tpus mellett (melyet string nven ismernk)
szmos karaktertpusbl tud karakterlncot kpezni:
typedef basic_string<unsigned char> Ustring;
struct Jchar { /* ... */ }; // japn karaktertpus
typedef basic_string<Jchar> Jstring;
Az ilyen karakterekbl kpzett karakterlncok ugyangy hasznlhatk, mint a char tpuson
alapulk, csak a karakterek szerepe szabhat hatrt:
Ustring first_word(const Ustring& us)
{
Ustring::size_type pos = us.find(' '); // 20.3.11
20. Karakterlncok 781
return Ustring(us,0,pos); // 20.3.4
}
Jstring first_word(const Jstring& js)
{
Jstring::size_type pos = js.find(' '); // 20.3.11
return Jstring(js,0,pos); // 20.3.4
}
Termszetesen hasznlhatunk olyan sablonokat is, melyek karakterlnc-paramtereket
hasznlnak:
template<class S> S first_word(const S& s)
{
typename S::size_type pos = s.find(' '); // 20.3.11
return S(s,0,pos); // 20.3.4
}
A basic_string<Ch> brmilyen karaktert tartalmazhat, amely szerepel a Ch tpusban, teht
pldul a 0 (nulla) karakter is elfordulhat a karakterlnc belsejben. A Ch karaktertpus-
nak gy kell viselkednie, mint egy karakternek, teht nem lehet felhasznli msol
konstruktora, destruktora, vagy msol rtkadsa.
20.3.2. Bejrk
A tbbi trolhoz hasonlan a string osztly is biztost nhny bejrt (itertort), melyek-
kel vgighaladhatunk az elemeken, akr a szoksos, akr fordtott sorrendben:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// bejrk (mint a vector-nl, a list-nl stb., 16.3.2)
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
// ...
};
A standard knyvtr 782
Mivel a string osztlyban megtallhatk a bejrk kezelshez szksges tagtpusok s
-fggvnyek, a szabvnyos algoritmusok (18. fejezet) string objektumokra is hasznlhatk:
void f(string& s)
{
string::iterator p = find(s.begin(),s.end(),'a');
// ...
}
A karakterlncokra leggyakrabban alkalmazott mveleteket kzvetlenl a string osztlyban
tallhatjuk meg. Remlhetleg ezeket a mveleteket kifejezetten a karakterlncokhoz iga-
ztottk, gy jobbak, mint az ltalnos algoritmusok.
A szabvnyos algoritmusok (18. fejezet) a karakterlncok esetben nem annyira hasznosak,
mint els rnzsre gondolnnk. Az ltalnos mveletek azt felttelezik, hogy a trolk ele-
mei nmagukban is rtelmes egysget alkotnak, de a karakterlncok esetben a teljes ka-
raktersorozat hordozza a lnyeges informcit. Egy karakterlnc rendezse (pontosabban
a karakterlnc karaktereinek rendezse) szinte teljesen megsemmisti a benne trolt infor-
mcikat, annak ellenre, hogy az ltalnos trolkban a rendezs inkbb mg hasznlha-
tbb szokta tenni az adatokat.
A string osztly bejri sem ellenrzik, hogy rvnyes tartomnyban llnak-e.
20.3.3. Az elemek elrse
A string-ek karaktereit egyesvel is elrhetjk, indexels segtsgvel:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// elemek elrse (mint a vector-nl, 16.3.3):
const_reference operator[ ](size_type n) const; // nem ellenrztt hozzfrs
reference operator[ ](size_type n);
const_reference at(size_type n) const; // ellenrztt hozzfrs
reference at(size_type n);
// ...
};
20. Karakterlncok 783
Ha az at() fggvny hasznlatakor a megengedett tartomnyon kvli indexrtket (sorsz-
mot) adunk meg, out_of_range kivtel vltdik ki.
A vector osztlytl eltren a string nem tartalmazza a front() s a back() fggvnyt. Ha
a karakterlnc els vagy utols elemre akarunk hivatkozni, az s[0] vagy az s[s.length()-1]
kifejezst kell hasznlnunk. A mutattmb egyenrtksg (5.3) a karakterlncok eset-
ben nem teljesl: ha s egy string, akkor a &s[0] nem egyezik meg s rtkvel.
20.3.4. Konstruktorok
A kezdeti rtkadshoz, illetve a msolsi mveletek elvgzshez a string ms fggvnye-
ket knl, mint az egyb trolk (16.3.4):
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// konstruktorok stb. (csak nagyjbl gy, mint a vector-nl s a list-nl, 16.3.4)
explicit basic_string(const A& a = A());
basic_string(const basic_string& s,
size_type pos = 0, size_type n = npos, const A& a = A());
basic_string(const Ch* p, size_type n, const A& a = A());
basic_string(const Ch* p, const A& a = A());
basic_string(size_type n, Ch c, const A& a = A());
template<class In> basic_string(In first, In last, const A& a = A());
~basic_string();
static const size_type npos; // "minden karakter"
// ...
};
Egy string objektumnak C stlus karakterlnccal, msik string objektummal, annak egy
rszvel, vagy karakterek egy sorozatval adhatunk kezdrtket, karakterrel vagy egsz
rtkkel nem:
void f(char* p,vector<char>&v)
{
string s0; // res karakterlnc
string s00 = ""; // ez is res karakterlnc
A standard knyvtr 784
string s1 = 'a'; // hiba: nincs konverzi char-rl string-re
string s2 = 7; // hiba: nincs konverzi int-rl string-re
string s3(7); // hiba: a konstruktornak nem lehet egy int paramtere
string s4(7,'a'); // 7 darab 'a', vagyis "aaaaaaa"
string s5 = "Frodo"; // "Frodo" msolata
string s6 = s5; // s5 msolata
string s7(s5,3,2); // s5[3] s s5[4], vagyis "do"
string s8(p+7,3); // p[7], p[8], s p[9]
string s9(p,7,3); // string(string(p),7,3), valsznleg "kltsges"
string s10(v.begin(),v.end()); // v minden karakternek msolsa
}
A karakterek helyt nulltl kezdve indexeljk, teht egy karakterlnc nem ms, mint 0-tl
length()-1-ig szmozott karakterek sorozata.
A length() fggvny a string esetben a size() megfelelje: mindkett a karakterlncban
szerepl karakterek szmt adja meg. Figyeljnk r, hogy ezek nem egy C stlus karakter-
lnc hosszt llaptjk meg, teht nem szmoljk a lezr nullkaraktert (20.4.1).
A basic_string osztlyt a lezr karakter alkalmazsa helyett a karakterlnc hossznak tro-
lsval illik megvalstani.
A rszlncokat a kezdpozci s a karakterszm megadsval azonosthatjuk. Az alaprtel-
mezett npos rtke a lehet legnagyobb szm, jelentse az sszes elem.
Nincs olyan konstruktor, amely n darab meghatrozatlan karakterbl hoz ltre karakterln-
cot. Ehhez legkzelebb taln az a konstruktor ll, amely egy adott karakter n pldnybl
pt fel egy karakterlncot. Az olyan konstruktorok hinya, melyeknek egyetlen karaktert
vagy csak a karakterlncban lv karakterek szmt adnnk meg, lehetv teszi, hogy a for-
dt szrevegyen olyan hibalehetsgeket, amilyeneket az s2 s az s3 fenti meghatrozsa
rejt magban.
A msol konstruktor egy ngyparamter konstruktor. E paramterek kzl hromnak
alaprtelmezett rtke van. A hatkonysg rdekben kt klnll konstruktorknt is el-
kszthetjk; a felhasznl nem lesz kpes klnbsget tenni a kt megolds kztt, ha a le-
fordtott kdot meg nem nzi.
20. Karakterlncok 785
A legltalnosabb konstruktor egy sablon tagfggvnye. Ez lehetv teszi, hogy a karakter-
lnc kezdrtkt tetszleges sorozatbl lltsuk el, pldul egy ms karaktertpust hasz-
nl karakterlnc segtsgvel, amennyiben a karaktertpusok kztti konverzi rendelke-
zsnkre ll:
void f(string s)
{
wstring ws(s.begin(),s.end()); // s minden karakternek msolsa
// ...
}
A ws karakterlnc minden wchar_t karakternek az s megfelel char eleme ad kezdrtket.
20.3.5. Hibk
A karakterlncokat igen egyszeren olvashatjuk, rhatjuk, megjelenthetjk, trolhatjuk,
sszehasonlthatjuk, lemsolhatjuk stb. Ez ltalban nem okoz problmkat, legfeljebb
a hatkonysggal tmadhatnak gondjaink. Ha azonban elkezdnk karakterlncok egyes
rszlncaival, karaktereivel foglalkozni s gy ltez karakterlncokbl akarunk jakat
sszelltani, elbb-utbb hibkat fogunk elkvetni, melynek kvetkeztben a karakterlnc
hatrain kvlre prblunk meg rni.
Az egyes karakterek kzvetlen elrsre szolgl at() fggvny ellenrzi az ilyen hibkat
s out-of_range kivtelt vlt ki, ha rvnytelen hivatkozst adunk meg. A [ ] opertor ilyen
vizsglatot nem vgez.
A legtbb karakterlnc-mveletnek egy karakterpozcit s egy karakterszmot kell meg-
adnunk. Ha a megadott pozcirtk nagyobb, mint a karakterlnc mrete, azonnal
out_of_range kivtelt kapunk; ha a karakterszm tl nagy, rtelmezse ltalban az lesz,
hogy az sszes htralv karaktert hasznlni akarjuk:
void f()
{
string s = "Snobol4";
string s2(s,100,2); // a megadott karakterpozci a lnc vgn tl van:
// out_of_range() vltdik ki
string s3(s,2,100); // a karakterszm tl nagy: egyenrtk a s3(s,2,s.size()-2)
// kifejezssel
string s4(s,2,string::npos); // az s[2]-tl kezdd sszes karakter
}
A standard knyvtr 786
A tl nagy karakterpozcikat ki kell szrnnk, de a tl nagy karakterszm hasznos le-
het a programokban. Valjban az npos a lehet legnagyobb size_type tpus rtk.
rdemes kiprblnunk, mi trtnik akkor, ha negatv karakterpozcit vagy karakterszmot
adunk meg:
void g(string& s)
{
string s5(s,-2,3); // nagy pozcirtk!: out_of_range()
string s6(s,3,-2); // nagy karakterszm!: rendben
}
Mivel a size_type tpust arra hasznljuk, hogy pozcikat vagy darabszmokat adjunk meg,
unsigned tpusknt definilt, gy a negatv szmok hasznlata csak flrevezet mdja a nagy
pozitv szmok megadsnak (16.3.4).
Azok a fggvnyek, amelyek egy string rszlnct keresik meg (20.3.11), az npos rtket
adjk vissza, ha nem talljk meg a megfelel rszt. Teht maguk nem vltanak ki kivtelt,
de ha ezt az npos rtket karakterpozciknt akarjuk felhasznlni a kvetkez mveletben,
akkor mr kivtelt kapunk.
Egy rszlnc kijellsnek msik mdja, hogy kt bejrt (iterator) adunk meg. Az els ha-
trozza meg a pozcit, mg a kt bejr klnbsge a karakterszmot. Szoks szerint
a bejrk nem ellenrzttek.
Ha C stlus karakterlncokat hasznlunk, a tartomnyellenrzs nehezebb feladat. Ha pa-
ramterknt adunk meg ilyen karakterlncot (teht egy char-ra hivatkoz mutatt),
a basic_string fggvnyei felttelezik, hogy a mutat nem 0. Ha egy C stlus karakterlnc-
ban pozcit adunk meg, a fggvnyek elvrjk, hogy a karakterlnc elg hossz legyen
a pozci rtelmezshez. Mindig legynk nagyon vatosak, st kifejezetten gyanakvak.
Az egyetlen kivtel, amikor karakterliterlokat hasznlunk.
Minden karakterlnc esetben igaz, hogy length()<npos. Nhny nagyon egyedi helyzetben
(igen ritkn) elfordulhat, hogy egy olyan jelleg mvelet, mint egy karakterlnc beszr-
sa egy msikba, tl hossz karakterlncot eredmnyez, amelyet a rendszer mr nem kpes
brzolni. Ebben az esetben length_error kivtel keletkezik:
string s(string::npos,'a'); // length_error() vltdik ki
20. Karakterlncok 787
20.3.6. rtkads
Termszetesen a karakterlncok esetben is rendelkezsnkre ll az (egyszer) rtkads
mvelete:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// rtkads (kicsit hasonlt a vector-ra s a list-re, 16.3.4):
basic_string& operator=(const basic_string& s);
basic_string& operator=(const Ch* p);
basic_string& operator=(Ch c);
basic_string& assign(const basic_string&);
basic_string& assign(const basic_string& s, size_type pos, size_type n);
basic_string& assign(const Ch* p, size_type n);
basic_string& assign(const Ch* p);
basic_string& assign(size_type n, Ch c);
template<class In> basic_string& assign(In first, In last);
// ...
};
A tbbi trolhoz hasonlan a string osztlyban is rtk szerinti rtelmezs mkdik, ami
azt jelenti, hogy amikor egy karakterlncot rtkl adunk egy msiknak, akkor az eredeti
karakterlncrl msolat kszl, s az rtkads utn kt, ugyanolyan tartalm, de nll
(rtk) karakterlnc ll majd rendelkezsnkre:
void g()
{
string s1 = "Knold";
string s2 = "Tot";
s1 = s2; // "Tot"-bl kt pldny lesz
s2[1] = 'u'; // s2 "Tut", s1 marad "Tot"
}
Annak ellenre, hogy egyetlen karakterrel nem adhatunk kezdrtket egy karakterlnc-
nak, az ilyen mdon trtn egyszer rtkads megengedett:
void f()
{
string s = 'a'; // hiba: kezdeti rtkads char-ral
s = 'a'; // rendben: egyszer rtkads
s = "a";
s = s;
}
A standard knyvtr 788
Az a lehetsg, hogy karakterlncnak rtkl adhatunk egy karaktert, nem tlsgosan hasz-
nos, s sok hibalehetsget rejt magban. Gyakran azonban nlklzhetetlen, hogy egy ka-
raktert a += mvelettel hozzfzhessnk egy karakterlnchoz (20.3.9), s igen furcsn nz-
ne ki, ha az s+='c' utasts vgrehajthat lenne, mg az s='c' nem.
Az rtkadshoz az assign() nevet hasznljuk, amely a tbbparamter konstruktorok meg-
feleljnek tekinthet (176.3.4, 20.3.4).
A 11.12 pontban mr emltettk, hogy a string osztly optimalizlhat gy, hogy amg
nincs szksg egy karakterlnc kt pldnyra, addig nem hajtjuk vgre a tnyleges mso-
lst. A szabvnyos string felptse tmogatja az ilyen takarkosan msol megvalstsok
ltrehozst, mert gy hatkonyan rhatunk le csak olvashat karakterlncokat, s a karak-
terlncok tadsa fggvnyek paramtereknt sokkal egyszerbben megvalsthat, mint
azt els rnzsre gondolnnk. Az azonban meggondolatlansg lenne, ha egy programoz
sajt fejlesztkrnyezetnek ellenrzse nlkl olyan programokat rna, amelyek a string-
ek optimalizlt msolsra tmaszkodnak (20.6[13]).
20.3.7. talakts C stlus karakterlncra
A 20.3.4 pontban bemutattuk, hogy a string objektumoknak val kezdeti s egyszer
rtkadsra egyarnt hasznlhatunk C stlus karakterlncokat. Fordtott irny mveletek
elvgzsre is van lehetsg, teht egy string karaktereit is elhelyezhetjk egy tmbben:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// talakts C stlus karakterlncc
const Ch* c_str() const;
const Ch* data() const;
size_type copy(Ch* p, size_type n, size_type pos = 0) const;
// ...
};
A data() fggvny a string karaktereit egy tmbbe msolja, majd visszaad egy erre a tmb-
re hivatkoz mutatt. A tmb a string objektumhoz tartozik, teht a felhasznlnak nem
szabad trlnie azt s a string egy nem const fggvnynek meghvsa utn mr nem tud-
hatja, milyen rtk van benne. A c_str() fggvny szinte pontosan ugyangy mkdik, csak
a karakterlnc vgn elhelyez egy 0 (null) karaktert is, a C stlus lezrsnak megfelelen:
20. Karakterlncok 789
void f()
{
string s = "equinox"; // s.length()==7
const char* p1 = s.data(); // p1 ht karakterre mutat
printf("p1 = %s\n",p1); // hiba: hinyz lezr
p1[2] = 'a'; // hiba: p1 konstans tmbre mutat
s[2] = 'a';
char c = p1[1]; // hiba: s.data() elrsnek ksrlete s mdostsa utn
const char* p2 = s.c_str(); // p2 nyolc karakterre mutat
printf("p2 = %s\n",p2); // rendben: c_str() hozzadja a lezr karaktert
}
A klnbsget gy is megfogalmazhatjuk, hogy a data() a karakterek egy tmbjt adja
vissza, mg a c_str() egy C stlus karakterlncot llt el. Ezen fggvnyek elsdleges fel-
adata, hogy knnyen hasznlhatv tegyk az olyan fggvnyeket, melyek C stlus karak-
terlncot vrnak paramterknt. gy teht a c_str() fggvnyt sokkal gyakrabban hasznl-
juk, mint a data() eljrst:
void f(string s)
{
int i = atoi(s.c_str()); // a karakterlnc int rtknek lekrse (20.4.1)
// ...
}
ltalban rdemes a karaktereket mindaddig egy string objektumban trolni, amg nincs r-
juk szksgnk, de ha mgsem tudjuk azonnal feldolgozni, akkor is rdemes tmsolni
azokat a c_str(), illetve a data() ltal lefoglalt terletrl egy kln tmbbe. A copy() fgg-
vny pontosan erre szolgl:
char* c_string(const string& s)
{
char* p = new char[s.length()+1]; // megjegyzs: +1
s.copy(p,string::npos);
p[s.length()] = 0; // megjegyzs: lezr hozzadsa
return p;
}
Az s.copy(p,n,m) fggvny legfeljebb n karaktert msol a p cmre az s[m] pozcitl kezd-
ve. Ha az s karakterlncbl n-nl kevesebb karaktert lehet csak tmsolni, a copy() egysze-
ren az sszes karaktert tmsolja.
A standard knyvtr 790
Figyeljnk r, hogy a string objektumokban szerepelhet a 0 karakter. A C stlus karakter-
lncokat kezel fggvnyek az els ilyen karaktert tekintik lezrnak. Teht mindig figyel-
jnk, hogy 0 karaktert csak akkor hasznljunk, ha C stlust hasznl fggvnyekre nem lesz
szksgnk, vagy a 0-kat pontosan oda tegyk, ahol a karakterlncot le szeretnnk zrni.
A C stlus karakterlncra val talaktst a c_str() helyett megoldhattuk volna egy operator
const char* () mvelettel is, az automatikus konverzi knyelmnek azonban az lenne az
ra, hogy meglepetsnkre idnknt akkor is vgbemenne, amikor nem is szmtunk r.
Ha gy rezzk, hogy programunkban sokszor lesz szksg a c_str() fggvnyre, valszn-
leg tlsgosan ragaszkodunk a C stlus fellethez. ltalban rendelkezsnkre llnak azok
az eszkzk, melyekkel a C stlus karakterlncokra vonatkoz mveletek kzvetlenl
string objektumokon is elvgezhetk. Ezek hasznlatval sok konverzit elkerlhetnk. Egy
msik lehetsges megolds az explicit konverzik elkerlsre az, hogy tlterheljk azokat
a fggvnyeket, melyek a c_str() hasznlatra knyszertenek bennnket:
extern "C" int atoi(const char*);
int atoi(const string& s)
{
return atoi(s.c_str());
}
20.3.8. sszehasonlts
Karakterlncokat azonos tpus karakterlncokkal vagy olyan karaktertmbkkel hasonlt-
hatunk ssze, melyek szintn ugyanolyan tpus karaktereket tartalmaznak:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
int compare(const basic_string& s) const; // > s == hasznlata egytt
int compare(const Ch* p) const;
int compare(size_type pos, size_type n, const basic_string& s) const;
int compare(size_type pos, size_type n,
const basic_string& s, size_type pos2, size_type n2) const;
int compare(size_type pos, size_type n, const Ch* p, size_type n2 = npos) const;
// ...
};
20. Karakterlncok 791
Ha a compare() meghvsakor a pozci s mret paramtereket is megadjuk, csak a kijellt
rszsorozat vesz rszt az sszehasonltsban. Pldul s.compare(pos,n,s2) egyenrtk
string(s,pos,n).compare(s2)-vel. Az sszehasonlt eljrst a char_traits<Ch> osztly
compare() fggvnye adja (20.2.1). gy az s.compare(s2) fggvny 0 rtket ad vissza, ha
a karakterlncok egyenl rtkek; negatv szmot kapunk, ha s bcsorrendben s2 eltt
ll; fordtott esetben pedig pozitv lesz az eredmny.
A felhasznl itt nem adhat meg gy sszehasonltsi felttelt, mint a 13.4 pontban. Ha ilyen
szint rugalmassgra van szksgnk, a lexicographical_compare() (18.9) segtsgvel k-
sztsnk a fenti rszben levhz hasonl sszehasonlt fggvnyt. Egy msik lehetsg,
hogy sajt ciklust runk a feladat megoldsra. A toupper() fggvny (20.4.2) pldul lehe-
tv teszi, hogy kis- s nagybetkkel nem foglalkoz sszehasonltst valstsunk meg:
int cmp_nocase(const string& s, const string& s2)
{
string::const_iterator p = s.begin();
string::const_iterator p2 = s2.begin();
while (p!=s.end() && p2!=s2.end()) {
if (toupper(*p)!=toupper(*p2)) return (toupper(*p)<toupper(*p2)) ? -1 : 1;
++p;
++p2;
}
return (s2.size()==s.size()) ? 0 : (s.size()<s2.size()) ? -1 : 1; // 'size' eljel nlkli
}
void f(const string& s, const string& s2)
{
if (s == s2) { // kis- s nagybetket figyelembe vev sszehasonlts s s s2 kztt
// ...
}
if (cmp_nocase(s,s2) == 0) { // kis- s nagybetket figyelmen kvl hagy
// sszehasonlts s s s2 kztt
// ...
}
// ...
}
A basic_string osztlyban rendelkezsnkre llnak a szoksos sszehasonlt opertorok is
(==, !=, <, >, <=, >=):
A standard knyvtr 792
template<class Ch, class Tr, class A>
bool operator==(const basic_string<Ch,Tr,A>&, const basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
bool operator==(const Ch*, const basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
bool operator==(const basic_string<Ch,Tr,A>&, const Ch*);
// s ugyanilyen deklarcik a !=, >, <, >=, s a <= szmra
Az sszehasonlt opertorok nem tag fggvnyek, gy a konverzik mindkt operandusra
ugyangy vonatkoznak (11.2.3). A C stlus karakterlncokat hasznl vltozatokra azrt
volt szksg, hogy a literlokkal val sszehasonltst hatkonyabb tegyk:
void f(const string& name)
{
if (name =="Obelix" || "Asterix"==name) { // optimalizlt == hasznlata
// ...
}
}
20.3.9. Beszrs
Miutn ellltottunk egy karakterlncot, sokfle mveletet vgezhetnk vele. A karakter-
lnc rtkt mdost fggvnyek kzl taln a legfontosabb a hozzfzs, amely a karak-
terlnc vgn helyez el j karaktereket. Az ltalnos beszr mveletekre ritkbban van
szksg:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// karakterek hozzadsa (*this)[length()-1] utn
basic_string& operator+=(const basic_string& s);
basic_string& operator+=(const Ch* p);
basic_string& operator+=(Ch c);
void push_back(Ch c);
basic_string& append(const basic_string& s);
basic_string& append(const basic_string& s, size_type pos, size_type n);
basic_string& append(const Ch* p, size_type n);
20. Karakterlncok 793
basic_string& append(const Ch* p);
basic_string& append(size_type n, Ch c);
template<class In> basic_string& append(In first, In last);
// karakterek beszrsa (*this)[pos] el
basic_string& insert(size_type pos, const basic_string& s);
basic_string& insert(size_type pos, const basic_string& s, size_type pos2, size_type n);
basic_string& insert(size_type pos, const Ch* p, size_type n);
basic_string& insert(size_type pos, const Ch* p);
basic_string& insert(size_type pos, size_type n, Ch c);
// karakterek beszrsa p el
iterator insert(iterator p, Ch c);
void insert(iterator p, size_type n, Ch c);
template<class In> void insert(iterator p, In first, In last);
// ...
};
Nagyjbl ugyanazok a fggvnyvltozatok llnak rendelkezsnkre a beszr s a hozz-
fz eljrsoknl is, mint a konstruktorok s az rtkads esetben.
A += opertor hagyomnyos jellse a hozzfzsnek:
string complete_name(const string& first_name, const string& family_name)
{
string s = first_name;
s += ' ';
s += family_name;
return s;
}
A karakterlnc vghez val hozzfzs jelentsen hatkonyabb lehet, mint a ms pozci-
kra val beszrs:
string complete_name2(const string& first_name, const string& family_name)
// szegnyes algoritmus
{
string s = family_name;
s.insert(s.begin(),' ');
s.insert(0,first_name);
return s;
}
A standard knyvtr 794
A beszrs gyakran arra knyszerti a string-et, hogy lass memriamveleteket vgezzen
s thelyezzen nhny karaktert.
Mivel a string osztlyban is szerepel a push_back() mvelet (16.3.5), a back_inserter
ugyangy hasznlhat string objektumokhoz, mint brmely ltalnos trolhoz.
20.3.10. sszefzs
A hozzfzs klnleges vltozata az sszefzsnek (konkatencinak). Az sszefzst
teht egy karakterlnc ellltst gy, hogy kt msikat egyms utn helyeznk a +
opertor valstja meg:
template<class Ch, class Tr, class A>
basic_string<Ch,Tr,A>
operator+(const basic_string<Ch,Tr,A>&, const basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
basic_string<Ch,Tr,A> operator+(const Ch*, const basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
basic_string<Ch,Tr,A> operator+(Ch, const basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
basic_string<Ch,Tr,A> operator+(const basic_string<Ch,Tr,A>&, const Ch*);
template<class Ch, class Tr, class A>
basic_string<Ch,Tr,A> operator+(const basic_string<Ch,Tr,A>&, Ch);
Szoks szerint, a + mveletet nem tag fggvnyknt adjuk meg. A tbb paramtert haszn-
l sablonok esetben ez nmi kellemetlensget okoz, mert a sablonparamtereket meg kell
ismtelnnk.
Msrszt az sszefzs hasznlata egyszer s knyelmes:
string complete_name3(const string& first_name, const string& family_name)
{
return first_name + ' ' + family_name;
}
Ez a knyelem megr egy kis futsi idej teljestmnyvesztesget a complete_name() fgg-
vnyhez viszonytva. A complete_name3() fggvnyben kln ideiglenes vltozra
(11.3.2) van szksgnk. Vlemnyem szerint ez ritkn fontos, de azrt rdemes tudnunk
20. Karakterlncok 795
rla, ha egy nagy ciklust runk egy olyan programban, ahol figyelnnk kell a teljestmny-
re. Ilyenkor esetleg rdemes megszntetnnk a fggvnyhvst a complete_name() hely-
ben (inline) kifejtsvel, az eredmnyknt kapott karakterlncot gy helyben pthetjk fel,
alacsonyabb szint mveletek segtsgvel (20.6[14]).
20.3.11. Keress
Zavarba ejt mennyisgben llnak rendelkezsnkre olyan fggvnyek, melyekkel egy ka-
rakterlnc rszlncait kereshetjk meg:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// rszsorozat kerse (mint a search(), 18.5.5)
size_type find(const basic_string& s, size_type i = 0) const;
size_type find(const Ch* p, size_type i, size_type n) const;
size_type find(const Ch* p, size_type i = 0) const;
size_type find(Ch c, size_type i = 0) const;
// rszsorozat keresse visszafel (mint a find_end(), 18.5.5)
size_type rfind(const basic_string& s, size_type i = npos) const;
size_type rfind(const Ch* p, size_type i, size_type n) const;
size_type rfind(const Ch* p, size_type i = npos) const;
size_type rfind(Ch c, size_type i = npos) const;
// karakter keresse (mint a find_first_of(), 18.5.2)
size_type find_first_of(const basic_string& s, size_type i = 0) const;
size_type find_first_of(const Ch* p, size_type i, size_type n) const;
size_type find_first_of(const Ch* p, size_type i = 0) const;
size_type find_first_of(Ch c, size_type i = 0) const;
// karakter keresse paramter alapjn visszafel
size_type find_last_of(const basic_string& s, size_type i = npos) const;
size_type find_last_of(const Ch* p, size_type i, size_type n) const;
size_type find_last_of(const Ch* p, size_type i = npos) const;
size_type find_last_of(Ch c, size_type i = npos) const;
// paramterben nem szerepl karakter keresse
A standard knyvtr 796
size_type find_first_not_of(const basic_string& s, size_type i = 0) const;
size_type find_first_not_of(const Ch* p, size_type i, size_type n) const;
size_type find_first_not_of(const Ch* p, size_type i = 0) const;
size_type find_first_not_of(Ch c, size_type i = 0) const;
// paramterben nem szerepl karakter keresse visszafel
size_type find_last_not_of(const basic_string& s, size_type i = npos) const;
size_type find_last_not_of(const Ch* p, size_type i, size_type n) const;
size_type find_last_not_of(const Ch* p, size_type i = npos) const;
size_type find_last_not_of(Ch c, size_type i = npos) const;
// ...
};
Az sszes fenti fggvny const. Teht arra hasznlhatk, hogy valamilyen clbl megkeres-
senek egy rszlncot, de maguk nem vltoztatjk meg azt a karakterlncot, amelyre alkal-
maztuk azokat.
A basic_string::find mveletek jelentst gy rthetjk meg legjobban, ha megrtjk a ve-
lk egyenrtk ltalnos algoritmusokat:
void f()
{
string s = "accdcde";
typedef ST;
string::size_type i1 = s.find("cd"); // i1 = 2 s[2]=='c' && s[3]=='d'
string::size_type i2 = s.rfind("cd"); // i2 = 4 s[4]=='c' && s[5]=='d'
string::size_type i3 = s.find_first_of("cd"); // i3 = 1 s[1] == 'c'
string::size_type i4 = s.find_last_of("cd"); // i4 = 5 s[5] == 'd'
string::size_type i5 = s.find_first_not_of("cd"); // i5 = 0 s[0]!='c' && s[0]!='d'
string::size_type i6 = s.find_last_not_of("cd"); // i6 = 6 s[6]!='c' && s[6]!='d'
}
Ha a find() fggvnyek nem talljk meg, amit kellene, npos rtket adnak vissza, amely r-
vnytelen karakterpozcit jelent. Ha az npos rtket karakterpozciknt hasznljuk,
range_error kivtelt kapunk (20.3.5). Figyeljnk r, hogy a find() eredmnye unsigned rtk.
20.3.12. Csere
Miutn meghatroztunk egy karakterpozcit a karakterlncban, az indexels segtsgvel
az egyes karaktereket mdosthatjuk, de a replace() mvelet felhasznlsval lecserlhe-
tnk teljes rszlncokat is:
20. Karakterlncok 797
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// [ (*this)[i], (*this)[i+n] [ felcserlse ms karakterekkel
basic_string& replace(size_type i, size_type n, const basic_string& s);
basic_string& replace(size_type i, size_type n,
const basic_string& s, size_type i2, size_type n2);
basic_string& replace(size_type i, size_type n, const Ch* p, size_type n2);
basic_string& replace(size_type i, size_type n, const Ch* p);
basic_string& replace(size_type i, size_type n, size_type n2, Ch c);
basic_string& replace(iterator i, iterator i2, const basic_string& s);
basic_string& replace(iterator i, iterator i2, const Ch* p, size_type n);
basic_string& replace(iterator i, iterator i2, const Ch* p);
basic_string& replace(iterator i, iterator i2, size_type n, Ch c);
template<class In> basic_string& replace(iterator i, iterator i2, In j, In j2);
// karakterek trlse a lncbl ("csere semmire")
basic_string& erase(size_type i = 0, size_type n = npos);
iterator erase(iterator i);
iterator erase(iterator first, iterator last);
void clear(); // az sszes karakter trlse
// ...
};
Az j karakterek szmnak nem kell megegyeznie a karakterlncban eddig szerepl karak-
terek szmval. A karakterlnc mrete gy vltozik, hogy az j rszlnc pontosan elfrjen
benne. Valjban az erase() fggvny sem csinl mst, mint hogy a megadott rszlncot
res karakterlncra cserli s az eredeti karakterlnc mrett megfelelen mdostja:
void f()
{
string s = "Mrpedig ez mkdik, ha elhiszed, ha nem.";
s.erase(0,8); // a "Mrpedig " trlse
s.replace(s.find("ez"),2,"Csak akkor");
s.replace(s.find(", ha nem"),8,""); // trls "" behelyettestsvel
}
Az erase() fggvny egyszer, paramter nlkli meghvsa a teljes karakterlncot res ka-
rakterlncc alaktja. Ezt a mveletet az ltalnos trolk esetben a clear() fggvny val-
stja meg (16.3.6).
A standard knyvtr 798
A replace() fggvny vltozatai az rtkads vltozataihoz igazodnak, hiszen a replace() va-
ljban nem ms, mint rtkads egy rszlncnak.
20.3.13. Rszlncok
A substr() fggvny lehetv teszi, hogy kivlasszunk egy rszlncot, amelyet kezdpoz-
cijval s hosszval adunk meg:
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// rszlnc cmzse
basic_string substr(size_type i = 0, size_type n = npos) const;
// ...
};
A substr() fggvny egyszer mdszert biztost a karakterlnc egy rszletnek kiolvass-
ra. Ebbl a szemszgbl a prja a replace() fggvny, mellyel fellrhatjuk a karakterlnc
egy rszt. Mindkt fggvnynek a kezdpozcit s a rszlnc hosszt kell megadnunk.
A find() segtsgvel viszont mr rtk szerint is megtallhatunk rszlncokat, gy ez a fgg-
vnycsoport lehetv teszi, hogy definiljunk egy rszlnc-osztlyt, amelyet rni s olvasni
is tudunk:
template<class Ch> class Basic_substring {
public:
typedef typename basic_string<Ch>::size_type size_type;
Basic_substring(basic_string<Ch>& s, size_type i, size_type n); // s[i]..s[i+n-1]
Basic_substring(basic_string<Ch>& s, const basic_string<Ch>& s2); // s2 az s-ben
Basic_substring(basic_string<Ch>& s, const Ch* p); // *p az s-ben
Basic_substring& operator=(const basic_string<Ch>&); // rs *ps-be
Basic_substring& operator=(const Basic_substring<Ch>&);
Basic_substring& operator=(const Ch*);
Basic_substring& operator=(Ch);
operator basic_string<Ch>() const; // olvass *ps-be
operator const Ch* () const; // a c_str() hasznlata
20. Karakterlncok 799
private:
basic_string<Ch>* ps;
size_type pos;
size_type n;
};
Az osztly megvalstsa is nagyon egyszer:
template<class Ch>
Basic_substring<Ch>::Basic_substring(basic_string<Ch>& s, const basic_string<Ch>& s2)
: ps(&s), n(s2.length())
{
pos = s.find(s2);
}
template<class Ch>
Basic_substring<Ch>& Basic_substring<Ch>::operator=(const basic_string<Ch>& s)
{
ps->replace(pos,n,s); // rs *ps-be
return *this;
}
template<class Ch> Basic_substring<Ch>::operator basic_string<Ch>() const
{
return basic_string<Ch>(*ps,pos,n); // msols *ps-be
}
Ha s2 nem tallhat meg az s karakterlncban, pos rtke npos lesz, gy ha egy ilyen rsz-
lncot prblunk meg rni vagy olvasni, range_error kivtelt kapunk (20.3.5).
A Basic_substring osztly a kvetkez formban hasznlhat:
typedef Basic_substring<char> Substring;
void f()
{
string s = "Mary had a little lamb";
Substring(s,"lamb") = "fun";
Substring(s,"a little") = "no";
string s2 = "Joe" + Substring(s,s.find(' '),string::npos);
}
Termszetesen sokkal rdekesebb feladatokat is elvgezhetnnk, ha a Substring osztly
mintaillesztst is tudna vgezni (20.6[7]).
A standard knyvtr 800
20.3.14. Mret s kapacits
A memriakezelssel kapcsolatos krdseket a string nagyjbl ugyangy kezeli, mint
a vector (16.3.8):
template<class Ch, class Tr = char_traits<Ch>, class A = allocator<Ch> >
class basic_string {
public:
// ...
// mret, kapacits stb. (mint a 16.3.8 pontban)
size_type size() const; // karakterek szma (20.3.4)
size_type max_size() const; // a legnagyobb lehetsges karakterlnc
size_type length() const { return size(); }
bool empty() const { return size()==0; }
void resize(size_type n, Ch c);
void resize(size_type n) { resize(n,Ch()); }
size_type capacity() const; // mint a vector, 16.3.8
void reserve(size_type res_arg = 0); // mint a vector, 16.3.8
allocator_type get_allocator() const;
};
A reverse(res_arg) fggvny length_error kivtelt vlt ki, ha res_arg>max_size().
20.3.15. Ki- s bemeneti mveletek
A string osztly egyik legfontosabb felhasznlsi terlete, hogy bemeneti mveletek clja,
illetve kimeneti mveletek forrsa legyen. A basic_string I/O opertorait a <string> (s nem
az <iostream>) tartalmazza:
template<class Ch, class Tr, class A>
basic_istream<Ch,Tr>& operator>>(basic_istream<Ch,Tr>&, basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
basic_ostream<Ch,Tr>& operator<<(basic_ostream<Ch,Tr>&,const
basic_string<Ch,Tr,A>&);
template<class Ch, class Tr, class A>
basic_istream<Ch,Tr>& getline(basic_istream<Ch,Tr>&, basic_string<Ch,Tr,A>&, Ch eol);
template<class Ch, class Tr, class A>
basic_istream<Ch,Tr>& getline(basic_istream<Ch,Tr>&, basic_string<Ch,Tr,A>&);
20. Karakterlncok 801
A << a karakterlncot egy kimeneti adatfolyamra (ostream, 21.2.1) rja, mg a >> opertor
egy reshely karakterekkel hatrolt szt olvas be a karakterlncba egy bemeneti adatfo-
lyamrl (3.6, 21.3.1). A sz eltti szkz, tabultor, jsor stb. karaktereket egyszeren t-
ugorja s a sz utn kvetkez ilyen karakterek sem kerlnek bele a karakterlncba.
A getline() fggvny meghvsval egy teljes, eol karakterrel lezrt sort olvashatunk be a ka-
rakterlncba, a karakterlnc mrete pedig gy nvekszik, hogy a sor elfrjen benne (3.6).
Ha nem adjuk meg az eol paramtert, a fggvny az '\n' jsor karaktert hasznlja. A sorz-
r karaktert beolvassa az adatfolyamrl, de a karakterlncba nem kerl be. Mivel a string
mrete gy vltozik, hogy a beolvasott sor elfrjen benne, nincs szksg arra, hogy a lez-
r karaktert az adatfolyamban hagyjuk vagy visszaadjuk a beolvasott karakterek szmt
a hvnak. A karaktertmbk esetben a get() s a getline() knytelen volt ilyen, ellenr-
zst segt megoldsokat alkalmazni (21.3.4).
20.3.16. Felcserls
Ugyangy, mint a vector-nl (16.3.9), a swap() fggvnybl a karakterlncokhoz is sokkal
hatkonyabb specializcit kszthetnk, mint az ltalnos algoritmus:
template<class Ch, class Tr, class A>
void swap(basic_string<Ch,Tr,A>&, basic_string<Ch,Tr,A>&);
20.4. A C standard knyvtra
A C++ standard knyvtra rklte a C stlus karakterlncok kezelsvel foglalkoz fgg-
vnyeket. Ebben a pontban felsorolunk nhnyat a legfontosabb, C karakterlncokat keze-
l fggvnyek kzl. A lers egyltaln nem terjed ki mindre; ha rszletes informcikat
szeretnnk megtudni, nzzk meg fejlesztrendszernk kziknyvt. Legynk vatosak
ezen fggvnyek hasznlatakor, mert a knyvtrak kszti gyakran sajt, nem szabvnyos
fggvnyeket adnak meg a szabvnyos fejllomnyokban, gy nehz megllaptani, hogy
melyek a minden rendszerben elrhet fggvnyek.
A C standard knyvtrnak lehetsgeit ler fejllomnyok listjt a 16.1.2 pontban adtuk
meg. A memriakezel fggvnyekkel a 19.4.6 pontban foglalkozunk, a C ki- s bemene-
ti fggvnyeivel a 21.8 rszben, a C matematikai knyvtrval pedig a 22.3 pontban.
A standard knyvtr 802
A program indtsval s befejezsvel kapcsolatos fggvnyeket a 3.2 s a 9.4.1.1 pont-
ban mutattuk be, mg a meghatrozatlan fggvnyparamterek beolvasst segt fggv-
nyeket a 7.6 pont trgyalta. Azon C stlus fggvnyek, melyek a szles karaktereket keze-
lik, a <cwchar> s a <wchar.h> fejllomnyban tallhatk.
20.4.1. C stlus karakterlncok
A C stlus karakterlncok kezelsvel foglalkoz fggvnyek a <string.h> s a <cstring>
fejllomnyban tallhatk:
char* strcpy(char* p, const char* q); // msols q-bl p-be (a lezrval egytt)
char* strcat(char* p, const char* q); // hozzfzs q-bl p-be (a lezrval egytt)
char* strncpy(char* p, const char* q, int n); // n karakter msolsa q-bl p-be
char* strncat(char* p, const char* q, int n); // n karakter hozzfzse q-bl p-be
size_t strlen(const char* p); // p hossza (a lezr nlkl)
int strcmp(const char* p, const char* q); // p s q sszehasonltsa
int strncmp(const char* p, const char* q, int n); // az els n karakter sszehasonltsa
char* strchr(char* p, int c); // az els c keresse p-ben
const char* strchr(const char* p, int c);
char* strrchr(char* p, int c); // az utols c keresse p-ben
const char* strrchr(const char* p, int c);
char* strstr(char* p, const char* q); // az els q keresse p-ben
const char* strstr(const char* p, const char* q);
char* strpbrk(char* p, const char* q); // q els karakternek keresse p-ben
const char* strpbrk(const char* p, const char* q);
size_t strspn(const char* p, const char* q); // p karaktereinek szma az els,
// q-ban szerepl karakter eltt
size_t strcspn(const char* p, const char* q); // p karaktereinek szma az els, q-ban
nem szerepl karakter eltt
A megadott mutatkat a fggvnyek nem-nullknak felttelezik, azoknak a char tmbk-
nek pedig, melyekre a mutatk mutatnak, 0 karakterrel lezrt sorozatoknak kell lennik.
Az strn fggvnyek 0 karakterekkel tltik fel a hinyz terletet, ha nincs n darab feldol-
gozand karakter. A karakterlnc-sszehasonltsok nullt adnak vissza, ha a kt karakter-
lnc egyenl; negatv szmot, ha az els paramter bcsorrendben elbb kvetkezik,
mint a msodik; fordtott esetben pedig pozitv szmot.
20. Karakterlncok 803
Termszetesen a C nem biztost tlterhelt fggvnyprokat, a C++-ban azonban szksg
van ezekre a const biztonsgos megvalstshoz:
void f(const char* pcc, char* pc) // C++
{
*strchr(pcc,'a') = 'b'; // hiba: const char-nak nem adhatunk rtket
*strchr(pc,'a') = 'b'; // rendben, br nem tkletes: lehet, hogy pc-ben nincs 'a'
}
A C++ strchr() fggvnye nem engedi meg, hogy const karakterlncba rjunk, egy C prog-
ram azonban kihasznlhatja a C-beli strchr() gyengbb tpusellenrzst:
char* strchr(const char* p, int c); /* C standard knyvtrbeli fggvny, nem C++ */
void g(const char* pcc, char* pc) /* C, a C++ nem fordtja le */
{
*strchr(pcc,'a') = 'b'; /* const talaktsa nem const-t: C-ben j, C++-ban hiba */
*strchr(pc,'a') = 'b'; /* j C-ben s C++-ban is */
}
Ha tehetjk, felttlenl kerljk a C stlus karakterlncok hasznlatt s hasznljuk helyet-
tk a string osztlyt. A C stlus karakterlncok s a hozzjuk tartoz szabvnyos fggv-
nyek segtsgvel nagyon hatkony programokat rhatunk, de mg a gyakorlott C s C++
programozk is gyakran kvetnek el buta kis hibkat mikzben ezekkel dolgoznak. Per-
sze minden C++ programoz tallkozni fog olyan rgebbi programokkal, melyek ezeket
a lehetsgeket hasznljk. me egy haszontalan kis plda, amellyel a leggyakoribb fgg-
vnyeket mutatjuk be:
void f(char* p, char* q)
{
if (p==q) return; // a mutatk egyenrtkek
if (strcmp(p,q)==0) { // a karakterlnc-rtkek egyenrtkek
int i = strlen(p); // a karakterek szma (a lezr nlkl)
// ...
}
char buf[200];
strcpy(buf,p); // p msolsa a buf-ba (a lezrval egytt)
// nem tkletes, tlcsordulhat
strncpy(buf,p,200); // 200 karakter msolsa p-bl a buf-ba
// nem tkletes; lehet, hogy nem msolja t a lezrt
// ...
}
A standard knyvtr 804
A C stlus karakterlncok ki- s bevitelt ltalban a printf fggvnycsald segtsgvel va-
lstjuk meg (21.8).
Az <stdlib.h> s a <cstdlib> fejllomnyban a standard knyvtr nhny olyan hasznos
fggvnyt nyjt, melyekkel szmrtket jell karakterlncokat alakthatunk szmm:
double atof(const char* p); // p talaktsa double-l
int atoi(const char* p); // p talaktsa int-t
long atol(const char* p); // p talaktsa long-g
A bevezet reshely karaktereket ezek a fggvnyek nem veszik figyelembe. Ha a karak-
terlnc nem szmot brzol, a visszatrsi rtk 0 lesz. (Az atoi(ht) eredmnye is 0!)
Ha a karakterlnc szmot jell, de az nem brzolhat az eredmnyknt vrt szmtpusban,
akkor az errno (16.1.2, 22.3) vltoz rtke ERANGE lesz, a visszatrsi rtk pedig egy
nagyon nagy vagy nagyon kicsi szm, a konvertlt rtknek megfelelen.
20.4.2. Karakterek osztlyozsa
A <ctype.h> s a <cctype> fejllomnyban a standard knyvtr olyan hasznos fggvnyeket
knl, melyek az ASCII, illetve a hasonl karakterkszletek karaktereit osztlyozzk:
int isalpha(int); // 'a'..'z' 'A'..'Z' betk, C-hez (20.2.1, 21.7)
int isupper(int); // 'A'..'Z' nagybetk, C-hez (20.2.1, 21.7)
int islower(int); // 'a'..'z' kisbetk, C-hez (20.2.1, 21.7)
int isdigit(int); // tzes szmrendszer szmok: '0'..'9'
int isxdigit(int); // hexadecimlis szmok: '0'..'9' vagy 'a'..'f' vagy 'A'..'F'
int isspace(int); // ' ', '\t', '\v', kocsivissza, jsor, fggleges tabultor
int iscntrl(int); // vezrlkarakter (ASCII 0..31 s 127)
int ispunct(int); // kzpontozs, a fentiek egyike sem tartozik bele
int isalnum(int); // isalpha() | isdigit()
int isprint(int); // nyomtathat karakterek: ascii ' '..'~'
int isgraph(int); // isalpha() | isdigit() | ispunct()
int toupper(int c); // c nagybets megfelelje
int tolower(int c); // c kisbets megfelelje
A fentieket ltalban egyszer kiolvasssal valstjk meg, gy, hogy a karaktert indexknt
hasznljk egy tblban, amely a karakterek jellemzit trolja. Ezrt az albbi kifejezs
amellett, hogy tlsgosan hossz s sok hibalehetsget rejt magban (pldul egy EBCDIC
karakterkszletet hasznl rendszerben nem alfabetikus karakterek is teljestik a felttelt),
nem is hatkony:
20. Karakterlncok 805
if (('a'<=c && c<='z') || ('A'<=c && c<='Z')) { // bet
// ...
}
Ezek a fggvnyek egy int paramtert vrnak, s az tadott egsznek vagy brzolhatnak
kell lennie egy unsigned char tpussal vagy az EOF rtknek kell lennie (ami ltalban
a 1). Ez az rtelmezs problmt jelenthet az olyan rendszerekben, ahol a char eljeles t-
pus (lsd: 20.6[11]).
Ugyanilyen szerep fggvnyek rendelkezsre llnak szles karakterekre is, a <cwtype> s
a <wtype.h> fejllomnyban.
20.5. Tancsok
[1] A C stlus fggvnyek helyett lehetleg hasznljuk a string osztly eljrsait.
20.4.1.
[2] A string lehet vltoz vagy tag, de bzisosztlynak nem val. 20.3, 25.2.1.
[3] A karakterlncokat rdemes rtk szerint tadni s visszaadni, mert gy a rend-
szerre bzzuk a memriakezelst. 20.3.6.
[4] A bejrk s a [ ] opertor helyett hasznljuk az at() fggvnyt, ha tartomnyel-
lenrzsre van szksgnk. 20.3.2, 20.3.5.
[5] A bejrk s a [ ] opertor gyorsabb, mint az at() fggvny. 20.3.2, 20.3.5.
[6] Kzvetve vagy kzvetlenl, a rszlncok olvasshoz hasznljuk a substr(),
rsukhoz a replace() fggvnyt. 20.3.12, 20.3.13.
[7] Adott rtk megkeresshez egy karakterlncban hasznljuk a find() fggvnyt
(s ne rjunk sajt ciklust erre a feladatra). 20.3.1.
[8] Ha hatkonyan akarunk egy karakterlncot karakterekkel bvteni, hasznljuk
a hozzfzs mveleteit. 20.3.9.
[9] Ha az id nem ltfontossg szempont, a karakterbevitelre hasznljunk string
objektumokat. 20.3.1.5.
[10] Hasznljuk a string::npos rtket a karakterlnc htralv rsznek jelzsre.
20.3.5.
[11] Ha gyakran hasznlunk egy alacsonyszint szolgltatst, akkor azt ksztsk el
a string osztlyhoz, ne hasznljunk emiatt mindenhol alacsonyszint adatszer-
kezeteket. 20.3.10.
A standard knyvtr 806
[12] Ha a string osztlyt hasznljuk, valahol mindig kapjuk el a range_error s
out_of_range kivteleket. 20.3.5.
[13] Figyeljnk r, hogy ne adjunk t char* mutatt 0 rtkkel egy karakterlnc-
kezel fggvnynek. 20.3.7.
[14] Ha nagyon nagy szksgnk van r, a c_str() fggvny segtsgvel egy string
objektumbl elllthatjuk a C stlus karakterlnc megfeleljt. 20.3.7.
[15] Hasznljuk az isalpha(), isdigit() stb. fggvnyeket, ha a karaktereket osztlyoz-
nunk kell, ne rjunk sajt ellenrzseket a karakterrtkek alapjn. 20.4.2.
20.6. Gyakorlatok
A fejezet feladatainak tbbsghez a standard knyvtr brmely vltozatnak forrskdj-
ban tallhatunk megoldst, mieltt azonban megnznnk, hogy a knyvtr alkoti hogyan
kzeltettk meg a problmt, prbljunk sajt megoldst keresni.
1. (*2) Ksztsnk egy fggvnyt, amely kt string objektumot vr paramterknt
s egy jabb karakterlncot ad vissza, amelyben a kt karakterlnc sszefzse
szerepel gy, hogy kzttk egy pont ll. A file s a write karakterlncokbl
pldul a fggvny a file.write eredmnyt lltsa el. Oldjuk meg ugyanezt a fel-
adatot C stlus karakterlncokkal s a C lehetsgeinek (pldul a malloc() s
az strlen() fggvnyek) felhasznlsval. Hasonltsuk ssze a kt fggvnyt.
Milyen fontos sszehasonltsi szempontokat kell megvizsglnunk?
2. (*2) Foglaljuk ssze egy listban a vector s a basic_string osztly kztti k-
lnbsgeket. Mely klnbsgek igazn fontosak?
3. (*2) A karakterlnc-kezel lehetsgek nem teljesen szablyosak. Egy karaktert
(char) pldul rtkl adhatunk egy karakterlncnak, de kezdeti rtkadsra
nem hasznlhatjuk. Ksztsnk egy listt ezekrl a szablytalansgokrl. Melye-
ket kszblhettk volna ki ezek kzl anlkl, hogy a karakterlncok haszn-
latt tlbonyoltottuk volna? Milyen ms szablytalansgokat eredmnyezne ez
az talakts?
4. (*1.5) A basic_string osztlynak nagyon sok tagfggvnye van. Melyeket emel-
hetnnk ki az osztlybl s tehetnnk nem tag fggvnny ezek kzl anlkl,
hogy a hatkonysgot s knyelmes hasznlhatsgot feladnnk?
5. (*1.5) rjuk meg a back_inserter() (19.2.4) azon vltozatt, amely a basic_string
osztllyal mkdik.
6. (*2) Fejezzk be a 20.3.13 pontban bemutatott Basic_substring osztly megva-
lstst s ptsk be ezt egy olyan String tpusba, amely tlterheli a () oper-
tort a rszlnca jelentssel, de egybknt ugyangy viselkedik, mint a string.
20. Karakterlncok 807
7. (*2.5) Ksztsnk egy find() fggvnyt, amely egy egyszer szablyos (regul-
ris) kifejezs els elfordulst keresi meg egy string objektumban. A kifejezs-
ben a ? egyetlen, tetszleges karaktert jelent, a * akrhny olyan karaktert,
amely a kifejezs tovbbi rszre nem illeszthet, az [abc] azon karakterek br-
melyikt, amely a megadott halmazban szerepel (esetnkben a, vagy b, vagy c).
A tbbi karakter nmagt brzolja. A find(s,name:) pldul egy olyan muta-
tt ad vissza, amely a name: els elfordulsra mutat, mg
a find(s,[nN]ame(*)) kifejezs azt a rszlncot jelli ki az s karakterlncban,
amelynek elejn a name vagy a Name sz szerepel, majd zrjelek kztt egy
(esetleg res) karaktersorozat.
8. (*2.5) Gondolkodjunk el rajta, hogy az elbbi (20.6[7]), regulris kifejezs-
illeszt fggvnynkbl milyen lehetsgek hinyoznak mg. Hatrozzuk meg
s valstsuk meg ezeket. Hasonltsuk ssze az ltalunk ltrehozott fggvny ki-
fejezkpessgt egy szles krben elterjedt regulris kifejezs-illeszt kifejez-
kpessgvel. Hasonltsuk ssze a kt eljrst hatkonysg szempontjbl is.
9. (*2.5) Egy regulris kifejezs-knyvtr segtsgvel ksztsnk mintailleszt
mveleteket egy String osztlyban, amelyhez egy Substring osztly is tartozik.
10. Kpzeljnk el egy idelis osztlyt az ltalnos szveg-feldolgozsi feladatok
elvgzshez. Legyen az osztly neve Text. Milyen lehetsgeket kell megval-
stanunk? Milyen korltozsokat s kltsgeket knyszertenek az osztlyra az
ltalunk elkpzelt idelis mveletek?
11. (*1.5) Hatrozzuk meg az isalpha(), isdigit() stb. fggvnyek tlterhelseit gy,
hogy megfelelen mkdjenek char, unsigned char s signed char tpusra is.
12. (*2.5) Ksztsnk egy String osztlyt, amelyet arra optimalizlunk, hogy nyolc
karakternl rvidebb karakterlncokat kezeljen. Hasonltsuk ssze ennek hat-
konysgt a 11.12 pont String osztlyval, illetve a szabvnyos string osztllyal.
Kszthet-e olyan karakterlnc osztly, amely egyesti a nagyon rvid karakter-
lncokra optimalizlt s a tkletesen ltalnos vltozat elnyeit?
13. (*2) Mrjk le egy string msolsnak hatkonysgt. Megfelelen optimalizlt
sajt fejlesztrendszernk string osztlya a msolsra?
14. (*2.5) Hasonltsuk ssze a 20.3.9 s a 20.3.10 pontban bemutatott hrom
complete_name() fggvny hatkonysgt. Prbljuk elkszteni
a complete_name() fggvny azon vltozatt, amely a lehet leggyorsabban fut.
Jegyezzk fel azokat a hibkat, amelyeket a megvalsts s tesztels kzben
tapasztaltunk.
15. (*2.5) Kpzeljk el, hogy rendszernk legknyesebb mvelete a kzepes
hosszsg (5-25 karakteres) karakterlncok beolvassa a cin adatfolyamrl.
rjunk egy bemeneti fggvnyt, amely az ilyen karakterlncokat tudja beolvasni
olyan gyorsan, ahogy csak el tudjuk kpzelni. Dnthetnk gy, hogy a fgg-
A standard knyvtr 808
vny felletnek knyelmes hasznlatt felldozzuk a sebessg rdekben. Ha-
sonltsuk ssze eljrsunk hatkonysgt a string osztly >> opertornak hat-
konysgval.
16. (*1.5) rjuk meg az itos(int) fggvnyt, amely a paramterknt megadott int
rtk string brzolst adja vissza.
20. Karakterlncok 809
Adatfolyamok
Csak azt kapod, amit ltsz
(Brian Kernighan)
Kimenet s bemenet Kimeneti adatfolyamok Beptett tpusok kimenete Felhaszn-
li tpusok kimenete Virtulis kimeneti fggvnyek Bemeneti adatfolyamok Beptett
tpusok bemenete Formzatlan bemenet Adatfolyamok llapota Felhasznli tpusok
bemenete I/O kivtelek Adatfolyamok sszektse rszemek Egsz s lebeg-
pontos kimenet formzsa Mezk s igazts Mdostk Szabvnyos mdostk Fel-
hasznli mdostk Fjlfolyamok Adatfolyamok lezrsa Karakterlnc-folyamok
Adatfolyamok s tmeneti trolk Helyi sajtossgok Adatfolyam-visszahvsok
printf() Tancsok Gyakorlatok
21.1. Bevezet
Egy programozsi nyelvben ltalnos be- s kimeneti (input/output, I/O) rendszert megva-
lstani rendkvl nehz. Rgebben az I/O lehetsgek csak arra korltozdtak, hogy n-
hny beptett adattpust kezeljenek. A legegyszerbbek kivtelvel azonban a C++ prog-
ramok szmos felhasznli tpust is hasznlnak, gy e tpusok ki- s bemenett is meg kell
oldani. Egy I/O rendszernek a rugalmassg s hatkonysg mellett egyszernek, knyel-
21
mesnek, biztonsgosnak s mindenekfelett tfognak kell lennie. Eddig mg senki nem llt
el olyan megoldssal, amellyel mindenki elgedett lenne, ezrt lehetv kell tennnk,
hogy a felhasznl sajt ki- s bemeneti eszkzket kszthessen, illetve a szabvnyos le-
hetsgeket egyedi felhasznlsi terletek kezelsvel bvthesse ki.
A C++ olyan nyelv, melyben a felhasznl j tpusokat adhat meg s e tpusok hasznlata
ugyanolyan hatkony s knyelmes lehet, mint a beptett adattpusok. Ezrt logikus elv-
rs a C++ nyelv I/O szolgltatsaitl, hogy C++ nyelven kszljenek s csak olyan eszk-
zket hasznljanak, melyek minden programoz szmra elrhetk. Az itt bemutatott, adat-
folyam-bemenetet s -kimenetet kezel lehetsgek az e kvetelmnyek kielgtsre tett
erfesztsek eredmnyei:
21.2 Kimenet: A kimenet alatt az alkalmazsprogramoz azt rti, hogy tetszle-
ges tpus (int, char* vagy Employee_record) objektumokbl karakterso-
rozatot llthat el. Ebben a pontban azokat a lehetsgeket ismertetjk,
melyekkel a beptett s a felhasznli tpusok kimenete megvalsthat.
21.3 Bemenet: Azok az eszkzk, melyekkel karaktereket, karakterlncokat
vagy ms (akr beptett, akr felhasznli) tpusok rtkeit beolvashatjuk.
21.4 Formzs: A kimenet elrendezsvel kapcsolatban gyakran klnleges
elvrsaink vannak. Az int rtkeket pldul ltalban tzes szmrend-
szerben szeretjk kirni, a mutatk hagyomnyos formja a hexadecim-
lis (tizenhatos szmrendszer) alak, a lebegpontos szmokat pedig
megadott pontossggal kell megjelenteni. Ez a rsz a formzssal s az
azt segt programozsi eszkzkkel foglalkozik.
21.5 Fjlok s adatfolyamok: Alaprtelmezs szerint minden C++ program
hasznlhatja a szabvnyos adatfolyamokat: a szabvnyos kimenetet
(cout), a szabvnyos bemenetet (cin) s a hibakimenetet (cerr). Ha ms
eszkzket vagy fjlokat akarunk hasznlni, akkor adatfolyamokat kell
ltrehoznunk s ezeket hozz kell ktnnk a megfelel eszkzhz, illet-
ve fjlhoz. Ez a rsz a fjlok megnyitsnak s bezrsnak, illetve az
adatfolyamok fjlokhoz s karakterlncokhoz val hozzktsnek md-
szereivel foglalkozik.
21.6 tmeneti trols: Hatkony ki- s bemenetet gy lehet megvalstani, ha
tmeneti trakat (puffereket) hasznlunk. A mdszer alkalmazhat
mindkt oldalon, gy az tmeneti trbl kiolvashatjuk s oda rhatjuk is
az adatokat. Ebben a pontban az tmeneti trols (pufferels) alapmd-
szereit mutatjuk be.
21.7 Helyi sajtossgok: A locale objektum azt hatrozza meg, hogy a szmo-
kat ltalban milyen formban jelentjk meg, milyen karaktereket tekin-
tnk betnek s gy tovbb. Teht az adott kultrra jellemz specialit-
A standard knyvtr 812
sokat foglalja ssze. Az I/O rendszer automatikusan hasznlja ezeket az
informcikat, gy ez a rsz csak nagy vonalakban foglalkozik a tmval.
21.8 C stlus I/O: Itt a C <stdio.h> fejllomnynak printf() fggvnyt s a C
knyvtrnak a C++ <iostream> knyvtrhoz fzd viszonyt
mutatjuk be.
Ahhoz, hogy az adatfolyam-knyvtrat hasznljuk, nem kell rtennk a megvalstshoz
hasznlt sszes mdszert, mr csak azrt sem, mert a klnbz C++-vltozatokban kln-
bz mdszereket alkalmazhatnak. Egy szp I/O rendszer megvalstsa azonban komoly
kihvst jelent. Megrsa kzben szmos olyan lehetsggel megismerkedhetnk, amelyet
ms programozsi s tervezsi feladatok megoldsban is felhasznlhatunk. Ezrt nagyon
fontos, hogy megismerjk azokat a mdszereket, melyekkel egy ki- s bemeneti rendszer
elkszthet.
Ebben a fejezetben csak addig a mlysgig mutatjuk be az adatfolyamok bemeneti/kime-
neti rendszert, hogy pontosan megrtsk annak szerkezett, felhasznlhassuk a leggyako-
ribb I/O mveletek vgrehajtshoz, s ki tudjuk egszteni gy, hogy az ltalunk ltreho-
zott j tpusokat is kpes legyen kezelni. Ha szabvnyos vagy j tpus adatfolyamokat aka-
runk hasznlni, esetleg sajt helyi sajtossgokat akarunk meghatrozni, a knyvtr forrs-
kdjra, j rendszerlersra, s mkd pldaprogramokra is szksgnk lesz az itt lert in-
formcikon kvl.
Az adatfolyam I/O rendszer sszetevit az albbi brval szemlltethetjk:
21. Adatfolyamok 813
ios_base:
helyi sajtossgoktl fgget-
len llapot
basic_streambuf<>:
tmeneti trols
locale:
formzsi informcik
karakterek tmeneti trolsa
tnyleges cl/forrs
basic_ios<>:
helyi sajtossgoktl fgg
llapot, adatfolyam-llapot
basic_iostream<>:
formzs (<<, >> stb.),
ltrehozs/felszmols
A basic_iostream<> fell indul pontozott nyl azt jelzi, hogy a basic_ios<> egy virtulis
bzisosztly, az egyszer vonalak mutatkat brzolnak. Azon osztlyok, melyek neve utn
a <> jel szerepel, olyan sablonok, melyek paramtere egy karaktertpus s tartalmaznak egy
locale objektumot is.
Az adatfolyamok (streams) s az hozzjuk hasznlt ltalnos jelrendszer szmos kommuni-
kcis problma megoldst lehetv teszik. Adatfolyamokat hasznlhatunk objektumok
gpek kztti tvitelre (25.4.1), zenetfolyamok titkostshoz (21.10[22]), adattmr-
tshez, objektumok lland (perzisztens) trolshoz s gy tovbb. Ennek ellenre az alb-
biakban csak az egyszer, karakterkzpont ki- s bemenetet mutatjuk be.
Az adatfolyam ki- s bemenethez kapcsold osztlyok s sablonok deklarcijt (melyek
elg informcit adnak ahhoz, hogy hivatkozzunk rjuk, de ahhoz nem, hogy mveleteket
hajtsunk vgre rajtuk), valamint a szabvnyos tpus-meghatrozsokat (typedef-eket) az
<iosfwd> fejllomnyban tallhatjuk meg. Erre a fjlra nha szksgnk lesz, amikor ms
I/O fejllomnyokat de nem az sszeset hasznlni akarunk.
21.2. Kimenet
A beptett s a felhasznli tpusok egysges s tpusbiztos kezelse egyszeren megval-
sthat, ha tlterheljk a kimeneti fggvnyeket:
put(cerr,"x = "); // cerr a hibazenetek kimeneti adatfolyama
put(cerr,x);
put(cerr,'\n');
A paramter tpusa hatrozza meg, melyik put() fggvny kerl meghvsra az adott hely-
zetben. Ezt a megoldst sok nyelv alkalmazza, br sok ismtlst kvn. Ha az rd ki m-
velet megvalstshoz a << opertort terheljk tl, szebb jellst kapunk s lehetsget
adunk a programoznak, hogy objektumok sorozatt egyetlen utastssal rja ki:
cerr << "x = " << x << '\n';
Ha x egy int tpus vltoz, melynek rtke 123, akkor az elz parancs a szabvnyos hiba-
kimeneti adatfolyamra (cerr) az albbi szveget rja ki (egy jsor karakterrel lezrva):
x = 123
A standard knyvtr 814
Ugyanakkor, ha x tpusa complex (22.5), rtke pedig (1,2.4), a kirt szveg az albbi lesz:
x = (1,2.4)
Ez a stlus mindaddig mkdkpes, amg x tpusra a << mvelet definilt, mrpedig a fel-
hasznl knnyedn kszthet ilyen fggvnyt sajt tpusaihoz.
Ha el akarjuk kerlni a kimeneti fggvny hasznlatbl ered knyelmetlen megfogalma-
zst, mindenkppen szksgnk van egy kimeneti opertorra. De mirt pont a << oper-
tort vlasszuk? Arra nincs lehetsgnk, hogy j nyelvi elemet vezessnk be (11.2). Az r-
tkad opertor hasznlhat lenne a ki- s a bemenet jellsre is, de gy tnt, a legtbb
programoz klnbz opertort szeretne hasznlni a kimenethez s a bemenethez. R-
adsul az = rossz irnyba kt: a cout=a=b jelentse cout=(a=b), mg neknk a (cout=a)=b
rtelmezsre lenne szksgnk (6.2). Prblkoztam a < s a > opertor hasznlatval is, de
az emberek tudatban ez a kt jel annyira a kisebb, mint s nagyobb, mint jelentshez
kapcsoldik, hogy az j I/O utastsok teljesen olvashatatlanok voltak.
A << s a >> opertort nem hasznljuk olyan gyakran a beptett tpusokra, hogy ez prob-
lmt okozzon programjaink olvashatsgban. Elg szimmetrikusak ahhoz, hogy jelk-
pezzk a kimenet s a bemenet mveletet. Amikor ezeket az opertorokat ki- s beme-
netre hasznljuk, a << jelenti a kirst, a >> pedig a beolvasst. Azok, akik jobban szeretik
a mszaki kifejezseket, nevezhetik ezt a kt mveletet sorrendben output-nak s input-
nak, esetleg beszrsnak s kinyersnek (inserter, extractor). A << opertornak a preceden-
cia sorrendben elfoglalt helye elg alacsony ahhoz, hogy zrjelezs nlkl lehetv tegye
a paramterekben aritmetikai mveletek elvgzst:
cout << "a*b+c=" << a*b+c << '\n';
Ha azonban olyan mveletet akarunk hasznlni, amely precedencija alacsonyabban
helyezkedik el, mint a << opertor, akkor nem szabad elfelejtennk a zrjeleket:
cout << "a^b|c=" << (a^b|c) << '\n';
A balra lptet opertor (6.2.4) szintn hasznlhat kimeneti utastsban, de termszete-
sen ezt is zrjelek kz kell rnunk:
cout << "a<<b=" << (a<<b) << '\n';
21. Adatfolyamok 815
21.2.1. Kimeneti adatfolyamok
Az ostream arra val, hogy tetszleges tpus rtkeket karakterek sorozatv alaktsunk.
Ezutn ezeket a karaktereket ltalban alacsonyszint kimeneti mveletekkel rjuk ki. Sok-
fle karakter ltezik (20.2) s ezeket egy-egy char_traits objektum jellemzi (20.2.1). Eb-
bl kvetkezik, hogy az ostream egy bizonyos tpus karakterre specializlt vltozata
a basic_ostream sablonnak:
template <class Ch, class Tr = char_traits<Ch> >
class std::basic_ostream : virtual public basic_ios<Ch,Tr> {
public:
virtual ~basic_ostream();
// ...
};
A sablon s a hozz tartoz mveletek az std nvtrben szerepelnek s az <ostream> fejl-
lomnybl rhetk el, amely az <iostream> fejllomny kimenettel kapcsolatos rszeit
tartalmazza.
A basic_ostream paramterei meghatrozzk, hogy a megvalsts milyen tpus karakte-
reket hasznl, de nem rgztik a kirhat objektumok tpust. Az egyszer char tpust s
a szles karaktereket hasznl adatfolyamokat viszont minden vltozat kzvetlenl tmo-
gatja:
typedef basic_ostream<char> ostream;
typedef basic_ostream<wchar_t> wostream;
Sok rendszerben a szles karakterek (wide character) rsa olyan szinten optimalizlhat
a wostream osztly segtsgvel, amelyhez a kimeneti egysgknt bjtot hasznl adatfo-
lyamok nehezen rhetnek fel.
Lehetsg van olyan adatfolyamok meghatrozsra is, melyekben a fizikai ki- s bemene-
tet nem karakterszinten valstjuk meg, ezek az adatfolyamok azonban meghaladjk a stan-
dard knyvtr hatkrt, gy ebben a knyvben nem foglalkozunk velk (21.10[15]).
A basic_ios bzisosztly az <ios> fejllomnyban tallhat. Ez kezeli a formzst (21.4),
a helyi sajtossgokat (21.7) s az tmeneti trak elrst (21.6) is. Az egysges jells-
rendszer rdekben nhny tpust is meghatroz:
A standard knyvtr 816
template <class Ch, class Tr = char_traits<Ch> >
class std::basic_ios : public ios_base {
public:
typedef Ch char_type;
typedef Tr traits_type;
typedef typename Tr::int_type int_type; // a karakter egsz rtknek tpusa
typedef typename Tr::pos_type pos_type; // pozci az tmeneti trban
typedef typename Tr::off_type off_type; // eltols az tmeneti trban
// ... lsd mg 21.3.3, 21.3.7, 21.4.4, 21.6.3 s 21.7.1. ...
};
A basic_ios osztly letiltja a msol konstruktort s az rtkad opertort. Ebbl kvetke-
zik, hogy az ostream s istream objektumok nem msolhatk. Ha egy adatfolyam clja
megvltozik, knytelenek vagyunk vagy az tmeneti trat kicserlni (21.6.4) vagy mutat-
kat hasznlni (6.1.7).
Az ios_base bzisosztly olyan informcikat s mveleteket tartalmaz, amelyek fggetle-
nek a hasznlt karaktertpustl. (Ilyen pldul a lebegpontos szmok kimeneti pontoss-
ga.) Ezrt ez az osztly nem kell, hogy sablon legyen.
Az ios_base osztlyban szerepl tpus-meghatrozsokon kvl az adatfolyam I/O knyvtr
egy eljeles egsz tpust is hasznl, melynek neve streamsize s az egy I/O mvelettel t-
vitt karakterek szmt, illetve az tmeneti tr mrett adhatjuk meg a segtsgvel. Emellett
rendelkezsnkre ll a streamoff is, amely az adatfolyamokban s az tmeneti trakban va-
l eltolst (offset) brzolja.
Az <iostream> fejllomny tbb szabvnyos adatfolyamot is megad:
ostream cout; // karakterek szabvnyos kimeneti adatfolyama
ostream cerr; // hibazenetek szabvnyos, nem pufferelt kimeneti adatfolyama
ostream clog; // hibazenetek szabvnyos kimeneti adatfolyama
wostream wcout; // a cout-nak megfelel "szles" adatfolyam
wostream wcerr; // a cerr-nek megfelel "szles" adatfolyam
wostream wclog; // a clog-nak megfelel "szles" adatfolyam
A cerr s a clog adatfolyamok ugyanarra a kimeneti eszkzre mutatnak, a klnbsg kz-
tk csak a hasznlt tmeneti tr tpusa. A cout ugyanarra az eszkzre r, mint a C stdout
(21.8), mg a cerr s a clog ugyanarra, mint a C stderr. Ha szksg van r, a programoz
tovbbi adatfolyamokat is ltrehozhat (21.5).
21. Adatfolyamok 817
21.2.2. Beptett tpusok kimenete
Az ostream osztly megadja a << (kimenet) opertort, amellyel a beptett tpusok kir-
st vgezhetjk el:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ostream : virtual public basic_ios<Ch,Tr> {
public:
// ...
basic_ostream& operator<<(short n);
basic_ostream& operator<<(int n);
basic_ostream& operator<<(long n);
basic_ostream& operator<<(unsigned short n);
basic_ostream& operator<<(unsigned int n);
basic_ostream& operator<<(unsigned long n);
basic_ostream& operator<<(float f);
basic_ostream& operator<<(double f);
basic_ostream& operator<<(long double f);
basic_ostream& operator<<(bool n);
basic_ostream& operator<<(const void* p); // mutatrtk kirsa
basic_ostream& put(Ch c); // c kirsa
basic_ostream& write(const Ch* p, streamsize n); // p[0]..p[n-1]
// ...
};
A put() s write() fggvnyek egyszeren karaktereket rnak ki, gy az erre a mveletre
szolgl << opertornak nem kell tagnak lennie. A karakter-operandust vr operator<<()
fggvnyek a put() segtsgvel nem tagknt kszthetk el:
template <class Ch, class Tr>
basic_ostream<Ch, Tr>& operator<<(basic_ostream<Ch, Tr>&, Ch);
template <class Ch, class Tr>
basic_ostream<Ch, Tr>& operator<<(basic_ostream<Ch, Tr>&, char);
template <class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<char, Tr>&, char);
template <class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<char, Tr>&, signed char);
template <class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<char, Tr>&, unsigned char);
A standard knyvtr 818
A << a nullval lezrt karaktertmbk kirshoz is rendelkezsre ll:
template <class Ch, class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<Ch, Tr>&, const Ch*);
template <class Ch, class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<Ch, Tr>&, const char*);
template <class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<char, Tr>&, const char*);
template <class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<char, Tr>&, const signed char*);
template <class Tr>
basic_ostream<char, Tr>& operator<<(basic_ostream<char, Tr>&, const unsigned char*);
A string-ek kimeneti opertorait a <string> tartalmazza (20.3.15).
Az operator <<() mvelet egy referencit ad vissza ugyanarra az ostreamobjektumra, amely-
re meghvtuk, gy azonnal alkalmazhatjuk r a kvetkez operator <<() mveletet:
cerr << "x = " << x;
Itt x egy int, az utasts jelentse pedig a kvetkez:
(cerr.operator<<("x = ")).operator<<(x);
Fontos kvetkezmny, hogy amikor tbb elemet ratunk ki egyetlen kimeneti utastssal,
azok a megfelel sorrendben, balrl jobbra haladva jelennek meg:
void val(char c)
{
cout << "int('" << c << "') = " << int(c) << '\n';
}
int main()
{
val('A');
val('Z');
}
Egy ASCII karaktereket hasznl rendszerben a fenti programrszlet eredmnye a kvet-
kez lesz:
int('A') = 65
int('Z') = 90
21. Adatfolyamok 819
Megfigyelhetjk, hogy a karakterliterlok tpusa char (4.3.1), gy a cout<<Z a Z bett fog-
ja kirni, nem annak int rtkt, a 90-et.
Ha logikai (bool) rtket ratunk ki, az alaprtelmezs szerint 0 vagy 1 formban jelenik
meg. Ha ez nem megfelel szmunkra, bellthatjuk az <iomanip> fejllomnyban
(21.4.6.2) meghatrozott boolalpha formzsjelzt, gy a true vagy a false szveg jelenik
meg:
int main()
{
cout << true << ' ' << false << '\n';
cout << boolalpha; // a true s false szimbolikus brzolsa
cout << true << ' ' << false << '\n';
}
A kirt szveg:
1 0
true false
Pontosabban fogalmazva a boolalpha tulajdonsg azt biztostja, hogy a bool rtkeknek
a helyi sajtossgoknak (locale) megfelel rtkt kapjuk. Ha n megfelelen belltom
a locale-t (21.7), a kvetkez eredmnyt kapom:
1 0
sandt falsk
A lebegpontos szmok formzsrl, az egszek szmrendszerrl stb. a 21.4. pontban
lesz sz.
Az ostream::operator<<(const void*) fggvny egy mutat rtkt jelenti meg, az ppen
hasznlt szmtgp felptsnek megfelel formban:
int main()
{
int i = 0;
int* p = new int;
cout << "local " << &i << ", free store " << p << '\n';
}
A standard knyvtr 820
Az eredmny az n gpemen a kvetkez:
local 0x7fffead0, free store 0x500c
Ms rendszerek mskppen jelenthetik meg a mutat rtkt.
21.2.3. Felhasznli tpusok kimenete
Kpzeljk el az albbi, felhasznli complex tpust (11.3):
class complex {
public:
double real() const { return re; }
double imag() const { return im; }
// ...
};
A << opertort a kvetkezkppen definilhatjuk az j complex szmra:
ostream& operator<<(ostream&s, const complex& z)
{
return s << '(' << z.real() << ',' << z.imag() << ')';
}
Ezutn a << mveletet pontosan ugyangy hasznlhatjuk sajt tpusunkra, mint a beptett
tpusokra:
int main()
{
complex x(1,2);
cout << "x = " << x << '\n';
}
Az eredmny:
x = (1,2)
A felhasznli tpusok kimeneti mveleteinek megvalstshoz nem kell megvltoztat-
nunk az ostream osztly deklarcijt. Ez azrt szerencss, mert az ostream osztly az
<iostream> fejllomnyban definilt, amit a felhasznlk jobb ha nem vltoztatnak meg (er-
21. Adatfolyamok 821
re nincs is lehetsgk). Az, hogy az ostreambvtst nem tettk lehetv, vdelmet nyjt
az adatszerkezet vletlen mdostsa ellen is, s lehetv teszi, hogy anlkl vltoztassuk
meg az ostream osztly megvalstst, hogy a felhasznli programokra hatssal lennnk.
21.2.3.1. Virtulis kimeneti fggvnyek
Az ostreamtagfggvnyei nem virtulisak. Azok a kimeneti mveletek, melyeket egy prog-
ramoz valst meg, nem az osztly rszei, gy ezek sem lehetnek virtulisak. Ennek egyik
oka az, hogy az egyszer mveletek esetben (pldul egyetlen karakter tmeneti trba
rsa) gy kzel optimlis sebessget rhetnk el. Ez olyan terlet, ahol a futsi idej hat-
konysg rendkvl fontos s szinte ktelez helyben kifejtett (inline) eljrsokat kszteni.
A virtulis fggvnyek hasznlata it arra vonatkozik, hogy a lehet legnagyobb rugalmas-
sgot biztostsuk azon mveletek szmra, melyek kifejezetten a tmeneti tr tlcsordul-
sval, illetve alulcsordulsval foglalkoznak (21.6.4).
Ennek ellenre a programozk gyakran kvnnak megjelenteni olyan objektumokat, me-
lyeknek csak egy bzisosztlya ismert. Mivel a pontos tpust nem ismerjk, a megfelel ki-
menetet nem llthatjuk el gy, hogy egyszeren minden j tpushoz megadjuk a << m-
veletet. Ehelyett kszthetnk egy virtulis kimeneti fggvnyt az absztrakt bzisosztlyban:
class My_base {
public:
// ...
virtual ostream& put(ostream& s) const = 0; // *this kirsa s-re
};
ostream& operator<<(ostream& s, const My_base& r)
{
return r.put(s); // a megfelel put() hasznlata
}
Teht a put() egy olyan virtulis fggvny, amely biztostja, hogy a megfelel kimeneti m-
velet kerljn vgrehajtsra a << opertorban.
Ennek felhasznlsval a kvetkez programrszletet rhatjuk.
class Sometype : public My_base {
public:
// ...
A standard knyvtr 822
ostream& put(ostream& s) const; // az igazi kimeneti fggvny: My_base::put()
// fellrsa
};
void f(const My_base& r, Sometype& s) // hasznljuk a << opertort a
// megfelel put() hvshoz
{
cout << r << s;
}
Ezzel a virtulis put() fggvny bepl az ostream osztly s a << mvelet ltal biztostott
keretbe. A mdszer jl alkalmazhat minden olyan esetben, amikor egy virtulis fggvny-
knt mkd mveletre van szksgnk, de futsi idben a msodik paramter alapjn
akarunk vlasztani.
21.3. Bemenet
A bemenetet a kimenethez nagyon hasonlan kezelhetjk. Az istream osztly biztostja
a >> bemeneti opertort nhny ltalnos tpushoz, a felhasznli tpusokhoz pedig kszt-
hetnk egy-egy operator>>() fggvnyt.
21.3.1. Bemeneti adatfolyamok
A basic_ostream (21.2.1) osztllyal prhuzamosan az <istream> fejllomnyban megtall-
hatjuk a basic_istream osztlyt, amely az <iostream> bemenettel kapcsolatos rszeit
tartalmazza:
template <class Ch, class Tr = char_traits<Ch> >
class std::basic_istream : virtual public basic_ios<Ch,Tr> {
public:
virtual ~basic_istream();
// ...
};
A basic_ios bzisosztlyt a 21.2.1. pontban mutattuk be.
21. Adatfolyamok 823
A cin s a wcin kt szabvnyos bemeneti adatfolyam, melyeket az <iostream> fejllomny r le:
typedef basic_istream<char> istream;
typedef basic_istream<wchar_t> wistream;
istream cin; // karakterek szabvnyos bemeneti adatfolyama
wistream wcin; // szabvnyos bemeneti adatfolyam wchar_t rszre
A cin adatfolyam ugyanazt a forrst hasznlja, mint a C stdin adatfolyama (21.8).
21.3.2. Beptett tpusok bemenete
A beptett tpusokhoz az istream biztostja a >> opertort:
template <class Ch, class Tr = char_traits<Ch> >
class basic_istream : virtual public basic_ios<Ch,Tr> {
public:
// ...
// formzott bevitel:
basic_istream& operator>>(short& n); // olvass n-be
basic_istream& operator>>(int& n);
basic_istream& operator>>(long& n);
basic_istream& operator>>(unsigned short& u); // olvass u-ba
basic_istream& operator>>(unsigned int& u);
basic_istream& operator>>(unsigned long& u);
basic_istream& operator>>(float& f); // olvass f-be
basic_istream& operator>>(double& f);
basic_istream& operator>>(long double& f);
basic_istream& operator>>(bool& b); // olvass b-be
basic_istream& operator>>(void*& p); // mutatrtk olvassa p-be
// ...
};
Az operator>>() fggvnyek a kvetkez stlust kvetik:
istream& istream::operator>>(T& tvar) // T egy tpus, melyre az istream::operator>>
// deklarlt
{
// reshely karaktereket tlpni,
A standard knyvtr 824
// majd valahogyan beolvasni egy T tpus elemet `tvar'-ba
return *this;
}
Mivel a >> tugorja az reshely (whitespace) karaktereket, az ilyenekkel elvlasztott eg-
szeket az albbi egyszer ciklussal olvashatjuk be:
int read_ints(vector<int>& v) // feltlti v-t , visszatr a beolvasott egszek szmval
{
int i = 0;
while (i<v.size() && cin>>v[i]) i++;
return i;
}
Ha a bemeneten egy nem egsz rtk jelenik meg, a bemeneti mvelet hibba tkzik, gy
a ciklus is megszakad. Pldul ezt a bemenetet felttelezve:
1 2 3 4 5.6 7 8.
a read_ints() fggvny t egsz szmot fog beolvasni:
1 2 3 4 5
A mvelet utn a bemeneten a kvetkez beolvashat karakter a pont lesz. A nem lthat
reshely (whitespace) karakterek meghatrozsa itt is ugyanaz, mint a szabvnyos C-ben
(szkz, tabultor, jsor, fggleges tabultorra illeszts, kocsivissza), s a <cctype> fejllo-
mnyban lev isspace() fggvnnyel ellenrizhetk valamely karakterre (20.4.2).
Az istream objektumok hasznlatakor a leggyakoribb hiba, hogy a bemenet nem egszen
abban a formtumban rkezik, mint amire felkszltnk, ezrt a bemenet nem trtnik
meg. Ezrt mieltt hasznlni kezdennk azokat az rtkeket, melyeket remnyeink szerint
beolvastunk, ellenriznnk kell a bemeneti adatfolyam llapott (21.3.3), vagy kivteleket
kell hasznlnunk (21.3.6).
A bemenethez hasznlt formtumot a helyi sajtossgok (locale) hatrozzk meg (21.7).
Alaprtelmezs szerint a logikai rtkeket a 0 (hamis) s az 1 (igaz) rtk jelzi, az egsze-
ket tzes szmrendszerben kell megadnunk, a lebegpontos szmok formja pedig olyan,
ahogy a C++ programokban rhatjuk azokat. A basefield (21.4.2) tulajdonsg belltsval
lehetsg van arra is, hogy a 0123 szmot a 83 tzes szmrendszerbeli szm oktlis alakja-
knt, a 0xff bemenetet pedig a 255 hexadecimlis alakjaknt rtelmezzk. A mutatk beol-
vasshoz hasznlt formtum teljesen az adott nyelvi vltozattl fgg (nzznk utna, sajt
fejlesztrendszernk hogyan mkdik).
21. Adatfolyamok 825
Meglep mdon nincs olyan >> tagfggvny, mellyel egy karaktert olvashatnnk be. Ennek
oka az, hogy a >> opertor a karakterekhez a get() karakter-beolvas fggvny (21.3.4) se-
gtsgvel knnyen megvalsthat, gy nem kell tagfggvnyknt szerepelnie. Az adatfo-
lyamokrl sajt karaktertpusaiknak megfelel karaktereket olvashatunk be. Ha ez a karak-
tertpus a char, akkor beolvashatunk signed char s unsigned char tpus adatot is:
template<class Ch, class Tr>
basic_istream<Ch,Tr>& operator>>(basic_istream<Ch,Tr>&, Ch&);
template<class Tr>
basic_istream<char,Tr>& operator>>(basic_istream<char,Tr>&, unsigned char&);
template<class Tr>
basic_istream<char,Tr>& operator>>(basic_istream<char,Tr>&, signed char&);
A felhasznl szempontjbl teljesen mindegy, hogy a >> tagfggvny vagy nll eljrs-e.
A tbbi >> opertorhoz hasonlan ezek a fggvnyek is elszr tugorjk a bevezet
reshely karaktereket:
void f()
{
char c;
cin >> c;
// ...
}
Ez a kdrszlet az els nem reshely karaktert a cin adatfolyambl a c vltozba helyezi.
A beolvass clja lehet karaktertmb is:
template<class Ch, class Tr>
basic_istream<Ch,Tr>& operator>>(basic_istream<Ch,Tr>&, Ch*);
template<class Tr>
basic_istream<char,Tr>& operator>>(basic_istream<char,Tr>&, unsigned char*);
template<class Tr>
basic_istream<char,Tr>& operator>>(basic_istream<char,Tr>&, signed char*);
Ezek a mveletek is eldobjk a bevezet reshely karaktereket, majd addig olvasnak, amg
egy reshely karakter vagy fjlvge jel nem kvetkezik, vgl a karaktertmb vgre egy 0
karaktert rnak. Termszetesen ez a megolds alkalmat ad a tlcsordulsra, gy rdemesebb
inkbb egy string objektumba (20.3.15) helyezni a beolvasott adatokat. Ha mgis az elbbi
A standard knyvtr 826
megoldst vlasztjuk, rgztsk, hogy legfeljebb hny karaktert akarunk beolvasni a >> ope-
rtor segtsgvel. Az is.width(n) fggvnyhvssal azt hatrozzuk meg, hogy a kvetkez,
is bemeneti adatfolyamon vgrehajtott >> mvelet legfeljebb n-1 karaktert olvashat be:
void g()
{
char v[4];
cin.width(4);
cin >> v;
cout << "v = " << v << endl;
}
Ez a programrszlet legfeljebb 3 karaktert olvas be a v vltozba s a vgn elhelyezi a 0
karaktert.
Egy istream esetben a width() ltal belltott rtk csak az utna kvetkez >> mveletre
van hatssal, s arra is csak akkor, ha a cltpus egy tmb.
21.3.3. Az adatfolyam llapota
Minden adatfolyam (az istream s az ostream is) rendelkezik valamilyen llapottal (state).
A hibkat s a szokatlan llapotokat ezen llapotrtk megfelel belltsval s lekrdez-
svel kezelhetjk.
Az adatfolyam-llapot (stream state) fogalmt a basic_istreambzisosztlya, a basic_ios rja
le az <ios> fejllomnyban:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ios : public ios_base {
public:
// ...
bool good() const; // a kvetkez mvelet sikerlhet
bool eof() const; // fjlvge kvetkezett be
bool fail() const; // a kvetkez mvelet sikertelen lesz
bool bad() const; // az adatfolyam srlt
iostate rdstate() const; // io llapotjelz lekrdezse
void clear(iostate f = goodbit); // io llapotjelz belltsa
void setstate(iostate f) { clear(rdstate()|f); } // az f io llopotjelz belltsa
21. Adatfolyamok 827
operator void*() const; // nemnulla, ha !fail()
bool operator!() const { return fail(); }
// ...
};
Ha az llapot good(), a megelz bemeneti mvelet sikeres volt. Ilyenkor a kvetkez be-
meneti mvelet helyes vgrehajtsra is van esly, ellenkez esetben viszont az biztosan
nem lesz hibtlan. Ha egy olyan adatfolyamon prblunk meg bemeneti mveletet vgre-
hajtani, amely nem good() llapotban van, akkor semmi sem trtnik. Ha megprblunk a v
vltozba rtket beolvasni, de a bemeneti mvelet sikertelen, v rtknek nem szabad
megvltoznia. (Ha v olyan tpus, amit az istream s az ostream tagfggvnyei kezelnek,
biztosan megmarad az rtke.) A fail() s a bad() llapot kztti klnbsg igen kicsi. Ha
az llapot fail(), de nem bad(), akkor az adatfolyam rvnytelenn vlt, de nem vesztek el
karakterek. Ha bad() llapotba kerltnk, mr nem remnykedhetnk.
Az adatfolyam llapott jelzk (jelzbitek, flag-ek) hatrozzk meg. Az adatfolyamok lla-
pott kifejez legtbb konstanshoz hasonlan ezeket is a basic_ios bzisosztlya, az
ios_base rja le:
class ios_base {
public:
// ...
typedef implementation_defined2 iostate;
static const iostate badbit, // az adatfolyam srlt
eofbit, // fjlvge kvetkezett be
failbit, // a kvetkez mvelet sikertelen lesz
goodbit; // goodbit==0
// ...
};
Az I/O llapot jelzbitjeit kzvetlenl mdosthatjuk:
void f()
{
ios_base::iostate s = cin.rdstate(); // iostate bitek halmazval tr vissza
if (s & ios_base::badbit) {
// cin karakterek elveszhettek
}
// ...
cin.setstate(ios_base::failbit);
// ...
}
A standard knyvtr 828
Ha az adatfolyamot felttelknt hasznljuk, annak llapott az operator void*() vagy az ope-
rator !() fggvnnyel vizsglhatjuk meg. A vizsglat csak akkor lesz sikeres, ha az llapot
!fail() (a void*() esetben), illetve fail() (a !() esetben). Egy ltalnos msol eljrst pl-
dul az albbi formban rhatunk meg.
template<class T> void iocopy(istream& is, ostream& os)
{
T buf;
while (is>>buf) os << buf << '\n';
}
Az is>>buf mvelet egy referencit ad vissza, amely az is adatfolyamra hivatkozik, a vizsg-
latot pedig az is::operator void*() mvelet vgzi:
void f(istream& i1, istream& i2, istream& i3, istream& i4)
{
iocopy<complex>(i1,cout); // komplex szmok msolsa
iocopy<double>(i2,cout); // ktszeres pontossg lebegpontos szmok msolsa
iocopy<char>(i3,cout); // karakterek msolsa
iocopy<string>(i4,cout); // reshelyekkel elvlasztott szavak msolsa
}
21.3.4. Karakterek beolvassa
A >> opertor formzott bemenetre szolgl, teht adott tpus s adott formtum objektumok
beolvassra. Ha erre nincs szksgnk s inkbb valban karakterekknt akarjuk beolvasni
a karaktereket, hogy ksbb mi dolgozzuk fel azokat, hasznljuk a get() fggvnyeket:
template <class Ch, class Tr = char_traits<Ch> >
class basic_istream : virtual public basic_ios<Ch,Tr> {
public:
// ...
// formzatlan bemenet
streamsize gcount() const; // a legutbbi get() ltal beolvasott karakterek szma
int_type get(); // egy Ch (vagy Tr::eof()) beolvassa
basic_istream& get(Ch& c); // egy Ch olvassa c-be
basic_istream& get(Ch* p, streamsize n); // jsor a lezrjel
basic_istream& get(Ch* p, streamsize n, Ch term);
21. Adatfolyamok 829
basic_istream& getline(Ch* p, streamsize n); // jsor a lezrjel
basic_istream& getline(Ch* p, streamsize n, Ch term);
basic_istream& ignore(streamsize n = 1, int_type t = Tr::eof());
basic_istream& read(Ch* p, streamsize n); // legfeljebb n karakter beolvassa
// ...
};
Ezek mellett a <string> fejllomny biztostja a getline() fggvnyt is, amely a szabvnyos
string-ek (20.3.15) beolvassra hasznlhat.
A get() s a getline() fggvnyek ugyangy kezelik az reshely karaktereket, mint brmely
ms karaktert. Kifejezetten olyan adatok beolvassra szolglnak, ahol nem szmt a beol-
vasott karakterek jelentse.
Az istream::get(char&) fggvny egyetlen karaktert olvas be paramterbe. Egy karakte-
renknt msol programot pldul a kvetkezkppen rhatunk meg.
int main()
{
char c;
while(cin.get(c)) cout.put(c);
}
A hromparamter s.get(p,n,term) legfeljebb n-1 karaktert olvas be a p[0],, p[n-2] tmb-
be. A get() az tmeneti trban mindenkppen elhelyez egy 0 karaktert a beolvasott karak-
terek utn, gy a p mutatnak egy legalbb n karakter mret tmbre kell mutatnia. A har-
madik paramter (term) az olvasst lezr karaktert hatrozza meg. A hromparamter
get() leggyakoribb felhasznlsi mdja az, hogy beolvasunk egy sort egy rgztett mret
trba s ksbb innen hasznljuk fel karaktereit:
void f()
{
char buf[100];
cin >> buf; // gyans: tlcsordulhat
cin.get(buf,100,'\n'); // biztonsgos
// ...
}
Ha a get() megtallja a lezr karaktert, az adatfolyamban hagyja, teht a kvetkez beme-
neti mvelet ezt a karaktert kapja meg elsknt. Soha ne hvjuk meg jra a get() fggvnyt
a lezr karakter eltvoltsa nlkl. Ha egy get() vagy getline() fggvny egyetlen karaktert
sem olvas s tvolt el az adatfolyamrl meghvdik setstate(failbit), gy a kvetkez beol-
vassok sikertelenek lesznek (vagy kivtel vltdik ki, 21.3.6).
A standard knyvtr 830
void subtle_error
{
char buf[256];
while (cin) {
cin.get(buf,256); // a sor beolvassa
cout << buf; // a sor kirsa.
// Hopp: elfelejtettk eltvoltani cin-rl '\n'-t, a kvetkez get() sikertelen lesz
}
}
Ez a plda jl szemllteti, mirt rdemes a get() helyett a getline() fggvnyt hasznlnunk.
A getline() ugyanis pontosan gy mkdik, mint a megfelel get(), de a lezr karaktert is
eltvoltja az adatfolyambl:
void f()
{
char word[MAX_WORD][MAX_LINE]; // MAX_WORD darab tmb,
// mindegyik MAX_LINE karaktert tartalmaz
int i = 0;
while(cin.getline(word[i++],MAX_LINE,'\n') && i<MAX_WORD);
// ...
}
Ha nem a hatkonysg a legfontosabb, rdemes string (3.6, 20.3.15) objektumba olvas-
nunk, mert azzal elkerlhetjk a leggyakoribb tlcsordulsi, illetve helyfoglalsi problm-
kat. A get(), a getline() s a read() fggvnyre viszont az ilyen magas szint szolgltatsok
megvalstshoz szksg van. A nagyobb sebessg ra a viszonylag kusza fellet, de leg-
albb nem kell jra megvizsglnunk a bemeneti adatfolyamot, ahhoz, hogy megtudjuk, mi
szaktotta meg a beolvassi mveletet; pontosan korltozhatjuk a beolvasand karakterek
szmt s gy tovbb.
A read(p,n) fggvny legfeljebb n karaktert olvas be a p[0], , p[n-1] tmbbe. A read()
nem foglalkozik a bemeneten megjelen lezr karakterekkel s az eredmnytmb vgre
sem helyez 0 karaktert. Ezrt kpes tnyleg n karaktert beolvasni (s nem csak n-1-t). Te-
ht a read() fggvny egyszeren karaktereket olvas, s nem prblja az eredmnyt C st-
lus karakterlncc alaktani.
Az ignore() fggvny ugyangy karaktereket olvas, mint a read(), de egyltaln nem trol-
ja azokat. Egy msik hasonlsg a read() fggvnnyel, hogy tnyleg kpes n (s nem n-1)
darab karakter beolvassra. Az ignore() alaprtelmezs szerint egy karaktert olvas be, te-
ht ha paramterek nlkl hvjuk meg, akkor jelentse dobd el a kvetkez karaktert.
21. Adatfolyamok 831
A getline() fggvnyhez hasonlan itt is megadhatunk egy lezr karaktert, amit eltvolt
a bemeneti adatfolyambl, ha beletkzik. Az ignore() alaprtelmezett lezr karaktere
a fjlvge jel. Ezeknl a fggvnyeknl nem vilgos azonnal, hogy mi lltotta meg az olva-
sst, s nha mg arra is nehz emlkezni, melyik olvassi mveletnl mi volt a lellsi fel-
ttel. Azt viszont mindig meg tudjuk krdezni, hogy elrtk-e mr a fjlvge jelet (21.3.3).
Hasonl segtsg, hogy a gcount() fggvnnyel megllapthatjuk, hogy a legutbbi form-
zatlan bemeneti eljrs hny karaktert olvasott be az adatfolyambl:
void read_a_line(int max)
{
// ...
if (cin.fail()) { // Hopp: hibs bemeneti formtum
cin.clear(); // a bemeneti jelzbitek trlse (21.3.3)
cin.ignore(max,';'); // ugrs a pontosvesszre
if (!cin) {
// hopp: elrtk az adatfolyam vgt
}
else if (cin.gcount()==max) {
// hopp: max szm karaktert beolvastunk
}
else {
// megtalltuk s eldobtuk a pontosvesszt
}
}
}
Sajnos ha a megengedett legtbb karaktert olvastuk be, nincs mdunk annak megllapt-
sra, hogy megtalltuk-e a lezr karaktert (utols beolvasott karakterknt).
A paramter nlkli get() a <cstdio> fejllomnyban szerepl getchar() fggvny (21.8)
<iostream> -beli megfelelje. Egyszeren beolvas egy karaktert s visszaadja annak szm-
rtkt. Ezzel a megoldssal semmit nem kell feltteleznie az ppen hasznlt karaktertpus-
rl. Ha nincs beolvashat karakter a get() adatfolyamban, akkor a megfelel fjlvge jelet
(teht a traits_type::eof() karaktert) adja vissza, majd belltja az istream objektum eof lla-
pott (21.3.3):
void f(unsigned char* p)
{
int i;
while((i = cin.get()) && i!=EOF) {
*p++ = i;
// ...
}
}
A standard knyvtr 832
Az EOF az eof() fggvny ltal visszaadott rtk a char tpus szoksos char_traits osztly-
bl. Az EOF rtket az <iostream> fejllomny hatrozza meg. Teht e ciklus helyett nyu-
godtan rhattuk volna a read(p, MAX_INT) utastst, de tegyk fel, hogy explicit ciklust
akartunk rni, mert, mondjuk, minden beolvasott karaktert egyesvel akartunk ltni. A C
nyelv egyik legnagyobb erssgnek tartjk azt a lehetsget, hogy karaktereket olvasha-
tunk be, s gy dnthetnk, hogy semmit sem csinlunk velk, radsul mindezt gyorsan
tehetjk. Ez tnyleg fontos s albecslt erssg, gy a C++ igyekszik ezt megtartani.
A <cctype> szabvnyos fejllomny sok olyan fggvnyt biztost, amelynek hasznt vehet-
jk a bemenet feldolgozsban (20.4.2). Az eatwhite() fggvny pldul, amely az
reshely karaktereket trli az adatfolyambl, a kvetkezkppen definilhat:
istream& eatwhite(istream& is)
{
char c;
while (is.get(c)) {
if (!isspace(c)) { // c reshely karakter?
is.putback(c); // c visszaraksa a bemenet tmeneti trba
break;
}
}
return is;
}
Az is.putback(c) fggvnyhvsra azrt van szksg, hogy a c legyen a kvetkez karakter,
amit az is adatfolyambl beolvasunk (21.6.4).
21.3.5. Felhasznli tpusok beolvassa
A felhasznli tpusok beolvasst pontosan ugyangy lehet megvalstani, mint kimeneti
mveleteiket. Az egyetlen klnbsg, hogy a bemeneti mveletek esetben a msodik pa-
ramternek nem-konstans referencia tpusnak kell lennie:
istream& operator>>(istream& s, complex& a)
/*
a complex tpus lehetsges bemeneti formtumai ("f" lebegpontos szm)
f
( f )
( f , f )
*/
{
21. Adatfolyamok 833
double re = 0, im = 0;
char c = 0;
s >> c;
if (c == '(') {
s >> re >> c;
if (c == ',') s >> im >> c;
if (c != ')') s.clear(ios_base::failbit); // llapot belltsa
}
else {
s.putback(c);
s >> re;
}
if (s) a = complex(re,im);
return s;
}
A nagyon kevs hibakezel utasts ellenre ez a programrszlet szinte minden hibatpust
kpes kezelni. A loklis c vltoznak azrt adunk kezdrtket, hogy ha az els >> mve-
let sikertelen, nehogy vletlenl pont a ( karakter legyen benne. Az adatfolyam llapotnak
ellenrzsre a fggvny vgn azrt van szksg, mert az a paramter rtkt csak akkor
vltoztathatjuk meg, ha a korbbi mveleteket sikeresen vgrehajtottuk. Ha formzsi hiba
trtnik az adatfolyam llapota failbit lesz. Azrt nem badbit, mert maga az adatfolyam nem
srlt meg. A felhasznl a clear() fggvnnyel alapllapotba helyezheti az adatfolyamot s
tovbblpve rtelmes adatokat nyerhet onnan.
Az adatfolyam llapotnak belltsra szolgl fggvny neve clear(), mert ltalban arra
hasznljuk, hogy az adatfolyam llapott good() rtkre lltsuk. Az ios_base::clear()
(21.3.3) paramternek alaprtelmezett rtke ios_base::goodbit.
21.3.6. Kivtelek
Nagyon knyelmetlen minden egyes I/O mvelet utn kln ellenrizni, hogy sikeres volt-
e, ezrt nagyon gyakori hiba, hogy elfelejtnk egy ellenrzst ott, ahol felttlenl szksg
van r. A kimeneti mveleteket ltalban nem ellenrzik, annak ellenre, hogy nha ott is
elfordulhat hiba.
Egy adatfolyam llapotnak kzvetlen megvltoztatsra csak a clear() fggvnyt hasznl-
hatjuk. Ezrt ha rteslni akarunk az adatfolyam llapotnak megvltozsrl, elg nyilvn-
val mdszer, hogy a clear() fggvnyt kivtelek kivltsra krjk. Az ios_base osztly
exceptions() tagfggvnye pontosan ezt teszi:
A standard knyvtr 834
template <class Ch, class Tr = char_traits<Ch> >
class basic_ios : public ios_base {
public:
// ...
class failure; // kivtelosztly (lsd 14.10)
iostate exceptions() const; // kivtel-llapot kiolvassa
void exceptions(iostate except); // kivtel-llapot belltsa
// ...
};
A kvetkez utastssal pldul elrhetjk, hogy a clear() egy ios_base::failure kivtelt vlt-
son ki, ha a cout adatfolyam bad, fail vagy eof llapotba kerl, vagyis ha valamelyik mve-
let nem hibtlanul fut le:
cout.exceptions(ios_base::badbit|ios_base::failbit|ios_base::eofbit);
Ha szksg van r, a cout vizsglatval pontosan megllapthatjuk, milyen problma tr-
tnt. Ehhez hasonlan a kvetkez utastssal azokat a ritknak egyltaln nem nevezhet
eseteket dolgozhatjuk fel, amikor a beolvasni kvnt adatok formtuma nem megfelel, s
ennek kvetkeztben a bemeneti mvelet nem ad vissza rtket:
cin.exceptions(ios_base::badbit|ios_base::failbit);
Ha az exceptions() fggvnyt paramterek nlkl hvjuk meg, akkor azokat az I/O llapot-
jelzket adja meg, amelyek kivtelt vltanak ki:
void print_exceptions(ios_base& ios)
{
ios_base::iostate s = ios.exceptions();
if (s&ios_base::badbit) cout << "bad kivtelek";
if (s&ios_base::failbit) cout << "fail kivtelek";
if (s&ios_base::eofbit) cout << "eof kivtelek";
if (s == 0) cout << "nincs kivtel";
}
Az I/O kivtelek leggyakoribb felhasznlsi terlete az, hogy a ritkn elfordul (ezrt knnyen
elfelejthet) hibkat kezeljk. Msik fontos feladatuk a ki- s bemenet vezrlse lehet:
21. Adatfolyamok 835
void readints(vector<int>& s) // nem a kedvenc stlusom!
{
ios_base::iostate old_state = cin.exceptions(); // menti a kivtel-llapotot
cin.exceptions(ios_base::eofbit); // eof kivtelt vlt ki
for (;;)
try {
int i;
cin>>i;
s.push_back(i);
}
catch(ios_base::failure) {
// rendben: elrtk a fjl vgt
}
cin.exceptions(old_state); // kivtel-llapot visszalltsa
}
A kivtelek ilyen formban val felhasznlsakor felmerl a krds, nevezhetjk-e az adott
helyzetet hibnak vagy tnyleg kivteles helyzetnek. ltalban mindkt krdsre inkbb
a nem vlaszt adjuk, ezrt gy gondolom, clszerbb az adatfolyam llapott kzvetlenl
vizsglni. Amit egy fggvny belsejben, loklis vezrlsi szerkezetekkel kezelhetnk, azt
a kivtelek sem kezelik jobban.
21.3.7. Adatfolyamok sszektse
A basic_ios osztly tie() fggvnye arra hasznlhat, hogy kapcsolatot ltestsnk egy
istream s egy ostream kztt:
template <class Ch, class Tr = char_traits<Ch> >
class std::basic_ios : public ios_base {
// ...
basic_ostream<Ch,Tr>* tie() const; // mutat sszekttt adatfolyamokra
basic_ostream<Ch,Tr>* tie(basic_ostream<Ch,Tr>* s); // *this hozzktse s-hez
// ...
};
A standard knyvtr 836
Vegyk pldul az albbi programrszletet:
string get_passwd()
{
string s;
cout << "Jelsz: ";
cin >> s;
// ...
}
Hogyan bizonyosodhatnnk meg arrl, hogy a Jelsz: szveg megjelent-e a kpernyn, mi-
eltt a bemeneti mveletet vgrehajtjuk? A cout adatfolyamra kldtt kimenet tmeneti tr-
ba kerl, gy ha a cin s a cout fggetlen egymstl, a Jelsz esetleg mindaddig nem jele-
nik meg a kpernyn, amg a kimeneti tr meg nem telik. A megoldst az jelenti, hogy
a cout adatfolyamot hozzktjk a cin adatfolyamhoz a cin.tie(&cout) utastssal.
Ha egy ostream objektumot sszektnk egy istream objektummal, az ostream mindig ki-
rl, amikor egy bemeneti mvelet alulcsordulst okoz az istream adatfolyamban, azaz
amikor egy bemeneti feladat vgrehajtshoz j karakterekre van szksg a kijellt beme-
neti eszkzrl. Teht ilyenkor a
cout << "Jelsz: ";
cin >> s;
utastssorozat egyenrtk az albbival:
cout << "Jelsz: ";
cout.flush();
cin >> s;
Adott idben minden adatfolyamhoz legfeljebb egy ostream objektum kthet. Az s.tie(0)
utastssal levlaszthatjuk az s objektumhoz kttt adatfolyamot (ha volt ilyen). A tbbi
olyan adatfolyam-fggvnyhez hasonlan, melyek rtket lltanak be, a tie(s) is a korbbi
rtket adja vissza, teht a legutbb ide kttt adatfolyamot, vagy ha ilyen nincs, akkor a 0
rtket. Ha a tie() fggvnyt paramterek nlkl hvjuk meg, akkor egyszeren visszakap-
juk az aktulis rtket, annak megvltoztatsa nlkl.
A szabvnyos adatfolyamok esetben a cout hozz van ktve a cin bemenethez, a wcout
pedig a wcin adatfolyamhoz. A cerr adatfolyamot felesleges lenne brmihez is hozzktni,
mivel ehhez nincs tmeneti tr, a clog pedig nem vr felhasznli kzremkdst.
21. Adatfolyamok 837
21.3.8. rszemek
Amikor a << s a >> opertort a complex tpusra hasznltuk, egyltaln nem foglalkoztunk
az sszekttt adatfolyamok (21.3.7) krdsvel, vagy azzal, hogy az adatfolyam llapot-
nak megvltozsa kivteleket okoz-e (21.3.6). Egyszeren azt feltteleztk (s nem ok nl-
kl), hogy a knyvtr ltal knlt fggvnyek figyelnek helyettnk ezekre a problmkra.
De hogyan kpesek erre? Nhny tucat ilyen fggvnnyel kell megbirkznunk, gy ha olyan
bonyolult eljrsokat kellene ksztennk, amely az sszekttt adatfolyamokkal, a helyi
sajtossgokkal (locale, 21.7, D), a kivtelekkel, s egyebekkel is foglalkoznak, akkor
igen kusza kdot kapnnk.
A megoldst a sentry (rszem) osztly bevezetse jelenti, amely a kzs kdrszleteket
tartalmazza. Azok a rszek, melyeknek elsknt kell lefutniuk (a prefix kd, pldul egy
lekttt adatfolyam kirtse), a sentry konstruktorban kaptak helyet. Az utolsknt fut
sorokat (a suffix kdokat, pldul az llapotvltozsok miatt elvrt kivtelek kivltst)
a sentry destruktora hatrozza meg:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ostream : virtual public basic_ios<Ch,Tr> {
// ...
class sentry;
// ...
};
template <class Ch, class Tr = char_traits<Ch> >
class basic_ostream<Ch,Tr>::sentry {
public:
explicit sentry(basic_ostream<Ch,Tr>& s);
~sentry();
operator bool();
// ...
};
Teht egy ltalnos kdot rtunk, melynek segtsgvel az egyes fggvnyek a kvetkez
formba rhatk:
template <class Ch, class Tr = char_traits<Ch> >
basic_ostream<Ch,Tr>& basic_ostream<Ch,Tr>::operator<<(int i)
{
sentry s(*this);
if (!s) { // ellenrzs, minden rendben van-e a kirs megkezdshez
setstate(failbit);
return *this;
}
A standard knyvtr 838
// az int kirsa
return *this;
}
A konstruktorok s a destruktorok ilyen jelleg felhasznlsa ltalnos az eltag (prefix) s ut-
tag (suffix) kdrszletek beillesztshez, s nagyon sok helyzetben alkalmazhat mdszer.
Termszetesen a basic_istream hasonl sentry tagosztllyal rendelkezik.
21.4. Formzs
A 21.2. pontban bemutatott pldk mind abba a kategriba tartoztak, amit ltalnosan
formzatlan kimenetnek (unformatted output) neveznk. Ez azt jelenti, hogy az objektu-
mot gy alaktottuk karaktersorozatt, hogy csak alaprtelmezett szablyokat hasznltunk.
A programozknak gyakran ennl rszletesebb vezrlsi lehetsgekre van szksgk. N-
ha pldul meg kell hatroznunk, hogy a kimenet mekkora terletet hasznljon, vagy hogy
a szmok milyen formban jelenjenek meg. Az ehhez hasonl tnyezk vezrlsre a be-
menet esetben is szksg lehet.
A ki- s bemenet formzst vezrl eszkzk a basic_ios osztlyban, illetve annak
ios_base bzisosztlyban kaptak helyet. A basic_ios pldul informcikat tartalmaz
a szmrendszerrl (nyolcas, tzes vagy tizenhatos), amit egsz szmok kirsakor s beolva-
ssakor hasznl a rendszer, vagy a lebegpontos szmok pontossgrl s gy tovbb.
Az ezen adatfolyam-szint (minden adatfolyamra kln bellthat) vltozk lekrdezs-
re s belltsra szolgl fggvnyek szintn ebben az osztlyban kaptak helyet.
A basic_ios bzisosztlya a basic_istreams a basic_ostreamosztlynak, gy a formzs ve-
zrlse minden adatfolyamra kln adhat meg.
21. Adatfolyamok 839
21.4.1. Formzsi llapot
A ki- s bemenet formzst nhny jelzbit s nhny egsz rtk vezrli, melyek az adat-
folyamok ios_base bzisosztlyban szerepelnek:
class ios_base {
public:
// ...
// formzsjelzk nevei:
typedef megvalsts_fgg1 fmtflags;
static const fmtflags
skipws, // reshelyek tlpse a bemeneten
left, // mezigazts: feltlts az rtk utn
right, // feltlts az rtk eltt
internal, // feltlts eljel s rtk kztt
boolalpha, // a true s false szimbolikus megjelentse
dec, // szmrendszer alapja egszeknl: 10 (decimlis)
hex, // 16 (hexadecimlis)
oct, // 8 (oktlis)
scientific, // lebegpontos jells: d.ddddddEdd
fixed, // dddd.dd
showbase, // kirskor oktlisak el 0, hexadecimlisok el 0x eltag
showpoint, // a zr nullk kirsa
showpos, // '+' a pozitv egszek el
uppercase, // 'E', 'X' alkalmazsa ('e', 'x' helyett)
adjustfield, // mezigaztssal kapcsolatos jelz (21.4.5)
basefield, // egsz szmrendszer alapjval kapcsolatos jelz (21.4.2)
floatfield; // lebegpontos kimenettel kapcsolatos jelz (21.4.3)
fmtflags unitbuf; // minden kimenet utn az tmeneti tr rtse
fmtflags flags() const; // jelzbitek kiolvassa
fmtflags flags(fmtflags f); // jelzbitek belltsa
fmtflags setf(fmtflags f) { return flags(flags()|f); } // jelzbit hozzadsa
A standard knyvtr 840
// a jelzbitek trlse s belltsa a maszkban
fmtflags setf(fmtflags f, fmtflags mask) { return flags((flags()&~mask)|(f&mask)); }
void unsetf(fmtflags mask) { flags(flags()&~mask); } // jelzbitek trlse
// ...
};
Az egyes jelzbitek (flag) rtke az adott nyelvi vltozattl fgg, gy mindig a szimbolikus
neveket hasznljuk a konkrt szmrtkek helyett, mg akkor is, ha az ltalunk hasznlt r-
tkek vletlenl ppen megfelelen mkdnek.
Egy felletet jelzk sorozatval, illetve azokat bellt s lekrdez fggvnyekkel megha-
trozni idtakarkos, de kicsit rgimdi mdszer. Legfbb ernye, hogy a felhasznl a le-
hetsgeket sszeptheti:
const ios_base::fmtflags my_opt = ios_base::left|ios_base::oct|ios_base::fixed;
Ez lehetv teszi, hogy egy fggvnynek belltsokat adjunk t, s akkor kapcsoljuk be
azokat, amikor szksg van rjuk:
void your_function(ios_base::fmtflags opt)
{
ios_base::fmtflags old_options = cout.flags(opt); // rgi belltsok mentse, jak belltsa
// ...
cout.flags(old_options); // visszallts
}
void my_function()
{
your_function(my_opt);
// ...
}
A flags() fggvny az eddig rvnyben lev belltsokat adja vissza.
Ha kpesek vagyunk az sszes tulajdonsg egyttes rsra s olvassra, egyesvel is be-
llthatjuk azokat:
myostream.flags(myostream.flags()|ios_base::showpos);
Ezen utasts hatsra a myostream adatfolyam minden pozitv szm eltt egy + jelet fog
megjelenteni; ms belltsok nem vltoznak meg. Elszr beolvassuk a rgi belltsokat,
21. Adatfolyamok 841
majd belltjuk a showpos tulajdonsgot gy, hogy az eredeti szmhoz a bitenknti vagy
mvelettel hozzkapcsoljuk ezt az rtket. A setf() fggvny pontosan ugyanezt teszi, teht
a fenti pldval teljesen egyenrtk az albbi sor:
myostream.setf(ios_base::showpos);
Egy jelz mindaddig megtartja rtkt, amg azt meg nem vltoztatjuk.
A ki- s bemeneti tulajdonsgok vezrlse a jelzbitek kzvetlen belltsval igen nyers
megolds s knnyen hibkhoz vezethet. Az egyszerbb esetekben a mdostk
(manipulator) (21.4.6) tisztbb felletet adnak. A jelzbitek hasznlata egy adatfolyam l-
lapotnak vezrlsre a megvalstsi mdszerek tanulmnyozshoz megfelel, a felletter-
vezshez mr kevsb.
21.4.1.1. Formzsi llapot lemsolsa
A copyfmt() fggvny segtsgvel egy adatfolyam teljes formzsi llapott lemsolhatjuk:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ios : public ios_base {
public:
// ...
basic_ios& copyfmt(const basic_ios& f);
// ...
};
Az adatfolyam tmeneti trn (21.6) s annak llapotn kvl a copyfmt() minden llapot-
tulajdonsgot tmsol, teht a kivtelkezelsi mdot (21.3.6) s a felhasznl ltal meg-
adott tovbbi belltsokat is (21.7.1).
21.4.2. Egsz kimenet
A bitenknti vagy hasznlata egy j bellts megadshoz a flags() s setf() fggvnyek
segtsgvel csak akkor hasznlhat, ha az adott tulajdonsgot egy bit hatrozza meg. Nem
ez a helyzet azonban egy olyan jellemznl, mint pldul az egszek kirshoz hasznlt
szmrendszer vagy a lebegpontos szmok kimeneti formtuma. Az ilyen tulajdonsgok
esetben az rtk, amely egy adott stlust brzol, nem fogalmazhat meg egyetlen bittel
vagy akr egymstl fggetlen bitek sorozatval.
A standard knyvtr 842
Az <iostream> fejllomnyban alkalmazott megolds az, hogy a setf() fggvny olyan vl-
tozatt hatrozza meg, amelynek egy msodik, lparamtert is meg kell adnunk. Ez a pa-
ramter hatrozza meg, milyen tulajdonsgot akarunk belltani az j rtkkel:
cout.setf(ios_base::oct,ios_base::basefield); // oktlis
cout.setf(ios_base::dec,ios_base::basefield); // decimlis
cout.setf(ios_base::hex,ios_base::basefield); // hexadecimlis
Ezek az utastsok az egszek szmrendszer-alapjt lltjk be, anlkl, hogy az adatfolyam
llapotnak brmely ms rszt megvltoztatnk. Az alapszm mindaddig vltozatlan ma-
rad, amg meg nem vltoztatjuk. Pldul az albbi utastssorozat eredmnye 1234 1234
2322 2322 4d2 4d2.
cout << 1234 << ' ' << 1234 << ' '; // alaprtelmezett: decimlis
cout.setf(ios_base::oct,ios_base::basefield); // oktlis
cout << 1234 << ' ' << 1234 << ' ';
cout.setf(ios_base::hex,ios_base::basefield); // hexadecimlis
cout << 1234 << ' ' << 1234 << ' ';
Ha az eredmnyrl meg kell tudnunk llaptani, hogy melyik szm milyen szmrendszerben
rtend, a showbase belltst hasznlhatjuk. Teht ha az elbbi utastsok el beszrjuk a
cout.setf(ios_base::showbase);
utastst, akkor az 1234 1234 02322 02322 0x4d2 0x4d2 szmsorozatot kapjuk. A szabv-
nyos mdostk (manipulator) (21.4.6.2) elegnsabb megoldst knlnak az egsz szmok
kirshoz hasznlt szmrendszer belltsra.
21.4.3. Lebegpontos szm kimenet
A lebegpontos szmok kimenett kt tnyez befolysolja: a formtum(format) s a pon-
tossg (precision).
Az ltalnos (general) formtum lehetv teszi a megvalsts szmra, hogy olyan
szmformtumot hasznljon, amely a rendelkezsre ll terleten a lehet legjob-
ban brzolja a lebegpontos rtket. A pontossg a megjelen szmjegyek maxi-
mlis szmt hatrozza meg. Ez a jellemz a printf() fggvny %g belltsnak
felel meg (21.8).
21. Adatfolyamok 843
A tudomnyos (scientific) formtumban egy szmjegy szerepel a tizedespont eltt,
s egy kitev (exponens) hatrozza meg a szm nagysgrendjt. A pontossg ez
esetben a tizedespont utn ll szmjegyek maximlis szmt jelenti. Ezt az esetet
a printf() fggvny %e belltsa valstja meg.
A fixpontos (fixed) formtum a szmot egszrsz, tizedespont, trtrsz formban je-
lenti meg. A pontossg most is a tizedespont utn ll szmjegyek szmnak maxi-
mumt adja meg. A printf() fggvny %f belltsa viselkedik gy.
A lebegpontos szmok kimeneti formtumt az llapotmdost fggvnyek (state
manipulator function) segtsgvel szablyozhatjuk. Ezek felhasznlsval a lebegpontos
rtkek megjelentst gy llthatjuk be, hogy az adatfolyam llapotnak ms rszeit nem
befolysoljuk:
cout << "Alaprtelmezett:\t" << 1234.56789 << '\n';
cout.setf(ios_base::scientific,ios_base::floatfield); // tudomnyos formtum hasznlata
cout << "Tudomnyos:\t" << 1234.56789 << '\n';
cout.setf(ios_base::fixed,ios_base::floatfield); // fixpontos formtum hasznlata
cout << "Fixpontos:\t" << 1234.56789 << '\n';
cout.setf(0,ios_base::floatfield); // alaprtelmezs visszalltsa
// (ltalnos formtum)
cout << "Alaprtelmezett:\t" << 1234.56789 << '\n';
Az eredmny a kvetkez lesz:
default: 1234.57
scientific: 1.234568e+03
fixed: 1234.567890
default: 1234.57
A pontossg alaprtelmezett rtke minden formtum esetben 6, amit az ios_base osztly
kln tagfggvnyvel mdosthatunk:
class ios_base {
public:
// ...
streamsize precision() const; // pontossg lekrdezse
streamsize precision(streamsize n); // pontossg belltsa (s a rgi pontossg lekrdezse)
// ...
};
A standard knyvtr 844
A precision() fggvny meghvsval minden lebegpontos I/O mvelet pontossgt meg-
vltoztatjuk az adatfolyamban a fggvny kvetkez meghvsig. Ezrt a
cout.precision(8);
cout << 1234.56789 << ' ' << 1234.56789 << ' ' << 123456 << '\n';
cout.precision(4);
cout << 1234.56789 << ' ' << 1234.56789 << ' ' << 123456 << '\n';
utastssorozat eredmnye a kvetkez lesz:
1234.5679 1234.5679 123456
1235 1235 123456
A pldban kt dolgot figyelhetnk meg: az egyik, hogy a lebegpontos rtkek kerektve
jelennek meg, nem egyszeren levgva, a msik, hogy a precision() az egsz rtkek meg-
jelentsre nincs hatssal.
Az uppercase jelzbit (21.4.1) azt hatrozza meg, hogy a tudomnyos formtumban e vagy
E bet jellje a kitevt.
A mdostk (manipulator) elegnsabb megoldst knlnak a lebegpontos szmok kime-
neti formtumnak belltsra (21.4.6.2).
21.4.4. Kimeneti mezk
Gyakran arra van szksgnk, hogy egy kimeneti sor adott terlett tltsk fel szveggel.
Ilyenkor pontosan n karaktert akarunk hasznlni, kevesebbet semmikpp (tbbet pedig
csak akkor, ha a szveg nem fr el a meghatrozott terleten). Ehhez meg kell adnunk a te-
rlet szlessgt s a kitlt karaktert:
class ios_base {
public:
// ...
streamsize width() const; // mezszlessg lekrdezse
streamsize width(streamsize wide); // mezszlessg belltsa
// ...
};
template <class Ch, class Tr = char_traits<Ch> >
class basic_ios : public ios_base {
public:
// ...
21. Adatfolyamok 845
Ch fill() const; // kitlt karakter lekrdezse
Ch fill(Ch ch); // kitlt karakter belltsa
// ...
};
A width() fggvny a kimen karakterek legkisebb szmt hatrozza meg a standard
knyvtr kvetkez olyan << mveletben, amellyel szmrtket, logikai rtket, C stlus
karakterlncot, karaktert, mutatt (21.2.1), string objektumot (20.3.15) vagy bitfield vlto-
zt (17.5.3.3) runk ki:
cout.width(4);
cout << 12;
Ez az utasts a 12 szmot kt szkz karakter utn rja ki.
A kitlt karaktert a fill() fggvny segtsgvel adhatjuk meg:
cout.width(4);
cout.fill('#');
cout << "ab";
Az eredmny ##ab lesz.
Az alaprtelmezett kitlt karakter a szkz, az alaprtelmezett pontossg pedig 0, ami
annyi karaktert jelent, amennyire szksg van. A kimeneti mez mrett teht a kvetkez
utastssal llthatjuk vissza alaprtelmezett rtkre:
cout.width(0); // "annyi karakter, amennyi csak kell"
A width(n) utastssal a kirhat karakterek legkisebb szmt n-re lltjuk. Ha ennl tbb
karaktert adunk meg, akkor azok mind megjelennek:
cout.width(4);
cout << "abcdef";
Az eredmny abcdef lesz, nem pedig csak abcd. Ez azrt van gy, mert ltalban jobb
a megfelel rtket csnya formban megkapni, mint a rossz rtket szpen igaztva (lsd
mg 21.10[21]).
A standard knyvtr 846
A width(n) fggvnyhvs csak a kzvetlenl utna kvetkez << kimeneti mveletre
vonatkozik:
cout.width(4);
cout.fill('#');
cout << 12 << ':' << 13;
Az eredmny csak ##12:13 lesz s nem ##12###:##13, ami akkor jelenne meg, ha
a width(4) minden ksbbi mveletre vonatkozna. Ha a width() fggvnyt tbb, egyms
utni kimeneti mveletre is alkalmazni szeretnnk, knytelenek lesznk minden egyes r-
tkhez kln-kln megadni.
A szabvnyos mdostk (modifier) (21.4.6.2) elegnsabb megoldst knlnak a kimeneti
mezk mretnek szablyozsra is.
21.4.5. Mezk igaztsa
A karakterek mezn belli igaztst (adjustment) a setf() fggvny meghvsval llthat-
juk be:
cout.setf(ios_base::left,ios_base::adjustfield); // bal
cout.setf(ios_base::right,ios_base::adjustfield); // jobb
cout.setf(ios_base::internal,ios_base::adjustfield); // bels
Ezek az utastsok az ios_base::width() fggvnnyel meghatrozott kimeneti mezn bell
adjk meg az igaztst, az adatfolyam egyb belltsaira nincsenek hatssal.
Az igaztst a kvetkezkppen hasznlhatjuk.
cout.fill('#');
cout << '(';
cout.width(4);
cout << -12 << "),(";
cout.width(4);
cout.setf(ios_base::left,ios_base::adjustfield);
cout << -12 << "),(";
cout.width(4);
cout.setf(ios_base::internal,ios_base::adjustfield);
cout << -12 << ")";
21. Adatfolyamok 847
Az eredmny: (#-12), (-12#), (-#12). Az internal bellts a kitlt karaktereket az eljel s az
rtk kz helyezi. A pldbl lthatjuk, hogy az alaprtelmezett bellts a jobbra igazts.
21.4.6. Mdostk
A standard knyvtr szeretn megkmlni a programozt attl, hogy az adatfolyamok lla-
pott jelzbiteken keresztl lltsa be, ezrt kln fggvnyeket knl ezen feladat megol-
dshoz. Az alaptlet az, hogy a kirt vagy beolvasott objektumok kztt adjuk meg azokat
a mveleteket is, melyek az adatfolyam llapott megvltoztatjk. Az albbi utastssal pl-
dul a kimenet tmeneti trnak azonnali kirtsre szlthatjuk fel az adatfolyamot:
cout << x << flush << y << flush;
A megfelel helyeken a cout.flush() fggvny fut le. Ezt az tletet gy valsthatjuk meg,
hogy egy olyan << vltozatot ksztnk, amely egy fggvnyre hivatkoz mutatt vr para-
mterknt s trzsben lefuttatja a hivatkozott fggvnyt:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ostream : virtual public basic_ios<Ch,Tr> {
public:
// ...
basic_ostream& operator<<(basic_ostream& (*f)(basic_ostream&)) { return f(*this); }
basic_ostream& operator<<(ios_base& (*f)(ios_base&));
basic_ostream& operator<<(basic_ios<Ch,Tr>& (*f)(basic_ios<Ch,Tr>&));
// ...
};
Ahhoz, hogy ez a megolds mkdjn, a (mutatknt tadott) fggvnynek nem szabad
tagfggvnynek lennie (legfeljebb statikus tagfggvnynek), s a megfelel tpussal kell
rendelkeznie. Ezrt a flush() fggvnyt pldul a kvetkezkppen kell meghatroznunk:
template <class Ch, class Tr = char_traits<Ch> >
basic_ostream<Ch,Tr>& flush(basic_ostream<Ch,Tr>& s)
{
return s.flush(); // az ostream osztly flush() tagfggvnynek meghvsa
}
Ezen deklarcik utn a
cout << flush;
A standard knyvtr 848
utasts egyenrtk lesz a
cout.operator<<(flush);
utastssal, ami pedig meghvja a
flush(cout);
fggvnyt, gy vgl a
cout.flush();
kerl vgrehajtsra.
A teljes eljrs fordtsi idben trtnik, gy lehetv teszi, hogy a basic_ostream::flush()
fggvnyt cout<<flush formban hvjuk meg. Nagyon sok olyan mvelet van, amelyet kz-
vetlenl egy ki- vagy bemeneti mvelet eltt vagy utn akarunk vgrehajtani:
cout << x;
cout.flush();
cout << y;
unset(ios_base::skipws); // ne ugorjuk t az reshelyeket
cin >> x;
Ha ezeket a mveleteket kln utastsokknt kell megfogalmaznunk, a mveletek kzt-
ti kapcsolatok kevsb fognak ltszdni, mrpedig az ilyen logikai kapcsolatok elvesztse
ersen rontja a program olvashatsgt. A mdostk (manipulator) lehetv teszik, hogy
az olyan mveleteket, mint a flush() s a noskipws(), kzvetlenl a ki- vagy bemeneti m-
veletek listjban helyezzk el:
cout << x << flush << y << flush;
cin >> noskipws >> x;
Megjegyzend, hogy a mdostk az std nvtrhez tartoznak, ezrt minstennk kell azo-
kat, amennyiben az std nem rsze az adott hatkrnek:
std::cout << endl; // hiba: endl nincs a hatkrben
std::cout << std::endl; // rendben
21. Adatfolyamok 849
Termszetesen a basic_istreama mdostk szmra is ugyangy biztostja a >> opertoro-
kat, mint a basic_ostream:
template <class Ch, class Tr = char_traits<Ch> >
class basic_istream : virtual public basic_ios<Ch,Tr> {
public:
// ...
basic_istream& operator>>(basic_istream& (*pf)(basic_istream&));
basic_istream& operator>>(basic_ios<Ch,Tr>& (*pf)(basic_ios<Ch,Tr>&));
basic_istream& operator>>(ios_base& (*pf)(ios_base&));
// ...
};
21.4.6.1. Mdostk, melyek paramtereket vrnak
Nagyon hasznosak lennnek az olyan mdostk is, melyeknek paramtereket is t tudunk
adni. Pldul j lenne, ha lerhatnnk az albbi sort, s vele az angle lebegpontos vltoz
megjelentst 4 jegy pontossgra llthatnnk:
cout << setprecision(4) << angle;
Ennek elrshez a setprecision-nek egy objektumot kell visszaadnia, amelynek a 4 kezd-
rtket adjuk, s amely meghvja a cout::setprecision(4) fggvnyt. Az ilyen mdostk fgg-
vnyobjektumok, amelyeket a () helyett a << opertor hv meg. A fggvnyobjektum pon-
tos tpusa az adott megvalststl fgg; egy lehetsges defincija pldul a kvetkez:
struct smanip {
ios_base& (*f)(ios_base&,int); // meghvand fggvny
int i;
smanip(ios_base& (*ff)(ios_base&,int), int ii) : f(ff), i(ii) { }
};
template<class Ch, class Tr>
ostream<Ch,Tr>& operator<<(ostream<Ch,Tr>& os, const smanip& m)
{
return m.f(os,m.i);
}
Az smanip konstruktor paramtereit az f-ben s az i-ben trolja, majd az operator<< egy f(i)
fggvnyhvst hajt vgre. Ennek felhasznlsval a fenti setprecision() mdost a kvet-
kezkppen definilhat:
A standard knyvtr 850
ios_base& set_precision(ios_base& s, int n) // segd
{
return s.precision(n); // a tagfggvny hvsa
}
inline smanip setprecision(int n)
{
return smanip(set_precision,n); // fggvnyobjektum ltrehozsa
}
gy mr lerhatjuk az utastst:
cout << setprecision(4) << angle ;
A programozk sajt mdostkat is megadhatnak az smanip stlusban, ha ms lehets-
gekre is szksgk van (21.10[22]). Ehhez nem kell megvltoztatniuk a standard knyvtr
osztlyait s sablonjait (pldul basic_istream, basic_ostream, basic_ios vagy ios_base).
21.4.6.2. Szabvnyos ki- s bemeneti mdostk
A standard knyvtrban sok mdost (manipulator) szerepel a klnbz formzsi llapo-
tok kezelshez. A szabvnyos mdostk az std nvtrben tallhatk. Az ios_base osztlyhoz
kapcsold mdostk az <ios> fejllomnyon keresztl rhetk el, mg az istream vagy az
ostream felhasznlsval mkd mdostk az <istream> s az <ostream> (illetve nha az
<iostream>) fejllomnybl. A tbbi szabvnyos mdostt az <iomanip> fejllomny adja
meg.
ios_base& boolalpha(ios_base&); // true s false szimbolikus jelzse (bemenet s
// kimenet)
ios_base& noboolalpha(ios_base& s); // s.unsetf(ios_base::boolalpha)
ios_base& showbase(ios_base&); // kimenetnl oktlishoz 0, hexadecimlishoz 0x
// eltag
ios_base& noshowbase(ios_base& s); // s.unsetf(ios_base::showbase)
ios_base& showpoint(ios_base&);
ios_base& noshowpoint(ios_base& s); // s.unsetf(ios_base::showpoint)
ios_base& showpos(ios_base&);
ios_base& noshowpos(ios_base& s); // s.unsetf(ios_base::showpos)
ios_base& skipws(ios_base&); // reshelyek tugrsa
ios_base& noskipws(ios_base& s); // s.unsetf(ios_base::skipws)
21. Adatfolyamok 851
ios_base& uppercase(ios_base&); // X s E (x s e helyett)
ios_base& nouppercase(ios_base&); // x s e (X s E helyett)
ios_base& internal(ios_base&); // igazts (21.4.5)
ios_base& left(ios_base&); // feltlts rtk utn
ios_base& right(ios_base&); // feltlts rtk eltt
ios_base& dec(ios_base&); // egsz szmrendszer alapja: 10 (21.4.2)
ios_base& hex(ios_base&); // egsz szmrendszer alapja: 16
ios_base& oct(ios_base&); // egsz szmrendszer alapja: 8
ios_base& fixed(ios_base&); // lebegpontos, fixpontos: dddd.dd (21.4.3)
ios_base& scientific(ios_base&); // tudomnyos formtum: d.ddddEdd
template <class Ch, class Tr>
basic_ostream<Ch,Tr>& endl(basic_ostream<Ch,Tr>&); // '\n' kirsa s az
// adatfolyam rtse
template <class Ch, class Tr>
basic_ostream<Ch,Tr>& ends(basic_ostream<Ch,Tr>&); // '\0' kirsa s az
// adatfolyam rtse
template <class Ch, class Tr>
basic_ostream<Ch,Tr>& flush(basic_ostream<Ch,Tr>&); // az adatfolyam rtse
template <class Ch, class Tr>
basic_istream<Ch,Tr>& ws(basic_istream<Ch,Tr>&); // reshely "lenyelse"
smanip resetiosflags(ios_base::fmtflags f); // jelzbitek trlse (21.4)
smanip setiosflags(ios_base::fmtflags f); // jelzbitek belltsa (21.4)
smanip setbase(int b); // egszek kirsa b alap szmrend
// szerben (21.4.2)
smanip setfill(int c); // legyen c a kitlt karakter (21.4.4)
smanip setprecision(int n); // n szmjegy (21.4.3, 21.4.6)
smanip setw(int n); // a kvetkez mezszlessg n karakter
// (21.4.4)
Pldul a
cout << 1234 << ',' << hex << 1234 << ',' << oct << 1234 << endl;
utasts eredmnye 1234, 4d2, 2322, mg a
cout << '(' << setw(4) << setfill('#') << 12 << ") (" << 12 << ")\n";
eredmnye (##12) (12).
A standard knyvtr 852
Figyeljnk r, hogy ha olyan mdostkat hasznlunk, melyek nem vesznek t paramtere-
ket, akkor nem szabad kitennnk a zrjeleket. A paramtereket is fogad mdostk hasz-
nlathoz az <iomanip> fejllomnyt be kell ptennk (#include):
#include <iostream>
using namespace std;
int main()
{
cout << setprecision(4) // hiba: setprecision nem meghatrozott (<iomanip> kimaradt)
<< scientific() // hiba: ostream<<ostream& ("hamis" zrjelek)
<< 3.141421 << endl;
}
21.4.6.3. Felhasznli mdostk
A szabvnyos mdostk stlusban a programoz is kszthet j mdostkat. Az albbiak-
ban egy olyan eszkzt mutatunk be, melynek a lebegpontos szmok formzsakor vehet-
jk hasznt.
A pontossg minden tovbbi kimeneti mveletre vonatkozik, mg a szlessg-bellts csak
a kvetkez numerikus kimeneti utastsra. Clunk most az lesz, hogy egy lebegpontos
szmot az ltalunk megkvnt formban jelentsnk meg, anlkl, hogy a ksbbi kimene-
ti mveletek formzsra hatssal lennnk. Az alaptlet az, hogy egy olyan osztlyt ksz-
tnk, amely formtumokat brzol, s egy olyat, amely a formzson kvl a formzni k-
vnt rtket is trolja. Ennek felhasznlsval kszthetnk egy olyan << opertort, amely
a kimeneti adatfolyamon az adott formban jelenti meg az rtket:
Form gen4(4); // ltalnos formtum, pontossg 4
void f(double d)
{
Form sci8 = gen4;
sci8.scientific().precision(8); // tudomnyos formtum, pontossg 8
cout << d << ' ' << gen4(d) << ' ' << sci8(d) << ' ' << d << '\n';
}
Az f(1234.56789) fggvnyhvs eredmnye a kvetkez lesz:
1234.57 1235 1.23456789e+03 1234.57
Figyeljk meg, hogy egy Form hasznlata nem befolysolja az adatfolyam llapott, hiszen
21. Adatfolyamok 853
a d utols kiratsakor ugyanazt a formt kapjuk, mint az elsben.
me, egy leegyszerstett vltozat:
class Bound_form; // forma s rtk
class Form {
friend ostream& operator<<(ostream&, const Bound_form&);
int prc; // pontossg
int wdt; // szlessg, 0 jelentse: a szksges szlessg
int fmt; // ltalnos, tudomnyos, vagy fix (21.4.3)
// ...
public:
explicit Form(int p = 6) : prc(p) // alaprtelmezett pontossg 6
{
fmt = 0; // ltalnos formtum (21.4.3)
wdt = 0; // szksges szlessg
}
Bound_form operator()(double d) const; // Bound_form objektum ltrehozsa *this
// s d alapjn
Form& scientific() { fmt = ios_base::scientific; return *this; }
Form& fixed() { fmt = ios_base::fixed; return *this; }
Form& general() { fmt = 0; return *this; }
Form& uppercase();
Form& lowercase();
Form& precision(int p) { prc = p; return *this; }
Form& width(int w) { wdt = w; return *this; } // minden tpusra
Form& fill(char);
Form& plus(bool b = true); // explicit pozitv eljel
Form& trailing_zeros(bool b = true); // zr nullk kirsa
// ...
};
Az tlet az, hogy a Form minden olyan informcit trol, ami egy adatelem formzshoz
szksges. Az alaprtelmezett rtkeket gy vlasztottuk meg, hogy azok a legtbb esetben
megfelelk legyenek; az egyes formzsi belltsokat a tagfggvnyek segtsgvel kln-
kln adhatjuk meg. A kirand rtkhez a meghatrozott formzst a ( ) opertor segts-
gvel ktjk hozz. A Bound_form objektum ezek utn a megfelel << opertor segtsg-
vel tetszleges kimeneti adatfolyamon megjelenthet:
A standard knyvtr 854
struct Bound_form {
const Form& f;
double val;
Bound_form(const Form& ff, double v) : f(ff), val(v) { }
};
Bound_form Form::operator()(double d) { return Bound_form(*this,d); }
ostream& operator<<(ostream& os, const Bound_form& bf)
{
ostringstream s; // karakterlnc-folyamok lersa: 21.5.3
s.precision(bf.f.prc);
s.setf(bf.f.fmt,ios_base::floatfield);
s << bf.val; // s sszelltsa
return os << s.str(); // s kirsa os-re
}
A << opertor egy kevsb egyszer vltozatnak elksztst feladatnak hagyjuk
(21.10[21]). A Form s a Bound_form osztlyt knnyen kibvthetjk, hogy egszek, ka-
rakterlncok stb. formzsra is hasznlhat legyen (lsd 21.10[20]).
Megfigyelhetjk, hogy ezen deklarcik a << s a ( ) prostsval egy hrmas opertort
hoznak ltre. A cout<<sci4(d) utastssal egyetlen fggvnyben kapcsolunk ssze egy
ostream objektumot, egy formtumot s egy rtket, mieltt a tnyleges mveletet vgre-
hajtannk.
21.5. Fjl- s karakterlnc-folyamok
Amikor egy C++ programot elindtunk, a cout, a cerr, a clog, s a cin, illetve
szleskarakteres megfelelik (21.2.1) azonnal elrhetk. Ezeket az adatfolyamokat a rend-
szer automatikusan hozza ltre s kti hozz a megfelel I/O eszkzhz vagy fjlhoz. Eze-
ken kvl azonban sajt adatfolyamokat is ltrehozhatunk, s ezek esetben neknk kell
megmondanunk, hogy mihez akarjuk azokat ktni. Az adatfolyamoknak fjlokhoz, illetve
karakterlncokhoz val ktse elg ltalnos feladat ahhoz, hogy a standard knyvtr kz-
vetlenl tmogassa. Az albbi bra a szabvnyos adatfolyam-osztlyok viszonyrendszert
mutatja be:
21. Adatfolyamok 855
Azok az osztlyok, melyek neve utn a <> jel szerepel, olyan sablonok, melyeknek para-
mtere egy karaktertpus. Ezek teljes neve a basic_ eltaggal kezddik. A pontozott vonal
virtulis bzisosztlyt jell (15.2.4).
A fjlok s a karakterlncok azon trolk kz tartoznak, melyeket rsra s olvassra is fel-
hasznlhatunk. Ezrt ezekhez olyan adatfolyamokat hatrozhatunk meg, melyek a << s
a >> mveleteket egyarnt tmogatjk. Az ilyen adatfolyamok bzisosztlya az iostream,
amely az std nvtrhez tartozik s az <iostream> fejllomny rja le:
template <class Ch, class Tr = char_traits<Ch> >
class basic_iostream : public basic_istream<Ch,Tr>, public basic_ostream<Ch,Tr> {
public:
explicit basic_iostream(basic_streambuf<Ch,Tr>* sb);
virtual ~basic_iostream();
};
typedef basic_iostream<char> iostream;
typedef basic_iostream<wchar_t> wiostream;
Egy iostream rst s olvasst a streambuf objektumn (21.6.4) vgzett ki- s bemeneti
trmveletekkel vezrelhetjk.
21.5.1. Fjlfolyamok
A kvetkez pldban bemutatunk egy teljes programot, amely egy fjlt egy msikba m-
sol. A fjlneveket parancssori paramterekknt lehet megadni:
A standard knyvtr 856
ios_base
ios<>
istream<> ostream<>
iostream<>
istrigstream<> ifstream<> ofstream<> ostringstream<>
stringstream<> fstream<>
#include <fstream>
#include <cstdlib>
void error(const char* p, const char* p2 = "")
{
cerr << p << ' ' << p2 << '\n';
std::exit(1);
}
int main(int argc, char* argv[ ])
{
if (argc != 3) error("Hibs paramterszm");
std::ifstream from(argv[1]); // bemeneti fjlfolyam megnyitsa
if (!from) error("A bemeneti fjl nem nyithat meg",argv[1]);
std::ofstream to(argv[2]); // kimeneti fjlfolyam megnyitsa
if (!to) error("A kimeneti fjl nem nyithat meg",argv[2]);
char ch;
while (from.get(ch)) to.put(ch);
if (!from.eof() || !to) error("Vratlan esemny trtnt");
}
Egy fjlt olvassra az ifstreamosztly egy objektumnak ltrehozsval nyithatunk meg, pa-
ramterknt a fjl nevt megadva. Ugyangy az ofstreamosztly felhasznlsval a fjlt rs-
ra kszthetjk fel. Mindkt esetben a ltrehozott objektum llapotnak vizsglatval ellen-
rizzk, hogy sikerlt-e a fjl megnyitsa. A basic_ofstream az <fstream> fejllomnyban
a kvetkezkppen szerepel:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ofstream : public basic_ostream<Ch,Tr> {
public:
basic_ofstream();
explicit basic_ofstream(const char* p, openmode m = out);
basic_filebuf<Ch,Tr>* rdbuf() const; // mutat az aktulis tmeneti trra (21.6.4)
bool is_open() const;
void open(const char* p, openmode m = out);
void close();
};
21. Adatfolyamok 857
A basic_ifstream nagyon hasonlt a basic_ofstream osztlyra, azzal a klnbsggel, hogy
a basic_istream osztlybl szrmazik, s alaprtelmezs szerint olvassra nyithatjuk meg.
Ezeken kvl a standard knyvtr biztostja a basic_fstreamosztlyt is, amely szintn hason-
lt a basic_ofstream-re, csak itt a bzisosztly a basic_iostream, s alaprtelmezs szerint r-
hat s olvashat is.
Szoks szerint a leggyakrabban hasznlt tpusokhoz nll tpusnevek (typedef-ek) tartoznak:
typedef basic_ifstream<char> ifstream;
typedef basic_ofstream<char> ofstream;
typedef basic_fstream<char> fstream;
typedef basic_ifstream<wchar_t> wifstream;
typedef basic_ofstream<wchar_t> wofstream;
typedef basic_fstream<wchar_t> wfstream;
A fjlfolyamok konstruktorainak msodik paramterben ms megnyitsi mdokat is meg-
adhatunk:
class ios_base {
public:
// ...
typedef megvalsts_fgg3 openmode;
static openmode app, // hozzfzs
ate, // megnyits s pozcionls a fjl vgre
// (kiejtse: "at end")
binary, // binris I/O (a szveges (text) md
// ellentte)
in, // megnyits olvassra
out, // megnyits rsra
trunc; // fjl csonkolsa 0 hosszsgra
// ...
};
Az openmode konstansok konkrt rtke s jelentse a megvalststl fgg, ezrt ha rsz-
leteket szeretnnk megtudni, akkor sajt fejlesztrendszernk s standard knyvtrunk le-
rst kell elolvasnunk, vagy ksrleteznnk kell. A megjegyzsekbl kvetkeztethetnk,
hogy az egyes mdoktl krlbell mit vrhatunk. Pldul egy fjlt megnyithatunk gy,
hogy minden, amit belerunk, a vgre kerljn:
ofstream mystream(name.c_str(),ios_base::app);
A standard knyvtr 858
De megnyithatunk egy fjlt egyszerre rsra s olvassra is:
fstream dictionary("concordance",ios_base::in|ios_base::out);
21.5.2. Adatfolyamok lezrsa
A fjlokat kzvetlenl az adatfolyam close() tagfggvnynek meghvsval zrhatjuk be:
void f(ostream& mystream)
{
// ...
mystream.close();
}
Ennek ellenre az adatfolyam destruktora is elvgzi ezt a feladatot, gy a close() fggvny
meghvsra akkor van csak szksg, ha a fjlt mr azeltt be kell zrnunk, mieltt az adat-
folyam hatkrbl kikerlnnk.
Ez felveti a krdst, hogy az adott fejlesztkrnyezet hogyan biztosthatja, hogy a cout, cin,
cerr s clog adatfolyamok mr els hasznlatuk eltt ltrejjjenek s csak utols hasznla-
tuk utn zrdjanak le. Termszetesen az <iostream> adatfolyam-knyvtr klnbz vl-
tozatai klnbz mdszereket alkalmazhatnak e cl elrshez. Vgeredmnyben az,
hogy hogyan oldjuk meg ezt a problmt, rszletkrds, amelyet nem kell s nem is szabad
a felhasznl ltal lthatv tenni. Az albbiakban csak egy lehetsges megoldst mutatunk
be, amely elg ltalnos arra, hogy klnbz tpus globlis objektumok
konstruktorainak s destruktorainak lefutsi sorrendjt rgztsk. Egy konkrt megvalsts
ennl hatkonyabb megoldst is knlhat a fordt s az sszeszerkeszt (linker) egyedi le-
hetsgeinek felhasznlsval.
Az alaptlet az, hogy egy olyan segdosztlyt hozunk ltre, amely szmon tartja, hnyszor
ptettk be az <iostream> fejllomnyt egy kln fordtott forrsfjlba:
class ios_base::Init {
static int count;
public:
Init();
~Init();
};
21. Adatfolyamok 859
namespace { // az <iostream> llomnyban, egy msolat minden fjlban,
ios_base::Init __ioinit; // ahov <iostream>-et beptik
}
int ios_base::Init::count = 0; // valamelyik .c llomnyban
Minden fordtsi egysg (9.1) deklarl egy-egy sajt __ioinit nev objektumot. Az __ioinit
objektumok konstruktora az ios_base::Init::count felhasznlsval biztostja, hogy az I/O
knyvtr globlis objektumainak kezdeti rtkadsa csak egyszer trtnjen meg:
ios_base::Init::Init()
{
if (count++ == 0) { /* kezdrtk cout, cerr, cin stb. szmra */ }
}
Ugyangy az __ioinit objektumok destruktora az ios_base::Init::count segtsgvel biztost-
ja az adatfolyamok lezrst:
ios_base::Init::~Init()
{
if (--count == 0) { /* felszmolja cout-ot (flush stb.), illetve a cerr-t, cin-t stb. */ }
}
Ez a mdszer ltalnosan hasznlhat olyan knyvtrak esetben, melyekben globlis objek-
tumoknak kell kezdrtket adni, illetve meg kell azokat semmisteni. Az olyan rendszerek-
ben, ahol a teljes program az elsdleges memriban kap helyet futs kzben, ez a mdszer
nem jelent teljestmnyromlst. Ha azonban nem ez a helyzet, akkor jelents vesztesget
jelenthet, hogy a kezdeti rtkadsok miatt knytelenek vagyunk a trgykdokat (object fj-
lokat) sorban beolvasni az elsdleges memriba. Ha lehetsges, kerljk el a globlis objek-
tumok hasznlatt. Egy olyan osztlyban, ahol minden mvelet jelents, rdemes lehet min-
den fggvnyben elvgezni egy olyan ellenrzst, mint amit az ios_base::Init::count vgez,
ezzel biztosthatjuk a kezdeti rtkadsokat. Ez a megolds azonban az adatfolyamok eset-
ben rendkvl kltsges lenne. Egy olyan fggvny szmra, amely egyetlen karaktert r ki
vagy olvas be, egy ilyen ellenrzs komoly tbbletterhelst jelentene.
21.5.3. Karakterlnc-folyamok
Az adatfolyamokat hozzkthetjk karakterlncokhoz is. Ez azt jelenti, hogy az adatfolya-
mok ltal knlt formzsi lehetsgek felhasznlsval karakterlncokat is rhatunk s ol-
vashatunk. Az ilyen adatfolyamok neve stringstream, lersukat az <sstream> fejllomny
tartalmazza:
A standard knyvtr 860
template <class Ch, class Tr=char_traits<Ch> >
class basic_stringstream : public basic_iostream<Ch,Tr> {
public:
explicit basic_stringstream(ios_base::openmode m = out|in);
explicit basic_stringstream(const basic_string<Ch>& s, openmode m = out|in);
basic_string<Ch> str() const; // a karakterlnc msolatt veszi
void str(const basic_string<Ch>& s); // az rtket s msolatra lltja
basic_stringbuf<Ch,Tr>* rdbuf() const; // mutat az aktulis tmeneti trra
};
A basic_istringstream a basic_stringstream osztlyra hasonlt, azzal a klnbsggel, hogy
a basic_istream osztlybl szrmazik, s alaprtelmezs szerint olvassra nyithatjuk meg.
A basic_ostringstream is a basic_stringstream testvre, csak itt a bzisosztly
a basic_ostream, s alaprtelmezs szerint rsra nyitjuk meg.
Szoks szerint a leggyakrabban hasznlt egyedi cl vltozatokhoz nll tpusnevek
(typedef-ek) tartoznak:
typedef basic_istringstream<char> istringstream;
typedef basic_ostringstream<char> ostringstream;
typedef basic_stringstream<char> stringstream;
typedef basic_istringstream<wchar_t> wistringstream;
typedef basic_ostringstream<wchar_t> wostringstream;
typedef basic_stringstream<wchar_t> wstringstream;
Az ostringstream objektumokat pldul zenet-karakterlncok formzsra hasznlhatjuk:
string compose(int n, const string& cs)
{
extern const char* std_message[ ];
ostringstream ost;
ost << "error(" << n << ") " << std_message[n] << " (user comment: " << cs << ')';
return ost.str();
}
A tlcsordulst ez esetben nem kell ellenriznnk, mert az ost mrete az ignyek szerint n.
Ez a lehetsg klnsen hasznos olyan esetekben, amikor a megkvnt formzsi felada-
tok bonyolultabbak annl, amit az ltalnos, sorokat elllt kimeneti eszkzk kezelni
tudnak.
A karakterlnc-folyamok kezdrtkt ugyangy rtelmezi a rendszer, mint a fjlfolyamok
esetben:
21. Adatfolyamok 861
string compose2(int n, const string& cs) // egyenrtk a compose()-zal
{
extern const char* std_message[ ];
ostringstream ost("hiba(",ios_base::ate); // a kezdeti karakterlnc vgtl kezd rni
ost << n << ") " << std_message[n] << " (felhasznli megjegyzs: " << cs << ')';
return ost.str();
}
Az istringstreamegy olyan bemeneti adatfolyam, amely az adatokat a konstruktorban meg-
adott karakterlncbl olvassa (ugyangy, ahogy az ifilestream a megadott fjlbl olvas):
#include <sstream>
void word_per_line(const string& s) // soronknt egy szt r ki
{
istringstream ist(s);
string w;
while (ist>>w) cout << w << '\n';
}
int main()
{
word_per_line("Ha azt hiszed, a C++ nehz, tanulj angolul");
}
A kezdrtket ad string bemsoldik az istringstream objektumba. A karakterlnc vge
a bemenet vgt is jelenti.
Lehetsg van olyan adatfolyamok meghatrozsra is, melyek kzvetlenl karaktertm-
bkbl olvasnak, illetve oda rnak (21.10[26]). Ez igen hasznos segtsg, fleg, ha rgebbi
programokkal kell foglalkoznunk. Az ezen szolgltatst megvalst ostrstream s
istrstream osztly mr rgebben bekerlt a szabvnyos adatfolyam-knyvtrba.
21.6. Ki- s bemeneti tmeneti trak
A kimeneti adatfolyamok vezrelve, hogy egy tmeneti trba (pufferbe) rjk a karaktere-
ket, s azok egy kis id mlva innen kerlnek t oda, ahova valjban rni akartuk azokat.
Az tmeneti trak osztlynak neve streambuf (21.6.4), melynek defincija a <streambuf>
fejllomnyban szerepel. A klnbz tpus streambuf osztlyok klnbz trolsi md-
A standard knyvtr 862
szereket alkalmaznak. A legltalnosabb megolds az, hogy a streambuf egy tmbben t-
rolja a karaktereket mindaddig, amg tlcsorduls nem kvetkezik be, s csak ilyenkor rja
ki a teljes tmbt a megfelel helyre. Teht egy ostream objektumot az albbi formban
brzolhatunk:
Az ostream-nek s a hozz tartoz streambuf objektumnak ugyanazokat a sablonparam-
tereket kell hasznlnia, s ezek hatrozzk meg a karakterek tmeneti trban hasznlt
karaktertpust.
Az istreamosztlyok nagyon hasonlak, csak ott a karakterek az ellenkez irnyba folynak.
Az tmenetileg nem trolt (unbuffered) I/O olyan egyszer ki- s bemenet, ahol az adatfo-
lyam tmeneti tra azonnal tovbbt minden karaktert, s nem vrakozik addig, amg meg-
felel szm karakter gylik ssze a hatkony tovbbtshoz.
21.6.1. Kimeneti adatfolyamok s tmeneti trolk
Az ostream osztly szmtalan klnbz tpus rtk karakterr talaktst teszi lehetv
az alaprtelmezseknek (21.2.1) s a megadott formzsi utastsoknak (21.4) megfelel-
en. Az ostreamezenkvl nyjt nhny olyan fggvnyt is, melyek kzvetlenl a streambuf
kezelsvel foglalkoznak:
template <class Ch, class Tr = char_traits<Ch> >
class basic_ostream : virtual public basic_ios<Ch,Tr> {
public:
// ...
explicit basic_ostream(basic_streambuf<Ch,Tr>* b);
21. Adatfolyamok 863
tellp()
begin
current
end
valdi cl
ostream:
karakter tmeneti tr
streambuf:
pos_type tellp(); // aktulis pozci lekrse
basic_ostream& seekp(pos_type); // aktulis pozci belltsa
basic_ostream& seekp(off_type, ios_base::seekdir); // aktulis pozci belltsa
basic_ostream& flush(); // tmeneti tr rtse (a tnyleges cl fel)
basic_ostream& operator<<(basic_streambuf<Ch,Tr>* b); // rs b-bl
};
Az ostream konstruktorban megadott strembuf paramter hatrozza meg, hogy a kirt ka-
raktereket hogyan kezeljk s mikor legyenek tnylegesen kirva a meghatrozott eszkz-
re. Pldul egy ostringcstream(21.5.3) vagy egy ofstream(21.5.1) ltrejttekor az ostream
objektumnak a megfelel streambuf (21.6.4) megadsval adunk kezdrtket.
A seekp() fggvnyek azt lltjk be, hogy az ostream milyen pozcira rjon. A p uttag azt
jelzi, hogy ez a pozci az adatfolyamban a karakterek kirsra (put) szolgl. Ezek a fgg-
vnyek csak akkor mkdnek, ha az adatfolyam olyasvalamihez van ktve, amin a pozici-
onls rtelmes mvelet (teht pldul egy fjlhoz). A pos_type tpus egy karakterhelyet b-
rzol a fjlban, mg az off_type az ios_base::seekdir vltoz ltal kijellt helytl val eltrst
jelli:
class ios_base {
// ...
typedef implementation_defined seekdir;
static const seekdir beg, // keress a fjl elejtl
cur, // keress az aktulis pozcitl
end; // keress a fjl vgtl
// ...
};
Az adatfolyamok kezdpozcija a 0, gy a fjlokat nyugodtan kpzelhetjk n karakterbl
ll tmbknek:
int f(ofstream& fout) // fout valamilyen fjlra hivatkozik
{
fout.seekp(10);
fout << '#'; // karakter kirsa s pozci mozgatsa (+1)
fout.seekp(-2,ios_base::cur);
fout << '*';
}
A standard knyvtr 864
A fenti programrszlet egy # karaktert helyez a file[10] pozcira s egy * szimblumot
a file[9] helyre. Az egyszer istream s ostream osztly esetben ilyen kzvetlen hozzf-
rsre nincs lehetsgnk (lsd 21.10[13]). Ha megprblunk a fjl eleje el vagy vge utn
rni, az adatfolyam bad() llapotba kerl (21.3.3).
A flush() mvelet lehetv teszi a programoz szmra, hogy a tlcsorduls megvrsa nl-
kl rtse ki az tmeneti trat.
Lehetsg van arra is, hogy a << opertor segtsgvel egy streambuf objektumot kzvetle-
nl a kimeneti adatfolyamba rjunk. Ez elssorban az I/O rendszerek ksztinek hasznos
segtsg.
21.6.2. Bemeneti adatfolyamok s tmeneti trolk
Az istream elssorban olyan mveleteket knl, melyek segtsgvel karaktereket olvasha-
tunk be s alakthatunk t klnbz tpus rtkekre (21.3.1). Ezenkvl azonban nyjt
nhny olyan szolgltatst is, melyekkel kzvetlenl a streambuf objektumot rhetjk el:
template <class Ch, class Tr = char_traits<Ch> >
class basic_istream : virtual public basic_ios<Ch,Tr> {
public:
// ...
explicit basic_istream(basic_streambuf<Ch,Tr>* b);
pos_type tellg(); // aktulis pozci lekrdezse
basic_istream& seekg(pos_type); // aktulis pozci belltsa
basic_istream& seekg(off_type, ios_base::seekdir); // aktulis pozci belltsa
basic_istream& putback(Ch c); // c visszaraksa az tmeneti trba
basic_istream& unget(); // putback alkalmazsa a legutbb beolvasott
// karakterre
int_type peek(); // a kvetkez beolvasand karakter
int sync(); // tmeneti tr rtse (flush)
basic_istream& operator>>(basic_streambuf<Ch,Tr>* b); // olvass b-be
basic_istream& get(basic_streambuf<Ch,Tr>& b, Ch t = Tr::newline());
streamsize readsome(Ch* p, streamsize n); // legfeljebb n karakter beolvassa
};
21. Adatfolyamok 865
A pozicionl fggvnyek ugyangy mkdnek, mint az ostream osztlybeli megfelelik
(21.6.1). A g uttag azt jelzi, hogy ez a pozci a karakterek beolvassnak (get) helye. A p
s a g uttagokra azrt van szksg, mert kszthetnk olyan iostream osztlyt is, melynek
bzisosztlya az istream s az ostream is, s ilyenkor is meg kell tudnunk klnbztetni az
rsi s az olvassi pozcit.
A putback() fggvnyek azt teszik lehetv, hogy a program visszategye az adatfolyamba
azokat a karaktereket, amelyekre egyelre nincs szksge, de ksbb mg fel szeretnnk
dolgozni. Erre a 21.3.5 pontban mutattunk pldt. Az unget() fggvny a legutoljra beol-
vasott karaktert teszi vissza az adatfolyamba. Sajnos a bemeneti adatfolyamok ilyen vissza-
grgetse nem mindig lehetsges. Pldul ha megprbljuk visszarni az utols eltti beol-
vasott karaktert is, az ios_base::failbit hibajelz bekapcsoldik. Csak abban lehetnk
biztosak, hogy egyetlen, sikeresen beolvasott karaktert vissza tudunk rni. A peek() beolvas-
sa a kvetkez karaktert, de azt az tmeneti trban hagyja, gy jra beolvashatjuk. Teht
a c=peek() utasts egyenrtk a (c=get(),unget(),c) s a (putback(c=get()),c) parancs-so-
rozatokkal. Figyeljnk r, hogy a failbit bekapcsolsa kivtelt vlthat ki (21.3.6).
Az istream-ek tmeneti trnak azonnali kirtst (flushing) a sync() paranccsal knysze-
rthetjk ki, de ezt a mveletet sem mindig hasznlhatjuk biztonsgosan. Bizonyos tpus
adatfolyamokban ehhez jra kellene olvasnunk a karaktereket az eredeti forrsbl, ami
nem mindig lehetsges vagy hibs bemenetet eredmnyezhet. Ezrt a sync() 0 rtket ad
vissza, ha sikeresen lefutott, s 1-t, ha nem. Ha a mvelet sikertelen volt, erre az
ios_base::badbit (21.3.3) is felhvja a figyelmnket. A badbit rtknek megvltozsa is ki-
vtelt vlthat ki (21.3.6). Ha a sync() fggvnyt olyan tmeneti trra hasznljuk, amely egy
ostream objektumhoz kapcsoldik, akkor az tmeneti tr tartalma a kimenetre kerl.
A >> s a get() mveletnek is van olyan vltozata, mely kzvetlenl az tmeneti trba r, de
ezek is elssorban az I/O szolgltatsok ksztinek jelentenek hasznos segtsget, hiszen
az adatfolyamok tmeneti trainak kzvetlen elrsre csak nekik van szksgk.
A readsome() fggvny egy alacsonyszint mvelet, mellyel a programoz megllapthat-
ja, hogy van-e az adatfolyamban beolvassra vr karakter. Ez a szolgltats akkor nagyon
hasznos, ha nem akarunk a bemenet megrkezsre vrni (pldul a billentyzetrl). Lsd
mg: in_avail() (21.6.4).
21.6.3. Az adatfolyamok s az tmeneti trak kapcsolata
Az adatfolyam s a hozz tartoz tmeneti tr kztti kapcsolatot az adatfolyamok basic_ios
osztlya tartja fenn:
A standard knyvtr 866
template <class Ch, class Tr = char_traits<Ch> >
class basic_ios : public ios_base {
public:
// ...
basic_streambuf<Ch,Tr>* rdbuf() const; // mutat az tmeneti trra
// az tmeneti tr belltsa, clear(), s mutat visszaadsa a rgi trra
basic_streambuf<Ch,Tr>* rdbuf(basic_streambuf<Ch,Tr>* b);
locale imbue(const locale& loc); // helyi sajtossgok belltsa (s
// a rgi rtk kiolvassa)
char narrow(char_type c, char d) const; // char rtk char_type tpus c-bl
char_type widen(char c) const; // char_type rtk a c char-bl
// ...
protected:
basic_ios();
void init(basic_streambuf<Ch,Tr>* b); // a kezdeti tmeneti tr belltsa
};
Azon kvl, hogy lekrdezhetjk s bellthatjuk az adatfolyam streambuf objektumt
(21.6.4), a basic_ios osztlyban szerepel az imbue() fggvny is, mellyel beolvashatjuk s
tllthatjuk az adatfolyam helyi sajtossgokat ler locale objektumt (21.7). Az imbue()
fggvnyt az ios_base (21.7.1) objektumra, a pubimbue() fggvnyt az tmeneti trra
(21.6.4) kell meghvnunk.
A narrow() s a widen() fggvny segtsgvel az tmeneti tr karaktertpust konvertl-
hatjuk char tpusra, vagy fordtva. A narrow(c,d) fggvnyhvs msodik paramtere az
a char, amelyet akkor szeretnnk visszakapni, ha a c-ben megadott char_type rtknek
nincs char megfelelje.
21.6.4. Adatfolyamok tmeneti trolsa
Az I/O mveletek meghatrozsa fjltpus emltse nlkl trtnt, de nem kezelhetnk min-
den eszkzt ugyangy az tmeneti trolsnl. Egy karakterlnchoz kttt ostream objek-
tumnak (21.5.3) pldul msfle trolsra van szksge, mint egy fjlhoz kttt (21.5.1)
kimeneti adatfolyamnak. Ezeket a problmkat gy oldhatjuk meg, hogy a klnbz adat-
folyamokhoz a kezdeti rtkadskor klnbz tpus tmeneti trakat rendelnk. Mivel
a klnbz tpus trak ugyanazokat a mveleteket biztostjk, az ostream osztlynak
nem kell klnbznek lennie hozzjuk. Minden tmeneti tr a streambuf osztlybl szr-
21. Adatfolyamok 867
mazik. A streambuf virtulis fggvnyeket nyjt azokhoz a mveletekhez, melyek a kln-
bz trolsi mdszereknl eltrek lehetnek. Ilyenek pldul a tlcsordulst s az alul-
csordulst kezel eljrsok.
A basic_streambuf osztly kt felletet tesz elrhetv. A nyilvnos (public) fellet elssor-
ban azoknak hasznos, akik olyan adatfolyam-osztlyokat akarnak elkszteni, mint az
istream, az ostream, az fstream vagy a stringstream. A vdett (protected) fellet clja azon
programozk ignyeinek kiszolglsa, akik j trolsi mdszert akarnak bevezetni, vagy j
bemeneti forrsokat s kimeneti clokat akarnak kezelni.
A streambuf osztly megrtshez rdemes elszr megismerkednnk az alapjt kpez t-
meneti trterlet modellel, amelyet a vdett fellet biztost. Kpzeljk el, hogy a streambuf
objektumnak van egy kimeneti terlete, amelybe a << opertor segtsgvel rhatunk, s
van egy bemeneti terlete, amelybl a >> opertor olvas. Mindkt terletet hrom mutat r
le: egy a kezdpontra, egy az aktulis pozcira, egy pedig az utols utni elemre mutat.
Ezeket a mutatkat fggvnyeken keresztl rhetjk el:
template <class Ch, class Tr = char_traits<Ch> >
class basic_streambuf {
protected:
Ch* eback() const; // a bemeneti tr kezdete
Ch* gptr() const; // kvetkez karakter (a kvetkez olvass innen trtnik)
Ch* egptr() const; // a bemeneti tr utols eleme utnra mutat
void gbump(int n); // n hozzadsa gptr()-hez
void setg(Ch* begin, Ch* next, Ch* end); // eback(), gptr(), s egptr() belltsa
Ch* pbase() const; // a kimeneti tr kezdete
Ch* pptr() const; // a kvetkez res karakter (a kvetkez kirs ide trtnik)
Ch* epptr() const; // a kimeneti tr utols eleme utnra mutat
void pbump(int n); // n hozzadsa pptr()-hez
void setp(Ch* begin, Ch* end); // pbase() s pptr() begin-re, epptr() end-re lltsa
// ...
};
Egy karaktertmbre a setg() s a setp() fggvny segtsgvel megfelelen bellthatjuk
a mutatkat. A programoz a bemeneti terletet a kvetkez formban rheti el:
template <class Ch, class Tr = char_traits<Ch> >
basic_streambuf<Ch,Tr>::int_type basic_streambuf<Ch,Tr>::snextc()
// az aktulis karakter tlpse, a kvetkez beolvassa
{
A standard knyvtr 868
if (1<egptr()-gptr()) { // ha legalbb kt karakter van az tmeneti trban
gbump(1); // az aktulis karakter tlpse
return Tr::to_int_type(*gptr()); // az j aktulis karakter visszaadsa
}
if ( 1==egptr()-gptr()) { // ha pontosan egy karakter van az tmeneti trban
gbump(1); // az aktulis karakter tlpse
return underflow();
}
// az tmeneti tr res (vagy nem hasznlt), prbljuk feltlteni
if (Tr::eq_int_type(uflow(), Tr::eof()) return Tr::eof();
if (0<eptr()-gptr()) return Tr::to_int_type(*gptr()); // az j aktulis karakter
visszaadsa
return underflow;
}
Az tmeneti trat a gptr()-en keresztl rjk el, az egptr() a bemeneti terlet hatrt jelzi.
A karaktereket a valdi forrsbl az uflow() s az underflow() olvassk ki.
A traits_type::to_int_type() meghvsa biztostja, hogy a kd fggetlen lesz az ppen hasz-
nlt karaktertpustl. A kd tbbfle tpus adatfolyam-trral hasznlhat s figyelembe ve-
szi azt is, hogy az uflow() s az underflow() virtulis fggvnyek (a setg() segtsgvel) j
bemeneti terletet is meghatrozhatnak.
A streambuf nyilvnos fellete a kvetkez:
template <class Ch, class Tr = char_traits<Ch> >
class basic_streambuf {
public:
// szoksos tpus-meghatrozsok (21.2.1)
virtual ~basic_streambuf();
locale pubimbue(const locale &loc); // locale belltsa (s rgi kiolvassa)
locale getloc() const; // locale kiolvassa
basic_streambuf* pubsetbuf(Ch* p, streamsize n); // tmeneti trterlet belltsa
pos_type pubseekoff(off_type off, ios_base::seekdir way, // pozci (21.6.1)
ios_base::openmode m = ios_base::in|ios_base::out);
pos_type pubseekpos(pos_type p, ios_base::openmode m = ios_base::in|ios_base::out);
int pubsync(); // sync(), lsd 21.6.2
int_type snextc(); // aktulis karakter tlpse, a kvetkez kiolvassa
int_type sbumpc(); // gptr() lptetse 1-el
int_type sgetc(); // az aktulis karakter beolvassa
21. Adatfolyamok 869
streamsize sgetn(Ch* p, streamsize n); // beolvass p[0]..p[n-1]-be
int_type sputbackc(Ch c); // c visszahelyezse az tmeneti trba (21.6.2)
int_type sungetc(); // az utols karakter visszahelyezse
int_type sputc(Ch c); // c kirsa
streamsize sputn(const Ch* p, streamsize n); // p[0]..p[n-1] kirsa
streamsize in_avail(); // bemenet rendben?
// ...
};
A nyilvnos fellet olyan fggvnyeket tartalmaz, melyekkel karaktereket helyezhetnk az
tmeneti trba, illetve karaktereket vehetnk ki onnan. Ezek a fggvnyek ltalban na-
gyon egyszerek s helyben kifejtve (inline) is fordthatk, ami a hatkonysg szempontj-
bl kulcsfontossg.
Azok a fggvnyek, melyek tevkenysge fgg a hasznlt trolsi mdtl, a vdett fellet
megfelel eljrst hvjk meg. A pubsetbuf() pldul a setbuf() fggvnyt hvja meg, ame-
lyet a leszrmazott osztly fellr az tmenetileg trolt karakterek szmra val memriafog-
lalshoz. Teht az olyan mveletek megvalstsra, mint a setbuf(), kt fggvny szolgl,
ami azrt praktikus, mert gy egy iostream is vgezhet rendfenntart mveleteket, mikz-
ben a felhasznl fggvnyt meghvja. A virtulis fggvny meghvst egy try blokkba
helyezhetjk, s elkaphatjuk a felhasznli kd ltal kivltott kivteleket is.
Alaprtelmezs szerint a setbuf(0,0) az tmeneti trols hinyt jelenti, mg a setbuf(p,n)
a p[0],, p[n-1] tartomny hasznlatt rja el a karakterek tmeneti trolsra.
Az in_avail() fggvny meghvsval azt llapthatjuk meg, hny karakter ll rendelkez-
snkre az tmeneti trban. Ennek vizsglatval elkerlhetjk a bemenetre val vrakozst.
Amikor olyan adatfolyambl olvasunk, amely a billentyzethez kapcsoldik, a cin.get(c)
akr addig vrakozik, amg a felhasznl vissza nem tr az ebdjrl. Egyes rendszerekben
s alkalmazsokban rdemes felkszlnnk erre a lehetsgre:
if (cin.rdbuf()->in_avail()) { // get() nem fog lefagyni
cin.get(c);
// csinlunk valamit
}
else { // get() lefagyhat
// valami mst csinlunk
}
A standard knyvtr 870
Vigyzzunk e lehetsg hasznlatakor, mert nha nem knny annak megllaptsa, hogy
van-e beolvashat bemenet. Az in_avail() adott vltozata esetleg 0 rtket ad vissza, ha egy
bemeneti mveletet sikeresen vgrehajthatunk.
A nyilvnos fellet mellett amelyet a basic_istream s a basic_ostream hasznl
a basic_streambuf egy vdett felletet is knl, mellyel az adatfolyamok tmeneti trainak
elksztst segti, s azokat a virtulis fggvnyeket vezeti be, amelyek meghatrozzk az
tmeneti trols irnyelveit:
template <class Ch, class Tr = char_traits<Ch> >
class basic_streambuf {
protected:
// ...
basic_streambuf();
virtual void imbue(const locale &loc); // locale belltsa
virtual basic_streambuf* setbuf(Ch* p, streamsize n);
virtual pos_type seekoff(off_type off, ios_base::seekdir way,
ios_base::openmode m = ios_base::in|ios_base::out);
virtual pos_type seekpos(pos_type p,
ios_base::openmode m = ios_base::in|ios_base::out);
virtual int sync(); // sync(), lsd 21.6.2
virtual int showmanyc();
virtual streamsize xsgetn(Ch* p, streamsize n); // n karakter beolvassa
virtual int_type underflow(); // az olvassi terlet res, eof vagy
// karakter visszaadsa
virtual int_type uflow(); //az olvassi terlet res, eof vagy
// karakter visszaadsa, gptr() nvelse
virtual int_type pbackfail(int_type c = Tr::eof()); // a putback nem jrt sikerrel
virtual streamsize xsputn(const Ch* p, streamsize n); // n karakter kirsa
virtual int_type overflow(int_type c = Tr::eof()); // kir terlet megtelt
};
Az underflow() s az uflow() fggvny szolgl arra, hogy a kvetkez karaktereket beolvas-
suk a tnyleges bemeneti forrsrl, ha az tmeneti tr res. Ha az adott forrson nincs beol-
vashat bemenet, az adatfolyam eof llapotba (21.3.3) kerl. Ha ez nem vlt ki kivtelt,
a traits_type::eof() rtket kapjuk vissza. A gptr()-t a visszaadott karakter utn az uflow() n-
21. Adatfolyamok 871
veli, az underflow() viszont nem. A rendszerben ltalban nem csak azok az tmeneti trak
vannak, amelyeket az iostream knyvtr hoz ltre, gy akkor is elfordulhatnak tmeneti t-
rols miatti kslekedsek, amikor tmenetileg nem trolt adatfolyamot hasznlunk.
Az overflow() fggvny akkor fut le, amikor az tmeneti tr megtelik, s ez tovbbtja a ka-
raktereket a kimenet valdi clllomsra. Az overflow(c) utasts az tmeneti tr tartalmt
s a c karaktert is kirja. Ha a clllomsra nem lehet tbb karaktert rni, az adatfolyam eof
llapotba kerl (21.3.3). Ha ez nem okoz kivtelt, a traits_type::eof() rtket kapjuk.
A showmanyc() show how many characters, mondd meg, hny karakter egy igen r-
dekes fggvny. Clja az, hogy a felhasznl lekrdezhesse, hny karaktert tudunk gyor-
san beolvasni, azaz az opercis rendszer tmeneti trainak kirtsvel, de a lemezrl va-
l olvass megvrsa nlkl. A showmanyc() fggvny 1 rtket ad vissza, ha nem tudja
garantlni, hogy akr egy karaktert is be tudnnk olvasni a fjlvge jel megrkezse eltt.
Ez az eljrs (szksgszeren) elg alacsony szint, s nagymrtkben fgg az adott meg-
valststl. Soha ne hasznljuk a showmanyc() fggvnyt fejlesztrendszernk dokumen-
tcijnak alapos tanulmnyozsa s egy kis ksrletezs nlkl.
Alaprtelmezs szerint minden adatfolyam a globlis helyi sajtossgoknak (global local)
megfelelen (21.7) mkdik. A pubimbue(loc) vagy az imbue(loc) fggvny meghvs-
val az adatfolyamot a loc objektumban megfogalmazott helyi sajtossgok hasznlatra uta-
sthatjuk.
Az adott adatfolyamhoz hasznlt streambuf osztlyt a basic_streambuf osztlybl kell szr-
maztatnunk. Ebben kell szerepelnie azoknak a konstruktoroknak s kezdrtk-ad fggv-
nyeknek, melyek a streambuf objektumot a karakterek valdi forrshoz vagy cljhoz ktik,
s ez rja fell a virtulis fggvnyeket az tmeneti trols mdjnak megvalstshoz:
template <class Ch, class Tr = char_traits<Ch> >
class basic_filebuf : public basic_streambuf<Ch,Tr> {
public:
basic_filebuf();
virtual ~basic_filebuf();
bool is_open() const;
basic_filebuf* open(const char* p, ios_base::openmode mode);
basic_filebuf* close();
protected:
virtual int showmanyc();
virtual int_type underflow();
virtual int_type uflow();
virtual int_type pbackfail(int_type c = Tr::eof());
A standard knyvtr 872
virtual int_type overflow(int_type c = Tr::eof());
virtual basic_streambuf<Ch,Tr>* setbuf(Ch* p, streamsize n);
virtual pos_type seekoff(off_type off, ios_base::seekdir way,
ios_base::openmode m = ios_base::in|ios_base::out);
virtual pos_type seekpos(pos_type p,
ios_base::openmode m = ios_base::in|ios_base::out);
virtual int sync();
virtual void imbue(const locale& loc);
};
Az tmeneti trak kezelsre hasznlt fggvnyek vltozatlan formban a basic_streambuf
osztlybl szrmaznak. Csak a kezdeti rtkadsra s a trolsi mdszerre hat fggvnye-
ket kell kln megadnunk.
Szoks szerint a leggyakoribb osztlyokat s szles megfeleliket kln typedef-ek teszik
knnyen elrhetv:
typedef basic_streambuf<char> streambuf;
typedef basic_stringbuf<char> stringbuf;
typedef basic_filebuf<char> filebuf;
typedef basic_streambuf<wchar_t> wstreambuf;
typedef basic_stringbuf<wchar_t> wstringbuf;
typedef basic_filebuf<wchar_t> wfilebuf;
21.7. Helyi sajtossgok
A locale egy olyan objektum, amely a karakterek betk, szmok stb. szerinti osztlyozs-
nak mdjt adja meg, illetve a karakterlncok rendezsi sorrendjt s a szmok megjelen-
si formjt kirskor s beolvasskor. Az iostream knyvtr ltalban automatikusan hasz-
nl egy ltalnos locale objektumot, amely biztostja nhny nyelv s kultra szoksainak
betartst. Ha ez megfelel cljainknak, nem is kell foglalkoznunk locale objektumokkal. Ha
azonban ms szoksokat akarunk kvetni, akkor az adatfolyam viselkedst gy vltoztat-
hatjuk meg, hogy lecserljk a hozz kttt locale objektumot.
21. Adatfolyamok 873
Az std nvtrhez tartoz locale osztlyt a <locale> fejllomny rja le:
class locale { //a teljes deklarcit lsd D.2
public:
// ...
locale() throw(); // az aktulis globlis locale msolata
explicit locale(const char* name); // locale ltrehozsa C stlus locale nv alapjn
basic_string<char> name() const; // a hasznlt locale neve
locale(const locale&) throw(); // locale msolsa
const locale& operator=(const locale& ) throw(); // locale msolsa
static locale global(const locale&); // a globlis locale belltsa
// (az elz rtk kiolvassa)
static const locale& classic(); // a C ltal meghatrozott locale
};
A helyi sajtossgok legkznsgesebb hasznlata, amikor egy locale objektumot egy m-
sikra kell cserlnnk:
void f()
{
std::locale loc("POSIX"); // szabvnyos POSIX locale
cin.imbue(loc); // cin hasznlja loc-ot
// ...
cin.imbue(std::locale()); // cin visszalltsa az alaprtelmezett (globlis)
// locale hasznlatra
}
Az imbue() fggvny a basic_ios (21.7.1) osztly tagja.
Lthatjuk, hogy nhny, viszonylag szabvnyos locale objektum sajt karakterlnc-neveket
hasznl. Ezeket az elnevezseket a C nyelv is hasznlja.
Elrhetjk azt is, hogy a C++ minden jonnan ksztett adatfolyamhoz automatikusan az l-
talunk megadott locale objektumot hasznlja:
void g(const locale& loc = locale()) // alaprtelmezs szerint az aktulis
// globlis locale hasznlata
{
locale old_global = locale::global(loc); // legyen loc az alaprtelmezs
// ...
}
A standard knyvtr 874
A globlis locale objektum tlltsa nincs hatssal a mr ltez adatfolyamokra, mert azok
tovbbra is a globlis locale rgi rtkt hasznljk. Ez vonatkozik pldul a cin, cout stb.
adatfolyamra is. Ha ezeket is meg akarjuk vltoztatni, kzvetlenl az imbue() fggvnyt kell
hasznlnunk.
Azzal, hogy egy adatfolyamhoz j locale objektumot rendelnk, tbb helyen is megvltoz-
tatjuk arculatt, viselkedst. A locale osztly tagjait kzvetlenl is hasznlhatjuk, j helyi sa-
jtossgok meghatrozsra vagy a rgiek bvtsre. A locale arra is hasznlhat, hogy be-
lltsuk a pnzegysgek, dtumok stb. megjelensi formjt ki- s bemenetkor (21.10[25]),
vagy a klnbz kdkszletek kztti talaktst. A helyi sajtossgok hasznlatnak elvt,
illetve a locale s facet (arculat, viselkeds) osztlyokat a D fggelk rja le. A C stlus
locale meghatrozsa a <clocale> s a <locale.h> fejllomnyokban tallhat.
21.7.1. Adatfolyam-visszahvsok
Nha a programozk az adatfolyamok llapotlerst bvteni akarjk. Pldul elvrhatjuk
egy adatfolyamtl, hogy tudja, milyen formban kell egy komplex szmot megjelenteni:
polr- vagy Descartes-koordintarendszerben. Az ios_base osztlyban szerepel az xalloc()
fggvny, mellyel az ilyen egyszer llapotinformcik szmra foglalhatunk memrit.
Az xalloc() ltal visszaadott rtk azt a kt memriaterletet adja meg, amelyet az iword()
s a pword() fggvnnyel elrhetnk:
class ios_base {
public:
// ...
~ios_base();
locale imbue(const locale& loc); // locale kiolvassa s belltsa, lsd D.2.3
locale getloc() const; // locale kiolvassa
static int xalloc(); // egy egsz s egy mutat lefoglalsa (mindkett 0
// kezdrtkkel)
long& iword(int i); // az iword(i) egsz elrse
void*& pword(int i); // a pword(i) mutat elrse
// visszahvsok
enum event { erase_event, imbue_event, copyfmt_event }; // esemnytpusok
typedef void (*event_callback)(event, ios_base&, int i);
void register_callback(event_callback f, int i); // f hozzrendelse word(i)-hez
};
21. Adatfolyamok 875
A felhasznlknak s a knyvtrak ksztinek nha szksgk van arra, hogy rtestst
kapjanak az adatfolyam llapotnak megvltozsrl. A register_callback() eljrs segts-
gvel a fggvnyeket bejegyezhetjk, s azok akkor futnak le, amikor a nekik kijellt
esemny bekvetkezik. Teht pldul az imbue(), a copyfmt() vagy a ~ios_base() fgg-
vny meghvsa maga utn vonhatja egy bejegyzett fggvny lefutst, amely sorrendben
az imbue_event, a copyfmt_event, illetve az erase_event esemnyt figyeli. Amikor az llapot
megvltozik, a bejegyzett fggvny a register_callback() fggvnyben megadott i param-
tert kapja meg.
Ez a trolsi s visszahvsi eljrs meglehetsen bonyolult, teht csak akkor hasznljuk, ha
felttlenl szksgnk van az alacsonyszint formzsi szolgltatsok kibvtsre.
21.8. C stlus ki- s bemenet
Mivel a C s a C++ kdokat gyakran keverik, a C++ adatfolyamon alapul ki- s bemeneti
eljrsait sokszor egytt hasznljuk a C nyelv printf() fggvnycsaldjt hasznl I/O rend-
szerrel. A C stlus I/O fggvnyek a <cstdio> s az <stdio.h> fejllomnyban tallhatk.
Mivel a C fggvnyek szabadon meghvhatk a C++ programokbl, sok programoz szve-
sebben hasznlja a megszokott C ki- s bemeneti szolgltatsokat, de mg ha az adatfolya-
mokon alapul I/O eljrsokat rszestjk is elnyben, nha akkor is tallkozni fogunk C
stlus megoldsokkal.
A C s a C++ stlus be- s kimenet karakterszinten keverhet. Ha a sync_with_stdio() fgg-
vnyt meghvjuk a legels adatfolyam alap I/O mvelet eltt, akkor a C s a C++ stlus
eljrsok ugyanazokat az tmeneti trakat fogjk hasznlni. Ha az els adatfolyam mvelet
eltt a sync_with_stdio(false) parancsot adjuk ki, az tmeneti trakat a rendszer biztosan
nem fogja megosztani, gy egyes esetekben nvelhetjk a teljestmnyt:
class ios_base {
// ...
static bool sync_with_stdio(bool sync = true); // kiolvass s bellts
};
Az adatfolyam elv kimeneti fggvnyek legfbb elnye a C standard knyvtrnak
printf() fggvnyvel szemben, hogy az adatfolyam-fggvnyek tpusbiztosak s egysges
stlust biztostanak a klnbz objektumok megadsra, legyen azok tpusa akr beptett,
akr felhasznli.
A standard knyvtr 876
A C ltalnos kimeneti fggvnyei formzott kimenetet lltanak el, melyben a megadott
paramterek sorozatnak megjelenst a format formzsi karakterlnc hatrozza meg:
int printf(const char* format ...); // rs az stdout-ra
int fprintf(FILE*, const char* format ...); // rs "file"-ba (stdout, stderr)
int sprintf(char* p, const char* format ...); // rs p[0] ... stb.-re
A formzsi karakterlnc ktfajta elemet tartalmazhat: sima karaktereket, amelyeket a rend-
szer egyszeren a kimeneti adatfolyamba msol, illetve talakts-meghatrozsokat
(conversion-specification), melyek mindegyike egy paramter talaktst s kirst vezrli.
Az talakts-meghatrozsokat a % karakterrel kell kezdeni:
printf("Jelen volt %d szemly.",no_of_members);
Itt a %d azt hatrozza meg, hogy a no_of_members vltozt int rtkknt kell kezelni s
a megfelel decimlis szmjegyek kirsval kell megjelenteni.
Ha a no_of_members==127, akkor a megjelen eredmny a kvetkez lesz:
Jelen volt 127 szemly.
Az talakts-meghatrozsokat igen sokflekppen megadhatjuk s nagyfok rugalmass-
got biztostanak. A % karaktert kveten az albbi jelek llhatnak:
- A nem ktelez mnuszjel azt rja el, hogy a rendszer a megadott mezben az t-
alaktott rtket balra igaztsa.
+ A nem ktelez pluszjel hasznlata azt eredmnyezi, hogy az eljeles tpus rt-
kek mindig + vagy - eljellel fognak megjelenni.
0 A nem ktelez nulla jelentse: a mezszlessg feltltsre numerikus rtkek
esetn 0 karakterekkel trtnik. Ha a - vagy a pontossg megadott, akkor a 0
elrst figyelmen kvl hagyjuk.
# A szintn nem ktelez # azt jelenti, hogy a lebegpontos rtkek mindenkppen
tizedesponttal egytt jelennek meg, fggetlenl attl, hogy utna esetleg csak 0
szmjegyek llnak; hogy a lezr nullk is megjelennek; illetve hogy az oktlis sz-
mok eltt egy 0 karakter, hexadecimlis szmok eltt pedig a 0x vagy a 0X
karakterpr jelenik meg.
d A nem ktelez szmjegysorozat a mez szlessgt hatrozza meg. Ha az talak-
tott rtk kevesebb karaktert tartalmaz, mint a mez szlessge, akkor annak bal
oldaln (illetve balra igazts esetn a jobb oldaln) res karakterek jelennek meg
a megfelel szlessg elrse rdekben. Ha a mezszlessg megadst 0 karak-
terrel kezdjk, a szlessgnvel karakterek szkz helyett nullk lesznek.
21. Adatfolyamok 877
. A nem ktelez pont kirsval vlaszthatjuk el a mezszlessget megad rtket
a kvetkez szmjegy-sorozattl.
d jabb, nem ktelez szmjegy-sorozat. A pontossgot hatrozza meg, azaz e s f-
talakts esetn a tizedes pont utn megjelen szmjegyek szmt, karakterlncok
esetben pedig a megjelentend karakterek legnagyobb szmt.
* A mezszlessg vagy a pontossg konkrt megadsa helyett hasznlhatjuk a * ka-
raktert, melynek hatsra a kvetkez paramterben szerepl egsz rtk adja meg
a kvnt rtket.
h A nem ktelez h karakter azt jelzi, hogy a kvetkez d, o, x vagy u egy short int
paramterre vonatkozik.
l A nem ktelez l karakter azt jelzi, hogy a kvetkez d, o, x vagy u egy long int
paramterre vonatkozik.
% Azt jelzi, hogy a % karakter kirand. Paramtert nem hasznl fel.
c Az talakts tpust jelz karakter. Az talakt karakterek s jelentseik
a kvetkezk:
d Egsz rtk, amit tzes szmrendszerben kell megjelenteni.
i Egsz rtk, amit tzes szmrendszerben kell megjelenteni.
o Egsz rtk, amit nyolcas szmrendszerben kell megjelenteni.
x Egsz rtk, amit tizenhatos szmrendszerben kell megjelenteni, 0x kezdettel
X Egsz rtk, amit tizenhatos szmrendszerben kell megjelenteni, 0X kezdettel.
f Egy float vagy egy double paramtert kell tzes szmrendszerbeli rtkk
alaktani a [-]ddd.ddd formtummal. A tizedespont utn ll szmjegyek sz-
mt a megadott paramter pontossga hatrozza meg. Ha szksg van r, az
rtket a rendszer kerekti. Ha pontossg nincs megadva, 6 szmjegy jelenik
meg; ha pontossgknt 0 rtket adunk meg s a # jelet nem hasznljuk,
a tizedesjegyek nem rdnak ki.
e Egy float vagy double paramtert alakt tzes szmrendszerbeli alakjra a tu-
domnyos [-]d.ddde+dd vagy a [-]d.ddde-dd alakra, ahol a tizedespont eltt
pontosan egy jegy szerepel, mg a tizedespont utn ll szmjegyek szmt
az adott paramter pontossg-meghatrozsa adja meg. Ha szksges, az r-
tket a rendszer kerekti. Ha pontossg nincs megadva, az alaprtelmezett
rtk 6 lesz; ha a pontossg nulla s a # jelet nem hasznltuk, sem a tizedes-
pont, sem az utna ll szmjegyek nem jelennek meg.
E Nagyon hasonlt az e-re, de a kitev jellsre ez a forma nagy E bett hasznl.
g A float vagy double tpus paramtert d, f, vagy e stlusban rja ki, aszerint,
hogy melyik biztostja a legnagyobb pontossgot a lehet legkisebb terleten.
G Ugyanaz, mint a g, de a kitev jellsre a nagy E bett hasznlja.
c Karakter paramtert jelent meg. A nullkaraktereket figyelmen kvl hagyja.
s Az gy tvett paramter egy karakterlnc (karakterre hivatkoz mutat).
A standard knyvtr 878
A karaktereket addig msolja a kimenetre, amg a nullkaraktert, vagy a pon-
tossg-meghatrozsban meghatrozott karakterszmot el nem ri. Ha
a pontossg 0 vagy nincs megadva, akkor csak a nullkarakter jelenti a karak-
terlnc kirsnak vgt.
p A paramter egy mutat. A megjelentshez hasznlt formtum a megvals-
tstl fgg.
u Eljel nlkli egsz paramtert alakt tzes szmrendszerbeli alakra.
n A printf(), az fprintf() vagy az sprintf() meghvsval eddig kirt karakterek
szmt a mutatott int-be rja.
Az nem fordulhat el, hogy nulla vagy kicsi mezszlessg miatt csonkols trtnjen, mert
a rendszer csak akkor foglalkozik a szlessg kezelsvel, ha a mez szlessge meghalad-
ja a benne szerepl rtk szlessgt.
me egy kicsivel bonyolultabb plda:
char* line_format = "#sor szma %d \"%s\"\n";
int line = 13;
char* file_name = "C++/main.c";
printf("int a;\n");
printf(line_format,line,file_name);
printf("int b;\n");
Az eredmny a kvetkez lesz:
int a;
#sor szma 13 "C++/main.c"
int b;
A printf() fggvny hasznlata nem biztonsgos, mert a paramterek beolvassakor nem
trtnik tpusellenrzs. Az albbi hiba pldul ltalban megjsolhatatlan kimenetet ered-
mnyez vagy mg nagyobb hibt:
char x;
// ...
printf("rossz bemeneti karakter: %s",x); // %s helyett %c kell
A printf() ennek ellenre nagyfok rugalmassgot biztost, olyan formban, amelyet a C
programozk mr jl ismernek.
21. Adatfolyamok 879
A getchar() fggvny hasonlan jl ismert mdszert ad karakterek beolvassra:
int i;
while ((i=getchar())!=EOF) { /* i hasznlata */ }
Figyeljnk r, hogy a fjlvge jelet csak akkor tudjuk felismerni az int tpus EOF konstans
segtsgvel, ha a getchar() ltal visszaadott rtket is int tpus vltozban troljuk s nem
char tpus adatknt.
A C bemeneti/kimeneti rendszernek alaposabb megismershez olvassunk el egy C kzi-
knyvet vagy Kernighan s Ritchie A C programozsi nyelv cm knyvt (Mszaki Knyv-
kiad, 1994) [Kernighan, 1988].
21.9. Tancsok
[1] Az olyan felhasznli tpusokhoz, melyekhez rtelmes szveges forma rendelhet,
rdemes megadnunk a >> s a << opertort. 21.2.3, 21.3.5.
[2] Ha alacsony precedencij opertorokat tartalmaz kifejezsek eredmnyt akarjuk
megjelenteni, zrjeleket kell hasznlnunk. 21.2.
[3] j >> s << opertorok ltrehozshoz nem kell mdostanunk az istream s az
ostream osztlyt. 21.2.3.
[4] Olyan fggvnyeket is kszthetnk, amelyek a msodik (vagy az utni) paramter
alapjn virtulisknt mkdnek. 21.2.3.1.
[5] Ne feledjk, hogy a >> alaprtelmezs szerint tugorja az reshely karaktereket.
21.3.2.
[6] Alacsonyszint bemeneti fggvnyeket (pldul get() s read()) ltalban csak ma-
gasabb szint eljrsok megvalstshoz kell hasznlnunk. 21.3.4.
[7] Mindig krltekinten fogalmazzuk meg a befejezsi felttelt, ha a get(), a getline()
vagy a read() fggvnyt hasznljuk. 21.3.4.
[8] Az llapotjelz bitek kzvetlen tlltsa helyett hasznljunk mdostkat
(manipulator) az I/O vezrlshez. 21.3.3, 21.4, 21.4.6.
[9] Kivteleket csak a ritkn elfordul I/O hibk kezelsre hasznljunk. 21.3.6.
[10] A lekttt adatfolyamok az interaktv (felhasznli kzremkdst vr) ki- s
bemenethez nyjtanak segtsget. 21.3.7.
[11] Ha tbb fggvny be- s kilpsi mveleteit egy helyen akarjuk megfogalmazni,
hasznljunk rszemeket. 21.3.8.
A standard knyvtr 880
[12] Paramter nlkli mdost utn ne tegynk zrjeleket. 21.4.6.2.
[13] Ha szabvnyos mdostkat hasznlunk, ne felejtsk ki programunkbl az
#include <iomanip> sort. 21.4.6.2.
[14] Egy egyszer fggvnyobjektum ltrehozsval akr egy hromparamter oper-
tor hatst (s hatkonysgt) is megvalsthatjuk. 21.4.6.3.
[15] A width meghatrozsok csak a kvetkez I/O mveletre vonatkoznak. 21.4.4.
[16] A precision belltsai minden ksbbi lebegpontos kimeneti mveletre hatssal
vannak. 21.4.3.
[17] A memriban val formzshoz hasznljunk karakterlnc-folyamokat. 21.5.3.
[18] A fjlfolyamok kezelsi mdjt meghatrozhatjuk. 21.5.1.
[19] Az I/O rendszer bvtsekor klntsk el a formzst (iostream) s az tmeneti t-
rolst (streambuf). 21.1, 21.6.
[20] Az rtkek nem szabvnyos tovbbtst tmeneti trral oldjuk meg. 21.6.4.
[21] Az rtkek nem szabvnyos formzst adatfolyam-mveletekkel oldjuk meg.
21.2.3, 21.3.5.
[22] A felhasznli kdrszleteket elklnthetjk s nll egysgknt kezelhetjk, ha
fggvnyprokat hasznlunk. 21.6.4.
[23] Az in_avail() fggvny segtsgvel megllapthatjuk, hogy a kvetkez bemeneti
mveletnek vrakoznia kell-e majd a bemenetre. 21.6.4.
[24] Vlasszuk szt a biztonsgi krdsekkel foglalkoz eljrsokat azon egyszer m-
veletektl, melyek legfontosabb tulajdonsga a hatkonysg. (Az elbbieket
a virtual, az utbbiakat az inline kulcsszval adjuk meg.) 21.6.4.
[25] Hasznljuk a locale osztlyt a kulturlis klnbsgek megfogalmazsra. 21.7.
[26] A sync_with_stdio(x) fggvnnyel a C stlus ki- s bemenetet sszeegyeztethetjk
a C++ I/O rendszervel, de teljesen szt is vlaszthatjuk azokat. 21.8.
[27] A C stlus I/O hasznlatakor mindig nagyon figyeljnk a tpushibk elkerlsre.
21.8.
21.10. Feladatok
1. (*1.5) Olvassunk be egy lebegpontos szmokat tartalmaz fjlt, a beolvasott
szmprokbl ksztsnk komplex rtkeket, majd ezeket rjuk ki.
2. (*1.5) Hatrozzuk meg a Name_and_address (Nv s cm) tpust. Adjuk meg hozz
a << s a >> opertort. Msoljunk le egy Name_and_address objektumokat tartal-
maz adatfolyamot.
3. (*2.5) Prbljunk meg lemsolni olyan Name_and_address objektumokbl ll
adatfolyamot, melyben a lehet legtbb hiba szerepel (pldul formzsi hibk,
21. Adatfolyamok 881
karakterlncok tl korai vgzdse stb.). Prbljuk meg gy kezelni ezeket a hib-
kat, hogy a msol fggvny a helyesen formzott Name_and_address objektumok
tbbsgt be tudja olvasni mg akkor is, ha a bemenet korbban teljesen sszeke-
veredett.
4. (*2.5) rjuk jra a Name_and_address tpus kimeneti formjt gy, hogy az kevs-
b legyen rzkeny a formzsi hibkra.
5. (*2.5) Ksztsnk nhny fggvnyt klnbz tpus informcik bekrshez
s beolvasshoz (pldul egszekhez, lebegpontos szmokhoz, fjlnevekhez,
e-mail cmekhez, dtumokhoz, szemlyi adatokhoz stb.). Prbljunk minl zem-
biztosabb fggvnyeket kszteni.
6. (*1.5) rjunk programot, amely kirja (a) az sszes kisbett, (b) az sszes bett, (c)
az sszes bett s szmjegyet, (d) minden karaktert, amely rendszernkben C++
azonostban szerepelhet, (e) az sszes rsjelet, (f) a vezrlkarakterek kdjait, (g)
az sszes reshely karaktert, (h) az reshely karakterek kdjait, s vgl (i) minden
lthat karaktert.
7. (*2) Olvassunk be szvegsorokat egy rgztett mret karakteres tmeneti trba.
Trljnk minden reshely karaktert s az alfanumerikus karaktereket helyettest-
sk az bc kvetkez karaktervel (a z bett a-ra, a 9-et 0-ra cserljk). rjuk ki
az gy keletkez sorokat.
8. (*3) Ksztsnk egy miniatr adatfolyam I/O rendszert, melyben definiljuk az
istream, az ostream, az ifstream, s az ofstream osztlyt, melyek biztostjk az
operator<<( ) s az operator>>( ) fggvnyt egsz rtkekhez, illetve az olyan elj-
rsokat, mint az open() s a close() a fjlok kezelshez.
9. (*4) rjuk meg a C szabvnyos ki- s bemeneti knyvtrt (<stdio.h>) a C++ szab-
vnyos I/O knyvtrnak (<iostream>) felhasznlsval.
10. (*4) rjuk meg a C++ szabvnyos I/O knyvtrt (<iostream>) a C szabvnyos I/O
knyvtrnak (<stdio.h>) felhasznlsval.
11. (*4) rjuk meg a C s a C++ knyvtrat gy, hogy azokat egyszerre hasznlhassuk.
12. (*2) Ksztsnk egy osztlyt, amelyben a [ ] opertor segtsgvel kzvetlenl ol-
vashatjuk be egy fjl karaktereit.
13. (*3) Ismteljk meg a 21.10[12] feladatot gy, hogy a [ ] opertort rsra s olvass-
ra is hasznlhassuk. tlet: a [ ] adjon vissza egy olyan objektumot, amely egy fjl-
pozcit azonost. Az ezen objektumra vonatkoz rtkads rja a fjlba a megfelel
adatokat, mg az objektumnak char tpusra val automatikus konverzija jelentse
a megfelel karakter beolvasst az llomnybl.
14. (*2) Ismteljk meg a 21.10[13] feladatot gy, hogy a [ ] opertor tetszleges tpu-
s objektum beolvasshoz hasznlhat legyen, ne csak karakterekhez.
15. (*3.5) rjuk meg az istream s az ostream olyan vltozatt, amely a szmokat bin-
ris formban rja ki, s olvassa be ahelyett, hogy szveges alakra alaktan azokat.
Vizsgljuk meg ezen megkzelts elnyeit s htrnyait a karakteralap megkze-
ltssel szemben.
A standard knyvtr 882
16. (*3.5) Tervezznk s rjunk meg egy mintailleszt bemeneti mveletet. Hasznl-
junk printf stlus formzott karakterlncokat a minta meghatrozshoz. Azt is te-
gyk lehetv, hogy tbbfle mintt rprblhassunk a bemenetre a formtum
megtallshoz. A mintailleszt bemeneti osztlyt szrmaztassuk az istream osz-
tlybl.
17. (*4) Talljunk ki (s rjunk meg) egy sokkal jobb mintaillesztsi eljrst. Hatroz-
zuk meg pontosan, miben jobb a mi megoldsunk.
18. (*2) Hatrozzunk meg egy kimeneti mdostt (neve legyen based), amely kt pa-
ramtert kap: egy alapszmot s egy int rtket, s az egsznek az alapszm sze-
rinti szmrendszerbeli alakjt rja a kimenetre. A based(2,9) hatsra pldul az
1001 jelenjen meg a kimeneten.
19. (*2) Ksztsnk mdostkat, melyek ki- s bekapcsoljk a karakterek visszarst
(echoing).
20. (*2) rjuk meg a 21.4.6.3 rsz Bound_form osztlyt a szoksos beptett tpusok-
ra.
21. (*2) rjuk meg a 21.4.6.3 rsz Bound_form osztlyt gy, hogy egy kimeneti m-
velet soha ne csordulhasson tl a szmra megadott width() rtken. Azt is biztos-
tanunk kell a programoz szmra, hogy a kimenet soha ne csonkuljon a megadott
pontossgi rtk miatt.
22. (*3) Ksztsnk egy encrypt(k) mdostt, melynek hatsra az ostream objektu-
mon minden kimenet a k rtkkel titkostva jelenik meg. rjuk meg a mdost
decrypt(k) prjt is az istream osztlyra. Adjunk lehetsget a titkosts kikapcsol-
sra is, teht tudjunk jra olvashat szveget rni a kimenetre.
23. (*2) Kvessk vgig egy karakter tjt rendszernkben a billentyzettl a kper-
nyig az albbi egyszer utastssorozat esetben:
char c;
cin >> c;
cout << c << endl;
24. (*2) Mdostsuk a 21.3.6 pont readints() fggvnyt gy, hogy minden kivtelt
kezelni tudjon. tlet: kezdeti rtkads az erforrs megszerzsvel.
25. (*2.5) Minden rendszerben lehetsg van arra, hogy dtumokat rjunk, olvassunk
s brzoljunk egy locale objektum lersa szerint. Talljuk meg sajt rendszernk
dokumentcijban, hogyan valsthatjuk ezt meg, s ksztsnk egy rvid progra-
mot, amely e mdszer felhasznlsval r s olvas karaktereket. tlet: struct tm.
26. (*2.5) Hozzuk ltre az ostrstream osztlyt az ostream osztlybl val szrmaztats-
sal gy, hogy egy karaktertmbhz (C stlus karakterlnchoz) kthessk hozz,
ugyangy, ahogy az ostringstream kthet egy string objektumhoz. Ne msoljuk
be a tmbt az ostrstream objektumba, sem ki onnan. Az ostrstream osztlynak
21. Adatfolyamok 883
egyszer lehetsget kell adnia a tmbbe val rsra. Az osztlyt olyan memri-
ban vgzett formzsokra szeretnnk felhasznlni, mint az albbi:
char buf[message_size];
ostrstream ost(buf,message_size);
do_something(arguments,ost); // kirs buf-ra ost-on keresztl
cout << buf; // ost hozzadja a lezr 0-t
A do_something()-hoz hasonl mveletek az ost adatfolyamra rnak, esetleg to-
vbbadjk azt sajt rsz-mveleteiknek, s a szabvnyos kimeneti fggvnyeket
hasznljk. A tlcsorduls ellenrzsre nincs szksg, mert az ost ismeri a sajt
mrett s fail() llapotba kerl, amikor megtelik. Vgl a display() mvelet meg-
hvsval az zenetet egy valdi kimeneti adatfolyamba rhatjuk. Ez a mdszer
nagyon hasznos lehet akkor, ha a vgs kimeneti eszkz bonyolultabban tudja
megvalstani a kimenetet, mint a szoksos sorkirson alapul kimeneti eszkzk.
Az ost objektumban trolt szveget pldul megjelenthetjk a kperny egy rgz-
tett mret terletn. Ugyangy hozzuk ltre az istrstream osztlyt, amely egy be-
meneti karakterlnc-folyam, ami nullkarakterrel lezrt karakterlncbl olvas. A le-
zr nullkaraktert hasznljuk fjlvge jelknt. Ezek az strstream osztlyok az erede-
ti adatfolyam-knyvtr rszt kpeztk s gyakran szerepelnek a <strstream.h> fej-
llomnyban.
27. (*2.5) Ksztsk el a general() mdostt, amely visszalltja az adatfolyamot az
eredeti formtumra, hasonlan ahhoz, ahogy a scientific() (21.4.6.2) az adatfolya-
mot tudomnyos formtumra lltja.
A standard knyvtr 884
Szmok
A szmts clja nem a szm,
hanem a tisztnlts.
(R.W. Hamming)
de a tanulnak gyakran
szmokon keresztl
vezet az t a tisztnltshoz.
(A. Ralston)
Bevezets A szmtpusok korltai Matematikai fggvnyek valarray Vektormve-
letek Szeletek slice_array Az ideiglenes vltozk kikszblse gslice_array
mask_array indirect_array complex ltalnostott algoritmusok Vletlen szmok
Tancsok Gyakorlatok
22
22.1. Bevezets
A gyakorlatban ritkn akadunk olyan programozsi feladatra, ahol nincs szksg valami-
lyen szmokkal vgzett mveletre. Programjainkban ltalban az alapvet aritmetikai m-
veleteken kvl szksgnk van egy kis igazi matematikra is. Ez a fejezet a standard
knyvtr ehhez kapcsold szolgltatsait mutatja be.
Sem a C, sem a C++ nyelvet nem elsdlegesen szmmveletek elvgzsre terveztk. Azok
viszont jellemzen ms krnyezetbe begyazva fordulnak el megemlthet itt az adatb-
zis- illetve hlzatkezels, a mszerek vezrlse, a grafika, a pnzgyi szmtsok, valamint
a szimulci klnbz terletei , gy a C++ remek lehetsgeket nyjthat a nmileg bo-
nyolultabb matematikai feladatok elvgzsre is, amelyekkel az elbb emltett terletek
megfelelen kiszolglhatk. A szmmveletek sklja igen szles, az egyszer ciklusoktl
a lebegpontos szmokbl alkotott vektorokig terjed. A C++ ereje az ilyen sszetettebb
adatszerkezeteken vgzett mveleteknl mutatkozik meg igazn, ezrt egyre gyakrabban
alkalmazzk mrnki s tudomnyos szmtsok elvgzsre. A fejezetben a standard
knyvtr azon szolgltatsaival s eljrsaival ismerkedhetnk meg, melyek kifejezetten
a szmmveletek elvgzst tmogatjk a C++ krnyezetben. A matematikai alapok tiszt-
zsra ksrletet sem tesznk, ezeket ismertnek ttelezzk fel. Az esetleges hinyossgok
matematikai (s nem szmtstechnikval foglalkoz) szakknyvekbl ptolhatk.
22.2. A szmtpusok korltozsai
Ha egy programban nem csupn alapszinten vgznk szmmveleteket, mindenkppen
szksg van arra, hogy ismerjk a beptett tpusok alapvet tulajdonsgait. Ezek sokkal in-
kbb az adott fejlesztkrnyezettl fggnek, mintsem a nyelv szablyaitl (4.6). Pldul
melyik a legnagyobb brzolhat int? Mekkora a legkisebb float? Mi trtnik egy double t-
pus szmmal, ha float tpusv alaktjuk? Kerekti vagy csonktja a rendszer? Hny bitet tar-
talmaz a char tpus?
Az ilyen, s ehhez hasonl krdsekre a <limits> fejllomnyban lert numeric_limits sab-
lon (template) specializcii szolglhatnak vlasszal. Lssunk egy pldt:
A standard knyvtr 886
void f(double d, int i)
{
if (numeric_limits<unsigned char>::digits != 8) {
// szokatlan bjt (a bitek szma nem 8)
}
if (i<numeric_limits<short>::min() || numeric_limits<short>::max()<i) {
// i nem trolhat short tpusban rtkveszts nlkl
}
if (0<d && d<numeric_limits<double>::epsilon()) d = 0;
if (numeric_limits<Quad>::is_specialized) {
// a Quad tpushoz rendelkezsre llnak korlt-adatok
}
}
Minden specializci biztostja a sajt paramtertpusnak legfontosabb informcikat. K-
vetkezskppen az ltalnos numeric_limits sablon egyszeren arra szolgl, hogy szabv-
nyos nevet adjon nhny konstansnak s helyben kifejtett (inline) fggvnynek:
template<class T> class numeric_limits {
public:
static const bool is_specialized = false; // ll rendelkezsre informci
// a numeric_limits<T>-rl?
// rdektelen alaprtelmezsek
};
A tnyleges informci az egyedi cl vltozatokban (specializcikban) szerepel. A stan-
dard knyvtr minden vltozata az sszes alaptpushoz (karakterekhez, egszekhez, vals
szmokhoz, valamint a logikai tpushoz) szolgltat egy-egy specializcit a numeric_limits-
bl. Nem szerepel viszont ilyen a tbbi tpushoz: a void mutathoz, a felsorol tpusokhoz,
vagy a knyvtri tpusokhoz (pldul a complex<double> szerkezethez).
A beptett tpusok (pldul a char) esetben csupn nhny adat emltsre mlt. Lssunk
egy numeric_limits<char>-t olyan megvalstsban, ahol a char 8 bites s eljeles:
class numeric_limits<char> {
public:
static const bool is_specialized = true; // igen, van adat
static const int digits = 7; // bitek szma ("binris szmjegyek") eljel nlkl
22. Szmok 887
static const bool is_signed = true; // ebben a megvalstsban a char tpus
// eljeles (signed)
static const bool is_integer = true; // a char egsz jelleg tpus
static char min() throw() { return -128; } // legkisebb rtk
static char max() throw() { return 127; } // legnagyobb rtk
// deklarcik, amelyek kzmbsek a char tpus szmra
};
Jegyezzk meg, hogy egy eljeles egsz tpus tnylegesen szmbrzolsra hasznlt bitjei-
nek szma (digits) eggyel kevesebb a tpushoz rendelt bitszmnl, hiszen egy bitet az el-
jel foglal le.
A numeric_limits tagjainak tbbsge a lebegpontos szmok lersra szolgl. A kvetke-
z plda egy lehetsges float-vltozatot mutat:
class numeric_limits<float> {
public:
static const bool is_specialized = true;
static const int radix = 2; // a kitev tpusa (ebben az esetben 2-es alap)
static const int digits = 24; // radix szmjegyek a mantisszban
static const int digits10 = 6; // 10-es alap szmjegyek a mantisszban
static const bool is_signed = true;
static const bool is_integer = false;
static const bool is_exact = false;
static float min() throw() { return 1.17549435E-38F; }
static float max() throw() { return 3.40282347E+38F; }
static float epsilon() throw() { return 1.19209290E-07F; }
static float round_error() throw() { return 0.5F; }
static float infinity() throw() { return /* valamilyen rtk */; }
static float quiet_NaN() throw() { return /* valamilyen rtk */; }
static float signaling_NaN() throw() { return /* valamilyen rtk */; }
static float denorm_min() throw() { return min(); }
static const int min_exponent = -125;
static const int min_exponent10 = -37;
static const int max_exponent = +128;
static const int max_exponent10 = +38;
A standard knyvtr 888
static const bool has_infinity = true;
static const bool has_quiet_NaN = true;
static const bool has_signaling_NaN = true;
static const float_denorm_style has_denorm = denorm_absent; // enum a <limits>-bl
static const bool has_denorm_loss = false;
static const bool is_iec559 = true; // megfelel IEC-559-nek
static const bool is_bounded = true;
static const bool is_modulo = false;
static const bool traps = true;
static const bool tinyness_before = true;
static const float_round_style round_style = round_to_nearest; // enum a <limits>-bl
};
Ne feledjk, hogy a min() a legkisebb pozitv normlt szm, az epsilon pedig a legkisebb
pozitv lebegpontos szm, amelyre az 1+epsilon-1 brzolhat.
Amikor egy skalr tpust egy beptett tpus segtsgvel definilunk, rdemes egyttal
a numeric_limits megfelel specializcijt is megadnunk. Ha pldul egy ngyszeres pon-
tossg Quad, vagy egy klnlegesen pontos egsz, long long tpust ksztnk, a felhasz-
nlk jogos elvrsa, hogy ltezzenek a numeric_limits<Quad> s a numeric_limits<long
long> specializcik.
A numeric_limits-nek elkpzelhet olyan vltozata, mely egy olyan felhasznli tpus tulaj-
donsgait rja le, melynek nem sok kze van lebegpontos szmokhoz. Az ilyen esetekben
rendszerint ajnlatosabb a tpustulajdonsgok lersra hasznlt ltalnos eljrs alkalmaz-
sa, mint egy j numeric_limits meghatrozsa olyan tulajdonsgokkal, melyek a szabvny-
ban nem szerepelnek.
A lebegpontos szmokat helyben kifejtett (inline) fggvnyek brzoljk. A numeric_limits
osztlyban szerepl egsz rtkeket viszont olyan formban kell brzolnunk, amely meg-
engedi, hogy konstans kifejezsekben felhasznljuk azokat, ezrt ezek az elemek osztlyon
belli kezdeti rtkadssal rendelkeznek (10.4.6.2). Ha ilyen clokra static const tagokat
hasznlunk felsorol tpusok helyett, ne felejtsk el definilni a static elemeket.
22. Szmok 889
22.2.1. Korltozsi makrk
A C++ rklte a C-tl azokat a makrkat, melyek lerjk az egsz tpusok tulajdonsgait.
Ezek a <climits>, illetve a <limits.h> fejllomnyban szerepelnek. Ilyen makr pldul
a CHAR_BIT vagy az INT_MAX. Ugyangy a <cfloat> s a <float.h> fejllomny azokat
a makrkat tartalmazza, amelyek a lebegpontos szmok tulajdonsgait rjk le. Ezekre pl-
da a DBL_MIN_EXP, a FLT_RADIX vagy a LDBL_MAX.
Mint mindig, a makrkat most is rdemes elkerlnnk.
22.3. Szabvnyos matematikai fggvnyek
A <cmath> s a <math.h> fejllomny szolgltatja azokat a fggvnyeket, amelyeket lta-
lban szoksos matematikai fggvnyeknek neveznk:
double abs(double); // abszoltrtk, ugyanaz mint fabs(); ilyen nincs a C-ben
double fabs(double); // abszoltrtk
double ceil(double d); // a d-nl nem kisebb legkisebb egsz
double floor(double d); // a d-nl nem nagyobb legnagyobb egsz
double sqrt(double d); // d ngyzetgyke, d nem negatv kell legyen
double pow(double d, double e); // d e-edik hatvnya,
// hiba, ha d==0 s e<=0, vagy ha d<0 s e nem egsz
double pow(double d, int i); // d i-edik hatvnya; ilyen nincs a C-ben
double cos(double); // koszinusz
double sin(double); // szinusz
double tan(double); // tangens
double acos(double); // arkusz koszinusz
double asin(double); // arkusz szinusz
double atan(double); // arkusz tangens
double atan2(double x, double y); // atan(x/y)
double sinh(double); // szinusz hiperbolikusz
double cosh(double); // koszinusz hiperbolikusz
double tanh(double); // tangens hiperbolikusz
A standard knyvtr 890
double exp(double); // e alap exponencilis
double log(double d); // termszetes (e alap) logaritmus,
// d nagyobb kell legyen 0-nl
double log10(double d); // 10-es alap logaritmus, d nagyobb kell legyen 0-nl
double modf(double d, double* p); // d trt rszvel tr vissza,
// az egsz rszt *p-be helyezi
double frexp(double d, int* p); // x eleme [.5,1) s y gy, hogy d = x*pow(2,y) legyen;
// visszatr rtk x, y *p-be helyezse
double fmod(double d, double m); // lebegpontos maradk, eljele megfelel d eljelnek
double ldexp(double d, int i); // d*pow(2,i)
A <cmath> s a <math.h> fejllomny ugyanezeket a fggvnyeket float s long double pa-
ramterekkel is elrhetv teszi.
Ha egy mveletnek tbb lehetsges eredmnye is van (mint pldul az asin() esetben),
a fggvnyek a nullhoz legkzelebbi rtket adjk vissza. Az acos() eredmnye mindig
nemnegatv.
Ezek a fggvnyek a hibkat az <errno> fejllomnyban szerepl errno vltoz bellts-
val jelzik. Ennek rtke EDOM, ha egy fggvny nem rtelmezhet a megadott paramter-
re, s ERANGE ha az eredmny nem brzolhat az adott tpussal:
void f()
{
errno = 0; // trli az elz hibakdot
sqrt(-1);
if (errno==EDOM) cerr << "A sqrt() nem definilt negatv paramter esetn";
pow(numeric_limits<double>::max(),2);
if (errno == ERANGE) cerr << "A pow() eredmnye tl nagy ahhoz,"
<< "hogy double-knt brzoljuk";
}
A C++ elzmnyeire visszavezethet okokbl nhny matematikai fggvny a <cstdlib> fej-
llomnyban szerepel a <cmath> helyett:
int abs(int); // abszoltrtk
long abs(long); // abszoltrtk ; ilyen nincs a C-ben
long labs(long); // abszoltrtk
struct div_t { megvalsts_fgg quot, rem; };
struct ldiv_t { megvalsts_fgg quot, rem; };
22. Szmok 891
div_t div(int n, int d); // n osztsa d-vel, visszatrs (kvciens,maradk)
ldiv_t div(long int n, long int d); // n osztsa d-vel, visszatrs (kvciens,maradk);
// ilyen nincs a C-ben
ldiv_t ldiv(long int n, long int d); // n osztsa d-vel, visszatrs (kvciens,maradk)
22.4. Vektormveletek
A legtbb szmtsi feladat viszonylag egyszer, egydimenzis vektorokra vonatkozik,
amelyek lebegpontos szmokat trolnak. Ezrt a nagyteljestmny szmtgpek kln
tmogatjk az ilyen vektorok kezelst, a velk foglalkoz knyvtrak szles krben hasz-
nlatosak, s nagyon sok terleten ltfontossg az ilyen vektorokat kezel fggvnyek le-
het legoptimlisabb kialaktsa. A standard knyvtr tartalmaz egy olyan vektort
(valarray), amelyet kifejezetten a szoksos matematikai vektormveletek gyors vgrehajt-
sra dolgoztak ki.
Mikzben ttekintjk a valarray lehetsgeit, mindig gondoljunk arra, hogy ez egy vi-
szonylag alacsony szint programelem, amely nagy hatkonysg szmtsok elvgzshez
kszlt. Teht az osztly tervezsekor a legfontosabb clkitzs nem a knyelmes haszn-
lat volt, hanem a nagyteljestmny szmtgpek lehetsgeinek minl jobb kihasznlsa,
a legersebb optimalizlsi mdszerek alkalmazsa. Ha sajt programunkban a rugalmas-
sg s az ltalnossg fontosabb, mint a hatkonysg, valsznleg rdemesebb a szabv-
nyos trolk kzl vlasztanunk (melyeket a 16. s a 17. fejezet mutat be), ne is prbljunk
a valarray egyszer, hatkony s szndkosan hagyomnyos kereteihez igazodni.
Felmerlhet bennnk a krds, hogy mirt nem a valarray neve lett vector, hiszen ez a ha-
gyomnyos, matematikai vektor valdi megfelelje, s a 16.3 pont vector osztlyt kne in-
kbb array tpusnak nevezni. A terminolgia mgsem ezt az elnevezsi rendet kveti.
A valarray numerikus szmtsokra optimalizlt vektor, mg a vector egy rugalmas trol,
amely a legklnbzbb tpus objektumok trolsra s kezelsre szolgl. Az array
(tmb) fogalma ersen ktdik a beptett tmbtpushoz.
A valarray tpust ngy kisegt tpus egszti ki, melyek a valarray egy-egy rszhalmazt
kpezik:
A slice_array s a gslice_array a szeletek (slice) fogalmt brzoljk (22.4.6,
22.4.8).
A mask_array egy rszhalmazt hatroz meg, gy, hogy minden elemrl meg-
mondja, hogy az benne van-e a rszhalmazban vagy sem (22.4.9).
A standard knyvtr 892
Az indirect_array a rszhalmazba tartoz elemek indexrtkeinek (sorszmai-
nak) listjt tartalmazza (22.4.10).
22.4.1. Valarray ltrehozsa
A valarray tpus s a hozz tartoz szolgltatsok definicija az std nvtrben szerepel s
a <valarray> fejllomny segtsgvel rhet el:
template<class T> class std::valarray {
// brzols
public:
typedef T value_type;
valarray(); // valarray size()==0 mrettel
explicit valarray(size_t n); // n elem, rtkk: T()
valarray(const T& val, size_t n); // n elem, rtkk: val
valarray(const T* p, size_t n); // n elem, rtkk: p[0], p[1], ...
valarray(const valarray& v); // v msolata
valarray(const slice_array<T>&); // lsd 22.4.6
valarray(const gslice_array<T>&); // lsd 22.4.8
valarray(const mask_array<T>&); // lsd 22.4.9
valarray(const indirect_array<T>&); // lsd 22.4.10
~valarray();
// ...
};
Ezek a konstruktorok lehetv teszik, hogy egy valarray objektumnak a kisegt numeri-
kus tmbtpusok vagy nll rtkek segtsgvel adjunk kezdrtket:
valarray<double> v0; // helyfoglals, v0-nak ksbb adunk rtket
valarray<float> v1(1000); // 1000 elem, mindegyik rtke float()==0.0F
valarray<int> v2(-1,2000); // 2000 elem, mindegyik rtke -1
valarray<double> v3(100,9.8064); // hiba: lebegpontos valarray mret
valarray<double> v4 = v3; // v4 elemszma v3.size()
A ktparamter konstruktorokban az rtket az elemszm eltt kell megadnunk. Ez eltr
a szabvnyos trolkban hasznlt megoldstl (16.3.4).
22. Szmok 893
A msol konstruktornak tadott valarray paramter elemeinek szma hatrozza meg a lt-
rejv valarray mrett.
A legtbb program tblkbl vagy valamilyen bemeneti mvelet segtsgvel jut hozz az ada-
tokhoz. Ezt tmogatja az a konstruktor, amely egy beptett tmbbl msolja ki az elemeket:
const double vd[ ] = { 0, 1, 2, 3, 4 };
const int vi[ ] = { 0, 1, 2, 3, 4 };
valarray<double> v3(vd,4); // 4 elem: 0,1,2,3
valarray<double> v4(vi,4); // tpushiba: vi nem double-ra mutat
valarray<double> v5(vd,8); // nem meghatrozott: tl kevs elem a kezdrtk-adban
Ez a kezdrtk-ad forma nagyon fontos, mert a legtbb numerikus program nagy tm-
bk formjban adja meg az adatokat.
A valarray tpust s kisegt szolgltatsait nagy sebessg szmtsokhoz terveztk. Ez n-
hny, a felhasznlkra vonatkoz korltozsban, s nhny, a megvalstkra vonatkoz
engedmnyben nyilvnul meg. A valarray ksztje szinte brmilyen optimalizlsi md-
szert hasznlhat, amit csak el tud kpzelni. A mveletek pldul lehetnek helyben kifejtet-
tek, a valarray mveleteit pedig mellkhatsok nlklinek tekinthetjk (persze sajt para-
mtereikre ez nem vonatkozik). Egy valarray objektumrl felttelezhetjk, hogy nem
rendelkezik lnvvel (alias), kisegt tpusokat brmikor bevezethetnk, s az ideiglenes
vltozkat is szabadon kikszblhetjk, ha az alapvet jelentst gy is meg tudjuk tartani.
Ezrt a <valarray> fejllomnyban szerepl deklarcik jelentsen el is trhetnek az itt be-
mutatott (s a szabvnyban szerepl) formtl, de azon programok szmra, melyek nem
srtik meg a szablyokat, mindenkppen ugyanazokat a mveleteket kell biztostaniuk,
ugyanazzal a jelentssel. A valarray elemeinek msolsa pldul a szoksos mdon kell,
hogy mkdjn (17.1.4).
22.4.2. A valarray indexelse s az rtkads
A valarray osztly esetben az indexels egyarnt hasznlhat nll elemek elrshez s
rsztmbk kijellshez:
template<class T> class valarray {
public:
// ...
valarray& operator=(const valarray& v); // v msolsa
valarray& operator=(const T& val); // minden elem val-t kapja rtkl
A standard knyvtr 894
T operator[ ](size_t) const;
T& operator[ ](size_t);
valarray operator[ ](slice) const; // lsd 22.4.6
slice_array<T> operator[ ](slice);
valarray operator[ ](const gslice&) const; // lsd 22.4.8
gslice_array<T> operator[ ](const gslice&);
valarray operator[ ](const valarray<bool>&) const; // lsd 22.4.9
mask_array<T> operator[ ](const valarray<bool>&);
valarray operator[ ](const valarray<size_t>&) const; // lsd 22.4.10
indirect_array<T> operator[ ](const valarray<size_t>&);
valarray& operator=(const slice_array<T>&); // lsd 22.4.6
valarray& operator=(const gslice_array<T>&); // lsd 22.4.8
valarray& operator=(const mask_array<T>&); // lsd 22.4.9
valarray& operator=(const indirect_array<T>&); // lsd 22.4.10
// ...
};
Egy valarray objektumot rtkl adhatunk egy msik, ugyanolyan mret valarray-nek.
Elvrsainknak megfelelen a v1=v2 utasts a v2 minden elemt a v1 megfelel elembe
msolja. Ha a tmbk klnbz mretek, az eredmny nem meghatrozott lesz, mert
a sebessgre optimalizlt valarray osztlytl nem kvetelhetjk meg, hogy nem megfelel
mret objektum rtkl adsakor knnyen rthet hibajelzst (pldul kivtelt) kapjunk,
vagy ms, logikus viselkedst tapasztaljunk.
Ezen hagyomnyos rtkads mellett lehetsg van arra is, hogy egy valarray objektumhoz
egy skalr rtket rendeljnk. A v=7 utasts pldul a v valarray minden egyes elembe
a 7 rtket rja. Ez els rnzsre elg meglep, s szerept gy rthetjk meg leginkbb,
ha gy gondolunk r, mint az rtkad mveleteknek egy, nhny esetben hasznos, elfaj-
zott vltozatra (22.4.3).
Az egszekkel val sorszmozs a szoksos mdon mkdik, tartomnyellenrzs nlkl.
Az nll elemek kivlasztsa mellett a valarray indexelse lehetsget ad rsztmbk
ngyfle kijellsre is (22.4.6). Megfordtva, az rtkad utastsok (s a konstruktorok,
22.4.1) ilyen rsztmbket is elfogadnak paramterknt. A valarray tpusra megvalstott
rtkad utastsok biztostjk, hogy a kisegt tmb tpusokat (mint a slice_array) ne kell-
jen talaktanunk valarray tpusra, mieltt rtkl adjuk azokat. A hatkonysg biztostsa
22. Szmok 895
rdekben egy alapos fejlesztkrnyezetnek illik a vektor ms mveleteihez (pldul a +
s a *) is biztostani az ilyen vltozatokat. Ezenkvl a vektormveletek szmtalan mdon
optimalizlhatk, szeletek (slice) vagy ms kisegt vektortpusok hasznlatval.
22.4.3. Mveletek tagfggvnyknt
A nyilvnval s a kevsb nyilvnval tagfggvnyek listja kvetkez:
template<class T> class valarray {
public:
// ...
valarray& operator*=(const T& arg); // v[i]*=arg minden elemre
// hasonlan: /=, %=, +=, -=, ^=, &=, |=, <<=, s >>=
T sum() const; // elemek sszege, += hasznlatval
T min() const; // legkisebb rtk, < hasznlatval
T max() const; // legnagyobb rtk, < hasznlatval
valarray shift(int i) const; // logikai lptets (balra, ha 0<i; jobbra, ha i<0)
valarray cshift(int i) const; // ciklikus lptets (balra, ha 0<i; jobbra, ha i<0)
valarray apply(T f(T)) const; // eredmny[i] = f(v[i]) minden elemre
valarray apply(T f(const T&)) const;
valarray operator-() const; // eredmny[i] = -v[i] minden elemre
// hasonlan: +, ~, !
size_t size() const; // elemek szma
void resize(size_t n, const T& val = T()); // n elem, rtkk val
};
Ha size()=0, akkor sum(), min() s max() rtke nem meghatrozott.
Pldul, ha v egy valarray, akkor a v*=.2 vagy a v/=1.3 utastsok alkalmazhatak r. Ha
skalrmveleteket hajtunk vgre egy vektoron, akkor az azt jelenti, hogy a vektor minden
elemre elvgezzk azt. Szoks szerint, egyszerbb a *= mveletet optimalizlni, mint a *
s az = prostst (11.3.1).
A standard knyvtr 896
Figyeljk meg, hogy a nem rtkad mveletek mindig j valarray objektumot hoznak ltre:
double incr(double d) { return d+1; }
void f(valarray<double>& v)
{
valarray<double> v2 = v.apply(incr); // j, megnvelt valarray-t hoz ltre, megnvelt rtkkel
}
Ezen programrszlet hatsra a v rtke nem vltozik. Sajnos az apply() nem kpes fgg-
vnyobjektumokat (18.4) hasznlni paramterknt (22.9.[1]).
A logikai s a ciklikus lptet fggvnyek (a shift() s a cshift()) olyan j valarray objektu-
mokat adnak vissza, amelyben az elemek megfelelen el vannak lptetve. Az eredeti vek-
tor itt is vltozatlan marad. A v2=v.cshift(n) ciklikus lptets pldul egy olyan valarray ob-
jektumot eredmnyez, melynek elemeire v2[i]==v[(i+n)%v.size()].. A v3=v.shift(n) logikai
lptet mvelet hatsra a v3[i] a v[i+n] rtket veszi fel, ha az i+n ltez indexe a v vektor-
nak. Ellenkez esetben az adott elembe a vektor alaprtelmezett rtke kerl. Ebbl kvet-
kezik, hogy a shift() s a cshift() is balra tolja az elemeket, ha pozitv paramtert adunk
meg, s jobbra, ha negatv rtket:
void f()
{
int alpha[ ] = { 1, 2, 3, 4, 5 ,6, 7, 8 };
valarray<int> v(alpha,8); // 1, 2, 3, 4, 5, 6, 7, 8
valarray<int> v2 = v.shift(2); // 3, 4, 5, 6, 7, 8, 0, 0
valarray<int> v3 = v<<2; // 4, 8, 12, 16, 20, 24, 28, 32
valarray<int> v4 = v.shift(-2); // 0, 0, 1, 2, 3, 4, 5, 6
valarray<int> v5 = v>>2; // 0, 0, 0, 1, 1, 1, 1, 2
valarray<int> v6 = v.cshift(2); // 3, 4, 5, 6, 7, 8, 1, 2
valarray<int> v7 = v.cshift(-2); // 7, 8, 1, 2, 3, 4, 5, 6
}
A valarray tpus esetben a << s a >> opertor bitenknti lptetst vgez, teht nem ele-
meket tolnak el s nem is I/O mveletek (22.4). Ennek megfelelen a <<= s a >>= mve-
letekkel elemeken belli eltolst vgezhetnk egsz tpus elemek esetben:
void f(valarray<int> vi, valarray<double> vd)
{
vi <<= 2; // vi[i]<<=2, vi minden elemre
vd <<= 2; // hiba: a lptets nem meghatrozott lebegpontos rtkekre
}
22. Szmok 897
A valarray mrett mdosthatjuk is. A resize() itt nem arra szolgl, hogy a valarray osz-
tlyt egy dinamikusan nvekedni kpes adatszerkezett tegye mint ahogy a vector s
a string esetben trtnik , hanem j kezdrtket ad mvelet, amely a ltez elemeket
is lecserli a valarray alaprtelmezett rtkre, gy a rgi elemeket vglegesen elvesztjk.
Az tmretezsre sznt valarray objektumokat gyakran res vektorknt hozzuk ltre. Gon-
doljuk vgig pldul, hogyan adhatunk kezdrtket egy valarray objektumnak bemenet
alapjn:
void f()
{
int n = 0;
cin >> n; // a tmb mretnek beolvassa
if (n<=0) error("hibs tmbmret");
valarray<double> v(n); // tmb ltrehozsa a szksges mrettel
int i = 0;
while (i<n && cin>>v[i++]) ; // a tmb feltltse
if (i!=n) error("tl kevs bemeneti elem");
// ...
}
Ha a bemenetet kln fggvnyben szeretnnk kezelni, a kvetkezt tehetjk:
void initialize_from_input(valarray<double>& v)
{
int n = 0;
cin >> n; // a tmb mretnek beolvassa
if (n<=0) error("hibs tmbmret");
v.resize(n); // v tmretezse a szksges mretre
int i = 0;
while (i<n && cin>>v[i++]) ; // a tmb feltltse
if (i!=n) error("tl kevs bemeneti elem");
}
void g()
{
valarray<double> v; // alaprtelmezett tmb ltrehozsa
initialize_from_input(v); // v mretezse s feltltse
// ...
}
Ezzel a megoldssal elkerlhetjk nagy mret adatterletek msolst.
A standard knyvtr 898
Ha azt szeretnnk, hogy egy valarray megrizze az rtkes adatokat, mikzben dinamiku-
san nvekszik, ideiglenes vltozt kell hasznlnunk:
void grow(valarray<int>& v, size_t n)
{
if (n<=v.size()) return;
valarray<int> tmp(n); // n alaprtelmezett elem
copy(&v[0],&v[v.size()],&tmp[0]); // msol algoritmus 18.6.1-bl
v.resize(n);
copy(&tmp[0],&tmp[v.size()],&v[0]);
}
A valarray tpust nem az ilyen felhasznlsi terletekre terveztk. Egy valarray objektum-
nak nem illik megvltoztatnia mrett, miutn a kezdeti helyfoglals megtrtnt.
A valarray elemei egyetlen sorozatot alkotnak, teht a v[0],,v[n-1] elemek egyms utn
tallhatk a memriban. Ebbl kvetkezik, hogy a T* egy kzvetlen elrs (random-
access) bejr (itertor, 19.2.1) a valarray<T> vektorhoz, gy a szabvnyos algoritmusok,
pldul a copy(), alkalmazhatk r. Ennek ellenre jobban illik a valarray szellemisghez,
ha a msolst rtkadsok s rsztmbk formjban fejezzk ki:
void grow2(valarray<int>& v, size_t n)
{
if (n<=v.size()) return;
valarray<int> tmp = v;
slice s(0,v.size(),1); // v.size() elemszm rsztmb (lsd 22.4.5)
v.resize(n); // az tmretezs nem rzi meg az elemek rtkt
v[s] = tmp; // elemek visszamsolsa v els rszbe
}
Ha valamilyen okbl a bemeneti adatok olyan elrendezsek, hogy be kell azokat olvas-
nunk, mieltt megtudnnk a trolsukhoz szksges vektor mrett, ltalban rdemes el-
szr egy vector (16.3.5) objektumba olvasni az elemeket s onnan msolni azokat egy
valarray vltozba.
22. Szmok 899
22.4.4. Nem tagfggvnyknt megvalstott mveletek
A szoksos binris (ktoperandus) opertorok s matematikai fggvnyek gy szerepel-
nek a knyvtrban:
template<class T> valarray<T> operator*(const valarray<T>&, const valarray<T>&);
template<class T> valarray<T> operator*(const valarray<T>&, const T&);
template<class T> valarray<T> operator*(const T&, const valarray<T>&);
// hasonlan: /, %, +, -, ^, &, |, <<, >>, &&, ||, ==, !=, <, >, <=, >=, atan2, s pow
template<class T> valarray<T> abs(const valarray<T>&);
// hasonlan: acos, asin, atan, cos, cosh, exp, log, log10, sin, sinh, sqrt, tan, s tanh
A binris mveleteket kt valarray objektumra, vagy egy valarray objektum s a megfele-
l tpus skalr rtk egyttesre alkalmazhatjuk:
void f(valarray<double>& v, valarray<double>& v2, double d)
{
valarray<double> v3 = v*v2; // v3[i] = v[i]*v2[i] minden i-re
valarray<double> v4 = v*d; // v4[i] = v[i]*d minden i-re
valarray<double> v5 = d*v2; // v5[i] = d*v2[i] minden i-re
valarray<double> v6 = cos(v); // v6[i] = cos(v[i]) minden i-re
}
Ezek a vektormveletek valarray operandusuk (operandusaik) minden elemre vgrehajt-
jk a megfelel mveletet, gy, ahogy a * s a cos() pldkon keresztl bemutattuk. Ter-
mszetesen minden mvelet csak akkor alkalmazhat, ha a megfelel fggvny definilt
a sablonparamterknt megadott tpusra. Ellenkez esetben a fordt hibazenetet ad, ami-
kor megprblja pldnyostani a sablont (13.5).
Ha az eredmny egy valarray, akkor annak hossza megegyezik a paramter(ek)ben hasz-
nlt valarray mretvel. Ha kt valarray paramter mrete nem egyezik meg (binris m-
veletnl), az eredmny nem meghatrozott lesz.
rdekes mdon I/O mveletek nem llnak rendelkezsnkre a valarray tpushoz (22.4.3);
a << s a >> opertor csak lptetsre szolgl. Ha mgis szksgnk van a kt opertor ki-
s bemenetet kezel vltozatra, minden gond nlkl definilhatjuk azokat (22.9[5]).
A standard knyvtr 900
Jegyezzk meg, hogy ezek a valarray mveletek j valarray objektumokat adnak vissza,
s nem operandusaikat mdostjk. Ez egy kicsit kltsgesebb teheti a fggvnyeket, de
ha kellen hatkony optimalizcis mdszereket alkalmazunk, ez a vesztesg nem jelent-
kezik (lsd pldul 22.4.7).
A valarray objektumokra alkalmazhat opertorok s matematikai fggvnyek ugyangy
hasznlhatk slice_array (22.4.6), gslice_array (22.4.8), mask_array (22.4.9) s
indirect_array (22.4.10) objektumokra is, egyes nyelvi vltozatok azonban lehet, hogy
a nem valarray tpus operandusokat elszr valarray tpusra alaktjk, majd ezen vgzik
el a kijellt mveletet.
22.4.5. Szeletek
A slice (szelet) olyan elvont tpus, amely lehetv teszi, hogy vektorokat akrhny dimen-
zis mtrixknt hatkonyan kezeljnk. Ez a tpus a Fortran vektorok alapeleme s kulcssze-
repet jtszik a BLAS (Basic Linear Algebra Subprograms) knyvtrban, amely viszont a leg-
tbb szmmvelet kiindulpontja. A szelet alapjban vve nem ms, mint egy valarray egy
rszletnek minden n-ik eleme:
class std::slice {
// kezdindex, hossz, s lpskz
public:
slice();
slice(size_t start, size_t size, size_t stride);
size_t start() const; // az els elem indexrtke
size_t size() const; // elemek szma
size_t stride() const; // az n-ik elem helye: start()+n*stride()
};
A stride a lpskz, vagyis a tvolsg (az elemek szmban kifejezve) a slice kt, egymst
kvet eleme kztt. Teht a slice egszek egy sorozatt rja le:
size_t slice_index(const slice& s, size_t i) // i lekpezse a megfelel indexrtkre
{
return s.start()+i*s.stride();
}
void print_seq(const slice& s) // s elemeinek kirsa
{
for (size_t i = 0; i<s.size(); i++) cout << slice_index(s,i) << " ";
}
22. Szmok 901
void f()
{
print_seq(slice(0,3,4)); // 0. sor
cout << ", ";
print_seq(slice(1,3,4)); // 1. sor
cout << ", ";
print_seq(slice(0,4,1)); // 0. oszlop
cout << ", ";
print_seq(slice(4,4,1)); // 1. oszlop
}
A megjelen szveg a kvetkez lesz: 0 4 8 , 1 5 9 , 0 1 2 3 , 4 5 6 7
Teht egy slice nem tesz mst, mint hogy nemnegatv egsz rtkeket sorszmokra kpez
le. Az elemek szma (size()) nincs hatssal a lekpezsre (a cmzsre), de lehetsget ad
szmunkra a sorozat vgnek megtallsra. Ez a lekpezs egy egyszer, hatkony, lta-
lnos s viszonylag knyelmes lehetsget ad egy ktdimenzis tmb utnzsra egy egy-
dimenzis tmbben (pldul egy valarray objektumban). Pldul lssunk egy 3-szor 4-es
mtrixot abban a formban, ahogy megszoktuk (C.7):
A Fortran szoksainak megfelelen ezt a kvetkez formban helyezhetnnk el a memriban:
A C++ nem ezt a megoldst hasznlja (lsd C.7), ugyanakkor biztostanunk kell egy
rendszert, amely tiszta s logikus kezelfelletet ad, s ehhez olyan brzolst kell vlasz-
tanunk, amely megfelel a problma ktttsgeinek. Itt most a Fortran elrendezst vlasz-
tottam, hogy knnyen megvalsthat legyen az egyttmkds az olyan numerikus prog-
A standard knyvtr 902
00 01 02
10 11 12
20 21 22
30 31 32
00
0 1 2 3
0 4 8
01 02 10 11 12 20 21 22 30 31 32
ramokkal, melyek ezt az elvet kvetik. Annyira azrt nem voltam hajland vltoztatni, hogy
az indexelst 0 helyett 1-rl indtsam ha ilyen cljaink vannak, oldjuk meg a 22.9[9] fel-
adatot. A legtbb szmmveletet tovbbra is szmos nyelv s mg tbb knyvtr egyttes
hasznlatval kell megoldanunk. A klnbz nyelvek s knyvtrak eltr formtum
adatainak kezelse gyakran ltszksglet.
Egy x sort a slice(x,3,4) kifejezssel rhatunk le. Teht az x sor els eleme a vektor x -ik ele-
me, a sor kvetkez eleme az (x+4)-ik s gy tovbb. A fenti pldban minden sor hrom
elemet tartalmaz. Az brk szerint a slice(0,3,4) a 00, a 01 s a 02 elemet jelli.
Egy y oszlopot a slice(4*y,4,1) hatroz meg. Az y oszlop els eleme a vektornak 4*y-ik ele-
me, a kvetkez oszlopelem a (4*y+1)-ik vektorelem stb. Minden oszlopban 4 elem szere-
pel. A slice(0,4,1) kifejezs teht az brk szerint a 00, a 10, a 20 s a 30 elemet jelli. Azon
kvl, hogy knnyen utnozhatunk ktdimenzis tmbket, a slice segtsgvel szmtalan
ms sorozatot is lerhatunk; az egyszer sorozatok megadsra gy kellen ltalnos md-
szert biztost. Ezzel a 22.4.8 pont foglalkozik rszletesen.
Egy szeletet elkpzelhetnk gy is, mint egy klnleges bejrt (iterator): a slice lehetv
teszi, hogy lerjunk egy indexsorozatot egy valarray objektumban. Ez alapjn egy valdi
bejrt is felpthetnk:
template<class T> class Slice_iter {
valarray<T>* v;
slice s;
size_t curr; // az aktulis elem indexe
T& ref(size_t i) const { return (*v)[s.start()+i*s.stride()]; }
public:
Slice_iter(valarray<T>* vv, slice ss) :v(vv), s(ss), curr(0) { }
Slice_iter end()
{
Slice_iter t = *this;
t.curr = s.size(); // az utols utni elem indexrtke
return t;
}
Slice_iter& operator++() { curr++; return *this; }
Slice_iter operator++(int) { Slice_iter t = *this; curr++; return t; }
T& operator[ ](size_t i) { return ref(curr=i); } // C stlus index
T& operator()(size_t i) { return ref(curr=i); } // Fortran stlus index
T& operator*() { return ref(curr); } // az aktulis elem
// ...
};
22. Szmok 903
Mivel a slice rgztett mret, tartomnyellenrzst is vgezhetnk. A fenti pldban
a slice::size() kihasznlsval valstottuk meg az end() mveletet, hogy a valarray utols
utni elemre llthassuk a bejrt.
A slice lerhat egy sort s egy oszlopot is, gy a Slice_iter is lehetv teszi, hogy akr egy
sort, akr egy oszlopot jrjunk be a valarray objektumban.
Ahhoz, hogy a Slice_iter igazn jl hasznlhat legyen, meg kell hatroznunk az ==, a != s
a < opertort is:
template<class T> bool operator==(const Slice_iter<T>& p, const Slice_iter<T>& q)
{
return p.curr==q.curr && p.s.stride()==q.s.stride() && p.s.start()==q.s.start();
}
template<class T> bool operator!=(const Slice_iter<T>& p, const Slice_iter<T>& q)
{
return !(p==q);
}
template<class T> bool operator<(const Slice_iter<T>& p, const Slice_iter<T>& q)
{
return p.curr<q.curr && p.s.stride()==q.s.stride() && p.s.start()==q.s.start();
}
22.4.6. A slice_array
A valarray s a slice felhasznlsval olyan szerkezetet pthetnk, amely gy nz ki s gy
is viselkedik, mint egy valarray, pedig valjban csak a szelet ltal kijellt terletre hivat-
kozik. Ezt a slice_array tpust a kvetkezkppen definilhatjuk:
template <class T> class std::slice_array {
public:
typedef T value_type;
void operator=(const valarray<T>&);
void operator=(const T& val); // minden elem val-t kapja rtkl
void operator*=(const T& val); // v[i]*=val minden elemre
// hasonlan: /=, %=, +=, -=, ^=, &=, |=, <<=, >>=
A standard knyvtr 904
~slice_array();
private:
slice_array(); // ltrehozs megakadlyozsa
slice_array(const slice_array&); // msols megakadlyozsa
slice_array& operator=(const slice_array&); // msols megakadlyozsa
valarray<T>* p; // megvalststl fgg brzols
slice s;
};
A felhasznlk nem hozhatnak ltre kzvetlenl egy slice_array objektumot, csak egy
valarray indexelsvel az adott szeletre. Miutn a slice_array objektumnak kezdrtket
adtunk, minden r trtn hivatkozs kzvetve arra a valarray objektumra hat majd, amely-
hez ltrehoztuk. Az albbi formban pldul egy olyan szerkezetet hozhatunk ltre, amely
egy tmb minden msodik elemt vlasztja ki:
void f(valarray<double>& d)
{
slice_array<double>& v_even = d[slice(0,d.size()/2+d.size()%2,2)];
slice_array<double>& v_odd = d[slice(1,d.size()/2,2)];
v_even*= v_odd; // az elemprok szorzatt troljuk a pros elemekben
v_odd = 0; // 0 rtkl adsa a pratlan elemeknek
}
A slice_array objektumok msolst nem engedhetjk meg, mert csak gy tehetjk lehet-
v olyan optimalizcis mdszerek alkalmazst, melyek kihasznljk, hogy egy objektum-
ra csak egyflekppen, lnevek nlkl hivatkozhatunk. Ez a korltozs nha nagyon kelle-
metlen:
slice_array<double> row(valarray<double>& d, int i)
{
slice_array<double> v = d[slice(0,2,d.size()/2)]; // hiba: msols ksrlete
return d[slice(i%2,i,d.size()/2)]; // hiba: msols ksrlete
}
Egy slice_array msolsa gyakran helyettesthet a megfelel slice msolsval.
A szeletek alkalmasak a tmbk bizonyos tpus rszhalmazainak kifejezsre. A kvetke-
z pldban pldul szeleteket hasznlunk egy folytonos rsztartomny kezelshez:
22. Szmok 905
inline slice sub_array(size_t first, size_t count) // [first:first+count[
{
return slice(first,count,1);
}
void f(valarray<double>& v)
{
size_t sz = v.size();
if (sz<2) return;
size_t n = sz/2;
size_t n2 = sz-n;
valarray<double> half1(n);
valarray<double> half2(n2);
half1 = v[sub_array(0,n)]; // v els felnek msolsa
half2 = v[sub_array(n,n2)]; // v msodik felnek msolsa
// ...
}
A standard knyvtrban nem szerepel mtrix osztly. Ehelyett a valarray s a slice egytte-
sen igyekszik biztostani azokat az eszkzket, melyekkel klnbz ignyekhez igaztott
mtrixokat pthetnk fel. Nzzk meg, hogyan kszthetnk egy egyszer, ktdimenzis
mtrixot a valarray s a slice_array felhasznlsval:
class Matrix {
valarray<double>* v;
size_t d1, d2;
public:
Matrix(size_t x, size_t y); // megjegyzs: nincs alaprtelmezett konstruktor
Matrix(const Matrix&);
Matrix& operator=(const Matrix&);
~Matrix();
size_t size() const { return d1*d2; }
size_t dim1() const { return d1; }
size_t dim2() const { return d2; }
Slice_iter<double> row(size_t i);
Cslice_iter<double> row(size_t i) const;
Slice_iter<double> column(size_t i);
Cslice_iter<double> column(size_t i) const;
double& operator()(size_t x, size_t y); // Fortran stlus index
double operator()(size_t x, size_t y) const;
A standard knyvtr 906
Slice_iter<double> operator()(size_t i) { return row(i); }
Cslice_iter<double> operator()(size_t i) const { return row(i); }
Slice_iter<double> operator[ ](size_t i) { return row(i); } // C stlus index
Cslice_iter<double> operator[ ](size_t i) const { return row(i); }
Matrix& operator*=(double);
valarray<double>& array() { return *v; }
};
A Matrix-ot egy valarray brzolja. Erre a tmbre a ktdimenzis jelleget a szeletels segt-
sgvel hzhatjuk r. Ezt az brzolst tekinthetjk egy-, kt-, hrom- stb. dimenzis
tmbnek is, ugyangy, ahogy az alaprtelmezett ktdimenzis nzetet biztostjuk a row()
s a column() fggvny megvalstsval. A Slice_iter objektumok segtsgvel megkerl-
hetjk a slice_array objektumok msolsnak tilalmt. Egy slice_array tpus objektumot
nem adhatunk vissza:
slice_array<double> row(size_t i) { return (*v)(slice(i,d1,d2)); } // hiba
Ezrt a slice_array helyett egy bejrt (iterator) adunk meg, amely egy mutatt tartalmaz
a valarray objektumra, illetve magt a slice objektumot.
Meg kell hatroznunk egy tovbbi osztlyt is, a bejrt a konstans szeletekhez. Ez
a Cslice_iter, amely egy const Matrix s egy nem konstans Matrix szeletei kztti klnb-
sget fejezi ki.
inline Slice_iter<double> Matrix::row(size_t i)
{
return Slice_iter<double>(v,slice(i,d1,d2));
}
inline Cslice_iter<double> Matrix::row(size_t i) const
{
return Cslice_iter<double>(v,slice(i,d1,d2));
}
inline Slice_iter<double> Matrix::column(size_t i)
{
return Slice_iter<double>(v,slice(i*d2,d2,1));
}
inline Cslice_iter<double> Matrix::column(size_t i) const
{
return Cslice_iter<double>(v,slice(i*d2,d2,1));
}
22. Szmok 907
A Cslice_iter definicija megegyezik a Slice_iter-vel, a klnbsg mindssze annyi, hogy
itt a szelet elemeire const referencik fognak mutatni.
A tbbi tagfggvny meglehetsen egyszer:
Matrix::Matrix(size_t x, size_t y)
{
// ellenrzs: x s y rtelmes-e
d1 = x;
d2 = y;
v = new valarray<double>(x*y);
}
double& Matrix::operator()(size_t x, size_t y)
{
return row(x)[y];
}
double mul(Cslice_iter<double>& v1, const valarray<double>& v2)
{
double res = 0;
for (size_t i = 0; i<v1.size(); i++) res+= v1[i]*v2[i];
return res;
}
valarray<double> operator*(const Matrix& m, const valarray<double>& v)
{
valarray<double> res(m.dim1());
for (size_t i = 0; i<m.dim1(); i++) res[i] = mul(m.row(i),v);
return res;
}
Matrix& Matrix::operator*=(double d)
{
(*v) *= d;
return *this;
}
A Matrix indexelshez az (i,j) jellst hasznltam, mert a ( ) egy elg egyszer opertor,
s ez a jells a matematikus trsadalomban elgg elfogadott. A sor fogalma ennek elle-
nre lehetv teszi a C s C++ vilgban megszokottabb [i][j] jellst is:
void f(Matrix& m)
{
m(1,2) = 5; // Fortran stlus index
m.row(1)(2) = 6;
A standard knyvtr 908
m.row(1)[2] = 7;
m[1](2) = 8; // zavaros, kevert stlus (de mkdik)
m[1][2] = 9; // C++ stlus index
}
Ha a slice_array osztlyt hasznljuk az indexelshez, ers optimalizlsra van szksgnk.
Ennek a szerkezetnek az ltalnostsa n-dimenzis mtrixra s tetszleges elemtpusra
a megfelel mvelethalmaz biztostsval a 22.9[7] feladat tmja.
Elkpzelhet, hogy els tletnk a ktdimenzis vektor megvalstsra a kvetkez volt:
class Matrix {
valarray< valarray<double> > v;
public:
// ...
};
Ez a megolds is mkdkpes (22.9[10]), de gy igen nehz anlkl sszeegyeztetni a hat-
konysgot a nagyteljestmny szmtsok ltal megkvnt kvetelmnyekkel, hogy
a valarray s a slice osztly ltal brzolt alacsonyabb s hagyomnyosabb szintre trnnk t.
22.4.7. Ideiglenes vltozk, msols, ciklusok
Ha megprblunk kszteni egy vektor vagy egy mtrix osztlyt, rvid idn bell rjhe-
tnk, hogy hrom, egymssal sszefgg problmt kell figyelembe vennnk a teljest-
mnyt hangslyoz felhasznlk ignyeinek kielgtshez:
1. Az ideiglenes vltozk szmt a lehet legkisebbre kell cskkentennk.
2. A mtrixok msolsnak szmt a lehet legkisebbre kell cskkentennk.
3. Az sszetettebb mveletekben az ugyanazon adatokon vgighalad ciklusok
szmt a lehet legkisebbre kell cskkentennk.
A standard knyvtr nem kzvetlenl ezekkel a clokkal foglalkozik. Ennek ellenre lte-
zik olyan eljrs, melynek segtsgvel optimlis megoldst biztosthatunk.
Vegyk pldul az U=M*V+W mveletet, ahol U, V s W vektor, M pedig mtrix. A legegy-
szerbb megoldsban kln-kln ideiglenes vltozra van szksgnk az M*V s az
M*V+W szmra, s msolnunk kell az M*V s az M*V+W eredmnyt is. Egy okosabb
22. Szmok 909
vltozatban elkszthetjk a mul_add_and_assign(&U, &M, &V, &W) fggvnyt, amely-
nek nincs szksge ideiglenes vltozkra, sem a vektorok msolsra, s radsul minden
mtrix minden elemt a lehet legkevesebbszer rinti.
Ilyen mrv optimalizlsra egy-kt kifejezsnl tbbszr ritkn van szksgnk. gy a ha-
tkonysgi problmk megoldsra a mul_add_and_assign() jelleg fggvnyek rsa, az
egyik legegyszerbb mdszer melyeket a felhasznl szksg esetn meghvhat. Ugyanak-
kor lehetsg van olyan Matrix kifejlesztsre is, amely automatikusan alkalmaz ilyen
optimalizcit, ha a kifejezseket megfelel formban fogalmazzuk meg. A lnyeg az, hogy
az U=M*V+W kifejezst tekinthetjk egyetlen, ngy operandussal rendelkez opertor al-
kalmazsnak. Az eljrst mr bemutattuk az ostream mdostknl (21.4.6.3). Az ott be-
mutatott mdszer ltalnosan hasznlhat arra, hogy n darab ktoperandus opertor
egyttest egy (n+1) paramter opertorknt kezeljk. Az U=M*V+W kifejezs kezels-
hez be kell vezetnnk kt segdosztlyt. Ennek ellenre bizonyos rendszerekben ez a elj-
rs igen jelents (akr 30-szoros) sebessgnvekedst is elrhet azzal, hogy tovbbi opti-
malizlst tesz lehetv.
Elszr is meg kell hatroznunk egy Matrix s egy Vector szorzsakor kpzd eredmny
tpust:
struct MVmul {
const Matrix& m;
const Vector& v;
MVmul(const Matrix& mm, const Vector &vv) :m(mm), v(vv) { }
operator Vector(); // az eredmny kiszmtsa s visszaadsa
};
inline MVmul operator*(const Matrix& mm, const Vector& vv)
{
return MVmul(mm,vv);
}
A szorzs semmi mst nem tesz, mint hogy referencikat trol az operandusairl; az M*V
tnyleges kiszmtst ksbbre halasztjuk. Az objektum, amelyet a * mvelet eredmnyez,
igen kzel ll ahhoz, amit gyakran lezrs (closure; inkbb befoglal objektum) nven
emlegetnek. Ugyangy kezelhetjk egy Vector hozzadst az eddigi eredmnyhez:
struct MVmulVadd {
const Matrix& m;
const Vector& v;
const Vector& v2;
A standard knyvtr 910
MVmulVadd(const MVmul& mv, const Vector& vv) :m(mv.m), v(mv.v), v2(vv) { }
operator Vector(); // az eredmny kiszmtsa s visszaadsa
};
inline MVmulVadd operator+(const MVmul& mv, const Vector& vv)
{
return MVmulVadd(mv,vv);
}
Ezzel az M*V+Wkifejezs kiszmtst is elhalasztjuk. Most mr csak azt kell biztostanunk,
hogy hatkony algoritmussal szmoljuk ki a tnyleges eredmnyt, amikor a kifejezs ered-
mnyt rtkl adjuk egy Matrix objektumnak:
class Vector {
// ...
public:
Vector(const MVmulVadd& m) // kezdeti rtkads m eredmnyvel
{
// elemek lefoglalsa, stb.
mul_add_and_assign(this,&m.m,&m.v,&m.v2);
}
Vector& operator=(const MVmulVadd& m) // m eredmnynek rtkl adsa *this-nek
{
mul_add_and_assign(this,&m.m,&m.v,&m.v2);
return *this;
}
// ...
};
gy teht az U=M*V+W kifejezs automatikusan a kvetkez kifejezsre vltozik:
U.operator=(MVmulVadd(MVmul(M,V),W))
Ezt pedig a helyben kifejts alkalmazsa az eredeti, megkvnt fggvnyhvsra alaktja:
mul_add_and_assign(&U,&M,&V,&W)
Ezzel a megoldssal kikszbltk a msolsokat s az ideiglenes vltozkat, radsul
a mul_add_and-assign() fggvnyen tovbbi optimalizlst is vgrehajthatunk. St, ha
a mul_add_and_assign() fggvnyt a lehet legegyszerbb mdon ksztjk el, akkor is
olyan formt alaktottunk ki, amely az automatikus optimalizl eszkzk szmra sokkal
kedvezbb.
22. Szmok 911
A fenti pldban egy j Vector tpust hasznltunk (nem az egyszer valarray osztlyt), mert
j rtkad opertorokat kellett meghatroznunk s az rtkadsoknak mindenkppen tag-
fggvnyeknek kell lennik (11.2.2). Nagy valsznsggel azonban a valarray osztlyt
hasznljuk a Vector megvalstshoz. Ennek a mdszernek abban ll a jelentsge, hogy
a leginkbb idignyes vektor- s mtrixmveletek elvgzst nhny egyszer nyelvi elem-
mel fogalmazzuk meg. Annak igazn soha sincs rtelme, hogy gy optimalizljunk egy fltucat
opertort hasznl kifejezst, ezekre a hagyomnyosabb eljrsok (11.6) is megfelelek.
Az tlet lnyege, hogy fordtsi idej vizsglatokat vgznk s befoglal objektumokat
(closure) hasznlunk, azzal a cllal, hogy a rszkifejezsek kiszmtst a teljes mveletet
brzol objektumra bzzuk. Az elv szmos terleten hasznlhat; lnyege, hogy tbb nl-
l informcielemet egyetlen fggvnybe gyjtnk ssze, mieltt a tnyleges mveleteket
elkezdennk. A ksleltetett kiszmts rdekben ltrehozott objektumokat kompozcis le-
zrt (befoglal) objektumoknak (composition closure object) vagy egyszeren kompo-
zitoroknak (compositor) nevezzk.
22.4.8. ltalnostott szeletek
A 22.4.6 pontban szerepl Matrix plda megmutatta, hogyan hasznlhatunk kt slice ob-
jektumot egy ktdimenzis tmb sorainak s oszlopainak lersra. ltalnosan az igaz,
hogy egy slice alkalmas egy n-dimenzis tmb brmely sornak vagy oszlopnak kijell-
sre (22.9[7]), de nha szksgnk lehet olyan rsztmb kijellsre is, amely nem egyet-
len sora, vagy egyetlen oszlopa az n-dimenzis tmbnek. Tegyk fel, hogy egy 3-szor 4-es
mtrix bal fels sarkban elhelyezked 2-szer 3-as rszmtrixot akarunk kijellni:
Sajnos ezek az elemek nem gy helyezkednek el a memriban, hogy egy slice segtsg-
vel lerhatnnk azokat:
A standard knyvtr 912
00 01 02
10 11 12
20 21 22
30 31 32
00
4 5 6
0 1 2
01 02 10 11 12 20 21 22 30 31 32
A gslice egy olyan ltalnostott slice, amely (majdnem) n darab szelet informciit tartalmazza:
class std::gslice {
// ahelyett, hogy a slice-hoz hasonlan 1 lpskz s 1 mret lenne,
// a gslice-ban n lpskz s n mret van
public:
gslice();
gslice(size_t s, const valarray<size_t>& l, const valarray<size_t>& d);
size_t start() const; // az els elem indexrtke
valarray<size_t> size() const; // elemek szma a dimenziban
valarray<size_t> stride() const; // lpskz index[0], index[1], ... kztt
};
Az j rtkek lehetv teszik, hogy a gslice lekpezst hatrozzon meg n darab egsz s
egy index kztt, amit a tmb elemeinek elrshez hasznlhatunk fel. Egy 2-szer 3-as mt-
rix elhelyezkedst pldul kt (hosszsg, lpskz) elemprral rhatjuk le. A 22.4.5 pont-
ban bemutattuk, hogy a 2 hosszsg s a 4 lpskz a 3-szor 4-es mtrix egy sornak kt
elemt rja le, ha a Fortran elrendezst hasznljuk. Ugyangy, a 3 hosszsg s az 1 lps-
kz egy oszlop 3 elemt jelli ki. A kett egytt kpes lerni egy 2-szer 3-as rszmtrix
sszes elemt. Az elemek felsorolshoz az albbi eljrsra lesz szksgnk:
size_t gslice_index(const gslice& s, size_t i, size_t j)
{
return s.start()+i*s.stride()[0]+j*s.stride()[1];
}
size_t len[ ] = { 2, 3 }; // (len[0],str[0]) egy sort r le
size_t str[ ] = { 4, 1 }; // (len[1],str[1]) egy oszlopot r le
valarray<size_t> lengths(len,2);
valarray<size_t> strides(str,2);
void f()
{
gslice s(0,lengths,strides);
for (int i = 0 ; i<s.size()[0]; i++) cout << gslice_index(s,i,0) << " "; // sor
cout << ", ";
for (int j = 0 ; j<s.size()[1]; j++) cout << gslice_index(s,0,j) << " "; // oszlop
}
Az eredmny 0 4 , 0 1 2 lesz.
22. Szmok 913
Ezzel a mdszerrel egy gslide kt (hosszsg, lpskz) pr segtsgvel kpes megadni
egy ktdimenzis tmb tetszleges rszmtrixt. Hrom (hosszsg, lpskz) prral ler-
hatjuk egy hromdimenzis tmb rsztmbjeit s gy tovbb. Ha egy gslice objektumot
hasznlunk egy valarray indexelshez, akkor egy gslice_array tpus objektumot kapunk,
ami a gslice ltal kijellt elemeket tartalmazza. Pldul:
void f(valarray<float>& v)
{
gslice m(0,lengths,strides);
v[m] = 0; // rtkl adja 0-t v[0],v[1],v[2],v[4],v[5],v[6] elemeknek
}
A gslice_array ugyanazokat a tagfggvnyeket biztostja, mint a slice_array, gy egy
gslice_array objektumot sem hozhat ltre kzvetlenl a felhasznl, s nem is msolhatja
(22.4.6), viszont gslice_array objektumot kapunk eredmnyknt, ha egy valarray (22.4.2)
objektumot egy gslice objektummal indexelnk.
22.4.9. Maszkok
A mask_array tpus a valarray tmb valamely rsze kijellsnek msik mdja. Radsul
az eredmnyt is egy valarray-szer formban kapjuk meg. A valarray osztly szempontj-
bl a maszk egyszeren egy valarray<bool> objektum. Amikor maszkot hasznlunk egy
valarray indexelshez, a true bitek jelzik, hogy a valarray megfelel elemt az eredmny-
ben is meg szeretnnk kapni. Ez a megolds lehetv teszi, hogy egy valarray objektum-
nak olyan rszhalmazt dolgozzuk fel, amely nem valamilyen egyszer elrendezsben (pl-
dul egy szeletben) helyezkedik el:
void f(valarray<double>& v)
{
bool b[ ] = { true , false, false, true, false, true };
valarray<bool> mask(b,6); // a 0, 3, s 5 elem
valarray<double> vv = cos(v[mask]); // vv[0]==cos(v[0]), vv[1]==cos(v[3]),
// vv[2]==cos(v[5])
}
A mask_array osztly ugyanazokat a mveleteket biztostja, mint a slice_array. Egy
mask_array objektumot sem hozhatunk ltre kzvetlenl, s nem is msolhatjuk (22.4.6).
Egy mask_array meghatrozshoz egy valarray (22.4.2) objektumot kell indexelnnk
valarray<bool> objektummal. A maszkolshoz hasznlt valarray mrete nem lehet na-
gyobb, mint azon valarray elemeinek szma, amelyben ezt indexelsre akarjuk hasznlni.
A standard knyvtr 914
22.4.10. Indirekt tmbk
Az indirect_array (indirekt vagyis kzvetett tmb) lehetsget ad arra, hogy egy valarray
objektumot tetszlegesen indexeljnk s trendezznk:
void f(valarray<double>& v)
{
size_t i[ ] = { 3, 2, 1, 0 }; // az els 4 elem fordtott sorrendben
valarray<size_t> index(i,4); // a 3, 2, 1, 0 elemek (ebben a sorrendben)
valarray<double> vv = log(v[index]); // vv[0]==log(v[3]), vv[1]==log(v[2]),
// vv[2]==log(v[1]), vv[3]==log(v[0])
}
Ha egy sorszm ktszer szerepel, akkor a valarray objektum valamely elemre ktszer hi-
vatkozunk egy mveleten bell. Ez pontosan az a tpus tbbszrs hivatkozs (lnv),
amit a valarray nem enged meg. gy az indirect_array mkdse nem meghatrozott, ha
tbbszr hasznljuk ugyanazt az indexrtket.
Az indirect_array osztly ugyanazokat a mveleteket biztostja, mint a slice_array, teht
nem hozhatunk ltre kzvetlenl indirect_array objektumokat, s nem is msolhatjuk azo-
kat (22.4.6); erre a clra egy valarray (22.4.2) objektum valarray<size_t> objektummal
val indexelse szolgl.
22.5. Komplex aritmetika
A standard knyvtr tartalmaz egy complex sablont, krlbell azokkal a tulajdonsgokkal,
amelyekkel a 11.3 pontban szerepl complex osztlyt meghatroztuk. A knyvtrban sze-
repl complex osztlynak azrt kell sablonknt (template) szerepelnie, hogy klnbz
skalr tpusokra pl komplex szmokat is kezelni tudjunk. A knyvtrban kln-kln
vltozat szerepel a float, a double s a long double skalrtpushoz.
A complex osztly az std nvtrhez tartozik s a <complex> fejllomny segtsgvel rhet el:
template<class T> class std::complex {
T re, im;
public:
typedef T value_type;
22. Szmok 915
complex(const T& r = T(), const T& i = T()) : re(r), im(i) { }
template<class X> complex(const complex<X>& a) : re(a.real()), im(a.imag()) { }
T real() const { return re; }
T imag() const { return im; }
complex<T>& operator=(const T& z); // complex(z,0) rtkl adsa
template<class X> complex<T>& operator=(const complex<X>&);
// hasonlan: +=, -=, *=, /=
};
Az itt bemutatott megvalsts s a helyben kifejtett (inline) fggvnyek csak illusztrci-
knt szerepelnek. Ha nagyon akarunk, el tudunk kpzelni ms megvalstst is a standard
knyvtr complex osztlyhoz. Figyeljk meg a sablon tagfggvnyeket, amelyek lehetv
teszik, hogy brmilyen tpus complex objektumnak egy msikkal adjunk kezdrtket,
vagy azt egyszer rtkadssal rendeljk hozz (13.6.2).
A knyv folyamn a complex osztlyt nem sablonknt hasznltuk, csak egyszer osztly-
knt. Erre azrt volt lehetsg, mert a nvterekkel gyeskedve a double rtkek complex
osztlyt tettk alaprtelmezett:
typedef std::complex<double> complex;
Rendelkezsnkre llnak a szoksos egyoperandus (unris) s ktoperandus (binris)
opertorok is:
template<class T> complex<T> operator+(const complex<T>&, const complex<T>&);
template<class T> complex<T> operator+(const complex<T>&, const T&);
template<class T> complex<T> operator+(const T&, const complex<T>&);
// hasonlan: -, *, /, ==, s !=
template<class T> complex<T> operator+(const complex<T>&);
template<class T> complex<T> operator-(const complex<T>&);
A koordinta-fggvnyek a kvetkezk:
template<class T> T real(const complex<T>&);
template<class T> T imag(const complex<T>&);
template<class T> complex<T> conj(const complex<T>&);
// polr koordintarendszer szerinti ltrehozs (abs(),arg()):
A standard knyvtr 916
template<class T> complex<T> polar(const T& rho, const T& theta);
template<class T> T abs(const complex<T>&); // nha rho a neve
template<class T> T arg(const complex<T>&); // nha theta a neve
template<class T> T norm(const complex<T>&); // az abs() ngyzetgyke
A szoksos matematikai fggvnyeket is hasznlhatjuk:
template<class T> complex<T> sin(const complex<T>&);
// hasonlan: sinh, sqrt, tan, tanh, cos, cosh, exp, log, s log10
template<class T> complex<T> pow(const complex<T>&,int);
template<class T> complex<T> pow(const complex<T>&, const T&);
template<class T> complex<T> pow(const complex<T>&, const complex<T>&);
template<class T> complex<T> pow(const T&, const complex<T>&);
A ki- s bemeneti adatfolyamok kezelsre pedig a kvetkezk szolglnak:
template<class T, class Ch, class Tr>
basic_istream<Ch,Tr>& operator>>(basic_istream<Ch,Tr>&, complex<T>&);
template<class T, class Ch, class Tr>
basic_ostream<Ch,Tr>& operator<<(basic_ostream<Ch,Tr>&, const complex<T>&);
A komplex szmok kirsi formja (x,y), mg beolvassra hasznlhatjuk az x, az (x) s az
(x,y) formtumot is (21.2.3, 21.3.5). A complex<float>, a complex<double> s
a complex<long double> specializcik azrt szerepelnek, hogy korltozzuk a konverzikat
(13.6.2) s lehetsget adjunk az optimalizlsra is:
template<> class complex<double> {
double re, im;
public:
typedef double value_type;
complex(double r = 0.0, double i = 0.0) : re(r), im(i) { }
complex(const complex<float>& a) : re(a.real()), im(a.imag()) { }
explicit complex(const complex<long double>& a) : re(a.real()), im(a.imag()) { }
// ...
};
22. Szmok 917
Ezek utn egy complex<float> rtk gond nlkl complex<double> szmra konvertlhat,
de egy complex<long double> mr nem. Ugyanilyen specializcik biztostjk, hogy egy
complex<float> vagy egy complex<double> automatikusan konvertlhat complex<long
double> rtkre, de egy complex<long double> nem alakthat titokban complex<float>
vagy complex<double> szmm. rdekes mdon az rtkadsok nem ugyanazt a vdelmet
biztostjk, mint a konstruktorok:
void f(complex<float> cf, complex<double> cd, complex<long double> cld, complex<int> ci)
{
complex<double> c1 = cf; // j
complex<double> c2 = cd; // j
complex<double> c3 = cld; // hiba: esetleg csonkol
complex<double> c4(cld); // rendben: explicit konverzi
complex<double> c5 = ci; // hiba: nincs konverzi
c1 = cld; // rendben, de vigyzat: esetleg csonkol
c1 = cf; // rendben
c1 = ci; // rendben
}
22.6. ltalnostott numerikus algoritmusok
A <numeric> fejllomnyban a standard knyvtr biztost nhny ltalnostott numerikus
algoritmust is, az <algorithm> fejllomny (18. fejezet) nem numerikus algoritmusainak
stlusban:
A standard knyvtr 918
ltalnostott numerikus algoritmusok <numeric>
accumulate() Egy sorozat elemein vgez el egy mveletet.
inner_product() Kt sorozat elemein vgez el egy mveletet.
partial_sum() Egy sorozatot llt el egy msik sorozatra alkal-
mazott mvelettel.
adjacent_difference() Egy sorozatot llt el egy msik sorozatra alkal-
mazott mvelettel.
Ezek az algoritmusok egy-egy jellegzetes, gyakran hasznlt mveletet ltalnostanak.
Az egyik pldul kiszmtja egy sorozat elemeinek sszegt, de gy, hogy brmilyen tpu-
s sorozatra hasznlhassuk az eljrst, a mveletet pedig, amit a sorozat elemeire el kell v-
gezni, paramterknt adjuk meg. Mindegyik algoritmus esetben az ltalnos mveletet ki-
egszti egy olyan vltozat, amely kzvetlenl a leggyakoribb opertort hasznlja az adott
algoritmushoz.
22.6.1. Az accumulate()
Az accumulate() algoritmust felfoghatjuk gy, mint egy vektor elemeit sszegz eljrs l-
talnostst. Az accumulate() algoritmus az std nvtrhez tartozik s a <numeric> fejllo-
mnybl rhetjk el:
template <class In, class T> T accumulate(In first, In last, T init)
{
while (first != last) init = init + *first++; // sszeads
return init;
}
template <class In, class T, class BinOp> T accumulate(In first, In last, T init, BinOp op)
{
while (first != last) init = op(init,*first++); // ltalnos mvelet
return init;
}
Az accumulate() legegyszerbb vltozata egy sorozat elemeit adja ssze, az elemekre r-
telmezett + opertor segtsgvel:
void f(vector<int>& price, list<float>& incr)
{
int i = accumulate(price.begin(),price.end(),0); // int-be sszegzs
double d = 0;
d = accumulate(incr.begin(),incr.end(),d); // double-ba sszegzs
// ...
}
A visszatrsi rtk tpust az tadott kezdrtk tpusa hatrozza meg.
22. Szmok 919
Gyakran azok az rtkek, melyeket sszegezni szeretnnk, nem egy sorozat elemeiknt ll-
nak rendelkezsnkre. Ha ilyen helyzetbe kerlnk, akkor az accumulate() szmra meg-
adhat mveletet felhasznlhatjuk arra is, hogy az rtkeket ellltsuk. A legegyszerbb
ilyen eljrs csak annyit tesz, hogy a sorozatban trolt adatszerkezetbl kivlasztja a kvnt
rtket:
struct Record {
// ...
int unit_price;
int number_of_units;
};
long price(long val, const Record& r)
{
return val + r.unit_price * r.number_of_units;
}
void f(const vector<Record>& v)
{
cout << "sszeg: " << accumulate(v.begin(),v.end(),0,price) << '\n';
}
Az accumulate() szerepnek megfelel mveletet egyes fejlesztk reduce vagy reduction
nven valstjk meg.
22.6.2. Az inner_product
Egyetlen sorozat elemeinek sszegzse, illetve az ilyen jelleg mveletek nagyon gyakori-
ak, ugyanakkor a sorozatprokon ilyen mveletet vgz eljrsok viszonylag ritkk.
Az inner_product() algoritmus meghatrozsa az std nvtrben szerepel, deklarcijt pe-
dig a <numeric> fejllomnyban tallhatjuk meg:
template <class In, class In2, class T>
T inner_product(In first, In last, In2 first2, T init)
{
while (first != last) init = init + *first++ * *first2++;
return init;
}
template <class In, class In2, class T, class BinOp, class BinOp2>
T inner_product(In first, In last, In2 first2, T init, BinOp op, BinOp2 op2)
{
while (first != last) init = op(init,op2(*first++,*first2++));
return init;
}
A standard knyvtr 920
Szoks szerint, a msodik sorozatnak csak az elejt kell megadnunk paramterknt. A m-
sodik bemeneti sorozatrl azt felttelezzk, hogy legalbb olyan hossz, mint az els.
Amikor egy Matrix objektumot egy valarray objektummal akarunk sszeszorozni, akkor
a legfontosabb mvelet az inner_product:
valarray<double> operator*(const Matrix& m, valarray<double>& v)
{
valarray<double> res(m.dim2());
for (size_t i=0; i<m.dim1(); i++) {
const Cslice_iter<double>& ri = m.row(i);
res[i] = inner_product(ri,ri.end(),&v[0], double(0));
}
return res;
}
valarray<double> operator*(valarray<double>& v, const Matrix& m)
{
valarray<double> res(m.dim1());
for (size_t i=0; i<m.dim2(); i++) {
const Cslice_iter<double>& ci = m.column(i);
res[i] = inner_product(ci, ci.end(), &v[0], double(0));
}
return res;
}
Az inner_product (bels szorzat) bizonyos formit gyakran emlegetjk pont-szorzs (dot
product) nven is.
22.6.3. Nvekmnyes vltozs
A partial_sum() s az adjacent_difference() algoritmus egyms fordtottjnak (inverznek)
tekinthet, de mindkett az inkrementlis vagy nvekmnyes vltozs (incremental
change) fogalmval foglalkozik. Lersuk az std nvtrben s a <numeric> fejllomnyban
szerepel:
template <class In, class Out> Out adjacent_difference(In first, In last, Out res);
template <class In, class Out, class BinOp>
Out adjacent_difference(In first, In last, Out res, BinOp op);
22. Szmok 921
Az a, b, c, d, sorozatbl pldul az adjacent_difference() a kvetkez sorozatot lltja
el: a, b-a, c-b, d-c,
Kpzeljnk el egy sorozatot, amely hmrsklet-rtkeket tartalmaz. Ebbl knnyen kszt-
hetnk egy olyan vektort, amely a hmrsklet-vltozsokat tartalmazza:
vector<double> temps;
void f()
{
adjacent_difference(temps.begin(),temps.end(),temps.begin());
}
A 17, 19, 20, 20, 17 sorozatbl pldul a 17, 2, 1, 0, -3 eredmnyt kapjuk.
Megfordtva, a partial_sum() azt teszi lehetv, hogy tbb nvekmnyes vltozs vgered-
mnyt kiszmtsuk:
template <class In, class Out, class BinOp>
Out partial_sum(In first, In last, Out res, BinOp op)
{
if (first==last) return res;
*res = *first;
T val = *first;
while (++first != last) {
val = op(val,*first);
*++res = val;
}
return ++res;
}
template <class In, class Out> Out partial_sum(In first, In last, Out res)
{
return partial_sum(first,last,res,plus); // 18.4.3
}
Az a, b, c, d, sorozatbl a partial_sum() a kvetkez eredmnyt lltja el: a, a+b,
a+b+c, a+b+c+d, Pldul:
void f()
{
partial_sum(temps.begin(),temps.end(),temps.begin());
}
A standard knyvtr 922
Figyeljk meg, hogy a partial_sum() nveli a res rtkt, mieltt j rtket adna a hivatko-
zott terletnek. Ez lehetv teszi, hogy a res ugyanaz a sorozat legyen, mint a bemeneti.
Ugyangy mkdik az adjacent_difference() is. Teht a kvetkez utasts az a, b, c, d so-
rozatot az a, a+b, a+b+c, a+b+c+d sorozatra kpezi le:
partial_sum(v.begin(),v.end(),v.begin());
Az albbi utastssal pedig visszallthatjuk az eredeti sorozatot:
adjacent_difference(v.begin(),v.end(),v.begin());
Egy msik plda: a partial_sum() a 17, 2, 1, 0, -3 sorozatbl a 17, 19, 20, 20, 17 sorozatot
lltja vissza.
Akik szerint a hmrsklet-vltozsok figyelse csupn rendkvl unalmas rszletkrdse
a meteorolginak vagy a tudomnyos, laboratriumi ksrleteknek, azok szmra megje-
gyezzk, hogy a kszlettartalkok vltozsainak vizsglata pontosan ugyanezen a kt m-
veleten alapul.
22.7. Vletlenszmok
A vletlenszmok fontos szerepet tltenek be igen sok szimulcis s jtkprogramban.
A <cstdlib> s az <stdlib.h> fejllomnyban a standard knyvtr egyszer alapot biztost
a vletlenszmok ellltshoz:
#define RAND_MAX megvalsts_fgg /* nagy pozitv egsz */
int rand(); // l-vletlenszm 0 s RAND_MAX kztt
int srand(int i); // a vletlenszm-elllt belltsa i-vel
J vletlenszm-elllt (genertor) ksztse nagyon nehz feladat, ezrt sajnos nem min-
den rendszerben ll rendelkezsnkre j rand() fggvny. Klnsen igaz ez
a vletlenszmok als bitjeire, aminek kvetkeztben a rand()%n kifejezs egyltaln nem
tekinthet ltalnos (ms rendszerre tvihet) mdszernek arra, hogy 0 s n-1 kz es
vletlenszmokat lltsunk el. A (double(rand())/RAND_MAX)*n ltalban sokkal elfo-
gadhatbb eredmnyt ad.
22. Szmok 923
Az srand() fggvny meghvsval egy j vletlenszm-sorozatot kezdhetnk a paramter-
knt megadott alaprtktl. A programok tesztelsekor gyakran nagyon hasznos, hogy egy
adott alaprtkbl szrmaz vletlenszm-sorozatot meg tudjunk ismtelni. Ennek ellen-
re ltalban a program minden futtatsakor ms alaprtkrl akarunk indulni. Ahhoz hogy
jtkainkat tnyleg megjsolhatatlann tegyk, gyakran szksg van arra, hogy az alapr-
tket a program krnyezetbl szerezzk be. Az ilyen programokban a szmtgp rja l-
tal jelzett id nhny bitje ltalban j alaprtk.
Ha sajt vletlenszm-ellltt kell ksztennk, elengedhetetlen az alapos tesztels
(22.9[14]).
Egy vletlenszm-elllt gyakran hasznlhatbb, ha osztly formjban ll rendelkez-
snkre. gy knnyen kszthetnk vletlenszm-ellltkat a klnbz rtktartom-
nyokhoz:
class Randint { // egyenletes eloszls 32 bites long-ot felttelezve
unsigned long randx;
public:
Randint(long s = 0) { randx=s; }
void seed(long s) { randx=s; }
// bvs szmok: 31 bitet hasznlunk egy 32 bites long-bl:
long abs(long x) { return x&0x7fffffff; }
static double max() { return 2147483648.0; } // figyelem: double
long draw() { return randx = randx*1103515245 + 12345; }
double fdraw(){ return abs(draw())/max(); } //a [0,1] tartomnyban
long operator()() { return abs(draw()); } //a [0,pow(2,31)] tartomnyban
};
class Urand : public Randint { // egyenletes eloszls [0:n[ intervallumban
long n;
public:
Urand(long nn) { n = nn; }
long operator()() { long r = n*fdraw(); return (r==n) ? n-1 : r; }
};
class Erand : public Randint { // exponencilis eloszls vletlenszm-elllt
long mean;
public:
Erand(long m) { mean=m; }
long operator()() { return -mean * log( (max()-draw())/max() + .5); }
};
A standard knyvtr 924
me egy rvid prbaprogram:
int main()
{
Urand draw(10);
map<int,int> bucket;
for (int i = 0; i< 1000000; i++) bucket[draw()]++;
for(int j = 0; j<10; j++) cout << bucket[j] << '\n';
}
Ha nem minden bucket rtke van a 100 000 kzelben, valahol valamilyen hibt vtettnk.
Ezek a vletlenszm-ellltk annak a megoldsnak kiss talaktott vltozatai, amit a C++
knyvtr legels megvalstsban (pontosabban az els osztlyokkal bvtett C knyv-
trban, 1.4) alkalmaztam.
22.8. Tancsok
[1] A numerikus problmk gyakran nagyon rnyaltak. Ha nem vagyunk 100 szza-
lkig biztosak valamely problma matematikai vonatkozsaiban, mindenkppen
krjk szakember segtsgt vagy ksrletezznk sokat. 22.1.
[2] A beptett adattpusok tulajdonsgainak megllaptshoz hasznljuk
a numeric_limits osztlyt. 22.2.
[3] A felhasznli skalrtpusokhoz adjunk meg kln numeric_limits osztlyt.
22.2.
[4] Ha a futsi idej hatkonysg fontosabb, mint a rugalmassg az elemtpusokra
s a mveletekre nzve, a szmmveletekhez hasznljuk a valarray osztlyt.
22.4.
[5] Ha egy mveletet egy tmb rszre kell alkalmaznunk, ciklusok helyett hasznl-
junk szeleteket. 22.4.6.
[6] Hasznljunk kompozitorokat, ha egyedi algoritmusokkal s ideiglenes vltozk
kikszblsvel akarjuk nvelni rendszernk hatkonysgt. 22.4.7.
[7] Ha komplex szmokkal kell szmolnunk, hasznljuk az std::complex osztlyt.
22.5.
[8] Ha egy rtket egy lista elemeinek vgignzsvel kell kiszmtanunk, akkor
mieltt sajt ciklus megvalstsba kezdennk, vizsgljuk meg, nem felel-e
meg cljainknak az accumulate(), az inner_product() vagy az
adjacent_difference() algoritmus. 22.6.
22. Szmok 925
[9] Az adott eloszlsokhoz ksztett vletlenszm-elllt osztlyok hasznosabbak,
mint a rand() kzvetlen hasznlata. 22.7.
[10] Figyeljnk r, hogy vletlenszmaink valban vletlenek legyenek. 22.7.
22.9. Gyakorlatok
1. (*1.5) Ksztsnk egy fggvnyt, amely gy viselkedik, mint az apply(), azzal
a klnbsggel, hogy nem tagfggvny s fggvnyobjektumokat is elfogad.
2. (*1.5) Ksztsnk fggvnyt, amely csak abban tr el az apply() fggvnytl,
hogy nem tagfggvnyknt szerepel, elfogad fggvnyobjektumokat, s a sajt
valarray paramtert mdostja.
3. (*2) Fejezzk be a Slice_iter-t (22.4.5). Klnsen figyeljnk a destruktor ltre-
hozsakor.
4. (*1.5) rjuk meg jbl a 17.4.1.3 pontban szerepl programot az accumulate()
felhasznlsval.
5. (*2) Ksztsk el a << s a >> ki-, illetve bemeneti opertort a valarray osztly-
hoz. Ksztsnk egy get_array() fggvnyt, amely gy hoz ltre egy valarray
objektumot, hogy annak mrett is maga a bemenet adja meg.
6. (*2.5) Hatrozzunk meg s ksztsnk el egy hromdimenzis tmbt a megfe-
lel mveletek ltrehozsval.
7. (*2.5) Hatrozzunk meg s ksztsnk el egy n-dimenzis mtrixot a megfelel
mveletek ltrehozsval.
8. (*2.5) Ksztsnk egy valarray-szer osztlyt, s adjuk meg hozz a + s a *
mveletet. Hasonltsuk ssze ennek hatkonysgt a sajt C++-vltozatunk
valarray osztlynak hatkonysgval. tlet: prbljuk ki tbbek kztt az
x=0.5*(x+y)-z kifejezs kirtkelst klnbz mret x, y s z vektor felhasz-
nlsval.
9. (*3) Ksztsnk egy Fortran stlus tmbt (pldul Fort_array nven), ahol az
indexek nem 0-tl, hanem 1-tl indulnak.
10. (*3) Ksztsk el a Matrix osztlyt gy, hogy az egy sajt valarray objektumot
hasznljon az elemek brzolshoz (nem pedig egy mutatt vagy referencit
a valarray objektumra).
11. (*2.5) Kompozitorok (22.4.7) segtsgvel ksztsnk egy hatkony tbbdimen-
zis indexelsi rendszert a [ ] jellssel. A kvetkez kifejezsek mindegyike
a megfelel elemeket, illetve rsztmbket jellje ki, mghozz egyszer index-
mveletek segtsgvel: v1[x], v2[x][y], v2[x], v3[x][y][z], v3[x][y], v3[x] stb.
A standard knyvtr 926
12. (*2) A 22.7 pontban szerepl program tletnek ltalnostsval rjunk olyan
fggvnyt, amely paramterknt egy vletlenszm-ellltt fogad s egyszer
grafikus formban szemllteti annak eloszlst, gy, hogy segtsgvel szeml-
letesen is ellenrizhessk a vletlenszm-elllt helyessgt.
13. (*1) Ha n egy int rtk, akkor milyen a (double(rand())/RAND_MAX)*n kifeje-
zs eloszlsa?
14. (*2.5) Rajzoljunk pontokat egy ngyzet alak terleten. Az egyes pontok
koordinta-prjait az Urand(N) osztly segtsgvel lltsuk el, ahol N a megje-
lentsi terlet oldalnak kppontban mrt hossza. Milyen kvetkeztetst von-
hatunk le az eredmny alapjn az Urand ltal ltrehozott vletlenszmok
eloszlsrl?
15. (*2) Ksztsnk egy Normal eloszls vletlenszm-ellltt Nrand nven.
22. Szmok 927
Negyedik rsz
Tervezs a C++ segtsgvel
Ez a rsz a programfejleszts tfogbb szemszgbl mutatja be a C++-t s az ltala tmo-
gatott eljrsokat. A hangslyt a tervezsre s a nyelv lehetsgein alapul hatkony meg-
valstsra fektetjk.
Fejezetek
23. Fejleszts s tervezs
24. Tervezs s programozs
25. Az osztlyok szerepe
Csak most kezdem felfedezni, milyen nehz a gondolatainkat paprra vetni. Amg csu-
pn lersbl ll, elg knnyen megy, de amint rvelni kell, a gondolatok kztt megfelel
kapcsolatokat kell teremteni, vilgosan s grdlkenyen kell fogalmazni, s ez, mint mon-
dottam, szmomra oly nehzsget jelent, amire nem is gondoltam
(Charles Darwin)
Fejleszts s tervezs
Nincs ezstgoly.
(F. Brooks)
Programpts Clok s eszkzk A fejlesztsi folyamat fejleszts ciklus Tervezsi
clok Tervezsi lpsek Osztlyok keresse Mveletek meghatrozsa Fggsek
meghatrozsa Felletek meghatrozsa Osztlyhierarchik jraszervezse Modellek
Ksrletezs s elemzs Tesztels A programok karbantartsa Hatkonysg Veze-
ts jrahasznosts Mret s egyensly Az egynek fontossga Hibrid tervezs
Bibliogrfia Tancsok
23.1. ttekints
Ez az els a hrom fejezetbl, melyek rszletesebben bemutatjk a szoftvertermk ksz-
tst, a viszonylag magas szint tervezsi szemlletmdtl azokig a programozsi fogal-
makig s eljrsokig, amelyekkel a C++ a tervezst kzvetlenl tmogatja. Ez a fejezet
a bevezetn s a szoftverfejleszts cljait s eszkzeit rviden trgyal 23.3 ponton kvl
kt f rszbl ll:
23
23.4 A szoftverfejleszts folyamatnak ttekintse.
23.5 Gyakorlati tancsok a szoftverfejleszti munka megszervezshez.
A 24. fejezet a tervezs s a programozsi nyelv kzti kapcsolatot trgyalja, a 25. pedig az osz-
tlyoknak a tervezsben jtszott szerepvel foglalkozik. Egszben vve a 4. rsz clja, hogy
thidalja a szakadkot a nyelvfggetlensgre trekv tervezs s a rvidltan a rszletekre
sszpontost programozs kztt. Egy nagyobb program elksztsi folyamatban mindket-
tnek megvan a helye, de a tervezsi szempontok s a hasznlt eljrsok kztt megfelel
sszhangot kell teremteni, hogy elkerljk a katasztrfkat s a felesleges kiadsokat.
23.2. Bevezets
A legegyszerbbek kivtelvel minden program elksztse sszetett s gyakran csggesz-
t feladat. A programutastsok megrsa mg az nllan dolgoz programoz szmra is
csupn egy rsze a folyamatnak. A problma elemzse, az tfog programtervezs, a doku-
mentls, a tesztels s a program fenntartsnak, mdostsnak krdsei, valamint
mindennek az sszefogsa mellett eltrpl az egyes kdrszek megrsnak s kijavts-
nak feladata. Termszetesen nevezhetnnk e tevkenysgek sszessgt programozs-
nak, majd logikus mdon kijelenthetnnk: n nem tervezek, csak programozok. Akrmi-
nek nevezzk is azonban a tevkenysget, nha az a fontos, hogy a rszletekre sszponto-
stsunk, nha pedig az, hogy az egsz folyamatot tekintsk. Sem a rszleteket, sem a vgclt
nem szabad szem ell tvesztennk br nha pontosan ez trtnik , csak gy kszthe-
tnk teljes rtk programokat.
Ez a fejezet a programfejleszts azon rszeivel foglalkozik, melyek nem vonjk magukkal
egyes kdrszek rst s javtst. A trgyals kevsb pontos s rszletes, mint a korb-
ban bemutatott nyelvi tulajdonsgok s programozsi eljrsok trgyalsa, de nem is lehet
olyan tkletes szakcsknyvet rni, amely lern, hogyan kell j programot kszteni. L-
tezhetnek rszletes hogyan kell lersok egyes programfajtkhoz, de az ltalnosabb
alkalmazsi terletekhez nem. Semmi sem ptolja az intelligencit, a tapasztalatot, s
a programozsi rzket. Kvetkezskppen ez a fejezet csak ltalnos tancsokat ad, illet-
ve megkzeltsi mdokat mutat be s tanulsgos megfigyelseket knl.
A problmk megkzeltst bonyoltja a programok eleve elvont termszete s az a tny,
hogy azok a mdszerek, melyek kisebb projekteknl (pldul amikor egy vagy kt ember
r 10 000 sornyi kdot) mkdnek, nem szksgszeren alkalmazhatk kzepes vagy nagy
Tervezs a C++ segtsgvel 932
projektekben. Ezrt szmos krdst inkbb a kevsb elvont mrnki tudomnyokbl t-
vett hasonlatokon keresztl kzeltnk meg, kdpldk hasznlata helyett. A programter-
vezs trgyalsa a C++ fogalmaival, illetve a kapcsold pldk a 24. s 25. fejezetben ta-
llhatk, de az itt bemutatott elvek tkrzdnek mind magban a C++ nyelvben, mind
a knyv egyes pldiban.
Arra is emlkeztetnm az olvast, hogy az alkalmazsi terletek, emberek s programfej-
leszt krnyezetek rendkvl sokflk, ezrt nem vrhat el, hogy minden itteni javaslatnak
kzvetlenl hasznt vehessk egy adott problma megoldsban. A megfigyelsek vals
helyzetekben szlettek s szmos terleten felhasznlhatk, de nem tekinthetk ltalnos
rvnynek, ezrt nem rt nmi egszsges szkepticizmus.
A C++ gy is tekinthet, mint egy jobb C. De ha gy tesznk, kihasznlatlanul maradnak
a C++ igazi erssgei, gy a nyelv elnyeinek csak tredke rvnyesl. Ez a fejezet kimon-
dottan azokra a tervezsi megkzeltsekre sszpontost, melyek lehetv teszik a C++ el-
vont adatbrzolsi s objektumkzpont programozsi lehetsgeinek hatkony haszn-
latt. Az ilyen mdszereket gyakran nevezzk objektumorientlt (object-oriented)
tervezsnek.
A fejezet f tmi a kvetkezk:
A szoftverfejleszts legfontosabb szempontja, hogy tisztban legynk vele, mit
is ksztnk.
A sikeres szoftverfejleszts sokig tart.
Az ltalunk ptett rendszerek sszetettsgnek hatrait sajt tudsunk s eszk-
zeink kpessgei szabjk meg.
Semmifle tanknyvi mdszer nem helyettestheti az intelligencit, a tapasztala-
tot, a j tervezsi s programozsi rzket.
A ksrletezs minden bonyolultabb program elksztsnl lnyeges.
A programkszts klnbz szakaszai a tervezs, a programozs s a teszte-
ls nem vlaszthatk el szigoran egymstl.
A programozs s a tervezs nem vlaszthatk el e tevkenysgek megszerve-
zsnek krdstl.
Knnyen abba a hibba eshetnk s persze megfizetjk az rt , hogy ezeket a szem-
pontokat albecsljk, pedig az elvont tleteket nehz a gyakorlatba ttenni. Tudomsul
kell vennnk a tapasztalat szksgessgt: ppgy, mint a csnakpts, a kerkprozs,
vagy ppen a programozs, a tervezs sem olyan kpessg, amely pusztn elmleti tanul-
mnyokkal elsajtthat.
23. Fejleszts s tervezs 933
Gyakran elfeledkeznk a rendszerpts emberi tnyezirl, s a programfejleszts folya-
matt egyszeren meghatrozott lpsek sorozatnak tekintjk, mely a bemenetbl adott
mveletek vgrehajtsval ellltja a kvnt kimenetet, a lefektetett szablyoknak megfele-
len. A programozsi nyelv, amelyet hasznlunk, elfedi az emberi tnyez jelenltt, mr-
pedig a tervezs s a programozs emberi tevkenysgek ha errl megfeledkeznk, nem
jrhatunk sikerrel.
Ez a fejezet olyan rendszerek tervezsvel foglalkozik, melyek a rendszert pt emberek
tapasztalathoz s erforrsaihoz kpest ignyesek. gy tnik, a fejlesztk szeretik fesze-
getni lehetsgeik hatrait. Az olyan munkknl, melyek nem jelentenek ilyen kihvst,
nincs szksg a tervezs megvitatsra, hiszen ezeknek kidolgozott kereteik vannak,
amelyeket nem kell szttrni. Csak akkor van szksg j, jobb eszkzk s eljrsok elsa-
jttsra, amikor valami ignyes dologra vllalkozunk. Arra is hajlamosak vagyunk, hogy
hozznk kpest joncokra olyan feladatokat bzzunk, melyekrl mi tudjuk, hogyan kell el-
vgezni, de k nem.
Nem ltezik egyetlen helyes mdszer minden rendszer tervezsre s ptsre. A hitet az
egyetlen helyes mdszerben gyermekbetegsgnek tekintenm, ha nem fordulna el oly
gyakran, hogy tapasztalt programozk s tervezk engednek neki. Emlkeztetek arra, hogy
azrt, mert egy eljrs a mlt vben egy adott munknl jl mkdtt, mg nem biztos,
hogy ugyanaz mdosts nlkl mkdni fog valaki msnl vagy egy msik munka sorn
is. A legfontosabb, hogy mentesek legynk az ilyesfajta felttelezsektl.
Az itt lertak termszetesen a nagyobb mret programok fejlesztsre vonatkoznak. Azok,
akik nem mkdnek kzre ilyen fejlesztsben, visszalhetnek s rlhetnek, ltvn, mi-
lyen rmsgektl menekltek meg, illetve nzegethetik az egyni munkval foglalkoz r-
szeket. A programok mretnek nincs als hatra, ahol a kdols eltti tervezs mg
sszer, de ltezik ilyen hatr, amennyiben a tervezs s dokumentls megkzeltsi
mdjrl van sz. A clok s a mret kztti egyensly fenntartsnak krdseivel
a 23.5.2 pont foglalkozik.
A programfejleszts kzponti problmja a bonyolultsg. A bonyolultsggal pedig egyetlen
mdon lehet elbnni, az oszd meg s uralkodj! elvet kvetve. Ha egy problmt kt n-
magban kezelhet rszproblmra vlaszthatunk szt, flig mris megoldottuk azt. Ez az
egyszer elv bmulatosan vltozatos mdokon alkalmazhat. Nevezetesen, egy modul
vagy egy osztly hasznlata a rendszer megtervezsnl kt rszre osztja a progra-
mot a tnyleges megvalstsra s a felhasznli kdra amelyeket idelis esetben csak
egy jl definilt fellet (interface) kapcsol ssze: ez a programmal jr bonyolultsg keze-
lsnek alapvet megkzeltse. Ugyangy a programtervezsi folyamat is kln tevkeny-
Tervezs a C++ segtsgvel 934
sgekre bonthat, s gy a kzremkd s egymssal kapcsolatban ll fejlesztk kztt
feladatokra bontva sztoszthat: ez pedig a fejlesztsi folyamat s a rsztvev programozk
kztti bonyolult kapcsolatok kezelsnek alapvet megkzeltse.
Mindkt esetben a feloszts s a kapcsolatot megteremt felletek meghatrozsa az, ahol
a legnagyobb tapasztalatra s rzkre van szksg. Ez nem egyszer, mechanikusan meg-
oldhat feladat; jellemzen lesltst ignyel, melyre csak egy rendszer alapos tanulmnyo-
zsa s az elvonatkoztatsi szintek megfelel megrtse ltal tehetnk szert (lsd: 23.4.2,
24.3.1 s 25.3). Ha egy programot vagy fejlesztsi folyamatot csak bizonyos szemszgbl
vizsglunk, slyos hibkat vthetnk. Azt is vegyk szre, hogy klnvlasztani mind az
emberek feladatait, mind a programokat knny. A feladat nehz rsze a hatkony kapcso-
lattarts biztostsa a vlaszfal kt oldaln lev felek kztt anlkl, hogy lerombolnnk
a vlaszfalat vagy elnyomnnk az egyttmkdshez szksges kommunikcit.
Ez a fejezet egy tervezsi megkzeltst mutat be, nem egy teljes tervezsi mdszert; annak
bemutatsa tlmutatna e knyv keretein. Az itt bemutatott megkzelts az ltalnos-
ts vagyis a formlis megfogalmazs klnbz fokozataival s a mgttk megbv
alapvet elvekkel ismertet meg. Nem szakirodalmi ttekints s nem szndkszik rinteni
minden, a szoftverfejlesztsre vonatkoz tmt, vagy bemutatni minden szempontot. Ez is-
mt csak meghaladn e knyv lehetsgeit. Ilyen ttekintst tallhatunk [Booch,1994]-ben.
Feltnhet, hogy a kifejezseket itt elg ltalnos s hagyomnyos mdon hasznlom.
A legrdekesebbeknek tervezs, prototpus, programoz a szakirodalomban szmos
klnbz s gyakran egymssal ellenttes defincija tallhat. Legynk vatosak, nehogy
olyan nem szndkolt jelentst olvassunk ki az itt elmondottakbl, melyek az egyes kifeje-
zsek nmagban vett vagy csupn helyileg pontos meghatrozsain alapulnak.
23.3. Clok s eszkzk
A professzionlis programozs clja olyan termket ltrehozni, mellyel felhasznli elge-
dettek lesznek. Ennek elsdleges mdja olyan programot alkotni, melynek bels felptse
tiszta, s csapatot kovcsolni olyan tervezkbl s programozkbl, akik elg gyesek s
lelkesek ahhoz, hogy gyorsan s hatkonyan reagljanak a vltozsokra s lni tudjanak le-
hetsgeikkel.
23. Fejleszts s tervezs 935
Mirt? A program bels szerkezete s keletkezsnek folyamata idelis esetben nem rinti
a vgfelhasznlt. Nyltabban fogalmazva: ha a vgfelhasznlnak agglya van, hogyan r-
tk meg a programot, akkor azzal a programmal valami baj van. Ezt figyelembe vve felte-
hetjk a krdst: miben ll a program szerkezetnek s a programot megalkot emberek-
nek a fontossga?
Elszr is, egy programnak tiszta bels felptssel kell rendelkeznie, hogy megknnytse
a tesztelst,
a ms rendszerekre val tltetst,
a program karbantartst s mdostst,
a bvtst,
az jraszervezst s
a kd megrtst.
A lnyeg, hogy egyetlen sikeres nagy program trtnete sem r vget a piacra kerlssel;
jabb s jabb programozk s tervezk dolgoznak rajta, j hardverre viszik t, elre nem
ltott clokra hasznljk fel s tbbszr is talaktjk szerkezett. A program lete folyamn
j vltozatokat kell kszteni, elfogadhat mennyisg hibval, elfogadhat id alatt. Ha ez-
zel nem szmolunk, kudarcra vagyunk tlve.
Vegyk szre, hogy br a vgfelhasznlk idelis esetben nem kell, hogy ismerjk egy rend-
szer bels felptst, elfordulhat, hogy kvncsiak r, pldul azrt, hogy fel tudjk be-
cslni megbzhatsgt, lehetsges fellvizsglatt s bvtst. Ha a krdses program
nem egy teljes rendszer, csak ms programok ptst segt knyvtrak kszlete, a felhasz-
nlk mg tbb rszletet akarnak tudni, hogy kpesek legyenek jobban kihasznlni
a knyvtrakat s tleteket merthessenek bellk.
Egyenslyt kell teremteni a program tfog tervezsnek mellzse s a szerkezetre fekte-
tett tlzott hangsly kztt. Az elbbi vg nlkli javtgatsokhoz vezet (ezt most leszllt-
juk, a problmt meg majd kijavtjuk a kvetkez kiadsban), az utbbi tlbonyoltja
a tervezst s a lnyeg elvsz a formai tkletessgre val trekvs kzben, ami azt ered-
mnyezi, hogy a tnyleges megvalsts ksedelmet szenved a program szerkezetnek
folytonos alaktgatsa miatt (de ez az j felpts sokkal jobb, mint a rgi; az emberek
hajlandak vrni r). A forma tartalom fl rendelse gyakran eredmnyez olyan erfor-
rs-igny rendszereket is, melyeket a leend felhasznlk tbbsge nem engedhet meg
magnak. Az egyensly megtartsa a tervezs legnehezebb rsze s ez az a terlet, ahol
a tehetsg s a tapasztalat megmutatkozik. A vlaszts az nll programoz szmra is ne-
hz, de mg nehezebb a nagyobb programoknl, melyek tbb, klnbz kpessg em-
ber munkjt ignylik.
Tervezs a C++ segtsgvel 936
A programot egy olyan szervezett kzssgnek kell megalkotnia s fenntartania, mely
a szemlyi s vezetsi vltozsok ellenre kpes a feladatot megoldani. Npszer megk-
zelts a programfejlesztst nhny viszonylag alacsony szint feladatra szkteni, melyek
egy merev vzba illeszkednek. Az elgondols egy knnyen betanthat (olcs) s cserl-
het, alacsony szint programozkbl (kdolkbl) ll osztly s egy valamivel kevs-
b olcs, de ugyangy cserlhet (s ugyangy mellzhet) tervezkbl ll osztly
ltrehozsa. A kdolkrl nem ttelezzk fel, hogy tervezsi dntseket hoznak, mg a ter-
vezkrl nem ttelezzk fel, hogy a kdols piszkos rszleteivel trdnek. Ez a megk-
zelts gyakran csdt mond; ahol pedig mkdik, nagy mret, de gyenge teljestmny
programokat eredmnyez.
E megolds buktati a kvetkezk:
Elgtelen kapcsolattarts a megvalstst vgzk s a tervezk kztt, ami alkal-
mak elmulasztst, ksst, rossz hatkonysgot, s a tapasztalatbl val tanuls
kptelensge miatt ismtld problmkat eredmnyez.
A programozk kezdemnyezsi hatskrnek elgtelensge, ami a szakmai fej-
lds hinyhoz, felletessghez s koszhoz vezet.
E rendszer alapvet gondja a visszajelzs hinya, mely lehetv tenn, hogy a kzremk-
dk egyms tapasztalataibl tanuljanak. Ez a szks emberi tehetsg elvesztegetse.
Az olyan keretek biztostsa, melyen bell mindenki megcsillanthatja tehetsgt, j kpes-
sgeket fejleszthet ki, tletekkel jrulhat hozz a sikerhez s jl rezheti magt, nemcsak az
egyedli tisztessges megolds, hanem gyakorlati s gazdasgi rtelme is van.
Msrszt egy rendszert nem lehet felpteni, dokumentlni s a vgtelensgig fenntartani
valamilyen ttekinthet szerkezet nlkl. Egy jtsokat is ignyl munka elejn gyakran az
a legjobb, ha egyszeren megkeressk a legjobb szakembereket s hagyjuk ket, hogy gy
kzeltsk meg a problmt, ahogy a legjobbnak tartjk. A munka elrehaladtval azonban
egyre inkbb gyelni kell a helyes temezsre, a rszfeladatok kiosztsra, a bevont sze-
mlyek kztti kapcsolattartsra s bizonyos a jellsre, elnevezsekre, dokumentcira,
tesztelsre stb. vonatkoz irnyelvek betartsra. Ismt csak egyenslyra s a helynval
irnti rzkre van szksg. Egy tl merev rendszer akadlyozhatja a fejldst s elfojthatja
az innovcit. Ebben az esetben a vezet tehetsge s tapasztaltsga mrettetik meg, de az
nll programoz szmra is ugyanilyen dilemma kivlasztani, hol prbljon gyeskedni
s hol csinlja a knyv szerint.
Az adott munknl ajnlatos nem csak a kvetkez kiadsra, hanem hossz tvra tervezni.
Az elrelts hinya mindig megbosszulja magt. A szervezeti felptst s a programfej-
lesztsi stratgit gy kell megvlasztani, hogy tbb program tbb kiadsnak megalkot-
st clozzuk meg vagyis sikerek sorozatt kell megterveznnk.
23. Fejleszts s tervezs 937
A tervezs clja a program szmra tiszta s viszonylag egyszer bels szerkezet archi-
tektra ltrehozsa. Ms szval, olyan vzat akarunk alkotni, melybe beilleszthetk az
egyes kdrszek s ezltal irnyelvknt szolgl e kdrszek megrshoz.
A terv a tervezsi folyamat vgtermke (amennyiben egy krkrs folyamatnak vgterm-
ke lehet). Ez ll a tervez s a programoz, illetve az egyes programozk kzti kapcsolat-
tarts kzppontjban. Fontos, hogy itt kell arnyrzknk legyen. Ha n mint nll
programoz egy kis programot tervezek, melyet holnap fogok elkszteni, megfelel pon-
tossg s rszletessg lehet egy bortk htra firklt pr sor. A msik vglet: egy terve-
zk s programozk szzait foglalkoztat rendszer fejlesztse knyveket kitev, gondosan
megrt szabvnyokat kvnhat, valamilyen szablyhoz rszben vagy teljes egszben iga-
zod jellsek hasznlatval. Egy terv megfelel rszletessgi, pontossgi s formalitsi
szintjnek meghatrozsa nmagban vve is kihv szakmai s vezetsi feladat.
Ebben s a kvetkez fejezetekben felttelezem, hogy egy program terve osztlydeklarci-
k egy halmazval (a privt deklarciikat, mint zavar rszleteket, ltalban elhagyom) s
a kztk lev kapcsolatokkal van kifejezve. Ez persze egyszersts: egy konkrt tervben
sok ms krds is felmerl; pldul a prhuzamos folyamatok, a nvterek kezelse, a nem
tag fggvnyek s adatok hasznlata, az osztlyok s fggvnyek paramterezse, az jra-
fordts szksgessgt a lehet legalacsonyabb szintre visszaszort kdszervezs, a
perzisztencia s a tbb-szmtgpes hasznlat. A trgyalsnak ezen a szintjn mindenkp-
pen szksg van egyszerstsre s a C++-ban a tervezshez az osztlyok megfelel kiin-
dulpontot jelenthetnek. A fent emltett tmakrk kzl nhnyat futlag ez a fejezet is
rint, de a C++-ban val tervezsre kzvetlenl hatst gyakorlkat a 24. s 25. fejezet tr-
gyalja. Ha egy bizonyos objektumorientlt tervezsi modellre rszleteiben vagyunk kvn-
csiak, [Booch, 1994] lehet segtsgnkre.
Az elemzs (analzis, analysis) s a tervezs (design) kztti klnbsgre nem trek ki k-
lnsebben, egyrszt mert nem tmja e knyvnek, msrszt az egyes tervezsi mdszerek
esetben e klnbsget ms-ms mdon hatrozhatnnk meg. Rviden annyit mondha-
tunk, hogy az elemzsi mdszert mindig az adott tervezsi megkzeltshez kell igaztani,
azt pedig a hasznlt programozsi nyelv s stlus alapjn kell megvlasztani.
Tervezs a C++ segtsgvel 938
23.4. A fejlesztsi folyamat
A szoftverfejleszts ismtlst s gyarapodst jelent: a folyamat minden szakasza a fejleszts
alatt jra s jra fellvizsglatra kerl s minden egyes fellvizsglat finomtja az adott sza-
kasz vgtermkt. A fejlesztsi folyamatnak ltalban nincs kezdete s nincs vge. Amikor
egy programrendszert megterveznk s elksztnk, ms emberek terveit, knyvtrait s
programjait vesszk alapul. Amikor befejezzk, a terv s a kd trzst msokra hagyjuk,
hogy finomtsk, fellvizsgljk, bvtsk s ms gpekre ttegyk azt. Termszetesen egy
adott munknak lehet meghatrozott kezdete s vge, s fontos is (br gyakran meglep-
en nehz), hogy azt idben s trben tisztn s pontosan behatroljuk. Ha azonban gy te-
sznk, mintha tiszta lappal indulnnk, komoly problmkat okozhatunk. gy tenni, mint-
ha a vilg a program vgs tadsnl vgzdne, egyarnt fejfjst okozhat utdaink s
gyakran sajt magunk szmra.
Ebbl kvetkezik, hogy a kvetkez fejezetek brmilyen sorrendben olvashatk, mivel egy
vals munka sorn a tervezsi s megvalstsi szempontok majdnem tetszs szerint tfed-
hetik egymst. Ez azt jelenti, hogy a tervezs majdnem mindig jratervezs, amely egy
elz tervezsen s nmi fejlesztsi tapasztalaton alapul. Tovbb, a tervezst korltozzk
az temtervek, a rsztvev emberek kpessgei, a kompatibilitsi krdsek s gy tovbb.
A tervez, vezet vagy programoz szmra nagy kihvst jelent rendet teremteni e folya-
matban anlkl, hogy elfojtannk az jtsi trekvseket s tnkretennnk a visszajelzs
rendszert, melyek a sikeres fejlesztshez szksgesek.
A fejlesztsi folyamat hrom szakaszbl ll:
Elemzs: a megoldand problma kiterjedsnek meghatrozsa
Tervezs: a rendszer tfog felptsnek megalkotsa
Megvalsts: a kd megrsa s ellenrzse
Mg egyszer emlkeztetnk e folyamat ismtld termszetre nem vletlen, hogy a sza-
kaszok kztt nem lltottam fel sorrendet. Vegyk szre azt is, hogy a programfejleszts
nhny f szempontja nem kln szakaszknt jelentkezik, mivel ezeknek a folyamat sorn
mindvgig rvnyeslnik kell:
Ksrletezs
Ellenrzs
A tervek s a megvalsts elemzse
Dokumentls
Vezets
23. Fejleszts s tervezs 939
A program fenntartsa vagy karbantartsa egyszeren e fejlesztsi folyamat tbbszri is-
mtlst jelenti (23.4.6).
A legfontosabb, hogy az elemzs, a tervezs s a megvalsts ne vljon el tlzottan egyms-
tl, s hogy a bevont emberek kzs szellemisg, kultra s nyelv rvn hatkonyan tudja-
nak egymssal kommuniklni. Nagyobb programok fejlesztsnl ez sajnos ritkn van gy.
Idelis esetben az egynek a munka sorn tbb szakaszban is rszt vesznek; ez az ismeretek
s tapasztalatok tadsnak legjobb mdja. Sajnos a programfejleszt cgek gyakran akad-
lyozzk az ilyen mozgst, pldul azltal, hogy a tervezknek magasabb beosztst s/vagy
magasabb fizetst adnak, mint a csak programozknak. Ha gyakorlati szempontbl nem
clszer, hogy a munkatrsak vndorolva tanuljanak s tantsanak, legalbb arra biztassuk
ket, hogy rendszeresen beszlgessenek a fejleszts ms szakaszaiba bevontakkal.
Kisebb s kzepes projekteknl gyakran nem klnbztetik meg az elemzst s a tervezst;
e kt szakasz egyesl. A kis programok ksztsnl ltalban ugyangy nem vlik szt a ter-
vezs s a programozs, ami persze megoldja a kommunikcis problmkat. Fontos, hogy
a formalitsok s a szakaszok elklntse mindig az adott munkhoz igazodjanak
(23.5.2); nem ltezik egyetlen, rk rvny t.
Az itt lert programfejlesztsi modell gykeresen klnbzik a hagyomnyos vzess mo-
delltl, amelyben a fejleszts szablyosan s egyenes irnyban halad a fejlesztsi szakaszo-
kon t, az elemzstl a tesztelsig. A vzess modell alapvet baja, hogy az informci
folysa egyirny. Ha a folys irnyban haladva problmkba tkznk, a szervezeti fel-
pts s a munkamdszerek arra knyszertenek, hogy helyben oldjuk meg azokat, az el-
z szakaszokra val hats nlkl. A visszacsatols ezen hinya gyenge tervekhez vezet,
a helyi javtsok pedig torz megvalstst eredmnyeznek. Vannak elkerlhetetlen esetek,
amikor az informci mindenkppen visszafel ramlik s megvltoztatja az eredeti terve-
ket. A gerjesztett hullm csak lassan s nehzkesen jut el cljhoz, hiszen a rendszert gy
alkottk meg, hogy ne legyen szksg vltoztatsokra s a rendszer emiatt lassan s kellet-
lenl reagl. A semmi vltoztats vagy a helyi javts elve teht olyan elvv vltozik,
mely szerint egy rszleg nem adhat tbbletmunkt ms rszlegeknek a sajt knyelme r-
dekben. Lehetsges, hogy mire egy nagyobb hibt szlelnek, mr olyan sok paprmunkt
vgeztek, hogy a kd javtshoz szksges erfeszts eltrpl ahhoz kpest, ami a doku-
mentci mdostshoz kell. Ilyenkor a paprmunka vlik a programfejleszts f kerkk-
tjv. Termszetesen ilyen problmk elfordulhatnak s el is fordulnak, jllehet a nagy
rendszerek fejlesztst alaposan megszervezik. Mindenkppen elengedhetetlen nmi pa-
prmunka. Az egyenes vonal (vzess) fejlesztsi modell alkalmazsa azonban nagyban
nveli a valsznsgt, hogy a problma kezelhetetlenn vlik.
Tervezs a C++ segtsgvel 940
A vzesses modellel az a problma, hogy nincs benne megfelel visszacsatols s kpte-
len a vltozsokra reaglni. Az itt vzolt ciklikus megkzelts veszlye viszont a ksrts,
hogy a valdi gondolkods s halads helyett nem azonos cl rdekben vgrehajtott m-
dostsok sort vgezzk el. Mindkt problmt knnyebb felismerni, mint megoldani, s
brmilyen jl is szervezzk meg a feladat vgrehajtst, knnyen sszetveszthetjk a pusz-
ta munkt a haladssal. Termszetesen a munka elrehaladtval a fejlesztsi folyamat k-
lnbz szakaszainak jelentsge vltozik. Kezdetben a hangsly az elemzsen s a terve-
zsen van, s kevesebb figyelmet fordtunk a programozsi krdsekre. Ahogy mlik az
id, az erforrsok a tervezs s a programozs fel toldnak, majd inkbb a programozs
s a tesztels fel. Kulcsfontossg azonban, hogy soha ne sszpontostsunk csupn az
elemzs/tervezs/megvalsts egyikre, kizrva ltkrnkbl a tbbit.
Ne felejtsk el, hogy nem segthet rajtunk semmilyen, a rszletekre is kiterjed figyelem,
vezetsi mdszer vagy fejlett technolgia, ha nincs tiszta elkpzelsnk arrl, mit is aka-
runk elrni. Tbb terv hisul meg amiatt, hogy nincsenek jl meghatrozott s valszer
cljai, mint brmilyen ms okbl. Brmit tesznk is s brhogyan fogunk is hozz, tzznk
ki kzzelfoghat clokat, jelljk ki a hozz vezet t llomsait, s hasznljunk fel minden
megfelel technolgit, ami csak elrhet mg akkor is, ha az beruhzst ignyel , mert
az emberek jobban dolgoznak megfelel eszkzkkel s megfelel krnyezetben. Ne
higgyk persze, hogy knny ezt a tancsot megfogadni.
23.4.1. A fejleszts krforgsa
Egy program fejlesztse sorn lland fellvizsglatra van szksg. A f ciklus a kvetkez
lpsek ismtelt vgrehajtsbl ll:
1. Vizsgljuk meg a problmt.
2. Alkossunk tfog tervet.
3. Keressnk szabvnyos sszetevket.
Igaztsuk ezeket a tervhez.
4. Ksztsnk j szabvnyos sszetevket.
Igaztsuk ezeket is a tervhez.
5. lltsuk ssze a konkrt tervet.
Hasonlatknt vegynk egy autgyrat. A projekt beindtshoz kell, hogy legyen egy tfo-
g terv az j auttpusrl. Az elzetes tervnek valamilyen elemzsen kell alapulnia s lta-
lnossgban kell lernia az autt, inkbb annak szndkolt hasznlatt, mintsem a kvnt
tulajdonsgok kialaktsnak rszleteit szem eltt tartva. Eldnteni, mik is a kvnt tulajdon-
sgok vagy mg inkbb, a dntshez viszonylag egyszer irnyelveket adni gyakran
23. Fejleszts s tervezs 941
a munka legnehezebb rsze. Ezt sikeresen ltalban egyetlen, nagy ttekintssel rendelke-
z egyn tudja elvgezni, s ez az, amit gyakran ltomsnak (vision) neveznk. Igen gya-
kori, hogy hinyoznak az ilyen tiszta clok mrpedig a terv ezek nlkl meginoghat vagy
meg is hisulhat.
Tegyk fel, hogy egy kzepes nagysg autt akarunk pteni, ngy ajtval s ers motor-
ral. A tervezs els szakasza a leghatrozottabban nem az aut (s az alkatrszek) megter-
vezse a semmibl. Egy meggondolatlan programtervez vagy programoz hasonl k-
rlmnyek kztt (ostobn) pontosan ezt prbln tenni.
Az els lps annak szmbavtele, milyen alkatrszek szerezhetk be a gyr sajt kszlete-
ibl s megbzhat szlltktl. Az gy tallt alkatrszek nem kell, hogy pontosan illeszked-
jenek az j autba. Lesz md az alkatrszek hozzigaztsra. Azt is megtehetjk, hogy az
ilyen alkatrszek kvetkez kiadshoz irnyelveket adunk, hogy sajt elkpzelseink-
hez jobban illeszkedjenek. Pldul ltezhet egy motor, melynek megfelelek a tulajdons-
gai, azzal a kivtellel, hogy kicsit gyengbb a leadott teljestmnye. A cg vagy a motor szl-
ltja rszerelhet egy turbfeltltt, hogy ezt helyrehozza, anlkl, hogy befolysoln az
alapvet terveket. Vegyk szre, hogy az ilyen, az alaptervet nem befolysol vltoztats
valsznsge kicsi, hacsak az eredeti terv nem ellegezi meg valamilyen formban az iga-
zthatsgot. Az ilyen igazthatsg ltalban egyttmkdst kvn kztnk s a motor
szlltja kztt. A programoz hasonl lehetsgekkel rendelkezik: a tbbalak (poli-
morf) osztlyok s sablonok (template) pldul hatkonyan felhasznlhatk egyedi vlto-
zatok ksztsre. Nem szabad azonban elvrnunk, hogy az osztly ksztje a mi szjunk
ze szerint bvtse alkotst; ehhez a kszt elreltsra vagy a velnk val egyttmk-
dsre van szksg.
Ha kimerlt a megfelel szabvnyos alkatrszek vlasztka, az auttervez nem rohan,
hogy megtervezze az j authoz az optimlis j alkatrszeket. Ez egyszeren tl kltsges
lenne. Tegyk fel, hogy nem kaphat megfelel lgkondicionl egysg s hogy egy meg-
felel L alak tr ll rendelkezsre a motortren bell. Az egyik megolds egy L alak lg-
kondicionl egysg megtervezse lenne, de annak valsznsge, hogy ez a klnleges
forma ms auttpusoknl is hasznlhat, mg alapos igazts esetn is csekly. Ebbl k-
vetkezik, hogy autterveznk nem lesz kpes az ilyen egysgek termelsi kltsgeit ms
auttpusok tervezivel megosztani, gy az egysg kihasznlhat lettartama rvid lesz. r-
demes teht olyan egysget tervezni, mely szlesebb krben szmthat rdekldsre; vagyis
egy tisztbb vonal egysget kell tervezni, mely jobban az ignyekhez igazthat, mint a mi
elmletben elksztett L alak furcsasgunk. Ez valsznleg tbb munkval jr, mint az L
alak egysg s magval vonhatja az aut tfog terveinek mdostst is, hogy illeszked-
jen az ltalnosabb cl egysghez. Mivel az j egysg szlesebb krben hasznlhat, mint
a mi L alak csodnk, felttelezhet, hogy szksg lesz egy kis igaztsra, hogy tkletesen
Tervezs a C++ segtsgvel 942
illeszkedjen a mi fellvizsglt ignyeinkhez. A programoz ismt hasonl lehetsgek k-
zl vlaszthat: ahelyett, hogy az adott programra nzve egyedi kdot rna, j, ltalnosabb
sszetevt tervezhet, amely remlhetleg valamilyen szabvnny vlik.
Amikor vgkpp kifogytunk a lehetsges szabvnyos sszetevkbl, sszelltjuk a vgle-
ges tervet. A lehet legkevesebb egyedi tervezs alkatrszt hasznljuk, mert jvre kis-
s ms formban ismt vgig kell csinlnunk ugyanezt a munkt a kvetkez j modell-
hez, s az egyedi alkatrszek lesznek azok, amelyeket a legvalsznbb, hogy jra kell
ptennk vagy el kell dobnunk. Sajnos a hagyomnyosan tervezett programokkal kapcso-
latban az a tapasztalat, hogy kevs bennk az olyan rsz, amelyet egyltaln nll ssze-
tevnek lehetne tekinteni s az adott programon kvl mshol is fel lehetne hasznlni.
Nem lltom, hogy minden auttervez olyan sszeren gondolkodik, mint ahogy a hason-
latban felvzoltam vagy hogy minden programtervez elkveti az emltett hibkat. ppen
ellenkezleg, a bemutatott modell a programtervezsben is hasznlhat. Ez a fejezet s
a kvetkezk olyan eljrsokat ismertetnek, melyek a C++-ban mkdnek. A programok
nem kzzelfoghat termszete miatt viszont a hibkat ktsgtelenl nehezebb elkerlni
(24.3.1, 24.3.4), s rmutatok majd arra is (23.5.3.1), hogy a cgek mkdsi elve gyakran
elveszi az emberek btorsgt, hogy az itt vzolt modellt hasznljk. Ez a fejlesztsi modell
valjban csak akkor mkdik jl, ha hosszabb tvra terveznk. Ha ltkrnk csak a k-
vetkez kiadsig terjed, a szabvnyos sszetevk megalkotsa s fenntartsa rtelmetlen,
egyszeren felesleges tlmunknak fogjuk ltni. E modell kvetse olyan fejlesztkzss-
gek szmra javasolt, melyek lete szmos projektet fog t s melyek mrete miatt rdemes
pnzt fektetni a szksges eszkzkbe (a tervezshez, programozshoz s projektvezets-
hez) s oktatsba (a tervezk, programozk s vezetk rszre). Modellnk valjban egy-
fajta szoftvergyr vzlata, amely furcsa mdon csak mreteiben klnbzik a legjobb
nll programozk gyakorlattl, akik az vek sorn mdszerek, tervek, eszkzk s
knyvtrak kszlett ptettk fel, hogy fokozzk szemlyes hatkonysgukat. Sajnos gy
tnik, a legtbb cg elmulasztotta kihasznlni a legjobbak gyakorlati tudst, mert hiny-
zott belle az elrelts s a kpessg, hogy az ltaluk hasznlt megkzeltst nagyobb
programokra is alkalmazza.
Vegyk szre, hogy nincs rtelme minden szabvnyos sszetevtl elvrni, hogy ltal-
nos rvny szabvny legyen. Nemzetkzileg szabvnyos knyvtrbl csak nhny marad-
hat fenn. A legtbb sszetev vagy alkatrsz (csak) egy orszgon, egy ipargon, egy
gyrtsoron, egy osztlyon, egy alkalmazsi terleten stb. bell lesz szabvny. A vilg egy-
szeren tl nagy ahhoz, hogy valsgos vagy igazn kvnatos cl legyen minden eszkz-
re egyetemes szabvnyt alkotni.
23. Fejleszts s tervezs 943
Ha mr az elejn az egyetemessget tzzk ki clul, a tervet arra tljk, hogy soha ne le-
gyen befejezve. Ennek egyik oka, hogy a fejleszts krkrs folyamat, s elengedhetetlen,
hogy legyen egy mkd rendszere, amelybl tapasztalatokat merthetnk (23.4.3.6).
23.4.2. Tervezsi clok
Melyek a tervezs tfog cljai? Az egyik termszetesen az egyszersg, de milyen szem-
pontok szerint? Felttelezzk, hogy a tervnek fejldnie kell, vagyis a programot majd bv-
teni, ms rendszerre tvinni, finomhangolni s tbbfle, elre nem lthat mdon mdos-
tani lesz szksges. Kvetkezskppen olyan tervet s rendszert kell megcloznunk, amely
egyszer, de knnyen s sokfle mdon mdosthat. A valsgban a rendszerrel szemben
tmasztott kvetelmnyek mr a kezdeti terv s a program els kibocstsa kztt tbbszr
megvltoznak.
Ez magval vonja, hogy a programot gy kell megtervezni, hogy a mdostsok sorozata
alatt a lehet legegyszerbb maradjon. A mdosthatsgot figyelembe vve a kvetkez-
ket kell clul kitznnk:
Rugalmassg
Bvthetsg
Hordozhatsg (ms rendszerekre val tltethetsg)
A legjobb, ha megprbljuk elszigetelni a program azon terleteit, melyek valsznleg vl-
tozni fognak, a felhasznlk szmra pedig lehetv tesszk, hogy mdostsaikat e rszek
rintse nlkl vgezhessk el.
Ezt a program kulcsfogalmainak azonostsval s azzal rhetjk el, hogy minden egyes
osztlyra kizrlagos felelssggel rbzzuk egy fogalommal kapcsolatos minden inform-
ci kezelst. Ez esetben mindennem vltoztats csupn az adott osztly mdostsval
rhet el. Idelis esetben egy fogalom mdostshoz elg egy j osztly szrmaztatsa
(23.4.3.5) vagy egy sablon eltr paramterezse. Termszetesen az elvet a gyakorlatban
sokkal nehezebb kvetni.
Vegynk egy pldt: egy meteorolgiai jelensgeket utnz programban egy esfelht
akarunk megjelenteni. Hogyan csinljuk? Nem lehet ltalnos eljrsunk a felh megjele-
ntsre, mivel a felh kinzete fgg a felh bels llapottl s ez az llapot a felh kiz-
rlagos felelssge al kell, hogy tartozzon.
Tervezs a C++ segtsgvel 944
A problma els megoldsa, hogy hagyjuk a felhre a sajt megjelentst. Ez bizonyos k-
rlmnyek kztt elfogadhat, de nem ltalnos megolds, mert egy felht sokfle mdon
lehet megjelenteni: rszletes kppel, elnagyolt vzlatknt, vagy mint egy jelet egy trkpen.
Ms szval az, hogy egy felh mire hasonlt, fgg mind a felhtl, mind a krnyezettl.
A msik megolds, hogy a felht tjkoztatjuk krnyezetrl, majd hagyjuk, hogy megje-
lentse magt. Ez mg tbb esetben elfogadhat, de mg mindig nem ltalnos mdszer. Ha
tudatjuk a felhvel krnyezetnek rszleteit, megsrtjk azt a szablyt, hogy egy osztly
csak egy dologrt lehet felels s hogy minden dolog valamelyik osztly felelssge. Nem
biztos, hogy eljuthatunk a felh krnyezetnek egy kvetkezetes jellshez, mert ltal-
ban az, hogy egy felh mihez hasonlt, fgg mind a felhtl, mind a nztl. Az, hogy sz-
momra mihez hasonlt egy felh, mg a vals letben is meglehetsen fgg attl, hogyan
nzem: puszta szemmel, polrszrn keresztl vagy meteorolgiai radarral. A nzn s
a felhn kvl lehet, hogy valamilyen ltalnos htteret is figyelembe kell venni, pldul
a nap viszonylagos helyzett. Tovbb bonyoltja a dolgot, ha egyb objektumokat is ms
felhket, replgpeket szmtsba vesznk. Hogy a tervez lett igazn megnehezt-
sk, adjuk hozz azt a lehetsget is, hogy egyszerre tbb nznk van.
A harmadik megolds, hogy hagyjuk a felht s a tbbi objektumot (a replgpeket s
a napot), hogy lerjk magukat a nzknek. E megolds elg ltalnos ahhoz, hogy a leg-
tbb clnak megfeleljen, de jelents rat fizethetnk rte, mert a program tlsgosan bo-
nyolult s lass lehet. Hogyan rtetjk meg pldul a nzvel a felhk s ms objektumok
ltal ksztett lersokat?
A programokban nem srn fordulnak el esfelhk (br a plda kedvrt lsd 15.2), de
a klnbz I/O mveletekbe szksgszeren bevont objektumok is hasonlan viselked-
nek. Ez teszi a felh pldt tallv a programok s klnsen a knyvtrak tervezsre
nzve. Logikailag hasonl plda C++ kdjt mutattam be az adatfolyamok be- s kimeneti
rendszernek trgyalsakor, a formzott kimenetre hasznlt mdostknl (manipulator,
21.4.6, 21.4.6.3). Le kell azonban szgeznnk, hogy a harmadik megolds nem a helyes,
csupn a legltalnosabb megolds. A terveznek mrlegelnie kell a rendszer klnbz
szksgleteit, hogy megvlassza az adott problmhoz az adott rendszerben megfelel
mrtk ltalnossgot s elvont brzolsmdot. Alapszably, hogy egy hossz letnek
tervezett programban az elvonatkoztatsi szintnek a mg megrthet s megengedhet leg-
ltalnosabbnak, nem az abszolt legltalnosabbnak kell lennie. Ha az ltalnosts tlha-
ladja az adott program kereteit s a ksztk tapasztalatt, kros lehet, mert ksedelmet, el-
fogadhatatlanul rossz hatkonysgot, kezelhetetlen terveket s nyilvnval kudarcot von
maga utn.
23. Fejleszts s tervezs 945
Az ilyen mdszerek kezelhetv s gazdasgoss ttelhez az jrahasznosts mikntjt is
meg kell terveznnk (23.5.1) s nem szabad teljesen elfeledkezni a hatkonysgrl
(23.4.7).
23.4.3. Tervezsi lpsek
Vegyk azt az esetet, amikor egyetlen osztlyt terveznk. Ez ltalban nem j tlet. Fogal-
mak nemlteznek elszigetelten; ms fogalmakkal val sszefggseik hatrozzk meg azo-
kat. Ugyanez rvnyes az osztlyokra is; meghatrozsuk logikailag kapcsold osztlyok
meghatrozsval egytt trtnik. Jellemzen egymssal kapcsolatban lv osztlyok egy
halmazn dolgozunk. Az ilyen halmazt gyakran osztlyknyvtrnak (class library) vagy
sszetevnek (komponens, component) nevezzk. Nha az adott sszetevben lv sszes
osztly egyetlen osztlyhierarchit alkot, nha egyetlen nvtr tagjai, nha viszont csupn
deklarcik ad hoc gyjtemnyt kpezik (24.4).
Az egy sszetevben lv osztlyok halmazt logikai ktelkek egyestik; kzs stlusuk
van vagy azonos szolgltatsokra tmaszkodnak. Az sszetev teht a tervezs, a doku-
mentci, a tulajdonls s az jrafelhasznls egysge. Ez nem jelenti azt, hogy ha valaki-
nek egy sszetevbl csak egy osztlyra van szksge, rtenie s hasznlnia kell az ssze-
tev minden osztlyt vagy be kell tltenie a programjba az sszes osztly kdjt. ppen
ellenkezleg, ltalban arra treksznk, hogy az osztlyoknak a lehet legkevesebb gpi s
emberi erforrsra legyen szksgk. Ahhoz azonban, hogy egy sszetev brmely rszt
hasznlhassuk, rtennk kell az sszetevt sszetart s meghatroz logikai kapcsolato-
kat, (a dokumentciban ezek remlhetleg elgg vilgosak), a felptsben s doku-
mentcijban megtesteslt elveket s stlust, illetve a kzs szolgltatsokat (ha vannak).
Lssuk teht, hogyan tervezhetnk meg egy sszetevt. Mivel ez ltalban komoly kihvst
jelent, megri lpsekre bontani, hogy knnyebb legyen a klnbz rszfeladatokra ssz-
pontostani. Szokott mdon, az sszetevk megtervezsnek sincs egyetlen helyes tja. Itt
csupn egy olyan lpssorozatot mutatok be, amely egyesek szmra mr bevlt:
1. Keressk meg a fogalmakat/osztlyokat s legalapvetbb kapcsolataikat.
2. Finomtsuk az osztlyokat a rajtuk vgezhet mveletek meghatrozsval.
Osztlyozzuk a mveleteket. Klnsen figyeljnk a ltrehozs, a msols
s a megsemmists szksgessgre.
Trekedjnk a minl kisebb mretre, a teljessgre s a knyelemre.
3. Finomtsuk az osztlyokat az sszefggsek meghatrozsval.
gyeljnk a paramterezsre s az rklsre, s hasznljuk ki
a fggsgeket.
Tervezs a C++ segtsgvel 946
4. Hatrozzuk meg a felleteket.
Vlasszuk szt a fggvnyeket privt s vdett mveletekre.
A mveleteket hatrozzuk meg pontosan az adott osztlyokra.
szrevehetjk, hogy ezek is egy ismtld folyamat lpsei. ltalban tbbszr kell rajtuk
vgigmenni, hogy knyelmesen hasznlhat tervet kapjunk, melyet felhasznlhatunk egy
els vagy egy jabb megvalstsnl. Ha az itt lertak szerinti elemezzk a tennivalkat s
ksztjk el az elvont adatbrzolst, az azzal az elnnyel jr, hogy viszonylag knny lesz
az osztlykapcsolatok trendezse a kd megrsa utn is. (Br ez sohasem tl egyszer.)
Ha vgeztnk, elksztjk az osztlyokat s visszatrnk fellvizsglni a tervet annak alap-
jn, amit a megvalsts sorn megtudtunk. A kvetkezkben ezeket a lpseket egyenknt
vesszk sorra.
23.4.3.1. Els lps: keressnk osztlyokat
Keressk meg a fogalmakat/osztlyokat s legalapvetbb kapcsolataikat. A j tervezs kul-
csa a valsg valamely rsznek kzvetlen modellezse vagyis a program fogalmainak
osztlyokra val lekpezse, az osztlyok kzti kapcsolatok brzolsa meghatrozott m-
don (pldul rklssel), s mindezek ismtelt vgrehajtsa klnbz elvonatkoztatsi
szinteken. De hogyan fogjunk hozz a fogalmak megkeresshez? Milyen megkzeltst
clszer kvetni, hogy eldnthessk, mely osztlyokra van szksg?
A legjobb, ha elszr magban a programban nznk szt, ahelyett, hogy a szmtgptu-
ds fogalomzskjban keresglnnk. Hallgassunk valakire, aki a rendszer elkszlte utn
annak szakrt felhasznlja lesz, s valakire, aki a kivltand rendszer kiss elgedetlen
felhasznlja. Figyeljk meg, milyen szavakat hasznlnak.
Gyakran mondjk, hogy a fnevek fognak megfelelni a programhoz szksges osztlyok-
nak s objektumoknak; s ez tbbnyire gy is van. Ezzel azonban semmikppen sincs vge
a trtnetnek. Az igk jelenthetik az objektumokon vgzett mveleteket, a hagyomnyos
(globlis) fggvnyeket, melyek paramtereik rtkei vagy osztlyok alapjn j rtkeket
lltanak el. Az utbbira pldk a fggvnyobjektumok (function object, 18.4) s a m-
dostk (manipulator, 21.4.6). Az olyan igk, mint a bejr vagy a vglegest brzolha-
tk egy bejr (itertor) objektummal, illetve egy adatbzis commit mvelett vgrehajt
objektummal. Mg a mellknevek is gyakran hasznlhat mdon brzolhatk osztlyok-
kal. Vegyk a trolhat, prhuzamosan fut, bejegyzett s lekttt mellkneveket.
Ezek is lehetnek osztlyok, melyek lehetv teszik a terveznek vagy a programoznak,
hogy virtulis bzisosztlyok meghatrozsa ltal kivlassza a ksbb megtervezend osz-
tlyok kvnt jellemzit (15.2.4).
23. Fejleszts s tervezs 947
Nem minden osztly felel meg programszint fogalmaknak. Vannak pldul rendszer-er-
forrsokat vagy a megvalsts fogalmait brzol osztlyok (24.3.1) is. Az is fontos, hogy
elkerljk a rgi rendszer tl kzeli modellezst. Pldul biztosan nem akarunk olyan
rendszert kszteni, mely egy adatbzis kr plve egy kzi rendszer lehetsgeit ut-
nozza s az egynnek csak paprok fizikai tologatsnak irnytst engedi meg.
Az rkls (inheritance) fogalmak kzs tulajdonsgainak brzolsra szolgl. Legfonto-
sabb felhasznlsa a hierarchikus felpts brzolsa az egyes fogalmakat kpvisel oszt-
lyok viselkedse alapjn (1.7, 12.2.6, 24.3.2). Erre nha gy hivatkoznak, mint osztlyo-
zs (classification), st rendszertan (taxonomy). A kzs tulajdonsgokat aktvan keresni
kell. Az ltalnosts s az osztlyozs magas szint tevkenysgek, melyek lesltst ig-
nyelnek, hogy hasznos s tarts eredmnyt adjanak. Egy kzs alapnak egy ltalnosabb
fogalmat kell brzolnia, nem pedig egy hasonl, az brzolshoz esetleg kevesebb adatot
ignylt.
Vegyk szre, hogy az osztlyozst a rendszerben modellezett fogalmak alapjn kell vgez-
ni, nem ms terleteken rvnyes szemszgbl. A matematikban pldul a kr az ellipszis
egy fajtja, de a legtbb programban a krt nem az ellipszisbl s az ellipszist sem a krbl
szrmaztatjk. Azok a gyakran hallhat rvek, hogy azrt, mert a matematikban ez a md-
ja s azrt, mert a kr az ellipszis rszhalmaza, nem meggyzek s ltalban rosszak.
Ennek az az oka, hogy a legtbb programban a kr f tulajdonsga az, hogy kzppontja
van s tvolsga a kerletig rgztett. A kr minden viselkedse (minden mvelete) meg
kell, hogy tartsa ezt a tulajdonsgot (invarins; 24.3.7.1). Msrszt az ellipszist kt fkusz-
pont jellemzi, melyeket sok programban egymstl fggetlenl lehet mdostani. Ha ezek
a fkuszpontok egybeesnek, az ellipszis olyan lesz, mint egy kr, de nem kr, mivel a m-
veletei nem tartjk krnek. A legtbb rendszerben ez a klnbsg gy tkrzdik, hogy
van egy kr s egy ellipszis, amelyeknek vannak mvelethalmazaik, de azok nem egyms
rszhalmazai.
Nem az trtnik, hogy kiagyalunk egy osztlyhalmazt az osztlyok kztti kapcsolatokkal
s felhasznljuk azokat a vgs rendszerben. Inkbb osztlyok s kapcsolatok kezdeti hal-
mazt alkotjuk meg, majd ezeket ismtelten finomtjuk (23.4.3.5), hogy eljussunk egy
olyan osztlykapcsolat-halmazig, mely elgg ltalnos, rugalmas s stabil ahhoz, hogy va-
ldi segtsget nyjtson a rendszer ksbbi megalkotshoz.
A kezdeti f fogalmak/osztlyok felvzolsra a legjobb eszkz egy tbla, finomtsukra pe-
dig az alkalmazsi terlet szakrtivel s nhny bartunkkal folytatott vita, melynek sorn
kialakthatunk egy hasznlhat kezdeti sztrat s fogalmi vzlatot. Kevs ember tudja ezt
egyedl elvgezni. A kiindul osztlyhalmazbl valban hasznlhat osztlyhalmaz kiala-
ktsnak egyik mdja a rendszer utnzsa, ahol a tervezk veszik t az osztlyok szerept.
Tervezs a C++ segtsgvel 948
A nyilvnos vita feltrja a kezdeti tletek hibit, ms megoldsok keressre sztnz s
elsegti a kialakul terv kzs megrtst. A tevkenysg lapokra rott feljegyzsekkel
tmogathat, amelyek ksbb visszakereshetk. Az ilyen krtykat rendszerint CRC kr-
tyknak nevezik (Class, Responsibility, Collaborators, vagyis Osztlyok, Felelssg,
Egyttmkdk, [Wirfs-Brock, 1990]) a rjuk feljegyzett informcik miatt.
A hasznlati eset (use case) a rendszer egy konkrt felhasznlsnak lersa. me a haszn-
lati eset egyszer pldja egy telefonrendszerre: felvesszk a kagylt, trcszunk egy
szmot, a telefon a msik oldalon csenget, a msik oldalon felveszik a kagylt. Ilyen hasz-
nlati esetek halmaznak meghatrozsa hatalmas rtk lehet a fejleszts minden szaka-
szban, a tervezs elejn pedig a hasznlati esetek megkeresse segt, hogy megrtsk, mit
is prblunk pteni. A tervezs alatt nyomon kvethetjk velk a rendszer mkdsi tjt
(pldul CRC krtyk hasznlatval), hogy ellenrizzk, van-e rtelme a felhasznl szem-
pontjbl a rendszer osztlyokkal s objektumokkal val viszonylag statikus lersnak;
a programozs s tesztels alatt pedig lehetsges helyzeteket modellezhetnk velk. Ily
mdon a hasznlati eseteket a rendszer valszersgnek ellenrzsre hasznlhatjuk.
A hasznlati esetek a rendszert (dinamikusan) mkd egysgknt tekintik. Ezrt a terve-
zt abba a csapdba ejthetik, hogy a rendszer rendeltetst tartsa szem eltt, ami elvonja
a figyelmt az osztlyokkal brzolhat, hasznlhat fogalmak keressnek elengedhetet-
len feladattl. Klnsen azok, akiknek a rendszerezett elemzs erssgk, de az objek-
tumorientlt programozsban s tervezsben kevs tapasztalattal rendelkeznek, a haszn-
lati esetek hangslyozsval hajlamosak az eszkzket a clnak alrendelni. A hasznlati
esetek halmaza mg nem tekinthet tervnek. Nem elg a rendszer hasznlatt szem eltt
tartani, gondolni kell annak felptsre is.
A tervezk csapata eredenden hibaval s kltsges ksrlet csapdjba eshet, ha az
sszes hasznlati esetet meg akarja keresni s le akarja rni. Amikor a rendszerhez osztlyo-
kat keresnk, ugyangy eljn az id, amikor ki kell mondani: Ami sok, az sok. Itt az id,
hogy kiprbljuk, amink van s lssuk, mi trtnik. A tovbbi fejleszts sorn csak egy el-
fogadhat osztlyhalmaz s egy elfogadhat hasznlatieset-halmaz hasznlatval kaphatjuk
meg a j rendszer elrshez elengedhetetlen visszajelzst. Persze nehz felismerni, mikor
lljunk meg egy hasznos tevkenysgben, klnsen akkor, ha tudjuk, hogy ksbb vissza
kell trnnk a feladatot befejezni.
Mennyi eset elegend? Erre a krdsre ltalban nem lehet vlaszt adni. Az adott munka so-
rn azonban eljn az id, amikor a rendszer f szolgltatsainak legtbbje mkdik s
a szokatlanabb problmk, illetve a hibakezelsi krdsek j rszt is rintettk. Ekkor ide-
je elvgezni a tervezs s programozs kvetkez lpst.
23. Fejleszts s tervezs 949
Amikor a program felhasznlsi terleteit hasznlati esetek segtsgvel prbljuk felbe-
cslni, hasznos lehet azokat elsdleges s msodlagos hasznlati esetekre sztvlasztani.
Az elsdlegesek a rendszer legltalnosabb, normlis tevkenysgeit, a msodlagosak
a szokatlanabb, illetve a hibakezelssel kapcsolatos tevkenysgeket rjk le. A msodlagos
hasznlati esetre plda a telefonhvs egy vltozata, ahol a hvott lloms kagyljt fel-
emeltk, mert ppen a hv szmt trcszzk. Gyakran mondjk, hogy ha az elsdleges
hasznlati esetek 80%-t s nhny msodlagosat mr kiprbltunk, ideje tovbbmenni, de
mivel elre nem tudhatjuk, mi alkotja az sszes esetet, ez csupn irnyelv. Itt a tapaszta-
lat s a j rzk szmt.
A fogalmak, mveletek s kapcsolatok a programok alkalmazsi terleteibl termszetsze-
ren addnak vagy az osztlyszerkezeten vgzett tovbbi munkbl szletnek. Ezek jelzik,
hogy alapveten rtjk a program mkdst s kpesek vagyunk az alapfogalmak oszt-
lyozsra. Pldul a tzoltkocsi egy tzolt gp, ami egy teheraut, ami egy jrm.
A 23.4.3.2 s 23.4.5 pontok bemutatnak nhny mdszert, amelyekkel az osztlyokra s
osztlyhierarchikra a tovbbfejleszts szndkval nzhetnk.
vakodjunk a rajzos tervezstl! Termszetesen a fejleszts bizonyos szakaszban megkr-
nek majd, hogy mutassuk be a tervet valakinek, s akkor bemutatunk egy sereg diagramot,
melyek az pl rendszer felptst magyarzzk. Ez nagyon hasznos lehet, mert segt,
hogy figyelmnket arra sszpontostsuk, ami a rendszerben fontos, s knytelenek vagyunk
tleteinket msok ltal is rthet szavakkal kifejezni. A bemutat rtkes tervezsi eszkz.
Elksztse azzal a cllal, hogy valban megrtsk azok, akiket rdekel s akik kpesek p-
t brlatot alkotni, j gyakorlat a gondolatok megfogalmazsra s tiszta kifejezsre.
A terv hivatalos bemutatsa azonban veszlyes is lehet, mivel ers a ksrts, hogy az ide-
lis rendszert mutassuk be olyat, amit brcsak meg tudnnk pteni, egy rendszert, melyre
a vezetsg vgyik ahelyett, amivel rendelkeznk, s amit elfogadhat id alatt meg tu-
dunk csinlni. Amikor klnbz megkzeltsek vetlkednek s a dntshozk nem iga-
zn rtik a rszleteket (vagy nem is trdnek azokkal), a bemutatk hazugsgversenny
vlnak, amelyben a leggrandizusabb rendszert bemutat csapat nyeri el a munkt. Ilyen
esetekben a gondolatok vilgos kifejezst gyakran szakzsargon s rvidtsek helyettes-
tik. Ha ilyen bemutatt hallgatunk s klnsen, ha dntshozk vagyunk s fejlesztsi
erforrsokrl rendelkeznk ltfontossg, hogy kpesek legynk klnbsget tenni
vgylom s relis terv kztt. A j bemutatanyag nem garantlja a lert rendszer mins-
gt. Tapasztalatom szerint a vals problmkra sszpontost cgek, amikor eredmnyeik
bemutatsra kerl sor, rvidre fogjk a szt azokhoz kpest, akik kevsb rdekeltek va-
ls rendszerek elksztsben.
Tervezs a C++ segtsgvel 950
Amikor osztlyokkal brzoland fogalmakat keresnk, vegyk szre, hogy a rendszernek
vannak olyan fontos tulajdonsgai is, melyeket nem brzolhatunk osztlyokkal. A megbz-
hatsg, a teljestmny, a tesztelhetsg fontos mrhet rendszertulajdonsgok. Mgsem le-
het mg a legtisztbban objektumokra alapozott rendszer megbzhatsgt sem egy meg-
bzhatsgi objektummal brzolni. A rendszerben mindentt jelenlev tulajdonsgok
meghatrozhatk, megtervezhetk s mrssel igazolhatk; gondot kell fordtani rjuk
minden osztlynl, s tkrzdnik kell az egyes osztlyok s sszetevk tervezsi s meg-
valstsi szablyaiban (23.4.3).
23.4.3.2. Msodik lps: hatrozzuk meg a mveleteket
Finomtsuk az osztlyokat a rajtuk vgezhet mveletek meghatrozsval. Termszetes,
hogy nem lehet az osztlyok keresst elvlasztani attl, hogy kigondoljuk, milyen mve-
leteket kell rajtuk vgezni. Gyakorlati klnbsg van azonban abban, hogy az osztlyok ke-
ressekor a f fogalmakra sszpontostunk s tudatosan httrbe szortjuk a szmtstech-
nikai szempontokat, mg a mveletek meghatrozsakor azt tartjuk szem eltt, hogy teljes
s hasznlhat mvelethalmazt talljunk. ltalban tl nehz egyszerre mindkettre gon-
dolni, klnsen azrt, mert az egymssal kapcsolatban lv osztlyokat egytt kell meg-
tervezni, de ebben is segthetnek a CRC krtyk (23.4.3.1).
Amikor az a krds, milyen szolgltatsokrl gondoskodjunk, tbbfle megkzelts lehet-
sges. n a kvetkez stratgit javaslom:
1. Gondoljuk vgig, hogyan jn ltre, hogyan msoldik (ha egyltaln msol-
dik) s hogyan semmisl meg az osztly egy objektuma.
2. Hatrozzuk meg az osztly ltal ignyelt mveletek minimlis halmazt. ltal-
ban ezek lesznek a tagfggvnyek.
3. Gondoljuk vgig, milyen mveleteket lehetne mg megadni a knyelmesebb je-
lls rdekben. Csak nhny valban fontos mveletet adjunk meg. Ezekbl
lesznek a nem tag segdfggvnyek (10.3.2).
4. Gondoljuk vgig, mely mveleteknek kell virtulisaknak lennik, vagyis olyan
mveleteknek, melyekkel az osztly felletknt szolglhat egy szrmaztatott
osztly ltal adott megvalsts szmra.
5. Gondoljuk vgig, milyen kzs elnevezsi mdszert s szolgltatsokat biztost-
hatunk egy sszetev minden osztlyra vonatkozan.
Vilgos, hogy ezek a minimalizmus elvei. Sokkal knnyebb minden hasznosnak gondolt
fggvnyt megadni s minden mveletet virtuliss tenni. Minl tbb fggvnynk van
azonban, annl valsznbb, hogy nem fogjk hasznlni azokat, s hogy a fls fggv-
23. Fejleszts s tervezs 951
nyek korltozni fogjk a rendszer megvalstst, tovbbi fejldst. Nevezetesen azok
a fggvnyek, melyek egy osztly egy objektuma llapotnak valamely rszt kzvetlenl
olvassk vagy rjk, gyakran egyedi megvalstst kvetelnek az osztlytl s komoly mr-
tkben korltozzk az jratervezs lehetsgt. Az ilyen fggvnyek az elvont brzolst
a fogalom szintjrl a megvalsts szintjre szlltjk le. A fggvnyek hozzadsa a prog-
ramoznak is tbb munkt jelent s a terveznek is, a kvetkez jratervezskor. Sokkal
knnyebb egy fggvnyt megadni, amikor vilgoss vlt, hogy szksg van r, mint eltvo-
ltani azt, ha koloncc vlik.
Annak oka, hogy egy fggvnyrl vilgosan el kell dnteni, hogy virtulis legyen vagy sem,
s ez nem alaprtelmezett viselkeds vagy megvalstsi rszlet, az, hogy egy fggvny vir-
tuliss ttele alapjaiban befolysolja osztlynak hasznlatt s az osztly kapcsolatait ms
osztlyokkal. Egy olyan osztly objektumainak elrendezse, melyben akr csak egyetlen
virtulis fggvny is van, jelents mrtkben klnbzik a C vagy Fortran nyelvek objektu-
maitl. A virtulis fggvnyt is tartalmaz osztlyok felletet nyjthatnak a ksbb
definiland osztlyok szmra, de egy virtulis fggvny egyben fggst is jelent azoktl
(24.3.2.1).
Vegyk szre, hogy a minimalizmus inkbb tbb, mint kevesebb munkt kvetel a terveztl.
Amikor kivlasztjuk a mveleteket, fontos, hogy inkbb arra sszpontostsunk, mit kell ten-
ni, nem pedig arra, hogyan tegyk. Vagyis inkbb a kvnt viselkedsre, mint a megvals-
ts krdseire figyeljnk.
Nha hasznos az osztlyok mveleteit aszerint osztlyozni, hogyan hasznljk az objektu-
mok bels llapott:
Alapvet mveletek: konstruktorok, destruktorok s msol opertorok.
Lekrdezsek: mveletek, melyek nem mdostjk egy objektum llapott.
Mdostk: mveletek, melyek mdostjk egy objektum llapott.
Konverzik: mveletek, melyek ms tpus objektumot hoznak ltre annak az
objektumnak az rtke (llapota) alapjn, amelyre alkalmazzuk ket.
Bejrk: opertorok, melyek a tartalmazott objektumsorozatokhoz val hozzf-
rst, illetve azok hasznlatt teszik lehetv.
A fentiek nem egymst kizr kategrik. Egy bejr pldul lehet egyben lekrdez vagy
mdost is. Ezek a kategrik egyszeren egy osztlyozst jelentenek, mely segthet az
osztlyfelletek megtervezsben. Termszetesen lehetsgesek ms osztlyozs is. Az ilyen
osztlyozsok klnsen hasznosak az sszetevkn belli osztlyok egysgessgnek
fenntartsban.
Tervezs a C++ segtsgvel 952
A C++ a lekrdezk s mdostk megklnbztetst a konstans s nem konstans tag-
fggvnyekkel segti. Ugyangy a konstruktorok, destruktorok, msol opertorok s kon-
verzis fggvnyek is kzvetlenl tmogatottak.
23.4.3.3. Harmadik lps: hatrozzuk meg az sszefggseket
Finomtsuk az osztlyokat az sszefggsek meghatrozsval. A klnfle sszefggse-
ket a 24.3 trgyalja. A tervezssel kapcsolatosan vizsgland f sszefggsek a param-
terezsi, rklsi (inheritance) s hasznlati (use) kapcsolatok. Mindegyiknl meg kell vizs-
glni, mit jelent, hogy az osztly a rendszer egyetlen tulajdonsgrt felels. Felelsnek len-
ni biztos, hogy nem azt jelenti, hogy az osztly az sszes adatot maga kell, hogy tartalmaz-
za, vagy hogy az sszes szksges mveletet kzvetlenl tagfggvnyei kell, hogy vgre-
hajtsk. Ellenkezleg: az, hogy minden osztlynak egyetlen felelssgi terlete van, azt biz-
tostja, hogy egy osztly munkja jobbra abbl ll, hogy a krseket egy msik osztlyhoz
irnytja, mely az adott rszfeladatrt felels. Legynk azonban vatosak, mert e mdszer
tlzott mrtk hasznlata kis hatkonysg s ttekinthetetlen tervekhez vezet, azltal,
hogy olyan mrtkben elburjnzanak az osztlyok s objektumok, hogy ms munka nem
trtnik, mint a krsek tovbbtsa. Amit az adott helyen s idben meg lehet tenni, azt
meg kell tenni.
Annak szksgessge, hogy az rklsi s hasznlati kapcsolatokat a tervezsi szakaszban
vizsgljuk (s nem csak a megvalsts sorn), kzvetlenl kvetkezik abbl, hogy az osz-
tlyokat fogalmak brzolsra hasznljuk. Ez pedig azt is magban foglalja, hogy nem az
egyedi osztly, hanem a komponens (23.4.3, 24.4) a tervezs valdi egysge.
A paramterezs mely gyakran sablonok (template) hasznlathoz vezet egy md arra,
hogy a mgttes fggseket nyilvnvalv tegyk, gy, hogy j fogalmak hozzadsa nl-
kl tbb lehetsges megolds legyen. Gyakran vlaszthatunk, meghagyunk-e valamit kr-
nyezettl fgg egy rklsi fa gaknt brzolt elemknt, vagy inkbb paramtert
hasznlunk (24.4.1).
23.4.3.4. Negyedik lps: hatrozzuk meg a felleteket
Hatrozzuk meg a felleteket. A privt fggvnyekkel a tervezsi szakaszban nem kell fog-
lalkozni. Azt, hogy ekkor a megvalsts mely krdseit vesszk figyelembe, legjobb a har-
madik lpsben, a fggsi viszonyok meghatrozsakor eldnteni. Vilgosabban kifejezve:
vegyk alapszablynak, hogy ha egy osztlynak nem lehetsges legalbb kt jelentsen el-
tr megvalstsa, akkor valszn, hogy ezzel az osztllyal valami nincs rendben, s va-
ljban lczott megvalstssal (implementation), nem pedig igazi fogalommal llunk
23. Fejleszts s tervezs 953
szemben. Sok esetben az Elgg fggetlen ennek az osztlynak a fellete a megvalsts
mdjtl? krds j megkzeltse, ha megvizsgljuk, alkalmazhat-e az osztlyra valami-
lyen takarkos kirtkelsi mdszer (lusta kirtkels, lazy evaluation).
Vegyk szre, hogy a nyilvnos (public) alaposztlyok s bart fggvnyek (friend) az osz-
tly nyilvnos felletnek rszei (lsd mg 11.5 s 24.4.2). Kifizetd lehet, ha kln fe-
lletekrl gondoskodunk az rkls, illetve az ltalnos felhasznlk szmra, azltal, hogy
kln protected s public felleteket adunk meg.
Ez az a lps, amelyben a paramterek pontos tpust meghatrozzuk. Az az idelis, ha
annyi statikus, programszint tpusokat tartalmaz felletnk van, amennyi csak lehetsges
(24.2.3 s 24.4.2).
Amikor a felleteket (interface) meghatrozzuk, vigyzni kell azoknl az osztlyoknl, ahol
a mveletek ltszlag tbb fogalmi szintet tmogatnak. A File osztly egyes tagfggvnyei
pldul File_descriptor (fjller) tpus paramtereket kaphatnak, msok pedig karakter-
lnc paramtereket, melyek fjlneveket jellnek. A File_descriptor mveletek ms fogalmi
szinten dolgoznak, mint a fjlnv-mveletek, gy elgondolkozhatunk azon, vajon ugyanab-
ba az osztlyba tartoznak-e. Lehet, hogy jobb lenne, ha kt fjlosztlyunk lenne, az egyik
a fjller fogalmnak tmogatsra, a msik a fjlnv fogalomhoz. ltalban egy osztly
minden mvelete ugyanazt a fogalmi szintet kell, hogy tmogassa. Ha nem gy van, t kell
szervezni az osztlyt s a vele kapcsolatban lv osztlyokat.
23.4.3.5. Osztlyhierarchik jraszervezse
Az els s a harmadik lpsben megvizsgltuk az osztlyokat s osztlyhierarchikat, hogy
lssuk, megfelelen kiszolgljk-e ignyeinket. A vlasz ltalban nem s ilyenkor t kell
szerveznnk, osztlyainkat, hogy tkletestsk a szerkezetet, a tervet vagy ppen a meg-
valsts mdjt. Az osztlyhierarchia legkznsgesebb tszervezsi mdszere kt osztly
kzs rsznek kiemelse egy j osztlyba, illetve az osztly kt j osztlyra bontsa. Mind-
kt esetben hrom osztly lesz az eredmny: egy bzisosztly (base class) s kt szrmaz-
tatott osztly (derived class). Mikor kell gy jraszervezni? Mi jelzi azt, hogy egy ilyen jra-
szervezs hasznos lehet?
Sajnos ezekre a krdsekre nincsenek egyszer, ltalnos vlaszok. Ez nem igazn megle-
p, mert amirl beszlnk, nem apr rszletek, hanem egy rendszer alapfogalmainak m-
dostsa. Az alapvet br nem egyszer feladat az osztlyok kzs tulajdonsgainak
megkeresse s a kzs rsz kiemelse. Azt, hogy mi szmt kzsnek, nem lehet ponto-
san meghatrozni, de a kzs rsznek a fogalmak kzti, s nem csak a megvalsts knyel-
mt szolgl kzssget kell tkrznie. Arrl, hogy kt vagy tbb osztly olyan kzs tu-
Tervezs a C++ segtsgvel 954
lajdonsgokkal rendelkezik, melyeket egy bzisosztlyban foglalhatunk ssze, a kzs
hasznlati minta, a mvelethalmazok hasonlsga, illetve a hasonl megvalsts rulko-
dik, valamint az, hogy ezek az osztlyok gyakran egytt merlnek fel a tervezsi vitk so-
rn. Ezzel szemben egy osztly valsznleg kt msikra bonthat, ha mveleteinek rsz-
halmazai eltr hasznlati mintjak, ha ezen rszhalmazok az brzols kln rszhalma-
zaihoz frnek hozz, vagy ha az osztly egymssal nyilvnvalan nem kapcsolatos tervez-
si vitk sorn merl fel. A rokon osztlyok halmaznak sablonba (template) foglalsa rend-
szerezett mdot ad a szksges alternetvk biztostsra (24.4.1).
Az osztlyok s fogalmak kzti szoros kapcsolatok miatt az osztlyhierarchik szervezsi
problmi gyakran mint az osztlyok elnevezsi problmi merlnek fel. Ha az osztlyne-
vek nehzkesen hangzanak vagy az osztlyhierarchikbl kvetkez osztlyozsok tls-
gosan bonyolultak, valsznleg itt az alkalom, hogy tkletestsk a hierarchikat. Vle-
mnyem szerint kt ember sokkal jobban tud egy osztlyhierarchit elemezni, mint egy. Ha
trtnetesen nincs valaki, akivel egy tervet meg tudnnk vitatni, hasznos lehet, ha runk egy
bevezet tervismertett, amelyben az osztlyok neveit hasznljuk.
A terv egyik legfontosabb clkitzse olyan felletek (interface) biztostsa, melyek a vl-
tozsok sorn llandak maradnak (23.4.2). Ezt gyakran gy rhetjk el legjobban, ha egy
osztlyt, amelytl szmos osztly s fggvny fgg, olyan absztrakt osztlly tesznk, mely
csak nagyon ltalnos mveleteket biztost. A rszletesebb mveletek jobb, ha egyedi cl
szrmaztatott osztlyokhoz kapcsoldnak, melyektl kevesebb osztly s fggvny fgg
kzvetlenl. Vilgosabban: minl tbb osztly fgg egy osztlytl, annl ltalnosabbnak
kell lennie.
Ers a ksrts, hogy egy sokak ltal hasznlt osztlyhoz mveleteket (s adatokat) tegynk
hozz. Ezt gyakran gy tekintik, mint ami egy osztlyt hasznlhatbb s (tovbbi) vltoz-
tatst kevsb ignylv tesz. Pedig az eredmny csak annyi, hogy a fellet meghzik (k-
vr fellet, 24.4.3) s olyan adattagjai lesznek, melyek szmos gyengn kapcsold szol-
gltatst tmogatnak. Ez ismt azzal jr, hogy az osztlyt mdostani kell, amikor az ltala
tmogatott osztlyok kzl akr csak egy is jelentsen megvltozik. Ez viszont magval
vonja olyan felhasznli osztlyok s szrmaztatott osztlyok mdostst is, melyek nyil-
vnvalan nincsenek vele kapcsolatban. Ahelyett, hogy bonyoltannk egy, a tervben kz-
ponti szerepet jtsz osztlyt, rendszerint ltalnosnak s elvontnak kellene hagynunk. Ha
szksges, az egyedi szolgltatsokat szrmaztatott osztlyokknt kell biztostani. (Pldkat
lsd [Martin,1995]).
Ez a gondolatfzr absztrakt (abstract) osztlyok hierarchijhoz vezet, ahol a gykrhez
kzeli osztlyok a legltalnosabbak, s ezektl fgg a legtbb ms osztly s fggvny.
A levl osztlyok a legegyedibbek s csak nagyon kevs kdrsz fgg kzvetlenl tlk.
Pldaknt emlthetnnk az lval_box hierarchia vgs vltozatt (12.4.3, 12.4.4).
23. Fejleszts s tervezs 955
23.4.3.6. Modellek hasznlata
Amikor cikket rok, egy megfelel modellt prblok kvetni. Vagyis ahelyett, hogy azonnal
elkezdem a gpelst, hasonl tmj cikkeket keresek, hogy lssam, tallok-e olyat, amely
a cikkem kezdeti mintjaknt szolglhat. Ha az ltalam vlasztott modell egy sajt, rokon
tmrl szl cikk, mg arra is kpes vagyok, hogy helykn hagyok szvegrszeket, m-
sokat szksg szerint mdostok s j informcit csak ott teszek hozz, ahol mondandm
logikja megkvnja. Ez a knyv pldul ilyen mdon rdott, az els s msodik kiads
alapjn. Ennek a mdszernek szlssges esete egy levlsablon hasznlata. Ekkor egysze-
ren egy nevet rok be s esetleg hozzrok mg nhny sort, hogy a levl szemlyre sz-
l legyen. Az ilyen leveleket lnyegben gy rom meg, hogy a sablont csak az alapmo-
delltl eltr rszekkel egsztem ki.
A ltez rendszerek ilyen, j rendszerek modelljeiknt val felhasznlsa ltalnos eljrs
az alkot trekvsek minden formjnl. Amikor csak lehetsges, a tervezs s programo-
zs elz munkra kell, hogy alapozdjon. Ez korltozza a tervez szabadsgt, de lehet-
v teszi, hogy figyelmt egyszerre csak nhny krdsre sszpontostsa. Egy nagy projekt
tiszta lappal indtsa frisst hatssal lehet, de knnyen mrgezv is vlhat, mert a terve-
zsi mdszerek kztti kvlygst eredmnyezheti. Ennek ellenre, ha van modellnk, az
nem jelent korltozst s nem kveteli meg, hogy szolgai mdon kvessk; egyszeren
megszabadtja a tervezt attl, hogy egy tervet egyszerre csak egy szempontbl kzelthes-
sen meg.
Vegyk szre, hogy a modellek hasznlata elkerlhetetlen, mivel minden terv tervezinek
tapasztalataibl tevdik ssze. Egy adott modell birtokban a modellvlaszts tudatos elha-
trozss vlik, felttelezseink megalapozottak, a hasznlt kifejezsek kre pedig megha-
trozott lesz, a tervnek lesz egy kezdeti vza s n a valsznsge, hogy a tervezk meg
tudnak egyezni egy kzs megkzeltsben.
Termszetesen a kiindul modell kivlasztsa nmagban is fontos tervezsi dnts, amely
gyakran csak a lehetsges modellek keresse s az alternatvk gondos kirtkelse utn
hozhat meg. Tovbb sok esetben egy modell csak akkor felel meg, ha megrtjk, hogy
az elkpzelseknek egy j konkrt alkalmazshoz val igaztsa szmos mdostst ig-
nyel. A programtervezs nem knny, gy minden megszerezhet segtsgre szksgnk
van. Nem szabad visszautastanunk a modellek hasznlatt, csak a rossz utnzs erltet-
st. Az utnzs a hzelgs legszintbb mdja, s a modellek s elz munkk sztnzs-
knt val felhasznlsa a tulajdon s a msols jognak trvnyes hatrain bell az in-
novatv munka minden terleten elfogadhat mdszer: ami j volt Shakespeare-nek, az j
neknk is. Vannak, akik a modellek ilyen hasznlatt a tervezsben terv-jrahasznosts-
nak nevezik.
Tervezs a C++ segtsgvel 956
Az ltalnosan hasznlt, tbb programban elfordul elemek feljegyzse az ltaluk meg-
oldott problma s hasznlatuk feltteleinek lersval egytt kzenfekv tlet; feltve,
hogy esznkbe jut. Az ilyen ltalnos s hasznos tervezsi elemek lersra ltalban
a minta (pattern) szt hasznljuk, a mintk dokumentlsnak s hasznlatnak pedig ko-
moly irodalma van (pl. [Gamma, 1994] s [Coplien, 1995]).
Clszer, hogy a tervez ismerje az adott alkalmazsi terlet npszer mintit. Mint progra-
moz, olyan mintkat rszestek elnyben, melyekhez valamilyen kd is tartozik, ami pl-
daknt tekinthet. Mint a legtbb ember, egy ltalnos tletet (ebben az esetben mintt) ak-
kor tartok a legjobbnak, ha segtsgemre van egy konkrt plda is (ebben az esetben egy,
a minta hasznlatt illusztrl kdrszlet). Akik gyakran hasznlnak mintkat, szakzsargont
hasznlnak egyms kztt, ami sajnos privt nyelvv vlhat, ami kvlllk szmra lehe-
tetlenn teszi a megrtst. Mint mindig, elengedhetetlen a megfelel kommunikci bizto-
stsa a projekt klnbz rszeiben rsztvevk (23.3) s ltalban a tervez s programo-
z kzssgek kztt.
Minden sikeres nagy rendszer egy valamivel kisebb mkd rendszer ttervezse. Nem is-
merek kivtelt e szably all. Mg leginkbb olyan tervek jutnak eszembe, melyek vekig
szenvedtek zrzavaros llapotban s nagy kltsggel, vekkel a tervezett befejezsi dtum
utn esetleg sikeresek lettek. Az ilyen projektek eredmnye szndkolatlanul s persze el-
ismers nlkl elszr mkdskptelen rendszer lett, amit ksbb mkdkpess tet-
tek, vgl jraterveztek olyan rendszerr, mely megkzelti az eredeti clkitzseket. Ebbl
kvetkezik, hogy ostobasg egy nagy rendszert jonnan megtervezni s megpteni, pon-
tosan kvetve a legfrissebb elveket. Minl nagyobb s nagyratrbb a rendszer, melyet
megcloztunk, annl fontosabb, hogy legyen egy modellnk, melybl dolgozunk. Egy
nagy rendszerhez az egyetlen valban elfogadhat modell egy valamivel kisebb, mkd
rokon rendszer.
23.4.4. Ksrletezs s elemzs
Egy ignyes fejleszts kezdetn nem tudjuk a mdjt, hogyan alkossuk meg legjobban
a rendszert. Gyakran mg azt sem tudjuk pontosan, mit kellene a rendszernek tennie, mi-
vel a konkrtumok csak akkor tisztulnak ki, amikor a rendszert ptjk, teszteljk s hasz-
nljuk. Hogyan kapjuk meg a teljes rendszer felptse eltt a szksges informcikat ah-
hoz, hogy megrtsk, melyek a jelents tervezsi dntsek s felbecsljk azok buktatit?
23. Fejleszts s tervezs 957
Ksrleteket folytatunk. Mihelyt van mit, elemezzk a terveket s azok megvalstst is.
A leggyakoribb s legfontosabb a lehetsgek megvitatsa. A tervezs ltalban kzssgi
tevkenysg, melyben a tervek bemutatk s vitk ltal fejldnek. A legfontosabb tervez-
eszkz a tbla; enlkl az embrionlis llapot tervezsi fogalmak nem tudnak kifejldni s
elterjedni a tervezk s programozk kztt.
A ksrlet legnpszerbb formja egy prototpus ptse, vagyis a rendszer vagy egy rsz-
nek lekicsinytett vltozatban val ltrehozsa. A prototpus teljestmnyre nzve nincsenek
szigor elrsok, ltalban elegend a gpi s programozsi krnyezetbeli erforrs, a ter-
vezk s programozk pedig igyekeznek klnsen kpzettnek, tapasztaltnak s motivlt-
nak ltszani. Arrl van sz, hogy egy vltozatot a lehet leggyorsabban futtatni lehessen,
hogy lehetv tegyk a tervezsi s megvalstsi mdszerek vlasztknak feldertst.
Ez a megkzelts nagyon sikeres lehet, ha jl csinljuk, de lehet rgy a pongyolasgra is.
Az a problma, hogy a prototpus slypontja a lehetsgek feldertstl eltoldhat a lehe-
t leghamarabb valamilyen mkdkpes rendszert kapni fel. Ez knnyen a prototpus
bels felptse irnti rdektelensghez (vgl is csak prototpusrl van sz) s a tervez-
si erfesztsek elhanyagolshoz vezet, mert a prototpus gyors elksztse ezekkel szem-
ben elnyt lvez. Az a bkken, hogy az ily mdon elksztett vltozatban sokszor nyoma
sincs az erforrsok helyes felhasznlsnak vagy a mdosthatsg lehetsgnek, mikz-
ben egy majdnem teljes rendszer illzijt adja. Szinte trvnyszer, hogy a prototpus
nem rendelkezik azzal a bels felptssel, hatkonysggal s ttekinthetsggel, mely
megengedn, hogy rajta keresztl a vals hasznlatot felmrhessk. Kvetkezskppen
egy prototpus, melybl majdnem termk lesz, elszvja azt az idt s energit, amit a va-
ldi termkre lehetett volna fordtani. Mind a fejlesztk, mind a vezetk szmra ksrtst
jelent, hogy a prototpusbl termket csinljanak s a teljestmny megtervezst elha-
lasszk a kvetkez kiadsig. A prototpusksztsnek ez a helytelen hasznlata tagadsa
mindennek, amirt a tervezs ltezik.
Rokon problma, hogy a prototpus fejleszti beleszerethetnek az eszkzeikbe. Elfelejthe-
tik, hogy az ltaluk lvezett knyelem kltsgeit egy valdi rendszer nem mindig engedhe-
ti meg magnak, s az a korltok nlkli szabadsg, melyet kis kutatcsoportjuk nyjtott,
nemigen tarthat fenn egy nagyobb kzssgben, mely szoros s egymstl fgg ha-
tridk szerint dolgozik.
Msrszrl, a prototpusok felbecslhetetlen rtkek. Vegyk pldul egy felhasznli fe-
llet tervezst. Ebben az esetben a rendszer a felhasznlval kzvetlen klcsnhatsban
nem lev rsznek bels felptse tnyleg lnyegtelen, s a prototpus hasznlatn kvl
nincs ms lehetsges md, hogy tapasztalatokat szerezznk arrl, mi a vlemnye a fel-
hasznlknak a rendszer klsejvel s hasznlatval kapcsolatban. De pldaknt vehetjk
Tervezs a C++ segtsgvel 958
az ellenkezjt is, egy olyan prototpust, melyet szigoran egy program bels mkds-
nek tanulmnyozsra terveztek. Itt a felhasznli fellet lehet kezdetleges lehetsg sze-
rint a valdi felhasznlk helyett szimulltakkal.
A prototpuskszts a ksrletezs egy mdja. Eredmnyei pedig azok a megltsok kelle-
nek, hogy legyenek, melyeket nem maga a prototpus, hanem annak ptse hoz magval.
Tulajdonkppen a prototpus legfbb ismrveknt a tkletlensget kellene meghatrozni,
hogy vilgos legyen, ez csupn ksrleti eszkz s nem vlhat termkk nagymrtk jra-
tervezs nlkl. Ha tkletlen prototpusunk van, az segt, hogy a ksrletre sszpontost-
sunk s a lehet legkisebbre cskkenti annak veszlyt, hogy a prototpusbl termk
legyen, valamint megsznteti azt a ksrtst is, hogy a termk tervezst tl szorosan a pro-
totpusra alapozzuk, elfelejtve vagy elhanyagolva annak eredend korltait. A prototpus
hasznlat utn eldoband.
Vannak olyan ksrleti mdszerek is, melyeket a prototpuskszts helyett alkalmazhatunk.
Ahol ezek hasznlhatk, gyakran elnyben is rszestjk ket, mert szigorbbak s keve-
sebb tervezi idt s rendszer-erforrst ignyelnek. Ilyenek pldul a matematikai model-
lek s a klnfle szimultorok. Tulajdonkppen fel is vzolhatunk egy folyamatot,
a matematikai modellektl az egyre rszletesebb szimulcikon, a prototpusokon, s
a rszleges megvalstson t a teljes rendszerig.
Ez ahhoz az tlethez vezet, hogy egy rendszert a kezdeti tervbl s megvalstsbl tbb-
szri jratervezssel s jbli elksztssel finomtsunk. Ez az idelis mdszer, de nagyon
nagy ignyeket tmaszthat a tervezi s fejleszti eszkzkkel szemben. A megkzelts
azzal a kockzattal is jr, hogy tl sok kd kszl a kezdeti dntsek alapjn, gy egy jobb
tervet nem lehet megvalstani. gy ez a stratgia egyelre csak kis s kzepes mret prog-
ramok ksztsnl mkdik jl, melyekben nem valszn, hogy az tfog tervek kln-
sebben mdosulnnak, illetve a programok els kiadsa utni jratervezsekre s jbli
megvalstsokra, ahol elkerlhetetlen az ilyen megkzelts.
A tervezsi lehetsgek ttekintsre szolgl ksrleteken kvl egy terv vagy megvals-
ts elemzse nmagban is tleteket adhat. A legnagyobb segtsg pldul az osztlyok
kzti klnbz sszefggsek (24.3) tanulmnyozsa lehet, de nem elhanyagolhatk
a hagyomnyos eszkzk sem, mint a hvsi fk (call graphs) megrajzolsa, a teljestmny-
mrsek stb.
Ne feledjk, hogy a fogalmak meghatrozsa (az elemzsi szakasz kimenete) s a terve-
zs sorn ppgy elkvethetnk hibkat, mint a megvalsts folyamn. Valjban mg
tbbet is, mivel ezen tevkenysgek eredmnye kevsb konkrt, kevsb pontosan meg-
hatrozott, nem vgrehajthat s ltalban nem tmogatjk olyan kifinomult eszkzk,
23. Fejleszts s tervezs 959
mint amilyenek a megvalsts elemzshez s ellenrzshez hasznlhatk. Ha a tervezs
kifejezsre hasznlt nyelvet vagy jellsmdot formlisabb tesszk, valamilyen mrtkig
ilyen eszkzt adhatunk a tervez kezbe, ezt azonban nem tehetjk annak az rn, hogy
szegnyebb tesszk a megvalstsra hasznlt programozsi nyelvet (24.3.1). Ugyanak-
kor egy formlis jellsmd maga is problmk forrsa lehet, pldul ha a hasznlt jells-
rendszer nem illik jl arra a gyakorlati problmra, melyre alkalmazzuk; amikor a formai
kvetelmnyek szigora tllpi a rsztvev tervezk s programozk matematikai felk-
szltsgt s rettsgt; vagy amikor a formlis lers elszakad a lerand rendszertl.
A tervezs eredenden ki van tve a hibknak s nehz tmogatni hatkony eszkzkkel.
Ezrt nlklzhetetlen a tapasztalat s a visszajelzs. Kvetkezskppen alapveten hibs
a programfejlesztst egyenes vonal folyamatknt tekinteni, mely az elemzssel kezddik s
a tesztelssel vgzdik. Figyelmet kell fordtani az ismtelt tervezsre s megvalstsra,
hogy a fejleszts klnbz szakaszaiban elegend visszajelzsnk legyen a tapasztaltakrl.
23.4.5. Tesztels
Az a program, melyet nem teszteltek, nem tekinthet mkdnek. Az eszmnykp, hogy
egy programot gy ksztsnk el, hogy mr az els alkalommal mkdjn, a legegyszerbb
programokat kivve elrhetetlen. Trekednnk kell erre, de nem szabad ostobn azt hin-
nnk, hogy tesztelni knny.
A Hogyan teszteljnk? olyan krds, melyre nem lehet csak gy ltalban vlaszolni.
A Mikor teszteljnk? krdsre azonban van ltalnos vlasz: olyan hamar s olyan gyak-
ran, amennyire csak lehetsges. A tesztelsi mdszert clszer mr a tervezs s megvals-
ts rszeknt, de legalbb azokkal prhuzamosan kidolgozni. Mihelyt van egy fut
rendszer, el kell kezdeni a tesztelst. A komoly tesztels elhalasztsa a befejezs utnig
a hatridk csszst s /vagy hibs kiadst eredmnyezhet.
Ha csak lehetsges, a rendszert gy kell megtervezni, hogy viszonylag knnyen tesztelhe-
t legyen. Az ellenrzst gyakran bele lehet tervezni a rendszerbe. Nha ezt nem tesszk,
attl val flelmnkben, hogy a futsi idej tesztels rontja a hatkonysgot, vagy hogy az
ellenrzshez szksges redundns elemek tlsgosan nagy adatszerkezeteket eredm-
nyeznek. Az ilyen flelem rendszerint alaptalan, mert a legtbb tesztkd szksg esetn le-
vlaszthat a tnyleges kdrl, mieltt a rendszert tadjuk. Az assertion-k (24.3.7.2) hasz-
nlata nha hasznos lehet.
Maguknl a teszteknl is fontosabb, hogy a rendszer olyan felpts legyen, ami elfogad-
hat eslyt ad arra, hogy sajt magunkat s felhasznlinkat/fogyasztinkat meggyzzk
Tervezs a C++ segtsgvel 960
arrl, hogy a hibkat statikus ellenrzs, statikus elemzs s tesztels egyttes hasznlat-
val ki tudjuk kszblni. Ahol a hibatrs biztostsra kidolgoztak valamilyen mdszert
(14.9), ott ltalban a tesztels is bepthet a teljes tervbe.
Ha a tervezsi szakaszban a tesztelsi krdsekkel egyltaln nem szmoltunk, akkor az el-
lenrzssel, a hatridk betartsval s a program mdostsval problmink lesznek.
Az osztlyfelletek s az osztlyfggsek rendszerint j kiindulpontot jelentenek a teszte-
lsi mdszer kidolgozshoz(24.3, 24.4.2).
Rendszerint nehz meghatrozni, mennyi tesztels elegend. A tl kevs tesztels azonban
ltalnosabb problma, mint a tl sok. Az, hogy a tervezshez s megvalstshoz viszo-
nytva pontosan mennyi erforrst kell a tesztelsre kijellni, termszetesen fgg a rendszer
termszettl s a mdszerektl, melyeket ptshez hasznlunk. Irnyelvknt javasolha-
tom azonban, hogy idben, erfesztsben s tehetsgben tbb erforrst fordtsunk
a rendszer tesztelsre, mint els vltozatnak elksztsre. A tesztelsnek olyan probl-
mkra kell sszpontostania, melyeknek vgzetes kvetkezmnyei lennnek s olyanokra,
melyek gyakran fordulnnak el.
23.4.6. A programok karbantartsa
A program-karbantarts (software maintenance) helytelen elnevezs. A karbantarts sz
flrevezet hasonlatot sugall a hardverrel. A programoknak nincs szksgk olajozsra,
nincsenek mozg alkatrszeik, amelyek kopnak, s nincsenek repedseik, ahol a vz ssze-
gylik s rozsdsodst okoz. A szoftver pontosan lemsolhat s percek alatt nagy tvol-
sgra tvihet. A szoftver nem hardver.
Azok a tevkenysgek, melyeket program-karbantarts nven emlegetnk, valjban az j-
ratervezs s az jbli megvalsts, teht a szoksos programfejlesztsi folyamathoz tartoz-
nak. Amikor a tervezsben hangslyosan szerepel a rugalmassg, bvthetsg s hordoz-
hatsg, kzvetlenl a program fenntartsval kapcsolatos hibk hagyomnyos forrsaira
sszpontostunk.
A tesztelshez hasonlan a karbantarts sem lehet utlagos megfontols vagy a fejleszts f
sodrtl elvlasztott tevkenysg. Klnsen fontos, hogy a projektben vgig ugyanazok
a szemlyek vegyenek rszt, vagy legalbbis biztostsuk a folytonossgot . Nem knny
ugyanis karbantartst vgeztetni egy olyan j (s ltalban kisebb tapasztalat) emberek-
bl ll csoporttal, akik az eredeti tervezkkel s programozkkal nem llnak kapcsolat-
ban. Ha mgis msoknak kell tadni a munkt, hangslyt kell fektetni arra, hogy az j em-
berek tisztban legyenek a rendszer felptsvel s clkitzseivel. Ha a karbantart
23. Fejleszts s tervezs 961
szemlyzet magra marad abban, hogy kitallja a rendszer szerkezett, vagy hogy a rend-
szer sszetevinek cljt megvalstsukbl kvetkeztesse ki, a rendszer a helyi foltozga-
ts hatsra gyorsan degenerldik. A dokumentci nem segt, mert az inkbb a rszle-
tek lersra val, nem pedig arra, hogy az j munkatrsakat segtse a f tletek s elvek
megrtsben.
23.4.7. Hatkonysg
Donald Knuth megfigyelte, hogy az id eltti optimalizls a gykere minden rossznak.
Nmelyek tlsgosan megtanultk ezt a leckt s minden hatkonysgi trekvst rossznak
tekintenek, pedig a hatkonysgra vgig gyelni kell a tervezs s megvalsts sorn. Ez
azonban nem azt jelenti, hogy a terveznek minden aprsgot hatkonny kell tennie,
hanem csak azt, hogy az elsrend hatkonysgi krdsekkel foglalkoznia kell.
A hatkonysg elrshez a legjobb mdszer egy vilgos s egyszer terv ksztse. Csak
egy ilyen terv maradhat viszonylag lland a projekt lettartama alatt s szolglhat a telje-
stmny behangolsnak alapjul. Lnyeges, hogy elkerljk a nagy projekteket sjt me-
galomnit. Tl gyakori, hogy a programozk ha szksg lenne r tulajdonsgokat
(23.4.3.2, 23.5.3) ptenek be, majd a sallangok tmogatsval megktszerezik, meg-
ngyszerezik a rendszerek mrett s futsi idejt. Ennl is rosszabb, hogy az ilyen tlfino-
mtott rendszereket gyakran szksgtelenl nehz elemezni, gy nehz lesz megklnbz-
tetni az elkerlhet tlterhelst az elkerlhetetlentl. Ez pedig mg az alapvet elemzst s
optimalizlst is akadlyozza. Az optimum belltsa elemzs s teljestmnymrs eredm-
nye kell, hogy legyen, nem tallomra piszmogs a kddal. A nagy rendszerek esetben
a tervezi vagy programozi intuci klnsen megbzhatatlan tmutat a hatkonysgi
krdsekben.
Fontos, hogy elkerljk az eredenden rossz hatkonysg szerkezeteket, illetve az olya-
nokat, melyeknek elfogadhat teljestmnyszintre val optimalizlsa sok idt s gyess-
get kvn. Hasonlkppen fontos, hogy cskkentsk az eredenden hordozhatatlan szer-
kezetek s eszkzk hasznlatt, mert ez arra krhoztatja a projektet, hogy rgebbi (kisebb
teljestmny) vagy ppen drgbb gpeken fusson.
Tervezs a C++ segtsgvel 962
23.5. Vezets
Feltve, hogy legalbb valamilyen rtelme van, a legtbb ember azt teszi, amire rveszik.
Nevezetesen, ha egy projekttel sszefggsben bizonyos mkdsi mdokat djazunk s
msokat bntetnk, csak kivteles programozk s tervezk fogjk kockztatni a karrierj-
ket, hogy azt tegyk, amit helyesnek tartanak, a vezets ellenvlemnyvel, kznyvel s
brokrcijval szemben?.
3
Ebbl kvetkezik, hogy a cgnek kell, hogy legyen egy jutalma-
zsi rendszere, mely megfelel az ltala megllaptott tervezsi s programozsi clkitz-
seknek. Mindazonltal gyakran nem ez a helyzet: a programozsi stlusban nagy vltozs
csak a tervezsi stlus megfelel mdostsval rhet el s ahhoz, hogy hatsos legyen, l-
talban mindkett a vezetsi stlus megvltoztatst ignyli. A szellemi s szervezeti tehe-
tetlensg knnyen eredmnyezhet olyan helyi vltoztatsokat, melyeket nem tmogatnak
a sikert biztost globlis vltoztatsok. Meglehetsen jellemz plda egy objektum-
orientlt programozst tmogat nyelvre (mondjuk a C++-ra) val tlls, a nyelv lehets-
geit kihasznl megfelel tervezsi stratgiabeli vltoztats nlkl, vagy ppen fordtva,
objektumorientlt tervezsre vlts azt tmogat programozsi nyelv bevezetse nlkl.
23.5.1. jrahasznosts
A kdok s tervek jrahasznostst gyakran emlegetik, mint f okot egy j programozsi
nyelv vagy tervezsi mdszer bevezetsre. A legtbb szervezet azonban azokat az egy-
neket tmogatja, akik a melegvz jrafeltallst vlasztjk. Pldul ha egy programoz tel-
jestmnyt kdsorokban mrik; vajon kis programokat fog rni a standard knyvtrakra t-
maszkodva, bevtelnek s esetleg sttusznak rovsra? s ha egy vezett a csoportjban
lv emberek szmval arnyosan fizetnek, vajon fel fogja-e hasznlni a msok ltal ksz-
tett programot, ha helyette a sajt csoportjba jabb embereket vehet fel? Vagy ha egy cg-
nek odatlnek egy kormnyszerzdst, ahol a profit a fejlesztsi kltsgeknek egy rgz-
tett szzalka, vajon ez a cg kzvetetten cskkenteni fogja-e a profitjt a leghatkonyabb
fejleszteszkzk hasznlatval? Az jrahasznostst nehz jutalmazni, de ha a vezets nem
tall mdot az sztnzsre s jutalmazsra, nem lesz jrahasznosts. Az jrahasznosts-
nak ms vonatkozsai is vannak. Akkor hasznlhatjuk fel valaki ms programjt, ha:
1. Mkdik: ahhoz, hogy jrahasznosthat legyen, a programnak elbb hasznl-
hatnak kell bizonyulnia.
23. Fejleszts s tervezs 963
3
Az olyan szervezetnek, mely a programozival gy bnik, mint az iditkkal, hamarosan olyan programozi
lesznek, akik csak iditaknt akarnak s kpesek cselekedni.
2. ttekinthet: fontos a program szerkezete, a megjegyzsek, a dokumentci s
az oktatanyag.
3. Egytt ltezhet egy olyan programmal, melyet nem kimondottan a vele val
egyttmkdsre rtak.
4. Tmogatott (vagy magunk akarjuk tmogatni; n ltalban nem akarom).
5. Gazdasgos (megoszthatom-e ms felhasznlkkal a fejlesztsi s karbantartsi
kltsgeket?).
6. Meg lehet tallni.
Ehhez hozztehetjk, hogy egy sszetev nem jrahasznosthat, amg nincs valaki, aki
jra felhasznlta. Egy sszetev valamely krnyezetbe beillesztse jellemzen mkds-
nek finomtsval, viselkedsnek ltalnostsval s ms programokkal val egyttmk-
dsi kpessgnek javtsval jr egytt. Amg legalbb egyszer el nem vgeztk ezt
a feladatot, mg a leggondosabban megtervezett s elksztett sszetevknek is lehetnek
szndkolatlan s vratlan egyenetlensgei.
Az a tapasztalatom, hogy az jrahasznostsnak csak akkor vannak meg a szksges feltt-
elei, ha valaki kzs gynek tekinti, hogy ilyen munkamegoszts mkdjn. Egy kis cso-
portban ez ltalban azt jelenti, hogy valaki tervezetten vagy vletlenl a kzs knyv-
trak s dokumentci gazdja lesz, egy nagyobb cgnl pedig azt, hogy megbznak egy
csoportot vagy osztlyt, hogy gyjtse ssze, ksztse el, dokumentlja, npszerstse s
tartsa karban a tbb csoport ltal felhasznlt programsszetevket. Nem lehet tlbecsl-
ni a szabvnyos sszetevkkel foglalkoz csoport fontossgt. Vegyk szre, hogy egy
program azt a kzssget tkrzi, amely ltrehozta. Ha a kzssgnek nincs egyttmk-
dst s munkamegosztst elsegt s jutalmaz rendszere, ritkn lesz egyttmkds s
munkamegoszts. A szabvnyos sszetevkkel foglalkoz csoport aktvan kell, hogy np-
szerstse ezeket az sszetevket. Ebbl kvetkezik, hogy a jl megrt dokumentci nl-
klzhetetlen, de nem elegend. A csoportnak ezen kvl gondoskodnia kell oktatanya-
gokrl s minden ms informcirl is, ami lehetv teszi, hogy a remnybeli felhasznl
megtalljon egy sszetevt s megrtse, mirt segthet az neki. Ennek kvetkezmnye,
hogy a piackutatssal s az oktatssal kapcsolatos tevkenysgeket ennek a csoportnak kell
vllalnia.
Ha lehetsges, e csoport tagjainak szorosan egytt kell mkdnik az alkalmazsfejlesztk-
kel. Csak gy tudnak megfelelen rteslni a felhasznlk ignyeirl s felhvni a figyelmet
az alkalmakra, amikor egyes sszetevket klnbz programok kzsen hasznlhatnak.
Ezzel amellett rvelek, hogy az ilyen csoportoknak legyen vlemny-nyilvntsi s tancs-
adi szerepe, s hogy mkdjenek a bels kapcsolatok a csoportba bejv s onnan kime-
n informci tvitelre.
Tervezs a C++ segtsgvel 964
Az komponens-csoport sikert az gyfelek sikervel kell mrni. Ha a sikert egyszeren
azzal mrjk, hogy mennyi eszkzt s szolgltatst tudnak elfogadtatni a fejleszt cgekkel,
az ilyen csoport kereskedelmi gynkk alacsonyodik s llandan vltoz hbortok el-
mozdtja lesz.
Nem minden kdnak kell jrahasznosthatnak lennie, ez nem mindenhat tulajdonsg.
Ha azt mondjuk, hogy egy sszetev jrahasznosthat, ez azt jelenti, hogy felhasznlsa
bizonyos kereteken bell kevs vagy semmi munkt nem ignyel. A legtbb esetben egy
ms vzba trtn tkltztets jelents munkval jr. Ebbl a szempontbl az jrahaszno-
sts ersen emlkeztet a hordozhatsgra (vagyis ms rendszerre val tltetsre). Fontos
megjegyezni, hogy az jrahasznosts az ezt clz tervezsnek, az sszetevk tapasztalat
alapjn val finomtsnak s annak a szndkos erfesztsnek az eredmnye, hogy mr
ltez sszetevket keressnk (jbli) felhasznlsra. Az jrahasznosts nem az egyes
nyelvi tulajdonsgok vagy kdolsi mdszerek vletlenszer hasznlatbl keletkezik,
mintegy csodaknt. A C++ olyan szolgltatsai, mint az osztlyok, a virtulis fggvnyek s
a sablonok lehetv teszik, hogy a tervezs kifejezsmdja az jrahasznostst knnyebb
(s ezltal valsznbb) tegye, de nmagukban nem biztostjk azt.
23.5.2. Mret s egyensly
Az nll vagy kzssgben dolgoz programozt sokszor arra knyszertik, hogy dolgt
a megfelel mdon vgezze. Intzmnyes felllsban ez gyakran gy hangzik: a megfe-
lel eljrsok kifejlesztse s szigor kvetse szksges. Mindkt esetben elsknt a j-
zan sz eshet ldozatul annak a kvnsgnak, hogy mindenron javtsunk a dolgok vgz-
snek mdjn. Sajnos, ha egyszer hinyzik a jzan sz, az akaratlanul okozhat kroknak
nincs hatra.
Vegyk a fejlesztsi folyamat 23.4-ben felsorolt szakaszait s a 23.4.3-ban felsorolt terve-
zsi lpseket. E lpsekbl viszonylag knnyen kidolgozhat egy megfelel tervezsi
mdszer, ahol minden szakasz pontosan krlhatrolt, meghatrozott bemenete s kime-
nete van, valamint rendelkezik az ezek kifejezshez szksges, rszben formlis jells-
mddal. Ellenrz listkkal biztosthatjuk, hogy a tervezsi mdszer kvetkezetesen tmo-
gassa a nagy szm eljrsi s jellsbeli szably rvnyre juttatst, s hogy ehhez
eszkzket lehessen kifejleszteni. Tovbb, az sszefggsek 24.3-ban bemutatott oszt-
lyozst nzve valaki kinyilatkoztathatn, hogy bizonyos kapcsolatok jk, msok pedig
rosszak, s elemz eszkzket adna annak biztostsra, hogy ezeket az rtktleteket az
adott projekten bell egysgesen alkalmazzk. A programfejlesztsi folyamat e megers-
tst tklyre fejlesztend, valaki lerhatn a dokumentls szablyait (belertve a helyes-
rsi, nyelvtani s gpelsi szablyokat) s a kd ltalnos kllemre vonatkoz elrsokat
23. Fejleszts s tervezs 965
(belertve azt is, mely nyelvi tulajdonsgokat s knyvtrakat szabad s melyeket nem sza-
bad hasznlni, hol hasznlhat behzs, hogyan lehet elnevezni a fggvnyeket, vltoz-
kat s tpusokat stb.).
Ezek kztt akad olyan, amely elsegtheti a sikert. Termszetesen ostobasg lenne egy
esetleg tzmilli kdsorbl ll rendszer tervezst elkezdeni melyet tbb szz ember fej-
lesztene s tbb ezer ember tartana karban s tmogatna tz vagy mg tbb vig egy be-
csletesen kidolgozott s meglehetsen szilrd vz nlkl.
Szerencsre a legtbb program nem ebbe a kategriba esik. Ha azonban egyszer elfogad-
tuk, hogy egy ilyen tervezsi mdszer vagy egy ilyen kdolsi s dokumentlsi szabvny-
gyjtemnyhez val ragaszkods a helyes t, ktelez lesz annak ltalnos s minden
rszletre kiterjed hasznlata. Ez kis programoknl nevetsges korltozsokhoz s tbblet-
munkhoz vezethet: a halads s siker mrtkt jelent hatkony munka helyett aktatolo-
gats s rlaptltgets folyik majd. Ha ez trtnik, az igazi tervezk s programozk t-
vozni fognak s brokratk lpnek a helykbe.
Ha egy kzssgen bell mr elfordult, hogy egy tervezsi mdszert ilyen nevetsgesen
rosszul alkalmaztak, annak sikertelensge rgyet szolgltat arra, hogy elkerljenek szinte
minden szablyozst a fejlesztsi folyamatban. Ez viszont ppen olyan zrzavarhoz s bal-
sikerekhez vezet, mint amilyeneket az j tervezsi mdszer kialaktsval meg akartunk
elzni.
Az igazi problma megtallni az egyenslyt a megfelel fok szablyozs s az adott prog-
ram fejlesztshez szksges szabadsg kztt. Ne higgyk, hogy erre a problmra
knny lesz megoldst tallni. Lnyegben minden megkzelts csak kis projekteknl m-
kdik, pontosabban ami mg rosszabb, hiszen a rendszer rosszul tervezett s kegyetlen
a belevont egynekkel szemben nagy projekteknl is, feltve, hogy szemrmetlenl sok
idt s pnzt akarunk elpocskolni a problmra.
Minden programfejleszti munka kulcsproblmja, hogyan tartsuk magunkat a terv erede-
ti szempontjaihoz. Ez a problma a mrettel hatvnyozottan nvekszik. Csak egy nll
programoz vagy egy kis csoport tudja szilrdan kzbentartani s betartani egy nagyobb
munka tfog clkitzseit. A legtbb embernek olyan sok idt kell tltenie rszprojektek-
kel, technikai rszletkrdsekkel, napi adminisztrcival stb., hogy az tfog clok
knnyen feledsbe merlnek vagy alrendeldnek helyi s kzvetlenebb cloknak. Az is
sikertelensghez vezet, ha nincs egy egyn vagy egy csoport, melynek kizrlag az a fel-
adata, hogy fenntartsa a terv srtetlensgt. Recept a siker ellen, ha egy ilyen egynnek
vagy csoportnak nem engedjk, hogy a munka egszre hasson.
Tervezs a C++ segtsgvel 966
Egy sszer hossz tv clkitzs hinya krosabb, mint brmilyen egyedi tulajdonsg hi-
nya. Egy ilyen tfog clkitzs megfogalmazsa, szben tartsa, az tfog tervdokumen-
tci megrsa, a f fogalmak ismertetse, s ltalban msokat segteni, hogy szben tart-
sk az tfog clt, kis szm egyn feladata kell, hogy legyen.
23.5.3. Egynek
Az itt lertak szerinti tervezs jutalom a tehetsges tervezk s programozk szmra, vi-
szont a sikerhez elengedhetetlen, hogy ezeket a tervezket s programozkat megtalljuk.
A vezetk gyakran elfelejtik, hogy a szervezetek egynekbl llnak. Npszer vlemny,
hogy a programozk egyformk s csereszabatosak. Ez tveszme, mely tnkreteheti a k-
zssget, azltal, hogy elzavarja a leghatkonyabb egyneket s mg a megmaradtakat is
arra tli, hogy jval a kpessgeik alatti szinten dolgozzanak. Az egynek csak akkor fel-
cserlhetk, ha nem engedjk, hogy hasznostsk azokat a kpessgeiket, melyek a krd-
ses feladat ltal megkvetelt minimum fl emelik ket. A csereszabatossg elve teht nem
humnus s eredenden pazarl.
A legtbb programozsi teljestmnymrs sztnzi a pazarlst s kihagyja a szmtsbl az
egyni hozzjrulst. A legkzenfekvbb plda az a viszonylag elterjedt gyakorlat, hogy
a haladst a megrt kdsorok, a dokumentci lapjainak, vagy az elvgzett tesztek szm-
val mrik. Az ilyen szmok jl mutatnak diagramokon, de a valsghoz vajmi kevs kzk
van. Pldul, ha a termelkenysget a kdsorok szmval mrjk, akkor egy sszetev si-
keres jrahasznostsa negatv programozi teljestmnynek minslhet. Egy nagy prog-
ramrsz jratervezsnl a legjobb elvek sikeres alkalmazsa ugyanilyen hats.
A teljestett munka minsgt sokkal nehezebb mrni, mint a kimenet mennyisgt, az
egyneket s csoportokat viszont a munka minsge alapjn kell jutalmazni, nem durva
mennyisgmrs alapjn. Sajnos a minsg gyakorlati mrse legjobb tudsom sze-
rint gyerekcipben jr. Ezenkvl azon teljestmny-elrsok, melyek a munknak csak
egy szakaszra vonatkoznak, hajlamosak befolysolni a fejlesztst. Az emberek alkalmaz-
kodnak a helyi hatridkhz s az egyni s csoportteljestmnyt a kvtk ltal elrthoz
igaztjk. Ezt kzvetlenl a rendszer tfog psge s teljestmnye szenvedi meg. Ha egy
hatridt pldul az eltvoltott vagy a megmaradt programhibkkal hatroznak meg, belt-
hatjuk, hogy a hatrid csak a futsi idej teljestmny figyelmen kvl hagysval vagy
a rendszer futtatshoz szksges hardver-erforrsok optimalizlsnak mellzsvel lesz
tarthat. Ellenben ha csak a futsi idej teljestmnyt mrjk, a hibaarny biztosan nveked-
ni fog, amikor a fejlesztk a rendszert a futsi teljestmnyt mr programokra igyekeznek
optimalizlni. Az rtelmes minsgi elrsok hinya nagy kvetelmnyeket tmaszt a ve-
23. Fejleszts s tervezs 967
zetk szakrtelmvel szemben, de az alternatva az, hogy a halads helyett egyes tallom-
ra kivlasztott tevkenysgeket fognak jutalmazni. Ne feledjk, hogy a vezetk is emberek.
Nekik is legalbb annyi oktatsra van szksgk, mint az ltaluk vezetetteknek. Mint
a programfejleszts ms terletein, itt is hosszabb tvra kell terveznnk. Lnyegben lehe-
tetlen megtlni egy egyn teljestmnyt egyetlen v munkja alapjn. A kvetkezetesen s
hossz tvon vezetett feljegyzsek azonban megbzhat elrejelzst biztostanak az egyes
munkatrsak teljestmnyvel kapcsolatban s hasznos segtsget adnak a kzvetlen mlt
rtkelshez. Az ilyen feljegyzsek figyelmen kvl hagysa ahogy akkor trtnik, ami-
kor az egyneket pusztn cserlhet fogaskerknek tekintik a szervezet gpezet-
ben a vezetket kiszolgltatja a flrevezet mennyisgi mrseknek.
A hossz tvra tervezs egyik kvetkezmnye, hogy az egynek (mind a fejlesztk, mind
a vezetk) az ignyesebb s rdekesebb feladatokhoz hosszabb idt ignyelnek. Ez elveszi
a kedvt az llsvltoztatknak ppgy, mint a karriercsinls miatt munkakrt vltozta-
tknak. Trekedni kell arra, hogy kicsi legyen a fluktuci mind a mszakiak, mind a kulcs-
fontossg vezetk krben. Egyetlen vezet sem lehet sikeres a kulcsemberekkel val
egyetrts s friss, a trgyra vonatkoz technikai tuds nlkl, de ugyangy egyetlen terve-
zkbl s fejlesztkbl ll csoport sem lehet hossz tvon sikeres alkalmas vezetk tmo-
gatsa nlkl s anlkl, hogy alapjaiban rtsk azt a krnyezetet, melyben dolgoznak.
Ahol szksges az innovci, az idsebb szakemberek, elemzk, tervezk, programozk
stb. ltfontossg szerepet jtszanak az j eljrsok bevezetsben. k azok, akiknek j
mdszereket kell megtanulniuk s sok esetben el kell felejtenik a rgi szoksokat, ami
nem knny, hiszen ltalban komoly erfesztseket tettek a rgi munkamdszerek elsa-
jttsra, radsul az e mdszerekkel elrt sikereikre, szakmai tekintlykre tmaszkod-
nak. Sok szakmai vezetvel ugyanez a helyzet.
Termszetesen az ilyen egynek gyakran flnek a vltozstl. Ezrt a vltozssal jr prob-
lmkat tlbecslik s nehezen ismerik el a rgi mdszerekkel kapcsolatos problmkat.
Ugyanilyen termszetes, hogy a vltozs mellett rvelk hajlamosak tlbecslni az j md-
szerek elnyeit s albecslni a vltozssal jr problmkat. A kt csoportnak kommuni-
klnia kell: meg kell tanulniuk ugyanazon a nyelven beszlni s segtenik kell egymsnak,
hogy az tmenetre megfelel mdszert dolgozhassanak ki. Ha ez nem trtnik meg, a szer-
vezet megbnul s a legjobb kpessg egynek tvoznak mindkt csoportbl. Mindkt
csoportnak emlkeznie kell arra, hogy a legsikeresebb regek gyakran azok, akik tavaly
az ifj titnok voltak. Ha adott az esly arra, hogy megalzkods nlkl tanuljanak,
a tapasztaltabb programozk s tervezk lehetnek a legsikeresebb s legnagyobb betekin-
tssel rendelkez hvei a vltoztatsnak. Egszsges szkepticizmusuk, a felhasznlk isme-
rete s a szervezet mkdsvel kapcsolatban szerzett tapasztalataik felbecslhetetlenl
rtkesek lehetnek. Az azonnali s gykeres vltozsok javasli szre kell vegyk, hogy az
Tervezs a C++ segtsgvel 968
tmenet, amely az j eljrsok fokozatos elsajttsval jr, tbbnyire elengedhetetlen.
Azoknak viszont, akik nem kvnnak vltoztatni, olyan terleteket kell keresnik, ahol nem
szksges vltoztatni, nem pedig dhs htvdharcot vvni olyan terleteken, ahol az j k-
vetelmnyek mr jelentsen megvltoztattk a siker feltteleit.
23.5.4. Hibrid tervezs
j munkamdszerek bevezetse egy cgnl fradsgos lehet. Jelents trst okozhat mind
a szervezetben, mind az egynekben. Egy vratlan vltozs, mely a rgi iskola hatkony
s tapasztalt tagjait egyik naprl a msikra az j iskola zldfl joncaiv vltoztatja, lta-
lban elfogadhatatlan. Vltozsok nlkl azonban ritkn rhetnk el nagy nyeresget, a je-
lents vltozsok pedig tbbnyire kockzattal jrnak.
A C++-t gy terveztk, hogy a kockzatot a lehet legkisebbre cskkentse, azltal, hogy
lehetv teszi a mdszerek fokozatos elsajttst. Br vilgos, hogy a C++ hasznlatnak
legnagyobb elnyei az elvont adatbrzolsbl s az objektumorientlt szemlletbl add-
nak, nem biztos, hogy e nyeresgeket a leggyorsabban a mlttal val gykeres szaktssal
lehet elrni. Egyszer-egyszer keresztlvihet az ilyen egyrtelm szakts, de gyakoribb,
hogy a javts vgyt egy idre flre kell tennnk, hogy meggondoljuk, hogyan kezeljk az
tmenetet. A kvetkezket kell figyelembe vennnk:
A tervezknek s programozknak id kell az j szakismeretek megszerzshez.
Az j kdnak egytt kell mkdnie a rgi kddal.
A rgi kdot karban kell tartani (gyakran a vgtelensgig).
A ltez terveket s programokat be kell fejezni (idre).
Az j eljrsokat tmogat eszkzket be kell vezetni az adott krnyezetbe.
Ezek a tnyezk termszetesen hibrid (kevert) tervezsi stlushoz vezetnek mg ott is,
ahol nmelyik terveznek nem ez a szndka. Az els kt pontot knny alulrtkelni.
Azltal, hogy szmos programozsi irnyelvet tmogat, a C++ vltozatos mdon tmogatja
a nyelv hasznlatnak fokozatos bevezetst:
A programozk produktvak maradhatnak a C++ tanulsa kzben.
A C++ jelents elnyket nyjt egy eszkzszegny krnyezetben.
A C++ programrszek jl egyttmkdnek a C-ben vagy ms hagyomnyos
nyelven rt kddal.
A C++-nak jelents C-kompatibilis rszhalmaza van.
23. Fejleszts s tervezs 969
Az alaptlet az, hogy a programozk egy hagyomnyos nyelvrl gy trhetnek t a C++-ra,
hogy a nyelvre ttrve elszr mg megtartjk a hagyomnyos (eljrskzpont) progra-
mozsi stlust, azutn hasznlni kezdik az elvont adatbrzols mdszereit, vgl amikor
mr elsajttottk a nyelvet s a hozz tartoz eszkzk hasznlatt ttrnek az objektum-
orientlt (object-oriented) s az ltalnostott programozsra (generic programming). Egy
jl tervezett knyvtrat sokkal knnyebb hasznlni, mint megtervezni s elkszteni, gy
egy kezd mr az elrehalads korai szakaszaiban is rszeslhet az elvont brzols hasz-
nlatnak elnyeibl.
Az objektumorientlt tervezst s programozst, valamint a C++ fokozatosan trtn meg-
tanulst tmogatjk azok a szolgltatsok, melyekkel a C++ kdot keverhetjk olyan
nyelveken rt kddal, melyek nem tmogatjk a C++ elvont adatbrzolsi s objektum-
orientlt programozsi fogalmait (24.2.1). Sok fellet eljrskzpont maradhat, mivel
nincs kzvetlen haszna, ha valamit bonyolultabb tesznk. Sok kulcsfontossg knyvtr-
nl az tltetst mr elvgezte a knyvtr ltrehozja, gy a C++ programoznak nem kell
tudnia, mi a tnyleges megvalsts nyelve. A C-ben vagy hasonl nyelven rt knyvtrak
hasznlata az jrahasznosts elsdleges s kezdetben legfontosabb formja a C++-ban.
A kvetkez lps melyet csak akkor kell elvgezni, amikor tnylegesen szksg van a ki-
finomultabb eljrsokra a C, Fortran vagy hasonl nyelven rt szolgltatsok osztlyok for-
mjban val tlalsa, az adatszerkezetek s fggvnyek C++ nyelv felletosztlyokba
zrsa ltal. Egy egyszer plda a jelents bvtsre az eljrs s adatszerkezet szintjrl
az elvont adatbrzols szintjre a 11.12 string osztlya. Itt a C karakterlnc-brzolst s
szabvnyos karakterlnc-fggvnyeit hasznljuk fel egy sokkal egyszerbben hasznlhat
karakterlnc-tpus ltrehozsra.
Hasonl mdszer hasznlhat egy beptett vagy egyedi tpusnak egy osztlyhierarchiba
illesztsre (23.5.1). Ez lehetv teszi, hogy a C++-ra kszlt terveket az elvont adatbr-
zols s az osztlyhierarchik hasznlathoz tovbbfejlesszk mg olyan nyelveken rt kd
jelenltben is, ahonnan hinyoznak ezek a fogalmak, st azzal a megszortssal is, hogy az
eredmnyl kapott kd eljrskzpont nyelvekbl is meghvhat legyen.
Tervezs a C++ segtsgvel 970
23.6. Jegyzetek
Ez a fejezet csak rintette a programozs tervezsi s vezetsi krdseit A tovbbi tanulm-
nyokat segtend sszegyjtttnk egy rvid irodalomjegyzket. Rszletesebbet [Booch,
1994] alatt tallunk.
[Anderson, 1990] Bruce Anderson s Sanjiv Gossain: An Iterative Design Model for Reusable Object-
Oriented Software. Proc. OOPSLA90. Ottawa, Canada. Egy tervez s jratervez
modell lersa, pldkkal s a tapasztalatok trgyalsval.
[Booch, 1994] Grady Booch: Object-Oriented Analysis and Design with Applications.
Benjamin/Cummings. 1994. ISBN 0-8053-5340-2. Rszletes lerst tartalmaz a terve-
zsrl, s egy grafikai jellsmddal tmogatott tervezsi mdrl. Szmos nagyobb
tervezsi plda ll rendelkezsre C++ nyelven. Kivl knyv, melybl e fejezet is
sokat mertett. Az e fejezetben rintett krdsek j rszt nagyobb mlysgben
trgyalja.
[Booch, 1996] Grady Booch: Object Solutions. Benjamin/Cummings. 1996. ISBN 0-8053-0594-7.
Az objektumkzpont rendszerek fejlesztst a vezets szemszgbl vizsglja.
Szmos C++ pldt tartalmaz.
[Brooks, 1982] Fred Brooks: The Mythical man Month. Addison-Wesley. 1982. Ezt a knyvet min-
denkinek pr vente jra el kellene olvasnia! Ints a nagykpsg ellen. Technika-
ilag kiss eljrt felette az id, de az emberi, szervezeti s mretezsi vonatkozsai
idtllak. 1997-ben kibvtve jra kiadtk. ISBN 1-201-83595-9.
[Brooks, 1987] Fred Brooks: No Silver Bullet. IEEE Computer. Vol 20. No. 4. 1987 prilis. A nagy-
bani szoftverfejleszts megkzeltseinek sszegzse, a rgta aktulis figyelmez-
tetssel: nincsenek csodaszerek (nincs ezstgoly).
[Coplien, 1995] James O. Coplien s Douglas C. Schmidt (szerk.): Pattern Languages of Program
Design. Addison-Wesley. 1995. ISBN 1-201-60734-4.
[De Marco, 1987] T. DeMarco s T. Lister: Peopleware. Dorset House Publishing Co. 1987. Azon ritka
knyvek egyike, melyek az egynnek a programfejlesztsben betlttt szerepvel
foglalkoznak. Vezetknek ktelez, kellemes esti olvasmny, szmos ostoba hiba
ellenszert tartalmazza.
[Gamma, 1994] Eric Gamma s msok: Design Patterns. Addison-Wesley. 1994. ISBN 0-201-63361-
2. Gyakorlati katalgus, mely rugalmas s jrahasznosthat programok ksztsi
mdszereit tartalmazza, bonyolultabb, jl kifejtett pldkkal. Szmos C++ pldt
tartalmaz.
[Jacobson, 1992] Ivar Jacobson s msok: Object-Oriented Software Engineering. Addison-Wesley.
1992. ISBN 0-201-54435-0. Alapos s gyakorlati lers, amely hasznlati esetek (use
case, 23.4.3.1) alkalmazsval rja le a szoftverfejlesztst ipari krnyezetben. Fl-
rerti a C++ nyelvet, 10 vvel ezeltti llapotval lerva.
23. Fejleszts s tervezs 971
[Kerr, 1987] Ron Kerr: A Materialistic View of the Software Engineering Analogy. SIGPLAN
Notices, 1987 mrcius. Az ebben s a kvetkez fejezetekben hasznlt hasonlatok
nagyban ptenek e cikkre s a Ron ltal tartott bemutatkra, illetve vele folytatott
beszlgetsekre.
[Liskov, 1987] Barbara Liskov: Data Abstraction and Hierarchy. Proc. OOPSLA87 (Fggelk).
Orlando, Florida. Az rklds szerepe az elvont adatbrzolsban. Megjegyzen-
d, hogy a C++ szmos eszkzt biztost a felvetett problmk tbbsgnek megol-
dsra (24.3.4).
[Martin, 1995] Robert C. Martin: Designing Object-Oriented C++ Applications Using the Booch
Method. Prentice-Hall. 1995. ISBN 0-13-203837-4. Rendszerezetten mutatja be, ho-
gyan jutunk el a problmtl a C++ kdig. Lehetsges tervezsi mdokat s a kz-
tk val dnts elveit ismerteti. A tbbi tervezssel foglalkoz knyvnl gyakorla-
tiasabb s konkrtabb. Szmos C++ pldt tartalmaz.
[Meyer, 1988] Bertrand Meyer: Object Oriented Software Construction. Prentice Hall. 1988. Az 1-64.
s 323-334. oldalakon j bevezetst ad az objektumkzpont programozs s terve-
zs egy nzetrl, szmos hasznlhat gyakorlati tanccsal. A knyv maradk rsze
az Eiffel nyelvet rja le. Hajlamos az Eiffelt s az ltalnos elveket sszekeverni.
[Parkinson, 1957] C. N. Parkinson: Parkinsons Law and other Studies in Administration. Houghton
Mifflin. Boston. 1957. Az egyik leghumorosabb s legpontosabb lers, melyet a b-
rokrcirl rtak.
[Shlaer, 1988] S. Shlaer s S. J. Mellor: Object-Oriented Systems Analysis s Object Lifecycles.
Yourdon Press. ISBN 0-13-629023-X s 0-13-629940-7. Az elemzs, tervezs s
programozs egy olyan szemllett mutatja be, mely jelentsen eltr az itt bemuta-
tottl s a C++ ltal tmogatottl.
[Snyder, 1986] Alan Snyder: Encapsulation and Inheritance in Object-Oriented Programming
Languages. Proc. OOPSLA86. Portland, Oregon. Valsznleg a betokozs
(enkapszulci) s rklds kapcsolatnak els j lersa. A tbbszrs rkl-
dst ugyancsak trgyalja.
[Wirfs-Brock, 1990] Rebecca Wirfs-Brock, Brian Wilkerson s Lauren Wiener: Designing Object-
Oriented Software. Prentice Hall. 1990. Szerepmintkon alapul emberkzpont
tervezsi mdszertant r le CRC krtyk hasznlatval. A szveg (taln a mdszer-
tan is) rszrehajl a Smalltalk irnyban.
Tervezs a C++ segtsgvel 972
23.7. Tancsok
[1] Legynk tisztban vele, mit akarunk elrni. 23.3.
[2] Tartsuk szben, hogy a programfejleszts emberi tevkenysg. 23.2, 23.5.3.
[3] Hasonlat ltal bizonytani mts. 23.2.
[4] Legyenek meghatrozott, kzzelfoghat cljaink. 23.4.
[5] Ne prbljunk emberi problmkat technikai megoldsokkal orvosolni. 23.4.
[6] Tekintsnk hosszabb tvra a tervezsben s az emberekkel val bnsmdban.
23.4.1, 23.5.3.
[7] Nincs mretbeli als hatra azon programoknak, melyeknl rtelme van a k-
dols eltti tervezsnek. 23.2.
[8] sztnzzk a visszajelzst. 23.4.
[9] Ne cserljk ssze a tevkenysget a haladssal. 23.3, 23.4.
[10] Ne ltalnostsunk jobban, mint szksges, mint amivel kapcsolatban kzvetlen
tapasztalataink vannak, s ami tesztelhet. 23.4.1, 23.4.2.
[11] brzoljuk a fogalmakat osztlyokknt. 23.4.2, 23.4.3.1.
[12] A program bizonyos tulajdonsgait nem szabad osztlyknt brzolni. 23.4.3.1.
[13] A fogalmak kztti hierarchikus kapcsolatokat brzoljuk osztlyhierarchik-
knt. 23.4.3.1.
[14] Aktvan keressk a kzs vonsokat az alkalmazs s a megvalsts fogalmai-
ban s az eredmnyl kapott ltalnosabb fogalmakat brzoljuk bzisoszt-
lyokknt. 23.4.3.1, 23.4.3.5.
[15] Mshol alkalmazott osztlyozsok nem szksgszeren hasznlhatak egy
program rklsi modelljben. 23.4.3.1.
[16] Az osztlyhierarchikat a viselkeds s a nem vltoz (invarins) tulajdonsgok
alapjn ptsk fel. 23.4.3.1, 23.4.3.5, 24.3.7.1.
[17] Vizsgljuk meg a hasznlati eseteket. 23.4.3.1.
[18] Hasznljunk CRC krtykat, ha szksges. 23.4.3.1.
[19] Modellknt, sztnzsknt s kiindulpontknt hasznljunk ltez rendszere-
ket. 23.4.3.6.
[20] vakodjunk a rajzos tervezstl. 23.4.3.1.
[21] Dobjuk el a prototpust, mieltt teherr vlik. 23.4.4.
[22] Szmoljunk a vltoztats lehetsgvel, sszpontostsunk a rugalmassgra, a b-
vthetsgre, a hordozhatsgra s az jrahasznosthatsgra. 23.34.2.
[23] A kzppontba az sszetevk tervezst helyezzk. 23.4.3.
[24] A felletek az egyes fogalmakat egyetlen elvonatkoztatsi szinten brzoljk.
23.4.3.1.
[25] A vltoztats kszbn tartsuk szem eltt a stabilitst. 23.4.2.
23. Fejleszts s tervezs 973
[26] A gyakran hasznlt felletek legyenek kicsik, ltalnosak s elvontak, hogy az
eredeti terv lnyegt ne kelljen mdostani. 23.4.3.2, 23.4.3.5..
[27] Trekedjnk a minimalizmusra. Ne hasznljunk ha szksg lenne r tulajdon-
sgokat. 23.4.3.2.
[28] Mindig vizsgljuk meg egy osztly ms lehetsges brzolsait. Ha nincs kzen-
fekv alternatva, az osztly valsznleg nem kpvisel tiszta fogalmat. 23.4.3.4.
[29] Tbbszr is vizsgljuk fell s finomtsuk mind a tervezst, mind a megvalsts
mdjt. 23.4, 23.4.3.
[30] Hasznljuk az elrhet legjobb eszkzket a tesztelshez s a problma, a terv,
illetve a megvalsts elemzshez. 23.3, 23.4.1, 23.4.4.
[31] Ksrletezznk, elemezznk s teszteljnk a lehet leghamarabb s leggyakrab-
ban. 23.4.4, 23.4.5.
[32] Ne feledkezznk meg a hatkonysgrl. 23.4.7.
[33] Teremtsnk egyenslyt a formalitsok szintje s a projekt mrete kztt. 23.5.2.
[34] Mindenkppen bzzunk meg valakit, aki az tfog tervezsrt felel. 23.5.2.
[35] Dokumentljuk, npszerstsk s tmogassuk az jrahasznosthat sszetev-
ket. 23.5.1.
[36] Dokumentljuk a clokat s elveket ppgy, mint a rszleteket. 23.4.6.
[37] Gondoskodjunk oktatanyagrl az j fejlesztk rszre a dokumentci rsze-
knt. $23.4.6.
[38] Jutalmazzuk s sztnzzk a tervek, knyvtrak s osztlyok jrahasznostst.
23.5.1.
Tervezs a C++ segtsgvel 974
Tervezs s programozs
Legyen egyszer: annyira egyszer,
amennyire csak lehet de ne egyszerbb.
(A. Einstein)
A tervezs s a programozsi nyelv Osztlyok rkls Tpusellenrzs
Programozs Mit brzolnak az osztlyok? Osztlyhierarchik Fggsgek
Tartalmazs Tartalmazs s rkls Tervezsi kompromisszumok Hasznlati kapcso-
latok Beprogramozott kapcsolatok Invarinsok Hibaellenrzs felttelezsekkel
Betokozs Komponensek Sablonok Fellet s megvalsts Tancsok
24.1. ttekints
Ebben a fejezetben azt vizsgljuk meg, hogy a programozsi nyelvek s maga a C++ ho-
gyan tmogatjk a tervezst.
24.2 Az osztlyok, osztlyhierarchik, a tpusellenrzs s maga a programo-
zs alapvet szerepe
24.3 Az osztlyok s osztlyhierarchik hasznlata, klns tekintettel a prog-
ramrszek kztti fggsgekre
24
24.4 A komponens mint a tervezs alapegysge fogalma, s a felletek fel-
ptsre vonatkoz gyakorlati megfigyelsek
Az ltalnosabb tervezsi krdsekkel a 23. fejezetben foglalkoztunk, mg az osztlyok k-
lnbz hasznlati mdjait rszletesebben a 25. fejezet trgyalja.
24.2. A tervezs s a programozsi nyelv
Ha hidat szeretnnk pteni, figyelembe kellene vennnk az anyagot, amibl ptjk. A hd
tervezst ersen befolysoln az anyag megvlasztsa s viszont. A khidakat mskpp
kell megtervezni, mint az aclhidakat vagy a fahidakat s gy tovbb. Nem lennnk kpe-
sek kivlasztani a hdhoz a megfelel anyagot, ha nem tudnnk semmit a klnbz anya-
gokrl s hasznlatukrl. Termszetesen nem kell csmesternek lenni ahhoz, hogy valaki
fahidat tervezzen, de ismernie kell a faszerkezetek alapelveit, hogy vlasztani tudjon, fbl
vagy vasbl ptsen-e hidat. Tovbb br nem kell valakinek szemlyesen csmesternek
lennie egy fahd tervezshez kell, hogy rszletesen ismerje a fa tulajdonsgait s az csok
szoksait.
Ehhez hasonlan, ahhoz, hogy valamilyen programhoz nyelvet vlasszunk, tbb nyelv
ismerete szksges, a program egyes rszeinek sikeres megtervezshez pedig meglehet-
sen rszletesen kell ismernnk a megvalstshoz vlasztott nyelvet mg akkor is, ha sze-
mlyesen egyetlen kdsort sem runk. A j hdtervez figyelembe veszi az anyagok tulaj-
donsgait s azok megfelel felhasznlsval nveli a terv rtkt. A j szoftvertervez
ugyangy a vlasztott programozsi nyelv erssgeire pt s amennyire csak lehet elke-
rli annak olyan hasznlatt, ami problmkat okozhat a kd rinak.
Azt gondolhatnnk, hogy ez a nyelvi krdsek irnti rzkenysg termszetes, ha csak
egyetlen tervezt vagy programozt rint. Sajnos azonban mg az nll programoz is k-
srtsbe eshet, hogy a nyelvet hinyos tapasztalata vagy gykeresen eltr nyelvekben
kialakult programozsi stlusa miatt helytelenl hasznlja. Amikor a tervez s a progra-
moz nem azonos s klnsen ha szakmai s kulturlis htterk klnbz , szinte bi-
zonyos, hogy a program hibs, krlmnyes vagy nem hatkony lesz.
Mit nyjthat teht a programozsi nyelv a terveznek? Olyan tulajdonsgokat, melyek lehe-
tv teszik a terv alapfogalmainak kzvetlen brzolst a programban. Ez megknnyti
Tervezs a C++ segtsgvel 976
a kd megrst, knnyebb teszi a tervezs s megvalsts kztti klcsns megfelels
fenntartst, javtja a tervezk s programozk kztti kapcsolattartst s jobb eszkzk k-
sztst teszi lehetv mindkt csoport tmogatsra.
A legtbb tervezsi mdszer a program klnbz rszei kzti fggsgekkel foglalkozik
(rendszerint azrt, hogy a lehet legkisebbre cskkentse szmukat s biztostsa, hogy a fg-
gsgek pontosan meghatrozottak s tlthatak legyenek). Egy nyelv, mely tmogatja
a programrszek kapcsolatt biztost felleteket, kpes tmogatni az ezekre pl terve-
zst is, illetve garantlni tudja, hogy tnylegesen csak az elre ltott fggsgek ltezzenek.
Mivel az ilyen nyelvekben sok fggs kzvetlenl a kdban is megjelenik, beszerezhetk
olyan eszkzk, melyek a programot olvasva fggsgi diagramokat ksztenek. Ez meg-
knnyti a tervezk s azok dolgt, akiknek szksgk van a program szerkezetnek meg-
rtsre. Egy olyan programozsi nyelv, mint a C++ felhasznlhat a terv s a program kz-
ti szakadk kisebbtsre s a zavarok s flrertsek krnek kvetkezetes szktsre.
A C++ legfontosabb fogalma az osztly. A C++ osztlyai tpusok. A nvterekkel egytt az
osztlyok is az adatrejts elsdleges eszkzei. A programok felhasznli tpusok hierarchi-
iknt pthetk fel. Mind a beptett, mind a felhasznli tpusok a statikusan ellenrztt
tpusokra vonatkoz szablyoknak engedelmeskednek. A virtulis fggvnyek ezen szab-
lyok megsrtse nlkl a futsi idej ktsrl gondoskodnak. A sablonok a paramterezett
tpusok tervezst tmogatjk, a kivtelek pedig szablyozottabb hibakezelsre adnak m-
dot. A C++ ezen szolgltatsai anlkl hasznlhatk, hogy tbbletterhet jelentennek a C
programokhoz kpest. Ezek a C++ azon elsrend tulajdonsgai, melyeket a terveznek
meg kell rtenie s tekintetbe kell vennie. Ezenkvl a szles krben elrhet nagy prog-
ramknyvtrak a mtrixknyvtrak, adatbzis-felletek, a grafikus felhasznli felletek
knyvtrai s a prhuzamossgot tmogat knyvtrak is ersen befolysolhatjk a terve-
zsi dntseket.
Az jdonsgtl val flelem nha a C++ optimlisnl rosszabb felhasznlshoz vezet.
A ms nyelveknl, ms rendszereken s alkalmazsi terleteken tanultak helytelen alkalma-
zsa ugyanezt eredmnyezi. A gyenge tervezeszkzk szintn elronthatjk a terveket. me
t a leggyakrabban elkvetett a nyelvi tulajdonsgok rossz kihasznlst s korltozsok
felrgst eredmnyez tervezi hibk kzl:
1. Az osztlyok figyelmen kvl hagysa s olyan tervezs, amely a programo-
zkat arra knyszerti, hogy csak a C rszhalmazt hasznljk.
2. A szrmaztatott osztlyok s virtulis fggvnyek figyelmen kvl hagysa,
csak az absztrakt adatbrzolsi mdszerek rszhalmaznak hasznlata.
3. A statikus tpusellenrzs figyelmen kvl hagysa s olyan tervezs, amely
a programozkat arra knyszerti, hogy utnozzk a dinamikus tpusellenrzst.
24. Tervezs s programozs 977
4. A programozs figyelmen kvl hagysa s a rendszer olyan megtervezse,
amely a programozk kikszblst clozza.
5. Az osztlyhierarchik kivtelvel mindennek a figyelmen kvl hagysa.
Ezeket a hibkat ltalban a kvetkez httrrel rendelkez tervezk kvetik el:
1. akik korbban a C-vel, a hagyomnyos CASE eszkzzel, vagy struktrlt ter-
vezssel foglalkoztak,
2. akik korbban Ada83, Visual Basic vagy ms absztrakt brzolst tmogat
nyelven dolgoztak,
3. akik Smalltalk vagy Lisp mlttal rendelkeznek,
4. akik nem mszaki vagy nagyon specilis terleten dolgoztak, s
5. akik olyan terletrl rkeztek, ahol ers hangslyt kapott a tiszta objektum-
orientlt programozs.
Mindegyik esetben ktelkednnk kell, vajon jl vlasztottk-e meg a megvalsts nyel-
vt, a tervezsi mdszert, illetve hogy a tervez elsajttotta-e a kezben lv eszkzk
hasznlatt.
Nincs semmi szokatlan vagy szgyellni val az ilyen problmkban. Ezek egyszeren olyan
hinyossgok, amelyek nem optimlis terveket eredmnyeznek s a programozkra feles-
leges terheket hrtanak. A tervezk ugyanezekkel a problmkkal talljk magukat szem-
ben, ha a tervezsi mdszer fogalmi felptse szreveheten szegnyesebb, mint a C++-.
Ezrt ahol lehetsges, kerljk az ilyen hibkat.
A kvetkez fejtegets ellenvetsekre adott vlaszokbl ll, mivel ez a valsgban is gy
szokott lenni.
24.2.1. Az osztlyok figyelmen kvl hagysa
Vegyk azt a tervezst, amely figyelmen kvl hagyja az osztlyokat. Az eredmnyl kapott
C++ program nagyjbl egyenrtk az ugyanezen tervezsi folyamat eredmnyeknt kap-
hat C programmal s ez a program ugyancsak nagyjbl egyenrtk azzal a COBOL
programmal, melyet ugyanezen tervezsi folyamat eredmnyeknt kapnnk. A tervezs l-
nyegben programozsi nyelvtl fggetlenl folyt, a programozt arra knyszertve, hogy
a C s a COBOL kzs rszhalmazban kdoljon. Ennek a megkzeltsnek vannak el-
nyei. Pldul az adat s a kd szigor elklntse, ami knnyv teszi az ilyen programok-
hoz tervezett hagyomnyos adatbzisok hasznlatt. Mivel egy minimlis programozsi
nyelvet hasznlunk, kevesebb tudst vagy legalbb is kevesebb fle tudst kvetelnk
Tervezs a C++ segtsgvel 978
meg a programozktl. Sok programnl mondjuk egy hagyomnyos, szekvencilis adat-
bzist frisstnl ez a gondolkodsmd egszen sszer, az vtizedek alatt kifejlesztett ha-
gyomnyos eljrsok pedig megfelelek a feladathoz.
Tegyk fel azonban, hogy a program a rekordokat (vagy karaktereket) a hagyomnyos
szekvencilis feldolgozstl eltren kezeli, vagy bonyolultabb mondjuk, egy interaktv
CASE rendszerrl van sz. Az absztrakt adatbrzols nyelvi tmogatsnak hinya, amit az
osztlyok elhanyagolsa melletti dnts okoz, fj lesz. Az eredend bonyolultsg az alkal-
mazsban valahol meg fog mutatkozni, s ha a rendszert egy szegnyes nyelven ksztet-
tk, a kd nem fogja a tervet kzvetlenl tkrzni. A program kdja tl hossz lesz, hiny-
zik belle a tpusellenrzs s ltalban nem megkzelthet segdeszkzk szmra.
A program fenntartsa s ksbbi mdosthatsga szempontjbl ez igazi rmlom.
A problmra ltalnos megolds, ha eszkzket ksztnk a tervezsi mdszer fogalmai-
nak tmogatsra. Ezek az eszkzk magasabb szint ptkezst s ellenrzst tesznek le-
hetv, ami ellenslyozza a (szndkosan legyengtett) programozsi nyelv gyengesgt.
A tervezsi mdszer teht egy egyedi cl (s ltalban testleti tulajdont kpez) progra-
mozsi nyelvv vlik. Az ilyen programozsi nyelvek legtbb esetben csak gyenge ptlkai
a szles krben elrhet ltalnos cl programozsi nyelveknek, melyeket hozzjuk val
tervezeszkzk tmogatnak.
Az osztlyok tervezsbl val kihagysnak legltalnosabb oka egyszeren a tehetetlen-
sg. A hagyomnyos programozsi nyelvek nem tmogatjk az osztly fogalmt, a hagyo-
mnyos tervezsi mdszerek pedig tkrzik ezt a gyengesget. A tervezs legtbbszr
a problmk eljrsokra bontsra sszpontosul, melyek a kvnt mveleteket hajtjk vg-
re. Ezt a 2. fejezetben eljrskzpont (procedurlis) programozsnak nevezett fogalmat
a tervezssel sszefggsben ltalban funkcionlis (fggvnyekre vagy mveletekre val)
lebontsnak (functional decomposition) nevezzk. Gyakori krds, hogy tudjuk-e hasz-
nlni a C++-t egy funkcionlis lebontson alapul tervezsi mdszerrel egytt? A vlasz
igen, de a legvalsznbb, hogy a C++-t vgl egyszeren csak mint egy jobb C-t fogjuk
hasznlni s a fentebb emltett problmkkal fogunk knldni. tmeneti idszakban, mr
befejezett tervezsnl, vagy olyan alrendszereknl, ahol (a bevont szemlyek tapasztalatt
figyelembe vve) nem vrhat, hogy az osztlyok jelents elnnyel jrnak, ez elfogadhat.
Hosszabb tvon s ltalban azonban az osztlyok hasznlatnak a funkcionlis lebonts-
bl kvetkez ellenzse nem sszeegyeztethet a C++ vagy brmely ms, az absztrakt b-
rzolst tmogat nyelv hatkony hasznlatval.
A programozs eljrskzpont s objektumorientlt szemlletei alapveten klnbznek
s ugyanarra a problmra jellemzen gykeresen klnbz megoldsokat adnak. Ez
24. Tervezs s programozs 979
a megfigyels ppgy igaz a tervezsi, mint a megvalstsi szakaszra: lehet sszpontosta-
ni az elvgzend tevkenysgekre s az brzoland fogalmakra, de nem lehet egyszerre
mindkettre.
Mirt rszestjk elnyben az objektumorientlt tervezst a funkcionlis lebontson ala-
pul hagyomnyos tervezsi mdszerekkel szemben? Elssorban azrt, mert az utbbi nem
biztost elegend lehetsget az absztrakt adatbrzolsra. Ebbl pedig az kvetkezik,
hogy az eredmnyl kapott terv
kevsb mdosthat,
eszkzkkel kevsb tmogathat,
kevsb alkalmas prhuzamos fejlesztsre,
kevsb alkalmas prhuzamos vgrehajtsra.
A problma az, hogy a funkcionlis lebonts kvetkeztben a lnyeges adatok globlisak
lesznek, mivel amikor egy rendszer fggvnyekbl ll fa szerkezet, brmely adat, mely-
re kt fggvnynek van szksge, mindkett szmra elrhet kell, hogy legyen. Ez azt
eredmnyezi, hogy az rdekes adatok egyre feljebb vndorolnak a fn a gykr fel (ne
feledjk, a szmtstechnikban a fk mindig a gykrtl lefel nvekednek), ahogy egyre
tbb fggvny akar hozzjuk frni. Pontosan ugyanez a folyamat figyelhet meg az egy-
gyker osztlyhierarchikban, melyekben az rdekes adatok s fggvnyek hajlamosak
felfel vndorolni egy gykrosztly fel (24.4). A problmt gy oldhatjuk meg, ha az
osztlyok meghatrozsra s az adatok betokozsra (encapsulation) sszpontostunk,
gy ugyanis a programrszek kztti fggseket ttekinthetv tehetjk, s ami mg fon-
tosabb cskkentjk a programban lev fggsgek szmt, azltal, hogy az adatokra va-
l hivatkozsok loklisak lesznek.
Egyes problmkat azonban a legjobban a megfelel eljrsok megrsval oldhatunk meg.
Az objektumorientlt megkzeltsnl a tervezs lnyege nem az, hogy egyetlen nem tag
fggvny se legyen a programban vagy hogy a rendszer egyetlen rsze se legyen eljrs-
kzpont. Lnyegesebb, hogy a program klnbz rszeit gy vlasszuk el, hogy jobban
tkrzzk a fogalmakat. Ez ltalban gy rhet el a legjobban, ha elssorban az osztlyok
s nem a fggvnyek llnak a tervezs kzppontjban. Az eljrskzpont stlus haszn-
lata tudatos dnts kell, hogy legyen, nem pedig az alaprtelmezs. Az osztlyokat s el-
jrsokat az alkalmazsnak megfelelen kell hasznlni, nem egy rugalmatlan tervezsi
mdszer mellktermkeiknt.
Tervezs a C++ segtsgvel 980
24.2.2. Az rkls elkerlse
Tegyk fel, hogy a tervezsnl nem ptnk az rklsre. Az eredmnyl kapott program
nem fog lni a C++ egyik legelnysebb tulajdonsgval, mikzben persze kihasznlja
a C++ sok ms elnyt a C, Pascal, Fortran, COBOL stb. nyelvekkel szemben. A leggyak-
rabban hangoztatott rvek a tehetetlensgtl eltekintve az rkls hasznlata csak
rszletkrds a megvalsts sorn, az rkls megsrti az adatrejts elvt s az rkls
megnehezti az egyttmkdst ms programokkal.
Az rklst pusztn rszletkrdsnek tekintve nem vesszk figyelembe azt, hogy az osz-
tlyhierarchik kzvetlenl brzoljk az alkalmazsi terlet fogalmai kztti kapcsolato-
kat. Mrpedig az ilyen kapcsolatokat nyilvnvalv kell tenni a tervezsben, hogy a terve-
zk vitatkozhassanak rluk.
Elfordulhat az is, hogy az rklst olyan C++ programrszekbl zrjuk ki, amelyek kz-
vetlenl rintkeznek ms nyelveken rott kddal. Ez azonban nem elgsges ok arra, hogy
a program egszben elkerljk az rklst, csupn a program klvilg fel mutatott fe-
llett kell gondosan lernunk s betokoznunk. Hasonlkppen, az adatrejtsnek az
rkls ltali veszlyeztetse miatti agglyok (24.3.2.1) csak arra adnak okot, hogy elvi-
gyzatosak legynk a virtulis fggvnyek s vdett tagok hasznlatval (15.3), az rkl-
ds ltalnos elkerlsre nem.
Sok esetben nem szrmazik valdi elny az rklsbl. A nagyobb programoknl azonban
a nincs rkls megkzelts kevsb ttekinthet s rugalmatlanabb rendszert eredm-
nyez. Az rklst ekkor csak tettetjk, hagyomnyos nyelvi szerkezetek s tervezsi m-
dok hasznlatval. Az is valszn, hogy az ilyen hozzlls ellenre az rklst mgis hasz-
nlni fogjuk, mert a C++ programozk a program tbb rszben is meggyz rveket
fognak tallni az rkls alap tervezs mellett. Ezrt a nincs rkls csak azt fogja ered-
mnyezni, hogy a program felptse nem lesz kvetkezetes s az osztlyhierarchik hasz-
nlata csak egyes alrendszerekre fog korltozdni.
Ms szval, ne legynk elfogultak. Az osztlyhierarchik nem minden j program nlklz-
hetetlen rszei, de sok esetben segteni tudnak mind az alkalmazs megrtsben, mind
egy megolds kifejezsben. Az a tny, hogy az rklst lehet helytelen vagy tlzott m-
don hasznlni, ok az vatossgra, de a tiltsra nem.
24. Tervezs s programozs 981
24.2.3. A statikus tpusellenorzs figyelmen kvl hagysa
Vegynk azt az esetet, amikor a tervezsnl elhanyagoljuk a statikus tpusellenrzst. Ezt l-
talban a kvetkezkkel indokoljk: a tpusok a programozsi nyelv termkei, termsze-
tesebb objektumokban s nem tpusokban gondolkodni s a statikus tpusellenrzs arra
knyszert, hogy tl korn gondoljunk a megvalsts krdseire. Ez a hozzlls addig j,
amg nem okoz krt. Tervezskor sszernek tnhet nem foglalkozni a tpusellenrzs
rszleteivel, s az elemzsi s a korai tervezsi szakaszban ltalban nyugodtan figyelmen
kvl is hagyhatjuk ezeket a krdseket. Az osztlyok s osztlyhierarchik azonban na-
gyon hasznosak a tervezsben. Nevezetesen megengedik, hogy a fogalmakat brzolhas-
suk, kapcsolataikat meghatrozhassuk, s segtenek, hogy a fogalmakrl vitzzunk. A ter-
vezs elrehaladtval ez a pontos brzols az osztlyokrl s felleteikrl tett egyre
preczebb megllaptsok formjban jelentkezik.
Fontos, hogy szrevegyk, hogy a pontosan meghatrozott s ersen tpusos (lnyegben
tpusokra pt) felletek a tervezs alapvet eszkzei. A C++ felptse is ennek figyelem-
be vtelvel trtnt. Egy ersen tpusos fellet biztostja (egy hatrig), hogy csak kompati-
bilis programrszeket lehessen egytt fordtani s sszeszerkeszteni, ami lehetv teszi,
hogy ezek a programrszek egymsrl viszonylag ers felttelezsekkel lhessenek. Eze-
ket a felttelezseket a tpusrendszer biztostja; hatsra cskkenteni lehet a futsi idej el-
lenrzst, ezltal n a hatkonysg s jelentsen rvidl a tbbszemlyes projektek
integrlsi szakasza. Valjban az ersen tpusos felletekrl gondoskod rendszerek in-
tegrlsban szerzett nagyon pozitv tapasztalatok okozzk, hogy az integrls nem kap
nagy teret e fejezetben.
Nzznk egy hasonlatot. Gyakran kapcsolunk ssze klnbz szerkentyket, a csatla-
koz-szabvnyok szma pedig ltszlag vgtelen. A dugaszoknl kzenfekv, hogy egye-
di clra tervezettek, ami lehetetlenn teszi kt szerkezet egymssal val sszekapcsolst,
hacsak nem pont erre terveztk ket, ez esetben viszont csak a helyes mdon kapcsolha-
tk ssze. Nem lehet egy villanyborotvt egy nagyfeszltsg aljzatba bedugni. Ha lehetne,
az eredmny vagy egy slt villanyborotva vagy gsi srls lenne. A tervezk igen tal-
lkonynak bizonyultak, hogy biztostsk, hogy az ssze nem ill hardvereszkzket ne le-
hessen egymssal sszedugni. A nem megfelel dugaszok ellen lehet olyan kszlkeket
kszteni, melyek az aljzataikba dugott kszlkek nemkvnatos viselkedsvel szemben
megvdik magukat. J plda erre egy elektromos zavarvd. Miutn a dugaszok szintjn
nem garantlhat a teljes sszeegyeztethetsg, alkalmanknt szksgnk van drgbb v-
dramkrkre, melyek dinamikusan alkalmazkodnak a bemenethez vagy vdelmet nyj-
tanak azzal szemben.
Tervezs a C++ segtsgvel 982
A hasonlat majdnem pontos. A statikus tpusellenrzs a dugasz megfelelsgnek biztos-
tsval egyenrtk, a dinamikus ellenrzs pedig az alkalmazkod/vd ramkrnek fe-
lel meg. Ha mindkt ellenrzs hinyzik, az komoly krt okozhat. Nagy rendszerekben
mindkt ellenrzsi formt hasznljk. A tervezs korai szakaszban sszer lehet egysze-
ren kijelenteni, hogy ezt a kt kszlket ssze kell dugni, hamarosan fontos lesz azon-
ban, hogy pontosan megmondjuk, hogyan kell sszedugni ket. Milyen garancikat ad
a dugasz a viselkedssel kapcsolatban? Milyen krlmnyek kztt fordulhatnak el hibk?
Milyen kltsgekkel jr a megfelel viselkeds biztostsa?
A statikus tpusellenrzs hasznlata nem korltozdik a fizikai vilgra. A mrtkegys-
gek (pl. mter, kilogramm, msodperc) hasznlata a fizikban s a mrnki tudomnyok-
ban az ssze nem egyeztethet elemek sszekeverst akadlyozza meg.
Amikor a 23.4.3-ban a tervezs lpseit ismertettk, a tpusinformcik a 2. lpsben ke-
rltek el (az 1. lpsben rendszerint csak felletesen foglalkozunk velk), s a 4. lpsben
vltak kzponti krdss.
A statikusan ellenrztt felletek a klnbz programozi csoportok ltal fejlesztett C++
programok egyttmkdsnek f biztostkai. Ezek dokumentcija (belertve a hasznlt
tpusokt is) az elsdleges kapcsolattartsi eszkz az egyes programozi csoportok kztt.
Ezen felletek jelentik a tervezsi folyamat legfontosabb eredmnyt s ezek llnak a ter-
vezk s programozk kztti kapcsolat kzppontjban.
A tpusok elhanyagolsa a felletek kialaktsnl olyan felptshez vezet, amely homly-
ba burkolja a program szerkezett s a futs idejig elhalasztja a hibk szlelst. Tegyk
fel pldul, hogy egy felletet nazonost objektumokkal runk le:
// a plda dinamikus tpusellenrzst felttelez statikus ellenrzs helyett
Stack s; // a verem brmilyen tpus objektumra hivatkoz mutatkat trolhat
void f()
{
s.push(new Saab900); // ez egy auttpus
s.push(new Saab37B); // ez egy repltpus
s.pop()->takeoff(); // j: a Saab 37B egy replgp
s.pop()->takeoff(); // futsi idej hiba: egy aut nem tud felszllni
}
24. Tervezs s programozs 983
Ez a fellet (a Stack::push() felletnek) komoly tlegyszerstse, ami statikus ellenrzs
helyett dinamikus ellenrzsre pt. Az s verem replgpek (Plane) trolsra val, de
ez a kdban rejtett maradt, gy a felhasznl ktelessge lesz e kvetelmny betartst biz-
tostani.
Egy preczebb meghatrozs egy sablon s egy virtulis fggvny a megszorts nlkli dinamikus t-
pusellenrzs helyett a hibk szlelst a futsi idbl tteszi a fordtsi idbe:
Stack<Plane*> s; // a verem Plane-ekre hivatkoz mutatkat trolhat
void f()
{
s.push(new Saab900); // hiba: a Saab900 nem Plane tpus
s.push(new Saab37B);
s.pop()->takeoff(); // rendben: a Saab 37B egy replgp
s.pop()->takeoff();
}
Hasonl krdst trgyal a 16.2.2 pont. A klnbsg a futsi idej dinamikus ellenrzs s
a statikus ellenrzs kztt jelents lehet. A dinamikus ellenrzs rendszerint 3-10-szer
tbb feladatot r a rendszerre. Nem szabad viszont a msik vgletbe sem esni. Nem lehet
statikus ellenrzssel minden hibt elcspni. Mg a legalaposabb statikusan ellenrztt
program is ki van tve a hardverhibk okozta srlsnek. A 25.4.1 pontban tovbbi pldt
tallhatunk arra, hogy nem lehet tkletes statikus ellenrzst megvalstani, az idelis
azonban az, ha a felletek nagy tbbsge alkalmazsszint statikus tpusokat hasznl (lsd
24.4.2).
Egy msik problma, hogy a terv elvont szinten lehet tkletesen sszer, de komoly prob-
lmkat okozhat, ha nem szmol a hasznlt eszkz (esetnkben a C++) korltaival. Pld-
ul, egy f() fggvny, mely egy paramtern a turn_right() mveletet hajtja vgre, csak ak-
kor hozhat ltre, ha minden paramtere ugyanolyan tpus:
class Plane {
// ...
void turn_right();
};
class Car {
// ...
void turn_right();
};
Tervezs a C++ segtsgvel 984
void f(X* p) // milyen tpus kell legyen X?
{
p->turn_right();
// ...
}
Egyes nyelvek (mint a Smalltalk s a CLOS) megengedik kt ugyanazon mveletekkel ren-
delkez tpus felcserlt hasznlatt, azltal, hogy minden tpust egy kzs bzisosztly l-
tal kapcsolnak ssze s a futsi idre halasztjk a nv feloldst. A C++ azonban ezt (szn-
dkosan) csak sablonokkal (template) s fordtsi idej feloldssal tmogatja. Egy nem
sablon fggvny kt klnbz tpus paramtert csak akkor fogad el, ha a kt tpus auto-
matikusan kzs tpusra konvertlhat. Az elbbi pldban teht az X-nek a Plane s Car
(Aut) kzs bzisosztlynak kell lennie (pl. a Vehicle (Jrm) osztlynak).
A C++-tl idegen fogalmakra pl rendszerek termszetesen brzolhatk a C++-ban is,
ha a kapcsolatokra vonatkoz felttelezseket kifejezetten megadjuk. A Plane s a Car pl-
dul (kzs bzisosztly nlkl is) osztlyhierarchiba helyezhet, ami lehetv teszi, hogy
tadjunk egy Car-t vagy Plane-t tartalmaz objektumot f(X*)-nek (25.4.1). Ha azonban ezt
tesszk, az gyakran nemkvnatos mennyisg mveletet s gyessget kvetel, de a sab-
lonok hasznos eszkznek bizonyulhatnak az ilyen lekpezsek egyszerstsre. A terve-
zsi fogalmak s a C++ kzti rossz megfeleltets ltalban termszetellenes kinzet s
kis hatkonysg kdhoz vezet. A karbantart programozk nem szeretik a nyelvben
szokatlan kdot, amely ilyen rossz megfeleltetsekbl szrmazik.
A tervezsi md s a megvalstshoz hasznlt nyelv kzti rossz megfeleltets hasonlt
a (termszetes nyelvek esetben vgzett) szrl szra fordtshoz. Pldul az angol nyelv
magyar nyelvtannal ugyanolyan nehzkes, mint a magyar nyelv angol nyelvtannal, annak
pedig, aki csak a kt nyelv egyikt beszli folykonyan, mindkt vltozat rthetetlen lehet.
A programban lv osztlyok a tervezs fogalmainak konkrt brzolsai. Kvetkezskp-
pen, ha az osztlyok kztti kapcsolatok nem vilgosak, a terv alapfogalmai sem lesznek
azok.
24.2.4. A programozs elkerlse
A programozs sok ms tevkenysghez kpest kltsges s elre nehezen felmrhet
munka, az eredmnyl kapott kd pedig gyakran nem 100%-ig megbzhat. A programo-
zs munkaignyes s szmos okbl a munkt ltalban az htrltatja a legkomolyabban,
ha egy kdrsz nem tadsra ksz. Nos, mirt ne kszbljk ki a programozst, mint te-
vkenysget, egszben vve?
24. Tervezs s programozs 985
Sok vezet szmra jelents elnnyel jrna megszabadulni az arrogns, tlfizetett, szak-
mailag megszllott, nem megfelel ltzk stb. programozktl?
4
. Egy programoznak
persze ez a javaslat abszurdnak hangzik. Vannak azonban olyan fontos terletek, melyeknl
a programozsnak vannak vals alternatvi. Egyes esetekben lehet kzvetlenl egy magas
szint brzolsbl ltrehozni a kdot; msutt a kpernyn lv alakzatok kezelsvel. Kz-
vetlen kezelssel hasznlhat felhasznli felleteket lehet pteni annak az idnek trt r-
sze alatt, ami ugyanezen felletnek hagyomnyos kddal val lershoz kellene. Ugyangy
kdot kszthetnk adatbzis-kapcsolatokhoz s az adatok ilyen kapcsolatok szerinti hozz-
frshez pusztn azokbl a specifikcikbl, melyek sokkal egyszerbbek, mint a mvele-
tek kzvetlen kifejezshez szksges C++-ban vagy ms, ltalnos cl programozsi
nyelven rt kd. Ilyen lersokbl/meghatrozsokbl vagy egy kzvetlen kezelfellet se-
gtsgvel llapotautomatk (state machines) kszthetk, melyek kisebbek, gyorsabbak s
jobban mkdnek, mint amit a legtbb programoz kpes volna alkotni.
Ezek a mdszerek olyan terleteken hasznlhatk jl, ahol ersek az elmleti alapok (pl.
matematika, llapotautomatk, relcis adatbzisok) vagy van egy ltalnos vz, amelybe
be lehet gyazni kis programtredkeket (pl. grafikus felhasznli felletek,
hlzatszimulcik, adatbzis-smk). Az a tny, hogy ezen mdszerek egyes lnyeges te-
rleteken (br ezek kre korltozott) igen hasznosak lehetnek, elhitethetik velnk, hogy
a hagyomnyos programozs kivltsa e mdszerek segtsgvel mr a kszbn ll.
Nem gy van: ha az brzolsi mdszerek az ers elmleti vzon tllpnek, a ler nyelv
szksgszeren ppoly bonyolult lesz, mint egy ltalnos cl programozsi nyelv.
Nha elfelejtjk, hogy a vz, mely valamely terleten lehetsget ad a hagyomnyos prog-
ramozs kikszblsre, valjban egy hagyomnyos mdon tervezett, programozott s
tesztelt rendszer vagy knyvtr. A C++ s az e knyvben lert eljrsok egyik npszer fel-
hasznlsa is pontosan ilyen rendszerek tervezse s ptse.
A legrosszabb eset, ha egy ltalnos cl nyelv kifejez kpessgnek csak a tredkt biz-
tost kompromisszumos megoldst az eredeti (korltozott) alkalmazsi terleten kvl kell
felhasznlnunk. A tervezk, akik egy magas szint modellezsi szemponthoz ragaszkod-
nak, bosszankodnak a bonyolultsg miatt s olyan rendszerlerst ksztenek, melybl szr-
nysges kd jn ltre, a kznsges programozsi eljrsokat hasznl programozk pe-
dig csaldottak lesznek a nyelvi tmogats hinya miatt, s jobb kdot csak tlzott
erfesztssel s a magasszint modellek elhagysval lesznek kpesek kszteni.
Nem ltom jelt annak, hogy a programozs, mint tevkenysg sikeresen kikszblhet
lenne az olyan terleteken kvl, amelyeknek jl megalapozott elmlete van vagy amelyek-
ben az alapvet programozsi mdszer egy vzhoz igazodik. A hatkonysg mindkt eset-
ben drmai mdon lecskken, amint elhagyjuk az eredeti vzat s ltalnosabb cl mun-
Tervezs a C++ segtsgvel 986
4
Igen. n programoz vagyok.
kt ksrelnk meg elvgezni. Mst sznlelni csbt, de veszlyes dolog. Ugyanakkor rlt-
sg lenne figyelmen kvl hagyni a magas szint lersokat s a kzvetlen kezelsre szolg-
l eljrsokat olyan terleteken, ahol azok jl megalapozottak s meglehetsen kiforrottak.
Az eszkzk, knyvtrak s vzak tervezse a tervezs s programozs egyik legmagasabb
rend fajtja. Jl hasznlhat matematikai alap modellt pteni egy alkalmazsi terletre
az egyik legmagasabb rend elemzsfajta. Adni egy eszkzt, nyelvet, vzat stb., amely az
ilyen munka eredmnyt ezrek szmra teszi elrhetv, mdot ad a programozknak s
a tervezknek elkerlni a csapdt, hogy tucattermkek ksztiv vljanak.
A legfontosabb, hogy az adott ler rendszer vagy alapknyvtr kpes legyen felletknt
hatsosan egyttmkdni egy ltalnos cl programozsi nyelvvel. Egybknt az adott
vz magban hordja korltait. Ebbl kvetkezik, hogy azon ler vagy kzvetlen kezelst
biztost rendszereknek, melyek megfelelen magas szint kdot ksztenek valamilyen el-
fogadott ltalnos cl programozsi nyelven, nagy elnyk van. Az egyedi nyelvek hossz
tvon csak ksztiknek jelentenek knnyebbsget. Ha a ltrehozott kd olyan alacsony
szint, hogy a hozztett ltalnos kdot az absztrakt brzols elnyei nlkl kell megrni,
elvesztjk a megbzhatsgot, a mdosthatsgot s a gazdasgossgot. Egy kdkszt
rendszert lnyegben gy kell megrni, hogy egyestjk a magasabb szint lersok s a ma-
gasabb szint nyelvek erssgeit. Kihagyni az egyiket vagy a msikat annyi, mint felldoz-
ni a rendszerptk rdekeit az eszkzksztk rdekeirt. A sikeres nagy rendszerek tbb-
szintek, modulrisak s folytonosan fejldnek. Kvetkezskppen az ilyen rendszerek
megalkotst clz sikeres erfesztsekbe sokfle nyelvet, knyvtrat, eszkzt s mdszert
kell bevonni.
24.2.5. Az osztlyhierarchik kizrlagos hasznlata
Amikor gy talljuk, hogy egy jdonsg tnyleg mkdik, gyakran esnk tlzsba, s nyak-
ra-fre azt alkalmazzuk. Ms szval, az egyes problmk esetben j megoldsrl gyakran
hisszk, hogy gygyrt jelenthet majdnem minden problmra. Az osztlyhierarchik s az
objektumokon vgzett tbbalak (polimorf) mveletek sok problmra adnak j megol-
dst, de nem minden fogalom brzolhat a legjobban egy hierarchia rszeknt, s nem
minden programkomponens legjobb brzolsa egy osztlyhierarchia.
Mirt nem? Az osztlyhierarchia kapcsolatokat fejez ki osztlyai kztt, az osztly pedig egy
fogalmat kpvisel. Nos, akkor mi a kzs kapcsolat egy mosoly, a CD-meghajtm, Richard
Strauss Don Juanjnak egy felvtele, egy sor szveg, egy mhold, az orvosi leleteim s egy
valsidej ra kztt? Ha az egszet egyetlen hierarchiba helyezzk, mikzben egyetlen
kzs tulajdonsguk, hogy mindnyjan programozsi elemek (objektumok), csak kevs
24. Tervezs s programozs 987
rtkkel br, zavaros rendszert hozunk ltre (15.4.5). Ha mindent egyetlen hierarchiba
erltetnk, mestersges hasonlsgok jhetnek ltre s elhomlyosthatjk a valdi egye-
zseket. Hierarchit csak akkor szabad hasznlnunk, ha az elemzs fogalmi kzssget mu-
tat ki, vagy ha a tervezs s programozs fed fel egyezseket a fogalmak brzolsra hasz-
nlt szerkezetekben. Az utbbi esetben nagyon figyelnnk kell arra, hogy
megklnbztessk a valdi (altpusok ltal rklt nyilvnos tulajdonsgban tkrzd)
kzssget s a hasznos egyszerstseket (ami privt rklsben tkrzdik, 24.3.2.1).
Ez a gondolatmenet olyan programhoz vezet, melyben szmos egymssal kapcsolatban
nem lv, vagy gyengn kapcsold osztlyhierarchia van, s ezek mindegyike szorosan
sszekapcsolt fogalmak halmazt kpviseli. Elvezet a konkrt osztly (25.2) fogalmhoz is,
mely nem hierarchia tagja, mert egy ilyen osztlyt hierarchiba helyezve csorbtannk az
osztly teljestkpessgt s fggetlensgt a rendszer tbbi rsztl. A hatkonysg
szemszgbl egy osztlyhierarchia rszt kpez osztly leglnyegesebb mveleteinek
virtulis fggvnyeit kell tekintennk, tovbb az osztly szmos adatnak privt (private)
helyett vdettnek (protected) kell lennie. Ez persze sebezhetv teszi a tovbbi szrmazta-
tott osztlyok mdostsaival szemben s komoly bonyodalmakat okozhat a tesztelsnl.
Ott, ahol tervezsi szempontbl szigorbb betokozst (enkapszulcit) rdemes hasznlni,
nem virtulis fggvnyeket s privt adatokat kell alkalmazni (24.3.2.1).
Ha egy mveletnek egyetlen paramtere van (az, amelyik az objektumot jelli), a terv l-
talban torzul. Ha tbb paramterrel egyformn lehet bnni, a mvelet egy nem tag fgg-
vnnyel brzolhat a legjobban. Ebbl nem kvetkezik, hogy az ilyen fggvnyeket glo-
bliss kell tennnk, csupn az, hogy majdnem minden ilyen nll fggvnynek egy
nvtr tagjnak kell lennie (24.4).
24.3. Osztlyok
Az objektumorientlt tervezs s programozs alapvet elve, hogy a program a valsg va-
lamely rsznek modellje. A programban az osztlyok a modellezett valsg alapfogalma-
it, mg ezen osztlyok objektumai a vals vilgbeli objektumokat s a megvalsts sorn
ltrehozott elemeket brzoljk.
Az osztlyok kzti s egy osztly rszein belli kapcsolatok elemzse a rendszer tervezs-
ben kzponti szerepet jtszik:
Tervezs a C++ segtsgvel 988
24.3.2 rklsi kapcsolatok
24.3.3 Tartalmazsi kapcsolatok
24.3.5 Hasznlati kapcsolatok
24.2.4 Beprogramozott kapcsolatok
24.3.7 Osztlyon belli kapcsolatok
Mivel a C++ osztlyai tpusok, az osztlyok s az osztlyok kztti kapcsolatok jelents t-
mogatst kapnak a fordtprogram rszrl s ltalban a statikus elemzs al tartoznak.
Ahhoz, hogy a rendszerben fontos legyen, egy osztlynak nemcsak hasznlhat fogalmat
kell brzolnia; megfelel felletet is kell nyjtania. Az idelis osztly alapveten csekly
mrtkben, de pontosan meghatrozottan fgg a tbbi programelemtl, s olyan felletet
nyjt, amely azok szmra csak a felttlenl szksges informcikat biztostja (24.4.2).
24.3.1. Mit brzolnak az osztlyok?
Egy rendszerben lnyegben ktfajta osztly van:
1. Osztlyok, melyek kzvetlenl az alkalmazsi terlet fogalmait brzoljk; azaz
olyan fogalmakat, melyeket a vgfelhasznlk hasznlnak a problmk s meg-
oldsok brzolsra.
2. Osztlyok, melyek a megvalstsbl kvetkeznek, vagyis olyan fogalmak, melye-
ket a tervezk s programozk a megvalstsi mdszerek lersra hasznlnak.
Nhny osztly, mely a megvalsts termke, brzolhat valsgos dolgot is. A rendszer
hardver- s szoftver-erforrsai pldul alkalmasak arra, hogy egy programban osztlyok
legyenek. (Ez azt a tnyt tkrzi, hogy egy rendszer tbb nzpontbl is tekinthet.) Ebbl
kvetkezik, hogy ami valaki szmra csak a megvalsts rszletkrdse, az ms szmra
a teljes alkalmazs lehet. Egy jl tervezett rendszer olyan osztlyokat tartalmaz, melyek
a rendszer logikailag nll nzeteit brzoljk:
1. Felhasznli szint fogalmakat brzol osztlyok (pl. autk s teherautk)
2. Felhasznli fogalmak ltalnostsait brzol osztlyok (pl. jrmvek)
3. Hardver-erforrsokat brzol osztlyok (pl. egy memriakezel osztly)
4. Rendszer-erforrsokat brzol osztlyok (pl. kimeneti adatfolyamok)
5. Ms osztlyok megvalstsa hasznlt osztlyok (pl. listk, vrakozsi sorok, zrak)
6. Beptett adattpusok s vezrlsi szerkezetek
24. Tervezs s programozs 989
Nagyobb rendszerekben kihvst jelent a logikailag nll osztlytpusok elvlasztsa s
a klnbz elvonatkoztatsi (fogalmi) szintek kztti elklnts fenntartsa. Vegynk
egy egyszer pldt, ahol hrom fogalmi szintnk van:
1+2 A rendszer alkalmazsszint nzete
3+4 Annak a gpnek az brzolsa, amelyen a modell fut
5+6 A megvalsts alacsonyszint (programozsi nyelvi) nzete
Minl nagyobb a rendszer, annl tbb fogalmi szintre van szksg annak lershoz s annl
nehezebb a szinteket meghatrozni s fenntartani. Vegyk szre, hogy az ilyen fogalmi szin-
teknek kzvetlen megfeleli vannak mind a termszetben, mind a ms tpus, ember ltal
ltrehozott rendszerekben. Egy hz pldul gy is tekinthet, mint ami az albbiakbl ll:
1. atomok,
2. molekulk,
3. faanyag s tgla,
4. padl, falak s mennyezet,
5. szobk.
Mindaddig, amg ezek a szintek kln maradnak, fenntarthat a hz fogalmnak kvetke-
zetes megkzeltse, ha azonban keverjk ket, abszurditsok keletkeznek. Pldul az a ki-
jelents, hogy Az n hzam tbbezer kil sznbl, komplex polimerbl, kb. 5000 tglbl,
kt frdszobbl s 13 mennyezetbl ll, ostobasg. A programok absztrakt termszet-
bl addik, hogy ha egy bonyolult programrendszerrl hasonl kijelentst tesznk, azt nem
mindig lehet ilyen egyszeren minsteni.
Az alkalmazsi terlet valamely fogalmnak lefordtsa egy tervezsi osztlyra nem egy-
szer, mechanikus mvelet, gyakran jelents ttekintst kvetel. Vegyk szre, hogy magu-
kat az adott alkalmazsi terlet fogalmait is elvonatkoztats segtsgvel rjuk le. Pldul
adfizetk, szerzetesek s alkalmazottak a termszetben nem lteznek; az ilyen fogal-
mak csupn cmkk, melyeket egynekre aggatunk, hogy valamely rendszer szerint oszt-
lyozzuk ket. A vals, st a kpzelt vilgbl (az irodalombl, klnsen a tudomnyos fan-
tasztikumbl) mertett fogalmak gykeresen megvltoznak, amikor osztlyokkal brzoljuk
azokat. A PC kpernyje pldul nem igazn emlkeztet az rasztalra, sokszor mgis ezzel
a hasonlattal rjk le?
5
, a kpernyn lv ablakok pedig csak halvnyan emlkeztetnek
azokra a szerkezetekre, melyek huzatot engednek be az irodba. A valsg modellezsnl
nem az a lnyeg, hogy szolgai mdon kvessk azt, amit ltunk, hanem hogy a tervezs ki-
indul pontjaknt, sztnz forrsknt, s horgonyknt hasznljuk azt, melybe kapaszkod-
hatunk, amikor a program megfoghatatlan termszete azzal fenyeget, hogy legyzi azt a k-
pessgnket, hogy megrtsk sajt programjainkat.
Tervezs a C++ segtsgvel 990
5
n semmikppen sem trnk akkora rendetlensget a kpernymn.
Egy figyelmeztets: a kezdk gyakran nehezen talljk meg az osztlyokat, de ezt a prob-
lmt rendszerint hamarosan lekzdik, maradand kros hatsok nlkl. Ezt azonban
gyakran kveti egy szakasz, amelyben az osztlyok s azok rklsi kapcsolatai ltsz-
lag ellenrizhetetlenl megsokszorozdnak, ami hossz tvon viszont bonyolultabb s t-
tekinthetetlenebb teheti az eredmnyl kapott programot s ronthatja annak hatkonys-
gt. Nem kell minden kis rszletet kln osztllyal s minden osztlyok kztti kapcsolatot
rklsi kapcsolattal brzolni. Prbljuk szben tartani, hogy a tervezs clja a rendszer
megfelel rszletessggel s megfelel elvonatkoztatsi szinten val modellezse. Az egy-
szersg s az ltalnossg kztt nem knny az egyenslyt megtallni.
24.3.2. Osztlyhierarchik
Vegyk egy vros forgalmnak szimulcijt: meg kell hatroznunk, vrhatan mennyi id-
re van szksg, hogy a mentjrmvek rendeltetsi helykre rjenek. Vilgos, hogy br-
zolnunk kell autkat, teherautkat, mentautkat, klnfle tzoltjrmveket, rendrau-
tkat, buszokat s gy tovbb. Az rkls mindenkppen szerephez jut, mivel a vals
vilgbeli fogalmak nem lteznek elszigetelten, csak ms fogalmakkal kapcsolatban. A kap-
csolatok megrtse nlkl nem rthetjk meg a fogalmakat sem. Kvetkezskppen az
a modell, amely nem brzol ilyen kapcsolatokat, nem brzolja megfelelen a fogalmakat
sem. Programjainkban teht szksgnk van osztlyokra a fogalmak brzolshoz, de ez
nem elg; szksgnk van az osztlyok kapcsolatainak brzolsra is. Az rkls kitn
mdszer hierarchikus kapcsolatok kzvetlen brzolsra. Pldnkban a mentjrmveket
valsznleg klnlegesnek tekintennk s megklnbztetnnk autszer s teherau-
tszer jrmveket is. Az osztlyhierarchia ezek alapjn gy nzne ki:
24. Tervezs s programozs 991
Police_car
Car
Vehicle
Emergency Truck
Ambulance Fire_engine
Hook_and_ladder
Itt az Emergency a megklnbztetett jrmvek azon jellemzit kpviseli, melyek a szimu-
lci szempontjbl lnyegesek: megsrthet bizonyos forgalmi szablyokat, elsbbsge
van az tkeresztezdsekben, diszpcser irnytja stb.
me a C++ vltozat:
class Vehicle { /* ... */ }; // Jrm
class Emergency { /* ... */ }; // Megklnbztetett
class Car : public Vehicle { /* ... */ }; // Aut
class Truck : public Vehicle { /* ... */ }; // Teheraut
class Police_car : public Car , protected Emergency { /* ... */ }; // Rendraut
class Ambulance : public Car , protected Emergency { /* ... */ }; // Mentaut
class Fire_engine : public Truck , protected Emergency { /* ... */ }; // Tzoltaut
class Hook_and_ladder : public Fire_engine { /* ... */ }; // Ltrsaut
Az rkls a C++-ban kzvetlenl brzolhat legmagasabb szint kapcsolat, s a tervezs
korai szakaszban a legnagyobb a szerepe. Gyakran vlaszthatunk, hogy rklst vagy tag-
sgot hasznlunk-e egy kapcsolat brzolsra. Nzznk egy msik megkzeltst, mit is je-
lent megklnbztetett jrmnek lenni: egy jrm megklnbztetett, ha villog fnyjelz-
je van. Ez lehetv tenn, hogy gy egyszerstsk az osztlyhierarchit, hogy az
Emergency osztlyt a Vehicle osztly egyik tagjval helyettestjk:
Az Emergency osztlyt most egyszeren azon osztlyok tagjaknt hasznljuk, melyeknek
szksgk lehet arra, hogy megklnbztetett jrmknt szerepeljenek:
Tervezs a C++ segtsgvel 992
Police_car
Car
Vehicle { eptr }
Truck
Ambulance Fire_engine
Hook_and_ladder
class Emergency { /* ... */ };
class Vehicle { protected: Emergency* eptr; /* ... */ }; // jobb: helyes hozzfrst biztost eptr-
hez
class Car : public Vehicle { /* ... */ };
class Truck : public Vehicle { /* ... */ };
class Police_car : public Car { /* ... */ };
class Ambulance : public Car { /* ... */ };
class Fire_engine : public Truck { /* ... */ };
class Hook_and_ladder : public Fire_engine { /* ... */ };
Itt egy jrm akkor megklnbztetett, ha a Vehicle::eptr nem nulla. A sima autknl s
teherautknl a Vehicle::eptr kezdrtke nulla; a tbbinl nem nulla:
Car::Car() // Car konstruktor
{
eptr = 0;
}
Police_car::Police_car() // Police_car konstruktor
{
eptr = new Emergency;
}
Az elemek ilyen meghatrozsa lehetv teszi, hogy egy megklnbztetett jrmvet k-
znsges jrmv alaktsunk t s megfordtva:
void f(Vehicle* p)
{
delete p->eptr;
p->eptr = 0; // Tbb nem megklnbztetett jrm
// ...
p->eptr = new Emergency; // Ismt megklnbztetett jrm
}
Nos, melyik jobb osztlyhierarchia? Az ltalnos vlasz: Az a program, amely a vals vilg
bennnket legjobban rdekl rszt a legkzvetlenebb mdon modellezi. Vagyis a model-
lek kzti vlasztskor a cl a valsg minl jobb megkzeltse, persze a hatkonysgra s
egyszersgre val trekvs mellett. Esetnkben a knny tvltozs a kznsges s
megklnbztetett jrmvek kztt szmomra nem tnik valszernek. A tzoltautk s
a mentautk klnleges clra kszlt jrmvek, kikpzett szemlyzettel; eligaztsuk pe-
dig egyedi kommunikcis berendezseket ignyel. Ez a szemllet jelzi, hogy megkln-
24. Tervezs s programozs 993
bztetett jrmnek lenni alapvet fogalom s a programban kzvetlenl kell brzolni,
hogy segtse a tpusellenrzst s ms eszkzk hasznlatt. Ha olyan helyet modellez-
nnk, ahol a jrmvek szerepe kevsb szigoran meghatrozott mondjuk egy olyan te-
rletet, ahol magnjrmveket szoks a mentszemlyzet helysznre szlltsra hasznlni
s ahol a kommunikci elssorban hordozhat rdikon keresztl folyik, ms modellez-
si mdszer megfelelbb lenne. Azok szmra, akik a forgalomszimulcit elvontnak tekin-
tik, rdemes lehet rmutatni, hogy az rklds s a tagsg kzti vlaszts szinte minden
esetben elkerlhetetlen (lsd a 24.3.3 grdtsv pldjt).
24.3.2.1. Osztlyhierarchin belli fggsek
A szrmaztatott osztlyok termszetesen fggnek bzisosztlyaiktl. Ritkbban esik sz r-
la, de az ellenkezje is igaz lehet?
6
. Ha egy osztlynak van virtulis fggvnye, az osztly
fgg a belle szrmaztatott osztlyoktl, melyek e fggvny fellbrlsval az osztly
egyes szolgltatsait biztostjk. Ha magnak a bzisosztlynak egy tagja hvja meg az osz-
tly egyik virtulis fggvnyt, megint csak a bzisosztly fgg a szrmaztatott osztlyaitl,
sajt megvalstsa miatt. Vegyk az albbi pldt:
class B {
// ...
protected:
int a;
public:
virtual int f();
int g() { int x = f(); return x-a; }
};
Mit csinl g() ? A vlaszt jelentsen befolysolja, definilja f()-et valamelyik szrmaztatott
osztly. me egy vltozat, mely biztostja, hogy g() visszatrsi rtke 1 lesz:
class D1 : public B {
int f() { return a+1; }
};
me egy msik, amely kirja a Hell, vilg! szveget s nullval tr vissza:
class D2 : public B {
int f() { cout<<"Hell, vilg!\n"; return a; }
};
Tervezs a C++ segtsgvel 994
6
Ez a megfigyels gy sszegezhet: Az esztelensg rklhet. A gyerekeidtl kapod meg.
A fentiek a virtulis fggvnyekkel kapcsolatos egyik legfontosabb dolgot szemlltetik. Mi-
rt rossz a plda? Mirt nem rna programoz soha ilyet? Azrt, mert a virtulis fggvnyek
a bzisosztly felletnek rszei, s az osztly felteheten a belle szrmaz osztlyok is-
merete nlkl is hasznlhat. Kvetkezskppen gy kell tudnunk meghatrozni a
bzisosztly egy objektumnak elvrt viselkedst, hogy a szrmaztatott osztlyok ismere-
te nlkl is rhassunk programokat. Minden osztly, mely fellrja (override) a virtulis fgg-
vnyt, e viselkedsnek egy vltozatt kell, hogy lerja. Pldul a Shape (Alak) osztly
rotate() (forgats) virtulis fggvnye egy alakzatot forgat el. A szrmaztatott Circle (Kr)
s Triangle (Hromszg) osztlyok rotate() fggvnyeinek a nekik megfelel tpus objek-
tumokat kell forgatniuk, klnben megsrtennek egy alapvet felttelezst a Shape osz-
tlyrl. A fenti B osztlyrl s a belle szrmaz D1 s D2 osztlyokrl ilyesmit nem tte-
leztnk fel, ezrt a plda rtelmetlen. Mg a B, D1, D2, f s g neveket is gy vlasztottuk
meg, hogy brmilyen lehetsges jelentst homlyban hagyjanak. A virtulis fggvnyek el-
vrt viselkedsnek meghatrozsa az osztlyok tervezsnek egyik f szempontja. J ne-
veket vlasztani az osztlyok s fggvnyek szmra szintn fontos de nem mindig
knny.
J vagy rossz-e egy fggs az ismeretlen (esetleg mg meg sem rt) szrmaztatott osztlyok-
tl? Termszetesen ez fgg a programoz szndktl. Ha egy osztlyt gy akar elszigetel-
ni minden kls befolystl, hogy az meghatrozott mdon viselkedjen, akkor legjobb el-
kerlni a vdett (protected) tagokat s a virtulis fggvnyeket. Ha azonban egy vzat akar
adni, amelyhez egy ksbbi programoz (vagy sajt maga nhny httel ksbb) kdot ad-
hat hozz, a virtulis fggvnyek hasznlata ehhez elegns mdszert jelenthet, a protected
tagfggvnyek pedig jl tmogatjk az ilyen megoldsokat. Ezt a megoldst vlasztottuk az
adatfolyam I/O knyvtrban (21.6), az Ival_box hierarchia vgs vltozatnl (12.4.2) pe-
dig pldt is mutattunk r.
Ha egy virtulis fggvnyt csak a szrmaztatott osztlyok ltali kzvetett hasznlatra sz-
nunk, private maradhat. Pldaknt vegyk egy tmeneti tr (puffer) egyszer sablonjt:
template<class T> class Buffer {
public:
void put(T); // overflow(T) meghvsa, ha az tmeneti tr megtelt
T get(); // underflow() meghvsa, ha a tr res
// ...
private:
virtual int overflow(T);
virtual int underflow();
// ...
};
24. Tervezs s programozs 995
A put() s get() fggvnyek a virtulis overflow(), illetve underflow() fggvnyeket hvjk
meg. A felhasznl ezen fggvnyek fellrsval a klnbz ignyek szerint egy sor tr-
tpust hatrozhat meg:
template<class T> class Circular_buffer : public Buffer<T> {
int overflow(T); // krbelp, ha teli
int underflow();
// ...
};
template<class T> class Expanding_buffer : public Buffer<T> {
int overflow(T); // megnveli az tmeneti trat, ha megtelt
int underflow();
// ...
};
Az overflow() s underflow() fggvnyeknek csak akkor kellene private helyett protected-
nek lennik, ha a szrmaztatott osztlynak szksge lenne e fggvnyek kzvetlen meg-
hvsra.
24.3.3. Tartalmazsi kapcsolatok
Ott, ahol tartalmazst hasznlunk, egy X osztly egy objektumt kt f mdszerrel brzol-
hatjuk:
1. Bevezetnk egy X tpus tagot.
2. Bevezetnk egy X* tpus vagy egy X& tpus tagot.
Ha a mutat rtke sohasem vltozik, ezek a megoldsok egyenrtkek, kivve a hat-
konysg krdseit s azt a mdot, ahogyan konstruktorokat s destruktorokat runk:
class X {
public:
X(int);
// ...
};
class C {
X a;
X* p;
X& r;
public:
Tervezs a C++ segtsgvel 996
C(int i, int j, int k) : a(i), p(new X(j)), r(*new X(k)) { }
~C() { delete p; delete &r; }
};
Ilyen esetekben rendszerint elnyben kell rszestennk magnak az objektumnak a tags-
gt (C::a), mert ez biztostja a leggyorsabb mkdst, ez ignyli a legkevesebb helyet, s
persze ezt lehet a leggyorsabban lerni. Kevesebb hiba forrsa is, mivel a tartalmaz s a tar-
talmazott objektum kapcsolata a ltrehozs s megsemmists szablya al tartozik (10.4.1,
12.2.2, 14.4.1, de lsd mg: 24.4.2 s 25.7).
A mutatt hasznl megoldst akkor hasznljuk, ha a tartalmaz objektum lettartama
alatt a tartalmazott objektumra hivatkoz mutatt meg kell vltoztatnunk:
class C2 {
X* p;
public:
C2(int i) : p(new X(i)) { }
~C2() { delete p; }
X* change(X* q)
{
X* t = p;
p = q;
return t;
}
};
Egy msik ok a mutat tag hasznlatra, hogy meg akarjuk engedni a tartalmazott objek-
tum paramterknt val szereplst:
class C3 {
X* p;
public:
C3(X* q) : p(q) { }
// ...
};
Azltal, hogy olyan objektumaink vannak, melyek ms objektumokra hivatkoz mutatkat
tartalmaznak, tulajdonkppen egy objektumhierarchit hoztunk ltre. Ez az osztlyhierar-
chik hasznlatt egyszerre helyettestheti s kiegsztheti. Mint a 24.3.2 jrm pldja
mutatta, gyakran nehz tervezskor vlasztani, hogy egy osztlytulajdonsgot bzisosztly-
knt vagy tagknt brzoljunk. Ha fellrt fggvnyeket kell hasznlnunk, az azt jelzi, hogy
az els a jobb vlaszts, ha pedig arra van szksg, hogy egy tulajdonsgot tbb tpussal
brzolhassunk, valsznleg clszerbb a msodik megolds mellett dnteni:
24. Tervezs s programozs 997
class XX : public X { /* ... */ };
class XXX : public X { /* ... */ };
void f()
{
C3* p1 = new C3(new X); // C3 "tartalmaz" egy X objektumot
C3* p2 = new C3(new XX); // C3 "tartalmaz" egy XX objektumot
C3* p3 = new C3(new XXX); // C3 "tartalmaz" egy XXX objektumot
// ...
}
Itt nem lenne megfelel brzols, ha C3-at X-bl szrmaztatnnk vagy ha C3-nak egy X t-
pus tagja lenne, mivel a tag pontos tpust kell hasznlni. Ez a virtulis fggvnyekkel ren-
delkez osztlyoknl fontos, pldul egy alakzatosztlynl (2.6.2) vagy egy absztrakt hal-
mazosztlynl (25.3).
A mutat tagokra pl osztlyok egyszerstsre azokban az esetekben, amikor a tartal-
maz objektum lettartama alatt csak egyetlen objektumra hivatkozunk, referencikat
hasznlhatunk:
class C4 {
X& r;
public:
C4(X& q) : r(q) { }
// ...
};
Akkor is szksg van mutat s referencia tpus tagokra, amikor egy objektumon osztoz-
ni kell:
X* p = new XX;
C4 obj1(*p);
C4 obj2(*p); // obj1 s obj2 most osztoznak az j XX objektumon
Termszetesen a kzsen hasznlt objektumok kezelse kln vatossgot kvn, fleg
a prhuzamos feldolgozst tmogat rendszerekben.
24.3.4. Tartalmazs s rkls
Az rklsi kapcsolatok fontossgnak ismeretben nem meglep, hogy ezeket a kapcso-
latokat gyakran tlzottan hasznljk vagy flrertik. Ha egy D osztly nyilvnos s a B osz-
tlybl szrmaztatott, gyakran azt mondjuk, hogy egy D valjban egy B (D is a B):
Tervezs a C++ segtsgvel 998
class B { /* ... */ };
class D : public B { /* ... */ }; // D is a kind of B: D egyfajta B
gy is kifejezhetjk, hogy az rkls egy is-a kapcsolat, vagy valamivel preczeb-
ben hogy egy D valjban egyfajta B (D is a kind of B). Ezzel szemben ha a D osztly va-
lamelyik tagja egy B osztly, azt mondjuk, hogy van (have) egy B-je, illetve tartalmaz
(contain) egy B-t:
class D { // D tartalmaz egy B-t
public:
B b;
// ...
};
Mskppen ezt gy fejezzk ki, hogy a tagsg egy has-a kapcsolat.
Adott B s D osztlyok esetben hogyan vlasszunk rkls s tagsg kztt? Vegynk egy
Replgp-et s egy Motor-t. A kezdk gyakran kvncsiak, j tlet-e egy Replgp osztlyt
a Motor-bl szrmaztatni. Rossz tlet, mert br a replgpnek van motorja, a replgp
nem motor. Vegyk figyelembe, hogy egy replgpnek kett vagy tbb motorja is lehet.
Mivel sszernek ltszik mg akkor is, ha az adott programban minden Replgp egy-
motoros , hasznljunk rkls helyett tagsgot. A lehet-e neki kett? krds sok esetben
hasznos lehet, ha ktsg merl fel. Mint rendszerint, a programok megfoghatatlan term-
szete az, ami fontoss teszi ezt a vizsglatot. Ha minden osztlyt olyan knnyen elkpzel-
hetnnk, mint a Replgp-et s a Motor-t, knnyen elkerlhetnnk az olyan nyilvnval t-
vedseket, mint egy Replgp szrmaztatsa egy Motor-bl. Az ilyen tvedsek azonban
igen gyakoriak klnsen azoknl, akik a szrmaztatst egyszeren egy olyan eljrsnak
tekintik, mellyel programozsi nyelvi szint szerkezeteket lehet egytt hasznlni. Annak el-
lenre, hogy az rkls hasznlata knyelmes s a kdot is rvidti, majdnem kivtel nl-
kl olyan kapcsolatok kifejezsre hasznljuk, melyeket a tervezsnl pontosan meghat-
roztunk. Vegyk a kvetkezt:
class B {
public:
virtual void f();
void g();
};
class D1 { // D1 tartalmaz egy B-t
public:
B b;
void f(); // nem rja fell a b.f() virtulis fggvnyt
};
24. Tervezs s programozs 999
void h1(D1* pd)
{
B* pb = pd; // hiba: nincs konverzi D1* -rl B* -ra
pb = &pd->b;
pb->g(); // B::g() meghvsa
pd->g(); // hiba: D1-nek nincs g() tagja
pd->b.g();
pb->f(); // B::f() meghvsa (D1::f() nem rta fell)
pd->f(); // D1::f() meghvsa
}
Vegyk szre, hogy egy osztly nem konvertlhat automatikusan (implicit mdon) sajt
tagjv, s egy osztly, mely egy msik osztly egy tagjt tartalmazza, nem rja fell a tag
virtulis fggvnyeit. Ez ellenttes a nyilvnos szrmaztatssal:
class D2 : public B { // D2 egy B
public:
void f(); // fellrja a B::f() virtulis fggvnyt
};
void h2(D2* pd)
{
B* pb = pd; // rendben: automatikus konverzi D2* -rl B* -ra
pb->g(); // B::g() meghvsa
pd->g(); // B::g() meghvsa
pb->f(); // virtulis hvs: D2::f() meghvsa
pd->f(); // D2::f() meghvsa
}
A D2 plda a D1 pldhoz kpest knyelmesebb jellst biztost, s ez olyan tnyez, ami
a megolds tlzott hasznlathoz vezethet. Emlkeztetni kell arra, hogy ezrt a knyelemrt
a B s a D2 kzti nagyobb fggssel kell fizetni (lsd 24.3.2.1). A D2-rl B-re trtn au-
tomatikus konverzirl klnsen knny megfeledkezni. Hacsak az ilyen konverzik
nem tartoznak szorosan a hasznlt osztlyok fogalombrzolshoz, kerljk a public szr-
maztatst. Ha egy osztlyt egy fogalom brzolsra hasznlunk, az rkls pedig is-a kap-
csolatot fejez ki, szinte biztos, hogy ppen ilyen konverzikra lesz szksgnk.
Vannak esetek, melyekben rklst szeretnnk, de nem engedhetjk meg, hogy konverzi
trtnjen. Vegyk egy Cfield osztly rst (controlled field, ellenrztt mez), amely
egyebeken kvl futsi idej hozzfrs-ellenrzst biztost egy msik Field osztly rsz-
re. Els rnzsre a Cfield meghatrozsa a Field-bl val szrmaztatssal ppen jnak
ltszik:
class Cfield : public Field { /* ... */ };
Tervezs a C++ segtsgvel 1000
Ez kifejezi, hogy egy Cfield valjban egyfajta Field, knyelmesebb jellst biztost, amikor
olyan Cfield fggvnyt runk, mely a Cfield Field rsznek egy tagjt hasznlja s ez a leg-
fontosabb megengedi, hogy egy Cfield fellrja a Field virtulis fggvnyeit. Az a baj, hogy
a Cfield*-rl Field*-ra trtn konverzi, amit a Cfield deklarcija sugall, meghist min-
den, a Field-hez val hozzfrs ellenrzsre irnyul ksrletet:
void g(Cfield* p)
{
*p = "asdf"; // a Field elrse a Cfield rtkad opertorval vezrelt:
// p->Cfield::operator=("asdf")
Field* q = p; // automatikus talakts Cfield*-rl Field*-ra
*q = "asdf"; // hopp! nincs Cfield-en keresztli ellenrzs
}
Egy megolds az lehetne, hogy gy hatrozzuk meg a Cfield-et, mint amelynek Field egy
tagja, de ha ezt tesszk, eleve kizrjuk, hogy Cfield fellrhassa Field virtulis fggvnyeit.
Jobb megolds, ha private rkldst hasznlunk:
class Cfield : private Field { /* ... */ };
Tervezsi szempontbl a privt szrmaztats egyenrtk a tartalmazssal, kivve a fell-
rs (alkalmanknt lnyeges) krdst. E mdszer fontos felhasznlsi terlete egy osztly
nyilvnos szrmaztatsa egy felletet ler absztrakt bzisosztlybl, valamint a privt vagy
vdett szrmaztats egy konkrt osztlybl, implementci cljbl (2.5.4, 12.3, 25.3).
Mivel a private s protected szrmaztatsbl kvetkez rklds nem tkrzdik a szr-
maztatott osztly tpusban, nha implementcis rkldsnek nevezzk a nyilvnos szr-
maztatssal szemben, melynl a bzisosztly fellete rkldik s az alaptpus automatikus
konverzija megengedett. Az utbbira nha mint altpusksztsre (subtyping) vagy fellet-
rklsre (interface inheritance) hivatkozunk.
gy is megfogalmazhatjuk ezt, hogy rmutatunk, egy szrmaztatott osztly objektuma hasz-
nlhat kell legyen mindentt, ahol bzisosztlynak objektumai hasznlhatk. Ezt nha
Liskov-fle helyettestsi elvnek (Liskov Substitution Principle) nevezik (23.6 [Liskov,
1987]). A nyilvnos/vdett/privt megklnbztets kzvetlenl tmogatja ezt, amikor
tbbalak tpusokat mutatkon s hivatkozsokon keresztl kezelnk.
24. Tervezs s programozs 1001
24.3.4.1. Tag vagy hierarchia?
Hogy tovbb vizsgljuk a tartalmazst s rklst magval von tervezsi vlasztsokat, ve-
gyk egy grdtsv (scrollbar) brzolst egy interaktv grafikus rendszerben s azt, ho-
gyan kapcsolhatunk egy grdtsvot egy ablakhoz. Ktfle grdtsvra van szksgnk:
vzszintesre s fgglegesre. Ezt kt tpussal brzolhatjuk Horizontal_scrollbar s
Vertical_scrollbar vagy egyetlen olyan Scrollbar tpussal, mely paramterknt megkapja,
vzszintes vagy fggleges-e. Az elbbi vlaszts kvetkezmnye, hogy egy harmadik t-
pusra, a sima Scrollbar-ra is szksg van, mint a kt grdtsv-tpus alaptpusra. Az utb-
bi vlaszts azt eredmnyezi, hogy egy kln paramtert kell hasznlnunk s rtkeket kell
vlasztanunk a kt tpus brzolsra:
enum Orientation { horizontal, vertical };
Vlasztsunk meghatrozza, milyen mdostsok szksgesek a rendszer bvtshez. Le-
het, hogy be kell vezetnnk egy harmadik tpus grdtsvot. Eredetileg gy gondolhat-
tuk, elg ktfle grdtsv (egy ablaknak vgl is csak kt dimenzija van), de szinte
minden esetben lehetsgesek bvtsek, melyek az jratervezs szksgessgt vonjk ma-
guk utn. Pldul lehet, hogy valaki a kt grdtsv helyett egy navigl gombot szeret-
ne hasznlni. Egy ilyen gomb klnbz irny grgetst okozna, aszerint, hol nyomja meg
a felhasznl. Fell kzpen nyomva felfel grget, bal oldalon kzpen nyomva balra,
mg a bal fels sarkot nyomva balra felfel. Az ilyen gombok nem szokatlanok.
A grdtsvok tovbbfejlesztett vltozatainak tekinthetk, s klnsen illenek olyan
programokhoz, melyekben a grgetett adatok nem csupn szvegek, hanem kpek.
Ha egy programba, melyben egy hrom grdtsvbl ll osztlyhierarchia van, egy navi-
gl gombot tesznk, ltre kell hoznunk hozz egy j osztlyt, de a grdtsv rgi kdjt
nem kell mdostanunk:
Tervezs a C++ segtsgvel 1002
Horizontal_scrollbar Vertical_scrollbar Navigation_button
Scrollbar
Ez a hierarchikus megolds szp oldala.
Ha paramterben adjuk meg a grgets irnyt, a grdtsv-objektumokban tpusmezk-
nek, a grdtsv-tagfggvnyek kdjban pedig switch utastsoknak kell szerepelnik. Ez
azt jelenti, hogy vlasztanunk kell, deklarcik vagy kd ltal fejezzk ki a rendszer szer-
kezetnek ezt a vonst. Az elbbi nveli a statikus ellenrzs fokt s azt az adatmennyi-
sget, melyet az eszkzknek fel kell dolgozniuk. Az utbbi a futsi idre halasztja el a dn-
tseket s az egyes fggvnyek mdostsval anlkl tesz lehetv vltoztatsokat, hogy
befolysoln a rendszer tfog szerkezett a tpusellenrz s ms eszkzk szempontj-
bl. A legtbb helyzetben azt javaslom, hasznljunk osztlyhierarchit a fogalmak kapcso-
latainak kzvetlen modellezsre.
Az egyetlen tpust hasznl megolds megknnyti a grdtsv fajtjt ler adatok trol-
st s tovbbtst:
void helper(Orientation oo)
{
// ...
p = new Scrollbar(oo);
// ...
}
void me()
{
helper(horizontal);
// ...
}
Ez az brzolsmd megknnyti a grgets irnynak futsi idben trtn megvltoztat-
st. Nem valszn, hogy ennek itt nagy jelentsge van, de ms hasonl pldk esetben
fontos lehet. A lnyeg, hogy mindig vlasztani kell, s a vlaszts gyakran nem knny.
24.3.4.2. Tartalmazs vagy hierarchia?
Most vegyk azt a krdst, hogyan kapcsoljunk egy grdtsvot egy ablakhoz. Ha egy
Window_with_scrollbar-t gy tekintnk, mint ami egyszerre Window s Scrollbar is, vala-
mi ilyesmit kapunk:
class Window_with_scrollbar : public Window, public Scrollbar {
// ...
};
24. Tervezs s programozs 1003
Ez megengedi, hogy egy Window_with_scrollbar gy is viselkedhessen, mint egy Scrollbar
s gy is, mint egy Window, de arra knyszert, hogy az egyetlen tpust hasznl megoldst
hasznljuk.
Msrszt, ha a Window_with_scrollbar-t egy Scrollbar-ral rendelkez Window-nak tekint-
jk, valami ilyesmit kapunk:
class Window_with_scrollbar : public Window {
// ...
Scrollbar* sb;
public:
Window_with_scrollbar(Scrollbar* p, /* ... */) : Window(/* ...*/), sb(p) { /* ... */ }
// ...
};
Ez megengedi, hogy a grdtsv-hierarchia megoldst hasznljuk. Ha a grdtsvot para-
mterben adjuk t, az ablak figyelmen kvl hagyhatja annak pontos tpust. St, egy
Scrollbar-t ahhoz hasonlan is tadhatunk, ahogy az Orientation-t a 24.3.4.1 pontban. Ha
arra van szksg, hogy a Window_with_scrollbar grdtsvknt mkdjn, hozztehe-
tnk egy konverzis mveletet:
Window_with_scrollbar::operator Scrollbar&()
{
return *sb;
}
n azt rszestem elnyben, ha az ablak a grdtsvot tartalmazza. Knnyebb egy olyan
ablakot elkpzelni, melynek grdtsvja van, mint egy olyat, amely amellett, hogy ablak,
mg grdtsv is. Kedvenc mdszerem az, hogy olyan grdtsvot hatrozok meg, amely
egy klnleges ablak, s ezt tartalmazza egy, a grdtsv-szolgltatsokat ignyl ablak.
Ez a tartalmazs hasznlatt ignyli, de emellett szl egy msik rv is, mely a lehet neki
kett is szablybl kvetkezik (24.3.4). Mivel nincs logikus indok, mirt ne lehetne egy
ablaknak kt grdtsvja (valjban sok ablaknak van vzszintes s fggleges is), nem k-
telez a Window_with_scrollbar-t a Scrollbar-bl szrmaztatni.
Vegyk szre, hogy ismeretlen osztlybl nem lehet szrmaztatni, fordtskor ismerni kell a
bzisosztly pontos tpust (12.2). Msrszt, ha egy osztly egy tulajdonsgt paramter-
ben adjuk t konstruktornak, akkor valahol az osztlyban kell, hogy legyen egy tag, mely
azt brzolja. Ha azonban ez a tag egy mutat vagy referencia, akkor a taghoz megadott
osztlybl szrmaztatott osztly egy objektumt is tadhatjuk. Az elz pldban pldul
a Scrollbar* sb tagja mutathat egy olyan Scrollbar tpusra, mint a Navigation_button, amely
a Scrollbar* felhasznlja szmra ismeretlen.
Tervezs a C++ segtsgvel 1004
24.3.5. Hasznlati kapcsolatok
Annak ismerete, hogy egy osztly milyen ms osztlyokat hasznl s milyen mdon, gyak-
ran ltfontossg a programszerkezet kifejezse s megrtse szempontjbl. Az ilyen fg-
gseket a C++ csak rejtetten (implicit mdon) tmogatja. Egy osztly csak olyan elemeket
hasznlhat, melyeket (valahol) deklarltak, de azok felsorolsa nem szerepel a C++ forrs-
kdban. Eszkzk szksgesek (vagy megfelel eszkzk hinyban gondos elolvass) az
ilyen adatok kinyershez. A mdok, ahogy egy X osztly egy Y osztlyt felhasznlhat,
tbbflekppen osztlyozhatk. me egy lehetsges vltozat:
X hasznlja az Y nevet.
X hasznlja Y-t.
X meghvja Y egy tagfggvnyt.
X olvassa Y egy tagjt.
X rja Y egy tagjt.
X ltrehoz egy Y-t.
X lefoglal egy auto vagy static Y vltozt.
X a new segtsgvel ltrehoz egy Y-t.
veszi egy Y mrett.
Az utols azrt kerlt kln kategriba, mivel ehhez ismerni kell az osztly deklarcijt,
de fggetlen a konstruktoroktl. Az Y nevnek hasznlata szintn kln mdszert jelent,
mert ehhez nmagban pldul egy Y* deklarlsnl vagy Y-nak egy kls fggvny
deklarcijban val emltsnl egyltaln nem kell hozzfrni Y deklarcijhoz (5.7):
class Y; // Y az osztly neve
Y* p;
extern Y f(const Y&);
Gyakran fontos, hogy klnbsget tegynk egy osztly felletnek (az osztly deklarci-
jnak) s az osztly megvalstsnak fggsei kztt. Egy jl tervezett rendszerben az
utbbinak ltalban jval tbb fggsge van, s azok egy felhasznl szmra sokkal ke-
vsb rdekesek, mint az osztly deklarcijnak fggsei (24.4.2). A tervezskor arra kell
trekednnk, hogy a felletek fggseinek szmt cskkentsk, mert ezekbl az osztly
felhasznlinak fggsei lesznek (8.2.4.1, 9.3.2, 12.4.1.1 s 24.4).
A C++ nem kvnja meg egy osztly ksztjtl, hogy rszletesen lerja, milyen ms oszt-
lyokat s hogyan hasznl fel. Ennek egyik oka, hogy a legjelentsebb osztlyok oly sok ms
osztlytl fggnek, hogy az olvashatsg miatt ezen osztlyok rvidtett felsorolsra pl-
dul egy #include utastsra lenne szksg. A msik ok, hogy az ilyen fggsek osztlyo-
24. Tervezs s programozs 1005
zsa nem a programozsi nyelv krdse. Az, hogy a hasznlja (use) tpus fggseket
pontosan hogyan szemlljk, fgg a tervez, a programoz vagy az eszkz cljtl, az pe-
dig, hogy mely fggsek az rdekesek, fgghet az adott fejlesztkrnyezettl is.
24.3.6. Beprogramozott kapcsolatok
Egy programozsi nyelv nem kpes kzvetlenl tmogatni s nem is szabad, hogy tmo-
gassa minden tervezsi mdszer minden fogalmt. Hasonlkppen egy tervezsi nyelv-
nek sem clszer tmogatnia minden programozsi nyelv minden tulajdonsgt. Egy terve-
zsi nyelv legyen gazdagabb s kevsb trdjn a rszletekkel, mint amennyire ez egy
rendszerprogramozsi nyelvnl szksges. Megfordtva, egy programozsi nyelvnek kpes-
nek kell lennie tbbfle tervezsi filozfia tmogatsra, klnben csorbul az alkalmazha-
tsga.
Ha egy programozsi nyelvben nincs lehetsg egy tervezsi fogalom kzvetlen brzol-
sra, nem szabad hagyomnyos lekpezst alkalmazni a tervezsi szerkezetek s a progra-
mozsi nyelv szerkezetei kztt. A tervezsi mdszer pldul hasznlhatja a delegls
(delegation) fogalmt, vagyis a terv meghatrozhatja, hogy minden olyan mveletet, amely
A osztlyhoz nincs definilva, egy p mutat ltal mutatott B osztlybeli objektum szolgltas-
son. A C++ ezt nem tudja kzvetlenl kifejezni, de rendelkezsre llnak olyan szerkezetek,
melyek segtsgvel knny elkpzelni egy programot, mely a fogalombl kdot hoz ltre.
Vegyk az albbiakat:
class B {
// ...
void f();
void g();
void h();
};
class A {
B* p;
// ...
void f();
void ff();
};
Annak meghatrozsa, hogy A osztly az A::p mutatn keresztl truhzza (deleglja)
a feladatot B-re, ilyen kdot eredmnyezne:
Tervezs a C++ segtsgvel 1006
class A {
B* p; // delegls p-n keresztl
// ...
void f();
void ff();
void g() { p->g(); } // g() delegls
void h() { p->h(); // h() delegls
};
Egy programoz szmra nyilvnval, hogy mi trtnik itt, de vilgos, hogy egy tervezsi
fogalom kddal val utnzsa kevesebb, mint valamely egy az egyhez megfeleltets.
Az ilyen beprogramozott kapcsolatokat a programozsi nyelv nem rti olyan jl, ezrt
nehezebb azokat eszkzkkel tmogatni. A szabvnyos eszkzk pldul nem ismernk
fel, hogy az A::p-n keresztl A feladatt ruhztuk t B-re, nem pedig ms clra hasznltuk
a B*-ot.
Ahol csak lehetsges, hasznljunk egy az egyhez lekpezst a tervezs s a programoz-
si nyelv fogalmai kztt. Az ilyen lekpezs biztostja az egyszersget, s azt, hogy a prog-
ram valban tkrzi a tervezst, hogy a programozk s az eszkzk hasznosthassk an-
nak fogalmait. A beprogramozott kapcsolatokkal rendelkez osztlyok kifejezst a nyelv
konverzis mveletekkel segti. Ez azt jelenti, hogy az X::operator Y() konverzis opertor
meghatrozza, hogy ahol elfogadhat egy Y, ott hasznlhatunk egy X-et is (11.4.1).
A Y::Y(X) konstruktor ugyanezt a kapcsolatot fejezi ki. Vegyk szre, hogy a konverzis
opertorok (s a konstruktorok) j objektumokat hoznak ltre, nem pedig ltez objektu-
mok tpust vltoztatjk meg. Egy konverzis fggvny deklarlsa Y-ra nem ms, mint egy
Y-t visszaad fggvny rejtett alkalmazsnak egy mdja. Mivel a konstruktorok s
konverzis opertorok ltal definilt talaktsok automatikus alkalmazsa csalka lehet,
nha hasznos, ha a terven bell kln elemezzk azokat.
Fontos, hogy biztostsuk, hogy egy program konverzis diagramjai ne tartalmazzanak cik-
lusokat. Ha tartalmaznak, az ebbl add tbbrtelmsgek megakadlyozzk, hogy a cik-
lusokban szerepl tpusokat egytt hasznlhassuk:
class Rational;
class Big_int {
public:
friend Big_int operator+(Big_int,Big_int);
operator Rational();
// ...
};
24. Tervezs s programozs 1007
class Rational {
public:
friend Rational operator+(Rational,Rational);
operator Big_int();
// ...
};
A Rational s a Big_int tpusok nem fognak olyan grdlkenyen egyttmkdni, mint
remlnnk:
void f(Rational r, Big_int i)
{
g(r+i); // hiba, tbbrtelm: operator+(r,Rational(i)) vagy operator+(Big_int(r),i) ?
g(r+Rational(i)); // explicit felolds
g(Big_int(r)+i); // msik explicit felolds
}
Az ilyen klcsns konverzikat elkerlhetjk, ha legalbb nhnyat kzlk explicitt
tesznk. A Big_int talaktsa Rational-l pldul konverzis opertor helyett definilhat
lett volna make_Rational()-knt, az sszeadst pedig g(Big_int,i)-re lehetett volna felol-
dani. Ahol nem kerlhetk el az ilyen klcsns konverzik, a keletkezett ellentmond-
sokat vagy explicit talaktsokkal (lsd fent), vagy ktoperandus opertorok (pl. +) tbb
kln vltozatnak deklarlsval kell feloldanunk.
24.3.7. Osztlyon belli kapcsolatok
Az osztlyok a megvalsts mdjt (s a rossz megoldsokat) csaknem teljes egszben el-
rejthetik s nha el is kell rejtenik. A legtbb osztly objektumainak azonban szablyos
szerkezete van s oly mdon kezelhet, amit meglehetsen knny lerni. Egy osztly va-
lamely objektuma alobjektumok (tagok) gyjtemnye, melyek kzl sok ms objektumok-
ra mutat vagy hivatkozik. Egy objektum teht gy tekinthet, mint egy objektumfa gyke-
re, a benne szerepl objektumok pedig mint egy objektum-hierarchia alkotelemei, ami
az osztlyhierarchia kiegsztje (lsd 25.3.2.1). Vegynk pldul egy nagyon egyszer
String-et:
class String {
int sz;
char* p;
public:
String(const char* q);
~String();
// ...
};
Tervezs a C++ segtsgvel 1008
Egy String objektum grafikusan gy brzolhat:
24.3.7.1. Invarinsok
A tagok s a tagok ltal hivatkozott objektumok rtkeit gyjtnven az objektum llapo-
tnak (vagy egyszeren rtknek) nevezzk. Egy osztly tervezsnl fontos az objektu-
mok pontosan definilt llapotba hozsa (kezdeti rtkads/ltrehozs), ezen llapot fenn-
tartsa a mveletek vgrehajtsa sorn, s vgl az objektumok megfelel megsemmistse.
Az a tulajdonsg, amely pontosan definilt teszi egy objektum llapott, az objektum in-
varinsa (llapotbiztostja, invariant).
A kezdeti rtkads clja teht az objektumot abba az llapotba helyezni, amelyet az
invarins ler. Ezt ltalban egy konstruktorral vgezzk. Egy osztlyon vgzett minden m-
velet felttelezheti, hogy belpskor rvnyesnek tallja az llapotot (vagyis hogy az inva-
rins logikai rtke igaz) s kilpskor ugyanez a helyzet ll fenn. Az invarinst vgl
a destruktor rvnytelenti, az objektum megsemmistsvel. A String::String(const char*)
konstruktor pldul biztostja, hogy p egy legalbb sz+1 elem tmbre mutat, ahol sz-nek
rtelmes rtke van s p[sz]==0. Ezt az lltst minden karakterlnc-mveletnek igaznak
kell hagynia.
Az osztlytervezsben a legtbb szakrtelem ahhoz kell, hogy az adott osztlyt elg egysze-
rv tegyk, hogy hasznlhat, egyszeren kifejezhet invarinssal valsthassuk meg.
Elg knny kijelenteni, hogy minden osztlynak szksge van invarinsra. Ami nehz, egy
hasznlhat invarinssal elhozakodni, amely knnyen rthet s nem jr elfogadhatat-
lan megszortsokkal a ksztre vagy a mveletek hatkonysgra vonatkozan. Vegyk
szre, hogy az invarinst itt egy olyan kdrszlet megjellsre hasznljuk, melyet futtat-
hatunk egy objektum llapotnak ellenrzsre. Vilgos, hogy be lehetne vezetni egy szi-
gorbb, matematikaibb s bizonyos krnyezetben megfelelbb fogalmat is. Az inva-
rins, ahogyan itt trgyaljuk, az objektum llapotnak egyfajta gyakorlati s emiatt
ltalban gazdasgos, de logikailag nem tkletes ellenrzse.
24. Tervezs s programozs 1009
int sz ;
char* p ;
...elemek...\0
Az invarins fogalma Floyd, Naur s Hoare elfelttelekkel s utfelttelekkel foglalkoz
munkibl ered s lnyegben minden absztrakt adattpusokrl s programhelyessg-el-
lenrzsrl szl munka emlti, amit az utbbi 30 vben rtak, gy a C hibakeressnek is
fontos elemt kpezi. Az invarins ellenrzsre a tagfggvnyek vgrehajtsa kzben l-
talban nem kerl sor. Azok a fggvnyek, melyek meghvhatk, mikzben az invarins r-
vnytelen, nem lehetnek a nyilvnos fellet rszei; e clra a privt s vdett fggvnyek
szolglhatnak.
Hogyan fejezhetjk ki egy C++ programban az invarins fogalmt? Erre egy egyszer md
egy invarins-ellenrz fggvnyt megadni s a nyilvnos mveletekbe e fggvny megh-
vst beiktatni:
class String {
int sz;
char* p;
public:
class Range {}; // kivtelosztly
class Invariant {}; // kivtelosztly
enum { TOO_LARGE = 16000 }; // mrethatr
void check(); // llapot-ellenrzs
String(const char* q);
String(const String&);
~String();
char& operator[](int i);
int size() { return sz; }
// ...
};
void String::check()
{
if (p==0 || sz<0 || TOO_LARGE<=sz || p[sz]) throw Invariant();
}
char& String::operator[](int i)
{
check(); // ellenrzs belpskor
if (i<0 || sz<=i) throw Range();
// itt vgezzk a tnyleges munkt
check(); // ellenrzs kilpskor
return p[i];
}
Tervezs a C++ segtsgvel 1010
Ez szpen fog mkdni s alig jelent munkt a programoz szmra. Viszont egy egyszer
osztlynl, mint a String, az llapotellenrzs dnten befolysolja a futsi idt s lehet,
hogy mg a kd mrett is. Ezrt a programozk gyakran csak hibakeress alatt hajtjk vg-
re az invarins ellenrzst:
inline void String::check()
{
#ifndef NDEBUG
if (p==0 || sz<0 || TOO_LARGE<=sz || p[sz]) throw Invariant();
#endif
}
Itt az NDEBUG makrt hasznljuk, hasonl mdon, mint ahogy azt a szabvnyos C assert()
makr hasznlja. Az NDEBUG rendszerint azt jelzi, hogy nem trtnik hibakeress.
Az invarinsok megadsa s hibakeress alatti hasznlata felbecslhetetlen segtsget jelent
a kd belvsnl s ami mg fontosabb akkor, amikor az osztlyok ltal brzolt fo-
galmakat pontosan meghatrozott s szablyoss tesszk. A lnyeg az, hogy az invarin-
sok beptsvel az osztlyokat ms szempontbl vizsglhatjuk s a kdban ellenrzst he-
lyezhetnk el. Mind a kett nveli a valsznsgt, hogy megtalljuk a hinyossgokat,
kvetkezetlensgeket s tvedseket.
24.3.7.2. Felttelezsek
Az invarins kd egyfajta felttelezs (assertion), ami viszont nem ms, mint annak a kije-
lentse, hogy egy adott logikai felttelnek teljeslnie kell. Az a krds, mit kell tenni, ami-
kor nem teljesl.
A C standard knyvtra s kvetkezskppen a C++ standard knyvtra tartalmazza az
assert() makrt a <cassert>-ben vagy <assert.h>-ban. Az assert() kirtkeli a paramtert s
meghvja az abort()-ot, ha az eredmny nulla (vagyis hamis):
void f(int* p)
{
assert(p!=0); // felttelezi, hogy p!=0; abort() meghvsa, ha p nulla
// ...
}
A program megszaktsa eltt az assert() kirja sajt forrsfjljnak nevt s annak a sornak
a szmt, amelyben elfordult. Ezltal az assert() hasznos hibakeres eszkzt jelent.
Az NDEBUG belltsa rendszerint fordti utastsok segtsgvel trtnik, fordtsi egys-
24. Tervezs s programozs 1011
genknt. Ebbl kvetkezik, hogy az assert() hasznlata tilos olyan helyben kifejtett (inline)
fggvnyekben s sablon fggvnyekben, melyek tbb fordtsi egysgben is elfordul-
nak, hacsak nem fordtunk igen nagy gondot az NDEBUG kvetkezetes belltsra (9.2.3).
Mint minden makr-varzslat, az NDEBUG hasznlata is tl alacsony szint, rendetlen s
hibaforrst jelenthet. ltalban j tlet, ha mg a legjobban ellenrztt programban is ha-
gyunk nhny ellenrz kdot; az NDEBUG viszont erre nem nagyon alkalmas, s az
abort() meghvsa is ritkn fogadhat el a vgleges kdban.
A msik megolds egy Assert() sablon hasznlata, mely a programbl val kilps helyett
egy kivtelt vlt ki, gy kvnsgra a vgleges kdban bennmaradhatnak az ellenrzsek.
Sajnos a standard knyvtr nem gondoskodik ilyen Assert()-rl, de mi knnyen elkszthet-
jk:
template<class X, class A> inline void Assert(A assertion)
{
if (!assertion) throw X();
}
Az Assert() egy X() kivtelt vlt ki, ha az assertion hamis:
class Bad_arg { };
void f(int* p)
{
Assert<Bad_arg>(p!=0); // felttelezi, hogy p!=0; Bad_arg kivtelt vlt ki, hacsak p!=0
// ...
}
Az ellenrzsben a felttelt pontosan meg kell hatroznunk, teht ha csak hibakeress alatt
akarunk ellenrizni, jeleznnk kell e szndkunkat:
void f2(int* p)
{
Assert<Bad_arg>(NDEBUG || p!=0); // vagy nem hibakeress folyik vagy p!=0
// ...
}
A || hasznlata az ellenrzsben && helyett meglepnek tnhet, de az Assert<E> (a||b)
a !(a||b)-t ellenrzi, ami !a&&!b.
Az NDEBUG ilyen mdon val hasznlata megkveteli, hogy az NDEBUG-nak megfelel r-
tket adjunk, aszerint, hogy akarunk-e hibakeresst vgezni vagy sem. A C++ egyes vlto-
zatai alaprtelmezs szerint nem teszik ezt meg neknk, ezrt jobb sajt rtket hasznlni:
Tervezs a C++ segtsgvel 1012
#ifdef NDEBUG
const bool ARG_CHECK = false; // nem hibakeress: ellenrzs kikapcsolsa
#else
const bool ARG_CHECK = true; // hibakeress
#endif
void f3(int* p)
{
Assert<Bad_arg>(!ARG_CHECK || p!=0); // vagy nem hibakeress folyik vagy p!=0
// ...
}
Ha egy ellenrzssel kapcsolatos kivtelt nem kapunk el, az Assert() lelltja a programot
(terminate()), hasonlan ahhoz, ahogyan a megfelel assert() abort()-tal jrna. Egy kivtel-
kezel azonban kpes lehet valamilyen kevsb durva beavatkozsra.
A valsgos mret programokban azon veszem szre magamat, hogy egyes csoportokban
be- s kikapcsolom a felttelezseket, aszerint, hogy kell-e tesztelni. Ennek az eljrsnak az
NDEBUG hasznlata a legnyersebb formja. A fejleszts elejn a legtbb ellenrzs be van
kapcsolva, mg az tadott kdban csak a legfontosabbak megengedettek. Ez akkor rhet
el a legknnyebben, ha a tnyleges ellenrzsek kt rszbl llnak, ahol az els egy meg-
enged felttel (mint az ARG_CHECK), s csak a msodik maga a felttelezs.
Amennyiben a megenged felttel konstans kifejezs, az egsz ellenrzs kimarad a ford-
tsbl, ha nincs bekapcsolva. Lehet azonban vltoz is, mely a hibakeress szksgletei
szerint futsi idben be- s kikapcsolhat:
bool string_check = true;
inline void String::check()
{
Assert<Invariant>(!string_check || (p && 0<=sz && sz<TOO_LARGE && p[sz]==0));
}
void f()
{
String s = "csoda";
// a karakterlncokat itt ellenrizzk
string_check = false;
// itt a karakterlncokat nem ellenrizzk
}
24. Tervezs s programozs 1013
Termszetesen ilyen esetekben ltrejn a megfelel programkd, teht gyelnnk kell, ne-
hogy a program meghzzon az ilyen ellenrzsek kiterjedt hasznlattl.
Ha azt mondjuk:
Assert<E>(a);
az egyszeren egy msik mdja ennek:
if (!a) throw E();
gy viszont felmerl a krds: minek bajldjunk az Assert()-tel az utasts kzvetlen kirsa
helyett? Azrt, mert az Assert() hasznlata nyilvnvalv teszi a tervez szndkt. Azt feje-
zi ki, hogy valamirl felttelezzk, hogy mindig igaz. Ez nem a program logikjnak lnyeg-
telen rsze, hanem rtkes informci a program olvasja szmra. Gyakorlati elnye, hogy
egy assert()-et vagy Assert()-et knny megtallni, mg a kivteleket kivlt feltteles utas-
tsokat nehz.
Az Assert() ltalnosthat olyan kivtelek kivltsra is, melyek paramtereket vagy kiv-
tel-vltozkat vehetnek t:
template<class A, class E> inline void Assert(A assertion, E except)
{
if (!assertion) throw except;
}
struct Bad_g_arg {
int* p;
Bad_g_arg(int* pp) : p(pp) { }
};
bool g_check = true;
int g_max = 100;
void g(int* p, exception e)
{
Assert(!g_check || p!=0, e); // a mutat rvnyes
Assert(!g_check || (0<*p&&*p<=g_max),Bad_g_arg(p)); // az rtk sszer
// ...
}
Sok programban dnt fontossg, hogy ne jjjn ltre kd olyan Assert() esetn, ahol az
ellenrzs a fordts sorn kirtkelhet. Sajnos egyes fordtk az ltalnostott Assert()-nl
Tervezs a C++ segtsgvel 1014
ezt kptelenek elrni. Kvetkezskppen a ktparamter Assert()-et csak akkor hasznl-
juk, ha a kivtel nem E() alak vagy ha valamilyen, az ellenrztt rtktl fggetlen kdot
kell kszteni.
A 23.4.3.5 pontban emltettk, hogy az osztlyhierarchia tszervezsnek kt legkzns-
gesebb formja az osztly ketthastsa, illetve kt osztly kzs rsznek kiemelse egy
bzisosztlyba. Az tszervezs lehetsgt mindkt esetben megfelel invarinsokkal biz-
tosthatjuk. Viszont ha sszehasonltjuk az invarinst a mveletek kdjval, egy hastsra
rett osztlyban az llapotellenrzst flslegesnek tallhatjuk. Ilyen esetekben a mvele-
tek rszhalmazai csak az objektum llapotnak rszhalmazaihoz fognak hozzfrni. Meg-
fordtva, azok az osztlyok, melyek sszefslhetk, hasonl invarinsokkal rendelkeznek
akkor is, ha megvalstsuk klnbzik.
24.3.7.3. Elfelttelek s utfelttelek
A felttelezsek (assert) egyik npszer hasznlata egy fggvny elfeltteleinek s utfel-
tteleinek kifejezse. Ez azt jelenti, hogy ellenrizzk a bemenetre vonatkoz alapfelttele-
zsek rvnyessgt s azt, hogy kilpskor a fggvny a programot a vrt llapotban hagy-
ja-e. Sajnos amit a felttelezssel ki szeretnnk fejezni, gyakran magasabb szinten van, mint
aminek a hatkony kifejezsre a programozsi nyelv lehetsget ad:
template<class Ran> void sort(Ran first, Ran last)
{
Assert<Bad_sequence>("A [first,last) rvnyes sorozat"); // lkd
// ... rendez algoritmus ...
Assert<Failed_sort>("A [first,last) nvekv sorrend"); // lkd
}
Ez alapvet problma. Amit egy programrl feltteleznk, az matematikai alap, magasabb
szint nyelven fejezhet ki a legjobban, nem azon az algoritmusokra pl programozsi
nyelven, amelyben a programot rjuk.
Az invarinsok meghatrozshoz hasonlan a felttelezsek megfogalmazsa is bizonyos
fok gyessget ignyel, mert az ellenrizni kvnt lltst gy kell megadnunk, hogy az
egy algoritmussal valban ellenrizhet legyen:
24. Tervezs s programozs 1015
template<class Ran> void sort(Ran first, Ran last)
{
// A [first,last) rvnyes sorozat; a hihetsg ellenrzse:
Assert<Bad_sequence>(NDEBUG || first<=last);
// ... rendez algoritmus ...
// A [first,last) nvekv sorrend; prba-ellenrzs:
Assert<Failed_sort>(NDEBUG ||
(last-first<2 || (*first<=last[-1]
&& *first<=first[(last-first)/2] && first[(last-first)/2]<=last[-1])));
}
n gyakran egyszerbbnek tallom a kznsges kdellenrz paramterek s eredm-
nyek hasznlatt, mint a felttelezsek sszelltst. Fontos azonban, hogy megprbljuk
a vals (idelis) el- s utfeltteleket kifejezni s legalbb megjegyzsek formjban do-
kumentlni azokat mieltt olyan, kevsb absztrakt mdon brzolnnk, ami a programo-
zsi nyelven hatkonyan kifejezhet.
Az elfelttelek ellenrzse knnyen egyszer paramterrtk-ellenrzss fajulhat. Mivel
a paramterek gyakran tbb fggvnyen keresztl addnak t, az ellenrzs ismtldv
s kltsgess vlhat. Annak az egyszer kijelentse azonban, hogy minden fggvnyben
minden mutat-paramter nem nulla, nem sokat segt, s hamis biztonsgrzetet adhat
klnsen akkor, ha a tbbletterhels elkerlse vgett az ellenrzseket csak hibakeress
alatt vgezzk. Ez az egyik f oka, hogy javaslom, fordtsunk figyelmet az invarinsokra.
24.3.7.4. Betokozs
Vegyk szre, hogy a C++-ban az osztlyok nem az egyes objektumok a betokozs
(enkapszulci, encapsulation) alapegysgei:
class List {
List* next;
public:
bool on(List*);
// ...
};
bool List::on(List* p)
{
if (p == 0) return false;
for(List* q = this; q; q=q->next) if (p == q) return true;
return false;
}
Tervezs a C++ segtsgvel 1016
A privt List::next mutat betokozsa elfogadhat, mivel a List::on() hozzfr a List osztly
minden objektumhoz, amelyre hivatkozni tud. Ahol ez knyelmetlen, egyszersthetnk,
ha nem hasznljuk ki azt a kpessget, hogy egy tagfggvnybl ms objektumok brzo-
lshoz hozz lehet frni:
bool List::on(List* p)
{
if (p == 0) return false;
if (p == this) return true;
if (next==0) return false;
return next->on(p);
}
Ez azonban a bejrst (itercit) ismtlss (rekurziv) alaktja t, ami komoly teljest-
mnyromlst okozhat, ha a fordt nem kpes az optimlis mkdshez az eljrst vissza-
alaktani.
24.4. Komponensek
A tervezs egysgei az osztlyok, fggvnyek stb. gyjtemnyei, nem egy egyes osztlyok.
Ezeket a gyjtemnyeket gyakran knyvtrnak (library) vagy keretrendszernek
(framework) nevezzk (25.8), s az jrahasznosts (23.5.1) s karbantarts is ezeket he-
lyezi a kzppontba. A logikai felttelek ltal sszekapcsolt szolgltatsok halmazt jelen-
t fogalom kifejezsre a C++ hrom mdot biztost:
1. Osztly ltrehozsa, mely adat-, fggvny-, sablon- s tpustagok gyjtemnyt
tartalmazza.
2. Osztlyhierarchia ltrehozsa, mely osztlyok gyjtemnyt foglalja magban.
3. Nvtr meghatrozsa, mely adat-, fggvny-, sablon- s tpustagok gyjtem-
nybl ll.
Az osztlyok szmos lehetsget biztostanak arra, hogy knyelmesen hozhassunk ltre l-
taluk meghatrozott tpus objektumokat. Sok jelents komponens azonban nem rhat le
gy, mint adott tpus objektumokat ltrehoz rendszer. Az osztlyhierarchia egymssal ro-
kon tpusok halmaznak fogalmt fejezi ki. A komponensek egyes tagjainak kifejezsre
azonban nem mindig az osztlyok jelentik a legjobb mdszert s nem minden osztly il-
leszthet hasonlsg alapjn osztlyhierarchiba (24.2.5). Ezrt a komponens fogalmnak
24. Tervezs s programozs 1017
a C++-ban a nvtr a legkzvetlenebb s legltalnosabb megtestestse. A komponense-
ket nha osztlykategriknak nevezzk, de nem minden esetben llnak osztlyokbl (s
ez nem is elrs).
Idelis esetben egy komponens a megvalstsra hasznlt felletekkel, valamint azokkal
a felletekkel rhat le, melyeket felhasznli rszre biztost. Minden ms rszletkrds
s a rendszer tbbi rsze szmra rejtett. A tervez szemszgbl ez a komponens megha-
trozsa. A programoz ezt a fogalmat deklarcikra lekpezve brzolhatja. Az osztlyok
s osztlyhierarchik adjk a felleteket, melyek csoportostsra a nvterek adnak lehet-
sget, s ugyancsak a nvterek biztostjk, hogy a programoz elvlaszthassa a felhasznlt
felleteket a szolgltatottaktl. Vegyk az albbi pldt:
A 8.2.4.1-ben lert mdszerek felhasznlsval ebbl az albbi lesz:
namespace A { // az X fellete ltal hasznlt szolgltatsok
// ...
}
namespace X { // az X komponens fellete
using namespace A; // az A deklarciitl fgg
// ...
void f();
}
namespace X_impl { // az X megvalstshoz szksges szolgltatsok
using namespace X;
// ...
}
Tervezs a C++ segtsgvel 1018
X fellete ltal hasznlt X megvalstsa ltal hasznlt
X fellete
X megvalstsa
void X::f()
{
using namespace X_impl; // az X_impl deklarciitl fgg
// ...
}
Az X ltalnos fellet nem fgghet az X_impl megvalstsi fellettl.
Egy komponensnek szmos olyan osztlya lehet, melyek nem ltalnos hasznlatra szn-
tak. Az ilyen osztlyokat megvalst osztlyokba vagy nvterekbe kell elrejteni:
namespace X_impl { // az X megvalstsnak rszletei
class Widget {
// ...
};
// ...
}
Ez biztostja, hogy a Widget-et nem hasznlhatjuk a program ms rszeibl. Az egysges fo-
galmakat brzol osztlyok ltalban jrahasznosthatk, ezrt rdemes befoglalni azokat
a komponens felletbe:
class Car {
class Wheel {
// ...
};
Wheel flw, frw, rlw, rrw;
// ...
public:
// ...
};
A legtbb krnyezetben el kell rejtennk a kerekeket (Wheel), hogy megtartsuk az aut
(Car) brzolsnak pontossgt (egy valdi aut esetben sem tudjuk a kerekeket fgget-
lenl mkdtetni). Maga a Wheel osztly viszont alkalmasnak tnik szlesebb kr haszn-
latra, gy lehet, hogy jobb a Car osztlyon kvlre tenni:
class Wheel {
// ...
};
24. Tervezs s programozs 1019
class Car {
Wheel flw, frw, rlw, rrw;
// ...
public:
// ...
};
A dnts, hogy begyazzuk-e az egyik fogalmat a msikba, attl fgg, mik a tervezs cljai
s mennyire ltalnosak a fogalmak. Mind a begyazs, mind a nem begyazs szles kr-
ben alkalmazhat mdszer. Az alapszably az legyen, hogy az osztlyokat a lehet legnl-
lbb (helyiv) tesszk, amg ltalnosabb elrsket szksgesnek nem ltjuk.
Az rdekes fggvnyeknek s adatoknak van egy kellemetlen tulajdonsga, mgpedig az,
hogy a globlis nvtr fel, a szles krben hasznlt nvterekbe vagy a hierarchik gykr-
osztlyaiba trekednek. Ez knnyen vezethet a megvalsts rszleteinek nem szndkos
nyilvnoss ttelhez s a globlis adatokkal s globlis fggvnyekkel kapcsolatos prob-
lmkhoz. Ennek valsznsge az egyetlen gyker hierarchikban s az olyan progra-
mokban a legnagyobb, ahol csak nagyon kevs nvteret hasznlunk. E jelensg lekzds-
re az osztlyhierarchikkal kapcsolatban a virtulis bzisosztlyokat (15.2.4) hasznlhatjuk
fel, a nvtereknl pedig a kis implementcis nvterek kialaktsa lehet megolds.
Megjegyzend, hogy a fejllomnyok (header) komoly segtsget nyjthatnak abban, hogy
a komponenseket felhasznliknak klnbz szempontbl mutathassuk, s kizrjk
azokat az osztlyokat, melyek a felhasznl szempontjbl a megvalsts rszeinek tekint-
hetk (9.3.2).
24.4.1. Sablonok
A tervezs szempontjbl a sablonok (template) kt, laza kapcsolatban lv szksgletet
szolglnak ki:
ltalnostott (generic) programozs
Eljrsmdot (policy) meghatroz paramterezs
A tervezs korai szakaszban a mveletek csupn mveletek. Ksbb, amikor az
operandusok tpust meg kell hatrozni, az olyan, statikus tpusokat hasznl programoz-
si nyelvekben, mint a C++, a sablonok lnyeges szerepet kapnak. Sablonok nlkl a fgg-
vny-defincikat jra meg jra meg kellene ismtelni, vagy az ellenrzseket a futsi id-
re kellene elhalasztani (24.2.3). Azokat a mveleteket, amelyek egy algoritmust tbbfle
operandustpusra kell, hogy lerjanak, clszer sablonknt elkszteni. Ha az sszes ope-
Tervezs a C++ segtsgvel 1020
randus egyetlen osztlyhierarchiba illeszthet (s klnsen akkor, ha futsi idben szk-
sg van j operandustpusok hozzadsra is), az operandustpusok osztlyknt, mgpedig
ltalban absztrakt osztlyknt brzolhatk a legjobban. Ha az operandustpusok nem il-
lenek bele egyetlen hierarchiba (s klnsen akkor, ha a futsi idej teljestmny ltfon-
tossg), a mveletet legjobb sablonknt elkszteni. A szabvnyos trolk s az azokat t-
mogat algoritmusok annak pldi, amikor a tbbfle, nem rokon tpus operandus
hasznlatnak s a futsi idej teljestmny fokozsnak egyidej szksgessge vezet sab-
lonok hasznlathoz (16.2). Vegyk egy egyszer bejrs ltalnostst, hogy jobban
szemgyre vehessk a sablon vagy hierarchia? dilemmt:
void print_all(Iter_for_T x)
{
for (T* p = x.first(); p; p = x.next()) cout << *p;
}
Itt azzal a felttelezssel ltnk, hogy az Iter_for_T olyan mveleteket biztost, melyek
T*-okat hoznak ltre.
Az Iter_for_T-t sablonparamterr tehetjk:
template<class Iter_for_T> void print_all(Iter_for_T x)
{
for (T* p = x.first(); p; p = x.next()) cout << *p;
}
Ez lehetsget ad arra, hogy egy sereg egymssal nem rokon bejrt (iterator) hasznljunk,
ha ezek mindegyike biztostja a helyes jelents first()-t s next()-et s ha fordtsi idben
minden print_all() hvs bejrjnak tpust ismerjk. A standard knyvtr troli s algo-
ritmusai ezen az tleten alapulnak.
Felhasznlhatjuk azt a megfigyelst is, hogy a first() s a next() a bejrk szmra felletet
alkotnak s ltrehozhatunk egy osztlyt, amely ezt az felletet kpviseli:
class Iter {
public:
virtual T* first() const = 0;
virtual T* next() = 0;
};
void print_all2(Iter& x)
{
for (T* p = x.first(); p; p = x.next()) cout << *p;
}
24. Tervezs s programozs 1021
Most mr az Iter-bl szrmaztatott valamennyi bejrt hasznlhatjuk. A kdok azonosak,
fggetlenl attl, hogy sablonokat vagy osztlyhierarchit hasznlunk a paramterezs b-
rzolsra csak a futsi id, jrafordts stb. tekintetben klnbznek. Az Iter osztly k-
lnsen alkalmas arra, hogy az albbi sablon paramtere legyen:
void f(Iter& i)
{
print_all(i); // a sablon hasznlata
print_all2(i);
}
Kvetkezskppen a kt megkzelts egymst kiegsztnek tekinthet.
Egy sablonnak gyakran van szksge arra, hogy fggvnyeket s osztlyokat gy hasznl-
jon, mint megvalstsnak rszeit. Soknak kzlk maguknak is sablonoknak kell lenni-
k, hogy megtartsuk az ltalnossgot s a hatkonysgot. gy az algoritmusok tbb tpus-
ra ltalnosthatk. A sablonok hasznlatnak ezt a mdjt nevezzk ltalnostott vagy
generikus programozsnak (2.7). Ha az std::sort()-ot egy vector-ra hvjuk meg, a sort()
operandusai a vektor elemei lesznek, vagyis a sort() az egyes elemtpusokra ltalnostott.
A szabvnyos rendez fggvny emellett ltalnos a troltpusokra nzve is, mert br-
mely, a szabvnyhoz igazod trol bejrjra meghvhat (16.3.1).
A sort() algoritmus az sszehasonltsi felttelekre is paramterezett (18.7.1). Tervezsi
szempontbl ez ms, mint amikor vesznk egy mveletet s ltalnostjuk az operandus t-
pusra. Sokkal magasabb szint tervezsi dnts, hiszen egy objektumon (vagy mveleten)
dolgoz algoritmust gy paramtereznk, hogy az az algoritmus mkdsmdjt vezrli.
Ez egy olyan dnts, ami rszben a tervez/programoz kezbe adja a vezrlst az algorit-
mus mkdsi elveinek kialaktsban.
24.4.2. Fellet s megvalsts
Az idelis fellet
teljes s egysges fogalomkszletet ad a felhasznlnak,
a komponensek minden rszre nzve kvetkezetes,
nem fedi fel a megvalsts rszleteit a felhasznlnak,
tbbflekppen elkszthet,
tpusai fordtsi idben ellenrizhetk,
alkalmazsszint tpusok hasznlatval kifejezett,
ms felletektl korltozott s pontosan meghatrozott mdokon fgg.
Tervezs a C++ segtsgvel 1022
Miutn megjegyeztk, hogy az osztlyok kztt, melyek a klvilg fel a komponens fel-
lett kpezik, kvetkezetessgre van szksg (24.4), a trgyalst egyetlen osztlyra egysze-
rsthetjk. Vegyk az albbi pldt:
class Y { /* ... */ }; // X ltal ignyelt
class Z { /* ... */ }; // X ltal ignyelt
class X { // plda a szegnyes felletre
Y a;
Z b;
public:
void f(const char * ...);
void g(int[],int);
void set_a(Y&);
Y& get_a();
};
Ennl a felletnl tbb problma is felmerlhet:
A fellet Y s Z tpust gy hasznlja, hogy a fordtnak ismernie kell Y s Z
deklarcijt.
Az X::f() fggvny tetszleges szm ismeretlen tpus paramtert vehet t
(valsznleg egy, az els paramterben megkapott formzott karakterlnc
ltal vezrelt mdon, 21.8).
Az X::g() egy int[] paramtert vesz t. Ez elfogadhat lehet, de annak a jele,
hogy az elvonatkoztats szintje tl alacsony. Egy egszekbl ll tmb nem
nler, teht nem magtl rtetd, hny eleme van.
A set_a()s get_a() fggvnyek valsznleg felfedik X osztly objektumainak
brzolst, azltal, hogy X::a-hoz kzvetlen hozzfrst tesznek lehetv.
Ezek a tagfggvnyek nagyon alacsony elvonatkoztatsi (fogalmi) szinten nyjtanak felle-
tet. Az ilyen szint felletekkel rendelkez osztlyok lnyegben egy nagyobb komponens
megvalstsnak rszletei kz tartoznak ha egyltaln tartoznak valahov. Idelis eset-
ben egy felletfggvny paramtere elg informcit tartalmaz ahhoz, hogy nler legyen.
Alapszably, hogy a krelmek vkony drton t legyenek tovbbthatak a tvoli kiszol-
gl fel.
A C++ a programoznak lehetsget ad arra, hogy az osztlyokat a fellet rszeknt br-
zolja. Az brzols lehet rejtett (private vagy protected hasznlatval), de a fordt megen-
gedi automatikus vltozk ltrehozst, fggvnyek helyben kifejtst stb. is. Ennek nega-
tv hatsa, hogy az osztlytpusok hasznlata az osztly brzolsban nemkvnatos
24. Tervezs s programozs 1023
fggseket idzhet el. Az, hogy problma-e Y s Z tpusok tagjainak hasznlata, attl fgg,
valjban milyen fajta tpus Y s Z. Ha egyszer tpusok, mint a list, a complex vagy a string,
hasznlatuk ltalban teljesen megfelel. Az ilyen tpusok stabilnak minslnek s osztly-
deklarciik beptse a fordt rszre elfogadhat terhels. Ha azonban Y s Z maguk is
jelents komponensek (pldul egy grafikus rendszer vagy egy banki kezelrendszer osz-
tlyai), blcsebb lenne, ha nem fggnnk tlk tl kzvetlenl. Ilyen esetekben gyakran
jobb vlaszts egy mutat vagy referencia hasznlata:
class Y;
class Z;
class X { // X csak mutatn s referencin t ri el Y-t s Z-t
Y* a;
Z& b;
// ...
};
Ez levlasztja X defincijt Y s Z definciirl, vagyis X defincija csak Y s Z nevtl
fgg. X megvalstsa termszetesen mg mindig fgg Y s Z defincijtl, de ez nem rin-
ti X felhasznlit.
Ezzel egy fontos dolgot szemlltettnk: egy felletnek, mely jelents adatmennyisget rejt
el ahogyan ez egy hasznlhat fellettl elvrhat sokkal kevesebb lesz a fggse, mint
az ltala elrejtett megvalstsnak. Az X osztly defincija pldul anlkl fordthat le,
hogy Y s Z definciihoz hozzfrnnk. A fggsek elemzsekor kln kell kezelni a fel-
let s kln a megvalsts fggseit. Mindkt esetben idelis, ha a rendszer fggsgi b-
ri irnytott, krmentes grfok, melyek megknnytik a rendszer megrtst s tesztelst.
Ennek elrse a felleteknl fontosabb (s knnyebb is), mint a megvalstsnl.
Vegyk szre, hogy egy osztly hrom felletet rhat le:
class X {
private:
// csak tagok s bart fggvnyek szmra elrhet
protected:
// csak tagok s "bartok", valamint
// a szrmaztatott osztly tagjai szmra elrhet
public:
// ltalnosan elrhet
};
Ezenkvl mg a nyilvnos fellet rszt kpezik a bartok is (friend, 11.5).
Tervezs a C++ segtsgvel 1024
A tagokat a legkorltozottabb elrs fellet rszeknt clszer megadni. Ez azt jelenti,
hogy a tagok mindig privtok legyenek, hacsak nincs okunk arra, hogy hozzfrhetbb te-
gyk azokat. Ha hozzfrhetbbnek kell lennik, legyenek vdettek (protected), hacsak
nincs okunk arra, hogy nyilvnosknt (public) adjuk meg ket. Az adattagokat szinte soha
nem szerencss nyilvnoss vagy vdett tenni. A nyilvnos felletet alkot fggvnyeknek
s osztlyoknak az osztly olyan nzett kell biztostaniuk, amely illik ahhoz a szerephez,
hogy az osztly egy fogalmat brzol.
Az brzols elrejtsnek egy tovbbi szintjt absztrakt osztlyok hasznlatval biztosthat-
juk (2.5.4, 12.3, 25.3).
24.4.3. Kvr felletek
Idelis esetben egy fellet csak olyan mveleteket knlhat fel, melyeknek rtelmk van s
a felletet megvalst brmely szrmaztatott osztly ltal megfelelen lerhatk. Ez azon-
ban nem mindig knny. Vegyk a listkat, tmbket, asszociatv tmbket, fkat stb. Mint
ahogy a 16.2.2 pontban rmutattunk, csbt s nha hasznos ezeket a tpusokat ltalno-
stani rendszerint egy trol (kontner) hasznlatval, melyet mindegyikk felleteknt
hasznlhatunk. Ez (ltszlag) felmenti a felhasznlt a trol rszleteinek kezelse all. Egy
ltalnos trolosztly felletnek kialaktsa azonban nem knny feladat. Tegyk fel,
hogy a Container-t absztrakt tpusknt akarjuk meghatrozni. Milyen mveleteket biztost-
son a Container? Megadhatnnk csak azokat a mveleteket, melyeket minden trol kpes
tmogatni a mvelethalmazok metszett , de ez nevetsgesen szk fellet. Sok esetben
a metszet valjban res. A msik md, ha az sszes mvelethalmaz unijt adjuk meg, fu-
tskor pedig hibajelzst adunk, ha ezen a felleten keresztl valamelyik objektumra egy
nem ltez mvelet alkalmazsa trtnik. Az olyan felletet, mely egy fogalomhalmaz fe-
lleteinek unija, kvr felletnek (fat interface) nevezzk. Vegynk egy T tpus objektu-
mokat tartalmaz ltalnos trolt:
class Container {
public:
struct Bad_oper { // kivtelosztly
const char* p;
Bad_oper(const char* pp) : p(pp) { }
};
virtual void put(const T*) { throw Bad_oper("Container::put"); }
virtual T* get() { throw Bad_oper("Container::get"); }
24. Tervezs s programozs 1025
virtual T*& operator[](int) { throw Bad_oper("Container::[](int)"); }
virtual T*& operator[](const char*) { throw Bad_oper("Container::[](char*)"); }
// ...
};
A Container-eket ezutn gy vezethetjk be:
class List_container : public Container, private list {
public:
void put(const T*);
T* get();
// ... nincs operator[] ...
};
class Vector_container : public Container, private vector {
public:
T*& operator[](int);
T*& operator[](const char*);
// ... nincs put() vagy get() ...
};
Amg vatosak vagyunk, minden rendben van:
void f()
{
List_container sc;
Vector_container vc;
// ...
user(sc,vc);
}
void user(Container& c1, Container& c2)
{
T* p1 = c1.get();
T* p2 = c2[3];
// ne hasznljuk c2.get() vagy c1[3] mveletet
// ...
}
Kevs azonban az olyan adatszerkezet, mely mind az indexelses, mind a lista stlus m-
veleteket jl tmogatja. Kvetkezskppen valsznleg nem j tlet olyan felletet megha-
trozni, mely mindkettt megkveteli. Ha ezt tesszk, a hibk elkerlse rdekben futsi
idej tpuslekrdezst (15.4) vagy kivtelkezelst (14. fejezet) kell alkalmaznunk:
Tervezs a C++ segtsgvel 1026
void user2(Container& c1, Container& c2) // szlelni knny, de a helyrellts nehz
lehet
{
try {
T* p1 = c1.get();
T* p2 = c2[3];
// ...
}
catch(Container::Bad_oper& bad) {
// Hopp!
// Most mit tegynk?
}
}
vagy
void user3(Container& c1, Container& c2)
// a korai szlels fraszt, de a helyrellts mg mindig nehz
{
if (dynamic_cast<List_container*>(&c1) && dynamic_cast<Vector_container*>(&c2)) {
T* p1 = c1.get();
T* p2 = c2[3];
// ...
}
else {
// Hopp!
// Most mit tegynk?
}
}
A futsi idej teljestmny mindkt esetben cskkenhet s a ltrehozott kd meglepen
nagy lehet. Ez azt eredmnyezi, hogy a programozk hajlamosak figyelmen kvl hagyni
a lehetsges hibkat, remlve, hogy valjban nem is fordulnak el, amikor a program a fel-
hasznlk kezbe kerl. E megkzeltssel az a problma, hogy a kimert tesztels szintn
nehz s drga munka.
Kvetkezskppen a kvr felleteket a legjobb elkerlni ott, ahol a futsi idej teljest-
mny elsrenden fontos, ahol megkveteljk a kd helyessgre vonatkoz garancit, s
ltalban ott, ahol van ms j megolds. A kvr felletek hasznlata gyengti a megfelelte-
tst a fogalmak s osztlyok kztt s gy szabad utat enged annak, hogy a szrmaztatst
pusztn a knyelmesebb megvalsts kedvrt hasznljuk.
24. Tervezs s programozs 1027
24.5. Tancsok
[1] Trekedjnk az absztrakt adatbrzolsra s az objektumorientlt programozsra.
24.2.
[2] A C++ tulajdonsgait s technikit (csak) szksg szerint hasznljuk. 24.2.
[3] Teremtsnk sszhangot a tervezs s programozs stlusa kztt. 24.2.1.
[4] A fggvnyek s a feldolgozs helyett a tervezsben elsdlegesen az osztlyokra s
fogalmakra sszpontostsunk. 24.2.1.
[5] A fogalmak brzolsra hasznljunk osztlyokat. 24.2.1, 24.3.
[6] A fogalmak kztti hierarchikus kapcsolatokat (s csak azokat) rklssel brzol-
juk. 24.2.2, 24.2.5, 24.3.2.
[7] A felletekre vonatkoz garancikat fejezzk ki alkalmazsszint statikus tpusok-
kal. 24.2.3.
[8] A pontosan meghatrozhat feladatok elvgzsnek megknnytsre hasznljunk
programgenertorokat s kzvetlen kezeleszkzket. 24.2.4.
[9] Kerljk az olyan programgenertorokat s kzvetlen kezeleszkzket, melyek
nem illeszkednek pontosan az adott ltalnos cl programozsi nyelvhez. 24.2.4.
[10] Az elhatrolhat fogalmi szinteket vlasszuk el. 24.3.1.
[11] sszpontostsunk a komponensek tervezsre. 24.4.
[12] Mindig gyzdjnk meg rla, hogy egy adott virtulis fggvnynek pontosan
definilt jelentse van-e s hogy az azt fellbrl minden fggvny a kvnt visel-
keds egy vltozatt rja-e le. 24.3.5, 24.3.2.1.
[13] Hasznljunk nyilvnos rklst az is-a kapcsolatok brzolsra. 24.3.4.
[14] Hasznljunk tagsgot a has-a kapcsolatok brzolsra. 24.3.4.
[15] nllan ltrehozott objektumra hivatkoz mutatk helyett hasznljunk inkbb
kzvetlen tagsgot az egyszer tartalmazsi kapcsolatok kifejezsre. 24.3.3,
24.3.4.
[16] A uses fggsek legyenek vilgosak, (ahol lehetsges) klcsns fggstl mente-
sek, s minl kevesebb legyen bellk. 24.3.5.
[17] Minden osztlyhoz adjunk meg invarinsokat. 24.3.7.1.
[18] Az elfeltteleket, utfeltteleket s ms lltsokat felttelezsekkel (lehetleg az
Assert() hasznlatval) fejezzk ki. 24.3.7.2.
[19] A felletek csak a felttlenl szksges informcikat tegyk elrhetv. 24.4.
[20] Cskkentsk a felletek fggseit ms felletektl. 24.4.2.
[21] A felletek legyenek ersen tpusosak. 24.4.2.
[22] A felleteket alkalmazsszint tpusokkal fejezzk ki. 24.4.2.
[23] A felletek tegyk lehetv, hogy a krsek tvihetk legyenek egy tvoli
kiszolglra. 24.4.2.
[24] Kerljk a kvr felleteket. 24.4.3.
Tervezs a C++ segtsgvel 1028
[25] Hasznljunk private adatokat s tagfggvnyeket, ahol csak lehetsges. 24.4.2.
[26] A tervezk, illetve az ltalnos felhasznlk ltal ignyelt szrmaztatott osztlyok
elklntsre hasznljuk a public/protected megklnbztetst. 24.4.2.
[27] Az ltalnostott programozs rdekben hasznljunk sablonokat. 24.4.1.
[28] Az algoritmusok eljrsmdot megad paramterezsre szintn sablonokat hasz-
nljunk. 24.4.1.
[29] Ha fordtsi idej tpusfeloldsra van szksg, hasznljunk sablonokat. 24.4.1.
[30] Ha futsi idej tpusfeloldsra van szksg, hasznljunk osztlyhierarchikat.
24.4.1.
24. Tervezs s programozs 1029
Az osztlyok szerepe
Vannak dolgok, melyek jobb, ha vltoznak...
de az alapvet tmknak llandnak kell maradniuk.
(Stephen J. Gould)
Az osztlyok fajti Konkrt tpusok Absztrakt tpusok Csompontok Vltoz fel-
letek Objektum I/O Mveletek Felletosztlyok Lerk Hasznlatszmlls
Keretrendszerek Tancsok Gyakorlatok
25.1. Az osztlyok fajti
A C++ osztly egy programozsi nyelvi szerkezet, tbbfle tervezsi igny kiszolglsra.
A bonyolultabb tervezsi problmk megoldsa ltalban j osztlyok bevezetsvel jr
(esetleg ms osztlyok elhagysval). Az j osztlyokkal olyan fogalmakat brzolunk,
amelyeket az elz tervvzlatban mg nem hatroztunk meg pontosan. Az osztlyok sok-
fle szerepet jtszhatnak, ebbl pedig az addik, hogy a konkrt ignyekhez egyedi osz-
tlyfajtkra lehet szksgnk. E fejezetben lerunk nhny alapvet osztlytpust, azok ere-
dend erssgeivel s gyengivel egytt:
25
25.2 Konkrt tpusok
25.2 Absztrakt tpusok
25.4 Csompontok
25.5 Mveletek
25.6 Felletek
25.7 Lerk
25.8 Keretrendszerek
Ezek az osztlyfajtk tervezsi fogalmak, nem nyelvi szerkezetek. A valsznleg elrhe-
tetlen idel az lenne, ha rendelkezsre llna egyszer s nll osztlyfajtk egy olyan leg-
kisebb halmaza, melybl az sszes megfelelen viselked s hasznlhat osztlyt megal-
kothatnnk. Fontos, hogy szrevegyk: a fenti osztlyfajtk mindegyiknek helye van
a tervezsben s egyik sem eredenden jobb minden hasznlatra a tbbinl. A tervezsi s
programozsi vitk sorn szmos flrerts addhat abbl, hogy vannak, akik kizrlag egy
vagy kt fajta osztlyt prblnak hasznlni. Ez rendszerint az egyszersg nevben trtnik,
de a kedvenc osztlyfajtk torz s termszetellenes hasznlathoz vezet.
Jelen lers ezeknek az osztlyfajtknak a tiszta alakjaival foglalkozik, de termszetesen
kevert formk is hasznlhatk. A keverkek azonban tervezsi dntsek eredmnyeknt
kell, hogy szlessenek, a lehetsges megoldsok kirtkelsvel, nem pedig a dntshoza-
talt elkerlve, cltalan ksrletezssel. A dntsek elhalasztsa sokszor valjban a gon-
dolkods elkerlst jelenti. A kezd tervezk rendszerint jl teszik, ha vakodnak a ke-
vert megoldsoktl s egy ltez sszetev stlust kvetik, melynek tulajdonsgai az j
komponens kvnt tulajdonsgaira emlkeztetnek. Csak tapasztalt programozk ksreljk
meg egy ltalnos cl komponens vagy knyvtr megrst, s minden knyvtrtervezt
arra kellene tlni, hogy nhny vig a sajt alkotst hasznlja, dokumentlja s tmogas-
sa. (Lsd mg: 23.5.1.)
25.2. Konkrt tpusok
Az olyan osztlyok, mint a vector (16.3), a list (17.2.2), a Date (10.3) s a complex (11.3,
22.5) konkrtak abban az rtelemben, hogy mindegyikk egy-egy viszonylag egyszer fo-
galmat brzol, az sszes, e fogalom tmogatshoz nlklzhetetlen mvelettel egytt.
Mindegyikknl fennll az egy az egyhez megfelels a fellet s a megvalsts kztt s
egyiket sem szntk bzisosztlynak szrmaztatshoz. A konkrt tpusok ltalban nem il-
lenek bele egymssal kapcsolatos osztlyok hierarchijba. Minden konkrt osztly nma-
Tervezs a C++ segtsgvel 1032
gban megrthet, a lehet legkevesebb hivatkozssal ms osztlyokra. Ha egy konkrt t-
pust megfelelen runk le, az azt hasznl programok mretben s sebessgben sszemr-
hetk lesznek azokkal a programokkal, melyekben a programoz egynileg dolgozza ki
a fogalom brzolst, majd annak egyedi cl (szrmaztatott) vltozataival dolgozik. Ezen-
kvl, ha a megvalsts jelentsen vltozik, rendszerint a fellet is mdosul, hogy tkrz-
ze a vltozst. A konkrt tpusok mindezekben a beptett tpusokra emlkeztetnek. A be-
ptett tpusok termszetesen mind konkrtak. A felhasznli konkrt tpusok, mint
a komplex szmok, mtrixok, hibazenetek s szimbolikus hivatkozsok, gyakran az egyes
alkalmazsi terletek alaptpusaiknt hasznltak.
Az adott osztly felletnek termszete hatrozza meg, mi minsl jelents vltoztatsnak
a megvalstsban; az elvontabb felletek ezen vltoztatsoknak tbb teret hagynak, de
ronthatjk a futsi idej hatkonysgot. Ezenkvl egy j megvalsts nem fgg ms oszt-
lyoktl a felttlenl szksgesnl ersebben, gy az osztly a programban lv hasonl
osztlyokhoz val alkalmazkods szksgessgnek elkerlsvel fordtsi vagy futsi ide-
j tlterhels nlkl hasznlhat.
sszegezve, egy konkrt tpust ler osztly cljai a kvetkezk:
1. Szoros illeszkeds egy konkrt fogalomhoz s a megvalsts mdjhoz.
2. Az egynileg kidolgozott kdhoz mrhet gyorsasg s kis mret, a helyben
kifejts, valamint a fogalom s megvalstsa sszes elnyt kihasznl mvele-
tek hasznlatval.
3. A ms osztlyoktl val lehet legkisebb arny fggs.
4. rthetsg s nll hasznlhatsg.
Az eredmny a felhasznli s a megvalst kd kzti szoros kts. Ha a megvalsts br-
milyen mdon megvltozik, a felhasznli kdot jra kell fordtani, mivel a felhasznli
kd szinte mindig tartalmaz helyben kifejtett (inline) fggvnyhvsokat vagy a konkrt t-
pushoz tartoz helyi (loklis) vltozkat.
A konkrt tpus elnevezst az ltalnosan hasznlt absztrakt tpus elnevezs ellentte-
knt vlasztottuk. A konkrt s absztrakt tpusok kzti viszonyt a 25.3 pont trgyalja.
A konkrt tpusok nem tudnak kzvetlenl kzssget kifejezni. A list s a vector mvelet-
halmaza pldul hasonl s nhny sablon fggvnyben egymst helyettesthetik.
A list<int> s a vector<int>, vagy a list<Shape*> s a list<Circle*> kztt azonban nincs ro-
konsg (13.6.3), jllehet mi szrevesszk a hasonlsgokat.
25. Az osztlyok szerepe 1033
Ez azt is jelenti, hogy az egyes konkrt tpusokat hasonl mdon hasznl kdok klsejk-
ben klnbzk lesznek. Egy List bejrsa a next() mvelettel pldul jelentsen klnb-
zik egy Vector indexelssel trtn bejrstl:
void my(List& sl)
{
for (T* p = sl.first(); p; p = sl.next()) { // "termszetes" lista-bejrs
// egyik kd
}
// ...
}
void your(Vector& v)
{
for (int i = 0; i<v.size(); i++) { // "termszetes" vektor-bejrs
// msik kd
}
// ...
}
A bejrs stlusbeli klnbsge termszetes, abban az rtelemben, hogy a vedd a kvetke-
z elemet mvelet nlklzhetetlen a lista fogalmnl, de nem ilyen magtl rtetd egy
vektornl, illetve hogy az indexels nlklzhetetlen a vektor fogalmnl, de a listnl nem
az. Az, hogy a vlasztott megvalstsi mdhoz a termszetes mveletek rendelkezsre
lljanak, ltalban ltfontossg, mind a hatkonysg szempontjbl, mind azrt, hogy
a kd knnyen megrhat legyen.
Magtl rtetd nehzsg, hogy az alapveten hasonl mveletekhez (pldul az elbbi
kt ciklushoz) rott kd eltren nzhet ki, a hasonl mveletekhez klnbz konkrt t-
pusokat hasznl kdok pedig nem cserlhetk fel. Vals programoknl fejtrst ignyel,
hogy megtalljuk a hasonlsgokat, s jelents jratervezst, hogy a megtallt hasonls-
gok kihasznlsra mdot adjunk. A szabvnyos trolk s algoritmusok is gy teszik lehe-
tv a konkrt tpusok hasonlsgainak kihasznlst, hatkonysguk s elegancijuk el-
vesztse nlkl (16.2).
Egy fggvnynek ahhoz, hogy paramterknt elfogadjon egy konkrt tpust, pontosan az
adott tpust kell megadnia paramtertpusknt. Nem llnak rendelkezsre rklsi kapcso-
latok, melyeket arra hasznlhatnnk, hogy a paramterek deklarlst ltalnosabb te-
gyk. Kvetkezskppen a konkrt tpusok kzti hasonlsgok kihasznlsra tett ksrlet
magval vonja a sablonok hasznlatt s az ltalnostott (generikus) programozst, amint
arrl a 3.8 pontban mr emltst tettnk. Ha a standard knyvtrat hasznljuk, a bejrs gy
fog kinzni:
Tervezs a C++ segtsgvel 1034
template<class C> void ours(const C& c)
{
for (C::const_iterator p = c.begin(); p!=c.end(); ++p) { // standard knyvtrbeli bejrs
// ...
}
}
Itt kihasznltuk a trolk alapvet hasonlsgt, s ezzel megnyitottuk az utat a tovbbi ha-
sonlsgok kihasznlsa fel, ahogyan azt a szabvnyos algoritmusok is teszik (18. fejezet).
A felhasznlnak a konkrt tpus megfelel hasznlathoz meg kell rtenie annak pontos
rszleteit. (ltalban) nem lteznek olyan ltalnos tulajdonsgok, melyek egy knyvtrban
az sszes konkrt tpusra rvnyesek lennnek, s amelyekre tmaszkodva a felhasznl-
nak nem kell az egyes osztlyok ismeretvel trdnie. Ez az ra a futsi idej tmrsgnek
s hatkonysgnak. Nha megri, nha nem. Olyan eset is elfordul, hogy knnyebb meg-
rteni s hasznlni egy konkrt osztlyt, mint egy ltalnosabb (absztrakt) osztlyt. A jl is-
mert adattpusokat (pl. tmbket, listkat) brzol osztlyoknl gyakran ez a helyzet.
Vegyk szre azonban, hogy az idelis tovbbra is az, ha a megvalstsbl a lehet legna-
gyobb rszt elrejtjk, anlkl, hogy a teljestmnyt komolyabban rontannk. A helyben ki-
fejtett fggvnyek ebben a krnyezetben nagy nyeresget jelenthetnek. Szinte soha nem j
tlet, ha a tag vltozkat nyilvnoss tesszk vagy set s get fggvnyekrl gondoskodunk,
melyekkel a felhasznl kzvetlenl kezelheti azokat (24.4.2). A konkrt tpusok maradja-
nak meg tpusoknak, s ne bitcsomagok legyenek, melyeket a knyelem kedvrt nhny
fggvnnyel ltunk el.
25.2.1. A konkrt tpusok jrahasznostsa
A konkrt tpusok ritkn hasznlhatk alaptpusknt tovbbi szrmaztatshoz. Minden
konkrt tpus clja egyetlen fogalom vilgos s hatkony brzolsa. Az olyan osztly, mely
ezt a kvetelmnyt jl teljesti, ritkn alkalmas klnbz, de egymssal rokon osztlyok
nyilvnos szrmaztats ltali ltrehozsra. Az ilyen osztlyok gyakrabban tehetk tagokk
vagy privt bzisosztlyokk, mert gy anlkl hasznlhatk hatkonyan, hogy felletket
s megvalstsukat keverni s rontani kellene j osztlyokval. Vegyk egy j osztly szr-
maztatst a Date-bl:
class My_date : public Date {
// ...
};
25. Az osztlyok szerepe 1035
Szablyos-e a My_date-et gy hasznlni, mint az egyszer Date-et? Nos, ez attl fgg, mi
a My_date, de tapasztalatom szerint ritkn lehet olyan konkrt tpust tallni, mely mdos-
ts nlkl megfelel bzisosztlynak.
A konkrt tpusok ugyangy mdosts nlkl jrahasznosthatk, mint az int-hez hasonl
beptett tpusok (10.3.4):
class Date_and_time {
private:
Date d;
Time t;
public:
// ...
};
A felhasznlsnak (jrahasznostsnak) ez a mdja rendszerint egyszer s hatkony.
Lehet, hogy hiba volt a Date-et nem gy tervezni, hogy szrmaztatssal knnyen lehessen
mdostani? Nha azt mondjk, minden osztlynak mdosthatnak kell lennie fellrs s
szrmaztatott osztlyok tagfggvnyeibl val hozzfrs ltal. Ezt a szemlletet kvetve
a Date-bl az albbi vltozatot kszthetjk el:
class Date2 {
public:
// nyilvnos fellet, elssorban virtulis fggvnyekbl ll
protected:
// egyb megvalstsi rszletek (esetleg nmi brzolst is tartalmaz)
private:
// adatbrzols s egyb megvalstsi rszletek
};
A fellr fggvnyek hatkony megrst megknnytend, az brzolst protected-knt
deklarltuk. Ezzel elrtk clunkat: a Date2-t szrmaztatssal tetszlegesen hajlthatv
tettk, vltozatlanul hagyva felhasznli fellett. Ennek azonban ra van:
1. Kevsb hatkony alapmveletek. A C++ virtulis fggvnyeinek meghvsa kis-
s lassabb, mint a szoksos fggvnyhvsok; a virtulis fggvnyek nem fordt-
hatk helyben kifejtve olyan gyakran, mint a nem virtulis fggvnyek; radsul
a virtulis fggvnyekkel rendelkez osztlyok egy gpi szval tbb helyet ig-
nyelnek.
2. A szabad tr hasznlatnak szksgessge. A Date2 clja, hogy a belle szr-
maztatott osztlyok objektumai felcserlhetek legyenek. Mivel e szrmaztatott
Tervezs a C++ segtsgvel 1036
osztlyok mrete klnbz, kzenfekv, hogy a szabad trban foglaljunk sz-
mukra helyet s mutatkkal vagy referencikkal frjnk hozzjuk. A valdi
loklis vltozk hasznlata teht drmaian cskken.
3. Knyelmetlensg a felhasznlnak. A virtulis fggvnyek tbbalaksgt (poli-
morfizmus) kihasznland, a Date2-khz mutatk vagy referencik ltal kell
hozzfrnnk.
4. Gyengbb betokozs. A virtulis mveletek fellrhatk s a vdett adatok
a szrmaztatott osztlyokbl mdosthatk (12.4.1.1).
Termszetesen ezek a kltsgek nem mindig jelentsek s az gy ltrehozott osztly gyak-
ran pontosan gy viselkedik, mint ahogy szerettk volna (25.3, 25.4). Egy egyszer konk-
rt tpusnl (mint a Date2) azonban ezek nagy s szksgtelen kltsgek.
A hajlthatbb tpus idelis brzolsra sokszor egy jl megtervezett konkrt tpus a leg-
alkalmasabb:
class Date3 {
public:
// nyilvnos fellet, elssorban virtulis fggvnyekbl ll
private:
Date d;
};
gy a konkrt tpusok (a beptett tpusokat is belertve) osztlyhierarchiba is illeszthetk,
ha szksges. (Lsd mg 25.10[1].)
25.3. Absztrakt tpusok
Az osztlyokat, valamint az objektumokat ltrehoz s az azokat felhasznl kd kztti
ktelk laztsnak legegyszerbb mdja egy absztrakt osztly bevezetse, mely a fogalom
klnbz megvalstsainak halmazhoz kzs felletet biztost. Vegynk egy termsze-
tes Set-et (halmazt):
template<class T> class Set {
public:
virtual void insert(T*) = 0;
virtual void remove(T*) = 0;
25. Az osztlyok szerepe 1037
virtual int is_member(T*) = 0;
virtual T* first() = 0;
virtual T* next() = 0;
virtual ~Set() { }
};
Ez meghatrozza a halmaz fellett s tartalmazza az elemek bejrsnak mdjt is.
A konstruktor hinya s a virtulis destruktor jelenlte szokvnyos (12.4.2). Tbbfle meg-
valsts lehetsges (16.2.1):
template<class T> class List_set : public Set<T>, private list<T> {
// ...
};
template<class T> class Vector_set : public Set<T>, private vector<T> {
// ...
};
Az egyes megvalstsokhoz az absztrakt osztly adja a kzs felletet, vagyis a Set-tel anl-
kl dolgozhatunk, hogy tudnnk, melyik megvalstst hasznljuk:
void f(Set<Plane*>& s)
{
for (Plane** p = s.first(); p; p = s.next()) {
// sajt kd
}
// ...
}
List_set<Plane*> sl;
Vector_set<Plane*> v(100);
void g()
{
f(sl);
f(v);
}
A konkrt tpusoknl megkveteltk a megvalst osztlyok jratervezst, hogy kifejez-
zk a kzssget, annak kiaknzsra pedig sablont hasznltunk. Itt a kzs felletet kell
megterveznnk (esetnkben a Set-et), de semmi ms kzset nem kvnunk a megvals-
tsra hasznlt osztlyoktl, mint azt, hogy meg tudjk valstani a felletet.
Tervezs a C++ segtsgvel 1038
Tovbb, a Set-et felhasznl programelemeknek nem kell ismernik a List_set s
a Vector_set deklarciit, teht nem kell fggnik ezektl. Ezenkvl nem kell sem jraford-
tanunk, sem brmilyen ms vltoztatst vgeznnk, ha a List_set vagy a Vector_set megvl-
tozik, vagy ha bevezetjk a Set egy jabb megvalstst, mondjuk a Tree_set-et. Minden fg-
gst a fggvnyek tartalmaznak, melyek a Set-bl szrmaztatott osztlyokat hasznljk.
Vagyis felttelezve, hogy a szoksos mdon hasznljuk a fejllomnyokat az f(Set&)
megrsakor csak a Set.h-t kell beptennk (#include), a List_set.h-t vagy a Vector_set.h-t
nem. Csak ott van szksg egy implementcis fejllomnyra, ahol egy List_set, illetve egy
Vector_set ltrehozsa trtnik. Az egyes megvalstsok tovbbi elszigetelst jelentheti
a tnyleges osztlyoktl, ha egy absztrakt osztlyt vezetnk be, mely az objektum-ltrehoz-
si krseket kezeli (gyr, factory, 12.4.4).
A felletnek ez az elvlasztsa a megvalststl azzal a kvetkezmnnyel jr, hogy elt-
nik a hozzfrs azon mveletekhez, melyek termszetesek egy adott megvalsts sz-
mra, de nem elg ltalnosak ahhoz, hogy a fellet rszei legyenek. Pldul, mivel a Set
nem rendelkezik a rendezs kpessgvel, a Set felletben nem tmogathatunk egy index-
kezel opertort mg akkor sem, ha trtnetesen egy tmbt hasznl Set-et hozunk ltre.
A kzi optimalizls hinya miatt ez nveli a futsi idt, ezenkvl ltalban nem lhetnk
a helyben kifejts elnyeivel sem (kivve azokat az egyedi eseteket, amikor a fordt isme-
ri a valdi tpust), s a fellet minden lnyeges mvelete virtulis fggvnyhvs lesz. Mint
a konkrt tpusok, az absztrakt tpusok hasznlata is nha megri; nha nem. sszefoglal-
va, az absztrakt tpusok cljai a kvetkezk:
1. Egy egyszer fogalom oly mdon val lersa, mely lehetv teszi, hogy a prog-
ramban a fogalomnak tbb megvalstsa is ltezhessen.
2. Elfogadhat futsi id s takarkos helyfoglals biztostsa virtulis fggvnyek
hasznlatval.
3. Az egyes megvalstsok lehet legkisebb fggse ms osztlyoktl.
4. Legyen rthet nmagban.
Az absztrakt tpusok nem jobbak, mint a konkrt tpusok, csak msmilyenek. A felhaszn-
lnak kell dntenie, melyiket hasznlja. A knyvtr ksztje elkerlheti a krdst azltal,
hogy mind a kettt biztostja, a felhasznlra hagyva a vlasztst. Az a fontos, hogy vilgos
legyen, az adott osztly melyik tpusba tartozik. Ha korltozzuk egy absztrakt tpus ltal-
nossgt, hogy sebessgben llja a versenyt egy konkrt tpussal, rendszerint kudarcot val-
lunk. Ez ugyanis veszlyezteti azt a kpessgt, hogy egyes megvalstsait egymssal he-
lyettesthessk, a vltoztats utni jrafordts szksgessge nlkl. Hasonlkppen
rendszerint kudarccal jr, ha konkrt tpusokat ltalnosabb akarunk tenni, hogy az
absztrakt tpusok tulajdonsgaival sszemrhetk legyenek, mert gy veszlybe kerl az
25. Az osztlyok szerepe 1039
egyszer osztly hatkonysga s pontossga. A kt elkpzels egytt ltezhet valjban
egytt kell, hogy ltezzen, mivel az absztrakt tpusok megvalstst konkrt tpusok ad-
jk , de nem szabad ket sszekeverni egymssal.
Az absztrakt tpusok gyakran kzvetlen megvalstsukon tl nem szolglnak tovbbi szr-
maztatsok alapjul. j fellet azonban felpthet egy absztrakt osztlybl, ha belle egy
mg tgabb absztrakt osztlyt szrmaztatunk. Ezt az j absztrakt osztlyt kell majd tovbbi
szrmaztatson keresztl megvalstani egy nem absztrakt osztllyal (15.2.5).
Mirt nem szrmaztattunk a Set-bl egy lpsben List s Vector osztlyokat, elhagyva
a List_set s Vector_set osztlyok bevezetst? Ms szval, mirt legyenek konkrt tpusaink,
ha absztrakt tpusaink is lehetnek?
1. Hatkonysg. Azt akarjuk, hogy konkrt tpusaink legyenek (mint a vector s
a list), azon tlterhels nlkl, amit a megvalstsnak a fellettl val elvlasz-
tsa okoz (az absztrakt tpusoknl ez trtnik).
2. jrahasznosts. Szksgnk van egy mdra, hogy a mshol tervezett tpuso-
kat (mint a vector s a list) beilleszthessk egy j knyvtrba vagy alkalmazs-
ba, azltal, hogy j felletet adunk nekik (nem pedig jrarjuk ket).
3. Tbb fellet. Ha egyetlen kzs alapot hasznlunk tbbfle osztlyhoz, az kvr
felletekhez vezet (24.4.3). Gyakran jobb, ha az j clra hasznlni kvnt osz-
tlynak j felletet adunk (mint egy vector-nak egy Set felletet), ahelyett, hogy
a tbb clra val hasznlhatsg kedvrt az eredeti felletet mdostannk.
Termszetesen ezek egymssal sszefgg krdsek. Az Ival_box pldban (12.4.2,
15.2.5) s a trolk tervezsvel kapcsolatban (16.2) rszletesebben trgyaltuk ezeket. Ha
a Set bzisosztlyt hasznltuk volna, az egy csompont-osztlyokon nyugv alaptrolt
eredmnyezett volna (25.4).
A 25.7 pont egy rugalmasabb bejrt (itertort) r le, ahol a bejr a kezdeti rtkadsnl
kthet az objektumokat biztost megvalstshoz s ez a kts futsi idben mdosthat.
Tervezs a C++ segtsgvel 1040
25.4. Csompont-osztlyok
Az osztlyhierarchik ms szrmaztatsi szemllet alapjn plnek fel, mint az absztrakt
osztlyoknl hasznlt, felletbl s megvalstsbl ll rendszer. Itt az osztlyokat alapnak
tekintjk, melyre ms osztlyokat ptnk. Mg ha absztrakt osztly is, rendszerint van va-
lamilyen brzolsa s ad valamilyen szolgltatsokat a belle szrmaztatott osztlyoknak.
Ilyen csompont-osztlyok pldul a Polygon (12.3), a (kiindul) Ival_slider (12.4.1) s
a Satellite (15.2).
A hierarchin belli osztlyok jellemzen egy ltalnos fogalmat brzolnak, mg a bellk
szrmaztatott osztlyok gy tekinthetk, mint az adott fogalom konkretizlt (egyedi cl,
szakostott) vltozatai. Az egy hierarchia szerves rszeknt tervezett osztlyok a csom-
pont-osztlyok (node class) a bzisosztly szolgltatsaira tmaszkodva biztostjk sajt
szolgltatsaikat, vagyis a bzisosztly tagfggvnyeit hvjk meg. A csompont-osztlyok
ltalban nem csupn a bzisosztlyuk ltal meghatrozott fellet egy megvalstst adjk
(mint egy absztrakt tpusnak egy megvalst osztly), hanem maguk is hozztesznek j
fggvnyeket, ezltal szlesebb felletet biztostanak. Vegyk a 24.3.2 forgalomszimu-
lcis pldjban szerepl Car-t:
class Car : public Vehicle {
public:
Car(int passengers, Size_category size, int weight, int fc)
: Vehicle(passengers,size,weight), fuel_capacity(fc) { /* ... */ }
// a Vehicle lnyeges virtulis fggvnyeinek fellrsa
void turn(Direction);
// ...
// Car-ra vonatkoz fggvnyek hozzadsa
virtual void add_fuel(int amount); // az aut zemanyagot ignyel
// ...
};
A fontos fggvnyek: a konstruktor, mely ltal a programoz a szimulci szempontjbl
lnyeges alaptulajdonsgokat hatrozza meg s azok a (virtulis) fggvnyek, melyek a szi-
mulcis eljrsoknak lehetv teszik, hogy egy Car-t anlkl kezeljenek, hogy tudnk an-
nak pontos tpust. Egy Car az albbi mdon hozhat ltre s hasznlhat:
25. Az osztlyok szerepe 1041
void user()
{
// ...
Car* p = new Car(3,economy,1500,60);
drive(p,bs_home,MH); // belps a szimullt forgalmi helyzetbe
// ...
}
A csompont-osztlyoknak rendszerint szksgk van konstruktorokra s ez a konstruktor
gyakran bonyolult. Ebben a csompont-osztlyok eltrnek az absztrakt osztlyoktl, me-
lyeknek ritkn van konstruktoruk.
A Car mveleteinek megvalstsai ltalban a Vehicle bzisosztlybl vett mveleteket
hasznljk, a Car-okat hasznl elemek pedig a bzisosztlyok szolgltatsaira tmaszkod-
nak. A Vehicle pldul megadja a sllyal s mrettel foglalkoz alapfggvnyeket, gy
a Car-nak nem kell gondoskodnia azokrl:
bool Bridge::can_cross(const Vehicle& r)
{
if (max_weight<r.weight()) return false;
// ...
}
Ez lehetv teszi a programoz szmra, hogy j osztlyokat (Car s Truck) hozzon ltre
a Vehicle csompont-osztlybl, gy, hogy csak az attl klnbz tulajdonsgokat s m-
veleteket kell megadnia, illetve elksztenie. Ezt az eljrst gyakran gy emltik, mint k-
lnbsg ltali programozst vagy bvt programozst.
Sok csompont-osztlyhoz hasonlan a Car maga is alkalmas a tovbbi szrmaztatsra.
Az Ambulance-nek pldul tovbbi adatokra s mveletekre van szksge a vszhelyzetek
kezelshez:
class Ambulance : public Car, public Emergency {
public:
Ambulance();
// a Car lnyeges virtulis fggvnyeinek fellrsa
void turn(Direction);
// ...
// az Emergency lnyeges virtulis fggvnyeinek fellrsa
Tervezs a C++ segtsgvel 1042
virtual void dispatch_to(const Location&);
// ...
// Ambulance-ra vonatkoz fggvnyek hozzadsa
virtual int patient_capacity(); // hordgyak szma
// ...
};
sszegezzk a csompont-osztlyok jellemzit:
1. Mind megvalstst, mind a felhasznlknak adott szolgltatsokat tekintve
bzisosztlyaira tmaszkodik.
2. Szlesebb felletet (vagyis tbb nyilvnos tagfggvnnyel rendelkez felletet)
ad felhasznlinak, mint bzisosztlyai.
3. Elsdlegesen (de nem felttlenl kizrlagosan) a nyilvnos felletben lev
virtulis fggvnyekre tmaszkodik.
4. Fgg az sszes (kzvetlen s kzvetett) bzisosztlytl.
5. Csak bzisosztlyaival sszefggsben rthet meg.
6. Tovbbi szrmaztats bzisosztlyaknt felhasznlhat.
7. Objektumok ltrehozsra hasznlhat.
Nem minden csompont-osztly fog eleget tenni az 1, 2, 6 s 7 mindegyiknek, de a leg-
tbb igen. A 6-nak eleget nem tev osztlyok a konkrt tpusokra emlkeztetnek, gy konk-
rt csompont-osztlyoknak nevezhetk. A konkrt csompont-osztlyok pldul absztrakt
osztlyok megvalstshoz hasznlhatk (12.4.2), az ilyen osztlyok vltozi szmra pe-
dig statikusan s a veremben is foglalhatunk helyet. Az ilyen osztlyokat nha levl oszt-
lyoknak nevezzk. Ne feledjk azonban, hogy minden kd, amely egy osztlyra hivatkoz
mutatn vagy referencin virtulis fggvnyek ltal vgez mveletet, szmtsba kell, hogy
vegye egy tovbbi osztly szrmaztatsnak lehetsgt (vagy nyelvi tmogats nlkl el
kell fogadnia, hogy nem trtnt tovbbi szrmaztats). Azok az osztlyok, melyek nem tesz-
nek eleget a 7-es pontnak, az absztrakt tpusokra emlkeztetnek, gy absztrakt csompont-
osztlyoknak hvhatk. A hagyomnyok miatt sajnos sok csompont-osztlynak van leg-
albb nhny protected tagja, hogy a szrmaztatott osztlyok szmra kevsb korltozott
felletrl gondoskodjon (12.4.1.1).
A 4-es pontbl az kvetkezik, hogy a csompont-osztlyok fordtshoz a programoznak
be kell ptenie (#include) azok sszes kzvetlen s kzvetett bzisosztly-deklarciit s
az sszes olyan deklarcit, melyektl azok fggnek. Ez megint csak egy klnbsg a cso-
mpont-osztlyok s az absztrakt tpusok kztt, az utbbiakat hasznl programelemek
ugyanis nem fggnek a megvalstsukra hasznlt osztlyoktl, gy nem kell azokat a for-
dtshoz bepteni.
25. Az osztlyok szerepe 1043
25.4.1. Felletek mdostsa
Minden csompont-osztly egy osztlyhierarchia tagja, egy hierarchin bell viszont nem
minden osztlynak kell ugyanazt a felletet nyjtania. Nevezetesen, egy szrmaztatott osz-
tly tbb tagfggvnnyel rendelkezhet s egy testvrosztly fggvnyhalmaza is teljesen
ms lehet. A tervezs szempontjbl a dynamic_cast (15.4) gy tekinthet, mint az az el-
jrs, mellyel egy objektumot megkrdezhetnk, biztost-e egy adott felletet.
Pldaknt vegynk egy egyszer objektum I/O (bemeneti/kimeneti) rendszert. A felhasz-
nl egy adatfolyambl objektumokat akar olvasni, meg kvnja hatrozni, hogy a vrt t-
pusak-e, majd fel akarja hasznlni azokat:
void user()
{
// ... felttelezs szerint alakzatokat (Shape) tartalmaz fjl megnyitsa
// ss ltal azonostott bemeneti adatfolyamknt ...
Io_obj* p = get_obj(ss); // objektum olvassa az adatfolyamrl
if (Shape* sp = dynamic_cast<Shape*>(p)) {
sp->draw(); // alakzat hasznlata
// ...
}
else {
// hopp: ez nem alakzat
}
}
A user() fggvny az alakzatokat kizrlag az absztrakt Shape osztlyon keresztl kezeli s
ezltal minden fajta alakzatot kpes hasznlni. A dynamic_cast hasznlata lnyeges, mert
az objektum I/O rendszer szmos objektumfajtt kezel, a felhasznl pedig vletlenl olyan
fjlt is megnyithatott, amely olyan osztlyok egybknt tkletesen j objektumait tar-
talmazza, melyekrl a felhasznl nem is hallott.
A rendszer felttelezi, hogy minden olvasott vagy rt objektum olyan osztlyhoz tartozik,
amely az Io_obj leszrmazottja. Az Io_obj osztlynak tbbalaknak (polimorfnak) kell len-
nie, hogy lehetv tegye szmunkra a dynamic_cast hasznlatt:
class Io_obj {
public:
virtual Io_obj* clone() const =0; // tbbalak
virtual ~Io_obj() {}
};
Tervezs a C++ segtsgvel 1044
A rendszer ltfontossg eleme a get_obj() fggvny, mely egy istream adatfolyambl ada-
tokat olvas s osztlyobjektumokat hoz ltre azokra alapozva. Tegyk fel, hogy a bemene-
ti adatfolyamban az egyik objektumot kpvisel adatok eltagknt egy, az objektum oszt-
lyt azonost karakterlncot tartalmaznak. A get_obj() fggvny feladata ezt elolvasni,
majd egy olyan fggvnyt meghvni, amely kpes a megfelel osztly objektumot ltre-
hozni s kezdrtket adni neki:
typedef Io_obj* (*PF)(istream&); // Io_obj* visszatrsi tpus fggvnyre hivatkoz
mutat
map<string,PF> io_map; // karakterlncok hozzrendelse a ltrehoz fggvnyekhez
bool get_word(istream& is, string& s); // sz beolvassa is-bl s-be
Io_obj* get_obj(istream& s)
{
string str;
bool b = get_word(s,str); // a kezd sz beolvassa str-be
if (b == false) throw No_class(); // io formtumhiba
PF f = io_map[str]; // az "str" kikeresse, hogy kivlasszuk a fggvnyt
if (f == 0) throw Unknown_class(); // nem talljuk "str"-t
return f(s); // objektum ltrehozsa az adatfolyambl
}
Az io_map nev map neveket tartalmaz karakterlncok s a nevekhez tartoz osztlyok
objektumait ltrehozni kpes fggvnyek prjaibl ll.
A Shape osztlyt a szoksos mdon hatrozhatjuk meg, azzal a kivtellel, hogy a user() l-
tal megkvetelten az Io_obj-bl szrmaztatjuk:
class Shape : public Io_obj {
// ...
};
Mg rdekesebb (s sok esetben valszerbb) lenne azonban egy meghatrozott Shape-et
(2.6.2) mdosts nlkl felhasznlni:
class Io_circle : public Circle, public Io_obj {
public:
Io_circle* clone() const { return new Io_circle(*this); } // msol konstruktor hasznlata
Io_circle(istream&); // kezdeti rtkads a bemeneti adatfolyambl
25. Az osztlyok szerepe 1045
static Io_obj* new_circle(istream& s) { return new Io_circle(s); }
// ...
};
Ez annak pldja, hogyan lehet egy osztlyt egy absztrakt osztly hasznlatval beilleszte-
ni egy hierarchiba, ami kevesebb elreltst ignyel, mint amennyi ahhoz kellene, hogy
elszr csompont-osztlyknt hozzuk ltre (12.4.2, 25.3).
Az Io_circle(istream&) konstruktor az objektumokat az iostream-bl kapott adatokkal tl-
ti fel. A new_circle() fggvnyt az io_map-ba tesszk, hogy az osztlyt a bemeneti/kimene-
ti rendszer szmra ismertt tegyk:
io_map["Io_circle"]=&Io_circle::new_circle;
Ms alakzatokat hasonl mdon hozhatunk ltre:
class Io_triangle : public Triangle, public Io_obj {
// ...
};
Ha az objektum I/O rendszer elksztse tl fradsgos, segthet egy sablon:
template<class T> class Io : public T, public Io_obj {
public:
Io* clone() const { return new Io(*this); } // fellbrlja Io_obj::clone()-t
Io(istream&); // kezdeti rtkads bemeneti adatfolyambl
static Io* new_io(istream& s) { return new Io(s); }
// ...
};
Ha a fenti adott, meghatrozhatjuk az Io_circle-t:
typedef Io<Circle> Io_circle;
Termszetesen tovbbra is pontosan definilnunk kell az Io<Circle>::Io(istream&)-et, mi-
vel annak rszleteiben ismernie kell a Circle-t. Az Io sablon plda arra, hogyan illeszthetnk
be konkrt tpusokat egy osztlyhierarchiba egy ler (handle) segtsgvel, amely az adott
hierarchia egy csompontja. A sablon sajt paramterbl szrmaztat, hogy lehetv tegye
az talaktst az Io_obj-rl, de ez sajnos kizrja az Io-nak beptett tpusokra trtn
alkalmazst:
Tervezs a C++ segtsgvel 1046
typedef Io<Date> Io_date; // konkrt tpus beburkolsa
typedef Io<int> Io_int; // hiba: nem szrmaztathatunk beptett tpusbl
A problma gy kezelhet, ha a beptett tpusok rszre kln sablont biztostunk vagy ha
egy beptett tpust kpvisel osztlyt hasznlunk (25.10[1]).
Ez az egyszer objektum I/O rendszer nem teljesthet minden kvnsgot, de majdnem el-
fr egyetlen lapon s f eljrsait szmos clra felhasznlhatjuk, pldul meghvhatjuk ve-
lk a felhasznl ltal karakterlnccal megadott fggvnyeket vagy ismeretlen tpus ob-
jektumokat kezelhetnk futsi idej tpusazonostssal feldertett felleten keresztl.
25.5. Mveletek
A C++-ban a mveletek meghatrozsnak legegyszerbb s legkzenfekvbb mdja fgg-
vnyek rsa. Ha azonban egy adott mveletet vgrehajts eltt ksleltetni kell, t kell vin-
ni mshov, prostani kell ms mveletekkel vagy a mvelet sajt adatokat ignyel
(25.10 [18, 19]), gyakran clszerbb azt osztly alakjban megadni, mely vgre tudja hajta-
ni a kvnt eljrst s egyb szolgltatsokat is nyjthat. J pldk erre a szabvnyos algo-
ritmusok ltal hasznlt fggvny-objektumok (18.4), valamint az iostream-mel hasznlt
mdostk (21.4.6). Az elbbi esetben a tnyleges mveletet a fggvnyhv opertor hajt-
ja vgre, mg az utbbinl a << vagy a >> opertor. A Form (21.4.6.3) s a Matrix (22.4.7)
esetben a vgrehajts ksleltetsre egyedi (compositor) osztlyokat hasznlunk, amg
a hatkony vgrehajtshoz elegend informci ssze nem gylik.
A mveletosztlyok ltalnos formja egy (jellemzen valamilyen csinld (do_it) nev)
virtulis fggvnyt tartalmaz egyszer osztly:
class Action {
public:
virtual int do_it(int) = 0;
virtual ~Action() { }
};
Ha ez adott, olyan kdot rhatunk mondjuk egy ment amely a mveleteket ksbbi
vgrehajtsra elraktrozhatja, anlkl, hogy fggvnymutatkat hasznlna, brmit is tud-
na a meghvott objektumokrl vagy egyltaln ismern a meghvott mvelet nevt:
25. Az osztlyok szerepe 1047
class Write_file : public Action {
File& f;
public:
int do_it(int) { return f.write().succeed(); }
};
class Error_response : public Action {
string message;
public:
int do_it(int);
};
int Error_response::do_it(int)
{
Response_box db(message.c_str(), "Folytat","Mgse","Ismt");
switch (db.get_response()) {
case 0:
return 0;
case 1:
abort();
case 2:
current_operation.redo();
return 1;
}
}
Action* actions[] = {
new Write_file(f),
new Error_response("Megint elrontotta"),
// ...
};
Az Action-t hasznl kd teljesen elszigetelhet, nem kell, hogy brmit is tudjon az olyan
szrmaztatott osztlyokrl, mint a Write_file s az Error_response.
Ez igen erteljes mdszer, mellyel vatosan kell bnniuk azoknak, akik leginkbb a funk-
cionlis elemekre bontsban szereztek tapasztalatot. Ha tl sok osztly kezd az Action-re
hasonltani, lehet, hogy a rendszer tfog terve tlzottan mveletkzpontv vlt.
Megemltend, hogy az osztlyok jvbeni hasznlatra is trolhatnak mveleteket, illetve
olyanokat is tartalmazhatnak, melyeket egy tvoli gp hajt majd vgre (25.10 [18]).
Tervezs a C++ segtsgvel 1048
25.6. Felletosztlyok
Az egyik legfontosabb osztlyfajta a szerny s legtbbszr elhanyagolt felletosztly. Egy
felletosztly nem sok dolgot csinl ha csinlna, nem lenne felletosztly , csupn helyi
szksgletekhez igaztja egy szolgltats megjelenst. Mivel elvileg lehetetlen mindig,
minden szksgletet egyformn jl kielgteni, a felletosztlyok nlklzhetetlenek, mert
anlkl teszik lehetv a kzs hasznlatot, hogy minden felhasznlt egyetlen, kzs
knyszerzubbonyba erszakolnnak.
A felletek legtisztbb formjukban mg gpi kdot sem ignyelnek. Vegyk a 13.5-bl
a Vector specializlt vltozatt:
template<class T> class Vector<T*> : private Vector<void*> {
public:
typedef Vector<void*> Base;
Vector() : Base() {}
Vector(int i) : Base(i) {}
T*& operator[](int i) { return static_cast<T*&>(Base::operator[](i)); }
// ...
};
Ez a (rszlegesen) specializlt vltozat a nem biztonsgos Vector<void*>-ot egy sokkal
hasznlhatbb, tpusbiztos vektorosztly-csaldd teszi. A helyben kifejtett (inline) fggv-
nyek gyakran nlklzhetetlenek ahhoz, hogy a felletosztlyokat megengedhessk ma-
gunknak. A fentihez hasonl esetekben, ahol a helyben kifejtett kzvett fggvny csak t-
pusigaztst vgez, sem a futsi id, sem a trigny nem n.
Termszetesen az absztrakt tpust brzol absztrakt bzisosztlyok, melyeket konkrt tpu-
sok (25.2) valstanak meg, szintn a felletosztlyok egy formjt jelentik, mint ahogy
a 25.7 leri is azok. Itt azonban azon osztlyokra sszpontostunk, melyeknek a fellet
igaztsn kvl nincs ms feladatuk.
Vegyk kt, tbbszrs rklst hasznl hierarchia sszefslsnek problmjt. Mit le-
het tenni, ha nvtkzs lp fel, vagyis kt osztly ugyanazt a nevet hasznlja teljesen elt-
r mveleteket vgz virtulis fggvnyekhez? Vegynk pldul egy vadnyugati videojt-
kot, melyben a felhasznli beavatkozsokat egy ltalnos ablakosztly kezeli:
25. Az osztlyok szerepe 1049
class Window {
// ...
virtual void draw(); // kp megjelentse
};
class Cowboy {
// ...
virtual void draw(); // pisztoly elrntsa a pisztolytskbl
};
class Cowboy_window : public Cowboy, public Window {
// ...
};
A jtkban a cowboy mozgatst egy Cowboy_windowkpviseli s ez kezeli a felhasznl (j-
tkos) s a cowboy figura kztti klcsnhatsokat is. A Window s a Cowboy tagokknt
val bevezetse helyett tbbszrs rklst szeretnnk hasznlni, mivel szmos kiszolgl
fggvnyt kell meghatroznunk mind a Window-k, mind a Cowboy-ok rszre,
a Cowboy_window-kat pedig olyan fggvnyeknek szeretnnk tadni, melyek nem kvnnak
a programoztl klnleges munkt. Ez azonban ahhoz a problmhoz vezet, hogy meg kell
adni a Cowboy::draw() s Window::draw() fggvnyek Cowboy_window vltozatt.
A Cowboy_window-ban csak egy draw() nev fggvny lehet. Ennek viszont mivel
a Window-kat s Cowboy-okat a Cowboy_window-k ismerete nlkl kezeljk fell kell
rnia mind a Cowboy, mind a Window draw() fggvnyt. Egyetlen vltozattal nem rhatjuk
fell mind a kettt, mert a kzs nv ellenre a kt draw() fggvny szerepe ms. Vgeze-
tl azt is szeretnnk, hogy a Cowboy_window egyedi, egyrtelm neveket biztostson az
rklt Cowboy::draw() s Window:: draw() fggvnyek szmra.
A problma megoldshoz szksgnk van egy-egy tovbbi osztlyra a Cowboy-hoz s
a Window-hoz. Ezek az osztlyok vezetik be a kt j nevet a draw() fggvnyekre s biz-
tostjk, hogy a Cowboy-ban s a Window-ban a draw() fggvnyhvsok az j nven hv-
jk a fggvnyeket:
class CCowboy : public Cowboy { // fellet a Cowboy-hoz: a draw()-t tnevezi
public:
virtual int cow_draw() = 0;
void draw() { cow_draw(); } // fellrja Cowboy::draw()-t
};
class WWindow : public Window { // fellet a Window-hoz: a draw()-t tnevezi
public:
virtual int win_draw() = 0;
void draw() { win_draw(); } // fellrja Window::draw()-t
};
Tervezs a C++ segtsgvel 1050
Most mr sszerakhatunk egy Cowboy_window-t a CCowboy s a WWindow felletoszt-
lyokbl s a kvnt hatssal fellbrlhatjuk a cow_draw()-t s win_draw()-t:
class Cowboy_window : public CCowboy, public WWindow {
// ...
void cow_draw();
void win_draw();
};
Vegyk szre, hogy a problma csak amiatt volt komoly, hogy a kt draw() fggvnynek
ugyanaz a paramtertpusa. Ha a paramtertpusok klnbznek, a szoksos tlterhels-
feloldsi szablyoknak ksznheten nem lesz problma, annak ellenre, hogy az egyms-
sal kapcsolatban nem lv fggvnyeknek ugyanaz a nevk.
Egy felletosztly minden hasznlatra elkpzelhetnk egy klnleges cl nyelvi bvtst,
mely a kvnt igaztst hatkonyabban vagy elegnsabban tudn elvgezni. A felletoszt-
lyok egyes hasznlatai azonban ritkk, s ha mindet egyedi nyelvi szerkezettel tmogatnnk,
tlzottan bonyolultt tennnk programjainkat. Az osztlyhierarchik sszefslsbl ered
nvtkzsek nem ltalnosak (ahhoz kpest, hogy milyen gyakran r osztlyt egy progra-
moz), inkbb abbl erednek, hogy eltr krnyezetben ltrehozott hierarchikat olvasz-
tunk ssze (pl. jtkokat s ablakrendszereket). Az ilyen eltr hierarchikat nem knny
sszefslni s a nvtkzsek feloldsa gyakran csak csepp a tengerben a programoz sz-
mra. Problmt jelenthet az eltr hibakezels, kezdeti rtkads s trkezelsi md is.
A nvtkzsek feloldst itt azrt trgyaljuk, mert a kzvett/tovbbt szerepet betlt fe-
lletosztlyok bevezetse ms terleteken is alkalmazhat; nem csak nevek megvltoztat-
sra, hanem paramterek s visszatrsi rtkek tpusnak mdostsra, futsi idej ellen-
rzs bevezetsre stb. is. Minthogy a CCowboy::draw() s WWindow::draw() fggvnyek
virtulisak, nem optimalizlhatk egyszer helyben kifejtssel. Lehetsges viszont, hogy
a fordt felismeri, hogy ezek egyszer tovbbt fggvnyek, s a rajtuk tmen hvsi ln-
cok alapjn kpes optimlis kdot kszteni.
25.6.1. Felletek igaztsa
A felletfggvnyek egyik f felhasznlsa egy fellet igaztsa gy, hogy az jobban illesz-
kedjk a felhasznl elvrsaihoz; vagyis egyetlen felletbe helyezzk azt a kdot, amely
klnben a felhasznli kdon bell szt lenne szrva. A szabvnyos vector pldul nulla
alap, azaz az els elem indexe 0. Azoknak a felhasznlknak, akik a 0-tl size-1-ig terje-
d tartomnytl eltrt akarnak, igaztst kell vgeznik:
25. Az osztlyok szerepe 1051
void f()
{
vector v<int>(10); // [0:9] tartomny
// gy tesznk, mintha v a [1:10] tartomnyban lenne
for (int i = 1; i<=10; i++) {
v[i-1] = 7; // ne felejtsk az indexet igaztani
// ...
}
}
Mg jobb, ha a vector-nak tetszleges hatrokat biztostunk:
class Vector : public vector<int> {
int lb;
public:
Vector(int low, int high) : vector<int>(high-low+1) { lb=low; }
int& operator[](int i) { return vector<int>::operator[](i-lb); }
int low() { return lb; }
int high() { return lb+size()-1; }
};
A Vector az albbi mdon hasznlhat:
void g()
{
Vector v(1,10); // [1:10] tartomny
for (int i = 1; i<=10; i++) {
v[i] = 7;
// ...
}
}
Ez az elz pldhoz kpest nem okoz tbbletterhelst. Vilgos, hogy a Vector vltozatot
knnyebb olvasni s kisebb a hibzs lehetsge is.
A felletosztlyok rendszerint meglehetsen kicsik s kevs feladatot vgeznek, de minde-
ntt felbukkannak, ahol klnbz hagyomnyok szerint megrt programoknak kell
egyttmkdnik, mivel ilyenkor a klnbz szablyok kztt kzvettsre van szksg.
Tervezs a C++ segtsgvel 1052
A felletosztlyokat gyakran hasznljk pldul arra, hogy nem C++ kdnak C++ felletet
adjanak, s az alkalmazs kdjt elszigeteljk a knyvtraktl (nyitva hagyva egy knyv-
tr msikkal val helyettestsnek a lehetsgt).
A felletosztlyok msik fontos felhasznlsi terlete az ellenrztt vagy korltozott felle-
tek biztostsa. Nem szokatlan pldul, ha olyan egsz tpus vltozink vannak, melyek
rtke csak egy adott tartomnyon bell mozoghat. Ez (futsi idben) egy egyszer sablon-
nal knyszerthet ki:
template<int low, int high> class Range {
int val;
public:
class Error { }; // kivtelosztly
Range(int i) { Assert<Error>(low<=i&&i<high); val = i; } // lsd 24.3.7.2
Range operator=(int i) { return *this=Range(i); }
operator int() { return val; }
// ...
};
void f(Range<2,17>);
void g(Range<-10,10>);
void h(int x)
{
Range<0,2001> i = x; // Range::Error kivtelt vlthat ki
int i1 = i;
f(3);
f(17); // Range::Error kivtelt vlt ki
g(-7);
g(100); // Range::Error kivtelt vlt ki
}
A Range sablon knnyen bvthet gy, hogy tetszleges skalr tpus tartomnyok keze-
lsre legyen kpes (25.10[7]).
Azokat a felletosztlyokat, amelyek a ms osztlyokhoz val hozzfrst ellenrzik vagy
azok fellett igaztjk, nha beburkol (csomagol, wrapper) osztlyoknak nevezzk.
25. Az osztlyok szerepe 1053
25.7. Ler osztlyok
Az absztrakt tpusok hatkony elvlasztst biztostanak a felletek s megvalstsaik k-
ztt, de az absztrakt tpus ltal adott fellet s annak egy konkrt tpus ltal nyjtott meg-
valstsa kztti kapcsolat ahogyan a 25.3-ban hasznltuk tarts. Nem lehet pldul
egy absztrakt bejrt az egyik forrsrl (mondjuk egy halmazrl) tktni egy msikra
(mondjuk egy adatfolyamra), ha az eredeti forrs kimerlt.
Tovbb, hacsak nem mutatkon vagy referencikon keresztl kezelnk egy absztrakt osz-
tlyt kpvisel objektumot, elvesztjk a virtulis fggvnyek elnyeit. A felhasznli kd
fggni fog a megvalst osztlyoktl, mivel az absztrakt tpusok szmra nem foglalhat
hely statikusan vagy a veremben (belertve az rtk szerinti paramtertvtelt is), anlkl,
hogy tudnnk a tpus mrett. A mutatk s hivatkozsok hasznlata azzal jr, hogy a tr-
kezels terhe a felhasznl kdra hrul.
Az absztrakt osztlyos megkzelts msik korltja az, hogy az osztlyobjektumok rgztett
mretek. Az osztlyokat azonban fogalmak brzolsra hasznljuk, melyek megvalst-
shoz klnbz mennyisg trterlet kell.
E krdsek kezelsnek kedvelt mdja kt rszre osztani azt, amit egyetlen objektumknt
hasznlunk. Az egyik lesz a felhasznli felletet ad ler (handle), a msik pedig az b-
rzols, amely az objektum llapotnak egszt vagy annak java rszt trolja. A ler s az
brzols kztti kapcsolatot ltalban egy, a lerban lev mutat biztostja. A lerban az
brzolsra hivatkoz mutatn kvl ltalban mg van egy-kt adat, de nem sok. Ebbl k-
vetkezik, hogy a ler szerkezete rendszerint stabil (mg akkor is, ha az brzols megvl-
tozik), illetve hogy a lerk meglehetsen kicsik s viszonylag szabadon mozgathatk, gy
a felhasznlnak nem kell mutatkat s referencikat hasznlnia.
Tervezs a C++ segtsgvel 1054
Ler brzols
A 11.12 String-je a ler egyszer pldja. A ler felletet, hozzfrs-szablyozst s tr-
kezelst biztost az brzols rszre. Ebben az esetben mind a ler, mind az brzols
konkrt tpusok, br az brzol osztly tbbnyire absztrakt osztly szokott lenni.
Vegyk a 25.3-bl a Set absztrakt tpust. Hogyan gondoskodhatunk szmra egy lerrl s
milyen elnykkel, illetve htrnyokkal jrhat ez? Egy adott halmazosztlyhoz egyszeren
megadhatunk egy lert, a -> opertor tlterhelsvel:
template<class T> class Set_handle {
Set<T>* rep;
public:
Set<T>* operator->() { return rep; }
Set_handle(Set<T>* pp) : rep(pp) { }
};
Ez nem befolysolja jelentsen a Set-ek hasznlatnak mdjt; egyszeren Set_handle-eket
adunk t Set&-ek vagy Set*-ok helyett:
void f(Set_handle<int> s)
{
for (int* p = s->first(); p; p = s->next())
{
// ...
}
}
void user()
{
Set_handle<int> sl(new List_set<int>);
Set_handle<int> v(new Vector_set<int>(100));
f(sl);
f(v);
}
Gyakran tbbet kvnunk egy lertl, mint hogy a hozzfrsrl gondoskodjon. Pldul,
ha a Set osztlyt s a Set_handle osztlyt egytt terveztk, a hivatkozsokat knnyen meg-
szmllhatjuk, ha minden Set-ben elhelyeznk egy hasznlatszmllt. Persze a lert lta-
lban nem akarjuk egytt tervezni azzal, aminek a lerja, gy nll objektumban kell t-
rolnunk minden adatot, amit a lernak biztostania kell. Ms szval, szksgnk lenne nem
tolakod (non-intensive) lerkra is a tolakodk mellett. me egy ler, mely felszmol egy
objektumot, amikor annak utols lerja is megsemmisl:
25. Az osztlyok szerepe 1055
template<class X> class Handle {
X* rep;
int* pcount;
public:
X* operator->() { return rep; }
Handle(X* pp) : rep(pp), pcount(new int(1)) { }
Handle(const Handle& r) : rep(r.rep), pcount(r.pcount) { (*pcount)++; }
Handle& operator=(const Handle& r)
{
if (rep == r.rep) return *this;
if (--(*pcount) == 0) {
delete rep;
delete pcount;
}
rep = r.rep;
pcount = r.pcount;
(*pcount)++;
return *this;
}
~Handle() { if (--(*pcount) == 0) { delete rep; delete pcount; } }
// ...
};
Egy ilyen ler szabadon tadhat:
void f1(Handle<Set>);
Handle<Set> f2()
{
Handle<Set> h(new List_set<int>);
// ...
return h;
}
void g()
{
Handle<Set> hh = f2();
f1(hh);
// ...
}
Tervezs a C++ segtsgvel 1056
Itt az f2()-ben ltrehozott halmaz trldik a g()-bl val kilpskor hacsak f1() fenn nem
tartja annak egy msolatt. (A programoznak errl nem kell tudnia.)
Termszetesen ennek a knyelemnek ra van, de a hasznlatszmll trolsnak s fenn-
tartsnak kltsge a legtbb alkalmazsnl elfogadhat.
Nha hasznos az brzolsra hivatkoz mutatt a lerbl kinyerni s kzvetlenl felhasz-
nlni. Erre akkor lehet szksg, ha egy objektumot egy olyan fggvnynek kell tadni,
amely nem ismeri a lerkat. Ez a megolds jl mkdik, feltve, hogy a hvott fggvny
nem semmisti meg a neki tadott objektumot vagy egy arra hivatkoz mutatt nem trol
a hvhoz val visszatrs utni hasznlatra. Egy olyan mvelet szintn hasznos lehet,
amely a lert egy j brzolshoz kapcsolja:
template<class X> class Handle {
// ...
X* get_rep() { return rep; }
void bind(X* pp)
{
if (pp != rep) {
if (--*pcount == 0) {
delete rep;
*pcount = 1; // pcount jrahasznostsa
}
else
pcount = new int(1); // j pcount
rep = pp;
}
}
};
Vegyk szre, hogy Handle-bl j osztlyokat szrmaztatni nem klnsebben hasznos, ez
ugyanis egy konkrt tpus, virtulis fggvnyek nlkl. Az alapelv, hogy egy bzisosztly
ltal meghatrozott teljes osztlycsaldhoz egyetlen ler osztlyunk legyen. Ebbl a bzis-
osztlybl szrmaztatni viszont mr hasznos lehet. Ez a csompont-osztlyokra ppgy r-
vnyes, mint az absztrakt tpusokra.
Fenti formjban a Handle nem foglalkozik rklssel. Ahhoz, hogy egy olyan osztlyunk
legyen, mely gy mkdik, mint egy valdi hasznlatszmll, a Handle-t egytt kell hasz-
nlni a 13.6.3.1 Ptr-jvel (lsd 25.10[2]).
25. Az osztlyok szerepe 1057
Az olyan lert, melynek fellete kzel azonos azon osztlyval, melynek lerja, gyakran
nevezzk proxy-nak. Ez klnsen azokra a lerkra vonatkozik, melyek tvoli gpen lv
objektumokra hivatkoznak.
25.7.1. A lerk mveletei
A -> opertor tlterhelse lehetv teszi, hogy egy ler minden hozzfrskor megkapja
a vezrlst, s valamilyen mveletet vgezzen egy objektumon. A lern keresztl hozz-
frhet objektum felhasznlsainak szmrl pldul statisztikt kszthetnk:
template <class T> class Xhandle {
T* rep;
int no_of_accesses;
public:
T* operator->() { no_of_accesses++; return rep; }
// ...
};
Azon lerk, melyeknl a hozzfrs eltt s utn is valamilyen mveletet kell vgezni, ki-
dolgozottabb kdot ignyelnek. Tegyk fel pldul, hogy egy olyan halmazt szeretnnk,
amely zrolhat, amg beszrs vagy eltvolts folyik. Az brzol osztly fellett lnye-
gben meg kell ismtelni a ler osztlyban:
template<class T> class Set_controller {
Set<T>* rep;
Lock lock;
// ...
public:
void insert(T* p) { Lock_ptr x(lock); rep->insert(p); } // lsd 14.4.1
void remove(T* p) { Lock_ptr x(lock); rep->remove(p); }
int is_member(T* p) { return rep->is_member(p); }
T get_first() { T* p = rep->first(); return p ? *p : T(); }
T get_next() { T* p = rep->next(); return p ? *p : T(); }
T first() { Lock_ptr x(lock); T tmp = *rep->first(); return tmp; }
T next() { Lock_ptr x(lock); T tmp = *rep->next(); return tmp; }
// ...
};
Tervezs a C++ segtsgvel 1058
Ezekrl a tovbbt fggvnyekrl gondoskodni fradsgos (gy hibt is vthetnk kz-
ben), jllehet nehzsget nem jelent s a futsi idt sem nveli.
Vegyk szre, hogy a Set-nek csak nmelyik fggvnye kvn zrolst. Tapasztalatom sze-
rint ltalnos, hogy egy el- s uttevkenysgeket ignyl osztly a mveleteket csak n-
hny tagfggvnynl kvnja meg. A minden mveletnl val zrols ahogy egyes rend-
szerfigyelknl lenni szokott felesleges zrolsokhoz vezet s szreveheten lassthatja
a prhuzamos vgrehajtst.
A lern vgzett mveletek alapos kidolgozsnak elnye a -> opertor tlterhelsvel
szemben az, hogy a Set_controller osztlybl szrmaztathatunk. Sajnos a lerk nhny el-
nys tulajdonsgt feladjuk, ha a szrmaztatott osztlyhoz adattagokat tesznk, mert a k-
zsen hasznlt kd mennyisge az egyes lerkban lev kd mennyisghez viszonytva
cskken.
25.8. Keretrendszerek
A 25.225.7-ben lert osztlyfajtkbl ptett komponensek azltal tmogatjk a kdterve-
zst s -jrahasznostst, hogy ptkockkat s kombincis lehetsgeket biztostanak.
A programozk s az alkalmazskszt eszkzk ptik fel azt a vzat, amelybe ezek az
ptkockk beleillenek. A tervezs s jrahasznosts tmogatsnak egy msik, ltalban
nehezebb mdja egy olyan, kzs vzat ad kd megrsa, melybe az alkalmazskszt
ptkockkknt az adott alkalmazsra jellemz kdokat illeszti be. Ez az, amit ltalban
keretrendszernek (application framework) hvunk. Az ilyen vzat biztost osztlyok fel-
lete gyakran olyan kvr, hogy hagyomnyos rtelemben aligha nevezhetk tpusoknak;
inkbb teljes alkalmazsnak tnnek, br nem vgeznek semmilyen tevkenysget; azokat
az alkalmazsprogramoz biztostja.
Pldakppen vegynk egy szrt, vagyis egy olyan programot, amely egy bemeneti adat-
folyambl olvas, majd annak alapjn (esetleg) elvgez nhny mveletet, (esetleg) egy ki-
meneti adatfolyamot hoz ltre, s (esetleg) ad egy vgeredmnyt. Els tletnk bizonyra
az, hogy a programhoz olyan keretrendszert ksztsnk, amely olyan mvelethalmazt ad
meg, melyet egy alkalmazsprogramoz biztost:
25. Az osztlyok szerepe 1059
class Filter {
public:
class Retry {
public:
virtual const char* message() { return 0; }
};
virtual void start() { }
virtual int read() = 0;
virtual void write() { }
virtual void compute() { }
virtual int result() = 0;
virtual int retry(Retry& m) { cerr << m.message() << '\n'; return 2; }
virtual ~Filter() { }
};
Azon fggvnyeket, melyeket a szrmaztatott osztlynak kell biztostania, tisztn virtulis
(pure virtual) fggvnyekknt deklarltuk; a tbbit egyszeren gy definiltuk, mint amik
nem vgeznek mveletet.
A keretrendszer gondoskodik egy fciklusrl s egy kezdetleges hibakezel eljrsrl is:
int main_loop(Filter* p)
{
for(;;) {
try {
p->start();
while (p->read()) {
p->compute();
p->write();
}
return p->result();
}
catch (Filter::Retry& m) {
if (int i = p->retry(m)) return i;
}
catch (...) {
cerr << "Vgzetes szrhiba\n";
return 1;
}
}
}
Tervezs a C++ segtsgvel 1060
A programot vgl gy rhatjuk meg:
class My_filter : public Filter {
istream& is;
ostream& os;
int nchar;
public:
int read() { char c; is.get(c); return is.good(); }
void compute() { nchar++; }
int result() { os << nchar << " elolvasott karakter\n"; return 0; }
My_filter(istream& ii, ostream& oo) : is(ii), os(oo), nchar(0) { }
};
s gy indthatjuk el:
int main()
{
My_filter f(cin,cout);
return main_loop(&f);
}
Termszetesen ahhoz, hogy igazn hasznt vegyk, a keretrendszernek tbb szerkezetet s
jval tbb szolgltatst kellene nyjtania, mint ebben az egyszer pldban. A keretrendszer
ltalban csompont-osztlyokbl ll hierarchia. Ha egy mlyen egymsba gyazott ele-
mekbl ll hierarchiban az alkalmazsprogramozval ratjuk meg a levl osztlyokat, le-
hetv vlik a kzs elemek tbb program ltal val hasznlata s a hierarchia ltal nyjtott
szolgltatsok jrahasznostsa. A keretrendszert egy knyvtr is tmogathatja, olyan oszt-
lyokkal, melyek az alkalmazsprogramoz szmra a mveletosztlyok meghatrozsnl
hasznosnak bizonyulhatnak.
25.9. Tancsok
[1] Az egyes osztlyok hasznlatra vonatkozan hozzunk megfontolt dntseket.
25.1.
[2] vakodjunk a vlaszts knyszertl, melyet az osztlyok eltr fajti okoznak.
25.1.
[3] Egyszer, fggetlen fogalmak brzolsra hasznljunk konkrt tpusokat. 25.2.
[4] Hasznljunk konkrt tpusokat azon fogalmak brzolsra, melyeknl nlk-
lzhetetlen az optimlishoz kzeli hatkonysg. 25.2.
25. Az osztlyok szerepe 1061
[5] Konkrt osztlybl ne szrmaztassunk. 25.2.
[6] Hasznljunk absztrakt osztlyokat olyan felletek brzolsra, ahol az objektu-
mok brzolsa vltozhat. 25.3.
[7] Hasznljunk absztrakt osztlyokat olyan felletek brzolsra, ahol az objektu-
mok klnbz brzolsainak egytt kell lteznik. 25.3.
[8] A ltez tpusok j felleteinek brzolsra hasznljunk absztrakt osztlyokat.
25.3.
[9] Ha hasonl fogalmak kzsen hasznljk a megvalsts egyes rszeit, hasznl-
junk csompont-osztlyokat. 25.4.
[10] A megvalsts fokozatos kidolgozshoz hasznljunk csompont-osztlyokat.
25.4.
[11] Az objektumok felletnek kinyersre hasznljunk futsi idej tpusazonos-
tst. 25.4.1.
[12] Az llapothoz kapcsold mveletek brzolsra hasznljunk osztlyokat.
25.5.
[13] Azon mveletek brzolsra, melyeket trolni, tvinni, vagy ksleltetni kell,
hasznljunk osztlyokat. 25.5.
[14] Ha egy osztlyt j felhasznlshoz kell igaztani (az osztly mdostsa nlkl),
hasznljunk felletosztlyokat. 25.6.
[15] Ellenrzs hozzadsra hasznljunk felletosztlyokat. 25.6.1.
[16] Hasznljunk lerkat, hogy elkerljk a mutatk s referencik kzvetlen hasz-
nlatt. 25.7.
[17] A kzsen hasznlt brzolsok kezelsre hasznljunk lerkat. 25.7.
[18] Ha az alkalmazsi terlet lehetv teszi, hogy a vezrlsi szerkezetet elre meg-
hatrozzuk, hasznljunk keretrendszert. 25.8.
25.10. Gyakorlatok
1. (*1) A 25.4.1 Io sablonja nem mkdik beptett tpusokra. Mdostsuk gy,
hogy mkdjn.
2. (*1.5) A 25.7 Handle sablonja nem tkrzi azon osztlyok rklsi kapcsolatait,
amelyeknek lerja. Mdostsuk gy, hogy tkrzze (vagyis lehessen egy
Handle<Circle>-lel egy Handle<Shape>-nek rtket adni, de nem fordtva).
3. (*2.5) Ha adott egy String osztly, azt brzolsknt hasznlva s mveleteit vir-
tulis fggvnyekknt megadva hozzunk ltre egy msik karakterlnc-osztlyt.
Hasonltsuk ssze a kt osztly teljestmnyt. Prbljunk tallni egy rtelmes
osztlyt, amely a legjobban a virtulis fggvnyekkel rendelkez karakterlnc-
osztlybl trtn nyilvnos szrmaztatssal valsthat meg.
Tervezs a C++ segtsgvel 1062
4. (*4) Tanulmnyozzunk kt szles krben hasznlt knyvtrat. Osztlyozzuk
a knyvtri osztlyokat konkrt tpusokknt, absztrakt tpusokknt, csompont-
osztlyokknt, ler osztlyokknt, s felletosztlyokknt. Hasznlnak-e
absztrakt s konkrt csompont-osztlyokat? Van-e a knyvtrakban lev oszt-
lyokra megfelelbb osztlyozs? Hasznlnak-e kvr felleteket? Milyen trke-
zelsi mdot hasznlnak? Milyen lehetsgek vannak ha vannak futsi idej
tpusinformcira?
5. (*2) A Filter vz (25.8) segtsgvel rjunk olyan programot, mely egy bemeneti
adatfolyambl eltvoltja a szomszdos ismtelt szavakat, majd az eredmnyt t-
msolja a kimenetre.
6. (*2) A Filter keretrendszer segtsgvel rjunk olyan programot, mely egy beme-
neti adatfolyamban megszmllja a szavak gyakorisgt s kimenetknt gyakori-
sgi sorrendben felsorolja a (sz, szm) prokat.
7. (*1.5) rjunk egy Range sablont, mely sablonparamterekknt veszi t a tarto-
mnyt s az elemtpust.
8. (*1) rjunk egy Range sablont, mely a tartomnyt konstruktor-paramterekknt
veszi t.
9. (*2) rjunk egy egyszer karakterlnc-osztlyt, mely nem vgez hibaellenrzst.
rjunk egy msik osztlyt, mely ellenrzi az elbbihez val hozzfrst. Vitassuk
meg az alapszolgltatsok s a hibaellenrzs elvlasztsnak elnyeit s htr-
nyait.
10. (*2.5) Ksztsk el a 25.4.1 objektum I/O rendszert nhny tpusra, kztk
legalbb az egszekre, a karakterlncokra s egy tetszlegesen kivlasztott osz-
tlyhierarchira.
11. (*2.5) Hatrozzuk meg a Storable osztlyt, mint absztrakt bzisosztlyt
a write_out() s read_in() virtulis fggvnyekkel. Az egyszersg kedvrt t-
telezzk fel, hogy egy perzisztens trolhely meghatrozshoz egy karakter-
lnc elegend. Hasznljuk fel a Storage osztlyt egy olyan szolgltatsban, mely
a Storable-bl szrmaztatott osztlyok objektumait rja lemezre s ugyanilyen
objektumokat olvas lemezrl. Ellenrizzk nhny tetszs szerint vlasztott osz-
tllyal.
12. (*4) Hozzuk ltre a Persistent alaposztlyt a save() s no_save() mveletekkel,
melyek egy destruktor ltal ellenrzik, hogy egy objektum bekerlt-e az lland
trba. A save()-en s no_save()-en kvl mg milyen hasznlhat mveleteket
nyjthatna a Persistent? Teszteljk a Persistent osztlyt nhny tetszs szerint v-
lasztott osztllyal. Csompont-osztly, konkrt tpus, vagy absztrakt tpus-e
Persistent? Mirt?
13. (*3) rjunk egy Stack osztlyt, melynek megvalstsa futsi idben mdostha-
t. Tipp: Egy jabb indirekci minden problmt megold.
25. Az osztlyok szerepe 1063
14. (*3.5) Ksztsk el az Oper osztlyt, amely egy Id tpus (string vagy C stlus ka-
rakterlnc) azonostt s egy mveletet (fggvnymutatt vagy fggvnyobjektu-
mot) tartalmaz. Hatrozzuk meg a Cat_object osztlyt, mely Oper-ek listjt s
egy void*-ot tartalmaz. Lssuk el a Cat_object-et egy add_oper(Oper) mvelettel,
mely egy Oper-t ad a listhoz; egy remove_oper(Id)-del, mely egy Id-del azonos-
tott Oper-t eltvolt a listbl; valamint egy operator() (Id, arg)-gal, mely meghv-
ja az Id-del azonostott Oper-t. Ksztsnk egy Cat-eket trol vermet egy
Cat_object segtsgvel. rjunk egy kis programot ezen osztlyok hasznlatra.
15. (*3) Ksztsnk egy Object sablont a Cat_object osztly alapjn. Hasznljuk fel
az Object-et egy String-verem megvalstshoz. rjunk egy kis programot
a sablon hasznlatra.
16. (*2.5) Hatrozzuk meg az Object osztly Class nev vltozatt, mely biztostja,
hogy az azonos mveletekkel rendelkez objektumok kzs mveletsort hasz-
nljanak. rjunk egy kis programot a sablon hasznlatra.
17. (*2) Ksztsnk egy olyan Stack sablont, mely egy, az Object sablon ltal megva-
lstott verem rszre egy hagyomnyos, tpusbiztos felletrl gondoskodik.
Hasonltsuk ssze ezt a vermet az elz gyakorlatokban tallt veremosztlyok-
kal. rjunk egy kis programot a sablon hasznlatra.
18. (*3) rjunk egy osztlyt olyan mveletek brzolsra, melyeket vgrehajtsra
egy msik gpre kell tvinni. Teszteljk egy msik gpnek tnylegesen elkl-
dtt parancsokkal vagy parancsoknak egy fjlba rsval, melyeket ezutn a fjl-
bl kiolvasva hajtunk vgre.
19. (*2) rjunk egy osztlyt fggvnyobjektumok alakjban brzolt mveletek
egyttes hasznlatra. Ha adott f s g fggvnyobjektum, a Compose(f,g) hoz-
zon ltre egy olyan objektumot, mely egy g-hez illeszked x paramterrel meg-
hvhat, s f(g(x))-et ad vissza, feltve, hogy a g() ltal visszaadott rtk egy, az
f() ltal elfogadhat paramtertpus.
Tervezs a C++ segtsgvel 1064
Fggelkek s trgymutat
A fggelkek a C++ nyelvtanval; a C s a C++ kztt, valamint a szabvnyos s a szabv-
nyosts eltti C++-vltozatok kztt felmerl kompatibilitsi krdsekkel; illetve a nyelv
nhny egyb szolgltatsval foglalkoznak. Az igen rszletes trgymutat a knyv lnye-
ges rsze.
Fejezetek
A Nyelvtan
B Kompatibilits
C Technikai rszletek
D Helyi sajtossgok
E Kivtelbiztossg a standard knyvtrban
I Trgymutat
Nyelvtan
Nincs nagyobb veszly, ami egy tanrra leselkedik,
mint hogy szavakat tant a dolgok helyett.
(Marc Block)
Bevezets Kulcsszavak Nyelvi egysgek Programok Kifejezsek Utastsok
Deklarcik Deklartorok Osztlyok Szrmaztatott osztlyok Klnleges tagfgg-
vnyek Tlterhels Sablonok Kivtelkezels Az elfeldolgoz direktvi
A.1. Bevezets
A C++ szintaxisnak (formai kvetelmnyeinek) itt tallhat sszefoglalsa a megrts
megknnytst clozza. Nem a nyelv pontos lersa a cl; az albb lertaknl a C++ tbb
rvnyes nyelvtani szerkezetet is elfogad. Az egyszer kifejezseknek a deklarciktl va-
l megklnbztetsre a tbbrtelmsg-feloldsi szablyokat (A.5, A.7), a formailag
helyes, de rtelmetlen szerkezetek kiszrsre pedig a hozzfrsi, tbbrtelmsgi s t-
pusszablyokat egyttesen kell alkalmaznunk.
A
A C s C++ szabvny a legkisebb klnbsgeket is formai klnbsgekkel s nem megszo-
rtsokkal fejezi ki. Ez nagyfok pontossgot ad, de nem mindig javtja a kd olvashatsgt.
A.2. Kulcsszavak
A typedef (4.9.7), nvtr (8.2), osztly (10. fejezet), felsorol tpus (4.8), s template (13.
fejezet) deklarcik j krnyezetfgg kulcsszavakat vezetnek be a programba.
typedef-nv:
azonost
nvtr-nv:
eredeti-nvtr-nv
nvtr-lnv
eredeti-nvtr-nv:
azonost
nvtr-lnv:
azonost
osztlynv:
azonost
sablon-azonost
felsorolsnv:
azonost
sablonnv:
azonost
Jegyezzk meg, hogy egy osztlyt megnevez typedef-nv egyben osztlynv is.
Ha nem adjuk meg kifejezetten egy azonostrl, hogy egy tpus neve, a fordt felttelezi,
hogy nem tpust nevez meg (lsd C.13.5).
A C++- kulcsszavai a kvetkezk:
Fggelkek s trgymutat 1068
A.3. Nyelvi egysgek
A szabvnyos C s C++ nyelvtanok a nyelvi egysgeket nyelvtani szerkezetekknt mutatjk be.
Ez nveli a pontossgot, de a nyelvtan mrett is, s nem mindig javtja az olvashatsgot:
hex-quad:
hexadecimlis-szmjegy hexadecimlis-szmjegy hexadecimlis-szmjegy hexadecimlis-szmjegy
ltalnos-karakternv:
\u hex-quad
\U hex-quad hex-quad
elfeldolgoz-szimblum:
fejllomny-nv
azonost
pp-szm
karakterliterl
karakterlnc-literl
elfeldolgoz-utasts-vagy-mveleti-jel
minden nem reshely karakter, ami nem tartozik a fentiek kz
szimblum:
azonost
kulcssz
literl
A. Nyelvtan 1069
C++ kulcsszavak
and and_eq asm auto bitand bitor
bool break case catch char class
compl const const_cast continue default delete
do double dynamic_cast else enum explicit
export extern false float for friend
goto if inline int long mutable
namespace new not not_eq operator or
or_eq private protected public register reinterpret_cast
return short signed sizeof static static_cast
struct switch template this throw true
try typedef typeid typename union unsigned
using virtual void volatile wchar_t while
xor xor_eq
opertor
mveleti-jel
fejllomny-nv:
<h-char-sorozat>
"q-char-sorozat"
h-char-sorozat:
h-char
h-char-sorozat h-char
h-char:
a forrs-karakterkszlet brmely tagja, kivve az jsor s > karaktereket
q-char-sorozat:
q-char
q-char-sorozat q-char
q-char:
a forrs-karakterkszlet brmely tagja, kivve az jsor s " karaktereket
pp-szm:
szmjegy
. szmjegy
pp-szm szmjegy
pp-szm nem-szmjegy
pp-szm e eljel
pp-szm E eljel
pp-szm .
azonost:
nem-szmjegy
azonost nem-szmjegy
azonost szmjegy
nem-szmjegy: a kvetkezk egyike
ltalnos-karakternv
_ a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
szmjegy: a kvetkezk egyike
0 1 2 3 4 5 6 7 8 9
Fggelkek s trgymutat 1070
elfeldolgoz-utasts-vagy-mveleti-jel: a kvetkezk egyike
{ } [ ] # ## ( ) <: :> <% %> %:%:
%: ; : ? :: . .* + - * / % ^
& | ~ ! = < > += -= *= /= %= ^=
&= |= <<= >>= << >> == != <= >= && || ++
-- , -> ->* ... new delete and and_eq bitand
bitor compl not or not_eq xor or_eq xor_eq
literl:
egszliterl
karakterliterl
lebegpontos-literl
karakterlnc-literl
logikai-literl
egszliterl:
decimlis-literl egsz-uttag
nem ktelez
oktlis-literl egsz-uttag
nem ktelez
hexadecimlis-literl egsz-uttag
nem ktelez
decimlis-literl:
nem-nulla-szmjegy
decimlis-literl szmjegy
oktlis-literl:
0
oktlis-literl oktlis-szmjegy
hexadecimlis-literl:
0x hexadecimlis-szmjegy
0X hexadecimlis-szmjegy
hexadecimlis-literl hexadecimlis-szmjegy
nem-nulla-szmjegy: a kvetkezk egyike
1 2 3 4 5 6 7 8 9
oktlis-szmjegy: a kvetkezk egyike
0 1 2 3 4 5 6 7
hexadecimlis-szmjegy: a kvetkezk egyike
0 1 2 3 4 5 6 7 8 9
a b c d e f
A B C D E F
egsz-uttag:
eljel-nlkli-uttag long-uttag
nem ktelez
long-uttag eljel-nlkli-uttag
nem ktelez
A. Nyelvtan 1071
eljel-nlkli-uttag: a kvetkezk egyike
u U
long-uttag: a kvetkezk egyike
l L
karakterliterl:
'c-char-sorozat'
L'c-char-sorozat'
c-char-sorozat:
c-char
c-char-sorozat c-char
c-char:
a forrs-karakterkszlet brmely tagja, kivve az egyszeres idzjelet, a fordtott perjelet,
s az jsor karaktert
escape-sorozat
ltalnos-karakternv
escape-sorozat:
egyszer-escape-sorozat
oktlis-escape-sorozat
hexadecimlis-escape-sorozat
egyszer-escape-sorozat: a kvetkezk egyike
\' \" \? \\ \a \b \f \n \r \t \v
oktlis-escape-sorozat:
\ oktlis-szmjegy
\ oktlis-szmjegy oktlis-szmjegy
\ oktlis-szmjegy oktlis-szmjegy oktlis-szmjegy
hexadecimlis-escape-sorozat:
\x hexadecimlis-szmjegy
hexadecimlis-escape-sorozat hexadecimlis-szmjegy
lebegpontos-literl:
trt-konstans kitev-rsz
nem ktelez
lebegpontos-uttag
nem ktelez
szmjegy-sorozat kitev-rsz lebegpontos-uttag
nem ktelez
trt-konstans:
szmjegy-sorozat
nem ktelez
. szmjegy-sorozat
szmjegy-sorozat .
kitev-rsz:
e eljel
nem ktelez
szmjegy-sorozat
E eljel
nem ktelez
szmjegy-sorozat
Fggelkek s trgymutat 1072
eljel: a kvetkezk egyike
+ -
szmjegy-sorozat:
szmjegy
szmjegy-sorozat szmjegy
lebegpontos-uttag: a kvetkezk egyike
f l F L
karakterlnc-literl:
"s-char-sorozat
nem ktelez
"
L"s-char-sorozat
nem ktelez
"
s-char-sorozat:
s-char
s-char-sorozat s-char
s-char:
a forrs-karakterkszlet brmely tagja, kivve a ketts idzjelet, a fordtott perjelet, s az jsort
escape-sorozat
ltalnos-karakternv
logikai-literl:
false
true
A.4. Programok
A programok sszeszerkeszts ltal sszelltott fordtsi egysgek (translation unit) gyjtem-
nyei (9.4). A fordtsi egysgek vagy mskpp forrsfjlok, deklarcik sorozatbl llnak:
fordtsi-egysg:
deklarci-sorozat
nem ktelez
A. Nyelvtan 1073
A.5. Kifejezsek
A kifejezseket a 6. fejezet rja le s a 6.2 pont sszegzi. A kifejezs-lista (expression-list)
defincija azonos a kifejezsvel (expression). A fggvnyparamtereket elvlaszt vessz-
nek a vessz opertortl (comma, sequencing operator, 6.2.2) val megklnbztetsre
kt szably szolgl.
elsdleges-kifejezs:
literl
this
:: azonost
:: opertorfggvny-azonost
:: minstett-azonost
( kifejezs )
azonost-kifejezs
azonost-kifejezs:
nem-minstett-azonost
minstett-azonost
nem-minstett-azonost:
azonost
opertorfggvny-azonost
talaktfggvny-azonost
~ osztlynv
sablon-azonost
minstett-azonost:
begyazott-nv-meghatrozs template
nem ktelez
nem-minstett-azonost
begyazott-nv-meghatrozs:
osztly-vagy-nvtr-nv :: begyazott-nv-meghatrozs
nem ktelez
osztly-vagy-nvtr-nv :: template begyazott-nv-meghatrozs
osztly-vagy-nvtr-nv:
osztlynv
nvtr-nv
uttag-kifejezs:
elsdleges-kifejezs
uttag-kifejezs [ kifejezs ]
uttag-kifejezs ( kifejezs-lista
nem ktelez
)
egyszer-tpus-meghatrozs ( kifejezs-lista
nem ktelez
)
typename ::
nem ktelez
begyazott-nv-meghatrozs azonost ( kifejezs-lista
nem ktelez
)
Fggelkek s trgymutat 1074
typename ::
nem ktelez
begyazott-nv-meghatrozs template
nem ktelez
sablon-azonost ( kifejezs-lista
nem ktelez
)
uttag-kifejezs . template
nem ktelez
::
nem ktelez
azonost-kifejezs
uttag-kifejezs -> template
nem ktelez
::
nem ktelez
azonost-kifejezs
uttag-kifejezs . l-destruktor-nv
uttag-kifejezs -> l-destruktor-nv
uttag-kifejezs ++
uttag-kifejezs --
dynamic-cast < tpusazonost > ( kifejezs )
static-cast < tpusazonost > ( kifejezs )
reinterpret-cast < tpusazonost > ( kifejezs )
const-cast < tpusazonost > ( kifejezs )
typeid ( kifejezs )
typeid ( tpusazonost )
kifejezs-lista:
rtkad-kifejezs
kifejezs-lista , rtkad-kifejezs
l-destruktor-nv:
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
tpusnv :: ~ tpusnv
::
nem ktelez
begyazott-nv-meghatrozs template sablon-azonost :: ~ tpusnv
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
~ tpusnv
egyoperandus-kifejezs:
uttag-kifejezs
++ cast-kifejezs
-- cast-kifejezs
egyoperandus-opertor cast-kifejezs
sizeof egyoperandus-kifejezs
sizeof ( tpusazonost )
new-kifejezs
delete-kifejezs
egyoperandus-opertor: a kvetkezk egyike
* & + - ! ~
new-kifejezs:
::
nem ktelez
new elhelyez-utasts
nem ktelez
new-tpusazonost new-kezdrtk-ad
nem ktelez
::
nem ktelez
new elhelyez-utasts
nem ktelez
( tpusazonost ) new-kezdrtk-ad
nem ktelez
elhelyez-utasts:
( kifejezs-lista )
new-tpusazonost:
tpus-meghatroz-sorozat new-deklartor
nem ktelez
A. Nyelvtan 1075
new-deklartor:
ptr-opertor new-deklartor
nem ktelez
kzvetlen-new-deklartor
kzvetlen-new-deklartor:
[ kifejezs ]
kzvetlen-new-deklartor [ konstans-kifejezs ]
new-kezdrtk-ad:
( kifejezs-lista
nem ktelez
)
delete-kifejezs:
::
nem ktelez
delete cast-kifejezs
::
nem ktelez
delete [ ] cast-kifejezs
cast-kifejezs:
egyoperandus-kifejezs
( tpusazonost ) cast-kifejezs
pm-kifejezs:
cast-kifejezs
pm-kifejezs .* cast-kifejezs
pm-kifejezs ->* cast-kifejezs
szorz-kifejezs:
pm-kifejezs
szorz-kifejezs * pm-kifejezs
szorz-kifejezs / pm-kifejezs
szorz-kifejezs % pm-kifejezs
sszead-kifejezs:
szorz-kifejezs
sszead-kifejezs + szorz-kifejezs
sszead-kifejezs szorz-kifejezs
eltol-kifejezs:
sszead-kifejezs
eltol-kifejezs << sszead-kifejezs
eltol-kifejezs >> sszead-kifejezs
viszonyt-kifejezs:
eltol-kifejezs
viszonyt-kifejezs < eltol-kifejezs
viszonyt-kifejezs > eltol-kifejezs
viszonyt-kifejezs <= eltol-kifejezs
viszonyt-kifejezs >= eltol-kifejezs
Fggelkek s trgymutat 1076
egyenrtksg-kifejezs:
viszonyt-kifejezs
egyenrtksg-kifejezs == viszonyt-kifejezs
egyenrtksg-kifejezs != viszonyt-kifejezs
s-kifejezs:
egyenrtksg-kifejezs
s-kifejezs & egyenrtksg-kifejezs
kizr-vagy-kifejezs:
s-kifejezs
kizr-vagy-kifejezs ^ s-kifejezs
megenged-vagy-kifejezs:
kizr-vagy-kifejezs
megenged-vagy-kifejezs | kizr-vagy-kifejezs
logikai-s-kifejezs:
megenged-vagy-kifejezs
logikai-s-kifejezs && megenged-vagy-kifejezs
logikai-vagy-kifejezs:
logikai-s-kifejezs
logikai-vagy-kifejezs || logikai-s-kifejezs
feltteles-kifejezs:
logikai-vagy-kifejezs
logikai-vagy-kifejezs ? kifejezs : rtkad-kifejezs
rtkad-kifejezs:
feltteles-kifejezs
logikai-vagy-kifejezs rtkad-opertor rtkad-kifejezs
throw-kifejezs
rtkad-opertor: a kvetkezk egyike
= *= /= %= += -= >>= <<= &= ^= |=
kifejezs:
rtkad-kifejezs
kifejezs , rtkad-kifejezs
konstans-kifejezs:
feltteles-kifejezs
A. Nyelvtan 1077
A fggvny stlus tpustalaktsok (cast) s a deklarcik hasonlsgbl tbbrtelms-
gek addhatnak. Pldul:
int x;
void f()
{
char(x); // x talaktsa char tpusra
// vagy egy x nev char deklarcija?
}
A fordt minden ilyen tbbrtelm szerkezetet deklarciknt rtelmez, vagyis a szably:
ha lehet deklarciknt rtelmezni, akkor deklarci. Pldul:
T(a)->m; // kifejezs-utasts
T(a)++; // kifejezs-utasts
T(*e)(int(3)); // deklarci
T(f)[4]; // deklarci
T(a); // deklarci
T(a)=m; // deklarci
T(*b)(); // deklarci
T(x),y,z=7; // deklarci
A tbbrtelmsg ilyen feloldsa tisztn szintaktikus. A fordt a nvrl csak annyi inform-
cit hasznl fel, hogy az egy ismert tpus- vagy sablonnv-e. Ha nem az, akkor ezektl k-
lnbz jelentst ttelez fel rla.
A template nem-minstett-azonost szerkezet azt jelenti, hogy a nem-minstett-
azonost egy sablon neve, mgpedig egy olyan krnyezetben, ahonnan ezt nem lehetne
levezetni (C.13.6)
Fggelkek s trgymutat 1078
A.6. Utastsok
Lsd 6.3-at.
utasts:
cmkzett-utasts
kifejezs-utasts
sszetett-utasts
kivlaszt-utasts
ciklus-utasts
ugr-utasts
deklarci-utasts
try-blokk
cmkzett-utasts:
azonost : utasts
case konstans-kifejezs : utasts
default : utasts
kifejezs-utasts:
kifejezs
nem ktelez
;
sszetett-utasts:
{ utasts-sorozat
nem ktelez
}
utasts-sorozat:
utasts
utasts-sorozat utasts
kivlaszt-utasts:
if ( felttel ) utasts
if ( felttel ) utasts else utasts
switch ( felttel ) utasts
felttel:
kifejezs
tpus-meghatrozs-sorozat deklartor = rtkad-kifejezs
ciklus-utasts:
while ( felttel ) utasts
do utasts while ( kifejezs ) ;
for ( for-init-utasts felttel
nem ktelez
; kifejezs
nem ktelez
) utasts
for-init-utasts:
kifejezs-utasts
egyszer-deklarci
A. Nyelvtan 1079
ugr-utasts:
break ;
continue ;
return kifejezs
nem ktelez
;
goto azonost ;
deklarci-utasts:
blokk-deklarci
A.7. Deklarcik
A deklarcik szerkezett a 4. fejezet rja le, a felsorol tpusokat (enumeration) a 4.8,
a mutatkat (pointer) s tmbket (array) az 5. fejezet, a fggvnyeket (function) a 7. feje-
zet, a nvtereket (namespace) a 8.2, az sszeszerkesztsi direktvkat (linkage directive)
a 9.2.4, a trolsi mdokat (storage class) pedig a 10.4.
deklarci-sorozat:
deklarci
deklarci-sorozat deklarci
deklarci:
blokk-deklarci
fggvnydefinci
sablondeklarci
explicit-pldnyosts
explicit-specializci
sszeszerkesztsi-md
nvtr-definci
blokk-deklarci:
egyszer-deklarci
asm-definci
nvtr-lnv-definci
using-deklarci
using-utasts
egyszer-deklarci:
deklarci-meghatrozs-sorozat
nem ktelez
kezdrtk-deklartor-lista
nem ktelez
;
deklarci-meghatrozs:
trolsi-md-meghatrozs
tpus-meghatrozs
Fggelkek s trgymutat 1080
fggvny-meghatrozs
friend
typedef
deklarci-meghatrozs-sorozat:
deklarci-meghatrozs-sorozat
nem ktelez
deklarci-meghatrozs
trolsi-md-meghatrozs:
auto
register
static
extern
mutable
fggvny-meghatrozs:
inline
virtual
explicit
typedef-nv:
azonost
tpus-meghatrozs:
egyszer-tpus-meghatrozs
osztly-meghatrozs
felsorols-meghatrozs
sszetett-tpus-meghatrozs
cv-minst
egyszer-tpus-meghatrozs:
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
tpusnv
::
nem ktelez
begyazott-nv-meghatrozs template
nem ktelez
sablon-azonost
char
wchar_t
bool
short
int
long
signed
unsigned
float
double
void
tpusnv:
osztlynv
felsorolsnv
typedef-nv
A. Nyelvtan 1081
sszetett-tpus-meghatrozs:
osztlykulcs ::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
azonost
enum ::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
azonost
typename ::
nem ktelez
begyazott-nv-meghatrozs azonost
typename ::
nem ktelez
begyazott-nv-meghatrozs template
nem ktelez
sablon-azonost
felsorolsnv:
azonost
felsorols-meghatrozs:
enum azonost
nem ktelez
{ felsorols-lista
nem ktelez
}
felsorols-lista:
felsorol-definci
felsorols-lista , felsorol-definci
felsorol-definci:
felsorol
felsorol = konstans-kifejezs
felsorol:
azonost
nvtr-nv:
eredeti-nvtr-nv
nvtr-lnv
eredeti-nvtr-nv:
azonost
nvtr-definci:
nevestett-nvtr-definci
nevestetlen-nvtr-definci
nevestett-nvtr-definci:
eredeti-nvtr-definci
bvtett-nvtr-definci
eredeti-nvtr-definci:
namespace azonost { nvtr-trzs }
bvtett-nvtr-definci:
namespace eredeti-nvtr-nv { nvtr-trzs }
nevestetlen-nvtr-definci:
namespace { nvtr-trzs }
Fggelkek s trgymutat 1082
nvtr-trzs:
deklarci-sorozat
nem ktelez
nvtr-lnv:
azonost
nvtr-lnv-definci:
namespace azonost = minstett-nvtr-meghatrozs ;
minstett-nvtr-meghatrozs:
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
nvtr-nv
using-deklarci:
using typename
nem ktelez
::
nem ktelez
begyazott-nv-meghatrozs nem-
minstett-azonost ;
using :: nem-minstett-azonost ;
using-utasts:
using namespace ::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
nvtr-nv ;
asm-definci:
asm ( karakterlnc-literl ) ;
sszeszerkesztsi-md:
extern karakterlnc-literl { deklarci-sorozat
nem ktelez
}
extern karakterlnc-literl deklarci
A nyelvtan megengedi a deklarcik tetszleges egymsba gyazst, de bizonyos megszo-
rtsok rvnyesek. Pldul nem szabad fggvnyeket egymsba gyazni, azaz egy fggv-
nyen bell egy msik fggvnyt kifejteni.
A deklarcikat kezd meghatrozsok (minstk, specifier) listja nem lehet res (azaz
nincs implicit int, B.2) s a meghatrozsok leghosszabb lehetsges sorozatbl ll. Pldul:
typedef int I;
void f(unsigned I) { /* ... */ }
Itt f() paramtere egy unsigned int.
Az asm() szerkezet az assembly kd beszrsra szolgl. Jelentst az adott nyelvi vltozat
hatrozza meg, de clja az, hogy a megadott helyen az adott szvegnek megfelel assembly
kd kerljn be a fordt ltal ltrehozott kdba.
A. Nyelvtan 1083
Ha egy vltozt register-knt vezetnk be, azzal azt jelezzk a fordtprogram szmra,
hogy a gyakori hozzfrsekre kell optimalizlnia a kdot. Az jabb fordtprogramok leg-
tbbje szmra ezt felesleges megadni.
A.7.1. Deklartorok
Lsd a 4.9.1 pontot, az 5. fejezetet (mutatk s tmbk), a 7.7 pontot (fggvnymutatk)
s a 15.5 pontot (tagokra hivatkoz mutatk).
kezdrtk-deklartor-lista:
kezdrtk-deklartor
kezdrtk-deklartor-lista , kezdrtk-deklartor
kezdrtk-deklartor:
deklartor kezdrtk-ad
nem ktelez
deklartor:
kzvetlen-deklartor
ptr-opertor deklartor
kzvetlen-deklartor:
deklartor-azonost
kzvetlen-deklartor ( paramter-deklarci-zradk ) cv-minst-sorozat
nem ktelez
kivtel-meghatrozs
nem ktelez
kzvetlen-deklartor [ konstans-kifejezs
nem ktelez
]
( deklartor )
ptr-opertor:
* cv-minst-sorozat
nem ktelez
&
::
nem ktelez
begyazott-nv-meghatrozs * cv-minst-sorozat
nem ktelez
cv-minst-sorozat:
cv-minst cv-minst-sorozat
nem ktelez
cv-minst:
const
volatile
deklartor-azonost:
::
nem ktelez
azonost-kifejezs
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
tpusnv
Fggelkek s trgymutat 1084
tpusazonost:
tpus-meghatrozs-sorozat elvont-deklartor
nem ktelez
tpus-meghatrozs-sorozat:
tpus-meghatrozs tpus-meghatrozs-sorozat
nem ktelez
elvont-deklartor:
ptr-opertor elvont-deklartor
nem ktelez
kzvetlen-elvont-deklartor
kzvetlen-elvont-deklartor:
kzvetlen-elvont-deklartor
nem ktelez
( paramter-deklarci-zradk ) cv-minst-
sorozat
nem ktelez
kivtel-meghatrozs
nem ktelez
kzvetlen-elvont-deklartor
nem ktelez
[ konstans-kifejezs
nem ktelez
]
( elvont-deklartor )
paramter-deklarci-zradk:
paramter-deklarci-lista
nem ktelez
...
nem ktelez
paramter-deklarci-lista , ...
paramter-deklarci-lista:
paramter-deklarci
paramter-deklarci-lista , paramter-deklarci
paramter-deklarci:
deklarci-meghatrozs-sorozat deklartor
deklarci-meghatrozs-sorozat deklartor = rtkad-kifejezs
deklarci-meghatrozs-sorozat elvont-deklartor
nem ktelez
deklarci-meghatrozs-sorozat elvont-deklartor
nem ktelez
= rtkad-kifejezs
fggvnydefinci:
deklarci-meghatrozs-sorozat
nem ktelez
deklartor ctor-kezdrtk-ad
nem ktelez
fggvnytrzs
deklarci-meghatrozs-sorozat
nem ktelez
deklartor fggvny-try-blokk
fggvnytrzs:
sszetett-utasts
kezdrtk-ad:
= kezdrtk-ad-zradk
( kifejezs-lista )
kezdrtk-ad-zradk:
rtkad-kifejezs
{ kezdrtk-lista ,
nem ktelez
}
{ }
A. Nyelvtan 1085
kezdrtk-lista:
kezdrtk-ad-zradk
kezdrtk-lista , kezdrtk-ad-zradk
A volatile meghatrozs/minsts azt jelzi a fordtprogram szmra, hogy az objektum
a nyelv ltal nem meghatrozott mdon vltoztatja az rtkt, gy az agresszv optimaliz-
ls kerlend. Egy valsidej rt pldul gy deklarlhatunk:
extern const volatile clock;
A clock kt egymst kvet leolvassa klnbz eredmnyeket adhat.
A.8. Osztlyok
Lsd a 10. fejezetet.
osztlynv:
azonost
sablon-azonost
osztly-meghatrozs:
osztlyfej { tag-meghatrozs
nem ktelez
}
osztlyfej:
osztlykulcs azonost
nem ktelez
alap-zradk
nem ktelez
osztlykulcs begyazott-nv-meghatrozs azonost alap-zradk
nem ktelez
osztlykulcs begyazott-nv-meghatrozs template sablon-azonost alap-zradk
nem ktelez
osztlykulcs:
class
struct
union
tag-meghatrozs:
tag-deklarci tag-meghatrozs
nem ktelez
hozzfrs-meghatrozs : tag-meghatrozs
nem ktelez
Fggelkek s trgymutat 1086
tag-deklarci:
deklarci-meghatrozs-sorozat
nem ktelez
tag-deklartor-lista
nem ktelez
;
fggvnydefinci ;
nem ktelez
::
nem ktelez
begyazott-nv-meghatrozs template
nem ktelez
nem-minstett-azonost ;
using-deklarci
sablondeklarci
tag-deklartor-lista:
tag-deklartor
tag-deklartor-lista , tag-deklartor
tag-deklartor:
deklartor res-meghatrozs
nem ktelez
deklartor konstans-kezdrtk-ad
nem ktelez
azonost
nem ktelez
: konstans-kifejezs
res-meghatrozs:
= 0
konstans-kezdrtk-ad:
= konstans-kifejezs
A C-vel val sszeegyeztethetsget megrzend egy azonos nev osztly s egy nem-osz-
tly ugyanabban a hatkrben is deklarlhat (5.7). Pldul:
struct stat { /* ... */ };
int stat(char* nv, struct stat* buf);
Ebben az esetben a sima nv (stat) a nem-osztly neve. Az osztlyra egy osztlykulcs el-
taggal (class, struct vagy union) kell hivatkozni.
A konstans kifejezseket a C.5 pont rja le.
A.8.1. Szrmaztatott osztlyok
Lsd a 12. s 15. fejezetet.
alap-zradk:
: alap-meghatrozs-lista
alap-meghatrozs-lista:
alap-meghatrozs
alap-meghatrozs-lista , alap-meghatrozs
A. Nyelvtan 1087
alap-meghatrozs:
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
osztlynv
virtual hozzfrs-meghatrozs
nem ktelez
::
nem ktelez
begyazott-nv-
meghatrozs
nem ktelez
osztlynv
hozzfrs-meghatrozs virtual
nem ktelez
::
nem ktelez
begyazott-nv-
meghatrozs
nem ktelez
osztlynv
hozzfrs-meghatrozs:
private
protected
public
A.8.2. Klnleges tagfggvnyek
Lsd a 11.4 (talakt opertorok), 10.4.6 (osztlytagok kezdeti rtkadsa) s 12.2.2
(alaposztlyok kezdeti rtkadsa) pontokat.
talaktfggvny-azonost:
operator talakts-tpusazonost
talakts-tpusazonost:
tpus-meghatrozs-sorozat talakts-deklartor
nem ktelez
talakts-deklartor:
ptr-opertor talakts-deklartor
nem ktelez
ctor-kezdrtk-ad:
: mem-kezdrtk-lista
mem-kezdrtk-lista:
mem-kezdrtk-ad
mem-kezdrtk-ad , mem-kezdrtk-lista
mem-kezdrtk-ad:
mem-kezdrtk-ad-azonost ( kifejezs-lista
nem ktelez
)
mem-kezdrtk-ad-azonost:
::
nem ktelez
begyazott-nv-meghatrozs
nem ktelez
osztlynv
azonost
Fggelkek s trgymutat 1088
A.8.3. Tlterhels
Lsd a 11. fejezetet.
opertorfggvny-azonost:
operator opertor
opertor: a kvetkezk egyike
new delete new[] delete[]
+ - * / % ^ & | ~ ! = < >
+= -= *= /= %= ^= &= |= << >> >>= <<= ==
!= <= >= && || ++ -- , ->* -> () []
A.9. Sablonok
A sablonokkal a 13. fejezet s a C.13. pont foglalkozik rszletesen.
sablondeklarci:
export
nem ktelez
template < sablonparamter-lista > deklarci
sablonparamter-lista:
sablonparamter
sablonparamter-lista , sablonparamter
sablonparamter:
tpus-paramter
paramter-deklarci
tpus-paramter:
class azonost
nem ktelez
class azonost
nem ktelez
= tpusazonost
typename azonost
nem ktelez
typename azonost
nem ktelez
= tpusazonost
template < sablonparamter-lista > class azonost
nem ktelez
template < sablonparamter-lista > class azonost
nem ktelez
= sablonnv
sablon-azonost:
sablonnv < sablonargumentum-lista
nem ktelez
>
sablonnv:
azonost
A. Nyelvtan 1089
sablonargumentum-lista:
sablonargumentum
sablonargumentum-lista , sablonargumentum
sablonargumentum:
rtkad-kifejezs
tpusazonost
sablonnv
explicit-pldnyosts:
template deklarci
explicit-specializci:
template < > deklarci
Az explicit sablonargumentum-meghatrozs egy rdekes tbbrtelmsgre ad lehets-
get. Vegyk a kvetkez pldt:
void h()
{
f<1>(0); // tbbrtelm: ((f)<1) > (0) vagy (f<1>)(0) ?
// feloldsa: f<1> meghvsa a 0 paramterrel
}
A felolds egyszer s hatkony: ha f egy sablon neve, akkor f< egy minstett sablonnv
kezdete s az azt kvet nyelvi egysgek eszerint rtelmezendek; ha nem ez a helyzet, a <
jel a kisebb mint mveletet jelenti. Ugyangy az els pr nlkli > jel lezrja a sablonpara-
mterek listjt. Ha a nagyobb mint jelre van szksgnk, zrjelezst kell alkalmaznunk:
f< a>b >(0); // szintaktikus hiba
f< (a>b) >(0); // rendben
Hasonl tbbrtelmsg lphet fel, ha a zr > jelek tl kzel kerlnek:
list<vector<int>> lv1; // szintaktikus hiba: nem vrt >> (jobbra lptets)
list< vector<int> > lv2; // helyes: vektorok listja
Figyeljnk a szkzre a kt > jel kztt (a >> a jobbra lptet opertor!), mert nagyon
knny elnzni.
Fggelkek s trgymutat 1090
A.10. Kivtelkezels
Lsd a 8.3 pontot s a 14. fejezetet.
try-blokk:
try sszetett-utasts kezel-sorozat
fggvny-try-blokk:
try ctor-kezdrtk-ad
nem ktelez
fggvnytrzs kezel-sorozat
kezel-sorozat:
kezel kezel-sorozat
nem ktelez
kezel:
catch ( kivtel-deklarci ) sszetett-utasts
kivtel-deklarci:
tpus-meghatrozs-sorozat deklartor
tpus-meghatrozs-sorozat elvont-deklartor
tpus-meghatrozs-sorozat
...
throw-kifejezs:
throw rtkad-kifejezs
nem ktelez
kivtel-meghatrozs:
throw ( tpusazonost-lista
nem ktelez
)
tpusazonost-lista:
tpusazonost
tpusazonost-lista , tpusazonost
A.11. Az elfeldolgoz direktvi
Az elfeldolgoz (preprocessor, pp) egy viszonylag egyszer makr-feldolgoz rendszer,
amely elsdlegesen nyelvi egysgeken s nem egyes karaktereken dolgozik. A makrk
meghatrozsnak s hasznlatnak (7.8) kpessgn kvl az elfeldolgoz a szvegfj-
loknak s szabvnyos fejllomnyoknak (9.2.1) a forrsba ptsre is lehetsget ad s
makrkon alapul feltteles fordtst is tud vgezni (9.3.3). Pldul:
A. Nyelvtan 1091
#if OPT==4
#include "fejllomny4.h"
#elif 0<OPT
#include "fejllomny.h"
#else
#include<cstdlib>
#endif
Az sszes elfeldolgozi utasts (direktva) a # jellel kezddik, amelynek az els nem
reshely karakternek kell lennie a sorban.
elfeldolgoz-fjl:
csoport
nem ktelez
csoport:
csoport-rsz
csoport csoport-rsz
csoport-rsz:
pp-szimblumok
nem ktelez
jsor
if-rsz
vezrlsor
if-rsz:
if-csoport elif-csoportok
nem ktelez
else-csoport
nem ktelez
endif-sor
if-csoport:
# if konstans-kifejezs jsor csoport
nem ktelez
# ifdef azonost jsor csoport
nem ktelez
# ifndef azonost jsor csoport
nem ktelez
elif-csoportok:
elif-csoport
elif-csoportok elif-csoport
elif-csoport:
# elif konstans-kifejezs jsor csoport
nem ktelez
else-csoport:
# else jsor csoport
nem ktelez
endif-sor:
# endif jsor
Fggelkek s trgymutat 1092
vezrlsor:
# include pp-szimblumok jsor
# define azonost helyettest-lista jsor
# define azonost balzrjel azonost-lista
nem ktelez
) helyettest-lista jsor
# undef azonost jsor
# line pp-szimblumok jsor
# error pp-szimblumok
nem ktelez
jsor
# pragma pp-szimblumok
nem ktelez
jsor
# jsor
balzrjel:
a bal oldali zrjel karakter megelz reshely nlkl
helyettest-lista:
pp-szimblumok
nem ktelez
pp-szimblumok:
elfeldolgoz-szimblum
pp-szimblumok elfeldolgoz-szimblum
jsor:
jsor karakter
azonost-lista:
azonost
azonost-lista , azonost
A. Nyelvtan 1093
Kompatibilits
Te msz a te utadon, a te
szoksaid szerint, s n is k-
vetem a sajt elveimet.
(C. Napier)
C/C++ kompatibilits szrevtlen klnbsgek a C s a C++ kztt C program, ami
nem C++ program Elavult szolgltatsok C++ program, ami nem C program Rgebbi
C++-vltozatok hasznlata Fejllomnyok A standard knyvtr Nvterek Helyfog-
lalsi hibk Sablonok A for utasts kezdrtk-ad rsze Tancsok Gyakorlatok
B.1. Bevezets
Ebben a fggelkben azokat a klnbsgeket vizsgljuk meg, amelyek a C s a C++, illet-
ve a szabvnyos C++ s a rgebbi C++-vltozatok kztt llnak fenn. Clunk az, hogy egy-
rszt lerjuk azokat a klnbsgeket, amelyek a programozknak problmt okozhatnak,
msrszt mdszereket mutassunk ezen problmk kezelsre. A legtbb kompatibilitsi
problmval akkor kerlnk szembe, amikor egy C programot a C++-bl akarunk hasznl-
ni, vagy egy nem szabvnyos C++ rendszerben ltrehozott programot egy msik krnyezet-
be akarunk tvinni, esetleg j lehetsgeket akarunk egy rgebbi C++-vltozatban hasznl-
B
ni. A cl nem az, hogy rszletesen bemutassuk az sszes kompatibilitsi problmt, ami va-
laha is elfordulhat, hanem az, hogy a leggyakoribb problmkra szabvnyos megoldst
adjunk.
Amikor kompatibilitsi problmkrl beszlnk, a legfontosabb krds az, hogy progra-
munk a klnbz nyelvi vltozatok milyen szles krben kpes mkdni. A C++ nyelv
megismershez rdemes a legteljesebb s legknyelmesebb rendszert hasznlnunk, rend-
szerfejlesztskor azonban ennl konzervatvabb stratgit kell kvetnnk, hiszen olyan
programot szeretnnk, amely a lehet legtbb krnyezetben mkdkpes. Rgebben ez
nagyon j kifogs volt arra, hogy a C++ tl fejlettnek minstett lehetsgeit elkerljk.
Az egyes vltozatok azonban kzeledtek egymshoz, gy a klnbz platformok kztti
hordozhatsg kvetelmnye ritkn ignyel olyan komoly erfesztseket a programoz-
tl, mint nhny vvel ezeltt.
B.2. C/C++ kompatibilits
Kisebb kivtelektl eltekintve a C++ a C nyelv tovbbfejlesztsnek tekinthet. A legtbb
klnbsg a C++ hangslyozottabb tpusellenrzsbl kvetkezik. A jl megrt C progra-
mok ltalban C++ programok is. A C s C++ kztti sszes klnbsget a fordtnak is je-
leznie kell.
B.2.1. szrevtlen klnbsgek
Nhny kivteltl eltekintve azok a programok, melyek C s C++ nyelven is rtelmezhetk,
ugyanazt jelentik mind a kt nyelvben. Szerencsre azok a klnbsgek, melyek mgis el-
fordulnak, ltalban csak rnyalatnyiak:
A C-ben a karakterkonstansok s a felsorol tpusok mrete sizeof(int). A C++-ban
sizeof(a) egyenrtk sizeof(char)-ral, a felsorol tpusok megvalstshoz pedig az
egyes C++-vltozatok olyan mretet hasznlhatnak, ami az adott krnyezetben a legclsze-
rbb (4.8).
A C++ lehetv teszi a // jellel bevezetett megjegyzsek hasznlatt; ez a C-ben nem ll ren-
delkezsnkre (br nagyon sok C-vltozat tartalmazza bvtsknt). Ezt az eltrst olyan
programok ksztshez hasznlhatjuk fel, melyek a kt nyelvben klnbzkppen visel-
kednek.
Fggelkek s trgymutat 1096
Pldul:
int f(int a, int b)
{
return a //* elg valszntlen */ b
; /* pontosvessz a szintaktikus hiba elkerlsre */
}
Az ISO C ma mr a C++-hoz hasonlan megengedi a // hasznlatt.
Egy bels hatkrben bevezetett adatszerkezet-nv elrejtheti egy kls hatkrben dekla-
rlt objektum, fggvny, felsorols, vagy tpus nevt:
int x[99];
void f()
{
struct x { int a; };
sizeof(x); /* a tmb mrete C-ben, a struct mrete C++-ban */
}
B.2.2. C program, ami nem C++ program
A leggyakrabban problmt okoz klnbsgek ltalban nem tl lesek. A legtbbet ezek
kzl a fordtk is knnyen szreveszik. Ebben a pontban olyan C kdokat mutatunk be,
amelyek nem C++ kdok. Ezek legtbbjt mr az jabb C-vltozatok is rossz stlusnak,
st elavultnak minstik.
A C-ben a legtbb fggvnyt deklarlsuk eltt is meghvhatjuk:
main() /* rossz stlus C-ben, hibs C++-ban */
{
double sq2 = sqrt(2); /* deklarlatlan fggvny meghvsa */
printf("the square root of 2 is %g\n",sq2); /* deklarlatlan fggvny meghvsa */
}
A fggvny-deklarcik (fggvny-prototpusok) teljes s kvetkezetes hasznlata minden
C-megvalstsban ajnlott. Ha ezt a tancsot megfogadjuk (s ezt a fordtk ltalban va-
lamilyen kapcsolval biztostani is tudjk), C programjaink illeszkedni fognak a C++ szab-
lyaihoz. Ha deklarlatlan fggvnyeket hvunk meg, nagyon pontosan ismernnk kell C
rendszernk fggvnyhvssal kapcsolatos szablyait, hogy eldnthessk, okoztunk-e hi-
bt vagy hordozhatsgi problmt. Az elbbi main() program pldul legalbb kt hibt
tartalmaz C programknt is.
B. Kompatibilits 1097
A C-ben azok a fggvnyek, melyeket paramtertpus megadsa nlkl vezetnk be, tet-
szleges szm s tpus paramterrel meghvhatk. Az ilyen fggvnyhasznlat a Stan-
dard C-ben is elavult, ennek ellenre sok helyen tallkozhatunk vele:
void f(); /* a paramtertpusokat elhagyjuk */
void g()
{
f(2); /* rossz stlus C-ben, hibs C++-ban */
}
A C-ben megengedett az a fggvny-meghatrozsi forma, melyben a paramterek tpust
a paramterek listjnak megadsa utn rgztjk:
void f(a,p,c) char *p; char c; { /* ... */ } /* helyes C-ben, hibs C++-ban */
Ezeket a defincikat t kell rnunk:
void f(int a, char* p, char c) { /* ... */ }
A C-ben s a C++ szabvnyosts eltti vltozataiban az int alaprtelmezett tpus volt. Pl-
dul:
const a = 7; /* C-ben "int" tpust felttelez; C++-ban hibs */
Az ISO C, a C++-hoz hasonlan, megtiltotta az implicit int hasznlatt.
A C megengedi, hogy a visszatrsi tpusok s a paramtertpusok deklarciiban struct-
okat adjunk meg:
struct S { int x,y; } f(); /* helyes C-ben, hibs C++-ban */
void g(struct S { int x,y; } y); /* helyes C-ben, hibs C++-ban */
A C++ tpus-meghatrozsokra vonatkoz szablyai az ilyen deklarcikat feleslegess te-
szik s nem is engedik meg.
A C-ben a felsorol tpus vltozknak rtkl adhatunk egszeket is:
enum Direction { up, down };
Direction d = 1; /* hiba: int rtkads Direction-nek; C-ben helyes */
Fggelkek s trgymutat 1098
A C++ sokkal tbb kulcsszt ismer, mint a C. Ha ezek valamelyikt azonostknt hasznl-
juk egy C programban, knytelenek lesznk azt lecserlni, hogy C++-ban is rtelmezhet
programot kapjunk.
A C-ben nhny C++ kulcssz makrknt szerepel a szabvnyos fejllomnyokban:
Ez azt is jelenti, hogy a C-ben ezeket tesztelhetjk az #ifdef segtsgvel, meghatrozsukat
fellbrlhatjuk stb.
A C-ben a globlis adatobjektumokat tbbszr is deklarlhatjuk egy fordtsi egysgen be-
ll, anlkl, hogy az extern kulcsszt hasznlnnk. Amg a deklarcik kzl csak egy ad
kezdrtket, a fordt az objektumot egyszer meghatrozottnak (egyszer definiltnak)
tekinti.
B. Kompatibilits 1099
C++ kulcsszavak, melyek a C-ben nem kulcsszavak
and and_eq asm bitand bitor bool
catch class compl const_cast delete dynamic_cast
explicit export false friend inline mutable
namespace new not not_eq operator or
or_eq private protected public reinterpret_cast static_cast
template this throw true try typeid
typename using virtual wchar_t xor xor_eq
C++ kulcsszavak, melyek C makrk
and and_eq bitand bitor compl not
not_eq or or_eq wchar_t xor xor_eq
Pldul:
int i; int i; /* egyetlen 'i' egszet hatroz meg vagy vezet be; C++-ban hibs */
A C++-ban minden elemet csak egyszer hatrozhatunk meg (9.2.3).
A C++-ban egy osztlynak nem lehet ugyanaz a neve, mint egy typedef elemnek, amely
ugyanabban a hatkrben valamilyen ms tpusra hivatkozik (5.7).
A C-ben a void* hasznlhat brmilyen mutat tpus vltoz egyszer vagy kezdeti rtk-
adsnak jobb oldaln. A C++-ban ezt nem tehetjk meg (5.6.):
void f(int n)
{
int* p = malloc(n*sizeof(int)); /* C++-ban hibs; hasznljuk inkbb a 'new'
opertort */
}
A C megengedi, hogy ugrsokkal kikerljnk egy kezdeti rtkadst, a C++-ban ez sem
lehetsges.
A C-ben a globlis konstansokat a fordt automatikusan extern elemekknt kezeli, mg
a C++-ban nem. Ktelez vagy kezdrtket adni, vagy az extern kulcsszt hasznlni
(5.4.).
A C-ben a begyazott szerkezetek nevei ugyanabba a hatkrbe kerlnek, mint a felettk
ll szerkezet:
struct S {
struct T { /* ... */ };
// ...
};
struct T x; /* helyes C-ben, jelentse 'S::T x;'; C++-ban hibs */
A C-ben egy tmbnek olyan elemmel is adhatunk kezdrtket, amelynek tbb eleme van,
mint amennyire a deklarlt tmbnek szksge van:
char v[5] = "Oscar"; /* helyes C-ben, a lezr 0-t nem hasznlja; C++-ban hibs */
Fggelkek s trgymutat 1100
B.2.3. Elavult szolgltatsok
Ha a szabvnyost bizottsg egy szolgltatst elavultnak nyilvnt, akkor ezzel azt fejezi ki,
hogy a szolgltatst el kell kerlni. A bizottsgnak azonban nincs joga egy korbban gyak-
ran hasznlhat lehetsget teljesen megszntetni, akkor sem, ha az esetleg felesleges vagy
kifejezetten veszlyes. Teht az elavultsg kimondsa csak egy (nyomatkos) ajnls
a programozknak arra, hogy ne hasznljk a lehetsget.
A static kulcssz, melynek alapjelentse: statikusan lefoglalt, ltalban azt jelzi, hogy egy
fggvny vagy egy objektum helyinek (loklisnak) szmt a fordtsi egysgre nzve:
// fjl1:
static int glob;
// fjl2:
static int glob;
Ennek a programnak valjban kt glob nev, egsz tpus vltozja lesz. Mindkettt kiz-
rlag azok a fggvnyek fogjk hasznlni, amelyekkel megegyez fordtsi egysgben
szerepel.
A static kulcssz hasznlata a loklis a fordtsi egysgre nzve rtelemben elavult a C++-
ban. Helyettk a nvtelen nvterek hasznlata javasolt (8.2.5.1).
A karakterlnc-literlok automatikus talaktsa (nem konstans) char* tpusra szintn el-
avult. Hasznljunk inkbb nvvel rendelkez karaktertmbket, gy elkerlhetjk, hogy
karakterlnc-literlokat kelljen rtkl adnunk char* vltozknak (5.2.2).
A C stlus tpustalaktst az j talaktsoknak szintn elavultt kellett volna tennik; saj-
nos mg sokan hasznljk, pedig a felhasznlnak programjaiban rdemes komolyan meg-
fogadnia a C stlus talaktsok tilalmt. Ha explicit tpustalaktsra van szksgnk,
a static_cast, a reinterpret_cast, illetve a const_cast kulcsszval vagy ezek egyttes haszn-
latval ugyanazt az eredmnyt rhetjk el, mint a C stlus talaktssal. Az j talaktsokat
azrt is rdemes hasznlnunk, mert ezek szlesebb krben hasznlhatk s pontosabban
megfogalmazottak (6.2.7).
B. Kompatibilits 1101
B.2.4. C++ program, ami nem C program
Ebben a pontban azokat a szolgltatsokat soroljuk fel, melyek a C++-ban megtallhatk,
de a C-ben nem. A lehetsgeket szerepk szerint soroljuk fel. Termszetesen szmtalan
osztlyozs lehetsges, mert a legtbb szolgltats tbb clt is szolgl, teht az itt bemuta-
tott nem az egyetlen j csoportosts:
Lehetsgek, melyek elssorban knyelmes jellsrendszert biztostanak:
1. A // megjegyzsek (2.3); a C-ben is szerepelni fognak.
2. Korltozott karakterkszletek hasznlatnak lehetsge (C.3.1).
3. Bvtett karakterkszletek hasznlatnak lehetsge (C.3.3), a C-ben is
megjelenik.
4. A static trban lev objektumok nem-konstans kezdrtket is kaphatnak
(9.4.1).
5. A const a konstans kifejezsekben (5.4, C.5).
6. A deklarcik utastsokknt szerepelnek.
7. Deklarcik szerepelhetnek a for utasts kezdrtk-ad elemben s az if
felttelben is (6.3.3, 6.3.2.1).
8. Az adatszerkezetek nevei eltt nem kell szerepelnie a struct kulcssznak.
Lehetsgek, melyek elssorban a tpusrendszer erstst szolgljk:
1. A fggvnyparamterek tpusellenrzse (7.1); ksbb a C-ben is megjelent
(B.2.2).
2. Tpusbiztos sszeszerkeszts (9.2, 9.2.3).
3. A szabad tr kezelse a new s a delete opertor segtsgvel (6.2.6, 10.4.5,
15.6).
4. A const (5.4, 5.4.1); ksbb a C-be is bekerlt.
5. A logikai bool adattpus (4.2).
6. j tpustalaktsi forma (6.2.7).
Felhasznli tpusokat segt lehetsgek:
1. Osztlyok (10. fejezet).
2. Tagfggvnyek (10.2.1) s tagosztlyok (11.12).
3. Konstruktorok s destruktorok (10.2.3, 10.4.1).
4. Szrmaztatott osztlyok (12. fejezet, 15. fejezet).
5. Virtulis fggvnyek s elvont osztlyok (12.2.6, 12.3).
6. A public/protected/private hozzfrs-szablyozs (10.2.2, 15.3, C.11).
7. A bartok (friend) lehetsgei (11.5).
8. Mutatk tagokra (15.5, C.12).
9. static tagok (10.2.4).
Fggelkek s trgymutat 1102
10. mutable tagok (10.2.7.2).
11. Opertor-tlterhels (11. fejezet).
12. Hivatkozsok (5.5).
Lehetsgek, melyek elssorban a program rendszerezsre szolglnak (az osz-
tlyokon tl):
1. Sablonok (13. fejezet, C.13).
2. Helyben kifejtett (inline) fggvnyek (7.1.1).
3. Alaprtelmezett paramterek (7.5).
4. Fggvnynv-tlterhels (7.4).
5. Nvterek (8.2).
6. Explicit hatkr-meghatrozs (a :: opertor, 4.9.4).
7. Kivtelkezels (8.3, 14. fejezet).
8. Futsi idej tpus-meghatrozs (15.4).
A C++-ban bevezetett j kulcsszavak (B.2.2) a legtbb olyan szolgltatst bemutatjk, me-
lyek kifejezetten a C++-ban jelentek meg. Van azonban nhny nyelvi elem pldul
a fggvny-tlterhels vagy a consts alkalmazsa konstans kifejezsekben , melyeket nem
j kulcssz segtsgvel valst meg a nyelv. Az itt felsorolt nyelvi lehetsgek mellett a C++
knyvtr is (16.1.2) nagy rszben csak a C++-ra jellemz.
A __cplusplus makr segtsgvel mindig megllapthatjuk, hogy programunkat ppen C
vagy C++ fordtval dolgozzuk-e fel (9.2.4).
B.3. Rgebbi C++-vltozatok hasznlata
A C++ nyelvet 1983 ta folyamatosan hasznljk (1.4). Azta szmtalan vltozat s nll
fejlesztkrnyezet kszlt belle. A szabvnyosts alapvet clja, hogy a programozk s
felhasznlk rszre egy egysges C++ lljon rendelkezsre. Amg azonban a szabvny tel-
jes krben el nem terjed a C++-rendszerek kszti krben, mindig figyelembe kell ven-
nnk azt a tnyt, hogy nem minden megvalsts nyjtja az sszes szolgltatst, ami ebben
a knyvben szerepel.
Sajnos nem ritka, hogy a programozk els komoly benyomsaikat a C++-rl egy ngy-t
ves vltozat alapjn szerzik meg. Ennek oka az, hogy ezek szles krben s olcsn elr-
hetk. Ha azonban a legkisebb lehetsge is van, egy magra valamit is ad szakember
B. Kompatibilits 1103
ilyen antik rendszernek a kzelbe sem megy. Egy kezdnek a rgebbi vltozatok szmta-
lan rejtett problmt okozhatnak. A nyelvi lehetsgek s a knyvtrban megvalstott szol-
gltatsok hinya olyan problmk kezelst teszi szksgess, melyek az jabb vltoza-
tokban mr nem jelentkeznek. A kevs lehetsget biztost rgebbi vltozatok a kezd
programoz programozsi stlusnak is sokat rtanak, radsul helytelen kpnk alakul ki
arrl, mi is valjban a C++. Vlemnyem szerint a C++ els megismersekor nem az a leg-
megfelelbb rszhalmaz, amely az alacsonyszint szolgltatsokat tartalmazza (teht sem-
mikppen sem a C s a C++ kzs rsze). Azt ajnlom, elszr a standard knyvtrat s
a sablonokat ismerjk meg, mert ezekkel egyszeren megoldhatunk komolyabb feladato-
kat is s j zeltt kapunk a valdi C++ szolgltatsaibl.
A C++ els kereskedelmi kiadsa 1985-ben jelent meg; e knyv els kiadsa alapjn ksz-
tettk. Akkor a C++ mg nem biztostott tbbszrs rkldst, sablonokat, futsi idej t-
pusinformcikat, kivteleket s nvtereket sem. Mai szemmel semmi rtelmt nem ltom
annak, hogy olyan rendszerrel kezdjnk el dolgozni, amely ezen szolgltatsok legalbb
egy rszt nem biztostja. A tbbszrs rklds, a sablonok s a kivtelek 1989-ben ke-
rltek be a C++-ba. A sablonok s kivtelek els megvalstsa mg nagyon kiforratlan s
szegnyes volt. Ha ezek hasznlatakor problmkba tkznk, srgsen szerezznk be
egy jabb nyelvi vltozatot.
ltalban igaz, hogy olyan vltozatot rdemes hasznlnunk, amely minden lehetsges he-
lyen igazodik a szabvnyhoz, hogy elkerlhessk a megvalstsbl ered klnbsgeket,
illetve az adott nyelvi vltozatnak a szabvny ltal nem meghatrozott tulajdonsgait. Ter-
vezzk programjainkat gy, mintha a teljes nyelv a rendelkezsnkre llna, majd valstsuk
meg nllan a hinyz rszletek szolgltatsait. gy jobban rendszerezett s knnyebben
tovbbfejleszthet programot kapunk, mint ha a C++ mindenhol megtallhat, kzs mag-
jhoz terveznnk a programot. Arra is mindig figyeljnk, hogy megvalsts-fgg szolgl-
tatsokat csak akkor hasznljunk, ha elkerlhetetlen.
B.3.1. Fejllomnyok
Eredetileg minden fejllomny kiterjesztse .h volt, gy a C++ egyes vltozataiban is megje-
lentek az olyan fejllomnyok, mint a <map.h> vagy az <iostream.h>. A kompatibilits r-
dekben ezek ltalban ma is hasznlhatk.
Amikor a szabvnyost bizottsgnak j fejllomnyokat kellett bevezetnie a szabvnyos
knyvtrak trt vltozatai, illetve az jonnan meghatrozott knyvtri szolgltatsok keze-
lshez, a fejllomnyok elnevezse problmkba tkztt. A rgi .h kiterjeszts hasznla-
Fggelkek s trgymutat 1104
ta sszeegyeztethetsgi problmkat okozott volna. A megoldst a .h kiterjeszts elhagy-
sa jelentette az j szabvnyos fejllomny-nevekben. Az uttag egybknt is felesleges volt,
mert a < > enlkl is jelezte, hogy szabvnyos fejllomnyt neveztnk meg.
Teht a standard knyvtr kiterjeszts nlkli fejllomnyokat biztost (pldul <map> vagy
<iostream>). Ezen llomnyok deklarcii az std nvtrben szerepelnek. A rgebbi fejllo-
mnyok a globlis nvtrben kapnak helyet s ezek nevben szerepel a .h kiterjeszts.
Pldul:
#include<iostream>
int main()
{
std::cout << "Hell, vilg!\n";
}
Ha ezt a programrszletet nem sikerl lefordtanunk, prblkozzunk a hagyomnyosabb
vltozattal:
#include<iostream.h>
int main()
{
cout << "Hell, vilg!\n";
}
A legtbb hordozhatsgi problmt a nem teljesen sszeegyeztethet fejllomnyok
okozzk. A szabvnyos fejllomnyok ebbl a szempontbl ritkbban jelentenek gondot.
Nagyobb programoknl gyakran elfordul, hogy sok fejllomnyt hasznlunk, s ezek nem
mindegyike szerepel az sszes rendszerben vagy sok deklarci nem ugyanabban a fejl-
lomnyban jelenik meg. Az is elfordul, hogy bizonyos deklarcik szabvnyosnak tnnek
(mert szabvnyos nev fejllomnyokban szerepelnek), de valjban semmilyen szabvny-
ban nem szerepelnek.
Sajnos nincs teljesen kielgt mdszer a fejllomnyok ltal okozott hordozhatsgi prob-
lmk kezelsre. Egy gyakran alkalmazott megolds, hogy elkerljk a fejllomnyoktl
val kzvetlen fggst s a fennmarad fggsgeket kln fogalmazzuk meg. gy a hor-
dozhatsgot a kzvetett hivatkozsokkal s az elklntett fggsgkezelssel javtjuk.
Pldul, ha egy szksges deklarci a klnbz rendszerekben klnbz fejllomnyo-
kon keresztl rhet el, egy alkalmazsfgg fejllomnyt hasznlhatunk, amely az
B. Kompatibilits 1105
#include segtsgvel magba foglalja az egyes rendszerek megfelel llomnyait. Ugyan-
gy, ha valamilyen szolgltatst a klnbz rendszerekben mskppen rhetnk el, a szol-
gltatshoz alkalmazsfgg osztlyokat s fggvnyeket kszthetnk.
B.3.2. A standard knyvtr
Termszetesen a C++ szabvny eltti vltozataibl hinyozhatnak a standard knyvtr bi-
zonyos rszei. A legtbb rendszerben szerepelnek az adatfolyamok, a complex adattpus
(ltalban nem sablonknt), a klnbz string osztlyok s a C standard knyvtra.
A map, a list, a valarray stb. azonban gyakran hinyozik ezekbl a megvalstsokbl.
Ilyenkor prbljuk az elrhet knyvtrakat gy hasznlni, hogy ksbb lehetsgnk le-
gyen az talaktsra, ha szabvnyos krnyezetbe kerlnk. ltalban jobban jrunk, ha
a nem szabvnyos string, list vagy map osztlyt hasznljuk, ahelyett, hogy a standard
knyvtr osztlyainak hinya miatt visszatrnnk a C stlus programozshoz. Egy msik le-
hetsg, hogy a standard knyvtr STL rsznek (16., 17., 18. s 19. fejezet) j megvals-
tsai tlthetk le ingyenesen az Internetrl.
A standard knyvtr rgebbi vltozatai mg nem voltak teljesek. Pldul sokszor jelentek
meg trolk gy, hogy memriafoglalk hasznlatra mg nem volt lehetsg, vagy ppen
ellenkezleg, mindig ktelez volt megadni a memriafoglalt. Hasonl problmk fordul-
tak el az eljrsmd-paramterek esetben is, pldul az sszehasonltsi feltteleknl:
list<int> li; // rendben, de nhny megvalsts megkveteli a memriafoglalt
list<int,allocator<int> > li2; // rendben, de nhny megvalsts nem ismeri
// a memriafoglalt
map<string,Record> m1; // rendben, de nhny megvalsts megkvetel egy
// 'kisebb mint' mveletet
map<string,Record,less<string> > m2;
Mindig azt a vltozatot kell hasznlnunk, amelyet az adott fejlesztkrnyezet elfogad. Sze-
rencss esetben rendszernk az sszes formt ismeri.
A rgebbi C++-vltozatokban gyakran istrstream s ostrstream osztly szerepelt,
a <strstream.h> nev fejllomny rszeknt, mg a szabvny az istringstream s
ostringstream osztlyokat adja meg, melyeknek helye az <sstream> fejllomny.
A strstream adatfolyamok kzvetlenl karaktertmbkn vgeztek mveleteket (lsd
21.10 [26]).
Fggelkek s trgymutat 1106
A szabvny eltti C++-vltozatokban az adatfolyamok nem voltak paramteresek. A basic_
eltag sablonok jak a szabvnyban; a basic_ios osztlyt rgebben ios osztlynak hvtk.
Kiss meglep mdon az iostate rgi neve viszont io_state volt.
B.3.3. Nvterek
Ha rendszernk nem tmogatja a nvterek hasznlatt, a program logikai szerkezett kife-
jezhetjk forrsfjlok segtsgvel is (9.fejezet). A fejllomnyok ugyangy jl hasznlhatk
sajt vagy C kddal kzsen hasznlt felleteink lersra.
Ha a nvterek nem llnak rendelkezsre, a nvtelen nvterek hinyt a static kulcssz hasz-
nlatval ellenslyozhatjuk. Szintn sokat segthet, ha a globlis nevekben egy eltaggal je-
lezzk, hogy milyen logikai egysghez tartoznak:
// nvtr eltti nyelvi vltozatokban:
class bs_string { /* ... */ }; // Bjarne sajt string tpusa
typedef int bs_bool; // Bjarne sajt bool tpusa
class joe_string; // Joe sajt string tpusa
enum joe_bool { joe_false, joe_true }; // Joe sajt bool tpusa
Az eltagok kivlasztsakor azonban legynk elvigyzatosak, mert a ltez C s C++
knyvtrak esetleg ugyanilyen eltagokat hasznlnak.
B.3.4. Helyfoglalsi hibk
Mieltt a kivtelkezels megjelent a C++-ban, a new opertor 0 rtket adott vissza, ha
a helyfoglals nem sikerlt. A szabvnyos C++ new opertora ma mr egy bad_alloc kiv-
tellel jelzi a hibt.
ltalban rdemes programjainkat a szabvnynak megfelelen trni. Itt ez annyit jelent,
hogy nem a 0 visszatrsi rtket, hanem a bad_alloc kivtelt figyeljk. ltalban mindkt
esetben nagyon nehz a problmt egy hibazenetnl hatkonyabban kezelni.
Ha gy rezzk, nem rdemes a 0 rtk vizsglatt a bad_alloc kivtel figyelsre talak-
tani, ltalban elrhetjk, hogy a program gy mkdjn, mintha nem llnnak rendelke-
zsnkre a kivtelek. Ha rendszernkben nincs _new_handler teleptve, a nothrow mem-
riafoglal segtsgvel kivtelek helyett a 0 visszatrsi rtkkel vizsglhatjuk
a helyfoglalsi hibk elfordulst:
B. Kompatibilits 1107
X* p1 = new X; // bad_alloc kivtelt vlt ki, ha nincs memria
X* p2 = new(nothrow) X; // visszatrsi rtke 0, ha nincs memria
B.3.5. Sablonok
A szabvny szmos j szolgltatst vezetett be a sablonok krben s a rgebbi lehets-
gek szablyait is tisztbban fogalmazta meg.
Ha rendszernk nem tmogatja a rszlegesen egyedi cl vltozatok (specializcik) meg-
adst, akkor azokhoz a sablonokhoz, amelyeket egybknt egyszer szakostssal hoz-
nnk ltre, nll nevet kell rendelnnk:
template<class T> class plist : private list<void*> { // list<T*> kellett volna
// ...
};
Ha az adott nyelvi vltozat nem tmogatja a sablon tagok hasznlatt, nhny mdszerrl
knytelenek lesznk lemondani. A sablon tagok segtsgvel pldul olyan rugalmasan ha-
trozhatjuk meg elemek ltrehozst, illetve talaktst, amire ezek nlkl nincs lehets-
gnk. (13.6.2.) Nha kisegt megoldst jelent az, hogy egy nll (nem tag) fggvnyt
adunk meg, amely ltrehozza a megfelel objektumot:
template<class T> class X {
// ...
template<class A> X(const A& a);
};
Ha nem hasznlhatunk sablon tagokat, knytelenek vagyunk konkrt tpusokra korltozni
tevkenysgnket:
template<class T> class X {
// ...
X(const A1& a);
X(const A2& a);
// ...
};
A korbbi C++-rendszerek tbbsge a sablon osztlyok pldnyostsakor a sablon sszes
fggvnyt lemsolja s ltrehozza kdjukat. Ez ahhoz vezethet, hogy a nem hasznlt tag-
fggvnyek hibt okoznak (C.13.9.1.). A megoldst az jelenti, ha a krdses tagfggvnyek
meghatrozst az osztlyon kvl helyezzk el.
Fggelkek s trgymutat 1108
A
template<class T> class Container {
// ...
public:
void sort() { /* hasznlja < mveletet */ } // osztlyon belli meghatrozs
};
class Glob { /* a Glob-ban nincs < mvelet */ };
Container<Glob> cg; // nhny szabvny eltti vltozatban
// Container<Glob>::sort() szerepel
helyett pldul a kvetkez megoldst adhatjuk:
template<class T> class Container {
// ...
public:
void sort();
};
template<class T> void Container<T>::sort() { /* a < mvelet hasznlata */ }
// osztlyon kvli definici
class Glob { /* a Glob-ban nincs < mvelet */ };
Container<Glob> cg; // nincs baj, amg a cg.sort()-ot meg nem hvjuk
A C++ rgebbi megvalstsai nem teszik lehetv az osztlyban ksbb bevezetett tagok
kezelst:
template<class T> class Vector {
public:
T& operator[](size_t i) { return v[i]; } // v ksbb bevezetend
// ...
private:
T* v; // hopp: nem tallja!
size_t sz;
};
Ilyenkor vagy gy rendezzk t a tagokat, hogy elkerljk az ilyen problmkat, vagy
a fggvny meghatrozst az osztlydeklarci utn helyezzk.
B. Kompatibilits 1109
Nhny, szabvny eltti C++-vltozat a sablonok esetben nem fogadja el az alaprtelme-
zett paramterek hasznlatt (13.4.1.). Ez esetben minden sablonparamtert kln meg
kell adnunk:
template<class Key, class T, class LT = less<T> > class map {
// ...
};
map<string,int> m; // hopp: nem lehet alaprtelmezett sablonparamter
map< string,int,less<string> > m2; // megolds: explicit megads
B.3.6. A for utasts kezdrtk-ad rsze
Vizsgljuk meg az albbi pldt:
void f(vector<char>& v, int m)
{
for (int i= 0; i<v.size() && i<=m; ++i) cout << v[i];
if (i == m) { // hiba: hivatkozs 'i'-re a 'for' utasts utn
// ...
}
}
Az ilyen programok rgebben mkdtek, mert az eredeti C++-ban a vltoz hatkre a for
utastst tartalmaz hatkr vgig terjedt. Ha ilyen programokkal tallkozunk, a vltozt
rdemes egyszeren a for ciklus eltt bevezetnnk:
void f2(vector<char>& v, int m)
{
int i= 0; // 'i' kell a ciklus utn
for (; i<v.size() && i<=m; ++i) cout << v[i];
if (i == m) {
// ...
}
}
Fggelkek s trgymutat 1110
B.4. Tancsok
[1] A C++ megtanulshoz hasznljuk a szabvnyos C++ legjabb s legteljesebb
vltozatt, amihez csak hozz tudunk frni. B.3.
[2] A C s a C++ kzs rsze egyltaln nem az a rsze a C++-nak, amit elszr
meg kell tanulnunk. 1.6, B.3.
[3] Amikor komoly alkalmazst ksztnk, gondoljunk r, hogy nem minden C++-
vltozat tartalmazza a szabvny sszes lehetsgt. Mieltt egy fbb szolglta-
tst hasznlunk egy komoly programban, rdemes kitapasztalnunk azt egy-kt
kisebb program megrsval. Ezzel ellenrizhetjk, hogy rendszernk mennyire
illeszkedik a szabvnyhoz s mennyire hatkony a megvalsts. Pldakppen
lsd 8.5[6-7], 16.5[10], B.5[7]
[4] Kerljk az elavult szolgltatsokat, pldul a static kulcssz globlis hasznla-
tt, vagy a C stlus tpustalaktst.
[5] A szabvny megtiltotta az implicit int hasznlatt, teht mindig pontosan ad-
juk meg fggvnyeink, vltozink, konstansaink stb. tpust. B.2.2.
[6] Amikor egy C programot C++ programm alaktunk, elszr a fggvnydeklar-
cikat (prototpusokat), s a szabvnyos fejllomnyok kvetkezetes hasznla-
tt ellenrizzk. B.2.2.
[7] Amikor egy C programot C++ programm alaktunk, nevezzk t azokat a vlto-
zkat, melyek C++ kulcsszavak. B.2.2.
[8] Amikor egy C programot C++ programm alaktunk, a malloc() eredmnyt
mindig megfelel tpusra kell alaktanunk. Mg hasznosabb, ha a malloc()
sszes hvst a new opertorral helyettestjk. B.2.2.
[9] Ha a malloc() s a free() fggvnyeket a new s a delete opertorra cserljk,
vizsgljuk meg, hogy a realloc() fggvny hasznlata helyett nem tudjuk-e
a vector, a push_back() s a reserve() szolgltatsait ignybe venni. 3.8, 16.3.5.
[10] Amikor egy C programot C++ programm alaktunk, gondoljunk r, hogy nincs
automatikus talakts egszekrl felsorol tpusokra, teht meghatrozott tala-
ktst kell alkalmaznunk, ha ilyen talaktsra van szksg. 4.8.
[11] Az std nvtrben meghatrozott szolgltatsok kiterjeszts nlkli fejllom-
nyokban szerepelnek. (Az std::cout deklarcija pldul az <iostream> fejllo-
mny rsze.) A rgebbi vltozatokban a standard knyvtr nevei is a globlis
nvtrbe kerltek, a fejllomnyok pedig .h kiterjesztssel rendelkeztek.
(A ::cout deklarcija pldul az <iostream.h> fejllomnyban szerepelt.) 9.2.2,
B.3.1.
[12] Ha rgebbi C++ programok a new visszatrsi rtkt a 0 rtkkel hasonltjk
ssze, akkor ezt a bad_alloc kivtel ellenrzsre kell cserlnnk, vagy
a new(nothrow) utastst kell helyette hasznlnunk. B.3.4.
B. Kompatibilits 1111
[13] Ha fejlesztkrnyezetnk nem tmogatja az alaprtelmezett sablonparamterek
hasznlatt, meg kell adnunk minden paramtert. A typedef segtsgvel a sab-
lonparamterek ismtelgetse elkerlhet (ahhoz hasonlan, ahogy a string t-
pus-meghatrozs megkml minket a basic_string< char, char_traits<char>,
allocator<char> > lerstl). B.3.5.
[14] Az std::string osztly hasznlathoz a <string> fejllomnyra van szksgnk.
(A <string.h> llomnyban a rgi, C stlus karakterlnc-fggvnyek szerepel-
nek.) 9.2.2, B.3.1.
[15] Minden <X.h> szabvnyos C fejllomnynak (amely a globlis nvtrbe vezet
be neveket) megfelel egy <cX> fejllomny, amely az elemeket az std nvtrbe
helyezi. B.3.1.
[16] Nagyon sok rendszerben tallhat egy String.h fejllomny, amely egy karak-
terlnc-tpust r le. Mindig gondoljunk r, hogy ezek eltrhetnek a szabvnyos
string osztlytl.
[17] Ha lehetsgnk van r, a szabvnyos eszkzket hasznljuk a nem szabv-
nyos lehetsgek helyett. 20.1, B.3, C.2.
[18] Ha C fggvnyeket vezetnk be, hasznljuk az extern C formt.
B.5. Gyakorlatok
1. (*2.5) Vegynk egy C programot s alaktsuk t C++-ra. Soroljuk fel a program-
ban hasznlt, a C++-ban nem hasznlhat elemeket, s vizsgljuk meg, rvnye-
sek-e az ANSI C szabvny szerint. Els lpsben a programot csak ANSI C for-
mtumra alaktsuk (prototpusokkal stb.), csak ezutn C++-ra. Becsljk meg,
mennyi idt vesz ignybe egy 100 000 soros C program talaktsa C++-ra.
2. (*2.5) rjunk programot, amely segt talaktani egy C programot C++-ra. Vgez-
ze el azon vltozk tnevezst, melyek a C++-ban kulcsszavak, a malloc() h-
vsokat helyettestse a new opertorral stb. Ajnls: ne akarjunk tkletes prog-
ramot rni.
3. (*2) Egy C stlus C++ programban (pldul amit mostanban alaktottak t egy
C programbl) a malloc() fggvny hvsait cserljk a new opertor meghv-
sra. tlet: B.4[8-9]
4. (*2.5) Egy C stlus C++ programban (pldul amit mostanban alaktottak t
egy C programbl) a makrk, globlis vltozk, kezdrtk nlkli vltozk s
tpustalaktsok szmt cskkentsk a minimumra.
Fggelkek s trgymutat 1112
5. (*3) Vegynk egy C++ programot, amit egy egyszer eljrssal alaktottunk t egy
C programbl, s brljuk azt, mint C++ programot az adatrejts, az elvont br-
zols, az olvashatsg, a bvthetsg s a rszek esetleges jrahasznosthats-
ga szerint. Vgezznk valamilyen nagyobb talaktst ezen brlatok alapjn.
6. (*2) Vegynk egy kicsi (mondjuk 500 soros) C++ programot s alaktsuk azt C
programm. Hasonltsuk ssze a kt programot a mret s az elkpzelhet to-
vbbfejlesztsek szempontjbl.
7. (*3) rjunk nhny kicsi tesztprogramot, mellyel megllapthatjuk, hogy egy
C++-vltozat rendelkezik-e a legjabb szabvnyos lehetsgekkel. Pldul mi
a for utasts kezdrtk-ad rszben bevezetett vltoz hatkre? (B.3.6.)
Hasznlhatk-e alaprtelmezett sablonparamterek? (B.3.5.) Hasznlhatk-e
sablon tagok? (8.2.6.) Ajnls: B.2.4.
8. (*2.5) Vegynk egy C++ programot, amely egy <X.h> fejllomnyt hasznl s
alaktsuk t gy, hogy az <X> s <cX> fejllomnyokat hasznlja. Cskkentsk
a lehet legkevesebbre a using utastsok hasznlatt.
B. Kompatibilits 1113
Technikai rszletek
Valahol a llek s az Univer-
zum legmlyn mindennek
megvan a maga oka"
(Slartibartfast)
Mit gr a szabvny? Karakterkszletek Egsz literlok Konstans kifejezsek Kiter-
jesztsek s talaktsok Tbbdimenzis tmbk Mezk s unik Memriakezels
Szemtgyjts Nvterek Hozzfrs-szablyozs Mutatk adattagokra Sablonok
static tagok friend-ek Sablonok, mint sablonparamterek Sablonparamterek leveze-
tse A typename s a template minstk Pldnyosts Nvkts Sablonok s nv-
terek Explicit pldnyosts Tancsok
C.1. Bevezets s ttekints
Ebben a fejezetben olyan technikai rszleteket s pldkat mutatok be, amelyeket nem le-
hetett elegnsan beilleszteni abba a szerkezetbe, amit a C++ nyelv fbb lehetsgeinek be-
mutatsra hasznltam. Az itt kzlt rszletek fontosak lehetnek programjaink megrsakor
is, de elengedhetetlenek, ha ezek felhasznlsval kszlt programot kell megrtennk.
Technikai rszletekrl beszlek, mert nem szabad, hogy a tanulk figyelmt elvonja az el-
C
sdleges feladatrl, a C++ nyelv helyes hasznlatnak megismersrl, vagy hogy a prog-
ramozkat eltrtse a legfontosabb cltl, gondolataik olyan tiszta s kzvetlen megfogal-
mazstl, amennyire csak a C++ lehetv teszi.
C.2. A szabvny
Az ltalnos hiedelemmel ellenttben a felttlen ragaszkods a C++ nyelvhez s a szabv-
nyos knyvtrakhoz nmagban vve nem jelent sem tkletes programot, sem hordozha-
t kdot. A szabvny nem mondja meg, hogy egy programrszlet j vagy rossz, mindssze
azt rulja el, hogy a programoz mit vrhat el egy megvalststl s mit nem. Van, aki
a szabvny betartsval is borzalmas programokat r, mg a legtbb j program hasznl
olyan lehetsgeket is, melyeket a szabvny nem tartalmaz.
A szabvny nagyon sok fontos dolgot megvalsts-fggnek (implementation-defined) mi-
nst. Ez azt jelenti, hogy minden nyelvi vltozatnak egy konkrt, pontosan meghatrozott
megoldst kell adnia az adott krdsre s ezt a megoldst pontosan dokumentlnia is kell:
unsigned char c1 = 64; // megfelel meghatrozs: egy karakter legalbb 8 bit s
// mindig kpes 64-et trolni
unsigned char c2 = 1256; // megvalsts-fgg: csonkol, ha a char csak 8 bites
A c1 kezdeti rtkadsa megfelelen meghatrozott, mert a char tpusnak legalbb 8 bites-
nek kell lennie, a c2- viszont megvalsts-fgg, mert a char tpus brzolshoz hasz-
nlt bitek pontos szma is az. Ha a char csak 8 bites, az 1256 rtk 232-re csonkul
(C.6.2.1). A legtbb megvalsts-fgg szolgltats a programok futtatshoz hasznlt
hardverhez igazodik.
Amikor komoly programokat runk, gyakran szksg van arra, hogy megvalsts-fgg le-
hetsgeket hasznljunk. Ez az ra annak, ha szmtalan klnbz rendszeren hatkonyan
futtathat programokat akarunk rni. Sokat egyszerstett volna a nyelven, ha a karaktere-
ket 8 bitesknt, az egszeket pedig 32 bitesknt hatrozzuk meg. Nem ritkk azonban a 16
vagy 32 bites karakterkszletek, sem az olyan egsz rtkek, melyek nem brzolhatk 32
biten. Ma mr lteznek 32 gigabjtot meghalad kapacits merevlemezek is, gy a lemez-
cmek brzolshoz rdemes lehet 48 vagy 64 bites egsz rtkeket hasznlni.
Fggelkek s trgymutat 1116
A hordozhatsg lehet leghatkonyabb megvalstshoz rdemes pontosan megfogal-
maznunk, hogy milyen megvalsts-fgg nyelvi elemeket hasznltunk a programunkban,
s rdemes vilgosan elklntennk a program azon rszeit, ahol ezeket a szolgltatso-
kat hasznljuk. Egy jellemz plda erre a megoldsra, hogy az sszes olyan elemet, amely
valamilyen mdon fgg a hardvertl, egy fejllomnyban, konstansok s tpus-meghatro-
zsok formjban rgztjk. Ezt az eljrst tmogatja a standard knyvtr a numeric_limits
osztllyal (22.2).
A nem meghatrozott (definilatlan) viselkeds kellemetlenebb dolog. Egy nyelvi szerkeze-
tet akkor nevez nem meghatrozottnak a szabvny, ha semmilyen rtelmes mkdst nem
vrhatunk el a megvalststl. A szoksos megvalstsi mdszerek ltalban a nem meg-
hatrozott lehetsgeket hasznl programok nagyon rossz viselkedst eredmnyezik:
const int size = 4*1024;
char page[size];
void f()
{
page[size+size] = 7; // nem meghatrozhat
}
Egy ilyen programrszlet teljesen kzenfekv kvetkezmnye, hogy a memriban olyan
adatokat runk fell, melyeket nem lenne szabad, vagy hardverhibk, kivtelek lpnek fel.
A megvalststl nem vrhatjuk el, hogy ilyen kzenfekv szablytalansg esetben is
sszeren mkdjn. Ha a programot komolyan optimalizljuk, a nem meghatrozott szer-
kezetek hasznlatnak eredmnye teljesen kiszmthatatlann vlik. Ha ltezik kzenfekv
s knnyen megvalsthat megoldsa egy problmnak, azt inkbb megvalsts-fgg-
nek minsti a szabvny s nem definilatlannak.
rdemes jelents idt s erfesztst sznnunk annak biztostsra, hogy programunk sem-
mi olyat ne hasznljon, ami a szabvny ltal nem meghatrozott helyzetekhez vezethet. Sok
esetben kln eszkzk segtik ezt a munkt.
C. Technikai rszletek 1117
C.3. Karakterkszletek
E knyv pldaprogramjai az ASCII-nek (ANSI3.4-1968) nevezett, 7 bites, nemzetkzi, ISO
646-1983 karakterkszlet amerikai angol vltozatnak felhasznlsval kszltek. Ez h-
romfle problmt jelenthet azoknak, akik ms karakterkszletet hasznl C++-
krnyezetben rjk programjaikat:
1. Az ASCII tartalmaz olyan elvlaszt karaktereket s szimblumokat is pldul
a ], a { vagy a ! , melyek egyes karakterkszletekben nem tallhatk meg.
2. Azokhoz a karakterekhez, melyeknek nincs hagyomnyos brzolsa, valami-
lyen jellsmdot kell vlasztanunk (pldul az jsorhoz, vagy a 17-es kd ka-
rakterhez).
3. Az ASCII nem tartalmaz bizonyos karaktereket pldul a , a vagy az ,
melyeket az angoltl eltr nyelvekben viszonylag gyakran hasznlnak.
C.3.1. Korltozott karakterkszletek
A klnleges ASCII karakterek [, ], {, }, | s \ az ISO szerint betnek minstett karak-
terpozcit foglalnak el. A legtbb eurpai ISO-646 karakterkszletben ezeken a pozci-
kon az angol bcben nem szerepl betk szerepelnek. A dn nemzeti karakterkszlet
pldul ezeket a pozcikat az , , , , , magnhangzk brzolsra hasznlja s
ezek nlkl nem tl sok dn szveg rhat le.
A trigrf (hrom jelbl ll) karakterek szolglnak arra, hogy tetszleges nemzeti karakte-
reket rhassunk le, hordozhat formban, a legkisebb szabvnyos karakterkszlet felhasz-
nlsval. Ez a forma hasznos lehet, ha programunkat tnyleg t kell vinnnk ms rendszer-
re, de a programok olvashatsgt ronthatja. Termszetesen a hossz tv megolds erre
a problmra a C++ programozk szmra az, hogy beszereznek egy olyan rendszert, ahol
sajt nemzeti karaktereiket s a C++ jeleit is hasznlhatjk. Sajnos ez a megolds nem min-
denhol megvalsthat, s egy j rendszer beszerzse idegesten lass megolds lehet.
A C++ az albbi jellsekkel teszi lehetv, hogy a programozk hinyos karakterkszlet-
tel is tudjanak programot rni:
Fggelkek s trgymutat 1118
Azok a programok melyek ezeket a kulcsszavakat s digrf karaktereket hasznljk, sokkal
olvashatbbak, mint a velk egyenrtk, de trigrf karakterekkel kszlt programok. Ha
azonban az olyan karakterek, mint a { nem rhetk el rendszernkben, knytelenek va-
gyunk a trigrf karaktereket hasznlni a karakterlncokban s karakter-konstansokban a hi-
nyz jelek helyett. A '{' helyett pldul a '??<' karakterkonstanst rhatjuk.
Egyes programozk hagyomnyos opertor-megfelelik helyett szvesebben hasznljk az
and s hasonl kulcsszavakat.
C. Technikai rszletek 1119
Kulcsszavak Digrf Trigrf
and && <% { ??= #
and_eq &= %> } ??( [
bitand & <: [ ??< {
bitor | :> ] ??/ \
compl ~ %: # ??) ]
not ! %:%: ## ??> }
or || ??' ^
or_eq |= ??! |
xor ^ ??- ~
xor_eq ^=
not_eq !=
C.3.2. Vezrlkarakterek
A \ jel segtsgvel nhny olyan karaktert rhetnk el, melyeknek szabvnyos neve is van:
Megjelensi formjuk ellenre ezek is egyetlen karaktert jelentenek.
Lehetsgnk van arra is, hogy egy karaktert egy-, kt- vagy hromjegy, oktlis (a \ utn
nyolcas szmrendszerbeli), vagy hexadecimlis (a \x utn 16-os szmrendszerbeli) szm-
knt adjunk meg. A hexadecimlis jegyek szma nem korltozott. Az oktlis vagy hexade-
cimlis szmjegyek sorozatnak vgt az els olyan karakter jelzi, amely nem rtelmezhe-
t oktlis, illetve hexadecimlis jegyknt:
Fggelkek s trgymutat 1120
Nv ASCII jells C++ jells
jsor/sortrs NL (LF) \n
vzszintes tabultor HT \t
fggleges tabultor VT \v
visszatrls BS \b
kocsivissza CR \r
lapdobs FF \f
cseng BEL \a
fordtott perjel \ \\
krdjel ? \?
aposztrf \
idzjel \
oktlis szm ooo \ooo
hexadecimlis szm hhh \xhhh
Oktlis Hexadecimlis Decimlis ASCII
\6 \x6 6 ACK
\60 \x30 48 0
\137 \x05f 95 _
Ez a jells lehetv teszi, hogy az adott rendszer tetszleges karaktert lerjuk vagy karak-
terlncokban felhasznljuk azokat (lsd 5.2.2). Viszont ha a karakterek jellsre szmo-
kat hasznlunk, programunk nem lesz tvihet ms karakterkszletet hasznl rendszerre.
Lehetsg van arra is, hogy egy karakterliterlban egynl tbb karaktert adjunk meg (pl-
dul ab). Ez a megolds azonban elavult s megvalsts-fgg, gy rdemes elkerlnnk.
Ha egy karakterlncban oktlis konstansokat hasznlunk a karakterek jellsre, rdemes
mindig hrom jegyet megadnunk. A jellsrendszer elg knyelmetlen, gy knnyen elt-
veszthetjk, hogy a konstans utni karakter szmjegy-e vagy sem. A hexadecimlis kons-
tansok esetben hasznljunk kt szmjegyet:
char v1[] = "a\xah\129"; // 6 karakter: 'a' '\xa' 'h' '\12' '9' '\0'
char v2[] = "a\xah\127"; // 5 karakter: 'a' '\xa' 'h' '\127' '\0'
char v3[] = "a\xad\127"; // 4 karakter: 'a' '\xad' '\127' '\0'
char v4[] = "a\xad\0127"; // 5 karakter: 'a' '\xad' '\012' '7' '\0'
C.3.3. Nagy karakterkszletek
C++ programot rhatunk olyan karakterkszletek felhasznlsval is, melyek sokkal bveb-
bek, mint a 127 karakterbl ll ASCII kszlet. Ha az adott nyelvi vltozat tmogatja a na-
gyobb karakterkszleteket, az azonostk, megjegyzsek, karakter konstansok s karakter-
lncok is tartalmazhatnak olyan karaktereket, mint az , a vagy a . Ahhoz azonban, hogy
az gy kszlt program hordozhat legyen, a fordtnak ezeket a klnleges karaktereket
valahogy olyan karakterekre kell kdolnia, melyek minden C++-vltozatban elrhetk.
Ezt az talaktst a C++ a knyvben is hasznlt alap karakterkszletre a rendszer lta-
lban mg azeltt vgrehajtja, hogy a fordt brmilyen ms tevkenysgbe kezdene, teht
a programok jelentst ez nem rinti.
A nagy karakterkszletekrl a C++ ltal tmogatott kisebb karakterkszletre val szabv-
nyos talaktst ngy vagy nyolc jegybl ll hexadecimlis szmok valstjk meg:
ltalnos karakternv:
\U X X X X X X X X
\u X X X X
Itt X egyetlen, hexadecimlis szmjegyet jell (pldul \u1e2b). A rvidebb \uXXXX jel-
ls egyenrtk a \U0000XXXX-szel. Ha a megadott szmban a jegyek szma nem ngy s
nem is nyolc, a fordt hibt jelez.
C. Technikai rszletek 1121
A programoz ezeket a karakterkdolsokat kzvetlenl hasznlhatja, de valjban arra ta-
lltk ki, hogy a programoz ltal lthat karaktereket a megvalsts belsleg egy kisebb
karakterkszlettel trolhassa.
Ha a bvtett karakterkszletek azonostkban val hasznlathoz egy adott krnyezet
egyedi szolgltatsaira tmaszkodunk, programunk hordozhatsgt ersen lerontjuk. Ha
nem ismerjk azt a termszetes nyelvet, amelyet az azonostk, illetve megjegyzsek meg-
fogalmazshoz hasznltak, a program nagyon nehezen olvashat lesz, ezrt a nemzetk-
zileg hasznlt programokban rdemes megmaradnunk az angol nyelvnl s az ASCII karak-
terkszletnl.
C.3.4. Eljeles s eljel nlkli karakterek
Megvalsts-fgg az is, hogy az egyszer char eljeles vagy eljel nlkli tpus-e, ami n-
hny kellemetlen meglepetst s vratlan helyzeteket eredmnyezhet:
char c = 255; // a 255 "csupa egyes", hexadecimlis jellssel 0xFF
int i = c;
Mi lesz itt az i rtke? Sajnos nem meghatrozhat. A vlasz az ltalam ismert sszes nyelvi
vltozatban attl fgg, hogy a char rtk bitmintja milyen rtket eredmnyez, ha egsz-
knt rtelmezzk. Egy SGI Challenge gpen a char eljel nlkli, gy az eredmny 255. Egy
Sun SPARC vagy egy IBM PC gpen viszont a char eljeles, aminek kvetkeztben az i r-
tke -1 lesz. Ilyenkor a fordtnak illik figyelmeztetnie, hogy a 255 literl karakterr alak-
tsval a -1 rtket kapjuk. A C++ nem ad ltalnos megoldst ezen problma kikszb-
lsre. Az egyik lehetsg, hogy teljesen elkerljk az egyszer char tpus hasznlatt s
mindig valamelyik egyedi cl vltozatot hasznljuk. Sajnos a standard knyvtr bizonyos
fggvnyei (pldul a strcmp()) egyszer char tpus paramtert vrnak (20.4.1).
Egy char rtknek mindenkppen signed char vagy unsigned char tpusnak kell lennie,
de mind a hrom karaktertpus klnbz, gy pldul a klnbz char tpusokra hivat-
koz mutatk sem keverhetk ssze:
void f(char c, signed char sc, unsigned char uc)
{
char* pc = &uc; // hiba: nincs mutat-talakts
signed char* psc = pc; // hiba: nincs mutat-talakts
unsigned char* puc = pc; // hiba: nincs mutat-talakts
psc = puc; // hiba: nincs mutat-talakts
}
Fggelkek s trgymutat 1122
A klnbz char tpusok vltozi szabadon rtkl adhatk egymsnak, de amikor egy
eljeles char vltozba tl nagy rtket akarunk rni, az eredmny nem meghatrozhat
lesz:
void f(char c, signed char sc, unsigned char uc)
{
c = 255; // megvalsts-fgg, ha a sima karakterek eljelesek s 8 bitesek
c = sc; // rendben
c = uc; // megvalsts-fgg, ha a sima karakterek eljelesek s 'uc' rtke tl nagy
sc = uc; // megvalsts-fgg, ha 'uc' rtke tl nagy
uc = sc; // rendben: talakts eljel nlklire
sc = c; // megvalsts-fgg, ha a sima karakterek eljel nlkliek s 'c' rtke tl nagy
uc = c; // rendben: talakts eljel nlklire
}
Ha mindenhol az egyszer char tpust hasznljuk, ezek a problmk nem jelentkeznek.
C.4. Az egsz literlok tpusa
Az egsz literlok tpusa ltalban alakjuktl, rtkktl s uttagjuktl is fgg:
Ha decimlis rtkrl van sz s a vgre nem runk semmilyen uttagot, tpusa
az els olyan lesz a kvetkezk kzl, amelyben brzolhat: int, long int,
unsigned long int.
Ha oktlis vagy hexadecimlis formban, uttag nlkl adjuk meg az rtket,
annak tpusa az els olyan lesz a kvetkezk kzl, amelyben brzolhat: int,
unsigned int, long int, unsigned long int.
Ha az u vagy az U uttagot hasznljuk, a tpus a kvetkez lista els olyan tpu-
sa lesz, amelyben az rtk brzolhat: unsigned int, unsigned long int.
Ha l vagy L uttagot adunk meg, az rtk tpusa a kvetkez lista els olyan t-
pusa lesz, amelyben az rtk brzolhat: long int, unsigned long int.
Ha az uttag ul, lu, uL, Lu, Ul, lU, UL vagy LU, az rtk tpusa unsigned long int
lesz.
A 100000 pldul egy olyan gpen, amelyen az int 32 bites, int tpus rtk, de long int
egy olyan gpen, ahol az int 16, a long int pedig 32 bites. Ugyangy a 0XA000 tpusa int,
C. Technikai rszletek 1123
ha az int 32 bites, de unsigned int, ha 16 bites. Ezeket a megvalsts-fggsgeket az
uttagok hasznlatval kerlhetjk el: a 100000L minden gpen long int tpus,
a 0XA000U pedig minden gpen unsigned int.
C.5. Konstans kifejezsek
Az olyan helyeken, mint a tmbhatrok (5.2.), a case cmkk (6.3.2.) vagy a felsorol ele-
mek kezdrtk-adi (4.8.), a C++ konstans kifejezseket hasznl. A konstans kifejezs r-
tke egy szm vagy egy felsorolsi konstans. Ilyen kifejezseket literlokbl (4.3.1, 4.4.1,
4.5.1), felsorol elemekbl (4.8) s konstans kifejezsekkel meghatrozott const rtkek-
bl pthetnk fel. Sablonokban egsz tpus sablonparamtereket is hasznlhatunk
(C.13.3). Lebegpontos literlok (4.5.1) csak akkor hasznlhatk, ha azokat meghatro-
zott mdon egsz tpusra alaktjuk. Fggvnyeket, osztlypldnyokat, mutatkat s hivat-
kozsokat csak a sizeof opertor (6.2) paramtereknt hasznlhatunk.
A konstans kifejezs egy olyan egyszer kifejezs, melyet a fordt ki tud rtkelni, mieltt
a programot sszeszerkesztennk s futtatnnk.
C.6. Automatikus tpustalakts
Az egsz s a lebegpontos tpusok (4.1.1) szabadon keverhetk az rtkadsokban s
a kifejezsekben. Ha lehetsg van r, a fordt gy alaktja t az rtkeket, hogy ne veszt-
snk informcit. Sajnos az informcivesztssel jr talaktsok is automatikusan vgre-
hajtsra kerlnek. Ebben a rszben az talaktsi szablyokrl, az talaktsok problmirl
s ezek kvetkezmnyeirl lesz sz.
C.6.1. Kiterjesztsek
Azokat az automatikus talaktsokat, melyek megrzik az rtkeket, kzsen kiterjeszt-
seknek (promotion) nevezzk. Mieltt egy aritmetikai mveletet vgrehajtunk, egy egsz t-
pus kiterjeszts az int tpusnl kisebb rtkeket int rtkre alaktja. Figyeljnk r, hogy
Fggelkek s trgymutat 1124
ezek a kiterjesztsek nem long tpusra alaktanak (hacsak valamelyik operandus nem
wchar_t vagy olyan felsorol rtk, amely mr eleve nagyobb, mint egy int). Ez a kiterjesz-
ts eredeti cljt tkrzi a C nyelvben: az operandusokat termszetes mretre akarjuk
alaktani az aritmetikai mveletek elvgzse eltt.
Az egsz tpus kiterjesztsek a kvetkezk:
A char, signed char, unsigned char, short int s unsigned short int rtkeket
int tpusra alaktja a rendszer, ha az int az adott tpus sszes rtkt kpes b-
rzolni; ellenkez esetben a cltpus unsigned int lesz.
A wchar_t tpust (4.3) s a felsorol tpusokat (4.8) a rendszer a kvetkez lis-
ta els olyan tpusra alaktja, amely brzolni tudja a megfelel tpus sszes r-
tkt: int, unsigned int, long, unsigned long.
A bitmezk (C.8.1) int tpusak lesznek, ha az kpes a bitmez sszes rtkt
brzolni. Ha az int nem, de az unsigned int kpes erre, akkor az utbbi lesz
a cltpus. Ha ez sem valsthat meg, akkor nem kvetkezik be egsz tpus
kiterjeszts.
Logikai rtkek int tpusra alaktsa: a false rtkbl 0, a true rtkbl 1 lesz.
A kiterjesztsek a szoksos aritmetikai talaktsok rszt kpezik. (C.6.3)
C.6.2. talaktsok
Az alaptpusok szmos mdon alakthatk t: vlemnyem szerint tl sok is az engedlye-
zett talakts:
void f(double d)
{
char c = d; // vigyzat: ktszeres pontossg lebegpontos rtk talaktsa
karakterre
}
Amikor programot runk, mindig figyelnnk kell arra, hogy elkerljk a nem meghatrozott
szolgltatsokat s azokat az talaktsokat, melyek sz nlkl tntetnek el informcikat.
A fordtk a legtbb veszlyes talaktsra figyelmeztethetnek s szerencsre ltalban meg
is teszik.
C. Technikai rszletek 1125
C.6.2.1. Egsz talaktsok
Egy egsz rtk brmely ms egsz tpusra talakthat, de a felsorol rtkeket is egsz t-
pusokra alakthatjuk.
Ha a cltpus eljel nlkli, az eredmny egyszeren annyi bit lesz a forrsbl, amennyi
a cltpusban elfr. (Ha kell, a magas helyirtkk biteket a rendszer eldobja.) Pontosab-
ban fogalmazva, az eredmny a legkisebb olyan egsz, amelynek osztsa a 2 n-edik hatv-
nyval azonos rtket ad, mint a forrsrtk hasonl osztsa, ahol n az eljel nlkli tpus
brzolshoz hasznlt bitek szma. Pldul:
unsigned char uc = 1023; // binris 1111111111: uc binris 11111111 lesz; ami 255
Ha a cltpus eljeles s az talaktand rtk brzolhat vele, az rtk nem vltozik meg.
Ha a cltpus nem tudja brzolni az rtket, az eredmny az adott nyelvi vltozattl fgg
lesz:
signed char sc = 1023; // megvalsts-fgg
A lehetsges eredmnyek: 127 s -1 (C.3.4).
A logikai s felsorol rtkek automatikusan a megfelel egsz tpusra alakthatk (4.2,
4.8).
C.6.2.2. Lebegpontos talaktsok
Lebegpontos rtkeket ms lebegpontos tpusokra alakthatunk t. Ha a forrsrtk pon-
tosan brzolhat a cltpusban, az eredmny az eredeti szmrtk lesz. Ha a forrsrtk
a cltpus kt egymst kvet brzolhat rtke kztt ll, eredmnyknt ezen kt rtk
valamelyikt kapjuk. A tbbi esetben az eredmny nem meghatrozhat:
float f = FLT_MAX; // a legnagyobb lebegpontos rtk
double d = f; // rendben: d == f
float f2 = d; // rendben: f2 == f
double d3 = DBL_MAX; // a legnagyobb double rtk
float f3 = d3; // nem meghatrozott, ha FLT_MAX<DBL_MAX
Fggelkek s trgymutat 1126
C.6.2.3. Mutat- s hivatkozs-talaktsok
Az objektumtpusra hivatkoz mutatk mind void* (5.6) tpusra alakthatk; a leszrma-
zott osztlyra hivatkoz mutatk s hivatkozsok brmelyike talakthat egy elrhet s
egyrtelm alaposztlyra hivatkoz mutat, illetve hivatkozs tpusra (12.2). Fontos vi-
szont, hogy egy fggvnyre vagy egy tagra hivatkoz mutat automatikusan nem alaktha-
t void* tpusra.
A 0 rtk konstans kifejezsek (C.5) brmilyen mutat s tagra hivatkoz mutat (5.1.1)
tpusra automatikusan talakthatk:
int* p =
! ! ! ! ! !
! ! ! ! ! ! !
! ! ! ! ! ! !
! ! !!!!!! !!!!! !!!!1;
Egy T* rtk automatikusan const T* tpusra alakthat (5.4.1) s ugyangy egy T& rtk is
talakthat const T& tpusra.
C.6.2.4. Tagra hivatkoz mutatk talaktsa
A tagokra hivatkoz mutatk s hivatkozsok gy alakthatk t automatikusan, ahogy
a 15.5.1 pontban lertuk.
C.6.2.5. Logikai rtkek talaktsa
A mutatk, egszek s lebegpontos rtkek automatikusan bool tpusv alakthatk
(4.2). A nem nulla rtkek eredmnye true, a nulla eredmnye false lesz:
void f(int* p, int i)
{
bool is_not_zero = p; // igaz, ha p!=0
bool b2 = i; // igaz, ha i!=0
}
C. Technikai rszletek 1127
C.6.2.6. Lebegpontosegsz talaktsok
Amikor egy lebegpontos rtket egssz alaktunk, a trtrsz elveszik, vagyis a lebeg-
pontosrl egszre val talakts csonkolst eredmnyez. Az int(1.6) rtke pldul 1 lesz.
Ha a csonkolssal keletkez rtk sem brzolhat a cltpusban, az eredmny nem meg-
hatrozhatnak minsl:
int i = 2.7; // i rtke 2 lesz
char b = 2000.7; // nem meghatrozott 8 bites char tpusra: a 2000 nem brzolhat
// 8 bites karakterben
Az egszrl lebegpontosra val talakts matematikailag annyira pontos, amennyire
a hardver megengedi. Ha egy egsz rtk egy lebegpontos tpus rtkeknt nem brzol-
hat, az eredmny nem lesz egszen pontos:
int i = float(1234567890);
Itt az i rtke 1234567936 lesz az olyan gpeken, amely az int s a float brzolsra is 32
bitet hasznlnak.
Termszetesen clszer elkerlni az olyan automatikus talaktsokat, ahol informcit ve-
szthetnk. A fordtk kpesek szlelni nhny veszlyes talaktst, pldul a lebegpon-
tosrl egszre vagy a long int tpusrl char tpusra valt, ennek ellenre az ltalnos, ford-
tsi idben trtn ellenrzs nem mindig kellen megbzhat, ezrt a programoznak el-
vigyzatosnak kell lennie. Amennyiben az elvigyzatossg nem elegend, a programoz-
nak ellenrzseket kell beiktatnia a hibk elkerlsre:
class check_failed { };
char checked(int i)
{
char c = i; // figyelem: nem hordozhat (C.6.2.1)
if (i != c) throw check_failed();
return c;
}
void my_code(int i)
{
char c = checked(i);
// ...
}
Ha gy szeretnnk csonkolst vgezni, hogy a program ms rendszerre is vltoztats nl-
kl tvihet legyen, vegyk figyelembe a numeric_limits-ben (22.2) megadottakat.
Fggelkek s trgymutat 1128
C.6.3. Szoksos aritmetikai talaktsok
Ezek az talaktsok akkor kvetkeznek be, ha egy ktoperandus (binris) mvelet kt
operandust kzs tpusra kell alaktani. Ez a kzs tpus hatrozza meg az eredmny t-
pust is.
1. Ha az egyik operandus long double tpus, a rendszer a msikat is long double
tpusv alaktja.
Ha egyik sem long double, de valamelyik double, a msik is double tpusv
alakul.
Ha egyik sem double, de valamelyik float, a msik is float rtkre lesz tala-
ktva.
Ha a fentiek egyike sem teljesl, a rendszer mindkt operandusra egsz ki-
terjesztst (C.6.1) alkalmaz.
2. Ezutn, ha valamelyik operandus unsigned long, akkor a msik is unsigned
long lesz.
Ha a fenti nem teljesl, de az egyik operandus long int, a msik pedig
unsigned int, s a long int tpus kpes brzolni az sszes unsigned int r-
tket, akkor az unsigned int rtket a fordt long int tpusra alaktja. Ha
a long int nem elg nagy, mindkt rtket unsigned long int tpusra kell ala-
ktani.
Ha a fenti nem teljesl, de valamelyik operandus long, a msik is long tpu-
sra alakul.
Ha valamelyik operandus unsigned, a msik is unsigned rtkre alakul.
Ha a fentiek egyike sem teljesl, mindkt rtk int lesz.
C.7. Tbbdimenzis tmbk
Nem ritka, hogy vektorok vektorra van szksgnk, st vektorok vektorainak vektorra.
A krds az, hogy a C++-ban hogyan brzolhatjuk ezeket a tbbdimenzis tmbket. Eb-
ben a pontban elszr megmutatjuk, hogyan hasznljuk a standard knyvtr vector oszt-
lyt erre a clra, majd megvizsgljuk, hogyan kezelhetk a tbbdimenzis tmbk a C-ben
s a C++-ban akkor, ha csak a beptett lehetsgek llnak rendelkezsnkre.
C. Technikai rszletek 1129
C.7.1. Vektorok
A szabvnyos vector (16.3) egy nagyon ltalnos megoldst knl:
vector< vector<int> > m(3, vector<int>(5));
Ezzel az utastssal egy 3 olyan vektort tartalmaz vektort hozunk ltre, melyek mindegyi-
ke 5 darab egsz rtk trolsra kpes. Mind a 15 egsz elem a 0 alaprtelmezett rtket
kapja, melyet a kvetkez mdon mdosthatunk:
void init_m()
{
for (int i = 0; i<m.size(); i++) {
for (int j = 0; j<m[i].size(); j++) m[i][j] = 10*i+j;
}
}
Grafikusan ezt gy brzolhatjuk:
A vector objektumot egy, az elemekre hivatkoz mutatval s az elemek szmval brzol-
tuk. Az elemeket ltalban egy szoksos tmbben troljuk. Szemlltetskppen mindegyik
egsz elembe a koordintinak megfelel rtket rtuk.
Az egyes elemeket ktszeres indexelssel rhetjk el. Az m[i][j] kifejezs pldul az i-edik
vektor j-edik elemt vlasztja ki. Az m elemeit a kvetkezkppen rathatjuk ki:
Fggelkek s trgymutat 1130
00 01
3
5
5
5
02 03 04
10 11 12 13 14
20 21 22 23 24
m:
m[0]:
m[1]:
m[2]:
void print_m()
{
for (int i = 0; i<m.size(); i++) {
for (int j = 0; j<m[i].size(); j++) cout << m[i][j] << '\t';
cout << '\n';
}
}
Ennek eredmnye a kvetkez lesz:
0 1 2 3 4
10 11 12 13 14
20 21 22 23 24
Figyeljk meg, hogy az mvektorok vektora, nem egyszer tbbdimenzis tmb, ezrt a gya-
korlatban lehetsgnk van arra, hogy egyesvel mdostsuk a bels vektorok mrett:
void reshape_m(int ns)
{
for (int i = 0; i<m.size(); i++) m[i].resize(ns);
}
A vector< vector<int> > szerkezetben szerepl vector<int> vektorok mretnek nem feltt-
lenl kell azonosnak lennie.
C.7.2. Tmbk
A beptett tmbk jelentik a C++-ban a legfbb hibaforrst, klnsen ha tbbdimenzis
tmbk ksztsre hasznljuk fel azokat. Kezdk szmra ezek okozzk a legtbb kevere-
dst is, ezrt amikor lehet, hasznljuk helyettk a vector, list, valarray, string stb. osztlyokat.
A tbbdimenzis tmbket tmbk tmbjeknt brzolhatjuk. Egy 3x5-s mret tmbt
az albbi mdon vezethetnk be:
int ma[3][5]; // 3 tmb, mindegyikben 5 int elem
C. Technikai rszletek 1131
Az ma adatszerkezetet a kvetkezkppen tlthetjk fel:
void init_ma()
{
for (int i = 0; i<3; i++) {
for (int j = 0; j<5; j++) ma[i][j] = 10*i+j;
}
}
brval:
Az ma tmb egyszeren 15 egsz rtket trol, melyeket gy rnk el, mintha 3 darab 5 ele-
m tmbrl lenne sz. Teht a memriban nincs olyan objektum, amely magt az ma mt-
rixot jelenti, csak az nll elemeket troljuk. A 3 s 5 dimenzirtkek csak a forrsban je-
lennek meg. Amikor ilyen programot runk, neknk kell valahogy emlkeznnk az rtkek-
re. Az ma tmbt pldul a kvetkez kdrszlet segtsgvel rathatjuk ki:
void print_ma()
{
for (int i = 0; i<3; i++) {
for (int j = 0; j<5; j++) cout << ma[i][j] << '\t';
cout << '\n';
}
}
Az a vesszs jells, amit nhny nyelv a tbbdimenzis tmbk kezelshez biztost,
a C++-ban nem hasznlhat, mert a vessz (,) a mveletsorozat-opertor (6.2.2). Szeren-
csre a fordt a legtbb hibra figyelmeztethet:
int bad[3,5]; // hiba: a vessz nem megengedett konstans kifejezsben
int good[3][5]; // 3 tmb, mindegyikben 5 int elem
int ouch = good[1,4]; // hiba: int kezdrtke nem lehet int* tpus (good[1,4]
// jelentse good[4], ami int* tpus)
int nice = good[1][4]; // helyes
Fggelkek s trgymutat 1132
00 ma: 01 04 13 22 10 14 23 24 02 11 20 03 12 21
C.7.3. Tbbdimenzis tmbk tadsa
Kpzeljnk el egy fggvnyt, mellyel egy tbbdimenzis tmbt akarunk feldolgozni. Ha
a dimenzikat fordtskor ismerjk, nincs problma:
void print_m35(int m[3][5])
{
for (int i = 0; i<3; i++) {
for (int j = 0; j<5; j++) cout << m[i][j] << '\t';
cout << '\n';
}
}
A tbbdimenzis tmbknt megjelen mtrixokat egyszeren mutatknt adjuk t (nem
kszl rla msolat, 5.3). Az els dimenzi rdektelen az egyes elemek helynek megha-
trozsakor, csak azt rgzti, hogy az adott tpusbl (esetnkben az int[5]-bl) hny darab
ll egyms utn (esetnkben 3). Pldul, nzzk meg az ma elbbi brzolst. Megfigyel-
hetjk, hogy ha csak annyit tudunk, hogy a msodik dimenzi 5, akkor is brmely ma[i][5]
elem helyt meg tudjuk llaptani. Ezrt az els dimenzit tadhatjuk kln paramterknt:
void print_mi5(int m[][5], int dim1)
{
for (int i = 0; i<dim1; i++) {
for (int j = 0; j<5; j++) cout << m[i][j] << '\t';
cout << '\n';
}
}
A problms eset az, ha mindkt dimenzit paramterknt akarjuk tadni. A nyilvnval
megolds nem mkdik:
void print_mij(int m[][], int dim1, int dim2) // nem gy viselkedik,
// ahogy legtbben gondolnnk
{
for (int i = 0; i<dim1; i++) {
for (int j = 0; j<dim2; j++) cout << m[i][j] << '\t'; // meglepets!
cout << '\n';
}
}
C. Technikai rszletek 1133
Elszr is, az m[][] deklarci hibs, mert egy tbbdimenzis tmb esetben a msodik di-
menzit ismernnk kell ahhoz, hogy az elemek helyt meg tudjuk hatrozni. Msrszt, az
m[i][j] kifejezst (teljesen szablyosan) *(*(m+i)+j)-knt rtelmezi a fordt, ami ltalban
nem azt jelenti, amit a programoz ki akart vele fejezni. A helyes megolds a kvetkez:
void print_mij(int* m, int dim1, int dim2)
{
for (int i = 0; i<dim1; i++) {
for (int j = 0; j<dim2; j++) cout << m[i*dim2+j] << '\t'; // zavaros
cout << '\n';
}
}
A print_mij() fggvnyben az elemek elrshez hasznlt kifejezs egyenrtk azzal, amit
a fordt llt el, amikor ismeri az utols dimenzit.
Ennek a fggvnynek a meghvshoz a mtrixot egyszer mutatknt adjuk t:
int main()
{
int v[3][5] = { {0,1,2,3,4}, {10,11,12,13,14}, {20,21,22,23,24} };
print_m35(v);
print_mi5(v,3);
print_mij(&v[0][0],3,5);
}
Figyeljk meg az utols sorban a &v[0][0] kifejezs hasznlatt. A v[0] szintn megfelelne,
mert az elbbivel teljesen egyenrtk, a v viszont tpushibt okozna. Az ilyen csnya s
krlmnyes programrszleteket jobb elrejteni. Ha kzvetlenl tbbdimenzis tmbkkel
kell foglalkoznunk, prbljuk klnvlasztani a vele foglalkoz programrszleteket. Ezzel
leegyszersthetjk a kvetkez programoz dolgt, akinek majd a programhoz hozz kell
nylnia. Ha kln megadunk egy tbbdimenzis tmb tpust s benne egy megfelel inde-
xel opertort, a legtbb felhasznlt megkmlhetjk attl, hogy az adatok fizikai elrende-
zsvel kelljen foglalkoznia (22.4.6).
A szabvnyos vector (16.3) osztlyban ezek a problmk mr nem jelentkeznek.
Fggelkek s trgymutat 1134
C.8. Ha kevs a memria
Ha komolyabb alkalmazst ksztnk, gyakran tbb memrira lenne szksgnk, mint
amennyi rendelkezsnkre ll. Kt mdszer van arra, hogy tbb helyet prseljnk ki:
1. Egyetlen bjtban tbb kisebb objektumot trolhatunk.
2. Ugyanazt a terletet klnbz idpontokban klnbz objektumok trols-
ra hasznlhatjuk.
Az elbbit a mezk, az utbbit az unik segtsgvel valsthatjuk meg. Ezekrl az eszk-
zkrl a korbbiakban mr volt sz. A mezket s az unikat szinte csak optimalizlshoz
hasznljuk, ami gyakran a memria adott rendszerbeli felptsn alapul, gy a program
nem lesz ms rendszerre tvihet. Teht rdemes ktszer is meggondolnunk, mieltt eze-
ket a szerkezeteket hasznljuk. Gyakran jobb megoldst jelent, ha az adatokat mskpp
kezeljk, pldul tbb dinamikusan lefoglalt terletet (6.2.6) s kevesebb elre lefoglalt
(statikus) memrit hasznlunk.
C.8.1. Mezk
Gyakran rettent pazarlsnak tnik, hogy egy binris vltozt pldul egy ki/be kapcso-
lt egy teljes bjt (char vagy bool) felhasznlsval brzolunk, de ez a legkisebb egysg,
amelyet a memriban kzvetlenl megcmezhetnk a C++ segtsgvel (5.1). Viszont, ha
a struct tpusban mezket hasznlunk, lehetsg van arra, hogy tbb ilyen kis vltozt
egyetlen csomagba fogjunk ssze. Egy tag akkor vlik mezv, ha utna megadjuk az lta-
la elfoglalt bitek szmt. Nvtelen mezk is megengedettek. Ezek a C++ szempontjbl nin-
csenek hatssal az elnevezett mezk jelentsre, segtsgkkel azonban pontosan lerha-
tunk bizonyos gpfgg adatokat:
struct PPN { // R6000 fizikai lapszm
unsigned int PFN : 22; // lapkeret-szm
int : 3; // nem hasznlt
unsigned int CCA : 3; // Cache Coherency Algorithm
bool nonreachable : 1;
bool dirty : 1;
bool valid : 1;
bool global : 1;
};
Ezzel a pldval a mezk msik legfontosabb felhasznlsi terlett is bemutattuk: valami-
lyen kls (nem C++) szerkezet rszeit is pontosan elnevezhetjk velk. A mezk egsz
vagy felsorol tpusak (4.1.1). Egy meznek nem lehet lekrdezni a cmt, viszont ettl
C. Technikai rszletek 1135
eltekintve teljesen tlagos vltozknt kezelhetjk. Figyeljk meg, hogy egy bool tpus r-
tket itt tnyleg egy bittel brzolhatunk. Egy opercis rendszer magjban vagy egy hiba-
keresben a PPN tpus a kvetkezkppen hasznlhat:
void part_of_VM_system(PPN* p)
{
// ...
if (p->dirty) { // a tartalom megvltozott
// lemezre msols
p->dirty = 0;
}
// ...
}
Meglep mdon, ha tbb vltozt mezk segtsgvel egyetlen bjtba srtnk ssze, akkor
sem felttlenl takartunk meg memrit. Az adatterlet cskken, de az ilyen adatok kezels-
hez szksges kd a legtbb szmtgpen jelentsen nagyobb. A programok mrete jelent-
sen cskkenthet azzal, hogy a bitmezkknt brzolt binris adatokat char tpusra alaktjuk,
radsul egy char vagy int elrse ltalban sokkal gyorsabb, mint a bitmezk elrse. A me-
zk csak egy knyelmes rvidtst adnak a bitenknti logikai mveletekhez (6.2.4), hogy egy
gpi sz rszeibe egymstl fggetlenl tudjunk adatokat rni, illetve onnan kiolvasni.
C.8.2. Unik
Az uni olyan struct, melyben minden tag ugyanarra a cmre kerl a memriban, gy az
uni csak annyi terletet foglal, mint a legnagyobb tagja. Termszetesen az uni egyszerre
csak egy tagjnak rtkt tudja trolni. Kpzeljnk el pldul egy szimblumtbla-bejegy-
zst, amely egy nevet s egy rtket trol:
enum Type { S, I };
struct Entry {
char* name;
Type t;
char* s; // s hasznlata, ha t==S
int i; // i hasznlata, ha t==I
};
void f(Entry* p)
{
if (p->t == S) cout << p->s;
// ...
}
Fggelkek s trgymutat 1136
Az s s az i tagot sohasem fogjuk egyszerre hasznlni, gy feleslegesen pazaroltuk a mem-
rit. Ezen a problmn knnyen segthetnk a union adatszerkezet segtsgvel:
union Value {
char* s;
int i;
};
A rendszer nem tartja nyilvn, hogy a union ppen melyik rtket trolja, teht ezt tovbb-
ra is a programoznak kell megtennie:
struct Entry {
char* name;
Type t;
Value v; // v.s hasznlata, ha t==S; v.i hasznlata, ha t==I
};
void f(Entry* p)
{
if (p->t == S) cout << p->v.s;
// ...
}
Sajnos a union bevezetse arra knyszert minket, hogy az egyszer s helyett a v.s kifeje-
zst hasznljuk. Ezt a problmt a nvtelen uni segtsgvel kerlhetjk el, amely egy
olyan uni, amelynek nincs neve, gy tpust sem ad meg, csak azt biztostja, hogy tagjai
ugyanarra a memriacmre kerljenek:
struct Entry {
char* name;
Type t;
union {
char* s; // s hasznlata, ha t==S
int i; // i hasznlata, ha t==I
};
};
void f(Entry* p)
{
if (p->t == S) cout << p->s;
// ...
}
gy az Entry szerkezetet hasznl programrszleteken nem kell vltoztatnunk. A union t-
pus olyan felhasznlsakor, amikor egy rtket mindig abbl a tagbl olvasunk vissza, ame-
lyiken keresztl rtuk, csak optimalizlst hajtunk vgre. Az uni ilyen jelleg felhasznl-
C. Technikai rszletek 1137
st azonban nem mindig knny biztostani s a helytelen hasznlat nagyon kellemetlen hi-
bkat okozhat. A hibk elkerlse rdekben az unit befoglalhatjuk egy olyan egysgbe,
amely biztostja, hogy az rtk tpusa s a hozzfrs mdja egymsnak megfelel lesz
(10.6[20]). Az unit nha szndkosan helytelenl hasznljuk, valamifle tpustalakts
rdekben. Ezt a lehetsget ltalban azok a programozk hasznljk, akik az explicit
tpustalaktst nem tmogat nyelven is rnak programokat. Ott az ilyen csalsra tnyleg
szksg van. Az albbi szerkezettel az int s az int* tpus kztti talaktst prbljuk meg-
valstani, egyszer bitenknti egyenrtksg felttelezsvel:
union Fudge {
int i;
int* p;
};
int* cheat(int i)
{
Fudge a;
a.i = i;
return a.p; // hibs hasznlat
}
Ez valjban nem is igazi talakts. Bizonyos gpeken az int s az int* nem ugyanannyi he-
lyet foglal, ms gpeken az egsz rtkeknek nem lehet pratlan cme. Teht az uni ilyen
felhasznlsa nagyon veszlyes s nem is vihet t ms rendszerre, radsul ugyanerre
a feladatra van egy egyszerbb, biztonsgosabb s hordozhat megolds: a meghatro-
zott (explicit) tpustalakts (6.2.7).
Az unit idnknt szndkosan a tpustalakts elkerlsre hasznljk. Pldul szks-
gnk lehet arra, hogy a Fudge segtsgvel megllaptsuk, hogy a 0 mutatt hogyan br-
zolja a rendszer:
int main()
{
Fudge foo;
foo.p = 0;
cout << "A 0 mutat egsz rtke: " << foo.i << '\n';
}
C.8.3. Unik s osztlyok
A bonyolultabb unik esetben gyakran elfordul, hogy egyes tagok jval nagyobbak, mint
a rendszeresen hasznlt tagok. Mivel a union mrete legalbb akkora, mint a legnagyobb
tagja, sok helyet elpazarolhatunk gy. A pazarlst ltalban elkerlhetjk, ha uni helyett
szrmaztatott osztlyokat hasznlunk.
Fggelkek s trgymutat 1138
Egy konstruktorral, destruktorral s msol mvelettel rendelkez osztly nem tartozhat
egy uni tag tpusba (10.4.12), hiszen a fordt nem tudja, melyik tagot kell trlnie.
C.9. Memriakezels
A C++-ban a memria kezelsre hrom alapvet mdszer ll rendelkezsnkre:
Statikus memria: Ebben az esetben az objektum szmra az sszeszerkeszt
(linker) foglal helyet a program teljes futsi idejre. Statikus memriban kap-
nak helyet a globlis s nvtr-vltozk, a static adattagok (10.2.4) s a fggv-
nyekben szerepl static vltozk (7.1.2). Azok az objektumok, melyek a stati-
kus memriban kapnak helyet, egyszer jnnek ltre s a program vgig ltez-
nek. Cmk a program futsa kzben nem vltozik meg. A statikus objektumok
a szlakat (osztott cm memriaterleteket prhuzamosan) hasznl progra-
mokban problmt jelenthetnek, mert a megfelel elrshez zrolsokat kell al-
kalmaznunk.
Automatikus memria: Ezen a terleten a fggvnyparamterek s helyi vltozk
jnnek ltre. A fggvny vagy blokk minden egyes lefutsakor sajt pldnyok
jnnek ltre az ilyen vltozkbl. Ezt a tpus memrit automatikusan foglalja
le s szabadtja fel a rendszer, ezrt kapta az automatikus memria nevet.
Az automatikus memrira gyakran hivatkozunk gy, hogy az elemek a verem-
ben helyezkednek el. Ha felttlenl hangslyozni akarjuk ezt a tpus helyfog-
lalst, a C++-ban az auto kulcsszt hasznlhatjuk.
Szabad tr: Errl a terletrl a program brmikor kzvetlenl ignyelhet memrit
s brmikor felszabadthatja az itt lefoglalt terleteket (a new, illetve a delete
opertor segtsgvel). Amikor a programnak jabb terletekre van szksge
a szabad trbl, a new opertorral ignyelhet az opercis rendszertl. A sza-
bad trra gyakran hivatkozunk dinamikus memria vagy kupac (heap) nven
is. A szabad tr a program teljes lettartama alatt csak nhet, mert az ilyen ter-
leteket a program befejezdsig nem adjuk vissza az opercis rendszernek,
gy ms programok azokat nem hasznlhatjk.
A programoz szemszgbl az automatikus s a statikus tr egyszeren, biztonsgosan s
automatikusan kezelhet. Az rdekes krdst a szabad tr kezelse jelenti. A new segts-
gvel knnyen foglalhatunk terleteket, de ha nincs kvetkezetes stratgink a memria
felszabadtsra (a memria visszaadsra a szabad tr kezeljnek), a memria elbb-
utbb elfogy, fleg ha programunk elg sokig fut.
C. Technikai rszletek 1139
A legegyszerbb mdszer, ha automatikus objektumokat hasznlunk a szabad trban lev
objektumok kezelshez. Ennek megfelelen a trolkat sokszor gy valstjk meg, hogy
a szabad trban lev elemekre mutatkat lltanak (25.7). Az automatikus String-ek
(11.12) pldul a szabad trban lev karaktersorozatokat kezelik s automatikusan felsza-
badtjk a terletet, amikor kikerlnek a hatkrbl. Az sszes szabvnyos trol (16.3, 17.
fejezet, 20. fejezet, 22.4) knyelmesen megvalsthat ilyen formban.
C.9.1. Automatikus szemtgyjts
Ha ez az egyszer megolds nem felel meg ignyeinknek, akkor hasznlhatunk valamilyen
memriakezel rendszert, amely megkeresi az olyan objektumokat, melyekre mr nincs hi-
vatkozs, s az ezek ltal lefoglalt memriaterletet elrhetv teszi j objektumok szm-
ra. Ezt a rendszert ltalban automatikus szemtgyjt algoritmusnak vagy egyszeren sze-
mtgyjtnek (garbage collection) nevezzk.
A szemtgyjts alaptlete az, hogy ha egy objektumra mr sehonnan sem hivatkozunk
a programbl, arra a ksbbiekben sem fogunk, teht az ltala lefoglalt memriaterletet
biztonsgosan felszabadthatjuk vagy odaadhatjuk ms objektumoknak:
void f()
{
int* p = new int;
p = 0;
char* q = new char;
}
Itt a p=0 rtkads trli az egyetlen hivatkozst, ami a p ltal eddig meghatrozott int ob-
jektumra mutatott, gy ezen int rtk terlett odaadhatjuk egy j objektumnak. A char t-
pus objektum teht mr szerepelhet ugyanezen a memriaterleten, ami azt jelenti, hogy
a q ugyanazt az rtket kapja, mint eredetileg a p.
A szabvny nem kveteli meg, hogy minden nyelvi vltozat tartalmazza a szemtgyjtst, de
egyre gyakrabban hasznljk az olyan terleteken, ahol a C++-ban a szabad tr egyni keze-
lse kltsgesebb, mint a szemtgyjts. Amikor a kt mdszer kltsgeit sszehasonltjuk, fi-
gyelembe kell vennnk a futsi idt, a memria-felhasznlst, a megbzhatsgot, a hordoz-
hatsgot, a programozs pnzbeli kltsgeit s a teljestmny megjsolhatsgt is.
Fggelkek s trgymutat 1140
C.9.1.1. Rejtett mutatk
Mikor nevezhetnk egy objektumot hivatkozs nlklinek? Vizsgljuk meg pldul az alb-
bi programrszletet:
void f()
{
int* p = new int;
long i1 = reinterpret_cast<long>(p)&0xFFFF0000;
long i2 = reinterpret_cast<long>(p)&0x0000FFFF;
p = 0;
// #1 pont: nem ltezik az int-re hivatkoz mutat
p = reinterpret_cast<int*>(i1|i2);
// itt az int-re ismt ltezik mutat
}
Ha egy programban bizonyos mutatkat idnknt nem mutatknt trolunk, akkor rejtett
mutatkrl beszlnk. Esetnkben azt a mutatt, amit eredetileg a p vltoz trolt, elrejtet-
tk az i1 s az i2 egszekben. A szemtgyjt rendszertl azonban nem vrhatjuk el, hogy
kezelni tudja a rejtett mutatkat, teht amikor a program a #1 ponthoz r, a szemtgyjt
felszabadtja az int rtk szmra lefoglalt terletet. Valjban az ilyen programok helyes
mkdst mg az olyan rendszerekben sem garantlhatjuk, ahol nem hasznlunk szemt-
gyjt algoritmust, mert a reinterpret_cast hasznlata egszek s mutatk kztti
tpustalaktsra mg a legjobb esetben is megvalsts-fgg.
Az olyan union objektumok, melyek lehetv teszik mutatk s nem mutatk trolst is,
kln problmt jelentenek a szemtgyjtk szmra. ltalban nincs md annak meglla-
ptsra, hogy egy ilyen szerkezet ppen mutatt trol-e:
union U { // uni mutat s nem mutat taggal
int* p;
int i;
};
void f(U u, U u2, U u3)
{
u.p = new int;
u2.i = 999999;
u.i = 8;
// ...
}
C. Technikai rszletek 1141
A biztonsgos felttelezs az, hogy minden rtk mutat, ami egy ilyen union objektum-
ban megjelenik. Egy kellen okos szemtgyjt ennl kicsit jobb megoldst is adhat. Pl-
dul szreveheti, hogy (az adott rendszerben) int rtkek nem helyezhetk el pratlan me-
mriacmen s semmilyen objektumot nem hozhatunk ltre olyan kicsi memriacmen,
mint a 8. Ezek figyelembevtelvel a szemtgyjt algoritmusnak nem kell feltteleznie,
hogy a 999999 vagy a 8 cmen rtelmes objektum szerepel, amit az f() esetleg felhasznl.
C.9.1.2. A delete
Ha rendszernk automatikus szemtgyjtst hasznl, a memria felszabadtshoz nincs
szksg a delete s a delete[] opertorokra, teht a szemtgyjtst hasznl programozk-
nak nem kell ezeket hasznlniuk. A memria felszabadtsn kvl azonban a delete s
a delete[] destruktorok meghvsra is hasznlatos.
Ha szemtgyjt algoritmust hasznlunk, a
delete p;
utasts meghvja a p ltal mutatott objektum destruktort (ha van ilyen), viszont a mem-
ria jrafelhasznlst elhalaszthatjuk addig, amg a memriaterletre szksg lesz. Ha sok
objektumot egyszerre szabadtunk fel, cskkenthetjk a memria feltredezdsnek mr-
tkt (C.9.1.4) Ez a megolds rtalmatlann teszi azt az egybknt rendkvl veszlyes hi-
bt is, hogy ugyanazt az objektumot ktszer semmistjk meg, amikor a destruktor csak
a memria trlst vgzi.
Az olyan objektumok elrse, melyeket mr trltnk, szoks szerint nem meghatrozhat
eredmnnyel jr.
C.9.1.3. Destruktorok
Amikor a szemtgyjt algoritmus egy objektumot meg akar semmisteni, kt lehetsg k-
zl vlaszthat:
1. Meghvja az adott objektum destruktort (ha az ltezik).
2. Felttelezi, hogy nyers memriaterletrl van sz s nem hv meg destruktort.
Alaprtelmezs szerint a szemtgyjt algoritmus a msodik lehetsget hasznlja, mert
a new segtsgvel ltrehozott, de a delete opertorral nem trlt objektumokat nem kell
megsemmistennk. Teht a szemtgyjt algoritmust bizonyos szempontbl vgtelen m-
Fggelkek s trgymutat 1142
ret memria utnzsnak tekinthetjk. Lehetsg van arra is, hogy a szemtgyjt algorit-
must gy valstsuk meg, hogy a destruktor az algoritmus ltal bejegyzett objektumokra
fusson le. A bejegyzsre azonban nincs szabvnyos megolds. Figyeljnk arra, hogy az
objektumokat mindig olyan sorrendben kell megsemmistennk, hogy az egyik objektum
destruktora soha ne hivatkozzon olyan objektumra, amelyet korbban mr trltnk.
A programoz segtsge nlkl ilyen sorrendet a szemtgyjt algoritmusok nagyon ritkn
kpesek betartani.
C.9.1.4. A memria feltredezdse
Ha sok klnbz mret objektumot hozunk ltre, illetve semmistnk meg, a memria
feltredezdik. Ez azt jelenti, hogy a memrit olyan kis darabokban hasznljuk, amelyek
tl kicsik ahhoz, hogy hatkonyan kezelhetk legyenek. Ennek oka az, hogy a memriake-
zel rendszerek nem mindig tallnak pontosan olyan mret memriadarabot, amilyenre
egy adott objektumnak ppen szksge van. Ha a kelletnl nagyobb terletet hasznlunk,
a megmarad memriatredk mg kisebb lesz. Ha egy ilyen egyszer memriakezelvel
programunk elg sokig fut, nem ritka, hogy akr a rendelkezsre ll memria felt olyan
memriatredkek tltik ki, melyek tl kis mretek ahhoz, hogy hasznlhatk legyenek.
A memria-feltredezds problmjnak kezelsre szmos mdszert dolgoztak ki. A leg-
egyszerbb megolds, hogy csak nagyobb memriadarabok lefoglalst tesszk lehetv
s minden ilyen darabban azonos mret objektumokat trolunk (15.3, 19.4.2). Mivel
a legtbb helyfoglalst kis mret objektumok ignylik (pldul a trolk elemei), ez
a mdszer nagyon hatkony lehet. Az eljrst akr egy nll memriafoglal is automati-
kusan megvalsthatja. A memriatredezdst mindkt esetben tovbb cskkenthetjk,
ha az sszes nagyobb memriadarabot azonos mretre (pldul a lapmretre) lltjuk, gy
ezek lefoglalsa sem okoz tredezdst.
A szemtgyjt rendszerek kt f tpusba tartoznak:
1. A msol szemtgyjtk az objektumokat thelyezik a memriban, ezzel egye-
stik a feltredezett memria darabjait.
2. A konzervatv szemtgyjtk gy prblnak helyet foglalni az objektumoknak,
hogy a memriatredezds a lehet legkisebb legyen.
A C++ szemszgbl a konzervatv szemtgyjtk a gyakoribbak, mert rendkvl nehz
(st a komoly programokban valsznleg lehetetlen) az objektumokat gy thelyezni,
hogy minden mutatt helyesen tlltsunk. A konzervatv szemtgyjtk azt is lehetv te-
szik, hogy a C++ programrszletek egyttmkdjenek akr a C nyelven kszlt program-
C. Technikai rszletek 1143
rszletekkel is. A msol szemtgyjtket tbbnyire csak olyan nyelvekben valstjk meg,
melyek az objektumokat mindig kzvetve mutatkon vagy hivatkozsokon keresz-
tl rik el. (Ilyen nyelv pldul a Lisp vagy a Smalltalk.) Az olyan nagyobb programok
esetben, melyeknl a memriafoglal s a lapkezel rendszer kztti kapcsolattarts, illet-
ve a msols mennyisge jelents, az jabb konzervatv szemtgyjtk legalbb olyan ha-
tkonynak tnnek, mint a msol szemtgyjtk. Kisebb programok esetben gyakran az
a legjobb megolds, ha egyltaln nem hasznlunk szemtgyjtt fleg a C++ esetben,
ahol az objektumok tbbsgt termszetszerleg automatikusan kezelhetjk.
C.10. Nvterek
Ebben a pontban olyan aprsgokat vizsglunk meg a nvterekkel kapcsolatban, melyek
csupn technikai rszleteknek tnnek, de a vitk sorn, illetve a komoly programokban
gyakran felsznre kerlnek.
C.10.1. Knyelem s biztonsg
A using deklarcik egy j nevet vezetnek be a helyi hatkrbe, a using utastsok (direk-
tvk) azonban nem gy mkdnek, csak elrhetv teszik a megadott hatkrben szerep-
l neveket:
namespace X {
int i, j, k;
}
int k;
void f1()
{
int i = 0;
using namespace X; // az X-beli nevek elrhetv ttele
i++; // helyi i
j++; // X::j
k++; // hiba: X::k vagy globlis k ?
::k++; // a globlis k
X::k++; // az X-beli k
}
Fggelkek s trgymutat 1144
void f2()
{
int i = 0;
using X::i; // hiba: i ktszer deklarlt f2()-ben
using X::j;
using X::k; // elfedi a globlis k nevet
i++;
j++; // X::j
k++; // X::k
}
A helyileg (nll deklarcival vagy using deklarcival) bevezetett nevek elrejtik az
ugyanilyen nven szerepl nem helyi neveket; a fordt pedig minden hibs tlterhelst
a deklarci helyn jelez.
Figyeljk meg az f1() fggvnyben a k++ utasts ktrtelmsgt. A globlis nevek nem
lveznek elsbbsget azokkal a nevekkel szemben, melyek globlis hatkrben elrhet
nvterekbl szrmaznak. Ez jelents mrtkben cskkenti a vletlen nvkeveredsek lehe-
tsgt s biztostja, hogy ne a globlis nvtr beszennyezsvel jussunk elnykhz.
Amikor using utastsok segtsgvel olyan knyvtrakat tesznk elrhetv, melyek sok
nevet vezetnek be, komoly knnytst jelent, hogy a felhasznlatlan nevek tkzse nem
okoz hibt.
A globlis nvtr ugyanolyan, mint brmelyik msik, tlagos nvtr. A globlis nvtr egyet-
len klnlegessge, hogy egy r vonatkoz explicit minsts esetben nem kell kirnunk
a nevt. Teht a ::k jells azt jelenti, hogy keresd meg a k nevet a globlis nvtrben vagy
a globlis nvtrhez using utastssal csatolt nvterekben, mg az X::k jelentse: keresd
meg a k nevet az X nvtrben, vagy a benne using utastssal megemltett nvterekben
(8.2.8).
Remljk, hogy a nvtereket hasznl j programokban radiklisan cskkenni fog a glob-
lis nevek szma a hagyomnyos C s C++ programokhoz viszonytva. A nvterek szablya-
it kifejezetten gy talltk ki, hogy ne adjanak elnyket a globlis nevek lusta felhaszn-
linak azokkal szemben, akik vigyznak r, hogy a globlis hatkrt ne szennyezzk
ssze.
C. Technikai rszletek 1145
C.10.2. Nvterek egymsba gyazsa
A nvterek egyik nyilvnval felhasznlsi terlete az, hogy deklarcik s defincik vala-
milyen rtelemben zrt halmazt egyetlen egysgbe fogjuk ssze:
namespace X {
// sajt deklarcik
}
A deklarcik listja ltalban tartalmazni fog jabb nvtereket is. Teht gyakorlati okokbl
is szksg van a nvterek egymsba gyazsra, azon egyszer ok mellett, hogy ha valami-
lyen rendszerben nem kifejezetten kptelensg az egymsba gyazs megvalstsa, akkor
illik azt lehetv tennnk:
void h();
namespace X {
void g();
// ...
namespace Y {
void f();
void ff();
// ...
}
}
Az albbi jellsek a szoksos, hatkrkre vonatkoz, illetve minstsi szablyokbl
kvetkeznek:
void X::Y::ff()
{
f(); g(); h();
}
void X::g()
{
f(); // hiba: nincs f() X-ben
Y::f(); // rendben
}
void h()
{
f(); // hiba: nincs globlis f()
Y::f(); // hiba: nincs globlis Y
X::f(); // hiba: nincs f() X-ben
X::Y::f(); // rendben
}
Fggelkek s trgymutat 1146
C.10.3. Nvterek s osztlyok
A nvterek elnevezett hatkrk, az osztlyok pedig tpusok, melyeket egy elnevezett ha-
tkrrel hatrozunk meg, ami azt rja le, hogy a tpus objektumait hogyan kell ltrehozni s
kezelni. Teht a nvtr egyszerbb fogalom, mint az osztly, s idelis esetben az osztlyo-
kat gy is meghatrozhatjuk, mint tovbbi lehetsgeket biztost nvtereket. A meghat-
rozs azonban csak majdnem pontos, ugyanis a nvtr nyitott szerkezet (8.2.9.3), mg az
osztly teljesen zrt. A klnbsg abbl addik, hogy az osztlyoknak objektumok szerke-
zett kell lerniuk, ez pedig nem engedi meg azt a szabadsgot, amit a nvterek nyjtanak.
Ezenkvl a using deklarcik s using utastsok csak nagyon korltozott esetekben hasz-
nlhatk az osztlyokra (15.2.2).
Ha csak a nevek egysgbe zrsra van szksgnk, a nvterek jobban hasznlhatk, mint
az osztlyok, mert ekkor nincs szksgnk az osztlyok komoly tpusellenrzsi lehets-
geire s az objektumksztsi mdszerekre; az egyszerbb nvtr-kezelsi elvek is elegen-
dek.
C.11. Hozzfrs-szablyozs
Ebben a pontban a hozzfrs-szablyozs olyan vonatkozsaira mutatunk pldkat, me-
lyekrl a 15.3 pontban nem volt sz.
C.11.1. Tagok elrse
Vizsgljuk meg az albbi deklarcit:
class X {
// az alaprtelmezs privt:
int priv;
protected:
int prot;
public:
int publ;
void m();
};
C. Technikai rszletek 1147
Az X::m() fggvnynek korltlan hozzfrse van a tagokhoz:
void X::m()
{
priv = 1; // rendben
prot = 2; // rendben
publ = 3; // rendben
}
A szrmaztatott osztlyok tagjai a public s protected tagokat rhetik el (15.3):
class Y : public X {
void mderived();
};
void Y::mderived()
{
priv = 1; // hiba: priv privt
prot = 2; // rendben: prot vdett s mderived() az Y szrmaztatott osztly tagja
publ = 3; // rendben: publ nyilvnos
}
A globlis fggvnyek csak a nyilvnos tagokat ltjk:
void f(Y* p)
{
p->priv = 1; // hiba: priv privt
p->prot = 2; // hiba: prot vdett s f() se nem bart, se nem X vagy Y tagja
p->publ = 3; // rendben: publ nyilvnos
}
C.11.2. Alaposztlyok elrse
A tagokhoz hasonlan az alaposztlyokat is bevezethetjk private, protected vagy public
minstssel:
class X {
public:
int a;
// ...
};
class Y1 : public X { };
class Y2 : protected X { };
class Y3 : private X { };
Fggelkek s trgymutat 1148
Mivel az X nyilvnos alaposztlya az Y1 osztlynak, brmely fggvny szabadon (s auto-
matikusan) talakthat egy Y1* rtket X* tpusra s elrheti az X osztly nyilvnos tagjait is:
void f(Y1* py1, Y2* py2, Y3* py3)
{
X* px = py1; // rendben: X nyilvnos alaposztlya Y1 osztlynak
py1->a = 7; // rendben
px = py2; // hiba: X vdett alaposztlya Y2 osztlynak
py2->a = 7; // hiba
px = py3; // hiba: X privt alaposztlya Y3 osztlynak
py3->a = 7; // hiba
}
Vegyk az albbi deklarcikat:
class Y2 : protected X { };
class Z2 : public Y2 { void f(Y1*, Y2*, Y3*); };
Mivel X vdett alaposztlya Y2-nek, csak Y2 tagjai s bart (friend) osztlyai, fggvnyei, il-
letve Y2 leszrmazottainak (pldul Z2-nek) tagjai s ezek bart elemei alakthatjk t (au-
tomatikusan) az Y2* tpus rtket X*-ra, s csak ezek frhetnek hozz az X osztly public
s protected tagjaihoz:
void Z2::f(Y1* py1, Y2* py2, Y3* py3)
{
X* px = py1; // rendben: X nyilvnos alaposztlya Y1 osztlynak
py1->a = 7; // rendben
px = py2; // rendben: X vdett alaposztlya Y2-nek,
// s Z2 osztly Y2-bl szrmazik
py2->a = 7; // rendben
px = py3; // hiba: X privt alaposztlya Y3 osztlynak
py3->a = 7; // hiba
}
Vgl vizsgljuk meg az albbit:
class Y3 : private X { void f(Y1*, Y2*, Y3*); };
C. Technikai rszletek 1149
Az X az Y3-nak privt alaposztlya, gy kizrlag Y3 tagjai s bartai alakthatjk t (auto-
matikusan) az Y3* rtkeket X*-ra, s csak azok rhetik el az X nyilvnos s vdett tagjait:
void Y3::f(Y1* py1, Y2* py2, Y3* py3)
{
X* px = py1; // rendben: X nyilvnos alaposztlya Y1 osztlynak
py1->a = 7; // rendben
px = py2; // hiba: X vdett alaposztlya Y2 osztlynak
py2->a = 7; // hiba
px = py3; // rendben: X privt alaposztlya Y3-nak, s Y3::f() Y3 tagja
py3->a = 7; // rendben
}
C.11.3. Tagosztlyok elrse
A tagosztlyok tagjainak nincs klnleges jogosultsguk a befoglal osztly tagjainak elr-
sre s ugyangy a befoglal osztly tagjai sem rendelkeznek klnleges hozzfrssel a be-
gyazott osztly tagjaihoz. Minden a szoksos szablyok (10.2.2) szerint mkdik:
class Outer {
typedef int T;
int i;
public:
int i2;
static int s;
class Inner {
int x;
T y; // hiba: Outer::T privt
public:
void f(Outer* p, int v);
};
int g(Inner* p);
};
void Outer::Inner::f(Outer* p, int v)
{
p->i = v; // hiba: Outer::i privt
p->i2 = v; // rendben: Outer::i2 nyilvnos
}
Fggelkek s trgymutat 1150
int Outer::g(Inner* p)
{
p->f(this,2); // rendben: Inner::f() nyilvnos
return p->x; // hiba: Inner::x privt
}
Ennek ellenre gyakran hasznos, ha a begyazott osztly elrheti a begyaz osztly tagja-
it. Ezt gy rhetjk el, ha a tagosztlyt friend minstssel vezetjk be:
class Outer {
typedef int T;
int i;
public:
class Inner; // a tagosztly elzetes bevezetse
friend class Inner; // elrsi jog Outer::Inner szmra
class Inner {
int x;
T y; // rendben: Inner egy "bart"
public:
void f(Outer* p, int v);
};
};
void Outer::Inner::f(Outer* p, int v)
{
p->i = v; // rendben: Inner egy "bart"
}
C.11.4. Bartok
A friend minsts hatsa nem rkldik s nem is tranzitv:
class A {
friend class B;
int a;
};
class B {
friend class C;
};
C. Technikai rszletek 1151
class C {
void f(A* p)
{
p->a++; // hiba: C nem bartja A-nak, br A bartjnak bartja
}
};
class D : public B {
void f(A* p)
{
p->a++; // hiba: D nem bartja A-nak, br A bartjbl szrmazik
}
};
C.12. Adattagokra hivatkoz mutatk
Termszetesen a tagokra hivatkoz mutatk (15.5) fogalma az adattagokra, valamint a rg-
ztett paramterekkel s visszatrsi rtkkel rendelkez fggvnyekre vonatkozik:
struct C {
const char* val;
int i;
void print(int x) { cout << val << x << '\n'; }
int f1(int);
void f2();
C(const char* v) { val = v; }
};
typedef void (C::*PMFI)(int); // C osztly egy int paramterrel meghvhat
// tagfggvnyre hivatkoz mutat
typedef const char* C::*PM; // C osztly egy char* tpus adattagjra
// hivatkoz mutat
void f(C& z1, C& z2)
{
C* p = &z2;
PMFI pf = &C::print;
PM pm = &C::val;
z1.print(1);
(z1.*pf)(2);
Fggelkek s trgymutat 1152
z1.*pm = "nv1 ";
p->*pm = "nv2 ";
z2.print(3);
(p->*pf)(4);
pf = &C::f1; // hiba: nem megfelel visszatrsi tpus
pf = &C::f2; // hiba: nem megfelel paramtertpus
pm = &C::i; // hiba: nem megfelel tpus
pm = pf; // hiba: nem megfelel tpus
}
A fggvnyekre hivatkoz mutatk tpust a rendszer ugyangy ellenrzi, mint brmely
ms tpust.
C.13. Sablonok
A sablon osztlyok azt hatrozzk meg, hogyan hozhat ltre egy osztly, ha a szksges
sablonparamtereket megadjuk. A sablon fggvnyek ehhez hasonlan azt rgztik, ho-
gyan kszthetnk el egy konkrt fggvnyt a megadott sablonparamterekkel. Teht a sab-
lonok tpusok s futtathat kdrszletek ltrehozshoz hasznlhatk fel. Ez a nagy kifeje-
zer azonban nhny bonyodalmat okoz. A legtbb problma abbl addik, hogy ms
krnyezet ll rendelkezsnkre a sablon meghatrozsakor s felhasznlsakor.
C.13.1. Statikus tagok
Az osztlysablonoknak is lehetnek static tagjaik. Ezekbl a tagokbl minden, a sablonbl
ltrehozott osztly sajt pldnnyal rendelkezik. A statikus tagokat kln kell meghatroz-
nunk, de egyedi cl vltozatokat is kszthetnk bellk:
template<class T> class X {
// ...
static T def_val;
static T* new_X(T a = def_val);
};
template<class T> T X<T>::def_val(0,0);
template<class T> T* X<T>::new_X(T a) { /* ... */ }
C. Technikai rszletek 1153
template<> int X<int>::def_val<int> = 0;
template<> int* X<int>::new_X<int>(int i) { /* ... */ }
Ha egy objektumot vagy fggvnyt a sablonbl ltrehozott sszes osztly sszes pldny-
ban kzsen szeretnnk hasznlni, bevezethetnk egy alaposztlyt, amely nem sablon:
struct B {
static B* nil; // kzs nullpointer minden B-bl szrmaztatott osztlynak
};
template<class T> class X : public B {
// ...
};
B* B::nil = 0;
C.13.2. Bartok s sablonok
Ms osztlyokhoz hasonlan a sablon is tartalmazhat friend osztlyokat s fggvnyeket.
Vizsgljuk meg pldul a 11.5 pontban szerepl Matrix s Vector osztlyt. ltalban mind-
kt osztlyt sablonknt valstjuk meg:
template<class T> class Matrix;
template<class T> class Vector {
T v[4];
public:
friend Vector operator* <> (const Matrix<T>&, const Vector&);
// ...
};
template<class T> class Matrix {
Vector<T> v[4];
public:
friend Vector<T> operator* <> (const Matrix&, const Vector<T>&);
// ...
};
A bart fggvny neve utn szerepl <> jel nem hagyhat el, mert ez jelzi, hogy a bart egy
sablon fggvny. A <> jel nlkl a rendszer nem sablon fggvnyt felttelezne. A fenti be-
vezets utn a szorzs opertort gy hatrozhatjuk meg, hogy a Vector s a Matrix tagjaira
kzvetlenl hivatkozunk:
Fggelkek s trgymutat 1154
template<class T> Vector<T> operator* <> (const Matrix<T>&, const Vector<T>&)
{
// ... m.vi[i] s v.v[i] hasznlata az elemek kzvetlen elrshez
}
A bart nincs hatssal arra a hatkrre, melyben a sablon osztlyt meghatroztuk, sem ar-
ra, melyben a sablon osztlyt felhasznljuk. Ehelyett a bart fggvnyeket s opertorokat
paramtertpusaik alapjn tallja meg a rendszer (11.2.4, 11.5.1). A tagfggvnyekhez ha-
sonlan a bart fggvnyekbl is csak akkor kszl pldny, ha meghvjuk azokat.
C.13.3. Sablonok, mint sablonparamterek
Gyakran hasznos, ha sablonparamterknt osztlyok vagy objektumok helyett sablonokat
adunk t:
template<class T, template<class> class C> class Xrefd {
C<T> mems;
C<T*> refs;
// ...
};
Xrefd<Entry,vector> x1; // az Entry kereszthivatkozsokat vektorban troljuk
Xrefd<Record,set> x2; // a Record kereszthivatkozsokat halmazban troljuk
Ahhoz, hogy egy sablont sablonparamterknt hasznljunk, meg kell adnunk az ltala vrt
paramtereket, a paramterknt hasznlt sablon sablonparamtereit viszont nem kell is-
mernnk. Sablonokat ltalban akkor hasznlunk sablonparamterknt, ha klnbz pa-
ramtertpusokkal akarjuk azokat pldnyostani (fent pldul a T s a T* tpussal). Teht
a sablon tagjainak deklarciit egy msik sablon fogalmaival fejezzk ki, de ez utbbi sab-
lont paramterknt szeretnnk megadni, hogy a felhasznl vlaszthassa meg tpust.
Ha a sablonnak egy trolra van szksge azon elemek trolshoz, melyek tpust sablon-
paramterknt kapja meg, gyakran egyszerbb a troltpust tadni (13.6, 17.3.1).
Sablonparamterek csak sablon osztlyok lehetnek.
C. Technikai rszletek 1155
C.13.4. Sablon fggvnyek paramtereinek levezetse
A fordt kpes arra, hogy levezessen egy tpus- (T vagy TT) vagy nem tpus sablonparam-
tert (I) egy olyan fggvnysablon-paramterbl, melyet a kvetkez szerkezetek valamelyi-
kvel lltottunk el:
T const T volatile T
T* T& T[konstans_kifejezs]
tpus[I] osztlysablon_nv<T> osztlysablon_nv<I>
TT<T> T<I> T<>
T tpus::* T T::* tpus T::*
T (*)(paramterek) tpus (T::*)(paramterek) T (tpus::*)(paramterek)
tpus (tpus::*)(paramterek_TI) T (T::*)(paramterek_TI) tpus (T::*)(paramterek_TI)
T (tpus::*)(paramterek_TI) tpus (*)(paramterek_TI)
Ebben a gyjtemnyben a param_T1 egy olyan paramterlista, melybl egy T vagy egy I
meghatrozhat a fenti szablyok tbbszri alkalmazsval, mg a param egy olyan para-
mterlista, amely nem tesz lehetv kvetkeztetst. Ha nem minden paramter kvetkez-
tethet ki ezzel a megoldssal, a hvs tbbrtelmnek minsl:
template<class T, class U> void f(const T*, U(*)(U));
int g(int);
void h(const char* p)
{
f(p,g); // T char, U int
f(p,h); // hiba: U nem vezethet le
}
Ha megnzzk az f() els hvsnak paramtereit, knnyen kikvetkeztethetjk a sablon-
paramtereket. Az f() msodik meghvsban viszont lthatjuk, hogy a h() nem illeszthet
az U(*)(U) mintra, mert paramtere s visszatrsi rtke klnbz.
Ha egy sablonparamter egynl tbb fggvnyparamterbl is kikvetkeztethet, akkor
mindegyiknek ugyanazt az eredmnyt kell adnia, ellenkez esetben hibazenetet kapunk:
template<class T> void f(T i, T* p);
void g(int i)
{
f(i,&i); // rendben
f(i,"Vigyzat!"); // hiba, tbbrtelm: T int vagy T const char?
}
Fggelkek s trgymutat 1156
C.13.5. A typename s a template
Az ltalnostott (generikus) programozs tovbbi egyszerstse s mg ltalnosabb t-
tele rdekben a standard knyvtr troli szabvnyos fggvnyeket s tpusokat biztosta-
nak (16.3.1):
template<class T> class vector {
public:
typedef T value_type;
typedef T* iterator;
iterator begin();
iterator end();
// ...
};
template<class T> class list {
class link { /* ... */ };
public:
typedef link* iterator;
iterator begin();
iterator end();
// ...
};
Ez arra sztnz minket, hogy az albbi formt hasznljuk:
template<class C> void f(C& v)
{
C::iterator i = v.begin(); // formai hiba
}
Sajnos a fordt nem gondolatolvas, gy nem tudja, hogy a C::iterator egy tpus neve. Bi-
zonyos esetekben egy okosabb fordt kitallhatja, hogy egy nevet tpusnvnek szntunk-
e vagy valami egszen msnak (pldul fggvny- vagy sablonnvnek), de ez ltalban
nem lehetsges. Figyeljk meg pldul az albbit:
int y;
template<class T> void g(T& v)
{
T::x(y); // fggvnyhvs vagy vltoz-deklarci?
}
C. Technikai rszletek 1157
Felttlenl igaz-e, hogy ez esetben a T::x egy fggvny, melyet az y paramterrel hvtunk
meg? Ugyanezt a sort tekinthetjk az y vltoz deklarcijnak is, ahol a T::x egy tpus s
a zrjelek csak felesleges s termszetellenes (de nem tiltott) jelek. Teht el tudunk kp-
zelni olyan krnyezetet, melyben X::x(y) egy fggvnyhvs, mg az Y::x(y) egy deklarci.
A megolds rendkvl egyszer: ha mst nem mondunk, az azonostkrl a rendszer azt fel-
ttelezi, hogy olyasvalamire hivatkoznak, ami nem tpus s nem sablon. Ha azt akarjuk kife-
jezni, hogy egy azonostt tpusknt kell rtelmezni, a typename kulcsszt kell hasznlnunk:
template<class C> void h(C& v)
{
typename C::iterator i = v.begin();
// ...
}
A typename kulcsszt a minstett nv el rva azt fejezhetjk ki, hogy a megadott elem egy
tpus. Ebbl a szempontbl szerepe hasonlt a struct s a class szerepre.
A typename kulcsszra mindig szksg van, ha a tpus neve egy sablonparamtertl fgg:
void k(vector<T>& v)
{
vector<T>::iterator i = v.begin(); // formai hiba: a "typename" hinyzik
typename vector<T>::iterator i = v.begin(); // rendben
// ...
}
Ebben az esetben a fordt esetleg kpes lenne megllaptani, hogy az iterator a vector sab-
lon minden pldnyosztlyban egy tpusnevet ad meg, de a szabvny ezt nem kveteli
meg. Teht, ha egy fordt ilyesmire kpes, akkor az nem szabvnyos szolgltats s nem
tekinthet hordozhat nyelvkiterjesztsnek. Az egyetlen krnyezet, ahol a fordtnak felt-
teleznie kell, hogy egy sablon-paramtertl fgg azonost tpusnv, az a nhny helyzet,
amikor a nyelvtan csak tpusneveket enged meg. Ilyenek pldul az alap-meghatrozsok
(A.8.1).
A sablondeklarcikban a typename a class kulcssz szinonimjaknt is hasznlhat:
template<typename T> void f(T);
Mivel a kt vltozat kztt nincs rdemi klnbsg, de a kperny mindig kicsinek bizo-
nyul, n a rvidebb vltozatot ajnlom:
template<class T> void f(T);
Fggelkek s trgymutat 1158
C.13.6. Sablonok, mint minstk
A typename kulcssz bevezetsre azrt volt szksg, mert hivatkozhatunk olyan adatta-
gokra is, melyek tpusok, s olyanokra is, melyek nem azok. Ugyanilyen problmt jelent
a sablontagok nevnek megklnbztetse ms tagoktl. Vizsgljuk meg pldul egy l-
talnos memriakezel osztly egy lehetsges fellett:
class Memory { // valamilyen memriafoglal
public:
template<class T> T* get_new();
template<class T> void release(T&);
// ...
};
template<class Allocator> void f(Allocator& m)
{
int* p1 = m.get_new<int>(); // formai hiba: 'int' a 'kisebb mint' opertor utn
int* p2 = m.template get_new<int>(); // explicit minsts
// ...
m.release(p1); // sablonparamter levezetse: nem kell explicit minst
m.release(p2);
}
A get_new()fggvny explicit minstsre van szksg, mert a sablonparamtert nem le-
het levezetni. Esetnkben a template eltagra van szksg ahhoz, hogy a fordtnak (s az
emberi olvasnak) elruljuk, hogy a get_new() egy sablontag, teht a minsts lehetsges
a megadott elemtpussal. A template minsts nlkl formai hibt kapunk, mert a < jelet
a fordt kisebb, mint opertorknt prblja meg rtelmezni. A template kulcsszval val
minstsre ritkn van szksg, mert a legtbb esetben a sablonparamtereket a fordt ki
tudja kvetkeztetni.
C.13.7. Pldnyosts
Ha adott egy sablon-meghatrozs s ezt a sablont hasznlni szeretnnk, a fordt feladata,
hogy a megfelel programkdot ellltsa. Egy sablon osztlybl s a megadott sablonpa-
ramterekbl a fordtnak el kell lltania egy osztly meghatrozst s sszes olyan tag-
fggvnynek defincijt, melyet programunkban felhasznltunk. Egy sablon fggvny
esetben a megfelel fggvnyt kell ellltani. Ezt a folyamatot nevezzk a sablon pld-
nyostsnak.
C. Technikai rszletek 1159
A ltrehozott osztlyokat s fggvnyeket egyedi cl (szakostott) vltozatoknak
(specializciknak) nevezzk. Ha meg akarjuk klnbztetni a fordt ltal automatikusan
ltrehozott vltozatokat a programoz ltal megadottaktl (13.5), akkor fordti (generlt)
specializcikrl, illetve explicit specializcikrl beszlnk. Az explicit specializcikat
nha felhasznli specializciknak nevezzk.
Ahhoz, hogy a sablonokat bonyolultabb programokban is hatkonyan hasznlhassuk, meg
kell rtennk, hogy a sablon-meghatrozsokban szerepl neveket hogyan kti a fordt
konkrt deklarcikhoz s hogyan tudjuk forrsprogramunkat rendszerezni (13.7).
Alaprtelmezs szerint a fordt olyan sablonokbl hoz ltre osztlyokat s fggvnyeket,
melyeket a szoksos nvktsi szablyok szerint hasznltunk (C.13.8). Ez azt jelenti, hogy
a programoznak nem kell megmondania, mely sablonok mely vltozatait kell elkszteni.
Ez azrt fontos, mert a programoz ltalban nagyon nehezen tudja megmondani, hogy
a sablonoknak pontosan mely vltozataira is van szksge. A knyvtrak gyakran olyan
sablonokat hasznlnak, melyekrl a programoz mg nem is hallott, st az sem ritka, hogy
egy, a programoz ltal egyltaln nem ismert sablont annak szmra szintn teljesen isme-
retlen sablonparamterekkel pldnyostunk. ltalban ahhoz, hogy megkeressk az
sszes olyan fggvnyt, melynek kdjt a fordtnak el kell lltania, az alkalmazsban s
a knyvtrakban hasznlt sablonok tbbszri tvizsglsra van szksg. Az ilyen vizsgla-
tok sokkal jobban illenek a szmtgphez, mint a programozhoz.
Ennek ellenre bizonyos helyzetekben fontos, hogy a programoz pontosan megmondhas-
sa, a fordtnak hol kell elhelyeznie a sablonbl ltrehozott programrszleteket (C.13.10).
Ezzel a programoz pontosan szablyozhatja a pldny krnyezett. A legtbb fordtsi
krnyezetben ez azt is jelenti, hogy pontosan rgztjk, mikor jjjn ltre a pldny.
Az explicit pldnyosts pldul jl hasznlhat arra, hogy a fordtsi hibkat megjsolha-
t helyen s idben kapjuk, ne pedig ott s akkor, amikor az adott fordt gy dnt, hogy
ltre kell hoznia egy pldnyt. Bizonyos krlmnyek kztt a tkletesen megjsolhat
programkd-ltrehozs elengedhetetlenl fontos lehet.
C.13.8. Nvkts
Nagyon fontos, hogy a sablon fggvnyeket gy hatrozzuk meg, hogy a lehet legkisebb
mrtkben fggjenek nem helyi adatoktl. Ennek oka az, hogy a sablonbl ismeretlen tpu-
sok alapjn, ismeretlen helyen fog a fordt fggvnyt vagy osztlyt ltrehozni. Minden ap-
r krnyezetfggsg eslyes arra, hogy a programoz szmra tesztelsi problmaknt je-
lentkezzen, pedig a programoz valsznleg egyltaln nem is akar tudni a sablon megva-
lstsnak rszleteirl. Az az ltalnos szably, miszerint amennyire csak lehet, el kell ke-
Fggelkek s trgymutat 1160
rlnnk a globlis nevek hasznlatt, a sablonok esetben mg elengedhetetlenebb. Ezrt
a sablon-meghatrozsokat prbljuk annyira nllv tenni, amennyire csak lehet, s min-
den olyan elemet, amelyet egybknt esetleg globlis eszkzkkel valstannk meg, sab-
lonparamterknt adjunk t (pl. traits a 13.4 s a 20.2.1 pontban).
Ennek ellenre knytelenek vagyunk nhny nem helyi nevet is felhasznlni. Sokkal gyak-
rabban ksztnk pldul egymssal egyttmkd sablon fggvnyeket, mint egyetlen
nagy, nll eljrst. Nha ezek a fggvnyek jl sszefoghatk egy osztlyba, de nem min-
dig. Idnknt a nem helyi fggvnyek hasznlata mellett dntnk. Jellemz plda erre
a sort() fggvnyben szerepl swap() s less() eljrshvs (13.5.2). A standard knyvtr al-
goritmusai nagymret pldaknt tekinthetk (18. fejezet).
A hagyomnyos nvvel s szereppel megvalstott fggvnyek (pldul a +, a *, a [] vagy
a sort()) egy ms jelleg forrst jelentenek a sablon-meghatrozsokban lev nem helyi ne-
vek hasznlatra. Vizsgljuk meg pldul az albbi programrszletet:
#include<vector>
bool tracing;
// ...
template<class T> T sum(std::vector<T>& v)
{
T t = 0;
if (tracing) cerr << "sum(" << &v << ")\n";
for (int i = 0; i<v.size(); i++) t = t + v[i];
return t;
}
// ...
#include<quad.h>
void f(std::vector<Quad>& v)
{
Quad c = sum(v);
}
Az rtatlan kinzet sum() sablon fggvny a + opertortl fgg. Pldnkban a + mvele-
tet a <quad.h> fejllomny hatrozza meg:
Quad operator+(Quad,Quad);
C. Technikai rszletek 1161
Fontos, hogy a sum() kifejtsekor semmilyen, a komplex szmokhoz kapcsold elem nem
elrhet s a sum() fggvny megalkotja semmit sem felttelezhet a Quad osztlyrl. gy
knnyen elfordulhat pldul, hogy a + opertort jval ksbb hatrozzuk meg, mint
a sum() eljrst, mind a program szvegben, mind idben.
Azt a folyamatot, melynek sorn a fordt megkeresi a sablonban elfordul nevek dekla-
rcijt, nvktsnek (name binding) nevezzk. A sablonok nvktsnl a legnagyobb
problmt az jelenti, hogy pldnyostskor hrom klnbz krnyezetet kell figyelembe
venni s ezek nem vlaszthatk el egymstl pontosan. A hrom krnyezet a kvetkez:
1. A sablon meghatrozsnak krnyezete
2. A paramtertpus bevezetsnek krnyezete
3. A sablon felhasznlsi helynek krnyezete
C.13.8.1. Fgg nevek
Amikor egy fggvny sablont meghatrozunk, biztosak szeretnnk lenni abban, hogy ren-
delkezsnkre ll a megfelel krnyezet, melyben az aktulis paramterek fogalmaival el
tudjuk vgezni az adott feladatot, anlkl, hogy a felhasznlsi krnyezet elemeit vletle-
nl beptennk az eljrsba. A nyelv ennek megvalstst azzal segti, hogy a sablon de-
fincijban felhasznlt neveket kt kategriba osztja:
1. A sablonparamterektl fgg nevek. Ezeket a neveket a pldnyosts helyn
kti le a fordt (C.13.8.3). A sum() pldafggvnyben a + opertor meghatro-
zsa a pldnyostsi krnyezetben tallhat, mert operandusaiban felhasznlja
a sablon tpusparamtert.
2. A sablonparamterektl fggetlen nevek. Ezeket a neveket a sablon meghatro-
zsnl kti le a fordt (C.13.8.2). A sum() pldafggvnyben a vector sablont
a <vector> szabvnyos fejllomny hatrozza meg, a logikai tracing vltoz pe-
dig akkor is a hatkrben van, amikor a fordt tallkozik a sum() fggvny
meghatrozsval.
Az N fgg a T sablonparamtertl kifejezs legegyszerbb defincija az lenne, hogy N
tagja a T osztlynak. Sajnos ez nem mindig elegend: A Quad elemek sszeadsa (C.13.8)
j ellenplda erre. Teht egy fggvnyhvst egy sablonparamtertl fggnek akkor neve-
znk, ha az albbi felttelek valamelyike teljesl:
1. Az aktulis paramter tpusa fgg egy T sablonparamtertl a tpuslevezetsi
szablyok szerint. Pldul f(T(1)), f(t), f(g(t)) vagy f(&t), ha felttelezzk, hogy
t tpusa T.
Fggelkek s trgymutat 1162
2. A meghvott fggvnynek van egy olyan formlis paramtere, amely fgg a T t-
pustl, a tpuslevezetsi szablyok szerint (13.3.1). Pldul f(T), f(list<T>&)
vagy f(const T*).
Alapjban vve azt mondhatjuk, hogy a meghvott fggvny akkor fgg egy sablonparam-
tertl, ha a konkrt s formlis paramtereire nzve nyilvnvalan fgg tle.
Egy olyan hvs, melyben a fggvny egyik paramternek tpusa vletlenl ppen megfe-
lel az aktulis sablonparamternek, nem jelent fggsget:
template<class T> T f(T a)
{
return g(1); // hiba: nincs g() a hatkrben s g(1) nem fgg T-tl
}
void g(int);
int z = f(2);
Az nem szmt, hogy az f(2) hvsban a T ppen az int tpust jelli s a g() paramtere is
ilyen tpus. Ha a g(1) hvst fggnek tekintennk, a sablon-meghatrozs olvasjnak
a fggvny jelentse teljesen rthetetlen lenne. Ha a programoz azt akarja, hogy a g(int)
fggvny fusson le, akkor a g(int)-et be kell vezetnie az f() kifejtse eltt, hogy a g(int)
fggvny elrhet legyen az f() feldolgozsakor. Ez teljesen ugyanaz a szably, mint a nem
sablon fggvnyek esetben.
A fggvnyneveken kvl a vltozk, tpusok, konstansok stb. neve is fgg, ha tpusuk
fgg a sablonparamtertl:
template<class T> void fct(const T& a)
{
typename T::Memtype p = a.p; // p s Memtype fgg T-tl
cout << a.i << ' ' << p->j; // i s j fgg T-tl
}
C.13.8.2. Kts meghatrozskor
Amikor a fordt egy sablon meghatrozsval tallkozik, megllaptja, mely nevek fggk
(C.13.8.1). Ha egy nv fgg, deklarcijnak megkeresst el kell halasztanunk a pld-
nyostsig (C.13.8.3).
C. Technikai rszletek 1163
Azoknak a neveknek, melyek nem fggnek sablonparamterektl, a sablon meghatroz-
sakor (defincijnl) elrhetnek kell lennik (4.9.4):
int x;
template<class T> T f(T a)
{
x++; // rendben
y++; // hiba: nincs y a hatkrben s y nem fgg T-tl
return a;
}
int y;
int z = f(2);
Ha a fordt tall megfelel deklarcit, mindenkppen azt fogja hasznlni, fggetlenl at-
tl, hogy ksbb esetleg jobb deklarcit is tallhatna hozz:
void g(double);
template<class T> class X : public T {
public:
void f() { g(2); } // g(double) meghvsa
// ...
};
void g(int);
class Z { };
void h(X<Z> x)
{
x.f();
}
Amikor a fordt elkszti az X<Z>::f() fggvnyt, nem a g(int) fggvnyt fogja hasznlni,
mert azt az X utn vezettk be. Az nem szmt, hogy az X sablont nem hasznljuk a g(int)
bevezetse eltt. Ehhez hasonlan egy fggetlen hvst sem trthet el egy alaposztly:
class Y { public: void g(int); };
void h(X<Y> x)
{
x.f();
}
Fggelkek s trgymutat 1164
Az X<Y>::f() most is a g(double) fggvnyt fogja meghvni. Ha a programoz az alaposz-
tly g() fggvnyt akarja hasznlni, az f() meghatrozst a kvetkezkppen kell megfo-
galmaznia:
template<class T> class XX : public T {
void f() { T::g(2); } // T::g() meghvsa
// ...
};
Ez termszetesen annak az alapszablynak a megjelense, hogy a sablon defincijnak
a lehet legnllbbnak kell lennie (C.13.8).
C.13.8.3. Kts pldnyostskor
A sablon minden klnbz sablonparamter-halmazzal val felhasznlsakor j
pldnyostsi pontot hatrozunk meg. A pldnyostsi pont helye a legkzelebbi glob-
lis vagy nvtr-hatkrben van, kzvetlenl a felhasznlst tartalmaz deklarci eltt:
template<class T> void f(T a) { g(a); }
void g(int);
void h()
{
extern g(double);
f(2);
}
Esetnkben az f<int>() megfelel pldnya kzvetlenl a h() eltt jn ltre, teht az f()
fggvnyben meghvott g() a globlis g(int) lesz, nem pedig a helyi g(double).
A pldnyostsi pont meghatrozsbl kvetkezik, hogy a sablonparamtereket nem
kthetjk helyi nevekhez vagy osztlytagokhoz:
void f()
{
struct X { /* ... */ }; // helyi szerkezet
vector<X> v; // hiba: helyi szerkezet nem hasznlhat sablonparamterknt
// ...
}
C. Technikai rszletek 1165
Ugyangy a sablonban hasznlt minstetlen neveket sem kthetjk helyi nvhez. A min-
stetlen nevek akkor sem fognak egy osztly tagjaihoz ktdni, ha a sablont elszr ebben
az osztlyban hasznljuk. A helyi nevek elkerlse nagyon fontos, ha nem akarunk szm-
talan, makrszer problmval szembekerlni:
template<class T> void sort(vector<T>& v)
{
sort(v.begin(),v.end()); // standard knyvtrbeli sort() hasznlata
}
class Container {
vector<int> v; // elemek
// ...
public:
void sort() // elemek rendezse
{
sort(v); // a sort(vector<int>&) meghvsa a Container::sort() helyett
}
// ...
};
Ha a sort(vector<T>&) a sort() fggvnyt az std::sort() jellssel hvta volna meg, az ered-
mny ugyanez lett volna, de program sokkal olvashatbb lenne.
Ha egy nvtrben meghatrozott sablon pldnyostsi pontja egy msik nvtrben tallha-
t, a nvktsnl mindkt nvtr nevei rendelkezsre llnak. A fordt szoks szerint a tl-
terhelsi szablyokat hasznlja arra, hogy megllaptsa, melyik nvtr nevt kell hasznlnia
(8.2.9.2).
Figyeljnk r, hogy ha egy sablont tbbszr hasznlunk ugyanazokkal a sablonparamterek-
kel, mindig j pldnyostsi pontot hatrozunk meg. Ha a fggetlen neveket a klnbz
helyeken klnbz nevekhez ktjk, programunk helytelen lesz. Az ilyen hibt nagyon ne-
hz szrevenni egy alkalmazsban, fleg ha a pldnyostsi pontok klnbz fordtsi
egysgekben vannak. A legjobb, ha a nvktssel jr problmkat kikerljk azzal, hogy
a lehet legkevesebb nem helyi nevet hasznljuk a sablonban s fejllomnyok segtsgvel
biztostjuk, hogy mindenhol a megfelel krnyezet lljon a sablon rendelkezsre.
C.13.8.4. Sablonok s nvterek
Amikor egy fggvnyt meghvunk, annak deklarcijt a fordt akkor is megtallhatja, ha
az nincs is az aktulis hatkrben. Ehhez az kell, hogy a fggvnyt ugyanabban a nvtr-
ben vezessk be, mint valamelyik paramtert (8.2.6). Ez nagyon fontos a sablon-megha-
Fggelkek s trgymutat 1166
trozsokban meghvott fggvnyek szempontjbl, mert ez a szolgltats teszi lehetv,
hogy a fgg fggvnyeket a fordt a pldnyosts kzben megtallja.
A sablon egyedi cl vltozata a pldnyosts tetszleges pontjn ltrejhet (C.13.8.3), de
a pldnyostst kveten az adott fordtsi egysgben, esetleg egy olyan fordtsi egysg-
ben is, mely kifejezetten az egyedi cl vltozatok ltrehozshoz kszlt. Ebbl hrom
nyilvnval mdszer alakthat ki az egyedi cl vltozatok elksztshez:
1. Akkor hozzuk ltre azokat, amikor els meghvsukkal tallkozunk.
2. A fordtsi egysg vgn ltrehozzuk az sszeset, amelyre az adott fordtsi egy-
sgben szksg van.
3. Miutn a program sszes fordtsi egysgt megvizsgltuk, a programban hasz-
nlt sszes egyedi cl vltozatot egyszerre ksztjk el.
Mindhrom mdszernek vannak elnyei s htrnyai, ezrt gyakran ezen lehetsgek va-
lamilyen prostst hasznljk.
A fggetlen nevek ktst mindenkppen a sablon meghatrozsakor vgzi el a fordt.
A fgg nevek ktshez kt dolgot kell megvizsglni:
1. A sablon meghatrozsakor a hatkrben lv neveket
2. A fgg hvsok paramtereinek nvterben szerepl neveket (a globlis fgg-
vnyeket gy tekintjk, hogy a beptett tpusok nvterben szerepelnek)
Pldul:
namespace N {
class A { /* ... */ };
char f(A);
}
char f(int);
template<class T> char g(T t) { return f(t); }
char c = g(N::A()); // N::f(N::A) meghvst okozza
Itt az f(t) egyrtelmen fgg, gy a definci feldolgozsakor nem kthetjk sem az f(int),
sem az f(N::A) fggvnyhez. A g<N::A>(N::A) szakostsakor a fordt az N nvtrben ke-
resi a meghvott f() fggvny meghatrozst s ott az N::f(N::A) vltozatot tallja meg.
C. Technikai rszletek 1167
A program hibs, ha klnbz eredmnyeket kaphatunk azzal, hogy klnbz pld-
nyostsi pontokat vlasztunk, vagy azzal, ha az egyedi cl vltozat ltrehozsakor hasz-
nlt krnyezetekben a nvterek tartalmt megvltoztatjuk:
namespace N {
class A { /* ... */ };
char f(A,int);
}
template<class T, class T2> char g(T t, T2 t2) { return f(t,t2); }
char c = g(N::A(),'a'); // hiba: f(t) ms feloldsa is lehetsges
namespace N { // az N nvtr kibvtse (8.2.9.3)
void f(A,char);
}
Ha a pldnyosts pillanatban hozzuk ltre a szakostott vltozatot, az f(N::A,int) fgg-
vny kerl meghvsra, viszont ha elksztst a fordtsi egysg vgre halasztjuk, a ford-
t az f(N::A,char) fggvnyt tallja meg elbb. Teht a g(N::A(),a) hvs helytelen.
Nagyon rendetlen programozsi stlust mutat, ha egy tlterhelt fggvnyt kt deklarcija
kztt hvunk meg. Egy nagyobb program esetben a programoz nem gyanakodna hib-
ra, ebben az esetben azonban a fordt kpes szrevenni a tbbrtelmsget. Hasonl
problmk jelentkezhetnek klnbz fordtsi egysgekben is, gy a hibk szlelse mr
sokkal nehezebb vlik. Az egyes C++-vltozatoknak nem ktelessgk az ilyen tpus hi-
bk figyelse.
A fggvnyhvsok tbbfle feloldsi lehetsgnek problmja leggyakrabban akkor je-
lentkezik, amikor beptett tpusokat hasznlunk. Teht a legtbb hibt kiszrhetjk azzal,
hogy a beptett tpusokat nagy krltekintssel hasznljuk a paramterekben.
Szoks szerint a globlis fggvnyek csak mg bonyolultabb teszik a dolgokat. A globlis
nvteret a beptett tpusok szintjnek tekinthetjk, gy a globlis fggvnyek felhasznlha-
tk olyan fgg fggvnyek lektsre is, melyeket beptett tpusokkal hvtunk meg:
int f(int);
template<class T> T g(T t) { return f(t); }
char c = g('a'); // hiba: f(t) ms feloldsa is lehetsges
char f(char);
Fggelkek s trgymutat 1168
A g<char>(char) fggvny egyedi cl vltozatt elkszthetjk a pldnyostsi ponton is;
ekkor az f(int) fggvnyt fogjuk hasznlni. Ha a vltozatot a fordtsi egysg vgn lltjuk
el, az f(char) fggvny fut majd le. Teht a g(a) hvs hibs.
C.13.9. Mikor van szksg egyedi cl vltozatokra?
Egy sablon osztly egyedi cl vltozatt csak akkor kell ellltani, ha az adott osztlyra
tnyleg szksg van. Teht amikor egy, az osztlyra hivatkoz mutatt adunk meg, az osz-
tly tnyleges meghatrozsra mg nincs szksgnk:
class X;
X* p; // rendben: X meghatrozsra nincs szksg
X a; // hiba: X meghatrozsra szksg van
A sablon osztlyok meghatrozsakor ez a klnbsg fontos lehet. A sablon osztlyt mind-
addig nem pldnyostjuk, amg arra nincs szksg:
template<class T> class Link {
Link* suc; // rendben: Link meghatrozsra (mg) nincs szksg
// ...
};
Link<int>* pl; // Link<int> pldnyostsra nincs szksg
Link<int> lnk; // most van szksg Link<int> pldnyostsra
A pldnyostsi pont az a hely, ahol a defincira elszr szksg van.
C.13.9.1. Sablon fggvnyek pldnyostsa
A fordt a sablon fggvnyeket csak akkor pldnyostja, amikor a fggvnyt felhasznl-
juk. Ennek megfelelen egy sablon osztly pldnyostsa nem vonja maga utn sem sszes
tagjnak pldnyostst, sem a sablon osztly deklarcijban szerepl tagok pldnyos-
tst. Ez nagy rugalmassgot biztost a sablon osztlyok meghatrozsakor:
template<class T> class List {
// ...
void sort();
};
C. Technikai rszletek 1169
class Glob { /* nincs sszehasonlt mvelet */ };
void f(List<Glob>& lb, List<string>& ls)
{
ls.sort();
// lb mveleteinek hasznlata, kivve az lb.sort()-ot
}
Itt a List<string>::sort() fggvnyt a fordt pldnyostja, de a List<Glob>::sort() fggvny-
re nincs szksg. Ez egyrszt kevesebb kd ltrehozst teszi lehetv, msrszt megkml
minket attl, hogy az egsz programot t kelljen szerveznnk. Ha a List<Glob>::sort() fgg-
vnyt is ltrehozn a fordt, akkor a Glob osztlyban szerepelnie kellene az sszes olyan
mveletnek, melyet a List::sort() felhasznl, a sort() fggvnyt ki kellene vennnk a List sab-
lonbl, vagy a Glob objektumokat kellene valamilyen ms trolban trolnunk.
C.13.10. Explicit pldnyosts
A pldnyostst gy knyszerthetjk ki, ha a template kulcssz utn (melyet ez esetben
nem kvet < jel) deklarljuk a kvnt egyedi cl vltozatot:
template class vector<int>; // osztly
template int& vector<int>::operator[](int); // tag
template int convert<int,double>(double); // fggvny
Teht egy sablon deklarcija a template< kifejezssel kezddik, mg egy pldnyostsi k-
relmet a template kulcsszval fejeznk ki. Figyeljk meg, hogy a template sz utn a teljes
deklarci szerepel, nem elg csak a pldnyostani kvnt nevet lernunk:
template vector<int>::operator[]; // formai hiba
template convert<int,double>; // formai hiba
A fggvnyparamterekbl levezethet sablonparamterek ugyangy elhagyhatk, mint
a sablon fggvnyek meghvsakor (13.3.1):
template int convert<int,double>(double); // rendben (felesleges)
template int convert<int>(double); // rendben
Amikor egy sablon osztlyt gy pldnyostunk, a tagfggvnyekbl is ltrejn egy pldny.
Fggelkek s trgymutat 1170
Az explicit pldnyosts klnbz ellenrzsek elvgzsre is felhasznlhat (13.6.2):
template<class T> class Calls_foo {
void constraints(T t) { foo(t); } // minden konstruktorbl meghvand
// ...
};
template class Calls_foo<int>; // hiba: foo(int) nem meghatrozott
template Calls_foo<Shape*>::constraints(); // hiba: foo(Shape*) nem meghatrozott
A pldnyostsi krelmek hatsa az sszeszerkesztsi idre s az jrafordts hatkonys-
gra nzve jelents lehet. Arra is lttam mr pldt, hogy a sablon-pldnyostsok egyet-
len fordtsi egysgbe val sszefogsval a fordtsi idt nhny rrl nhny percre si-
kerlt cskkenteni.
Hibt jelent, ha ugyanarra az egyedi cl vltozatra kt meghatrozs is van. Nem szmt,
hogy ezek felhasznl ltal megadottak (13.5), automatikusan ltrehozottak (C.13.7),
vagy pldnyostsi krelemmel kszltek. A fordt nem kteles szrevenni a tbbszrs
pldnyostst, ha az egyes vltozatok klnbz fordtsi egysgekben fordulnak el. Ez
lehetv teszi a felesleges pldnyostsok elegns elkerlst, gy megszabadulhatunk
azoktl a problmktl, melyek a tbb knyvtrt hasznl programokban az explicit pl-
dnyostsbl szrmazhatnak (C.13.7). A szabvny azonban nem kveteli meg, hogy
a megvalstsok elegnsak legyenek. A kevsb elegns megvalstsok hasznlinak
rdemes elkerlnik a tbbszrs pldnyostst. Ha ezt a tancsot mgsem fogadjuk meg,
programunk a legrosszabb esetben nem fog futni, de a programban nem kvetkezik be
rejtett vltozs.
A nyelv nem kveteli meg, hogy a felhasznlk explicit pldnyostst hasznljanak. Ez
csupn olyan optimalizcis lehetsg, mellyel sajt kezleg szablyozhatjuk a fordts s
sszeszerkeszts menett (C.13.7).
C.14. Tancsok
[1] A technikai rszletek helyett sszpontostsunk a programfejleszts egszre.
C.1.
[2] A szabvny szigor betartsa sem garantlja a hordozhatsgot. C.2.
[3] Kerljk a nem meghatrozott helyzeteket (a nyelvi bvtsekben is). C.2.
C. Technikai rszletek 1171
[4] Legynk tisztban a megvalsts-fgg szolgltatsokkal. C.32.
[5] A {, }, [, ], | s ! jelek helyett csak akkor hasznljunk kulcsszavakat s digrf je-
leket, ha ezek nem elrhetek az adott rendszerben; trigrf karaktereket pedig
csak akkor, ha a \ sem llthat el gpnkn. C.3.1.
[6] Az egyszer adatkzls rdekben a programok lersra hasznljuk az ANSI
karaktereket. C.3.3.
[7] A karakterek szmmal jellse helyett lehetleg hasznljuk vezrlkarakter
megfeleliket. C.3.2.
[8] Soha ne hasznljuk ki a char tpus eljelessgt vagy eljel nlklisgt. C.3.4.
[9] Ha ktsgeink vannak egy egsz literl tpusval kapcsolatban, hasznljunk
uttagokat. C.4.
[10] Kerljk az rtkveszt automatikus talaktsokat. C.6.
[11] Hasznljuk a vector osztlyt a beptett tmbk helyett. C.7.
[12] Kerljk a union tpus hasznlatt. C.8.2.
[13] A nem ltalunk ksztett szerkezetek lersra hasznljunk mezket. C.8.1.
[14] Figyeljnk a klnbz memriakezelsi stlusok elnyeire s htrnyaira. C.9.
[15] Ne szennyezzk be a globlis nvteret. C.10.1.
[16] Ha nem tpusra, hanem nll hatkrre (modulra) van szksgnk, osztlyok
helyett hasznljunk nvtereket. C.10.3.
[17] Sablon osztlyokban is megadhatunk static tagokat. C.13.1.
[18] Ha egy sablonparamter tagtpusaira van szksgnk, az egyrtelmsg rdek-
ben hasznljuk a typename kulcsszt. C.13.5.
[19] Ha sablonparamterekkel val kifejezett minstsre van szksgnk, hasznl-
juk a template kulcsszt a sablon osztly tagjainak egyrtelm megadshoz.
C.13.6.
[20] A sablon meghatrozst gy fogalmazzuk meg, hogy a lehet legkevesebbet
hasznljuk fel a pldnyostsi krnyezetbl. C.13.8.
[21] Ha egy sablon pldnyostsa tl sokig tart, esetleg rdemes explicit pldnyo-
stst alkalmaznunk. C.13.10.
[22] Ha a fordts sorrendjnek pontosan megjsolhatnak kell lennie, explicit pl-
dnyostsra lehet szksg. C.13.10.
Fggelkek s trgymutat 1172
Helyi sajtossgok
,Ha Rouau ::z, g, g,, u!u a :oua!a
A kulturlis eltrsek kezelse A !oa! osztly Nevestett loklok Loklok ltreho-
zsa Loklok msolsa s sszehasonltsa A g!oa!(; s !a::!(; loklok Karakter-
lncok sszehasonltsa A Ja osztly Jellemzk elrse a loklokban Egyszer fel-
hasznli Ja-ek Szabvnyos Ja-ek Karakterlncok sszehasonltsa Numerikus
I/O Pnz I/O Dtum s id I/O Alacsonyszint idmveletek Egy Da osztly
A karakterek osztlyozsa Karakterkd-talaktsok zenetkatalgusok Tancsok
Gyakorlatok
D.1. A kulturlis eltrsek kezelse
A !oa! (lokl) a helyi sajtossgokat jelkpez objektum. Olyan kulturlis jellemzket tar-
talmaz, mint a karakterek trolsnak s a karakterlncok sszehasonltsnak mdja, illet-
ve a szmok megjelensi formja a kimeneten. A helyi sajtossgok kre bvthet: a prog-
ramoz a loklhoz tovbbi olyan jellemzket (Ja; arculat, szempont) adhat, amelyeket
a standard knyvtr nem tmogat kzvetlenl. Ilyenek pldul az irnytszmok vagy a te-
lefonszmok. A !oa! elsdleges szerepe a standard knyvtrban a kimeneti adatfolyam-
ban (o::au) lev adatok megjelentsi formjnak, illetve a bemeneti adatfolyamok
(!::au) ltal elfogadott informci formtumnak szablyozsa.
D
A 21.7 pontban mr trgyaltuk, hogyan mdosthatjuk az adatfolyamok formtumt; ez
a fggelk azt rja le, hogy pthet fel jellemzkbl (Ja) egy !oa!, illetve azt magyarz-
za el, hogyan befolysolja a lokl az adatfolyamot. Ismerteti a Ja-ek definilsnak md-
jt is, felsorolja a szabvnyos jellemzket, amelyekkel az adatfolyamok tulajdonsgai bel-
lthatk s mdszereket mutat be mindezek ltrehozsra s hasznlatra. Az adat- s
idbrzolst segt standard knyvtrbeli eszkzket a dtumok be- s kivitelnek trgya-
lsakor mutatjuk be.
A loklok s jellemzk trgyalst a kvetkez rszekre bontottam:
D.1 A kulturlis eltrsek loklokkal val brzolsa mgtt rejl alapgondo-
latok bemutatsa
D.2 A !oa! osztly
D.3 A Ja osztly
D.4 A szabvnyos jellemzk ttekintse s rszletes ismertetse:
D.4.1 Karakterlncok sszehasonltsa
D.4.2 Szmrtkek bevitele s kivitele
D.4.3 Pnzrtkek bevitele s kivitele
D.4.4 Dtum s id bevitele s kivitele
D.4.5 A karakterek osztlyozsa
D.4.6 Karakterkd-konverzik
D.4.7 zenetkatalgusok
A !oa! nem a C++ fogalma, a legtbb opercis rendszerben s felhasznli krnyezet-
ben ltezik. A helyi sajtossgokon (terleti belltsokon) elvileg az adott rendszer-
ben megtallhat valamennyi program osztozik, fggetlenl attl, hogy a programok mi-
lyen programnyelven rdtak. Ezrt a C++ standard knyvtrnak !oa!-jt gy
tekinthetjk, mint amelynek segtsgvek a programok szabvnyos s hordozhat mdon
frhetnek hozz olyan adatokhoz, melyek brzolsa a klnbz rendszerekben jelent-
sen eltr. gy a C++ !oa! kzs felletet biztost azon rendszeradatok elrshez, melye-
ket az egyes rendszerek ms s ms mdon trolnak.
D.1.1. A kulturlis eltrsek programozsa
Kpzeljk el, hogy olyan programot runk, melyet sok orszgban fognak hasznlni. Azt,
hogy a programot olyan stlusban rjuk meg, ami ezt lehetv teszi, gyakran hvjk uuz-
oz! uoga:ua (internacionalizci; ez kihangslyozza, hogy a programot tbb or-
szgban hasznljk) vagy ouo:::ua (lokalizcinak, kihangslyozva a program helyi
Fggelkek s trgymutat 1174
viszonyokhoz igaztst). A program ltal kezelt adatok nmelyike a szoksoknak megfe-
lelen klnbzkppen jelenik meg az egyes orszgokban. A problmt gy kezelhet-
jk, hogy a bemeneti/kimeneti eljrsok megrsnl ezt figyelembe vesszk:
:o!u p:!u_ua(ou: Da& u; !::: a ugJ!!o Jo:uau
{
:u!(u:_au_1; J!a:zu!o! !zo:
{
a: DK. p! 7 ua:: IUUU
on uua,(; u_uou/uuou(;] u,a:(;,
:a,
a: UK. p! 7 IUUU
on uua,(; uuou(; u,a:(;,
:a,
a: US. p! 7IUUU
on uuou(; uua,(; u,a:(;,
:a,

}
}
Egy ilyen kddal a feladat megoldhat, de a kd elg csnya s csak kvetkezetes haszn-
latval biztosthatjuk, hogy minden kimenet megfelelen igazodjon a helyi szoksokhoz.
Ami mg ennl is rosszabb: ha jabb dtumformtummal szeretnnk kiegszteni, mdos-
tanunk kell a kdot. Felmerlhet az tlet, hogy a problmt egy osztlyhierarchia ltreho-
zsval oldjuk meg (12.2.4). A Da-ben trolt adatok azonban fggetlenek a megjelents
mdjtl, gy nem Da tpusok (pldul US_ua, UK_ua, s {P_ua) rendszert kell lt-
rehoznunk, hanem a dtumok megjelentsre kell megadnunk tbbfle mdszert (mond-
juk amerikai, brit s japn stlus kimenetet). Lsd mg: D.4.4.5.
Az engedjk meg a felhasznlnak, hogy bemeneti/kimeneti fggvnyeket rjon s kezel-
jk azok a helyi sajtossgokat megkzeltsnek is vannak buktati:
1. Egy rendszerfejleszt programoz nem tudja knnyen, ms rendszerre tltet-
het mdon s hatkonyan mdostani a beptett tpusok megjelenst a stan-
dard knyvtr segtsge nlkl.
2. Egy nagy programban nem mindig lehet az sszes I/O mveletet (s minden
olyan mveletet, amely a helyi sajtossgokhoz igaztva kszt el adatot I/O-
hoz) megtallni.
3. A programot nha nem igazthatjuk az j szablyokhoz s mg ha lehetsges
is lenne, jobban szeretnnk egy olyan megoldst, amely nem ignyel jrarst.
D. Helyi sajtossgok 1175
4. Pazarls lenne minden felhasznlval megterveztetni s elkszttetni az eltr
helyi sajtossgokat kezel programelemeket.
5. A klnbz programozk klnflekppen kezelik a kulturlis eltrseket,
ezrt a valjban ugyanazokkal az adatokkal dolgoz programok is klnbzni
fognak, noha alapveten azonosnak kellene lennik. gy azoknak a programo-
zknak, akik tbb forrsfjlban lv kdot tartanak karban, tbbfajta progra-
mozsi megkzeltst kell megtanulniuk, ami fraszt s hibalehetsget rejt
magban.
Kvetkezskppen a standard knyvtr a helyi sajtossgok kezelshez bvthet eljrs-
gyjtemnyt knl. Az !o::au knyvtr (21.7) mind a beptett, mind a felhasznli tpu-
sok kezelshez ezekre az eljrsokra tmaszkodik. Vegynk pldul egy egyszer ciklust,
amely mrsek sorozatt vagy tranzakcik egy halmazt brzol (Da, uon!) prokat
msol:
:o!u p,(!::au& !:, o::au& o:; (Da,uon!; auaJo!,auo u:o!
{
Da u,
uon! :o!nu,
u!! (!: >> u >> :o!nu; o: u :o!nu `u,
}
Egy valdi program termszetesen csinlna valamit a rekordokkal s egy kicsit jobban t-
rdne a hibakezelssel is.
Hogyan kszthetnk ebbl egy olyan programot, amely a francia szoksoknak megfelel
fjlt olvas be (ahol a magyarhoz hasonlan vesszt hasznlnak a tizedespont jells-
re a lebegpontos szmokban; pldul a I2,5 jelentse tizenkett s fl) s az amerikai for-
mnak megfelelen rja azt ki? Loklok s I/O mveletek meghatrozsval a p,(;-t hasz-
nlhatjuk az talaktsra:
:o!u J(!::au& J!u, o::au& Jon, !::au& J!u2, o::au& Jon2;
{
J!u!un(!oa!(u_US;;, au:!a! augo!
Jon!un(!oa!(J:;;, J:au!a
p,(J!u,Jon;, au:!a! augo! o!:a::, J:au!a :::
J!u2!un(!oa!(J:;;, J:au!a
Jon2!un(!oa!(u_US;;, au:!a! augo!
p,(J!u2,Jon2;, J:au!a o!:a::, au:!a! augo! :::
}
Fggelkek s trgymutat 1176
Ha adottak a kvetkez adatfolyamok,
p: I2, IUUU IUUU
p: I, IUUU 4545
p: I4, IUUU Uo332I

n!!! IU5U IU,


n!!! IU5I I4,45
n!!! IU52 o7,U

a program a kvetkezt fogja kirni:


I2 a::!! IUUU IUUU,
I a::!! IUUU 45,45
I4 a::!! IUUU Uo33,2I

{n!, , IU5U IU
{n!, , IU5I I445
{n!, , IU52 o7U

A fggelk tovbbi rsznek legjavt annak szenteljk, hogy lerjuk, milyen nyelvi tulajdon-
sgok teszik ezt lehetv, illetve hogy elmagyarzzuk, hogyan hasznljuk azokat. Jegyez-
zk meg, hogy a programozk tbbsgnek nem kell rszletekbe menen foglalkoznia
a loklokkal vagy kifejezetten azokat kezel kdot rnia. Akik mgis megteszik, azok is leg-
inkbb egy szabvnyos !oa!-t fognak elkeresni, hogy az adott adatfolyamnak elrjk an-
nak hasznlatt (21.7). Azok az eljrsok azonban, melyekkel a loklokat ltrehozhatjuk s
hasznlatukat egyszerv tehetjk, sajt programnyelvet alkotnak.
Ha egy program vagy rendszer sikeres, olyanok is hasznlni fogjk, akiknek az ignye s
zlse eltr attl, amire az eredeti tervezk s programozk szmtottak. A legtbb sikeres
programot hasznlni fogjk azokban az orszgokban is, ahol a (termszetes) nyelvek s ka-
rakterkszletek eltrnek az eredeti tervezk s programozk ltal ismertektl. Egy program
szleskr hasznlata a siker jele, ezrt a nyelvi s kulturlis hatrok kztt tvihet prog-
ramok tervezse s rsa a sikerre val felkszlst jelenti.
A nemzetkzi tmogats fogalma egyszer, a gyakorlati megszortsok azonban a !oa!
objektumok elksztst meglehetsen bonyolultt teszik:
1. A loklok az olyan helyi sajtossgokat tartalmazzk, mint a dtumok megjele-
nsi formja. A szablyok azonban egy adott kultrn bell is szmos apr s
D. Helyi sajtossgok 1177
nem rendszerezhet mdon eltrhetnek. A helyi szoksoknak semmi kzk
a programnyelvekhez, ezrt egy programnyelv nem szabvnyosthatja azokat.
2. A lokloknak bvthetnek kell lennik, mert nem lehetsges az sszes helyi
sajtossgot felsorolni, amely minden C++ felhasznlnak fontos.
3. A !oa! objektumokat olyan I/O mveletekben hasznljuk, melyeknl a futsi
id igen fontos.
4. A !oa! objektumoknak lthatatlannak kell lennik a legtbb programoz sz-
mra, hiszen k anlkl szeretnk kihasznlni a megfelel dolgot vgz adat-
folyam-bemenetet s -kimenetet, hogy pontosan ismernk annak felptst,
megvalstst.
5. A lokloknak elrhetnek kell lennik azok szmra, akik olyan eszkzket ter-
veznek, amelyek helyi sajtossgoktl fgg adatokat kezelnek az adatfolyam
I/O knyvtr keretein kvl.
Egy bemeneti s kimeneti mveleteket vgz program tervezsekor vlasztanunk kell,
hogy a kimenet formtumt szoksos kddal vagy !oa!-ek felhasznlsval vezreljk-
e. Az elbbi (hagyomnyos) megkzelts ott clszer, ahol biztostani tudjuk, hogy min-
den bemeneti mveletet knnyen t lehet alaktani a helyi sajtossgoknak megfelelen.
Ha azonban a beptett tpusok megjelensnek kell vltoznia, ha klnbz karakterksz-
letekre van szksg, vagy ha a bemenetre/kimenetre vonatkoz szablyok halmazai kztt
kell vlasztanunk, a !oa! objektumok hasznlata tnik sszerbbnek.
A !oa! objektumok gynevezett Ja-ekbl llnak, amelyek az egyes jellemzket (lebe-
gpontos rtk kirsakor hasznlt elvlaszt karakter (u!ua!_po!u(;, D.4.2), pnzrtk
beolvassakor hasznlt formtum (uou,pnu, D.4.3) stb.) szablyozzk. A Ja egy,
a !oa!..Ja osztlybl szrmaz osztly objektuma (D.3); leginkbb gy kpzelhetjk
el, hogy a !oa! Ja-ek trolja (D.2, D.3.1).
D.2. A locale osztly
A !oa! osztly s a hozz tartoz szolgltatsok a !oa!> fejllomnyban tallhatk:
!a:: :u..!oa! {
pn!!.
!a:: Ja, !o!!!uzo a:a!uazo :pn:, D
!a:: !u, !o! azouo::o :pn:, D
,puJ !u ago:,, Ja- :opo:o::::a :zo!g!o :pn:
Fggelkek s trgymutat 1178
:a! ou: ago:, a u,!g: : ug:a!o:::Jnggo
uou U,
o!!a I,
,p II,
uoua:, I2,
unu:! I,
!u I4,
u::ag: I5,
a!! o!!a ' ,p ' uoua:, ' unu:! ' !u ' u::ag:,
!oa!(; :ou(;, a g!o!!: !o! u:o!aa (D2I;
!oa!(ou: !oa!& .; :ou(;, . u:o!aa
.p!!! !oa!(ou: a:* p;, a p u:n !o! u:o!aa (D2I;
-!oa!(; :ou(;,
!oa!(ou: !oa!& ., ou: a:* p, ago:, ;, . u:o!aa, p!n:z p-!! Ja-
!oa!(ou: !oa!& ., ou: !oa!& ,, ago:, ;, . u:o!aa, p!n:z ,-!! Ja-
up!a !a:: 1a> !oa!(ou: !oa!& ., 1a* J;, . u:o!aa, p!n:z az J Ja
up!a !a:: 1a> !oa! ou!u(ou: !oa!& .;, *!: u:o!aa,
p!n:z az .-!! 1a
ou: !oa!& op:ao:(ou: !oa!& .; :ou(;,
oo! op:ao:(ou: !oa!&; ou:, !o!o o::za:ou!::a
oo! op:ao::(ou: !oa!&; ou:,
::!ug uau(; ou:, az auo !o! u: (D2I;
up!a !a:: , !a:: T:, !a:: > a:a:!uo o::za:ou!::a
az auo !o! :g::g:!
oo! op:ao:(;(ou: a:!_::!ug,T:,>&, ou: a:!_::!ug,T:,>&; ou:,
:a! !oa! g!oa!(ou: !oa!&;, a g!o!!: !o! !!::a : :!::za:: a :g!:!
:a! ou: !oa!& !a::!(;, a !a::z!n: -::!n: !o!
p:!:a.
:zo!:
},
A !oa!-ekre gy gondolhatunk, mint uap!u,Ja*>-ek felletre, vagyis valami olyasmi-
re, ami lehetv teszi, hogy egy !oa!..!u segtsgvel megtalljuk a megfelel objektumot,
amelynek osztlya a !oa!..Ja-bl szrmazik. A !oa! megvalstsa alatt az e gondolat
alapjn elksztett (hatkony) szerkezeteket rtjk. Az elrendezs ilyesmi lesz:
D. Helyi sajtossgok 1179
Itt a o!!aa:> s a unupnua:> a standard knyvtr Ja-jei (D.4). Mint mind-
egyik Ja, ezek is a !oa!..Ja-bl szrmaznak.
A !oa!-nek szabadon s olcsn msolhatnak kell lennie. Kvetkezskppen a !oa!-t
majdnem mindig gy valstjk meg, mint egy lert arra a szakostott uap!u,Ja*>-re,
amely a szolgltatsok legtbbjt tartalmazza. A lokl jellemzinek (a Ja-eknek) gyorsan
elrhetnek kell lennik, ezrt az egyedi cl uap!u,Ja*> a tmbkhz hasonl gyors
hozzfrst kell, hogy nyjtson. A !oa! jellemzinek elrse a n:_Ja1a>(!o; jel-
lssel trtnik (lsd D.3.1-et).
A standard knyvtr a Ja-ek gazdag vlasztkt nyjtja. A programoz ezeket logikai cso-
portokban kezelheti, mert a szabvnyos Ja-ek kategrikat alkotnak (pl. unu:! s
o!!a, D.4).
A programoz az egyes kategrikban lev jellemzket kicserlheti (D.4, D.4.2.1), de
nem adhat meg j kategrikat. A kategria fogalma csak a standard knyvtrbeli Ja-
ekre vonatkozik, nem terjeszthet ki a felhasznli jellemzkre. Ezrt nem szksges, hogy
egy Ja kategriba tartozzon s sok felhasznli Ja nem is tartozik ilyenbe.
A loklokat messze a leggyakrabban az adatfolyamok bemeneti s kimeneti mveletinl
hasznljuk, mg ha nem is tudunk rla. Minden !::au s o::au rendelkezik sajt lokl-
lal. Az adatfolyamok loklja a folyam ltrehozsnak pillanatban alaprtelmezs szerint
a globlis !oa! (D.2.1) lesz. A folyam lokljt az !un(; (megtlts) mvelettel lehet
belltani, a !oa! msolatt pedig a g!o(; fggvnnyel kaphatjuk meg (21.6.3).
Fggelkek s trgymutat 1180
u!ua!_po!u ( ;
:nuau ( ;

oupa: ( ;
a: ( ;

!oa!.
o!!aa:>.
unupnua:>.
D.2.1. Nevestett loklok
A loklok msik !oa!-bl s jellemzkbl hozhatk ltre. A legegyszerbb egy mr lte-
z !oa! lemsolsa:
!oa! !oU, az ::u,: g!o!!: !o! u:o!aa (D2;
!oa! !oI !oa!(;, az ::u,: g!o!!: !o! u:o!aa (D2;
!oa! !o2(;, a J!a:zu!o !a! !ou,u ::z:: !o!
u:o!aa
!oa! !o(;, a !o! u:o!aa
!oa! !o4 !oa!..!a::!(;, a !o! u:o!aa
!oa! !o5(POS1A;, az auo J!:zoo:u,z !a! uga:ozo
POS1A !o! u:o!aa
A !oa!(; jelentst a szabvny klasszikus C loklknt hatrozza meg; ebben a knyv-
ben vgig ezt a loklt hasznljuk. A tbbi !oa! neve a hasznlt C++-vltozattl fgg.
A !oa!(; a felhasznl ltal elnyben rszestett lokl, melyet a program vgrehajtsi
krnyezetben a nyelven kvli eszkzk lltanak be.
A legtbb opercis rendszer biztost valamilyen eszkzt a programok terleti belltsai-
nak megadsra. A belltsra legtbbszr akkor kerl sor, amikor a felhasznl elszr ta-
llkozik a rendszerrel. Egy olyan gpen pldul, ahol a rendszer alaprtelmezett nyelveknt
az argentin spanyolt adtk meg, a !oa!(; valsznleg a !oa!(:_R;-t jelenti.
Az egyik rendszerem gyors ellenrzse 51 megjegyezhet nvvel rendelkez loklt muta-
tott ki (pldul POS1A, u, u_UK, u_US, :, :_R, J:, ::, ua, p!, s !:o_335U_I). A POSIX
ltal ajnlott formtum: kisbets nyelvnv, amit nagybets orszgnv kvet (ez nem kte-
lez), valamint egy kdjelz (ez sem ktelez); pldul p_{P!. Ezek a nevek azonban
nem szabvnyostottak a klnbz platformok kztt. Egy msik rendszerben egyb
!oa! nevek mellett a kvetkezket talltam: g, n, n:, :, J:, :u, s ua. A C++ szabvny nem
adja meg az orszgok s nyelvek !oa!-jt, br az egyes platformokra ltezhetnek szabv-
nyok. Kvetkezskppen, ha a programoz nevestett loklokat akar hasznlni, a rendszer
dokumentcijra s tapasztalataira kell hagyatkoznia.
ltalban clszer elkerlni a loklneveket jelz karakterlncok programszvegbe gyaz-
st. A fjlnevek s rendszerllandk programban val szerepeltetse korltozza a prog-
ram hordozhatsgt, a programot j krnyezetbe beilleszteni kvn programoz pedig
gyakran arra knyszerl, hogy megkeresse ezeket az rtkeket, hogy mdosthassa azokat.
D. Helyi sajtossgok 1181
A loklnevek megemltse is hasonl kellemetlen kvetkezmnyekkel jr. Jobb, ha a lok-
lokat kivesszk a program vgrehajtsi krnyezetbl (pldul a !oa!(; felhasznls-
val) vagy a programra bzzuk, hogy a tapasztaltabb felhasznlktl a lokl meghatrozst
krje, mondjuk egy karakterlnc bekrsvel:
:o!u n::_:_!oa!(ou: ::!ug& n:!ou_::!ug;
{
on n:!ou_::!ug, p! Ha u: !o! ::u a:zu!u!, aua ug a u:
::!ug :,
!u >> :,
!oa!..g!oa!(!oa!(:_::(;;;, a J!a:zu!o !a! ugauo g!o!!: !o! !!::a
}
A kezd felhasznlk szmra ltalban jobb, ha lehetv tesszk, hogy listbl vlaszthassa-
nak. Az ezt kezel eljrsnak viszont tudnia kell, hol s hogyan trolja a rendszer a loklokat.
Ha a paramterknt megadott karakterlnc nem definilt !oa!-re hivatkozik, a konstruktor
:nu!u_::o: kivtelt vlt ki (14.10):
:o!u :_!o(!oa!& !o, ou: a:* uau;
:,
{
!o !oa!(uau;,
}
a (:nu!u_::o:; {
:: ` uau ` !o! uu uJ!u!!`u,

}
Ha a !oa! nevestett, a uau(; visszaadja annak nevt, ha nem, a ::!ug(*;-gal tr vissza.
A nv elssorban arra val, hogy hivatkozhassunk a vgrehajtsi krnyezetben trolt lok-
lokra, de a hibakeressben is segthet:
:o!u p:!u_!oa!_uau:(ou: !oa!& u,_!o;
{
on uau oJ n::u g!oa! !oa!. !oa!(;uau(; `u,
on uau oJ !a::! !oa!. !oa!..!a::!(;uau(; `u,
on uau oJ n::: p:J::u !oa!. !oa!(;uau(; `u,
on uau oJ u, !oa!. u,_!ouau(; `u,
}
Az alaprtelmezett ::!ug(*;-tl eltr, de azonos nev loklok sszehasonltskor egyen-
rtknek minslnek, az vagy : opertorokkal azonban az sszehasonlts kzvetle-
nebb mdon is elvgezhet.
Fggelkek s trgymutat 1182
A nvvel rendelkez !oa!-ek msolatai ugyanazt a nevet kapjk, mint az eredeti !oa! (ha
annak van neve), gy azonos nven tbb !oa! is szerepelhet. Ez logikus, mert a loklok
nem mdosthatk, gy ezen objektumok mindegyike a helyi sajtossgok ugyanazon hal-
mazt rja le.
A !oa!(!o,1oo,a; hvs a !o-hoz hasonl loklt hoz ltre, de annak jellemzit (a Ja-
eket) a !oa!(1oo; a kategrijbl veszi. Az eredmnyl kapott loklnak kizrlag ak-
kor lesz neve, ha a !o-nak is volt. A szabvny nem hatrozza meg pontosan, milyen nevet
kap az j !oa!, de feltehet, hogy klnbzni fog a !o-tl. A legegyszerbb, ha a nevet
a !o nevbl s a 1oo-bl ptjk fel. Pldul ha a !o neve u_UK, az j lokl neve
u_UK.1oo lesz.
Az j loklok elnevezsre vonatkoz szablyok a kvetkezkppen foglalhatk ssze:
A programoz az jonnan ltrehozott !oa!-ek neveknt nem adhat meg C stlus karak-
terlncot. A neveket a program vgrehajtsi krnyezete hatrozza meg vagy a !oa!
konstruktorok ptik fel azokat a nevek prostsbl.
D.2.1.1. j loklok ltrehozsa
j !oa! objektumot gy kszthetnk, hogy vesznk egy mr ltez !oa!-t s ehhez jel-
lemzket adunk vagy kicserlnk benne nhnyat. Az j !oa!-ek jellemzen egy mr l-
tez !oa! kiss eltr vltozatai:
D. Helyi sajtossgok 1183
Lokl Nv
!oa!(1oo; 1oo
!oa!(!o; !ouau(;
!oa!(!o,1oo,a; j nv, ha a !o-nak van neve; egybknt
::!ug(*;
!oa!(!o,!o2,a; j nv, ha a !o-nak s a !o2-nek is van
neve; egybknt ::!ug(*;
!oa!(!o,1a; ::!ug(*;
!oou!u(!o2; ::!ug(*;
:o!u J(ou: !oa!& !o, ou: M,_uou,_!o* u!o; D4I-u !::
M,_uou,_!o
{
!oa! !oI(!oa!(POS1A;, !o, !oa!..uoua:,;, !o-!! puzJo:unu-
!!uzo a:zu!aa
!oa! !o2 !oa!(!oa!..!a::!(;, u!o;, a !a::z!n:, p!n:z u!o

}
Itt !oI a POS1A lokl msolata, amit gy mdostottunk, hogy a !o pnzformtum-jellem-
zit hasznlja (D.4.3). Ehhez hasonlan, !o2 a lokl msolata, amely a M,_uou,_!o-t
hasznlja (D.4.3.1). Ha a 1a* paramter (itt a M,_uou,_!o) rtke U, az eredmnyl
kapott lokl egyszeren a !oa! paramter msolata lesz.
Ha a kvetkezt rjuk,
!oa!(ou: !oa!& ., 1a* J;,
az J paramternek egy meghatrozott Ja tpust kell jellnie. Egy egyszer Ja* nem ele-
gend:
:o!u g(ou: !oa!..Ja* u!oI, ou: M,_uou,_!o* u!o2;
{
!oa! !o !oa!(!oa!..!a::!(;, u!oI;, !a. a Ja :pn:a uu !:u:
!oa! !o4 !oa!(!oa!..!a::!(;, u!o2;, :uuu. a Ja :pn:a !:u:

}
Ennek az az oka, hogy a !oa! a 1a* paramter tpust hasznlja arra, hogy fordtsi id-
ben megllaptsa a Ja tpust. Pontosabban, a !oa! megvalstsa a jellemz azonost-
jt, a Ja..!u-t (D.3.3) hasznlja, hogy a jellemzt megtallja a loklban (D.3.1).
Jegyezzk meg, hogy a
up!a !a:: 1a> !oa!(ou: !oa!& ., 1a* J;,
konstruktor a nyelv ltal nyjtott egyetlen eljrs a programoz szmra, hogy Ja-eket
adhasson meg, melyeket egy !oa!-en keresztl hasznlni lehet. Az egyb loklokat a meg-
valst programozknak kell megadniuk, nevestett !oa!-ek formjban (D.2.1), melye-
ket a program vgrehajtsi krnyezetbl lehet megszerezni. Ha a programoz rti az erre
hasznlatos a fejlesztkrnyezettl fgg eljrst, a meglev loklok krt jakkal b-
vtheti (D.6[11,12]).
Fggelkek s trgymutat 1184
A loklok konstruktorainak halmazt gy terveztk, hogy minden jellemz tpusa meglla-
pthat legyen, vagy tpuslevezets tjn (a 1a sablonparamter tpusbl), vagy azrt,
mert egy msik loklbl szrmazik, amely ismerte a jellemz tpust. A ago:, paramter
megadsval a Ja-ek tpust kzvetett mdon is meghatrozhatjuk, hiszen a loklok isme-
rik a kategrikban lv jellemzk tpust. Ebbl kvetkezik, hogy a !oa! osztly nyomon
kvetheti a Ja-ek tpust, gy azokat klnsebb tbbletterhels nlkl mdosthatja is.
A lokl a Ja tpusok azonostsra a !oa!..!u tagtpust hasznlja (D.3).
Nha hasznos lehet olyan loklt ltrehozni, amely egy msik lokl msolata, de az egyik jel-
lemzje egy harmadik loklbl szrmazik. A ou!u(; sablon tagfggvny erre val:
:o!u J(ou: !oa!& !o, ou: !oa!& !o2;
{
!oa! !o !oou!u M,_uou,_!o >(!o2;,

}
Az eredmnyl kapott !o gy viselkedik, mint a !o, de a pnzformtumot a !o2-ben le-
v M,_uou,_!o (D.4.3.1) msolata alapjn lltja be. Ha a !o2 nem rendelkezik
a M,_uou,_!o-val, hogy tadhassa azt az j loklnak, a ou!u(; :nu!u_::o:-t
(14.10) vlt ki. A ou!u(; eredmnyeknt kapott lokl nem nevestett.
D.2.2. Loklok msolsa s sszehasonltsa
A loklok kezdeti vagy egyszer rtkadssal msolhatk:
:o!u :uap(!oa!& ., !oa!& ,; ng,auaz, u!u az :u..:uap(;
{
!oa! up .,
. ,,
, up,
}
A msolat az eredetivel egyenrtk, de fggetlen, nll objektum:
:o!u J(!oa!* u,_!oa!;
{
!oa! !o !oa!..!a::!(;, !o!
!J (!o : !oa!..!a::!(;; {
:: H!a a ug:a!o:::au. :::: a :z:o`u,
D. Helyi sajtossgok 1185
.!(I;,
}
!J (&!o : &!oa!..!a::!(;; on Nu ug!po. a :u n!ouozu`u,
!oa! !o2 !oa!(!o, u,_!oa!, !oa!..unu:!;,
!J (!o !o2; {
on a !a::!(; a:ou!o Ja-u !:o!`u,

}

}
Ha a u,_!oa! rendelkezik olyan jellemzvel, amely a !a::!(; lokl szabvnyos
unupnua:>-jtl eltren adja meg a szmok elvlaszt rsjeleit (u,_unupnua:>),
az eredmnyl kapott loklok a kvetkezkppen lesznek brzolhatk:
Fggelkek s trgymutat 1186
oupa: ( ;
a: ( ;

u!ua!_po!u ( ;
n::_:,uo! ( ;

u!ua!_po!u ( ;
n::_:,uo! ( ;

!o.
o!!aa:>.
unupnua:>.
u,_unupnua:>.
!o2.
A !oa! objektumok nem mdosthatk, mveleteik viszont lehetv teszik j loklok lt-
rehozst a mr meglevkbl. A ltrehozs utni mdosts tiltsa (a lokl nem vltoz-
kony, !uuna! termszete) alapvet fontossg a futsi idej hatkonysg rdekben,
ez teszi ugyanis lehetv a felhasznl szmra, hogy meghvja a Ja-ek virtulis fggv-
nyeit s trolja a visszaadott rtkeket. A bemeneti adatfolyamok pldul anlkl tudhatjk,
milyen karakter hasznlatos a tizedesvessz (pontosabban tizedespont) jelzsre, illetve
anlkl ismerhetik a :n brzolst, hogy minden egyes alkalommal meghvnk szm
beolvassakor a u!ua!_po!u(; fggvnyt vagy logikai rtk beolvassakor
a :nuau(;-et (D.4.2). Csak az !un(; meghvsa az adatfolyamra (21.6.3) okozhatja,
hogy ezek a hvsok klnbz rtkeket adjanak vissza.
D.2.3. A global() s classic() loklok
A programban rvnyben lev lokl msolatt a !oa!(; adja vissza, a !oa!..g!oa!(.; pe-
dig .-re lltja azt. Az rvnyes loklt gyakran globlis loklnak hvjk, utalva arra, hogy
valsznleg globlis (vagy statikus) objektum.
Az adatfolyamok ltrehozsukkor automatikusan feltltdnek (imbue; 21.1, 21.6.3)
a globlis lokllal, vagyis a !oa!(; msolatval, ami elszr mindig a szabvnyos C
!oa!..!a::!(;.
A !oa!..g!oa!(; statikus tagfggvny megengedi, hogy a programoz meghatrozza, me-
lyik lokl legyen globlis. Az elz msolatt a g!oa!(; fggvny adja vissza; ennek segt-
sgvel a felhasznl visszallthatja az eredeti globlis loklt:
:o!u J(ou: !oa!& u,_!o;
{
!J::au J!uI(:ou_uau;, J!uI J!o!: a g!o!!: !o!!a!
!oa!& o!u_g!oa! !oa!..g!oa!(u,_!o;, g!o!!: !o! !!::a
!J::au J!u2(:ou_o:_uau;, J!u2 J!o!: a u,_!o !o!!a!

!oa!..g!oa!(o!u_g!oa!;, a :g! g!o!!: !o! :!::za!!::a
}
Ha az . lokl rendelkezik nvvel, a !oa!..g!oa!(.; szintn a C globlis loklt lltja be. Eb-
bl kvetkezik, hogy egy vegyes (C s C++) programban egysgesen s kvetkezetesen ke-
zelhetjk a loklt, ha meghvjuk a C standard knyvtrnak valamelyik loklfggvnyt.
Ha az . loklnak nincs neve, akkor nem meghatrozhat, hogy a !oa!..g!oa!(.; befoly-
solja-e a C globlis loklt, vagyis a C++ programok nem kpesek megbzhatan (s hor-
dozhat mdon) tlltani a C loklt egy olyan loklra, amely nem a vgrehajtsi krnye-
D. Helyi sajtossgok 1187
zetbl val. Nincs szabvnyos md arra sem, hogy egy C program bellthassa a C++ glo-
blis loklt (kivve ha meghv egy C++ fggvnyt, ami ezt megteszi), ezrt a hibk elker-
lse rdekben a vegyes programokban nem clszer a g!oa!(;-tl eltr C globlis loklt
hasznlni.
A globlis lokl belltsa nincs hatssal a mr ltez I/O adatfolyamokra; azok ugyanazt
a loklt fogjk hasznlni, amivel eredetileg feltltdtek. A J!uI-re pldul nem hat a glob-
lis lokl mdostsa, de a J!u2-t a mvelet a u,_!o-kal tlti fel.
A globlis lokl mdostsval ugyanaz a problma, mint a globlis adatokat megvltozta-
t egyb eljrsokkal: lnyegben nem tudhat, mire van hatssal a vltoztats. Ezrt a leg-
jobb, ha a g!oa!(;-t a lehet legkevesebbszer hasznljuk s a mdostst olyan kdrszle-
tekre korltozzuk, ahol annak hatsa pontosan nyomon kvethet. Szerencsre ezt segti
az a lehetsg, hogy az adatfolyamokat meghatrozott loklokkal tlthetjk meg (imbue,
21.6.3). Azrt vigyzzunk: ha a programban elszrva szmos explicit !oa!- s Ja-keze-
l kd tallhat, a program nehezen lesz fenntarthat s mdosthat.
D.2.4. Karakterlncok sszehasonltsa
A loklokat tbbnyire arra hasznljuk, hogy sszehasonltsunk kt karakterlncot egy
!oa! alapjn. Ezt a mveletet maga a !oa! nyjtja, a felhasznlknak nem kell sajt ssze-
hasonlt eljrst rniuk a o!!a jellemzbl (D.4.1). Az eljrs a lokl op:ao:(; (; ssze-
hasonlt fggvnye, gy kzvetlenl hasznlhat prediktumknt (18.4.2):
:o!u J(:o:::!ug>& :, ou: !oa!& u,_!oa!;
{
:o:(:g!u(;, :uu(;;, :uuz: a g!o!!: !o! :z:!u

:o:(:g!u(;, :uu(;, u,_!oa!;, :uuz: a u,_!oa! :za!,a! :z:!u

}
Alaprtelmezs szerint a standard knyvtrbeli :o:(; a mveletet alkalmazza a karakte-
rek szmrtkre, hogy eldntse a rendezsi sorrendet (18.7, 18.6.3.1).
Jegyezzk meg, hogy a loklok a:!_::!ug-eket hasonltanak ssze, nem C stlusakat.
Fggelkek s trgymutat 1188
D.3. Jellemzk
A jellemzk (Ja-ek) a lokl Ja tagosztlybl szrmaztatott osztlyok objektumai:
!a:: :u..!oa!..Ja {
p:ou.
.p!!! Ja(:!z_ : U;, :U. a !o! :za!,ozza a Ja !a:au
:!:na! -Ja(;, J!g,!u. :u u::no:
p:!:a.
Ja(ou: Ja&;, uu uga:ozo
:o!u op:ao:(ou: Ja&;, uu uga:ozo
:zo!:
},
A msol mveletek privt tagok s szndkosan nincsenek definilva, hogy a msolst
megakadlyozzk (11.2.2).
A Ja osztly bzisosztly szerepet tlt be, nyilvnos fggvnye nincs. Konstruktora v-
dett, hogy megakadlyozza az egyszer Ja objektumok ltrehozst, destruktora pedig
virtulis, hogy biztostsa a szrmazott osztlybeli objektumok megfelel megsemmistst.
A jellemzk kezelst a loklok elvileg mutatkon keresztl vgzik. A Ja
konstruktornak U rtk paramtere azt jelenti, hogy a loklnak trlnie kell az adott jel-
lemzt, ha mr nincs r hivatkozs. Ezzel szemben a nem nulla konstruktor-paramter azt
biztostja, hogy a !oa! sohasem trli a jellemzt. Ez az a ritka eset, amikor a Ja lettar-
tamt a programoz kzvetlenl, nem a loklon keresztl szablyozza.
A o!!a_,uaua:> szabvnyos Ja tpus objektumokat pldul gy hozhatjuk lt-
re (D.4.1.1):
:o!u J(ou: ::!ug& :I, ou: ::!ug& :2;
{
:zo:o: :. a U (a!ap:!uz; pa:au: az !z!,
og, a !o! J!! a J!:zuo!::
o!!aa:>* p uu o!!a_,uaua:>(p!;,
!oa! !o(!oa!(;, p;,
:!a :. a pa:au: : I, a J!a:zu!o J!! a J!:zuo!::
o!!aa:>* uu o!!a_,uaua:>(g,I;,
o!!a_,uaua:> ngI(:u;, !a. !o!!: :!ozo uu ! J!:zuo!u!
o!!a_,uaua:> ng2(uo,I;, !a. !o!!: :!ozo uu ! J!:zuo!u!

D. Helyi sajtossgok 1189
uu o:o!o. a o!!a_,uaua:> u::no:a :u
u!u: u! p, u: a !o! !uz! *p J!:zuo!:
}
Azaz a szabvnyos Ja-ek a lokl ltal kezelt bzisosztlyknt hasznosak, ritkn kell ms
mdon kezelnnk azokat.
A _,uau(; vgzds jellemzk a vgrehajtsi krnyezetben tallhat nevestett lokl
Ja-jei (D.2.1).
Minden Ja-nek rendelkeznie kell azonostval (!u), hogy a loklban a a:_Ja(; s
n:_Ja(; fggvnyekkel megtallhat legyen (D.3.1):
!a:: :u..!oa!..!u {
pn!!.
!u(;,
p:!:a.
!u(ou: !u&;, uu uJ!u!!
:o!u op:ao:(ou: !u&;, uu uJ!u!!
:zo!:
},
A msol mveletek privtok s nincsenek kifejtve, hogy a msolst megakadlyozzk
(11.2.2).
Az azonost arra val, hogy a jellemzk szmra j felletet ad osztlyokban (pldul
lsd D.4.1-et) !u tpus statikus tagokat hozhassunk ltre. A loklok eljrsai az !u-t hasz-
nljk a Ja-ek azonostsra (D.2, D.3.1). Az azonostk tbbnyire egy Ja-ekre hivat-
koz mutatkbl ll vektor indexrtkei (vagyis uap!u,Ja*>, ami igen hatkony).
A (szrmaztatott) jellemzket meghatroz adatokat a szrmaztatott osztly rja le, nem ma-
ga a Ja bzisosztly. Ebbl kvetkezik, hogy a programoz tetszleges mennyisg ada-
tot hasznlhat a Ja fogalmnak brzolsra s korltozs nlkl mdosthatja azokat.
Jegyezzk meg, hogy a felhasznli Ja-ek minden fggvnyt ou: tagknt kell megha-
trozni, mert a Ja-eknek llandnak kell lennik (D.2.2).
Fggelkek s trgymutat 1190
D.3.1. Jellemzk elrse a loklokban
A loklok Ja-jei a n:_Ja sablon fggvnyen keresztl rhetk el s a a:_Ja sab-
lon fggvnnyel krdezhetjk le, hogy a lokl rendelkezik-e egy adott jellemzvel:
up!a !a:: 1a> oo! a:_Ja(ou: !oa!&; :ou(;,
up!a !a:: 1a> ou: 1a& n:_Ja(ou: !oa!&;, au_a: !:! :!a
!
Ezekre a fggvnyekre gy kell gondolnunk, mintha azok !oa! paramterkben keresnk
1a sablonparamterket. Ms megkzeltsben a n:_Ja egyfajta tpusknyszerts
(cast), egy lokl konvertlsa egy meghatrozott jellemzre. Ez azrt lehetsges, mert
a !oa! objektumok egy adott tpus Ja-bl csak egy pldnyt tartalmazhatnak:
:o!u J(ou: !oa!& u,_!oa!;
{
a: n:_Ja unupnua:> >(u,_!oa!;u!ua!_po!u(; a :za:u,o:
Ja a:zu!aa

!J (a:_JaEu:,p>(u,_!oa!;; { a:a!uaz- a u,_!oa! Eu:,p Ja-.
ou: Eu:,p& J n:_JaEu:,p>(u,_!oa!;, az Eu:,p Ja !u,::
a u,_!oa!-o!
ou: :,po Jg_:,po(;, az Eu:,p Ja a:zu!aa

}

}
Jegyezzk meg, hogy a n:_Ja egy konstans Ja-re val referencit ad vissza, gy az
eredmnyt nem adhatjuk rtkl egy nem konstans vltoznak. Ez azrt logikus, mert a jel-
lemzknek elvileg nem mdosthatknak kell lennik s csak ou: tagokat tartalmazhatnak.
Ha meghvjuk a n:_JaA>(!o; fggvnyt s !o nem rendelkezik az A jellemzvel,
a n:_Ja(; au_a: kivtelt vlt ki (14.10). A szabvnyos Ja-ek garantltan hozzfr-
hetek minden !oa! szmra (D.4), gy az esetkben nem kell a a:_Ja-et hasznl-
nunk, ezekre a n:_Ja nem fog au_a: kivtelt kivltani.
Hogyan lehetne a n:_Ja s a:_Ja fggvnyeket megvalstani? Emlkezznk, hogy
egy loklra gy gondolhatunk, mintha uap!u,Ja*> lenne (D.2). Ha a 1a sablonpa-
ramterknt adott egy Ja tpus, a a:_Ja vagy n:_Ja a 1a..!u-re hivatkozhat s
ezt hasznlhatja a megfelel jellemz megkeressre. A a:_Ja s n:_Ja nagyon egy-
szer vltozata gy nzhetne ki:
D. Helyi sajtossgok 1191
!-ug:a!o:::. pz!n g,, og, a !o! :uu!z! g, Ja_uap-u u:z
uap!u,Ja*>-!
up!a !a:: 1a> oo! a:_Ja(ou: !oa!& !o; :ou(;
{
ou: !oa!..Ja* J !oJa_uap/1a..!u],
:n:u J . :n . Ja!:,
}
up!a !a:: 1a> ou: 1a& n:_Ja(ou: !oa!& !o;
{
ou: !oa!..Ja* J !oJa_uap/1a..!u],
!J (J; :n:u :a!_a:ou: 1a&>(*J;,
:ou au_a:(;,
}
A Ja..!u hasznlatt gy is tekinthetjk, mint a fordtsi idej tbbalaksg (parametrikus
polimorfizmus) egy formjt. A u,uau!_a: a n:_Ja-hez hasonl eredmnyt adna, de
az utbbi sokkal hatkonyabb, mert kevsb ltalnos.
Az !u valjban inkbb egy felletet s viselkedst azonost, mint osztlyt. Azaz ha kt Ja-
osztlynak pontosan ugyanaz a fellete s (a !oa! szempontjbl) ugyanaz a szerepk,
akkor ugyanaz az !u kell, hogy azonostsa ket. A o!!aa:> s
a o!!a_,uaua:> pldul felcserlhet egy loklban, gy mindkettt
a o!!aa:>..!u azonostja (D.4.1).
Ha egy jellemznek j felletet adunk mint az J(;-ben az Eu:,p-nek , definilnunk kell
szmra az azonostt (lsd D.3.2-t s D.4.1-et).
D.3.2. Egyszer felhasznli facet-ek
A standard knyvtr a kulturlis eltrsek leglnyegesebb terleteihez mint a karakter-
kszletek kezelse s a szmok be- s kivitele szabvnyos Ja-eket nyjt. Ahhoz, hogy
az ltaluk nyjtott szolgltatsokat a szles krben hasznlatos tpusok bonyolultsgtl s
a velk jr hatkonysgi problmktl elklntve vizsglhassuk, hadd mutassak be el-
szr egy egyszer felhasznli tpusra vonatkoz Ja-et:
unu Sa:ou { a:a:z, u,:, o:z, ! }, ::zao
Fggelkek s trgymutat 1192
Ez volt a legegyszerbb felhasznli tpus, ami ppen eszembe jutott. Az itt felvzolt I/O kis
mdostsokkal a legtbb egyszer felhasznli tpus esetben felhasznlhat.
!a:: Sa:ou_!o . pn!! !oa!..Ja {
pn!!.
Sa:ou_!o(!u ! U; . !oa!..Ja(!; { }
-Sa:ou_!o(; { } !o: :z! a Sa:ou_!o onuo J!:zuo!: (D;
. :zo!:a a:a:!uu
:!:na! ou: ::!ug& o_::(Sa:ou .; ou: U,
az : a:a:!uua ugJ!!o ::za !!,z: .-.
:!:na! oo! J:ou_::(ou: ::!ug& :, Sa:ou& .; ou: U,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
},
!oa!..!u Sa:ou_!o..!u, az azouo::o onu uga:oz:a
Az egyszersg kedvrt a Ja csak a a: tpust hasznl megvalstsokra korltozdik.
A Sa:ou_!o osztly ltalnos, elvont felletet nyjt minden Sa:ou_!o Ja szmra.
Ha a Sa:ou be- s kimenett egy adott loklra szeretnnk definilni, a Sa:ou_!o-bl szr-
maztatunk egy osztlyt, amelyben megfelelen kifejtjk a o_::(; s J:ou_::(; fggvnye-
ket.
A Sa:ou rtkt knny kirni. Ha az adatfolyam rendelkezik Sa:ou_!o jellemzvel, azt
hasznlva az rtket karakterlncc alakthatjuk, ha nem, kirhatjuk a Sa:ou egsz rtkt:
o::au& op:ao:(o::au& :, Sa:ou .;
{
ou: !oa!& !o :g!o(;, az auaJo!,au !o!ua !u,:: (2I7I;
!J (a:_JaSa:ou_!o>(!o;; :n:u : n:_JaSa:ou_!o>(!o;o_::(.;,
:n:u : !u(.;,
}
szrevehetjk, hogy a mveletet gy definiltuk, hogy ms tpusokra hvtuk meg a -
t. Ennek szmos elnye van: egyszerbb a -t hasznlnunk, mint a kimeneti adatfolyam
tmeneti traihoz kzvetlenl hozzfrnnk, a << mveletet kifejezetten a loklhoz igazt-
hatjuk s a mvelet hibakezelst is biztost. A szabvnyos Ja-ek a legnagyobb hatkony-
sg s rugalmassg elrse rdekben tbbnyire kzvetlenl az adatfolyam tmeneti trt
kezelik (D.4.2.2, D.4.2.3), de sok felhasznli tpus esetben nincs szksg arra, hogy
a ::aunJ absztrakcis szintjre sllyedjnk.
D. Helyi sajtossgok 1193
Ahogy lenni szokott, a bemenet kezelse nmileg bonyolultabb, mint a kimenet:
!::au& op:ao:>>(!::au& :, Sa:ou& .;
{
ou: !oa!& !o :g!o(;, az auaJo!,au !o!ua !u,:: (2I7I;
!J (a:_JaSa:ou_!o>(!o;; { a :zo:g: :zo!: o!:a::a
ou: Sa:ou_!o& J n:_JaSa:ou_!o>(!o;,
::!ug nJ,
!J (:(:>>nJ && JJ:ou_::(nJ,.;;; :::a(!o:_a:..Ja!!!;,
:n:u :,
}
!u !, a :zu:zo!: o!:a::a
: >> !,
. Sa:ou(!;,
:n:u :,
}
A hibakezels egyszer, a beptett tpusok hibakezelsnek stlust kveti. Azaz, ha a be-
men karakterlnc nem a vlasztott lokl valamelyik Sa:ou-jt jelli, az adatfolyam hibs
(Ja!!n:) llapotba kerl. Ha a kivtelek megengedettek, !o:_a:..Ja!!n: kivtel kivlts-
ra kerlhet sor (21.3.6).
Vegynk egy egyszer tesztprogramot:
!u ua!u(; g,:z:n :z
{
Sa:ou .,
az a!ap:!uz !o! a:zu!aa (u!u: Sa:ou_!o Ja;,
g:z :n 1O- :uuu,z.
!u >> .,
on . uu!,
!oa! !o(!oa!(;, uu US_:a:ou_!o;,
on!un(!o;, Sa:ou_!o Ja-! :uu!zo !o! a:zu!aa
!u!un(!o;, Sa:ou_!o Ja-! :uu!zo !o! a:zu!aa
!u >> .,
on . uu!,
}
A
2
:nuu:
Fggelkek s trgymutat 1194
bemenetre a program vlasza:
2
:nuu:
Ennek elrshez definilnunk kell a US_:a:ou_!o osztlyt, amelyben megadjuk az vszak-
ok karakterlnc-brzolst s fellrjuk a Sa:ou_!o azon fggvnyeit, amelyek a karakter-
lncokat a felsorolt elemekre alaktjk:
!a:: US_:a:ou_!o . pn!! Sa:ou_!o {
:a! ou: ::!ug :a:ou:/],
pn!!.
ou: ::!ug& o_::(Sa:ou; ou:,
oo! J:ou_::(ou: ::!ug&, Sa:ou&; ou:,
J!g,!u. u!u: US_:a:ou_!o..!u
},
ou: ::!ug US_:a:ou_!o..:a:ou:/] { :p:!ug, :nuu:, Ja!!, u!u: },
ou: ::!ug& US_:a:ou_!o..o_::(Sa:ou .; ou:
{
!J (.:p:!ug '' u!u:.; {
:a! ou: ::!ug :: N!u: !!,u ::za,
:n:u ::,
}
:n:u :a:ou:/.],
}
oo! US_:a:ou_!o..J:ou_::(ou: ::!ug& :, Sa:ou& .; ou:
{
ou: ::!ug* g &:a:ou:/:p:!ug],
ou: ::!ug* uu &:a:ou:/u!u:]-I,
ou: ::!ug* p J!uu(g,uu,:;, 3I, I352
!J (puu; :n:u Ja!:,
. Sa:ou(p-g;,
:n:u :n,
}
Vegyk szre, hogy mivel a US_:a:ou_!o csupn a Sa:ou_!o fellet megvalstsa, nem
adunk meg azonostt a US_:a:ou_!o szmra. St, ha a US_:a:ou_!o-t Sa:ou_!o-knt
akarjuk hasznlni, nem is szabad ilyet tennnk. A loklok mveletei (pldul a a:_Ja,
D.3.1) arra tmaszkodnak, hogy az azonos fogalmakat brzol Ja-eket ugyanaz az !u
azonostja (D.3).
D. Helyi sajtossgok 1195
A megvalstssal kapcsolatos egyetlen rdekes krds az, hogy mit kell tenni, ha rvny-
telen Sa:ou kirst krik? Termszetesen ennek nem lenne szabad megtrtnnie. Az egy-
szer felhasznli tpusoknl azonban nem ritka, hogy rvnytelen rtket tallunk, gy
szmtsba kell vennnk ezt a lehetsget is. Kivlthatnnk egy kivtelt, de miutn olyan
egyszer kimenettel foglalkozunk, amelyet emberek fognak olvasni, hasznos, ha a tartom-
nyon kvli rtkeket az rtktartomnyon kvli szveg is jelzi. A bemenetnl itt a kiv-
telkezels a >> mveletre hrul, mg a kimenet esetben ezt a Ja o_::(; fggvnye vg-
zi (hogy bemutathassuk a lehetsgeket). Valdi programoknl a Ja fggvnyei a be- s
kimeneti hibk kezelsvel egyarnt foglalkoznak, vagy csak jelentik a hibkat, a s >>
mveletekre bzva azok kezelst.
A Sa:ou_!o ezen vltozata arra tmaszkodott, hogy a szrmaztatott osztlyok adjk meg
a loklra jellemz karakterlncokat. Egy msik megolds, hogy a Sa:ou_!o maga szerzi
meg ezeket egy, a loklhoz kapcsold adattrbl (lsd D.4.7). Gyakorlatknt hagytuk an-
nak kidolgozst, hogy egyetlen Sa:ou_!o osztlyunk van, amelynek az vszakokat jelz
karakterlncok a konstruktor paramtereknt addnak t (D.6[2]).
D.3.3. A loklok s jellemzk hasznlata
A loklok elsdlegesen a standard knyvtron bell, az I/O adatfolyamokban hasznlato-
sak, de a !oa! a helyi sajtossgok brzolsnak ennl ltalnosabb eszkze. A u::ag:
(D.4.7) pldul olyan Ja, amelynek semmi kze a be- s kimeneti adatfolyamokhoz.
Az !o::auknyvtr esetleges bvtsei, st, a nem adatfolyamokkal dolgoz be- s kime-
neti eszkzk is kihasznlhatjk a loklok adta lehetsgeket, a felhasznl pedig a !oa!
objektumok segtsgvel tetszleges mdon rendezheti a helyi sajtossgokat.
A loklokon s jellemzkn alapul eljrsok ltalnossga rvn a felhasznli Ja-ek
adta lehetsgek korltlanok. A dtumok, idznk, telefonszmok, trsadalombiztostsi
szmok (szemlyi szmok), gyrtsi szmok, hmrskletek, ltalnos (mrtkegysg, r-
tk) prok, irnytszmok, ruhamretek, s ISBN szmok mind megadhatk Ja-knt.
Mint minden ers szolgltatssal, a Ja-ekkel is vatosan kell bnni. Az, hogy valamit le-
het jellemzknt brzolni, mg nem jelenti azt, hogy ez a legjobb megolds. A kulturlis
eltrsek brzolsnak kivlasztsakor a kulcskrds mint mindig az, hogy milyen ne-
hz a kd megrsa, mennyire knny a kapott kdot olvasni, valamint hogy hogyan befo-
lysoljk a dntsek a kapott program fenntarthatsgt, illetve az I/O mveletek id- s
trbeli hatkonysgt.
Fggelkek s trgymutat 1196
D.4. Szabvnyos facet-ek
A standard knyvtr !oa!> fejllomnya a kvetkez Ja-eket nyjtja a !a::!(; loklhoz:
A tblzatban a helyn a: vagy ua:_ tpus szerepelhet. Ha a felhasznlnak arra
van szksge, hogy a szabvnyos I/O msfajta A karaktertpust kezeljen, a megfelel Ja-
eket meg kell adnia az A szmra. A ou:A,a:,u:a_> (D.4.6) pldul szksges
lehet az A s a: tpusok kztti talaktsokhoz. Az u:a_ tpus arra val, hogy egy
tbbjtos karakterbrzols lptetsi llapotait jellje (D.4.6); definicija a ua:> s
a ua:> fejllomnyokban tallhat. Tetszleges A karaktertpus esetben az
u:a_-nek a a:_:a!:A>..:a_,p felel meg.
D. Helyi sajtossgok 1197
Szabvnyos facet-ek (a classic() loklban)
D.4.1 o!!a Karakterlncok o!!a>
sszehasonltsa
D.4.2 unu:! Szmok be- s kivitele unupnu>
unu_g>
unu_pn>
D.4.3 uoua:, Pnz I/O uou,pnu>
uou,pnu,:n>
uou,_g>
uou,_pn>
D.4.4 !u Id I/O !u_g>
!u_pn>
D.4.5 ,p Karakterek osztlyozsa ,p>
ou:,a:,u:a_>
D.4.7 u::ag: zenet-visszakeress u::ag:>
Kategria Rendeltets Facet-ek
A standard knyvtr tovbbi Ja-jei a !oa!> fejllomnyban a kvetkezk:
A tblzatban szerepl jellemzk pldnyostsakor a a: vagy ua:_ lehet; a br-
milyen karaktertpus (20.1), az 1u:ua!oua! rtke :n vagy Ja!:, ahol a :n azt jelenti,
hogy a valuta-szimblum ngykarakteres nemzetkzi brzolst hasznljuk (D.4.3.1).
Az u:a_ tpus a tbbjtos karakter-brzolsok lptetsi llapotait jelli (D.4.6), meg-
hatrozsa a ua:> s a ua:> fejllomnyokban tallhat.
Az 1u s az On bemeneti s kimeneti bejrk (itertorok, 19.1, 19.2.1). Ha a _pn s _g
jellemzket elltjuk ezekkel a sablonparamterekkel, olyan Ja-eket hozhatunk ltre,
amelyek nem szabvnyos tmeneti trakhoz frnek hozz (D.4.2.2). Az !o::au-ek tme-
neti trai (pufferei) adatfolyam tmeneti trak, gy bejrik o::aunJ_!:ao:-ok
(19.2.6.1, D.4.2.2), vagyis a hibakezelshez rendelkezsnkre ll a Ja!!u(; fggvny.
Az 1_,uau az 1 Ja-bl szrmazik; ugyanazt a felletet nyjtja mint az 1, de hozzad
egy konstruktort, amelynek egy loklt megnevez karakterlnc paramtere van (lsd
D.4.1-et). Az 1_,uau(u:; jelentse ugyanaz, mint az 1 !oa!(u:; szerkezet. Az el-
gondols az, hogy a program vgrehajtsi krnyezetben egy nevestett loklbl (D.2.1)
kivesszk a szabvnyos Ja egy adott vltozatt:
Fggelkek s trgymutat 1198
Szabvnyos facet-ek
Kategria Rendeltets Facet-ek
D.4.1 o!!a Karakterlncok o!!a_,uau>
sszehasonltsa
D.4.2 unu:! Szmok be- s kivitele unupnu_,uau>
unu_g,1u>
unu_pn,On>
D.4.3 uoua:, Pnz I/O uou,pnu_,uau,1u:ua!oua!>
uou,_g,1u>
uou,_pn,On>
D.4.4 !u Id I/O !u_pn_,uau>
D.4.5 ,p Karakterek osztlyozsa ,p_,uau>
D.4.7 u::ag: zenet-visszakeress u::ag:_,uau>
:o!u J(:o:::!ug>& :, ou: !oa!& !o;
{
!oa! uI(!o, uu o!!a_,uaua:>(ua;;, uu a:a:!u-o::za:ou!::
a:zu!aa
!oa! u(uI, uu ,p_,uaua:>(ua;;, uu a:a:o:z!,oz: a:zu!aa
:o:(:g!u(;, :uu(;, u;,

}
Az j u lokl dn stlus karakterlncokat fog hasznlni, de megtartja a szmokra vonat-
koz alaprtelmezett szablyokat. Mivel a Ja msodik paramtere alaprtelmezs szerint
U, a uu mvelettel ltrehozott Ja lettartamt a lokl fogja kezelni (D.3).
A karakterlnc paramterekkel rendelkez !oa!-konstruktorokhoz hasonlan a _,uau-
konstruktorok is hozzfrnek a program vgrehajtsi krnyezethez. Ebbl az kvetkezik,
hogy nagyon lassak azokhoz a konstruktorokhoz kpest, amelyeknek nem kell a krnye-
zethez fordulniuk informcirt. Majdnem mindig gyorsabb, ha ltrehozunk egy loklt s
azutn frnk hozz annak jellemzihez, mintha _,uau Ja-eket hasznlnnk tbb he-
lyen a programban. Ezrt j tlet, ha a Ja-et egyszer olvassuk be a krnyezetbl, majd
ksbb mindig a memriban lv msolatt hasznljuk:
!oa! u(ua;, a uu !o! o!:a::a (!:: o::z: Ja-; g,:z:,
uau a u !o! : !!uzo!u a:zu!aa !gu, :z:!u
:o!u J(:o:::!ug>& :, ou: !oa!& !o;
{
ou: o!!aa:>& o! n:_Ja o!!aa:> >(u;,
ou: o!!aa:>& ,p n:_Ja ,pa:> >(u;,
!oa! uI(!o,o!;, uu a:a:!u-o::za:ou!:: a:zu!aa
!oa! u2(uI,,p;, uu a:a:o:z!,oz: : a:a:!u-
o::za:ou!:: a:zu!aa
:o:(:g!u(;, :uu(;, u2;,

}
A kategrik egyszerbb teszik a loklok szabvnyos Ja-jeinek kezelst. Pldul ha
adott a u lokl, ltrehozhatunk belle egy msikat, amely a dn nyelv szablyainak meg-
felelen (ez az angolhoz kpest hrom tovbbi magnhangzt jelent) olvas be s hasonlt
ssze karakterlncokat, de megtartja a C++-ban hasznlatos szmformtumot:
!oa! u_n:(!oa!..!a::!(;, u, o!!a',p;, uu n, au:!a! :zuo
D. Helyi sajtossgok 1199
Az egyes szabvnyos Ja-ek bemutatsnl tovbbi pldkat nznk meg a jellemzk
hasznlatra. A o!!a (D.4.1) trgyalsakor pldul a Ja-ek sok kzs szerkezetbeli tu-
lajdonsga elkerl.
Jegyezzk meg, hogy a szabvnyos Ja-ek gyakran fggnek egymstl. A unu_pn pl-
dul a unupnu-ra tmaszkodik. Csak ha az egyes jellemzket mr rszletesen ismerjk,
akkor lehetnk kpesek azokat sikeresen egytt hasznlni, egyeztetni vagy j vltozataikat
elkszteni. Ms szavakkal, a 21.7 pontban emltett egyszer mveleteken tl a loklok
nem arra valk, hogy a kezdk kzvetlenl hasznljk azokat.
A jellemzk megtervezse gyakran nagyon krlmnyes. Ennek oka rszben az, hogy a jel-
lemzk nem rendszerezhet helyi sajtossgokat kell, hogy tkrzzenek, melyekre
a knyvtr tervezje nincs befolyssal; msrszt pedig az, hogy a C++ standard knyvtr-
beli eszkzeinek nagyrszt sszeegyeztethetnek kell maradniuk azzal, amit a C standard
knyvtra s az egyes platformok szabvnyai nyjtanak. A POSIX pldul olyan eszkzk-
kel tmogatja a loklok hasznlatt, melyeket a knyvtrak tervezi nem szabad, hogy fi-
gyelmen kvl hagyjanak.
Msfell a loklok s jellemzk ltal nyjtott szerkezet nagyon ltalnos s rugalmas.
A Ja-ek brmilyen adatot trolhatnak s azokon brmilyen mveletet vgezhetnek. Ha
az j Ja viselkedst a szablyok nem korltozzk tlsgosan, szerkezete egyszer s
tiszta lehet (D.3.2).
D.4.1. Karakterlncok sszehasonltsa
A szabvnyos o!!a jellemz tpus karakterekbl ll tmbk sszehasonltst teszi
lehetv:
up!a !a:: >
!a:: :u..o!!a . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ a:!_::!ug> ::!ug_,p,
.p!!! o!!a(:!z_ : U;,
!u oupa:(ou: * , ou: * , ou: * 2, ou: * 2; ou:
{ :n:u uo_oupa:(,,2,2;, }
!oug a:(ou: * , ou: * ; ou: { :n:u uo_a:(,;, }
::!ug_,p :au:Jo:u(ou: * , ou: * ; ou: { :n:u uo_:au:Jo:u(,;, }
Fggelkek s trgymutat 1200
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-o!!a(;, J!g,!u. :u u::no:
:!:na! !u uo_oupa:(ou: * , ou: * , ou: * 2, ou: * 2; ou:,
:!:na! ::!ug_,p uo_:au:Jo:u(ou: * , ou: * ; ou:,
:!:na! !oug uo_a:(ou: * , ou: * ; ou:,
},
A tbbi Ja-hez hasonlan a o!!a is nyilvnos mdon szrmazik a Ja osztlybl s
olyan konstruktorral rendelkezik, amelynek egyetlen paramtere azt mondja meg, hogy
a !oa! osztly vezrli-e a jellemz lettartamt (D.3).
Jegyezzk meg, hogy a destruktor vdett (protected). A o!!a nem kzvetlen hasznlatra
val, inkbb arra szntk, hogy minden (szrmaztatott) sszehasonlt osztly alapja legyen
s a !oa! kezelje (D.3). A rendszerfejlesztknek s a knyvtrak ksztinek a karakter-
lncokat sszehasonlt Ja-eket gy kell megrniuk, hogy a o!!a nyjtotta felleten ke-
resztl lehessen hasznlni azokat.
Az alapvet karakterlnc-sszehasonltst a oupa:(; fggvny vgzi, az adott o!!a-re
vonatkoz szablyok szerint; I-et ad vissza, ha az els karakterlnc tbb karakterbl ll,
mint a msodik, U-t, ha a karakterlncok megegyeznek, s -I-et, ha a msodik karakter-
lnc nagyobb, mint az els:
:o!u J(ou: ::!ug& :I, ou: ::!ug& :2, o!!aa:>& up;
{
ou: a:* :I :Iuaa(;, u!:! a oupa:(; un:! a:/] ouou uo!goz!
ou: a:* :2 :2uaa(;,
:u! ( upoupa:(:I,:I-:I:!z(;, :2,:2-:2:!z(;; ;
{
a: U. a up :z:!u azouo: a:a:!uo

:a,
a: -I. :I :2

:a,
a: I. :I > :2

:a,
}
}
D. Helyi sajtossgok 1201
Vegyk szre, hogy a o!!a tagfggvnyek tpus elemekbl ll tmbket hasonlta-
nak ssze, nem a:!_::!ug-eket vagy nulla vgzds C stlus karakterlncokat, vagyis
a U szmrtk kznsges karakternek minsl, nem vgzdsnek. A oupa:(; ab-
ban is klnbzik a ::up(;-tl, hogy pontosan a -I, U, I rtkeket adja vissza, nem egy-
szeren U-t s (tetszleges) pozitv s negatv rtkeket (20.4.1).
A standard knyvtrbeli ::!ug nem fgg a lokloktl, azaz a karakterlncok sszehasonl-
tsa az adott nyelvi vltozat karakterkszlete alapjn trtnik (C.2). Ezenkvl a szabv-
nyos ::!ug nem biztost kzvetlen mdot arra, hogy meghatrozzuk az sszehasonltsi fel-
ttelt (20. fejezet). A lokltl fgg sszehasonltshoz a o!!a kategria oupa:(;
fggvnyt hasznlhatjuk. Mg knyelmesebb, ha a fggvnyt a lokl op:ao:(; operto-
rn keresztl, kzvetett mdon hvjuk meg (D.2.4):
:o!u J(ou: ::!ug& :I, ou: ::!ug& :2, ou: a:* u;
{
oo! :I :2, o::za:ou!:: a ug:a!o::: a:a::z!u :! :z:!u
ou: a:* :I :Iuaa(;, u!:! a oupa:(; un:! a:/] ouou uo!goz!
ou: a:* :2 :2uaa(;,
,puJ o!!aa:> o!,
ou: o!& g!o n:_Jao!>(!oa!(;;, az ::u,: g!o!!: !o!o!
!u !U g!ooupa:(:I,:I-:I:!z(;,:2,:2-:2:!z(;;,
ou: o!& u,_o!! n:_Jao!>(!oa!(;;, az !ou,u ::z:: !o!o!
!u !I u,_o!!oupa:(:I,:I-:I:!z(;,:2,:2-:2:!z(;;,
ou: o!& o!! n:_Jao!>(!oa!(u;;, az u u:n !o!o!
!u !2 o!!oupa:(:I,:I-:I:!z(;,:2,:2-:2:!z(;;,
!u ! !oa!(;(:I,:2;, o::za:ou!:: az ::u,: g!o!!: !o! a!apu
!u !4 !oa!(;(:I,:2;, o::za:ou!:: az !ou,u ::z:: !o! a!apu
!u !5 !oa!(u;(:I,:2;, o::za:ou!:: az u !o! a!apu

}
Itt !U!, !I!4, s !2!5, de knny olyan eseteket elkpzelni, ahol !2, !, s !4 rtke
ms. Vegyk a kvetkez szavakbl ll sorozatot egy nmet sztrbl:
D!a!, D!a, u!, u!u, D!nug
Fggelkek s trgymutat 1202
A nyelv szablyainak megfelelen a fnevek (s csak azok) nagy kezdbetsek, de a ren-
dezs nem klnbzteti meg a kis- s nagybetket.
Egy kis- s nagybetket megklnbztet nmet nyelv rendezs minden D-vel kezdd
szt a u el tenne:
D!a!, D!a, D!nug, u!, u!u
Az a (umlautos a) egyfajta a-nak minsl, gy a el kerl. A legtbb karakterkszletben
azonban az a szmrtke nagyobb a szmrtknl, kvetkezskppen !u(; !u(a;,
a szmrtkeken alapul egyszer alaprtelmezett rendezs pedig a kvetkezket adja:
D!a!, D!nug, D!a, u!, u!u
rdekes feladat lehet egy olyan fggvnyt rni, amely a sztrnak megfelelen helyesen
rendezi ezt a sorozatot (D.6[3]).
A a:(; fggvny egy hastrtket szmt ki (17.6.2.3), ami magtl rtetden
hasttblk ltrehozsakor lehet hasznos.
A :au:Jo:u(; fggvny egy olyan karakterlncot llt el, amelyet ms karakterlncokkal
sszehasonltva ugyanazt az eredmnyt kapjuk, mint amit a paramter-karakterlnccal val
sszehasonlts eredmnyezne. A :au:Jo:u(; clja az, hogy optimlis kdot kszthessnk
az olyan programrszekbl, ahol egy karakterlncot szmos msikkal hasonltunk ssze. Ez
akkor hasznos, ha karakterlncok halmazban egy vagy tbb karakterlncot szeretnnk
megkeresni.
A pn!! oupa:(;, a a:(; s a :au:Jo:u(; fggvnyek megvalstst a uo_oupa:(;,
uo_a:(; s uo_:au:Jo:u(; nyilvnos virtulis fggvnyek meghvsa biztostja. Ezeket
a uo_ fggvnyeket a szrmaztatott osztlyokban fell lehet rni. A ktfggvnyes megol-
ds lehetv teszi a knyvtr azon ksztjnek, aki a nem virtulis fggvnyeket rja, hogy
valamilyen kzs szerepet biztostson minden hvsnak, fggetlenl attl, hogy mit csinl-
nnak a felhasznl ltal megadott uo_ fggvnyek.
A virtulis fggvnyek hasznlata megrzi a Ja-ek tbbalak (polimorfikus) termszett,
de kltsges lehet. A tl sok fggvnyhvs elkerlshez a !oa! pontosan meghatroz-
hatja a hasznlatos jellemzket s brmennyi rtket a gyorsttrba tehet, amennyire csak
szksge van a hatkony vgrehajtshoz (D.2.2).
D. Helyi sajtossgok 1203
A jellemzket !oa!..!u tpus statikus !u tagok azonostjk (D.3). A szabvnyos a:_Ja
s n:_Ja fggvnyek az azonostk s jellemzk kztti sszefggseken alapulnak
(D.3.1). Az azonos fellet s szerep Ja-eknek ugyanazzal az azonostval kell rendel-
keznik, gy a o!!aa:> s a o!!a_,uaua:> (D.4.1.1) azonostja is megegye-
zik. Kvetkezskppen kt Ja-nek biztosan klnbz az azonostja, ha (a !oa! szem-
pontjbl nzve) klnbz fggvnyeket hajtanak vgre, gy ez a helyzet
a unupnua:> s a unu_pna:> esetben is (D.4.2).
D.4.1.1. Nevestett Collate
A o!!a_,uau olyan jellemz, amely a o!!a azon vltozatt nyjtja az adott loklnak,
amelyet a konstruktor karakterlnc-paramtere nevez meg:
up!a !a:: >
!a:: :u..o!!a_,uau . pn!! o!!a> {
pn!!.
,puJ a:!_::!ug> ::!ug_,p,
!:oz: u::! :uu!zo !o!o!
.p!!! o!!a_,uau(ou: a:*, :!z_ : U;,
J!g,!u. u!u: azouo::o : u!u:u Jngg:u,
p:ou.
-o!!a_,uau(;, J!g,!u. :u u::no:
a o!!a> :!:n!!: Jngg:u,!u J!n!:::a
!u uo_oupa:(ou: * , ou: * , ou: * 2, ou: * 2; ou:,
::!ug_,p uo_:au:Jo:u(ou: * , ou: * ; ou:,
!oug uo_a:(ou: * , ou: * ; ou:,
},
gy a o!!a_,uau arra hasznlhat, hogy kivegynk egy o!!a-et egy, a program vg-
rehajtsi krnyezetben lev nevestett loklbl (D.4). A vgrehajtsi krnyezetben
a Ja-eket egyszeren fjlban, adatknt is trolhatjuk. Egy kevsb rugalmas megolds, ha
a jellemzket programszvegknt s adatknt brzoljuk egy _,uau Ja-ben.
A o!!a_,uaua:> osztly olyan Ja, amelynek nincs sajt azonostja (D.3). A lo-
klokban a o!!a_,uau> s a o!!a> felcserlhetk. Azonos lokl esetben
a o!!a s a o!!a_,uau csak az utbbi szerepben s a o!!a_,uau ltal felknlt
tovbbi konstruktorban klnbzik.
Fggelkek s trgymutat 1204
Jegyezzk meg, hogy a _,uau destruktor vdett. Ebbl kvetkezik, hogy loklis (helyi)
vltozknt nem hasznlhatunk _,uau Ja-et:
:o!u J(;
{
o!!a_,uaua:> u,_o!!(;, !a. a u,_o!! uu :zuo!ao J!

}
Ez azt a nzpontot tkrzi, hogy a loklok s jellemzk olyasmik, amiket a legjobb elg-
g magas szinten hasznlni a programban, hogy a program minl nagyobb rszre legynk
hatssal. Erre plda a globlis lokl belltsa (D.2.3) vagy egy adatfolyam megtltse
(21.6.3, D.1). Ha szksges, egy _,uau osztlybl egy nyilvnos destruktorral rendel-
kez osztlyt szrmaztathatunk s ebbl az osztlybl loklis vltozkat hozhatunk ltre.
D.4.2. Szmok be- s kivitele
A szm-kimenetet a unu_pn Ja kezeli, amely egy adatfolyam tmeneti trba r (21.6.4).
A bemenet kezelse a unu_g dolga; ez is tmeneti trbl olvas. A unu_pn s unu_g
ltal hasznlt formtumot a unupnu jellemz hatrozza meg.
D.4.2.1. Szmjegy-formtumok
A beptett tpusok (mint a oo!, az !u, s a uon!) be- s kimeneti formtumt
a unupnu jellemz rja le:
up!a !a:: >
!a:: :u..unupnu . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ a:!_::!ug> ::!ug_,p,
.p!!! unupnu(:!z_ : U;,
u!ua!_po!u(; ou:, a !a::!(; !o!au
on:auu:_:p(; ou:, , a !a::!(; !o!au
::!ug g:onp!ug(; ou:, a !a::!(; !o!au, !u:. u!u: :opo:o:::
::!ug_,p :nuau(; ou:, :n a !a::!(; !o!au
::!ug_,p Ja!:uau(; ou:, Ja!: a !a::!(; !o!au
D. Helyi sajtossgok 1205
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-unupnu(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
A g:onp!ug(; ltal visszaadott karakterlnc karaktereinek beolvassa kis egsz rtkek so-
rozataknt trtnik. Minden szm a szmjegyek szmt hatrozza meg egy csoport szm-
ra. A 0. karakter a jobb szls csoportot adja meg (ezek a legkisebb helyirtk szmje-
gyek), az 1. az attl balra lev csoportot s gy tovbb. gy a `UU4`UU2`UU egy szmot
r le (pl. I2-45-o73U, feltve, hogy a - az elvlasztsra hasznlt karakter). Ha szksges,
a csoportost minta utols karaktere ismtelten hasznlhat, gy a `UU egyenrtk
a `UU`UU`UU-mal. Ahogy az elvlaszt karakter neve, a on:auu:_:p(; mutatja is,
a csoportostst leggyakrabban arra hasznljk, hogy a nagy egszeket olvashatbb te-
gyk. A g:onp!ug(; s on:auu:_:p(; fggvnyek az egszek be- s kimeneti formtumt
is megadjk, a lebegpontos szmok szabvnyos be- s kimenethez azonban nem hasz-
nlatosak, gy nem tudjuk kiratni az I245o73U-et I,24,5o73U-knt csupn azltal,
hogy megadjuk a g:onp!ug(; s on:auu:_:p(; fggvnyeket.
A unupnu osztlybl szrmaztatssal j formtumot adhatunk meg. A M,_pnu jellem-
zben pldul lerhatjuk, hogy az egsz rtkek szmjegyeit szkzkkel elvlasztva s
hrmasval csoportostva, a lebegpontos rtkeket pedig eurpai stlus szerint, tizedes-
vesszvel elvlasztva kell kirni:
!a:: M,_pnu . pn!! :u..unupnua:> {
pn!!.
,puJ a: a:_,p,
,puJ ::!ug ::!ug_,p,
.p!!! M,_pnu(:!z_ : U; . :u..unupnua:>(:; { }
p:ou.
a: uo_u!ua!_po!u(; ou: { :n:u ,, } :::zo
a: uo_on:auu:_:p(; ou: { :n:u , } :zooz
::!ug uo_g:onp!ug(; ou: { :n:u `UU, } :zug,n :opo:o
},
:o!u J(;
{
on E!:o ::!n:. I245o73 *** I245o73 `u,
!oa! !o(!oa!(;, uu M,_pnu;,
on!un(!o;,
on M:ou! ::!n:. I245o73 *** I245o73 `u,
}
Fggelkek s trgymutat 1206
Ez a kvetkez eredmnyt adja:
E!:o ::!n:. I245o73 *** I2457-Uo
M:ou! ::!n:. I2 45 o73 *** I,2457-Uo
Jegyezzk meg, hogy az !un(; az adatfolyamban msolatot trol paramterrl. Kvet-
kezskppen az adatfolyam akkor is tmaszkodhat egy megtlttt loklra, ha annak erede-
ti pldnya mr nem ltezik. Ha a bemeneti adatfolyam szmra be van lltva a oo!a!pa
jelzbit (21.2.2, 21.4.1), a :n s Ja!: rtkeket a :nuau(; s a Ja!:uau(; ltal
visszaadott karakterlncok jellhetik, ms esetben a U s az I.
A unupnu _,uau vltozata (D.4, D.4.1) is adott:
up!a !a:: >
!a:: :u..unupnu_,uau . pn!! unupnu> { * * },
D.4.2.2. Szmok kirsa
Az tmeneti trba val rskor (21.6.4) a kimeneti adatfolyamok (o::au) a unu_pn jel-
lemzre tmaszkodnak:
up!a !a:: , !a:: On o::aunJ_!:ao:> >
!a:: :u..unu_pn . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ On !:_,p,
.p!!! unu_pn(:!z_ : U;,
a : : !!,z: az : auaJo!,au uu! :ua poz:!o:a.
On pn(On , !o:_a:& :, J!!!, oo! :; ou:,
On pn(On , !o:_a:& :, J!!!, !oug :; ou:,
On pn(On , !o:_a:& :, J!!!, nu:!guu !oug :; ou:,
On pn(On , !o:_a:& :, J!!!, uon! :; ou:,
On pn(On , !o:_a:& :, J!!!, !oug uon! :; ou:,
On pn(On , !o:_a:& :, J!!!, ou: :o!u* :; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-unu_pn(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
D. Helyi sajtossgok 1207
Az On kimeneti bejr (itertor) paramter azonostja, hogy a pn(; a szmrtket jell ka-
raktereket hol helyezi el a kimeneti adatfolyam tmeneti trban (21.6.4). A pn(; rtke az
a bejr (iterator), amely egy hellyel az utols karakter mg mutat.
Jegyezzk meg, hogy a unu_pn alaprtelmezett vltozata (amelynek bejrjval
o::aunJ_!:ao:> tpus karakterekhez lehet hozzfrni) a szabvnyos !oa!-ek
(D.4) rsze. Ha ms vltozatot akarunk hasznlni, akkor azt magunknak kell elksztennk:
up!a!a:: >
!a:: S:!ug_unupn . pn!! :u..unu_pn, ,puau a:!_::!ug>..!:ao:> {
pn!!.
S:!ug_unupn(; . unu_pn, ,puau a:!_::!ug>..!:ao:>(I; { }
},
:o!u J(!u !, ::!ug& :, !u po:; ! Jo:uz:a :-, a po: poz:!oo! zu:
{
S:!ug_unupna:> J,
!o:_a:& ... on, a on Jo:uz:! :za!,a!ua a:zu!aa
Jpn(:g!u(;-po:, ..., , !;, ! Jo:uz:a :-
}
Az !o:_a: paramterrel a formtumrl s a loklrl kaphatunk informcit. Pldul ha
res helyeket kell kitltennk, az !o:_a: paramter ltal megadott J!!! karakter lesz fel-
hasznlva. Az tmeneti tr, amelybe -n keresztl runk, ltalban ahhoz az o::au-hez
kapcsoldik, amelynek : a bzisosztlya. Az !o:_a: objektumokat nem knny ltrehoz-
ni, mert a formtummal kapcsolatban tbb dolgot is szablyoznak s ezeknek egysgesnek
kell lennik, hogy a kimenet elfogadhat legyen. Kvetkezskppen az !o:_a: osztly-
nak nincs nyilvnos konstruktora (21.3.3).
A pn(; fggvnyek szintn !o:_a: paramtereket hasznlnak az adatfolyam lokljnak le-
krdezshez. A lokl hatrozza meg az elvlaszt karaktereket (D.4.2.1), a logikai rtkek
szveges brzolst s a -ra val talaktst. Pldul ha feltesszk, hogy a pn(; fgg-
vny !o:_a: paramtere :, a pn(; fggvnyben ehhez hasonl kdot tallhatunk:
ou: !oa!& !o :g!o(;,

ua:_ u n:_Ja ,pa:> >(!o;u!uu(;, a!a:: a:-:o! -:a

::!ug pu n:_Ja unupnua:> >(!o;u!ua!_po!u(;, a!ap:!uz:.

::!ug J!: n:_Ja unupnua:> >(!o;Ja!:uau(;, a!ap:!uz:. Ja!:
Fggelkek s trgymutat 1208
A unu_pna:>-hoz hasonl szabvnyos Ja-eket a szabvnyos I/O adatfolyam-fgg-
vnyek ltalban automatikusan hasznljk, gy a legtbb programoznak nem is kell tud-
nia rluk. rdemes azonban szemgyre venni, hogy a standard knyvtr fggvnyei ho-
gyan hasznljk a jellemzket, mert jl mutatja, hogyan mkdnek a be- s kimeneti
adatfolyamok, illetve a Ja-ek. Mint mindig, a standard knyvtr most is rdekes progra-
mozsi eljrsokra mutat pldkat.
A unu_pn felhasznlsval az o::au ksztje a kvetkezket rhatja:
up!a!a:: , !a:: T:>
o::au& :u..a:!_o::au,T:>..op:ao:(uon! u;
{
:u:, gna:u(*!:;, !:u 2I3
!J (:gna:u; :n:u *!:,
:, {
!J (n:_Ja unu_pn> >(g!o(;;pn(*!:,*!:,!:->J!!!(;,u;Ja!!u(;;
::a(au!;,
}
a (; {
auu!_!o.p!ou(*!:;,
}
:n:u *!:,
}
Itt sok minden trtnik. Az rszem (sentry) biztostja, hogy minden mvelet vgrehajt-
dik (21.3.8). Az o::au lokljt a g!o(; tagfggvny meghvsval kapjuk meg, majd
a loklbl a n:_Ja sablon fggvnnyel (D.3.1) kiszedjk a unu_pn jellemzt. Miutn
ezt megtettk, meghvjuk a megfelel pn(; fggvnyeket az igazi munka elvgzshez.
A pn(; els kt paramtert knnyen megadhatjuk, hiszen az o::au-bl ltrehozhatjuk
az o::au_nJ bejrt (19.2.6), az adatfolyamot pedig automatikusan !o:_a: bzisosz-
tlyra alakthatjuk (21.2.1).
A pn(; kimeneti bejr paramtert adja vissza. A bejrt egy a:!_o::au-bl szerzi
meg, gy annak tpusa o::aunJ_!:ao:. Kvetkezskppen a Ja!!u(; (19.2.6.1) rendel-
kezsnkre ll a hibaellenrzshez s lehetv teszi szmunkra, hogy megfelelen bellt-
hassuk az adatfolyam llapott.
Nem hasznltuk a a:_Ja fggvnyt, mert a szabvnyos Ja-ek (D.4) garantltan ott
vannak minden loklban. Ha ez a garancia nem ll fenn, au_a: kivtel kivltsra kerl
sor (D.3.1).
D. Helyi sajtossgok 1209
A pn(; a uo_pn virtulis fggvnyt hvja meg. Kvetkezskppen lehet, hogy felhasznli
kd hajtdik vgre s az op:ao:(;-nek fel kell kszlnie arra, hogy a fellrt uo_pn(;
ltal kivltott kivtelt kezelje. Tovbb lehet, hogy a unu_pn nem ltezik valamilyen ka-
raktertpusra, gy a n:_Ja(; az :u..au_a: kivtelt vlthatja ki (D.3.1). A beptett t-
pusokra, mint amilyen a uon!, a viselkedst a C++ szabvny rja le. Kvetkezskp-
pen nem az a krds, hogy a auu!_!o.p!ou(; fggvnynek mit kell csinlnia, hanem
az, hogyan csinlja azt, amit a szabvny elr. Ha a au! jelzbit az o::au kivtel lla-
potra van lltva (21.3.6), a kivtel tovbbdobsra kerl sor, ms esetben a kivtel keze-
lse az adatfolyam llapotnak belltst s a vgrehajts folytatst jelenti. A au! jelz-
bitet mindkt esetben az adatfolyam llapotra kell lltani (21.3.3):
up!a!a:: , !a:: T:>
:o!u auu!_!o.p!ou(:u..a:!_o::au,T:>& :; a a ::zo! ug:::a
{
!J (:.p!ou:(;&!o:_a:..au!; {
:, {
:::a(!o:_a:..au!;, } a(; { }
:ou, o:uo:
}
:::a(!o:_a:..au!;, a:!_!o:..Ja!!n: !:! :!a !
}
A :, blokk azrt szksges, mert a ::a(; a:!_!o:..Ja!!n: kivtelt vlthat ki (21.3.3,
21.3.6). Ha azonban a au! a kivtel llapotra lltott, az op:ao:(;-nek tovbb kell
dobnia azt a kivtelt, amely a auu!_!o.p!ou(; meghvst okozta (nem pedig egysze-
ren a:!_!o:..Ja!!n: kivtelt kell kivltania).
A -t gy kell megvalstani a beptett tpusokra, pldul a uon!-ra, hogy kzvetlenl
az adatfolyam tmeneti trba rjunk. Ha a -t felhasznli tpusra rjuk meg, az ebbl ere-
d nehzsgeket gy kerlhetjk el, hogy a felhasznli tpusok kimenett mr meglv t-
pusok kimenetvel fejezzk ki (D.3.2).
D.4.2.3. Szmok bevitele
A bemeneti adatfolyamok (!::au) a unu_g jellemzre tmaszkodnak az tmeneti trbl
(21.6.4) val olvasshoz:
up!a !a:: , !a:: 1u !::aunJ_!:ao:> >
!a:: :u..unu_g . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ 1u !:_,p,
Fggelkek s trgymutat 1210
.p!!! unu_g(:!z_ : U;,
o!:a:: /.;-o! :-, az :-!! Jo:uz:! :za!,o a:zu!a:a!,
!a!z: az : !!:::a!.
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, oo!& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, !oug& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, nu:!guu :o:& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, nu:!guu !u& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, nu:!guu !oug& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, J!oa& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, uon!& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, !oug uon!& :; ou:,
1u g(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, :o!u*& :; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-unu_g(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
A unu_g szerkezete alapveten a unu_pn-hoz (D.4.2.2) hasonl. Mivel inkbb olvas,
mint r, a g(;-nek egy bejrprra van szksge, az olvass clpontjt meghatroz para-
mter pedig egy referencia. Az !o:a tpus : vltoz gy van belltva, hogy tkrzze az
adatfolyam llapott. Ha a kvnt tpus rtket nem lehet beolvasni, az :-ben a Ja!!! be-
lltsra kerl sor, ha elrtk a bemenet vgt, az oJ!-re. A bemeneti mveletek az :-t
arra hasznljk, hogy eldntsk, hogyan lltsk be az adatfolyam llapott. Ha nem trtnt
hiba, a beolvasott rtk a :-n keresztl rtkl addik; egybknt a : vltozatlan marad.
Az !::au ksztje a kvetkezket rhatja:
up!a!a:: , !a:: T:>
!::au& :u..a:!_!::au,T:>..op:ao:>>(uon!& u;
{
:u:, gna:u(*!:;, !:u 2I3
!J (:gna:u; :n:u *!:,
!o:a :a U, o
!::aunJ_!:ao:> o:,
uon! uu,
:, {
n:_Ja unu_g> >(g!o(;;g(*!:,o:,*!:,:a,uu;,
}
D. Helyi sajtossgok 1211
a (; {
auu!_!o.p!ou(*!:;, !:u D422
:n:u *!:,
}
!J (:aU '' :aoJ!; u uu, u :u !!::a :a ao:,
a a g(; :!::! :
::a(:a;,
:n:u *!:,
}
Az !::au szmra megengedett kivteleket hiba esetn a ::a(; fggvny vltja ki
(21.3.6).
Egy unupnu jellemzt mint amilyen a u,_unupnu a D.4.2 pontban megadva nem
szabvnyos elvlaszt karaktereket hasznlva is olvashatunk:
:o!u J(;
{
on E!:o ::!n:.
!u !I,
uon! uI,
!u >> !I >> uI, o!:a:: a :za:u,o: I245o73 Jo:ua a:zu!a:a!
!oa! !o(!oa!..!a::!(;, uu M,_pnu;,
!u!un(!o;,
on M:ou! ::!n:.
!u !2,
uon! u2,
!u >> !I >> u2, o!:a:: a I2 45 o73 Jo:ua a:zu!a:a!
}
Ha igazn ritkn hasznlt szmformtumokat szeretnnk beolvasni, fell kell rnunk
a uo_g(; fggvnyt. Megadhatunk pldul egy unu_g Ja-et, amely rmai szmokat
olvas be ( AA1 vagy MM, D.6[15]).
D.4.3. Pnzrtkek be- s kivitele
A pnzsszegek formzsnak mdja az egyszer szmok formzshoz hasonlt
(D.4.2), az elbbinl azonban a kulturlis eltrsek jelentsge nagyobb. A negatv ssze-
geket (vesztesg, tartozs, mondjuk -1,25) pldul egyes helyeken pozitv szmokknt, z-
rjelben kell feltntetni (1,25). Az is elfordulhat, hogy a negatv sszegek felismersnek
megknnytsre szneket kell hasznlnunk.
Fggelkek s trgymutat 1212
Nincs szabvnyos pnz tpus. Ehelyett kifejezetten a pnz Ja-eket kell hasznlnunk
az olyan szmrtkeknl, amelyekrl tudjuk, hogy pnzsszegeket jelentenek:
!a:: Mou, { g,:z:n :pn: puzo::zg :o!::a
!oug !u auonu,
pn!!.
Mou,(!oug !u !; . auonu(!; { }
op:ao: !oug !u(; ou: { :n:u auonu, }
},

:o!u J(!oug !u !;
{
on E: ! O::zg Mou,(!; uu!,
}
Ezen Ja-ek feladata az, hogy jelentsen megknnytsk az olyan kimeneti mveletek
megrst a Mou, tpusra, melyek az sszeget a helyi szoksoknak megfelelen rjk ki
(lsd D.4.3.2-t). A kimenet a on lokljtl fggen vltozik:
E: I245o7 O::zg !I245o7
E: I245o7 O::zg I245,o7 DKK
E: -I245o7 O::zg !-I245o7
E: -I245o7 O::zg -!I245o7
E: -I245o7 O::zg (H1I245,o7;
A pnzrtkek esetben rendszerint alapvet a legkisebb pnzegysgre is kiterjed pontos-
sg. Kvetkezskppen n azt a szokst kvetem, hogy inkbb a fillrek (penny, re, cent
stb.) szmt brzolom egsz rtkekkel, nem a forintokt (font, korona, dnr, eur stb.).
Ezt a megoldst a uou,_pnu J:a_u!g!:(; fggvnye tmogatja (D.4.3.1). A tizedes-el-
vlasztt a u!ua!_po!u(; adja meg.
A uou,_a: jellemz ltal lert formtumon alapul bemenetet s kimenetet kezel fgg-
vnyeket a uou,_g s uou,_pn Ja-ek biztostjk.
A be- s kimeneti formtum szablyozsra s a pnzrtkek trolsra egy egyszer
Mou, tpust hasznlhatunk. Az els esetben a pnzsszegek trolsra hasznlt (ms) t-
pusokat kirs eltt a Mou, tpusra alaktjuk, a beolvasst pedig szintn Mou, tpus vl-
tozkba vgezzk, mieltt az rtkeket ms tpusra alaktannk. Kevesebb hibval jr, ha
a pnzsszegeket kvetkezetesen a Mou, tpusban troljuk: gy nem feledkezhetnk meg
arrl, hogy egy rtket Mou, tpusra alaktsunk, mieltt kirnnk s nem kapunk bemene-
ti hibkat, ha a lokltl fggetlenl prblunk pnzsszegeket beolvasni. Lehetsges azon-
ban, hogy a Mou, tpus bevezetse kivitelezhetetlen, ha a rendszer nincs felksztve r.
Ilyen esetekben szksges a Mou,-konverzikat alkalmazni az olvas s r mveleteknl.
D. Helyi sajtossgok 1213
D.4.3.1. A pnzrtkek formtuma
A pnzsszegek megjelentst szablyoz uou,pnu termszetesen a kznsges sz-
mok formtumt megad unupnu Ja-re (D.4.2.1) hasonlt:
!a:: :u..uou,_a: {
pn!!.
unu pa: { uou, :pa, :,uo!, :!gu, :a!n }, az !:uuz: ::z!
::n pa:u { a: J!!u/4], }, !:uuz:
},
up!a !a:: , oo! 1u:ua!oua! Ja!:>
!a:: :u..uou,pnu . pn!! !oa!..Ja, pn!! uou,_a: {
pn!!.
,puJ a:_,p,
,puJ a:!_::!ug> ::!ug_,p,
.p!!! uou,pnu(:!z_ : U;,
u!ua!_po!u(; ou:, a !a::!(; !o!au
on:auu:_:p(; ou:, , a !a::!(; !o!au
::!ug g:onp!ug(; ou:, a !a::!(; !o!au, !u:. u!u: :opo:o:::
::!ug_,p n::_:,uo!(; ou:, ! a !a::!(; !o!au
::!ug_,p po:!!:_:!gu(; ou:, a !a::!(; !o!au
::!ug_,p uga!:_:!gu(; ou:, - a !a::!(; !o!au
!u J:a_u!g!:(; ou:, :zug, :zua a !zu::::zo nu, 2 a !a::!(; !o!au
pa:u po:_Jo:ua(; ou:, { :,uo!, :!gu, uou, :a!n } a !a::!(; !o!au
pa:u ug_Jo:ua(; ou:, { :,uo!, :!gu, uou, :a!n } a !a::!(; !o!au
:a! ou: oo! !u! 1u:ua!oua!, a uuzoz! puzJo:unu a:zu!aa
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-uou,pnu(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
A uou,pnu szolgltatsait elssorban a uou,_pn s uou,_g jellemzk ksztinek
szntk (D.4.3.2, D.4.3.3).
A u!ua!_po!u(;, on:auu:_:p(;, s g:onp!ug(; tagok gy viselkednek, mint a velk
egyenrtk fggvnyek a unupnu Ja-ben.
Fggelkek s trgymutat 1214
A n::_:,uo!(;, po:!!:_:!gu(; s uga!:_:!gu(; tagok rendre a valutajelet (!, +, 1:1,
DK:), a pluszjelet s a mnuszjelet jell karakterlncot adjk vissza. Ha az 1u:ua!oua!
sablonparamter rtke :n volt, az !u! tag szintn :n lesz s a valutajelek nemzetkzi
brzolsa lesz hasznlatos. A nemzetkzi brzols egy ngy karakterbl ll karakterlnc:
USD
DKK
EUR
Az utols karakter ltalban szkz. A hrom bets valuta-azonostt az ISO-4217 szabvny
rja le. Ha az 1u:ua!oua! rtke Ja!:, helyi valutajelet !, + vagy + lehet hasznlni.
A po:_Jo:ua(; s ug_Jo:ua(; ltal visszaadott minta (pa:u) ngy rszbl (pa:) ll,
amelyek a szmrtk, a valutajel, az eljel s az reshely megjelentsnek sorrendjt adjk
meg. A leggyakoribb formtumok ezt a mintt kvetik:
-! I245 { :!gu, :,uo!, :pa, :a!n }, ao! a po:!!:_:!gu(; :!::za::! : -
!-I245 { :,uo!, :!gu, :a!n, uou }, ao! a po:!!:_:!gu(; :!::za::! : -
!I245 { :,uo!, :!gu, :a!n, uou }, ao! a po:!!:_:!gu(; :!::za::! :
!I245- { :,uo!, :a!n, :!gu, uou }
-I245 DKK { :!gu, :a!n, :pa, :,uo! }
(!I245; { :!gu, :,uo!, :a!n, uou }, ao! a uga!:_:!gu(; :!::za::! : (;
(I245DKK; { :!gu, :a!n, :,uo!, uou }, ao! a uga!:_:!gu(; :!::za::! : (;
A negatv szmok zrjeles brzolst a uga!:_:!gu(; fggvny visszatrsi rtkeknt
a (; karakterekbl ll karakterlncot definilva biztostjuk. Az eljel-karakterlnc els ka-
raktere oda kerl, ahol a :!gu (eljel) tallhat a mintban, a maradk pedig a minta tbbi
rsze utn kvetkezik. Ezt a megoldst leggyakrabban ahhoz a szoksos pnzgyi jells-
hez hasznljk, miszerint a negatv sszegeket zrjelben tntetik fel, de msra is fel lehet
hasznlni:
-!I245 { :!gu, :,uo!, :a!n, uou }, ao! a uga!:_:!gu(; :!::za::! : -
*!I245 :!!!, { :!gu, :,uo!, :a!n, uou }, ao! a uga!:_:!gu(;
:!::za::! : * :!!!,
Az eljel, rtk s szimblum (:!gu, :a!n, :,uo!) rtkek csak egyszer szerepelhetnek
a mintban. A maradk rtk :pa (reshely) vagy uou (semmi) lehet. Ahol :pa szere-
pel, oda legalbb egy reshely karakternek kell kerlnie, a uou nulla vagy tbb reshely
karaktert jelent (kivve ha a uou a minta vgn szerepel).
D. Helyi sajtossgok 1215
Ezek a szigor szablyok nhny ltszlag sszer mintt is megtiltanak:
pa:u pa { :!gu, :a!n, uou, uou }, !a. a :,uo! u!u: ugau:a
A u!ua!_po!u(; helyt a J:a_u!g!:(; fggvny jelli ki. A pnzsszegeket ltalban
a legkisebb valutaegysggel brzoljk (D.4.3), ami jellemzen a f egysg szzadrsze
(pldul cent s dollr), gy a J:a_u!g!:(; rtke ltalban 2.
Kvetkezzen egy egyszer formtum, Ja-knt megadva:
!a:: M,_uou,_!o . pn!! uou,pnua:,:n> {
pn!!.
.p!!! M,_uou,_!o(:!z_ : U; . uou,pnua:,:n>(:; { }
a:_,p uo_u!ua!_po!u(; ou: { :n:u , }
a:_,p uo_on:auu:_:p(; ou: { :n:u ,, }
::!ug uo_g:onp!ug(; ou: { :n:u `UU`UU`UU, }
::!ug_,p uo_n::_:,uo!(; ou: { :n:u USD , }
::!ug_,p uo_po:!!:_:!gu(; ou: { :n:u , }
::!ug_,p uo_uga!:_:!gu(; ou: { :n:u (;, }
!u uo_J:a_u!g!:(; ou: { :n:u 2, } 2 :zug, a !zu::::zo nu
pa:u uo_po:_Jo:ua(; ou:
{
:a! pa:u pa { :!gu, :,uo!, :a!n, uou },
:n:u pa,
}
pa:u uo_ug_Jo:ua(; ou:
{
:a! pa:u pa { :!gu, :,uo!, :a!n, uou },
:n:u pa,
}
},
Ezt a Ja-et hasznljuk a Mou, albbi be- s kimeneti mveleteiben is (D.4.3.2 s
D.4.3.3).
A uou,pnu _,uau vltozata (D.4, D.4.1) is adott:
up!a !a:: , oo! 1u! Ja!:>
!a:: :u..uou,pnu_,uau . pn!! uou,pnu, 1u!> { * * },
Fggelkek s trgymutat 1216
D.4.3.2. Pnzrtkek kirsa
A uou,_pn a uou,pnu ltal meghatrozott formtumban rja ki a pnzsszegeket.
Pontosabban, a uou,_pn olyan pn(; fggvnyeket nyjt, amelyek megfelelen form-
zott karakter-brzolsokat tesznek egy adatfolyam tmeneti trba:
up!a !a:: , !a:: On o::aunJ_!:ao:> >
!a:: :u..uou,_pn . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ On !:_,p,
,puJ a:!_::!ug> ::!ug_,p,
.p!!! uou,_pn(:!z_ : U;,
a : : !!,z: az uu! : poz:!o:a.
On pn(On , oo! !u!, !o:_a:& :, J!!!, !oug uon! :; ou:,
On pn(On , oo! !u!, !o:_a:& :, J!!!, ou: ::!ug_,p& :; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-uou,_pn(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
A , :, J!!! s : paramterek ugyanarra hasznlatosak, mint a unu_pn jellemz pn(; fgg-
vnyeiben (D.4.3.2.2). Az !u! paramter jelzi, hogy a szabvnyos ngykarakteres nemzet-
kzi valutaszimblum vagy helyi valutajel hasznlatos-e (D.4.3.1).
Ha adott a uou,_pn jellemz, a Mou, osztly szmra(D.4.3) kimeneti mveletet rhatunk:
o::au& op:ao:(o::au& :, Mou, u;
{
o::au..:u:, gna:u(:;, !:u 2I3
!J (:gna:u; :n:u :,
:, {
ou: uou,_pna:>& J n:_Ja uou,_pna:> >(:g!o(;;,
!J (u:a!_a:!oug uon!>(u;; { u !oug uon!-u :zo!ao
!J (Jpn(:,:n,:,:J!!!(;,u;Ja!!u(;; :::a(!o:_a:..au!;,
}
!: {
o::!ug::au :,
: u, a:a:!u-:zo!::a a!a:
D. Helyi sajtossgok 1217
!J (Jpn(:,:n,:,:J!!!(;,:::(;;Ja!!u(;; :::a(!o:_a:..au!;,
}
}
a (; {
auu!_!o.p!ou(:;, !:u D422
}
:n:u :,
}
Ha a !oug uon! nem elg pontos a pnzrtk brzolshoz, az rtket karakterlncc
alaktjuk s azt rjuk ki az ilyen paramtert vr pn(; fggvnnyel.
D.4.3.3. Pnzrtkek beolvassa
A uou,_g a uou,pnu ltal meghatrozott formtumnak megfelelen olvassa be
a pnzsszegeket. Pontosabban, a uou,_g olyan g(; fggvnyeket nyjt, amelyek
a megfelelen formzott karakteres brzolst olvassk be egy adatfolyam tmeneti trbl:
up!a !a:: , !a:: 1u !::aunJ_!:ao:> >
!a:: :u..uou,_g . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ 1u !:_,p,
,puJ a:!_::!ug> ::!ug_,p,
.p!!! uou,_g(:!z_ : U;,
o!:a:: /.;-o! :-, az :-!! Jo:uz:! :za!,o a:zu!a:a!,
!a!z: az : !!:::a!.
1u g(1u , 1u , oo! !u!, !o:_a:& :, !o:_a:..!o:a& :, !oug uon!& :; ou:,
1u g(1u , 1u , oo! !u!, !o:_a:& :, !o:_a:..!o:a& :, ::!ug_,p& :; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-uou,_g(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
A , :, J!!!, s : paramterek ugyanarra hasznlatosak, mint a unu_g jellemz g(; fgg-
vnyeiben (D.4.3.2.2). Az !u! paramter jelzi, hogy a szabvnyos ngykarakteres nemzet-
kzi valutaszimblum vagy helyi valutajel hasznlatos-e (D.4.3.1).
Fggelkek s trgymutat 1218
Megfelel uou,_g s uou,_pn jellemzprral olyan formban adhatunk kimenetet,
amelyet hiba s adatveszts nlkl lehet visszaolvasni:
!u ua!u(;
{
Mou, u,
u!! (!u>>u; on u `u,
}
Ez az egyszer program kimenett el kell, hogy fogadja bemenetknt is. St, ha a progra-
mot msodszor is lefuttatjuk az els futtats eredmnyvel, a kimenetnek meg kell egyez-
nie a program eredeti bemenetvel.
A Mou, osztly szmra a kvetkez megfelel bemeneti mvelet lehet:
!::au& op:ao:>>(!::au& :, Mou,& u;
{
!::au..:u:, gna:u(:;, !:u 2I3
!J (gna:u; :, {
!o:_a:..!o:a :a U, o
!::aunJ_!:ao:a:> o:,
::!ug ::,
n:_Ja uou,_ga:> >(:g!o(;;g(:,o:,:n,:a,::;,
!J (:aU '' :a!o:_a:..oJ!; { :a ao: !!: :,
a a g(; :!::! :
!oug !u ! ::o!(::_::(;,U,U;,
!J (::uoERNE;
:a ' !o:_a:..Ja!!!,
!:
u !, :a ao: !!:a u :, a az a!a:: !oug !u-: :!:n!
:::a(:a;,
}
}
a (; {
auu!_!o.p!ou(:;, !:u D422
}
:n:u :,
}
D. Helyi sajtossgok 1219
D.4.4. Dtum s id beolvassa s kirsa
Sajnos a C++ standard knyvtra nem nyjt megfelel dtum tpust, de a C standard knyv-
trbl alacsonyszint eszkzket rklt a dtumok s idtartomnyok kezelshez. Ezek
a C-beli eszkzk szolglnak a C++ rendszerfggetlen idkezel eszkzeinek alapjul.
A kvetkezkben azt mutatjuk be, hogyan igazthat a dtum s id megjelentse a lokl-
hoz, tovbb pldkat adunk arra, hogyan illeszthet be egy felhasznli tpus (Da) az
!o::au (21. fejezet) s !oa! (D.2) ltal nyjtott szerkezetbe. A Da tpuson keresztl
olyan eljrsokkal is megismerkednk, amelyek segthetik az idkezelst, ha nem ll ren-
delkezsnkre a Da tpus.
D.4.4.1. rk s idztk
A legtbb rendszer a legalacsonyabb szinten rendelkezik egy finom idztvel. A standard
knyvtr a !o(; fggvnyt bocstja rendelkezsnkre, amely megvalsts-fgg !o_
tpus rtkkel tr vissza. A !o(; eredmnye a 1OK_PER_SE makr segtsgvel sza-
blyozhat. Ha nincs hozzfrsnk megbzhat idmr eszkzhz, akkor gy mrhetjk
meg egy ciklus idejt:
!u ua!u(!u a:g, a:* a:g:/]; oI7
{
!u u ao!(a:g:/I];, 2U4I
!o_ I !o(;,
!J (I !o_(-I;; { !o_(-I; !u:. a !o(; uu unou!
:: Sauo: u!u: o:u`u,
.!(I;,
}
Jo: (!u ! U, ! u, !--; uo_:ou!ug(;, !uoz:o !!n:
!o_ 2 !o(;,
!J (2 !o_(-I;; {
:: T!:o:un!:`u,
.!(2;,
}
on uo_:ou!ug(; u a!a!ouua! :a!o :g:a:a
uon!(2-I;1OKS_PER_SE u:oup: : !gu,
(u::! :zu,:g. 1OKS_PER_SE p: u:oup:;`u,
}
Fggelkek s trgymutat 1220
Az oszts eltt vgrehajtott uon!(2-I; talakts azrt szksges, mert a !o_ lehet,
hogy egsz tpus. Az, hogy a !o(; mikor kezd futni, az adott nyelvi vltozattl fgg;
a fggvny az egyes programrszek futsi idejnek mrsre val. A !o(; ltal vissza-
adott I s 2 rtkeket alapul vve a uon!(2-I;1OK_PER_SE a rendszer legjobb k-
zeltse kt hvs kzt eltelt idre (msodpercben).
Ha az adott feldolgozegysgre nincs megadva a !o(; fggvny vagy ha az id tl hossz
ahhoz, hogy mrhet legyen, a !o(; a !o_(-I; rtket adja vissza.
A !o(; fggvny a msodperc tredktl legfeljebb nhny msodpercig terjed idtar-
tomnyok mrsre val. Pldul ha a !o_ egy 32 bites eljeles egsz s
a 1OK_PER_SE rtke 1 000 000, a !o(; fggvnnyel az idt 0-tl valamivel tbb mint
2000 msodpercig (krlbell fl rig) mrhetjk (a msodperc milliomod rszben).
A programokrl lnyegi mrseket kszteni nem knny. A gpen fut tbbi program je-
lentsen befolysolhatja az adott program futsi idejt, nehz megjsolni a gyorsttrazs
s az utastscsvek hatsait s az algoritmusok nagymrtkben fgghetnek az adatoktl is.
Ha meg akarjuk mrni egy program futsi idejt, futtassuk tbbszr s vegyk hibsnak
azokat az eredmnyeket, amelyek jelentsen eltrnek a tbbitl.
A hosszabb idtartomnyok s a naptri id kezelsre a standard knyvtr a !u_ tpust
nyjtja, amely egy idpontot brzol; valamint a u szerkezetet, amely az idpontokat r-
szeikre bontja:
,puJ megvalsts_fgg !u_, ug:a!o:::-Jnggo a:!u!a! :pn: (4II;
p: !uoa:ouu, :zo!::a,
!a!au 2 !: g:z
::n u {
!u u_:, a p: u:oup:! /U,oI], a oU : a oI :zoo-u:oup:
!u u_u!u, az o:a p:! /U,5U]
!u u_on:, a uap o:! /U,2]
!u u_uua,, a ouap uapa! /I,I]
!u u_uou, az : ouapa! /U,II], a U !u: aun: (J!g,!u. NEM /I.I2];
!u u_,a:, : IUUU oa, a U !u: IUUU, a IU2- 2UU2
!u u_uua,, uapo :a::uap oa /U,o], a U !u: :a::uap
!u u_,ua,, uapo aun: I oa /U,o5], a U !u: aun: I
!u u_!:u:, u,:! !uo:zu:: !zo
},
A szabvny csak azt biztostja, hogy a urendelkezik a fenti emltett !u tpus tagokkal, azt
nem, hogy a tagok ilyen sorrendben szerepelnek vagy hogy nincsenek ms tagok.
D. Helyi sajtossgok 1221
A !u_ s u tpusok, valamint a hozzjuk kapcsold szolgltatsok a !u> s
!u> fejllomnyokban tallhatk:
!o_ !o(;, o:a! :zua a p:og:au !uun!:a oa
!u_ !u(!u_* p;, ::u,: uap:! !uo
uon! u!JJ!u(!u_ 2, !u_ I;, 2-I u:oup:u
u* !oa!!u(ou: !u_* p;, *p : !,! !uo :z:!u
u* gu!u(ou: !u_* p;, *p : g:uu!! ozp!uo (MT; :z:!u, :ag, U
(!:aa!o: u:. oo:u!uau Uu!:::a! T!u, UT;
!u_ u!u(u* pu;, *pu : !u_ Jo:uau, :ag, !u_(-I;
a:* a:!u(ou: u* pu;, *pu g, ::!n: a:a:!ua! :zo!:a
p! Snu Sp Io UI.U.52 IU7`u
a:* !u(ou: !u_* ; { :n:u a:!u(!oa!!u(;;, }
Vigyzzunk: mind a !oa!!u(;, mind a gu!u(; statikusan lefoglalt objektumra mutat
*u tpus rtket ad vissza, vagyis mindkt fggvny kvetkez meghvsa meg fogja vl-
toztatni az objektum rtkt. Ezrt rgtn hasznljuk fel a visszatrsi rtket vagy msol-
juk a u-et olyan memriahelyre, amit mi felgyelnk. Ugyangy az a:!u(; fggvny is
statikusan lefoglalt karaktertmbre hivatkoz mutatt ad vissza.
A u tpus legalbb tzezer vnyi dtumot (ez a legkisebb egsz esetben krlbell
a [-32000,32000] tartomny) kpes brzolni. A !u_ azonban ltalban 32 bites (eljeles)
!oug !u. Ha msodperceket szmolunk, a !u_ nem sokkal tbb, mint 68 vet tud br-
zolni valamilyen 0. vtl mindkt irnyban. Az alapv rendszerint 1970, a hozz tarto-
z alapid pedig janur 1., 0:00 GMT (UTC). Ha a !u_ 32 bites eljeles egsz, akkor 2038-
ban futunk ki az idbl, hacsak nem terjesztjk ki a !u_-t egy nagyobb egsz tpusra,
ahogy azt mr nhny rendszeren meg is tettk.
A !u_ alapveten arra val, hogy a jelenhez kzeli idt brzoljuk, ezrt nem szabad
elvrnunk, hogy kpes legyen az [1902,2038] tartomnyon kvli dtumokat is jellni. Ami
mg ennl is rosszabb, nem minden idkezel fggvny kezeli azonos mdon a negatv
szmokat. A hordozhatsg miatt az olyan rtkeknek, amelyeket u-knt s !u_-knt
is brzolni kell, az [1920,2038] tartomnyba kell esnik. Azoknak, akik az 1970-tl 2038-ig
terjed idkereten kvli dtumokat szeretnnek brzolni, tovbbi eljrsokat kell kidol-
gozniuk ahhoz, hogy ezt megtehessk.
Fggelkek s trgymutat 1222
Ennek egyik kvetkezmnye, hogy az u!u(; hibt eredmnyezhet. Ha az u!u(; pa-
ramtert nem lehet !u_-knt brzolni, a fggvny a !u_(-I; hibajelzst adja vissza.
Ha van egy sokig fut programunk, gy mrhetjk le futsi idejt:
!u ua!u(!u a:g, a:* a:g:/]; oI7
{
!u_ I !u(U;,
uo_a_!o(a:g,a:g:;,
!u_ 2 !u(U;,
uon! u u!JJ!u(2,I;,
on uo_a_!o(; :g:a:a u u:oup:!g a:o`u,
}
Ha a !u(; paramtere nem U, a fggvny eredmnyeknt kapott id rtkl addik
a fggvny !u_ tpusra mutat paramternek is. Ha a naptri id nem elrhet (mond-
juk valamilyen egyedi processzoron), akkor a !u_(-I; rtk addik vissza. A mai dtu-
mot a kvetkezkppen prblhatjuk meg vatosan megllaptani:
!u ua!u(;
{
!u_ ,
!J (!u(&; !u_(-I;; { a !u_(-I; !u:. a !u(; uu unou!
:: z !uo uu !!ap:ao ug`u,
.!(I;,
}
u* g gu!u(&;,
on g->u_uou-I g->u_uua, IUUU-g->u_,a: uu!,
}
D.4.4.2. Egy dtum osztly
Amint a 10.3 pontban emltettk, nem valszn, hogy egyetlen Da tpus minden ignyt
ki tud szolglni. A dtum felhasznlsa tbbfle megvalstst ignyel s a XIX. szzad eltt
a naptrak nagyban fggtek a trtnelem szeszlyeitl. Pldaknt azonban a 10.3-hoz ha-
sonlan kszthetnk egy Da tpust, a !u_ tpust felhasznlva:
!a:: Da {
pn!!.
unu Mou { auI, J, ua:, ap:, ua,, nu, n!, ang, :p, o, uo:, u },
D. Helyi sajtossgok 1223
!a:: 1au_ua {},
Da(!u uu, Mou uu, !u ,,;,
Da(;,
J:!uu o::au& op:ao:(o::au& :, ou: Da& u;,

p:!:a.
!u_ u, :za:u,o: unu- : !uo:zo!:
},
Da..Da(!u uu, Mou uu, !u ,,;
{
u . { U },
!J (uuU '' Iuu; :ou 1au_ua(;, !g,:z:n::. !:u IUI
.u_uua, uu,
!J (uuau '' uuu; :ou 1au_ua(;,
.u_uou uu-I, a u_uou U a!ap
.u_,a: ,,-IUUU, a u_,a: IUUU a!ap
u u!u(&.;,
}
Da..Da(;
{
u !u(U;, a!ap:!uz Da. a ua! uap
!J (u !u_(-I;; :ou 1au_ua(;,
}
A feladat az, hogy a s >> opertorokat a Da tpusra loklfgg mdon valstsuk
meg.
D.4.4.3. Dtum s id kirsa
A unu_pn Ja-hez (D.4.2) hasonlan a !u_pn is pn(; fggvnyeket ad, hogy bejr-
kon keresztl az tmeneti trakba rhassunk:
up!a !a:: , !a:: On o::aunJ_!:ao:> >
!a:: :u..!u_pn . pn!! !oa!..Ja {
pn!!.
,puJ a:_,p,
,puJ On !:_,p,
.p!!! !u_pn(:!z_ : U;,
Fggelkek s trgymutat 1224
::: az : auaJo!,au uu! :a -u ::zn!, az Ju Jo:unu a:zu!a:a!
On pn(On , !o:_a:& :, J!!!, ou: u* ,
ou: * Ju_, ou: * Ju_; ou:,
On pn(On , !o:_a:& :, J!!!, ou: u* , a: Ju, a: uou U; ou:
{ :n:u uo_pn(,:,J!!!,,Ju,uou;, }
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-!u_pn(;,
:!:na! On uo_pn(On, !o:_a:&, , ou: u*, a:, a:; ou:,
},
A pn(,:,J!!!,,Ju_,Ju_; a -ben lv dtumot -n keresztl az : adatfolyam tmeneti t-
rba helyezi. A J!!! karakterek a kitltshez hasznlatosak, ha az szksges. A kimeneti for-
mtumot a p:!uJ(; formzhoz hasonl (Ju_,Ju_; formzsi karakterlnc (vagy form-
tumvezrl) hatrozza meg. A tnyleges kimenethez a p:!uJ(;-hez hasonl (21.8)
formtum hasznlatos, ami a kvetkez klnleges formtumvezrlket tartalmazhatja:
%a A ht napjnak rvidtett neve (pl. Szo)
% A ht napjnak teljes neve (pl. Szombat)
% A hnap rvidtett neve (pl. Feb)
%1 A hnap teljes neve (pl. Februr)
% A dtum s id (pl. Szo Feb 06 21:46:05 1999)
%u A hnap napja [01,31] (pl. 06)
%H ra [00,23] (pl. 21)
%1 ra [01,12] (pl. 09)
% Az v napja [001,366] (pl. 037)
%u Az v hnapja [01,12] (pl. 02)
%M Perc [00,59] (pl. 48)
%p Dleltt/dlutn (am./pm. de./du.) jelzse a 12 rs rhoz (pl. PM)
%S Msodperc [00,61] (pl. 48)
%U Az v hete [00,53] vasrnappal kezdden (pl. 05): az 1. ht az els va-
srnappal kezddik
%u A ht napja [0,6]: a 0 jelenti a vasrnapot (pl. 6)
%W Az v hete [00,53] htfvel kezdden (pl. 05): az 1. ht az els htfvel
kezddik
%. Dtum (pl. 02/06/99)
%A Id (pl.21:48:40)
%, v az vszzad nlkl [00,99] (pl. 99)
% v (pl.1999)
%2 Az idzna jelzse (pl. EST), ha az idzna ismert
D. Helyi sajtossgok 1225
Ezeket az igen rszletes formz szablyokat a bvthet I/O rendszer paramtereiknt
hasznlhatjuk, de mint minden egyedi jellst, ezeket is clszerbb (s knyelmesebb) csak
eredeti feladatuk elltsra alkalmazni.
A fenti formz utastsokon tl a legtbb C++-vltozat tmogatja az olyan mdostkat
(modifier), mint amilyen a mezszlessget (21.8) meghatroz egsz szm (%IUA).
Az id- s dtumformtumok mdosti nem kpezik rszt a C++ szabvnynak, de nhny
platformszabvny, mint a POSIX, ignyelheti azokat. Kvetkezskppen a mdostkat ne-
hz elkerlni, mg akkor is, ha nem tkletesen hordozhatk.
A !u> vagy !u> fejllomnyban tallhat az :p:!uJ(;-hez (21.8) hasonl
::J!u(; fggvny a kimenetet az id- s dtumformz utastsok felhasznlsval hozza
ltre:
:!z_ ::J!u(a:* :, :!z_ ua., ou: a:* Jo:ua, ou: u* up;,
A fggvny legfeljebb ua. karaktert tesz a *up-bl s a Jo:ua-bl a *:-be, a Jo:ua for-
mznak megfelelen:
!u ua!u(;
{
ou: !u ua. 2U, au,ag. aau :z!, og, az ::J!u(;
:oa :u :uuu,z 2U-u! o a:a:
a: nJ/ua.],
!u_ !u(U;,
::J!u(nJ,ua.,%`u,!oa!!u(&;;,
on nJ,
}
A fenti program az alaprtelmezett !a::!(; loklban (D.2.3) egy szerdai napon
Wuu:ua,-t fog kirni, dn loklban ou:uag-ot.
Azok a karakterek, melyek nem rszei a meghatrozott formtumnak, mint a pldban az
jsor karakter, egyszeren bemsoldnak az els (:; paramterbe.
Amikor a pn(; azonost egy J formtumkaraktert (s egy nem ktelez u mdostt), meg-
hvja a uo_pn(; virtulis fggvnyt, hogy az vgezze el a tnyleges formzst:
uo_pn(,:,J!!!,,J,u;.
Fggelkek s trgymutat 1226
A pn(,:,J!!!,,J,u; hvs a pn(; egyszerstett formja, ahol a formtumkarakter (J) s
a mdost (u) pontosan meghatrozott. Ezrt a
ou: a: Ju/] %IUA,
pn(, :, J!!!, , Ju, Ju-:!zoJ(Ju;;,
hvst a kvetkez alakra lehet rvidteni:
pn(, :, J!!!, , A, IU;,
Ha egy formtum tbbjtos karaktereket tartalmaz, annak az alaprtelmezett llapotban
(D.4.6) kell kezddnie s vgzdnie.
A pn(; fggvnyt felhasznlhatjuk arra is, hogy a Da szmra lokltl fgg kimeneti m-
veletet adjunk meg:
o::au& op:ao:(o::au& :, ou: Da& u;
{
o::au..:u:, gna:u(:;, !:u 2I3
!J (:gna:u; :n:u :,
u* up !oa!!u(&uu;,
:, {
!J (n:_Ja !u_pna:> >(:g!o(;;pn(:,:,:J!!!(;,up,.;Ja!!u(;;
:::a(!o:_a:..Ja!!!;,
}
a (; {
auu!_!o.p!ou(:;, !:u D422
}
:n:u :,
}
Mivel nincs szabvnyos Da tpus, nem ltezik alaprtelmezett formtum a dtumok be- s
kivitelre. Itt gy hatroztam meg a %. formtumot, hogy formzknt az . karaktert ad-
tam t. Mivel a g_!u(; fggvny (D.4.4.4) alaprtelmezett formtuma a %., valszn-
leg ez ll legkzelebb a szabvnyhoz. A D.4.4.5 pontban pldt is lthatunk arra, hogyan
hasznlhatunk ms formtumokat.
D. Helyi sajtossgok 1227
D.4.4.4. Dtum s id beolvassa
Mint mindig, a bemenet trkksebb, mint a kimenet. Amikor rtk kirsra runk kdot,
gyakran klnbz formtumok kzl vlaszthatunk. A beolvassnl emellett a hibkkal is
foglalkoznunk kell s nha szmtanunk kell arra, hogy szmos formtum lehetsges.
A dtum s id beolvasst a !u_g jellemzn keresztl kezeljk. Az alaptlet az, hogy
egy lokl !u_g Ja-je el tudja olvasni a !u_pn ltal ltrehozott idket s dtumokat.
Szabvnyos dtum- s idtpusok azonban nincsenek, ezrt a programoz egy !oa!-t
hasznlhat arra, hogy a kimenetet klnfle formtumoknak megfelelen hozza ltre. A k-
vetkez brzolsokat pldul mind ltre lehet hozni egyetlen kimeneti utastssal, gy,
hogy a !u_pn jellemzt (D.4.4.5) klnbz loklokbl hasznljuk:
{auna:, I5 IUUU
Tn::ua, I5 {auna:, IUUU
I5 {au IUUUD
Tn:: I5IUU
A C++ szabvny arra biztat, hogy a !u_g elksztsnl gy fogadjuk el a dtum- s id-
formtumokat, ahogy azt a POSIX s ms szabvnyok elrjk. A problma az, hogy nehz
szabvnyostani a dtum s id beolvassnak brmilyen mdjt, amely egy adott kultr-
ban szablyos. Blcsebb ksrletezni s megnzni, mit nyjt egy adott !oa! (D.6[8]). Ha
egy formtum nem elfogadott, a programoz msik, megfelel !u_g Ja-et kszthet.
Az id beolvassra hasznlt szabvnyos !u_g a !u_a: osztlybl szrmazik:
!a:: :u..!u_a: {
pn!!.
unu uao:u: {
uo_o:u:, u!u: :o::uu, :!g o !u !: :au (p! a uapa;
uu,, uap, ouap, : :o::uu
uu,, ouap, uap, : :o::uu
,uu, :, ouap, uap :o::uu
,uu :, uap, ouap :o::uu
},
},
Ezt a felsorolst arra hasznlhatjuk, hogy egyszerstsk a dtumformtumok elemzst.
Fggelkek s trgymutat 1228
A unu_g -hez hasonlan a !u_g is egy bemeneti bejrpron keresztl fr hozz t-
meneti trhoz:
up!a !a:: , !a:: 1u !::aunJ_!:ao:> >
!a:: !u_g . pn!! !oa!..Ja, pn!! !u_a: {
pn!!.
,puJ a:_,p,
,puJ 1u !:_,p,
.p!!! !u_g(:!z_ : U;,
uao:u: ua_o:u:(; ou: { :n:u uo_ua_o:u:(;, }
o!:a:: /.;-o! u-, az :-!! Jo:uz:! :za!,o a:zu!a:a!, !a!z: az :
!!:::a!.
1u g_!u(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* u; ou:,
1u g_ua(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* u; ou:,
1u g_,a:(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* u; ou:,
1u g_uua,(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* u; ou:,
1u g_uouuau(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* u; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-!u_g(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
A g_!u(; a uo_g_!u(; fggvnyt hvja meg. Az alaprtelmezett g_!u(; a %A for-
mtum (D.4.4) felhasznlsval gy olvassa be az idt, ahogy a lokl !u_pn(; fggv-
nye ltrehozza azt. Ugyangy a g_ua(; fggvny a uo_g_ua(;-et hvja meg s az alap-
rtelmezett g_!u(; a %. formtum felhasznlsval (D.4.4) a lokl !u_pn(;
fggvnynek eredmnye szerint olvas.
A Da tpusok legegyszerbb bemeneti mvelete valami ilyesmi:
!::au& op:ao:>>(!::au& :, Da& u;
{
!::au..:u:, gna:u(:;, !:u 2I3
!J (:gna:u; :n:u :,
!o:_a:..!o:a :: U,
u . { U },
!::aunJ_!:ao:a:,a:_:a!:a:> > uu,
D. Helyi sajtossgok 1229
:, {
n:_Ja !u_ga:> >(:g!o(;;g_ua(:,uu,:,::,&.;,
!J (::U '' ::!o:_a:..oJ!;
u Da(.u_uua,,Da..Mou(.u_uou-I;,.u_,a:-IUUU;,
!:
:::a(::;,
}
a (; {
auu!_!o.p!ou(:;, !:u D422
}
:n:u :,
}
A g_ua(:,uu,:,::,&.; hvs az !::au-rl val kt automatikus talaktson alapul: az
els : paramter egy !::aunJ_!:ao: ltrehozsra hasznlatos, a harmadik paramter-
knt szerepl : pedig az !::au bzisosztlyra, az !o:_a:-re alakul t.
A fenti bemeneti mvelet azon dtumok esetben mkdik helyesen, amelyek a !u_ t-
pussal brzolhatk.
Egy egyszer prba a kvetkez lenne:
!u ua!u(;
:, {
Da oua,,
on oua, uu!, ::: %. Jo:unuua!
Da u(I2, Da..ua,, IUU3;,
on u uu!,
Da uu,
u!! (!u >> uu; on uu uu!, az %. Jo:unuua! !:ozo
unuo o!:a::a
}
a (Da..1au_ua; {
on Ro::z unu. a p:og:au !!p`u,
}
A pn_!u _,uau vltozata (D.4, D.4.1) szintn adott:
up!a !a:: , !a:: On o::aunJ_!:ao:> >
!a:: :u..!u_pn_,uau . pn!! !u_pn,On> { * * },
Fggelkek s trgymutat 1230
D.4.4.5. Egy rugalmasabb Date osztly
Ha megprblnnk felhasznlni a D.4.4.2 pontbl a Da osztlyt a D.4.4.3-ban s
D.4.4.4-ben szerepl be- s kimenettel, hamarosan korltokba tkznnk:
1. A Da csak olyan dtumokat tud kezelni, amelyek a !u_ tpussal brzolha-
tk: ez ltalban az [1970,2038] tartomnyt jelenti.
2. A Da csak a szabvnyos formtumban fogadja el a dtumokat brmilyen le-
gyen is az.
3. A Da-nl a bemeneti hibk jelzse elfogadhatatlan.
4. A Da csak a: tpus adatfolyamokat tmogat, nem tetszleges karakter
tpusakat.
Egy hasznosabb bemeneti mvelet a dtumok szlesebb tartomnyt fogadn el, felismer-
ne nhny gyakori formtumot s megbzhatan jelenten a hibkat valamilyen jl hasznl-
hat formban. Ahhoz, hogy ezt elrjk, el kell trnnk a !u_ brzolstl:
!a:: Da {
pn!!.
unu Mou { auI, J, ua:, ap:, ua,, nu, n!, ang, :p, o, uo:, u },
::n 1au_ua {
ou: a:* u,,
1au_ua(ou: a:* p; . u,(p; { }
},
Da(!u uu, Mou uu, !u ,,, !u ua,_oJ_u U;,
Da(;,
:o!u ua_u(u* ; ou:, a Da u :zo!: *- :z!
!u_ ua_!u_(; ou:, a Da !u_ :zo!::a! : :!::za
!u ,a:(; ou: { :n:u ,, }
Mou uou(; ou: { :n:u u, }
!u ua,(; ou: { :n:u u, }

p:!:a.
a: u,
Mou u,
!u ,,
},
Itt az egyszersg kedvrt a (u,u,,; brzolshoz (10.2) trtnk vissza.
D. Helyi sajtossgok 1231
A konstruktort gy adhatjuk meg:
Da..Da(!u uu, Mou uu, !u ,,, !u ua,_oJ_u;
. u(uu;, u(uu;, ,(,,;
{
!J (uU && uMou(U; && ,U; :n:u, Da(U,U,U; a un!! unu
!J (uuau '' uuu; :ou 1au_ua(:o::z a ouap;,
!J (uuI '' Iuu; o:u !g,:z:n:::, !:u IUI
:ou 1au_ua(:o::z a ouap uapa;,
!J (ua,_oJ_u && ua,_!u_u(,,,uu,uu;:ua,_oJ_u;
:ou 1au_ua(:o::z a uapa;,
}
Da..Da(; . u(U;, u(U;, ,(U; { } g, un!! unu
A ua,_!u_u(; kiszmtsa nem knny s nem kapcsoldik a loklokhoz, ezrt kihagy-
tam. Ha szksgnk van r, rendszernkben biztosan megtallhatjuk valahol.
Az sszehasonlt mveletek mindig hasznosak az olyan tpusok esetben, mint a Da:
oo! op:ao:(ou: Da& ., ou: Da& ,;
{
:n:u .,a:(;,,a:(; && .uou(;,uou(; && .ua,(;,ua,(;,
}
oo! op:ao::(ou: Da& ., ou: Da& ,;
{
:n:u :(.,;,
}
Mivel eltrtnk a szabvnyos u s !u_ formtumoktl, szksgnk van konverzis
fggvnyekre is, amelyek egytt tudnak mkdni azokkal a programokkal, amelyek eze-
ket a tpusokat vrjk el:
:o!u Da..ua_u(u* p; ou: a unu *p- !,z:
{
u . { U },
*p .,
p->u_,a: ,-IUUU,
p->u_uua, u,
p->u_uou u-I,
}
!u_ Da..ua_!u_(; ou:
{
!J (,IU7U '' 2U3,; !g,:z:n::
Fggelkek s trgymutat 1232
:ou 1au_ua( unu a !u_ a:ouu,u ::n! :!;,
u .,
ua_u(&.;,
:n:u u!u(&.;,
}
D.4.4.6. Dtumformtumok megadsa
A C++ nem hatrozza meg a dtumok kirsnak szabvnyos formtumt (a %. kzelti meg
a legjobban; D.4.4.3), de mg ha ltezne is ilyen, valsznleg szeretnnk ms formtumo-
kat is hasznlni. Ezt gy tehetjk meg, hogy meghatrozunk egy alaprtelmezett formtu-
mot s lehetsget adunk annak mdostsra:
!a:: Da_Jo:ua {
:a! a: Ju/], a!ap:!uz Jo:unu
ou: a:* n::, az ppu ::u,: Jo:unu
ou: a:* n::_uu,
pn!!.
Da_Jo:ua(; . n::(Ju;, n::_uu(Ju-::!u(Ju;; { }
ou: a:* g!u(; ou: { :n:u n::, }
ou: a:* uu(; ou: { :n:u n::_uu, }
:o!u :(ou: a:* p, ou: a:* ; { n::p, n::_uu, }
:o!u :(ou: a:* p; { n::p, n::_uun::-::!u(p;, }
:a! ou: a:* uJan!_Ju(; { :n:u Ju, }
},
ou: a: Da_Jo:uaa:>..Ju/] %, %1 %u, %, p! 1:!ua,, 1:na:, 5, IUUU
Da_Jo:ua ua_Ju,
Hogy az ::J!u(; formtumot (D.4.4.3) hasznlhassuk, tartzkodtam attl, hogy
a Da_Jo:ua osztlyt a hasznlt karaktertpussal paramterezzem. Ebbl kvetkezik, hogy
ez a megolds csak olyan dtumjellst enged meg, amelynek formtumt ki lehet fejezni
a a:/] tpussal. Ezenkvl felhasznltam egy globlis formtum-objektumot is (ua_Ju),
az alaprtelmezett dtumformtum megadshoz. Mivel a ua_Ju rtkt meg lehet vltoz-
tatni, a Da formzshoz rendelkezsnkre ll egy meglehetsen nyers mdszer, ha-
sonlan ahhoz, ahogy a g!oa!(; loklt (D.2.3) lehet hasznlni a formzsra.
D. Helyi sajtossgok 1233
Sokkal ltalnosabb megolds, ha ltrehozzuk a Da_!u s Da_on Ja-eket az adatfo-
lyambl trtn olvass s rs vezrlshez. Ezt a megkzeltst a D.4.4.7 pontban mutat-
juk be. Ha adott a Da_Jo:ua, a Da..op:ao:(;-t gy rhatjuk meg:
up!a!a:: , !a:: T:>
a:!_o::au,T:>& op:ao:(a:!_o::au,T:>& :, ou: Da& u;
::: J!a:zu!o! Jo:unuua!
{
,puau a:!_o::au,T:>..:u:, gna:u(:;, !:u 2I3
!J (:gna:u; :n:u :,
u ,
uua_u(&;,
:, {
ou: !u_pn>& J n:_Ja !u_pn> >(:g!o(;;,
!J (Jpn(:,:,:J!!!(;,&,ua_Jug!u(;,ua_Juuu(;;Ja!!u(;;
:::a(!o:_a:..Ja!!!;,
}
a (; {
auu!_!o.p!ou(:;, !:u D422
}
:n:u :,
}
A a:_Ja fggvnnyel ellenrizhetnnk, hogy az : loklja rendelkezik-e a !u_pn>
jellemzvel, itt azonban egyszerbb gy kezelni a problmt, hogy minden kivtelt elka-
punk, amit a n:_Ja kivlt.
me egy egyszer tesztprogram, ami a kimenet formtumt a ua_Ju-n keresztl vezrli:
!u ua!u(;
:, {
u!! (!u >> uu && uu : Da(;; on uu uu!, ::: az a!ap:!uz
ua_Ju a:zu!a:a!
ua_Ju:(%%u%u;,
u!! (!u >> uu && uu : Da(;; on uu uu!, ::: az %%u%u
a:zu!a:a!
}
a (Da..1au_ua ; {
on Ro::z unu. u, uu!,
}
Fggelkek s trgymutat 1234
D.4.4.7. Bemeneti facet-ek dtumokhoz
Mint mindig, a bemenet kezelse kicsit nehezebb, mint a kimenet. Mgis, mivel
a g_ua(; javtott a felleten az alacsonyszint bemenethez s mert a D.4.4.4 pontban
a Da tpushoz megadott op:ao:>>(; nem frt hozz kzvetlen mdon a Da brzol-
shoz, az op:ao:>>(;-t vltozatlanul hasznlhatjuk. me az op:ao:>>(; sablon fggvny-
knt megrt vltozata:
up!a!a:: , !a:: T:>
!::au,T:>& op:ao:>>(!::au,T:>& :, Da& u;
{
,puau !::au,T:>..:u:, gna:u(:;,
!J (gna:u; :, {
!o:_a:..!o:a :: U,
u . { U },
!::aunJ_!:ao:,T:> uu,
n:_Ja !u_g> >(:g!o(;;g_ua(:,uu,:,::,&.;,
!J (::U '' ::!o:_a:..oJ!;
u Da(.u_uua,,Da..Mou(.u_uou-I;,.u_,a:-IUUU,.u_uua,;,
!:
:::a(::;,
}
a (; {
auu!_!o.p!ou(:;, !:u D422
}
:n:u :,
}
Ez a bemeneti mvelet az !::au !u_g jellemzjnek g_ua(; fggvnyt hvja meg,
gy a bemenet j, rugalmasabb formjt adhatjuk meg, gy, hogy szrmaztatssal egy j
Ja-et ksztnk a !u_g-bl:
up!a!a:: , !a:: 1u !::aunJ_!:ao:> >
!a:: Da_!u . pn!! :u..!u_g,1u> {
pn!!.
Da_!u(:!z_ : U; . :u..!u_g>(:; { }
p:ou.
1u uo_g_ua(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* up; ou:,
p:!:a.
unu !,p { uo:a!n, nuuouu, ua,oJu, uou },
1u g:a!(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, !u* :, !,p* ::; ou:,
},
D. Helyi sajtossgok 1235
A g:a!(; fggvnynek egy vet, egy hnapot, a hnap napjt s a ht egy napjt kell be-
olvasnia (ez utbbi nem ktelez), az eredmnyt pedig egy u szerkezetbe kell tennie.
A hnapok s a ht napjainak nevei a lokltl fggnek, kvetkezskppen nem emlthet-
jk meg azokat kzvetlenl a bemeneti fggvnyben. Ehelyett a hnapokat s napokat gy
ismerjk fel, hogy meghvjuk azokat a fggvnyeket, amelyeket a !u_g ad erre a clra:
a g_uouuau(; s g_uua,(; fggvnyeket (D.4.4.4).
Az v, a hnap napja s valsznleg a hnap is egszknt van brzolva. Sajnos egy szm
nem jelzi, hogy egy hnap napjt jelli-e vagy valami egszen mst. A 7 pldul jellhet j-
liust, egy hnap 7. napjt vagy ppen a 2007-es vet is. A !u_g ua_o:u:(; fggvny-
nek valdi clja az, hogy feloldja az effle tbbrtelmsgeket.
A Da_!u rtkeket olvas be, osztlyozza azokat, majd a ua_o:u:(; fggvnnyel megn-
zi, a bert adatok rtelmesek-e (illetve hogyan rtelmesek). A tnyleges olvasst az adat-
folyam tmeneti trbl, illetve a kezdeti osztlyozst a privt g:a!(; fggvny vgzi:
up!a!a:: , !a:: 1u>
1u Da_!u,1u>..g:a!(1u ,1u ,!o:_a:& :,!o:_a:..!o:a& :,!u* :,!,p* ::;
ou:
Da ::zu o!:a::a. :zu, a uapa :ag, ouap ::!, : :::! !p:
{
ou: ,p>& n:_Ja ,p> >(:g!o(;;, a ,p !:::
!:u D45-u
,
*:: uo:a!n, uu a!! :
Jo: (,,; { n::!, : :::! !p:
!J ( ; :n:u ,
*,
!J (:(!:(,p_a:..:pa,; '' !:(,p_a:..pnu,;;; :a,
--,
}
!J (!:(,p_a:..u!g!,;; { g:z o!:a::a a unupnu J!g,!uu ::n! ag,::a!
!u ! U,
uo { :zo!g: a:a::z! :zug,u u!u!!: : a!a::a
:a! a: ou: u!g!:/] UI245o73U,
! !*IU - J!uu(u!g!:,u!g!:-IU,ua::ou(, ;;-u!g!:,
*--,
} u!! (!:(,p_a:..u!g!,;;,
*: !,
Fggelkek s trgymutat 1236
*:: nuuouu, g, g:z, u uu nun, u! :zo!
:n:u ,
}
!J (!:(,p_a:..a!pa,;; { ouap u:u :ag, a uapua :::
a:!_::!ug> ::,
u!! (!:(,p_a:..a!pa,;; { a:a: o!:a::a a:a:!ua
:: - ,
!J (-- ; :a,
*,
}
u ,
a:!_::!ug::au> ::(::;,
,puJ !::aunJ_!:ao:> S1, :o:pn: az :: uu! :oz
g_uouuau(:::unJ(;,S1(;,:,:,&;, o!:a:: uuo:!a!! auaJo!,au
uu! :o!
!J ((:&(!o:_a:..au!'!o:_a:..Ja!!!;;U; {
*: u_uou,
*:: uou,
:n:u ,
}
: U, az auaJo!,au-!!apo o:!: a u:ou! o!:a:: !o
g_uua,(:::unJ(;,S1(;,:,:,&;, o!:a:: uuo:!a!! auaJo!,au
uu! :o!
!J ((:&(!o:_a:..au!'!o:_a:..Ja!!!;;U; {
*: u_uua,,
*:: ua,oJu,
:n:u ,
}
}
: ' !o:_a:..Ja!!!,
:n:u ,
}
Itt a trkks rsz a ht napjainak s a hnapoknak a megklnbztetse. Mivel bemene-
ti bejrkon keresztl olvasunk, nem olvashatjuk be /,;-t ktszer, elszr egy hnapot,
msodszor pedig egy napot keresve. Msfell nem tudjuk megklnbztetni a hnapokat
sem a napoktl, ha egyszerre csak egy karaktert olvasunk be, mert csak
a g_uouuau(; s a g_uua,(; fggvnyek tudjk, hogy az adott loklban a hna-
pok s a ht napjainak nevei mely karaktersorozatokbl plnek fel. Azt a megoldst v-
lasztottam, hogy az alfabetikus karakterekbl ll sorozatokat beolvastam egy karakterlnc-
ba, ebbl egy ::!ug::au-et ksztettem, majd ismtelten olvastam az adatfolyam
::aunJ-jbl.
D. Helyi sajtossgok 1237
A hibajelz rendszer kzvetlenl hasznlja az olyan llapotbiteket, mint az !o:_a:..au!.
Ez azrt szksges, mert az adatfolyam llapott kezel knyelmesebb fggvnyeket, mint
a !a:(; s a ::a(;, a a:!_!o: osztly hatrozza meg, nem pedig annak bzisosztlya,
az !o:_a: (21.3.3). Ha szksges, a >> opertor felhasznlja a g_ua(; ltal jelentett hi-
bkat, hogy visszalltsa a bemeneti adatfolyam llapott.
Ha a g:a!(; adott, elszr beolvashatjuk az rtkeket, majd ksbb megnzhetjk, hogy
rtelmesek-e. A uao:u:(; szerepe dnt lehet:
up!a!a:: , !a:: 1u>
1u Da_!u,1u>..uo_g_ua(1u , 1u , !o:_a:& :, !o:_a:..!o:a& :, u* up; ou:
a uapa (uu o!zo;, u!, uu, uu,, uu,, :ag, ,uu Jo:unu o:
{
!u :a!/], uap, ouap, : : : :zu:a
!,p ::/] { uo:a!n }, :o:z!,oz:oz
Jo: (!u !U, : && !, --!; { uap, ouap, : o!:a::a
g:a!(,,:,:,&:a!/!],&::/!];,
!J (:; :n:u , opp. !a
!J (::/!]uo:a!n; { uu nun Jzu! a unuo
: ' !o:_a:..au!,
:n:u ,
}
!J (::/!]ua,oJu; {
up->u_uua, :a!/!],
--!, opp. uu uap, ouap, :ag, :
}
}
!u_a:..uao:u: o:u: uao:u:(;, uo: ugp:o!nu :ouu!
a o!:a:o aua !u::
!J (::/U] uou; { uu, Jo:unu :ag, !a

}
!: !J (::/I] uou; { uu, :ag, ,uu :ag, !a
up->u_uou :a!/I],
:u! (o:u:;
{
a: uu,.
up->u_uua, :a!/U],
up->u_,a: :a!/2],
:a,
a: ,uu.
up->u_,a: :a!/U],
up->u_uua, :a!/I],
Fggelkek s trgymutat 1238
:a,
uJan!.
: ' !o:_a:..au!,
:n:u ,
}
}
!: !J (::/2] uou; { ,uu :ag, !a

}
!: { ug:znu a uao:u:-u :ag, !a

}
up->u_,a: - IUUU, az a!ap: !gaz::a a u-z
:n:u ,
}
Azokat a kdrszleteket, amelyek nem jrulnak hozz a loklok, dtumok s a bemenet ke-
zelsnek megrtshez, kihagytam. A jobb s ltalnosabb dtumformtumok beolvass-
ra alkalmas fggvnyek megrst feladatknt tztem ki (D.6[9-10]).
me egy egyszer tesztprogram:
!u ua!u(;
:, {
!u!un(!o(!oa!(;, uu Da_!u;;, unuo o!:a::a a Da_!u a:zu!a:a!
u!! (!u >> uu && uu : Da(;; on uu uu!,
}
a (Da..1au_ua ; {
on Ro::z unu. u, uu!,
}
Vegyk szre, hogy a uo_g_ua(; rtelmetlen dtumokat is elfogad, mint amilyen a
Tn::ua, Oo: 7, IUU3
s az
IUUU1I
Az v, hnap, nap s (nem ktelezen) a ht napja egysgessgnek ellenrzse a Da
konstruktorban trtnik. A Da osztlynak kell tudnia, mi alkot helyes dtumot, a Da_!u
osztllyal pedig nem kell megosztania ezt a tudst.
D. Helyi sajtossgok 1239
Meg lehetne oldani azt is, hogy a g_:a!(; vagy a g_ua(; fggvnyek prbljk kitall-
ni a szmrtkek jelentst. Pldul a
I2 Ma, IU22
vilgos, hogy nem a 12-es v 1922. napja. Azaz gyanthatnnk, hogy egy olyan szmrtk,
ami nem lehet a megadott hnap napja, biztosan vet jell. Az ilyen feltevsek bizonyos
esetekben hasznosak lehetnek, de nem j tlet ezeket ltalnosabb krnyezetben hasznl-
ni. Pldul a
I2 Ma, I5
a 12., 15., 1912.,1915., 2012., vagy 2015. vben jellhet egy dtumot. Nha jobb megkze-
lts, ha kiegsztjk a jellst valamivel, ami segt az vek s napok megklnbztets-
ben. Az angolban a I
:
s I5

pldul biztosan egy hnap napjait jellik. Ugyangy


a 75I1-t s az I45D-t (i.e.751 s i.sz.1453) nyilvnvalan vknt lehetne azonostani.
D.4.5. Karakterek osztlyozsa
Amikor a bemenetrl karaktereket olvasunk be, gyakran van szksg arra, hogy osztlyoz-
zuk azokat, hogy rtelmezhessk, mit is olvastunk. Egy szm beolvasshoz pldul egy
bemeneti eljrsnak tudnia kell, hogy a szmjegyeket mely karakterek jellik. A 6.1.2 pont-
ban is bemutattuk a szabvnyos karakterosztlyoz fggvnyek egy felhasznlst a beme-
net elemzsre.
Termszetesen a karakterek osztlyozsa fgg az ppen hasznlatos bctl. Ezrt rendelke-
zsnkre ll a ,p jellemz, amely az adott loklban a karakterek osztlyozst brzolja.
A karakterosztlyokat a ua: felsorol tpus hatrozza meg:
!a:: :u..,p_a: {
pn!!.
unu ua: { a u,!g: : ug:a!o:::Jnggo
:pa I, n::!, (a !o!au , `u, `, ;
p:!u II, u,ouaao a:a:
u:! I2, :z:!oa:a:
npp: I, uag,n
!ou: I4, !:n
a!pa I5, a!Ja!n: a:a:
u!g! Io, u!u!!: :zug,
pnu I7, :::!
Fggelkek s trgymutat 1240
.u!g! I3, .au!u!!: :zug,
a!unua!pa'u!g!, a!Jaunu:!n: a:a:
g:apa!unu'pnu
},
},
A ua: nem fgg egyetlen karaktertpustl sem, kvetkezskppen a felsorols egy (nem
sablon) bzisosztlyban tallhat.
Vilgos, hogy a ua: a hagyomnyos C s C++-beli osztlyozst tkrzi (20.4.1). Eltr ka-
rakterkszletek esetben azonban a klnbz karakterrtkek klnbz osztlyokba es-
nek. Az ASCII karakterkszletben pldul a I25 egsz rtk a } karaktert jelli, ami az rs-
jelekhez (pnu) tartozik, a dn karakterkszletben viszont az a magnhangzt, amit egy
dn !oa!-ben az a!pa osztlyba kell sorolni.
Az osztlyozst maszknak nevezik, mert a kis karakterkszletek hagyomnyos s hat-
kony osztlyozsa egy tblval trtnik, amelyben minden bejegyzs az osztly(oka)t br-
zol biteket trolja:
a!/a] !ou:'a!pa'.u!g!
a!/I] u!g!
a!/ ] :pa
Ha a fenti adott, a a!/]&unem nulla, ha a karakter az uosztlyba tartozik ( egy u),
egybknt pedig U.
A ,p defincija gy fest:
up!a !a:: >
!a:: :u..,p . pn!! !oa!..Ja, pn!! ,p_a: {
pn!!.
,puJ a:_,p,
.p!!! ,p(:!z_ : U;,
oo! !:(ua: u, ; ou:, az u o:z!,a a:oz!.
u!uuu /.;-!! o:z!,oz:a, az :uuu, a :- :n!
ou: * !:(ou: * , ou: * , ua:* :; ou:,
ou: * :au_!:(ua: u, ou: * , ou: * ; ou:, u :::
ou: * :au_uo(ua: u, ou: * , ou: * ; ou:, uu u :::
D. Helyi sajtossgok 1241
onpp:( ; ou:,
ou: * onpp:(* , ou: * ; ou:, /.; a!a::a
o!ou:( ; ou:,
ou: * o!ou:(* , ou: * ; ou:,
u!uu(a: ; ou:,
ou: a:* u!uu(ou: a:* , ou: a:* , * 2; ou:,
a: ua::ou( , a: uJ; ou:,
ou: * ua::ou(ou: * , ou: * , a: uJ, a:* 2; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-,p(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
Az !:(,u; hvssal vizsglhatjuk meg, hogy a karakter az u osztlyba tartozik-e:
!u onu_:pa:(ou: ::!ug& :, ou: !oa!& !o;
{
ou: ,pa:>& n:_Ja ,pa:> >(!o;,
!u ! U,
Jo:(::!ug..ou:_!:ao: p :g!u(;, p : :uu(;, --p;
!J (!:(,p_a:..:pa,*p;; --!, n::!, a uga:oz:a a!apu
:n:u !,
}
Az !:(; fggvnnyel azt is megnzhetjk, hogy egy karakter adott osztlyok valamelyikbe
tartozik-e:
!:(,p_a:..:pa',p_a:..pnu,;, n::!, :ag, :::! a!apu.
Az !:(,,:; hvs a /,;-ben szerepl minden karakter osztlyt eldnti s a megfelel po-
zcira helyezi azokat a : tmbben.
A :au_!:(u,,; egy mutatt ad vissza a /,;-ben lv els olyan karakterre, amely az u
osztlyba tartozik. Ha egyetlen karakter sem tartozik az u osztlyba, a fggvny az -t ad-
ja vissza. Mint mindig a szabvnyos Ja-ek esetben, a nyilvnos tagfggvny sajt uo_
virtulis fggvnyt hvja meg. Egy egyszer vltozat a kvetkez lenne:
Fggelkek s trgymutat 1242
up!a !a:: >
ou: * :u..,p>..uo_:au_!:(ua: u, ou: * , ou: * ; ou:
{
u!! (: && :!:(u,*;; --,
:n:u ,
}
A :au_uo(u,,; a /,;-ben lv els olyan karakterre ad vissza mutatt, amely nem tar-
tozik az u osztlyba. Ha minden karakter az u osztlyba tartozik, a fggvny az -t adja
vissza.
A onpp:(; a nagybets vltozatt adja vissza, ha az ltezik a hasznlatban lv karak-
terkszletben; klnben pedig magt a -t.
A onpp:(,; a /,; tartomnyban minden karaktert nagybetsre alakt s az -t adja
vissza. Ennek egy egyszer megvalstsa a kvetkez lehet:
up!a !a:: >
ou: * :u..,p>..o_npp:(* , ou: * ;
{
Jo: ( , :, --; * onpp:(*;,
:n:u ,
}
A o!ou:(; fggvnyek hasonlak a onpp:(;-hez, azzal az eltrssel, hogy kisbetsre ala-
ktanak.
A u!uu(; a karaktert a neki megfelel tpus rtkre alaktja. Ha a karakterksz-
lete tbb -nek megfelel karaktert nyjt, a szabvny a legegyszerbb sszer talakts
alkalmazst rja el. Pldul a
uon n:_Ja ,pua:_> >(uong!o(;;u!uu(;,
az karakternek a uon loklban lv sszer megfeleljt fogja kirni.
A klnbz karakterbrzolsok, mint az ASCII s az EBCDIC kztti talaktst szintn el
lehet vgezni a u!uu(; fggvnnyel. Pldul tegyk fel, hogy ltezik az u! lokl:
a: E1D1_ n:_Ja ,pa:> >(u!;u!uu(;,
A u!uu(,,:; hvs a /,; tartomnyban lv minden karakter szlestett vltozatt he-
lyezi a megfelel pozcira a : tmbben.
D. Helyi sajtossgok 1243
A ua::ou(,uJ; egy a: tpus rtket ad a tpus karakternek. Itt is a legegysze-
rbb sszer talaktst kell alkalmazni. Ha nem ltezik megfelel a: tpus rtk,
a fggvny a uJ-et adja vissza.
A ua::ou(,,:; hvs a /,; tartomnyban lv minden karakter leszktett vltozatt he-
lyezi a megfelel pozcira a : tmbben.
Az ltalnos elgondols az, hogy a ua::ou(; a nagyobb karakterkszletet alaktja egy ki-
sebbre, mg a u!uu(; ennek ellenkezjt vgzi. Egy kisebb karakterkszletben szerepl
karakter esetben elvrnnk a kvetkezt:
ua::ou(u!uu(;, U; uu ga:au!
Ez akkor igaz, ha a ltal brzolt karakternek csak egyetlen jellse van a kisebbik ka-
rakterkszletben, ez azonban nem biztos. Ha a a: tpussal brzolt karakterek nem k-
pezik rszhalmazt azoknak, amelyeket a nagyobb () karakterkszlet brzol, rendelle-
nessgekre s problmkra szmthatunk a karaktereket ltalnossgban kezel
programokban.
A nagyobb karakterkszletben lv karakterre ugyangy elvrnnk az albbit:
u!uu(ua::ou(,uJ;; '' u!uu(ua::ou(,uJ;; u!uu(uJ; uu ga:au!
Ez gyakran teljesl, de a nagyobb karakterkszletben tbb rtkkel jellt karakterek eset-
ben nem mindig biztosthat, hogy csak egyszer legyenek brzolva a kisebbik kszletben.
Egy szmjegynek (pl. a 7-nek) pldul gyakran tbb eltr brzolsa ltezik egy nagy ka-
rakterkszletben. Ennek jellemzen az az oka, hogy egy nagy karakterkszletnek ltalban
szmos hagyomnyos karakterkszlet-rszhalmaza van s a kisebb karakterkszletek ka-
raktereibl msodpldnyok lteznek, hogy megknnytsk az talaktst.
Az alap karakterkszletben (C.3.3) szerepl minden karakterre biztostott a kvetkez:
u!uu(ua::ou(_!!,U;; _!!
Pldul:
u!uu(ua::ou(.;; .
Fggelkek s trgymutat 1244
A ua::ou(; s u!uu(; fggvnyek mindentt figyelembe veszik a karakterek osztlyoz-
st, ahol lehetsges. Pldul ha !:(a!pa,; igaz, akkor az !:(a!pa,ua::ou(,a;; s az
!:(a!pa,u!uu(;; is igaz lesz ott, ahol az a!pa rvnyes maszk a hasznlatban lv lokl-
ra nzve.
A ,p Ja-et klnsen a ua::ou(; s u!uu(; fggvnyeket ltalban arra hasznl-
juk, hogy olyan kdot rjunk, amely tetszleges karakterkszleten vgzi a be- s kimenet,
illetve a karakterlncok kezelst; azaz, hogy az ilyen kdot ltalnoss tegyk a karakter-
kszletek szempontjbl. Ebbl kvetkezik, hogy az !o::au-megvalstsok alapveten
fggnek ezektl az eszkzktl. A felhasznlnak legtbbszr nem kell kzvetlenl hasz-
nlnia a ,p jellemzt, ha az !o::au>-re s a ::!ug>-re tmaszkodik.
A ,p _,uau vltozata (D.4, D.4.1) szintn adott:
up!a !a:: > !a:: :u..,p_,uau . pn!! ,p> { * * },
D.4.5.1. Knyelmet szolgl felletek
A ,p Ja-et leggyakrabban arra hasznljuk, hogy lekrdezzk, hogy egy karakter egy
adott osztlyba tartozik-e. Kvetkezskppen erre a clra a kvetkez fggvnyek adottak:
up!a !a:: > oo! !::pa( , ou: !oa!& !o;,
up!a !a:: > oo! !:p:!u( , ou: !oa!& !o;,
up!a !a:: > oo! !:u:!( , ou: !oa!& !o;,
up!a !a:: > oo! !:npp:( , ou: !oa!& !o;,
up!a !a:: > oo! !:!ou:( , ou: !oa!& !o;,
up!a !a:: > oo! !:a!pa( , ou: !oa!& !o;,
up!a !a:: > oo! !:u!g!( , ou: !oa!& !o;,
up!a !a:: > oo! !:pnu( , ou: !oa!& !o;,
up!a !a:: > oo! !:.u!g!( , ou: !oa!& !o;,
up!a !a:: > oo! !:a!unu( , ou: !oa!& !o;,
up!a !a:: > oo! !:g:ap( , ou: !oa!& !o;,
A fenti fggvnyek a n:_Ja-et hasznljk:
up!a !a:: >
!u!!u oo! !::pa( , ou: !oa!& !o;
{
:n:u n:_Ja ,p> >(!o;!:(:pa, ;,
}
D. Helyi sajtossgok 1245
Ezen fggvnyek egyparamter vltozatai (20.4.2) az aktulis C globlis loklhoz, nem
a C++ globlis lokljhoz, a !oa!(;-hez kszltek. Azokat a ritka eseteket kivve, amikor
a C s a C++ globlis lokl klnbzik (D.2.3), ezeket a vltozatokat gy tekinthetjk,
mintha a ktparamter vltozatokat a !oa!(;-re alkalmaztuk volna:
!u!!u !u !::pa(!u !;
{
:n:u !::pa(!, !oa!(;;, uauuu
}
D.4.6. Karakterkd-talaktsok
A fjlban trolt karakterek brzolsa nha klnbzik ugyanazoknak a karaktereknek az
elsdleges memriban kvnatos brzolstl. A japn karaktereket pldul gyakran
olyan fjlokban troljk, amelyekben lptetsek jelzik, hogy az adott karaktersorozat
a ngy legelterjedtebb karakterkszlet (kandzsi, katakana, hiragana s romadzsi) kzl me-
lyikhez tartozik. Ez kiss esetlen megolds, mert minden bjt jelentse a lptetsi llapot-
tl fgg, de memrit takarthat meg, mert csak egy kandzsi karakter brzolsa ignyel
egy bjtnl tbbet. Az elsdleges memriban ezek a karakterek knnyebben kezelhetk,
ha tbbjtos karakterknt brzoltak, ahol minden karakter mrete megegyezik. Az ilyen
karakterek (pldul a Unicode karakterek) ltalban szles karakterekben vannak elhelyez-
ve (ua:_; 4.3). Mindezek miatt a ou: Ja eszkzket nyjt a karakterek egyik b-
rzolsrl a msikra val talaktsra; mikzben azokat olvassuk vagy rjuk:
Fggelkek s trgymutat 1246
brzols lemezen:
brzols az elsdleges memriban:
A ou: ltal felgyelt I/O talaktsok
JIS
Unicode
Ez a kdtalakt rendszer elg ltalnos ahhoz, hogy a karakterbrzolsok kztt tetsz-
leges talaktst tegyen lehetv, gy a bemeneti adatfolyamok ltal hasznlt !oa! bellt-
sval olyan programot rhatunk, amely (a:, ua:_ vagy ms tpusban trolt) alkalmas
bels karakterbrzolst hasznl s tbbfle karakter-adatfolyam brzolst elfogad beme-
netknt. A msik lehetsg az lenne, hogy magt a programot mdostjuk vagy a beolva-
sott s kirt fjlok formtumt alaktjuk oda-vissza.
A ou: a klnbz karakterkszletek kztti talaktsra akkor ad lehetsget, amikor
a karaktert az adatfolyam tmeneti tra s egy kls tr kztt mozgatjuk:
!a:: :u..ou:_a: {
pn!!.
unu ::n! { o, pa:!a!, ::o:, uoou: }, :uuu,!zo
},
up!a !a:: 1, !a:: E, !a:: Sa>
!a:: :u..ou: . pn!! !oa!..Ja, pn!! ou:_a: {
pn!!.
,puJ 1 !u:u_,p,
,puJ E .:u_,p,
,puJ Sa :a_,p,
.p!!! ou:(:!z_ : U;,
::n! !u(Sa&, ou: E* J:ou, ou: E* J:ou_uu, ou: E*& J:ou_u., o!:a::
1* o, 1* o_uu, 1*& o_u.; ou:,
::n! on(Sa&, ou: 1* J:ou, ou: 1* J:ou_uu, ou: 1*& J:ou_u., :::
E* o, E* o_uu, E*& o_u.; ou:,
::n! nu:!J(Sa&, E* o, E* o_uu, E*& o_u.; ou:, a:a::o:oza :g
!u uou!ug(; ou: :ou(;, a!ap:o ouo!:! n!auou:go
oo! a!ua,:_uoou:(; ou: :ou(;, u az 1O oua!a:: u!n!.
!u !ug(ou: Sa&, ou: E* J:ou, ou: E* J:ou_uu, :!z_ ua.; ou:,
!u ua._!ug(; ou: :ou(;, a !:g: !guag,o !ug(;
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-ou:(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
D. Helyi sajtossgok 1247
A ou: Ja-et a a:!_J!!nJ (21.5) karakterek olvasshoz s rshoz hasznlja s az
adatfolyam lokljbl olvassa ki (21.7.1).
A Sa sablonparamter az ppen talaktott adatfolyam lptetsi llapotnak trolsra
szolgl tpus. Ha specializcit ksztnk belle, a Sa-et a klnbz talaktsok azo-
nostsra is felhasznlhatjuk. Ez utbbi azrt hasznos, mert gy tbbfajta karakterkdols-
hoz (karakterkszlethez) tartoz karaktereket lehet ugyanolyan tpus objektumokban
trolni:
!a:: {1S:a { * * },
p uu ou:ua:_,a:,u:a_>, :za:u,o: a:-:o! :z!: a:a::
uu ou:ua:_,a:,{1S:a>, {1S-:o! :z!: a:a::
A klnbz Sa paramterek nlkl a Ja nem tudn megllaptani, melyik kdolst t-
telezze fel egy adott a: adatfolyamra. A rendszer szabvnyos talaktst a a: s
a ua:_ tpusok kztt a ua:> vagy ua:> fejllomnyban lv u:a_ tpus
rja le.
Szrmaztatott osztlyknt, nvvel azonostva j ou:-t is ltrehozhatunk:
!a:: {1S: . pn!! ou:ua:_,a:,u:a_> { * * },
Az !u(:,J:ou,J:ou_uu,J:ou_u.,o,o_uu,o_u.; hvs minden karaktert beolvas
a /J:ou,J:ou_u.; tartomnybl s megprblja talaktani azokat. Ha egy karakterrel vg-
zett, akkor azt j alakjban a /o,o_uu; tartomny megfelel helyre rja; ha talaktsra
nem kerl sor, a fggvny ezen a ponton megll. A visszatrskor az !u(; a J:ou_u.-ben
az utols beolvasott karakter utni pozcit, a o_u.-ben pedig az utols rott karakter ut-
ni pozcit trolja. Az !u(; ltal visszaadott ::n! rtk jelzi, hogy a fggvny mennyire tud-
ta elvgezni a munkt:
o: A /J:ou,J:ou_uu; tartomnyban minden karakter talaktsra kerlt.
pa:!a!: A /J:ou,J:ou_uu; tartomnyban nem minden karakter kerlt talaktsra.
::o:: Az on(; olyan karakterrel tallkozott, amit nem tudott talaktani.
uoou:: Nem volt szksg talaktsra.
A pa:!a! (rszleges) talakts nem biztos, hogy hiba. Elkpzelhet, hogy tbb karaktert
kell beolvasni, mieltt egy tbbjtos karakter teljes lesz s ki lehet rni, esetleg a kimeneti
tmeneti trat kell kirteni, hogy helyet csinljunk a tovbbi karaktereknek.
Fggelkek s trgymutat 1248
A bemeneti karaktersorozat llapott az !u(; meghvsnak kezdetn a Sa tpus : para-
mter jelzi. Ennek akkor van jelentsge, ha a kls karakterbrzols lptetsi llapotokat
hasznl. Jegyezzk meg, hogy az : (nem konstans) referencia paramter: a hvs vgn
a bemeneti sorozat lptetsnek llapott trolja, ami lehetv teszi, hogy a programoz
kezelhesse a rszleges talaktsokat s hossz sorozatokat is talakthasson az !u(; tbb-
szri meghvsval. Az on(:,J:ou,J:ou_uu,J:ou_u.,o,o_uu,o_u.; ugyangy ala-
ktja t a /J:ou,J:ou_uu;-et a bels brzolsrl a klsre, ahogy az !u(; a kls brzo-
lst a belsre.
A karakterfolyamoknak semleges (nem lptetett) llapotban kell kezddnik s vg-
zdnik. Ez az llapot ltalban a Sa(;. Az nu:!J(:,o,o_uu,o_u.; hvs megnzi az
:-t s a /o,o_uu;-be annyi karaktert tesz, amennyire szksg van ahhoz, hogy a karakter-
sorozatot visszalltsa a nem lptetett llapotba. Az nu:!J(; eredmnye s a o_u. fel-
hasznlsi mdja az on(; fggvnyvel azonos.
A !ug(:,J:ou,J:ou_uu,ua.; azoknak a karaktereknek a szmt adja vissza, amelyeket
az !u(; t tudott alaktani a /J:ou,J:ou_uu;-bl.
Az uou!ug(; visszatrsi rtkei a kvetkezk lehetnek:
-I, ha a kls karakterkszlet kdolsa llapotot (pldul lptetett s nem lptetett
karaktersorozatokat) hasznl,
U, ha a kdols az egyes karakterek brzolsra klnbz szm bjtot hasznl
(pldul egy bjtban egy bitet annak a jellsre, hogy egy vagy kt bjtos-e
a karakter brzolsa.),
u, ha a kls karakterkszlet minden karakternek brzolsa pontosan u bjtos.
Az a!ua,:_uoou:(; hvs :n-t ad vissza, ha a bels s a kls karakterkszletek kztt
nincs szksg talaktsra, klnben pedig Ja!:-t. Vilgos, hogy az a!ua,:_uoou:(;:n
megnyitja a lehetsget az adott nyelvi vltozat szmra, hogy a lehet leghatkonyabb
megvalstst nyjtsa; azltal, hogy egyltaln nem hvja meg az talakt fggvnyeket.
A ua._!ug(; hvs azt a legnagyobb rtket adja vissza, amit a !ug(; visszaadhat egy
adott rvnyes paramterhalmazra.
A bemenet nagybetsre alaktsa a legegyszerbb kdtalakts. Annyira egyszer,
amennyire csak egy ou: lehet, mgis elltja a feladatt:
!a:: :_o_npp: . pn!! ou:a:,a:,u:a_> { uag,n:: a!a::
.p!!! :_o_npp:(:!z_ : U; . ou:(:; { }
D. Helyi sajtossgok 1249
p:ou.
n!:o :zo!: o!:a::a, !:o :zo!: :::a.
::n! uo_!u(Sa& :, ou: a:* J:ou, ou: a:* J:ou_uu, ou: a:*& J:ou_u.,
a:* o, a:* o_uu, a:*& o_u.; ou:,
!:o :zo!: o!:a::a, n!:o :zo!: :::a.
::n! uo_on(Sa& :, ou: a:* J:ou, ou: a:* J:ou_uu, ou: a:*&
J:ou_u.,
a:* o, a:* o_uu, a:*& o_u.; ou:
{
:n:u ou:a:,a:,u:a_>..uo_on
(:,J:ou,J:ou_uu,J:ou_u.,o,o_uu,o_u.;,
}
::n! uo_nu:!J(Sa&, E* o, E* o_uu, E*& o_u.; ou: { :n:u o, }
!u uo_uou!ug(; ou: :ou(; { :n:u I, }
oo! uo_a!ua,:_uoou:(; ou: :ou(; { :n:u Ja!:, }
!u uo_!ug(ou: Sa&, ou: E* J:ou, ou: E* J:ou_uu, :!z_ ua.; ou:,
!u uo_ua._!ug(; ou: :ou(;, a !:g: !guag,o !ug(;
},
ou:a:,a:,u:a_>..::n!
:_o_npp:..uo_!u(Sa& :, ou: a:* J:ou, ou: a:* J:ou_uu,
ou: a:*& J:ou_u., a:* o, a:* o_uu, a:*& o_u.; ou:
{
Do/Io]
}
!u ua!u(; g,:z:n :z
{
!oa! n!oa!(!oa!(;, uu :_o_npp:;,
!u!un(n!oa!;,
u!! (!u>>; on ,
}
A ou: _,uau vltozata (D.4, D.4.1) is adott:
up!a !a:: 1, !a:: E, !a:: Sa>
!a:: :u..ou:_,uau . pn!! ou:1,E,Sa> { * * },
Fggelkek s trgymutat 1250
D.4.7. zenetek
Termszetesen minden felhasznl a sajt anyanyelvt szereti hasznlni, amikor trsalog
a programmal, a lokltl fgg zenetek kifejezsre azonban nem tudunk szabvnyos el-
jrst biztostani. A knyvtr csupn egyszer eszkzket nyjt ahhoz, hogy loklfgg ka-
rakterlnc-halmazokat trolhassunk, melyekbl egyszer zeneteket (message) hozhatunk
ltre. A u::ag: osztly alapjban vve egy igen egyszer, csak olvashat adatbzist r le:
!a:: :u..u::ag:_a: {
pn!!.
,puJ !u aa!og, aa!ogn:-azouo::o :pn:
},
up!a !a:: >
!a:: :u..u::ag: . pn!! !oa!..Ja, pn!! u::ag:_a: {
pn!!.
,puJ a:_,p,
,puJ a:!_::!ug> ::!ug_,p,
.p!!! u::ag:(:!z_ : U;,
aa!og opu(ou: a:!_::!uga:>& Ju, ou: !oa!&; ou:,
::!ug_,p g(aa!og , !u :, !u u:g!u, ou: ::!ug_,p& u; ou:,
:o!u !o:(aa!og ; ou:,
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
p:ou.
-u::ag:(;,
:!:n!!: uo_ Jngg:u, a u,!!:uo: Jngg:u, :zu:a (!:u D4I;
},
Az opu(:,!o; hvs megnyitja az : nev zenetkatalgust a !o loklban. A katalgus egy
megvalststl fggen elrendezett karakterlnc-halmaz, amelyhez a u::ag:..g(; fgg-
vnnyel frhetnk hozz. Ha az : nev katalgust nem lehet megnyitni, az opu(; negatv
rtket ad vissza. A katalgust meg kell nyitni a g(; els hasznlata eltt.
A !o:(a; hvs bezrja a a ltal azonostott katalgust s minden vele kapcsolatos er-
forrst felszabadt.
A g(a,:,!u,Joo; a (:,!u;-vel azonostott zenetet keresi meg a a katalgusban. Ha
megtallja a karakterlncot, akkor a g(; ezzel tr vissza; ha nem, az alaprtelmezett karak-
terlnccal (itt ez aJoo).
D. Helyi sajtossgok 1251
me egy plda a u::ag: Ja-re, ahol az zenetkatalgus zenetek halmazbl ll
vektor, az zenet pedig egy karakterlnc:
::n S {
:o:::!ug> u:g:,
},
::n a {
:o:S> ::,
},
!a:: M,_u::ag: . pn!! u::ag:a:> {
:o:a>& aa!og:,
pn!!.
.p!!! M,_u::ag:(:!z_ U; . aa!og:(*uu :o:a>; { }
aa!og uo_opu(ou: ::!ug& :, ou: !oa!& !o; ou:, az : aa!ogn: ugu,!:a
::!ug uo_g(aa!og , !u :, !u u, ou: ::!ug&; ou:, az (:,u; nzu
ug:z:z: -o!
:o!u uo_!o:(aa!og a; ou:
{
!J (aa!og::!z(;a; aa!og::a:(aa!og:g!u(;-a;,
}
-M,_u::ag:(; { u! &aa!og:, }
},
A u::ag: minden tagfggvnye ou:, gy a katalgus adatszerkezete (a :o:S>)
a Ja-en kvl troldik.
Az zeneteket egy katalgus, azon bell egy halmaz, majd a halmazon bell egy zenet-
karakterlnc megadsval jellhetjk ki. Egy karakterlncot paramterknt adunk meg; ezt
a fggvny alaprtelmezett visszatrsi rtkknt hasznlja, ha nem tallja az zenetet a
katalgusban:
::!ug M,_u::ag:..uo_g(aa!og a, !u :, !u u:g, ou: ::!ug& uJ; ou:
{
!J (aa!og::!z(;a; :n:u uJ,
a& aa!og:/a],
!J (:::!z(;:; :n:u uJ,
S& : ::/:],
!J (:u:g::!z(;u:g; :n:u uJ,
:n:u :u:g:/u:g],
}
Fggelkek s trgymutat 1252
A katalgus megnyitsakor a lemezrl egy szveges brzolst olvasunk egy a szerkezet-
be. Itt olyan brzolst vlasztottam, ami knnyen olvashat: a s >>> jelek egy hal-
mazt hatrolnak, az zenetek pedig szvegsorok:
u::ag:a:>..aa!og M,_u::ag:..uo_opu(ou: ::!ug& u, ou: !oa!& !o; ou:
{
::!ug uu u - !oa!(;uau(;,
!J::au J(uu_::(;;,
!J (:J; :n:u -I,
aa!og:pn:_a(a(;;, a aa!ogn: uuo:!a !,z:
a& aa!og:a(;,
::!ug :,
u!! (J>>: && :; { a!uaz o!:a::a
::pn:_a(S(;;,
S& :: ::a(;,
u!! (g!!u(J,:; && : : >>>; ::u:g:pn:_a(:;, nzu o!:a::a
}
:n:u aa!og::!z(;-I,
}
me egy egyszer felhasznls:
!u ua!u(;
{
!J (:a:_Ja M,_u::ag: >(!oa!(;;; {
:: !oa!(;uau(; !o!au u!u: u::ag: Ja`u,
.!(I;,
}
ou: u::ag:a:>& u n:_Ja M,_u::ag: >(!oa!(;;,
.:u ::!ug u::ag_u!:o:,, ! a:ou az nzu!u
!u a uopu(u::ag_u!:o:,,!oa!(;;,
!J (aU; {
:: N!u: aa!ogn:`u,
.!(I;,
}
on ug(a,U,U,Mg!u :::; uu!,
on ug(a,I,2,Mg!u :::; uu!,
on ug(a,I,,Mg!u :::; uu!,
on ug(a,,U,Mg!u :::; uu!,
}
D. Helyi sajtossgok 1253
Ha a katalgus

!!o
:!:z!
>>>

!gu
uu
a!u
>>>
a program a kvetkezt rja ki:
!!o
a!u
Mg!u :::
Mg!u :::
D.4.7.1. Ms facet-ekben lv zenetek hasznlata
Azon tl, hogy a felhasznlkkal val kapcsolattartsra alkalmas loklfgg karakterlnco-
kat trolnak, az zenetek arra is felhasznlhatk, hogy ms Ja-ek szmra troljanak ka-
rakterlncokat. A Sa:ou_!o jellemzt (D.3.2) pldul gy is megrhattuk volna:
!a:: Sa:ou_!o . pn!! !oa!..Ja {
ou: u::ag:a:>& u, nzuou,::
!u a, nzuaa!ogn:
pn!!.
!a:: M!::!ug_u::ag: { },
Sa:ou_!o(!u ! U;
. !oa!..Ja(!;,
u(n:_JaSa:ou_u::ag:>(!oa!(;;;,
a(uopu(u::ag_u!:o:,,!oa!(;;;
{ !J (aU; :ou M!::!ug_u::ag:(;, }
-Sa:ou_!o(; { } !o: :z! a Sa:ou_!o onuo J!:zuo!: (D;
ou: ::!ug& o_::(Sa:ou .; ou:, . :zo!:a a:a:!ua!
oo! J:ou_::(ou: ::!ug& :, Sa:ou& .; ou:, az :-u ugJ!!o
Sa:ou onuo .- !,z!
Fggelkek s trgymutat 1254
:a! !oa!..!u !u, Ja-azouo::o onu (D2, D, DI;
},
!oa!..!u Sa:ou_!o..!u, az azouo::o onu uga:oz:a
ou: ::!ug& Sa:ou_!o..o_::(Sa:ou .; ou:
{
:n:u u->g(a, ., N!u: !!,u ::za;,
}
oo! Sa:ou_!o..J:ou_::(ou: ::!ug& :, Sa:ou& .; ou:
{
Jo: (!u ! Sa:ou..:p:!ug, !Sa:ou..u!u:, !--;
!J (u->g(a, !, N!u: !!,u ::za; :; {
. Sa:ou(!;,
:n:u :n,
}
:n:u Ja!:,
}
Ez az zenetekre pl megolds abban klnbzik az eredetitl (D.3.2), hogy a Sa:ou
karakterlncok halmazt egy adott loklhoz megr programoznak a karakterlncokat
hozz kell tudnia adni egy u::ag: knyvtrhoz. Ezt az teheti meg knnyen, aki j loklt
ad a vgrehajtsi krnyezethez. Mgis, mivel a u::ag: csak olvashat felletet nyjt, az
vszakok egy jabb halmaznak megadsa a rendszerprogramozk hatskrn kvl esik.
A u::ag: _,uau vltozata (D.4, D.4.1) is adott:
up!a !a:: >
!a:: :u..u::ag:_,uau . pn!! u::ag:> { * * },
D.5. Tancsok
[1] Szmtsunk r, hogy a felhasznlkkal kzvetlenl trsalg programokat s
rendszereket tbb orszgban is hasznlni fogjk. D.1.
[2] Ne ttelezzk fel, hogy mindenki ugyanazt a karakterkszletet hasznlja, amit
mi. D.4.1.
[3] Ha a bemenet s kimenet fgg a helyi sajtossgoktl, loklokat hasznljuk s
ne alkalmi kdot rjunk. D.1.
[4] Kerljk a loklnevek begyazst a programszvegbe. D.2.1.
D. Helyi sajtossgok 1255
[5] Hasznljuk a lehet legkevesebb globlis formzst. D.2.3, D.4.4.7.
[6] Rszestsk elnyben a loklhoz igazod karakterlnc-sszehasonltsokat s -
rendezseket. D.2.4, D.4.1.
[7] A Ja-ek legyenek nem mdosthatk. D.2.2, D.3.
[8] Csak kevs helyen vltoztassuk meg a loklt egy programban. D.2.3.
[9] Hagyjuk, hogy a lokl szablyozza a Ja-ek lettartamt. D.3.
[10] Amikor loklfgg I/O fggvnyeket runk, ne felejtsk el kezelni a felhaszn-
li (fellrt) fggvnyek okozta kivteleket. D.4.2.2.
[11] A pnzrtkek trolsra hasznljunk egy egyszer Mou, tpust. D.4.3.
[12] Hasznljunk egyszer felhasznli tpusokat az olyan rtkek trolsra, ame-
lyek lokltl fgg I/O-t ignyelnek (s ne a beptett tpusok rtkeit alaktsuk
oda-vissza). D.4.3.
[13] Ne higgynk az idrtkeknek, amg nincs valamilyen j mdszernk arra,
hogy beleszmtsuk az sszes lehetsges mdost tnyezt. D.4.4.1.
[14] Legynk tisztban a !u_ tpus korltaival. D.4.4.1, D.4.4.5.
[15] Hasznljunk olyan dtumbeviteli eljrst, amely tbbfle formtumot is elfogad.
D.4.4.5.
[16] Rszestsk elnyben az olyan karakterosztlyoz fggvnyeket, amelyekben
a lokl kifejezetten megadott. D.4.5, D.4.5.1.
D.6. Gyakorlatok
1. (*2,5) Ksztsk el a Sa:ou_!o-t (D.3.2) az amerikai angoltl eltr nyelvre.
2. (*2) Hozzunk ltre egy Sa:ou_!o osztlyt (D.3.2), amelynek konstruktora ne-
vek halmazt veszi paramterknt, hogy a klnbz loklokban lv vszakok
neveit ezen osztly objektumaiknt lehessen brzolni.
3. (*3) rjunk egy o!!aa:>..oupa: fggvnyt, amely bcsorrendet llt
fel. Lehetleg olyan nyelvre ksztsk el, mint a nmet, a francia vagy a magyar,
mert ezek bcje az angolnl tbb bett tartalmaz.
4. (*2) rjunk egy programot, ami logikai rtkeket szmokknt, angol szavakknt
s egy tetszleges nyelv szavaiknt olvas be s r ki.
5. (*2,5) Hatrozzuk meg a T!u tpust a pontos id brzolsra. Hatrozzuk
meg a Da_auu_!u tpust is, a T!u s valamilyen Da tpus felhasznls-
val. Fejtsk ki ennek a megkzeltsnek a D.4.4. pontban szerepl Da tpus-
sal szembeni elnyeit s htrnyait. rjuk meg a T!u s a Da_auu_!u tpu-
sok loklfgg be- s kimeneti eljrsait.
Fggelkek s trgymutat 1256
6. (*2,5) Tervezznk meg s ksztsnk el egy postai irnytszm Ja-et. rjuk
meg legalbb kt, eltr cmzsi szoksokat hasznl orszgra.
7. (*2,5) Ksztsnk egy telefonszm Ja-et. rjuk meg legalbb kt, eltr tele-
fonszm-formt (pldul (973) 360-8000 s 1223 343000) hasznl orszgra.
8. (*2,5) Prbljuk meg kitallni, milyen be- s kimeneti formtumokat hasznl
C++-vltozatunk a dtumokhoz.
9. (*2,5) rjunk olyan g_!u(; fggvnyt, amely megprblja kitallni a tbbr-
telm dtumok pldul a I2U5IUU5 jelentst, de elutast minden vagy
majdnem minden hibt. Hatrozzuk meg pontosan, melyik tallgatst fogad-
juk el s gondoljuk t a hibk valsznsgt.
10. (*2) rjunk olyan g_!u(; fggvnyt, ami tbbfajta bemeneti formtumot fo-
gad el, mint a D.4.4.5 pontban szerepl vltozat.
11. (*2) Ksztsnk listt a rendszernk ltal tmogatott loklokrl.
12. (*2,5) Talljuk meg, rendszernk hol trolja a nevestett loklokat. Ha van hoz-
zfrsnk a rendszer azon rszhez, ahol a loklok troldnak, ksztsnk egy
j, nvvel rendelkez !oa!-t. Legynk vatosak, nehogy tnkretegyk a mr
ltez !oa!-eket.
13. (*2) Hasonltsuk ssze a Sa:ou_!o kt megvalstst (D.3.2 s D.4.7.1).
14. (*2) rjuk meg, s teszteljk le a Da_on Ja-et, ami Da-eket r ki
a konstruktornak megadott paramter ltal meghatrozott formban. Fejtsk ki
ennek a megkzeltsnek az elnyeit s htrnyait a ua_Ju ltal nyjtott glo-
blis adatformtummal (D.4.4.6) szemben.
15. (*2,5) rjuk meg a rmai szmok (pldul A1 s MD111) be- s kimeneti
eljrsait.
16. (*2,5) Ksztsk el s ellenrizzk a :_o_npp:-t (D.4.6).
17. (*2,5) llaptsuk meg a kvetkezk tlagos kltsgt a !o(; fggvny felhasz-
nlsval: (1) fggvnyhvs, (2) virtulis fggvnyhvs, (3) egy a: beolvas-
sa, (4) egy 1 szmjegy !u beolvassa, (5) egy 5 szmjegy !u beolvassa (6)
egy 5 szmjegy uon!, (7) egy 1 karakteres ::!ug, (8) egy 5 karakteres ::!ug,
s (9) egy 40 karakteres ::!ug beolvassa.
18. (*6,5) Tanuljunk meg egy msik termszetes nyelvet.
D. Helyi sajtossgok 1257
Kivtelbiztossg a standard knyvtrban
Minden gy mkdik, ahogy
elvrjuk feltve, hogy
elvrsaink helyesek.
(Hyman Rosen)
Kivtelbiztossg Kivtelbiztos eljrsok Erforrsok brzolsa rtkads
push_back() Konstruktorok s invarinsok A szabvnyos trolk szolgltatsai Ele-
mek beillesztse s eltvoltsa Garancik s kompromisszumok swap() A kezdeti r-
tkads s a bejrk Hivatkozs elemekre Prediktumok Karakterlncok, adatfolya-
mok, algoritmusok, a valarray s a complex A C standard knyvtra Javaslatok
a knyvtrak felhasznli szmra Tancsok Gyakorlatok
E.1. Bevezets
A standard knyvtr fggvnyei gyakran olyan fggvnyeket hvnak meg, melyeket a fel-
hasznl ad meg akr fggvny-, akr sablonparamterek formjban. Termszetesen ezek
a felhasznli eljrsok nha kivteleket vltanak ki. Egyes fggvnyek (pldul a mem-
riafoglal eljrsok) nmagukban is kpesek kivtel kivltsra:
E
void f(vector<X>& v, const X& g)
{
v[2] = g; // X tpus rtkadsa kivtelt vlthat ki
v.push_back(g); // vector<X> memriafoglalja kivtelt vlthat ki
sort(v.begin(), v.end()); // X "kisebb mint" opertora kivtelt vlthat ki
vector<X> u = v; // X msol konstruktora kivtelt vlthat ki
// ...
// u itt semmisl meg: biztostanunk kell, hogy X destruktora helyesen mkdjn
}
Mi trtnik, ha az rtkads kivtelt vlt ki, mikzben a g rtkt prbljuk lemsolni? A v
ezutn egy rvnytelen elemet fog tartalmazni? Mi trtnik, ha az a konstruktor, amit
a v.push_back() hasznl a g lemsolshoz, std::bad_alloc kivtelt vlt ki? Megvltozik az
elemek szma? Bekerlhet rvnytelen elem a trolba? Mi trtnik, ha az X osztly kisebb,
mint opertora eredmnyez kivtelt a rendezs kzben? Az elemek ezutn rszben rende-
zettekk vlnak? Elkpzelhet, hogy a rendez eljrs eltvolt egy elemet a trolbl s
egy hiba miatt nem teszi vissza?
A fenti pldban szerepl sszes kivtel-lehetsg megtallsa az E.8.[1] feladat clja, ezen
fggelk az, hogy elmagyarzzuk, hogyan illik viselkednie a fenti programnak az sszes
megfelelen meghatrozott X tpus esetben (belertve azt az esetet is, amikor az X kivtele-
ket vlthat ki). Termszetesen a fggelk nagy rszben azt fogjuk tisztzni, mit is neveznk
helyes viselkedsnek, illetve megfelelen meghatrozottnak a kivtelekkel kapcsolatban.
A fggelkben a kvetkezkkel foglalkozunk:
1. Megvizsgljuk, hogyan adhat meg a felhasznl olyan tpusokat, amelyek meg-
felelnek a standard knyvtr elvrsainak.
2. Rgztjk, milyen garancikat biztost a standard knyvtr.
3. Megnzzk, milyen elvrsokat llt a standard knyvtr a felhasznl ltal
megadott programrszekkel szemben.
4. Bemutatunk nhny hatkony mdszert, mellyel kivtelbiztos s hatkony tro-
lkat kszthetnk.
5. Megemltjk a kivtelbiztos programozs nhny ltalnos szablyt.
A kivtelbiztossg vizsglata rtelemszeren a legrosszabb esetben tapasztalhat viselke-
dssel foglalkozik. Hol jelentheti egy kivtel a legtbb problmt? Hogyan tudja a standard
knyvtr megvdeni magt s a felhasznlt a lehetsges problmktl? Hogyan segthet
a felhasznl a problmk elkerlsben? Ne hagyjuk, hogy az itt bemutatott kivtel-
Fggelkek s trgymutat 1260
kezelsi mdszerek elfeledtessk velnk, hogy a kivtelek kivltsa a legjobb mdszer a hi-
bk jelzsre (14.1, 14.9). Az elveket, a mdszereket s a standard knyvtr szolgltatsa-
it a kvetkez rendszerben trgyaljuk:
E.2. Ebben a pontban a kivtelbiztossg fogalmt vizsgljuk meg.
E.3. Itt mdszereket mutatunk arra, hogyan rhatunk hatkony, kivtelbiztos
trolkat s mveleteket.
E.4. Ebben a rszben krvonalazzuk a standard knyvtr troli s mveletei
ltal biztostott garancikat.
E.5. Itt sszefoglaljuk a kivtelbiztossg problmjt a standard knyvtr nem
trolkkal foglalkoz rszben.
E.6. Vgl jbl ttekintjk a kivtelbiztossg krdst a standard knyvtr
felhasznlinak szemszgbl.
Szoks szerint a standard knyvtr pldkat mutat azokra a problmatpusokra, amelyekre
egy alkalmazs fejlesztsekor oda kell figyelnnk. A standard knyvtrban a kivtelbiztos-
sg megvalstsra alkalmazott eljrsok szmos ms terleten is felhasznlhatk.
E.2. Kivtelbiztossg
Egy objektumon vgrehajtott mveletet akkor neveznk kivtelbiztosnak, ha a mvelet az
objektumot akkor is rvnyes llapotban hagyja, ha kivtel kivltsval rt vget. Ez az r-
vnyes llapot lehet egy hiballapot is, amit esetleg csak teljes jbli ltrehozssal lehet
megszntetni, de mindenkppen pontosan meghatrozottnak kell lennie, hogy az objek-
tumhoz logikus hibakezel eljrst adhassunk meg. A kivtelkezel pldul trlheti az ob-
jektumot, kijavthatja azt, megprblkozhat a mvelet egy msik vltozatval vagy megpr-
blhat egyszeren tovbbhaladni a hiba figyelmen kvl hagysval.
Ms szavakkal, az objektum rendelkezni fog egy invarinssal (llapotbiztost, nem vlto-
z llapot 24.3.7.1), melyet konstruktorai lltanak el, s minden tovbbi mveletnek meg
tartania az ltala lert llapotot, mg akkor is, ha kivtelek kvetkeztek be. A vgs rendra-
kst a destruktorok vgzik el. Minden mveletnek figyelnie kell arra, hogy a kivtelek ki-
vltsa eltt visszalltsk az invarinst, hogy az objektum rvnyes llapotban lpjen ki
a mveletbl. Termszetesen nagy valsznsggel ez az rvnyes llapot nem az az lla-
pot lesz, amire az alkalmazsnak ppen szksge lenne. Egy karakterlnc egy kivtel k-
vetkeztben pldul ress vlhat, egy trol pedig rendezetlen maradhat. Teht a kijav-
E. Kivtelbiztossg a standard knyvtrban 1261
ts csak azt jelenti, hogy az objektumnak olyan rtket adunk, ami elfogadhatbb a prog-
ram szmra, mint az, amit a flbeszakadt mvelet benne hagyott. A standard knyvtr
szempontjbl a legrdekesebb objektumok a trolk.
Az albbiakban azt vizsgljuk meg, milyen felttelek mellett nevezhetjk a szabvnyos t-
rolkat feldolgoz mveleteket kivtelbiztosnak. Elvileg mindssze kt egyszer megkze-
lts kzl vlaszthatunk:
1. Nincs biztosts: Ha kivtel lp fel, a mvelet ltal hasznlt valamennyi trolt
valsznleg srltnek ttelezzk fel.
2. Ers biztosts: Ha kivtel lp fel, minden trol pontosan abba az llapotba ke-
rl vissza, mint amiben a standard knyvtr mveletnek megkezdse eltt volt.
Sajnos ez a kt megolds annyira egyszer, hogy igazn nem is alkalmazhat. Az els azrt
elfogadhatatlan, mert ha ezen megolds mellett egy trolmvelet kivtelt vlt ki, a trolt
tbbet nem rhetjk el, st mg nem is trlhetjk anlkl, hogy futsi idej hibktl kne
rettegnnk. A msodik lehetsg azrt nem hasznlhat, mert ekkor a visszallts megva-
lstsnak kltsgei a standard knyvtr minden mveletben jelentkeznnek.
A problma megoldsra a C++ standard knyvtra kivtelbiztostsi szinteket knl, me-
lyek a helyes program ksztsnek terheit megosztjk a standard knyvtr megvalsti s
felhasznli kztt:
3a Alapbiztosts az sszes mveletre: A standard knyvtr alapvet invarinsai
mindenkppen fennmaradnak s semmilyen erforrs (pldul memriatarto-
mny) nem maradhat lefoglalt.
3b Ers biztosts a legfontosabb mveletekhez: Az alapbiztostson tl, a mvelet
vagy sikeresen vgrehajtsra kerl, vagy semmilyen vltozst nem eredmnyez.
Ilyen szint biztosts csak a knyvtr legfontosabb mveleteit illeti meg, pld-
ul a push_back(),a list osztlyban az egyelem insert(), illetve az
uninitialized_copy() fggvnyeket (E.3.1, E.4.1).
3c Nncs kivtel biztosts bizonyos mveletekre Az alapbiztosts mellett nhny
mvelet egyltaln nem vlthat ki kivteleket. Ez a tpus biztosts csak n-
hny egyszer mveletre valsthat meg, pldul a swap() vagy a pop_back()
fggvnyre (E.4.1).
Az alapbiztosts s az ers biztosts is felttelezi, hogy a felhasznl ltal megadott mve-
letek (pldul az rtkadsok s a swap() fggvnyek) nem hagyjk a trol elemeit r-
vnytelen llapotban s minden ltaluk lefoglalt erforrst felszabadtanak. Ezenkvl
a destruktoroknak nem szabad kivtelt kivltaniuk. Pldul vizsgljuk meg az albbi oszt-
lyokat (25.7):
Fggelkek s trgymutat 1262
template<class T> class Safe {
T* p; // p egy T tpus, new-val lefoglalt objektumra mutat
public:
Safe() : p(new T) { }
~Safe() { delete p; }
Safe& operator=(const Safe& a) { *p = *a.p; return *this; }
// ...
};
template<class T> class Unsafe { // hanyag s veszlyes kd
T* p; // egy T-re mutat
public:
Unsafe(T* pp) : p(pp) { }
~Unsafe() { if (!p->destructible()) throw E(); delete p; }
Unsafe& operator=(const Unsafe& a)
{
p->~T(); // a rgi rtk trlse (10.4.11)
new(p) T(a.p); // T ltrehozsa *p-ben a.p alapjn (10.4.11)
return *this;
}
// ...
};
void f(vector< Safe<Some_type> >&vg, vector< Unsafe<Some_type> >&vb)
{
vg.at(1) = Safe<Some_type>();
vb.at(1) = Unsafe<Some_type>(new Some_type);
// ...
}
Ebben a pldban a Safe osztly ltrehozsa csak akkor sikeres, ha a T osztly is sikeresen
ltrejn. Egy T objektum ltrehozsa meghisulhat azrt, mert nem sikerl a memriafog-
lals (ilyenkor std::bad_alloc kivtel jelentkezik), vagy azrt, mert a T konstruktora brmi-
lyen okbl kivtelt vlt ki. Ettl fggetlenl, egy sikeresen ltrehozott Safe esetben a p egy
hibtlan T objektumra mutat, mg ha a konstruktor sikertelen, sem T, sem Safe objektum
nem jn ltre. Ugyangy kivtelt vlthat ki a T rtkad mvelete is; ekkor a Safe rtkad
mvelete (a C++ mkdsnek megfelelen) tovbbdobja a kivtelt. Mindez nem jelent
problmt mindaddig, amg a T rtkad opertora sajt operandust megfelel llapotban
hagyja. Teht a Safe kvetelmnyeinknek megfelelen mkdik, gy a standard knyvtr
mveletei Safe objektumokra alkalmazva logikus s megfelelen meghatrozott eredmnyt
fognak adni.
E. Kivtelbiztossg a standard knyvtrban 1263
Ezzel szemben az Unsafe() konstruktort figyelmetlenl rtuk meg (pontosabban figyelme-
sen gy rtuk meg, hogy a helytelen formt bemutassa). Egy Unsafe objektum ltrehozsa
sohasem fog meghisulni. Ehelyett az objektumot hasznl mveletekre (pldul az rtk-
adsra s a megsemmistsre) bzzuk, hogy trdjenek sajt beltsuk szerint az sszes le-
hetsges problmval. Az rtkads meghisulhat gy, hogy a T msol konstruktora kivlt
egy kivtelt. Ilyenkor a T tpus objektumot nem meghatrozott llapotban hagyjuk, hiszen
a *p rgi rtkt trltk, de nem helyettestettk rvnyes rtkkel. Az ilyen mkds k-
vetkezmnyei ltalban megjsolhatatlanok. Az Unsafe destruktora tartalmaz egy remny-
telen prblkozst az rvnytelen megsemmists elkerlsre, egy kivtel meghvsa egy
msik kivtel kezelse kzben azonban a terminate() (14.7) meghvst eredmnyezi, mg
a standard knyvtr elvrja, hogy a destruktor szablyosan visszatrjen az objektum trlse
utn. A standard knyvtr nem kszlt fel s nem is tud felkszlni arra, hogy garancikat
adjon olyan felhasznli objektumokra, melyek nem megfelelen mkdnek. A kivtelke-
zels szempontjbl a Safe s az Unsafe abban klnbzik, hogy a Safe a konstruktort hasz-
nlja az invarins (24.3.7.1) ltrehozshoz, ami lehetv teszi, hogy mveleteit egyszer-
en s biztonsgosan valstsuk meg. Ha az llapotbiztost felttel nem elgthet ki,
kivtelt kapunk, mieltt mg az rvnytelen objektum ltrejnne. Ugyanakkor az Unsafe
nem rendelkezik rtelmes invarinssal s a klnll mveletek mindenfle kivteleket vl-
tanak ki, anlkl, hogy egy kzponti hibakezel eljrs rendelkezskre llna. Termsze-
tesen ez gyakran vezet a standard knyvtr (sszer) felttelezseinek megsrtshez. Az
Unsafe esetben pldul rvnytelen elemek maradhatnak egy trolban, miutn
a T::operator=() egy kivtelt vltott ki, s a destruktor is eredmnyezhet kivteleket.
A standard knyvtr biztostsainak viszonya a rossz viselkeds felhasznli mveletek-
hez ugyanolyan, mint a nyelv biztostsainak viszonya a tpusrendszer szablyait megsrt
mveletekhez. Ha egy alapmveletet nem meghatrozott szablyai szerint hasznlunk, az
eredmny nem meghatrozhat lesz. Ha egy vector elemeinek destruktora kivtelt vlt ki,
ugyangy nincs jogunk logikus viselkedst elvrni, mint amikor egy kezdrtkknt
vletlenszmmal feltlttt mutatval szeretnnk adatokat elrni:
class Bomb {
public:
// ...
~Bomb() { throw Trouble(); }
};
vector<Bomb> b(10); // nem meghatrozhat viselkedshez vezet
void f()
{
int* p = reinterpret_cast<int*>(rand()); // nem meghatrozhat viselkedshez vezet
*p = 7;
}
Fggelkek s trgymutat 1264
De nzzk a dolgok j oldalt: ha betartjuk a nyelv s a standard knyvtr alapvet szab-
lyait, a knyvtr jl fog viselkedni gy is, ha kivteleket vltunk ki.
Amellett, hogy rendelkezsnkre ll a tiszta kivtelbiztossg, szeretjk elkerlni az erfor-
rsokban elfordul lyukakat; azaz egy olyan mveletnek, amely kivtelt vlt ki, nem elg
az operandusait meghatrozott llapotban hagynia, biztostania kell azt is, hogy minden
ignyelt erforrs valamikor felszabaduljon. Egy kivtel kivltsnak helyn pldul min-
den korbban lefoglalt memriaterletet fel kell szabadtanunk vagy azokat valamilyen ob-
jektumhoz kell ktnnk, hogy ksbb a memria szablyosan felszabadthat legyen.
A standard knyvtr biztostja, hogy nem lesznek erforrs-lyukak, ha azok a felhasznli
mveletek, melyeket a knyvtr hasznl, maguk sem hoznak ltre ilyeneket:
void leak(bool abort)
{
vector<int> v(10); // nincs memria-elszivrgs
vector<int>* p = new vector<int>(10); // memria-elszivrgs lehetsges
auto_ptr< vector<int> > q(new vector<int>(10)); // nincs memria-elszivrgs (14.4.2)
if (abort) throw Up();
// ...
delete p;
}
Ha kivtel kvetkezik be, a v vector s a q ltal mutatott vector helyesen lesz trlve, gy
minden ltaluk lefoglalt erforrs felszabadul. Ezzel szemben a p ltal mutatott vector ob-
jektumot nem vdjk a kivtelektl, gy az nem fog trldni. Ha a kdrszletet biztonsgos-
s akarjuk tenni, vagy magunknak kell felszabadtani a p ltal mutatott terletet a kivtel ki-
vltsa eltt, vagy biztostanunk kell, hogy valamilyen objektum pldul egy auto_ptr
(14.4.2) birtokolja azt s szablyosan felszabadtsa akkor is, ha kivtel kvetkezett be.
Figyeljk meg, hogy a nyelv szablyai a rszleges ltrehozssal, illetve megsemmistssel
kapcsolatban biztostjk, hogy a rszobjektumok s az adattagok ltrehozsakor bekvetke-
z kivteleket a standard knyvtr eljrsai minden kln erfeszts nlkl helyesen kezel-
jk (14.4.1). Ez minden kivtelekkel kapcsolatos eljrs esetben nagyon fontos alapelv.
Gondoljunk arra is, hogy nem a memria az egyetlen olyan erforrs, amelyben lyukak for-
dulhatnak el. A megnyitott fjlok, zrolsok, hlzati kapcsolatok s a szlak is olyan
rendszererforrsok, melyeket egy fggvnynek fel kell szabadtania vagy t kell adnia va-
lamely objektumnak egy kivtel kivltsa eltt.
E. Kivtelbiztossg a standard knyvtrban 1265
E.3. A kivtelbiztossgot megvalst eljrsok
A standard knyvtr szoks szerint bemutatja azokat a problmkat, amelyek sok ms
helyzetben is elfordulhatnak s ezekre olyan megoldst ad, ami szles krben felhasznl-
hat. A kivtelbiztos programok rsnak alapvet eszkzei a kvetkezk:
1. A try blokk (8.3.1)
2. A kezdeti rtkads az erforrs megszerzsvel eljrs (14.4)
A kvetend ltalnos elvek:
3. Soha ne dobjunk ki adatokat gy, hogy nem tudjuk pontosan, mi kerl
a helykre.
4. Az objektumokat mindig rvnyes llapotban hagyjuk, amikor kivtelt vltunk ki.
Ha betartjuk ezeket a szablyokat, minden hibt megfelelen kezelhetnk. Ezen elvek k-
vetse a gyakorlatban azrt jelent problmt, mert mg a legrtalmatlanabbnak tn eljr-
sok (pldul a <, az = vagy a sort()) is kivlthatnak kivteleket. Annak megllaptshoz,
hogy egy programban mit kell megvizsglnunk, nagy tapasztalatra van szksg.
Ha knyvtrat runk, clszer az ers kivtelbiztossgot (E.2) clul kitznnk s minden-
hol meg kell valstanunk az alapbiztostst. Programok rsakor a kivtelbiztossg kevs-
b fontos szempont. Pldul, amikor egy egyszer adatfeldolgoz programot ksztnk sa-
jt magunknak, ltalban nem baj, ha a program egyszeren befejezdik, amikor a virtulis
memria valamilyen hiba miatt elfogy. Ugyanakkor a helyessg s az alapvet kivtelbiz-
tossg szorosan kapcsoldik egymshoz.
Az alapvet kivtelbiztossg megvalstsra szolgl eljrsok pldul az llapotbiztos-
tk megadsa s fenntartsa (24.3.7.1) nagyon hasonltanak azokhoz, melyek segtsg-
vel kicsi s helyesen mkd programokat hozhatunk ltre. Ebbl kvetkezik, hogy az
alapvet kivtelbiztossg (az alapbiztosts; E.2) vagy akr az ers biztosts megval-
stsa csupn jelentktelen teljestmnyromlssal jr. Lsd: E.8[17]
Az albbiakban a vector szabvnyos trol (16.3) egy megvalstst adjuk meg, hogy be-
mutassuk, milyen feladatokat kell megoldanunk az idelis kivtelbiztossg megvalsts-
hoz s hol rdemes alaposabb felgyeletet biztostanunk.
Fggelkek s trgymutat 1266
E.3.1. Egy egyszer vektor
A vector (16.3) leggyakoribb megvalstsban hrom mutat (vagy az ezzel egyenrtk
mutateltols pr) szerepel: egy az els elemre, egy az utols utni elemre s egy az utol-
s utni lefoglalt helyre hivatkozik (17.1.3):
Lssuk, mire van szksgnk a vector deklarcijban, ha csak a kivtelbiztossg s az er-
forrs-lyukak elkerlse szempontjbl vizsgljuk az osztlyt:
template<class T, class A = allocator<T> >
class vector {
private:
T* v; // a lefoglalt terlet eleje
T* space; // az elemsorozat vge, bvts szmra tartalkolt terlet kezdete
T* last; // a lefoglalt terlet vge
A alloc; // memriafoglal
public:
explicit vector(size_type n, const T& val = T(), const A& = A());
vector(const vector& a); // msol konstruktor
vector& operator=(const vector& a); // msol rtkads
~vector();
size_type size() const { return space-v; }
size_type capacity() const { return last-v; }
void push_back(const T&);
// ...
};
E. Kivtelbiztossg a standard knyvtrban 1267
vector:
first
space
last
tartalk hely elemek
Vizsgljunk elszr egy meggondolatlan konstruktor-megvalstst:
template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a) // vigyzat: nav megvalsts
: alloc(a) // memriafoglal msolsa
{
v = alloc.allocate(n); // memria lefoglalsa az elemek szmra (19.4.1)
space = last = v+n;
for (T* p = v; p!=last; ++p) a.construct(p,val); // val ltrehoz msolsa *p-be (19.4.1)
}
Itt hrom helyen keletkezhet kivtel:
1. Az allocate() fggvny kivtelt vlthat ki, ha nincs elegend memria.
2. A memriafoglal (alloktor) msol konstruktora is eredmnyezhet kivtelt.
3. A T elemtpus msol konstruktora is kivlthat kivtelt, ha nem tudja lemsolni
a val rtket.
Egyik esetben sem jn ltre j objektum, teht a vector konstruktora sem fut le (14.4.1).
Ha az allocate() vgrehajtsa sikertelen, a throw mr akkor kilp a konstruktorbl, amikor
mg semmilyen erforrst nem foglaltunk le, gy ezzel minden rendben.
Ha a T msol konstruktora eredmnyez hibt, mr lefoglaltunk valamennyi memrit, gy
azt fel kell szabadtanunk, ha el akarjuk kerlni a memria-elszivrgst. Az igazn nagy
problmt az jelenti, hogy a T msol konstruktora esetleg akkor vlt ki kivtelt, amikor
mr nhny objektumot sikeresen ltrehozott, de mg nem az sszeset.
Ezen problma kezelshez nyilvn kell tartanunk, hogy eddig mely elemeket hoztuk lt-
re, s amikor hiba kvetkezik be, ezeket (s csak ezeket) trlnnk kell:
template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a) // jl kidolgozott megvalsts
: alloc(a) // memriafoglal msolsa
{
v = alloc.allocate(n); // memria lefoglalsa az elemek szmra
iterator p;
try {
iterator end = v+n;
for (p=v; p!=end; ++p) alloc.construct(p,val); // elemek ltrehozsa (19.4.1)
last = space = p;
}
Fggelkek s trgymutat 1268
catch (...) {
for (iterator q = v; q!=p; ++q) alloc.destroy(q); // ltrehozott elemek megsemmistse
alloc.deallocate(v,n); // memria felszabadtsa
throw; // tovbbdobs
}
}
A pluszkltsg ez esetben is csak a try blokk kltsge. Egy j C++-vltozatban ez elhanya-
golhat a memriafoglalshoz s az elemek kezdeti rtkadshoz kpest. Ahol a try blokk
kltsge magas, esetleg beilleszthetjk az if(n) vizsglatot a try el, ezzel az res vektort k-
ln esetknt kezelhetjk.
A konstruktor nagy rszt az uninitialized_fill() fggvny kivtelbiztos megvalstsa tlti ki:
template<class For, class T>
void uninitialized_fill(For beg, For end, const T& x)
{
For p;
try {
for (p=beg; p!=end; ++p)
new(static_cast<void*>(&*p)) T(x); // x ltrehoz msolsa *p-ben (10.4.11)
}
catch (...) { // a ltrehozott elemek trlse s tovbbdobs:
for (For q = beg; q!=p; ++q) (&*q)->~T(); // (10.4.11)
throw;
}
}
A furcsa &*p kifejezsre azrt volt szksg, hogy a nem mutat bejrkat (itertorokat) is
kezelhessk. Ilyenkor ahhoz, hogy mutatt kapjunk, a hivatkozssal meghatrozott elem
cmt kell vennnk. A void* talakts biztostja, hogy a standard knyvtr elhelyez fgg-
vnyt hasznljuk (19.4.5), nem a felhasznl ltal a T* tpusra megadott operator new()
fggvnyt. Ez az eljrs olyan alacsony szinten mkdik, ahol a teljes ltalnossgot mr
elg nehz biztostani.
Szerencsre nem kell jrarnunk az uninitialized_fill() fggvnyt, mert a standard knyv-
tr biztostja hozz a megkvnt ers biztostst (E.2). Gyakran elengedhetetlen , hogy
olyan kezdrtk-ad mvelet lljon rendelkezsnkre, amely vagy minden elemet hibt-
lanul tlt fel kezdrtkkel, vagy hiba esetn egyltaln nem ad vissza elemeket. ppen
ezrt a standard knyvtrban szerepl uninitialized_fill(), uninitialized_fill_n() s
uninitialized_copy() fggvny (19.4.4) biztostja ezt az ers kivtelbiztossgot (E.4.4).
E. Kivtelbiztossg a standard knyvtrban 1269
Figyeljk meg, hogy az uninitialized_fill() nem vdekezik az elemek destruktora vagy
a bejrmveletek ltal kivltott kivtelek ellen (E.4.4), ez ugyanis elviselhetetlenl nagy
kltsget jelentene (lsd E.8[16-17]).
Az uninitialized_fill() nagyon sokfle sorozatra alkalmazhat, ezrt csak elre halad be-
jrkat vesz t (19.2.1), amivel viszont nem tudja garantlni, hogy az elemeket ltrehoz-
sukkal fordtott sorrendben trli.
Az uninitialized_fill() felhasznlsval az albbiakat rhatjuk:
template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a) // zavaros megvalsts
:alloc(a) // memriafoglal msolsa
{
v = alloc.allocate(n); // memria lefoglalsa az elemek szmra
try {
uninitialized_fill(v,v+n,val); // elemek msolsa
space = last = v+n;
}
catch (...) {
alloc.deallocate(v,n); // memria felszabadtsa
throw; // tovbbdobs
}
}
Ennek ellenre ezt a programot nem nevezhetjk szpnek. A kvetkezkben bemutatjuk,
hogyan tehetjk a programot sokkal egyszerbb.
Figyeljk meg, hogy a konstruktor jra kivltja azt a kivtelt, amit elkapott. A cl az, hogy
a vector osztlyt tltszv tegyk a kivtelek eltt, mert gy a felhasznl pontosan meg-
llapthatja a hiba okt. A standard knyvtr sszes trolja rendelkezik ezzel a tulajdon-
sggal. A kivtelekkel szembeni tltszsg gyakran a legjobb lehetsg a sablonok s a ha-
sonl vkony rtegek szmra. Ez ellenttben ll a programrendszerek nagyobb
rszeinek (moduljainak) irnyvonalval, hiszen ezeknek ltalban nllan kell kezelni-
k minden hibt. Pontosabban, az ilyen modulok ksztinek fel kell tudniuk sorolni az
sszes kivtelt, amit a modul kivlthat. Ennek megvalstshoz vagy a kivtelek csoporto-
stsra (14.2), vagy az alacsonyszint eljrsok s a modul sajt kivteleinek sszekapcso-
lsra (14.6.3), esetleg a kivtelek meghatrozsra (14.6) van szksg.
Fggelkek s trgymutat 1270
E.3.2. A memria brzolsa
A tapasztalatok bebizonytottk, hogy helyes, kivtelbiztos programok ksztse try blokkok
segtsgvel bonyolultabb annl, mint amit egy tlagos programoz mg elfogad. Valjban
feleslegesen bonyolult, hiszen van egy msik lehetsg: a kezdeti rtkads az erforrs
megszerzsvel (14.4), melynek segtsgvel cskkenthetjk azon programsorok szmt,
melyek egy stlusos program megvalstshoz szksgesek. Esetnkben a legfontosabb
erforrs, amire a vector osztlynak szksge van, egyrtelmen a memria, melyben az
elemeket troljuk. Ha bevezetnk egy segdosztlyt, amely a vector ltal hasznlt memri-
t brzolja, leegyszersthetjk programunkat s cskkenthetjk annak eslyt, hogy v-
letlenl elfelejtjk felszabadtani a memrit.
template<class T, class A = allocator<T> >
struct vector_base {
A alloc; // memriafoglal
T* v; // a lefoglalt terlet eleje
T* space; // az elemsorozat vge, bvts szmra tartalkolt terlet kezdete
T* last; // a lefoglalt terlet vge
vector_base(const A& a, typename A::size_type n)
: alloc(a), v(a.allocate(n)), space(v+n), last(v+n) { }
~vector_base() { alloc.deallocate(v,last-v); }
};
Amg a v s a last mutat helyes, a vector_base objektum megsemmisthet. A vector_base
osztly a T tpus szmra lefoglalt memrit kezeli s nem T tpus objektumokat, ezrt mi-
eltt egy vector_base objektumot trlnk, minden ennek segtsgvel ltrehozott objektu-
mot is trlnnk kell.
Termszetesen magt a vector_base osztlyt is gy kell megrni, hogy ha kivtel kvetkezik
be (a memriafoglal msol konstruktorban vagy az allocate() fggvnyben), ne jjjn
ltre vector_base objektum, gy memria-elszivrgs sem kvetkezik be.
A vector_base felhasznlsval a vector osztlyt a kvetkezkppen hatrozhatjuk meg:
template<class T, class A = allocator<T> >
class vector : private vector_base<T,A> {
void destroy_elements() { for (T* p = v; p!=space; ++p) p->~T(); } // 10.4.11
public:
explicit vector(size_type n, const T& val = T(), const A& = A());
E. Kivtelbiztossg a standard knyvtrban 1271
vector(const vector& a); // msol konstruktor
vector& operator=(const vector& a); // msol rtkads
~vector() { destroy_elements(); }
size_type size() const { return space-v; }
size_type capacity() const { return last-v; }
void push_back(const T&);
// ...
};
A vector destruktora az sszes elemre egyms utn meghvja a T tpus destruktort. Ebbl
kvetkezik, hogy ha egy elem destruktora kivtelt vlt ki, a vector destruktora sem tud hi-
btlanul lefutni. Ez igen nagy problmt okoz, ha egy msik kivtel miatt kezdemnyezett
verem-visszatekers kzben kvetkezik be, hiszen ekkor a terminate() fggvny fut le
(14.7). Ha a destruktor kivtelt vlt ki, ltalban erforrs-lyukak keletkeznek s azok az
eljrsok, melyeket szablyos viselkeds objektumokhoz fejlesztettek ki, megjsolhatatla-
nul fognak mkdni. Nem igazn van hasznlhat megolds a destruktorokban keletkez
kivtelek kezelsre, ezrt a knyvtr semmilyen biztostst nem vllal, ha az elemek ilyen
viselkedsek lehetnek (E.4).
A konstruktort az albbi egyszer formban adhatjuk meg:
template<class T, class A>
vector<T,A>::vector(size_type n, const T& val, const A& a)
: vector_base<T,A>(a,n) // terlet lefoglalsa n elem szmra
{
uninitialized_fill(v,v+n,val); // elemek msolsa
}
A msol konstruktor mindssze abban klnbzik ettl, hogy az uninitialized_fill() he-
lyett az uninitialized_copy() fggvnyt hasznlja:
template<class T, class A>
vector<T,A>::vector(const vector<T,A>& a)
: vector_base<T,A>(a.alloc,a.size())
{
uninitialized_copy(a.begin(),a.end(),v);
}
Fggelkek s trgymutat 1272
Figyeljk meg, hogy az ilyen stlus konstruktor kihasznlja a nyelvnek azon alapszablyt,
hogy ha a konstruktorban kivtel keletkezik, azokra a rszobjektumokra (s alapobjektu-
mokra), melyek sikeresen ltrejttek, a destruktor is szablyosan lefut (14.4.1). Az
uninitialized_fill() s testvrei (E.4.4) ugyanilyen szolgltatst nyjtanak a flig ltrehozott
sorozatok esetben.
E.3.3. rtkads
Szoks szerint az rtkads abban klnbzik a ltrehozstl, hogy a korbbi rtkekre is
figyelnnk kell. Vizsgljuk meg az albbi megvalstst:
template<class T, class A>
vector<T,A>& vector<T,A>::operator=(const vector& a) // ers biztostst ad (E.2)
{
vector_base<T,A> b(alloc,a.size()); // memria lefoglalsa
uninitialized_copy(a.begin(),a.end(),b.v); // elemek msolsa
destroy_elements();
alloc.deallocate(v,last-v); // a rgi memriaterlet felszabadtsa
vector_base::operator=(b); // brzols elhelyezse
b.v = 0; // felszabadts megelzse
return *this;
}
Ez az rtkads szp s kivtelbiztos is, de tl sok mindent ismtel meg a konstruktorbl s
a destruktorbl. Ennek elkerlsre a kvetkez eljrst rhatjuk:
template<class T, class A>
vector<T,A>& vector<T,A>::operator=(const vector& a) // ers biztostst ad (E.2)
{
vector temp(a); // "a" msolsa
swap< vector_base<T,A> >(*this,temp); // brzolsok felcserlse
return *this;
}
A rgi elemeket a temp destruktora trli, az ltaluk lefoglalt terletet pedig a temp vltoz
vector_base objektumnak destruktora szabadtja fel.
A kt vltozat teljestmnye szinte teljesen egyenl. Valjban csak kt klnbz formban
adjuk meg ugyanazokat a mveleteket. A msodik megvalsts viszont rvidebb s nem is-
mtli a vector osztly egyb fggvnyeiben mr szerepl kdrszleteket, gy az rtkads
ezen vltozata kevesebb hibalehetsget tartalmaz s egyszerbb rendben tartani is.
E. Kivtelbiztossg a standard knyvtrban 1273
Figyeljk meg, hogy az nrtkads szoksos vizsglata hinyzik az eljrsbl (10.4.4):
if (this == &a) return *this;
Ezek az rtkad mveletek elszr ltrehoznak egy msolatot, majd lecserlik az eleme-
ket. Ez a megolds automatikusan kezeli az nrtkads problmjt. gy dntttem, hogy
a ritkn elfordul nrtkads kln vizsglata nem jr annyi haszonnal, hogy rdemes le-
gyen ezzel lasstani az ltalnos esetet, amikor egy msik vector objektumot adunk rtkl.
Mindkt esetben kt, esetleg jelents optimalizlsi lehetsg hinyzik:
1. Ha az rtket kap vektor mrete elg nagy ahhoz, hogy az rtkl adott vek-
tort trolja, nincs szksg j memria lefoglalsra.
2. Az elemek kztti rtkads hatkonyabb lehet, mint egy elem trlse, majd
kln ltrehozsa.
Ha ezeket a javtsokat is beptjk, a kvetkez eredmnyt kapjuk:
template<class T, class A>
vector<T,A>& vector<T,A>::operator=(const vector& a) // optimalizlt, alapbiztosts (E.2)
{
if (capacity() < a.size()) { // j vektorbrzols szmra terlet lefoglalsa
vector temp(a); // "a" msolsa
swap< vector_base<T,A> >(*this,temp); // az brzolsok felcserlse
return *this;
}
if (this == &a) return *this; // vdelem az nrtkads ellen (10.4.4)
// a rgi elemek rtkadsa
size_type sz = size();
size_type asz = a.size();
alloc = a.get_allocator(); // memriafoglal msolsa
if (asz<=sz) {
copy(a.begin(),a.begin()+asz,v);
for (T* p = v+asz; p!=space; ++p) p->~T(); // felesleges elemek felszmolsa (10.4.11)
}
else {
copy(a.begin(),a.begin()+sz,v);
uninitialized_copy(a.begin()+sz,a.end(),space); // tovbbi elemek ltrehozsa
}
space = v+asz;
return *this;
}
Fggelkek s trgymutat 1274
Ezek az optimalizcik nem valsthatk meg bntetlenl. A copy() eljrs nem nyjt ers
kivtelbiztostst, mert nem garantlja, hogy a cl vltozatlan marad, ha msols kzben ki-
vtel kvetkezik be. Teht ha a T::operator=() kivtelt vlt ki a copy() mvelet kzben, el-
fordulhat, hogy az rtket kap vektor megvltozik, de nem lesz az rtkl adott vektor
pontos msolata. Lehetsges pldul, hogy az els t elemet sikerl lemsolni az rtkl
adott vektorbl, de a tbbi vltozatlan marad, st akr az is elkpzelhet, hogy egy elem
(az, amelyiket ppen msoltuk, amikor a T::operator=() kivtelt vltott ki) olyan rtket fog
tartalmazni, amely nem egyezik meg sem az eredetivel, sem a msolandval. Azt azrt el-
mondhatjuk, hogy ha a T::operator=() rvnyes llapotban hagyja operandust egy kivtel
kivltsakor is, a teljes vector is rvnyes llapotban marad, br nem abban az llapotban,
amit szerettnk volna.
A fentiekben a memriafoglalt is rtkadssal msoltuk le. Valjban a memriafoglalk-
tl nem mindig kveteljk meg, hogy rendelkezzenek rtkadssal (19.4.3, lsd
mg:E.8[9]).
A standard knyvtr vector rtkadsnak legutbbi megvalstsa gyengbb kivtelbiztos-
sgot, de nagyobb hatkonysgot biztost. Csak az alapbiztostst nyjtja, ami megfelel
a legtbb programoz kivtelbiztossgrl alkotott fogalmnak, de nem ll rendelkezsnk-
re ers biztosts (E.2). Ha olyan rtkadsra van szksgnk, amely kivtel fellptekor
a vector-t vltozatlanul hagyja, olyan knyvtr-megvalstst kell hasznlnunk, amely ers
biztostst nyjt az ilyen helyzetekben is vagy sajt magunknak kell megrni az rtkad
mveletet:
template<class T, class A>
void safe_assign(vector<T,A>& a, const vector<T,A>& b) // "magtl rtend" a = b
{
vector<T,A> temp(a.get_allocator());
temp.reserve(b.size());
for (typename vector<T,A>::iterator p = b.begin(); p!=b.end(); ++p)
temp.push_back(*p);
swap(a,temp);
}
Ha nincs elegend memria ahhoz, hogy ltrehozzuk a temp vltozt b.size() elem szm-
ra, az std:bad_alloc kivtelt vltjuk ki, mieltt brmilyen vltoztatst vgeznnk az a vekto-
ron. Ehhez hasonlan, ha a push_back() vgrehajtsa nem sikerl, az a akkor is rintetlen
marad, hiszen minden push_back() mveletet a temp objektumon hajtunk vgre az a he-
lyett. Ezzel a megoldssal azt is biztostjuk, hogy felszabaduljon a temp minden eleme, amit
a push_back() segtsgvel ltrehoztunk, mieltt a hibt okoz kivtelt jra kivltannk.
E. Kivtelbiztossg a standard knyvtrban 1275
A swap() nem msolja a vektorok elemeit, csupn lecserli a vector adattagjait, gy
a vector_base objektumot is. Ennek kvetkeztben a swap() akkor sem vlthat ki kivtelt,
ha az elemek mveletei kpesek erre (E.4.3). A safe_assign() nem kszt felesleges mso-
latokat az elemekrl, teht elg hatkony tud lenni.
Szoks szerint vannak ms lehetsgek is a nyilvnval megvalsts mellett, pldul rbz-
hatjuk magra a knyvtrra, hogy az elemeket az ideiglenes vektorba msolja:
template<class T, class A>
void safe_assign(vector<T,A>& a, const vector<T,A>& b) // egyszer a = b
{
vector<T,A> temp(b); // b elemeinek msolsa az ideiglenes vltozba
swap(a,temp);
}
St, egyszer rtk szerinti paramtertadst (7.2) is hasznlhatunk:
template<class T, class A>
void safe_assign(vector<T,A>& a, vector<T,A> b) // egyszer a = b
// (figyelem: b rtk szerint tadva)
{
swap(a,b);
}
A safe_assign() ez utbbi kt vltozata a vector memriafoglaljt nem msolja le, ami meg-
engedett optimalizci (lsd 19.4.3).
E.3.4. A push_back()
A kivtelbiztossg szemszgbl nzve a push_back() nagyon hasonlt az rtkadsra ab-
ban, hogy nem szabad a vector objektumot megvltoztatnunk, ha az j elem beillesztse va-
lamilyen problmba tkzik:
template< class T, class A>
void vector<T,A>::push_back(const T& x)
{
if (space == last) { // nincs tbb hely; thelyezs
vector_base b(alloc,size()?2*size():2); // a lefoglals megkettozse
uninitialized_copy(v,space,b.v);
new(b.space) T(x); // x msolatnak *b.space-be helyezse (10.4.11)
++b.space;
destroy_elements();
Fggelkek s trgymutat 1276
swap<vector_base<T,A> >(b,*this); // az brzolsok felcserlse
return;
}
new(space) T(x); // x msolatnak *space-be helyezse (10.4.11)
++space;
}
Termszetesen, a *space kezdeti rtkadsra szolgl msol konstruktor vlthat ki kiv-
telt. Ha ez trtnik, a vector vltozatlan marad s a space rtkt sem nveljk meg. Ilyen-
kor a vektor elemeit sem kell thelyeznnk a memriban, teht az eddigi bejrk is r-
vnyben maradnak. gy ez a megvalsts ers biztostst ad: ha egy memriafoglal vagy
egy felhasznli eljrs kivtelt vlt ki, a vector nem vltozik meg. A standard knyvtr ilyen
biztostst nyjt a push_back() fggvnyhez (E.4.1).
Figyeljk meg, hogy az eljrsban nincs egyetlen try blokk sem (attl eltekintve, ami az
uninitialized_copy() fggvnyben, rejtve szerepel). A mdostst a mveletek sorrendj-
nek pontos megvlasztsval hajtjuk vgre, gy ha kivtel keletkezik, a vektor rtke nem
vltozik meg.
Ez a megkzelts miszerint az utastsok sorrendje adja a kivtelbiztossgot s a kez-
deti rtkads az erforrs megszerzsvel (14.4) alkalmazsa elegnsabb s hatko-
nyabb megoldst knl, mint a hibk kifejezett kezelse try blokkok segtsgvel. Ha utas-
tsainkat szerencstlen sorrendben rjuk le, sokkal tbb kivtelbiztossgi problmval kell
megkzdennk, mint a kivtelkezel eljrsok elhagysa mellett. A sorrend meghatroz-
sakor az alapszably az, hogy ne semmistsnk meg informcit, mieltt az azt helyettest
adatokat ltre nem hoztuk s nem biztostottuk, hogy azokat kivtelek veszlye nlkl tr-
hassuk a helykre.
A kivtelkezels egyik kvetkezmnye, hogy a program vgrehajtsa sorn a vezrls meg-
lep helyekre kerlhet. Az olyan egyszer, helyi vezrlssel rendelkez fggvnyekben,
mint az operator=(), a safe_assign() vagy a push_back(), meglepetsek ritkbban fordulnak
el. Ha rnznk egy kdrszletre, viszonylag egyszeren megllapthatjuk, hogy egy adott
sor vlthat-e ki kivtelt s mi lesz annak kvetkezmnye. A nagyobb fggvnyekben, me-
lyekben bonyolult vezrlsi szerkezeteket (pldul bonyolult feltteles utastsokat s egy-
msba gyazott ciklusokat) hasznlunk, az ilyen krdsekre nehz vlaszt adni. A try blok-
kok alkalmazsa a helyi vezrlst mg tovbb bonyoltja, gy jabb keveredseket s
hibkat eredmnyezhet (14.4). Azt hiszem, az utastsrendezs s a kezdeti rtkads az
erforrs megszerzsvel hatkonysga a nagyobb mret try blokkokkal szemben ppen
a helyi vezrls egyszerstsben rejlik. Egyszerbben fogalmazva, a stlusos programo-
kat egyszerbb megrteni s egyszerbb helyesen megrni.
E. Kivtelbiztossg a standard knyvtrban 1277
Gondoljunk r, hogy az itt szerepl vector csak a kivtelek ltal okozott problmk s az
ezen problmk megoldsra kidolgozott eljrsok bemutatsra szolgl. A szabvny nem
kveteli meg, hogy minden megvalsts pontosan gy nzzen ki, ahogy itt bemutatjuk.
A szabvny ltal biztostott garancikat az E.4. pontban trgyaljuk.
E.3.5. Konstruktorok s invarinsok
A kivtelbiztossg szemszgbl nzve a vector tbbi mvelete vagy ugyangy viselkedik,
mint az eddig bemutatottak (mivel hasonl mdon foglalnak le s szabadtanak fel erfor-
rsokat), vagy megvalstsuk egyszer (mert nem vgeznek olyan tevkenysget, amely-
ben az rvnyes llapot fenntartsa problmt okozhatna). A legtbb osztlyban ezek a tri-
vilis fggvnyek jelentik a dnt tbbsget. Az ilyen eljrsok bonyolultsga elssorban
attl fgg, hogy a konstruktor milyen mkdsi krnyezetet biztost szmukra. Mskppen
fogalmazva, az ltalnos tagfggvnyek bonyolultsga elssorban a j osztlyinvarins
megvlasztsn mlik (24.3.7.1). Az egyszer vektormveletek vizsglatval megrthetjk,
mitl lesz j egy osztlyinvarins s hogyan kell megrnunk a konstruktorokat ahhoz, hogy
ezeket az invarinsokat biztostsk.
Az olyan mveleteket, mint a vektorok indexelse (16.3.3) azrt knny megvalstani,
mert ersen ptenek arra az invarinsra, amit a konstruktor hoz ltre s az erforrsokat
lefoglal, illetve felszabadt fggvnyek tartanak fenn. Az indexel mvelet pldul hivat-
kozhat a v tmbre, amely az elemeket trolja:
template< class T, class A>
T& vector<T,A>::operator[](size_type i)
{
return v[i];
}
Nagyon fontos s alapvet szably, hogy a konstruktornak kell lefoglalnia az erforrsokat
s biztostania kell egy egyszer invarinst. Ahhoz, hogy ennek fontossgt megrtsk, nz-
zk meg a vector_base defincijnak albbi vltozatt:
template<class T, class A = allocator<T> > // a konstruktor esetlen hasznlata
class vector_base {
public:
A alloc; // memriafoglal
T* v; // a lefoglalt terlet eleje
T* space; // az elemsorozat vge, bvts szmra tartalkolt terlet kezdete
T* last; // a lefoglalt terlet vge
Fggelkek s trgymutat 1278
vector_base(const A& a, typename A::size_type n) : alloc(a), v(0), space(0), last(0)
{
v = alloc.allocate(n);
space = last = v+n;
}
~vector_base() { if (v) alloc.deallocate(v,last-v); }
};
Itt a vector_base objektumot kt lpsben hozzuk ltre. Elszr egy biztonsgi llapotot
alaktunk ki, amely a v, a space s a last vltozt 0-ra lltja, s csak ezutn prblunk me-
mrit foglalni. Ez arra az indokolatlan flelemre vezethet vissza, hogy az elemek lefogla-
lsa kzben keletkez kivtelek miatt flig ltrejtt objektumot hozhatunk ltre. A flelem
azrt indokolatlan, mert ilyen objektumot egyltaln nem hozhatunk ltre. A szablyok,
melyek a statikus, automatikus, illetve tagobjektumok s a standard knyvtr trolinak
elemeire vonatkoznak, megakadlyozzk ezt. Azokban a szabvny eltti knyvtrakban
azonban, melyek a trolkban elhelyez new opertort (10.4.11) hasznltak (hasznlnak)
az objektumok ltrehozsra s nem foglalkoztak (foglalkoznak) a kivtelbiztossggal, ez
elfordulhatott (elfordulhat). A megrgztt szoksokon nehz vltoztatni.
Figyeljk meg, hogy a biztonsgosabb program ellltsra irnyul prblkozs tovbb
bonyoltja az osztlyinvarinst: nem lehetnk biztosak abban, hogy a v egy ltez, lefoglalt
memriaterletre mutat, mert szerepelhet benne a 0 rtk is. Ez a hiba azonnal megbosszul-
ja magt. A standard knyvtr nem kveteli meg a memriafoglalktl, hogy egy 0 rtket
tartalmaz mutatt biztonsgosan szabadtsanak fel (19.4.1). A memriafoglalk ebben el-
trnek a delete mvelettl (6.2.6). Ebbl kvetkezik, hogy a destruktorban kln ellenr-
zst kell vgeznnk. Ezenkvl minden elemnek kezdrtket adunk s csak ksbb rtel-
mes rtket. Ezen kltsgek egy olyan elemtpus esetben lehetnek jelentsek, ahol az
rtkads bonyolult, pldul a string vagy a list osztlynl.
A ktlpses ltrehozs nem szokatlan megolds. Gyakran olyan formban jelenik meg,
hogy a konstruktor csak egy egyszer s biztonsgos kezdeti rtkadst vgez, mellyel az
objektum trlhet llapotba kerl. A valdi kezdeti rtkadst egy init() fggvny vgzi
el, melyet a felhasznlnak kln meg kell hvnia:
template<class T> // rgies (szabvny s kivtelkezels eltti) stlus
class vector_base {
public:
T* v; // a lefoglalt terlet eleje
T* space; // az elemsorozat vge, bvts szmra tartalkolt terlet kezdete
T* last; // a lefoglalt terlet vge
E. Kivtelbiztossg a standard knyvtrban 1279
vector_base() : v(0), space(0), last(0) { }
~vector_base() { free(v); }
bool init(size_t n) // igazat ad vissza, ha a kezdeti rtkads sikerlt
{
if (v = (T*)malloc(sizeof(T)*n)) {
uninitialized_fill(v,v+n,T());
space = last = v+n;
return true;
}
return false;
}
};
Ezen stlus lthat elnyei a kvetkezk:
1. A konstruktor nem vlthat ki kivtelt s az init() segtsgvel megvalstott kez-
deti rtkads sikeressgt a szoksos mdszerekkel (azaz nem kivtelekkel)
ellenrizhetjk.
2. rvnyes llapot ll rendelkezsnkre, melyet brmely mvelet komoly probl-
ma esetn is biztostani tud.
3. Az erforrsok lefoglalst mindaddig halaszthatjuk, amg tnylegesen szks-
gnk nincs kezdrtkkel rendelkez objektumokra.
A kvetkez rszfejezetekben ezeket a szempontokat vizsgljuk meg s bemutatjuk, hogy
a ktlpses ltrehozs mirt nem biztostja az elvrt elnyket, amellett, hogy ms probl-
mkat is felvet.
E.3.5.1. Az init() fggvny hasznlata
Az els pont (az init() eljrs hasznlata a konstruktor helyett) valjban nem is elny.
A konstruktorok s kivtelek hasznlata sokkal ltalnosabb s rendezettebb megolds az
erforrs-lefoglalsi hibk s a kezdeti rtkadssal kapcsolatos problmk kezelsre
(14.1, 14.4). Ez a stlus a kivtelek nlkli C++ maradvnya.
Ha a kt stlusban ugyanolyan figyelmesen runk meg egy programot, azt tapasztalhatjuk,
hogy kt, szinte teljesen egyenrtk eredmnyt kapunk. Az egyik:
int f1(int n)
{
vector<X> v;
// ...
Fggelkek s trgymutat 1280
if (v.init(n)) {
// "v" n elem vektora
}
else {
// a problma kezelse
}
}
Mg msik vltozat:
int f2(int n)
try {
vector v<X> v(n);
// ...
// "v" n elem vektora
}
catch (...) {
// a problma kezelse
}
A kln init() fggvny hasznlata viszont lehetsget ad az albbi hibk elkvetsre:
1. Elfelejtjk meghvni az init() fggvnyt (10.2.3).
2. Elfelejtjk megvizsglni, hogy az init() sikerrel jrt-e.
3. Elfelejtjk, hogy az init() kivteleket vlthat ki.
4. Hasznlni kezdnk egy objektumot, mieltt meghvnnk az init() eljrst.
A vector<T>::init() defincija a [3] pontra mutat pldt.
Egy j C++-vltozatban az f2() egy kicsit gyorsabb is, mint az f1(), mert az ltalnos eset-
ben nem vgez ellenrzst.
E.3.5.2. Alaprtelmezett rvnyes llapot
A msodik pont (miszerint egy knnyen elllthat, alaprtelmezett rvnyes llapot ll
rendelkezsnkre) ltalban tnyleg elny, de a vector esetben ez felesleges kltsgeket
jelent, ugyanis elkpzelhet olyan vector_base, ahol v==0 s a vector megvalstsnak
mindenhol vdekeznie kell ez ellen:
template< class T>
T& vector<T>::operator[](size_t i)
{
E. Kivtelbiztossg a standard knyvtrban 1281
if (v) return v[i];
// hibakezels
}
Ha megengedjk a v==0 llapotot, a tartomny-ellenrzs nlkli indexels ugyanolyan
lass lesz, mint az ellenrztt hozzfrs:
template< class T>
T& vector<T>::at(size_t i)
{
if (i<v.size()) return v[i];
throw out_of_range("vector index");
}
Itt alapjban vve annyi trtnt, hogy a vector_base eredeti invarinst tlbonyoltottuk, az-
zal, hogy bevezettk a v==0 lehetsget. Ennek kvetkeztben a vector eredeti invarinst
is ugyangy kellett mdostanunk, teht a vector s a vector_base minden eljrst bonyo-
lultabban kell megfogalmaznunk. Ez szmtalan hiba forrsa lehet, pldul nehezebb lesz
a kd mdostsa s a program lassabban fog futni. Gondoljunk arra, hogy a modern kip-
ts szmtgpeknl a feltteles utastsok meglepen kltsgesek lehetnek. Ha fontos
a hatkonysg, a kulcsmveletek pldul a vektorindexels feltteles utastsok nlk-
li megvalstsa elsrend kvetelmny lehet.
rdekes mdon, mr a vector_base eredeti meghatrozsa is biztost egy knnyen ltrehoz-
hat rvnyes llapotot. Csak akkor ltezhet egy vector_base objektum, ha a kezdeti hely-
foglals sikeres volt. Ebbl kvetkezik, hogy a vector rjnak biztostania kell egy vszki-
jrat fggvnyt, pldul a kvetkez formban:
template< class T, class A>
void vector<T,A>::emergency_exit()
{
space = v; // *this mretnek 0-ra lltsa
throw Total_failure();
}
Ez a megolds tlsgosan drasztikus, mert nem hvja meg az elemek destruktorait s nem
szabadtja fel a vector_base objektumban az elemek ltal elfoglalt terletet. Rviden fogal-
mazva, nem nyjt alapbiztostst (E.2). Ha figyelnk a v s a space adattag tartalmra s az
elemek destruktoraira, elkerlhetjk az erforrs-lyukak kialakulst:
template< class T, class A>
void vector<T,A>::emergency_exit()
{
Fggelkek s trgymutat 1282
destroy_elements(); // takarts
throw Total_failure();
}
Figyeljk meg, hogy a szabvnyos vector olyan egyszer szerkezet, amely a lehet legki-
sebbre cskkenti a ktlpses ltrehozs miatt jelentkez problmk lehetsgt. Az init()
fggvny szinte egyenrtk a resize() eljrssal, a v==0 lehetsget pedig a legtbb eset-
ben a size()==0 vizsglat elvgzsvel is kezelhetjk. A ktlpses ltrehozs eddig bemu-
tatott negatv hatsai mg ersebben jelentkeznek, ha programunkban szerepel egy olyan
osztly, amelynek jelents erforrsokra pldul hlzati kapcsolatra vagy kls llom-
nyokra van szksge. Ezek az osztlyok ritkn kpezik egy olyan keretrendszer rszt,
amely felgyeli hasznlatukat s megvalstsukat, olyan formban, ahogy a standard
knyvtr kvetelmnyei felgyelik a vector hasznlatt. A problmk szma mg tovbb
nvekszik, ha az alkalmazs cljai s a megvalstsukhoz szksges erforrsok kapcso-
lata bonyolult. Nagyon kevs olyan osztly van, amely annyira kzvetlenl kapcsoldik
a rendszer erforrsaihoz, mint a vector.
Az egyszer biztonsgos llapot ltezsnek elve alapjban vve nagyon hasznos. Ha egy
objektumot nem tudunk rvnyes llapotba lltani anlkl, hogy kivtelektl kellene tar-
tanunk a mvelet befejezse eltt, valban problmink lehetnek. A biztonsgos llapot-
nak viszont az osztly szerephez termszetesen kell kapcsoldnia, nem erltetett mdon,
az osztly invarinst bonyoltva.
E.3.5.3. Az erforrs-lefoglals ksleltetse
A msodik ponthoz hasonlan (E.3.5.2) a harmadik is egy j tlet rossz megvalstsa, ami
nyeresg helyett inkbb vesztesget eredmnyez. A legtbb esetben klnsen az olyan
trolkban, mint a vector az erforrs-lefoglals ksleltetsnek legjobb mdja a progra-
moz szmra az, hogy magt az objektumot hozza ltre, amikor szksge van r. Nzzk
meg pldul a vector objektum albbi felhasznlst:
void f(int n)
{
vector<X> v(n); // n darab alaprtelmezett X tpus objektum ltrehozsa
// ...
v[3] = X(99); // v[3] igazi "kezdeti rtkadsa"
// ...
}
E. Kivtelbiztossg a standard knyvtrban 1283
Nagy pazarls egy X tpus objektumot csak azrt ltrehozni, mert valamikor, ksbb rt-
ket fogunk adni neki. Klnsen nagy a vesztesg, ha az X osztlyra az rtkads kltsges
mvelet. Ezrt az X ktlpses ltrehozsa elnysnek tnhet. Az X maga is lehet egy
vector, ezrt a vector ktlpses ltrehozstl az res vektorok ltrehozsi kltsgeinek
cskkentst remlhetjk, az alaprtelmezett (res) vektorok ltrehozsa azonban mr
egybknt is elg hatkony, ezrt felesleges a megvalstst azzal bonyoltanunk, hogy az
res vektort kln esetknt kezeljk. ltalnosabban fogalmazva, a felesleges kezdeti r-
tkadsok elkerlsre ritkn jelent tkletes megoldst az, hogy a konstruktorbl kiemel-
jk az sszetettebb kezdeti rtkadsokat:
void f2(int n)
{
vector<X> v; // res vektor ltrehozsa
// ...
v.push_back(X(99)); // elemek ltrehozsa, amikor szksges
// ...
}
sszefoglalva: a ktlpses ltrehozs sokkal bonyolultabb osztlyinvarinshoz s ltal-
ban kevsb elegns, tbb hibalehetsget tartalmaz s nehezebben kezelhet program-
hoz vezet. Ezrt a nyelv ltal tmogatott konstruktor elv jobban hasznlhat, mint az
init() fggvnyes megolds". Teht az erforrsokat mindig a konstruktorban foglaljuk le,
ha a ksleltetett erforrs-lefoglalst nem teszi ktelezv maga az osztly termszete.
E.4. A szabvnyos trolk garancii
Ha a knyvtr valamelyik mvelete nmaga vlt ki kivtelt, akkor biztostani tudja s biz-
tostja is , hogy az ltala hasznlt objektumok rvnyes llapotban maradnak. A vector ese-
tben pldul az at() fggvny (16.3.3) kpes kivltani egy out_of_range kivtelt, ez azon-
ban nem jelent problmt a vektor kivtelbiztossga szempontjbl. Az at() fggvny
megrjnak nem jelent problmt, hogy a vektort rvnyes llapotba lltsa a kivtel kivl-
tsa eltt. Problmk csak akkor jelentkeznek a knyvtr megvalsti, a knyvtr fel-
hasznli, illetve azok szmra, akik megprbljk megrteni a programot , amikor fel-
hasznli eljrsok vltanak ki kivtelt.
A standard knyvtr troli alapbiztostst nyjtanak (E.2): a knyvtr alap invarinsai
mindig megmaradnak s ha a felhasznl a kvetelmnyeknek megfelelen jr el, nem ke-
letkeznek erforrs-lyukak sem. A felhasznli eljrsoktl azt kveteljk meg, hogy ne
Fggelkek s trgymutat 1284
hagyjk a trolk elemeit rvnytelen llapotban s a destruktorok ne vltsanak ki kivtelt.
Az eljrsokon most azokat a fggvnyeket rtjk, melyeket a standard knyvtr megval-
stsban felhasznlunk, teht a konstruktorokat, az rtkadsokat, a destruktorokat, illetve
a bejrk mveleteit (E.4.4).
A programoz ezeket a mveleteket knnyen megrhatja a knyvtr elvrsainak megfele-
len. A kvetelmnyeket ltalban akkor is kielgtik eljrsaink, ha nem tudatosan figye-
lnk rjuk. A kvetkez tpusok biztosan kielgtik a standard knyvtr kvetelmnyeit
a trolk elemtpusaira vonatkozan:
1. A beptett tpusok, kztk a mutatk
2. Azok a tpusok, melyek nem tartalmaznak felhasznli mveleteket
3. Az olyan mveletekkel rendelkez osztlyok, melyek nem vltanak ki kivtele-
ket s nem hagyjk operandusaikat rvnytelen llapotban
4. Azok az osztlyok, melyek destruktora nem vlt ki kivtelt, s amelyeknl
knnyen ellenrizhet, hogy a standard knyvtr ltal hasznlt eljrsok (a
konstruktorok, az rtkadsok, a <, az == s a swap() fggvny) nem hagyjk
operandusaikat rvnytelen llapotban
Azt is ellenriznnk kell minden esetben, hogy a mveletek ne hozzanak ltre erforrs-
lyukakat:
void f(Circle* pc, Triangle* pt, vector<Shape*>& v2)
{
vector<Shape*> v(10); // vektor ltrehozsa vagy bad_alloc kivtel kivltsa
v[3] = pc; // nem vlt ki kivtelt
v.insert(v.begin()+4,pt); // vagy beszrja a pt elemet, vagy nincs hatsa v-re
v2.erase(v2.begin()+3); // vagy trli v2[3]-t, vagy nincs hatsa v2-re
v2 = v; // vagy tmsolja v-t, vagy nincs hatsa v2-re
// ...
}
Amikor az f() futsa vget r, v szablyosan trldni fog, mg v2 rvnyes llapotban lesz.
A fenti rszlet nem mutatja, ki felel a pc s a pt trlsrt. Ha f() a felels, akkor vagy el kell
kapnia a kivteleket s gy kezelni a szksges trlseket, vagy a mutatkat loklis auto_ptr
vltozkhoz kell ktnie.
Ennl rdekesebb krds, mikor ad a knyvtr ers biztostst, azaz mely mveletek m-
kdnek gy, hogy vagy sikeresen futnak le, vagy semmilyen vltoztatst nem hajtanak vg-
re operandusaikon.
E. Kivtelbiztossg a standard knyvtrban 1285
Pldul:
void f(vector<X>& vx)
{
vx.insert(vx.begin()+4,X(7)); // elem hozzadsa
}
ltalban az X mveletei s a vector<X> osztly memriafoglalja vlthat ki kivtelt. Mit
mondhatunk a vx elemeirl, ha az f() fggvny futsa kivtel kvetkeztben szakad meg?
Az alapbiztosts garantlja, hogy erforrs-lyukak nem keletkeznek s a vx elemei rv-
nyes llapotban maradnak. De pontosan milyen elemekrl van sz? Elkpzelhet, hogy egy
elem azrt trldik, mert az insert() csak gy tudja az alapbiztosts kvetelmnyeit vissza-
lltani? Gyakran nem elg annyit tudnunk, hogy a trol j llapotban van, pontosan tud-
ni akarjuk azt is, milyen llapotrl van sz. A kivtel kezelse utn ltalban tisztban sze-
retnnk lenni azzal, hogy milyen elemek szerepelnek a vektorban, mert ellenkez esetben
komolyabb hibakezelst kellene vgeznnk.
E.4.1. Elemek beszrsa s trlse
Az elemek beszrsa egy trolba, illetve az elemek trlse onnan nyilvnval pldja azon
mveleteknek, melyek a trolt megjsolhatatlan llapotban hagyhatnk egy kivtel bek-
vetkezsekor. Ennek oka leginkbb az, hogy a beszrs s a trls sorn sok olyan mve-
letet hajtunk vgre, amely kivtelt vlthat ki:
1. j rtket msolunk a trolba.
2. A trolbl eltvoltott elemet meg is kell semmistennk.
3. Az j elem trolshoz nha memrit is kell foglalnunk.
4. A vector s a deque elemeit nha j helyre kell thelyeznnk.
5. Az asszociatv trolk sszehasonlt eljrsokat alkalmaznak az elemekre.
6. Sok beszrs s trls esetben bejr mveleteket is vgre kell hajtanunk.
Ezek a mveletek mind okozhatnak kivteleket, ezt semmilyen biztosts (E.2) nem aka-
dlyozza meg. Ahhoz, hogy ezekre az esetekre valamilyen biztostst nyjtsunk, elviselhe-
tetlenl kltsges eljrsokra lenne szksg. Ennek ellenre a knyvtr vdi magt s
a felhasznlkat a tbbi, felhasznli fggvny kivteleitl.
Amikor lncolt adatszerkezeteken vgznk mveleteket (pldul egy list-en vagy map-en),
gy szrhatunk be s tvolthatunk el elemeket, hogy a trol tbbi elemre nem vagyunk
Fggelkek s trgymutat 1286
hatssal. Ugyanez nem valsthat meg az olyan trolkban, ahol tbb elem szmra egyet-
len, folytonos memriaterletet foglalunk le (pldul a vector s a deque esetben). Ilyen-
kor az elemeket nha j helyre kell mozgatnunk.
Az alapbiztostson tl a standard knyvtr ers biztostst is nyjt nhny olyan mvelet-
hez, amely elemeket szr be vagy trl. Mivel a lncolt adatszerkezetekkel megvalsthat
trolk ebbl a szempontbl jelentsen eltrnek az elemek trolshoz folytonos memria-
terletet hasznlktl, a standard knyvtr teljesen ms garancikat ad a klnbz
trolfajtkhoz:
1. Garancik a vector (16.3) s a deque (17.2.3) osztlyra:
Ha egy push_back() vagy egy push_front() mvelet okoz kivtelt, akkor az
nem vltoztatja meg operandusait.
Ha egy insert() utasts vlt ki kivtelt s azt nem egy elem msol
konstruktora vagy rtkad mvelete okozta, akkor az sem vltoztat
operandusain.
Az erase() mvelet csak akkor vlt ki kivtelt, ha azt az elemek msol
konstruktora vagy rtkad mvelete okozza.
A pop_back() s a pop_front() nem okoz kivtelt.
2. A list (17.2.2) garancii:
Ha egy push_back() vagy push_front() mvelet vlt ki kivtelt, akkor a fgg-
vny hatstalan.
Ha az insert() okoz kivtelt, akkor az nem vltoztatja meg operandusait.
Az erase(), a pop_back(), a pop_front(), a splice() s a reverse() sohasem vlt
ki kivtelt.
Ha a prediktumok s az sszehasonlt fggvnyek nem okoznak kivtelt,
akkor a list osztly remove(), remove_if(), unique(), sort() s merge() eljr-
sai sem vlthatnak ki kivtelt.
3. Garancik asszociatv trolkra (17.4):
Ha egy elem beszrsa kzben az insert() kivtelt vlt ki, akkor a fggvny
hatstalan.
Az erase() nem okozhat kivtelt.
Jegyezzk meg, hogy ha ers biztosts ll rendelkezsnkre egy trol valamelyik mve-
letben, akkor minden bejr, az elemekre hivatkoz sszes mutat s hivatkozs (referen-
cia) rvnyes marad kivtel bekvetkezse esetn is.
A szablyokat egy tblzatban foglalhatjuk ssze:
E. Kivtelbiztossg a standard knyvtrban 1287
Fggelkek s trgymutat 1288
A trolmveletek garancii
vector deque list map
clear() nem lehet kivtel nem lehet kivtel nem lehet kivtel nem lehet kivtel
(msols) (msols)
erase() nem lehet kivtel nem lehet kivtel nem lehet kivtel nem lehet kivtel
(msols) (msols)
1 elem insert() ers ers ers ers
(msols) (msols)
N elem insert() ers ers ers alap
(msols) (msols)
merge() nem lehet kivtel
(sszehasonlts)
push_back() ers ers ers
push_front() ers ers
pop_back() nem lehet kivtel nem lehet kivtel nem lehet kivtel
pop_front() nem lehet kivtel nem lehet kivtel
remove() nem lehet kivtel
(sszehasonlts)
remove_if() nem lehet kivtel
(prediktum)
reverse() nem lehet kivtel
splice() nem lehet kivtel
swap() nem lehet kivtel nem lehet kivtel nem lehet kivtel nem lehet kivtel
(sszehasonlts
msolsa)
unique() nem lehet kivtel
(sszehasonlts)
A tblzat elemeinek jelentse:
alap A mvelet csak alapbiztostst nyjt (E.2).
ers A mvelet ers biztostst nyjt (E.2).
nem lehet kivtel A mvelet nem vlthat ki kivtelt (E.2).
A mvelet ebben a trolban nem szerepel tagfggvnyknt.
Ahol a biztosts megkveteli, hogy a felhasznl ltal megadott bizonyos mveletek ne
vltsanak ki kivtelt, ott a biztosts alatt zrjelben feltntettk, milyen mveletekre kell fi-
gyelnnk. Ezek a kvetelmnyek pontosan megegyeznek a tblzat eltt, szvegesen meg-
fogalmazott felttelekkel.
A swap() fggvnyek abban klnbznek a tbbi eljrstl, hogy nem tagfggvnyek.
A clear() fggvnyre vonatkoz garancia az erase() biztostsbl kvetkezik. (16.3.6)
A tblzatban az alapbiztostson tli szolgltatsokat tntettk fel, teht nem szerepelnek
azok az eljrsok (pldul a reverse() vagy a unique() a vector osztlyra), melyek tovbbi
biztosts nlkl valstanak meg valamilyen algoritmust az sszes sorozatra.
A majdnem-trol basic_string (17.5, 20.3) minden mveletre garantlja az alapbiztos-
tst (E.5.1). A szabvny azt is biztostja, hogy a basic_string osztly erase() s swap() eljr-
sa nem okoz kivtelt, az insert() s a push_back() fggvnyre pedig ers biztostst kapunk.
Az ers biztostst nyjt eljrsokban amellett, hogy a trol vltozatlan marad, az sszes
bejr, mutat s referencia is rvnyes marad:
void update(map<string,X>& m, map<string,X>::iterator current)
{
X x;
string s;
while (cin>>s>>x)
try {
current = m.insert(current,make_pair(s,x));
}
catch(...) {
// itt a "current" mg mindig az aktulis elemet jelli
}
}
E. Kivtelbiztossg a standard knyvtrban 1289
E.4.2. Garancik s kompromisszumok
Az alapbiztostson tli szolgltatsok sszevisszasgai a megvalstsi lehetsgekkel ma-
gyarzhatk. A programozk azt szeretnk leginkbb, hogy mindenhol ers biztosts ll-
jon rendelkezskre a lehet legkevesebb korltozs mellett, de ugyanakkor azt is elvrjk,
hogy a standard knyvtr minden mvelete optimlisan hatkony legyen. Mindkt elvrs
jogos, de sok mvelet esetben lehetetlen egymssal prhuzamosan megvalstani. Ahhoz,
hogy jobban megvilgtsuk az elkerlhetetlen kompromisszumokat, megvizsgljuk, milyen
mdokon lehet egy vagy tbb elemet felvenni egy listba, vektorba vagy map-be.
Nzzk elszr, hogy egy elemet hogyan vihetnk be egy listba vagy egy vektorba. Szo-
ks szerint, a push_back() nyjtja a legegyszerbb lehetsget:
void f(list<X>& lst, vector<X>& vec, const X& x)
{
try {
lst.push_back(x); // hozzads a listhoz
}
catch (...) {
// lst vltozatlan
return;
}
try {
vec.push_back(x); // hozzads a vektorhoz
}
catch (...) {
// vec vltozatlan
return;
}
// lst s vec egy-egy x rtk j elemmel rendelkezik
}
Az ers biztosts megvalstsa ez esetben egyszer s olcs. Az eljrs azrt is hasznos,
mert teljesen kivtelbiztos megoldst ad az elemek felvtelre. A push_back() azonban
asszociatv trolkra nem meghatrozott: a map osztlyban nincs back(). Egy asszociatv t-
rol esetben az utols elemet a rendezs hatrozza meg, nem a pozci.
Az insert() fggvny garancii mr kicsit bonyolultabbak. A gondot az jelenti, hogy az
insert() mveletnek gyakran kell egy elemet a trol kzepn elhelyeznie. Lncolt adat-
szerkezeteknl ez nem jelent problmt, teht a list s a map egyszeren megvalsthat,
a vector esetben azonban elre lefoglalt terlet ll rendelkezsnkre, a vector<X>::insert()
fggvny egy tlagos megvalstsa pedig a beszrsi pont utni elemeket thelyezi, hogy
helyet csinljon az j elem szmra. Ez az optimlis megolds, de arra nincs egyszer md-
Fggelkek s trgymutat 1290
szer, hogy a vektort visszalltsuk eredeti llapotba, ha valamelyik elem msol rtkad-
sa vagy msol konstruktora kivtelt vlt ki (lsd E.8[10-11]), ezrt a vector azzal a felttel-
lel ad biztostsokat, hogy az elemek msol konstruktora nem vlt ki kivtelt. A list s
a map osztlynak nincs szksge ilyen korltozsra, ezek knnyedn be tudjk illeszteni az
j elemet a szksges msolsok elvgzse utn.
Pldakppen ttelezzk fel, hogy az X msol konstruktora s msol rtkadsa egy
X::cannot_copy kivtelt vlt ki, ha valamilyen okbl nem sikerl ltrehoznia a msolatot:
void f(list<X>& lst, vector<X>& vec, map<string,X>& m, const X& x, const string& s)
{
try {
lst.insert(lst.begin(),x); // hozzads a listhoz
}
catch (...) {
// lst vltozatlan
return;
}
try {
vec.insert(vec.begin(),x); // hozzads a vektorhoz
}
catch (X::cannot_copy) {
// hopp: vec vagy rendelkezik, vagy nem rendelkezik j elemmel
return;
}
catch (...) {
// vec vltozatlan
return;
}
try {
m.insert(make_pair(s,x)); // hozzads az asszociatv tmbhz
}
catch (...) {
// m vltozatlan
return;
}
// lst s vec egy-egy x rtk j elemmel rendelkezik
// m egy j (s,x) rtk elemmel rendelkezik
}
Ha X::cannot_copy kivtelt kapunk, nem tudhatjuk, hogy az j elem bekerlt-e a vec tro-
lba. Ha sikerlt beilleszteni az elemet, az rvnyes llapotban lesz, de pontos rtkt nem
ismerjk. Az is elkpzelhet, hogy egy X::cannot_copy kivtel utn nhny elem titokza-
E. Kivtelbiztossg a standard knyvtrban 1291
tosan megkettzdik (lsd E.8[11]), msik megvalstst alkalmazva pedig a vektor vgn
lv elemek tnhetnek el, mert csak gy lehet biztostani, hogy a trol rvnyes llapotban
maradjon s ne szerepeljenek benne rvnytelen elemek.
Sajnos az ers biztosts megvalstsa a vector osztly insert() fggvnye esetben lehetet-
len, ha megengedjk, hogy az elemek msol konstruktora kivtelt vltson ki. Ha egy vek-
torban teljesen meg akarnnk vdeni magunkat az elemek thelyezse kzben keletkez
kivtelektl, a kltsgek elviselhetetlenl megnnnek az egyszer, alapbiztostst nyjt
megoldshoz kpest.
Sajnos nem ritkk az olyan elemtpusok, melyek msol konstruktora kivtelt eredmnyez-
het. Mr a standard knyvtrban is tallhatunk pldt: a vector<string>, a vector<
vector<double> > s a map<string, int> is ilyen.
A list s a vector trol ugyanolyan biztostst ad az insert() egyelem s tbbelem vlto-
zathoz, mert azok megvalstsi mdja azonos. A map viszont ers biztostst ad az egy-
elem beszrshoz, mg a tbbelemhz csak alapbiztostst. Az egyelem insert() a map
esetben knnyen elkszthet ers biztostssal, a tbbelem vltozat egyetlen logikus
megvalstsi mdja azonban a map esetben az, hogy az j elemeket egyms utn szrjuk
be, s ehhez mr nagyon nehz lenne ers garancikat adni. A gondot itt az jelenti, hogy
nincs egyszer visszalpsi lehetsg (nem tudunk korbbi sikeres beszrsokat visszavon-
ni), ha valamelyik elem beszrsa nem sikerl.
Ha olyan tbbelem beszr mveletre van szksgnk, amely ers biztostst ad, azaz
vagy minden elemet hibtlanul beilleszt, vagy egyltaln nem vltoztatja meg a trolt, leg-
egyszerbben gy valsthatjuk meg, hogy egy teljesen j trolt ksztnk, majd ennek si-
keres ltrehozsa utn egy swap() mveletet alkalmazunk:
template<class C, class Iter>
void safe_insert(C& c, typename C::const_iterator i, Iter begin, Iter end)
{
C tmp(c.begin(),i); // az ell lev elemek msolsa ideiglenes vltozba
copy(begin,end,inserter(tmp,tmp.end())); // j elemek msolsa
copy(i,c.end(),inserter(tmp,tmp.end())); // a zr elemek msolsa
swap(c,tmp);
}
Szoks szerint, ez a fggvny is hibsan viselkedhet, ha az elemek destruktora kivtelt vlt
ki, ha viszont az elemek msol konstruktora okoz hibt, a paramterben megadott trol
vltozatlan marad.
Fggelkek s trgymutat 1292
E.4.3. A swap()
A msol konstruktorokhoz s rtkadsokhoz hasonlan a swap() eljrsok is nagyon fon-
tos szerepet jtszanak sok szabvnyos algoritmusban s kzvetlenl is gyakran hasznljk
a felhasznlk. A sort() s a stable_sort() pldul ltalban a swap() segtsgvel rendezi t
az elemeket. Teht ha a swap() kivtelt vlt ki, mikzben a trolban szerepl rtkeket
cserlgeti, akkor a trol elemei a csere helyett vagy vltozatlanok maradnak, vagy megket-
tzdnek.
Vizsgljuk meg a standard knyvtr swap() fggvnynek albbi, egyszer megvalstst
(18.6.8):
template<class T> void swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
Erre teljesl, hogy a swap() csak akkor eredmnyezhet kivtelt, ha azt az elemek msol
konstruktora vagy msol rtkadsa vltja ki.
Az asszociatv trolktl eltekintve a szabvnyos trolk biztostjk, hogy a swap() fgg-
vny ne vltson ki kivteleket. A trolkban ltalban gy is meg tudjuk valstani a swap()
fggvnyt, hogy csak az adatszerkezeteket cserljk fel, melyek mutatknt szolglnak
a tnyleges elemekhez (13.5, 17.1.3). Mivel gy magukat az elemeket nem kell mozgat-
nunk, azok konstruktorra vagy rtkad mveletre nincs szksgnk, teht azok nem
kapnak lehetsget kivtel kivltsra. Ezenkvl a szabvny biztostja, hogy a knyvtr
swap() fggvnye nem tesz rvnytelenn egyetlen hivatkozst, mutatt s bejrt sem
azok kzl, melyek a felcserlt trolk elemeire hivatkoznak. Ennek kvetkeztben kivte-
lek egyetlen ponton lphetnek fel: az asszociatv trolk sszehasonlt objektumaiban,
melyeket az adatszerkezet lerjnak rszeknt kell msolnunk. Teht az egyetlen kivtel,
amit a szabvnyos trolk swap() eljrsa eredmnyezhet, az sszehasonlt objektum m-
sol konstruktorbl vagy rtkad mveletbl szrmazik (17.1.4.1). Szerencsre az
sszehasonlt objektumoknak ltalban annyira egyszer msol mveleteik vannak,
hogy nincs lehetsgk kivtel kivltsra.
A felhasznli swap() fggvnyek viszonylag egyszeren nyjthatnak ugyanilyen biztost-
sokat, ha gondolunk r, hogy mutatkkal brzolt adatok esetben elegend csak a mu-
tatkat felcserlnnk, ahelyett, hogy lassan s preczen lemsolnnk a mutatk ltal kijellt
tnyleges adatokat (13.5, 16.3.9, 17.1.3).
E. Kivtelbiztossg a standard knyvtrban 1293
E.4.4. A kezdeti rtkads s a bejrk
Az elemek szmra val memriafoglals s a memriaterletek kezdeti rtkadsa alapve-
t rsze minden trolnak (E.3). Ebbl kvetkezik, hogy a fel nem tlttt (elksztetlen)
memriaterleten objektumot ltrehoz szabvnyos eljrsok az uninitialized_fill(), az
uninitialized_fill_n() s az uninitialized_copy() (19.4.4) semmikppen sem hagyhatnak
ltrehozott objektumokat a memriban, ha kivtelt vltanak ki. Ezek az algoritmusok ers
biztostst valstanak meg (E.2), amihez gyakran kell elemeket trlni, teht az a kvetel-
mny, miszerint a destruktoroknak tilos kivtelt kivltaniuk, elengedhetetlen ezeknl
a fggvnyeknl is (lsd E.8[14]). Ezenkvl azoknak a bejrknak is megfelelen kell vi-
selkednik, melyeket paramterknt adunk t ezeknek az eljrsoknak. Teht rvnyes be-
jrknak kell lennik, rvnyes sorozatokra kell hivatkozniuk, s a bejr mveleteknek
(pldul a ++, a != vagy a * opertornak) nem szabad kivtelt kivltaniuk, ha rvnyes be-
jrkra alkalmazzuk azokat.
A bejrk (itertorok) olyan objektumok, melyeket a szabvnyos algoritmusok s a szabv-
nyos trolk mveletei szabadon lemsolhatnak, teht ezek msol konstruktora s mso-
l rtkadsa nem eredmnyezhet kivtelt. A szabvny garantlja, hogy a szabvnyos tro-
lk ltal visszaadott bejrk msol konstruktora s msol rtkadsa nem vlt ki kivtelt,
gy a vector<T>::begin() ltal visszaadott bejrt pldul nyugodtan lemsolhatjuk, nem kell
kivteltl tartanunk.
Figyeljnk r, hogy a bejrkra alkalmazott ++ vagy -- mvelet eredmnyezhet kivtelt. Pl-
dul egy istreambuf_iterator (19.2.6) egy bemenethibt (logikusan) egy kivtel kivlts-
val jelezhet, egy tartomnyellenrztt bejr pedig teljesen szablyosan jelezheti kivtellel
azt, hogy megprbltunk kilpni a megengedett tartomnybl (19.3). Akkor azonban nem
eredmnyezhetnek kivtelt, ha a bejrt gy irnytjuk t egy sorozat egyik elemrl a m-
sikra, hogy kzben a ++ vagy a -- egyetlen szablyt sem srtjk meg. Teht az
uninitialized_fill(), az uninitialized_fill_n() s az uninitialized_copy() felttelezi, hogy
a bejrkra alkalmazott ++ s -- mvelet nem okoz kivtelt. Ha ezt mgis megteszik, a szab-
vny megfogalmazsa szerint ezek nem is igazn bejrk, vagy az ltaluk megadott soro-
zat nem rtelmezhet sorozatknt. Most is igaz, hogy a szabvny nem kpes megvdeni
a felhasznlt a sajt maga ltal okozott nem meghatrozhat viselkedstl (E.2).
E.4.5. Hivatkozsok elemekre
Ha elemre hivatkoz mutatt, referencit vagy bejrt adunk t egy eljrsnak, az tnkre-
teheti a listt azzal, hogy az adott elemet rvnytelenn teszi:
Fggelkek s trgymutat 1294
void f(const X& x)
{
list<X> lst;
lst.push_back(x);
list<X>::iterator i = lst.begin();
*i = x; // x listba msolsa
// ...
}
Ha az x vltozban rvnytelen rtk szerepel, a list destruktora nem kpes hibtlanul meg-
semmisteni az lst objektumot:
struct X {
int* p;
X() { p = new int; }
~X() { delete p; }
// ...
};
void malicious()
{
X x;
x.p = reinterpret_cast<int*>(7); // hibs x
f(x); // idztett bomba
}
Az f() vgrehajtsnak befejeztvel meghvdik a list<X> destruktora, amely viszont meg-
hvja az X destruktort egy rvnytelen rtkre. Ha megprbljuk a delete p parancsot vg-
rehajtani egy olyan p rtkre, amely nem 0 s nem is ltez X tpus rtkre mutat, az ered-
mny nem meghatrozhat lesz s akr a rendszer azonnali sszeomlst okozhatja. Egy
msik lehetsg, hogy a memria rvnytelen llapotba kerl, ami sokkal ksbb, a prog-
ram olyan rszben okoz megmagyarzhatatlan hibkat, amely teljesen fggetlen a tny-
leges problmtl.
Ez a hibalehetsg nem gtolja meg a programozkat abban, hogy referencikat s bejr-
kat hasznljanak a trolk elemeinek kezelsre, hiszen mindenkppen ez az egyik legegy-
szerbb s leghatkonyabb mdszer az ilyen feladatok elvgzshez. Mindenesetre rde-
mes klnsen elvigyzatosnak lennnk a trolk elemeire val hivatkozsokkal
kapcsolatban. Ha egy trol psge veszlybe kerlhet, rdemes a kevsb gyakorlott fel-
hasznlk szmra biztonsgosabb, ellenrztt vltozatokat is ksztennk, pldul megad-
hatunk egy olyan eljrst, amely ellenrzi, hogy az j elem rvnyes-e, mieltt beszrja azt
a fontos trolba. Termszetesen ilyen ellenrzseket csak akkor vgezhetnk, ha ponto-
san ismerjk a trolban trolt elemek tpust.
E. Kivtelbiztossg a standard knyvtrban 1295
ltalban, ha egy trol valamelyik eleme rvnytelenn vlik, a trolra alkalmazott min-
den tovbbi mvelet hibkat eredmnyezhet. Ez nem csak a trolk sajtja: brmely objek-
tum, amely valamilyen szempontbl hibs llapotba kerl, a ksbbiekben brmikor okoz-
hat problmkat.
E.4.6. Prediktumok
Szmos szabvnyos algoritmus s trol hasznl olyan prediktumokat, melyeket a felhasz-
nlk adhatnak meg. Az asszociatv trolk esetben ezek klnsen fontos szerepet tlte-
nek be: az elemek keresse s beszrsa is ezen alapul.
A szabvnyos trolk mveletei ltal hasznlt prediktumok is okozhatnak kivteleket, s
ha ez bekvetkezik, a standard knyvtr mveletei legalbb alapbiztostst nyjtanak, de
sok esetben (pldul az egyelem insert() mveletnl) ers biztosts ll rendelkezsnk-
re (E.4.1). Ha egy troljn vgzett mvelet kzben egy prediktum kivtelt vlt ki, elkp-
zelhet, hogy az ott trolt elemek nem pontosan azok lesznek, amelyeket szeretnnk, de
mindenkppen rvnyes elemek. Pldul ha az == okoz kivtelt a list::unique() (17.2.2.3)
mvelet vgrehajtsa kzben, nem vrhatjuk el, hogy minden rtkismtlds eltnjn.
A felhasznl mindssze annyit felttelezhet, hogy a listban szerepl rtkek rvnyesek
maradnak (lsd E.5.3).
Szerencsre a prediktumok ritkn csinlnak olyasmit, ami kivtelt eredmnyezhet. Ennek
ellenre a felhasznli <, ==, s != prediktumokat figyelembe kell vennnk, amikor kiv-
telbiztossgrl beszlnk.
Az asszociatv trolk sszehasonlt objektumairl a swap() mvelet vgrehajtsa sorn
msolat kszl (E.4.3), ezrt rdemes biztostanunk, hogy azon prediktumok msol m-
veletei, melyeket felhasznlhatunk sszehasonlt objektumokknt, ne vlthassanak ki
kivtelt.
E.5. A standard knyvtr tovbbi rszei
A kivtelbiztossg legfontosabb clja, hogy fenntartsuk az objektumok psgt s kvetke-
zetessgt, azaz az nll objektumok alap-invarinsa mindig igaz maradjon s az egyms-
sal kapcsolatban ll objektumok se srljenek. A standard knyvtr szemszgbl nzve
Fggelkek s trgymutat 1296
a kivtelbiztossg fenntartsa a trolk esetben a legbonyolultabb. Ha a kivtelbiztossg-
ra sszpontostunk, a standard knyvtr tbbi rsze nem tl rdekes, de a kivtelbiztossg
szempontjbl a beptett tmb is egy trol, melyet feleltlen mveletekkel knnyen
tnkretehetnk.
A standard knyvtr fggvnyei ltalban csak olyan kivteleket vlthatnak ki, melyeket
meghatroznak vagy amelyeket az ltaluk meghvott felhasznli mveletek eredmnyez-
hetnek. Emellett azok az eljrsok, melyek (kzvetve vagy kzvetlenl) memrit foglalnak
le, a memria elfogyst kivtellel jelezhetik (ltalban az std::bad_alloc kivtellel).
E.5.1. Karakterlncok
A string objektumokon vgzett mveletek sokfle kivtelt okozhatnak, a basic_string vi-
szont karaktereit a char_traits (20.2) osztly ltal biztostott fggvnyekkel kezeli s ezek-
nek nem szabad kivtelt okozniuk. A standard knyvtr char_traits objektumai nem vlta-
nak ki kivteleket, s ha egy felhasznli char_traits valamelyik eljrsa eredmnyez ilyet,
azrt a standard knyvtr semmilyen felelssget nem vllal. Klnsen fontos, hogy
a basic_string osztlyban elemknt (karakterknt) hasznlt tpus nem rendelkezhet felhasz-
nli msol konstruktorral s rtkadssal, mert gy nagyon sok kivtel-lehetsgtl sza-
badulunk meg.
A basic_string nagyon hasonlt a szabvnyos trolkra (17.5, 20.3), elemei valjban egy
egyszer sorozatot alkotnak, melyet a basic_string<Ch,Tr,A>::iterator vagy
a basic_string<Ch,Tr,A>::const_iterator objektumokkal rhetnk el. Ennek kvetkeztben
a string alapbiztostst (E.2) ad s az erase(), az insert(), a push_back() s a swap() (E.4.1)
fggvny garancii a basic_string osztly esetben is rvnyesek.
A basic_string<Ch,Tr,A>::push_back() pldul ers biztostst nyjt.
E.5.2. Adatfolyamok
Ha egy adatfolyamot megfelelen lltunk be, annak fggvnyei az llapotvltozsokat ki-
vtelekkel jelzik (21.3.6). Ezek jelentse pontosan meghatrozott s nem okoznak
kivtelbiztossgi problmkat. Ha egy felhasznli operator<<() vagy operator>>() eljrs
okoz kivtelt, az gy jelenhet meg a programoz szmra, mintha azt az iostream knyvtr
okozta volna. Ennek ellenre ezek a kivtelek nem hatnak az adatfolyam llapotra
(21.3.3). Az adatfolyam ksbbi mveletei esetleg nem talljk meg az ltaluk vrt adato-
kat mert egy korbbi mvelet kivtelt vltott ki a szablyos befejezds helyett , de ma-
E. Kivtelbiztossg a standard knyvtrban 1297
ga az adatfolyam nem vlik rvnytelenn. Szoks szerint az I/O problmk utn szksg
lehet a clear() fggvny meghvsra, mieltt tovbbi rst vagy olvasst kezdemnyeznnk
(21.3.3, 21.3.5).
A basic_string osztlyhoz hasonlan az iostream is egy char_traits objektumra hivatkozik
a karakterkezels megvalstshoz (20.2.1, E.5.1), teht felttelezheti, hogy a karaktere-
ken vgzett mveletek nem okoznak kivtelt, illetve semmilyen biztostst nem kell adnia,
ha a felhasznl megsrti ezt a kiktst.
Ahhoz, hogy a standard knyvtr kellen hatkony optimalizlst alkalmazhasson, feltte-
lezzk, hogy a locale (D.2) s a facet (D.3) objektumok sem okozhatnak kivtelt. Ha mg-
is gy mkdnek, akkor az azokat hasznl adatfolyamok rvnytelenn vlhatnak. Ennek
ellenre a leggyakoribb ilyen kivtel az std::bad_cast a use_facet (D.3.1) fggvnyben
csak olyan, felhasznl ltal rt programrszletekben fordul el, melyek fggetlenek a szab-
vnyos adatfolyamoktl, gy a legrosszabb esetben is csak a kirs flbeszakadst vagy hi-
bs beolvasst eredmnyez, az adatfolyam (legyen az akr istream, akr ostream) rvnyes
marad.
E.5.3. Algoritmusok
Eltekintve az uninitialized_copy(), az uninitialized_fill() s az uninitialized_fill_n() fgg-
vnytl (E.4.4) a standard knyvtr az algoritmusokhoz alapbiztostst (E.2) ad. Ez azt je-
lenti, hogy ha a felhasznl ltal megadott objektumok a kvetelmnyeknek megfelelen
viselkednek, az algoritmusok fenntartjk a standard knyvtr invarinsait s elkerlik az
erforrs-lyukakat. A nem meghatrozott viselkeds elkerlse rdekben a felhasznli
mveleteknek mindig rvnyes llapotban kell hagyniuk paramtereiket s
a destruktoroknak nem szabad kivteleket kivltaniuk.
Az algoritmusok maguk nem okoznak kivteleket, ehelyett visszatrsi rtkkn keresz-
tl jelzik a problmkat. A keres algoritmusok pldul tbbnyire a sorozat vgt adjk
vissza annak jelzsre, hogy nem talltk meg a keresett elemet (18.2). Teht a szabvnyos
algoritmusokban keletkez kivtelek valjban mindig egy felhasznli eljrsbl szrmaz-
nak. Ez azt jelenti, hogy a kivtel vagy az egyik elemen vgzett mvelet prediktum
(18.4), rtkads vagy swap() kzben jtt ltre, vagy egy memriafoglal (19.4) okozta.
Ha egy ilyen mvelet kivtelt okoz, az algoritmusok azonnal befejezik mkdsket s az
algoritmust elindt fggvny feladata lesz, hogy a kivtelt kezelje. Nhny algoritmus ese-
tben elfordulhat, hogy a kivtel akkor kvetkezik be, amikor a trol llapota a felhasz-
nl szempontjbl elfogadhatatlan. Nhny rendez eljrs pldul az elemeket ideigle-
Fggelkek s trgymutat 1298
nesen egy tmeneti trba msolja s ksbb innen teszi azokat vissza az eredeti trolba.
Egy ilyen sort() eljrs esetleg sikeresen kimsolja az elemeket a trolbl (azt tervezve,
hogy hamarosan a megfelel sorrendben rja azokat vissza), helyesen vgzi el a trlst is,
de ezutn azonnal kivtel kvetkezik be. A felhasznl szempontjbl a trol teljesen
megsemmisl, ennek ellenre minden elem rvnyes llapotban van, teht az alapbiztos-
ts megvalstsa egyszer feladat.
Gondoljunk r, hogy a szabvnyos algoritmusok a sorozatokat bejrkon keresztl rik el,
sohasem kzvetlenl a trolkon dolgoznak, hanem azok elemein. A tny, hogy ezek az al-
goritmusok soha nem kzvetlenl vesznek fel elemeket egy trolba vagy trlnek eleme-
ket onnan, leegyszersti annak vizsglatt, hogy egy kivtelnek milyen kvetkezmnyei le-
hetnek. Ha egy adatszerkezetet csak konstans bejrkon, mutatkon vagy referencikon
(pldul const Rec*) keresztl rhetnk el, ltalban nagyon egyszeren ellenrizhetjk,
hogy a kivtelek mvelnek-e valamilyen veszlyes dolgot.
E.5.4. A valarray s a complex
A szmkezel fggvnyek sem okoznak kifejezetten kivteleket (22. fejezet), de a valarray
osztlynak memrit kell foglalnia, gy hasznlatakor elfordulhat std::bad_alloc kivtel.
Ezenkvl a valarray s a complex kaphat olyan elemtpust is (skalrokat), amely kivtele-
ket vlthat ki. Szoks szerint a szabvny alapbiztostst (E.2) nyjt, de a kivtelek ltal meg-
szakadt szmtsok eredmnyrl semmit sem felttelezhetnk.
A basic_string osztlyhoz hasonlan (E.5.1) a valarray s a complex is felttelezheti, hogy
a sablonparamterben megadott tpus nem rendelkezik felhasznli msol mveletek-
kel, teht egyszeren, bjtonknt msolhat. A standard knyvtr numerikus tpusainak
tbbsge a sebessgre optimalizlt, gy felttelezi, hogy elemtpusai nem okoznak kivtele-
ket.
E.5.5. A C standard knyvtra
A standard knyvtr kivtel-meghatrozs nlkli mveletei az adott C++-vltozattl fgg-
en vlthatnak ki kivteleket, a C standard knyvtrnak fggvnyeinl azonban biztosak le-
hetnk abban, hogy csak akkor okoznak kivteleket, ha a nekik paramterknt tadott el-
jrsok kivtelt okoznak, hiszen vgeredmnyben ezeket a fggvnyeket C programok is
hasznljk s a C-ben nincsenek kivtelek. Egy szp megvalsts a szabvnyos C fggv-
nyeket res kivtel-meghatrozssal adhatja meg (throw()), ezzel lehetsget adhat a for-
dtnak jobb kd ellltsra.
E. Kivtelbiztossg a standard knyvtrban 1299
Az olyan fggvnyek, mint a qsort() vagy a bsearch(), egy fggvnyre hivatkoz mutatt
vesznek t paramterknt, gy okozhatnak kivtelt, ha paramterk kpes erre. Az alapbiz-
tosts (E.2) ezekre a fggvnyekre is kiterjed.
E.6. Javaslatok a knyvtr felhasznli szmra
A standard knyvtr vizsglatakor a kivtelbiztossgra gy tekinthetnk, mint egy probl-
mamentest eszkzre, amely sok mindentl megvd minket, ha nem okozunk sajt ma-
gunknak kellemetlensgeket. A knyvtr mindaddig helyesen fog mkdni, amg a felhasz-
nli eljrsok teljestik az alapkvetelmnyeket (E.2). A szabvnyos trolk mveletei
ltal kivltott kivtelek tbbnyire nem okoznak memria-elszivrgst s a trolt rvnyes
llapotban hagyjk. Teht a knyvtr hasznlinak a legfontosabb krds a kvetkez: ho-
gyan hatrozzuk meg sajt tpusainkat ahhoz, hogy elkerljk a kiszmthatatlan viselke-
dst s a memria-lyukak keletkezst?
Az alapszablyok a kvetkezk:
1. Amikor egy objektumot frisstnk, soha ne mdostsuk az eredeti brzolst ad-
dig, amg az j rtket teljesen ltre nem hoztuk s nem biztostottuk, hogy ki-
vtel veszlye nlkl le tudjuk cserlni az rtket. Pldakppen nzzk meg
a vector::operator=(), a safe_assign() vagy a vector::push_back() fggvny meg-
valstst az E.3 pontban.
2. Mieltt kivtelt vltunk ki, szabadtsunk fel minden olyan lefoglalt erforrst,
amelyet nem ktttnk (ms) objektumhoz.
2a A kezdeti rtkads az erforrs megszerzsvel mdszer (14.4) s
a nyelv szablyai, melyek szerint a rszben ltrehozott objektumok olyan
mrtkben trldnek, amennyire ltrejttek (14.4.1), nagyban elsegtik ezt
a clt. Pldakppen nzzk meg a leak() fggvnyt. (E.2).
2b Az uninitialized_copy() s testvrei automatikus erforrs-felszabadtst
tesznek lehetv, ha egy objektumhalmaz ltrehozsa nem sikerl (E.4.4).
3. Mieltt kivtelt vltunk ki, ellenrizzk, hogy minden operandus rvnyes lla-
potban van-e, azaz minden objektumot olyan llapotban kell hagynunk, hogy
az ksbb szablyosan elrhet s trlhet legyen anlkl, hogy nem megha-
trozhat eredmnyeket kapnnk s a destruktornak kivtelt kellene kivltania.
Pldakppen a vector rtkadst emlthetjk (E.3.2).
Fggelkek s trgymutat 1300
3a A konstruktorok abban is eltrnek az tlagos eljrsoktl, hogy ha ezekben
keletkezik kivtel, nem jn ltre objektum, amelyet ksbb trlnnk kelle-
ne. Ebbl kvetkezik, hogy ha egy konstruktorban kell kivtelt kivltanunk,
akkor nem kell invarinst helyrelltanunk, de minden erforrst fel kell sza-
badtanunk, amit a konstruktor megszakadsa eltt lefoglaltunk.
3b A destruktorok abban klnbznek a tbbi mvelettl, hogy ha itt kivtel
keletkezik, szinte biztosan elrontunk valamilyen invarinst s akr
a terminate() fggvny azonnali meghvst is elidzhetjk.
A gyakorlatban ezeket a szablyokat meglepen nehz betartani. Ennek legfbb oka az,
hogy a kivtelek gyakran ott kvetkeznek be, ahol egyltaln nem vrjuk azokat. Egy j pl-
da erre az std::bad_alloc. Brmely fggvny okozhatja ezt a kivtelt, amely kzvetve vagy
kzvetlenl hasznlja a new opertort vagy egy allocator objektumot memria lefoglals-
hoz. Bizonyos programokban ezt a hibt elkerlhetjk, ha nem ignylnk a lehetsgesnl
tbb memrit, az olyan programok esetben azonban, amelyek elg sokig futnak vagy je-
lents mennyisg adatot kell feldolgozniuk, fel kell kszlnnk a legklnbzbb hibkra
az erforrs-foglalsokkal kapcsolatban. Ez azt jelenti, hogy feltteleznnk kell, hogy min-
den fggvny kpes brmely kivtel kivltsra, amg mst nem bizonytottunk rjuk.
A meglepetsek elkerlsnek egyik mdja az, hogy csak olyan elemekbl ptnk trol-
kat, melyek nem hasznlnak kivteleket (pldul mutatkbl vagy egyszer, konkrt tpu-
sokbl) vagy lncolt trolkat (pldul list) hasznlunk, melyek ers biztostst nyjtanak
(E.4). A msik, ellenttes megkzelts, hogy elssorban az olyan mveletekre szmtunk,
melyek ers biztostst nyjtanak (pldul a push_back()). Ezek vagy sikeresen befejezd-
nek, vagy egyltaln nincs hatsuk (E.2), de nmagukban nem elegendek az erforrs-
lyukak elkerlsre s csak rendezetlen, pesszimista hibakezelst s helyrelltst tesznek
lehetv. A vector<T*> pldul tpusbiztos, ha a T tpuson vgzett mveletek nem okoznak
kivteleket, de ha kivtel kvetkezik be a vector objektumban s nem gondoskodunk vala-
hol a mutatott objektumok trlsrl, azonnal memria-lyukak keletkeznek. Ebbl kvet-
kezik, hogy be kell vezetnnk egy Handle osztlyt, amely mindig elvgzi a szksges fel-
szabadtsokat (25.7), s az egyszer vector<T*> helyett a vector< Handle<T> > szerkezetet
kell hasznlnunk. Ez a megolds az egsz programot rugalmasabb teszi.
Amikor j programot ksztnk, lehetsgnk van arra, hogy tgondoltabb megkzeltst
talljunk s biztostsuk, hogy erforrsainkat olyan osztlyokkal brzoljuk, melyek invari-
nsa alapbiztostst nyjt (E.2). Egy ilyen rendszerben lehetsg nylik arra, hogy kiv-
lasszuk a ltfontossg objektumokat s ezek mveleteihez visszagrgetsi mdszereket al-
kalmazzunk (azaz ers biztostst adhatunk nhny egyedi felttel mellett).
E. Kivtelbiztossg a standard knyvtrban 1301
A legtbb program tartalmaz olyan adatszerkezeteket s programrszeket, melyeket a kiv-
telbiztossgra nem gondolva rtak meg. Ha szksg van r, ezek a rszek egy kivtelbiztos
keretbe gyazhatk. Az egyik lehetsg, hogy biztostjuk, hogy kivtelek ne kvetkezzenek
be (ez trtnt a C standard knyvtrval, E.5.5), a msik megolds pedig az, hogy fellet-
osztlyokat hasznlunk, melyekben a kivtelek viselkedse s az erforrsok kezelse pon-
tosan meghatrozhat.
Amikor olyan j tpusokat terveznk, amelyek kivtelbiztos krnyezetben futnak majd, k-
ln figyelmet kell szentelnnk azoknak az eljrsoknak, melyeket a standard knyvtr
hasznlni fog: a konstruktoroknak, a destruktoroknak, az rtkadsoknak, sszehasonlt-
soknak, swap fggvnyeknek, a prediktumknt hasznlt fggvnyeknek s a bejrkat ke-
zel eljrsoknak. Ezt legknnyebben gy valsthatjuk meg, hogy egy j osztlyinvarinst
hatrozunk meg, amelyet minden konstruktor knnyedn biztosthat. Nha gy kell meg-
terveznnk az osztlyinvarinst, hogy az objektumoknak legyen egy olyan llapota, mely-
ben egyszeren trlhetk, ha egy mvelet kellemetlen helyen tkzik hibba. Idelis
esetben ez az llapot nem egy mestersgesen megadott rtk, amit csak a kivtelkezels mi-
att kellett bevezetni, hanem az osztly termszetbl kvetkez llapot (E.3.5).
Amikor kivtelbiztossggal foglalkozunk, a f hangslyt az objektumok rvnyes llapotai-
nak (invarinsainak) meghatrozsra s az erforrsok megfelel felszabadtsra kell he-
lyeznnk. Ezrt nagyon fontos, hogy az erforrsokat kzvetlenl osztlyokkal brzoljuk.
A vector_base (E.3.2) ennek egyszer pldja. Az ilyen erforrs-osztlyok konstruktora ala-
csonyszint erforrsokat foglal le (pldul egy memriatartomnyt a vector_base esetben),
s invarinsokat llt be (pldul a mutatkat a megfelel helyekre lltja a vector_base osz-
tlyban). Ezen osztlyok destruktora egyszeren felszabadtja a lefoglalt erforrst. A rszle-
ges ltrehozs szablyai (14.4.1) s a kezdeti rtkads az erforrs lefoglalsval md-
szer (14.4) alkalmazsa lehetv teszi, hogy az erforrsokat gy kezeljk.
Egy jl megrt konstruktor minden objektum esetben belltja a megfelel invarinst
(24.3.7.1), teht a konstruktor olyan rtket ad az objektumnak, amely lehetv teszi, hogy
a tovbbi mveleteket egyszeren meg tudjuk rni s sikeresen vgre tudjuk hajtani. Ebbl
kvetkezik, hogy a konstruktoroknak gyakran kell erforrst lefoglalniuk. Ha ezt nem tud-
jk elvgezni, kivtelt vlthatnak ki, gy az objektum ltrehozsa eltt foglalkozhatunk a je-
lentkez problmkkal. Ezt a megkzeltst a nyelv s a standard knyvtr kzvetlenl t-
mogatja (E.3.5).
Az a kvetelmny, hogy az erforrsokat fel kell szabadtanunk s az operandusokat rv-
nyes llapotban kell hagynunk a kivtel kivltsa eltt, azt jelenti, hogy a kivtelkezels ter-
heit megosztjuk a kivtelt kivlt fggvny, a hvsi lncban lev fggvnyek s a kivtelt
tnylegesen kezel eljrs kztt. Egy kivtel kivltsa nem azt a hibakezelsi stlust jelen-
Fggelkek s trgymutat 1302
ti, hogy hagyjuk az egszet valaki msra. Minden fggvnynek, amely kivtelt vlt ki vagy
ad tovbb, ktelessge felszabadtani azokat az erforrsokat, melyek hatskrbe tartoz-
nak, operandusait pedig megfelel rtkre kell lltania. Ha az eljrsok ezt a feladatot nem
kpesek vgrehajtani, a kivtelkezel nemigen tehet mst, minthogy megprblja szpen
befejezni a program mkdst.
E.7. Tancsok
[1] Legynk tisztban azzal, milyen szint kivtelbiztossgra van szksgnk. E.2.
[2] A kivtelbiztossgnak egy teljes kr hibatrsi stratgia rsznek kell lennie.
E.2.
[3] Az alapbiztostst minden osztlyhoz rdemes megvalstani, azaz az invarin-
sokat mindig tartsuk meg s az erforrs-lyukakat mindig kerljk el. E.2,
E.3.2, E.4.
[4] Ahol lehetsg s szksg van r, valstsunk meg ers biztostst, azaz egy
mvelet vagy sikeresen hajtdjon vgre, vagy minden operandust hagyja vlto-
zatlanul. E.2, E.3.
[5] Destruktorokban ne fordulhasson el kivtel. E.2, E.3.2, E.4.
[6] Ne vltson ki kivtelt egy rvnyes sorozatban mozg bejr. E.4.1, E.4.4.
[7] A kivtelbiztossg foglalja magban az nll mveletek alapos vizsglatt. E.3.
[8] A sablon osztlyokat gy tervezzk meg, hogy azok tltszak legyenek a ki-
vtelek szmra. E.3.1.
[9] Az init() fggvny helyett hasznljunk konstruktort az erforrsok lefoglals-
hoz. E.3.5.
[10] Adjunk meg invarinst minden osztlyhoz, hogy ezzel pontosan meghatrozzuk
rvnyes llapotaikat. E.2, E.6.
[11] Gyzdjnk meg rla, hogy objektumaink mindig rvnyes llapotba llthatk
anlkl, hogy kivtelektl kellene tartanunk. E.3.2, E.6.
[12] Az invarinsok mindig legyenek egyszerek. E.3.5.
[13] Kivtel kivltsa eltt minden objektumot lltsunk rvnyes llapotba. E.2,
E.6.
[14] Kerljk el az erforrs-lyukakat. E.2, E.3.1, E.6.
[15] Az erforrsokat kzvetlenl brzoljuk. E.3.2, E.6.
[16] Gondoljunk r, hogy a swap() fggvny gyakran hasznlhat az elemek mso-
lsa helyett. E.3.3.
E. Kivtelbiztossg a standard knyvtrban 1303
[17] Ha lehetsg van r, a try blokkok hasznlata helyett a mveletek sorrendjnek
j megvlasztsval kezeljk a problmkat. E.3.4.
[18] Ne trljk a rgi informcikat addig, amg a helyettest adatok nem vlnak
biztonsgosan elrhetv. E.3.3, E.6.
[19] Hasznljuk a kezdeti rtkads az erforrs megszerzsvel mdszert. E.3,
E.3.2, E.6.
[20] Vizsgljuk meg, hogy asszociatv trolinkban az sszehasonlt mveletek m-
solhatk-e. E.3.3.
[21] Keressk meg a ltfontossg adatszerkezeteket s ezekhez adjunk meg olyan
mveleteket, melyek ers biztostst adnak. E.6.
E.8. Gyakorlatok
1. (*1) Soroljuk fel az sszes kivtelt, amely elfordulhat az E.1 pont f()
fggvnyben.
2. (*1) Vlaszoljunk az E.1 pontban, a plda utn szerepl krdsekre.
3. (*1) Ksztsnk egy Tester osztlyt, amely idnknt a legalapvetbb mveletek-
ben okoz kivtelt, pldul a msol konstruktorban. A Tester osztly segtsg-
vel prbljuk ki sajt standard knyvtrunk trolit.
4. (*1) Keressk meg a hibt az E.3.1 pontban szerepl vector konstruktornak
rendezetlen vltozatban s rjunk programot, amely tnkreteszi az osztlyt.
Ajnls: elszr rjuk meg a vector destruktort.
5. (*2) Ksztsnk egyszer listt, amely alapbiztostst nyjt. llaptsuk meg na-
gyon pontosan, milyen kvetelmnyeket kell a felhasznlnak teljestenie a biz-
tosts megvalstshoz.
6. (*3) Ksztsnk egyszer listt, amely ers biztostst nyjt. Alaposan ellenriz-
zk az osztly mkdst. Indokoljuk meg, mirt tartjuk ezt a megoldst bizton-
sgosabbnak.
7. (*2.5)rjuk jra a 11.12 String osztlyt gy, hogy ugyanolyan biztonsgos le-
gyen, mint a szabvnyos trolk.
8. (*2) Hasonltsuk ssze a vector osztlyban meghatrozott rtkads s
a safe_assign() fggvny klnbz vltozatait a futsi id szempontjbl.
(E.3.3)
9. (*1.5) Msoljunk le egy memriafoglalt az rtkad opertor hasznlata nlkl
(hiszen az operator=() megvalstshoz erre van szksgnk az E.3.3
pontban).
Fggelkek s trgymutat 1304
10. (*2) rjunk a vector osztlyhoz alapbiztostssal egy egyelem s egy tbbelem
erase(), illetve insert() fggvnyt.E.3.2.
11. (*2) rjunk a vector osztlyhoz ers biztostssal egy egyelem s egy tbbelem
erase(), illetve insert() fggvnyt (E.3.2). Hasonltsuk ssze ezen fggvnyek
kltsgt s bonyolultsgt az elz feladatban szerepl fggvnyekvel.
12. (*2) Ksztsnk egy safe_insert() fggvnyt (E.4.2), amely egy ltez vector ob-
jektumba szr be elemet (nem pedig egy ideiglenes vltozt msol le). Milyen
kiktseket kell tennnk a mveletekre?
13. (*2.5) Hasonltsuk ssze mret, bonyolultsg s hatkonysg szempontjbl
a 12. s a 13. feladatban szerepl safe_insert() fggvnyt az E.4.2 pontban be-
mutatott safe_insert() fggvnnyel.
14. (*2.5) rjunk egy jobb (gyorsabb s egyszerbb) safe_insert() fggvnyt, kifeje-
zetten asszociatv trolkhoz. Hasznljuk a traits eljrst egy olyan safe_insert()
megvalstshoz, amely automatikusan kivlasztja az adott trolhoz optimlis
megvalstst. Ajnls: 19.2.3.
15. (*2.5) Prbljuk megrni az uninitialized_fill() fggvnyt (19.4.4, E.3.1) gy,
hogy az megfelelen kezelje a kivteleket kivlt destruktorokat is. Lehetsges
ez? Ha igen, milyen ron? Ha nem, mirt nem?
16. (*2.5) Keressnk egy trolt egy olyan knyvtrban, amely nem tartozik a szab-
vnyhoz. Nzzk t dokumentcijt s llaptsuk meg, milyen kivtelbiztossgi
lehetsgek llnak rendelkezsnkre. Vgezznk nhny tesztet, hogy meglla-
ptsuk, mennyire rugalmas a trol a memriafoglalsbl vagy a felhasznl ltal
megadott programrszekbl szrmaz kivtelekkel szemben. Hasonltsuk ssze
a tapasztaltakat a standard knyvtr megfelel troljnak szolgltatsaival.
17. (*3) Prbljuk optimalizlni az E.3 pontban szerepl vector osztlyt a kivtelek
lehetsgnek figyelmen kvl hagysval. Pldul trljnk minden try blok-
kot. Hasonltsuk ssze az gy kapott vltozat hatkonysgt a standard knyvtr
vector osztlynak hatkonysgval. Hasonltsuk ssze a kt vltozatot mret s
bonyolultsg szempontjbl is.
18. (*1) Adjunk meg invarinst a vector osztly (E.3) szmra gy, hogy megenged-
jk, illetve megtiltjuk a v==0 esetet (E.3.5).
19. (*2.5) Nzzk vgig egy vector osztly megvalstsnak forrskdjt. Milyen
biztosts ll rendelkezsnkre az rtkadsban, a tbbelem insert() utasts-
ban s a resize() fggvnyben?
20. (*3) rjuk meg a hash_map (17.6) olyan vltozatt, amely ugyanolyan biztons-
gos, mint a szabvnyos trolk.
E. Kivtelbiztossg a standard knyvtrban 1305

You might also like