Manual Programacion C++ (2013)

Apunte Español
Universidad Universidad Politécnica de Cataluña (UPC)
Grado Ingeniería Informática - 3º curso
Asignatura ESIN
Año del apunte 2013
Páginas 109
Fecha de subida 31/05/2014
Descargas 5

Descripción

Muchos apuntes sobre programacion en c++

Vista previa del texto

´ DOCENT PUBLICACIO MANUAL DE LABORATORI D’ESIN AUTOR: Bernardino Casas ASSIGNATURA: Estructura de la Informacio´ (ESIN) CURS: Segon TITULACIONS: Grau en Inform`atica DEPARTAMENT: Llenguatges i Sistemes Inform`atics ANY: 2013/2014 ´ 26 de setembre de 2013 Vilanova i la Geltru, ii ´ Index curt 1 Punters, pas de par`ametres i taules 1 A Dec`aleg per implementar una classe en C++ 13 2 19 Memoria ` din`amica B Compilacio, ´ muntatge i execucio´ en C++ 37 3 45 Excepcions i genericitat C Estil de programacio´ i documentacio´ 63 4 79 La biblioteca est`andard de C++ ´ Index alfab`etic 99 iii iv ´ INDEX CURT ´ Index 1 Punters, pas de par`ametres i taules 1.1 Punters . . . . . . . . . . . . . . . . . . . . .
1.2 Refer`encies . . . . . . . . . . . . . . . . . . .
1.3 Pas de par`ametres . . . . . . . . . . . . . . .
1.4 Retorn de resultats . . . . . . . . . . . . . .
1.5 Taules (arrays) . . . . . . . . . . . . . . . . .
1.5.1 Declaracio´ . . . . . . . . . . . . . . .
1.5.2 Consulta/Modificacio´ de les taules .
1.5.3 Connexio´ entre arrays i punters . . .
1.6 Exercici . . . . . . . . . . . . . . . . . . . . .
1.6.1 cj_enters.hpp . . . . . . . . . . . .
1.6.2 Activitats . . . . . . . . . . . . . . .
A Dec`aleg per implementar una classe en C++ 1a Llei Crear la capc¸alera de la classe . . . . . .
2a Llei Pensar la representacio´ . . . . . . . . . .
3a Llei Escriure la representacio´ . . . . . . . . .
4a Llei Crear fitxer .CPP . . . . . . . . . . . . . .
5a Llei Retocar les capc¸aleres de les operacions 6a Llei Afegir l’include . . . . . . . . . . . . . .
7a Llei Primera compilacio´ . . . . . . . . . . . .
8a Llei Implementacio´ incremental . . . . . . .
9a Llei Compilar, Linkar i Provar . . . . . . . .
10a Llei Proves globals . . . . . . . . . . . . . .
2 .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1 1 2 3 5 7 7 7 8 8 9 10 .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
13 13 13 14 15 15 16 17 17 17 18 Memoria ` din`amica 2.1 Introduccio´ . . . . . . . . . . . . . . . . . . . . . . .
` 2.2 Reserva i alliberament de memoria din`amica . . .
` 2.3 Problemes amb la gestio´ de la memoria din`amica .
` 2.4 Constructor per copia . . . . . . . . . . . . . . . . .
2.5 Destructor . . . . . . . . . . . . . . . . . . . . . . .
2.6 Operador d’assignacio´ . . . . . . . . . . . . . . . .
2.7 El component this . . . . . . . . . . . . . . . . . . .
2.8 Exemple pila . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
19 19 19 21 24 25 26 27 28 v .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
´ INDEX vi 2.9 2.8.1 Especificacio´ de la classe pila . . . . . . . .
2.8.2 Implementacio´ de la classe pila . . . . . . .
2.8.3 Programa d’exemple que usa la classe pila Exercici . . . . . . . . . . . . . . . . . . . . . . . . .
2.9.1 cj_enters_din.hpp . . . . . . . . . . . . . .
2.9.2 Activitats . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
B Compilacio, ´ muntatge i execucio´ en C++ B.1 Compilacio´ separada i muntatge . . . . . . . . . . .
B.1.1 Compilacio´ i muntatge b`asic . . . . . . . . .
B.1.2 Compilacio´ i muntatge de classes gen`eriques B.1.3 Compilacio´ i muntatge amb biblioteques . .
B.1.4 Altres opcions . . . . . . . . . . . . . . . . . .
B.2 Make . . . . . . . . . . . . . . . . . . . . . . . . . . .
B.3 Execucio´ . . . . . . . . . . . . . . . . . . . . . . . . .
3 Excepcions i genericitat 3.1 Gestio´ d’errors . . . . . . . . . . . . . . . . .
3.1.1 Avortar el programa . . . . . . . . .
3.1.2 Codis d’error . . . . . . . . . . . . .
3.1.3 Assercions . . . . . . . . . . . . . . .
3.2 Excepcions en C++ . . . . . . . . . . . . . . .
3.2.1 Deteccio´ i generacio´ d’una excepcio´ 3.2.2 Tractament de les excepcions . . . .
3.2.3 Propagar una excepcio´ . . . . . . . .
3.3 Classe error . . . . . . . . . . . . . . . . . .
3.4 Genericitat . . . . . . . . . . . . . . . . . . .
3.4.1 Plantilles de funcions . . . . . . . . .
3.4.2 Plantilles de classes . . . . . . . . . .
3.5 Exercici . . . . . . . . . . . . . . . . . . . . .
3.5.1 conjunt.hpp . . . . . . . . . . . . . .
3.5.2 Activitats . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
28 29 30 31 31 33 .
.
.
.
.
.
.
37 37 37 38 41 41 42 43 .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
45 45 46 46 47 47 47 48 50 51 53 53 55 58 59 61 C Estil de programacio´ i documentacio´ C.1 Noms de variables adequats . . . . . . . . . . . .
C.2 Conveni consistent pels identificadors . . . . . .
C.3 Utilitzar noms en forma activa per les funcions .
C.4 Nom d’identificadors precisos . . . . . . . . . . .
C.5 Identacio´ del codi . . . . . . . . . . . . . . . . . .
` C.6 Evitar una logica del programa antinatural . . .
C.7 Disminuir la complexitat . . . . . . . . . . . . . .
C.8 Useu construccions similars per tasques similars C.9 No usar variables globals . . . . . . . . . . . . .
C.10 Utilitzar variables locals . . . . . . . . . . . . . .
C.11 Codi ben estructurat . . . . . . . . . . . . . . . .
C.12 Bona documentacio´ . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
63 63 64 64 65 66 68 70 72 72 74 75 76 .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
´ INDEX vii 4 79 79 80 80 81 81 81 82 82 82 83 84 84 85 86 87 88 88 89 90 91 91 92 93 94 96 La biblioteca est`andard de C++ 4.1 Introduccio´ a l’STL . . . . . . . .
4.2 La classe string . . . . . . . . . .
´ b`asic . . . . . . . . . .
4.2.1 Us 4.2.2 Operadors + i += . . . . .
4.2.3 Longitud . . . . . . . . . .
4.2.4 Operadors de comparacio´ 4.2.5 M`etode substr . . . . . . .
4.2.6 M`etode replace . . . . . .
4.2.7 M`etode find . . . . . . . .
4.3 vector<T> . . . . . . . . . . . . .
´ b`asic . . . . . . . . . .
4.3.1 Us 4.3.2 Iteradors . . . . . . . . . .
4.3.3 Capacitat . . . . . . . . . .
4.3.4 Acc´es als elements . . . .
4.3.5 Modificadors . . . . . . .
4.4 list<T> . . . . . . . . . . . . . . .
´ b`asic . . . . . . . . . .
4.4.1 Us 4.4.2 Iteradors . . . . . . . . . .
4.4.3 Capacitat . . . . . . . . . .
4.4.4 Acc´es als elements . . . .
4.4.5 Modificadors . . . . . . .
4.4.6 Altres operacions . . . . .
4.5 Exercici . . . . . . . . . . . . . . .
4.5.1 polinomi.hpp . . . . . . .
4.5.2 Activitats . . . . . . . . .
´ Index alfab`etic .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
99 viii ´ INDEX ´ Index de figures 1.1 ´ de punters. . . . . . . . . . . . . . . . . . . . . . . .
Exemple gr`afic de l’us A.1 A.2 A.3 A.4 A.5 A.6 A.7 A.8 A.9 Exemple: Fitxer intcell.hpp . . . . . . . . . . . . . . . . . . . . . . . . . . .
Exemple: Fitxer intcell.rep . . . . . . . . . . . . . . . . . . . . . . . . . . .
Resultat d’aplicar aplicar la 4a llei sobre intcell.hpp . . . . . . . . . . . . .
Fitxer .CPP despr´es del primer pas de la 5a llei . . . . . . . . . . . . . . .
Fitxer .CPP despr´es del segon pas de la 5a llei . . . . . . . . . . . . . . . .
Fitxer .CPP despr´es del tercer pas de la 5a llei . . . . . . . . . . . . . . . .
Fitxer .CPP despr´es de la 6a llei . . . . . . . . . . . . . . . . . . . . . . . .
Fitxer prog.cpp per provar la constructora per defecte de la classe IntCell Fitxer intcell.cpp acabat . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 .
.
.
.
.
.
.
.
.
14 15 15 15 16 16 16 17 18 B.1 Compilacio´ i muntatge de les classes a i b, i el programa principal prog. . .
B.2 Compilacio´ i muntatge de les classes a (gen`erica) i b, i el programa principal prog. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39 ix 40 x ´ INDEX DE FIGURES 1 Punters, pas de par`ametres i taules 1.1 Punters ´ la declaracio´ d’una variable de cert tipus implica la reserva d’un En temps d’execucio, ` ` espai de memoria per valors d’aquest tipus. Aquesta reserva de memoria comenc¸a en ` una direccio´ de memoria concreta.
` Tenint en compte aixo: • L’operador & sobre una variable retorna la direccio´ on comenc¸a l’espai reservat per aquesta variable.
• La direccio´ d’una variable e´ s del tipus associat T*. A una variable del tipus T* se l’anomena tipus punter a T i pot contenir direccions de variables del tipus T.
• L’operador * aplicat a un punter permet accedir a la informacio´ d’aquest punter.
´ de punters veure la figura 1.1.
Per veure un exemple d’us 1 2 int x , z ; int ∗y ; 3 4 5 6 x = 12; y = &x ; z = ∗y ; // p.e. la direccio´ de x pot ser 6240fa102 // z valdr`a 12 (el mateix que x) 7 8 9 ∗y = 1 4 8 0 ; // x valdr`a 1480 y = x+z ; // ara y tindr`a una direccio´ incorrecta: 1492 Un punter que no apunta a cap lloc s’anomena punter nul i el seu valor e´ s 0 (≡NULL).
Si p e´ s un punter nul llavors *p no est`a definit, i en general provoca un error d’execucio´ immediat (tipus Segmentation fault), pero` tot dependr`a del sistema. En qualsevol cas e´ s un error greu.
1 ´ 1. PUNTERS, PAS DE PARAMETRES ` SESSIO I TAULES 2 ´ de punters.
Figura 1.1: Exemple gr`afic de l’us 1 2 3 4 5 6 char ∗p ; char c = ’b’ ; ∗p = ’a’ ; // ERROR! p no est`a inicialitzat, pot apuntar a qualsevol lloc! p = 0; // p e´ s nul p = &c ; // p apunta a c ∗p = 0 ; // c cont´e un car`acter amb codi ASCII 0 Es poden declarar punters gen`erics del tipus void* . Aquests punters poden apuntar a qualsevol cosa: 1 2 3 4 5 int x ; float y ; void ∗g = &x ; ...
g = &y ; ´ ja que son ´ una font d’errors molt dif´ıcil de corregir. El seu us ´ Conv´e evitar el seu us, ha de quedar restringit a funcions de molt baix nivell.
` 1.2 Referencies Una refer`encia e´ s un nom alternatiu a una variable ja declarada, e´ s a dir, un alias. Una refer`encia d’una variable de tipus T e´ s de tipus T&.
Tota refer`encia cal que sigui inicialitzada en la seva declaracio´ excepte quan la re´ En aquest darrer cas la refer`encia s’inicialitza fer`encia e´ s un par`ametre d’una funcio.
autom`aticament quan s’invoca la funcio´ i s’efectua el pas de par`ametres.
` 1.3. PAS DE PARAMETRES 1 2 3 char a ; char& b = a ; El valor d’una refer`encia no es pot canviar. Nom´es es pot canviar el valor a la que es refereix la refer`encia.
1 2 a = ’Z’ ; b = ’X’ ; // a ara val ’X’ La direccio´ d’una refer`encia e´ s la mateixa de la variable a la qual es refereix.
Les refer`encies estan implementades mitjanc¸ant punters, pero` no disposen de les operacions sobre punters.
` 1.3 Pas de parametres ´ de refer`encies, Els par`ametres de sortida i d’entrada/sortida s’especifiquen mitjanc¸ant l’us e´ s a dir, amb el pas de par`ametres per refer`encia on: • Par`ametre formal: T& identificador • Par`ametre actual: variable Per altra banda, els par`ametres d’entrada es poden especificar de tres maneres diferents: 1. Pas de par`ametre per valor: el par`ametre formal es comporta com una variable local ´ inicialitzada amb el valor del par`ametre actual (s’usa el constructor de la funcio, ` per copia del tipus T): • Par`ametre formal: T identificador • Par`ametre actual: expressi´ o 2. Pas de par`ametre per valor constant: el par`ametre formal es comporta com una cons´ inicialitzada amb el valor del par`ametre actual (s’usa el tant local de la funcio, ` constructor per copia del tipus T): • Par`ametre formal: const T identificador • Par`ametre actual: expressi´ o 3. Pas de par`ametre per refer`encia constant: el par`ametre formal e´ s una refer`encia al ` par`ametre actual (NO es fa cap copia) pero` el par`ametre formal no es pot modificar: • Par`ametre formal: const T& identificador • Par`ametre actual: variable ´ 1. PUNTERS, PAS DE PARAMETRES ` SESSIO I TAULES 4 Es recomana el pas de par`ametres per valor (T x, const T x) pels par`ametres elementals predefinits (int, char, bool, . . . ). En canvi, pels par`ametres d’entrada que siguin objectes “compostos” e´ s preferible el pas de par`ametres per ` refer`encia constant (const T& x) per aix´ı evitar fer copies costoses.
El qualificatiu const permet que el compilador detecti errors en els que es modifica inadvertidament un par`ametre d’entrada. Aixo` no seria un problema si usem pas per ` ´ si usem valor (T x), ja que treballem amb una copia, pero` e´ s un problema molt serios pas per refer`encia, doncs la funcio´ treballa amb el par`ametre actual (simplement se li ´ un altre nom o alias per referir-se a ell).
dona ¨ Els seguents exemples permeten apronfundir en el mecanisme de pas de par`ametres: ¨ Exemple 1) Qu`e s’escriur`a per pantalla quan s’executi el seguent codi? 1 2 3 4 5 void proc ( int x ) { cout << x << endl ; x = 14; cout << x << endl ; } 6 7 8 9 10 11 12 int main ( ) { int a = 1 0 ; cout << a << endl ; proc ( a ) ; cout << a << endl ; } // a no ha canviat el seu valor Exemple 2) I si afegim & al par`ametre de l’accio´ proc? Qu`e passaria? 1 2 3 4 5 void proc ( int &x ) { cout << x << endl ; x = 14; cout << x << endl ; } 6 7 8 9 10 11 12 int main ( ) { int a = 1 0 ; cout << a << endl ; proc ( a ) ; cout << a << endl ; } // a val 14 Exemple 3) I si la capc¸alera de l’accio´ proc fos aquesta: 1.4. RETORN DE RESULTATS 1 2 3 5 void proc ( const int &x ) { ...
} Qu`e passaria? 1 2 3 4 5 void proc ( const int &x ) { cout << x << endl ; x = 14; cout << x << endl ; } 6 7 8 9 10 11 12 int main ( ) { int a = 1 0 ; cout << a << endl ; proc ( a ) ; cout << a << endl ; } // ERROR de compilacio´ ´ de Els par`ametres de sortida o d’entrada/sortida poden ser simulats mitjanc¸ant l’us punters a l’estil de C. Fer-ho d’aquesta manera no e´ s recomanable.
1.4 Retorn de resultats ` Una funcio´ pot retornar els seus resultats per copia, per refer`encia o per refer`encia constant: tipus funcio(llista params formals) on tipus e´ s el nom d’un tipus (T o void), una refer`encia (T&), o una refer`encia constant (const T&).
1 2 3 int f 1 ( int x ) { return x ; } 4 5 6 7 int f 2 ( int& x ) { return x ; } 8 9 10 11 int& f 3 ( int& x ) { return x ; } 12 13 int& f 4 ( int x ) { // MALAMENT!! ´ 1. PUNTERS, PAS DE PARAMETRES ` SESSIO I TAULES 6 return x ; 14 15 } 16 17 18 void f ( ) { int w = 1 0 ; 19 int y = f 1 (w) ; ` // Amb aquesta crida es generen 3 copies del valor w: // w → x, x → resultat, resultat → y 20 21 22 23 int z = f 2 (w) ; // El par`ametre formal necess`ariament ha de ser una variable.
` // Es generen 2 copies del valor w: // w = x → resultat, resultat → z 24 25 26 27 28 int u = f 3 (w) ; ` // genera 1 copia: w = x = resultat → u 29 30 31 } Una funcio´ mai ha de retornar un punter o una refer`encia a ´ destruits una variable local o a un par`ametre donat que son ´ en sortir de la funcio.
El compilador detecta la major part d’errors com els que mostrem a continuacio´ si ` a vegades no son ´ detectats i els errors es produeixen activem els flags adequats. Pero, ´ en temps d’execucio.
1 2 3 4 5 6 7 8 float ∗ c a l c u l a ( int x , const float y ) { float z = 0 . 0 ; while ( x > 0 ) { z = z + y / x; −−x ; } return &z ; // ERROR!! } 9 10 11 12 13 14 15 16 17 float& c a l c u l a ( int x , const float y ) { float z = 0 . 0 ; while ( x > 0 ) { z = z + y / x; −−x ; } return z ; // ERROR!! } 1.5. TAULES (ARRAYS) 7 1.5 Taules (arrays) C++ t´e un constructor de vectors (arrays) predefinit que b`asicament funciona igual que C o Java.
Els arrays (i els punters) solen utilitzar-se com mecanismes de baix nivell per implementar classes. Aquests detalls d’implementacio´ queden amagats a l’usuari, que utilitzar`a classes segures (es realitzen les comprovacions que siguin necess`aries internament) i no haur`a de manipular arrays directament.
1.5.1 Declaracio´ Per declarar un vector de n elements del tipus T escriurem: T identificador[n]; ´ Per El valor n ha de ser constant o una expressio´ calculable en temps de compilacio.
crear un array din`amic s’ha d’utilitzar una t`ecnica diferent (veure la sessio´ 2).
Els components d’un vector d’n elements s’indexen de 0 fins a n - 1.
En C++ no es fa cap comprovacio´ de rang ni est`atica ni en ´ Donat un array A amb n elements, accetemps d’execucio.
dir a A[i], si i ¡ 0 o´ i ¿= n pot provocar un error immediat o ¨ encies encara pitjors.
tenir consequ` 1.5.2 Consulta/Modificacio´ de les taules Per consultar una de les caselles d’un vector cal indicar el nom del vector i entre cortxets ´ el numero de la casella.
identificador[index]; L’´ındex est`a compr`es entre 0 . . . numero elements - 1.
Exemple: v[0] v[1] ...
v[19] o tamb´e: v[0] = 15; ´ 1. PUNTERS, PAS DE PARAMETRES ` SESSIO I TAULES 8 1.5.3 Connexio´ entre arrays i punters La connexio´ entre arrays i punters en C++ es profunda: un array e´ s de fet un punter constant al primer component del vector: p ≡ & p[0]. En general, p + i ≡ & p[i].
´ L’unica difer`encia entre un punter i un array e´ s que un array no pot ser ”modificat”: 1 2 3 4 5 6 7 8 9 10 11 12 13 int p [ 1 0 ] ; int z [ 1 0 ] ; int ∗ q = p ; for ( int i = 0 ; i < 1 0 ; ++ i ) { p[ i ] = ( i + 1) ∗ 10; } cout << ∗q << endl ; // imprimeix 10 cout << p [ 2 ] << endl ; // imprimeix 30 cout << ∗ ( q + 2 ) << endl ; // imprimeix 30 q[2] = 33; cout << ∗ ( p + 2 ) << endl ; // imprimeix 33 q = z ; // e´ s correcte p = z ; // ERROR! la direccio´ guardada a p no pot ser modificada Donat que un array e´ s un punter, es pot passar com par`ametre eficientment, pero` caldr`a usar el qualificador const per evitar que es facin modificacions accidentals si es tracta d’un par`ametre d’entrada.
Un array nom´es es pot passar per refer`encia o per refer`encia constant (mai es pot passar un array per valor), amb les mateixes regles de pas de par`ametres que en la resta de casos.
El tipus d’un array de T’s e´ s, sigui quina sigui la mida, T* const o de manera equivalent T[].
No existeixen els arrays multidimensionals. Per crear matrius caldr`a usar un array d’arrays.
double mat [ 1 0 ] [ 2 0 ] ; 1.6 Exercici L’objectiu d’aquesta exercici e´ s implementar la classe cj_enters que ens permet representar i manipular conjunts (finits) d’enters. De fet, la classe nom´es ens permetr`a representar conjunts de com a m`axim cj_enters::MAXSIZE elements. El fitxer cj_enters.hpp ¨ ´ es descriu amb detall en la seguent subseccio.
B`asicament el que cal fer e´ s: 1. trobeu una representacio´ adequada pels objectes de la classe i escriviu els atributs necessaris en la part private de cj_enters.hpp; 1.6. EXERCICI 9 2. comenceu amb una implementacio´ trivial per tots els m`etodes en cj_enters.cpp, i despr´es implementeu i proveu els diferents m`etodes paulatinament. Els primers ´ la constructora (obvi!), inserta, conte i m`etodes que haurieu d’implementar son print.
En cas que no es tingui clar el passos a seguir per crear un classe en C++ es recomana llegir el Dec`aleg (veure l’ap`endix A).
1.6.1 cj_enters.hpp Un objecte de la classe cj_enters representa a un conjunt de com a m`axim MAXSIZE enters.
#ifndef _CJ_ENTERS_HPP_ #define _CJ_ENTERS_HPP_ #include <iostream> using namespace std; class cj_enters { public: Nombre m`axim d’elements que pot tenir un conjunt.
static const int MAXSIZE = 20; Constructora per defecte. Crea un conjunt buit.
cj_enters(); Insereix l’enter e en el conjunt. Naturalment si e ja pertanyia al conjunt, el m`etode no fa res.
void insereix(int e); ´ interseccio´ i difer`encia de conjunts. Operen modificant el conjunt sobre el que s’aplica Unio, el m`etode, usant el segon conjunt com argument. P.e.: a.unir(b) fa que el nou valor d’a sigui ´ els valors originals dels objectes a i b.
A ∪ B, on A i B son void unir(const cj_enters& B); void intersectar(const cj_enters& B); void restar(const cj_enters& B); ´ interseccio´ i difer`encia de conjunts. Operen creant un nou conjunt sense modificar el conUnio, ´ la resta a la junt sobre el que s’aplica el m`etode. La suma de conjunts correspon a la unio, ´ difer`encia i el producte a la interseccio.
cj_enters operator+(const cj_enters& B) const; ´ 1. PUNTERS, PAS DE PARAMETRES ` SESSIO I TAULES 10 cj_enters operator*(const cj_enters& B) const; cj_enters operator-(const cj_enters& B) const; Retorna cert si i nom´es si e pertany al conjunt.
bool conte(int e) const; Retornen els elements m`axim i m´ınim del conjunt, respectivament. El seu comportament no est`a definit si el conjunt e´ s buit.
int max() const; int min() const; Retorna el nombre d’elements (la cardinalitat) del conjunt.
int card() const; Operadors relacionals. == retorna cert si i nom´es si els dos conjunts (el conjunt sobre el que aplica ´ diferents.
i B) contenen els mateixos elements; != retorna cert si i nom´es si els conjunts son bool operator==(const cj_enters& B) const; bool operator!=(const cj_enters& B) const; Imprimeix el conjunt d’enters, ordenats en ordre ascendent, sobre el canal de sortida os; el format e´ s [e1 e2 ...en ], e´ s a dir, amb car`acters espaiadors simples separant els elements i tancant la ¨ encia entre corxets.
sequ` void print(ostream& os) const; private: Els atributs i m`etodes privats van aqu´ı.
}; #endif 1.6.2 Activitats Si ja has implementat la classe cj_enters llavors tens tamb´e acabades totes les activitats.
Repassa-les per estar preparat per l’Activitat numero ´ 1. En cas contrari primer acaba d’implementar la classe cj_enters abans de continuar mirant les Activitats.
NOTA: En cada una de les diferents activitats cal que implementis tots els m`etodes addicionals que usis expl´ıcitament.
1.1 a) Escriu la representacio´ de la classe cj_enters usant arrays.
1.6. EXERCICI 11 b) Implementa el m`etode unir de la classe cj_enters. Pots implementar els m`etodes auxiliars que creguis necessaris.
void u n i r ( const c j e n t e r s& c j ) ; 1.2 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode intersectar de la classe cj_enters. Cal que escriguis ´ tots els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
void i n t e r s e c t a r ( const c j e n t e r s& c j ) ; 1.3 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode restar de la classe cj_enters. Cal que escriguis tots els ´ m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
void r e s t a r ( const c j e n t e r s& c j ) ; 1.4 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode operator+ de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
c j e n t e r s operator +( const c j e n t e r s& c j ) const ; 1.5 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode operator* de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
c j e n t e r s operator ∗ ( const c j e n t e r s& c j ) const ; 1.6 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode operator- de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
Pots implementar els m`etodes auxiliars que creguis necessaris.
c j e n t e r s operator −( const c j e n t e r s& c j ) const ; 1.7 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode operator== de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator ==( const c j e n t e r s& c j ) const ; 1.8 a) Escriu la representacio´ de la classe cj_enters usant arrays.
b) Implementa el m`etode operator!= de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator ! = ( const c j e n t e r s& c j ) const ; 12 ´ 1. PUNTERS, PAS DE PARAMETRES ` SESSIO I TAULES A Dec`aleg per implementar una classe en C++ Comenc¸ar a implementar una classe en C++ des de cero pot ser un proc´es complicat els primers cops que ho feu. En aquest cap´ıtol veurem els passos que caldria seguir per tal d’implementar una classe en C++. Per tal d’il·lustrar el proc´es amb m´es detall es desenvolupar`a un exemple: la classe IntCell. El concepte d’aquesta classe e´ s molt ´ senzill, ja que nom´es ens permet emmagatzemar un unic enter dins la classe.
1a Llei Crear la capc¸alera de la classe F ER el fitxer de capc¸alera de la classe (a partir d’ara l’anomenarem fitxer .HPP) on apareixen els m`etodes (constructors, modificadors i consultors) que permeten actuar sobre una classe.
En el cas de les pr`actiques d’ESIN ja disposeu del fitxer capc¸alera en el Campus Di´ a dir, en aquest punt heu de tenir el fitxer capc¸alera gital1 . Per tant no l’heu de crear. Es de la classe similar al que es pot veure a la figura A.1.
2a Llei Pensar la representacio´ P ENSAR la representacio´ interna de la classe.
´ O sigui, s’ha La majoria de classes han d’emmagatzemar algun tipus d’informacio.
´ de pensar com guardar aquesta informacio.
Les possibles representacions d’una classe van des de les m´es simples (variable, struct, . . . ) fins a representacions m´es complicades (taules, arbres, . . . ).
A l’hora de triar la representacio´ d’una classe s’ha de pensar en l’efici`encia esperada de cada una de les operacions.
1 http://atenea.upc.edu 13 ` ` APENDIX A. DECALEG PER IMPLEMENTAR UNA CLASSE EN C++ 14 ✞ 1 2 ☎ # ifndef # define INTCELL HPP INTCELL HPP 3 4 # include <s t r i n g > 5 6 7 8 class I n t C e l l { public : explicit I n t C e l l ( int i n i t i a l V a l u e = 0 ) ; 9 int read ( ) const ; void w r i t e ( int x ) ; I n t C e l l operator +( const I n t C e l l &i c ) const ; 10 11 12 13 private : // Si la classe fos per una sessio´ de laboratori la representacio´ s’afegiria aqu´ı.
14 15 16 17 18 ✝ }; # endif ✆ Figura A.1: Exemple: Fitxer intcell.hpp Una vegada s’ha decidit quina e´ s la representacio´ adient, e´ s bo escriure-la sobre paper, i aix´ı acabar de visualitzar completament les estructures que s’hagin pensat.
En el cas de representacions complexes e´ s aconsellable (a m´es d’escriure l’explica´ fer una representaci´o gr`afica.
cio) 3a Llei Escriure la representacio´ E SCRIURE la representacio´ de la classe un cop ja tenim les idees clares. La representacio´ interna d’una classe normalment s’escriur`a dins el fitxer .HPP (a la part privada). A la pr`actica de cara a poder corregir les classes de manera autom`atica, la declararem en un ´ a dir: fitxer apart que anomenarem fitxer .REP. Es • Sessions de Laboratori: la representacio´ de la classe es posar`a en el fitxer .HPP.
• Pr`actica: s’ha de crear un fitxer amb el mateix nom de la classe pero` amb extensio´ .REP. Aquest fitxer contindr`a les variables, structs, tipus, . . . , que representen la nostra classe. Veure la figura A.2.
´ convenient documentar acuradament els fitxers per tal de deixar clar QU E` s’ha fet Es i PER QU E` s’ha fet d’aquesta manera.
15 ✞ 1 2 3 4 5 6 7 ☎ [...] private : ´ // La representacio´ de la classe IntCell est`a formada per un unic atribut // “ storedValue”, on emmagatzemem el valor de la cel·la.
int s t o r e d V a l u e ; }; # endif ✝ ✆ Figura A.2: Exemple: Fitxer intcell.rep 4a Llei Crear fitxer .CPP ´ • C OPIAR el fitxer .HPP de la classe com a fitxer .CPP (fitxer d’implementacio).
¨ Aquesta comanda a Linux e´ s la seguent: cp fitxer.hpp fitxer.cpp • Tot seguit, hem d’ESBORRAR del fitxer .CPP TOT el contingut menys les cap¸caleres de les operacions publiques ´ que hem d’implementar. Observeu la figura A.3 per tenir un exemple del resultat esperat d’aquest pas despr´es d’haver transformat el fitxer capc¸alera.
✞ 1 2 3 4 ☎ explicit I n t C e l l ( int i n i t i a l V a l u e = 0 ) ; int read ( ) const ; void w r i t e ( int x ) ; I n t C e l l operator +( const I n t C e l l &i c ) const ; ✝ ✆ Figura A.3: Resultat d’aplicar aplicar la 4a llei sobre intcell.hpp 5a Llei Retocar les capc¸aleres de les operacions 1. I NCLOURE davant del nom de cadascuna de les operacions el nom de la classe seguit de l’operador scope (::). Veure la figura A.4.
✞ 1 2 3 4 ☎ explicit I n t C e l l : : I n t C e l l ( int i n i t i a l V a l u e = 0 ) ; int I n t C e l l : : read ( ) const ; void I n t C e l l : : w r i t e ( int x ) ; I n t C e l l I n t C e l l : : operator +( const I n t C e l l &i c ) const ; ✝ Figura A.4: Fitxer .CPP despr´es del primer pas de la 5a llei 2. A FEGIR al final del nom del m`etode { } i esborrar el ;. Veure la figura A.5.
✆ ` ` APENDIX A. DECALEG PER IMPLEMENTAR UNA CLASSE EN C++ 16 ✞ 1 2 3 4 ☎ explicit I n t C e l l : : I n t C e l l ( int i n i t i a l V a l u e =0) { } int I n t C e l l : : read ( ) const { } void I n t C e l l : : w r i t e ( int x ) { } I n t C e l l I n t C e l l : : operator +( const I n t C e l l &i c ) const { } ✝ ✆ Figura A.5: Fitxer .CPP despr´es del segon pas de la 5a llei 3. E SBORRAR les inicialitzacions per defecte2 (=0, =1, ...) i el modificador explicit3 de les capc¸aleres del .CPP. Veure la figura A.6.
✞ 1 2 3 ☎ I n t C e l l : : I n t C e l l ( int i n i t i a l V a l u e ) { } int I n t C e l l : : read ( ) const { } void I n t C e l l : : w r i t e ( int x ) { } ✝ ✆ Figura A.6: Fitxer .CPP despr´es del tercer pas de la 5a llei 6a Llei Afegir l’include I NCLOURE al principi del fitxer .CPP un include amb el nom del fitxer .HPP de la classe que estem creant: #include "nom-classe.hpp" El resultat d’aplicar aquesta llei a l’exemple es pot veure en la figura A.7.
✞ 1 ☎ # include " intcell . hpp " 2 3 4 5 6 I n t C e l l : : I n t C e l l ( int i n i t i a l V a l u e ) { } int I n t C e l l : : read ( ) const { } void I n t C e l l : : w r i t e ( int x ) { } I n t C e l l I n t C e l l : : operator +( const I n t C e l l &i c ) const { } ✝ Figura A.7: Fitxer .CPP despr´es de la 6a llei 2 Les inicialitzacions per defecte o tamb´e dites par`ametres per defecte especifiquen quin valor ha de prendre un par`ametre en cas que no s’indiqui el seu valor. Els par`ametres per defecte nom´es figuraran en l’especificacio´ de la classe, e´ s a dir, el fitxer .HPP.
3 El modificador explicit indica que no es poden aplicar conversions de tipus impl´ıcites. Per exemple, si la constructora de la classe IntCell estigu´es declarada amb el modificador explicit en aquest cas: IntCell ic; ic = 37; el compilador donaria un error ja que els tipus no coincideixen. En cas contrari el compilador no es queixaria i l’assignacio´ mitjanc¸ant la conversio´ s’hauria produ¨ıt. Aquest modificador nom´es pot apar`eixer al fitxer .HPP.
✆ 17 7a Llei Primera compilacio´ - En aquests moments el fitxer .CPP ha de COMPILAR sense cap error.
g++ -c -ansi -Wall nom-classe.cpp Sols cal compilar la classe i no cal linkar-la amb un programa principal ja que no far`a res. Per tenir m´es detalls de com es compila/linka en C++ mirar l’ap`endix B d’aquest manual.
- Quan compilem pot ser que apareguin m´es d’un warning ja que els m`etodes de la classe no fan res, i alguns m`etodes poden necessitar retornar quelcom.
- Si apareixen errors vol dir que hem realitzat algun dels passos malament. Revisar tots els passos.
8a Llei Implementacio´ incremental ´ aconsellable comenc¸ar per la/les construcI MPLEMENTAR una operacio´ de la classe. Es tora/es per continuar amb les modificadores i acabar amb les consultores. En qualsevol ´ cas e´ s aconsellable implementar una unica operacio´ alhora.
9a Llei Compilar, Linkar i Provar Sempre despr´es d’acabar d’implementar un m`etode cal compilar, i sempre que es pugui provar el seu funcioment. Si ho fem aix´ı podrem estalviar-nos que en una compilacio´ ens sortin 300 errors, o que un error de funcionament de la classe estigui a l’operacio´ constructora (al principi de tot).
Per comprovar una classe cal provar-la generalment amb un programa que tingui un main. Es pot veure un exemple de programa principal en la figura A.8.
✞ 1 2 ☎ # include <iostream > # include " intcell . hpp " 3 4 using namespace s t d ; 5 6 7 8 9 int main ( ) { IntCell i c e l l ; cout << i c e l l . read ( ) << endl ; } ✝ Figura A.8: Fitxer prog.cpp per provar la constructora per defecte de la classe IntCell ✆ ` ` APENDIX A. DECALEG PER IMPLEMENTAR UNA CLASSE EN C++ 18 S’ha de compilar el programa principal, i muntar-ho tot (linkar) per generar l’executable.
g++ -o nom_executable.e nom-classe1.o nom-classe2.o Un cop s’ha comprovat que l’operacio´ funciona correctament cal tornar a aplicar la ´ 8 llei amb una altra operacio.
a 10a Llei Proves globals Un cop acabada la implementacio´ de totes les operacions de la classe hem de PROVAR-la amb els casos normals i els l´ımit. Es pot veure la implementacio´ acabada de la classe que estem fent servir com a exemple en la figura A.9.
✞ 1 ☎ # include " intcell . hpp " 2 3 4 5 I n t C e l l : : I n t C e l l ( int i n i t i a l V a l u e ) { storedValue = i n i t i a l V a l u e ; } 6 7 8 9 int I n t C e l l : : read ( ) const { return s t o r e d V a l u e ; } 10 11 12 13 void I n t C e l l : : w r i t e ( int x ) { storedValue = i n i t i a l V a l u e ; } 14 15 16 17 18 I n t C e l l I n t C e l l : : operator +( const I n t C e l l &i c ) const { I n t C e l l nou ( s t o r e d V a l u e + i c . s t o r e r e d V a l u e ) ; return nou ; } ✝ Figura A.9: Fitxer intcell.cpp acabat ✆ 2 ` Memoria din`amica 2.1 Introduccio´ ` ` La memoria din`amica permet la reserva i l’alliberament d’espai de memoria per dades ´ eficient de l’espai de memoria ` durant l’execucio´ del programa, e´ s a dir, ens permet un us ´ de memoria ` i utilitzar nom´es la quantitat necess`aria. L’us din`amica pot portar a simpli` existeix ficar el codi en alguns problemes i a complicar el mateix en d’altres. Per aixo, ´ de memoria ` un comprom´ıs entre l’us est`atica i din`amica.
` ` 2.2 Reserva i alliberament de memoria dinamica ` Per crear i destruir objectes en memoria din`amica s’utilitzen els operadors: ` new / delete: reserva o allibera una porcio´ de memoria.
new[] / delete[]: reserva o allibera un array de n objectes (taules din`amiques).
` Si un punter q apunta a una taula creada amb new[], la memoria ha de ser alliberada amb delete[] q. An`alogament, si p apunta a un objecte creat amb new, llavors l’objecte ´ important tenir en compte que els objectes creats amb s’ha de destruir amb delete p. Es ` ´ “anonims”, ` ´ memoria din`amica son e´ s a dir, accesibles unicament a trav´es de punters.
` Un objecte creat amb memoria din`amica existeix fins que no sigui destru¨ıt expl´ıcitament (o acabi l’execucio´ del programa).
¨ ` En el seguent codi es pot veure un exemple d’obtencio´ i alliberament de memoria din`amica per tipus predefinits: 1 2 int ∗ p i = new int ; ` // reserva memoria per un enter al qual apunta pi 3 4 5 6 char ∗ pc = new char [ 1 0 ] ; ` // reserva memoria per una taula de 10 car`acters [0..9] // pc apunta a la component 0 (pc ≡ &pc[0]) 19 ´ 2. MEMORIA ` ` SESSIO DINAMICA 20 7 // pc[i] ≡ *(pc+i) accedeix a la i-`essima component 8 9 ...
10 11 12 13 delete p i ; ` // allibera la memoria de l’enter apuntat per pi; el punter conserva el seu ` // valor, pero` ara apunta a una zona de memoria disponible per reservar.
14 15 16 delete [ ] pc ; ` // allibera la memoria de la taula de car`acters apuntada per pc ` Els operadors new y delete no es limiten a crear l’espai de memoria o alliberar-lo.
Una cop creat l’espai necessari per l’objecte, new invoca al constructor adequat per inicializar al nou objecte. Quan es crea una taula d’objectes amb new[] el sistema invoca ´ d’ells.
autom`aticament el constructor per cadascun Per la seva banda, delete aplica el destructor de la classe a la que pertany l’objecte ` ocupada per l’objecte. Quan es destrueix una taula apuntat i despr´es allibera la memoria ´ d’ells en ordre invers a la seva d’objectes el sistema invoca el destructor per cadascun ´ creacio.
1 2 3 4 5 6 7 8 class v e c t o r { public : v e c t o r ( int s = 8 ) ; v e c t o r ( const v e c t o r &v ) ; ˜ vector ( ) ; ...
}; // vector de s enters ` // constructor per copia // destructor 9 10 11 12 int main ( ) { v e c t o r ∗ pv1 = new v e c t o r ( 1 2 ) ; // crea un vector de 12 enters apuntat per pv1 13 14 15 v e c t o r ∗ pv2 = new v e c t o r ; // crea un vector de 8 enters apuntat per pv2 16 17 18 v e c t o r ∗ ptv1 = new v e c t o r [ 1 0 ] ; // crea una taula de 10 vectors de 8 enters apuntada per ptv1 19 20 21 v e c t o r ∗ pv3 = new v e c t o r ( ∗ pv2 ) ; ` // crea un copia de *pv2 apuntat per pv3 22 23 24 25 26 v e c t o r ∗ ptv2 = new v e c t o r [ 1 0 ] ( ∗ pv1 ) ; ` // crea una taula de 10 vectors on cada un dels vectors e´ s una copia de // *pv1 i est`a apuntada per ptv2 ´ DE LA MEMORIA ` ` 2.3. PROBLEMES AMB LA GESTIO DINAMICA delete pv1 ; delete pv2 ; delete [ ] ptv1 ; delete pv3 ; delete [ ] ptv2 ; ` // es destrueix la memoria assignada per tots els punters 27 28 29 30 31 32 33 21 } ` ` 2.3 Problemes amb la gestio´ de la memoria dinamica ` Alguns problemes comuns en la gestio´ de la memoria din`amica inclouen: • Emparellament incorrecte: No “aparellar” correctament els operadors.
Per exemple: intentar destruir amb delete una taula creada amb new[].
` • Dangling references: Alliberar un objecte que no ha estat creat amb memoria din`amica o ja ha estat alliberat.
Per exemple: 1 2 3 4 5 6 7 int x ; int ∗ p int ∗ q delete q = p; delete delete = new int ; = &x ; ` q ; // ERROR: q no apunta a un objecte creat usant memoria din`amica p; q; // OK // ERROR: l’objecte al que apuntava q ha deixat d’existir ` • Memory leaks: Perdre l’acc´es a objectes creats amb memoria din`amica i per tant no tenir la possibilitat de destruir-los.
Per exemple: 1 2 3 4 5 6 void f ( ) { int ∗ p = new int ; ∗p = 3 ; } // ERROR! quan finalitza l’execucio´ de la funcio´ f la variable local p deixa // d’existir i no tenim forma d’accedir a l’objecte.
7 8 9 10 11 12 13 int f 2 ( ) { int ∗ p = new int ; int ∗ q = new int ; p = q; // ERROR! perdem l’acc´es al primer objecte ...
} 14 15 int ∗ g ( int x ) { ´ 2. MEMORIA ` ` SESSIO DINAMICA 22 16 17 18 19 20 21 int ∗ p = new int ; ∗p = x ; return p ; } // OK: es pot ”recollir”el punter a l’objecte creat din`amicament en el punt de // crida a la funcio´ g; pero` aquesta forma de treballar no e´ s aconsellable.
22 23 24 25 26 27 28 void h ( node∗ p ) { node∗ q = new node ; p −> seg = q ; } // OK: es t´e acc´es al nou node a partir del punter seg que e´ s apuntat per un ´ // punter extern a la funcio´ h (el par`ametre de la funcio).
` • Desreferencia de NULL: Dereferenciar (amb * o amb ->) un punter nul. Aquest ` error t´e quasi sempre resultats catastrofics (segmentation fault, bus error, . . . ).
Per exemple: 1 2 3 4 5 6 7 8 bool e s t a ( const l l i s t a & l , int x ) { node∗ p = l . primer ; while ( p ! = NULL and p −> i n f o ! = x ) { p = p −> s i g ; } return ( p −> i n f o == x ) ; // ERROR! si p == NULL, p->info e´ s incorrecte! } ` • Problema de l’aliasing: Usar o implementar incorrectament constructores per copia ` din`amica.
o l’operador d’assignacio´ per classes implementades mitjanc¸ant memoria Per exemple: 1 2 3 4 5 6 7 8 char s [ ] = " abc " ; // s[0] = ’a’, s[1] = ’b’, s[2] = ’c’, // s[3] = ’\0’ char t [ 1 0 ] ; t = s; cout << t [ 0 ] ; // imprimeix ’a’ t [ 0 ] = ’b’ ; cout << s [ 0 ] ; // imprimeix ’b’!! t e´ s en realitat un char* i l’assignacio´ // t = s fa que t apunti a ’s[0]’.
O tamb´e: 1 2 3 4 5 6 class e s t u d i a n t { public : e s t u d i a n t ( char ∗ nom, int dni ) ; char ∗ consulta nom ( ) ; int c o n s u l t a d n i ( ) ; ...
´ DE LA MEMORIA ` ` 2.3. PROBLEMES AMB LA GESTIO DINAMICA private : char ∗ nom ; int d n i ; 7 8 9 10 23 }; 11 12 13 int main ( ) { e s t u d i a n t a ( " pepe " , 4 5 2 1 8 9 2 2 ) ; 14 ` e s t u d i a n t b = a ; // el constructor per copia d’ofici no serveix! 15 16 a = b ; // l’operador d’assignacio´ d’ofici e´ s inadequat! 17 18 } • Retornar punter local: Retornar un punter o una refer`encia a un objecte local d’una ´ El problema e´ s que a l’acabar la funcio, ´ l’objecte local es destrueix i el funcio.
punter o refer`encia deixa d’apuntar a un objecte v`alid.
Per exemple: 1 2 3 4 5 6 7 8 9 int& maxim ( int A[ ] , int n ) { int max = 0 ; for ( int i = 0 ; i < n ; ++ i ) { if (A[ i ] >= A[ max ] ) { max = i ; } } return A[ max ] ; // OK: es retorna una refer`encia a A[max] } 10 11 12 13 14 15 16 17 18 19 int& maxim ( int A[ ] , int n ) { int max = 0 ; for ( int i = 0 ; i < n ; ++ i ) { if (A[ i ] >= max ) { max = A[ i ] ; } } return max ; // ERROR: es retorna una refer`encia a una variable local! } • Delete de NULL: Fer delete p amb p == NULL no e´ s erroni. No t´e cap efecte i ´ ajuda a simplificar el codi.
ocasionalment el seu us ´ 2. MEMORIA ` ` SESSIO DINAMICA 24 REGLA DELS TRES GRANS La regla del tres grans (angl`es: the Law of the Big Three) indica que si necessites una implementacio´ no trivial del: ` • constructor per copia, • destructor, o ´ • operador d’assignacio.
segurament necessitar`as implementar els altres. Aquests tres m`etodes ´ autom`aticament creats pel compilador si no son ´ expl´ıcitament deson ` clarats pel programador. SEMPRE que una classe empri memoria din`amica caldr`a implementar aquests tres m`etodes, ja que les implementacions d’ofici amb punters no funcionen com nosaltres voldr´ıem.
Aix´ı doncs, per ser m´es flexibles, en les nostres especificacions posarem aquests tres m`etodes.
` 2.4 Constructor per copia ` ` Un constructor per copia inicialitza objectes amb copies d’altres objectes de la mateixa classe. Aquest m`etode t´e el mateix nom que la classe, rep com a par`ametre un objecte de la mateixa classe i com els m`etodes constructor no retorna res. El perfil d’aquest m`etode per una classe X seria: X : : X( const X&) { ...
} ` Tota classe t´e un constructor per copia.
Si no el programem, el compilador proporciona un ”d’ofici”que es limita a copiar un a un els atributs. En general, el constructor per ` ´ punters a copia ”d’ofici”ens va b´e, exceptuant si l’objecte t´e un o m´es atributs que son ` memoria din`amica o un array.
` Cal tenir en compte que el constructor per copia s’invoca autom`aticament quan fem: • pas de par`ametres per valor • retorns per valor • declaracions del tipus: v e c t o r v1 = v2 ; v e c t o r v1 ( v2 ) ; 2.5. DESTRUCTOR 25 ` Exemple de constructor per copia: 1 2 3 4 5 6 7 8 9 class v e c t o r { public : v e c t o r ( int s = 8 ) ; ` v e c t o r ( const v e c t o r& v ) ; // per copia ...
private : int ∗ t ; // punter de la taula d’enters int s ; // mida de la taula }; 10 11 v e c t o r : : v e c t o r ( int s ) : s ( s ) , t ( new int [ s ] ) {} 12 13 14 15 16 17 v e c t o r : : v e c t o r ( v e c t o r& v ) : s ( v . s ) , t ( new int [ s ] ) {} for ( int i = 0 ; i < s ; ++ i ) { t [ i ] = v. t [ i ]; } } 18 19 20 21 22 23 void f ( ) { v e c t o r v1 ( 1 0 ) ; ...
v e c t o r v2 = v1 ; } // vector de 10 enters ` // v2 e´ s copia de v1 2.5 Destructor Els destructors proporcionen un mecanisme autom`atic que garantitza la destruccio´ dels objectes. Es declaren com m`etodes que no retornen cap resultat i el nom del m`etode destructor e´ s el nom de la classe precedit del car`acter ˜. Els m`etodes destructors mai no tenen par`ametres. El perfil d’aquest m`etode per una classe X seria: X : : ˜ X() { ...
} ´ Tota classe t´e un unic destructor. Si no est`a programat el destructor, el compilador proporciona un ”d’ofici”. En general, el destructor ”d’ofici”ens va b´e exceptuant si l’ob´ punters a memoria ` jecte t´e un o m´es atributs que son din`amica.
El m`etode destructor l’invoca nom´es el sistema, just en el moment en que el bloc on es va declarar l’objecte s’acaba. Mai es cridar`a expl´ıcitament el destructor.
Exemple de destructor: ´ 2. MEMORIA ` ` SESSIO DINAMICA 26 1 2 3 4 5 6 7 8 9 class v e c t o r { public : ...
˜ v e c t o r ( ) ; // destructor ...
private : int ∗ t ; // punter de la taula d’enters int s ; // mida de la taula }; 10 11 12 13 vector : : ˜ vector ( ) { delete [ ] t ; } 14 15 16 17 18 19 void f ( ) { if ( . . . ) { v e c t o r v1 ; ...
} // destruccio´ de v1 en sortir del bloc if 20 21 22 23 v e c t o r v2 ; ...
} // destruccio´ de v2 en sortir del bloc de la funcio´ f 2.6 Operador d’assignacio´ Cal tenir en compte que inicialitzar e´ s diferent d’assignar. Quan redefinim l’operador ` d’assignacio´ =; e´ s similar al constructor per copia, pero` l’objecte modificat (la part esquerra) e´ s un objecte que ja existeix i retorna una refer`encia a l’objecte destinatari de la ` copia.
´ Si no la programem, el compilador proporciona Tota classe t´e definida l’assignacio.
l’operador d’assignacio´ ”d’ofici”que consisteix en cridar un a un l’operador d’assignacio´ per cada atribut de l’objecte en curs. En general, l’assignacio´ ”d’ofici”ens va b´e, exceptu´ punters a memoria ` ant si l’objecte t´e un o m´es atributs que son din`amica. En aquest cas cal redefinir l’operador d’assignacio´ per tal que tingui l’efecte que nosaltres desitgem.
El perfil d’aquest m`etode per una classe X seria: X& X : : operator =( const X&) { ...
} ` ´ L’operador retorna una refer`encia a l’objecte que rep la copia permetent aix´ı l’us d’expressions com ara: a = b = c;.
2.7. EL COMPONENT THIS 27 Altres operadors relacionats amb el d’assignacio´ (com ara +=, -=, etc.) acostumen a ´ tenir el mateix perfil per la mateixa rao.
´ Exemple d’operador d’assignacio: 1 2 3 4 5 6 7 8 9 class v e c t o r { public : ...
v e c t o r& operator =( const v e c t o r& v ) ; // assignacio´ ...
private : int ∗ t ; // punter de la taula d’enters int s ; // mida de la taula }; 10 11 12 13 14 15 16 17 18 19 20 21 22 23 v e c t o r& v e c t o r : : operator =( const v e c t o r& v ) { if (&v ! = this ) { ´ de la mateixa mida if ( s ! = v . s ) { // si no son delete [ ] t ; // la taula t no ens serveix.
s = v. s ; t = new int [ s ] ; } for ( int i = 0 ; i < s ; ++ i ) { t [ i ] = v. t [ i ]; } } return ∗ this ; } 2.7 El component this this@this this e´ s una paraula reserva en C++ i e´ s un punter a l’objecte que invoca el m`etode.
´ t´ıpic en C++ que l’assignacio´ retorni la refer`encia a l’objecte modificat de manera que, Es p.e., a = b = 0 tingui sentit.
a = b = 0 ; // equival a: a = (b = 0); // equival a: a . operator =( b . operator = ( 0 ) ) ; ´ 2. MEMORIA ` ` SESSIO DINAMICA 28 2.8 Exemple pila 2.8.1 Especificacio´ de la classe pila Fitxer pila.hpp: ✞ 1 2 # ifndef # define ☎ PILA HPP PILA HPP 3 4 5 6 class p i l a { public : pila ( ) ; // constructor 7 // tres grans ` p i l a ( const p i l a& p ) ; // constructor per copia ˜ pila ( ) ; // destructor p i l a& operator =( const p i l a& p ) ; // operador assignacio´ 8 9 10 11 12 void a p i l a r ( int x ) ; void d e s a p i l a r ( ) ; int cim ( ) const ; bool e s b u i d a ( ) const ; 13 14 15 16 17 private : struct node { // definicio´ de tipus privat ¨ node∗ seg ; // punter al seguent ’node’ int i n f o ; }; 18 19 20 21 22 23 node∗ 24 cim ; // la pila consisteix en un punter al node del cim 25 ` // m`etode privat de classe per alliberar memoria; allibera la cadena de // nodes que s’inicia en el node n.
static void e s b o r r a p i l a ( node∗ n ) ; 26 27 28 29 ` ` // m`etode privat de classe per realitzar copies; copia tota la cadena de nodes // a partir del node apuntat per origen i retorna un punter al node inicial de ` // la copia; la paraula reservada const indica que no es pot modificar el valor // apuntat pel punter origen.
static node∗ c o p i a p i l a ( const node∗ o r i g e n ) ; 30 31 32 33 34 35 36 }; # endif ✝ ✆ 2.8. EXEMPLE PILA 29 2.8.2 Implementacio´ de la classe pila Fitxer pila.cpp: ✞ 1 ☎ # include " pila . hpp " 2 3 4 5 // ————————` // METODES PRIVATS DE CLASSE // ————————- 6 7 8 9 10 11 12 void p i l a : : e s b o r r a p i l a ( node∗ n ) { if ( n ! = NULL) { e s b o r r a p i l a ( n−>seg ) ; // p->seg equival a (*p).seg ` delete n ; // allibera la memoria de l’objecte apuntat per n.
} } 13 14 15 16 17 18 19 20 // e´ s necessari posar pila::node com tipus del resultat per qu`e node // est`a definit de la classe pila p i l a : : node∗ p i l a : : c o p i a p i l a ( const node∗ o r i g e n ) { node∗ d e s t i = NULL if ( o r i g e n ! = NULL) { d e s t i = new node ; d e s t i −>i n f o = origen −>i n f o ; 21 // copia la resta de la cadena d e s t i −>seg = c o p i a p i l a ( origen −>seg ) ; 22 23 } return d e s t i ; 24 25 26 } 27 28 29 30 31 // ————————` ´ // METODES PUBLICS // ————————p i l a : : p i l a ( ) : cim (NULL) { } 32 33 34 35 36 ` // genera una copia de la pila apuntada per ’p. cim’ p i l a : : p i l a ( const p i l a& p ) { cim = c o p i a p i l a ( p . cim ) ; } 37 38 39 40 41 42 ` // allibera la memoria de la pila apuntada per ’ cim’ pila : : ˜ pila () { e s b o r r a p i l a ( cim ) ; } 30 43 44 45 46 47 48 49 50 ´ 2. MEMORIA ` ` SESSIO DINAMICA p i l a& p i l a : : operator =( const p i l a& p ) { if ( this ! = &p ) { node∗ aux = c o p i a p i l a ( p . cim ) ; e s b o r r a p i l a ( cim ) ; cim = aux ; } return ∗ this ; // retorna una refer`encia a la pila que invoca el m`etode.
} 51 52 53 54 55 56 57 58 void p i l a : : a p i l a r ( int x ) { node∗ n = new node ; n−>i n f o = x ; n−>seg = cim ; // connecta el nou node amb el primer node de la pila i // fa que aquest sigui el cim cim = n ; } 59 60 61 62 63 64 65 66 67 void p i l a : : d e s a p i l a r ( ) { node∗ n = cim ; if ( cim ! = NULL) { cim = cim−>seg ; delete n ; } // faltaria tractar l’error de pila buida } 68 69 70 71 72 73 74 int p i l a : : cim ( ) const { if ( cim ! = NULL) { return cim−>i n f o ; } // faltaria tractar l’error de pila buida } 75 76 77 78 bool p i l a : : e s b u i d a ( ) const { return cim == NULL; } ✝ 2.8.3 Programa d’exemple que usa la classe pila Fitxer exemple_pila.cpp: 1 2 # include <iostream > # include " pila . hpp " 3 4 5 using namespace s t d ; ✆ 2.9. EXERCICI 6 7 8 31 int main ( ) { int e l ; pila p; 9 c i n >> e l ; while ( e l ! = 0 ) { // mentre es vagin introdu¨ınt enters p. apilar ( el ) ; // diferents de 0, apilar-los.
c i n >> e l ; } ` p i l a q = p ; // inicialitzacio´ per copia while ( not q . e s b u i d a ( ) ) { // fem un pal´ındrom en p.
p . a p i l a r ( q . cim ( ) ) ; q . desapilar ( ) ; } while ( not p . e s b u i d a ( ) ) { // imprimim i buidem p.
cout << p . cim ; ( ) << ’ ’ ; p . desapilar ( ) ; } cout << endl ; 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 } 2.9 Exercici L’objectiu e´ s reimplementar la classe cj_enters de l’exercici anterior, pero` sense la res` s’emprar`a una llista enllac¸ada din`atriccio´ sobre la mida m`axima del conjunt. Per aixo, mica per implementar-la. La declaracio´ de la classe en el fitxer cj_enters_din.hpp e´ s id`entica a la de cj_enters.hpp, exceptuant que ja no e´ s necess`aria (i per tant no hi e´ s) ` la constant cj_enters::MAX_SIZE i apareixen la constructora per copia, la destructora i ´ donat que els corresponents m`etodes d’ofici no son ´ adequats.
l’operador d’assignacio, Evidentment heu d’implementar aquests m`etodes.
´ els mateixos que a l’exercici anterior: Els passos a seguir son 1. trobeu una representacio´ adequada pels objectes de la classe i escriviu els atributs necessaris en la part private de cj_enters_din.hpp; 2. comenceu amb una implementacio´ trivial (p.e., cos buit) per tots els m`etodes en cj_enters_din.cpp, i despr´es implementeu i proveu els diferents m`etodes pau´ la constructora latinament. Els primers m`etodes que haur´ıeu d’implementar son (obvi!), inserta, conte i print.
3. un cop provats aquests m`etodes continueu implementant i provant tota la resta fins a tenir la classe feta.
2.9.1 cj_enters_din.hpp 32 ´ 2. MEMORIA ` ` SESSIO DINAMICA Un objecte de la classe cj_enters representa a un conjunt finit d’enters pero` del qual no se sap la mida.
#ifndef _CJ_ENTERS_HPP_ #define _CJ_ENTERS_HPP_ #include <iostream> using namespace std; class cj_enters { public: Constructora per defecte. Crea un conjunt buit.
cj_enters(); ` Tres grans: constructora per copia, destructora i operador d’assignacio´ cj_enters(const cj_enters& cj); ˜cj_enters(); cj_enters& operator=(const cj_enters& cj); Insereix l’enter e en el conjunt. Naturalment si e ja pertanyia al conjunt, el m`etode no fa res.
void insereix(int e); ´ interseccio´ i difer`encia de conjunts. Operen modificant el conjunt sobre el que s’aplica Unio, el m`etode, usant el segon conjunt com argument. P.e.: a.unir(b) fa que el nou valor d’a sigui ´ els valors originals dels objectes a i b.
A ∪ B, on A i B son void unir(const cj_enters& B); void intersectar(const cj_enters& B); void restar(const cj_enters& B); ´ interseccio´ i difer`encia de conjunts. Operen creant un nou conjunt sense modificar el conUnio, ´ la resta a la junt sobre el que s’aplica el m`etode. La suma de conjunts correspon a la unio, ´ difer`encia i el producte a la interseccio.
cj_enters operator+(const cj_enters& B) const; cj_enters operator*(const cj_enters& B) const; cj_enters operator-(const cj_enters& B) const; Retorna cert si i nom´es si e pertany al conjunt.
bool conte(int e) const; 2.9. EXERCICI 33 Retornen els elements m`axim i m´ınim del conjunt, respectivament. El seu comportament no est`a definit si el conjunt e´ s buit.
int max() const; int min() const; Retorna el nombre d’elements (la cardinalitat) del conjunt.
int card() const; Operadors relacionals. == retorna cert si i nom´es si els dos conjunts (el conjunt sobre el que aplica ´ diferents.
i B) contenen els mateixos elements; != retorna cert si i nom´es si els conjunts son bool operator==(const cj_enters& B) const; bool operator!=(const cj_enters& B) const; Imprimeix el conjunt d’enters, ordenats en ordre ascendent, sobre el canal de sortida os; el format e´ s [e1 e2 ...en ], e´ s a dir, amb car`acters espaiadors simples separant els elements i tancant la ¨ encia entre corxets.
sequ` void print(ostream& os) const; private: Els atributs i m`etodes privats van aqu´ı.
}; #endif 2.9.2 Activitats Si ja has implementat la classe cj_enters llavors tens tamb´e acabades totes les activitats.
Repassa-les per estar preparat per l’Activitat numero ´ 2. En cas contrari primer acaba d’implementar la classe cj_enters abans de continuar mirant les Activitats.
NOTA: En cada una de les diferents activitats cal que implementis tots els m`etodes addicionals que usis expl´ıcitament.
` 2.1 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode unir de la classe cj_enters. Cal que escriguis tots els ´ m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
void u n i r ( const c j e n t e r s& c j ) ; ` 2.2 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
´ 2. MEMORIA ` ` SESSIO DINAMICA 34 b) Implementa el m`etode intersectar de la classe cj_enters. Cal que escriguis ´ tots els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
void i n t e r s e c t a r ( const c j e n t e r s& c j ) ; ` 2.3 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode restar de la classe cj_enters. Cal que escriguis tots els ´ m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
void r e s t a r ( const c j e n t e r s& c j ) ; ` 2.4 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode operator+ de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
Pots implementar els m`etodes auxiliars que creguis necessaris.
c j e n t e r s operator +( const c j e n t e r s& c j ) const ; ` 2.5 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode operator* de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
c j e n t e r s operator ∗ ( const c j e n t e r s& c j ) const ; ` 2.6 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode operator- de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
c j e n t e r s operator −( const c j e n t e r s& c j ) const ; ` 2.7 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode operator== de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator ==( const c j e n t e r s& c j ) const ; ` 2.8 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode operator!= de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator ! = ( const c j e n t e r s& c j ) const ; 2.9. EXERCICI 35 ` 2.9 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
b) Implementa el m`etode operator= de la classe cj_enters. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
c j e n t e r s& operator =( const c j e n t e r s& c j ) ; ` 2.10 a) Escriu la representacio´ de la classe cj_enters usant memoria din`amica (llista enllac¸ada).
` b) Implementa el constructor per copia i el destructor de la classe cj_enters. Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva im´ plementacio.
c j e n t e r s ( const c j e n t e r s& c j ) ; ˜ cj enters ( ) ; 36 ´ 2. MEMORIA ` ` SESSIO DINAMICA B ´ muntatge i execucio´ en C++ Compilacio, Un compilador e´ s un programa d’ordinador que permet traduir un programa escrit ( un llenguatge d’alt nivell) a un altre llenguatge de programacio´ (normalment llenguatge m`aquina), generant un programa equivalent que l’ordinador pot entendre.
Aquesta eina permet al programador descon`eixer el llenguatge que utilitza l’ordinador i escriure en un llenguatge m´es universal i m´es proper a com pensa un e´ sser hum`a.
´ muntatge i execucio´ en En aquest cap´ıtol veure’m com e´ s el proc´es de compilacio, ´ C++, i diferents eines que ens poden ser utils en cadascuna d’aquestes etapes.
B.1 Compilacio´ separada i muntatge ` B.1.1 Compilacio´ i muntatge basic La compilacio´ e´ s el proc´es durant el qual es tradueixen les instruccions escrites en un determinat llenguatge de programacio´ a llenguatge m`aquina.
Per compilar programes en C++ utilitzarem el compilador GNU gcc, en concret utilitzarem l’ordre g++.
Per compilar un fitxer font per separat s’usa l’ordre g++ (el text en cursiva indica un argument) amb l’ opci´o -c : $ g++ -c nom fitxer.cpp Aquesta ordre produeix un fitxer objecte nom fitxer.o.
Per generar el fitxer executable cal muntar (“linkar”) un o m´es fitxers objectes. Nom´es cal posar els noms dels fitxers objecte (sense que importi l’ordre) darrera de g++: $ g++ nom fitxer1.o nom fitxer2.o . . .
Aquest proc´es genera el fitxer executable per defecte que s’anomena a.out. Si es vol que el fitxer executable tingui un nom diferent llavors s’ha d’usar l’ opci´o -o : 37 38 ´ MUNTATGE I EXECUCIO ´ EN C++ ` APENDIX B. COMPILACIO, $ g++ -o nom executable.e nom fitxer1.o nom fitxer2.o . . .
´ Tamb´e es pot compilar i muntar varis fitxers en una unica ordre: $ g++ -o nom executable.e f1.cpp f2.o . . .
´ utilitzades per un programa prinPer exemple, donades dues classes (a i b) que son cipal (prog) (veure la la representacio´ en la figura B.1) es podria generar l’executable d’aquesta forma: $ g++ -o prog.e a.o prog.cpp b.o Es genera un fitxer executable anomenat prog.e, que es forma a partir de la compilacio´ del fitxer pr.cpp i el fitxer objecte resultant es munta amb els fitxers a.o i ba.o. El fitxer intermig pr.o no es conserva.
` B.1.2 Compilacio´ i muntatge de classes generiques ` Els moduls que defineixen classes o funcions gen`eriques no es compilen MAI per separat. Una forma adequada d’organitzar el codi consisteix en escriure la classe en dos fitxers: • declaraci´o de la classe: en el fitxer capc¸alera amb extensio´ .hpp com estem acostumats.
• implementaci´o de la classe: en un fitxer que per conveni li donarem l’extensio´ .t.
Cal tenir en compte que el fitxer .hpp ha d’incloure al fitxer .t. Aix´ı doncs, si un programa usa a la classe gen`erica X llavors haur`a d’incloure el fitxer X.hpp (i indirectament inclour`a a X.t).
Es pot obtenir una comprovacio´ sint`actica de la classe gen`erica mitjanc¸ant la inclusio´ del fitxer .hpp (i per tant del fitxer .t) en un fitxer .cpp. Aquest fitxer nom´es cal que ´ tingui la l´ınia d’inclusio.
´ utilitzades per un programa prinPer exemple, donades dues classes a i b, que son cipal (prog) i la classe a e´ s gen`erica (veure la representacio´ en la figura B.2) es podria generar l’executable despr´es de fer: $ g++ -c prog.cpp $ g++ -c b.cpp $ g++ -o prog.e prog.o b.o Com es pot comprovar la classe a no s’hauria de compilar.
´ SEPARADA I MUNTATGE B.1. COMPILACIO Figura B.1: Compilacio´ i muntatge de les classes a i b, i el programa principal prog.
39 40 ´ MUNTATGE I EXECUCIO ´ EN C++ ` APENDIX B. COMPILACIO, Figura B.2: Compilacio´ i muntatge de les classes a (gen`erica) i b, i el programa principal prog.
´ SEPARADA I MUNTATGE B.1. COMPILACIO 41 B.1.3 Compilacio´ i muntatge amb biblioteques Molts programes nom´es utilitzen classes, m`etodes i funcions definides a la biblioteca est`andard (per exemple, string, iostream, list, . . . ). El muntador (o linker) sempre empra per defecte aquesta biblioteca.
Si volem emprar una altra biblioteca caldr`a indicar-ho expl´ıcitament en l’ordre de muntatge mitjanc¸ant l’ opci´o -l .
A Unix el conveni e´ s anomenar a les biblioteques libxxxx amb extensio´ .a o .so (depenent de si la biblioteca e´ s est`atica o din` amica. Despr´es de l’opcio´ -l es posa la part variable del nom de la biblioteca, e´ s a dir, xxxx.
¨ Per exemple, per usar les biblioteques libB1 i libB2 caldria executar la seguent ordre: $ g++ -o nom executable.e f1.o f2.o . . . -lB1 -lB2 B.1.4 Altres opcions Opcio´ -I Si necessitem utilitzar fitxers de capc¸alera (.hpp) que no es troben en el mateix directori on estem compilant o en un directori est`andard d’inclusio´ (per exemple, /usr/include) caldr`a utilitzar l’ opci´o -I . Posarem tantes opcions -I com directoris volguem afegir ´ a la llista de directoris d’inclusio.
$ g++ -c -I /home/users/adam/headers f1.cpp Per especificar un cam´ı amb l’opcio´ -I es pot usar el cam´ı absolut o el cam´ı relatiu.
Si l’ordre de l’exemple anterior s’estigu´es executant en el directori /home/users/eva/pract podr´ıem haver escrit: $ g++ -c -I ../../adam/headers f1.cpp Opcio´ -L ´ si necessitem usar una biblioteca que no estigui Un problema similar a l’anterior es dona en el directori en curs o en un directori est`andard (por exemple, /usr/lib). En aquest cas s’utilitza l’ opci´o -L per indicar el cam´ı.
Per exemple, suposem que volem utilitzar libB1.a que es troba a /home/users/esin.
Llavors escriurem: $ g++ -L /home/users/esin -o prog.e prog.cpp f1.cpp -LB1 Opcio´ -Wall Per aconseguir que el compilador verifiqui instruccions dubtoses i avisi del m`axim nombre possible de fonts d’error cal utilitzar l’ opci´o -Wall : $ g++ -c -Wall nom fitxer.cpp ´ MUNTATGE I EXECUCIO ´ EN C++ ` APENDIX B. COMPILACIO, 42 Opcio´ -g Si es vol utilitzar un debugger caldr`a afegir l’ opci´o -g al compilar: $ g++ -g -c -Wall fitx.cpp o si es crea directament l’executable (sense compilacio´ separada) llavors: $ g++ -g -Wall -o nom executable.e f1.cpp f2.cpp . . .
B.2 Make make e´ s un programa d’Unix que simplifica notablement el treball de compilacio´ i muntatge. A m´es a m´es, es pot instruir adequadament a make per tal que recompili nom´es aquells fitxers que facin falta.
El programa make utilitza un fitxer anomenat Makefile que s’ha de trobar en el mateix lloc on s’executa el make. Aquest programa t´e un argument, el target, que e´ s el que volem que es construeixi. Si escrivim make sense cap argument, el target e´ s el primer que aparegui en el Makefile.
Per veure el funcionament del make escriurem el Makefile per l’exemple de la figura B.1. Per complicar-ho una mica suposarem que els fitxers .hpp es troben en el directori: /home/users/yo/elsmeusincludes, i a m´es a m´es necessitem la biblioteca /home/users/yo/elsmeuslibs/libB1.a.
1 2 3 4 5 6 CC = g++ INCL = /home/ u s e r s /yo/ e l s m e u s i n c l u d e s COMPILE = $ (CC) −c −Wall −a n s i −I $ ( INCL ) LIBS = /home/ u s e r s /yo/ e l s m e u s l i b s LINK = $ (CC) −L $ ( LIBS ) OBJS = prog . o a . o b . o 7 8 9 10 11 12 13 14 15 prog . e : $ ( OBJS ) $ ( LIBS )/ l i b B 1 . a $ ( LINK ) −o prog . e $ ( OBJS ) −lB1 prog . o : prog . cpp $ ( INCL)/ a . hpp $ ( INCL)/ b . hpp $ (COMPILE) prog . cpp a . o : a . cpp $ ( INCL)/ a . hpp $ (COMPILE) a . cpp b . o : b . cpp $ ( INCL)/ b . hpp $ (COMPILE) b . cpp Les primeres sis l´ınies defineixen variables. Es poden declarar tantes variables com vulguem. Si X e´ s una variable, es pot accedir al seu valor mitjanc¸ant $(X). Despr´es de la declaracio´ de variables v´enen una s`erie de blocs de la forma: ´ B.3. EXECUCIO 43 t a r g e t : depend e` ncies ordre1 ordre2 ...
` Cada l´ınia en la que escrivim una ordre ha de comenc¸ar obligatoriament amb un tabulador.
´ llistes que indiquen quins elements intervenen directament en Les depend`encies son la construccio´ del target i poden canviar.
Per exemple, prog.e dep`en dels fitxers prog.o, a.o, b.o i tamb´e de libB1.a, per aixo` apareixen aquests fitxers en la llista de depend`encies. Sota la llista apareix l’ordre per generar prog.e a partir de les depend`encies. I el mateix passa amb la resta.
Es poden fer moltes altres coses mitjanc¸ant make. No nom´es facilita el proc´es de compilacio´ i muntatge.
Per exemple, si afegim aquestes l´ınies al final del Makefile: 1 2 clean : rm −r f ∗ . o ; rm prog . e cada cop que escrivim make clean esborrarem tots el fitxers .o i l’executable.
B.3 Execucio´ Per executar un programa nom´es cal escriure el nom de l’executable despr´es del prompt de Unix: $ nom executable Si el programa llegeix i escriu pels canals est`andard d’entrada (cin) i sortida (cout) es pot redirigir l’entrada per tal que vingui d’un fitxer i no del teclat, i la sortida per tal que s’escrigui en un fitxer enlloc de la pantalla. Per fer aixo` utilitzarem els operadors de redireccionament (< per l’entrada i > per la sortida) seguits del nom del fitxer corresponent. Es pot redireccionat nom´es l’entrada, nom´es la sortida, o ambdues: $ nom executable < fitxer entrada > fitxer sortida Es pot connectar la sortida est`andard d’un programa amb l’entrada est`andard d’un altre programa mitjanc¸ant una pipe. L’operador corresponent e´ s la barra vertical |.
$ nom executable1 | nom executable2 Molts programes escriuen els seus missatges d’error pel canal est`andard d’error (cerr) que normalment est`a associat a la pantalla. Si utilitzem l’operador > els missatges continuen apareixent per pantalla. Per redirigir el canal cerr s’utilitza l’operador >& seguit del nom del fitxer. Per exemple: $ g++ -c pr.cpp >& errores.txt 44 ´ MUNTATGE I EXECUCIO ´ EN C++ ` APENDIX B. COMPILACIO, 3 Excepcions i genericitat 3.1 Gestio´ d’errors ´ anomalies que succeixen durant l’execucio´ d’un programa i que imLes excepcions son pedeixen que continu¨ı la seva execucio´ normal. Aquestes anomalies poden ser provo` cades per errors de l’usuari (intentar obrir un fitxer inexistent, etc.), per errors logics (divisio´ per zero, un acc´es a una posicio´ inexistent del vector, etc.), o b´e errors del sistema o del maquinari.
El disseny de la gestio´ d’errors ha de realitzar-se posant molt de compte tant en el tractament normal com en l’erroni. En general, un disseny adequat consisteix en: ´ 1. Assumir que les precondicions d’una determinada funcio´ o m`etode public que estem implementant poden no complir-se i detectar qualsevol error en aquest sentit (llevat que per raons d’efici`encia convingui no fer la comprovacio´ corresponent).
2. Utilitzar altres funcions i m`etodes de la mateixa o d’altres classes assegurant-nos que es verifiquen les seves respectives precondicions i no provoquen cap error en la mesura del possible. No s’ha d’incloure codi per propagar o tractar errors que ´ correcte de les funcions, m`etodes, classes, no es produiran perqu`e hem fet un us etc.
Hi ha v`aries formes de tractar les excepcions: ` • Ignorar-les (obviament no e´ s la millor forma).
• Avortar el programa.
• Usar codis d’error.
• Usar assercions.
• Usar el mecanisme de tractament d’excepcions del llenguatge C++.
45 ´ 3. EXCEPCIONS I GENERICITAT SESSIO 46 3.1.1 Avortar el programa Acabar immediatament l’execucio´ del programa e´ s un tractament d’errors simple pero` que permet com a m´ınim evitar obtenir resultats incorrectes.
Per fer aixo` es pot utilitzar les funcions exit() o abort() que estan incloses a la biblioteca <cstdlib>.
✞ 1 ☎ # include <c s t d l i b > 2 3 4 5 6 7 8 9 10 int main ( ) { ...
if ( n <= 0 ) { c e r r << " Missatge d’error " << endl ; exit (1); } ...
} ✝ ✆ 3.1.2 Codis d’error Els codis d’error consisteixen en interrompre l’execucio´ d’una funcio´ per delegar el problema (mitjanc¸ant un codi d’error, possiblement amb informacio´ addicional) a la funcio´ que l’ha invocat. Aquesta funcio´ a la seva vegada tamb´e pot delegar el problema i aix´ı successivament.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int f ( ) { int re s , r e t ; res = f1 ( ) ; if ( r e s == OK) { res = f2 ( ) ; if ( r e s == OK) { r e t = OK; } else { r e t = ERROR2 ; } } else { r e t = ERROR1 ; } return r e t ; } Els codis d’error obliguen a extendre la signatura de les funcions (a vegades retornant un codi d’error o afegint un par`ametre extra d’entrada i sortida) per poder passar la informacio´ sobre l’error entre les funcions. A m´es cal fer un tractament dels errors de manera interna en les funcions afegint m´es complexitat.
3.2. EXCEPCIONS EN C++ 47 3.1.3 Assercions La instruccio´ assert() (de fet e´ s una macro que est`a a la biblioteca <cassert>) comprova una condicio´ donada, interrompent l’execucio´ del programa i imprimint un missatge d’error si la condicio´ no es compleix.
✞ ☎ assert (n > 0); ✝ // si n <= 0 crida a abort() ✆ Les assercions donen escasa informacio´ sobre els tipus d’error que s’han produ¨ıt: • No permeten corregir situacions d’error.
• No alliberen els recursos compartits.
• Avorten l’execucio´ del programa.
Pero` per altra banda, com a m´ınim permeten evitar l’obtencio´ de resultats incorrectes. En general, s’utilitzen per la comprovacio´ de precondicions i postcondicions.
3.2 Excepcions en C++ Una excepcio´ e´ s un objecte que pot ser llanc¸at, per m´es tard ser capturat i tractat. El llenguatge C++ incorpora un mecanisme unificat de tractament d’excepcions. Permet tractar les excepcions com objectes d’una classe determinada.
El mecanisme d’excepcions e´ s molt convenient ja que permet dividir el programa en seccions separades per tractar les situacions normals i excepcionals.
´ D’ERRORS FASES DE LA GESTIO ` La gestio´ d’una situacio´ anomala en un programa t´e tres fases: de´ propagacio´ i tractament. Un cop s’hagi detectat un error en teccio, un punt del programa s’ha d’indicar la pres`encia del mateix generant ´ Aquesta informacio´ s’ha de fer arribar (propagar) fins el una excepcio.
punt en que es procedir`a a actuar (tractament).
3.2.1 Deteccio´ i generacio´ d’una excepcio´ Una vegada hem detectat la situacio´ d’error farem servir la instruccio´ throw expressi´o per ´ generar una excepcio´ llanc¸ant un objecte de la classe que avalua l’expressio.
✞ 1 2 ☎ ´ // Llanc¸a una excepcio´ de tipus enter. En concret el numero 15.
throw 1 5 ; 3 4 5 6 // Llanc¸a una excepcio´ de tipus string.
// En concret el missatge ”Error del Programa”.
throw " Error del Programa " ; ✝ ✆ ´ 3. EXCEPCIONS I GENERICITAT SESSIO 48 Aquesta instruccio´ s’ha d’executar: • Dins d’un bloc try, • O b´e dins d’una funcio´ que hagi estat invocada dins d’un bloc try.
• O dins d’una funcio´ que invoca a una funcio´ que ha estat invocada dins un bloc try, • etc.
´ L’excepcio´ “salta” imEl flux de l’execucio´ s’interromp tan aviat es llanc¸a l’excepcio.
mediatament al final de la funcio´ on es produeix, d’all`a a la funcio´ que va fer la crida, i ¨ encia de crides fins que troba el codi disposat a capturar d’all`a va saltant desfent la sequ` ´ a dir, la sequ` ¨ encia de “salts” acaba quan l’exi tractar l’excepcio´ que hem generat. Es cepcio´ arriba a un bloc try. Cada cop que es desfa una crida s’invoquen els destructors apropiats (es destrueixen totes les variables locals).
3.2.2 Tractament de les excepcions Un bloc try { ... } cont´e instruccions que poden ocasionar la generacio´ d’una excepcio´ que es vol controlar.
Un bloc catch (tipus) { ... } realitza el tractament de les excepcions del mateix tipus que el tipus indicat. Aquest bloc ha d’anar immediatament a continuacio´ d’un bloc try.
✞ 1 2 3 4 5 6 7 8 9 try { // bloc d’instruccions cr´ıtiques } catch ( t i p u s e x c e p c i o´ 1 var1 ) { // gestor 1 } catch ( t i p u s e x c e p c i o´ 2 var2 ) { ...
} ✝ ☎ ✆ Si entre els par`entesis posem tres punts llavors aquest bloc catch capturar`a qualsevol ´ sigui del tipus que sigui.
tipus d’excepcio, 1 2 3 4 5 6 7 8 try { // codi que pot generar excepcions de la classes el_meu_error i d’altres.
...
} catch ( e l m e u e r r o r e ) { // codi de tractament per excepcions de la classe el_meu_error; // dins d’aquest bloc l’excepcio´ pren el nom e.
...
3.2. EXCEPCIONS EN C++ 9 10 11 12 13 14 15 16 17 18 19 49 } catch ( s t d : : r a n g e e r r o r ) { c e r r << " fora de rang " << endl ; } catch ( s t d : : b a d a l l o c ) { c e r r << "no hi ha mem` o ria disponible " << endl ; } catch ( . . . ) { ´ // codi de tractament per qualsevol altra classe d’excepcio.
...
} Si una excepcio´ que ha estat llanc¸ada no troba un gestor apropiat, es cridar`a a la funcio´ terminate(), la qual per defecte realitza un abort().
Una excepcio´ pot ser capturada per un catch, tractada i rellanc¸ada per ser recollida per un bloc try m´es extern. Per rellanc¸ar una excepcio´ nom´es cal cridar a la instruccio´ throw sense passar-li cap par`ametre.
1 2 3 4 5 6 7 8 9 10 void f ( int x ) { try { ...
} catch ( e r r o r e ) { . . . // es tracta parcialment l’error throw ; // rellancem l’error } ...
} 11 12 13 14 15 16 17 18 19 20 21 int main ( ) { try { ...
f (x ); ...
} catch ( e r r o r e ) { ...
} } El que no s’ha de fer mai e´ s recollir una excepcio´ per a continuacio´ sense fer cap tractament rellanc¸ar-la. Per qu`e simplement no es deixa que es propagui l’excepcio´ ella sola? 1 2 void f ( int x ) { ...
´ 3. EXCEPCIONS I GENERICITAT SESSIO 50 try { g(y ) ; } catch ( e r r o r e ) { throw ; // rellancem l’error. REDUNDANT!! } ...
3 4 5 6 7 8 9 10 } 11 12 13 14 15 16 17 18 19 20 21 int main ( ) { try { ...
f (x ); ...
} catch ( e r r o r e ) { ...
} } 3.2.3 Propagar una excepcio´ ´ d’especificacions d’excepcions. Les esL’est`andard ANSI C++ permet, i recomana, l’us pecificacions d’excepcions permeten incloure a la capc¸alera de cada m`etode quin tipus d’excepcio´ pot generar o propagar aquest m`etode. Per fer aixo` usarem la instruccio´ throw al final de la capc¸alera del m`etode.
✞ 1 2 ☎ // L’accio´ proc1 pot propagar excepcions sols de tipus int.
void proc1 ( int a ) throw ( int ) { . . . } 3 4 5 // La funcio´ proc2 pot propagar excepcions de tipus int i char.
int proc2 ( int a ) throw ( int , char ) { . . . } 6 7 8 ´ // La funcio´ proc3 NO pot propagar cap tipus d’excepcio.
int proc3 ( int a ) throw ( ) const { . . . } ✝ ✆ L’abs`encia d’una especificacio´ d’excepcions significa que la funcio´ pot llanc¸ar o pro´ Aquest conveni pot semblar una mica estrany pero` obeeix a pagar qualsevol excepcio.
la compatibilitat amb versions anteriors de C++.
✞ 1 2 ´ // L’accio´ proc4 pot propagar qualsevol tipus d’excepcio.
void proc4 ( int a ) { . . . } ✝ ☎ ✆ Cal recordar que l’especificacio´ d’excepcions serveix per indicar tant la generacio´ ´ Dins del codi d’una funcio´ no cal que hi hagi forc¸osament un throw, com la propagacio.
potser qui genera l’excepcio´ e´ s una altra funcio´ que es crida dins.
3.3. CLASSE ERROR ✞ 1 2 3 51 ☎ void proc5 ( int a ) throw ( int ) { proc1 ( a ) ; } ✝ ✆ 3.3 Classe error Una de les classes que s’utilitzen en tots els exercicis i exemples de l’assignatura e´ s la classe error. Per tal de simplificar la gestio´ d’errors tots els m`etodes i funcions de qualsevol classe que poden provocar una excepcio´ llancen errors (exceptuant les classes est`andard com ara list, vector o string). Un objecte de la classe error e´ s una tupla ` amb dos strings (el nom del modul o classe on es produeix l’error i el missatge d’error) i un enter (el codi d’error).
La classe ofereix les operacions: • modul, mensaje i codigo: per consultar cadascun d’aquests camps per separat.
• print: per mostrar la informacio´ de l’error per l’ostream indicat.
• operator<<: per facilitar mostrar informacio´ de l’error.
` ´ de la classe error ha d’estar lliure d’excepcions ja que d’una altra Obviament, l’us manera entrar´ıem en un bucle sense fi.
En els exercicis de programacio´ de l’assignatura per provar ´ proles vostres classes disposeu de drivers. Els drivers son grames principals “avanc¸ats” que permeten testejar les classes de manera m´es acurada. Els drivers internament utilitzen la classe gen_driver i carreguen a l’inici un fitxer amb ` tots els errors poden crear-se indicant els errors. Per aixo, unicament ´ el seu codi.
` Tant el nom del modul o classe com el missatge d’error poden deixar-se buits si s’utilitza la classe gen_driver i es carrega inicialment un fitxer amb els errors. El nom del ` modul i el missatge associat s’obtindran de la informacio´ d’aquest fitxer d’errors. Una avantatge clar e´ s que aix´ı podem modificar amb molta m´es facilitat els missatges d’error (per exemple, per produir-los en diferents idiomes). En els exercicis de l’assignatura se us proporcionar`a el fitxer d’errors a usar. Per exemple: 1 2 3 4 // programa principal ...
g e n d r i v e r dr ( " fitxer_errors . txt " ) ; ...
´ 3. EXCEPCIONS I GENERICITAT SESSIO 52 1 2 3 4 // pila.cpp if ( . . . ) { throw e r r o r ( P i l a B u i d a ) ; } 5 6 7 8 9 1 2 3 4 5 6 // si el fitxer d’errors no estigu´es carregat haur´ıem de fer: if ( . . . ) { throw e r r o r ( PilaBuida , " pila " , "La pila ´ e s buida " ) ; } // fitxer errors.txt ...
21 l l i s t a Element i n e x i s t e n t 31 p i l a La p i l a e´ s buida 32 p i l a La p i l a e´ s plena ...
` La captura i tractament propiament dit dels errors es far`a en el modul principal, en aquells exercicis on se us demani. Les classes b`asicament no tractaran els errors, nom´es els llanc¸aran o els propagaran. En els exercicis sempre s’utilitzar`a el conveni d’imprimir la informacio´ dels errors pel canal de sortida est`andard (cout) i no pel d’error (cerr).
Per tal de no utilitzar directament els codis d’error e´ s interessant usar constants ` simboliques per gestionar els errors de cada classe.
1 2 3 // pila.hpp # include <s t r i n g > # include <e s i n /error> 4 5 6 7 8 class p i l a { public : ` // constants simboliques per gestionar els errors static const char nom mod [ ] = " pila " ; 9 static const int P i l a B u i d a = 3 1 ; static const int P i l a P l e n a = 3 2 ; 10 11 12 ...
p i l a ( int max elems = 1 0 ) ; ...
13 14 15 16 1 2 3 4 }; // pila.cpp # include " pila . hpp " ...
void p i l a : : a p i l a r ( int x ) throw ( e r r o r ) { 3.4. GENERICITAT if ( cim == max elems ) { throw e r r o r ( P i l a P l e n a ) ; } ...
5 6 7 8 9 53 } ´ D’ERRORS CONVENI DE PROPAGACIO Un conveni especial que s’usar`a als exercicis de l’assignatura e´ s el que fa refer`encia al comportament en cas de que es produeixin varis errors simult`aniament. La regla e´ s simple: si es una invocacio´ concreta d’una funcio´ es produeixen varis errors simult`aniament, l’excepcio´ llanc¸ada o propagada ha de ser aquella que tingui el codi d’error menor.
3.4 Genericitat ´ S’ent`en per programaci´o gen`erica un estil de programacio´ en la que els algorismes son el m´es independents possibles dels tipus de dades que els operen. La programacio´ gen`erica en C++ t´e el seu fonament en les plantilles (angl`es: templates) i en l’explotacio´ sistem`atica del concepte d’iterador.
L’aplicacio´ m´es visible d’aquestes t`ecniques e´ s la potent Standard Template Library ¨ ´ (STL), que presentarem en la seguent sessio.
Un template permet escriure una funcio´ o una classe en la que intervenen tipus no especificats, que en ser usada s’instanciar`a amb els tipus adequats.
3.4.1 Plantilles de funcions ´ Ara veurem un parell d’exemples de com fer una plantilla d’una funcio.
En el primer exemple, es vol implementar una funcio´ que calculi el m`axim per diferents tipus: 1 2 3 4 // m`axim de dos enters int max ( int a , int b ) { return a > b ? a : b ; } 5 6 7 8 9 // m`axim de dos reals float max ( float a , float b ) { return a > b ? a : b ; } 10 11 12 13 14 // m`axim de dos car`acters char max ( char a , char b ) { return a > b ? a : b ; } ´ 3. EXCEPCIONS I GENERICITAT SESSIO 54 ` Com es pot veure el codi de les diferents funcions e´ s exactament el mateix . Per aixo, per calcular el m`axim de qualsevol parell de dades de tipus T es pot usar templates: 1 2 3 4 5 // plantilla m`axim de dos elements template<typename T> T max ( T a , T b ) { return a > b ? a : b ; } ` seria millor passar els par`ametres per refer`encia per evitar les copies ` Pero, dels objectes en el pas de par`ametres. Tenint en compte aixo` un possible programa principal que calculi el m`axim de diferents tipus: 1 2 3 4 5 // plantilla m`axim de dos elements template<typename T> T max ( const T& a , const T& b ) { return a > b ? a : b ; } 6 7 8 9 int main ( ) { double d1 = 3 . 5 , d2 = 4 . 2 ; cout << max ( d1 , d2 ) ; 10 int i 1 = 3 , i 2 = 1 ; cout << max ( i 1 , i 2 ) ; 11 12 13 ...
14 15 } La funcio´ gen`erica max es pot aplicar sobre qualsevol tipus que tingui definit l’operador >.
Un segon exemple de plantilla de funcio´ e´ s una funcio´ que implementa l’algorisme d’insercio´ ordenada.
1 2 3 4 5 6 7 8 9 10 11 12 13 // plantilla m`axim de dos elements template<typename T> void i n s e r t i o n s o r t ( T a [ ] , int n ) { for ( int j = 1 ; j < n ; ++ j ) { T key = a [ j ] ; int i = j −1; while ( i >= 0 and a [ i ] > key ) { a [ i +1] = a [ i ] ; −−i ; } a [ i +1] = key ; } } 3.4. GENERICITAT 55 Per poder aplicar aquesta plantilla sobre un tipus T, cal que T tingui disponibles les ¨ operacions seguents: ` • Constructor per copia: T (const T&) ´ T& operator=(const T&) • Assignacio: ´ bool operator>(const T&) • Comparacio: 3.4.2 Plantilles de classes Definir una classe gen`erica mitjanc¸ant templates e´ s tan simple com definir una altra classe, encara que la sintaxi e´ s un p`el m´es molesta. Per exemple, si es vol implementar una classe gen`erica de piles d’elements definirem la classe pila<Elem> on Elem e´ s el par`ametre “formal” que de la classe gen`erica. Aquest par`ametre despr´es s’instanciar`a amb el tipus que necessitem en cada moment.
1 2 3 4 5 // pila.hpp template <class Elem> class p i l a { public : pila ( ) ; 6 p i l a ( const p i l a& p ) ; p i l a& operator =( const p i l a &p ) ; ˜ pila ( ) ; ...
void a p i l a r ( const Elem& x ) ; ...
private : struct node { // En realitat seria node¡Elem¿ node∗ seg ; Elem i n f o ; }; node∗ cim ; int nelems ; 7 8 9 10 11 12 13 14 15 16 17 18 19 20 }; La primera l´ınia (template ...) indica que en la declaracio´ de la classe cada aparicio´ d’Elem e´ s en realitat un par`ametre formal que ser`a substitu¨ıt per un tipus real a l’instanciar-se la classe. Un exemple d’instancio´ de classe seria: 1 2 3 4 5 # include " pila . hpp " // defineix la classe gen`erica pila<Elem> ...
p i l a <char> p ; // p e´ s una pila de car`acters p i l a <s t r i n g > q ; // q e´ s una pila d’strings p i l a <int∗> r ; // r e´ s una pila de punters a int ´ 3. EXCEPCIONS I GENERICITAT SESSIO 56 6 7 8 9 10 // s e´ s una pila de piles d’enters.
// l’espai entre <int> i > e´ s imprescindible per tal que el compilador // no ho confongui amb l’operador >>.
p i l a <p i l a <int> > s ; Donat que el tipus a instanciar, Elem, es desconeix a priori s’hauria d’evitar usar par`ametres o retorns per valor que podrien ser molt costosos si Elem no fos un tipus simple. En el seu lloc cal usar pas o retorns per refer`encia constant.
La implementacio´ d’una classe gen`erica segueix la mateixa pauta: cal anteposar la declaracio´ template ... en la definicio´ de cada m`etode: 1 2 3 4 5 template <class Elem> p i l a <Elem > : : p i l a ( ) { cim = NULL; nelems = 0 ; } 6 7 8 9 10 11 12 13 14 template <class Elem> void p i l a <Elem > : : a p i l a r ( const Elem& x ) { node∗ p = new node ; p −> seg = cim ; p −> i n f o = x ; cim = p ; ++ nelems ; } 15 16 ...
Hi ha dos punts interessants a comentar. Per un costat, el nom de la classe no e´ s pila, sino´ pila<Elem>. Per aixo` hem de definir pila<Elem>::apilar i no pila::apilar. Un cop ja hem donat la “pista” al compilador de que estem definint un m`etode de la classe pila<Elem> ja podem ometre en la resta <Elem> i escriure, per exemple: template <class Elem> p i l a <Elem>& p i l a <Elem > : : operator =( const p i l a& p ) { ...
} en comptes de: 3.4. GENERICITAT 57 template <class Elem> p i l a <Elem>& p i l a <Elem > : : operator =( const p i l a <Elem>& p ) { ...
} Per una altra banda, s’ha d’estar pendent de les suposicions que es fan al respecte sobre el tipus Elem. Per exemple, la constructora per defecte (d’ofici) per la classe node cridar`a a la constructora per defecte d’Elem per l’atribut info. Aix´ı doncs, les classes amb que instaciem Elem han de tenir constructora per defecte. Recordeu que si definim una constructora amb par`ametres per una classe, llavors no hi haur`a constructora per defecte per aquesta classe. An`alogament, al destruir un node s’invocar`a la destructora d’Elem i per tant e´ s necessari que existeixi. Finalment, en l’assignacio´ p -> info = x; que es fa a apilar, estem usant l’operador d’assignacio´ entre Elems, aix´ı doncs aquest tamb´e ha d’estar definit. En altres casos caldr`a que s’hagi definit la igualtat, els operadors de ´ el constructor per copia, ` comparacio, etc.
´ D’UNA CLASSE GENERICA ` IMPLEMENTACIO Cal remarcar que les classes gen`eriques en C++ (template) tenen la particularitat que la implementacio´ no es posa en el fitxer .cpp sino´ en un altre fitxer que anomenarem .t. Aixo` ho fem per indicar que el fitxer que cont´e la implementacio´ no s’ha de compilar per separat i que s’estan usant templates. Recordeu que en el cas de les classes gen`eriques el fitxer que cont´e la implementacio´ no inclou MAI al fitxer capc¸alera (.hpp).
Un problema del mecanisme de templates e´ s que actualment no hi ha cap compilador de C++ que admeti la seva compilacio´ separada, per tant el codi que implementa una classe o funcio´ gen`erica ha d’estar en la mateixa unitat de compilacio´ que el codi que instancia i usa el codi gen`eric.
Per tal de “simular” el model de separacio´ d’especificacio´ i implementacio´ que hem ¨ ` utilitzat fins ara amb les classes no gen`eriques adoptarem el conveni seguent: el modul que usa la classe gen`erica inclou al fitxer .hpp corresponent; el fitxer .hpp inclou al fitxer que cont´e la implementacio´ de la classe i/o funcions gen`eriques. El fitxer que cont´e la implementacio´ de la classe gen`erica no s’ha de compilar per separat, per aixo` li donarem l’extensio´ .t en comptes de l’habital .cpp Per obtenir m´es informacio´ sobre la compilacio´ de les classes gen`eriques veure la seccio´ B.1.2.
1 2 3 4 5 // programa que usa la classe gen`erica pila<Elem> # include " pila . hpp " ...
p i l a <int> p ; ...
´ 3. EXCEPCIONS I GENERICITAT SESSIO 58 1 2 3 4 5 // pila.hpp template <class Elem> class p i l a { public : pila ( ) ; 6 7 8 9 10 11 12 13 14 1 p i l a ( const p i l a& p ) ; p i l a& operator =( const p i l a &p ) ; ˜ pila ( ) ; ...
private : ...
}; # include " pila .t" // pila.t 2 3 4 // No pot apar`eixer #include “pila.hpp” ja que sino´ tindr´ıem un bucle infinit // d’inclusions.
5 6 7 8 9 10 11 template <class Elem> p i l a <Elem > : : p i l a ( ) { cim = NULL; nelems = 0 ; } ...
3.5 Exercici En aquest exercici desenvolupareu la classe conjunt<T>, e´ s a dir, una classe gen`erica on el tipus dels elements del conjunt pot ser qualsevol. S’assumeix que el tipus T t´e definit ` el constructora per copia, el destructora i l’operador <. Tamb´e suposarem que existeix l’operador << per imprimir objectes de la classe T sobre un canal de sortida, per exemple, cout.
En general n’hi haur`a prou amb modificar lleugerament la implementacio´ realitzada en l’exercici anterior per tal que en comptes de gestionar enters es manegin T’s. S’ha canviat l’especificacio´ d’algunes operacions per tal que els par`ametres d’entrada siguin const T& ja que en general ser`a m´es eficient el pas d’una refer`encia constant que un pas per valor (quan es tractava de int’s aixo` no era rellevant). Tamb´e s’han afegit especificacions d’error; en general, diversos m`etodes declaren que propaguen excepcions de ´ ` la classe error perqu`e pot ocorrer que no hi hagi prou memoria din`amica (i llavors new ´ llanc¸a una excepcio).
La implementacio´ s’ha d’escriure en un fitxer conjunto.t que s’inclou a trav´es del fitxer conjunt.hpp.
3.5. EXERCICI 59 Proveu la classe gen`erica sustituint cj_enters per conjunt<int> en els programes d’exemple d’exercicis anteriors. Proveu el funcionament amb conjunt<string> (cal posar #include <string> per poder utilitzar la classe string en el programa).
En el montatge (link) s’ha d’utilitzar la biblioteca libesin: g++ -o executable fich1.o ... -lesin ALERTA!! Llegir amb atencio´ l’explicacio´ inicial de la classe al respecte dels costos m`axims de les operacions.
3.5.1 conjunt.hpp Un objecte de la classe conjunt representa a un conjunt finit de T’s pero` del qual no se sap la ` mida. S’assumeix que la classe T t´e destructora, constructora per copia i l’operador <. Tamb´e s’assumeix que est`a definit l’operador << que permet escriure un T en un ostream.
´ els m`etodes insereix i conte han de tenir cost menor o igual que n (sent n el Implementacio: nombre d’elements del que t´e el conjunt) en el cas mig, els m`etodes max i min han de tenir cost constant i la resta de m`etodes han de tenir cost menor que n2 .
#ifndef _CONJUNT_HPP_ #define _CONJUNT_HPP_ #include <iostream> #include <esin/error> using namespace std; template <class T> class conjunt { public: Constructora per defecte. Crea un conjunt buit.
conjunt() throw(error); ` Tres grans: constructora per copia, destructora i operador d’assignacio´ conjunt(const conjunt& cj) throw(error); conjunt& operator=(const conjunt& cj) throw(error); ˜conjunt() throw(); Insereix l’enter e en el conjunt. Naturalment si e ja pertanyia al conjunt, el m`etode no fa res.
void insereix(const T& x) throw(error); ´ interseccio´ i difer`encia de conjunts. Operen modificant el conjunt sobre el que s’aplica Unio, el m`etode, usant el segon conjunt com argument. P.e.: a.unir(b) fa que el nou valor d’a sigui ´ els valors originals dels objectes a i b.
A ∪ B, on A i B son ´ 3. EXCEPCIONS I GENERICITAT SESSIO 60 void unir(const conjunt& B) throw(error); void intersectar(const conjunt& B) throw(error); void restar(const conjunt& B) throw(error); ´ interseccio´ i difer`encia de conjunts. Operen creant un nou conjunt sense modificar el conUnio, ´ la resta a la junt sobre el que s’aplica el m`etode. La suma de conjunts correspon a la unio, ´ difer`encia i el producte a la interseccio.
conjunt operator+(const conjunt& B) const throw(error); conjunt operator*(const conjunt& B) const throw(error); conjunt operator-(const conjunt& B) const throw(error); Retorna cert si i nom´es si e pertany al conjunt.
bool conte(const T& x) const throw(); Retornen els elements m`axim i m´ınim del conjunt, respectivament. Es llanc¸a una excepcio´ de la classe error (definida en <esin/error>) si el conjunt e´ s buit.
T max() const throw(error); T min() const throw(error); Retorna el nombre d’elements (la cardinalitat) del conjunt.
int card() const throw(); Operadors relacionals. == retorna cert si i nom´es si els dos conjunts (el conjunt sobre el que aplica ´ diferents.
i B) contenen els mateixos elements; != retorna cert si i nom´es si els conjunts son bool operator==(const conjunt& B) const throw(); bool operator!=(const conjunt& B) const throw(); Imprimeix el conjunt d’enters, ordenats en ordre ascendent, sobre el canal de sortida os; el format e´ s [e1 e2 ...en ], e´ s a dir, amb car`acters espaiadors simples separant els elements i tancant la ¨ encia entre corxets.
sequ` void print(ostream& os) const throw(); Gestio´ d’errors static const int NoMinMaxEnConjBuit = 10; private: Els atributs i m`etodes privats van aqu´ı.
3.5. EXERCICI 61 }; #include "conjunt.t" #endif 3.5.2 Activitats Si ja has implementat la classe conjunt llavors tens tamb´e acabades totes les activitats.
Repassa-les per estar preparat per l’Activitat numero ´ 3. En cas contrari primer acaba d’implementar la classe conjunt abans de continuar mirant les Activitats.
NOTA: En cada una de les diferents activitats ` 3.1 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode unir de la classe conjunt amb cost menor que Θ(n2 ). Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva im´ plementacio.
void u n i r ( const c o n j u n t& c j ) throw ( e r r o r ) ; ` 3.2 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode intersectar de la classe conjunt amb cost menor que Θ(n2 ). Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en ´ la teva implementacio.
void i n t e r s e c t a r ( const c o n j u n t& c j ) throw ( e r r o r ) ; ` 3.3 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode restar de la classe conjunt amb cost menor que Θ(n2 ).
Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva ´ implementacio.
void r e s t a r ( const c o n j u n t& c j ) throw ( e r r o r ) ; ` 3.4 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode operator+ de la classe conjunt amb cost menor que Θ(n2 ).
Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva ´ implementacio.
c o n j u n t operator +( const c o n j u n t& c j ) const throw ( e r r o r ) ; ` 3.5 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode operator* de la classe conjunt amb cost menor que Θ(n2 ).
Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva ´ implementacio.
c o n j u n t operator ∗ ( const c o n j u n t& c j ) const throw ( e r r o r ) ; ` 3.6 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
62 ´ 3. EXCEPCIONS I GENERICITAT SESSIO b) Implementa el m`etode operator- de la classe conjunt amb cost menor que Θ(n2 ).
Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva ´ implementacio.
c o n j u n t operator −( const c o n j u n t& c j ) const throw ( e r r o r ) ; ` 3.7 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode operator== de la classe conjunt amb cost menor que Θ(n2 ). Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en ´ la teva implementacio.
bool operator ==( const c o n j u n t& c j ) const throw ( ) ; ` 3.8 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode operator!= de la classe conjunt amb cost menor que Θ(n2 ). Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en ´ la teva implementacio.
bool operator ! = ( const c o n j u n t& c j ) const throw ( ) ; ` 3.9 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
b) Implementa el m`etode operator= de la classe conjunt amb cost menor que Θ(n2 ).
Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva ´ implementacio.
c o n j u n t& operator =( const c o n j u n t& c j ) throw ( e r r o r ) ; ` 3.10 a) Escriu la representacio´ de la classe conjunt usant memoria din`amica.
` b) Implementa el constructor per copia i el destructor de la classe conjunt amb 2 cost menor que Θ(n ). Cal que escriguis tots els m`etodes addicionals que usis ´ expl´ıcitament en la teva implementacio.
c o n j u n t ( const c o n j u n t& c j ) throw ( e r r o r ) ; ˜ c o n j u n t ( ) throw ( ) ; C Estil de programacio´ i documentacio´ ´ dos Un estil de programacio´ consistent i una documentacio´ clara, concisa i precisa son elements claus d’un bon programa. La deteccio´ d’errors o la modificacio´ d’un programa mal documentat i sense una m´ınima coher`encia (noms dels identificadors, sagnat1 , estructuracio´ dels components del programa, etc.) e´ s una tasca gaireb´e impossible. A continuacio´ us donem una s`erie de recomenacions sobre aquests dos aspectes.
C.1 Noms de variables adequats Useu noms descriptius per constants, classes i funcions visibles en diferents punts del ´ programa, i noms breus per les variables locals o els par`ametres formals d’una funcio.
Per exemple, e´ s inapropiat cridar a un m`etode f o a una classe X (menys en el cas que ´ Els seus identifies vulgui il·lustrar una caracter´ıstica del llenguatge de programacio!).
` i per tant solen ser llargs i estar compostos per cadors han de descriure el seu proposit v`aries paraules: copia pila, insereix, SopaLletres, . . . . En canvi, per un par`ametre formal o una variable local l’identificador n e´ s adequat, npuntos acceptable i numero de punts e´ s un “cano´ per matar mosques”.
Si una variable local s’utilitzar`a d’una manera convencional podem donar-li un identificador m´ınim: i, j i k s’acostumen a usar pels ´ındexos de bucles, p i q per punters, s i t per strings, etc. Per exemple: 1 2 3 4 5 6 7 void i n i c i a t a u l a ( elem t a u l a e l e m s [ ] , int n r e l em s ) { int i n d i c e e l e m ; for ( i n d i c e e l e m = 0 ; i n d i c e e l e m < n r e l em s ; i n d i c e e l e m ++) { taula elems [ indice elem ] = indice elem ; } } no es m´es comprensible que 1 ´ “Indentacio”.
63 ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 64 1 2 3 4 5 void i n i c i a t a u l a ( elem a [ ] , int n ) { for ( int i = 0 ; i < n ; i ++) { a[ i ] = i ; } } C.2 Conveni consistent pels identificadors “Inventeu” un esquema per expressar els identificadors i apliqueu-lo sistem`aticament ´ i consistent. Per exemple, sol recomenar-se utilitzar noms en majuscules per constants (p.e., ELEM SIZE, MAX ELEMS). Un conveni que hem utilitzat en aquest document i ´ ´ en el material de l’assignatura e´ s el de combinar majuscules i minuscules pels identificadors de codis i missatges d’error (p.e., NoSolReal, PilaPlena). En general, les connectives ´ (p.e., articles i preposicions) es deixen en minuscules.
Alguns programadors utilitzen el conveni descrit per tots els seus identificadors de funcions, classes, i altres elements glo´ bals: class Pila { . . . }, CopiaPila, elCim, unNode, . . . Altres prefereixen utilitzar minuscules i separar les paraules mitjanc¸ant el s´ımbol de subratllat: copia pila, el cim, . . . Un conveni que tamb´e t´e molts adeptes e´ s el d’anteposar el car`acter de subratllat als elements privats d’una classe: 1 2 3 4 5 6 7 8 template <typename T> class p i l a { ...
private : int cim ; T cont ; int max elems ; }; C.3 Utilitzar noms en forma activa per les funcions Es recomana usar verbs en veu activa pels m`etodes i les funcions, i reservar adjectius o noms de la forma es . . . per funcions o m`etodes el resultado dels quals e´ s un boole`a.
Conv´e pensar els identificadors des del punt de vista de l’usuari, no de l’implementador i tener en compte que els m`etodes s’apliquen sobre objectes. Per exemple, compareu: 1 2 3 amb conjunt c ; if ( c . pertany ( x ) ) ...
C.4. NOM D’IDENTIFICADORS PRECISOS 1 2 3 65 conjunt c ; if ( c . c o n t e ( x ) ) ...
El conveni o esquema de noms utilitzat ha de basar-se en el que les entitats represen´ desaconsellables per tant els convenis basats en una ten, no a com ho representen. Son ´ com per exemple anteposar el caracter´ıstica de baix nivell o lligada a la implementacio, prefix i a les variables i atributs de tipus enter i el prefix f a les variables i atributs de tipus real (float).
Sobretot, e´ s important ser consistent: al principi pot alentir una mica el treball haver de recordar els convenis que hem triat; pero` despr´es ho farem quasi sense pensar.
Acabem aquest punt amb un exemple d’inconsist`encia caricaturitzat, pero` indicatiu de la import`ancia que t´e aquest aspecte de l’estil: 1 2 3 4 5 6 class P i l a { public : P i l a ( int max elements ) ; ˜ Pila ( ) ; P i l a ( const P i l a& s ) ; P i l a& operator =( const P i l a& l a p i l a ) ; 7 8 9 10 11 void A p i l a r ( int element ) ; int d e s a p i l a ( ) ; int Cim ( ) ; bool Buida ( ) ; 12 13 14 15 16 17 18 19 20 21 private : struct tnodo { int i n f o ; tnode ∗ seguent ; }; tnode ∗ cim ; int N r E l e m s P i l a ; int maxElems ; }; C.4 Nom d’identificadors precisos ´ Un nom inadequat induir`a a confuUn nom no nom´es identifica; comporta informacio.
¨ sions i errors. Per exemple, la implementacio´ seguent no e´ s gens adequada: 1 2 3 4 bool c o n j u n t : : c o n t e ( int x ) { bool t r o b a t = true ; node∗ p = primer ; ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 66 while ( p ! = NULL and t r o b a t ) { t r o b a t = p −> i n f o ! = x ; p = p −> seg ; } return t r o b a t ; 5 6 7 8 9 10 } la variable local trobat significa el contrari del que el seu nom indica, i la funcio´ retorna el contrari del que el seu nom indica. Probablement es tracta d’un error indu¨ıt per l’identificador trobat, pero` canviar return trobat per return not trobat no e´ s una bona ´ solucio.
C.5 Identacio´ del codi Sagneu el codi i utilitzeu els par`entesis i espais en blanc per millorar la llegibilitat del codi. La majoria d’editors de text moderns (p.e., E MACS) incorporen identacio´ autom`atica, de manera que no porta massa problema. Tamb´e conv´e substituir, al final, tots els tabuladors (introdu¨ıts mitjanc¸ant la tecla TAB o autom`aticament per l’editor) per espais en blanc. Sino´ les impressions en paper poden quedar desajustades. Useu els par`entesis i ¨ els espais en blanc per resoldre ambiguetats i facilitar la comprensio´ de les expressions.
Per exemple, 1 2 3 4 5 6 7 bool e s a n y t r a s p a s ( int y ) { return y%4==0 and y%100!=0 or y%400==0; } ...
for ( int i = 0 ; i <n ; i ++) a t r a s p a s [ i ]= e s a n y t r a s p a s ( l l i s t a a n y s [ i ] ) ; ...
e´ s, per la majoria de la gent, m´es dif´ıcil de comprendre que 1 2 3 4 5 6 7 8 9 bool e s a n y t r a s p a s ( int y ) { return ( ( y % 4 == 0 ) and ( y % 100 ! = 0 ) ) or ( y % 400 == 0 ) ; } ...
for ( int i = 0 ; i < n ; ++ i ) { atraspas [ i ] = es any traspas ( l l i s t a a n y s [ i ] ) ; } ...
Aixo` mateix passa amb les claus ({}) d’obertura i tancament de blocs. Si un bloc ´ per millorar la llegibilitat nom´es cont´e una instruccio´ no fa falta usar-les, pero` pot ser util ¨ i evitar errors com en el seguent exemple: ´ DEL CODI C.5. IDENTACIO 1 2 3 4 5 6 7 8 9 67 if ( mes == FEBRER ) { c o r r e c t e = true ; if ( e s a n y t r a s p a s ( any ) ) if ( dia > 2 9 ) c o r r e c t e = false ; else // aquest else NO emparella amb el segon if!! if ( dia > 2 8 ) c o r r e c t e = false ; } Es pot resoldre el problema usant les claus en el lloc adequat o millor encara escrivint: 1 2 3 4 if ( mes == FEBRER ) { c o r r e c t e = ( dia <= 2 8 ) or ( dia == 29 and e s a n y t r a s p a s ( any ) ) ; } ´ de les claus. Alguns estils de sagnat i Sigueu consistents amb l’estil d’identacio´ i l’us ´ de les claus populars son: ´ us 1. les claus s’obren i es tanquen en l´ınies separades; ✞ // Exemple de l’estil 1 for ( int j = 0 ; j < k ; j ++) { i = j % 2; if ( i == 0 ) { ...
} else { ...
} } ✝ ☎ ✆ 2. la clau s’obre en la l´ınia que obre el bloc for, while, etc., i les claus de tancament van en l´ınies separades; ✞ // Exemple de l’estil 2 for ( int j = 0 ; j < k ; j ++) { i = j % 2; if ( i == 0 ) { ...
} else { ☎ ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 68 ...
} } ✝ ✆ 3. totes les claus de tancament consecutives es posen en la mateixa l´ınia.
✞ // Exemple de l’estil 3 for ( int j = 0 ; j < k ; j ++) { i = j % 2; if ( i == 0 ) { ...
} else { ...
} } ✝ ☎ ✆ ´ ben soportats pels editors de text i no Conv´e usar algun dels estils usuals ja que son resultaran xocants per altra gent que llegeixi el codi.
´ les alternatives multiples.
´ Una excepcio´ respecte les regles habituals de sagnat son En C i C++ e´ s t´ıpic escriure: ✞ if ( B1 ) S1 else if ( B2 ) S2 ...
else if ( Bn ) Sn else Sn+1 ✝ ☎ ✆ ´ mutuament excluyents (els if . . . else’s amb tots els else’s aliniats; quan les Bi ’s son ¨ s’avaluen sequencialment i no hi ha indeterminisme).
` C.6 Evitar una logica del programa antinatural ` ´ Per Eviteu un “flux” o logica del programa antinatural i “factoritzeu” el codi comu.
exemple, en comptes de 1 2 3 4 5 6 int s = 0 ; node∗ p = primer ; if ( p == NULL) { // la llista e´ s buida; no fem res } else { while ( p ! = NULL) { ` C.6. EVITAR UNA LOGICA DEL PROGRAMA ANTINATURAL s = s + p −> v a l o r ; p = p −> seg ; 7 8 } 9 10 11 69 } return s ; haur´ıem d’haver escrit: 1 2 3 4 5 6 7 int s = 0 ; node∗ p = primer ; while ( p ! = NULL) { s += p −> v a l o r ; p = p −> seg ; } return s ; o b´e 1 2 3 4 5 int s = 0 ; for ( node∗ p = primer ; p ! = NULL; p = p −> seg ) { s += p −> v a l o r ; } return s ; ¨ Un altre exemple e´ s la seguent funcio´ per insertar un element en un conjunt implementat mitjanc¸ant una llista enllac¸ada ordenada, amb fantasma i apuntadors al primer ´ i a l’ultim node: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void c o n j u n t : : i n s e r e i x ( const s t r i n g& x ) { node∗ q = primer ; // q apunta al fantasma while ( q −> seg ! = NULL and q −> seg −> i n f o < x ) { q = q −> seg ; } if ( q −> seg == NULL) { ´ node∗ p = new node ; // el nou node ser`a l’ultim p −> i n f o = x ; p −> seg = NULL; ultim = p ; q −> seg = p ; } else if ( q −> seg −> i n f o == x ) { // no es fa res } else { node∗ p = new node ; p −> i n f o = x ; p −> seg = q −> seg ; ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 70 q −> seg = p ; 19 } 20 21 } ` Hauria estat molt millor “factoritzar” la part comuna i simplificar la “logica” de la part que segueix al bucle de cerca: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void c o n j u n t : : i n s e r e i x ( const s t r i n g& x ) { node∗ q = primer ; // q apunta al fantasma while ( q −> seg ! = NULL and q −> s i g −> i n f o < x ) { q = q −> seg ; } if ( q −> seg == NULL or q −> seg −> i n f o ! = x ) { node∗ p = new node ; p −> i n f o = x ; p −> seg = q −> seg ; if ( q −> seg == NULL) { ultim = p ; } q −> seg = p ; } } C.7 Disminuir la complexitat ´ assenyat de la descompoDisminu¨ıu la complexitat del vostre codi mitjanc¸ant un us sicio´ funcional. Aprofiteu les solucions a problemes similars mitjanc¸ant una adequada descomposicio´ funcional. Per exemple, en una classe implementada amb una llista ¨ enllac¸ada e´ s frequent tenir un bucle, com a l’exemple previ, o el codi corresponent a la insercio´ en un punt concret de la llista. Per tant pot ser convenient tenir sengles opera´ cions privades i implementar les operaciones publiques usant les privades: 1 2 3 4 5 6 7 8 9 10 11 // llista ord.hpp class l l i s t a o r d { public : ...
// insereix ’x’ a la lista, en ordre void i n s e r e i x ( int x ) ; ...
private : ...
static void i n s e r e i x d a r r e r e ( node∗ p , int x ) ; node∗ t r o b a e l e m ( int x ) ; 12 13 }; C.7. DISMINUIR LA COMPLEXITAT 71 14 15 16 17 18 19 20 21 22 // llista ord.cpp ...
void l l i s t a o r d : : i n s e r e i x ( int x ) { node∗ q = t r o b a e l e m ( x ) ; if ( q −> seg == NULL or q −> seg −> i n f o > x ) { insereix darrere (q , x ) ; } } 23 24 25 26 27 28 29 30 31 32 33 34 35 // m`etode privat ´ // retorna un apuntador a l’ultim node de la llista tal que la seva info e´ s < // x. En alguns casos retornar`a el fantasma, si la llista e´ s buida o el primer ´ // de la llista e´ s >= x, o l’ultim node, si x e´ s major que la info de qualsevol // node de la llista.
l l i s t a o r d : : node∗ l l i s t a o r d : : t r o b a e l e m ( int x ) { node∗ q = primer ; while ( q −> seg ! = NULL and q −> seg −> i n f o < x ) { q = q −> seg ; } return q ; } 36 37 38 39 40 41 42 43 44 45 46 // m`etode privat de classe // insereix a la llista enllac¸ada un nou node, amb info == x, com a successor // del node apuntat per p. Pre: p != NULL void l l i s t a o r d : : i n s e r e i x d a r r e r e ( node∗ p , int x ) { node∗ n = new node ; n −> i n f o = x ; n −> s i g = p −> s i g ; p −> s i g = n ; } ...
Donat que nom´es els m`etodes de la classe poden utilitzar als m`etodes privats e´ s adequat suposar que les precondicions es compliran en ser invocats, i aix´ı evitem una gestio´ d’errors complexa.
En qualsevol cas, si la implementacio´ d’una funcio´ ocupa m´es de dues terceres parts d’una p`agina o necessita una llarga explicacio´ per documentar-la, llavors gaireb´e segur que convindria redissenyar l’algorisme o descomposar la implementacio´ en “peces” m´es manejables.
Un altre aspecte a considerar e´ s l’ordre en el que definim les funcions. Existeixen v`aries alternatives raonables: totes les funcions privades en primer lloc i despr´es les ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 72 ´ publiques; o al rev´es. Un conveni probablement millor e´ s el de situar les operacions privades el m´es properes (just abans o just despr´es) de l’operacio´ o operacions que les usin.
C.8 Useu construccions similars per tasques similars Useu construccions similars per realitzar tasques similars. Si en un punt del programa inicieu una taula t mitjanc¸ant: 1 2 for ( int i = 0 ; i < n ; ++ i ) t [ i ] = 0; ¨ llavors no escriviu el bucle que calcula quants elements no nuls hi ha en t de la seguent manera encara que sigui totalment correcte: 1 2 3 4 5 6 7 8 i = 0; nnuls = 0 ; while ( i <= n − 1 ) { if (A[ i ] ! = 0 ) { ++nnuls ; } ++ i ; } C.9 No usar variables globals No useu variables globals, exceptuant quan sigui estrictament imprescindible. Una va´ riable o objecte global e´ s extern a qualsevol funcio´ o m`etode. Els atributs de classe son b`asicament objectes globals, exceptuant que l’acc´es a elles pot restringir-se si es declaren a la part privada.
int n r e l em s ; // variable global! MALAMENT const int MAX ELEMS = 3 0 ; // constant global, OK class X { ...
static const int MAX SIZE = 2 0 ; // constant de classe, OK static int n r o b j e c t e s ; // variable de classe! MALAMENT ...
}; El problema amb els objectes globals e´ s que podem tenir efectes laterals en les funci¨ ons i m`etodes, i es trenquen els principis de modularitat. En el seguent exemple la funcio´ ` l’algorisme que esta nom´es funciona per l’array t la mida del qual e´ s n, i no obstant aixo, implementa e´ s igualment v`alid per qualsevol array: C.9. NO USAR VARIABLES GLOBALS 1 2 3 73 // variables globals int t [ 2 0 ] ; int n ; 4 5 6 7 8 9 10 // retorna cert si i nom´es si ’x’ est`a en t[0..n-1] bool e s t a ( int x ) { for ( int i = 0 ; i < n and t [ i ] ! = x ; ++ i ) ; // el cos del bucle e´ s buit return i < n ; } Tota comunicacio´ entre les funcions i els m`etodes amb el seu entorn hauria de produirse a trav´es dels seus par`ametres o retorns. Observeu que per un m`etode d’una clase X l’objecte al qual s’aplica el m`etode e´ s un par`ametre impl´ıcit i per tant no suposa una violacio´ d’aquesta regla.
´ una altra forma encoberta de trencar la Les anomenades variables locals est`atiques son modularitat. Una variable d’aquest tipus e´ s una variable local a una funcio´ o m`etode, pero` ret´e el seu valor entre execucions successives.
´ de variables globals o est`atiHi ha casos excepcionals i plenament justificats per l’us ´ objectes globals. Fixeu-vos, no obstant, que ques; p.e., els objectes cout, cin i cerr son acostumem a definir funcions del tipus print o els operadors << i >> de manera que reben un par`ametre de tipus ostream o istream expl´ıcit.
´ justificat de variables est`atiques i globals e´ s un generador Un exemple cl`assic d’us ´ de nombres pseudo-aleatoris: cada numero e´ s generat a partir de l’anterior (exceptuant ` la primera vegada) i no e´ s adequat ni comode que l’usuari haig de gestionar-ho a trav´es de par`ametres expl´ıcits: 1 2 3 4 5 6 7 8 9 10 11 12 double l l a v o r = 0 . 0 ; // variable global void i n i c i a l i t z a r a n d ( double sm) { l l a v o r = sm ; } double rand ( ) { static double x = l l a v o r ; // la primera vegada s’inicialitza amb el valor de la variable global; // i a partir d’aquest moment, la variable est`atica local x comenc¸a amb // el seu valor en l’execucio´ pr`evia x = funcio complicada ( x ) ; return x ; } El llenguaje C++ ens ofereix mecanismes que ens permeten donar una solucio´ m´es “neta” a aquest tipus de situacions (nom´es per mencionar un defecto de l’exemple anterior, observeu que res impediria que qualsevol funcio´ acced´ıs i modifiqu´es la variable llavor).
En particular podem usar variables privades de classe : ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 74 1 2 3 4 5 6 7 class Random { public : Random ( double l l = 0 . 0 ) { x = l l ; } ; double rand ( ) { x = f u n c i o c o m p l i c a d a ( x ) ; return private : static double x ; // variable de classe } x ; }; ´ les variables globals que en realitat s’usen com Una altra excepcio´ a la regla son ´ declarades com const perqu`e: constants globals, pero` que no son ´ • no poden ser inicialitzades en un unic pas, o • el seu valor inicial ha de ser calculat algor´ısmicament, o • existeix la necessitat de poder efectuar (molt ocasionalment) canvis en el seu valor.
´ usual que aquestes variables tamb´e s’implementin usant variables de clase priEs vades, per restringir la seva manipulacio´ i impedir en la mesura del que sigui possible usos incorrectes.
C.10 Utilitzar variables locals Utilizeu variables locals i eviteu m`etodes o funcions amb llargues llistes de par`ametres.
No incloeu atributs en un objecte o par`ametres en una operacio´ innecessaris si la seva missio´ es pot realitzar mitjanc¸ant una o m´es variables locals. Per exemple, si una classe llista no necessita la nocio´ de punt d’inter`es llavors e´ s absurd incloure aquest tipus d’atribut per fer un recorregut o posar un punter com par`ametre d’una funcio´ que fa el recorregut iterativament: 1 2 3 4 5 6 7 8 // usar var. local, NO un atribut actual! bool l l i s t a : : c o n t e ( const T& elem ) const { a c t u a l = primer ; while ( a c t u a l ! = NULL and a c t u a l −> i n f o < x ) { a c t u a l = a c t u a l −> s i g ; } return a c t u a l ! = NULL and a c t u a l −> i n f o == x ; } 9 10 11 12 13 14 15 16 // usar var. local, NO un par`ametre ‘p’! bool l l i s t a : : c o n t e p r i v ( const T& elem , node∗ p ) { while ( p ! = NULL and p −> i n f o < x ) { p = p −> s i g ; } return p ! = NULL and p −> i n f o == x ; } C.11. CODI BEN ESTRUCTURAT 75 17 18 19 20 21 22 23 24 25 26 27 28 // OK; aqu´ı ’p’ no e´ s un punter pel recorregut, en realitat // representa a la subllista que queda por explorar bool l l i s t a : : c o n t e r e c ( const T& elem , node∗ p ) { if ( p == NULL) { return false ; } if ( p −> i n f o >= x ) return p −> i n f o == x ; } return c o n t e r e c ( x , p −> s i g ) ; } C.11 Codi ben estructurat ´ ´ El codi ha de ser estructurat: cada bloc ha de tenir un unico punt d’entrada i un unic punt de sortida. No useu breaks (nom´es en els switchs), continues o gotos. No feu return dins de l’interior d’un bucle. Useu l’esquema de cerca quan sigui procedent, no un return o break des de l’interior d’un bucle que fa un recorregut. Tampoc e´ s un bon ¨ encia: estil “trencar” la iteracio´ modificant la variable de control que recorre la sequ` 1 2 3 4 5 6 for ( i = 0 ; i < n ; ++ i ) { if (A[ i ] == x ) { i = n; // Aixo` e´ s un nyap } ...
} ´ ´ Les uniques desviacions acceptables respecte a aquesta regla son: ´ trenquen inmediatament el flux normal d’exe• les excepcions —que, per definicio, cucio´ • els switchs, i • les composicions alternatives (no internes a un bucle) t´ıpiques de funcions recursives: 1 2 3 4 5 6 7 8 if ( i > 1 ) { result = x ; } else if ( i == 1 ) { result = y; } else { // if (i ¡ 1) result = z ; ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 76 9 10 } return r e s u l t ; es pot escriure 1 2 3 4 5 6 7 8 9 if ( i > 1 ) { return x ; } else if ( i == 1 ) return y ; } else { // if (i ¡ 1) return z ; } { o fins i tot 1 2 3 4 5 6 7 if ( i > 1 ) { return x ; } if ( i == 1 ) { return y ; } return z ; C.12 Bona documentacio´ Una bona documentacio´ e´ s essencial. La representacio´ d’una classe ha de contenir una explicacio´ detallada de com la implementacio´ concreta representa els valors abstractes i com e´ s aquesta representacio´ (invariant). Per exemple, 1 2 3 4 5 6 7 8 9 10 11 class l l i s t a { ...
private : struct node { s t r i n g clau ; int v a l o r ; node∗ seg ; }; ...
nodo∗ primer ; }; no ens diu si la llista est`a implementada mitjanc¸ant una llista simplement enllac¸ada, si est`a tancada circularment o no, si hi ha o no un node fantasma, si est`a ordenada o no ´ C.12. BONA DOCUMENTACIO 77 creixentment pel camp clau de cada node, etc. Per tant, s’ha de documentar adequadament la forma en que la representacio´ ser`a utilitzada.
No comenteu codi autoexplicatiu ni repetiu el que e´ s obvi. Aqu´ı teniu uns quants ´ exemples de comentaris inutils i superflus: 1 2 // retornem cert si trobat e´ s cert return t r o b a t ; 3 4 5 // incrementem el comptador c o nt ++; 6 7 8 9 // inicialitzem a 0 totes les components de ’v’ for ( int i = 0 ; i < n ; i ++) v[ i ] = 0; Un comentari ha d’aportar informacio´ que no e´ s inmediatament evident. Generalment, conv´e efectuar un comentari general previ sobre el comportament de cada funcio´ ´ Eviteu l’us ´ de o m`etode, amb indicacions sobre els punts subtils de la implementacio.
comentaris intercalats amb el propi codi, llevat que resultin absolutament imprescin´ no e´ s un sustitut adequat d’una descomposicio´ funcional dibles. La documentacion correcta, de manera que si la implementacio´ d’un determinat m`etode o funcio´ e´ s llarga i complexa, la solucio´ no e´ s posar abundants comentaris sino descomposar-la en “peces” manejables i autoexplicatives (vegeu els exemples de les seccions C.6 i C.7).
78 ´ I DOCUMENTACIO ´ ` APENDIX C. ESTIL DE PROGRAMACIO 4 La biblioteca est`andard de C++ 4.1 Introduccio´ a l’STL ¨ encia hem de gestionar estructures de dades que son ´ coleccions d’objectes Amb frequ` o elements. A aquest tipus d’estructures de dades se les anomena contenidors (contai´ les piles, les llistes, els conjunts, els diccionaris, les cues de ners). Exemples coneguts son prioritat, etc.
´ La biblioteca est`andard de C++ ofereix una gran varietat de classes i funcions utils.
Entre elles hem de destacar els stream’s per entrada/sortida, i els string’s. A m´es inclou l’anomenada STL (Standard Template Library) que defineix diverses classes gen`eriques denominades contenidores, i algorismes sobre aquestes classes. Algunes d’aquestes ´ classes contenidores son: • Vectors: vector<T> ¨ encia de mida variable i din`amica.
– Sequ` ¨ encia). Accedir – Acc´es aleatori (podem accedir a qualsevol element de la sequ` als elements per la posicio´ del seu ´ındex t´e cost constant.
– Les insercions i els esborrats pel final tenen cost constant.
• Dobles cues: deque<T> (double-ended queue) ¨ encia de mida variable i din`amica.
– Sequ` ¨ encia).
– Acc´es aleatori (podem accedir a qualsevol element de la sequ` – Les insercions i els esborrats pel final tenen cost constant.
– Vectors i deques tenen una interf´ıcie molt similar i poden ser utilitzats per ` proposits similats, encara que internament ambdues estructures treballen de manera molt diferent.
• Llistes: list<T> ¨ encia de mida variable i din`amica.
– Sequ` 79 ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 80 ¨ – Acc´es sequencial.
– Insercions i esborrats en qualsevol posicio´ designada per un iterador en temps constant.
• Conjunts: set<K> ´ ´ les claus.
– Elements unics.
Els elements mateixos son – Recuperacio´ r`apida de les claus (O(log(n)).
• Arrays associatius: map<K,I> ´ – claus uniques de tipus K amb informacio´ associada de tipus I.
– recuperacio´ r`apida de la informacio´ associada a les claus (O(log(n)).
A continuacio´ veurem les classes string, vector i list amb m´es detall.
4.2 La classe string Un string e´ s una cadena de car`acters. La biblioteca est`andard de C++ ofereix una classe string que ens permet gestionar-los amb tota comoditat. Ocasionalment, haurem d’utilitzar cadenes de car`acters representades segons les convencions de C, e´ s a dir, un array de car`acters (const char*) acabat amb el car`acter el codi ASCII del qual e´ s 0 (’\0’). A ´ aquestes ultimes cadenes les anomenarem C-strings per distinguir-les dels strings que ++ proporciona C .
Per usar string haurem d’incloure el fitxer del mateix nom: # include <s t r i n g > ´ basic ` 4.2.1 Us ´ asignacio, ´ etc. i podem llegirLa classe ofereix les operacions habituals de construccio, los o imprimir-los d’igual forma que els tipus elementals. Tamb´e podem assignar a un string una cadena literal entre cometes (un C-string constant). A vegades necessitem produir un C-string a partir d’un string, per aixo` usarem c_str(): 1 2 3 4 5 6 7 string s ; cout << " Nom del fitxer : " ; c i n >> s ; s t r i n g t = " hola " ; f s t r e a m f ( s . c s t r ( ) ) ; // la constructora de la classe fstream // (ver apendice B) t´e com par`ametre el // nom del fitxer que e´ s un const char* 4.2. LA CLASSE STRING 81 4.2.2 Operadors + i += Els operadors + i += serveixen per concatenar.
1 2 3 s t r i n g s , t ; // crea dos strings buits s = " Hola " ; // assigna el C-string constant ”Hola” // a l’string ’s’ 4 5 6 t = s + " mon !" ; // concatena ’s’ amb ”mon!” cout << t << endl ; // imprimeix Hola mon! 7 8 9 t += " i adeu !" ; cout << t << endl ; // imprimeix Hola mon! i adeu! Un car`acter (char) tamb´e pot utilitzar-se com un valor del tipus, exceptuant en la ´ inicialitzacio: 1 2 3 s t r i n g s = ’h’ ; // error! s t r i n g t ( " cola " ) ; t = t + ’*’ ; // ok! 4.2.3 Longitud La longitud d’un string s’obt´e amb length() o size() i podem accedir als car`acters individualment, com si es tract´es d’un array (pero` no ho e´ s): 1 bool e s v o c a l ( char c ) { . . . } 2 3 4 5 6 7 8 9 10 11 int q u a n t e s v o c a l s ( const s t r i n g& s ) { int nv = 0 ; for ( int i = 0 ; i < s . l e n g t h ( ) ; ++ i ) { if ( e s v o c a l ( s [ i ] ) ) { ++nv ; } } return nv ; } 4.2.4 Operadors de comparacio´ Els operadors de comparacio´ entre strings estan definits respetant l’ordre alfab`etic: 1 2 3 4 string string string string s t u v = = = = " casa " ; " cama " ; " dado " ; u + "s" ; // v == ”dados” ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 82 5 6 7 8 9 cout cout cout cout << << << << (s (s (u (s < t ) << endl ; <= u ) << endl ; > v ) << endl ; ! = t ) << endl ; // imprimeix false // imprimeix true // imprimeix false // imprimeix true ` 4.2.5 Metode substr El m`etode substr() ens permet extreure substrings d’un string donat: 1 2 3 4 s t r i n g s = " portaavions " ; s . substr ( ) // retorna una copia de s s . substr ( 5 ) ; // retorna el substring ”avions” s . substr ( 1 , 6 ) ; // retorna ”ortaav” En general, substr(i, n) retorna el substring que comenc¸a en la posicio´ i i t´e longitud ´ inclosos). Els car`acters s’indexen de 0 en endavant. Si n no es dona, ´ n (ambdos llavors ´ llavors es retorna el substring que va des de la posicio´ i fins el final. Si i tampoc es dona considera que i = 0. Es produeix un error si i > length().
` 4.2.6 Metode replace Amb replace() podem reemplac¸ar un substring per un altre. Per exemple, 1 s t r i n g s = " portaavions " ; 2 3 4 s . r e p l a c e ( 5 , 6 , " helicopter " ) ; cout << s << endl ; // imprimeix portahelicopter Existeixen versions m´es sofisticades de replace; en la versio´ b`asica de l’exemple donem la posicio´ inicial i la longitud del substring a reemplac¸ar i l’string que reemplac¸a.
` 4.2.7 Metode find Per acabar aquesta breu descripcio´ cal comentar algunes de las facilitats que proporciona la classe string per la cerca dins d’un string. El m`etode b`asic s’anomena find() i ens permet trobar la primera ocurr`encia d’un car`acter o substring en un string a partir d’una ´ El m`etode retorna la posicio´ d’inici del substring o car`acter buscat o npos certa posicio.
(un valor especial) per indicar el frac`as de la cerca.
1 s t r i n g s = "Lola , pasame la cola " ; 2 3 4 5 6 7 s . f i n d ( " ola " ) ; s . f i n d ( " ola " , 3 ) ; s . f i n d ( " pase " ) ; s . f i n d ( ’p’ ) ; s . f i n d ( ’l’ , 3 ) ; // retorna 1 // retorna 17 // retorna npos // retorna 7 // retorna 13 4.3. VECTOR<T> 83 El primer argument de find() e´ s el car`acter o substring que se busca. El segon e´ s la posicio´ (inclusivament) a partir de la qual s’inicia la cerca. Per defecte, la cerca s’inicia en el principi de l’string.
El conveni d’usar npos per indicar el frac`as d’una cerca introdueix certs inconvenients a l’hora d’usar el m`etode find(). El resultat d’una cerca sempre s’hauria de recollir en una variable del tipus string::size_type i haurem de testejar si el resultat e´ s string::npos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 // reemplac¸a totes les aparicions en s de s1 per s2 // ex: { s = “ana se come una banana” } // global replace(s, “ana”, “alle”) // { s = “alle se come una ballena” } void g l o b a l r e p l a c e ( s t r i n g& s , const s t r i n g& s1 , const s t r i n g& s2 ) { s t r i n g : : s i z e t y p e idx = 0 ; int l 1 = s1 . l e n g t h ( ) ; int l 2 = s2 . l e n g t h ( ) ; while ( ( idx = s . f i n d ( s1 , idx ) ) ! = s t r i n g : : npos ) { s . r e p l a c e ( idx , l 1 , s2 ) ; idx += l 2 ; } } 4.3 vector<T> Un objecte de la classe vector e´ s conceptualment similar a un array, pero` sense algunes de les desavantatges dels arrays i ofereix funcionalitats addicionals, com ara que s’expandeixin i es contreguin segons la necessitat.
Un dels desavantatges de vector e´ s que quan la capacitat es gestiona autom`aticamenten ` en general consumeix m´es memoria que un array. Aixo` e´ s degut a que reserva espai addicional d’emmagatzematge de cara a un creixement futur del vector.
Com a classe contenidora est`andard ofereix un repertori d’operacions molt extens, ´ t´ıpiques dels vectors (p.e. insercio´ i esborrat d’elements en incloent algunes que no son posicions intermitges).
En tots els contenidors la classe dels elements que es vol emmagatzemar (tipus T) ha ` de tenir definida el constructor per copia, l’asignacio´ i l’operador d’igualtat. A m´es la sem`antica d’aquestes tres operacions ha de ser coherent: 1 2 3 4 ` T a = b ; // constructor per copia c = b; // operador d’assignacio´ bool ok = ( a == c ) ; // ha de ser true bool ok = ( a == b ) ; // ha de ser true Per usar vector haurem d’incloure el fitxer del mateix nom: # include <v e c t o r > ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 84 ´ basic ` 4.3.1 Us ´ pero` pot augmentar La mida inicial d’un vector es pot indicar en el moment de la creacio, si s’afegeixen elements al final, p.e. mitjanc¸ant el m`etode push_back.
Constructor de vector.
explicit vector(); explicit vector(size_type n, const T& value= T()); Tres grans.
vector(const vector<T>& x); ˜vector(); vector<T>& operator=(const vector<T>& v); Exemple d’us: ´ 1 # include <v e c t o r > 2 3 4 5 int main ( ) { // creem un vector de 9 enters v e c t o r <int> v1 ( 9 ) ; 6 // crea un vector de 10 c`aracters inicialitzats amb ’a’ v e c t o r <char> v1 ( 1 0 , ’a’ ) ; 7 8 9 } 4.3.2 Iteradors Els iteradors de la classe vector funcionen igual que els iteradors de la classe list. Per m´es informacio´ sobre els iteradors consultar la classe list (veure la seccio´ 4.4.2).
Retorna un iterador al principi.
iterator begin(); const_iterator begin() const; Retorna un iterador al final.
iterator end(); const_iterator end() const; ´ Retorna un iterador invers que es refereix a l’ultim element de la llista.
reverse_iterator rbegin(); 4.3. VECTOR<T> 85 const_reverse_iterator rbegin() const; Return un iterador invers que es refereix al primer element de la llista.
reverse_iterator rend(); const_reverse_iterator rend() const; 4.3.3 Capacitat Internament, els vectors —com tots els contenidors— tenen una mida que representa la ` els vectors, tamb´e tenen una quantitat d’elements que cont´e el vector. No obstant aixo, capacitat, que determina la quantitat d’espai d’emmagatzematge que tenen assignats, i que pot ser igual o superior a la real. La quantitat extra d’emmagatzematge que el vector t´e assignat no s’utilitza, pero` est`a reservada per al vector per ser usada en el cas que creixi. D’aquesta manera, el vector no ha de reassignar l’emmagatzematge, cada vegada que creix. Nom´es ho far`a quan aquest espai extra s’hagi esgotat i un nou element ¨ encia logar´ıtmica en relacio´ amb la seva s’insereixi (que nom´es hauria de passar en frequ` grand`aria).
Les reassignacions poden ser una operacio´ costosa en termes de rendiment, ja que generalment involucren que tot l’espai d’emmagatzematge utilitzat pel vector es copi¨ı a ´ Per tant, cada vegada que hi hagi previst un gran augment de la mida una nova ubicacio.
per a un vector, es recomana indicar expl´ıcitament una capacitat per al vector utilitzant el m`etode vector::reserve.
Retorna la mida.
size_type size() const; Retorna la mida m`axima. Aquest m`axima e´ s la mida m`axima potencial que la llista pot arribar a tenir degut a limitacions del sistema o de la biblioteca.
size_type max_size() const; Canvia la mida, esborrant o afegint elements en cas necessari.
void resize(size_type sz, T c = T()); Retorna la mida de la capacitat d’emmagatzematge assignada.
size_type capacity() const; Indica si el vector e´ s buit.
bool empty() const; Demana que la capacitat ha de ser com a m´ınim la indicada.
´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 86 void reserve(size_type n); ´ als elements 4.3.4 Acces La classe ofereix m`etodes d’acc´es per l’´ındex: o b´e mitjanc¸ant l’operador d’indexacio´ tradicional [], o b´e mitjanc¸ant el m`etode at que comprova el rang i llanc¸a una excepcio´ en cas necessari.
Acc´es a l’element n-`essim.
reference operator[](size_type n); const_reference operator[](size_type n) const; Acc´es a l’element n-`essim.
reference at(size_type n); const_reference at(size_type n) const; Acc´es al primer element.
reference front(); const_reference front() const; ´ Acc´es a l’ultim element.
reference back(); const_reference back() const; Exemple d’us: ´ 1 2 # include <v e c t o r > # include <iostream > 3 4 using namespace s t d ; 5 6 7 8 9 10 11 12 13 14 15 int main ( ) { v e c t o r <int> v ; v e c t o r <v e c t o r <double> > mat ; for ( int i = 0 ; i < 1 0 ; ++ i ) { v . push back ( i ) ; } for ( int i = 0 ; i < v . s i z e ( ) ; ++ i ) { cout << v [ i ] << " " ; } cout << endl ; 4.3. VECTOR<T> try { for ( int i = 0 ; i < 1 0 0 ; ++ i ) { cout << v . a t ( i ) << " " ; // llanc¸a una excepcio´ si la posicio´ no existeix } cout << endl ; } catch ( . . . ) { c e r r << " Fora de rang !" << endl ; } 16 17 18 19 20 21 22 23 24 25 26 87 } 4.3.5 Modificadors ¨ encies (deque i list), els vectors son ´ En comparacio´ amb els contenidors b`asics de sequ` generalment els m´es eficients en temps per accedir als elements i per afegir o treure ¨ encia.
elements del final de la sequ` Per a les operacions que impliquin inserir o eliminar elements en posicions que no siguin el final, tenen pitjors resultats que deque i list, i a m´es els iteradors i refer`encies ´ menys consistents que els de les llistes.
son Afegeix nous elements a la llista, eliminant tots els elements que contenia la llista anteriorment.
En concret afegeix n vegades l’element u.
void assign(size_type n, const T& u); Afegeix un element al final.
void push_back(const T &x); ´ Esborra l’ultim element.
void pop_back(); Insereix elements just davant de l’element apuntat per l’iterador.
iterator insert(iterator position, const T& x); void insert(iterator position, size_type n, const T& x ); Elimina elements.
iterator erase(iterator position); iterator erase(iterator first, iterator last); Intercanvia el contingut dels dos vectors (el vector en curs i v).
´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 88 void swap(vector<T> &v); Esborra tot el contingut del vector.
void clear(); 4.4 list<T> La classe list ofereix operacions usuals de llistes, incloent recorreguts en ambdues direccions, insercions i esborrats en punts arbitraris, etc.
La flexibilitat i efici`encia d’aquesta classe resideix en les classes auxiliars d’iteradors.
Un iterador e´ s un “apuntador” a un element de la llista i s’usa per consultar l’element apuntat, eliminar-lo, inserir un nou element, etc.
¨ encies (vector i deque), Comparada amb els altres contenidors est`andards de sequ` les llistes en general acostumen a ser millors inserint, accedint i movent elements en ´ d’aquestes qualseol posicio´ dins del contenidor, i tamb´e en els algorismes que fan us ´ operacions, com ara els algorismes d’ordenacio.
El principal inconvenient de les llistes en comparacio´ amb els altres contenidors de ¨ encies e´ s que no tenen acc´es directe als elements per la seva posicio.
´ Per exemple, sequ` ´ per accedir al sis`e element d’una llista s’ha de recorrer des d’una posicio´ coneguda (com ´ la qual cosa pren un temps lineal que el principi o el final) fins arribar a aquesta posicio, dep`en de la dist`ancia on es troba l’element.
` Les llistes tamb´e consumeixen una mica de memoria extra per mantenir la informacio´ que encadena els diferents elements. Aquest pot ser un factor important quan la llista e´ s gran i els elements de mida petita.
Per usar list haurem d’incloure el fitxer del mateix nom: # include < l i s t > ´ basic ` 4.4.1 Us Constructor de list.
explicit list(); explicit list(size_type n, const T& value= T()); Tres grans.
list(const list<T> &l); ˜list(); list<T>& operator=(const list<T>& l); 4.4. LIST<T> 89 4.4.2 Iteradors ` A vegades necessitem recorrer els elements d’un d’aquests contenidors. Una possibilitat e´ s dota a la classe corresponent d’un punt d’inter`es i operacions que permetin desplac¸ar el punt d’inter`es i consultar l’element al qual apunta el punt d’inter`es. Una altra solucio´ que e´ s molt m´es potent i flexible consisteix en definir una o m´es classes auxiliars d’iteradors associats a la classe contenidora.
Un iterador abstreu la idea d’´ındex o punter a un element. S’assembla molt a un punt d’inter`es, pero` tenen difer`encies molt importants: ` • el punt d’inter`es est`a sota el control de la propia classe contenidora i nom´es pot haver-hi un.
´ externs a la classe contenidora i es poden crear tants com siguin • els iteradors son necessaris.
Els iteradors poden ser assignats i comparats (==, !=). Els operadors d’increment i decrement desplacen l’iterador des d’un element al seu successor o predecessor. L’operador de desrefer`encia * aplicat a un iterador permet accedir al seu contingut. Pero` un iterador NO e´ s un punter, encara que en molts aspectes es comporti de manera similar.
Aquesta classe est`a equipada tamb´e amb iteradors inversos (reverse_iterator i reverse_const_iterator) que operen al rev´es que els iteradors normals. En termes abstractes treballen sobre la llista invertida.
Retorna un iterador al principi.
iterator begin(); const_iterator begin() const; Retorna un iteradoral final.
iterator end(); const_iterator end() const; Retorna un iterador invers que comenc¸a pel final.
reverse_iterator rbegin(); const_reverse_iterator rbegin() const; Retorna un iterador invers que comenc¸a pel principi.
reverse_iterator rend(); const_reverse_iterator rend() const; Exemple d’us: ´ ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 90 1 2 3 4 5 6 7 8 9 // Accio´ que donada una llista d’enters inicialitza tots els elements amb // l’enter indicat.
void a s s i g n a x ( l i s t <int>& l , int x ) { l i s t <T > : : i t e r a t o r i t = l . begin ( ) ; while ( i t ! = l . end ( ) ) { ∗it = x; ++ i t ; } } 10 11 12 13 14 15 16 17 18 19 20 21 22 // Accio´ que donada una llista mostra els seus elements per l’ostream // indicat.
´ Per recorrer ` // ATENCIO: aquesta llista caldr`a usar un iterador constant // donat que la llista que ens passen no es pot modificar.
template <typename T> void mostrar ( const l i s t <T>& l , ostream& os ) { l i s t <T > : : c o n s t i t e r a t o r i t = l . begin ( ) ; while ( i t ! = l . end ( ) ) { os << ∗ i t << endl ; ++ i t ; } } 4.4.3 Capacitat Indica si la llista e´ s buida.
bool empty() const; Retorna la mida de la llista.
size_type size() const; Retorna la mida m`axima. Aquest m`axima e´ s la mida m`axima potencial que la llista pot arribar a tenir degut a limitacions del sistema o de la biblioteca.
size_type max_size() const; Canvia la mida de la llista, esborrant o afegint elements en cas necessari.
void resize(size_type sz, T c = T()); Exemple d’us: ´ 4.4. LIST<T> 1 2 91 # include <iostream > # include < l i s t > 3 4 using namespace s t d ; 5 6 7 8 9 10 11 12 13 14 15 int main ( ) { l i s t <float> l s t ; cout << l s t . empty ( ) << endl ; cout << l s t . s i z e ( ) << endl ; l s t . push back ( 1 . 0 ) ; l s t . push back ( 2 . 0 ) ; l s t . push back ( 3 . 0 ) ; cout << l s t . empty ( ) << endl ; cout << l s t . s i z e ( ) << endl ; } // true // 0 // false // 3 ´ als elements 4.4.4 Acces Acc´es al primer element.
reference front(); const_reference front() const; ´ Acc´es a l’ultim element.
reference back(); const_reference back() const; 4.4.5 Modificadors La classe list t´e les operacions usuals d’insercio´ i esborrat pel final (push_back, pop_back) i pel principi (push_front, pop_front) Afegeix nous elements a la llista, eliminant tots els elements que contenia la llista anteriorment.
En concret afegeix n vegades l’element u.
void assign(size_type n, const T& u); Afegeix un element al principi de la llista.
void push_front(const T &x); Esborra el primer element de la llista.
void pop_front(); 92 ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ Afegeix un element al final.
void push_back(const T &x); ´ Esborra l’ultim element de la llista.
void pop_back(); Insereix el nombre d’elements indicat abans de l’iterador.
iterator insert (iterator position, const T& x); void insert (iterator position, size_type n, const T& x); Esborra de la llista l’element apuntat per l’iterador o tots els elements que estan entre els dos iteradors.
iterator erase (iterator position); iterator erase (iterator first, iterator last); Intercanvia el contingut de les dues llistes (la llista en curs i lst).
void swap(list<T>& lst); Esborra tot el contingut de la llista.
void clear(); 4.4.6 Altres operacions ´ les seguents: ¨ Algunes de funcionalitats addicionals que tenen les llistes son Mou elements d’una llista a l’altra.
void splice(iterator pos, list<T>& x); void splice(iterator pos, list<T>& x, iterator i); void splice(iterator pos, list<T>& x, iterator first, iterator last); Esborra els elements que tenen un valor espec´ıfic.
void remove(const T& value); Esborra els valors duplicats.
void unique(); 4.5. EXERCICI 93 Fusiona dues llistes. Combina x a la llista en curs, inserint tots els elements de x en les seves respectives posicions ordenades. Aquest m`etode buida x i incrementa la mida de la llista.
void merge(list<T>& x); Ordena els elements que hi ha en el contenidor del m´es petit fins el m´es gran. L’ordenacio´ es realitza mitjanc¸ant la comparacio´ dels elements a la llista de dos en dos utilitzant un algorisme ´ Aquesta operacio´ no suposa la construccio´ o destruccio´ de cap objecte.
d’ordenacio.
void sort(); Exemple d’us ´ 1 2 3 # include < l i s t > # include <s t r i n g > # include <iostream > 4 5 6 7 8 9 10 11 12 13 int main ( ) { l i s t <s t r i n g > l s t ; string s ; while ( c i n >> s ) { // ’cin >> s’ retorna 0 ≡ false si s’acaba l’input l s t . push back ( s ) ; } l s t . sort ( ) ; // ordena ’lst’ mostrar ( l s t , cout ) ; } 4.5 Exercici L’exercici principal d’aquesta seccio´ consisteix en implementar la classe polinomi de la subseccio´ 4.5.1, en la que apareix llistat el fitxer polinomi.hpp juntament a l’especificacio´ de cadascun dels m`etodes de la classe.
¨ Per implementar aquesta classe es recomana seguir els seguents passos: 1. Decidiu la representacio´ interna d’un polinomi els coeficients del qual siguin enters. Podeu d’usar la classe list de la biblioteca STL.
2. Implementeu la constructora, la qual llegeix una llista de coeficients i una llista de graus. Ambdues llistes han de tenir la mateixa longitud. Si un grau apareix m´es d’una vegada en la llista de graus, els coeficients corresponents s’acumulen.
¨ encies de parells 3. Escriviu un programa (exercici.4.1.cpp) que llegeixi dues sequ` (coeficient, grau) que descriuen dos polinomis p(x) i q(x). A continuacio´ el progra´ l’usuari pugui escollir una ma ha d’entrar en un bucle en el que, per cada iteracio, ´ polinomis, avaluar p(x) en cert opcio´ de les possibles, per exemple: sumar ambdos punt, sortir del bucle i tornar a introduir altres polinomis, etc. De manera que es puguin provar tots els m`etodes.
´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 94 4. Implementeu el m`etode print() que mostra el polinomi comenc¸ant pel terme de menor grau.
• El polinomi 2x4 + 5x2 + 4 es mostrar`a com: 4 + 5xˆ2 + 2xˆ4.
• El polinomi −2x4 + 5x2 es mostrar`a com: 5xˆ2 - 2xˆ4.
• El polinomi x − 2 es mostrar`a com: -2 + x.
5. Implementeu el m`etode double avalua(double x) que avalua el polinomi per un cert punt x (NOTA: cal que el nombre de multiplicacions i sumes sigui el m´ınim possible).
6. Implementeu els m`etodes de comparacio´ ==, > i < entre polinomis. Un polinomi ´ d’igual grau i el p e´ s major que q si i nom´es si p e´ s de major grau que q o si son coeficient del monomi de major grau de p e´ s major que el coeficient corresponent en q o si tenen el mateix monomi a · xb de major grau i p − axb e´ s major que q − axb .
7. Implementeu els m`etodes de suma + i += aix´ı com els de resta - i -= entre polinomis.
8. Implementeu els m`etodes de divisio´ i multiplicacio´ (/,/=,*,*=) d’un polinomi. (Per fer-ho de manera sencilla, una condicio´ pel dividend e´ s que sigui un binomi de la forma (x + a)). En el cas de la divisio´ es retorna el quocient i es descarta la resta.
4.5.1 polinomi.hpp Un objecte de la classe polinomi representa a un polinomi de grau arbitrari amb coeficients enters.
#ifndef _POLINOMI_HPP_ #define _POLINOMI_HPP_ #include <iostream> #include <list> #include <esin/error> using namespace std; class polinomi { public: Constructora per defecte. Crea un polinomi buit, e´ s a dir, nom´es amb el terme independent cero.
polinomi() throw(error); Constructora. Rep una llista de graus i una llista de coeficients. Llanc¸a un error si hi ha algun grau negatiu o si les llistes no tenen la mateixa longitud.
polinomi(const list<int>& lgraus, const list<int>& lcoef) throw(error); 4.5. EXERCICI 95 ` Constructora per copia, operador d’assignacio´ i destructora.
polinomi(const polinomi& p) throw(error); polinomi& operator=(const polinomi& p) throw(error); ˜polinomi() throw(); Retorna el coeficient del monomi de grau n. Llanc¸a una excepcio´ si n < 0.
int coef(int n) const throw(error); Aquest m`etode avalua el polinomi en un punt.
double avalua(double punt) const throw(); Operadors de suma i resta entre polinomis.
polinomi operator+(const polinomi& b) const throw(error); polinomi operator-(const polinomi& b) const throw(error); polinomi& operator+=(const polinomi& b) throw(error); polinomi& operator-=(const polinomi& b) throw(error); Operadors de comparacio´ entre polinomis.
bool bool bool bool bool bool operator==(const polinomi& b) const throw(); operator!=(const polinomi& b) const throw(); operator<(const polinomi& b) const throw(); operator<=(const polinomi& b) const throw(); operator>(const polinomi& b) const throw(); operator>=(const polinomi& b) const throw(); ´ La divisio´ llanc¸a un error si el divisor e´ s nul o e´ s m´es Operadors de multiplicacio´ i divisio.
complexe que (x + a) (el grau m`axim e´ s major que 1).
polinomi operator*(const polinomi& b) const throw(error); polinomi operator/(const polinomi& b) const throw(error); polinomi& operator*=(const polinomi& b) throw(error); polinomi& operator/=(const polinomi& b) throw(error); Aquest m`etode imprimeix en os el polinomi representat com suma de monomis ordenats de menor a major grau.
void print(ostream& os) const throw(); Gestio´ d’errors i informacio´ interna static const int GrauNegatiu = 41; ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 96 static const int DiferentNombreCoeficientsGraus = 42; static const int DivisioPerZero = 43; static const int PolinomiNoBinomi = 44; private: Els atributs i m´etodes privats van aqu´ı }; #endif 4.5.2 Activitats Si ja has implementat la classe polinomi llavors tens tamb´e acabades totes les activitats.
Repassa-les per estar preparat per l’Activitat numero ´ 4. En cas contrari primer acaba d’implementar la classe polinomi abans de continuar mirant les Activitats.
4.1 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode constructor i avalua de la classe polinomi. Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva implemen´ tacio.
polinomi ( const l i s t <int>& l g r a u s , const l i s t <int>& l c o e f ) throw ( e r r o r ) ; double avalua ( double punt ) const throw ( ) ; 4.2 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator+ de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
polinomi operator +( const polinomi& b ) const throw ( e r r o r ) ; 4.3 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator- de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
polinomi operator −( const polinomi& b ) const throw ( e r r o r ) ; 4.4 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator* i operator== de la classe polinomi. Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva imple´ mentacio.
4.5. EXERCICI 97 polinomi operator ∗ ( const polinomi& b ) const throw ( e r r o r ) ; bool operator ==( const polinomi& b ) const throw ( ) ; 4.5 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator+= de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
polinomi& operator +=( const polinomi& b ) throw ( e r r o r ) ; 4.6 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator-= de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
polinomi& operator −=(const polinomi& b ) throw ( e r r o r ) ; 4.7 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator*= i operator!= de la classe polinomi. Cal que escriguis tots els m`etodes addicionals que usis expl´ıcitament en la teva imple´ mentacio.
polinomi& operator ∗=( const polinomi& b ) throw ( e r r o r ) ; 4.8 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator== de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator ==( const polinomi& b ) const throw ( e r r o r ) ; 4.9 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator!= de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator ! = ( const polinomi& b ) const throw ( e r r o r ) ; 4.10 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator< de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator <( const polinomi& b ) const throw ( e r r o r ) ; 98 ´ 4. LA BIBLIOTECA ESTANDARD ` SESSIO DE C++ 4.11 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator<= de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator <=(const polinomi& b ) const throw ( e r r o r ) ; 4.12 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator> de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator >( const polinomi& b ) const throw ( e r r o r ) ; 4.13 a) Escriu la representacio´ de la classe polinomi usant la classe list de la biblioteca STL.
b) Implementa el m`etode operator>= de la classe polinomi. Cal que escriguis tots ´ els m`etodes addicionals que usis expl´ıcitament en la teva implementacio.
bool operator >=(const polinomi& b ) const throw ( e r r o r ) ; ´ Index alfab`etic aliasing, 22 arrays, Vegeu taules ` constructor per copia, 24 dangling references, 21 delete, 19 delete[], 19 destructor, 25 emparellament incorrecte, 21 ´ 67 estil d’identacio, explicit, 16 refer`encia, 2 Regla dels tres grans, 24 ´ 13 representacio, Segmentation fault, 1 taules, 7 variable variable local, 74 variable privada de classe, 73 variables globals, 72 void*, Vegeu punter gen`eric identacio´ del codi, 66 inicialitzacions per defecte, 16 memory leaks, 21 modularitat, 72 new, 19 new[], 19 NULL, 1 delete de NULL, 23 desrefer`encia de NULL, 22 operator *, 1 operator =, 26 operator &, 1 pas de par`ametres, 3 per refer`encia constant, 3 per valor, 3 per valor constant, 3 punter, 1 punter gen`eric, 2 punter local, 23 punter nul, 1 99 ...

Tags: