-pastreaza cuvantul citit in memoria heap;
-realizeaza initializarile;
frecv = 1;
urm = 0.
printf( " tnod : : tnod %lun" , coreleft () ) ;
//se transfera cuvantul in zona rezervata
strcpy ( cuvant , q );
//initializari
frecv = 1 ;
urm = 0 ; //pointerul nul
break;
//sfarsit while
if ) sf = = EOF ) //s-a intalnit EOF
tnod : : ind = true ;
}
// sfarsit constructor
inline tnod : : ~ tnod ( )
inline void tnod::afistnod() //afiseaza cuvantul si frecventa
void tnod::operator delete(void*p) //supraincarcarea operatorului delete
void tnod : : operator ++ ( ) //incrementeaza frecv
Boolean tnod : : retind ( ) //returneaza valoarea ind
Functia coreleft returneaza dimensiunea,in octeti, a memoriei libere in momentul apelului.Ea a fost apelata in mai multe functii pentru a putea urmari momentele in care se dezaloca obiectele care sant instantieri ale clasei tnod.
23.12 Sa se defineasca tipul abstract slist pentru implementarea listei simplu inlantuite.
Listele simplu inlantuite au fost implementate in limbajul C in capitolul 11.
Pentru gestiunea listelor simplu inlantuite s-au folosit doua variabile prim si ultim care sant pointeri sper primul si respectiv ultimul nod al listei.
Aceste valori se vor utiliza si in cazul de fata cu acelasi scop.Ele sunt date membru protejate ale tipului abstract slist
Avantajul implementarii tipului abstract slist este acela ca , se pot defini simultan si simplu orice obiecte de tip lista.
Functiile membru ale tipului abstract slist sunt:
constructor implicit ;
deconstructor ;
adaugarea unui nou nod inaintea primului nod al listei ;
stergerea primului nod al listei ;
stergerea ultimului nod al listei ;
cautarea in lista a unui nod ;
afisarea datelor aflate in nodurile listei.
Nodurile listei sunt obiecte de tip tnod, tip definit in exercitiul 23.11
#ifndef __BXXIII11_H
#include "BXXIII1.CPP"
#define __ BXXIII1_H
class slist ;
Functiile membru ale clasei slist se definesc in fisierul de extensie CPP, de mai jos
#idndef __BXXIII12_H
#include "BXXIII12.H"
#define __BXIII12_H
#endif
inline slist : : slist ( ) //constructor
slist : : ~slist ( ) //destructor ; distruge toate nodurile listei
prim = ultim = 0 ;
tnod *slist::adauga(tnod*p) //adauga un nod dupa ultimul nod al listei
tnod *slist ; : insereaza (tnod *p)
//insereaza un nou nod inaintea celui spre care pointeaza prin
void slist : : sprim ( ) //sterge primul nod din lista
else
printf ( "lista vidan" ) ;
void slist : : sultim ( ) //sterge ultimul nod al listei
// p - pointeaza spre ultimul nod
//p1 - pointeaza spre nodul precedent
if (p1 = = 0 )
//nodul spre care pointeaza p1 devine ultimul nod al listei
p1 -> urm = 0 ;
ultim = p1 ;
//se sterge nodul spre care pointeaza p
delete p ; //se aplica operatorul supraincarcat
} else
printf ( " lista vidan " ) ;
tnod *slist : : cauta (tnod *p )
/* - cauta nodul pentru care cuvant pointeaza spre un sir identic cu cel spre
care pointeaza p -> cuvant ;
- returneaza pointerul spre nodul respectiv sau 0 daca nu exista un astfel
de nod. */
void slist : : afislist (
/* - afiseaza pentru fiecare nod:
-cuvant
si
-frecv.
}
23.13 Sa se scrie un program care citeste un text si afiseaza frecventa de aparitie a fiecarui cuvant din tex.Nu se face distinctie intre literele mari si mici
Aceasta problema a fost rezolvata in mai multe moduri in capitolele precedente.
In particular,in capitolul 11 se da o rezolvare folosind listele simplu inlantuite.
Programul de fata utilizeaza aceeasi metoda.
#include <alloc.h>
#ifndef __CONIO_H
#include <conio.h>
#define __CONIO_H
#endif
#include "BXIII12.CCP"
main ( )
/*citeste un text si afiseaza frecventa cuvintelor citite 8/
else
lista.adauga (nod);
/*nodul spre care pointeazanod contine un cuvant
negasit in lista; se adauga la lista */
else delete nod ;
printf("Pentru a continua actionati o tastan");
getch ( ) ;
} //sfarsit while
//listeaza nodurile listei
lista.afislist ( ) ;
23.2 Supraincarcarea operatorului =
In limbajul C++ se pot face atribuiri de obiecte care sant instantieri ale aceleeasi clase.
Fie instantierile:
numae_clasa obiect1,obiect2;
.
O atribuire de forma:
obiect2 = obiect1 ;
este acceptata de compilator.
La atribuirea de obiecte,in mod implicit,se atribuie datele membru aleobiectului din dreapta operatorului = la datele membru corespunzatoare ale obiectului din stanga aceluiasi operator.Astfel de atribuiri se pot utiliza si in declaratii.
Exemplu:
class complex
.
} ;
complex z (1,1) ; //z = 1+2I
complex z1 = z ; //z1 = 1 +2I
complex z2 ;
.
z2 = z1 ; //z2 = 1 +2I
Atat in cazul declaratiei lui z1 ,cat si in cazul instructiunii de atribuire pentru z2 , se copiaza componentele date ale obiectului complex din dreapta caracterului '"'"in zona de memorie alocata componentelor date ale obiectulu aflat in stanga aceluiasi caracter.
In cazul declaratiei lui z1 , caracterul "=" se utilizaeaza pentru a realiza o initializare , iar in cazul instructiunii:
z2 = z1 ;
acelasi carater se utilizeaza pentru a realiza o atribuire.
Ambele operatii sant definite in mod implicit si se realizeaza asa cum sa indicat mai sus,adica prin simpla copiere a datelor membru
B.Stroustrup numeste "bitwise copy" (copiere la nivel de biti) copierile de acelasi fel.
In cazul obiectelor clasei complex, copierea din cadrul operatiei de initializare este identica cu cea utilizata in cadrul operatiei de atribuire.
In general,cele doua operatii nu sant identice.De asemenea,copierea de tip "bitwise copy" nu este totdeauna suficienta.Acest fapt rezulta imediat daca reluam exemplul pentru implementarea tipului abstract string (vezi 22.90 sau sir (vezi exercitiul 22.12)
Consideram clasa sir,definita ca mai jos :
class sir
Functiile membru de mai sus ,sunt definte in exercitiul 22.12
Fie declaratia:
sir sir1 ("C++ este un C mai bun ");
La instantierea obiectului sir1 se apeleaza constructorul :
sir (char *s)
Acesta rezolva zona de memorie in memoria heap pentru textul:
C++ este un C mai bun
Si atribuie adresa de inceput a acestei zone pointerului:
sir1.psir
De asemene,se atribuie datei membru:
sir1.lung
valoarea 21 (numarul de caractere din compunearea textului de initializare a obiectului sir1,fara a considera si caracterul NUL).
Sa consideram declaratia pentru instantierea obiectului sir2 cu initializarea acestuia folosind obiectul sir1:
sir sir2 = sir1 ;
Utilizatorul foloseste o astfel de declaratie pentru a obtine acelasi efect ca si cand ar utiliza declaratia:
(2) sir sir2 ("C++ este un C mai bun") ;
In realitate ,cele doua declaratii nu realizeaza acelasi lucru.
In cazul declaratiei (10,la instantierea lui sir2 se apeleaza
constructorul:
sir (int nrcar = 70 )
care rezerva o zona de memorie de 70 de octeti in care se pastreaza sirul vid.
Adresa de inceput a acestei zone se atribuie lui sir.psir ,apoi se copiaza cu bit(bitwise copy) valorile componentelor obiectului sir1 (sir1.psir si sir.lung) in zonele de memorie alocate componentelor corespunzatoare ale obiectului sir2.In felul acesta ,sir2.psir are acceasi valoare ca si sir1.psir.Zona de memmorie de 70 de octeti alocata in memmoria heap este pierduta si nu se mai poate face acces la ea.De asemenea ,textul de initializare este pastrat intr-un singur exemplar,situatie care,de obicei,va conduce la erori in prelucrarea ulterioara a obiectelor sir1 si sir2.
De asemenea,la distrugerea obiectelor sir1 si sir2 ar urma ca textul de initializare,pastrat intr-un singur exemplar,sa fie eliberat din memmoria heap de doua ori.De accea,copierea implicita bit cu bit a obiectelor nu este o solutie corecta pentru obiectele clasei sir.
Instantierea lui sir2 cu ajutorul decalaratiei (2) nu are nici o legatura cu instantierea lui sir1 si se rezerva o zona de memorie in memoria heap distincta fata de cea rezervata pentru acelasi text la instantierea lui sir1.De accea,
sir1.psir
si
sir2.psir
au valori diferite.
Acelasi lucru este valabil si in cazul atriburilor.Fie de exemplu instantierile:
sir1 sir1("C++ este un C mai bun ");
sir sir2;
Sir2 = sir1 ;
realizeaza o copiere bit cu bit,deci sir1.psir si sir2.psir devin egale si pointeaza spre acceasi zona de memorie heap care a fost rezervata la instantierea lui sir1.Ca si in cazul declaratiei (2),zona de memorie din memoria heap alocata la instantierea lui sir2 este pierduta in urma atribuirii,deoarece ea devine inaccesibila.
Din cele de mai sus,rezulta ca pentru obiectele care sant instantieri ale clasei sir nu este suficienta copierea implcita bit cu bit.In astfel de cazuri este nevoie ca utilizatorul sa defineasca copieri specifice,care sa tina seama de natura componentelor obiectelor.
In general,operatia de initializare prin copiere direfita de cea de atribuire,deoarece initializarea este insotita de alocare.De accea,sant necesare doua tipuri de copieri,una care sa se realizeze la initializare prin copiere,adica declaratii de forma:
nume_clasa obiect2 = obiect1 ;
si una pentru atribuiri de obiecte:
obiect2 = obiect1
Initializarea prin copiere se realizeaza cu ajutorulconstructorului de copiere care a fost definit in paragraful 22.8.
Un astfel de constructor are un prototip de forma:
nume_clasa ( const nume_clasa & obiect ) ;
Mentionam ca,un astefel de constructor se apeleaza automat la instantierea unui obiect printr-o declaratie de forma (3).
Copierea bit cu bit se realizeaza numai dca clasa nu are un astfel de constructor.
Clasa sir, definita in exercitiul 22.12 .,are un constructor de copiere definit ca mai jos:
sir :: sir (sonst sir& s)
Acest constructor se va apela automat la intalnirea declaratiei (1) si rezultatul lui consta in accea ca declaratia (1) are acelasi efect ca si declaratia (2).
Constructorul de copiere nu se apeleaza la atribuiri.
Pentru a solutiona in mod corect atribuirile de obiectede felul celor de mai sus , este necesar sa supraincarcam in mod corespunzatoroperatorul de atrbuire ( = ).
In principiu,constructorii de copiere si supraincarcarea operatorului de atribuire sant necesari pentru clasele care au ca date membru pointeri spre zone alocate in memoria heap (alocate dinamic)
Un alt caz in care se aloca obiecte temporare pe stiva se de accea intervin operatii similare cu cele de la initializare.
Pentru ca aceste transferuri de obiecte sa se faca corect pentru obiecte cu componente pointeri spre date din memoria heap ,este necesar sa avem constructor de copiere in clasa respectiva.
Acesta se va apela automat atat la
transferul prin parametri a obiectelor precum si returnarea de obiecte,se
realizeazaaplicand-se copierea implicia,adica copierea bit cu bit.
Un exemplu in care se utilizeaza
copierea bit cu bit, la returnarea de obiecte,este clasa complex,implementat in exercitiul 22.1
In legatura cu supraincarcarea operatorului de atribuire este neecsar sa amintim urmatoarele:
supraincarcarea operatorului de atribuire se poate realizanumai printr-o
functie membru care nu este statica ;
obiectele operatorului se pot transfera prin valoare sau referinta;
obiectul din stanga operatorului de atribuire,dca are componente pointeri
spre zone alocate in memoria heap trebuie eliberate inaite de a se aloca
zone noi in vederea copierii elementelor corespunzatoare ale obiectului
din dreapta operatorului respectiv;se obisnuieste sa se spuna ca obiectul
din stanga operatorului de atribuire trebuie "curatat" inainte de a se
atribui valorile obiectului din dreapta operatorului de atribuire.
De obicei , functia pentru supraincarcarea operatorului de atribuire are un parametru de tip referinta si returneaza o referinta la obiectul curent.Aceasta deoarece transferul prin referinta este mai eficient decat transferul obiectelor.
Avand in vedere acest fapt,se recomanda urmatorul antet pentru supraincarcarea operatorului de atribuire :
nume_clasa& nume_clasa::operator = (const nume_clasa & operand_drept)
Obiectul curent este operandul stand al atribuirii
O astfel de functie se apeleza automat pentru reazlizarea atriburilor de forma:
obiect2 = obiect1;
unde:
obiect1 si obiect2 - Sant instantieri ale clasei nume_clasa.
Atribuirile de forma:
obiect1 = obiect2 ;
Nu au nici un efect.De accea ,atrbuirile de acest fel se vor elimina.In acest scop,in corpul functiei pentru supraincarcarea operatorului de atrivuire se testeaza daca operatorul curent este diferit de cel referit prin parametrul si numai in acest caz se executa corpul functiei.
Folosind antetul de mai sus,un astfel de test se poate realiza astfel:
if(this != &operand_drept) else //operanzi identici
return *this ;
Operatorul de atribuire are si o alta forma care se utilizeaza pentru a face prescurtari.Este vorba de formatul:
op=
unde:
op -Este un operator binar aritmetic sau logic pe biti.
Aceste variante pot fi ele supraincarcate in mod corespunzator.Mentionam ca,daca operatorii op si = sant supraincarcati,nu decurge ca este supraincarcat si operatorul op=.Acesta trebuie supraincarcat separat in mod corespunzator.
Operatorul op= se supraincarca folosind functii membru nestatice care au un prototip similar cu functiile pentru supraincarcarea operatorului de atribuire.
Exercitii:
23.14 Sa se implementeze tipul abstract sir pentru instantierea sirurilor de caractere.
Tipul sir a fost definit in exercitiul 22.12.
In exercitiul de fata,se schimba functia membru atribsir a clasei sir cu functia care supraincarca operatorulde atribuire.
In plus,se supraincarca operatorii + si += pentru concatenarea sirurilor de caractere.
Expresia:
sir1 = sir2 = sir3
este legata pentru obiectele clasei sir si ea atribuie lui sir1,sirul obtinut prin concatenarea lui sir3 la sfarsitul lui sir2.
Expresia
sir1 += sir2
este legata pentru obiectele clasei sir si ea are ca efect concatenarea lui sir2 la sfarsitul lui sir1.
FISIERUL BXXIII14.H
class sir ;
Fisierul de mai jos este extensie .cpp si el contine definitiile functiilor membru.
FISIERUL BXXIII14
#ifndef __STDIO_H
#include <stdio.h>
#define __STDIO_H
#endif
#ifndef __STRING_H
#include <string.h>
#define __STRING_H
#endif
#idndef __SIR_H
#include "BXXIII14.H"
#define __SIR_H
#endif
sir::sir (chat *s)
/* constructor utilizat la initializarea obiectului cu pointerul spre copia in memoria heap a sirului spre care pointeaza s */
sir:::sir (int dim)
/*-constructor utilizat pentru a rezolva in memoria heap o zona de dim+1 octeti;
-pastreaza sirul vid in zona respectiva.*/
sir::sir(const sir& s) //constructor de copiere
inline sir::~sir //destructor
inline int sir::retlung()
/*returneaza numarul de caractere din compunerea sirului fara caracterul NUL */
inline void sir::afsir() //afiseaza sirul spre care pointea psir
int sir:citsir()
/* -citeste un sir de la intrarea standard si-l pasteaza in zona heap rezervata pentru
obiectul curent;
-returneaza:
0 - la sfarsit de fisier;
-1 - la trunchiere;
1 - altfel
*/
sir& sir::operator = (const sir & operand_drept)
//supraincarca operatorul =
return *this ;
sir sir::operaot+ (sir& sir2)
/* returneaza un obiect care este rezultatul concatenarii obiectuluireferit de sir2 la
obiectul curent */
sir& sir ::operator += (sir& operand_drept(
/*concatenarea obiectului operand_drept la obiectul curent */
23.15. Sa se scrie un program care citeste intregi din intervalul [1,99] si afiseaza exprimarile lor prin cuvinte.
Programul construieste exprimarea in cuvinte a numerelor din intervalul respectiv prin concatenarea compoentelor sale.
Exemple:
11 unsprezece
12 doisprezece
.
15 cincisprezece
16 saisprezece
17 saptesprezece
.
20 douazeci
21 douazeci si unu
22 douazeci si doi
.
29 douazeci si noua
30 treizeci
.
37 treizeci si sapte
.
40 patruzeci
.
45 patruzeci si cinci
.
60 saizeci
.
66 saizeci si sase
.
70 saptezeci
.
76 saptezeci si sase
.
99 nousazeci si noua
Se observa ca numerele peste 10,dar mai mici de 20 se termina in sufizul sprezece.De asemenea,daca consideram numerele de la 1 la 9,observam ca exprimarea in cuvinte a acestora se urilizeaza drept componentepentru exprimarea in cuvinte a numerelor maimari.Exista cateva exceptii,ca de exemplu:
11,16,20-29 etc.
Tinand seama de aceste observatii,vom proceda camai jos.
Se defineste tabloul cu denumirile unitatilor:
char *unitate[] = ;
Pentru un numar n din intervalul [1,9] exprimarea este data de elementul unitate[n].
Pentru un numar n din intervalul [12,19] diferit de 16,exprimarea se obtine concatenand unitate[n/10] cu sprezece.
Pentru numerele n mai mari decat 20,exprimarile in cuvinte se termina prin:
Douazeci pentru 20-29
Treizeci pentru 30-39
Patruzeci pentru 40-49
Cincizeci pentru 50-59
Saizeci pentru 60-69
Saptezeci pentru 70-79
Optzeci pentru 80-89
Nouazeci pentru 90-99
Aceste cuvinte su sufixul zeci ,iar prefixul este un element al tabloului unitate,exceptand intervalele 20-29 si 60-69.Pentru primul interval prefizul este doua,iar pentru al doilea interval prefixul este sai.
Rezulta urmatorul procedeu:
daca n/10=2,atunci prefixul doua se concatenea cu zeci;
daca n/10=6,atunci prefixul sai se concateneaza cu zeci;
altfel unitate[n/10] se concateneaza cu zeci
PROGRAMUL BXXIII15
#include <stdlib.h>
#include "BXXIII14.CPP" //clasa sir
#include "BVIII2.CPP" //pcit_int
#include "BVIII3.CPP" //pcit_int_lim
main ()
/* -citeste intregi zecimali din intervalul [1,99[;
afiseaza exprimarile lor in cuvinte
*/
;
sir zece ("zece");
sir spre ("spre");
sir un ("un");
sir sai ("sai");
sir doua ("doua");
sir zeci ("zeci");
sir si ("si");
sir rez;
int q,r;
for (;;)
case 1:
break;
}
case 2 :
case 6:
default :
} //sfarsit switch
//afisare n si exprimare prin cuvinte
printf ("n n = %dt",n);
rez.afsir();
}
}
Observatie :
Corpul functiri poate fi scris mai compact folosind instructiuni if in locul instructiunii switch:
if (q = = 00 //[1,9]
sir rez0(unit[n]);
rez=rez0;
else
if (q = =1)
rez += spre + zece;
}
} //sfarsit [10,19]
else
rez += zeci;
if (r)
23.3 Supraincarcarea operatorului [] (operatorul de indexare)
Operatorul [] predefinit se utilizeaza pentru a face acces la elementele unui tablou.
De exemplu,daca tab este un tablou unidimensional de un tip predefinit,atunci o expresie de forma:
tab [exp]
permite acces la elementul tabloului tab de indice exp,exp fiind o expresie care furnizeaza o valoare de tip intreg.
Constructia (1) a fost numita ariabila cu indici.Ea este o expresie formata din doi operanzi:
tab si exp
la care se aplica operatorul de indexare ( [] ).De obicei, expresia (1) are si o interpretare unitara si anume se considera ca formand un operand.Aceasta interpretare este posibila datorita faptului ca parantezele sant operatori de prioritate maxima.
Interpretarea expresiei (1) ca operand se justifica datorita faptului ca ea area utilizari similare cu numele unei variabile simple;ambele permit acces la o data de un tip predefinit.
Constructia de mai sus este variabila si in cazuri mai generale,cand primul operand este chiar o expresie de pointeri,care are ca valoare un pointer.
Exemplu:
Int *p;
Int I,j;
Expresiile de mai jos sant legale:
p[1]
p[i]
p[i+2]
p[i-1]
(p+20[i*3+j]
(p-10)[i*3-2*j]
Utilizatorul poate supraincarca operatorul [] pentru a da sens constructiilor de felul celor de mai sus si in cazul in care operanzii sant obiecte.
Expresiile de forma (1) sunt expresii lvalue (se pot utiliza ca parte stanga intr-o atribuire).De accea,la supraincarcarea operatorului [] pentru tipuri abstracte,ca si la supraincarcarea operatorului = ,se va utiliza o functie membru nestatica,functie care sa returneze o referinta la elementul selectat prin functia respectiva.
In aceste conditii,functia pentru supraincarcarea operatorului [] are antetul general:
Tip & nume_clasa::operator[](tip_indice i)
Odata supraincarcat operatorul [] ca mai sus,sunt legate expresiile de forma:
obiect [exprsie]
unde:
obiect -Este o instatiere a clasei nume_clasa
expresie -Este o expresie care are tipul tip_indice sau un tip convertibil spre
acesta
Expresia (2) are ca rezultat o referinta la elementul definit prin functia care supraincarca operatorul [].Ea este o lvalue si deci se poate utiliza in ambele parti ale unei expresii de atribuire.De asemenea,expresia (2) se poate interpreta ca fiind un operand,neglijand faptul ca ea se compune din doi operanzi la care se aplica operatorul de indexare.
Operandul de aceasta forma reprezinta un apel al functiei membru operator[].
Astfel, operandul (2) de mai sus,se evalueaza prin apelul:
obiect.operator[] (expresie)
unde:
operator[] -Reprezinta numele functiei membru care supraincarca operatorul [] si ea este apelata pentru obiectul obiect si indicele expresie.
Interesul pentru formatul (2) fata de apelulexprimat prin (3) rezulta din faptul ca reprezinta o exprimare clara si care este incetatenita pentru tipurile predefinite de date.