Fortran, FISIERUL BXXIII12H, BXXIII12, PROGRAMUL BXXIII13



-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



Observatie


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


FISIERUL BXXIII12H


#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


FISIERUL BXXIII12


#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.


PROGRAMUL BXXIII13


#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.


Exemplu


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;


Ariburiea :


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.